diff mbox

[3/3] slimbus: Add messaging APIs to slimbus framework

Message ID 1434260958-13732-4-git-send-email-sdharia@codeaurora.org (mailing list archive)
State Not Applicable, archived
Delegated to: Andy Gross
Headers show

Commit Message

sdharia@codeaurora.org June 14, 2015, 5:49 a.m. UTC
Slimbus devices use value-element, and information elements to
control device parameters (e.g. value element is used to represent
gain for codec, information element is used to represent interrupt
status for codec when codec interrupt fires).
Messaging APIs are used to set/get these value and information
elements. Slimbus specification uses 8-bit "transaction IDs" for
messages where a read-value is anticipated. Framework uses a table
of pointers to store those TIDs and responds back to the caller in
O(1).
Caller can opt to do synchronous, or asynchronous reads/writes. For
asynchronous operations, the callback can be called from atomic
context.

Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
Tested-by: Naveen Kaje <nkaje@codeaurora.org>
---
 drivers/slimbus/slimbus.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/slimbus.h   | 124 +++++++++++++++++++++
 2 files changed, 397 insertions(+)

Comments

Mark Brown June 15, 2015, 11:08 a.m. UTC | #1
On Sat, Jun 13, 2015 at 11:49:18PM -0600, Sagar Dharia wrote:

> +	if (txn->mt == SLIM_MSG_MT_CORE &&
> +		(txn->mc == SLIM_MSG_MC_REQUEST_INFORMATION ||
> +		 txn->mc == SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION ||
> +		 txn->mc == SLIM_MSG_MC_REQUEST_VALUE ||
> +		 txn->mc == SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION)) {

The mc comparison here looks like you meant to write a switch
statement.

> +	ret = ctrl->xfer_msg(ctrl, txn);
> +	return ret;
> +}

No need for ret here.

> +static int slim_xfer_msg(struct slim_controller *ctrl,
> +			struct slim_device *sbdev, struct slim_val_inf *msg,
> +			u8 mc)
> +{
> +	DEFINE_SLIM_LDEST_TXN(txn_stack, mc, 6, sbdev->laddr, msg);
> +	struct slim_msg_txn *txn = &txn_stack;
> +	int ret;
> +	unsigned long flags;
> +	u16 sl, cur;
> +	bool tid_txn, async = false;
> +	DECLARE_COMPLETION_ONSTACK(complete);
> +
> +	ret = slim_val_inf_sanity(msg, mc);
> +	if (ret) {
> +		pr_err("Sanity check failed for msg:offset:0x%x, mc:%d",
> +				msg->start_offset, mc);

dev_err() seems better, and if you're going to print an error on this
why not move the error prints into the sanity check so someone seeing
the error message can tell what went wrong?

> +	sl = slim_slicesize(msg->num_bytes);
> +	dev_err(&ctrl->dev, "SB xfer msg:os:%x, len:%d, MC:%x, sl:%x\n",
> +				msg->start_offset, msg->num_bytes, mc, sl);

Looks like you left osme debug statements in here.

> +	if (!msg->comp_cb && tid_txn) {
> +		msg->comp_cb = slim_sync_default_cb;
> +		msg->ctx = &complete;
> +	} else
> +		async = true;

Coding style: if you have braces on one branch of an if they should be
on both.

> +	/* sync read */
> +	if (!ret && tid_txn && !async) {
> +		ret = wait_for_completion_timeout(&complete, HZ);
> +		if (!ret)
> +			ret = -ETIMEDOUT;
> +		else
> +			ret = 0;
> +	}

Are we sure that HZ is a good timeout here - might it be too short or
too long for some users?

> diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h
> index 61b7c74..1d98c58 100644
> --- a/include/linux/slimbus.h
> +++ b/include/linux/slimbus.h
> @@ -34,6 +34,7 @@ extern struct bus_type slimbus_type;
>  #define SLIM_FRM_SLOTS_PER_SUPERFRAME	16
>  #define SLIM_GDE_SLOTS_PER_SUPERFRAME	2
>  
> +#define SLIM_MAX_TXNS			256

Where did this number come from?
diff mbox

Patch

diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index f51b503..44614e1 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -26,6 +26,14 @@  static DEFINE_IDR(ctrl_idr);
 static struct device_type slim_dev_type;
 static struct device_type slim_ctrl_type;
 
+#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, }
+
 static int slim_compare_eaddr(struct slim_eaddr *a, struct slim_eaddr *b)
 {
 	if (a->manf_id == b->manf_id && a->prod_code == b->prod_code &&
@@ -762,6 +770,271 @@  int slim_get_logical_addr(struct slim_device *sb, struct slim_eaddr *e_addr,
 }
 EXPORT_SYMBOL(slim_get_logical_addr);
 
+/*
+ * slim_msg_response: Deliver Message response received from a device to the
+ *	framework.
+ * @ctrl: Controller handle
+ * @reply: Reply received from the device
+ * @len: Length of the reply
+ * @tid: Transaction ID received with which framework can associate reply.
+ * Called by controller to inform framework about the response received.
+ * This helps in making the API asynchronous, and controller-driver doesn't need
+ * to manage 1 more table other than the one managed by framework mapping TID
+ * with buffers
+ */
+void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid, u8 len)
+{
+	struct slim_val_inf *msg;
+
+	spin_lock(&ctrl->txn_lock);
+	msg = ctrl->txnt[tid];
+	if (msg == NULL || msg->rbuf == NULL) {
+		spin_unlock(&ctrl->txn_lock);
+		dev_err(&ctrl->dev, "Got response to invalid TID:%d, len:%d",
+				tid, len);
+		return;
+	}
+	memcpy(msg->rbuf, reply, len);
+	ctrl->txnt[tid] = NULL;
+	if (msg->comp_cb)
+		msg->comp_cb(msg->ctx);
+	spin_unlock(&ctrl->txn_lock);
+}
+EXPORT_SYMBOL(slim_msg_response);
+
+static int slim_processtxn(struct slim_controller *ctrl,
+				struct slim_msg_txn *txn)
+{
+	int i = 0, ret = 0;
+	unsigned long flags;
+
+	if (txn->mt == SLIM_MSG_MT_CORE &&
+		(txn->mc == SLIM_MSG_MC_REQUEST_INFORMATION ||
+		 txn->mc == SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION ||
+		 txn->mc == SLIM_MSG_MC_REQUEST_VALUE ||
+		 txn->mc == SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION)) {
+		spin_lock_irqsave(&ctrl->txn_lock, flags);
+		for (i = 0; i < ctrl->last_tid; i++) {
+			if (ctrl->txnt[i] == NULL)
+				break;
+		}
+		if (i >= ctrl->last_tid) {
+			if (ctrl->last_tid == 255) {
+				spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+				return -ENOMEM;
+			}
+			ctrl->last_tid++;
+		}
+		ctrl->txnt[i] = txn->msg;
+		txn->tid = i;
+		spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+	}
+
+	ret = ctrl->xfer_msg(ctrl, txn);
+	return ret;
+}
+
+static int slim_val_inf_sanity(struct slim_val_inf *msg, int oper)
+{
+	if (!msg || msg->num_bytes > 16 ||
+		(msg->start_offset + msg->num_bytes) > 0xC00)
+		return -EINVAL;
+	switch (oper) {
+	case SLIM_MSG_MC_REQUEST_VALUE:
+	case SLIM_MSG_MC_REQUEST_INFORMATION:
+		if (msg->rbuf == NULL)
+			return -EINVAL;
+		return 0;
+	case SLIM_MSG_MC_CHANGE_VALUE:
+	case SLIM_MSG_MC_CLEAR_INFORMATION:
+		if (msg->wbuf == NULL)
+			return -EINVAL;
+		return 0;
+	case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
+	case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
+		if (msg->rbuf == NULL || msg->wbuf == NULL)
+			return -EINVAL;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static u16 slim_slicecodefromsize(u16 req)
+{
+	static const u8 codetosize[8] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+	if (req >= ARRAY_SIZE(codetosize))
+		return 0;
+	else
+		return codetosize[req];
+}
+
+static u16 slim_slicesize(int code)
+{
+	static const u8 sizetocode[16] = {
+		0, 1, 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7
+	};
+
+	clamp(code, 1, (int)ARRAY_SIZE(sizetocode));
+	return sizetocode[code - 1];
+}
+
+static void slim_sync_default_cb(void *ctx)
+{
+	struct completion *comp = ctx;
+
+	complete(comp);
+}
+
+static int slim_xfer_msg(struct slim_controller *ctrl,
+			struct slim_device *sbdev, struct slim_val_inf *msg,
+			u8 mc)
+{
+	DEFINE_SLIM_LDEST_TXN(txn_stack, mc, 6, sbdev->laddr, msg);
+	struct slim_msg_txn *txn = &txn_stack;
+	int ret;
+	unsigned long flags;
+	u16 sl, cur;
+	bool tid_txn, async = false;
+	DECLARE_COMPLETION_ONSTACK(complete);
+
+	ret = slim_val_inf_sanity(msg, mc);
+	if (ret) {
+		pr_err("Sanity check failed for msg:offset:0x%x, mc:%d",
+				msg->start_offset, mc);
+		return ret;
+	}
+
+	tid_txn = slim_tid_txn(mc);
+
+	sl = slim_slicesize(msg->num_bytes);
+	dev_err(&ctrl->dev, "SB xfer msg:os:%x, len:%d, MC:%x, sl:%x\n",
+				msg->start_offset, msg->num_bytes, mc, sl);
+
+	cur = slim_slicecodefromsize(sl);
+	txn->ec = ((sl | (1 << 3)) | ((msg->start_offset & 0xFFF) << 4));
+
+	if (!msg->comp_cb && tid_txn) {
+		msg->comp_cb = slim_sync_default_cb;
+		msg->ctx = &complete;
+	} else
+		async = true;
+
+	if (mc == SLIM_MSG_MC_REQUEST_CHANGE_VALUE ||
+		mc == SLIM_MSG_MC_CHANGE_VALUE ||
+		mc == SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION ||
+		mc == SLIM_MSG_MC_CLEAR_INFORMATION)
+		txn->rl += msg->num_bytes;
+	if (tid_txn)
+		txn->rl++;
+
+	ret = slim_processtxn(ctrl, txn);
+
+	/* sync read */
+	if (!ret && tid_txn && !async) {
+		ret = wait_for_completion_timeout(&complete, HZ);
+		if (!ret)
+			ret = -ETIMEDOUT;
+		else
+			ret = 0;
+	}
+
+	if (ret && tid_txn) {
+		spin_lock_irqsave(&ctrl->txn_lock, flags);
+		/* Invalidate the transaction */
+		ctrl->txnt[txn->tid] = NULL;
+		spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+	}
+	if (ret)
+		pr_err("slimbus transfer error:%d:offset:0x%x, MC:%d", ret,
+					msg->start_offset, mc);
+	if (!async && tid_txn) {
+		msg->comp_cb = NULL;
+		msg->ctx = NULL;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(slim_xfer_msg);
+
+/* Message APIs Unicast message APIs used by slimbus slave drivers */
+
+/*
+ * Message API access routines.
+ * @sb: client handle requesting elemental message reads, writes.
+ * @msg: Input structure for start-offset, number of bytes to read.
+ * context: can sleep
+ * Returns:
+ * -EINVAL: Invalid parameters
+ * -ETIMEDOUT: If controller could not complete the request. This may happen if
+ *  the bus lines are not clocked, controller is not powered-on, slave with
+ *  given address is not enumerated/responding.
+ */
+int slim_request_val_element(struct slim_device *sb,
+				struct slim_val_inf *msg)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_VALUE);
+}
+EXPORT_SYMBOL(slim_request_val_element);
+
+int slim_request_inf_element(struct slim_device *sb,
+				struct slim_val_inf *msg)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_INFORMATION);
+}
+EXPORT_SYMBOL(slim_request_inf_element);
+
+int slim_change_val_element(struct slim_device *sb, struct slim_val_inf *msg)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_CHANGE_VALUE);
+}
+EXPORT_SYMBOL(slim_change_val_element);
+
+int slim_clear_inf_element(struct slim_device *sb, struct slim_val_inf *msg)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_CLEAR_INFORMATION);
+}
+EXPORT_SYMBOL(slim_clear_inf_element);
+
+int slim_request_change_val_element(struct slim_device *sb,
+					struct slim_val_inf *msg)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_CHANGE_VALUE);
+}
+EXPORT_SYMBOL(slim_request_change_val_element);
+
+int slim_request_clear_inf_element(struct slim_device *sb,
+					struct slim_val_inf *msg)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg,
+					SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION);
+}
+EXPORT_SYMBOL(slim_request_clear_inf_element);
+
 #if IS_ENABLED(CONFIG_OF)
 /* OF helpers for SLIMbus */
 int of_register_slim_devices(struct slim_controller *ctrl)
diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h
index 61b7c74..1d98c58 100644
--- a/include/linux/slimbus.h
+++ b/include/linux/slimbus.h
@@ -34,6 +34,7 @@  extern struct bus_type slimbus_type;
 #define SLIM_FRM_SLOTS_PER_SUPERFRAME	16
 #define SLIM_GDE_SLOTS_PER_SUPERFRAME	2
 
+#define SLIM_MAX_TXNS			256
 struct slim_controller;
 struct slim_device;
 
@@ -97,12 +98,67 @@  struct slim_addrt {
 #define SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS       0x2
 #define SLIM_MSG_MC_REPORT_ABSENT                0xF
 
+/* Information Element management messages */
+#define SLIM_MSG_MC_REQUEST_INFORMATION          0x20
+#define SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION    0x21
+#define SLIM_MSG_MC_REPLY_INFORMATION            0x24
+#define SLIM_MSG_MC_CLEAR_INFORMATION            0x28
+#define SLIM_MSG_MC_REPORT_INFORMATION           0x29
+
+/* Value Element management messages */
+#define SLIM_MSG_MC_REQUEST_VALUE                0x60
+#define SLIM_MSG_MC_REQUEST_CHANGE_VALUE         0x61
+#define SLIM_MSG_MC_REPLY_VALUE                  0x64
+#define SLIM_MSG_MC_CHANGE_VALUE                 0x68
+
 /* Destination type Values */
 #define SLIM_MSG_DEST_LOGICALADDR	0
 #define SLIM_MSG_DEST_ENUMADDR		1
 #define	SLIM_MSG_DEST_BROADCAST		3
 
 /*
+ * struct slim_val_inf: Slimbus value or information element
+ * @start_offset: Specifies starting offset in information/value element map
+ * @num_bytes: upto 16. This ensures that the message will fit the slicesize
+ *		per slimbus spec
+ * @comp_cb: Callback if this read/write is asynchronous
+ * @ctx: Argument for comp_cb
+ */
+struct slim_val_inf {
+	u16			start_offset;
+	u8			num_bytes;
+	u8			*rbuf;
+	const u8		*wbuf;
+	void			(*comp_cb)(void *ctx);
+	void			*ctx;
+};
+
+/*
+ * struct slim_msg_txn: Message to be sent by the controller.
+ * This structure has packet header, payload and buffer to be filled (if any)
+ * @rl: Header field. remaining length.
+ * @mt: Header field. Message type.
+ * @mc: Header field. LSB is message code for type mt.
+ * @dt: Header field. Destination type.
+ * @ec: Element code. Used for elemental access APIs.
+ * @len: Length of payload. (excludes ec)
+ * @tid: Transaction ID. Used for messages expecting response.
+ *	(relevant for message-codes involving read operation)
+ * @la: Logical address of the device this message is going to.
+ *	(Not used when destination type is broadcast.)
+ * @msg: Elemental access message to be read/written
+ */
+struct slim_msg_txn {
+	u8			rl;
+	u8			mt;
+	u8			mc;
+	u8			dt;
+	u16			ec;
+	u8			tid;
+	u8			la;
+	struct slim_val_inf	*msg;
+};
+/*
  * struct slim_controller: Controls every instance of SLIMbus
  *				(similar to 'master' on SPI)
  *	'Manager device' is responsible for  device management, bandwidth
@@ -137,6 +193,9 @@  struct slim_addrt {
  * @num_dev: Number of active slimbus slaves on this bus
  * @devs: List of devices on this controller
  * @wq: Workqueue per controller used to notify devices when they report present
+ * @txnt: Table of transactions having transaction ID
+ * @txn_lock: Lock to protect table of transactions
+ * @last_tid: size of the table txnt (can't grow beyond 256 since TID is 8-bits)
  * @dev_released: completion used to signal when sysfs has released this
  *	controller so that it can be deleted during shutdown
  * @xfer_msg: Transfer a message on this controller (this can be a broadcast
@@ -163,7 +222,12 @@  struct slim_controller {
 	u8			num_dev;
 	struct list_head	devs;
 	struct workqueue_struct *wq;
+	struct slim_val_inf	*txnt[SLIM_MAX_TXNS];
+	u8			last_tid;
+	spinlock_t		txn_lock;
 	struct completion	dev_released;
+	int			(*xfer_msg)(struct slim_controller *ctrl,
+				struct slim_msg_txn *txn);
 	int			(*set_laddr)(struct slim_controller *ctrl,
 				struct slim_eaddr *ea, u8 laddr);
 	int			(*get_laddr)(struct slim_controller *ctrl,
@@ -409,4 +473,64 @@  static inline void slim_set_clientdata(struct slim_device *dev, void *data)
 	dev_set_drvdata(&dev->dev, data);
 }
 
+/* Message APIs Unicast message APIs used by slimbus slave drivers */
+
+/*
+ * Message API access routines for value elements.
+ * @sb: client handle requesting elemental message reads, writes.
+ * @msg: Input structure for start-offset, number of bytes to read.
+ * context: can sleep
+ * Returns:
+ * -EINVAL: Invalid parameters
+ * -ETIMEDOUT: If controller could not complete the request. This may happen if
+ *  the bus lines are not clocked, controller is not powered-on, slave with
+ *  given address is not enumerated/responding.
+ */
+extern int slim_request_val_element(struct slim_device *sb,
+					struct slim_val_inf *msg);
+extern int slim_change_val_element(struct slim_device *sb,
+					struct slim_val_inf *msg);
+extern int slim_request_change_val_element(struct slim_device *sb,
+					struct slim_val_inf *msg);
+
+
+/*
+ * Message API access routines for information elements.
+ * @sb: client handle requesting elemental message reads, writes.
+ * @msg: Input structure for start-offset, number of bytes to read
+ *	wbuf will contain information element(s) bit masks to be cleared.
+ *	rbuf will return what the information element value was
+ */
+
+extern int slim_request_inf_element(struct slim_device *sb,
+					struct slim_val_inf *msg);
+extern int slim_clear_inf_element(struct slim_device *sb,
+					struct slim_val_inf *msg);
+extern int slim_request_clear_inf_element(struct slim_device *sb,
+					struct slim_val_inf *msg);
+
+/*
+ * slim_msg_response: Deliver Message response received from a device to the
+ *	framework.
+ * @ctrl: Controller handle
+ * @reply: Reply received from the device
+ * @len: Length of the reply
+ * @tid: Transaction ID received with which framework can associate reply.
+ * Called by controller to inform framework about the response received.
+ * This helps in making the API asynchronous, and controller-driver doesn't need
+ * to manage 1 more table other than the one managed by framework mapping TID
+ * with buffers
+ */
+extern void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid,
+				u8 len);
+
+/* end of message apis */
+
+static inline bool slim_tid_txn(u8 mc)
+{
+	return (mc == SLIM_MSG_MC_REQUEST_INFORMATION ||
+		mc == SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION ||
+		mc == SLIM_MSG_MC_REQUEST_VALUE ||
+		mc == SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION);
+}
 #endif /* _LINUX_SLIMBUS_H */