linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC 00/19] Runtime PM fixes for omap-ssi
@ 2016-06-18 16:26 Sebastian Reichel
  2016-06-18 16:26 ` [RFC 01/19] HSI: omap_ssi_port: remove useless newline Sebastian Reichel
                   ` (20 more replies)
  0 siblings, 21 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:26 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

Hi,

This series fixes runtime PM for omap-ssi, so that
the OMAP SoC can idle correctly. After applying the
series, the SSI module correctly goes into idle if
phonet0 interface is not configured or if it's
used by ofono (*). Apart from me Pavel Machek did
some testing of my devel branch [0] and reported,
that he was able to get the phone into full idle.

Note: I did my testing with kernel flashing over USB, so
idle is broken. But I found the bit in cm_idlest1_core,
that stands for SSI blocking status: 0x00800000 (documented
as "reserved" in the public TRM).

(*) "ifconfig phonet0 up" will result in blocking ssi module
    until modem setup has finished (data communication via
	phonet0 device), since it enables ssi wakelines.

[0] https://git.kernel.org/cgit/linux/kernel/git/sre/linux-hsi.git/log/?h=runtime-pm-fixes

-- Sebastian

Sebastian Reichel (19):
  HSI: omap_ssi_port: remove useless newline
  HSI: omap_ssi: do not reset module
  HSI: ssi_protocol: avoid ssi_waketest call with held spinlock
  HSI: ssi_protocol: replace spin_lock with spin_lock_bh
  HSI: ssi_protocol: fix ssip_xmit invocation
  HSI: omap_ssi: convert cawake irq handler to thread
  HSI: omap_ssi_port: replace wkin_cken with atomic bitmap operations
  HSI: core: switch port event notifier from atomic to blocking
  HSI: omap_ssi_port: prepare start_tx/stop_tx for blocking pm_runtime
    calls
  HSI: omap_ssi_core: use pm_runtime_put instead of pm_runtime_put_sync
  HSI: omap_ssi_core: remove pm_runtime_get_sync call from tasklet
  HSI: omap_ssi_port: switch to threaded pio irq
  HSI: omap_ssi_port: avoid pm_runtime_get_sync in ssi_start_dma and
    ssi_start_pio
  HSI: omap_ssi_port: avoid calling runtime_pm_*_sync inside spinlock
  HSI: omap_ssi_port: replace pm_runtime_put_sync with non-sync variant
  HSI: omap_ssi_port: ensure clocks are kept enabled during transfer
  HSI: omap_ssi: call msg->complete() from process context
  HSI: omap_ssi_port: use rpm autosuspend API
  HSI: omap_ssi: drop pm_runtime_irq_safe

 drivers/hsi/clients/ssi_protocol.c      | 110 +++++++++-------
 drivers/hsi/controllers/omap_ssi.h      |  18 +--
 drivers/hsi/controllers/omap_ssi_core.c |  46 +++----
 drivers/hsi/controllers/omap_ssi_port.c | 226 ++++++++++++++++++--------------
 drivers/hsi/hsi_core.c                  |   8 +-
 include/linux/hsi/hsi.h                 |   2 +-
 6 files changed, 229 insertions(+), 181 deletions(-)

-- 
2.8.1

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [RFC 01/19] HSI: omap_ssi_port: remove useless newline
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
@ 2016-06-18 16:26 ` Sebastian Reichel
  2016-06-18 16:26 ` [RFC 02/19] HSI: omap_ssi: do not reset module Sebastian Reichel
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:26 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
 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;
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 02/19] HSI: omap_ssi: do not reset module
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
  2016-06-18 16:26 ` [RFC 01/19] HSI: omap_ssi_port: remove useless newline Sebastian Reichel
@ 2016-06-18 16:26 ` Sebastian Reichel
  2016-06-21 11:38   ` Tony Lindgren
  2016-06-18 16:26 ` [RFC 03/19] HSI: ssi_protocol: avoid ssi_waketest call with held spinlock Sebastian Reichel
                   ` (18 subsequent siblings)
  20 siblings, 1 reply; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:26 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

module reset and power management rule setup
is already done by hwmod. Remove this cruft,
which predates hwmod.

Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
 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);
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 03/19] HSI: ssi_protocol: avoid ssi_waketest call with held spinlock
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
  2016-06-18 16:26 ` [RFC 01/19] HSI: omap_ssi_port: remove useless newline Sebastian Reichel
  2016-06-18 16:26 ` [RFC 02/19] HSI: omap_ssi: do not reset module Sebastian Reichel
@ 2016-06-18 16:26 ` Sebastian Reichel
  2016-06-18 16:26 ` [RFC 04/19] HSI: ssi_protocol: replace spin_lock with spin_lock_bh Sebastian Reichel
                   ` (17 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:26 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

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 <sre@kernel.org>
---
 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);
 
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 04/19] HSI: ssi_protocol: replace spin_lock with spin_lock_bh
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (2 preceding siblings ...)
  2016-06-18 16:26 ` [RFC 03/19] HSI: ssi_protocol: avoid ssi_waketest call with held spinlock Sebastian Reichel
@ 2016-06-18 16:26 ` Sebastian Reichel
  2016-06-18 16:26 ` [RFC 05/19] HSI: ssi_protocol: fix ssip_xmit invocation Sebastian Reichel
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:26 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

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 <sre@kernel.org>
---
 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:
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 05/19] HSI: ssi_protocol: fix ssip_xmit invocation
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (3 preceding siblings ...)
  2016-06-18 16:26 ` [RFC 04/19] HSI: ssi_protocol: replace spin_lock with spin_lock_bh Sebastian Reichel
@ 2016-06-18 16:26 ` Sebastian Reichel
  2016-06-18 16:26 ` [RFC 06/19] HSI: omap_ssi: convert cawake irq handler to thread Sebastian Reichel
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:26 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

ssip_xmit should be called from process context,
since it calls hsi_async_write.

Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
 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) {
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 06/19] HSI: omap_ssi: convert cawake irq handler to thread
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (4 preceding siblings ...)
  2016-06-18 16:26 ` [RFC 05/19] HSI: ssi_protocol: fix ssip_xmit invocation Sebastian Reichel
@ 2016-06-18 16:26 ` Sebastian Reichel
  2016-06-18 16:26 ` [RFC 07/19] HSI: omap_ssi_port: replace wkin_cken with atomic bitmap operations Sebastian Reichel
                   ` (14 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:26 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

Convert cawake interrupt handler from tasklet to
threaded interrupt handler in preparation of
blocking runtime_pm calls.

Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
 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;
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 07/19] HSI: omap_ssi_port: replace wkin_cken with atomic bitmap operations
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (5 preceding siblings ...)
  2016-06-18 16:26 ` [RFC 06/19] HSI: omap_ssi: convert cawake irq handler to thread Sebastian Reichel
@ 2016-06-18 16:26 ` Sebastian Reichel
  2016-06-18 16:26 ` [RFC 08/19] HSI: core: switch port event notifier from atomic to blocking Sebastian Reichel
                   ` (13 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:26 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

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 <sre@kernel.org>
---
 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;
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 08/19] HSI: core: switch port event notifier from atomic to blocking
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (6 preceding siblings ...)
  2016-06-18 16:26 ` [RFC 07/19] HSI: omap_ssi_port: replace wkin_cken with atomic bitmap operations Sebastian Reichel
@ 2016-06-18 16:26 ` Sebastian Reichel
  2016-06-18 16:26 ` [RFC 09/19] HSI: omap_ssi_port: prepare start_tx/stop_tx for blocking pm_runtime calls Sebastian Reichel
                   ` (12 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:26 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

port events should be sent from process context after
irq_safe runtime pm flag is removed in omap-ssi.
---
 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)
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 09/19] HSI: omap_ssi_port: prepare start_tx/stop_tx for blocking pm_runtime calls
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (7 preceding siblings ...)
  2016-06-18 16:26 ` [RFC 08/19] HSI: core: switch port event notifier from atomic to blocking Sebastian Reichel
@ 2016-06-18 16:26 ` Sebastian Reichel
  2016-06-18 16:27 ` [RFC 10/19] HSI: omap_ssi_core: use pm_runtime_put instead of pm_runtime_put_sync Sebastian Reichel
                   ` (11 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:26 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

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 <sre@kernel.org>
---
 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;
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 10/19] HSI: omap_ssi_core: use pm_runtime_put instead of pm_runtime_put_sync
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (8 preceding siblings ...)
  2016-06-18 16:26 ` [RFC 09/19] HSI: omap_ssi_port: prepare start_tx/stop_tx for blocking pm_runtime calls Sebastian Reichel
@ 2016-06-18 16:27 ` Sebastian Reichel
  2016-06-18 16:27 ` [RFC 11/19] HSI: omap_ssi_core: remove pm_runtime_get_sync call from tasklet Sebastian Reichel
                   ` (10 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:27 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

There is no need to disable the device synchronously, so
don't do it.

Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
 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);
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 11/19] HSI: omap_ssi_core: remove pm_runtime_get_sync call from tasklet
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (9 preceding siblings ...)
  2016-06-18 16:27 ` [RFC 10/19] HSI: omap_ssi_core: use pm_runtime_put instead of pm_runtime_put_sync Sebastian Reichel
@ 2016-06-18 16:27 ` Sebastian Reichel
  2016-06-18 16:27 ` [RFC 12/19] HSI: omap_ssi_port: switch to threaded pio irq Sebastian Reichel
                   ` (9 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:27 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

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 <sre@kernel.org>
---
 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++) {
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 12/19] HSI: omap_ssi_port: switch to threaded pio irq
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (10 preceding siblings ...)
  2016-06-18 16:27 ` [RFC 11/19] HSI: omap_ssi_core: remove pm_runtime_get_sync call from tasklet Sebastian Reichel
@ 2016-06-18 16:27 ` Sebastian Reichel
  2016-06-18 16:27 ` [RFC 13/19] HSI: omap_ssi_port: avoid pm_runtime_get_sync in ssi_start_dma and ssi_start_pio Sebastian Reichel
                   ` (8 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:27 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

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 <sre@kernel.org>
---
 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;
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 13/19] HSI: omap_ssi_port: avoid pm_runtime_get_sync in ssi_start_dma and ssi_start_pio
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (11 preceding siblings ...)
  2016-06-18 16:27 ` [RFC 12/19] HSI: omap_ssi_port: switch to threaded pio irq Sebastian Reichel
@ 2016-06-18 16:27 ` Sebastian Reichel
  2016-06-18 16:27 ` [RFC 14/19] HSI: omap_ssi_port: avoid calling runtime_pm_*_sync inside spinlock Sebastian Reichel
                   ` (7 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:27 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

These functions may be called from atomic context,
so avoid synchronous runtime pm calls.

Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
 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);
 
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 14/19] HSI: omap_ssi_port: avoid calling runtime_pm_*_sync inside spinlock
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (12 preceding siblings ...)
  2016-06-18 16:27 ` [RFC 13/19] HSI: omap_ssi_port: avoid pm_runtime_get_sync in ssi_start_dma and ssi_start_pio Sebastian Reichel
@ 2016-06-18 16:27 ` Sebastian Reichel
  2016-06-18 16:27 ` [RFC 15/19] HSI: omap_ssi_port: replace pm_runtime_put_sync with non-sync variant Sebastian Reichel
                   ` (6 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:27 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

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 <sre@kernel.org>
---
 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;
 }
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 15/19] HSI: omap_ssi_port: replace pm_runtime_put_sync with non-sync variant
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (13 preceding siblings ...)
  2016-06-18 16:27 ` [RFC 14/19] HSI: omap_ssi_port: avoid calling runtime_pm_*_sync inside spinlock Sebastian Reichel
@ 2016-06-18 16:27 ` Sebastian Reichel
  2016-06-18 16:27 ` [RFC 16/19] HSI: omap_ssi_port: ensure clocks are kept enabled during transfer Sebastian Reichel
                   ` (5 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:27 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

There is no need to wait for hardware to really reach idle states,
so just release runtime PM asynchronously.

Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
 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));
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 16/19] HSI: omap_ssi_port: ensure clocks are kept enabled during transfer
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (14 preceding siblings ...)
  2016-06-18 16:27 ` [RFC 15/19] HSI: omap_ssi_port: replace pm_runtime_put_sync with non-sync variant Sebastian Reichel
@ 2016-06-18 16:27 ` Sebastian Reichel
  2016-06-18 16:27 ` [RFC 17/19] HSI: omap_ssi: call msg->complete() from process context Sebastian Reichel
                   ` (4 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:27 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

ensure, that clocks remain enabled, when a transfer is started.

Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
 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)
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 17/19] HSI: omap_ssi: call msg->complete() from process context
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (15 preceding siblings ...)
  2016-06-18 16:27 ` [RFC 16/19] HSI: omap_ssi_port: ensure clocks are kept enabled during transfer Sebastian Reichel
@ 2016-06-18 16:27 ` Sebastian Reichel
  2016-06-18 16:27 ` [RFC 18/19] HSI: omap_ssi_port: use rpm autosuspend API Sebastian Reichel
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:27 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

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 <sre@kernel.org>
---
 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;
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 18/19] HSI: omap_ssi_port: use rpm autosuspend API
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (16 preceding siblings ...)
  2016-06-18 16:27 ` [RFC 17/19] HSI: omap_ssi: call msg->complete() from process context Sebastian Reichel
@ 2016-06-18 16:27 ` Sebastian Reichel
  2016-06-18 16:27 ` [RFC 19/19] HSI: omap_ssi: drop pm_runtime_irq_safe Sebastian Reichel
                   ` (2 subsequent siblings)
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:27 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

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 <sre@kernel.org>
---
 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;
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [RFC 19/19] HSI: omap_ssi: drop pm_runtime_irq_safe
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (17 preceding siblings ...)
  2016-06-18 16:27 ` [RFC 18/19] HSI: omap_ssi_port: use rpm autosuspend API Sebastian Reichel
@ 2016-06-18 16:27 ` Sebastian Reichel
  2016-06-21 11:41   ` Tony Lindgren
  2016-06-18 18:54 ` [RFC 00/19] Runtime PM fixes for omap-ssi Pavel Machek
  2016-06-28  0:33 ` Sebastian Reichel
  20 siblings, 1 reply; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 16:27 UTC (permalink / raw)
  To: Sebastian Reichel, linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

pm_runtime_irq_safe increases the parents runtime usage
counter effectively keeping the OMAP SoC from idling.

Signed-off-by: Sebastian Reichel <sre@kernel.org>
---
 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);
-- 
2.8.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [RFC 00/19] Runtime PM fixes for omap-ssi
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (18 preceding siblings ...)
  2016-06-18 16:27 ` [RFC 19/19] HSI: omap_ssi: drop pm_runtime_irq_safe Sebastian Reichel
@ 2016-06-18 18:54 ` Pavel Machek
  2016-06-18 20:21   ` Sebastian Reichel
  2016-06-28  0:33 ` Sebastian Reichel
  20 siblings, 1 reply; 26+ messages in thread
From: Pavel Machek @ 2016-06-18 18:54 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: linux-omap, linux-kernel, Tony Lindgren, Aaro Koskinen,
	Ivaylo Dimitrov, Pali Rohár

Hi!

> This series fixes runtime PM for omap-ssi, so that
> the OMAP SoC can idle correctly. After applying the
> series, the SSI module correctly goes into idle if
> phonet0 interface is not configured or if it's
> used by ofono (*). Apart from me Pavel Machek did
> some testing of my devel branch [0] and reported,
> that he was able to get the phone into full idle.
> 
> Note: I did my testing with kernel flashing over USB, so
> idle is broken. But I found the bit in cm_idlest1_core,
> that stands for SSI blocking status: 0x00800000 (documented
> as "reserved" in the public TRM).
> 
> (*) "ifconfig phonet0 up" will result in blocking ssi module
>     until modem setup has finished (data communication via
> 	phonet0 device), since it enables ssi wakelines.
> 
> [0] https://git.kernel.org/cgit/linux/kernel/git/sre/linux-hsi.git/log/?h=runtime-pm-fixes

I tried applying it over v4.7-rc, but:

Applying: HSI: core: switch port event notifier from atomic to
blocking
error: drivers/hsi/hsi_core.c: does not exist in index
Patch failed at 0008 HSI: core: switch port event notifier from atomic
to blocking
The copy of the patch that failed is found in:

I tried against v4.6, but could not, either:

git checkout 2dcd0af568b0cf583645c8a317dd12e344b1c72a
Checking out files: 100% (9566/9566), done.
Note: checking out '2dcd0af568b0cf583645c8a317dd12e344b1c72a'.

You are in 'detached HEAD' state. You can look around, make
experimental
changes and commit them, and you can discard any commits you make in
this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you
may
do so (now or later) by using -b with the checkout command
again. Example:

  git checkout -b new_branch_name

HEAD is now at 2dcd0af... Linux 4.6
pavel@amd /data/l/linux-n900 (2dcd0af...) $ git am /tmp/delme.patch
Applying: HSI: omap_ssi_port: remove useless newline
error: patch failed: drivers/hsi/controllers/omap_ssi_port.c:1018
error: drivers/hsi/controllers/omap_ssi_port.c: patch does not apply
Patch failed at 0001 HSI: omap_ssi_port: remove useless newline
The copy of the patch that failed is found in:
   /data/l/linux-n900/.git/rebase-apply/patch
   When you have resolved this problem, run "git am --continue".
   If you prefer to skip this patch, run "git am --skip" instead.
   To restore the original branch and stop patching, run "git am
   --abort".
   pavel@amd /data/l/linux-n900 (2dcd0af...)|AM 1/19 $

What tree should I try it against?

Thanks,									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC 00/19] Runtime PM fixes for omap-ssi
  2016-06-18 18:54 ` [RFC 00/19] Runtime PM fixes for omap-ssi Pavel Machek
@ 2016-06-18 20:21   ` Sebastian Reichel
  2016-06-18 22:00     ` Pavel Machek
  0 siblings, 1 reply; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-18 20:21 UTC (permalink / raw)
  To: Pavel Machek
  Cc: linux-omap, linux-kernel, Tony Lindgren, Aaro Koskinen,
	Ivaylo Dimitrov, Pali Rohár

[-- Attachment #1: Type: text/plain, Size: 1482 bytes --]

Hi,

On Sat, Jun 18, 2016 at 08:54:38PM +0200, Pavel Machek wrote:
> > This series fixes runtime PM for omap-ssi, so that
> > the OMAP SoC can idle correctly. After applying the
> > series, the SSI module correctly goes into idle if
> > phonet0 interface is not configured or if it's
> > used by ofono (*). Apart from me Pavel Machek did
> > some testing of my devel branch [0] and reported,
> > that he was able to get the phone into full idle.
> > 
> > Note: I did my testing with kernel flashing over USB, so
> > idle is broken. But I found the bit in cm_idlest1_core,
> > that stands for SSI blocking status: 0x00800000 (documented
> > as "reserved" in the public TRM).
> > 
> > (*) "ifconfig phonet0 up" will result in blocking ssi module
> >     until modem setup has finished (data communication via
> > 	phonet0 device), since it enables ssi wakelines.
> > 
> > [0] https://git.kernel.org/cgit/linux/kernel/git/sre/linux-hsi.git/log/?h=runtime-pm-fixes
> 
> I tried applying it over v4.7-rc, but [...]
> What tree should I try it against?

The patchset is based on hsi's for-next branch available here:

https://git.kernel.org/cgit/linux/kernel/git/sre/linux-hsi.git/log/?h=for-next

You can try to apply it directly on 4.7 by changing "hsi_core.c" to
"hsi.c" or by applying this additional patch:

https://git.kernel.org/cgit/linux/kernel/git/sre/linux-hsi.git/commit/?h=for-next&id=e01957abd4cb3816a1eaaf191b16de182ebb333e

-- Sebastian

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC 00/19] Runtime PM fixes for omap-ssi
  2016-06-18 20:21   ` Sebastian Reichel
@ 2016-06-18 22:00     ` Pavel Machek
  0 siblings, 0 replies; 26+ messages in thread
From: Pavel Machek @ 2016-06-18 22:00 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: linux-omap, linux-kernel, Tony Lindgren, Aaro Koskinen,
	Ivaylo Dimitrov, Pali Rohár

Hi!

> > > (*) "ifconfig phonet0 up" will result in blocking ssi module
> > >     until modem setup has finished (data communication via
> > > 	phonet0 device), since it enables ssi wakelines.
> > > 
> > > [0] https://git.kernel.org/cgit/linux/kernel/git/sre/linux-hsi.git/log/?h=runtime-pm-fixes
> > 
> > I tried applying it over v4.7-rc, but [...]
> > What tree should I try it against?
> 
> The patchset is based on hsi's for-next branch available here:
> 
> > > https://git.kernel.org/cgit/linux/kernel/git/sre/linux-hsi.git/log/?h=for-next

Aha, ok, thanks. I have some fun with wlan, but machine now goes to
sleep.

Thanks!

Tested-by: Pavel Machek <pavel@ucw.cz>
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC 02/19] HSI: omap_ssi: do not reset module
  2016-06-18 16:26 ` [RFC 02/19] HSI: omap_ssi: do not reset module Sebastian Reichel
@ 2016-06-21 11:38   ` Tony Lindgren
  0 siblings, 0 replies; 26+ messages in thread
From: Tony Lindgren @ 2016-06-21 11:38 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: linux-omap, linux-kernel, Aaro Koskinen, Pavel Machek,
	Ivaylo Dimitrov, Pali Rohár

* Sebastian Reichel <sre@kernel.org> [160618 09:30]:
> module reset and power management rule setup
> is already done by hwmod. Remove this cruft,
> which predates hwmod.

Sounds good to me:

Acked-by: Tony Lindgren <tony@atomide.com>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC 19/19] HSI: omap_ssi: drop pm_runtime_irq_safe
  2016-06-18 16:27 ` [RFC 19/19] HSI: omap_ssi: drop pm_runtime_irq_safe Sebastian Reichel
@ 2016-06-21 11:41   ` Tony Lindgren
  0 siblings, 0 replies; 26+ messages in thread
From: Tony Lindgren @ 2016-06-21 11:41 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: linux-omap, linux-kernel, Aaro Koskinen, Pavel Machek,
	Ivaylo Dimitrov, Pali Rohár

* Sebastian Reichel <sre@kernel.org> [160618 09:31]:
> pm_runtime_irq_safe increases the parents runtime usage
> counter effectively keeping the OMAP SoC from idling.

Good to see this :) Yeah pm_runtime_irq_safe keeps the parent
permanently enabled so not using it is the way to go:

Acked-by: Tony Lindgren <tony@atomide.com>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RFC 00/19] Runtime PM fixes for omap-ssi
  2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
                   ` (19 preceding siblings ...)
  2016-06-18 18:54 ` [RFC 00/19] Runtime PM fixes for omap-ssi Pavel Machek
@ 2016-06-28  0:33 ` Sebastian Reichel
  20 siblings, 0 replies; 26+ messages in thread
From: Sebastian Reichel @ 2016-06-28  0:33 UTC (permalink / raw)
  To: linux-omap, linux-kernel
  Cc: Tony Lindgren, Aaro Koskinen, Pavel Machek, Ivaylo Dimitrov,
	Pali Rohár

[-- Attachment #1: Type: text/plain, Size: 1023 bytes --]

Hi,

On Sat, Jun 18, 2016 at 06:26:50PM +0200, Sebastian Reichel wrote:
> This series fixes runtime PM for omap-ssi, so that
> the OMAP SoC can idle correctly. After applying the
> series, the SSI module correctly goes into idle if
> phonet0 interface is not configured or if it's
> used by ofono (*). Apart from me Pavel Machek did
> some testing of my devel branch [0] and reported,
> that he was able to get the phone into full idle.
> 
> Note: I did my testing with kernel flashing over USB, so
> idle is broken. But I found the bit in cm_idlest1_core,
> that stands for SSI blocking status: 0x00800000 (documented
> as "reserved" in the public TRM).
> 
> (*) "ifconfig phonet0 up" will result in blocking ssi module
>     until modem setup has finished (data communication via
> 	phonet0 device), since it enables ssi wakelines.
> 
> [0] https://git.kernel.org/cgit/linux/kernel/git/sre/linux-hsi.git/log/?h=runtime-pm-fixes

I just added this patchset to hsi's for-next branch.

-- Sebastian

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2016-06-28  0:33 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-06-18 16:26 [RFC 00/19] Runtime PM fixes for omap-ssi Sebastian Reichel
2016-06-18 16:26 ` [RFC 01/19] HSI: omap_ssi_port: remove useless newline Sebastian Reichel
2016-06-18 16:26 ` [RFC 02/19] HSI: omap_ssi: do not reset module Sebastian Reichel
2016-06-21 11:38   ` Tony Lindgren
2016-06-18 16:26 ` [RFC 03/19] HSI: ssi_protocol: avoid ssi_waketest call with held spinlock Sebastian Reichel
2016-06-18 16:26 ` [RFC 04/19] HSI: ssi_protocol: replace spin_lock with spin_lock_bh Sebastian Reichel
2016-06-18 16:26 ` [RFC 05/19] HSI: ssi_protocol: fix ssip_xmit invocation Sebastian Reichel
2016-06-18 16:26 ` [RFC 06/19] HSI: omap_ssi: convert cawake irq handler to thread Sebastian Reichel
2016-06-18 16:26 ` [RFC 07/19] HSI: omap_ssi_port: replace wkin_cken with atomic bitmap operations Sebastian Reichel
2016-06-18 16:26 ` [RFC 08/19] HSI: core: switch port event notifier from atomic to blocking Sebastian Reichel
2016-06-18 16:26 ` [RFC 09/19] HSI: omap_ssi_port: prepare start_tx/stop_tx for blocking pm_runtime calls Sebastian Reichel
2016-06-18 16:27 ` [RFC 10/19] HSI: omap_ssi_core: use pm_runtime_put instead of pm_runtime_put_sync Sebastian Reichel
2016-06-18 16:27 ` [RFC 11/19] HSI: omap_ssi_core: remove pm_runtime_get_sync call from tasklet Sebastian Reichel
2016-06-18 16:27 ` [RFC 12/19] HSI: omap_ssi_port: switch to threaded pio irq Sebastian Reichel
2016-06-18 16:27 ` [RFC 13/19] HSI: omap_ssi_port: avoid pm_runtime_get_sync in ssi_start_dma and ssi_start_pio Sebastian Reichel
2016-06-18 16:27 ` [RFC 14/19] HSI: omap_ssi_port: avoid calling runtime_pm_*_sync inside spinlock Sebastian Reichel
2016-06-18 16:27 ` [RFC 15/19] HSI: omap_ssi_port: replace pm_runtime_put_sync with non-sync variant Sebastian Reichel
2016-06-18 16:27 ` [RFC 16/19] HSI: omap_ssi_port: ensure clocks are kept enabled during transfer Sebastian Reichel
2016-06-18 16:27 ` [RFC 17/19] HSI: omap_ssi: call msg->complete() from process context Sebastian Reichel
2016-06-18 16:27 ` [RFC 18/19] HSI: omap_ssi_port: use rpm autosuspend API Sebastian Reichel
2016-06-18 16:27 ` [RFC 19/19] HSI: omap_ssi: drop pm_runtime_irq_safe Sebastian Reichel
2016-06-21 11:41   ` Tony Lindgren
2016-06-18 18:54 ` [RFC 00/19] Runtime PM fixes for omap-ssi Pavel Machek
2016-06-18 20:21   ` Sebastian Reichel
2016-06-18 22:00     ` Pavel Machek
2016-06-28  0:33 ` Sebastian Reichel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).