Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1328358 > unrolled thread
| Started by | Sagar Dharia <sdharia@codeaurora.org> |
|---|---|
| First post | 2016-02-06 19:50 +0100 |
| Last post | 2016-02-08 21:30 +0100 |
| Articles | 6 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH V4 0/6] Introduce framework for SLIMbus device drivers Sagar Dharia <sdharia@codeaurora.org> - 2016-02-06 19:50 +0100
[PATCH V4 2/6] of/slimbus: OF helper for SLIMbus Sagar Dharia <sdharia@codeaurora.org> - 2016-02-06 19:50 +0100
Re: [PATCH V4 2/6] of/slimbus: OF helper for SLIMbus Rob Herring <robh@kernel.org> - 2016-02-08 21:30 +0100
[PATCH V4 3/6] slimbus: Add messaging APIs to slimbus framework Sagar Dharia <sdharia@codeaurora.org> - 2016-02-06 19:50 +0100
[PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver Sagar Dharia <sdharia@codeaurora.org> - 2016-02-06 19:50 +0100
Re: [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver Rob Herring <robh@kernel.org> - 2016-02-08 21:30 +0100
| From | Sagar Dharia <sdharia@codeaurora.org> |
|---|---|
| Date | 2016-02-06 19:50 +0100 |
| Subject | [PATCH V4 0/6] Introduce framework for SLIMbus device drivers |
| Message-ID | <qZg5H-2Ri-5@gated-at.bofh.it> |
SLIMbus (Serial Low Power Interchip Media Bus) is a specification developed by MIPI (Mobile Industry Processor Interface) alliance. SLIMbus is a 2-wire implementation, which is used to communicate with peripheral components like audio-codec. SLIMbus uses Time-Division-Multiplexing to accommodate multiple data channels, and control channel. Control channel has messages to do device-enumeration, messages to send/receive control-data to/from slimbus devices, messages for port/channel management, and messages to do bandwidth allocation. Framework is introduced to support multiple instances of the bus (1 controller per bus), and multiple slave devices per controller. SPI and I2C frameworks, and comments from last time when I submitted the patches were referred-to while working on this framework. These patchsets introduce device-management, OF helpers, and messaging APIs, controller driver for Qualcomm's slimbus controller, and clock-pause feature for entering/exiting low-power mode for SLIMbus. Framework patches to do channel, port and bandwidth management are work-in-progress and will be sent out soon. These patchsets were tested on Qualcomm Snapdragon processor board using the controller driver, and a test slave device. Changes from V3 to V4: * Addressed inline-code review comments from Mark Brown and Rob Herring. * Use existing device-model to get list of children of the controller. * Use 4 cells to represent 4 components of the enumeration address. * Commit-text and comments to document performance arguments in using coherently mapped buffers for transaction buffer management. * Tried to address clock-pause management suggestion by making sure bus-framework votes/unvotes for runtime-PM while handling transfers. Sagar Dharia (6): SLIMbus: Device management on SLIMbus of/slimbus: OF helper for SLIMbus slimbus: Add messaging APIs to slimbus framework slim: qcom: Add Qualcomm Slimbus controller driver slimbus: Add support for 'clock-pause' feature slim: qcom: Add runtime-pm support using clock-pause feature Documentation/devicetree/bindings/slimbus/bus.txt | 54 ++ .../devicetree/bindings/slimbus/slim-qcom-ctrl.txt | 43 ++ Documentation/slimbus/summary | 109 +++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/slimbus/Kconfig | 21 + drivers/slimbus/Makefile | 5 + drivers/slimbus/slim-core.c | 852 +++++++++++++++++++++ drivers/slimbus/slim-messaging.c | 415 ++++++++++ drivers/slimbus/slim-qcom-ctrl.c | 678 ++++++++++++++++ drivers/slimbus/slim-qcom.h | 64 ++ drivers/slimbus/slim-sched.c | 122 +++ include/linux/mod_devicetable.h | 13 + include/linux/slimbus.h | 678 ++++++++++++++++ 14 files changed, 3057 insertions(+) create mode 100644 Documentation/devicetree/bindings/slimbus/bus.txt create mode 100644 Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt create mode 100644 Documentation/slimbus/summary create mode 100644 drivers/slimbus/Kconfig create mode 100644 drivers/slimbus/Makefile create mode 100644 drivers/slimbus/slim-core.c create mode 100644 drivers/slimbus/slim-messaging.c create mode 100644 drivers/slimbus/slim-qcom-ctrl.c create mode 100644 drivers/slimbus/slim-qcom.h create mode 100644 drivers/slimbus/slim-sched.c create mode 100644 include/linux/slimbus.h -- 1.8.2.1
[toc] | [next] | [standalone]
| From | Sagar Dharia <sdharia@codeaurora.org> |
|---|---|
| Date | 2016-02-06 19:50 +0100 |
| Subject | [PATCH V4 2/6] of/slimbus: OF helper for SLIMbus |
| Message-ID | <qZg5I-2Ri-19@gated-at.bofh.it> |
| In reply to | #1328358 |
OF helper routine scans the SLIMbus DeviceTree, allocates resources,
and creates slim_devices according to the hierarchy.
Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
---
Documentation/devicetree/bindings/slimbus/bus.txt | 54 ++++++++++++++++
drivers/slimbus/slim-core.c | 75 +++++++++++++++++++++++
2 files changed, 129 insertions(+)
create mode 100644 Documentation/devicetree/bindings/slimbus/bus.txt
diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
new file mode 100644
index 0000000..71c769d
--- /dev/null
+++ b/Documentation/devicetree/bindings/slimbus/bus.txt
@@ -0,0 +1,54 @@
+SLIM(Serial Low Power Interchip Media Bus) bus
+
+SLIMbus is a 2-wire bus, and is used to communicate with peripheral
+components like audio-codec.
+
+Controller is a normal device using binding for whatever bus it is
+on (e.g. platform bus).
+Required property for SLIMbus controller node:
+- compatible - name of SLIMbus controller following generic names
+ recommended practice.
+- #address-cells - should be 4 (number of cells required to define
+ 4 fields of the enumeration address for the SLIMbus
+ slave device)
+- #size-cells - should be 0
+
+No other properties are required in the SLIMbus controller bus node.
+
+Child nodes:
+Every SLIMbus controller node can contain zero or more child nodes
+representing slave devices on the bus. Every SLIMbus slave device is
+uniquely determined by the enumeration address containing 4 fields:
+Manufacturer ID, Product code, Device index, and Instance value for
+the device.
+
+Required property for SLIMbus child node:
+- reg - enumeration address fields of the device
+
+Optional:
+- compatible - Slave devices can use compatible field to have a name.
+ If this field is missing, name of the device will be
+ determined using slave's enumeration address.
+ (e.g. in the example below, slave's name will be:
+ 0x217:0x60:0x1:0x0)
+ Note that the property is listed as optional since the
+ devices are discoverable when they are functional.
+ However, the device may need additional non-standard
+ way to power it up so that it can start functioning.
+ In that case, compatible property will be
+ needed for the corresponding driver to probe and
+ perform the required procedure to make it functional.
+
+SLIMbus example for Qualcomm's slimbus manager component:
+
+ slim@28080000 {
+ compatible = "qcom,slim-msm";
+ reg = <0x28080000 0x2000>,
+ interrupts = <0 33 0>;
+ clocks = <&lcc SLIMBUS_SRC>, <&lcc AUDIO_SLIMBUS_CLK>;
+ clock-names = "iface_clk", "core_clk";
+
+ codec_slave@0217.0060.01.00 {
+ reg = <0x217 0x60 0x1 0x0>;
+ };
+ };
diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
index 5b9fefa..4c2c646 100644
--- a/drivers/slimbus/slim-core.c
+++ b/drivers/slimbus/slim-core.c
@@ -18,6 +18,7 @@
#include <linux/idr.h>
#include <linux/pm_runtime.h>
#include <linux/slimbus.h>
+#include <linux/of.h>
static DEFINE_MUTEX(slim_lock);
static DEFINE_IDR(ctrl_idr);
@@ -279,6 +280,79 @@ static LIST_HEAD(board_list);
static LIST_HEAD(slim_ctrl_list);
static DEFINE_MUTEX(board_lock);
+#if IS_ENABLED(CONFIG_OF)
+/* OF helpers for SLIMbus */
+static void of_register_slim_devices(struct slim_controller *ctrl)
+{
+ struct device_node *node;
+ struct slim_boardinfo *temp, *binfo = NULL;
+ int n = 0;
+
+ if (!ctrl->dev.of_node)
+ return;
+
+ for_each_child_of_node(ctrl->dev.of_node, node) {
+ int ret;
+ u32 ea[4];
+ struct slim_device *slim;
+ char *name;
+
+ ret = of_property_read_u32_array(node, "reg", ea, 4);
+ if (ret) {
+ dev_err(&ctrl->dev, "of_slim: E-addr err:%d\n", ret);
+ continue;
+ }
+ name = kcalloc(SLIMBUS_NAME_SIZE, sizeof(char), GFP_KERNEL);
+ if (!name)
+ goto of_slim_err;
+
+ slim = kzalloc(sizeof(struct slim_device), GFP_KERNEL);
+ if (!slim) {
+ kfree(name);
+ goto of_slim_err;
+ }
+ slim->e_addr.manf_id = (u16)ea[0];
+ slim->e_addr.prod_code = (u16)ea[1];
+ slim->e_addr.dev_index = (u8)ea[2];
+ slim->e_addr.instance = (u8)ea[3];
+
+ ret = of_modalias_node(node, name, SLIMBUS_NAME_SIZE);
+ if (ret < 0) {
+ /* use device address for name, if not specified */
+ snprintf(name, SLIMBUS_NAME_SIZE, "0x%x:0x%x:0x%x:0x%x",
+ slim->e_addr.manf_id, slim->e_addr.prod_code,
+ slim->e_addr.dev_index, slim->e_addr.instance);
+ }
+
+ temp = krealloc(binfo, (n + 1) * sizeof(struct slim_boardinfo),
+ GFP_KERNEL);
+ if (!temp) {
+ kfree(name);
+ kfree(slim);
+ goto of_slim_err;
+ }
+ binfo = temp;
+ slim->dev.of_node = of_node_get(node);
+ slim->name = name;
+ binfo[n].bus_num = ctrl->nr;
+ binfo[n].slim_slave = slim;
+ n++;
+ }
+ slim_register_board_info(binfo, n);
+ return;
+
+of_slim_err:
+ n--;
+ while (n >= 0) {
+ kfree(binfo[n].slim_slave->name);
+ kfree(binfo[n].slim_slave);
+ }
+ kfree(binfo);
+}
+#else
+static void of_register_slim_devices(struct slim_controller *ctrl) { }
+#endif
+
/* If controller is not present, only add to boards list */
static void slim_match_ctrl_to_boardinfo(struct slim_controller *ctrl,
struct slim_boardinfo *bi)
@@ -328,6 +402,7 @@ static void slim_ctrl_add_boarddevs(struct slim_controller *ctrl)
{
struct sbi_boardinfo *bi;
+ of_register_slim_devices(ctrl);
mutex_lock(&board_lock);
list_add_tail(&ctrl->list, &slim_ctrl_list);
list_for_each_entry(bi, &board_list, list)
--
1.8.2.1
[toc] | [prev] | [next] | [standalone]
| From | Rob Herring <robh@kernel.org> |
|---|---|
| Date | 2016-02-08 21:30 +0100 |
| Subject | Re: [PATCH V4 2/6] of/slimbus: OF helper for SLIMbus |
| Message-ID | <r00BC-1PS-25@gated-at.bofh.it> |
| In reply to | #1328359 |
On Sat, Feb 06, 2016 at 11:44:21AM -0700, Sagar Dharia wrote:
> OF helper routine scans the SLIMbus DeviceTree, allocates resources,
> and creates slim_devices according to the hierarchy.
>
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> ---
> Documentation/devicetree/bindings/slimbus/bus.txt | 54 ++++++++++++++++
> drivers/slimbus/slim-core.c | 75 +++++++++++++++++++++++
> 2 files changed, 129 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/slimbus/bus.txt
>
> diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
> new file mode 100644
> index 0000000..71c769d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/slimbus/bus.txt
> @@ -0,0 +1,54 @@
> +SLIM(Serial Low Power Interchip Media Bus) bus
> +
> +SLIMbus is a 2-wire bus, and is used to communicate with peripheral
> +components like audio-codec.
> +
> +Controller is a normal device using binding for whatever bus it is
> +on (e.g. platform bus).
> +Required property for SLIMbus controller node:
> +- compatible - name of SLIMbus controller following generic names
> + recommended practice.
> +- #address-cells - should be 4 (number of cells required to define
> + 4 fields of the enumeration address for the SLIMbus
> + slave device)
> +- #size-cells - should be 0
> +
> +No other properties are required in the SLIMbus controller bus node.
> +
> +Child nodes:
> +Every SLIMbus controller node can contain zero or more child nodes
> +representing slave devices on the bus. Every SLIMbus slave device is
> +uniquely determined by the enumeration address containing 4 fields:
> +Manufacturer ID, Product code, Device index, and Instance value for
> +the device.
> +
> +Required property for SLIMbus child node:
> +- reg - enumeration address fields of the device
> +
> +Optional:
> +- compatible - Slave devices can use compatible field to have a name.
> + If this field is missing, name of the device will be
> + determined using slave's enumeration address.
> + (e.g. in the example below, slave's name will be:
> + 0x217:0x60:0x1:0x0)
The compatible string should follow other discoverable buses like PCI
and USB and use the enumeration data for the compatible string.
Something like "slim<manuID>,<product code>".
> + Note that the property is listed as optional since the
> + devices are discoverable when they are functional.
> + However, the device may need additional non-standard
> + way to power it up so that it can start functioning.
> + In that case, compatible property will be
> + needed for the corresponding driver to probe and
> + perform the required procedure to make it functional.
I think the node being present should be optional, but the compatible
should be required if node is present.
> +
> +SLIMbus example for Qualcomm's slimbus manager component:
> +
> + slim@28080000 {
> + compatible = "qcom,slim-msm";
> + reg = <0x28080000 0x2000>,
> + interrupts = <0 33 0>;
> + clocks = <&lcc SLIMBUS_SRC>, <&lcc AUDIO_SLIMBUS_CLK>;
> + clock-names = "iface_clk", "core_clk";
> +
> + codec_slave@0217.0060.01.00 {
Unique fields should be separated by commas.
Probably should just be "codec@..."
> + reg = <0x217 0x60 0x1 0x0>;
> + };
> + };
[toc] | [prev] | [next] | [standalone]
| From | Sagar Dharia <sdharia@codeaurora.org> |
|---|---|
| Date | 2016-02-06 19:50 +0100 |
| Subject | [PATCH V4 3/6] slimbus: Add messaging APIs to slimbus framework |
| Message-ID | <qZg5H-2Ri-7@gated-at.bofh.it> |
| In reply to | #1328358 |
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 will be called from atomic
context.
TX and RX circular rings are used to allow queuing of multiple
transfers per controller. Controller can choose size of these rings
based of controller HW implementation. The buffers are coerently
mapped so that controller can utilize DMA operations for the
transactions without remapping every transaction buffer.
Statically allocated rings help to improve performance by avoiding
overhead of dynamically allocating transactions on need basis.
Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
Tested-by: Naveen Kaje <nkaje@codeaurora.org>
---
drivers/slimbus/Makefile | 2 +-
drivers/slimbus/slim-core.c | 44 +++++
drivers/slimbus/slim-messaging.c | 377 +++++++++++++++++++++++++++++++++++++++
include/linux/slimbus.h | 224 +++++++++++++++++++++++
4 files changed, 646 insertions(+), 1 deletion(-)
create mode 100644 drivers/slimbus/slim-messaging.c
diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
index aa9c147..d95a0a2 100644
--- a/drivers/slimbus/Makefile
+++ b/drivers/slimbus/Makefile
@@ -1,4 +1,4 @@
#
# Makefile for kernel slimbus framework.
#
-obj-$(CONFIG_SLIMBUS) += slim-core.o
+obj-$(CONFIG_SLIMBUS) += slim-core.o slim-messaging.o
diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
index 4c2c646..27c7dec 100644
--- a/drivers/slimbus/slim-core.c
+++ b/drivers/slimbus/slim-core.c
@@ -17,6 +17,7 @@
#include <linux/completion.h>
#include <linux/idr.h>
#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
#include <linux/slimbus.h>
#include <linux/of.h>
@@ -438,7 +439,37 @@ int slim_register_controller(struct slim_controller *ctrl)
ctrl->min_cg = SLIM_MIN_CLK_GEAR;
if (!ctrl->max_cg)
ctrl->max_cg = SLIM_MAX_CLK_GEAR;
+
mutex_init(&ctrl->m_ctrl);
+ spin_lock_init(&ctrl->tx.lock);
+ spin_lock_init(&ctrl->rx.lock);
+
+ ctrl->pending_wr = kcalloc((ctrl->tx.n - 1),
+ sizeof(struct slim_pending),
+ GFP_KERNEL);
+ if (!ctrl->pending_wr) {
+ ret = -ENOMEM;
+ goto wr_alloc_failed;
+ }
+
+ sema_init(&ctrl->tx_sem, (ctrl->tx.n - 1));
+
+ ctrl->tx.base = dma_alloc_coherent(ctrl->dev.parent,
+ (ctrl->tx.sl_sz * ctrl->tx.n),
+ &ctrl->tx.phy, GFP_KERNEL);
+ if (!ctrl->tx.base) {
+ ret = -ENOMEM;
+ goto tx_alloc_failed;
+ }
+
+ ctrl->rx.base = dma_alloc_coherent(ctrl->dev.parent,
+ (ctrl->rx.sl_sz * ctrl->rx.n),
+ &ctrl->rx.phy, GFP_KERNEL);
+ if (!ctrl->rx.base) {
+ ret = -ENOMEM;
+ goto rx_alloc_failed;
+ }
+
ret = device_register(&ctrl->dev);
if (ret)
goto dev_reg_failed;
@@ -456,9 +487,18 @@ int slim_register_controller(struct slim_controller *ctrl)
err_workq_failed:
device_unregister(&ctrl->dev);
dev_reg_failed:
+ dma_free_coherent(ctrl->dev.parent, (ctrl->rx.sl_sz * ctrl->rx.n),
+ ctrl->rx.base, ctrl->rx.phy);
+rx_alloc_failed:
+ dma_free_coherent(ctrl->dev.parent, (ctrl->tx.sl_sz * ctrl->tx.n),
+ ctrl->tx.base, ctrl->tx.phy);
+tx_alloc_failed:
+ kfree(ctrl->pending_wr);
+wr_alloc_failed:
mutex_lock(&slim_lock);
idr_remove(&ctrl_idr, ctrl->nr);
mutex_unlock(&slim_lock);
+ dev_err(&ctrl->dev, "slimbus controller registration failed:%d", ret);
return ret;
}
EXPORT_SYMBOL_GPL(slim_register_controller);
@@ -510,6 +550,10 @@ int slim_del_controller(struct slim_controller *ctrl)
wait_for_completion(&ctrl->dev_released);
list_del(&ctrl->list);
destroy_workqueue(ctrl->wq);
+ dma_free_coherent(ctrl->dev.parent, (ctrl->rx.sl_sz * ctrl->rx.n),
+ ctrl->rx.base, ctrl->rx.phy);
+ dma_free_coherent(ctrl->dev.parent, (ctrl->tx.sl_sz * ctrl->tx.n),
+ ctrl->tx.base, ctrl->tx.phy);
/* free bus id */
mutex_lock(&slim_lock);
idr_remove(&ctrl_idr, ctrl->nr);
diff --git a/drivers/slimbus/slim-messaging.c b/drivers/slimbus/slim-messaging.c
new file mode 100644
index 0000000..3785c35
--- /dev/null
+++ b/drivers/slimbus/slim-messaging.c
@@ -0,0 +1,377 @@
+/* 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/slab.h>
+#include <linux/slimbus.h>
+
+/**
+ * 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;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctrl->txn_lock, flags);
+ msg = ctrl->tid_tbl[tid];
+ if (msg == NULL || msg->rbuf == NULL) {
+ spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+ dev_err(&ctrl->dev, "Got response to invalid TID:%d, len:%d\n",
+ tid, len);
+ return;
+ }
+ memcpy(msg->rbuf, reply, len);
+ ctrl->tid_tbl[tid] = NULL;
+ if (msg->comp_cb)
+ msg->comp_cb(msg->ctx, 0);
+ spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+}
+EXPORT_SYMBOL_GPL(slim_msg_response);
+
+struct slim_cb_data {
+ struct completion *comp;
+ int ret;
+};
+
+static void slim_sync_default_cb(void *ctx, int err)
+{
+ struct slim_cb_data *cbd = ctx;
+
+ cbd->ret = err;
+ complete(cbd->comp);
+}
+
+int slim_processtxn(struct slim_controller *ctrl,
+ struct slim_msg_txn *txn)
+{
+ int ret, i = 0;
+ unsigned long flags;
+ u8 *buf;
+ bool async = false;
+ struct slim_cb_data cbd;
+ DECLARE_COMPLETION_ONSTACK(done);
+ bool need_tid = slim_tid_txn(txn->mt, txn->mc);
+
+ if (!txn->msg->comp_cb) {
+ txn->msg->comp_cb = slim_sync_default_cb;
+ cbd.comp = &done;
+ txn->msg->ctx = &cbd;
+ } else {
+ async = true;
+ }
+
+ buf = slim_get_tx(ctrl, txn, need_tid);
+ if (!buf)
+ return -ENOMEM;
+
+ if (need_tid) {
+ spin_lock_irqsave(&ctrl->txn_lock, flags);
+ for (i = 0; i < ctrl->last_tid; i++) {
+ if (ctrl->tid_tbl[i] == NULL)
+ break;
+ }
+ if (i >= ctrl->last_tid) {
+ if (ctrl->last_tid == (SLIM_MAX_TIDS - 1)) {
+ spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+ slim_return_tx(ctrl, -ENOMEM);
+ return -ENOMEM;
+ }
+ ctrl->last_tid++;
+ }
+ ctrl->tid_tbl[i] = txn->msg;
+ txn->tid = i;
+ spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+ }
+
+ ret = ctrl->xfer_msg(ctrl, txn, buf);
+
+ if (!ret && !async) { /* sync transaction */
+ /* Fine-tune calculation after bandwidth management */
+ unsigned long ms = txn->rl + 100;
+
+ ret = wait_for_completion_timeout(&done,
+ msecs_to_jiffies(ms));
+ if (!ret)
+ slim_return_tx(ctrl, -ETIMEDOUT);
+
+ ret = cbd.ret;
+ }
+
+ if (ret && need_tid) {
+ spin_lock_irqsave(&ctrl->txn_lock, flags);
+ /* Invalidate the transaction */
+ ctrl->tid_tbl[txn->tid] = NULL;
+ spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+ }
+ if (ret)
+ dev_err(&ctrl->dev, "Tx:MT:0x%x, MC:0x%x, LA:0x%x failed:%d\n",
+ txn->mt, txn->mc, txn->la, ret);
+ if (!async) {
+ txn->msg->comp_cb = NULL;
+ txn->msg->ctx = NULL;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(slim_processtxn);
+
+static int slim_val_inf_sanity(struct slim_controller *ctrl,
+ struct slim_val_inf *msg, u8 mc)
+{
+ if (!msg || msg->num_bytes > 16 ||
+ (msg->start_offset + msg->num_bytes) > 0xC00)
+ goto reterr;
+ switch (mc) {
+ case SLIM_MSG_MC_REQUEST_VALUE:
+ case SLIM_MSG_MC_REQUEST_INFORMATION:
+ if (msg->rbuf != NULL)
+ return 0;
+ break;
+ case SLIM_MSG_MC_CHANGE_VALUE:
+ case SLIM_MSG_MC_CLEAR_INFORMATION:
+ if (msg->wbuf != NULL)
+ return 0;
+ break;
+ case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
+ case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
+ if (msg->rbuf != NULL && msg->wbuf != NULL)
+ return 0;
+ break;
+ default:
+ break;
+ }
+reterr:
+ dev_err(&ctrl->dev, "Sanity check failed:msg:offset:0x%x, mc:%d\n",
+ msg->start_offset, mc);
+ 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];
+}
+
+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;
+ u16 sl, cur;
+
+ ret = slim_val_inf_sanity(ctrl, msg, mc);
+ if (ret)
+ return ret;
+
+ sl = slim_slicesize(msg->num_bytes);
+ dev_dbg(&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 (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 (slim_tid_txn(txn->mt, txn->mc))
+ txn->rl++;
+
+ return slim_processtxn(ctrl, txn);
+}
+EXPORT_SYMBOL_GPL(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 transmission of this message timed out (e.g. due to bus lines
+ * not being clocked or driven by controller)
+ * -ENOTCONN: If the transmitted message was not ACKed by destination device.
+ */
+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_GPL(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_GPL(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_GPL(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_GPL(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_GPL(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_GPL(slim_request_clear_inf_element);
+
+/* Functions to get/return TX, RX buffers for messaging. */
+
+void *slim_get_rx(struct slim_controller *ctrl)
+{
+ unsigned long flags;
+ int idx;
+
+ spin_lock_irqsave(&ctrl->rx.lock, flags);
+ if ((ctrl->rx.tail + 1) % ctrl->rx.n == ctrl->rx.head) {
+ spin_unlock_irqrestore(&ctrl->rx.lock, flags);
+ dev_err(&ctrl->dev, "RX QUEUE full!");
+ return NULL;
+ }
+ idx = ctrl->rx.tail;
+ ctrl->rx.tail = (ctrl->rx.tail + 1) % ctrl->rx.n;
+ spin_unlock_irqrestore(&ctrl->rx.lock, flags);
+ return ctrl->rx.base + (idx * ctrl->rx.sl_sz);
+}
+EXPORT_SYMBOL_GPL(slim_get_rx);
+
+int slim_return_rx(struct slim_controller *ctrl, void *buf)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctrl->rx.lock, flags);
+ if (ctrl->rx.tail == ctrl->rx.head) {
+ spin_unlock_irqrestore(&ctrl->rx.lock, flags);
+ return -ENODATA;
+ }
+ memcpy(buf, ctrl->rx.base + (ctrl->rx.head * ctrl->rx.sl_sz),
+ ctrl->rx.sl_sz);
+ ctrl->rx.head = (ctrl->rx.head + 1) % ctrl->rx.n;
+ spin_unlock_irqrestore(&ctrl->rx.lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(slim_return_rx);
+
+void slim_return_tx(struct slim_controller *ctrl, int err)
+{
+ unsigned long flags;
+ int idx;
+ struct slim_pending cur;
+
+ spin_lock_irqsave(&ctrl->tx.lock, flags);
+ idx = ctrl->tx.head;
+ ctrl->tx.head = (ctrl->tx.head + 1) % ctrl->tx.n;
+ cur = ctrl->pending_wr[idx];
+ spin_unlock_irqrestore(&ctrl->tx.lock, flags);
+ if (!cur.cb)
+ dev_err(&ctrl->dev, "NULL Transaction or completion");
+ else
+ cur.cb(cur.ctx, err);
+ up(&ctrl->tx_sem);
+}
+EXPORT_SYMBOL_GPL(slim_return_tx);
+
+void *slim_get_tx(struct slim_controller *ctrl, struct slim_msg_txn *txn,
+ bool need_tid)
+{
+ unsigned long flags;
+ int ret, idx;
+
+ ret = down_interruptible(&ctrl->tx_sem);
+ if (ret < 0) {
+ dev_err(&ctrl->dev, "TX semaphore down returned:%d", ret);
+ return NULL;
+ }
+ spin_lock_irqsave(&ctrl->tx.lock, flags);
+ if (((ctrl->tx.head + 1) % ctrl->tx.n) == ctrl->tx.tail) {
+ spin_unlock_irqrestore(&ctrl->tx.lock, flags);
+ dev_err(&ctrl->dev, "controller TX buf unavailable");
+ up(&ctrl->tx_sem);
+ return NULL;
+ }
+ idx = ctrl->tx.tail;
+ ctrl->tx.tail = (ctrl->tx.tail + 1) % ctrl->tx.n;
+ ctrl->pending_wr[idx].cb = txn->msg->comp_cb;
+ ctrl->pending_wr[idx].ctx = txn->msg->ctx;
+ ctrl->pending_wr[idx].need_tid = need_tid;
+ spin_unlock_irqrestore(&ctrl->tx.lock, flags);
+ return ctrl->tx.base + (idx * ctrl->tx.sl_sz);
+}
+EXPORT_SYMBOL_GPL(slim_get_tx);
diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h
index 2a78f79..3fd626c 100644
--- a/include/linux/slimbus.h
+++ b/include/linux/slimbus.h
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/mutex.h>
+#include <linux/semaphore.h>
#include <linux/mod_devicetable.h>
/**
@@ -34,6 +35,9 @@ extern struct bus_type slimbus_type;
#define SLIM_FRM_SLOTS_PER_SUPERFRAME 16
#define SLIM_GDE_SLOTS_PER_SUPERFRAME 2
+/* MAX in-flight transactions neededing transaction ID (8-bit, per spec) */
+#define SLIM_MAX_TIDS 256
+
struct slim_controller;
struct slim_device;
@@ -100,12 +104,115 @@ 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, int err);
+ 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;
+};
+
+/* Frequently used message transaction structures */
+#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, }
+
+#define DEFINE_SLIM_EDEST_TXN(name, mc, rl, la, msg) \
+ struct slim_msg_txn name = { rl, 0, mc, SLIM_MSG_DEST_ENUMADDR, 0,\
+ 0, la, msg, }
+
+/**
+ * struct slim_ctrl_buf: circular buffer used by contoller for TX, RX
+ * @base: virtual base address for this buffer
+ * @phy: physical address for this buffer (this is useful if controller can
+ * DMA the buffers for TX and RX to/from controller hardware
+ * @lock: lock protecting head and tail
+ * @head: index where buffer is returned back
+ * @tail: index from where buffer is consumed
+ * @sl_sz: byte-size of each slot in this buffer
+ * @n: number of elements in this circular ring, note that this needs to be
+ * 1 more than actual buffers to allow for one open slot
+ */
+struct slim_ctrl_buf {
+ void *base;
+ phys_addr_t phy;
+ spinlock_t lock;
+ int head;
+ int tail;
+ int sl_sz;
+ int n;
+};
+
+/**
+ * struct slim_pending: context of pending transfers
+ * @cb: callback for this transfer
+ * @ctx: contex for the callback function
+ * @need_tid: True if this transfer need Transaction ID
+ */
+struct slim_pending {
+ void (*cb)(void *ctx, int err);
+ void *ctx;
+ bool need_tid;
+};
+
+/**
* struct slim_controller: Controls every instance of SLIMbus
* (similar to 'master' on SPI)
* 'Manager device' is responsible for device management, bandwidth
@@ -139,6 +246,16 @@ struct slim_addrt {
* @addrt: Logical address table
* @num_dev: Number of active slimbus slaves on this bus
* @wq: Workqueue per controller used to notify devices when they report present
+ * @tid_tbl: Table of transactions having transaction ID
+ * @txn_lock: Lock to protect table of transactions
+ * @rx: RX buffers used by controller to receive messages. Ctrl may receive more
+ * than 1 message (e.g. multiple report-present messages or messages from
+ * multiple slaves).
+ * @tx: TX buffers used by controller to transmit messages. Ctrl may have
+ * ability to send/queue multiple messages to HW at once.
+ * @pending_wr: Pending write transactions to be acknowledged by controller
+ * @tx_sem: Semaphore for available TX buffers for this controller
+ * @last_tid: Last used entry for TID transactions
* @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
@@ -164,7 +281,16 @@ struct slim_controller {
struct slim_addrt *addrt;
u8 num_dev;
struct workqueue_struct *wq;
+ struct slim_val_inf *tid_tbl[SLIM_MAX_TIDS];
+ u8 last_tid;
+ spinlock_t txn_lock;
+ struct slim_ctrl_buf tx;
+ struct slim_ctrl_buf rx;
+ struct slim_pending *pending_wr;
+ struct semaphore tx_sem;
struct completion dev_released;
+ int (*xfer_msg)(struct slim_controller *ctrl,
+ struct slim_msg_txn *tx, void *buf);
int (*set_laddr)(struct slim_controller *ctrl,
struct slim_eaddr *ea, u8 laddr);
int (*get_laddr)(struct slim_controller *ctrl,
@@ -384,4 +510,102 @@ 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 transmission of this message timed out (e.g. due to bus lines
+ * not being clocked or driven by controller)
+ * -ENOTCONN: If the transmitted message was not ACKed by destination device.
+ */
+int slim_request_val_element(struct slim_device *sb,
+ struct slim_val_inf *msg);
+int slim_change_val_element(struct slim_device *sb,
+ struct slim_val_inf *msg);
+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
+ */
+
+int slim_request_inf_element(struct slim_device *sb,
+ struct slim_val_inf *msg);
+int slim_clear_inf_element(struct slim_device *sb,
+ struct slim_val_inf *msg);
+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
+ */
+void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid,
+ u8 len);
+
+/**
+ * slim_processtxn: Process a slimbus-messaging transaction
+ * @ctrl: Controller handle
+ * @txn: Transaction to be sent over SLIMbus
+ * Called by controller to transmit messaging transactions not dealing with
+ * Interface/Value elements. (e.g. transmittting a message to assign logical
+ * address to a slave device
+ * Returns:
+ * -ETIMEDOUT: If transmission of this message timed out (e.g. due to bus lines
+ * not being clocked or driven by controller)
+ * -ENOTCONN: If the transmitted message was not ACKed by destination device.
+ */
+int slim_processtxn(struct slim_controller *ctrl, struct slim_msg_txn *txn);
+
+/**
+ * Functions to get/put TX, RX buffers for messaging.
+ * @ctrl: Controller handle
+ * These functions are called by controller to process the TX/RX buffers.
+ * TX buffer is requested by controller when it's filled-in and sent to the
+ * HW. When HW has finished processing this buffer, controller should return it
+ * back to the pool.
+ * RX buffer is requested by controller when data is received from HW, but is
+ * not processed (e.g. 'report-present message was sent by HW in ISR and SW
+ * needs more time to process the buffer to assign Logical Address)
+ * RX buffer is returned back to the pool when associated RX action
+ * is taken (e.g. Received message is decoded and client's
+ * response buffer is filled in.)
+ */
+void *slim_get_rx(struct slim_controller *ctrl);
+
+int slim_return_rx(struct slim_controller *ctrl, void *buf);
+
+void *slim_get_tx(struct slim_controller *ctrl, struct slim_msg_txn *txn,
+ bool need_tid);
+
+void slim_return_tx(struct slim_controller *ctrl, int err);
+
+static inline bool slim_tid_txn(u8 mt, u8 mc)
+{
+ return (mt == SLIM_MSG_MT_CORE &&
+ (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));
+}
+/* end of message apis */
+
#endif /* _LINUX_SLIMBUS_H */
--
1.8.2.1
[toc] | [prev] | [next] | [standalone]
| From | Sagar Dharia <sdharia@codeaurora.org> |
|---|---|
| Date | 2016-02-06 19:50 +0100 |
| Subject | [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver |
| Message-ID | <qZg5I-2Ri-21@gated-at.bofh.it> |
| In reply to | #1328358 |
This controller driver programs manager, interface, and framer
devices for Qualcomm's slimbus HW block.
Manager component currently implements logical address setting,
and messaging interface.
Interface device reports bus synchronization information, and framer
device clocks the bus from the time it's woken up, until clock-pause
is executed by the manager device.
Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
---
.../devicetree/bindings/slimbus/slim-qcom-ctrl.txt | 43 ++
drivers/slimbus/Kconfig | 10 +
drivers/slimbus/Makefile | 1 +
drivers/slimbus/slim-qcom-ctrl.c | 556 +++++++++++++++++++++
drivers/slimbus/slim-qcom.h | 63 +++
5 files changed, 673 insertions(+)
create mode 100644 Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt
create mode 100644 drivers/slimbus/slim-qcom-ctrl.c
create mode 100644 drivers/slimbus/slim-qcom.h
diff --git a/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt b/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt
new file mode 100644
index 0000000..7b6c366
--- /dev/null
+++ b/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt
@@ -0,0 +1,43 @@
+Qualcomm SLIMBUS controller
+"qcom,slim-msm": This controller is used if applications processor
+ driver is controlling slimbus master component. This driver is
+ responsible for communicating with slave HW directly using
+ messaging interface, and doing data channel management.
+
+Required properties:
+
+- #address-cells - should be 4 (number of cells required to define
+ 4 fields of the enumeration address for the SLIMbus
+ slave device)
+- #size-cells - should be 0
+ - reg : Offset and length of the register region(s) for the device
+ - reg-names : Register region name(s) referenced in reg above
+ Required register resource entries are:
+ "slimbus_physical": Physical adderss of controller register blocks
+ - compatible : should be "qcom,slim-msm" if this is master component driver
+ - interrupts : Interrupt number used by this controller
+ - clocks : Interface and core clocks used by this slimbus controller
+ - clock-names : Required clock-name entries are:
+ "iface_clk" : Interface clock for this controller
+ "core_clk" : Interrupt for controller core's BAM
+
+Optional property:
+ - reg entry for slew rate : If slew rate control register is provided, this
+ entry should be used.
+ - reg-name for slew rate: "slimbus_slew_reg"
+
+Example:
+ slim@28080000 {
+ compatible = "qcom,slim-msm";
+ #address-cells = <4>;
+ #size-cells = <0>;
+ reg = <0x28080000 0x2000>, <0x80207C 4>;
+ reg-names = "slimbus_physical", "slimbus_slew_reg";
+ interrupts = <0 33 0>;
+ clocks = <&lcc SLIMBUS_SRC>, <&lcc AUDIO_SLIMBUS_CLK>;
+ clock-names = "iface_clk", "core_clk";
+
+ slimdev@0217.0060.01.00 {
+ reg = <0x217 0x60 0x1 0x0>;
+ };
+ };
diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig
index f0b118a..afe15b3 100644
--- a/drivers/slimbus/Kconfig
+++ b/drivers/slimbus/Kconfig
@@ -9,3 +9,13 @@ menuconfig SLIMBUS
If unsure, choose N.
+if SLIMBUS
+config SLIM_QCOM_CTRL
+ tristate "Qualcomm Slimbus Manager Component"
+ depends on SLIMBUS
+ default n
+ help
+ Select driver if Qualcomm's Slimbus Manager Component is
+ programmed using Linux kernel.
+
+endif
diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
index d95a0a2..574a892 100644
--- a/drivers/slimbus/Makefile
+++ b/drivers/slimbus/Makefile
@@ -2,3 +2,4 @@
# Makefile for kernel slimbus framework.
#
obj-$(CONFIG_SLIMBUS) += slim-core.o slim-messaging.o
+obj-$(CONFIG_SLIM_QCOM_CTRL) += slim-qcom-ctrl.o
diff --git a/drivers/slimbus/slim-qcom-ctrl.c b/drivers/slimbus/slim-qcom-ctrl.c
new file mode 100644
index 0000000..df20fb4
--- /dev/null
+++ b/drivers/slimbus/slim-qcom-ctrl.c
@@ -0,0 +1,556 @@
+/* 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/irq.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/slimbus.h>
+#include "slim-qcom.h"
+
+#define MSM_SLIM_NAME "msm_slim_ctrl"
+
+/* Manager registers */
+#define MGR_CFG 0x200
+#define MGR_STATUS 0x204
+#define MGR_INT_EN 0x210
+#define MGR_INT_STAT 0x214
+#define MGR_INT_CLR 0x218
+#define MGR_TX_MSG 0x230
+#define MGR_RX_MSG 0x270
+#define MGR_IE_STAT 0x2F0
+#define MGR_VE_STAT 0x300
+#define MGR_CFG_ENABLE 1
+
+/* Framer registers */
+#define FRM_CFG 0x400
+#define FRM_STAT 0x404
+#define FRM_INT_EN 0x410
+#define FRM_INT_STAT 0x414
+#define FRM_INT_CLR 0x418
+#define FRM_WAKEUP 0x41C
+#define FRM_CLKCTL_DONE 0x420
+#define FRM_IE_STAT 0x430
+#define FRM_VE_STAT 0x440
+
+/* Interface registers */
+#define INTF_CFG 0x600
+#define INTF_STAT 0x604
+#define INTF_INT_EN 0x610
+#define INTF_INT_STAT 0x614
+#define INTF_INT_CLR 0x618
+#define INTF_IE_STAT 0x630
+#define INTF_VE_STAT 0x640
+
+/* Interrupt status bits */
+#define MGR_INT_TX_NACKED_2 BIT(25)
+#define MGR_INT_MSG_BUF_CONTE BIT(26)
+#define MGR_INT_RX_MSG_RCVD BIT(30)
+#define MGR_INT_TX_MSG_SENT BIT(31)
+
+/* Framer config register settings */
+#define FRM_ACTIVE 1
+#define CLK_GEAR 7
+#define ROOT_FREQ 11
+#define REF_CLK_GEAR 15
+#define INTR_WAKE 19
+
+static int msm_slim_queue_tx(struct msm_slim_ctrl *dev, u32 *buf, u8 len,
+ u32 tx_reg)
+{
+ int i;
+
+ for (i = 0; i < (len + 3) >> 2; i++) {
+ dev_dbg(dev->dev, "AHB TX data:0x%x\n", buf[i]);
+ writel_relaxed(buf[i], dev->base + tx_reg + (i * 4));
+ }
+ /* Guarantee that message is sent before returning */
+ mb();
+ return 0;
+}
+
+static irqreturn_t msm_slim_interrupt(int irq, void *d)
+{
+ struct msm_slim_ctrl *dev = d;
+ u32 stat = readl_relaxed(dev->base + MGR_INT_STAT);
+ int err = 0;
+
+ if (stat & MGR_INT_TX_MSG_SENT || stat & MGR_INT_TX_NACKED_2) {
+ if (stat & MGR_INT_TX_MSG_SENT)
+ writel_relaxed(MGR_INT_TX_MSG_SENT,
+ dev->base + MGR_INT_CLR);
+ if (stat & MGR_INT_TX_NACKED_2) {
+ u32 mgr_stat = readl_relaxed(dev->base + MGR_STATUS);
+ u32 mgr_ie_stat = readl_relaxed(dev->base +
+ MGR_IE_STAT);
+ u32 frm_stat = readl_relaxed(dev->base + FRM_STAT);
+ u32 frm_cfg = readl_relaxed(dev->base + FRM_CFG);
+ u32 frm_intr_stat = readl_relaxed(dev->base +
+ FRM_INT_STAT);
+ u32 frm_ie_stat = readl_relaxed(dev->base +
+ FRM_IE_STAT);
+ u32 intf_stat = readl_relaxed(dev->base + INTF_STAT);
+ u32 intf_intr_stat = readl_relaxed(dev->base +
+ INTF_INT_STAT);
+ u32 intf_ie_stat = readl_relaxed(dev->base +
+ INTF_IE_STAT);
+
+ writel_relaxed(MGR_INT_TX_NACKED_2, dev->base +
+ MGR_INT_CLR);
+ dev_err(dev->dev, "TX Nack MGR:int:0x%x, stat:0x%x\n",
+ stat, mgr_stat);
+ dev_err(dev->dev, "TX Nack MGR:ie:0x%x\n", mgr_ie_stat);
+ dev_err(dev->dev, "TX Nack FRM:int:0x%x, stat:0x%x\n",
+ frm_intr_stat, frm_stat);
+ dev_err(dev->dev, "TX Nack FRM:cfg:0x%x, ie:0x%x\n",
+ frm_cfg, frm_ie_stat);
+ dev_err(dev->dev, "TX Nack INTF:intr:0x%x, stat:0x%x\n",
+ intf_intr_stat, intf_stat);
+ dev_err(dev->dev, "TX Nack INTF:ie:0x%x\n",
+ intf_ie_stat);
+ err = -ENOTCONN;
+ }
+ /**
+ * Guarantee that interrupt clear bit write goes through before
+ * signalling completion/exiting ISR
+ */
+ mb();
+ slim_return_tx(&dev->ctrl, err);
+ }
+ if (stat & MGR_INT_RX_MSG_RCVD) {
+ u8 mc, mt;
+ u8 len, i;
+ u32 *rx_buf, pkt[10];
+ bool q_rx = false;
+
+ pkt[0] = readl_relaxed(dev->base + MGR_RX_MSG);
+ mt = (pkt[0] >> 5) & 0x7;
+ mc = (pkt[0] >> 8) & 0xff;
+ len = pkt[0] & 0x1F;
+ dev_dbg(dev->dev, "RX-IRQ: MC: %x, MT: %x\n", mc, mt);
+
+ /**
+ * this message cannot be handled by ISR, so
+ * let work-queue handle it
+ */
+ if (mt == SLIM_MSG_MT_CORE &&
+ mc == SLIM_MSG_MC_REPORT_PRESENT)
+ rx_buf = (u32 *)slim_get_rx(&dev->ctrl);
+ else
+ rx_buf = pkt;
+
+ if (rx_buf == NULL) {
+ dev_err(dev->dev, "dropping RX:0x%x due to RX full\n",
+ pkt[0]);
+ goto rx_ret_irq;
+ }
+
+ rx_buf[0] = pkt[0];
+ for (i = 1; i < ((len + 3) >> 2); i++) {
+ rx_buf[i] = readl_relaxed(dev->base + MGR_RX_MSG +
+ (4 * i));
+ dev_dbg(dev->dev, "reading data: %x\n", rx_buf[i]);
+ }
+
+ switch (mc) {
+ u8 *buf, la;
+ u16 ele;
+
+ case SLIM_MSG_MC_REPORT_PRESENT:
+ q_rx = true;
+ break;
+ case SLIM_MSG_MC_REPLY_INFORMATION:
+ case SLIM_MSG_MC_REPLY_VALUE:
+ slim_msg_response(&dev->ctrl, (u8 *)(rx_buf + 1),
+ (u8)(*rx_buf >> 24), (len - 4));
+ break;
+ case SLIM_MSG_MC_REPORT_INFORMATION:
+ buf = (u8 *)rx_buf;
+ la = buf[2];
+ ele = (u16)buf[4] << 4;
+
+ ele |= ((buf[3] & 0xf0) >> 4);
+ /**
+ * report information is most likely loss of
+ * sync or collision detected in data slots
+ */
+ dev_err(dev->dev, "LA:%d report inf ele:0x%x\n",
+ la, ele);
+ for (i = 0; i < len - 5; i++)
+ dev_err(dev->dev, "bit-mask:%x\n",
+ buf[i+5]);
+ break;
+ default:
+ dev_err(dev->dev, "unsupported MC,%x MT:%x\n",
+ mc, mt);
+ break;
+ }
+rx_ret_irq:
+ writel_relaxed(MGR_INT_RX_MSG_RCVD, dev->base +
+ MGR_INT_CLR);
+ /**
+ * Guarantee that CLR bit write goes through
+ * before exiting
+ */
+ mb();
+ if (q_rx)
+ queue_work(dev->rxwq, &dev->wd);
+ }
+ /**
+ * All interrupts are handled: complex RX messages requiring more work
+ * are queued to work-queue, others are handled above
+ */
+ return IRQ_HANDLED;
+}
+
+static int msm_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn,
+ void *pbuf)
+{
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+ DECLARE_COMPLETION_ONSTACK(done);
+ u32 *head = (u32 *)pbuf;
+ u8 *puc = (u8 *)pbuf;
+ u8 la = txn->la;
+
+ /* HW expects length field to be excluded */
+ txn->rl--;
+
+ if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+ *head = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 0,
+ la);
+ else
+ *head = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 1,
+ la);
+
+ if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+ puc += 3;
+ else
+ puc += 2;
+ if (txn->mt == SLIM_MSG_MT_CORE && slim_tid_txn(txn->mt, txn->mc))
+ *(puc++) = txn->tid;
+ if ((txn->mt == SLIM_MSG_MT_CORE) &&
+ ((txn->mc >= SLIM_MSG_MC_REQUEST_INFORMATION &&
+ txn->mc <= SLIM_MSG_MC_REPORT_INFORMATION) ||
+ (txn->mc >= SLIM_MSG_MC_REQUEST_VALUE &&
+ txn->mc <= SLIM_MSG_MC_CHANGE_VALUE))) {
+ *(puc++) = (txn->ec & 0xFF);
+ *(puc++) = (txn->ec >> 8) & 0xFF;
+ }
+ if (txn->msg && txn->msg->wbuf)
+ memcpy(puc, txn->msg->wbuf, txn->msg->num_bytes);
+
+ return msm_slim_queue_tx(dev, head, txn->rl, MGR_TX_MSG);
+}
+
+static int msm_set_laddr(struct slim_controller *ctrl,
+ struct slim_eaddr *ead, u8 laddr)
+{
+ struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+ u8 buf[7];
+ int ret;
+ struct slim_val_inf msg = {0};
+
+ DEFINE_SLIM_EDEST_TXN(txn, SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS,
+ 10, laddr, &msg);
+
+ /* Enumeration address */
+ buf[0] = (u8)(ead->manf_id >> 8);
+ buf[1] = (u8)(ead->manf_id & 0xFF);
+ buf[2] = (u8) (ead->prod_code >> 8);
+ buf[3] = (u8) (ead->prod_code & 0xFF);
+ buf[4] = ead->dev_index;
+ buf[5] = ead->instance;
+ /* Logical address for this EA */
+ buf[6] = laddr;
+ /**
+ * Retries are needed since bus may lose sync when multiple devices
+ * are coming up and reporting present
+ */
+ msg.wbuf = buf;
+ msg.num_bytes = 7;
+
+ ret = slim_processtxn(&dev->ctrl, &txn);
+
+ if (ret)
+ dev_err(dev->dev, "set LA:0x%x failed:ret:%d\n",
+ laddr, ret);
+ return ret;
+}
+
+static void msm_slim_rxwq(struct work_struct *work)
+{
+ u8 buf[40];
+ u8 mc, mt, len;
+ int i, ret;
+ struct msm_slim_ctrl *dev = container_of(work, struct msm_slim_ctrl,
+ wd);
+
+ while ((slim_return_rx(&dev->ctrl, buf)) != -ENODATA) {
+ len = buf[0] & 0x1F;
+ mt = (buf[0] >> 5) & 0x7;
+ mc = buf[1];
+ if (mt == SLIM_MSG_MT_CORE &&
+ mc == SLIM_MSG_MC_REPORT_PRESENT) {
+ u8 laddr;
+ struct slim_eaddr ea;
+ u8 e_addr[6];
+
+ for (i = 0; i < 6; i++)
+ e_addr[i] = buf[7-i];
+
+ ea.manf_id = (u16)(e_addr[5] << 8) | e_addr[4];
+ ea.prod_code = (u16)(e_addr[3] << 8) | e_addr[2];
+ ea.dev_index = e_addr[1];
+ ea.instance = e_addr[0];
+ ret = slim_assign_laddr(&dev->ctrl, &ea, &laddr, false);
+ if (ret)
+ dev_err(dev->dev, "assign laddr failed:%d\n",
+ ret);
+ } else {
+ dev_err(dev->dev, "unexpected message:mc:%x, mt:%x\n",
+ mc, mt);
+
+ }
+
+ }
+}
+
+static void msm_slim_prg_slew(struct platform_device *pdev,
+ struct msm_slim_ctrl *dev)
+{
+ void __iomem *slew_reg;
+
+ /* SLEW RATE register for this slimbus */
+ dev->slew_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "slimbus_slew_reg");
+ if (!dev->slew_mem) {
+ dev_warn(&pdev->dev, "no slimbus slew resource\n");
+ return;
+ }
+
+ slew_reg = devm_ioremap(&pdev->dev, dev->slew_mem->start,
+ resource_size(dev->slew_mem));
+ if (!slew_reg) {
+ dev_err(dev->dev, "slew register mapping failed");
+ release_mem_region(dev->slew_mem->start,
+ resource_size(dev->slew_mem));
+ dev->slew_mem = NULL;
+ return;
+ }
+ writel_relaxed(1, slew_reg);
+ /* Make sure slimbus-slew rate enabling goes through */
+ wmb();
+}
+
+static int msm_slim_probe(struct platform_device *pdev)
+{
+ struct msm_slim_ctrl *dev;
+ struct resource *slim_mem;
+ struct resource *irq;
+ struct clk *hclk, *rclk;
+ int ret;
+
+ hclk = devm_clk_get(&pdev->dev, "iface_clk");
+ if (IS_ERR(hclk))
+ return PTR_ERR(hclk);
+
+ rclk = devm_clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(rclk)) {
+ /* unlikely that this is probe-defer */
+ dev_err(&pdev->dev, "rclk get failed:%ld\n", PTR_ERR(rclk));
+ return PTR_ERR(rclk);
+ }
+
+ ret = clk_set_rate(rclk, SLIM_ROOT_FREQ);
+ if (ret) {
+ dev_err(&pdev->dev, "ref-clock set-rate failed:%d\n", ret);
+ return ret;
+ }
+
+ slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "slimbus_physical");
+ if (!slim_mem) {
+ dev_err(&pdev->dev, "no slimbus physical memory resource\n");
+ return -ENODEV;
+ }
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "no slimbus IRQ resource\n");
+ return -ENODEV;
+ }
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->hclk = hclk;
+ dev->rclk = rclk;
+
+ dev->dev = &pdev->dev;
+ platform_set_drvdata(pdev, dev);
+ slim_set_ctrldata(&dev->ctrl, dev);
+ dev->base = devm_ioremap(dev->dev, slim_mem->start,
+ resource_size(slim_mem));
+ if (!dev->base) {
+ dev_err(&pdev->dev, "IOremap failed\n");
+ return -ENOMEM;
+ }
+
+ dev->ctrl.set_laddr = msm_set_laddr;
+ dev->ctrl.xfer_msg = msm_xfer_msg;
+ dev->ctrl.tx.n = MSM_TX_MSGS;
+ dev->ctrl.rx.n = MSM_RX_MSGS;
+ dev->ctrl.tx.sl_sz = SLIM_MSGQ_BUF_LEN;
+ dev->ctrl.rx.sl_sz = SLIM_MSGQ_BUF_LEN;
+
+ dev->irq = irq->start;
+
+ INIT_WORK(&dev->wd, msm_slim_rxwq);
+ dev->rxwq = create_singlethread_workqueue("msm_slim_rx");
+ if (!dev->rxwq) {
+ dev_err(dev->dev, "Failed to start Rx WQ\n");
+ return -ENOMEM;
+ }
+
+ dev->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
+ dev->framer.superfreq =
+ dev->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8;
+ dev->ctrl.a_framer = &dev->framer;
+ dev->ctrl.clkgear = SLIM_MAX_CLK_GEAR;
+ dev->ctrl.dev.parent = &pdev->dev;
+ dev->ctrl.dev.of_node = pdev->dev.of_node;
+
+ msm_slim_prg_slew(pdev, dev);
+
+ ret = devm_request_irq(&pdev->dev, dev->irq, msm_slim_interrupt,
+ IRQF_TRIGGER_HIGH, "msm_slim_irq", dev);
+ if (ret) {
+ dev_err(&pdev->dev, "request IRQ failed\n");
+ goto err_request_irq_failed;
+ }
+
+ ret = clk_prepare_enable(hclk);
+ if (ret)
+ goto err_hclk_enable_failed;
+
+ ret = clk_prepare_enable(rclk);
+ if (ret)
+ goto err_rclk_enable_failed;
+
+ /* Register with framework before enabling frame, clock */
+ ret = slim_register_controller(&dev->ctrl);
+ if (ret) {
+ dev_err(dev->dev, "error adding controller\n");
+ goto err_ctrl_failed;
+ }
+
+ dev->ver = readl_relaxed(dev->base);
+ /* Version info in 16 MSbits */
+ dev->ver >>= 16;
+ /* Component register initialization */
+ writel_relaxed(1, dev->base + CFG_PORT(COMP_CFG, dev->ver));
+ writel_relaxed((EE_MGR_RSC_GRP | EE_NGD_2 | EE_NGD_1),
+ dev->base + CFG_PORT(COMP_TRUST_CFG, dev->ver));
+
+ writel_relaxed((MGR_INT_TX_NACKED_2 |
+ MGR_INT_MSG_BUF_CONTE | MGR_INT_RX_MSG_RCVD |
+ MGR_INT_TX_MSG_SENT), dev->base + MGR_INT_EN);
+ writel_relaxed(1, dev->base + MGR_CFG);
+ /*
+ * Framer registers are beyond 1K memory region after Manager and/or
+ * component registers. Make sure those writes are ordered
+ * before framer register writes
+ */
+ wmb();
+
+ /* Framer register initialization */
+ writel_relaxed((1 << INTR_WAKE) | (0xA << REF_CLK_GEAR) |
+ (0xA << CLK_GEAR) | (1 << ROOT_FREQ) | (1 << FRM_ACTIVE) | 1,
+ dev->base + FRM_CFG);
+ /*
+ * Make sure that framer wake-up and enabling writes go through
+ * before any other component is enabled. Framer is responsible for
+ * clocking the bus and enabling framer first will ensure that other
+ * devices can report presence when they are enabled
+ */
+ mb();
+
+ writel_relaxed(MGR_CFG_ENABLE, dev->base + MGR_CFG);
+ /*
+ * Make sure that manager-enable is written through before interface
+ * device is enabled
+ */
+ mb();
+ writel_relaxed(1, dev->base + INTF_CFG);
+ /*
+ * Make sure that interface-enable is written through before enabling
+ * ported generic device inside MSM manager
+ */
+ mb();
+
+ writel_relaxed(1, dev->base + CFG_PORT(COMP_CFG, dev->ver));
+ /*
+ * Make sure that all writes have gone through before exiting this
+ * function
+ */
+ mb();
+
+ dev_dbg(dev->dev, "MSM SB controller is up:ver:0x%x!\n", dev->ver);
+ return 0;
+
+err_ctrl_failed:
+ clk_disable_unprepare(dev->rclk);
+err_rclk_enable_failed:
+ clk_disable_unprepare(dev->hclk);
+err_hclk_enable_failed:
+err_request_irq_failed:
+ destroy_workqueue(dev->rxwq);
+ return ret;
+}
+
+static int msm_slim_remove(struct platform_device *pdev)
+{
+ struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+
+ slim_del_controller(&dev->ctrl);
+ destroy_workqueue(dev->rxwq);
+ return 0;
+}
+
+static const struct of_device_id msm_slim_dt_match[] = {
+ {
+ .compatible = "qcom,slim-msm",
+ },
+ {}
+};
+
+static struct platform_driver msm_slim_driver = {
+ .probe = msm_slim_probe,
+ .remove = msm_slim_remove,
+ .driver = {
+ .name = MSM_SLIM_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = msm_slim_dt_match,
+ },
+};
+module_platform_driver(msm_slim_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+MODULE_DESCRIPTION("Qualcomm Slimbus controller");
+MODULE_ALIAS("platform:qcom-slim");
diff --git a/drivers/slimbus/slim-qcom.h b/drivers/slimbus/slim-qcom.h
new file mode 100644
index 0000000..0ad59c3
--- /dev/null
+++ b/drivers/slimbus/slim-qcom.h
@@ -0,0 +1,63 @@
+/* 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.
+ */
+
+#ifndef _SLIM_QCOM_H
+#define _SLIM_QCOM_H
+
+#include <linux/irq.h>
+#include <linux/workqueue.h>
+
+#define QC_MFGID_LSB 0x2
+#define QC_MFGID_MSB 0x17
+
+#define SLIM_MSG_ASM_FIRST_WORD(l, mt, mc, dt, ad) \
+ ((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16))
+
+#define SLIM_ROOT_FREQ 24576000
+
+/* MAX message size over control channel */
+#define SLIM_MSGQ_BUF_LEN 40
+#define MSM_TX_MSGS 2
+#define MSM_RX_MSGS 8
+
+#define CFG_PORT(r, v) ((v) ? CFG_PORT_V2(r) : CFG_PORT_V1(r))
+
+/* V2 Component registers */
+#define CFG_PORT_V2(r) ((r ## _V2))
+#define COMP_CFG_V2 4
+#define COMP_TRUST_CFG_V2 0x3000
+
+/* V1 Component registers */
+#define CFG_PORT_V1(r) ((r ## _V1))
+#define COMP_CFG_V1 0
+#define COMP_TRUST_CFG_V1 0x14
+
+/* Resource group info for manager, and non-ported generic device-components */
+#define EE_MGR_RSC_GRP (1 << 10)
+#define EE_NGD_2 (2 << 6)
+#define EE_NGD_1 0
+
+struct msm_slim_ctrl {
+ struct slim_controller ctrl;
+ struct slim_framer framer;
+ struct device *dev;
+ void __iomem *base;
+ struct resource *slew_mem;
+ int irq;
+ struct workqueue_struct *rxwq;
+ struct work_struct wd;
+ struct clk *rclk;
+ struct clk *hclk;
+ u32 ver;
+};
+
+#endif
--
1.8.2.1
[toc] | [prev] | [next] | [standalone]
| From | Rob Herring <robh@kernel.org> |
|---|---|
| Date | 2016-02-08 21:30 +0100 |
| Subject | Re: [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver |
| Message-ID | <r00BB-1PS-21@gated-at.bofh.it> |
| In reply to | #1328361 |
On Sat, Feb 06, 2016 at 11:44:23AM -0700, Sagar Dharia wrote: > This controller driver programs manager, interface, and framer > devices for Qualcomm's slimbus HW block. > Manager component currently implements logical address setting, > and messaging interface. > Interface device reports bus synchronization information, and framer > device clocks the bus from the time it's woken up, until clock-pause > is executed by the manager device. > > Signed-off-by: Sagar Dharia <sdharia@codeaurora.org> > --- > .../devicetree/bindings/slimbus/slim-qcom-ctrl.txt | 43 ++ > drivers/slimbus/Kconfig | 10 + > drivers/slimbus/Makefile | 1 + > drivers/slimbus/slim-qcom-ctrl.c | 556 +++++++++++++++++++++ > drivers/slimbus/slim-qcom.h | 63 +++ > 5 files changed, 673 insertions(+) > create mode 100644 Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt > create mode 100644 drivers/slimbus/slim-qcom-ctrl.c > create mode 100644 drivers/slimbus/slim-qcom.h > > diff --git a/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt b/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt > new file mode 100644 > index 0000000..7b6c366 > --- /dev/null > +++ b/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt > @@ -0,0 +1,43 @@ > +Qualcomm SLIMBUS controller > +"qcom,slim-msm": This controller is used if applications processor This should be more specific and include the soc name(s). > + driver is controlling slimbus master component. This driver is > + responsible for communicating with slave HW directly using > + messaging interface, and doing data channel management. > +
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web