From 5734987f6196854c9c51a69b28a4afbe8519e216 Mon Sep 17 00:00:00 2001 From: Ramprasad Katkam Date: Sun, 11 Nov 2018 18:34:57 +0530 Subject: [PATCH 1/2] soc: swr-mstr: Add wake lock support to prevent suspend Add lock and unlock sleep functions to master to prevent system suspend during interrupts or slave wakeup requests. Change-Id: Icb4ea354ec6e2925f83c2380f30ea8e37aca7449 Signed-off-by: Ramprasad Katkam --- soc/swr-mstr-ctrl.c | 215 ++++++++++++++++++++++++++++++++++++++++---- soc/swr-mstr-ctrl.h | 13 +++ 2 files changed, 210 insertions(+), 18 deletions(-) diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c index 8f1ad0822550..06f3883a1184 100644 --- a/soc/swr-mstr-ctrl.c +++ b/soc/swr-mstr-ctrl.c @@ -26,7 +26,8 @@ #include "swr-mstr-ctrl.h" #include "swrm_port_config.h" - +#define SWRM_SYSTEM_RESUME_TIMEOUT_MS 700 +#define SWRM_SYS_SUSPEND_WAIT 1 #define SWR_BROADCAST_CMD_ID 0x0F #define SWR_AUTO_SUSPEND_DELAY 3 /* delay in sec */ #define SWR_DEV_ID_MASK 0xFFFFFFFF @@ -82,6 +83,8 @@ static struct dentry *debugfs_poke; static struct dentry *debugfs_reg_dump; static unsigned int read_data; +static bool swrm_lock_sleep(struct swr_mstr_ctrl *swrm); +static void swrm_unlock_sleep(struct swr_mstr_ctrl *swrm); static bool swrm_is_msm_variant(int val) { @@ -1262,6 +1265,10 @@ static irqreturn_t swr_mstr_interrupt(int irq, void *dev) struct swr_device *swr_dev; struct swr_master *mstr = &swrm->master; + if (unlikely(swrm_lock_sleep(swrm) == false)) { + dev_err(swrm->dev, "%s Failed to hold suspend\n", __func__); + return IRQ_NONE; + } mutex_lock(&swrm->reslock); swrm_clk_request(swrm, true); @@ -1411,6 +1418,7 @@ handle_irq: mutex_lock(&swrm->reslock); swrm_clk_request(swrm, false); mutex_unlock(&swrm->reslock); + swrm_unlock_sleep(swrm); return ret; } @@ -1454,12 +1462,19 @@ static void swrm_wakeup_work(struct work_struct *work) mutex_lock(&swrm->devlock); if (!swrm->dev_up) { mutex_unlock(&swrm->devlock); - return; + goto exit; } mutex_unlock(&swrm->devlock); + if (unlikely(swrm_lock_sleep(swrm) == false)) { + dev_err(swrm->dev, "%s Failed to hold suspend\n", __func__); + goto exit; + } pm_runtime_get_sync(swrm->dev); pm_runtime_mark_last_busy(swrm->dev); pm_runtime_put_autosuspend(swrm->dev); + swrm_unlock_sleep(swrm); +exit: + pm_relax(swrm->dev); } static int swrm_get_device_status(struct swr_mstr_ctrl *swrm, u8 devnum) @@ -1545,6 +1560,10 @@ static void swrm_device_wakeup_vote(struct swr_master *mstr) __func__); return; } + if (unlikely(swrm_lock_sleep(swrm) == false)) { + dev_err(swrm->dev, "%s Failed to hold suspend\n", __func__); + return; + } pm_runtime_get_sync(swrm->dev); } @@ -1559,6 +1578,7 @@ static void swrm_device_wakeup_unvote(struct swr_master *mstr) } pm_runtime_mark_last_busy(swrm->dev); pm_runtime_put_autosuspend(swrm->dev); + swrm_unlock_sleep(swrm); } static int swrm_master_init(struct swr_mstr_ctrl *swrm) @@ -1639,6 +1659,7 @@ static int swrm_event_notify(struct notifier_block *self, case SWR_WAKE_IRQ_EVENT: if (swrm->ipc_wakeup && !swrm->ipc_wakeup_triggered) { swrm->ipc_wakeup_triggered = true; + pm_stay_awake(swrm->dev); schedule_work(&swrm->wakeup_work); } break; @@ -1745,6 +1766,21 @@ static int swrm_probe(struct platform_device *pdev) &swrm->clk_stop_mode0_supp)) { swrm->clk_stop_mode0_supp = FALSE; } + + ret = of_property_read_u32(swrm->dev->of_node, "qcom,swr-num-dev", + &swrm->num_dev); + if (ret) { + dev_dbg(&pdev->dev, "%s: Looking up %s property failed\n", + __func__, "qcom,swr-num-dev"); + } else { + if (swrm->num_dev > SWR_MAX_SLAVE_DEVICES) { + dev_err(&pdev->dev, "%s: num_dev %d > max limit %d\n", + __func__, swrm->num_dev, SWR_MAX_SLAVE_DEVICES); + ret = -EINVAL; + goto err_pdata_fail; + } + } + /* Parse soundwire port mapping */ ret = of_property_read_u32(pdev->dev.of_node, "qcom,swr-num-ports", &num_ports); @@ -1827,24 +1863,17 @@ static int swrm_probe(struct platform_device *pdev) mutex_init(&swrm->iolock); mutex_init(&swrm->clklock); mutex_init(&swrm->devlock); + mutex_init(&swrm->pm_lock); + swrm->wlock_holders = 0; + swrm->pm_state = SWRM_PM_SLEEPABLE; + init_waitqueue_head(&swrm->pm_wq); + pm_qos_add_request(&swrm->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); for (i = 0 ; i < SWR_MSTR_PORT_LEN; i++) INIT_LIST_HEAD(&swrm->mport_cfg[i].port_req_list); - ret = of_property_read_u32(swrm->dev->of_node, "qcom,swr-num-dev", - &swrm->num_dev); - if (ret) { - dev_dbg(&pdev->dev, "%s: Looking up %s property failed\n", - __func__, "qcom,swr-num-dev"); - } else { - if (swrm->num_dev > SWR_MAX_SLAVE_DEVICES) { - dev_err(&pdev->dev, "%s: num_dev %d > max limit %d\n", - __func__, swrm->num_dev, SWR_MAX_SLAVE_DEVICES); - ret = -EINVAL; - goto err_pdata_fail; - } - } - if (swrm->reg_irq) { ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm, SWR_IRQ_REGISTER); @@ -1940,6 +1969,9 @@ err_irq_fail: mutex_destroy(&swrm->force_down_lock); mutex_destroy(&swrm->iolock); mutex_destroy(&swrm->clklock); + mutex_destroy(&swrm->pm_lock); + pm_qos_remove_request(&swrm->pm_qos_req); + err_pdata_fail: err_memory_fail: return ret; @@ -1956,7 +1988,7 @@ static int swrm_remove(struct platform_device *pdev) free_irq(swrm->irq, swrm); else if (swrm->wake_irq > 0) free_irq(swrm->wake_irq, swrm); - + cancel_work_sync(&swrm->wakeup_work); pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); swr_unregister_master(&swrm->master); @@ -1966,6 +1998,8 @@ static int swrm_remove(struct platform_device *pdev) mutex_destroy(&swrm->iolock); mutex_destroy(&swrm->clklock); mutex_destroy(&swrm->force_down_lock); + mutex_destroy(&swrm->pm_lock); + pm_qos_remove_request(&swrm->pm_qos_req); devm_kfree(&pdev->dev, swrm); return 0; } @@ -2301,6 +2335,94 @@ int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data) } EXPORT_SYMBOL(swrm_wcd_notify); +/* + * swrm_pm_cmpxchg: + * Check old state and exchange with pm new state + * if old state matches with current state + * + * @swrm: pointer to wcd core resource + * @o: pm old state + * @n: pm new state + * + * Returns old state + */ +static enum swrm_pm_state swrm_pm_cmpxchg( + struct swr_mstr_ctrl *swrm, + enum swrm_pm_state o, + enum swrm_pm_state n) +{ + enum swrm_pm_state old; + + if (!swrm) + return o; + + mutex_lock(&swrm->pm_lock); + old = swrm->pm_state; + if (old == o) + swrm->pm_state = n; + mutex_unlock(&swrm->pm_lock); + + return old; +} + +static bool swrm_lock_sleep(struct swr_mstr_ctrl *swrm) +{ + enum swrm_pm_state os; + + /* + * swrm_{lock/unlock}_sleep will be called by swr irq handler + * and slave wake up requests.. + * + * If system didn't resume, we can simply return false so + * IRQ handler can return without handling IRQ. + */ + mutex_lock(&swrm->pm_lock); + if (swrm->wlock_holders++ == 0) { + dev_dbg(swrm->dev, "%s: holding wake lock\n", __func__); + pm_qos_update_request(&swrm->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + pm_stay_awake(swrm->dev); + } + mutex_unlock(&swrm->pm_lock); + + if (!wait_event_timeout(swrm->pm_wq, + ((os = swrm_pm_cmpxchg(swrm, + SWRM_PM_SLEEPABLE, + SWRM_PM_AWAKE)) == + SWRM_PM_SLEEPABLE || + (os == SWRM_PM_AWAKE)), + msecs_to_jiffies( + SWRM_SYSTEM_RESUME_TIMEOUT_MS))) { + dev_err(swrm->dev, "%s: system didn't resume within %dms, s %d, w %d\n", + __func__, SWRM_SYSTEM_RESUME_TIMEOUT_MS, swrm->pm_state, + swrm->wlock_holders); + swrm_unlock_sleep(swrm); + return false; + } + wake_up_all(&swrm->pm_wq); + return true; +} + +static void swrm_unlock_sleep(struct swr_mstr_ctrl *swrm) +{ + mutex_lock(&swrm->pm_lock); + if (--swrm->wlock_holders == 0) { + dev_dbg(swrm->dev, "%s: releasing wake lock pm_state %d -> %d\n", + __func__, swrm->pm_state, SWRM_PM_SLEEPABLE); + /* + * if swrm_lock_sleep failed, pm_state would be still + * swrm_PM_ASLEEP, don't overwrite + */ + if (likely(swrm->pm_state == SWRM_PM_AWAKE)) + swrm->pm_state = SWRM_PM_SLEEPABLE; + pm_qos_update_request(&swrm->pm_qos_req, + PM_QOS_DEFAULT_VALUE); + pm_relax(swrm->dev); + } + mutex_unlock(&swrm->pm_lock); + wake_up_all(&swrm->pm_wq); +} + #ifdef CONFIG_PM_SLEEP static int swrm_suspend(struct device *dev) { @@ -2309,7 +2431,49 @@ static int swrm_suspend(struct device *dev) struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); dev_dbg(dev, "%s: system suspend, state: %d\n", __func__, swrm->state); - if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) { + + mutex_lock(&swrm->pm_lock); + + if (swrm->pm_state == SWRM_PM_SLEEPABLE) { + dev_dbg(swrm->dev, "%s: suspending system, state %d, wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + swrm->pm_state = SWRM_PM_ASLEEP; + } else if (swrm->pm_state == SWRM_PM_AWAKE) { + /* + * unlock to wait for pm_state == SWRM_PM_SLEEPABLE + * then set to SWRM_PM_ASLEEP + */ + dev_dbg(swrm->dev, "%s: waiting to suspend system, state %d, wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + mutex_unlock(&swrm->pm_lock); + if (!(wait_event_timeout(swrm->pm_wq, swrm_pm_cmpxchg( + swrm, SWRM_PM_SLEEPABLE, + SWRM_PM_ASLEEP) == + SWRM_PM_SLEEPABLE, + msecs_to_jiffies( + SWRM_SYS_SUSPEND_WAIT)))) { + dev_dbg(swrm->dev, "%s: suspend failed state %d, wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + return -EBUSY; + } else { + dev_dbg(swrm->dev, + "%s: done, state %d, wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + } + mutex_lock(&swrm->pm_lock); + } else if (swrm->pm_state == SWRM_PM_ASLEEP) { + dev_dbg(swrm->dev, "%s: system is already suspended, state %d, wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + } + + mutex_unlock(&swrm->pm_lock); + + if ((!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev))) { ret = swrm_runtime_suspend(dev); if (!ret) { /* @@ -2355,6 +2519,21 @@ static int swrm_resume(struct device *dev) pm_request_autosuspend(dev); } } + mutex_lock(&swrm->pm_lock); + if (swrm->pm_state == SWRM_PM_ASLEEP) { + dev_dbg(swrm->dev, + "%s: resuming system, state %d, wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + swrm->pm_state = SWRM_PM_SLEEPABLE; + } else { + dev_dbg(swrm->dev, "%s: system is already awake, state %d wlock %d\n", + __func__, swrm->pm_state, + swrm->wlock_holders); + } + mutex_unlock(&swrm->pm_lock); + wake_up_all(&swrm->pm_wq); + return ret; } #endif /* CONFIG_PM_SLEEP */ diff --git a/soc/swr-mstr-ctrl.h b/soc/swr-mstr-ctrl.h index 13b85f42200e..a32fdc69b1b4 100644 --- a/soc/swr-mstr-ctrl.h +++ b/soc/swr-mstr-ctrl.h @@ -7,6 +7,8 @@ #define _SWR_WCD_CTRL_H #include #include +#include +#include #define SWR_ROW_48 0 #define SWR_ROW_50 1 @@ -35,6 +37,12 @@ enum { SWR_MSTR_SSR, }; +enum swrm_pm_state { + SWRM_PM_SLEEPABLE, + SWRM_PM_AWAKE, + SWRM_PM_ASLEEP, +}; + enum { SWR_IRQ_FREE, SWR_IRQ_REGISTER, @@ -111,6 +119,7 @@ struct swr_mstr_ctrl { struct mutex devlock; struct mutex mlock; struct mutex reslock; + struct mutex pm_lock; u32 swrm_base_reg; char __iomem *swrm_dig_base; u8 rcmd_id; @@ -149,6 +158,10 @@ struct swr_mstr_ctrl { u32 ipc_wakeup; bool dev_up; bool ipc_wakeup_triggered; + struct pm_qos_request pm_qos_req; + enum swrm_pm_state pm_state; + wait_queue_head_t pm_wq; + int wlock_holders; }; #endif /* _SWR_WCD_CTRL_H */ From 7e35478bf686128e73bbba9fcd9e61c6e173c233 Mon Sep 17 00:00:00 2001 From: Ramprasad Katkam Date: Wed, 21 Nov 2018 15:52:54 +0530 Subject: [PATCH 2/2] soc: swr-mstr: Store enabled interrupts information in master data Store interrupt mask variable in master private data to indicate the interrupts which are currently enabled. This is to avoid reading interrupt enable register, which varies on hardware version, for knowing the interrupt enabled info. Change-Id: Ic990decd1b1112a1ad2a6af0d9b42269fe4e1b4c Signed-off-by: Ramprasad Katkam --- soc/swr-mstr-ctrl.c | 27 ++++++++++++++------------- soc/swr-mstr-ctrl.h | 1 + 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c index 06f3883a1184..03d6c27b811d 100644 --- a/soc/swr-mstr-ctrl.c +++ b/soc/swr-mstr-ctrl.c @@ -977,6 +977,7 @@ static int swrm_slvdev_datapath_control(struct swr_master *master, bool enable) pr_err("%s: swrm is null\n", __func__); return -EFAULT; } + mutex_lock(&swrm->mlock); bank = get_inactive_bank_num(swrm); @@ -996,7 +997,7 @@ static int swrm_slvdev_datapath_control(struct swr_master *master, bool enable) return -EINVAL; } swr_master_write(swrm, SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN, - SWRM_INTERRUPT_STATUS_MASK); + SWRM_INTERRUPT_STATUS_MASK); /* apply the new port config*/ swrm_apply_port_config(master); } else { @@ -1257,7 +1258,7 @@ static int swrm_check_slave_change_status(struct swr_mstr_ctrl *swrm, static irqreturn_t swr_mstr_interrupt(int irq, void *dev) { struct swr_mstr_ctrl *swrm = dev; - u32 value, intr_sts, intr_mask; + u32 value, intr_sts, intr_sts_masked; u32 temp = 0; u32 status, chg_sts, i; u8 devnum = 0; @@ -1275,11 +1276,10 @@ static irqreturn_t swr_mstr_interrupt(int irq, void *dev) mutex_unlock(&swrm->reslock); intr_sts = swr_master_read(swrm, SWRM_INTERRUPT_STATUS); - intr_mask = swr_master_read(swrm, SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN); - intr_sts &= intr_mask; + intr_sts_masked = intr_sts & swrm->intr_mask; handle_irq: for (i = 0; i < SWRM_INTERRUPT_MAX; i++) { - value = intr_sts & (1 << i); + value = intr_sts_masked & (1 << i); if (!value) continue; @@ -1371,16 +1371,16 @@ handle_irq: break; case SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION: dev_err_ratelimited(swrm->dev, "SWR Port collision detected\n"); - intr_mask &= ~SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION; + swrm->intr_mask &= ~SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION; swr_master_write(swrm, - SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN, intr_mask); + SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN, swrm->intr_mask); break; case SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH: dev_dbg(swrm->dev, "SWR read enable valid mismatch\n"); - intr_mask &= + swrm->intr_mask &= ~SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH; swr_master_write(swrm, - SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN, intr_mask); + SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN, swrm->intr_mask); break; case SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED: complete(&swrm->broadcast); @@ -1408,9 +1408,9 @@ handle_irq: swr_master_write(swrm, SWRM_INTERRUPT_CLEAR, 0x0); intr_sts = swr_master_read(swrm, SWRM_INTERRUPT_STATUS); - intr_sts &= intr_mask; + intr_sts_masked = intr_sts & swrm->intr_mask; - if (intr_sts) { + if (intr_sts_masked) { dev_dbg(swrm->dev, "%s: new interrupt received\n", __func__); goto handle_irq; } @@ -1630,12 +1630,13 @@ static int swrm_master_init(struct swr_mstr_ctrl *swrm) reg[len] = SWRM_INTERRUPT_CLEAR; value[len++] = 0xFFFFFFFF; + swrm->intr_mask = SWRM_INTERRUPT_STATUS_MASK; /* Mask soundwire interrupts */ reg[len] = SWRM_INTERRUPT_MASK_ADDR; - value[len++] = 0x1FFFD; + value[len++] = swrm->intr_mask; reg[len] = SWR_MSTR_RX_SWRM_CPU_INTERRUPT_EN; - value[len++] = SWRM_INTERRUPT_STATUS_MASK; + value[len++] = swrm->intr_mask; swr_master_bulk_write(swrm, reg, value, len); diff --git a/soc/swr-mstr-ctrl.h b/soc/swr-mstr-ctrl.h index a32fdc69b1b4..8582c51ea96c 100644 --- a/soc/swr-mstr-ctrl.h +++ b/soc/swr-mstr-ctrl.h @@ -162,6 +162,7 @@ struct swr_mstr_ctrl { enum swrm_pm_state pm_state; wait_queue_head_t pm_wq; int wlock_holders; + u32 intr_mask; }; #endif /* _SWR_WCD_CTRL_H */