mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-25 22:23:14 -04:00
modules: muc_uart: update sleep/wake
Wake gpio has a pull-up resistor. Change it to active low, and set to input during sleep. Also rework the sleep/wake logic. ACK/NACK is no longer sent on sleep requests. Line is pulled for an ACK and NACK is changed to REJECT. Change-Id: I9f2f9784e5f71ba1acea2a149519ddca788aa430 Signed-off-by: Ryan Lattrel <ryanl@motorola.com> Reviewed-on: https://gerrit.mot.com/1211698 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Kenneth Kessler <kennykessler@motorola.com> Submit-Approved: Jira Key
This commit is contained in:
parent
775bdbb8ed
commit
2619e62890
3 changed files with 144 additions and 58 deletions
|
|
@ -99,6 +99,12 @@ static int mmi_tty_write(struct tty_struct *tty,
|
|||
struct mmi_tty_data *tty_data = tty->driver_data;
|
||||
int ret;
|
||||
|
||||
/* If multiple users are trying to open/close/write to
|
||||
* the same device the kernel panics sometimes.
|
||||
*/
|
||||
if (!tty_data)
|
||||
return -ENODEV;
|
||||
|
||||
pr_debug("muc_uart tty: Writing %d bytes.\n", len);
|
||||
ret = (*mmi_tty_rx_cb)(tty_data->pdev,
|
||||
tty->index,
|
||||
|
|
|
|||
|
|
@ -28,9 +28,8 @@ struct mmi_uart_hdr_t {
|
|||
#define MSG_NACK_MASK 0x4000
|
||||
|
||||
/* Msg ID's */
|
||||
#define UART_SLEEP_REJ 0x0000
|
||||
#define UART_SLEEP_REQ 0x0001
|
||||
#define UART_SLEEP_ACK (UART_SLEEP_REQ|MSG_ACK_MASK)
|
||||
#define UART_SLEEP_NACK (UART_SLEEP_REQ|MSG_NACK_MASK)
|
||||
#define PACKETBUS_PROT_MSG 0x0002
|
||||
#define POWER_STATUS 0x0003
|
||||
#define POWER_CONTROL 0x0004
|
||||
|
|
|
|||
|
|
@ -42,6 +42,13 @@
|
|||
#define MUC_UART_SEND_SLEEP_ON_IDLE 1
|
||||
#define MUC_UART_IDLE_TIME 5000 /* ms */
|
||||
|
||||
/* When a sleep request on occurs either to or from the muc, sending
|
||||
* is blocked. If the sleep request never finishes for some reason
|
||||
* we will be stuck forever. Use this value for a timer to clear
|
||||
* the request eventually to try to maybe recover or panic.
|
||||
*/
|
||||
#define MUC_UART_SLEEP_REQ_TIMEOUT 5000 /* ms */
|
||||
|
||||
struct write_data {
|
||||
struct list_head list;
|
||||
uint16_t cmd;
|
||||
|
|
@ -62,6 +69,7 @@ struct mod_muc_data_t {
|
|||
atomic_t waiting_for_ack;
|
||||
struct timer_list idle_timer;
|
||||
struct timer_list ack_timer;
|
||||
struct timer_list sleep_req_timer;
|
||||
atomic_t mod_attached;
|
||||
struct workqueue_struct *write_wq;
|
||||
struct write_data *write_data_q;
|
||||
|
|
@ -72,8 +80,8 @@ struct mod_muc_data_t {
|
|||
struct delayed_work idle_work;
|
||||
struct delayed_work attach_work;
|
||||
struct delayed_work write_work;
|
||||
|
||||
struct power_supply *phone_psy;
|
||||
atomic_t sleep_req_pending;
|
||||
};
|
||||
|
||||
/* How long until we move on without an ack */
|
||||
|
|
@ -132,7 +140,10 @@ static inline void set_wait_for_ack(struct mod_muc_data_t *mm_data,
|
|||
int cmd)
|
||||
{
|
||||
if (mm_data)
|
||||
if (!(cmd & MSG_ACK_MASK || cmd & MSG_NACK_MASK)) {
|
||||
if (!(cmd & MSG_ACK_MASK ||
|
||||
cmd & MSG_NACK_MASK ||
|
||||
cmd == UART_SLEEP_REQ ||
|
||||
cmd == UART_SLEEP_REJ)) {
|
||||
atomic_set(&mm_data->waiting_for_ack, 1);
|
||||
mod_timer(&mm_data->ack_timer, jiffies +
|
||||
msecs_to_jiffies(MUC_UART_ACK_TIME));
|
||||
|
|
@ -150,7 +161,7 @@ static inline void clear_wait_for_ack(struct mod_muc_data_t *mm_data)
|
|||
atomic_add_unless(&mm_data->write_credits, 1, 100);
|
||||
pr_info("muc_uart clear_wait_for_ack, num credits: %d\n",
|
||||
atomic_read(&mm_data->write_credits));
|
||||
cancel_delayed_work_sync(&mm_data->write_work);
|
||||
cancel_delayed_work(&mm_data->write_work);
|
||||
queue_delayed_work(mm_data->write_wq,
|
||||
&mm_data->write_work,
|
||||
msecs_to_jiffies(0));
|
||||
|
|
@ -223,6 +234,39 @@ static irqreturn_t muc_uart_attach_irq_handler(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static inline void set_sleep_req_pending(struct mod_muc_data_t *mm_data)
|
||||
{
|
||||
atomic_set(&mm_data->sleep_req_pending, 1);
|
||||
mod_timer(&mm_data->sleep_req_timer, jiffies +
|
||||
msecs_to_jiffies(MUC_UART_SLEEP_REQ_TIMEOUT));
|
||||
}
|
||||
|
||||
static inline void clear_sleep_req_pending(struct mod_muc_data_t *mm_data)
|
||||
{
|
||||
del_timer(&mm_data->sleep_req_timer);
|
||||
atomic_set(&mm_data->sleep_req_pending, 0);
|
||||
}
|
||||
|
||||
static int muc_uart_sync_wake(struct mod_muc_data_t *mm_data)
|
||||
{
|
||||
if (!mm_data->uart_pm_state) {
|
||||
mmi_uart_do_pm(mm_data->uart_data, true);
|
||||
mm_data->uart_pm_state = 1;
|
||||
}
|
||||
|
||||
gpio_direction_output(mm_data->wake_out_gpio, 0);
|
||||
|
||||
/* Only toggle the line if the muc isn't awake */
|
||||
if (gpio_get_value(mm_data->wake_in_gpio)) {
|
||||
gpio_set_value(mm_data->wake_out_gpio, 1);
|
||||
gpio_set_value(mm_data->wake_out_gpio, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Return 0 if the muc was already awake */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int muc_uart_send(struct mod_muc_data_t *mm_data,
|
||||
uint16_t cmd,
|
||||
uint8_t *payload,
|
||||
|
|
@ -242,12 +286,18 @@ static int muc_uart_send(struct mod_muc_data_t *mm_data,
|
|||
return -E2BIG;
|
||||
}
|
||||
|
||||
/* If we are waiting for an ack */
|
||||
if (atomic_read(&mm_data->waiting_for_ack)) {
|
||||
/* If we are waiting on a sleep req */
|
||||
if (atomic_read(&mm_data->sleep_req_pending)) {
|
||||
mmi_uart_report_tx_err(mm_data->uart_data);
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
/* If sending a sleep req, block further sends until
|
||||
* we get a rej or go to sleep
|
||||
*/
|
||||
if (cmd == UART_SLEEP_REQ)
|
||||
set_sleep_req_pending(mm_data);
|
||||
|
||||
/* If someone else is sending */
|
||||
if (mmi_uart_set_tx_busy(mm_data->uart_data)) {
|
||||
mmi_uart_report_tx_err(mm_data->uart_data);
|
||||
|
|
@ -255,28 +305,19 @@ static int muc_uart_send(struct mod_muc_data_t *mm_data,
|
|||
}
|
||||
|
||||
/* If powered off, try to wake. */
|
||||
if (!mm_data->uart_pm_state) {
|
||||
if (in_interrupt()) {
|
||||
mmi_uart_clear_tx_busy(mm_data->uart_data);
|
||||
schedule_delayed_work(&mm_data->wake_work, msecs_to_jiffies(0));
|
||||
mmi_uart_report_tx_err(mm_data->uart_data);
|
||||
return -EAGAIN;
|
||||
} else {
|
||||
mmi_uart_do_pm(mm_data->uart_data, true);
|
||||
mm_data->uart_pm_state = 1;
|
||||
}
|
||||
if (muc_uart_sync_wake(mm_data)) {
|
||||
mmi_uart_clear_tx_busy(mm_data->uart_data);
|
||||
mmi_uart_report_tx_err(mm_data->uart_data);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* Rising edge to wake muc */
|
||||
gpio_set_value(mm_data->wake_out_gpio, 0);
|
||||
gpio_set_value(mm_data->wake_out_gpio, 1);
|
||||
|
||||
reset_idle_timer(mm_data);
|
||||
|
||||
pkt_size = sizeof(struct mmi_uart_hdr_t) +
|
||||
payload_length + sizeof(calc_crc);
|
||||
pkt = kmalloc(pkt_size, GFP_ATOMIC);
|
||||
if (!pkt) {
|
||||
mmi_uart_clear_tx_busy(mm_data->uart_data);
|
||||
mmi_uart_report_tx_err(mm_data->uart_data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -291,7 +332,7 @@ static int muc_uart_send(struct mod_muc_data_t *mm_data,
|
|||
*(uint16_t *)(pkt + pkt_size - sizeof(calc_crc)) =
|
||||
cpu_to_le16(calc_crc);
|
||||
|
||||
pr_info("muc_uart sending message\n");
|
||||
pr_info("muc_uart sending message type 0x%x\n", cmd);
|
||||
time_uart_start = ktime_get();
|
||||
ret = mmi_uart_send(mm_data->uart_data, pkt, pkt_size);
|
||||
kfree(pkt);
|
||||
|
|
@ -303,6 +344,11 @@ static int muc_uart_send(struct mod_muc_data_t *mm_data,
|
|||
if (ret == 0) {
|
||||
set_wait_for_ack(mm_data, cmd);
|
||||
return payload_length;
|
||||
} else {
|
||||
/* Just in case we failed to send and we were
|
||||
* going to sleep
|
||||
*/
|
||||
clear_sleep_req_pending(mm_data);
|
||||
}
|
||||
|
||||
time_send_end = ktime_get();
|
||||
|
|
@ -322,6 +368,7 @@ static int muc_uart_queue_send(struct mod_muc_data_t *mm_data,
|
|||
if (payload_len > UART_MAX_MSG_SIZE - MSG_META_DATA_SIZE)
|
||||
return -E2BIG;
|
||||
|
||||
/* TODO this should probably panic */
|
||||
if (atomic_dec_if_positive(&mm_data->write_credits) < 0)
|
||||
pr_err("muc_uart_queue_send: out of credits! "
|
||||
"queue size is: %d\n", mm_data->write_q_size);
|
||||
|
|
@ -333,6 +380,7 @@ static int muc_uart_queue_send(struct mod_muc_data_t *mm_data,
|
|||
}
|
||||
|
||||
write_data->payload = kmalloc(payload_len, GFP_ATOMIC);
|
||||
/* TODO this should probably panic */
|
||||
if (WARN_ON(!write_data->payload)) {
|
||||
atomic_inc(&mm_data->write_credits);
|
||||
kfree(write_data);
|
||||
|
|
@ -401,6 +449,11 @@ static void muc_uart_send_work(struct work_struct *w)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Wake the muc up here if it isn't already awake
|
||||
* Send will wake it too, but hopefully this will
|
||||
* reduce the chances it returns an error.
|
||||
*/
|
||||
muc_uart_sync_wake(mm_data);
|
||||
mutex_lock(&tx_lock);
|
||||
ret = muc_uart_send(mm_data,
|
||||
write_data->cmd,
|
||||
|
|
@ -423,7 +476,7 @@ static void muc_uart_send_work(struct work_struct *w)
|
|||
cancel_delayed_work(&mm_data->write_work);
|
||||
queue_delayed_work(mm_data->write_wq,
|
||||
&mm_data->write_work,
|
||||
msecs_to_jiffies(100));
|
||||
msecs_to_jiffies(10));
|
||||
} else {
|
||||
kfree(write_data->payload);
|
||||
kfree(write_data);
|
||||
|
|
@ -438,11 +491,11 @@ static int muc_uart_send_bootmode(struct mod_muc_data_t *mm_data)
|
|||
|
||||
bootmode.hwid = bi_hwrev();
|
||||
|
||||
if(strncmp(param_bootmode, "mot-factory", strlen("mot-factory")) == 0) {
|
||||
if (strncmp(param_bootmode, "mot-factory", strlen("mot-factory")) == 0) {
|
||||
bootmode.boot_mode = FACTORY;
|
||||
} else if(strncmp(param_bootmode, "qcom", strlen("qcom")) == 0) {
|
||||
} else if (strncmp(param_bootmode, "qcom", strlen("qcom")) == 0) {
|
||||
bootmode.boot_mode = QCOM;
|
||||
} else if(strncmp(param_bootmode, "bp-tools", strlen("bp-tools")) == 0) {
|
||||
} else if (strncmp(param_bootmode, "bp-tools", strlen("bp-tools")) == 0) {
|
||||
bootmode.boot_mode = BP_TOOLS;
|
||||
} else {
|
||||
/* TODO defaults to normal. Should we have unknown/other? */
|
||||
|
|
@ -500,21 +553,25 @@ static void muc_uart_handle_message(struct mod_muc_data_t *mm_data,
|
|||
uint8_t *payload,
|
||||
size_t payload_len)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
pr_info("muc_uart_handle_message: got msg of type %x.\n", hdr->cmd);
|
||||
switch (hdr->cmd) {
|
||||
case UART_SLEEP_REQ: {
|
||||
/* TODO always ack for now. */
|
||||
muc_uart_send(mm_data, UART_SLEEP_ACK, NULL, 0);
|
||||
schedule_delayed_work(&mm_data->sleep_work, msecs_to_jiffies(0));
|
||||
spin_lock_irqsave(&write_q_lock, flags);
|
||||
if (mm_data->write_data_q)
|
||||
muc_uart_send(mm_data,
|
||||
UART_SLEEP_REJ,
|
||||
NULL, 0);
|
||||
else {
|
||||
set_sleep_req_pending(mm_data);
|
||||
schedule_delayed_work(&mm_data->sleep_work, msecs_to_jiffies(0));
|
||||
}
|
||||
spin_unlock_irqrestore(&write_q_lock, flags);
|
||||
break;
|
||||
}
|
||||
case UART_SLEEP_ACK: {
|
||||
clear_wait_for_ack(mm_data);
|
||||
schedule_delayed_work(&mm_data->sleep_work, msecs_to_jiffies(0));
|
||||
break;
|
||||
}
|
||||
case UART_SLEEP_NACK: {
|
||||
clear_wait_for_ack(mm_data);
|
||||
case UART_SLEEP_REJ: {
|
||||
clear_sleep_req_pending(mm_data);
|
||||
reset_idle_timer(mm_data);
|
||||
break;
|
||||
}
|
||||
|
|
@ -547,14 +604,14 @@ static void muc_uart_handle_message(struct mod_muc_data_t *mm_data,
|
|||
}*/
|
||||
case POWER_CONTROL: {
|
||||
/* TODO nack if size is wrong ? */
|
||||
if(payload_len == sizeof(struct power_control_t))
|
||||
if (payload_len == sizeof(struct power_control_t))
|
||||
muc_uart_set_power_control(
|
||||
(struct power_control_t *)payload);
|
||||
muc_uart_send(mm_data, hdr->cmd|MSG_ACK_MASK, NULL, 0);
|
||||
break;
|
||||
}
|
||||
case BOOT_MODE: {
|
||||
if(muc_uart_send_bootmode(mm_data) >= 0)
|
||||
if (muc_uart_send_bootmode(mm_data) >= 0)
|
||||
muc_uart_send(mm_data, BOOT_MODE|MSG_ACK_MASK, NULL, 0);
|
||||
else
|
||||
muc_uart_send(mm_data, BOOT_MODE|MSG_NACK_MASK, NULL, 0);
|
||||
|
|
@ -677,10 +734,9 @@ static void muc_uart_sleep_work(struct work_struct *w)
|
|||
mm_data->uart_pm_state = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gpio_get_value(mm_data->wake_out_gpio))
|
||||
gpio_set_value(mm_data->wake_out_gpio, 0);
|
||||
|
||||
/* Set to high Z / float */
|
||||
gpio_direction_input(mm_data->wake_out_gpio);
|
||||
clear_sleep_req_pending(mm_data);
|
||||
mmi_uart_clear_tx_busy(mm_data->uart_data);
|
||||
}
|
||||
}
|
||||
|
|
@ -692,7 +748,6 @@ static void muc_uart_wake_work(struct work_struct *w)
|
|||
struct mod_muc_data_t *mm_data =
|
||||
container_of(w, struct mod_muc_data_t, wake_work.work);
|
||||
|
||||
disable_irq(mm_data->wake_irq);
|
||||
cancel_delayed_work_sync(&mm_data->sleep_work);
|
||||
atomic_set(&mm_data->suspend_ok, 0);
|
||||
|
||||
|
|
@ -703,16 +758,7 @@ static void muc_uart_wake_work(struct work_struct *w)
|
|||
schedule_delayed_work(&mm_data->wake_work, msecs_to_jiffies(500));
|
||||
else {
|
||||
pr_info("muc_uart_wake_work: on.\n");
|
||||
if (!mm_data->uart_pm_state) {
|
||||
mmi_uart_do_pm(mm_data->uart_data, true);
|
||||
mm_data->uart_pm_state = 1;
|
||||
}
|
||||
|
||||
/* Rising edge to wake muc */
|
||||
gpio_set_value(mm_data->wake_out_gpio, 0);
|
||||
gpio_set_value(mm_data->wake_out_gpio, 1);
|
||||
|
||||
enable_irq(mm_data->wake_irq);
|
||||
muc_uart_sync_wake(mm_data);
|
||||
mmi_uart_clear_tx_busy(mm_data->uart_data);
|
||||
|
||||
/* Send boot mode on initial wake */
|
||||
|
|
@ -727,18 +773,37 @@ static irqreturn_t muc_uart_wake_irq_handler(int irq, void *data)
|
|||
{
|
||||
struct mod_muc_data_t *mm_data = (struct mod_muc_data_t *)data;
|
||||
|
||||
cancel_delayed_work(&mm_data->wake_work);
|
||||
schedule_delayed_work(&mm_data->wake_work, msecs_to_jiffies(0));
|
||||
/* 0->1 irq is to sleep, 1->0 irq is to wake */
|
||||
if (gpio_get_value(mm_data->wake_in_gpio)) {
|
||||
if (atomic_read(&mm_data->sleep_req_pending)) {
|
||||
cancel_delayed_work(&mm_data->sleep_work);
|
||||
schedule_delayed_work(&mm_data->sleep_work,
|
||||
msecs_to_jiffies(0));
|
||||
}
|
||||
} else {
|
||||
cancel_delayed_work(&mm_data->wake_work);
|
||||
schedule_delayed_work(&mm_data->wake_work, msecs_to_jiffies(0));
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void muc_uart_idle_work(struct work_struct *w)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct mod_muc_data_t *mm_data =
|
||||
container_of(w, struct mod_muc_data_t, idle_work.work);
|
||||
|
||||
if (muc_uart_queue_send(mm_data, UART_SLEEP_REQ, NULL, 0) < 0)
|
||||
schedule_delayed_work(&mm_data->idle_work, msecs_to_jiffies(1000));
|
||||
if (!atomic_read(&mm_data->sleep_req_pending)) {
|
||||
mutex_lock(&tx_lock);
|
||||
spin_lock_irqsave(&write_q_lock, flags);
|
||||
if(!mm_data->write_data_q &&
|
||||
muc_uart_send(mm_data, UART_SLEEP_REQ, NULL, 0) < 0)
|
||||
schedule_delayed_work(&mm_data->idle_work,
|
||||
msecs_to_jiffies(1000));
|
||||
spin_unlock_irqrestore(&write_q_lock, flags);
|
||||
mutex_unlock(&tx_lock);
|
||||
}
|
||||
|
||||
reset_idle_timer(mm_data);
|
||||
}
|
||||
|
|
@ -766,6 +831,18 @@ static void muc_uart_ack_cb(unsigned long timer_data)
|
|||
}
|
||||
}
|
||||
|
||||
static void muc_uart_sleep_req_cb(unsigned long timer_data)
|
||||
{
|
||||
struct mod_muc_data_t *mm_data =
|
||||
(struct mod_muc_data_t *)timer_data;
|
||||
|
||||
/* TODO this should probably panic */
|
||||
if (mm_data) {
|
||||
if(atomic_read(&mm_data->sleep_req_pending))
|
||||
atomic_set(&mm_data->sleep_req_pending, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int muc_uart_register_cb(struct platform_device *pdev,
|
||||
void *uart_data)
|
||||
{
|
||||
|
|
@ -880,6 +957,9 @@ static int muc_uart_probe(struct platform_device *pdev)
|
|||
setup_timer(&mm_data->ack_timer,
|
||||
muc_uart_ack_cb,
|
||||
(unsigned long)mm_data);
|
||||
setup_timer(&mm_data->sleep_req_timer,
|
||||
muc_uart_sleep_req_cb,
|
||||
(unsigned long)mm_data);
|
||||
|
||||
mm_data->wake_out_gpio = of_get_gpio(np, 0);
|
||||
if (mm_data->wake_out_gpio >= 0) {
|
||||
|
|
@ -903,7 +983,7 @@ static int muc_uart_probe(struct platform_device *pdev)
|
|||
if (devm_request_irq(&pdev->dev,
|
||||
mm_data->wake_irq,
|
||||
muc_uart_wake_irq_handler,
|
||||
IRQF_TRIGGER_RISING,
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
||||
pdev->dev.driver->name,
|
||||
mm_data))
|
||||
goto err2;
|
||||
|
|
@ -978,6 +1058,7 @@ static int muc_uart_remove(struct platform_device *pdev)
|
|||
|
||||
del_timer(&mm_data->idle_timer);
|
||||
del_timer(&mm_data->ack_timer);
|
||||
del_timer(&mm_data->sleep_req_timer);
|
||||
cancel_delayed_work_sync(&mm_data->wake_work);
|
||||
cancel_delayed_work_sync(&mm_data->sleep_work);
|
||||
cancel_delayed_work_sync(&mm_data->idle_work);
|
||||
|
|
|
|||
Loading…
Reference in a new issue