devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: srinivas.kandagatla@linaro.org
To: gregkh@linuxfoundation.org, broonie@kernel.org,
	alsa-devel@alsa-project.org
Cc: sdharia@codeaurora.org, bp@suse.de, poeschel@lemonage.de,
	treding@nvidia.com, andreas.noever@gmail.com,
	alan@linux.intel.com, mathieu.poirier@linaro.org,
	daniel@ffwll.ch, jkosina@suse.cz, sharon.dvir1@mail.huji.ac.il,
	joe@perches.com, davem@davemloft.net, james.hogan@imgtec.com,
	michael.opdenacker@free-electrons.com, robh+dt@kernel.org,
	pawel.moll@arm.com, mark.rutland@arm.com,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-arm-msm@vger.kernel.org, vinod.koul@intel.com,
	arnd@arndb.de, srinivas.kandagatla@linaro.org
Subject: [PATCH v8 07/13] slimbus: Add support for 'clock-pause' feature
Date: Thu, 30 Nov 2017 17:41:54 +0000	[thread overview]
Message-ID: <20171130174200.6684-8-srinivas.kandagatla@linaro.org> (raw)
In-Reply-To: <20171130174200.6684-1-srinivas.kandagatla@linaro.org>

From: Sagar Dharia <sdharia@codeaurora.org>

Per SLIMbus specification, a reconfiguration sequence known as
'clock pause' needs to be broadcast over the bus while entering low-
power mode. Clock-pause is initiated by the controller driver.
To exit clock-pause, controller typically wakes up the framer device.
Since wakeup precedure is controller-specific, framework calls it via
controller's function pointer to invoke it.

Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 drivers/slimbus/Makefile    |   2 +-
 drivers/slimbus/core.c      |  16 ++++++
 drivers/slimbus/messaging.c |  35 +++++++++++-
 drivers/slimbus/sched.c     | 128 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/slimbus/slimbus.h   |  53 ++++++++++++++++++
 5 files changed, 232 insertions(+), 2 deletions(-)
 create mode 100644 drivers/slimbus/sched.c

diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
index a65677ec3f7a..fd794f83d1cf 100644
--- a/drivers/slimbus/Makefile
+++ b/drivers/slimbus/Makefile
@@ -2,4 +2,4 @@
 # Makefile for kernel SLIMbus framework.
 #
 obj-$(CONFIG_SLIMBUS)			+= slimbus.o
-slimbus-y				:= core.o messaging.o
+slimbus-y				:= core.o messaging.o sched.o
diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c
index 0c66f76b1a85..17485b0d0771 100644
--- a/drivers/slimbus/core.c
+++ b/drivers/slimbus/core.c
@@ -16,6 +16,7 @@
 #include <linux/init.h>
 #include <linux/idr.h>
 #include <linux/of.h>
+#include <linux/pm_runtime.h>
 #include <linux/slimbus.h>
 #include "slimbus.h"
 
@@ -225,6 +226,8 @@ int slim_register_controller(struct slim_controller *ctrl)
 	ida_init(&ctrl->laddr_ida);
 	idr_init(&ctrl->tid_idr);
 	mutex_init(&ctrl->lock);
+	mutex_init(&ctrl->sched.m_reconf);
+	init_completion(&ctrl->sched.pause_comp);
 
 	dev_dbg(ctrl->dev, "Bus [%s] registered:dev:%p\n",
 		ctrl->name, ctrl->dev);
@@ -256,6 +259,8 @@ int slim_unregister_controller(struct slim_controller *ctrl)
 {
 	/* Remove all clients */
 	device_for_each_child(ctrl->dev, NULL, slim_ctrl_remove_device);
+	/* Enter Clock Pause */
+	slim_ctrl_clk_pause(ctrl, false, 0);
 	ida_simple_remove(&ctrl_ida, ctrl->id);
 
 	return 0;
@@ -423,6 +428,14 @@ int slim_device_report_present(struct slim_controller *ctrl,
 	struct slim_device *sbdev;
 	int ret;
 
+	ret = pm_runtime_get_sync(ctrl->dev);
+
+	if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) {
+		dev_err(ctrl->dev, "slim ctrl not active,state:%d, ret:%d\n",
+				    ctrl->sched.clk_state, ret);
+		goto slimbus_not_active;
+	}
+
 	sbdev = slim_get_device(ctrl, e_addr);
 	if (IS_ERR(sbdev))
 		return -ENODEV;
@@ -434,6 +447,9 @@ int slim_device_report_present(struct slim_controller *ctrl,
 
 	ret = slim_device_alloc_laddr(sbdev, true);
 
+slimbus_not_active:
+	pm_runtime_mark_last_busy(ctrl->dev);
+	pm_runtime_put_autosuspend(ctrl->dev);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(slim_device_report_present);
diff --git a/drivers/slimbus/messaging.c b/drivers/slimbus/messaging.c
index 7c12dc933816..5f3bab2cd9e3 100644
--- a/drivers/slimbus/messaging.c
+++ b/drivers/slimbus/messaging.c
@@ -10,6 +10,7 @@
  * GNU General Public License for more details.
  */
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
 #include "slimbus.h"
 
 /**
@@ -52,6 +53,10 @@ void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid, u8 len)
 	memcpy(msg->rbuf, reply, len);
 	if (txn->comp)
 		complete(txn->comp);
+
+	/* Remove runtime-pm vote now that response was received for TID txn */
+	pm_runtime_mark_last_busy(ctrl->dev);
+	pm_runtime_put_autosuspend(ctrl->dev);
 }
 EXPORT_SYMBOL_GPL(slim_msg_response);
 
@@ -71,10 +76,29 @@ EXPORT_SYMBOL_GPL(slim_msg_response);
 int slim_do_transfer(struct slim_controller *ctrl, struct slim_msg_txn *txn)
 {
 	DECLARE_COMPLETION_ONSTACK(done);
-	bool need_tid;
+	bool need_tid, clk_pause_msg = false;
 	unsigned long flags;
 	int ret, tid, timeout;
 
+	/*
+	 * do not vote for runtime-PM if the transactions are part of clock
+	 * pause sequence
+	 */
+	if (ctrl->sched.clk_state == SLIM_CLK_ENTERING_PAUSE &&
+		(txn->mt == SLIM_MSG_MT_CORE &&
+		 txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
+		 txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW))
+		clk_pause_msg = true;
+
+	if (!clk_pause_msg) {
+		ret = pm_runtime_get_sync(ctrl->dev);
+		if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) {
+			dev_err(ctrl->dev, "ctrl wrong state:%d, ret:%d\n",
+				ctrl->sched.clk_state, ret);
+			goto slim_xfer_err;
+		}
+	}
+
 	need_tid = slim_tid_txn(txn->mt, txn->mc);
 
 	if (need_tid) {
@@ -113,6 +137,15 @@ int slim_do_transfer(struct slim_controller *ctrl, struct slim_msg_txn *txn)
 		dev_err(ctrl->dev, "Tx:MT:0x%x, MC:0x%x, LA:0x%x failed:%d\n",
 			txn->mt, txn->mc, txn->la, ret);
 
+slim_xfer_err:
+	if (!clk_pause_msg && (!need_tid  || ret == -ETIMEDOUT)) {
+		/*
+		 * remove runtime-pm vote if this was TX only, or
+		 * if there was error during this transaction
+		 */
+		pm_runtime_mark_last_busy(ctrl->dev);
+		pm_runtime_mark_last_busy(ctrl->dev);
+	}
 	return ret;
 }
 
diff --git a/drivers/slimbus/sched.c b/drivers/slimbus/sched.c
new file mode 100644
index 000000000000..74300f1a6898
--- /dev/null
+++ b/drivers/slimbus/sched.c
@@ -0,0 +1,128 @@
+/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/errno.h>
+#include "slimbus.h"
+
+/**
+ * slim_ctrl_clk_pause() - Called by slimbus controller to enter/exit
+ *			   'clock pause'
+ * @ctrl: controller requesting bus to be paused or woken up
+ * @wakeup: Wakeup this controller from clock pause.
+ * @restart: Restart time value per spec used for clock pause. This value
+ *	isn't used when controller is to be woken up.
+ *
+ * Slimbus specification needs this sequence to turn-off clocks for the bus.
+ * The sequence involves sending 3 broadcast messages (reconfiguration
+ * sequence) to inform all devices on the bus.
+ * To exit clock-pause, controller typically wakes up active framer device.
+ * This API executes clock pause reconfiguration sequence if wakeup is false.
+ * If wakeup is true, controller's wakeup is called.
+ * For entering clock-pause, -EBUSY is returned if a message txn in pending.
+ */
+int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart)
+{
+	int i, ret = 0;
+	unsigned long flags;
+	struct slim_sched *sched = &ctrl->sched;
+	struct slim_val_inf msg = {0, 0, NULL, NULL};
+
+	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
+				3, SLIM_LA_MANAGER, &msg);
+
+	if (wakeup == false && restart > SLIM_CLK_UNSPECIFIED)
+		return -EINVAL;
+
+	mutex_lock(&sched->m_reconf);
+	if (wakeup) {
+		if (sched->clk_state == SLIM_CLK_ACTIVE) {
+			mutex_unlock(&sched->m_reconf);
+			return 0;
+		}
+
+		/*
+		 * Fine-tune calculation based on clock gear,
+		 * message-bandwidth after bandwidth management
+		 */
+		ret = wait_for_completion_timeout(&sched->pause_comp,
+				msecs_to_jiffies(100));
+		if (!ret) {
+			mutex_unlock(&sched->m_reconf);
+			pr_err("Previous clock pause did not finish");
+			return -ETIMEDOUT;
+		}
+		ret = 0;
+
+		/*
+		 * Slimbus framework will call controller wakeup
+		 * Controller should make sure that it sets active framer
+		 * out of clock pause
+		 */
+		if (sched->clk_state == SLIM_CLK_PAUSED && ctrl->wakeup)
+			ret = ctrl->wakeup(ctrl);
+		if (!ret)
+			sched->clk_state = SLIM_CLK_ACTIVE;
+		mutex_unlock(&sched->m_reconf);
+
+		return ret;
+	}
+
+	/* already paused */
+	if (ctrl->sched.clk_state == SLIM_CLK_PAUSED) {
+		mutex_unlock(&sched->m_reconf);
+		return 0;
+	}
+
+	spin_lock_irqsave(&ctrl->txn_lock, flags);
+	for (i = 0; i < SLIM_MAX_TIDS; i++) {
+		/* Pending response for a message */
+		if (idr_find(&ctrl->tid_idr, i)) {
+			spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+			mutex_unlock(&sched->m_reconf);
+			return -EBUSY;
+		}
+	}
+	spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+
+	sched->clk_state = SLIM_CLK_ENTERING_PAUSE;
+
+	/* clock pause sequence */
+	ret = slim_do_transfer(ctrl, &txn);
+	if (ret)
+		goto clk_pause_ret;
+
+	txn.mc = SLIM_MSG_MC_NEXT_PAUSE_CLOCK;
+	txn.rl = 4;
+	msg.num_bytes = 1;
+	msg.wbuf = &restart;
+	ret = slim_do_transfer(ctrl, &txn);
+	if (ret)
+		goto clk_pause_ret;
+
+	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
+	txn.rl = 3;
+	msg.num_bytes = 1;
+	msg.wbuf = NULL;
+	ret = slim_do_transfer(ctrl, &txn);
+
+clk_pause_ret:
+	if (ret) {
+		sched->clk_state = SLIM_CLK_ACTIVE;
+	} else {
+		sched->clk_state = SLIM_CLK_PAUSED;
+		complete(&sched->pause_comp);
+	}
+	mutex_unlock(&sched->m_reconf);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_ctrl_clk_pause);
diff --git a/drivers/slimbus/slimbus.h b/drivers/slimbus/slimbus.h
index 5a83ce15c63a..e0ae7bb1ba47 100644
--- a/drivers/slimbus/slimbus.h
+++ b/drivers/slimbus/slimbus.h
@@ -21,6 +21,16 @@
 /* SLIMbus message types. Related to interpretation of message code. */
 #define SLIM_MSG_MT_CORE			0x0
 
+/* Clock pause Reconfiguration messages */
+#define SLIM_MSG_MC_BEGIN_RECONFIGURATION        0x40
+#define SLIM_MSG_MC_NEXT_PAUSE_CLOCK             0x4A
+#define SLIM_MSG_MC_RECONFIGURE_NOW              0x5F
+
+/* Clock pause values per SLIMbus spec */
+#define SLIM_CLK_FAST				0
+#define SLIM_CLK_CONST_PHASE			1
+#define SLIM_CLK_UNSPECIFIED			2
+
 /* Destination type Values */
 #define SLIM_MSG_DEST_LOGICALADDR	0
 #define SLIM_MSG_DEST_ENUMADDR		1
@@ -87,6 +97,42 @@ struct slim_msg_txn {
 #define DEFINE_SLIM_LDEST_TXN(name, mc, rl, la, msg) \
 	struct slim_msg_txn name = { rl, 0, mc, SLIM_MSG_DEST_LOGICALADDR, 0,\
 					0, la, msg, }
+
+#define DEFINE_SLIM_BCAST_TXN(name, mc, rl, la, msg) \
+	struct slim_msg_txn name = { rl, 0, mc, SLIM_MSG_DEST_BROADCAST, 0,\
+					0, la, msg, }
+/**
+ * enum slim_clk_state: SLIMbus controller's clock state used internally for
+ *	maintaining current clock state.
+ * @SLIM_CLK_ACTIVE: SLIMbus clock is active
+ * @SLIM_CLK_ENTERING_PAUSE: SLIMbus clock pause sequence is being sent on the
+ *	bus. If this succeeds, state changes to SLIM_CLK_PAUSED. If the
+ *	transition fails, state changes back to SLIM_CLK_ACTIVE
+ * @SLIM_CLK_PAUSED: SLIMbus controller clock has paused.
+ */
+enum slim_clk_state {
+	SLIM_CLK_ACTIVE,
+	SLIM_CLK_ENTERING_PAUSE,
+	SLIM_CLK_PAUSED,
+};
+
+/**
+ * struct slim_sched: Framework uses this structure internally for scheduling.
+ * @clk_state: Controller's clock state from enum slim_clk_state
+ * @pause_comp: Signals completion of clock pause sequence. This is useful when
+ *	client tries to call SLIMbus transaction when controller is entering
+ *	clock pause.
+ * @m_reconf: This mutex is held until current reconfiguration (data channel
+ *	scheduling, message bandwidth reservation) is done. Message APIs can
+ *	use the bus concurrently when this mutex is held since elemental access
+ *	messages can be sent on the bus when reconfiguration is in progress.
+ */
+struct slim_sched {
+	enum slim_clk_state	clk_state;
+	struct completion	pause_comp;
+	struct mutex		m_reconf;
+};
+
 /**
  * struct slim_controller  - Controls every instance of SLIMbus
  *				(similar to 'master' on SPI)
@@ -102,6 +148,7 @@ struct slim_msg_txn {
  * @devices: Slim device list
  * @tid_idr: tid id allocator
  * @txn_lock: Lock to protect table of transactions
+ * @sched: scheduler structure used by the controller
  * @xfer_msg: Transfer a message on this controller (this can be a broadcast
  *	control/status message like data channel setup, or a unicast message
  *	like value element read/write.
@@ -112,6 +159,9 @@ struct slim_msg_txn {
  *	address table and get_laddr can be used in that case so that controller
  *	can do this assignment. Use case is when the master is on the remote
  *	processor side, who is resposible for allocating laddr.
+ * @wakeup: This function pointer implements controller-specific procedure
+ *	to wake it up from clock-pause. Framework will call this to bring
+ *	the controller out of clock pause.
  *
  *	'Manager device' is responsible for  device management, bandwidth
  *	allocation, channel setup, and port associations per channel.
@@ -146,12 +196,14 @@ struct slim_controller {
 	struct list_head	devices;
 	struct idr		tid_idr;
 	spinlock_t		txn_lock;
+	struct slim_sched	sched;
 	int			(*xfer_msg)(struct slim_controller *ctrl,
 					    struct slim_msg_txn *tx);
 	int			(*set_laddr)(struct slim_controller *ctrl,
 					     struct slim_eaddr *ea, u8 laddr);
 	int			(*get_laddr)(struct slim_controller *ctrl,
 					     struct slim_eaddr *ea, u8 *laddr);
+	int			(*wakeup)(struct slim_controller *ctrl);
 };
 
 int slim_device_report_present(struct slim_controller *ctrl,
@@ -161,6 +213,7 @@ int slim_register_controller(struct slim_controller *ctrl);
 int slim_unregister_controller(struct slim_controller *ctrl);
 void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid, u8 l);
 int slim_do_transfer(struct slim_controller *ctrl, struct slim_msg_txn *txn);
+int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart);
 
 static inline bool slim_tid_txn(u8 mt, u8 mc)
 {
-- 
2.15.0

  parent reply	other threads:[~2017-11-30 17:41 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-11-30 17:41 [PATCH v8 00/13] Introduce framework for SLIMbus device driver srinivas.kandagatla
2017-11-30 17:41 ` [PATCH v8 01/13] Documentation: Add SLIMbus summary srinivas.kandagatla
2017-12-01 10:27   ` Jonathan Neuschäfer
2017-12-05 22:01     ` Srinivas Kandagatla
2017-11-30 17:41 ` [PATCH v8 02/13] dt-bindings: Add SLIMbus bindings srinivas.kandagatla
2017-11-30 17:41 ` [PATCH v8 03/13] slimbus: Add SLIMbus bus type srinivas.kandagatla
2017-11-30 17:41 ` [PATCH v8 04/13] slimbus: core: Add slim controllers support srinivas.kandagatla
2017-11-30 17:41 ` [PATCH v8 05/13] slimbus: core: add support to device tree helper srinivas.kandagatla
2017-11-30 17:41 ` [PATCH v8 06/13] slimbus: Add messaging APIs to slimbus framework srinivas.kandagatla
2017-11-30 17:41 ` srinivas.kandagatla [this message]
2017-11-30 19:25   ` [PATCH v8 07/13] slimbus: Add support for 'clock-pause' feature Philippe Ombredanne
2017-12-01  9:51     ` Greg Kroah-Hartman
2017-12-01  9:54       ` Srinivas Kandagatla
2017-11-30 17:41 ` [PATCH v8 08/13] regmap: add SLIMbus support srinivas.kandagatla
2017-11-30 17:41 ` [PATCH v8 09/13] slimbus: core: add common defines required for controllers srinivas.kandagatla
2017-11-30 17:41 ` [PATCH v8 10/13] dt-bindings: Add qcom slimbus controller bindings srinivas.kandagatla
     [not found]   ` <20171130174200.6684-11-srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2017-12-04 21:28     ` Rob Herring
2017-12-05 22:03       ` Srinivas Kandagatla
2017-11-30 17:41 ` [PATCH v8 11/13] slimbus: qcom: Add Qualcomm Slimbus controller driver srinivas.kandagatla
2017-11-30 17:41 ` [PATCH v8 12/13] slimbus: qcom: Add runtime-pm support using clock-pause srinivas.kandagatla
2017-11-30 17:42 ` [PATCH v8 13/13] MAINTAINERS: Add SLIMbus maintainer srinivas.kandagatla

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20171130174200.6684-8-srinivas.kandagatla@linaro.org \
    --to=srinivas.kandagatla@linaro.org \
    --cc=alan@linux.intel.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=andreas.noever@gmail.com \
    --cc=arnd@arndb.de \
    --cc=bp@suse.de \
    --cc=broonie@kernel.org \
    --cc=daniel@ffwll.ch \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=james.hogan@imgtec.com \
    --cc=jkosina@suse.cz \
    --cc=joe@perches.com \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=mathieu.poirier@linaro.org \
    --cc=michael.opdenacker@free-electrons.com \
    --cc=pawel.moll@arm.com \
    --cc=poeschel@lemonage.de \
    --cc=robh+dt@kernel.org \
    --cc=sdharia@codeaurora.org \
    --cc=sharon.dvir1@mail.huji.ac.il \
    --cc=treding@nvidia.com \
    --cc=vinod.koul@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).