From f6004b7bc651c11591d544ce3fd60d7bf7d71ccd Mon Sep 17 00:00:00 2001 From: Muhammad Falak R Wani Date: Sat, 21 May 2016 18:49:21 +0530 Subject: [PATCH 01/24] HSI: cmt_speech: use vma_pages(). Replace explicit computation of vma page count by a call to vma_pages() Signed-off-by: Muhammad Falak R Wani Signed-off-by: Sebastian Reichel --- drivers/hsi/clients/cmt_speech.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hsi/clients/cmt_speech.c b/drivers/hsi/clients/cmt_speech.c index 95638df73d1c..b16cfa4b2360 100644 --- a/drivers/hsi/clients/cmt_speech.c +++ b/drivers/hsi/clients/cmt_speech.c @@ -1275,7 +1275,7 @@ static int cs_char_mmap(struct file *file, struct vm_area_struct *vma) if (vma->vm_end < vma->vm_start) return -EINVAL; - if (((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) != 1) + if (vma_pages(vma) != 1) return -EINVAL; vma->vm_flags |= VM_IO | VM_DONTDUMP | VM_DONTEXPAND; From b32bd7e7d5c1c04bb351420c09217f38dad8b8f1 Mon Sep 17 00:00:00 2001 From: Muhammad Falak R Wani Date: Thu, 19 May 2016 16:39:28 +0530 Subject: [PATCH 02/24] hsi: use kmemdup Use kmemdup when some other buffer is immediately copied into allocated region. It replaces call to allocation followed by memcpy, by a single call to kmemdup. Signed-off-by: Muhammad Falak R Wani Signed-off-by: Sebastian Reichel --- drivers/hsi/hsi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c index df380d55c58f..d7ce07ad67f3 100644 --- a/drivers/hsi/hsi.c +++ b/drivers/hsi/hsi.c @@ -90,19 +90,19 @@ struct hsi_client *hsi_new_client(struct hsi_port *port, cl->tx_cfg = info->tx_cfg; if (cl->tx_cfg.channels) { size = cl->tx_cfg.num_channels * sizeof(*cl->tx_cfg.channels); - cl->tx_cfg.channels = kzalloc(size , GFP_KERNEL); + cl->tx_cfg.channels = kmemdup(info->tx_cfg.channels, size, + GFP_KERNEL); if (!cl->tx_cfg.channels) goto err_tx; - memcpy(cl->tx_cfg.channels, info->tx_cfg.channels, size); } cl->rx_cfg = info->rx_cfg; if (cl->rx_cfg.channels) { size = cl->rx_cfg.num_channels * sizeof(*cl->rx_cfg.channels); - cl->rx_cfg.channels = kzalloc(size , GFP_KERNEL); + cl->rx_cfg.channels = kmemdup(info->rx_cfg.channels, size, + GFP_KERNEL); if (!cl->rx_cfg.channels) goto err_rx; - memcpy(cl->rx_cfg.channels, info->rx_cfg.channels, size); } cl->device.bus = &hsi_bus_type; From 3c13ab1d96e1924ef73b1a20c1ccccc993b6fb58 Mon Sep 17 00:00:00 2001 From: Iago Abal Date: Fri, 3 Jun 2016 11:21:25 +0200 Subject: [PATCH 03/24] HSI: cmt_speech: Fix double spin_lock Release &hi->lock before calling `cs_hsi_control_read_error' to avoid deadlock. The bug was found using EBA (https://github.com/models-team/eba), which reported the following: Double lock first at drivers/hsi/clients/cmt_speech.c:443 second at drivers/hsi/clients/cmt_speech.c:447 In cs_hsi_read_on_control_complete defined at drivers/hsi/clients/cmt_speech.c:438: (!) drivers/hsi/clients/cmt_speech.c:443: spin_lock(& hi->lock); (?) drivers/hsi/clients/cmt_speech.c:445: msg->status == 4 -> true (!) drivers/hsi/clients/cmt_speech.c:447: cs_hsi_control_read_error(hi, msg); (!) drivers/hsi/clients/cmt_speech.c:407: __cs_hsi_error_pre(hi, msg, "control read", & hi->control_state); (!) drivers/hsi/clients/cmt_speech.c:382: spin_lock(& hi->lock); Signed-off-by: Iago Abal Signed-off-by: Sebastian Reichel --- drivers/hsi/clients/cmt_speech.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hsi/clients/cmt_speech.c b/drivers/hsi/clients/cmt_speech.c index b16cfa4b2360..3deef6cc7d7c 100644 --- a/drivers/hsi/clients/cmt_speech.c +++ b/drivers/hsi/clients/cmt_speech.c @@ -444,8 +444,8 @@ static void cs_hsi_read_on_control_complete(struct hsi_msg *msg) hi->control_state &= ~SSI_CHANNEL_STATE_READING; if (msg->status == HSI_STATUS_ERROR) { dev_err(&hi->cl->device, "Control RX error detected\n"); - cs_hsi_control_read_error(hi, msg); spin_unlock(&hi->lock); + cs_hsi_control_read_error(hi, msg); goto out; } dev_dbg(&hi->cl->device, "Read on control: %08X\n", cmd); From e01957abd4cb3816a1eaaf191b16de182ebb333e Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Tue, 14 Jun 2016 11:13:04 -0500 Subject: [PATCH 04/24] hsi: Build hsi_boardinfo.c into hsi core if enabled If the HSI core is built as a module hsi_boardinfo may still be built-in as its Kconfig type is bool, which can cause build issues. Fix this by building this code into the HSI core when enabled. Reported-by: kbuild test robot Signed-off-by: Andrew F. Davis Signed-off-by: Sebastian Reichel --- Documentation/DocBook/device-drivers.tmpl | 2 +- drivers/hsi/Makefile | 3 ++- drivers/hsi/{hsi.c => hsi_core.c} | 0 3 files changed, 3 insertions(+), 2 deletions(-) rename drivers/hsi/{hsi.c => hsi_core.c} (100%) diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index de79efdad46c..c245e4eea560 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -511,7 +511,7 @@ X!Ilib/fonts/fonts.c !Iinclude/linux/hsi/hsi.h -!Edrivers/hsi/hsi.c +!Edrivers/hsi/hsi_core.c diff --git a/drivers/hsi/Makefile b/drivers/hsi/Makefile index 360371e134f1..96944783d584 100644 --- a/drivers/hsi/Makefile +++ b/drivers/hsi/Makefile @@ -1,7 +1,8 @@ # # Makefile for HSI # -obj-$(CONFIG_HSI_BOARDINFO) += hsi_boardinfo.o obj-$(CONFIG_HSI) += hsi.o +hsi-objs := hsi_core.o +hsi-$(CONFIG_HSI_BOARDINFO) += hsi_boardinfo.o obj-y += controllers/ obj-y += clients/ diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi_core.c similarity index 100% rename from drivers/hsi/hsi.c rename to drivers/hsi/hsi_core.c From ea12c45f1b368e2a0ad080e863291171ee2f98b1 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Mon, 13 Jun 2016 15:02:05 -0500 Subject: [PATCH 05/24] hsi: Only descend into hsi directory when CONFIG_HSI is set When CONFIG_HSI is not set make will still descend into the hsi directory but nothing will be built. This produces unneeded build artifacts and messages in addition to slowing the build. Fix this here. Signed-off-by: Andrew F. Davis Signed-off-by: Sebastian Reichel --- drivers/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/Makefile b/drivers/Makefile index 0b6f3d60193d..e8caa16f126b 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -78,7 +78,7 @@ obj-$(CONFIG_TARGET_CORE) += target/ obj-$(CONFIG_MTD) += mtd/ obj-$(CONFIG_SPI) += spi/ obj-$(CONFIG_SPMI) += spmi/ -obj-y += hsi/ +obj-$(CONFIG_HSI) += hsi/ obj-y += net/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ From 8c009f1f5def91a4764410c65f8b5739724b9e19 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 16:05:59 +0200 Subject: [PATCH 06/24] HSI: omap_ssi_port: remove useless newline Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi_port.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index 6b8f7739768a..38388442e310 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -1018,8 +1018,7 @@ static irqreturn_t ssi_wake_isr(int irq __maybe_unused, void *ssi_port) return IRQ_HANDLED; } -static int ssi_port_irq(struct hsi_port *port, - struct platform_device *pd) +static int ssi_port_irq(struct hsi_port *port, struct platform_device *pd) { struct omap_ssi_port *omap_port = hsi_port_drvdata(port); int err; @@ -1040,8 +1039,7 @@ static int ssi_port_irq(struct hsi_port *port, return err; } -static int ssi_wake_irq(struct hsi_port *port, - struct platform_device *pd) +static int ssi_wake_irq(struct hsi_port *port, struct platform_device *pd) { struct omap_ssi_port *omap_port = hsi_port_drvdata(port); int cawake_irq; From b6616be32412a4c9a0f04aec247d2760fcad8cfd Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 16:23:12 +0200 Subject: [PATCH 07/24] HSI: omap_ssi: do not reset module module reset and power management rule setup is already done by hwmod. Remove this cruft, which predates hwmod. Signed-off-by: Sebastian Reichel Acked-by: Tony Lindgren Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi.h | 2 -- drivers/hsi/controllers/omap_ssi_core.c | 19 +------------------ 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h index 7b4dec2c69ff..ba2f92722c09 100644 --- a/drivers/hsi/controllers/omap_ssi.h +++ b/drivers/hsi/controllers/omap_ssi.h @@ -138,7 +138,6 @@ struct gdd_trn { * @fck_rate: clock rate * @loss_count: To follow if we need to restore context or not * @max_speed: Maximum TX speed (Kb/s) set by the clients. - * @sysconfig: SSI controller saved context * @gdd_gcr: SSI GDD saved context * @get_loss: Pointer to omap_pm_get_dev_context_loss_count, if any * @port: Array of pointers of the ports of the controller @@ -158,7 +157,6 @@ struct omap_ssi_controller { u32 loss_count; u32 max_speed; /* OMAP SSI Controller context */ - u32 sysconfig; u32 gdd_gcr; int (*get_loss)(struct device *dev); struct omap_ssi_port **port; diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c index a3e0febfb64a..54943e439488 100644 --- a/drivers/hsi/controllers/omap_ssi_core.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -452,8 +452,6 @@ out_err: static int ssi_hw_init(struct hsi_controller *ssi) { struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); - unsigned int i; - u32 val; int err; err = pm_runtime_get_sync(ssi->device.parent); @@ -461,27 +459,12 @@ static int ssi_hw_init(struct hsi_controller *ssi) dev_err(&ssi->device, "runtime PM failed %d\n", err); return err; } - /* Reseting SSI controller */ - writel_relaxed(SSI_SOFTRESET, omap_ssi->sys + SSI_SYSCONFIG_REG); - val = readl(omap_ssi->sys + SSI_SYSSTATUS_REG); - for (i = 0; ((i < 20) && !(val & SSI_RESETDONE)); i++) { - msleep(20); - val = readl(omap_ssi->sys + SSI_SYSSTATUS_REG); - } - if (!(val & SSI_RESETDONE)) { - dev_err(&ssi->device, "SSI HW reset failed\n"); - pm_runtime_put_sync(ssi->device.parent); - return -EIO; - } /* Reseting GDD */ writel_relaxed(SSI_SWRESET, omap_ssi->gdd + SSI_GDD_GRST_REG); /* Get FCK rate in KHz */ omap_ssi->fck_rate = DIV_ROUND_CLOSEST(ssi_get_clk_rate(ssi), 1000); dev_dbg(&ssi->device, "SSI fck rate %lu KHz\n", omap_ssi->fck_rate); - /* Set default PM settings */ - val = SSI_AUTOIDLE | SSI_SIDLEMODE_SMART | SSI_MIDLEMODE_SMART; - writel_relaxed(val, omap_ssi->sys + SSI_SYSCONFIG_REG); - omap_ssi->sysconfig = val; + writel_relaxed(SSI_CLK_AUTOGATING_ON, omap_ssi->sys + SSI_GDD_GCR_REG); omap_ssi->gdd_gcr = SSI_CLK_AUTOGATING_ON; pm_runtime_put_sync(ssi->device.parent); From 6d6c30973b62f1979e39f5e768b3b31c6dc81c4e Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 18 Jun 2016 00:55:22 +0200 Subject: [PATCH 08/24] HSI: ssi_protocol: avoid ssi_waketest call with held spinlock This avoids calling ssi_waketest(), while a spinlock is being hold, since ssi_waketest may sleep once irq_safe runtime pm is disabled. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/clients/ssi_protocol.c | 41 ++++++++++++++++++------------ 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/drivers/hsi/clients/ssi_protocol.c b/drivers/hsi/clients/ssi_protocol.c index 6595d2091268..8534efda8140 100644 --- a/drivers/hsi/clients/ssi_protocol.c +++ b/drivers/hsi/clients/ssi_protocol.c @@ -88,6 +88,8 @@ void ssi_waketest(struct hsi_client *cl, unsigned int enable); #define SSIP_READY_CMD SSIP_CMD(SSIP_READY, 0) #define SSIP_SWBREAK_CMD SSIP_CMD(SSIP_SW_BREAK, 0) +#define SSIP_WAKETEST_FLAG 0 + /* Main state machine states */ enum { INIT, @@ -116,7 +118,7 @@ enum { * @main_state: Main state machine * @send_state: TX state machine * @recv_state: RX state machine - * @waketest: Flag to follow wake line test + * @flags: Flags, currently only used to follow wake line test * @rxid: RX data id * @txid: TX data id * @txqueue_len: TX queue length @@ -137,7 +139,7 @@ struct ssi_protocol { unsigned int main_state; unsigned int send_state; unsigned int recv_state; - unsigned int waketest:1; + unsigned long flags; u8 rxid; u8 txid; unsigned int txqueue_len; @@ -405,15 +407,17 @@ static void ssip_reset(struct hsi_client *cl) spin_lock_bh(&ssi->lock); if (ssi->send_state != SEND_IDLE) hsi_stop_tx(cl); - if (ssi->waketest) - ssi_waketest(cl, 0); + spin_unlock_bh(&ssi->lock); + if (test_and_clear_bit(SSIP_WAKETEST_FLAG, &ssi->flags)) + ssi_waketest(cl, 0); /* FIXME: To be removed */ + spin_lock_bh(&ssi->lock); del_timer(&ssi->rx_wd); del_timer(&ssi->tx_wd); del_timer(&ssi->keep_alive); ssi->main_state = 0; ssi->send_state = 0; ssi->recv_state = 0; - ssi->waketest = 0; + ssi->flags = 0; ssi->rxid = 0; ssi->txid = 0; list_for_each_safe(head, tmp, &ssi->txqueue) { @@ -437,7 +441,8 @@ static void ssip_dump_state(struct hsi_client *cl) dev_err(&cl->device, "Send state: %d\n", ssi->send_state); dev_err(&cl->device, "CMT %s\n", (ssi->main_state == ACTIVE) ? "Online" : "Offline"); - dev_err(&cl->device, "Wake test %d\n", ssi->waketest); + dev_err(&cl->device, "Wake test %d\n", + test_bit(SSIP_WAKETEST_FLAG, &ssi->flags)); dev_err(&cl->device, "Data RX id: %d\n", ssi->rxid); dev_err(&cl->device, "Data TX id: %d\n", ssi->txid); @@ -668,10 +673,12 @@ static void ssip_rx_bootinforeq(struct hsi_client *cl, u32 cmd) case HANDSHAKE: spin_lock(&ssi->lock); ssi->main_state = HANDSHAKE; - if (!ssi->waketest) { - ssi->waketest = 1; + spin_unlock(&ssi->lock); + + if (!test_and_set_bit(SSIP_WAKETEST_FLAG, &ssi->flags)) ssi_waketest(cl, 1); /* FIXME: To be removed */ - } + + spin_lock(&ssi->lock); /* Start boot handshake watchdog */ mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); spin_unlock(&ssi->lock); @@ -718,10 +725,12 @@ static void ssip_rx_waketest(struct hsi_client *cl, u32 cmd) spin_unlock(&ssi->lock); return; } - if (ssi->waketest) { - ssi->waketest = 0; + spin_unlock(&ssi->lock); + + if (test_and_clear_bit(SSIP_WAKETEST_FLAG, &ssi->flags)) ssi_waketest(cl, 0); /* FIXME: To be removed */ - } + + spin_lock(&ssi->lock); ssi->main_state = ACTIVE; del_timer(&ssi->tx_wd); /* Stop boot handshake timer */ spin_unlock(&ssi->lock); @@ -926,11 +935,11 @@ static int ssip_pn_open(struct net_device *dev) } dev_dbg(&cl->device, "Configuring SSI port\n"); hsi_setup(cl); - spin_lock_bh(&ssi->lock); - if (!ssi->waketest) { - ssi->waketest = 1; + + if (!test_and_set_bit(SSIP_WAKETEST_FLAG, &ssi->flags)) ssi_waketest(cl, 1); /* FIXME: To be removed */ - } + + spin_lock_bh(&ssi->lock); ssi->main_state = HANDSHAKE; spin_unlock_bh(&ssi->lock); From 866dcb9d57fcad11c2647a154b2b246ac10d0795 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 18 Jun 2016 00:57:20 +0200 Subject: [PATCH 09/24] HSI: ssi_protocol: replace spin_lock with spin_lock_bh To avoid setting irq_safe runtime pm flag in omap-ssi, multiple calls will be moved to process context. This also affects ssi-protocol, so use the safer spin_lock_bh instead of a simple spin_lock. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/clients/ssi_protocol.c | 64 +++++++++++++++--------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/hsi/clients/ssi_protocol.c b/drivers/hsi/clients/ssi_protocol.c index 8534efda8140..2388857db14b 100644 --- a/drivers/hsi/clients/ssi_protocol.c +++ b/drivers/hsi/clients/ssi_protocol.c @@ -520,17 +520,17 @@ static void ssip_start_rx(struct hsi_client *cl) dev_dbg(&cl->device, "RX start M(%d) R(%d)\n", ssi->main_state, ssi->recv_state); - spin_lock(&ssi->lock); + spin_lock_bh(&ssi->lock); /* * We can have two UP events in a row due to a short low * high transition. Therefore we need to ignore the sencond UP event. */ if ((ssi->main_state != ACTIVE) || (ssi->recv_state == RECV_READY)) { - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); return; } ssip_set_rxstate(ssi, RECV_READY); - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); msg = ssip_claim_cmd(ssi); ssip_set_cmd(msg, SSIP_READY_CMD); @@ -544,10 +544,10 @@ static void ssip_stop_rx(struct hsi_client *cl) struct ssi_protocol *ssi = hsi_client_drvdata(cl); dev_dbg(&cl->device, "RX stop M(%d)\n", ssi->main_state); - spin_lock(&ssi->lock); + spin_lock_bh(&ssi->lock); if (likely(ssi->main_state == ACTIVE)) ssip_set_rxstate(ssi, RECV_IDLE); - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); } static void ssip_free_strans(struct hsi_msg *msg) @@ -564,9 +564,9 @@ static void ssip_strans_complete(struct hsi_msg *msg) data = msg->context; ssip_release_cmd(msg); - spin_lock(&ssi->lock); + spin_lock_bh(&ssi->lock); ssip_set_txstate(ssi, SENDING); - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); hsi_async_write(cl, data); } @@ -671,17 +671,17 @@ static void ssip_rx_bootinforeq(struct hsi_client *cl, u32 cmd) /* Fall through */ case INIT: case HANDSHAKE: - spin_lock(&ssi->lock); + spin_lock_bh(&ssi->lock); ssi->main_state = HANDSHAKE; - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); if (!test_and_set_bit(SSIP_WAKETEST_FLAG, &ssi->flags)) ssi_waketest(cl, 1); /* FIXME: To be removed */ - spin_lock(&ssi->lock); + spin_lock_bh(&ssi->lock); /* Start boot handshake watchdog */ mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); dev_dbg(&cl->device, "Send BOOTINFO_RESP\n"); if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID) dev_warn(&cl->device, "boot info req verid mismatch\n"); @@ -703,14 +703,14 @@ static void ssip_rx_bootinforesp(struct hsi_client *cl, u32 cmd) if (SSIP_DATA_VERSION(cmd) != SSIP_LOCAL_VERID) dev_warn(&cl->device, "boot info resp verid mismatch\n"); - spin_lock(&ssi->lock); + spin_lock_bh(&ssi->lock); if (ssi->main_state != ACTIVE) /* Use tx_wd as a boot watchdog in non ACTIVE state */ mod_timer(&ssi->tx_wd, jiffies + msecs_to_jiffies(SSIP_WDTOUT)); else dev_dbg(&cl->device, "boot info resp ignored M(%d)\n", ssi->main_state); - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); } static void ssip_rx_waketest(struct hsi_client *cl, u32 cmd) @@ -718,22 +718,22 @@ static void ssip_rx_waketest(struct hsi_client *cl, u32 cmd) struct ssi_protocol *ssi = hsi_client_drvdata(cl); unsigned int wkres = SSIP_PAYLOAD(cmd); - spin_lock(&ssi->lock); + spin_lock_bh(&ssi->lock); if (ssi->main_state != HANDSHAKE) { dev_dbg(&cl->device, "wake lines test ignored M(%d)\n", ssi->main_state); - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); return; } - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); if (test_and_clear_bit(SSIP_WAKETEST_FLAG, &ssi->flags)) ssi_waketest(cl, 0); /* FIXME: To be removed */ - spin_lock(&ssi->lock); + spin_lock_bh(&ssi->lock); ssi->main_state = ACTIVE; del_timer(&ssi->tx_wd); /* Stop boot handshake timer */ - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); dev_notice(&cl->device, "WAKELINES TEST %s\n", wkres & SSIP_WAKETEST_FAILED ? "FAILED" : "OK"); @@ -750,20 +750,20 @@ static void ssip_rx_ready(struct hsi_client *cl) { struct ssi_protocol *ssi = hsi_client_drvdata(cl); - spin_lock(&ssi->lock); + spin_lock_bh(&ssi->lock); if (unlikely(ssi->main_state != ACTIVE)) { dev_dbg(&cl->device, "READY on wrong state: S(%d) M(%d)\n", ssi->send_state, ssi->main_state); - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); return; } if (ssi->send_state != WAIT4READY) { dev_dbg(&cl->device, "Ignore spurious READY command\n"); - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); return; } ssip_set_txstate(ssi, SEND_READY); - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); ssip_xmit(cl); } @@ -775,22 +775,22 @@ static void ssip_rx_strans(struct hsi_client *cl, u32 cmd) int len = SSIP_PDU_LENGTH(cmd); dev_dbg(&cl->device, "RX strans: %d frames\n", len); - spin_lock(&ssi->lock); + spin_lock_bh(&ssi->lock); if (unlikely(ssi->main_state != ACTIVE)) { dev_err(&cl->device, "START TRANS wrong state: S(%d) M(%d)\n", ssi->send_state, ssi->main_state); - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); return; } ssip_set_rxstate(ssi, RECEIVING); if (unlikely(SSIP_MSG_ID(cmd) != ssi->rxid)) { dev_err(&cl->device, "START TRANS id %d expected %d\n", SSIP_MSG_ID(cmd), ssi->rxid); - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); goto out1; } ssi->rxid++; - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); skb = netdev_alloc_skb(ssi->netdev, len * 4); if (unlikely(!skb)) { dev_err(&cl->device, "No memory for rx skb\n"); @@ -858,7 +858,7 @@ static void ssip_swbreak_complete(struct hsi_msg *msg) struct ssi_protocol *ssi = hsi_client_drvdata(cl); ssip_release_cmd(msg); - spin_lock(&ssi->lock); + spin_lock_bh(&ssi->lock); if (list_empty(&ssi->txqueue)) { if (atomic_read(&ssi->tx_usecnt)) { ssip_set_txstate(ssi, SEND_READY); @@ -866,9 +866,9 @@ static void ssip_swbreak_complete(struct hsi_msg *msg) ssip_set_txstate(ssi, SEND_IDLE); hsi_stop_tx(cl); } - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); } else { - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); ssip_xmit(cl); } netif_wake_queue(ssi->netdev); @@ -885,17 +885,17 @@ static void ssip_tx_data_complete(struct hsi_msg *msg) ssip_error(cl); goto out; } - spin_lock(&ssi->lock); + spin_lock_bh(&ssi->lock); if (list_empty(&ssi->txqueue)) { ssip_set_txstate(ssi, SENDING_SWBREAK); - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); cmsg = ssip_claim_cmd(ssi); ssip_set_cmd(cmsg, SSIP_SWBREAK_CMD); cmsg->complete = ssip_swbreak_complete; dev_dbg(&cl->device, "Send SWBREAK\n"); hsi_async_write(cl, cmsg); } else { - spin_unlock(&ssi->lock); + spin_unlock_bh(&ssi->lock); ssip_xmit(cl); } out: From df26d639e2f4628732a8da5a0f71e4e652ce809b Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 18 Jun 2016 00:58:39 +0200 Subject: [PATCH 10/24] HSI: ssi_protocol: fix ssip_xmit invocation ssip_xmit should be called from process context, since it calls hsi_async_write. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/clients/ssi_protocol.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/hsi/clients/ssi_protocol.c b/drivers/hsi/clients/ssi_protocol.c index 2388857db14b..6031cd146556 100644 --- a/drivers/hsi/clients/ssi_protocol.c +++ b/drivers/hsi/clients/ssi_protocol.c @@ -150,6 +150,7 @@ struct ssi_protocol { struct net_device *netdev; struct list_head txqueue; struct list_head cmdqueue; + struct work_struct work; struct hsi_client *cl; struct list_head link; atomic_t tx_usecnt; @@ -968,6 +969,15 @@ static int ssip_pn_set_mtu(struct net_device *dev, int new_mtu) return 0; } +static void ssip_xmit_work(struct work_struct *work) +{ + struct ssi_protocol *ssi = + container_of(work, struct ssi_protocol, work); + struct hsi_client *cl = ssi->cl; + + ssip_xmit(cl); +} + static int ssip_pn_xmit(struct sk_buff *skb, struct net_device *dev) { struct hsi_client *cl = to_hsi_client(dev->dev.parent); @@ -1020,7 +1030,7 @@ static int ssip_pn_xmit(struct sk_buff *skb, struct net_device *dev) dev_dbg(&cl->device, "Start TX on SEND READY qlen %d\n", ssi->txqueue_len); spin_unlock_bh(&ssi->lock); - ssip_xmit(cl); + schedule_work(&ssi->work); } else { spin_unlock_bh(&ssi->lock); } @@ -1097,6 +1107,7 @@ static int ssi_protocol_probe(struct device *dev) atomic_set(&ssi->tx_usecnt, 0); hsi_client_set_drvdata(cl, ssi); ssi->cl = cl; + INIT_WORK(&ssi->work, ssip_xmit_work); ssi->channel_id_cmd = hsi_get_channel_id_by_name(cl, "mcsaab-control"); if (ssi->channel_id_cmd < 0) { From cb70e4c1bc96604953f5792f71ba9ba104705154 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 16:34:43 +0200 Subject: [PATCH 11/24] HSI: omap_ssi: convert cawake irq handler to thread Convert cawake interrupt handler from tasklet to threaded interrupt handler in preparation of blocking runtime_pm calls. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi.h | 2 -- drivers/hsi/controllers/omap_ssi_core.c | 4 ++-- drivers/hsi/controllers/omap_ssi_port.c | 21 ++++++--------------- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h index ba2f92722c09..88a523902a8f 100644 --- a/drivers/hsi/controllers/omap_ssi.h +++ b/drivers/hsi/controllers/omap_ssi.h @@ -75,7 +75,6 @@ struct omap_ssm_ctx { * @wake_irq: IRQ number for incoming wake line (-1 if none) * @wake_gpio: GPIO number for incoming wake line (-1 if none) * @pio_tasklet: Bottom half for PIO transfers and events - * @wake_tasklet: Bottom half for incoming wake events * @wkin_cken: Keep track of clock references due to the incoming wake line * @wk_refcount: Reference count for output wake line * @sys_mpu_enable: Context for the interrupt enable register for irq 0 @@ -99,7 +98,6 @@ struct omap_ssi_port { int wake_irq; struct gpio_desc *wake_gpio; struct tasklet_struct pio_tasklet; - struct tasklet_struct wake_tasklet; bool wktest:1; /* FIXME: HACK to be removed */ bool wkin_cken:1; /* Workaround */ unsigned int wk_refcount; diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c index 54943e439488..a463420a0810 100644 --- a/drivers/hsi/controllers/omap_ssi_core.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -312,7 +312,7 @@ static int ssi_clk_event(struct notifier_block *nb, unsigned long event, continue; /* Workaround for SWBREAK + CAwake down race in CMT */ - tasklet_disable(&omap_port->wake_tasklet); + disable_irq(omap_port->wake_irq); /* stop all ssi communication */ pinctrl_pm_select_idle_state(omap_port->pdev); @@ -338,7 +338,7 @@ static int ssi_clk_event(struct notifier_block *nb, unsigned long event, /* resume ssi communication */ pinctrl_pm_select_default_state(omap_port->pdev); - tasklet_enable(&omap_port->wake_tasklet); + enable_irq(omap_port->wake_irq); } break; diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index 38388442e310..9001b06312c3 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -966,7 +966,7 @@ static irqreturn_t ssi_pio_isr(int irq, void *port) return IRQ_HANDLED; } -static void ssi_wake_tasklet(unsigned long ssi_port) +static irqreturn_t ssi_wake_thread(int irq __maybe_unused, void *ssi_port) { struct hsi_port *port = (struct hsi_port *)ssi_port; struct hsi_controller *ssi = to_hsi_controller(port->device.parent); @@ -1007,13 +1007,6 @@ static void ssi_wake_tasklet(unsigned long ssi_port) } spin_unlock(&omap_port->lock); } -} - -static irqreturn_t ssi_wake_isr(int irq __maybe_unused, void *ssi_port) -{ - struct omap_ssi_port *omap_port = hsi_port_drvdata(ssi_port); - - tasklet_hi_schedule(&omap_port->wake_tasklet); return IRQ_HANDLED; } @@ -1051,13 +1044,12 @@ static int ssi_wake_irq(struct hsi_port *port, struct platform_device *pd) } cawake_irq = gpiod_to_irq(omap_port->wake_gpio); - omap_port->wake_irq = cawake_irq; - tasklet_init(&omap_port->wake_tasklet, ssi_wake_tasklet, - (unsigned long)port); - err = devm_request_irq(&port->device, cawake_irq, ssi_wake_isr, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "cawake", port); + + err = devm_request_threaded_irq(&port->device, cawake_irq, NULL, + ssi_wake_thread, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "SSI cawake", port); if (err < 0) dev_err(&port->device, "Request Wake in IRQ %d failed %d\n", cawake_irq, err); @@ -1234,7 +1226,6 @@ static int ssi_port_remove(struct platform_device *pd) hsi_port_unregister_clients(port); - tasklet_kill(&omap_port->wake_tasklet); tasklet_kill(&omap_port->pio_tasklet); port->async = hsi_dummy_msg; From 2083057aacecbd79ed73670763f07e6e7c1acd66 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 17:11:54 +0200 Subject: [PATCH 12/24] HSI: omap_ssi_port: replace wkin_cken with atomic bitmap operations This simplifies the code and avoids holding a spin_lock when runtime pm calls are made. Once the irq_safe flag is removed for omap_ssi's runtime pm, pm_runtime_get/put_sync can sleep, which is a no-go while holding a spin_lock. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi.h | 6 ++++-- drivers/hsi/controllers/omap_ssi_port.c | 16 +++------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h index 88a523902a8f..6cdaad8805f7 100644 --- a/drivers/hsi/controllers/omap_ssi.h +++ b/drivers/hsi/controllers/omap_ssi.h @@ -35,6 +35,8 @@ #define SSI_MAX_GDD_LCH 8 #define SSI_BYTES_TO_FRAMES(x) ((((x) - 1) >> 2) + 1) +#define SSI_WAKE_EN 0 + /** * struct omap_ssm_ctx - OMAP synchronous serial module (TX/RX) context * @mode: Bit transmission mode @@ -75,7 +77,7 @@ struct omap_ssm_ctx { * @wake_irq: IRQ number for incoming wake line (-1 if none) * @wake_gpio: GPIO number for incoming wake line (-1 if none) * @pio_tasklet: Bottom half for PIO transfers and events - * @wkin_cken: Keep track of clock references due to the incoming wake line + * @flags: flags to keep track of states * @wk_refcount: Reference count for output wake line * @sys_mpu_enable: Context for the interrupt enable register for irq 0 * @sst: Context for the synchronous serial transmitter @@ -99,7 +101,7 @@ struct omap_ssi_port { struct gpio_desc *wake_gpio; struct tasklet_struct pio_tasklet; bool wktest:1; /* FIXME: HACK to be removed */ - bool wkin_cken:1; /* Workaround */ + unsigned long flags; unsigned int wk_refcount; /* OMAP SSI port context */ u32 sys_mpu_enable; /* We use only one irq */ diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index 9001b06312c3..0d3452393670 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -751,10 +751,8 @@ static int ssi_release(struct hsi_client *cl) * Drop the clock reference for the incoming wake line * if it is still kept high by the other side. */ - if (omap_port->wkin_cken) { + if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags)) pm_runtime_put_sync(omap_port->pdev); - omap_port->wkin_cken = 0; - } pm_runtime_get_sync(omap_port->pdev); /* Stop any SSI TX/RX without a client */ ssi_set_port_mode(omap_port, SSI_MODE_SLEEP); @@ -981,12 +979,8 @@ static irqreturn_t ssi_wake_thread(int irq __maybe_unused, void *ssi_port) * This workaround will avoid breaking the clock reference * count when such a situation ocurrs. */ - spin_lock(&omap_port->lock); - if (!omap_port->wkin_cken) { - omap_port->wkin_cken = 1; + if (!test_and_set_bit(SSI_WAKE_EN, &omap_port->flags)) pm_runtime_get_sync(omap_port->pdev); - } - spin_unlock(&omap_port->lock); dev_dbg(&ssi->device, "Wake in high\n"); if (omap_port->wktest) { /* FIXME: HACK ! To be removed */ writel(SSI_WAKE(0), @@ -1000,12 +994,8 @@ static irqreturn_t ssi_wake_thread(int irq __maybe_unused, void *ssi_port) omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); } hsi_event(port, HSI_EVENT_STOP_RX); - spin_lock(&omap_port->lock); - if (omap_port->wkin_cken) { + if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags)) pm_runtime_put_sync(omap_port->pdev); - omap_port->wkin_cken = 0; - } - spin_unlock(&omap_port->lock); } return IRQ_HANDLED; From de5a3774dde2c2f3b3a9a48b880fd820142706f0 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 30 Apr 2016 18:08:06 +0200 Subject: [PATCH 13/24] HSI: core: switch port event notifier from atomic to blocking port events should be sent from process context after irq_safe runtime pm flag is removed in omap-ssi. Signed-off-By: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/hsi_core.c | 8 ++++---- include/linux/hsi/hsi.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/hsi/hsi_core.c b/drivers/hsi/hsi_core.c index d7ce07ad67f3..c2a2a9795b0b 100644 --- a/drivers/hsi/hsi_core.c +++ b/drivers/hsi/hsi_core.c @@ -507,7 +507,7 @@ struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags) port[i]->stop_tx = hsi_dummy_cl; port[i]->release = hsi_dummy_cl; mutex_init(&port[i]->lock); - ATOMIC_INIT_NOTIFIER_HEAD(&port[i]->n_head); + BLOCKING_INIT_NOTIFIER_HEAD(&port[i]->n_head); dev_set_name(&port[i]->device, "port%d", i); hsi->port[i]->device.release = hsi_port_release; device_initialize(&hsi->port[i]->device); @@ -689,7 +689,7 @@ int hsi_register_port_event(struct hsi_client *cl, cl->ehandler = handler; cl->nb.notifier_call = hsi_event_notifier_call; - return atomic_notifier_chain_register(&port->n_head, &cl->nb); + return blocking_notifier_chain_register(&port->n_head, &cl->nb); } EXPORT_SYMBOL_GPL(hsi_register_port_event); @@ -709,7 +709,7 @@ int hsi_unregister_port_event(struct hsi_client *cl) WARN_ON(!hsi_port_claimed(cl)); - err = atomic_notifier_chain_unregister(&port->n_head, &cl->nb); + err = blocking_notifier_chain_unregister(&port->n_head, &cl->nb); if (!err) cl->ehandler = NULL; @@ -734,7 +734,7 @@ EXPORT_SYMBOL_GPL(hsi_unregister_port_event); */ int hsi_event(struct hsi_port *port, unsigned long event) { - return atomic_notifier_call_chain(&port->n_head, event, NULL); + return blocking_notifier_call_chain(&port->n_head, event, NULL); } EXPORT_SYMBOL_GPL(hsi_event); diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h index 2790591c77cf..57402544b53f 100644 --- a/include/linux/hsi/hsi.h +++ b/include/linux/hsi/hsi.h @@ -246,7 +246,7 @@ struct hsi_port { int (*stop_tx)(struct hsi_client *cl); int (*release)(struct hsi_client *cl); /* private */ - struct atomic_notifier_head n_head; + struct blocking_notifier_head n_head; }; #define to_hsi_port(dev) container_of(dev, struct hsi_port, device) From 7c5d81620ecd37702e86232de819eb1dd4c738e0 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 11 May 2016 20:33:45 +0200 Subject: [PATCH 14/24] HSI: omap_ssi_port: prepare start_tx/stop_tx for blocking pm_runtime calls ssi_start_tx and ssi_stop_tx may be called from atomic context. Once pm_runtime_irq_safe() is removed for omap-ssi, this will fail, due to blocking pm_runtime_*_sync() calls. This fixes ssi_stop_tx by using non-sync API and ssi_start_tx by using a worker thread. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi.h | 2 ++ drivers/hsi/controllers/omap_ssi_port.c | 23 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h index 6cdaad8805f7..5467f61e5086 100644 --- a/drivers/hsi/controllers/omap_ssi.h +++ b/drivers/hsi/controllers/omap_ssi.h @@ -79,6 +79,7 @@ struct omap_ssm_ctx { * @pio_tasklet: Bottom half for PIO transfers and events * @flags: flags to keep track of states * @wk_refcount: Reference count for output wake line + * @work: worker for starting TX * @sys_mpu_enable: Context for the interrupt enable register for irq 0 * @sst: Context for the synchronous serial transmitter * @ssr: Context for the synchronous serial receiver @@ -103,6 +104,7 @@ struct omap_ssi_port { bool wktest:1; /* FIXME: HACK to be removed */ unsigned long flags; unsigned int wk_refcount; + struct work_struct work; /* OMAP SSI port context */ u32 sys_mpu_enable; /* We use only one irq */ struct omap_ssm_ctx sst; diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index 0d3452393670..cc56d0ee82a2 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -567,12 +567,22 @@ static int ssi_flush(struct hsi_client *cl) return 0; } +static void start_tx_work(struct work_struct *work) +{ + struct omap_ssi_port *omap_port = + container_of(work, struct omap_ssi_port, work); + struct hsi_port *port = to_hsi_port(omap_port->dev); + struct hsi_controller *ssi = to_hsi_controller(port->device.parent); + struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); + + pm_runtime_get_sync(omap_port->pdev); /* Grab clocks */ + writel(SSI_WAKE(0), omap_ssi->sys + SSI_SET_WAKE_REG(port->num)); +} + static int ssi_start_tx(struct hsi_client *cl) { struct hsi_port *port = hsi_get_port(cl); struct omap_ssi_port *omap_port = hsi_port_drvdata(port); - struct hsi_controller *ssi = to_hsi_controller(port->device.parent); - struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); dev_dbg(&port->device, "Wake out high %d\n", omap_port->wk_refcount); @@ -581,10 +591,10 @@ static int ssi_start_tx(struct hsi_client *cl) spin_unlock_bh(&omap_port->wk_lock); return 0; } - pm_runtime_get_sync(omap_port->pdev); /* Grab clocks */ - writel(SSI_WAKE(0), omap_ssi->sys + SSI_SET_WAKE_REG(port->num)); spin_unlock_bh(&omap_port->wk_lock); + schedule_work(&omap_port->work); + return 0; } @@ -604,9 +614,10 @@ static int ssi_stop_tx(struct hsi_client *cl) return 0; } writel(SSI_WAKE(0), omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); - pm_runtime_put_sync(omap_port->pdev); /* Release clocks */ spin_unlock_bh(&omap_port->wk_lock); + pm_runtime_put(omap_port->pdev); /* Release clocks */ + return 0; } @@ -1149,6 +1160,8 @@ static int ssi_port_probe(struct platform_device *pd) omap_port->pdev = &pd->dev; omap_port->port_id = port_id; + INIT_WORK(&omap_port->work, start_tx_work); + /* initialize HSI port */ port->async = ssi_async; port->setup = ssi_setup; From ea88f717cdbe94e60bf2b0082648453cf2401ffc Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 17 Jun 2016 20:59:46 +0200 Subject: [PATCH 15/24] HSI: omap_ssi_core: use pm_runtime_put instead of pm_runtime_put_sync There is no need to disable the device synchronously, so don't do it. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi_core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c index a463420a0810..618db80577c3 100644 --- a/drivers/hsi/controllers/omap_ssi_core.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -58,7 +58,7 @@ static int ssi_debug_show(struct seq_file *m, void *p __maybe_unused) seq_printf(m, "REVISION\t: 0x%08x\n", readl(sys + SSI_REVISION_REG)); seq_printf(m, "SYSCONFIG\t: 0x%08x\n", readl(sys + SSI_SYSCONFIG_REG)); seq_printf(m, "SYSSTATUS\t: 0x%08x\n", readl(sys + SSI_SYSSTATUS_REG)); - pm_runtime_put_sync(ssi->device.parent); + pm_runtime_put(ssi->device.parent); return 0; } @@ -112,7 +112,7 @@ static int ssi_debug_gdd_show(struct seq_file *m, void *p __maybe_unused) readw(gdd + SSI_GDD_CLNK_CTRL_REG(lch))); } - pm_runtime_put_sync(ssi->device.parent); + pm_runtime_put(ssi->device.parent); return 0; } @@ -193,7 +193,7 @@ void ssi_waketest(struct hsi_client *cl, unsigned int enable) } else { writel_relaxed(SSI_WAKE(0), omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); - pm_runtime_put_sync(ssi->device.parent); + pm_runtime_put(ssi->device.parent); } } EXPORT_SYMBOL_GPL(ssi_waketest); @@ -217,7 +217,7 @@ static void ssi_gdd_complete(struct hsi_controller *ssi, unsigned int lch) if (msg->ttype == HSI_MSG_READ) { dir = DMA_FROM_DEVICE; val = SSI_DATAAVAILABLE(msg->channel); - pm_runtime_put_sync(ssi->device.parent); + pm_runtime_put(omap_port->pdev); } else { dir = DMA_TO_DEVICE; val = SSI_DATAACCEPT(msg->channel); @@ -265,7 +265,7 @@ static void ssi_gdd_tasklet(unsigned long dev) writel_relaxed(status_reg, sys + SSI_GDD_MPU_IRQ_STATUS_REG); status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG); - pm_runtime_put_sync(ssi->device.parent); + pm_runtime_put(ssi->device.parent); if (status_reg) tasklet_hi_schedule(&omap_ssi->gdd_tasklet); From 927d3f8f73217fb19c28496321510335176955de Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 17 Jun 2016 21:01:36 +0200 Subject: [PATCH 16/24] HSI: omap_ssi_core: remove pm_runtime_get_sync call from tasklet We may not call pm_runtime_get_sync() from tasklet, since it can block once pm_runtime_irq_safe is removed for omap-ssi. Since irq can should only be created for a running device, we assume, that the device is already running and use non- synchronous API instead. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi_core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c index 618db80577c3..79562ce65579 100644 --- a/drivers/hsi/controllers/omap_ssi_core.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -255,7 +255,13 @@ static void ssi_gdd_tasklet(unsigned long dev) unsigned int lch; u32 status_reg; - pm_runtime_get_sync(ssi->device.parent); + pm_runtime_get(ssi->device.parent); + + if (!pm_runtime_active(ssi->device.parent)) { + dev_warn(ssi->device.parent, "ssi_gdd_tasklet called without runtime PM!\n"); + pm_runtime_put(ssi->device.parent); + return; + } status_reg = readl(sys + SSI_GDD_MPU_IRQ_STATUS_REG); for (lch = 0; lch < SSI_MAX_GDD_LCH; lch++) { From c4a625731560021890df40e8d86958c3d8ea98fa Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 17 Jun 2016 21:18:09 +0200 Subject: [PATCH 17/24] HSI: omap_ssi_port: switch to threaded pio irq Move pio interrupt handler from tasklet into thread to allow runtime_pm_get_sync calls without irq_safe being set. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi.h | 2 - drivers/hsi/controllers/omap_ssi_port.c | 60 ++++++++++--------------- 2 files changed, 24 insertions(+), 38 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h index 5467f61e5086..99143f4f8837 100644 --- a/drivers/hsi/controllers/omap_ssi.h +++ b/drivers/hsi/controllers/omap_ssi.h @@ -76,7 +76,6 @@ struct omap_ssm_ctx { * @irq: IRQ number * @wake_irq: IRQ number for incoming wake line (-1 if none) * @wake_gpio: GPIO number for incoming wake line (-1 if none) - * @pio_tasklet: Bottom half for PIO transfers and events * @flags: flags to keep track of states * @wk_refcount: Reference count for output wake line * @work: worker for starting TX @@ -100,7 +99,6 @@ struct omap_ssi_port { unsigned int irq; int wake_irq; struct gpio_desc *wake_gpio; - struct tasklet_struct pio_tasklet; bool wktest:1; /* FIXME: HACK to be removed */ unsigned long flags; unsigned int wk_refcount; diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index cc56d0ee82a2..f62f0c482cea 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -877,7 +877,7 @@ static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue) u32 reg; u32 val; - spin_lock(&omap_port->lock); + spin_lock_bh(&omap_port->lock); msg = list_first_entry(queue, struct hsi_msg, link); if ((!msg->sgt.nents) || (!msg->sgt.sgl->length)) { msg->actual_len = 0; @@ -909,7 +909,7 @@ static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue) (msg->ttype == HSI_MSG_WRITE))) { writel(val, omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0)); - spin_unlock(&omap_port->lock); + spin_unlock_bh(&omap_port->lock); return; } @@ -925,12 +925,12 @@ static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue) writel_relaxed(reg, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); writel_relaxed(val, omap_ssi->sys + SSI_MPU_STATUS_REG(port->num, 0)); list_del(&msg->link); - spin_unlock(&omap_port->lock); + spin_unlock_bh(&omap_port->lock); msg->complete(msg); ssi_transfer(omap_port, queue); } -static void ssi_pio_tasklet(unsigned long ssi_port) +static irqreturn_t ssi_pio_thread(int irq, void *ssi_port) { struct hsi_port *port = (struct hsi_port *)ssi_port; struct hsi_controller *ssi = to_hsi_controller(port->device.parent); @@ -941,37 +941,29 @@ static void ssi_pio_tasklet(unsigned long ssi_port) u32 status_reg; pm_runtime_get_sync(omap_port->pdev); - status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0)); - status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0)); - for (ch = 0; ch < omap_port->channels; ch++) { - if (status_reg & SSI_DATAACCEPT(ch)) - ssi_pio_complete(port, &omap_port->txqueue[ch]); - if (status_reg & SSI_DATAAVAILABLE(ch)) - ssi_pio_complete(port, &omap_port->rxqueue[ch]); - } - if (status_reg & SSI_BREAKDETECTED) - ssi_break_complete(port); - if (status_reg & SSI_ERROROCCURED) - ssi_error(port); + do { + status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0)); + status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0)); - status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0)); - status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0)); - pm_runtime_put_sync(omap_port->pdev); + for (ch = 0; ch < omap_port->channels; ch++) { + if (status_reg & SSI_DATAACCEPT(ch)) + ssi_pio_complete(port, &omap_port->txqueue[ch]); + if (status_reg & SSI_DATAAVAILABLE(ch)) + ssi_pio_complete(port, &omap_port->rxqueue[ch]); + } + if (status_reg & SSI_BREAKDETECTED) + ssi_break_complete(port); + if (status_reg & SSI_ERROROCCURED) + ssi_error(port); - if (status_reg) - tasklet_hi_schedule(&omap_port->pio_tasklet); - else - enable_irq(omap_port->irq); -} + status_reg = readl(sys + SSI_MPU_STATUS_REG(port->num, 0)); + status_reg &= readl(sys + SSI_MPU_ENABLE_REG(port->num, 0)); -static irqreturn_t ssi_pio_isr(int irq, void *port) -{ - struct omap_ssi_port *omap_port = hsi_port_drvdata(port); - - tasklet_hi_schedule(&omap_port->pio_tasklet); - disable_irq_nosync(irq); + /* TODO: sleep if we retry? */ + } while (status_reg); + pm_runtime_put(omap_port->pdev); return IRQ_HANDLED; } @@ -1023,10 +1015,8 @@ static int ssi_port_irq(struct hsi_port *port, struct platform_device *pd) return err; } omap_port->irq = err; - tasklet_init(&omap_port->pio_tasklet, ssi_pio_tasklet, - (unsigned long)port); - err = devm_request_irq(&port->device, omap_port->irq, ssi_pio_isr, - 0, "mpu_irq0", port); + err = devm_request_threaded_irq(&port->device, omap_port->irq, NULL, + ssi_pio_thread, IRQF_ONESHOT, "SSI PORT", port); if (err < 0) dev_err(&port->device, "Request IRQ %d failed (%d)\n", omap_port->irq, err); @@ -1229,8 +1219,6 @@ static int ssi_port_remove(struct platform_device *pd) hsi_port_unregister_clients(port); - tasklet_kill(&omap_port->pio_tasklet); - port->async = hsi_dummy_msg; port->setup = hsi_dummy_cl; port->flush = hsi_dummy_cl; From 62aa292b3eae920a80c2fd0ed0601a70328627f6 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 17 Jun 2016 21:59:06 +0200 Subject: [PATCH 18/24] HSI: omap_ssi_port: avoid pm_runtime_get_sync in ssi_start_dma and ssi_start_pio These functions may be called from atomic context, so avoid synchronous runtime pm calls. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi_port.c | 30 ++++++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index f62f0c482cea..f7ed59ba3b2c 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -225,11 +225,21 @@ static int ssi_start_dma(struct hsi_msg *msg, int lch) u32 d_addr; u32 tmp; + /* Hold clocks during the transfer */ + pm_runtime_get(omap_port->pdev); + + if (!pm_runtime_active(omap_port->pdev)) { + dev_warn(&port->device, "ssi_start_dma called without runtime PM!\n"); + pm_runtime_put(omap_port->pdev); + return -EREMOTEIO; + } + if (msg->ttype == HSI_MSG_READ) { err = dma_map_sg(&ssi->device, msg->sgt.sgl, msg->sgt.nents, DMA_FROM_DEVICE); if (err < 0) { dev_dbg(&ssi->device, "DMA map SG failed !\n"); + pm_runtime_put(omap_port->pdev); return err; } csdp = SSI_DST_BURST_4x32_BIT | SSI_DST_MEMORY_PORT | @@ -246,6 +256,7 @@ static int ssi_start_dma(struct hsi_msg *msg, int lch) DMA_TO_DEVICE); if (err < 0) { dev_dbg(&ssi->device, "DMA map SG failed !\n"); + pm_runtime_put(omap_port->pdev); return err; } csdp = SSI_SRC_BURST_4x32_BIT | SSI_SRC_MEMORY_PORT | @@ -261,9 +272,6 @@ static int ssi_start_dma(struct hsi_msg *msg, int lch) dev_dbg(&ssi->device, "lch %d cdsp %08x ccr %04x s_addr %08x d_addr %08x\n", lch, csdp, ccr, s_addr, d_addr); - /* Hold clocks during the transfer */ - pm_runtime_get_sync(omap_port->pdev); - writew_relaxed(csdp, gdd + SSI_GDD_CSDP_REG(lch)); writew_relaxed(SSI_BLOCK_IE | SSI_TOUT_IE, gdd + SSI_GDD_CICR_REG(lch)); writel_relaxed(d_addr, gdd + SSI_GDD_CDSA_REG(lch)); @@ -290,11 +298,18 @@ static int ssi_start_pio(struct hsi_msg *msg) struct omap_ssi_controller *omap_ssi = hsi_controller_drvdata(ssi); u32 val; - pm_runtime_get_sync(omap_port->pdev); + pm_runtime_get(omap_port->pdev); + + if (!pm_runtime_active(omap_port->pdev)) { + dev_warn(&port->device, "ssi_start_pio called without runtime PM!\n"); + pm_runtime_put(omap_port->pdev); + return -EREMOTEIO; + } + if (msg->ttype == HSI_MSG_WRITE) { val = SSI_DATAACCEPT(msg->channel); /* Hold clocks for pio writes */ - pm_runtime_get_sync(omap_port->pdev); + pm_runtime_get(omap_port->pdev); } else { val = SSI_DATAAVAILABLE(msg->channel) | SSI_ERROROCCURED; } @@ -302,7 +317,7 @@ static int ssi_start_pio(struct hsi_msg *msg) msg->ttype ? "write" : "read"); val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put(omap_port->pdev); msg->actual_len = 0; msg->status = HSI_STATUS_PROCEEDING; @@ -388,6 +403,8 @@ static int ssi_async(struct hsi_msg *msg) queue = &omap_port->rxqueue[msg->channel]; } msg->status = HSI_STATUS_QUEUED; + + pm_runtime_get_sync(omap_port->pdev); spin_lock_bh(&omap_port->lock); list_add_tail(&msg->link, queue); err = ssi_start_transfer(queue); @@ -396,6 +413,7 @@ static int ssi_async(struct hsi_msg *msg) msg->status = HSI_STATUS_ERROR; } spin_unlock_bh(&omap_port->lock); + pm_runtime_put(omap_port->pdev); dev_dbg(&port->device, "msg status %d ttype %d ch %d\n", msg->status, msg->ttype, msg->channel); From fa1572d956ee072e965da01d2c46f10d2b67d017 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 17 Jun 2016 22:03:00 +0200 Subject: [PATCH 19/24] HSI: omap_ssi_port: avoid calling runtime_pm_*_sync inside spinlock runtime_pm_*_sync can block when irq_safe flag is removed from omap-ssi driver, so it may not be called while a spinlock is held. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi_port.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index f7ed59ba3b2c..92064221dbab 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -767,13 +767,12 @@ static int ssi_release(struct hsi_client *cl) struct omap_ssi_port *omap_port = hsi_port_drvdata(port); struct hsi_controller *ssi = to_hsi_controller(port->device.parent); - spin_lock_bh(&omap_port->lock); pm_runtime_get_sync(omap_port->pdev); + spin_lock_bh(&omap_port->lock); /* Stop all the pending DMA requests for that client */ ssi_cleanup_gdd(ssi, cl); /* Now cleanup all the queues */ ssi_cleanup_queues(cl); - pm_runtime_put_sync(omap_port->pdev); /* If it is the last client of the port, do extra checks and cleanup */ if (port->claimed <= 1) { /* @@ -782,15 +781,16 @@ static int ssi_release(struct hsi_client *cl) */ if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags)) pm_runtime_put_sync(omap_port->pdev); - pm_runtime_get_sync(omap_port->pdev); + pm_runtime_get(omap_port->pdev); /* Stop any SSI TX/RX without a client */ ssi_set_port_mode(omap_port, SSI_MODE_SLEEP); omap_port->sst.mode = SSI_MODE_SLEEP; omap_port->ssr.mode = SSI_MODE_SLEEP; - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put(omap_port->pdev); WARN_ON(omap_port->wk_refcount != 0); } spin_unlock_bh(&omap_port->lock); + pm_runtime_put_sync(omap_port->pdev); return 0; } From d2b8d695c61c4d2864eee900bebc2ced4f425645 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 17 Jun 2016 22:05:32 +0200 Subject: [PATCH 20/24] HSI: omap_ssi_port: replace pm_runtime_put_sync with non-sync variant There is no need to wait for hardware to really reach idle states, so just release runtime PM asynchronously. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi_port.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index 92064221dbab..aef5a8666d48 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -375,7 +375,7 @@ static int ssi_async_break(struct hsi_msg *msg) spin_unlock_bh(&omap_port->lock); } out: - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put(omap_port->pdev); return err; } @@ -515,7 +515,7 @@ static int ssi_setup(struct hsi_client *cl) omap_port->ssr.mode = cl->rx_cfg.mode; out: spin_unlock_bh(&omap_port->lock); - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put(omap_port->pdev); return err; } @@ -546,7 +546,7 @@ static int ssi_flush(struct hsi_client *cl) continue; writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i)); if (msg->ttype == HSI_MSG_READ) - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put(omap_port->pdev); omap_ssi->gdd_trn[i].msg = NULL; } /* Flush all SST buffers */ @@ -570,7 +570,7 @@ static int ssi_flush(struct hsi_client *cl) for (i = 0; i < omap_port->channels; i++) { /* Release write clocks */ if (!list_empty(&omap_port->txqueue[i])) - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put(omap_port->pdev); ssi_flush_queue(&omap_port->txqueue[i], NULL); ssi_flush_queue(&omap_port->rxqueue[i], NULL); } @@ -580,7 +580,7 @@ static int ssi_flush(struct hsi_client *cl) pinctrl_pm_select_default_state(omap_port->pdev); spin_unlock_bh(&omap_port->lock); - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put(omap_port->pdev); return 0; } @@ -687,7 +687,7 @@ static void ssi_cleanup_queues(struct hsi_client *cl) txbufstate |= (1 << i); status |= SSI_DATAACCEPT(i); /* Release the clocks writes, also GDD ones */ - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put(omap_port->pdev); } ssi_flush_queue(&omap_port->txqueue[i], cl); } @@ -742,7 +742,7 @@ static void ssi_cleanup_gdd(struct hsi_controller *ssi, struct hsi_client *cl) * ssi_cleanup_queues */ if (msg->ttype == HSI_MSG_READ) - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put(omap_port->pdev); omap_ssi->gdd_trn[i].msg = NULL; } tmp = readl_relaxed(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); @@ -790,7 +790,7 @@ static int ssi_release(struct hsi_client *cl) WARN_ON(omap_port->wk_refcount != 0); } spin_unlock_bh(&omap_port->lock); - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put(omap_port->pdev); return 0; } @@ -937,7 +937,7 @@ static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue) reg = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); if (msg->ttype == HSI_MSG_WRITE) { /* Release clocks for write transfer */ - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put(omap_port->pdev); } reg &= ~val; writel_relaxed(reg, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); From 604fdfa45886f04ad6070c1b8266c7f4154bc497 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 17 Jun 2016 22:09:10 +0200 Subject: [PATCH 21/24] HSI: omap_ssi_port: ensure clocks are kept enabled during transfer ensure, that clocks remain enabled, when a transfer is started. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi_port.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index aef5a8666d48..f91c6a4bb1a5 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -645,6 +645,7 @@ static void ssi_transfer(struct omap_ssi_port *omap_port, struct hsi_msg *msg; int err = -1; + pm_runtime_get(omap_port->pdev); spin_lock_bh(&omap_port->lock); while (err < 0) { err = ssi_start_transfer(queue); @@ -659,6 +660,7 @@ static void ssi_transfer(struct omap_ssi_port *omap_port, } } spin_unlock_bh(&omap_port->lock); + pm_runtime_put(omap_port->pdev); } static void ssi_cleanup_queues(struct hsi_client *cl) From 4e552310cdf0c81210b5fc9173f7cf497eeb9feb Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 20 May 2016 06:25:06 +0200 Subject: [PATCH 22/24] HSI: omap_ssi: call msg->complete() from process context msg->complete() should always be called from process context once irq_safe runtime pm flag is no longer set for omap-ssi. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi.h | 4 ++++ drivers/hsi/controllers/omap_ssi_core.c | 4 +++- drivers/hsi/controllers/omap_ssi_port.c | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/hsi/controllers/omap_ssi.h b/drivers/hsi/controllers/omap_ssi.h index 99143f4f8837..32ced0c8f789 100644 --- a/drivers/hsi/controllers/omap_ssi.h +++ b/drivers/hsi/controllers/omap_ssi.h @@ -73,6 +73,8 @@ struct omap_ssm_ctx { * @txqueue: TX message queues * @rxqueue: RX message queues * @brkqueue: Queue of incoming HWBREAK requests (FRAME mode) + * @errqueue: Queue for failed messages + * @errqueue_work: Delayed Work for failed messages * @irq: IRQ number * @wake_irq: IRQ number for incoming wake line (-1 if none) * @wake_gpio: GPIO number for incoming wake line (-1 if none) @@ -96,6 +98,8 @@ struct omap_ssi_port { struct list_head txqueue[SSI_MAX_CHANNELS]; struct list_head rxqueue[SSI_MAX_CHANNELS]; struct list_head brkqueue; + struct list_head errqueue; + struct delayed_work errqueue_work; unsigned int irq; int wake_irq; struct gpio_desc *wake_gpio; diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c index 79562ce65579..506a9f1ef7ad 100644 --- a/drivers/hsi/controllers/omap_ssi_core.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -235,7 +235,9 @@ static void ssi_gdd_complete(struct hsi_controller *ssi, unsigned int lch) spin_lock(&omap_port->lock); list_del(&msg->link); /* Dequeue msg */ spin_unlock(&omap_port->lock); - msg->complete(msg); + + list_add_tail(&msg->link, &omap_port->errqueue); + schedule_delayed_work(&omap_port->errqueue_work, 0); return; } spin_lock(&omap_port->lock); diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index f91c6a4bb1a5..7717c769c4dd 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -193,6 +193,21 @@ static int ssi_debug_add_port(struct omap_ssi_port *omap_port, } #endif +static void ssi_process_errqueue(struct work_struct *work) +{ + struct omap_ssi_port *omap_port; + struct list_head *head, *tmp; + struct hsi_msg *msg; + + omap_port = container_of(work, struct omap_ssi_port, errqueue_work.work); + + list_for_each_safe(head, tmp, &omap_port->errqueue) { + msg = list_entry(head, struct hsi_msg, link); + msg->complete(msg); + list_del(head); + } +} + static int ssi_claim_lch(struct hsi_msg *msg) { @@ -1170,6 +1185,7 @@ static int ssi_port_probe(struct platform_device *pd) omap_port->pdev = &pd->dev; omap_port->port_id = port_id; + INIT_DEFERRABLE_WORK(&omap_port->errqueue_work, ssi_process_errqueue); INIT_WORK(&omap_port->work, start_tx_work); /* initialize HSI port */ @@ -1237,6 +1253,8 @@ static int ssi_port_remove(struct platform_device *pd) ssi_debug_remove_port(port); #endif + cancel_delayed_work_sync(&omap_port->errqueue_work); + hsi_port_unregister_clients(port); port->async = hsi_dummy_msg; From ad60db2f9fe3367e60a21fc0afe19999516f8b27 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 11 May 2016 17:22:00 +0200 Subject: [PATCH 23/24] HSI: omap_ssi_port: use rpm autosuspend API Instead of immediately sending the SSI module to sleep, wait some time in case of new incoming or outgoing traffic. Signed-off-by: Sebastian Reichel Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi_port.c | 68 ++++++++++++++++--------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index 7717c769c4dd..f95efabc27b0 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -126,7 +126,7 @@ static int ssi_debug_port_show(struct seq_file *m, void *p __maybe_unused) seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch, readl(base + SSI_SSR_BUFFER_CH_REG(ch))); } - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); return 0; } @@ -150,7 +150,7 @@ static int ssi_div_get(void *data, u64 *val) pm_runtime_get_sync(omap_port->pdev); *val = readl(omap_port->sst_base + SSI_SST_DIVISOR_REG); - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); return 0; } @@ -166,7 +166,7 @@ static int ssi_div_set(void *data, u64 val) pm_runtime_get_sync(omap_port->pdev); writel(val, omap_port->sst_base + SSI_SST_DIVISOR_REG); omap_port->sst.divisor = val; - pm_runtime_put_sync(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); return 0; } @@ -245,7 +245,7 @@ static int ssi_start_dma(struct hsi_msg *msg, int lch) if (!pm_runtime_active(omap_port->pdev)) { dev_warn(&port->device, "ssi_start_dma called without runtime PM!\n"); - pm_runtime_put(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); return -EREMOTEIO; } @@ -254,7 +254,7 @@ static int ssi_start_dma(struct hsi_msg *msg, int lch) DMA_FROM_DEVICE); if (err < 0) { dev_dbg(&ssi->device, "DMA map SG failed !\n"); - pm_runtime_put(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); return err; } csdp = SSI_DST_BURST_4x32_BIT | SSI_DST_MEMORY_PORT | @@ -271,7 +271,7 @@ static int ssi_start_dma(struct hsi_msg *msg, int lch) DMA_TO_DEVICE); if (err < 0) { dev_dbg(&ssi->device, "DMA map SG failed !\n"); - pm_runtime_put(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); return err; } csdp = SSI_SRC_BURST_4x32_BIT | SSI_SRC_MEMORY_PORT | @@ -317,7 +317,7 @@ static int ssi_start_pio(struct hsi_msg *msg) if (!pm_runtime_active(omap_port->pdev)) { dev_warn(&port->device, "ssi_start_pio called without runtime PM!\n"); - pm_runtime_put(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); return -EREMOTEIO; } @@ -332,7 +332,7 @@ static int ssi_start_pio(struct hsi_msg *msg) msg->ttype ? "write" : "read"); val |= readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); writel(val, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); - pm_runtime_put(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); msg->actual_len = 0; msg->status = HSI_STATUS_PROCEEDING; @@ -390,7 +390,8 @@ static int ssi_async_break(struct hsi_msg *msg) spin_unlock_bh(&omap_port->lock); } out: - pm_runtime_put(omap_port->pdev); + pm_runtime_mark_last_busy(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); return err; } @@ -428,7 +429,8 @@ static int ssi_async(struct hsi_msg *msg) msg->status = HSI_STATUS_ERROR; } spin_unlock_bh(&omap_port->lock); - pm_runtime_put(omap_port->pdev); + pm_runtime_mark_last_busy(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); dev_dbg(&port->device, "msg status %d ttype %d ch %d\n", msg->status, msg->ttype, msg->channel); @@ -530,7 +532,8 @@ static int ssi_setup(struct hsi_client *cl) omap_port->ssr.mode = cl->rx_cfg.mode; out: spin_unlock_bh(&omap_port->lock); - pm_runtime_put(omap_port->pdev); + pm_runtime_mark_last_busy(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); return err; } @@ -561,7 +564,7 @@ static int ssi_flush(struct hsi_client *cl) continue; writew_relaxed(0, omap_ssi->gdd + SSI_GDD_CCR_REG(i)); if (msg->ttype == HSI_MSG_READ) - pm_runtime_put(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); omap_ssi->gdd_trn[i].msg = NULL; } /* Flush all SST buffers */ @@ -585,7 +588,7 @@ static int ssi_flush(struct hsi_client *cl) for (i = 0; i < omap_port->channels; i++) { /* Release write clocks */ if (!list_empty(&omap_port->txqueue[i])) - pm_runtime_put(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); ssi_flush_queue(&omap_port->txqueue[i], NULL); ssi_flush_queue(&omap_port->rxqueue[i], NULL); } @@ -595,7 +598,8 @@ static int ssi_flush(struct hsi_client *cl) pinctrl_pm_select_default_state(omap_port->pdev); spin_unlock_bh(&omap_port->lock); - pm_runtime_put(omap_port->pdev); + pm_runtime_mark_last_busy(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); return 0; } @@ -649,7 +653,9 @@ static int ssi_stop_tx(struct hsi_client *cl) writel(SSI_WAKE(0), omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); spin_unlock_bh(&omap_port->wk_lock); - pm_runtime_put(omap_port->pdev); /* Release clocks */ + pm_runtime_mark_last_busy(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); /* Release clocks */ + return 0; } @@ -675,7 +681,8 @@ static void ssi_transfer(struct omap_ssi_port *omap_port, } } spin_unlock_bh(&omap_port->lock); - pm_runtime_put(omap_port->pdev); + pm_runtime_mark_last_busy(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); } static void ssi_cleanup_queues(struct hsi_client *cl) @@ -704,7 +711,8 @@ static void ssi_cleanup_queues(struct hsi_client *cl) txbufstate |= (1 << i); status |= SSI_DATAACCEPT(i); /* Release the clocks writes, also GDD ones */ - pm_runtime_put(omap_port->pdev); + pm_runtime_mark_last_busy(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); } ssi_flush_queue(&omap_port->txqueue[i], cl); } @@ -758,8 +766,10 @@ static void ssi_cleanup_gdd(struct hsi_controller *ssi, struct hsi_client *cl) * Clock references for write will be handled in * ssi_cleanup_queues */ - if (msg->ttype == HSI_MSG_READ) - pm_runtime_put(omap_port->pdev); + if (msg->ttype == HSI_MSG_READ) { + pm_runtime_mark_last_busy(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); + } omap_ssi->gdd_trn[i].msg = NULL; } tmp = readl_relaxed(omap_ssi->sys + SSI_GDD_MPU_IRQ_ENABLE_REG); @@ -807,7 +817,7 @@ static int ssi_release(struct hsi_client *cl) WARN_ON(omap_port->wk_refcount != 0); } spin_unlock_bh(&omap_port->lock); - pm_runtime_put(omap_port->pdev); + pm_runtime_put_sync(omap_port->pdev); return 0; } @@ -954,7 +964,8 @@ static void ssi_pio_complete(struct hsi_port *port, struct list_head *queue) reg = readl(omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); if (msg->ttype == HSI_MSG_WRITE) { /* Release clocks for write transfer */ - pm_runtime_put(omap_port->pdev); + pm_runtime_mark_last_busy(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); } reg &= ~val; writel_relaxed(reg, omap_ssi->sys + SSI_MPU_ENABLE_REG(port->num, 0)); @@ -998,7 +1009,9 @@ static irqreturn_t ssi_pio_thread(int irq, void *ssi_port) /* TODO: sleep if we retry? */ } while (status_reg); - pm_runtime_put(omap_port->pdev); + pm_runtime_mark_last_busy(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); + return IRQ_HANDLED; } @@ -1032,8 +1045,10 @@ static irqreturn_t ssi_wake_thread(int irq __maybe_unused, void *ssi_port) omap_ssi->sys + SSI_CLEAR_WAKE_REG(port->num)); } hsi_event(port, HSI_EVENT_STOP_RX); - if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags)) - pm_runtime_put_sync(omap_port->pdev); + if (test_and_clear_bit(SSI_WAKE_EN, &omap_port->flags)) { + pm_runtime_mark_last_busy(omap_port->pdev); + pm_runtime_put_autosuspend(omap_port->pdev); + } } return IRQ_HANDLED; @@ -1222,6 +1237,9 @@ static int ssi_port_probe(struct platform_device *pd) omap_port->dev = &port->device; pm_runtime_irq_safe(omap_port->pdev); + + pm_runtime_use_autosuspend(omap_port->pdev); + pm_runtime_set_autosuspend_delay(omap_port->pdev, 250); pm_runtime_enable(omap_port->pdev); #ifdef CONFIG_DEBUG_FS @@ -1266,6 +1284,8 @@ static int ssi_port_remove(struct platform_device *pd) omap_ssi->port[omap_port->port_id] = NULL; platform_set_drvdata(pd, NULL); + + pm_runtime_dont_use_autosuspend(&pd->dev); pm_runtime_disable(&pd->dev); return 0; From 9c99e5e51988798af2a687ef9b1716dd79388550 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 17 Jun 2016 22:38:36 +0200 Subject: [PATCH 24/24] HSI: omap_ssi: drop pm_runtime_irq_safe pm_runtime_irq_safe increases the parents runtime usage counter effectively keeping the OMAP SoC from idling. Signed-off-by: Sebastian Reichel Acked-by: Tony Lindgren Tested-by: Pavel Machek --- drivers/hsi/controllers/omap_ssi_core.c | 1 - drivers/hsi/controllers/omap_ssi_port.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/drivers/hsi/controllers/omap_ssi_core.c b/drivers/hsi/controllers/omap_ssi_core.c index 506a9f1ef7ad..9a29b34ed2c8 100644 --- a/drivers/hsi/controllers/omap_ssi_core.c +++ b/drivers/hsi/controllers/omap_ssi_core.c @@ -543,7 +543,6 @@ static int ssi_probe(struct platform_device *pd) if (err < 0) goto out1; - pm_runtime_irq_safe(&pd->dev); pm_runtime_enable(&pd->dev); err = ssi_hw_init(ssi); diff --git a/drivers/hsi/controllers/omap_ssi_port.c b/drivers/hsi/controllers/omap_ssi_port.c index f95efabc27b0..7765de2f1ef1 100644 --- a/drivers/hsi/controllers/omap_ssi_port.c +++ b/drivers/hsi/controllers/omap_ssi_port.c @@ -1236,8 +1236,6 @@ static int ssi_port_probe(struct platform_device *pd) spin_lock_init(&omap_port->wk_lock); omap_port->dev = &port->device; - pm_runtime_irq_safe(omap_port->pdev); - pm_runtime_use_autosuspend(omap_port->pdev); pm_runtime_set_autosuspend_delay(omap_port->pdev, 250); pm_runtime_enable(omap_port->pdev);