@@ -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)
@@ -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 */