linux-arm-msm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V4 0/6] Introduce framework for SLIMbus device drivers
@ 2016-02-06 18:44 Sagar Dharia
       [not found] ` <1454784265-5194-1-git-send-email-sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
                   ` (6 more replies)
  0 siblings, 7 replies; 17+ messages in thread
From: Sagar Dharia @ 2016-02-06 18:44 UTC (permalink / raw)
  To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, sdharia-sgV2jX0FEOL9JmXXK+q4OQ,
	treding-DDmLM1+adcrQT0dZR+AlfA, broonie-DgEjT+Ai2ygdnm+yROfE0A,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	oded.gabbay-5C7GfCeVMHo, jkosina-AlSwsSmVLrQ,
	sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA, joe-6d6DIl74uiNBDgjK7y7TUQ,
	davem-fT/PcQaiUtIeIZ0/mPfg9Q, james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	daniel.thompson-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: kheitke-hxvC4TZJLZFWk0Htik3J/w, mlocke-sgV2jX0FEOL9JmXXK+q4OQ,
	agross-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA

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

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH V4 1/6] SLIMbus: Device management on SLIMbus
       [not found] ` <1454784265-5194-1-git-send-email-sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2016-02-06 18:44   ` Sagar Dharia
       [not found]     ` <1454784265-5194-2-git-send-email-sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  0 siblings, 1 reply; 17+ messages in thread
From: Sagar Dharia @ 2016-02-06 18:44 UTC (permalink / raw)
  To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, sdharia-sgV2jX0FEOL9JmXXK+q4OQ,
	treding-DDmLM1+adcrQT0dZR+AlfA, broonie-DgEjT+Ai2ygdnm+yROfE0A,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	oded.gabbay-5C7GfCeVMHo, jkosina-AlSwsSmVLrQ,
	sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA, joe-6d6DIl74uiNBDgjK7y7TUQ,
	davem-fT/PcQaiUtIeIZ0/mPfg9Q, james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	daniel.thompson-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: kheitke-hxvC4TZJLZFWk0Htik3J/w, mlocke-sgV2jX0FEOL9JmXXK+q4OQ,
	agross-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA

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.
The framework supports multiple instances of the bus (1 controller per
bus), and multiple slave devices per controller.

This patch does device enumeration, logical address assignment,
informing device when the device reports present/absent etc.
Reporting present may need the driver to do the needful (e.g. turning
on voltage regulators powering the device). So probe is called
if the device is added to board-info list for a controller.
Additionally device is probed when it reports present if that device
doesn't need any such steps mentioned above.

Signed-off-by: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
 Documentation/slimbus/summary   | 109 ++++++
 drivers/Kconfig                 |   2 +
 drivers/Makefile                |   1 +
 drivers/slimbus/Kconfig         |  11 +
 drivers/slimbus/Makefile        |   4 +
 drivers/slimbus/slim-core.c     | 720 ++++++++++++++++++++++++++++++++++++++++
 include/linux/mod_devicetable.h |  13 +
 include/linux/slimbus.h         | 387 +++++++++++++++++++++
 8 files changed, 1247 insertions(+)
 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 include/linux/slimbus.h

diff --git a/Documentation/slimbus/summary b/Documentation/slimbus/summary
new file mode 100644
index 0000000..73d86e9
--- /dev/null
+++ b/Documentation/slimbus/summary
@@ -0,0 +1,109 @@
+Overview of Linux kernel SLIMbus support
+========================================
+
+What is SLIMbus?
+----------------
+SLIMbus (Serial Low Power Interchip Media Bus) is a specification developed by
+MIPI (Mobile Industry Processor Interface) alliance. The bus uses master/slave
+configuration, and is a 2-wire multi-drop implementation (clock, and data).
+
+Currently, SLIMbus is used to interface between application processors of SoCs
+(System-on-Chip) and peripheral components (typically codec).SLIMbus uses
+Time-Division-Multiplexing to accommodate multiple data channels, and
+a control channel.
+
+The control channel is used for various control functions such as bus
+management, configuration and status updates.These messages can be unicast (e.g.
+reading/writing device specific values), or multicast (e.g. data channel
+reconfiguration sequence is a broadcast message announced to all devices)
+
+A data channel is used for data-transfer between 2 Slimbus devices. Data
+channel uses dedicated ports on the device.
+
+Hardware description:
+---------------------
+Slimbus specification has different types of device classifications based on
+their capabilities.
+A manager device is responsible for enumeration, configuration, and dynamic
+channel allocation. Every bus has 1 active manager.
+
+A generic device is a device providing application functionality (e.g. codec).
+
+Framer device is responsible for clocking the bus, and transmitting frame-sync
+and framing information on the bus.
+
+Each SLIMbus component has an interface device for monitoring physical layer.
+
+Typically each SoC contains SLIMbus component having 1 manager, 1 framer device,
+1 generic device (for data channel support), and 1 interface device.
+External peripheral SLIMbus component usually has 1 generic device (for
+functionality/data channel support), and an associated interface device.
+The generic device's registers are mapped as 'value elements' so that they can
+be written/read using Slimbus control channel exchanging control/status type of
+information.
+In case there are multiple framer devices on the same bus, manager device is
+responsible to select the active-framer for clocking the bus.
+
+Per specification, Slimbus uses "clock gears" to do power management based on
+current frequency and bandwidth requirements. There are 10 clock gears and each
+gear changes the Slimbus frequency to be twice its previous gear.
+
+Each device has a 6-byte enumeration-address and the manager assigns every
+device with a 1-byte logical address after the devices report presence on the
+bus.
+
+Software description:
+---------------------
+There are 2 types of SLIMbus drivers:
+
+slim_controller represents a 'controller' for SLIMbus. This driver should
+implement duties needed by the SoC (manager device, associated
+interface device for monitoring the layers and reporting errors, default
+framer device).
+
+slim_device represents the 'generic device/component' for SLIMbus, and a
+slim_driver should implement driver for that slim_device.
+
+Device notifications to the driver:
+-----------------------------------
+Since SLIMbus devices have mechanisms for reporting their presence, the
+framework allows drivers to bind when corresponding devices report their
+presence on the bus.
+However, it is possible that the driver needs to be probed
+first so that it can enable corresponding SLIMbus devie (e.g. power it up and/or
+take it out of reset). To support that behavior, the framework allows drivers
+to probe first as well  (e.g. using standard DeviceTree compatbility field).
+This creates the necessity for the driver to know when the device is functional
+(i.e. reported present). device_up callback is used for that reason when the
+device reports present and is assigned a logical address by the contoller.
+
+Similarly, SLIMbus devices 'report absent' when they go down. A 'device_down'
+callback notifies the driver when the device reports absent and its logical
+address assignment is invalidated by the controller.
+
+Another notification "boot_device" is used to notify the slim_driver when
+controller resets the bus. This notification allows the driver to take necessary
+steps to boot the device so that it's functional after the bus has been reset.
+
+Clock-pause:
+------------
+SLIMbus mandates that a reconfiguration sequence (known as clock-pause) be
+broadcast to all active devices on the bus before the bus can enter low-power
+mode. Controller uses this sequence when it decides to enter low-power mode so
+that corresponding clocks and/or power-rails can be turned off to save power.
+Clock-pause is exited by waking up framer device (if controller driver initiates
+exiting low power mode), or by toggling the data line (if a slave device wants
+to initiate it).
+
+Messaging APIs:
+---------------
+The framework supports APIs to exchange control-information with a SLIMbus
+device. APIs can be synchronous or asynchronous.
+From controller's perspective, multiple buffers can be queued to/from
+hardware for sending/receiving data using slim_ctrl_buf circular buffer.
+The header file <linux/slimbus.h> has more documentation about messaging APIs.
+
+-----------------------------------------------------------------
+<Sections will be added to this document when port/channel bandwidth management
+support, multi-xfer APIs are added to the framework>
+------------------------------------------------------------------
diff --git a/drivers/Kconfig b/drivers/Kconfig
index c0cc96b..e39c969 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -182,4 +182,6 @@ source "drivers/thunderbolt/Kconfig"
 
 source "drivers/android/Kconfig"
 
+source "drivers/slimbus/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 46d2554..37c1c88 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_TARGET_CORE)	+= target/
 obj-$(CONFIG_MTD)		+= mtd/
 obj-$(CONFIG_SPI)		+= spi/
 obj-$(CONFIG_SPMI)		+= spmi/
+obj-$(CONFIG_SLIMBUS)		+= slimbus/
 obj-y				+= hsi/
 obj-y				+= net/
 obj-$(CONFIG_ATM)		+= atm/
diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig
new file mode 100644
index 0000000..f0b118a
--- /dev/null
+++ b/drivers/slimbus/Kconfig
@@ -0,0 +1,11 @@
+#
+# SLIMBUS driver configuration
+#
+menuconfig SLIMBUS
+	tristate "Slimbus support"
+	help
+	  Slimbus is standard interface between System-on-Chip and audio codec,
+	  and other peripheral components in typical embedded systems.
+
+	  If unsure, choose N.
+
diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
new file mode 100644
index 0000000..aa9c147
--- /dev/null
+++ b/drivers/slimbus/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for kernel slimbus framework.
+#
+obj-$(CONFIG_SLIMBUS)			+= slim-core.o
diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
new file mode 100644
index 0000000..5b9fefa
--- /dev/null
+++ b/drivers/slimbus/slim-core.c
@@ -0,0 +1,720 @@
+/* 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/idr.h>
+#include <linux/pm_runtime.h>
+#include <linux/slimbus.h>
+
+static DEFINE_MUTEX(slim_lock);
+static DEFINE_IDR(ctrl_idr);
+static struct device_type slim_dev_type;
+static struct device_type slim_ctrl_type;
+
+static bool slim_eaddr_equal(struct slim_eaddr *a, struct slim_eaddr *b)
+{
+	return (a->manf_id == b->manf_id &&
+		a->prod_code == b->prod_code &&
+		a->dev_index == b->dev_index &&
+		a->instance == b->instance);
+}
+
+static const struct slim_device_id *
+slim_match(const struct slim_device_id *id, const struct slim_device *slim_dev)
+{
+	while (id->manf_id != 0 || id->prod_code != 0) {
+		if (id->manf_id == slim_dev->e_addr.manf_id &&
+		    id->prod_code == slim_dev->e_addr.prod_code &&
+		    id->dev_index == slim_dev->e_addr.dev_index)
+			return id;
+		id++;
+	}
+	return NULL;
+}
+
+static int slim_device_match(struct device *dev, struct device_driver *driver)
+{
+	struct slim_device *slim_dev;
+	struct slim_driver *drv = to_slim_driver(driver);
+
+	if (dev->type != &slim_dev_type)
+		return 0;
+
+	slim_dev = to_slim_device(dev);
+	if (drv->id_table)
+		return slim_match(drv->id_table, slim_dev) != NULL;
+	return 0;
+}
+
+struct sb_report_wd {
+	struct work_struct wd;
+	struct slim_device *sb;
+	bool report;
+};
+
+static void slim_report(struct work_struct *work)
+{
+	struct slim_driver *sbdrv;
+	struct sb_report_wd *sbw = container_of(work, struct sb_report_wd, wd);
+	struct slim_device *sbdev = sbw->sb;
+
+	mutex_lock(&sbdev->report_lock);
+	if (!sbdev->dev.driver)
+		goto report_exit;
+	/* check if device-up or down needs to be called */
+	if ((!sbdev->reported && !sbdev->notified) ||
+	    (sbdev->reported && sbdev->notified))
+		goto report_exit;
+
+	sbdrv = to_slim_driver(sbdev->dev.driver);
+	/**
+	 * address no longer valid, means device reported absent, whereas
+	 * address valid, means device reported present
+	 */
+	if (sbdev->notified && !sbdev->reported) {
+		sbdev->notified = false;
+		if (sbdrv->device_down)
+			sbdrv->device_down(sbdev);
+	} else if (!sbdev->notified && sbdev->reported) {
+		sbdev->notified = true;
+		if (sbdrv->device_up)
+			sbdrv->device_up(sbdev);
+	}
+report_exit:
+	mutex_unlock(&sbdev->report_lock);
+	kfree(sbw);
+}
+
+static void schedule_slim_report(struct slim_controller *ctrl,
+				 struct slim_device *sb, bool report)
+{
+	struct sb_report_wd *sbw;
+
+	dev_dbg(&ctrl->dev, "report:%d for slave:%s\n", report, sb->name);
+
+	sbw = kmalloc(sizeof(*sbw), GFP_KERNEL);
+	if (!sbw)
+		return;
+
+	INIT_WORK(&sbw->wd, slim_report);
+	sbw->sb = sb;
+	sbw->report = report;
+	if (!queue_work(ctrl->wq, &sbw->wd)) {
+		dev_err(&ctrl->dev, "failed to queue report:%d slave:%s\n",
+				    report, sb->name);
+		kfree(sbw);
+	}
+}
+
+static int slim_device_probe(struct device *dev)
+{
+	struct slim_device	*slim_dev;
+	struct slim_driver	*driver;
+	struct slim_controller	*ctrl;
+	int status = 0;
+
+	if (dev->type != &slim_dev_type)
+		return -ENXIO;
+
+	slim_dev = to_slim_device(dev);
+	driver = to_slim_driver(dev->driver);
+	if (!driver->id_table)
+		return -ENODEV;
+
+	slim_dev->driver = driver;
+
+	if (driver->probe)
+		status = driver->probe(slim_dev);
+	if (status) {
+		slim_dev->driver = NULL;
+	} else if (driver->device_up) {
+		ctrl = slim_dev->ctrl;
+		schedule_slim_report(ctrl, slim_dev, true);
+	}
+	return status;
+}
+
+static int slim_device_remove(struct device *dev)
+{
+	struct slim_device *slim_dev;
+	struct slim_driver *driver;
+	int status = 0;
+
+	if (dev->type != &slim_dev_type)
+		return -ENXIO;
+
+	slim_dev = to_slim_device(dev);
+	if (!dev->driver)
+		return 0;
+
+	driver = to_slim_driver(dev->driver);
+	if (driver->remove)
+		status = driver->remove(slim_dev);
+
+	mutex_lock(&slim_dev->report_lock);
+	slim_dev->notified = false;
+	if (status == 0)
+		slim_dev->driver = NULL;
+	mutex_unlock(&slim_dev->report_lock);
+	return status;
+}
+
+struct bus_type slimbus_type = {
+	.name		= "slimbus",
+	.match		= slim_device_match,
+	.probe		= slim_device_probe,
+	.remove		= slim_device_remove,
+};
+EXPORT_SYMBOL_GPL(slimbus_type);
+
+/**
+ * slim_driver_register: Client driver registration with slimbus
+ * @drv:Client driver to be associated with client-device.
+ * This API will register the client driver with the slimbus
+ * It is called from the driver's module-init function.
+ */
+int slim_driver_register(struct slim_driver *drv)
+{
+	drv->driver.bus = &slimbus_type;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(slim_driver_register);
+
+/**
+ * slim_driver_unregister: Undo effect of slim_driver_register
+ * @drv: Client driver to be unregistered
+ */
+void slim_driver_unregister(struct slim_driver *drv)
+{
+	if (drv)
+		driver_unregister(&drv->driver);
+}
+
+static void slim_ctrl_release(struct device *dev)
+{
+	struct slim_controller *ctrl = to_slim_controller(dev);
+
+	complete(&ctrl->dev_released);
+}
+
+static struct device_type slim_ctrl_type = {
+	.release	= slim_ctrl_release,
+};
+
+static struct slim_controller *slim_ctrl_get(struct slim_controller *ctrl)
+{
+	if (!ctrl || !get_device(&ctrl->dev))
+		return NULL;
+
+	return ctrl;
+}
+
+static void slim_ctrl_put(struct slim_controller *ctrl)
+{
+	if (ctrl)
+		put_device(&ctrl->dev);
+}
+
+static void slim_dev_release(struct device *dev)
+{
+	struct slim_device *sbdev = to_slim_device(dev);
+
+	slim_ctrl_put(sbdev->ctrl);
+}
+
+static struct device_type slim_dev_type = {
+	.release	= slim_dev_release,
+};
+
+/**
+ * slim_add_device: Add a new device without register board info.
+ * @ctrl: Controller to which this device is to be added to.
+ * Called when device doesn't have an explicit client-driver to be probed, or
+ * the client-driver is a module installed dynamically.
+ */
+int slim_add_device(struct slim_controller *ctrl, struct slim_device *sbdev)
+{
+	sbdev->dev.bus = &slimbus_type;
+	sbdev->dev.parent = &ctrl->dev;
+	sbdev->dev.type = &slim_dev_type;
+	sbdev->dev.driver = NULL;
+	sbdev->ctrl = ctrl;
+	slim_ctrl_get(ctrl);
+	if (!sbdev->name) {
+		sbdev->name = kasprintf(GFP_KERNEL, "0x%x:0x%x:0x%x:0x%x",
+					sbdev->e_addr.manf_id,
+					sbdev->e_addr.prod_code,
+					sbdev->e_addr.dev_index,
+					sbdev->e_addr.instance);
+		if (!sbdev->name)
+			return -ENOMEM;
+	}
+	dev_set_name(&sbdev->dev, "%s", sbdev->name);
+	mutex_init(&sbdev->report_lock);
+	/* probe slave on this controller */
+	return device_register(&sbdev->dev);
+}
+EXPORT_SYMBOL_GPL(slim_add_device);
+
+struct sbi_boardinfo {
+	struct list_head	list;
+	struct slim_boardinfo	board_info;
+};
+
+static LIST_HEAD(board_list);
+static LIST_HEAD(slim_ctrl_list);
+static DEFINE_MUTEX(board_lock);
+
+/* 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)
+{
+	int ret;
+
+	if (ctrl->nr != bi->bus_num)
+		return;
+
+	ret = slim_add_device(ctrl, bi->slim_slave);
+	if (ret != 0)
+		dev_err(ctrl->dev.parent, "can't create new device %s, ret:%d\n",
+			bi->slim_slave->name, ret);
+}
+
+/**
+ * slim_register_board_info: Board-initialization routine.
+ * @info: List of all devices on all controllers present on the board.
+ * @n: number of entries.
+ * API enumerates respective devices on corresponding controller.
+ * Called from board-init function.
+ */
+int slim_register_board_info(struct slim_boardinfo const *info, unsigned n)
+{
+	struct sbi_boardinfo *bi;
+	int i;
+
+	bi = kcalloc(n, sizeof(*bi), GFP_KERNEL);
+	if (!bi)
+		return -ENOMEM;
+
+	for (i = 0; i < n; i++, bi++, info++) {
+		struct slim_controller *ctrl;
+
+		memcpy(&bi->board_info, info, sizeof(*info));
+		mutex_lock(&board_lock);
+		list_add_tail(&bi->list, &board_list);
+		list_for_each_entry(ctrl, &slim_ctrl_list, list)
+			slim_match_ctrl_to_boardinfo(ctrl, &bi->board_info);
+		mutex_unlock(&board_lock);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(slim_register_board_info);
+
+static void slim_ctrl_add_boarddevs(struct slim_controller *ctrl)
+{
+	struct sbi_boardinfo *bi;
+
+	mutex_lock(&board_lock);
+	list_add_tail(&ctrl->list, &slim_ctrl_list);
+	list_for_each_entry(bi, &board_list, list)
+		slim_match_ctrl_to_boardinfo(ctrl, &bi->board_info);
+	mutex_unlock(&board_lock);
+}
+
+/**
+ * slim_register_controller: Controller bring-up and registration.
+ * @ctrl: Controller to be registered.
+ * A controller is registered with the framework using this API.
+ * If devices on a controller were registered before controller,
+ * this will make sure that they get probed when controller is up
+ */
+int slim_register_controller(struct slim_controller *ctrl)
+{
+	int id, ret = 0;
+
+	mutex_lock(&slim_lock);
+	id = idr_alloc(&ctrl_idr, ctrl, ctrl->nr, -1, GFP_KERNEL);
+	mutex_unlock(&slim_lock);
+
+	if (id < 0)
+		return id;
+
+	ctrl->nr = id;
+
+	dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr);
+	ctrl->dev.bus = &slimbus_type;
+	ctrl->dev.type = &slim_ctrl_type;
+	ctrl->num_dev = 0;
+	if (!ctrl->min_cg)
+		ctrl->min_cg = SLIM_MIN_CLK_GEAR;
+	if (!ctrl->max_cg)
+		ctrl->max_cg = SLIM_MAX_CLK_GEAR;
+	mutex_init(&ctrl->m_ctrl);
+	ret = device_register(&ctrl->dev);
+	if (ret)
+		goto dev_reg_failed;
+
+	dev_dbg(&ctrl->dev, "Bus [%s] registered:dev:%p\n",
+		ctrl->name, &ctrl->dev);
+
+	ctrl->wq = create_singlethread_workqueue(dev_name(&ctrl->dev));
+	if (!ctrl->wq)
+		goto err_workq_failed;
+
+	slim_ctrl_add_boarddevs(ctrl);
+	return 0;
+
+err_workq_failed:
+	device_unregister(&ctrl->dev);
+dev_reg_failed:
+	mutex_lock(&slim_lock);
+	idr_remove(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&slim_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_register_controller);
+
+/* slim_remove_device: Remove the effect of slim_add_device() */
+void slim_remove_device(struct slim_device *sbdev)
+{
+	struct slim_controller *ctrl = sbdev->ctrl;
+
+	mutex_lock(&ctrl->m_ctrl);
+	list_del_init(&sbdev->dev_list);
+	mutex_unlock(&ctrl->m_ctrl);
+	device_unregister(&sbdev->dev);
+}
+EXPORT_SYMBOL_GPL(slim_remove_device);
+
+static void slim_ctrl_remove_device(struct slim_controller *ctrl,
+				    struct slim_boardinfo *bi)
+{
+	if (ctrl->nr == bi->bus_num)
+		slim_remove_device(bi->slim_slave);
+}
+
+/**
+ * slim_del_controller: Controller tear-down.
+ * @ctrl: Controller to tear-down.
+ */
+int slim_del_controller(struct slim_controller *ctrl)
+{
+	struct slim_controller *found;
+	struct sbi_boardinfo *bi;
+
+	/* First make sure that this bus was added */
+	mutex_lock(&slim_lock);
+	found = idr_find(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&slim_lock);
+	if (found != ctrl)
+		return -EINVAL;
+
+	/* Remove all clients */
+	mutex_lock(&board_lock);
+	list_for_each_entry(bi, &board_list, list)
+		slim_ctrl_remove_device(ctrl, &bi->board_info);
+	mutex_unlock(&board_lock);
+
+	init_completion(&ctrl->dev_released);
+	device_unregister(&ctrl->dev);
+
+	wait_for_completion(&ctrl->dev_released);
+	list_del(&ctrl->list);
+	destroy_workqueue(ctrl->wq);
+	/* free bus id */
+	mutex_lock(&slim_lock);
+	idr_remove(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&slim_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(slim_del_controller);
+
+/**
+ * slim_report_absent: Controller calls this function when a device
+ *	reports absent, OR when the device cannot be communicated with
+ * @sbdev: Device that cannot be reached, or sent report absent
+ */
+void slim_report_absent(struct slim_device *sbdev)
+{
+	struct slim_controller *ctrl;
+	int i;
+
+	if (!sbdev)
+		return;
+	ctrl = sbdev->ctrl;
+	if (!ctrl)
+		return;
+	/* invalidate logical addresses */
+	mutex_lock(&ctrl->m_ctrl);
+	for (i = 0; i < ctrl->num_dev; i++) {
+		if (sbdev->laddr == ctrl->addrt[i].laddr)
+			ctrl->addrt[i].valid = false;
+	}
+	mutex_unlock(&ctrl->m_ctrl);
+	mutex_lock(&sbdev->report_lock);
+	sbdev->reported = false;
+	schedule_slim_report(ctrl, sbdev, false);
+	mutex_unlock(&sbdev->report_lock);
+}
+EXPORT_SYMBOL_GPL(slim_report_absent);
+
+static int slim_boot_child(struct device *dev, void *unused)
+{
+	struct slim_driver *sbdrv;
+	struct slim_device *sbdev = to_slim_device(dev);
+
+	if (sbdev && sbdev->dev.driver) {
+		sbdrv = to_slim_driver(sbdev->dev.driver);
+		if (sbdrv->boot_device)
+			sbdrv->boot_device(sbdev);
+	}
+	return 0;
+}
+
+static int slim_match_dev(struct device *dev, void *data)
+{
+	struct slim_eaddr *e_addr = data;
+	struct slim_device *slim = to_slim_device(dev);
+
+	return slim_eaddr_equal(&slim->e_addr, e_addr);
+}
+/**
+ * slim_framer_booted: This function is called by controller after the active
+ * framer has booted (using Bus Reset sequence, or after it has shutdown and has
+ * come back up).
+ * @ctrl: Controller associated with this framer
+ * Components, devices on the bus may be in undefined state,
+ * and this function triggers their drivers to do the needful
+ * to bring them back in Reset state so that they can acquire sync, report
+ * present and be operational again.
+ */
+void slim_framer_booted(struct slim_controller *ctrl)
+{
+	if (!ctrl)
+		return;
+
+	device_for_each_child(&ctrl->dev, NULL, slim_boot_child);
+}
+EXPORT_SYMBOL_GPL(slim_framer_booted);
+
+/**
+ * slim_query_device: Query and get handle to a device.
+ * @ctrl: Controller on which this device will be added/queried
+ * @e_addr: Enumeration address of the device to be queried
+ * Returns pointer to a device if it has already reported. Creates a new
+ * device and returns pointer to it if the device has not yet enumerated.
+ */
+struct slim_device *slim_query_device(struct slim_controller *ctrl,
+				      struct slim_eaddr *e_addr)
+{
+	struct device *dev;
+	struct slim_device *slim = NULL;
+	struct sbi_boardinfo *bi;
+
+	/* Check if the device is already present */
+	mutex_lock(&board_lock);
+	list_for_each_entry(bi, &board_list, list) {
+		if (bi->board_info.bus_num != ctrl->nr)
+			continue;
+		if (slim_eaddr_equal(&bi->board_info.slim_slave->e_addr,
+				     e_addr)) {
+			slim = bi->board_info.slim_slave;
+			break;
+		}
+	}
+	mutex_unlock(&board_lock);
+	if (slim)
+		return slim;
+
+	dev = device_find_child(&ctrl->dev, e_addr, slim_match_dev);
+	if (dev) {
+		slim = to_slim_device(dev);
+		return slim;
+	}
+
+	slim = kzalloc(sizeof(struct slim_device), GFP_KERNEL);
+	if (IS_ERR(slim))
+		return NULL;
+	slim->e_addr = *e_addr;
+	if (slim_add_device(ctrl, slim) != 0) {
+		kfree(slim);
+		return NULL;
+	}
+	return slim;
+}
+EXPORT_SYMBOL_GPL(slim_query_device);
+
+static int ctrl_getaddr_entry(struct slim_controller *ctrl,
+			      struct slim_eaddr *eaddr, u8 *entry)
+{
+	int i;
+
+	for (i = 0; i < ctrl->num_dev; i++) {
+		if (ctrl->addrt[i].valid &&
+		    slim_eaddr_equal(&ctrl->addrt[i].eaddr, eaddr)) {
+			*entry = i;
+			return 0;
+		}
+	}
+	return -ENXIO;
+}
+
+/**
+ * slim_assign_laddr: Assign logical address to a device enumerated.
+ * @ctrl: Controller with which device is enumerated.
+ * @e_addr: Enumeration address of the device.
+ * @laddr: Return logical address (if valid flag is false)
+ * @valid: true if laddr holds a valid address that controller wants to
+ *	set for this enumeration address. Otherwise framework sets index into
+ *	address table as logical address.
+ * Called by controller in response to REPORT_PRESENT. Framework will assign
+ * a logical address to this enumeration address.
+ * Function returns -EXFULL to indicate that all logical addresses are already
+ * taken.
+ */
+int slim_assign_laddr(struct slim_controller *ctrl, struct slim_eaddr *e_addr,
+		      u8 *laddr, bool valid)
+{
+	int ret;
+	u8 i = 0;
+	bool exists = false;
+	struct slim_device *slim;
+	struct slim_addrt *temp;
+
+	mutex_lock(&ctrl->m_ctrl);
+	/* already assigned */
+	if (ctrl_getaddr_entry(ctrl, e_addr, &i) == 0) {
+		*laddr = ctrl->addrt[i].laddr;
+		exists = true;
+	} else {
+		if (ctrl->num_dev >= (SLIM_LA_MANAGER - 1)) {
+			ret = -EXFULL;
+			goto ret_assigned_laddr;
+		}
+		for (i = 0; i < ctrl->num_dev; i++) {
+			if (ctrl->addrt[i].valid == false)
+				break;
+		}
+		if (i == ctrl->num_dev) {
+			temp = krealloc(ctrl->addrt,
+					(ctrl->num_dev + 1) *
+					sizeof(struct slim_addrt),
+					GFP_KERNEL);
+			if (!temp) {
+				ret = -ENOMEM;
+				goto ret_assigned_laddr;
+			}
+			ctrl->addrt = temp;
+			ctrl->num_dev++;
+		}
+		ctrl->addrt[i].eaddr = *e_addr;
+		ctrl->addrt[i].valid = true;
+		/* Preferred address is index into table */
+		if (!valid)
+			*laddr = i;
+	}
+
+	ret = ctrl->set_laddr(ctrl, &ctrl->addrt[i].eaddr, *laddr);
+	if (ret) {
+		ctrl->addrt[i].valid = false;
+		goto ret_assigned_laddr;
+	}
+	ctrl->addrt[i].laddr = *laddr;
+
+ret_assigned_laddr:
+	mutex_unlock(&ctrl->m_ctrl);
+	if (exists || ret)
+		return ret;
+
+	pr_info("setting slimbus l-addr:%x, ea:%x,%x,%x,%x\n",
+		*laddr, e_addr->manf_id, e_addr->prod_code,
+		e_addr->dev_index, e_addr->instance);
+	/**
+	 * Add this device to list of devices on this controller if it's
+	 * not already present
+	 */
+	slim = slim_query_device(ctrl, e_addr);
+	if (!slim) {
+		ret = -ENOMEM;
+	} else {
+		struct slim_driver *sbdrv;
+
+		slim->laddr = *laddr;
+		mutex_lock(&slim->report_lock);
+		slim->reported = true;
+		if (slim->dev.driver) {
+			sbdrv = to_slim_driver(slim->dev.driver);
+			if (sbdrv->device_up)
+				schedule_slim_report(ctrl, slim, true);
+		}
+		mutex_unlock(&slim->report_lock);
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_assign_laddr);
+
+/**
+ * slim_get_logical_addr: Return the logical address of a slimbus device.
+ * @sb: client handle requesting the adddress.
+ * @e_addr: Enumeration address of the device.
+ * @laddr: output buffer to store the address
+ * context: can sleep
+ * -EINVAL is returned in case of invalid parameters, and -ENXIO is returned if
+ *  the device with this enumeration address is not found.
+ */
+int slim_get_logical_addr(struct slim_device *sb, struct slim_eaddr *e_addr,
+			  u8 *laddr)
+{
+	int ret;
+	u8 entry;
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl || !laddr || !e_addr)
+		return -EINVAL;
+
+	mutex_lock(&ctrl->m_ctrl);
+	ret = ctrl_getaddr_entry(ctrl, e_addr, &entry);
+	if (!ret)
+		*laddr = ctrl->addrt[entry].laddr;
+	mutex_unlock(&ctrl->m_ctrl);
+
+	if (ret == -ENXIO && ctrl->get_laddr) {
+		ret = ctrl->get_laddr(ctrl, e_addr, laddr);
+		if (!ret)
+			ret = slim_assign_laddr(ctrl, e_addr, laddr, true);
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_get_logical_addr);
+
+static void __exit slimbus_exit(void)
+{
+	bus_unregister(&slimbus_type);
+}
+
+static int __init slimbus_init(void)
+{
+	return bus_register(&slimbus_type);
+}
+postcore_initcall(slimbus_init);
+module_exit(slimbus_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+MODULE_DESCRIPTION("Slimbus module");
+MODULE_ALIAS("platform:slimbus");
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 3bfd567..94abc09 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -427,6 +427,19 @@ struct spi_device_id {
 	kernel_ulong_t driver_data;	/* Data private to the driver */
 };
 
+/* SLIMbus */
+
+#define SLIMBUS_NAME_SIZE	32
+#define SLIMBUS_MODULE_PREFIX	"slim:"
+
+struct slim_device_id {
+	__u16 manf_id, prod_code;
+	__u8 dev_index, instance;
+
+	/* Data private to the driver */
+	kernel_ulong_t driver_data;
+};
+
 #define SPMI_NAME_SIZE	32
 #define SPMI_MODULE_PREFIX "spmi:"
 
diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h
new file mode 100644
index 0000000..2a78f79
--- /dev/null
+++ b/include/linux/slimbus.h
@@ -0,0 +1,387 @@
+/* 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 _LINUX_SLIMBUS_H
+#define _LINUX_SLIMBUS_H
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/mod_devicetable.h>
+
+/**
+ * Interfaces between SLIMbus manager drivers, SLIMbus client drivers, and
+ * SLIMbus infrastructure.
+ */
+
+extern struct bus_type slimbus_type;
+
+/* Standard values per SLIMbus spec needed by controllers and devices */
+#define SLIM_CL_PER_SUPERFRAME		6144
+#define SLIM_CL_PER_SUPERFRAME_DIV8	(SLIM_CL_PER_SUPERFRAME >> 3)
+#define SLIM_MAX_CLK_GEAR		10
+#define SLIM_MIN_CLK_GEAR		1
+#define SLIM_CL_PER_SL			4
+#define SLIM_SL_PER_SUPERFRAME		(SLIM_CL_PER_SUPERFRAME >> 2)
+#define SLIM_FRM_SLOTS_PER_SUPERFRAME	16
+#define SLIM_GDE_SLOTS_PER_SUPERFRAME	2
+
+struct slim_controller;
+struct slim_device;
+
+/**
+ * struct slim_eaddr: Enumeration address for a slimbus device
+ * @manf_id: Manufacturer Id for the device
+ * @prod_code: Product code
+ * @dev_index: Device index
+ * @instance: Instance value
+ */
+struct slim_eaddr {
+	u16 manf_id;
+	u16 prod_code;
+	u8 dev_index;
+	u8 instance;
+};
+
+/**
+ * struct slim_framer - Represents Slimbus framer.
+ * Every controller may have multiple framers. There is 1 active framer device
+ * responsible for clocking the bus.
+ * Manager is responsible for framer hand-over.
+ * @dev: Driver model representation of the device.
+ * @e_addr: Enumeration address of the framer.
+ * @rootfreq: Root Frequency at which the framer can run. This is maximum
+ *	frequency ('clock gear 10') at which the bus can operate.
+ * @superfreq: Superframes per root frequency. Every frame is 6144 bits.
+ */
+struct slim_framer {
+	struct device		dev;
+	struct slim_eaddr	e_addr;
+	int			rootfreq;
+	int			superfreq;
+};
+
+#define to_slim_framer(d) container_of(d, struct slim_framer, dev)
+
+/**
+ * struct slim_addrt: slimbus address used internally by the slimbus framework.
+ * @valid: If the device is present. Valid is set to false when device reports
+ *	absent.
+ * @eaddr: Enumeration address
+ * @laddr: It is possible that controller will set a predefined logical address
+ *	rather than the one assigned by framework. (i.e. logical address may
+ *	not be same as index into this table). This entry will store the
+ *	logical address value for this enumeration address.
+ */
+struct slim_addrt {
+	bool			valid;
+	struct slim_eaddr	eaddr;
+	u8			laddr;
+};
+
+/* SLIMbus message types. Related to interpretation of message code. */
+#define SLIM_MSG_MT_CORE			0x0
+#define SLIM_MSG_MT_DEST_REFERRED_CLASS		0x1
+#define SLIM_MSG_MT_DEST_REFERRED_USER		0x2
+#define SLIM_MSG_MT_SRC_REFERRED_CLASS		0x5
+#define SLIM_MSG_MT_SRC_REFERRED_USER		0x6
+
+/* SLIMbus core type Message Codes. */
+/* Device management messages used by this framework */
+#define SLIM_MSG_MC_REPORT_PRESENT               0x1
+#define SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS       0x2
+#define SLIM_MSG_MC_REPORT_ABSENT                0xF
+
+/* Destination type Values */
+#define SLIM_MSG_DEST_LOGICALADDR	0
+#define SLIM_MSG_DEST_ENUMADDR		1
+#define	SLIM_MSG_DEST_BROADCAST		3
+
+/**
+ * struct slim_controller: Controls every instance of SLIMbus
+ *				(similar to 'master' on SPI)
+ *	'Manager device' is responsible for  device management, bandwidth
+ *	allocation, channel setup, and port associations per channel.
+ *	Device management means Logical address assignment/removal based on
+ *	enumeration (report-present, report-absent) if a device.
+ *	Bandwidth allocation is done dynamically by the manager based on active
+ *	channels on the bus, message-bandwidth requests made by slimbus devices.
+ *	Based on current bandwidth usage, manager chooses a frequency to run
+ *	the bus at (in steps of 'clock-gear', 1 through 10, each clock gear
+ *	representing twice the frequency than the previous gear).
+ *	Manager is also responsible for entering (and exiting) low-power-mode
+ *	(known as 'clock pause').
+ *	Manager can do handover of framer if there are multiple framers on the
+ *	bus and a certain usecase warrants using certain framer to avoid keeping
+ *	previous framer being powered-on.
+ *
+ *	Controller here performs duties of the manager device, and 'interface
+ *	device'. Interface device is responsible for monitoring the bus and
+ *	reporting information such as loss-of-synchronization, data
+ *	slot-collision.
+ * @dev: Device interface to this driver
+ * @nr: Board-specific number identifier for this controller/bus
+ * @list: Link with other slimbus controllers
+ * @name: Name for this controller
+ * @min_cg: Minimum clock gear supported by this controller (default value: 1)
+ * @max_cg: Maximum clock gear supported by this controller (default value: 10)
+ * @clkgear: Current clock gear in which this bus is running
+ * @a_framer: Active framer which is clocking the bus managed by this controller
+ * @m_ctrl: Mutex protecting controller data structures
+ * @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
+ * @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
+ *	control/status message like data channel setup, or a unicast message
+ *	like value element read/write.
+ * @set_laddr: Setup logical address at laddr for the slave with elemental
+ *	address e_addr. Drivers implementing controller will be expected to
+ *	send unicast message to this device with its logical address.
+ * @get_laddr: It is possible that controller needs to set fixed logical
+ *	address table and get_laddr can be used in that case so that controller
+ *	can do this assignment.
+ */
+struct slim_controller {
+	struct device		dev;
+	unsigned int		nr;
+	struct list_head	list;
+	char			name[SLIMBUS_NAME_SIZE];
+	int			min_cg;
+	int			max_cg;
+	int			clkgear;
+	struct slim_framer	*a_framer;
+	struct mutex		m_ctrl;
+	struct slim_addrt	*addrt;
+	u8			num_dev;
+	struct workqueue_struct *wq;
+	struct completion	dev_released;
+	int			(*set_laddr)(struct slim_controller *ctrl,
+					     struct slim_eaddr *ea, u8 laddr);
+	int			(*get_laddr)(struct slim_controller *ctrl,
+					     struct slim_eaddr *ea, u8 *laddr);
+};
+
+#define to_slim_controller(d) container_of(d, struct slim_controller, dev)
+
+/**
+ * struct slim_driver: Slimbus 'generic device' (slave) device driver
+ *				(similar to 'spi_device' on SPI)
+ * @probe: Binds this driver to a slimbus device.
+ * @remove: Unbinds this driver from the slimbus device.
+ * @shutdown: Standard shutdown callback used during powerdown/halt.
+ * @suspend: Standard suspend callback used during system suspend
+ * @resume: Standard resume callback used during system resume
+ * @device_up: This callback is called when the device reports present and
+ *		gets a logical address assigned to it
+ * @device_down: This callback is called when device reports absent, or the
+ *		bus goes down. Device will report present when bus is up and
+ *		device_up callback will be called again when that happens
+ * @boot_device: This callback is called after framer is booted.
+ *		Driver should do the needful to boot the device,
+ *		so that device acquires sync and be operational.
+ * @driver: Slimbus device drivers should initialize name and owner field of
+ *	this structure
+ * @id_table: List of slimbus devices supported by this driver
+ */
+struct slim_driver {
+	int				(*probe)(struct slim_device *sl);
+	int				(*remove)(struct slim_device *sl);
+	void				(*shutdown)(struct slim_device *sl);
+	int				(*suspend)(struct slim_device *sl,
+						   pm_message_t pmesg);
+	int				(*resume)(struct slim_device *sl);
+	int				(*device_up)(struct slim_device *sl);
+	int				(*device_down)(struct slim_device *sl);
+	int				(*boot_device)(struct slim_device *sl);
+
+	struct device_driver		driver;
+	const struct slim_device_id	*id_table;
+};
+
+#define to_slim_driver(d) container_of(d, struct slim_driver, driver)
+
+/**
+ * Client/device handle (struct slim_device):
+ * ------------------------------------------
+ *  This is the client/device handle returned when a slimbus
+ *  device is registered with a controller. This structure can be provided
+ *  during register_board_info, or can be allocated using slim_add_device API.
+ *  Pointer to this structure is used by client-driver as a handle.
+ *  @dev: Driver model representation of the device.
+ *  @name: Name of driver to use with this device.
+ *  @e_addr: Enumeration address of this device.
+ *  @driver: Device's driver. Pointer to access routines.
+ *  @ctrl: Slimbus controller managing the bus hosting this device.
+ *  @laddr: 1-byte Logical address of this device.
+ *  @reported: Flag to indicate whether this device reported present. The flag
+ *	is set when device reports present, and is reset when it reports
+ *	absent. This flag alongwith notified flag below is used to call
+ *	device_up, or device_down callbacks for driver of this device.
+ *  @notified: Flag to indicate whether this device has been notified. The
+ *	device may report present multiple times, but should be notified only
+ *	first time it has reported present.
+ *  @report_lock: Lock to protect reporting and notification for this device
+ *  @dev_list: List of devices on a controller
+ */
+struct slim_device {
+	struct device		dev;
+	char		*name;
+	struct slim_eaddr	e_addr;
+	struct slim_driver	*driver;
+	struct slim_controller	*ctrl;
+	u8			laddr;
+	bool			reported;
+	bool			notified;
+	struct mutex		report_lock;
+	struct list_head	dev_list;
+};
+
+#define to_slim_device(d) container_of(d, struct slim_device, dev)
+
+/**
+ * struct slim_boardinfo: Declare board info for Slimbus device bringup.
+ * @bus_num: Controller number (bus) on which this device will sit.
+ * @slim_slave: Device to be registered with slimbus.
+ */
+struct slim_boardinfo {
+	int			bus_num;
+	struct slim_device	*slim_slave;
+};
+
+/* Manager's logical address is set to 0xFF per spec */
+#define SLIM_LA_MANAGER 0xFF
+/**
+ * slim_get_logical_addr: Return the logical address of a slimbus device.
+ * @sb: client handle requesting the adddress.
+ * @e_addr: Enumeration address of the device.
+ * @laddr: output buffer to store the address
+ * context: can sleep
+ * -EINVAL is returned in case of invalid parameters.
+ * -ENXIO is returned if the device with this elemental address is not found.
+ */
+
+int slim_get_logical_addr(struct slim_device *sb,
+				 struct slim_eaddr *e_addr, u8 *laddr);
+
+/**
+ * slim_driver_register: Client driver registration with slimbus
+ * @drv:Client driver to be associated with client-device.
+ * This API will register the client driver with the slimbus
+ * It is called from the driver's module-init function.
+ */
+int slim_driver_register(struct slim_driver *drv);
+
+/**
+ * slim_driver_unregister: Undo effects of slim_driver_register
+ * @drv: Client driver to be unregistered
+ */
+void slim_driver_unregister(struct slim_driver *drv);
+
+/**
+ * slim_del_controller: Controller tear-down.
+ * @ctrl: Controller to be torn-down.
+ */
+int slim_del_controller(struct slim_controller *ctrl);
+
+/**
+ * slim_add_device: Add a new device without register board info.
+ * @ctrl: Controller to which this device is to be added to.
+ * sbdev: slim_device to be added
+ * Called when device doesn't have an explicit client-driver to be probed, or
+ * the client-driver is a module installed dynamically.
+ */
+int slim_add_device(struct slim_controller *ctrl, struct slim_device *sbdev);
+
+/* slim_remove_device: Remove the effect of slim_add_device() */
+void slim_remove_device(struct slim_device *sbdev);
+
+/**
+ * slim_assign_laddr: Assign logical address to a device enumerated.
+ * @ctrl: Controller with which device is enumerated.
+ * @e_addr: Enumeration address of the device.
+ * @laddr: Return logical address (if valid flag is false)
+ * @valid: true if laddr holds a valid address that controller wants to
+ *	set for this enumeration address. Otherwise framework sets index into
+ *	address table as logical address.
+ * Called by controller in response to REPORT_PRESENT. Framework will assign
+ * a logical address to this enumeration address.
+ * Function returns -EXFULL to indicate that all logical addresses are already
+ * taken.
+ */
+int slim_assign_laddr(struct slim_controller *ctrl,
+		      struct slim_eaddr *e_addr, u8 *laddr, bool valid);
+
+/**
+ * slim_report_absent: Controller calls this function when a device
+ *	reports absent, OR when the device cannot be communicated with
+ * @sbdev: Device that cannot be reached, or that sent report absent
+ */
+void slim_report_absent(struct slim_device *sbdev);
+
+/**
+ * slim_framer_booted: This function is called by controller after the active
+ * framer has booted (using Bus Reset sequence, or after it has shutdown and has
+ * come back up). Components, devices on the bus may be in undefined state,
+ * and this function triggers their drivers to do the needful
+ * to bring them back in Reset state so that they can acquire sync, report
+ * present and be operational again.
+ */
+void slim_framer_booted(struct slim_controller *ctrl);
+
+/**
+ * slim_register_controller: Controller bring-up and registration.
+ * @ctrl: Controller to be registered.
+ * A controller is registered with the framework using this API.
+ * If devices on a controller were registered before controller,
+ * this will make sure that they get probed when controller is up
+ */
+int slim_register_controller(struct slim_controller *ctrl);
+
+/**
+ * slim_register_board_info: Board-initialization routine.
+ * @info: List of all devices on all controllers present on the board.
+ * @n: number of entries.
+ * API enumerates respective devices on corresponding controller.
+ * Called from board-init function.
+ */
+#ifdef CONFIG_SLIMBUS
+int slim_register_board_info(struct slim_boardinfo const *info, unsigned n);
+#else
+static inline int slim_register_board_info(struct slim_boardinfo const *info,
+					   unsigned n)
+{
+	return 0;
+}
+#endif
+
+static inline void *slim_get_ctrldata(const struct slim_controller *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void slim_set_ctrldata(struct slim_controller *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+static inline void *slim_get_devicedata(const struct slim_device *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void slim_set_clientdata(struct slim_device *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+#endif /* _LINUX_SLIMBUS_H */
-- 
1.8.2.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH V4 2/6] of/slimbus: OF helper for SLIMbus
  2016-02-06 18:44 [PATCH V4 0/6] Introduce framework for SLIMbus device drivers Sagar Dharia
       [not found] ` <1454784265-5194-1-git-send-email-sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2016-02-06 18:44 ` Sagar Dharia
  2016-02-08 20:24   ` Rob Herring
  2016-02-06 18:44 ` [PATCH V4 3/6] slimbus: Add messaging APIs to slimbus framework Sagar Dharia
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Sagar Dharia @ 2016-02-06 18:44 UTC (permalink / raw)
  To: gregkh, bp, poeschel, sdharia, treding, broonie, gong.chen,
	andreas.noever, alan, mathieu.poirier, daniel, oded.gabbay,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, daniel.thompson, robh+dt, pawel.moll,
	mark.rutland, ijc+devicetree, galak, devicetree, linux-kernel
  Cc: kheitke, mlocke, agross, linux-arm-msm

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

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

* [PATCH V4 3/6] slimbus: Add messaging APIs to slimbus framework
  2016-02-06 18:44 [PATCH V4 0/6] Introduce framework for SLIMbus device drivers Sagar Dharia
       [not found] ` <1454784265-5194-1-git-send-email-sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  2016-02-06 18:44 ` [PATCH V4 2/6] of/slimbus: OF helper for SLIMbus Sagar Dharia
@ 2016-02-06 18:44 ` Sagar Dharia
  2016-03-05  5:13   ` Mark Brown
  2016-02-06 18:44 ` [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver Sagar Dharia
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Sagar Dharia @ 2016-02-06 18:44 UTC (permalink / raw)
  To: gregkh, bp, poeschel, sdharia, treding, broonie, gong.chen,
	andreas.noever, alan, mathieu.poirier, daniel, oded.gabbay,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, daniel.thompson, robh+dt, pawel.moll,
	mark.rutland, ijc+devicetree, galak, devicetree, linux-kernel
  Cc: kheitke, mlocke, agross, linux-arm-msm

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

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

* [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver
  2016-02-06 18:44 [PATCH V4 0/6] Introduce framework for SLIMbus device drivers Sagar Dharia
                   ` (2 preceding siblings ...)
  2016-02-06 18:44 ` [PATCH V4 3/6] slimbus: Add messaging APIs to slimbus framework Sagar Dharia
@ 2016-02-06 18:44 ` Sagar Dharia
  2016-02-08 20:26   ` Rob Herring
       [not found]   ` <1454784265-5194-5-git-send-email-sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  2016-02-06 18:44 ` [PATCH V4 5/6] slimbus: Add support for 'clock-pause' feature Sagar Dharia
                   ` (2 subsequent siblings)
  6 siblings, 2 replies; 17+ messages in thread
From: Sagar Dharia @ 2016-02-06 18:44 UTC (permalink / raw)
  To: gregkh, bp, poeschel, sdharia, treding, broonie, gong.chen,
	andreas.noever, alan, mathieu.poirier, daniel, oded.gabbay,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, daniel.thompson, robh+dt, pawel.moll,
	mark.rutland, ijc+devicetree, galak, devicetree, linux-kernel
  Cc: kheitke, mlocke, agross, linux-arm-msm

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

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

* [PATCH V4 5/6] slimbus: Add support for 'clock-pause' feature
  2016-02-06 18:44 [PATCH V4 0/6] Introduce framework for SLIMbus device drivers Sagar Dharia
                   ` (3 preceding siblings ...)
  2016-02-06 18:44 ` [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver Sagar Dharia
@ 2016-02-06 18:44 ` Sagar Dharia
  2016-02-06 18:44 ` [PATCH V4 6/6] slim: qcom: Add runtime-pm support using clock-pause feature Sagar Dharia
  2016-03-05  5:31 ` [PATCH V4 0/6] Introduce framework for SLIMbus device drivers Mark Brown
  6 siblings, 0 replies; 17+ messages in thread
From: Sagar Dharia @ 2016-02-06 18:44 UTC (permalink / raw)
  To: gregkh, bp, poeschel, sdharia, treding, broonie, gong.chen,
	andreas.noever, alan, mathieu.poirier, daniel, oded.gabbay,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, daniel.thompson, robh+dt, pawel.moll,
	mark.rutland, ijc+devicetree, galak, devicetree, linux-kernel
  Cc: kheitke, mlocke, agross, linux-arm-msm

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

Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
---
 drivers/slimbus/Makefile         |   2 +-
 drivers/slimbus/slim-core.c      |  13 +++++
 drivers/slimbus/slim-messaging.c |  50 ++++++++++++++--
 drivers/slimbus/slim-sched.c     | 122 +++++++++++++++++++++++++++++++++++++++
 include/linux/slimbus.h          |  69 +++++++++++++++++++++-
 5 files changed, 248 insertions(+), 8 deletions(-)
 create mode 100644 drivers/slimbus/slim-sched.c

diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
index 574a892..cc0d20a 100644
--- a/drivers/slimbus/Makefile
+++ b/drivers/slimbus/Makefile
@@ -1,5 +1,5 @@
 #
 # Makefile for kernel slimbus framework.
 #
-obj-$(CONFIG_SLIMBUS)			+= slim-core.o slim-messaging.o
+obj-$(CONFIG_SLIMBUS)			+= slim-core.o slim-messaging.o slim-sched.o
 obj-$(CONFIG_SLIM_QCOM_CTRL)		+= slim-qcom-ctrl.o
diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
index 27c7dec..5d67e53 100644
--- a/drivers/slimbus/slim-core.c
+++ b/drivers/slimbus/slim-core.c
@@ -443,6 +443,8 @@ int slim_register_controller(struct slim_controller *ctrl)
 	mutex_init(&ctrl->m_ctrl);
 	spin_lock_init(&ctrl->tx.lock);
 	spin_lock_init(&ctrl->rx.lock);
+	mutex_init(&ctrl->sched.m_reconf);
+	init_completion(&ctrl->sched.pause_comp);
 
 	ctrl->pending_wr = kcalloc((ctrl->tx.n - 1),
 				   sizeof(struct slim_pending),
@@ -715,6 +717,14 @@ int slim_assign_laddr(struct slim_controller *ctrl, struct slim_eaddr *e_addr,
 	struct slim_device *slim;
 	struct slim_addrt *temp;
 
+	ret = pm_runtime_get_sync(ctrl->dev.parent);
+
+	if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) {
+		dev_err(&ctrl->dev, "slim ctrl not active,state:%d, ret:%d\n",
+				    ctrl->sched.clk_state, ret);
+		goto slimbus_not_active;
+	}
+
 	mutex_lock(&ctrl->m_ctrl);
 	/* already assigned */
 	if (ctrl_getaddr_entry(ctrl, e_addr, &i) == 0) {
@@ -783,6 +793,9 @@ ret_assigned_laddr:
 		}
 		mutex_unlock(&slim->report_lock);
 	}
+slimbus_not_active:
+	pm_runtime_mark_last_busy(ctrl->dev.parent);
+	pm_runtime_put_autosuspend(ctrl->dev.parent);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(slim_assign_laddr);
diff --git a/drivers/slimbus/slim-messaging.c b/drivers/slimbus/slim-messaging.c
index 3785c35..91d39d6 100644
--- a/drivers/slimbus/slim-messaging.c
+++ b/drivers/slimbus/slim-messaging.c
@@ -10,6 +10,7 @@
  * GNU General Public License for more details.
  */
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
 #include <linux/slimbus.h>
 
 /**
@@ -42,6 +43,9 @@ void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid, u8 len)
 	if (msg->comp_cb)
 		msg->comp_cb(msg->ctx, 0);
 	spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+	/* Remove runtime-pm vote now that response was received for TID txn */
+	pm_runtime_mark_last_busy(ctrl->dev.parent);
+	pm_runtime_put_autosuspend(ctrl->dev.parent);
 }
 EXPORT_SYMBOL_GPL(slim_msg_response);
 
@@ -64,11 +68,21 @@ int slim_processtxn(struct slim_controller *ctrl,
 	int ret, i = 0;
 	unsigned long flags;
 	u8 *buf;
-	bool async = false;
+	bool async = false, clk_pause_msg = false;
 	struct slim_cb_data cbd;
 	DECLARE_COMPLETION_ONSTACK(done);
 	bool need_tid = slim_tid_txn(txn->mt, txn->mc);
 
+	/*
+	 * do not vote for runtime-PM if the transactions are part of clock
+	 * pause sequence
+	 */
+	if (ctrl->sched.clk_state == SLIM_CLK_ENTERING_PAUSE &&
+		(txn->mt == SLIM_MSG_MT_CORE &&
+		 txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
+		 txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW))
+		clk_pause_msg = true;
+
 	if (!txn->msg->comp_cb) {
 		txn->msg->comp_cb = slim_sync_default_cb;
 		cbd.comp = &done;
@@ -77,7 +91,7 @@ int slim_processtxn(struct slim_controller *ctrl,
 		async = true;
 	}
 
-	buf = slim_get_tx(ctrl, txn, need_tid);
+	buf = slim_get_tx(ctrl, txn, need_tid, clk_pause_msg);
 	if (!buf)
 		return -ENOMEM;
 
@@ -91,7 +105,8 @@ int slim_processtxn(struct slim_controller *ctrl,
 			if (ctrl->last_tid == (SLIM_MAX_TIDS - 1)) {
 				spin_unlock_irqrestore(&ctrl->txn_lock, flags);
 				slim_return_tx(ctrl, -ENOMEM);
-				return -ENOMEM;
+				ret = cbd.ret;
+				return ret;
 			}
 			ctrl->last_tid++;
 		}
@@ -345,33 +360,56 @@ void slim_return_tx(struct slim_controller *ctrl, int err)
 	else
 		cur.cb(cur.ctx, err);
 	up(&ctrl->tx_sem);
+	if (!cur.clk_pause && (!cur.need_tid || err)) {
+		/**
+		 * remove runtime-pm vote if this was TX only, or
+		 * if there was error during this transaction
+		 */
+		pm_runtime_mark_last_busy(ctrl->dev.parent);
+		pm_runtime_put_autosuspend(ctrl->dev.parent);
+	}
 }
 EXPORT_SYMBOL_GPL(slim_return_tx);
 
 void *slim_get_tx(struct slim_controller *ctrl, struct slim_msg_txn *txn,
-		bool need_tid)
+		bool need_tid, bool clk_pause)
 {
 	unsigned long flags;
 	int ret, idx;
 
+	if (!clk_pause) {
+		ret = pm_runtime_get_sync(ctrl->dev.parent);
+
+		if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) {
+			dev_err(&ctrl->dev, "ctrl wrong state:%d, ret:%d\n",
+				ctrl->sched.clk_state, ret);
+			goto slim_tx_err;
+		}
+	}
+
 	ret = down_interruptible(&ctrl->tx_sem);
 	if (ret < 0) {
 		dev_err(&ctrl->dev, "TX semaphore down returned:%d", ret);
-		return NULL;
+		goto slim_tx_err;
 	}
 	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;
+		goto slim_tx_err;
 	}
 	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;
+	ctrl->pending_wr[idx].clk_pause = clk_pause;
 	spin_unlock_irqrestore(&ctrl->tx.lock, flags);
 	return ctrl->tx.base + (idx * ctrl->tx.sl_sz);
+slim_tx_err:
+	pm_runtime_mark_last_busy(ctrl->dev.parent);
+	pm_runtime_put_autosuspend(ctrl->dev.parent);
+	return NULL;
 }
 EXPORT_SYMBOL_GPL(slim_get_tx);
diff --git a/drivers/slimbus/slim-sched.c b/drivers/slimbus/slim-sched.c
new file mode 100644
index 0000000..301690d
--- /dev/null
+++ b/drivers/slimbus/slim-sched.c
@@ -0,0 +1,122 @@
+/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/errno.h>
+#include <linux/slimbus.h>
+
+/**
+ * slim_ctrl_clk_pause: Called by slimbus controller to enter/exit 'clock pause'
+ * Slimbus specification needs this sequence to turn-off clocks for the bus.
+ * The sequence involves sending 3 broadcast messages (reconfiguration
+ * sequence) to inform all devices on the bus.
+ * To exit clock-pause, controller typically wakes up active framer device.
+ * @ctrl: controller requesting bus to be paused or woken up
+ * @wakeup: Wakeup this controller from clock pause.
+ * @restart: Restart time value per spec used for clock pause. This value
+ *	isn't used when controller is to be woken up.
+ * This API executes clock pause reconfiguration sequence if wakeup is false.
+ * If wakeup is true, controller's wakeup is called.
+ * For entering clock-pause, -EBUSY is returned if a message txn in pending.
+ */
+int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart)
+{
+	int i, ret = 0;
+	unsigned long flags;
+	struct slim_sched *sched = &ctrl->sched;
+	struct slim_val_inf msg = {0, 0, NULL, NULL, NULL, NULL};
+
+	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION,
+				3, SLIM_LA_MANAGER, &msg);
+
+	if (wakeup == false && restart > SLIM_CLK_UNSPECIFIED)
+		return -EINVAL;
+	mutex_lock(&sched->m_reconf);
+	if (wakeup) {
+		if (sched->clk_state == SLIM_CLK_ACTIVE) {
+			mutex_unlock(&sched->m_reconf);
+			return 0;
+		}
+		/**
+		 * Fine-tune calculation based on clock gear,
+		 * message-bandwidth after bandwidth management
+		 */
+		ret = wait_for_completion_timeout(&sched->pause_comp,
+				msecs_to_jiffies(100));
+		if (!ret) {
+			mutex_unlock(&sched->m_reconf);
+			pr_err("Previous clock pause did not finish");
+			return -ETIMEDOUT;
+		}
+		ret = 0;
+		/**
+		 * Slimbus framework will call controller wakeup
+		 * Controller should make sure that it sets active framer
+		 * out of clock pause
+		 */
+		if (sched->clk_state == SLIM_CLK_PAUSED && ctrl->wakeup)
+			ret = ctrl->wakeup(ctrl);
+		if (!ret)
+			sched->clk_state = SLIM_CLK_ACTIVE;
+		mutex_unlock(&sched->m_reconf);
+		return ret;
+	}
+
+	/* already paused */
+	if (ctrl->sched.clk_state == SLIM_CLK_PAUSED) {
+		mutex_unlock(&sched->m_reconf);
+		return 0;
+	}
+
+	spin_lock_irqsave(&ctrl->txn_lock, flags);
+	for (i = 0; i < ctrl->last_tid; i++) {
+		/* Pending response for a message */
+		if (ctrl->tid_tbl[i]) {
+			spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+			mutex_unlock(&sched->m_reconf);
+			return -EBUSY;
+		}
+	}
+	spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+
+	sched->clk_state = SLIM_CLK_ENTERING_PAUSE;
+
+	/* clock pause sequence */
+	ret = slim_processtxn(ctrl, &txn);
+	if (ret)
+		goto clk_pause_ret;
+
+	txn.mc = SLIM_MSG_MC_NEXT_PAUSE_CLOCK;
+	txn.rl = 4;
+	msg.num_bytes = 1;
+	msg.wbuf = &restart;
+	ret = slim_processtxn(ctrl, &txn);
+	if (ret)
+		goto clk_pause_ret;
+
+	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
+	txn.rl = 3;
+	msg.num_bytes = 1;
+	msg.wbuf = NULL;
+	ret = slim_processtxn(ctrl, &txn);
+
+clk_pause_ret:
+	if (ret) {
+		sched->clk_state = SLIM_CLK_ACTIVE;
+	} else {
+		sched->clk_state = SLIM_CLK_PAUSED;
+		complete(&sched->pause_comp);
+	}
+	mutex_unlock(&sched->m_reconf);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(slim_ctrl_clk_pause);
+
diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h
index 3fd626c..55f2b71 100644
--- a/include/linux/slimbus.h
+++ b/include/linux/slimbus.h
@@ -117,11 +117,21 @@ struct slim_addrt {
 #define SLIM_MSG_MC_REPLY_VALUE                  0x64
 #define SLIM_MSG_MC_CHANGE_VALUE                 0x68
 
+/* Clock pause Reconfiguration messages */
+#define SLIM_MSG_MC_BEGIN_RECONFIGURATION        0x40
+#define SLIM_MSG_MC_NEXT_PAUSE_CLOCK             0x4A
+#define SLIM_MSG_MC_RECONFIGURE_NOW              0x5F
+
 /* Destination type Values */
 #define SLIM_MSG_DEST_LOGICALADDR	0
 #define SLIM_MSG_DEST_ENUMADDR		1
 #define	SLIM_MSG_DEST_BROADCAST		3
 
+/* Clock pause values per slimbus spec */
+#define SLIM_CLK_FAST				0
+#define SLIM_CLK_CONST_PHASE			1
+#define SLIM_CLK_UNSPECIFIED			2
+
 /**
  * struct slim_val_inf: Slimbus value or information element
  * @start_offset: Specifies starting offset in information/value element map
@@ -205,11 +215,46 @@ struct slim_ctrl_buf {
  * @cb: callback for this transfer
  * @ctx: contex for the callback function
  * @need_tid: True if this transfer need Transaction ID
+ * @clk_pause: True if this transfer is part of clock-pause sequence
  */
 struct slim_pending {
 	void (*cb)(void *ctx, int err);
 	void *ctx;
 	bool need_tid;
+	bool clk_pause;
+};
+
+/**
+ * enum slim_clk_state: Slimbus controller's clock state used internally for
+ *	maintaining current clock state.
+ * @SLIM_CLK_ACTIVE: Slimbus clock is active
+ * @SLIM_CLK_ENTERING_PAUSE: Slimbus clock pause sequence is being sent on the
+ *	bus. If this succeeds, state changes to SLIM_CLK_PAUSED. If the
+ *	transition fails, state changes back to SLIM_CLK_ACTIVE
+ * @SLIM_CLK_PAUSED: Slimbus controller clock has paused.
+ */
+enum slim_clk_state {
+	SLIM_CLK_ACTIVE,
+	SLIM_CLK_ENTERING_PAUSE,
+	SLIM_CLK_PAUSED,
+};
+
+/**
+ * struct slim_sched: Framework uses this structure internally for scheduling.
+ * @clk_state: Controller's clock state from enum slim_clk_state
+ * @pause_comp: Signals completion of clock pause sequence. This is useful when
+ *	client tries to call slimbus transaction when controller is entering
+ *	clock pause.
+ * @m_reconf: This mutex is held until current reconfiguration (data channel
+ *	scheduling, message bandwidth reservation) is done. Message APIs can
+ *	use the bus concurrently when this mutex is held since elemental access
+ *	messages can be sent on the bus when reconfiguration is in progress.
+ */
+struct slim_sched {
+	int			clkgear;
+	enum slim_clk_state	clk_state;
+	struct completion	pause_comp;
+	struct mutex		m_reconf;
 };
 
 /**
@@ -256,6 +301,7 @@ struct slim_pending {
  * @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
+ * @sched: scheduler structure used by the controller
  * @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
@@ -267,6 +313,9 @@ struct slim_pending {
  * @get_laddr: It is possible that controller needs to set fixed logical
  *	address table and get_laddr can be used in that case so that controller
  *	can do this assignment.
+ * @wakeup: This function pointer implements controller-specific procedure
+ *	to wake it up from clock-pause. Framework will call this to bring
+ *	the controller out of clock pause.
  */
 struct slim_controller {
 	struct device		dev;
@@ -288,6 +337,7 @@ struct slim_controller {
 	struct slim_ctrl_buf	rx;
 	struct slim_pending	*pending_wr;
 	struct semaphore	tx_sem;
+	struct slim_sched	sched;
 	struct completion	dev_released;
 	int			(*xfer_msg)(struct slim_controller *ctrl,
 					    struct slim_msg_txn *tx, void *buf);
@@ -295,6 +345,7 @@ struct slim_controller {
 					     struct slim_eaddr *ea, u8 laddr);
 	int			(*get_laddr)(struct slim_controller *ctrl,
 					     struct slim_eaddr *ea, u8 *laddr);
+	int			(*wakeup)(struct slim_controller *ctrl);
 };
 
 #define to_slim_controller(d) container_of(d, struct slim_controller, dev)
@@ -594,7 +645,7 @@ 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);
+		bool need_tid, bool clk_pause);
 
 void slim_return_tx(struct slim_controller *ctrl, int err);
 
@@ -608,4 +659,20 @@ static inline bool slim_tid_txn(u8 mt, u8 mc)
 }
 /* end of message apis */
 
+/**
+ * slim_ctrl_clk_pause: Called by slimbus controller to enter/exit 'clock pause'
+ * Slimbus specification needs this sequence to turn-off clocks for the bus.
+ * The sequence involves sending 3 broadcast messages (reconfiguration
+ * sequence) to inform all devices on the bus.
+ * To exit clock-pause, controller typically wakes up active framer device.
+ * @ctrl: controller requesting bus to be paused or woken up
+ * @wakeup: Wakeup this controller from clock pause.
+ * @restart: Restart time value per spec used for clock pause. This value
+ *	isn't used when controller is to be woken up.
+ * This API executes clock pause reconfiguration sequence if wakeup is false.
+ * If wakeup is true, controller's wakeup is called.
+ * For entering clock-pause, -EBUSY is returned if a message txn in pending.
+ */
+int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart);
+
 #endif /* _LINUX_SLIMBUS_H */
-- 
1.8.2.1

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

* [PATCH V4 6/6] slim: qcom: Add runtime-pm support using clock-pause feature
  2016-02-06 18:44 [PATCH V4 0/6] Introduce framework for SLIMbus device drivers Sagar Dharia
                   ` (4 preceding siblings ...)
  2016-02-06 18:44 ` [PATCH V4 5/6] slimbus: Add support for 'clock-pause' feature Sagar Dharia
@ 2016-02-06 18:44 ` Sagar Dharia
  2016-03-05  5:29   ` Mark Brown
  2016-03-05  5:31 ` [PATCH V4 0/6] Introduce framework for SLIMbus device drivers Mark Brown
  6 siblings, 1 reply; 17+ messages in thread
From: Sagar Dharia @ 2016-02-06 18:44 UTC (permalink / raw)
  To: gregkh, bp, poeschel, sdharia, treding, broonie, gong.chen,
	andreas.noever, alan, mathieu.poirier, daniel, oded.gabbay,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, daniel.thompson, robh+dt, pawel.moll,
	mark.rutland, ijc+devicetree, galak, devicetree, linux-kernel
  Cc: kheitke, mlocke, agross, linux-arm-msm

Slimbus HW mandates that clock-pause sequence has to be executed
before disabling relevant interface and core clocks.
Runtime-PM's autosuspend feature is used here to enter/exit low
power mode for Qualcomm's Slimbus controller. Autosuspend feature
enables driver to avoid changing power-modes too frequently since
entering clock-pause is an expensive sequence

Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
---
 drivers/slimbus/slim-qcom-ctrl.c | 124 ++++++++++++++++++++++++++++++++++++++-
 drivers/slimbus/slim-qcom.h      |   1 +
 2 files changed, 124 insertions(+), 1 deletion(-)

diff --git a/drivers/slimbus/slim-qcom-ctrl.c b/drivers/slimbus/slim-qcom-ctrl.c
index df20fb4..9cadcea 100644
--- a/drivers/slimbus/slim-qcom-ctrl.c
+++ b/drivers/slimbus/slim-qcom-ctrl.c
@@ -20,6 +20,7 @@
 #include <linux/clk.h>
 #include <linux/of.h>
 #include <linux/slimbus.h>
+#include <linux/pm_runtime.h>
 #include "slim-qcom.h"
 
 #define MSM_SLIM_NAME	"msm_slim_ctrl"
@@ -217,6 +218,28 @@ rx_ret_irq:
 	return IRQ_HANDLED;
 }
 
+static int msm_clk_pause_wakeup(struct slim_controller *ctrl)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+
+	clk_prepare_enable(dev->hclk);
+	clk_prepare_enable(dev->rclk);
+	writel_relaxed(1, dev->base + FRM_WAKEUP);
+	/* Make sure framer wakeup write goes through before ISR fires */
+	mb();
+	/**
+	 * HW Workaround: Currently, slave is reporting lost-sync messages
+	 * after slimbus comes out of clock pause.
+	 * Transaction with slave fail before slave reports that message
+	 * Give some time for that report to come
+	 * Slimbus wakes up in clock gear 10 at 24.576MHz. With each superframe
+	 * being 250 usecs, we wait for 5-10 superframes here to ensure
+	 * we get the message
+	 */
+	usleep_range(1250, 2500);
+	return 0;
+}
+
 static int msm_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn,
 			void *pbuf)
 {
@@ -253,6 +276,7 @@ static int msm_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn,
 	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);
 }
 
@@ -276,13 +300,13 @@ static int msm_set_laddr(struct slim_controller *ctrl,
 	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)
@@ -413,6 +437,8 @@ static int msm_slim_probe(struct platform_device *pdev)
 
 	dev->ctrl.set_laddr = msm_set_laddr;
 	dev->ctrl.xfer_msg = msm_xfer_msg;
+	dev->ctrl.wakeup =  msm_clk_pause_wakeup;
+
 	dev->ctrl.tx.n = MSM_TX_MSGS;
 	dev->ctrl.rx.n = MSM_RX_MSGS;
 	dev->ctrl.tx.sl_sz = SLIM_MSGQ_BUF_LEN;
@@ -510,6 +536,12 @@ static int msm_slim_probe(struct platform_device *pdev)
 	 */
 	mb();
 
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev, MSM_SLIM_AUTOSUSPEND);
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_mark_last_busy(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
 	dev_dbg(dev->dev, "MSM SB controller is up:ver:0x%x!\n", dev->ver);
 	return 0;
 
@@ -527,11 +559,100 @@ static int msm_slim_remove(struct platform_device *pdev)
 {
 	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
 
+	pm_runtime_disable(&pdev->dev);
 	slim_del_controller(&dev->ctrl);
 	destroy_workqueue(dev->rxwq);
 	return 0;
 }
 
+/**
+ * If PM_RUNTIME is not defined, these 2 functions become helper
+ * functions to be called from system suspend/resume.
+ */
+#ifdef CONFIG_PM
+static int msm_slim_runtime_suspend(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	int ret;
+
+	dev_dbg(device, "pm_runtime: suspending...\n");
+	ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
+	if (ret)
+		dev_err(device, "clk pause not entered:%d", ret);
+	else {
+		clk_disable_unprepare(dev->hclk);
+		clk_disable_unprepare(dev->rclk);
+	}
+	return ret;
+}
+
+static int msm_slim_runtime_resume(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	int ret = 0;
+
+	dev_dbg(device, "pm_runtime: resuming...\n");
+	ret = slim_ctrl_clk_pause(&dev->ctrl, true, 0);
+	if (ret)
+		dev_err(device, "clk pause not exited:%d", ret);
+	return ret;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_slim_suspend(struct device *dev)
+{
+	int ret = 0;
+
+	if (!pm_runtime_enabled(dev) ||
+		(!pm_runtime_suspended(dev))) {
+		dev_dbg(dev, "system suspend");
+		ret = msm_slim_runtime_suspend(dev);
+	}
+	if (ret == -EISCONN) {
+		/**
+		* If the clock pause failed due to active channels, there is
+		* a possibility that some audio stream is active during suspend.
+		* (e.g. modem usecase during suspend)
+		* We dont want to return suspend failure in that case so that
+		* display and relevant components can still go to suspend.
+		* If there is some other error, then it should prevent
+		* system level suspend
+		*/
+		ret = 0;
+	}
+	return ret;
+}
+
+static int msm_slim_resume(struct device *dev)
+{
+	if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {
+		int ret;
+
+		dev_dbg(dev, "system resume");
+		ret = msm_slim_runtime_resume(dev);
+		if (!ret) {
+			pm_runtime_mark_last_busy(dev);
+			pm_request_autosuspend(dev);
+		}
+		return ret;
+
+	}
+	return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops msm_slim_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(msm_slim_suspend, msm_slim_resume)
+	SET_RUNTIME_PM_OPS(
+			   msm_slim_runtime_suspend,
+			   msm_slim_runtime_resume,
+			   NULL
+	)
+};
+
 static const struct of_device_id msm_slim_dt_match[] = {
 	{
 		.compatible = "qcom,slim-msm",
@@ -546,6 +667,7 @@ static struct platform_driver msm_slim_driver = {
 		.name = MSM_SLIM_NAME,
 		.owner = THIS_MODULE,
 		.of_match_table = msm_slim_dt_match,
+		.pm = &msm_slim_dev_pm_ops,
 	},
 };
 module_platform_driver(msm_slim_driver);
diff --git a/drivers/slimbus/slim-qcom.h b/drivers/slimbus/slim-qcom.h
index 0ad59c3..8b1d649 100644
--- a/drivers/slimbus/slim-qcom.h
+++ b/drivers/slimbus/slim-qcom.h
@@ -23,6 +23,7 @@
 		((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16))
 
 #define SLIM_ROOT_FREQ 24576000
+#define MSM_SLIM_AUTOSUSPEND 1000
 
 /* MAX message size over control channel */
 #define SLIM_MSGQ_BUF_LEN	40
-- 
1.8.2.1

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

* Re: [PATCH V4 2/6] of/slimbus: OF helper for SLIMbus
  2016-02-06 18:44 ` [PATCH V4 2/6] of/slimbus: OF helper for SLIMbus Sagar Dharia
@ 2016-02-08 20:24   ` Rob Herring
  0 siblings, 0 replies; 17+ messages in thread
From: Rob Herring @ 2016-02-08 20:24 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: gregkh, bp, poeschel, treding, broonie, gong.chen,
	andreas.noever, alan, mathieu.poirier, daniel, oded.gabbay,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, daniel.thompson, pawel.moll, mark.rutland,
	ijc+devicetree, galak, devicetree, linux-kernel, kheitke, mlocke,
	agross, linux-arm-msm

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>;
> +		};
> +	};

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

* Re: [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver
  2016-02-06 18:44 ` [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver Sagar Dharia
@ 2016-02-08 20:26   ` Rob Herring
       [not found]   ` <1454784265-5194-5-git-send-email-sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
  1 sibling, 0 replies; 17+ messages in thread
From: Rob Herring @ 2016-02-08 20:26 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: gregkh, bp, poeschel, treding, broonie, gong.chen,
	andreas.noever, alan, mathieu.poirier, daniel, oded.gabbay,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, daniel.thompson, pawel.moll, mark.rutland,
	ijc+devicetree, galak, devicetree, linux-kernel, kheitke, mlocke,
	agross, linux-arm-msm

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.
> +

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

* Re: [PATCH V4 1/6] SLIMbus: Device management on SLIMbus
       [not found]     ` <1454784265-5194-2-git-send-email-sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2016-03-05  5:01       ` Mark Brown
  0 siblings, 0 replies; 17+ messages in thread
From: Mark Brown @ 2016-03-05  5:01 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, treding-DDmLM1+adcrQT0dZR+AlfA,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	oded.gabbay-5C7GfCeVMHo, jkosina-AlSwsSmVLrQ,
	sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA, joe-6d6DIl74uiNBDgjK7y7TUQ,
	davem-fT/PcQaiUtIeIZ0/mPfg9Q, james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	daniel.thompson-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kheitke-hxvC4TZJLZFWk0Htik3J/w, mlocke-sgV2jX0FEOL9JmXXK+q4OQ,
	agross-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA

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

On Sat, Feb 06, 2016 at 11:44:20AM -0700, Sagar Dharia wrote:

> +static void schedule_slim_report(struct slim_controller *ctrl,
> +				 struct slim_device *sb, bool report)
> +{
> +	struct sb_report_wd *sbw;
> +
> +	dev_dbg(&ctrl->dev, "report:%d for slave:%s\n", report, sb->name);
> +
> +	sbw = kmalloc(sizeof(*sbw), GFP_KERNEL);
> +	if (!sbw)
> +		return;
> +
> +	INIT_WORK(&sbw->wd, slim_report);
> +	sbw->sb = sb;
> +	sbw->report = report;
> +	if (!queue_work(ctrl->wq, &sbw->wd)) {
> +		dev_err(&ctrl->dev, "failed to queue report:%d slave:%s\n",
> +				    report, sb->name);
> +		kfree(sbw);
> +	}
> +}

I'm not 100% clear why we're scheduling this into a workqueue, it'd
probably help to at least explain what's going on in the code for future
reference.

> +}
> +/**

There's an awful lot of places in this which look like they're missing
blank lines.

> +ret_assigned_laddr:
> +	mutex_unlock(&ctrl->m_ctrl);
> +	if (exists || ret)
> +		return ret;
> +
> +	pr_info("setting slimbus l-addr:%x, ea:%x,%x,%x,%x\n",
> +		*laddr, e_addr->manf_id, e_addr->prod_code,
> +		e_addr->dev_index, e_addr->instance);

Not a dev_ print?

> +	slim = slim_query_device(ctrl, e_addr);
> +	if (!slim) {
> +		ret = -ENOMEM;

-ENOMEM?

> +static void __exit slimbus_exit(void)
> +{
> +	bus_unregister(&slimbus_type);
> +}
> +
> +static int __init slimbus_init(void)
> +{
> +	return bus_register(&slimbus_type);
> +}
> +postcore_initcall(slimbus_init);
> +module_exit(slimbus_exit);

Put the annotatations next to their functions.

> +MODULE_DESCRIPTION("Slimbus module");
> +MODULE_ALIAS("platform:slimbus");

This isn't a platform driver, it shouldn't have this alias.

> diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h
> new file mode 100644
> index 0000000..2a78f79
> --- /dev/null
> +++ b/include/linux/slimbus.h

Probably good to add a module_slimbus_device() macro like other buses
have.

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

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

* Re: [PATCH V4 3/6] slimbus: Add messaging APIs to slimbus framework
  2016-02-06 18:44 ` [PATCH V4 3/6] slimbus: Add messaging APIs to slimbus framework Sagar Dharia
@ 2016-03-05  5:13   ` Mark Brown
  0 siblings, 0 replies; 17+ messages in thread
From: Mark Brown @ 2016-03-05  5:13 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: gregkh, bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, oded.gabbay, jkosina, sharon.dvir1, joe,
	davem, james.hogan, michael.opdenacker, daniel.thompson, robh+dt,
	pawel.moll, mark.rutland, ijc+devicetree, galak, devicetree,
	linux-kernel, kheitke, mlocke, agross, linux-arm-msm

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

On Sat, Feb 06, 2016 at 11:44:22AM -0700, Sagar Dharia wrote:

> +	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);

Do we need to hold the lock for so long (especially with things like the
memcpy())?  As far as I can tell we only need the lock for this:

> +	msg = ctrl->tid_tbl[tid];
> +	ctrl->tid_tbl[tid] = NULL;

> +	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;

A switch statement might be nicer here.

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

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

* Re: [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver
       [not found]   ` <1454784265-5194-5-git-send-email-sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
@ 2016-03-05  5:23     ` Mark Brown
  2016-04-15 16:17       ` Sagar Dharia
  0 siblings, 1 reply; 17+ messages in thread
From: Mark Brown @ 2016-03-05  5:23 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, treding-DDmLM1+adcrQT0dZR+AlfA,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	oded.gabbay-5C7GfCeVMHo, jkosina-AlSwsSmVLrQ,
	sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA, joe-6d6DIl74uiNBDgjK7y7TUQ,
	davem-fT/PcQaiUtIeIZ0/mPfg9Q, james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	daniel.thompson-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kheitke-hxvC4TZJLZFWk0Htik3J/w, mlocke-sgV2jX0FEOL9JmXXK+q4OQ,
	agross-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA

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

On Sat, Feb 06, 2016 at 11:44:23AM -0700, Sagar Dharia wrote:

>  
> +if SLIMBUS
> +config SLIM_QCOM_CTRL
> +	tristate "Qualcomm Slimbus Manager Component"
> +	depends on SLIMBUS
> +	default n
> +	help

n is the default.

> +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_RX_MSG_RCVD) {

> +	}

> +	/**
> +	 * All interrupts are handled: complex RX messages requiring more work
> +	 * are queued to work-queue, others are handled above
> +	 */
> +	return IRQ_HANDLED;

This unconditionally returns IRQ_HANDLED even if no interrupts were
flagged.  This will break if the interrupt gets shared in some hardware
design or if something goes wrong with the hardware.

> +	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;
> +	}

Are you sure this is safe and we don't deallocate things the interrupt
handler uses before we disable the interrupt?

> +	ret = clk_prepare_enable(hclk);
> +	if (ret)
> +		goto err_hclk_enable_failed;
> +
> +	ret = clk_prepare_enable(rclk);
> +	if (ret)
> +		goto err_rclk_enable_failed;

The remove path doesn't disable these.

> +	/* 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;
> +	}

Should we have a devm_ version of slim_register_controller()?  I'd also
expect this to be the last thing we do in probe, things may start using
the device before we've finished initializing it.

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

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

* Re: [PATCH V4 6/6] slim: qcom: Add runtime-pm support using clock-pause feature
  2016-02-06 18:44 ` [PATCH V4 6/6] slim: qcom: Add runtime-pm support using clock-pause feature Sagar Dharia
@ 2016-03-05  5:29   ` Mark Brown
  0 siblings, 0 replies; 17+ messages in thread
From: Mark Brown @ 2016-03-05  5:29 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: gregkh, bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, oded.gabbay, jkosina, sharon.dvir1, joe,
	davem, james.hogan, michael.opdenacker, daniel.thompson, robh+dt,
	pawel.moll, mark.rutland, ijc+devicetree, galak, devicetree,
	linux-kernel, kheitke, mlocke, agross, linux-arm-msm

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

On Sat, Feb 06, 2016 at 11:44:25AM -0700, Sagar Dharia wrote:

> @@ -253,6 +276,7 @@ static int msm_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn,
>  	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);
>  }
>  

This change for some reason seems to have a few of these random
whitespace only changes here.

> +	ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
> +	if (ret)
> +		dev_err(device, "clk pause not entered:%d", ret);
> +	else {
> +		clk_disable_unprepare(dev->hclk);
> +		clk_disable_unprepare(dev->rclk);
> +	}
> +	return ret;

Coding style, {} should be on both sides.  It's also a bit odd that we
don't ever reenable these clocks.

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

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

* Re: [PATCH V4 0/6] Introduce framework for SLIMbus device drivers
  2016-02-06 18:44 [PATCH V4 0/6] Introduce framework for SLIMbus device drivers Sagar Dharia
                   ` (5 preceding siblings ...)
  2016-02-06 18:44 ` [PATCH V4 6/6] slim: qcom: Add runtime-pm support using clock-pause feature Sagar Dharia
@ 2016-03-05  5:31 ` Mark Brown
  6 siblings, 0 replies; 17+ messages in thread
From: Mark Brown @ 2016-03-05  5:31 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: gregkh, bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, oded.gabbay, jkosina, sharon.dvir1, joe,
	davem, james.hogan, michael.opdenacker, daniel.thompson, robh+dt,
	pawel.moll, mark.rutland, ijc+devicetree, galak, devicetree,
	linux-kernel, kheitke, mlocke, agross, linux-arm-msm

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

On Sat, Feb 06, 2016 at 11:44:19AM -0700, Sagar Dharia wrote:
> 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.

I've sent replies with some comments but they're all fairly low level
and specific, overall this is looking pretty good though.

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

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

* Re: [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver
  2016-03-05  5:23     ` Mark Brown
@ 2016-04-15 16:17       ` Sagar Dharia
  2016-04-18  9:19         ` Mark Brown
  0 siblings, 1 reply; 17+ messages in thread
From: Sagar Dharia @ 2016-04-15 16:17 UTC (permalink / raw)
  To: Mark Brown, robh
  Cc: gregkh, bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, oded.gabbay, jkosina, sharon.dvir1, joe,
	davem, james.hogan, michael.opdenacker, daniel.thompson, robh+dt,
	pawel.moll, mark.rutland, ijc+devicetree, galak, devicetree,
	linux-kernel, kheitke, mlocke, agross, linux-arm-msm

Hello Mark
Apologies for a late reply. I will incorporate most of your comments.
Please see inline response for 2 comments:
>> +	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;
>> +	}
> Are you sure this is safe and we don't deallocate things the interrupt
> handler uses before we disable the interrupt?

Since clock is not enabled before this step, we won't be getting any 
interrupts from HW at this stage.

>> +	/* 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;
>> +	}
> Should we have a devm_ version of slim_register_controller()?  I'd also
> expect this to be the last thing we do in probe, things may start using
> the device before we've finished initializing it.
register_controller also allocates controller's TX/RX ring buffers. 
These rings are needed when devices start
reporting present on the bus once they are enabled.
So register_controller needs to be done before enabling any devices.
All steps after register_controller in this function are related to 
enabling various internal component devices
(e.g. framer, interface, manager devices) of this slimbus controller.

Thank you for your comments
Sagar


-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

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

* Re: [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver
  2016-04-15 16:17       ` Sagar Dharia
@ 2016-04-18  9:19         ` Mark Brown
  2016-04-18 22:42           ` Sagar Dharia
  0 siblings, 1 reply; 17+ messages in thread
From: Mark Brown @ 2016-04-18  9:19 UTC (permalink / raw)
  To: Sagar Dharia
  Cc: robh, gregkh, bp, poeschel, treding, gong.chen, andreas.noever,
	alan, mathieu.poirier, daniel, oded.gabbay, jkosina,
	sharon.dvir1, joe, davem, james.hogan, michael.opdenacker,
	daniel.thompson, robh+dt, pawel.moll, mark.rutland,
	ijc+devicetree, galak, devicetree, linux-kernel, kheitke, mlocke,
	agross, linux-arm-msm

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

On Fri, Apr 15, 2016 at 10:17:36AM -0600, Sagar Dharia wrote:

Please leave blank lines between paragraphs, it makes things much easier
to read than a wall of uninterrupted text.

> >>+	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;
> >>+	}

> >Are you sure this is safe and we don't deallocate things the interrupt
> >handler uses before we disable the interrupt?

> Since clock is not enabled before this step, we won't be getting any
> interrupts from HW at this stage.

No, that's not what I'm saying - I'm asking about the *disable* path on
remove.

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

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

* Re: [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver
  2016-04-18  9:19         ` Mark Brown
@ 2016-04-18 22:42           ` Sagar Dharia
  0 siblings, 0 replies; 17+ messages in thread
From: Sagar Dharia @ 2016-04-18 22:42 UTC (permalink / raw)
  To: Mark Brown
  Cc: robh, gregkh, bp, poeschel, treding, gong.chen, andreas.noever,
	alan, mathieu.poirier, daniel, oded.gabbay, jkosina,
	sharon.dvir1, joe, davem, james.hogan, michael.opdenacker,
	daniel.thompson, robh+dt, pawel.moll, mark.rutland,
	ijc+devicetree, galak, devicetree, linux-kernel, kheitke, mlocke,
	agross, linux-arm-msm

On 4/18/2016 3:19 AM, Mark Brown wrote:
> On Fri, Apr 15, 2016 at 10:17:36AM -0600, Sagar Dharia wrote:
>
> Please leave blank lines between paragraphs, it makes things much easier
> to read than a wall of uninterrupted text.
>
>>>> +	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;
>>>> +	}
>>> Are you sure this is safe and we don't deallocate things the interrupt
>>> handler uses before we disable the interrupt?
>> Since clock is not enabled before this step, we won't be getting any
>> interrupts from HW at this stage.
> No, that's not what I'm saying - I'm asking about the *disable* path on
> remove.
Good point, I will change the del_controller as well to enter clock-pause,
and the controller's remove function to disable interrupt.

Thanks
Sagar

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation

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

end of thread, other threads:[~2016-04-18 22:42 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-02-06 18:44 [PATCH V4 0/6] Introduce framework for SLIMbus device drivers Sagar Dharia
     [not found] ` <1454784265-5194-1-git-send-email-sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-02-06 18:44   ` [PATCH V4 1/6] SLIMbus: Device management on SLIMbus Sagar Dharia
     [not found]     ` <1454784265-5194-2-git-send-email-sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-03-05  5:01       ` Mark Brown
2016-02-06 18:44 ` [PATCH V4 2/6] of/slimbus: OF helper for SLIMbus Sagar Dharia
2016-02-08 20:24   ` Rob Herring
2016-02-06 18:44 ` [PATCH V4 3/6] slimbus: Add messaging APIs to slimbus framework Sagar Dharia
2016-03-05  5:13   ` Mark Brown
2016-02-06 18:44 ` [PATCH V4 4/6] slim: qcom: Add Qualcomm Slimbus controller driver Sagar Dharia
2016-02-08 20:26   ` Rob Herring
     [not found]   ` <1454784265-5194-5-git-send-email-sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-03-05  5:23     ` Mark Brown
2016-04-15 16:17       ` Sagar Dharia
2016-04-18  9:19         ` Mark Brown
2016-04-18 22:42           ` Sagar Dharia
2016-02-06 18:44 ` [PATCH V4 5/6] slimbus: Add support for 'clock-pause' feature Sagar Dharia
2016-02-06 18:44 ` [PATCH V4 6/6] slim: qcom: Add runtime-pm support using clock-pause feature Sagar Dharia
2016-03-05  5:29   ` Mark Brown
2016-03-05  5:31 ` [PATCH V4 0/6] Introduce framework for SLIMbus device drivers Mark Brown

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