All of lore.kernel.org
 help / color / mirror / Atom feed
* [Patch v6 0/7] Introduce framework for SLIMbus device drivers
@ 2017-10-06 15:51 srinivas.kandagatla
  2017-10-06 15:51   ` srinivas.kandagatla
                   ` (4 more replies)
  0 siblings, 5 replies; 87+ messages in thread
From: srinivas.kandagatla @ 2017-10-06 15:51 UTC (permalink / raw)
  To: gregkh, broonie, alsa-devel
  Cc: sdharia, bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, jkosina, sharon.dvir1, joe, davem,
	james.hogan, michael.opdenacker, robh+dt, pawel.moll,
	mark.rutland, devicetree, linux-kernel, kheitke, linux-arm-msm,
	arnd, Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

It's been very long time since there was any activity on the slimbus patches,
Am currently working on getting Qualcomm DSP based audio working on top
of mainline. Slimbus is one of the major component for getting any analog
audio on QCOM SoC's. So am taking intiative to address the review
comments on the older patchset and send it.
I have tested this patch on IFC6410 board with wcd9310 codec.


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 once these
initial patches are accepted.

These patchsets were tested on Qualcomm Snapdragon processor board
using the controller driver, and a test slave device.

Changes from V5 to V6:
* aligned slim_driver_register more like other buses, suggested by Arnd.
* removed boardinfo and add_device apis for now, suggested by Arnd
* Few namespace cleanups suggested by Masami
* merged of apis in to first patch as suggested by Arnd.
* slimbus clients "compatible" name space made much inline with USB
  and PCIE, suggested by Rob and Arnd.
* Removed memory allocations to controller drivers, as suggested by Arnd.
* Various bindings comments addressed as suggested by Mark and others.
* Added regmap interface so that codecs can write more generic code.
* Added MAINTAINER file.

Sagar Dharia (5):
  slimbus: Device management on SLIMbus
  slimbus: Add messaging APIs to slimbus framework
  slimbus: qcom: Add Qualcomm Slimbus controller driver
  slimbus: Add support for 'clock-pause' feature
  slimbus: qcom: Add runtime-pm support using clock-pause feature

Srinivas Kandagatla (2):
  regmap: add SLIMBUS support
  MAINTAINERS: Add SLIMbus maintainer

 Documentation/devicetree/bindings/slimbus/bus.txt  |  57 ++
 .../devicetree/bindings/slimbus/slim-qcom-ctrl.txt |  43 ++
 Documentation/slimbus/summary                      | 109 ++++
 MAINTAINERS                                        |   8 +
 drivers/Kconfig                                    |   2 +
 drivers/Makefile                                   |   1 +
 drivers/base/regmap/Kconfig                        |   4 +
 drivers/base/regmap/Makefile                       |   1 +
 drivers/base/regmap/regmap-slimbus.c               |  89 +++
 drivers/slimbus/Kconfig                            |  20 +
 drivers/slimbus/Makefile                           |   8 +
 drivers/slimbus/slim-core.c                        | 725 +++++++++++++++++++++
 drivers/slimbus/slim-messaging.c                   | 509 +++++++++++++++
 drivers/slimbus/slim-qcom-ctrl.c                   | 714 ++++++++++++++++++++
 drivers/slimbus/slim-qcom.h                        |  64 ++
 drivers/slimbus/slim-sched.c                       | 126 ++++
 include/linux/mod_devicetable.h                    |  13 +
 include/linux/regmap.h                             |  18 +
 include/linux/slimbus.h                            | 512 +++++++++++++++
 19 files changed, 3023 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/base/regmap/regmap-slimbus.c
 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

-- 
2.9.3

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

* [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-06 15:51 [Patch v6 0/7] Introduce framework for SLIMbus device drivers srinivas.kandagatla
@ 2017-10-06 15:51   ` srinivas.kandagatla
  2017-10-06 15:51 ` [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver srinivas.kandagatla
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 87+ messages in thread
From: srinivas.kandagatla @ 2017-10-06 15:51 UTC (permalink / raw)
  To: gregkh, broonie, alsa-devel
  Cc: mark.rutland, michael.opdenacker, poeschel, Srinivas Kandagatla,
	andreas.noever, gong.chen, arnd, kheitke, bp, devicetree,
	james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1, robh+dt,
	sdharia, alan, treding, mathieu.poirier, jkosina, linux-kernel,
	daniel, joe, davem

From: Sagar Dharia <sdharia@codeaurora.org>

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). 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@codeaurora.org>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
 Documentation/slimbus/summary                     | 109 ++++
 drivers/Kconfig                                   |   2 +
 drivers/Makefile                                  |   1 +
 drivers/slimbus/Kconfig                           |  11 +
 drivers/slimbus/Makefile                          |   5 +
 drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
 include/linux/mod_devicetable.h                   |  13 +
 include/linux/slimbus.h                           | 299 ++++++++++
 9 files changed, 1192 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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 include/linux/slimbus.h

diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
new file mode 100644
index 0000000..cb658bb
--- /dev/null
+++ b/Documentation/devicetree/bindings/slimbus/bus.txt
@@ -0,0 +1,57 @@
+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 2
+- #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.
+If child node is not present and it is instantiated after device
+discovery (slave device reporting itself present).
+
+In some cases it may be necessary to describe non-probeable device
+details such as non-standard ways of powering up a device. In
+such cases, child nodes for those devices will be present as
+slaves of the slimbus-controller, as detailed below.
+
+Required property for SLIMbus child node if it is present:
+- reg		- Is Duplex (Device index, Instance ID) from Enumeration
+		  Address.
+		  Device Index Uniquely identifies multiple Devices within
+		  a single Component.
+		  Instance ID Is for the cases where multiple Devices of the
+		  same type or Class are attached to the bus.
+
+- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
+	 	  Product Code, shall be in lower case hexadecimal with leading
+		  zeroes suppressed
+
+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";
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		codec: wcd9310@1{
+			compatible = "slim217,60"";
+			reg = <1 0>;
+		};
+	};
diff --git a/Documentation/slimbus/summary b/Documentation/slimbus/summary
new file mode 100644
index 0000000..e7f90bb
--- /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 controller.
+
+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 505c676..8010c67 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -208,4 +208,6 @@ source "drivers/tee/Kconfig"
 
 source "drivers/mux/Kconfig"
 
+source "drivers/slimbus/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index d90fdc4..0449c7c 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_MTD)		+= mtd/
 obj-$(CONFIG_SPI)		+= spi/
 obj-$(CONFIG_SPMI)		+= spmi/
 obj-$(CONFIG_HSI)		+= hsi/
+obj-$(CONFIG_SLIMBUS)		+= slimbus/
 obj-y				+= net/
 obj-$(CONFIG_ATM)		+= atm/
 obj-$(CONFIG_FUSION)		+= message/
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..f580704
--- /dev/null
+++ b/drivers/slimbus/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for kernel slimbus framework.
+#
+obj-$(CONFIG_SLIMBUS)			+= slimbus.o
+slimbus-y				:= slim-core.o
diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
new file mode 100644
index 0000000..de3ef79
--- /dev/null
+++ b/drivers/slimbus/slim-core.c
@@ -0,0 +1,695 @@
+/* Copyright (c) 2011-2017, 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>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+static DEFINE_MUTEX(slim_lock);
+static DEFINE_IDR(ctrl_idr);
+
+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 *sbdev)
+{
+	while (id->manf_id != 0 || id->prod_code != 0) {
+		if (id->manf_id == sbdev->e_addr.manf_id &&
+		    id->prod_code == sbdev->e_addr.prod_code &&
+		    id->dev_index == sbdev->e_addr.dev_index)
+			return id;
+		id++;
+	}
+	return NULL;
+}
+
+static int slim_device_match(struct device *dev, struct device_driver *drv)
+{
+	struct slim_device *sbdev = to_slim_device(dev);
+	struct slim_driver *sbdrv = to_slim_driver(drv);
+
+	/* Attempt an OF style match first */
+	if (of_driver_match_device(dev, drv))
+		return 1;
+
+	/* Then try to match against the id table */
+	if (sbdrv->id_table)
+		return slim_match(sbdrv->id_table, sbdev) != NULL;
+
+	return 0;
+}
+
+struct sb_report_wd {
+	struct work_struct wd;
+	struct slim_device *sbdev;
+	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->sbdev;
+
+	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);
+}
+
+/**
+ * Report callbacks(device_up, device_down) are implemented by slimbus-devices.
+ * The calls are scheduled into a workqueue to avoid holding up controller
+ * initialization/tear-down.
+ */
+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->sbdev = 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	*sbdev;
+	struct slim_driver	*sbdrv;
+	int status = 0;
+
+	sbdev = to_slim_device(dev);
+	sbdrv = to_slim_driver(dev->driver);
+
+	sbdev->driver = sbdrv;
+
+	if (sbdrv->probe)
+		status = sbdrv->probe(sbdev);
+
+	if (status)
+		sbdev->driver = NULL;
+	else if (sbdrv->device_up)
+		schedule_slim_report(sbdev->ctrl, sbdev, true);
+
+	return status;
+}
+
+static int slim_device_remove(struct device *dev)
+{
+	struct slim_device *sbdev;
+	struct slim_driver *sbdrv;
+	int status = 0;
+
+	sbdev = to_slim_device(dev);
+	if (!dev->driver)
+		return 0;
+
+	sbdrv = to_slim_driver(dev->driver);
+	if (sbdrv->remove)
+		status = sbdrv->remove(sbdev);
+
+	mutex_lock(&sbdev->report_lock);
+	sbdev->notified = false;
+	if (status == 0)
+		sbdev->driver = NULL;
+	mutex_unlock(&sbdev->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.
+ * @owner: owning module/driver
+ * 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, struct module *owner)
+{
+	drv->driver.bus = &slimbus_type;
+	drv->driver.owner = owner;
+	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);
+}
+EXPORT_SYMBOL_GPL(slim_driver_unregister);
+
+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);
+	kfree(sbdev->name);
+	kfree(sbdev);
+}
+
+static int slim_add_device(struct slim_controller *ctrl,
+			   struct slim_device *sbdev)
+{
+	sbdev->dev.bus = &slimbus_type;
+	sbdev->dev.parent = &ctrl->dev;
+	sbdev->dev.release = slim_dev_release;
+	sbdev->dev.driver = NULL;
+	sbdev->ctrl = ctrl;
+
+	slim_ctrl_get(ctrl);
+	sbdev->name = kasprintf(GFP_KERNEL, "%x:%x:%x:%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);
+}
+
+/* Helper to get hex Manufacturer ID and Product id from compatible */
+static unsigned long str2hex(unsigned char *str)
+{
+	int value = 0;
+
+	while (*str) {
+		char c = *str++;
+
+		value = value << 4;
+		if (c >= '0' && c <= '9')
+			value |= (c - '0');
+		if (c >= 'a' && c <= 'f')
+			value |= (c - 'a' + 10);
+
+	}
+
+	return value;
+}
+
+/* OF helpers for SLIMbus */
+static void of_register_slim_devices(struct slim_controller *ctrl)
+{
+	struct device *dev = &ctrl->dev;
+	struct device_node *node;
+
+	if (!ctrl->dev.of_node)
+		return;
+
+	for_each_child_of_node(ctrl->dev.of_node, node) {
+		struct slim_device *slim;
+		const char *compat = NULL;
+		char *p, *tok;
+		int reg[2], ret;
+
+		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
+		if (!slim)
+			continue;
+
+		slim->dev.of_node = of_node_get(node);
+
+		compat = of_get_property(node, "compatible", NULL);
+		if (!compat)
+			continue;
+
+		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
+
+		tok = strsep(&p, ",");
+		if (!tok) {
+			dev_err(dev, "No valid Manufacturer ID found\n");
+			kfree(p);
+			continue;
+		}
+		slim->e_addr.manf_id = str2hex(tok);
+
+		tok = strsep(&p, ",");
+		if (!tok) {
+			dev_err(dev, "No valid Product ID found\n");
+			kfree(p);
+			continue;
+		}
+		slim->e_addr.prod_code = str2hex(tok);
+		kfree(p);
+
+		ret = of_property_read_u32_array(node, "reg", reg, 2);
+		if (ret) {
+			dev_err(dev, "Device and Instance id not found:%d\n",
+				ret);
+			continue;
+		}
+		slim->e_addr.dev_index = reg[0];
+		slim->e_addr.instance = reg[1];
+
+		ret = slim_add_device(ctrl, slim);
+		if (ret)
+			dev_err(dev, "of_slim device register err:%d\n", ret);
+	}
+}
+
+/**
+ * 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->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;
+
+	of_register_slim_devices(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() */
+static void slim_remove_device(struct slim_device *sbdev)
+{
+	device_unregister(&sbdev->dev);
+}
+
+static int slim_ctrl_remove_device(struct device *dev, void *null)
+{
+	slim_remove_device(to_slim_device(dev));
+	return 0;
+}
+
+/**
+ * slim_del_controller: Controller tear-down.
+ * @ctrl: Controller to tear-down.
+ */
+int slim_del_controller(struct slim_controller *ctrl)
+{
+	struct slim_controller *found;
+
+	/* 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 */
+	device_for_each_child(&ctrl->dev, NULL, slim_ctrl_remove_device);
+
+
+	destroy_workqueue(ctrl->wq);
+
+	/* free bus id */
+	mutex_lock(&slim_lock);
+	idr_remove(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&slim_lock);
+
+	device_unregister(&ctrl->dev);
+	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;
+
+	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;
+
+	dev_info(&ctrl->dev, "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 = -ENODEV;
+	} 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 address.
+ * @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);
+}
+module_exit(slimbus_exit);
+
+static int __init slimbus_init(void)
+{
+	return bus_register(&slimbus_type);
+}
+postcore_initcall(slimbus_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+MODULE_DESCRIPTION("Slimbus module");
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 694cebb..015e5f6 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -448,6 +448,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..b5064b6
--- /dev/null
+++ b/include/linux/slimbus.h
@@ -0,0 +1,299 @@
+/* Copyright (c) 2011-2017, 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
+ * @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;
+	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;
+	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.
+ *  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
+ */
+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;
+};
+
+#define to_slim_device(d) container_of(d, struct slim_device, dev)
+
+/* Manager's logical address is set to 0xFF per spec */
+#define SLIM_LA_MANAGER 0xFF
+
+int slim_get_logical_addr(struct slim_device *sb,
+				 struct slim_eaddr *e_addr, u8 *laddr);
+
+/*
+ * use a macro to avoid include chaining to get THIS_MODULE
+ */
+#define slim_driver_register(drv) \
+	__slim_driver_register(drv, THIS_MODULE)
+
+int __slim_driver_register(struct slim_driver *drv, struct module *owner);
+
+void slim_driver_unregister(struct slim_driver *drv);
+
+/**
+ * module_slim_driver() - Helper macro for registering a slimbus driver
+ * @__slimbus_driver: slimbus_driver struct
+ *
+ * Helper macro for slimbus drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_slim_driver(__slim_driver) \
+	module_driver(__slim_driver, slim_driver_register, \
+			slim_driver_unregister)
+
+int slim_del_controller(struct slim_controller *ctrl);
+int slim_assign_laddr(struct slim_controller *ctrl,
+		      struct slim_eaddr *e_addr, u8 *laddr, bool valid);
+void slim_report_absent(struct slim_device *sbdev);
+void slim_framer_booted(struct slim_controller *ctrl);
+int slim_register_controller(struct slim_controller *ctrl);
+
+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_devicedata(struct slim_device *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+#endif /* _LINUX_SLIMBUS_H */
-- 
2.9.3

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

* [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-06 15:51   ` srinivas.kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: srinivas.kandagatla @ 2017-10-06 15:51 UTC (permalink / raw)
  To: gregkh, broonie, alsa-devel
  Cc: sdharia, bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, jkosina, sharon.dvir1, joe, davem,
	james.hogan, michael.opdenacker, robh+dt, pawel.moll,
	mark.rutland, devicetree, linux-kernel, kheitke, linux-arm-msm,
	arnd, Srinivas Kandagatla

From: Sagar Dharia <sdharia@codeaurora.org>

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). 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@codeaurora.org>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
 Documentation/slimbus/summary                     | 109 ++++
 drivers/Kconfig                                   |   2 +
 drivers/Makefile                                  |   1 +
 drivers/slimbus/Kconfig                           |  11 +
 drivers/slimbus/Makefile                          |   5 +
 drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
 include/linux/mod_devicetable.h                   |  13 +
 include/linux/slimbus.h                           | 299 ++++++++++
 9 files changed, 1192 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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 include/linux/slimbus.h

diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
new file mode 100644
index 0000000..cb658bb
--- /dev/null
+++ b/Documentation/devicetree/bindings/slimbus/bus.txt
@@ -0,0 +1,57 @@
+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 2
+- #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.
+If child node is not present and it is instantiated after device
+discovery (slave device reporting itself present).
+
+In some cases it may be necessary to describe non-probeable device
+details such as non-standard ways of powering up a device. In
+such cases, child nodes for those devices will be present as
+slaves of the slimbus-controller, as detailed below.
+
+Required property for SLIMbus child node if it is present:
+- reg		- Is Duplex (Device index, Instance ID) from Enumeration
+		  Address.
+		  Device Index Uniquely identifies multiple Devices within
+		  a single Component.
+		  Instance ID Is for the cases where multiple Devices of the
+		  same type or Class are attached to the bus.
+
+- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
+	 	  Product Code, shall be in lower case hexadecimal with leading
+		  zeroes suppressed
+
+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";
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		codec: wcd9310@1{
+			compatible = "slim217,60"";
+			reg = <1 0>;
+		};
+	};
diff --git a/Documentation/slimbus/summary b/Documentation/slimbus/summary
new file mode 100644
index 0000000..e7f90bb
--- /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 controller.
+
+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 505c676..8010c67 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -208,4 +208,6 @@ source "drivers/tee/Kconfig"
 
 source "drivers/mux/Kconfig"
 
+source "drivers/slimbus/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index d90fdc4..0449c7c 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -86,6 +86,7 @@ obj-$(CONFIG_MTD)		+= mtd/
 obj-$(CONFIG_SPI)		+= spi/
 obj-$(CONFIG_SPMI)		+= spmi/
 obj-$(CONFIG_HSI)		+= hsi/
+obj-$(CONFIG_SLIMBUS)		+= slimbus/
 obj-y				+= net/
 obj-$(CONFIG_ATM)		+= atm/
 obj-$(CONFIG_FUSION)		+= message/
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..f580704
--- /dev/null
+++ b/drivers/slimbus/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for kernel slimbus framework.
+#
+obj-$(CONFIG_SLIMBUS)			+= slimbus.o
+slimbus-y				:= slim-core.o
diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
new file mode 100644
index 0000000..de3ef79
--- /dev/null
+++ b/drivers/slimbus/slim-core.c
@@ -0,0 +1,695 @@
+/* Copyright (c) 2011-2017, 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>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+static DEFINE_MUTEX(slim_lock);
+static DEFINE_IDR(ctrl_idr);
+
+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 *sbdev)
+{
+	while (id->manf_id != 0 || id->prod_code != 0) {
+		if (id->manf_id == sbdev->e_addr.manf_id &&
+		    id->prod_code == sbdev->e_addr.prod_code &&
+		    id->dev_index == sbdev->e_addr.dev_index)
+			return id;
+		id++;
+	}
+	return NULL;
+}
+
+static int slim_device_match(struct device *dev, struct device_driver *drv)
+{
+	struct slim_device *sbdev = to_slim_device(dev);
+	struct slim_driver *sbdrv = to_slim_driver(drv);
+
+	/* Attempt an OF style match first */
+	if (of_driver_match_device(dev, drv))
+		return 1;
+
+	/* Then try to match against the id table */
+	if (sbdrv->id_table)
+		return slim_match(sbdrv->id_table, sbdev) != NULL;
+
+	return 0;
+}
+
+struct sb_report_wd {
+	struct work_struct wd;
+	struct slim_device *sbdev;
+	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->sbdev;
+
+	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);
+}
+
+/**
+ * Report callbacks(device_up, device_down) are implemented by slimbus-devices.
+ * The calls are scheduled into a workqueue to avoid holding up controller
+ * initialization/tear-down.
+ */
+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->sbdev = 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	*sbdev;
+	struct slim_driver	*sbdrv;
+	int status = 0;
+
+	sbdev = to_slim_device(dev);
+	sbdrv = to_slim_driver(dev->driver);
+
+	sbdev->driver = sbdrv;
+
+	if (sbdrv->probe)
+		status = sbdrv->probe(sbdev);
+
+	if (status)
+		sbdev->driver = NULL;
+	else if (sbdrv->device_up)
+		schedule_slim_report(sbdev->ctrl, sbdev, true);
+
+	return status;
+}
+
+static int slim_device_remove(struct device *dev)
+{
+	struct slim_device *sbdev;
+	struct slim_driver *sbdrv;
+	int status = 0;
+
+	sbdev = to_slim_device(dev);
+	if (!dev->driver)
+		return 0;
+
+	sbdrv = to_slim_driver(dev->driver);
+	if (sbdrv->remove)
+		status = sbdrv->remove(sbdev);
+
+	mutex_lock(&sbdev->report_lock);
+	sbdev->notified = false;
+	if (status == 0)
+		sbdev->driver = NULL;
+	mutex_unlock(&sbdev->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.
+ * @owner: owning module/driver
+ * 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, struct module *owner)
+{
+	drv->driver.bus = &slimbus_type;
+	drv->driver.owner = owner;
+	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);
+}
+EXPORT_SYMBOL_GPL(slim_driver_unregister);
+
+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);
+	kfree(sbdev->name);
+	kfree(sbdev);
+}
+
+static int slim_add_device(struct slim_controller *ctrl,
+			   struct slim_device *sbdev)
+{
+	sbdev->dev.bus = &slimbus_type;
+	sbdev->dev.parent = &ctrl->dev;
+	sbdev->dev.release = slim_dev_release;
+	sbdev->dev.driver = NULL;
+	sbdev->ctrl = ctrl;
+
+	slim_ctrl_get(ctrl);
+	sbdev->name = kasprintf(GFP_KERNEL, "%x:%x:%x:%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);
+}
+
+/* Helper to get hex Manufacturer ID and Product id from compatible */
+static unsigned long str2hex(unsigned char *str)
+{
+	int value = 0;
+
+	while (*str) {
+		char c = *str++;
+
+		value = value << 4;
+		if (c >= '0' && c <= '9')
+			value |= (c - '0');
+		if (c >= 'a' && c <= 'f')
+			value |= (c - 'a' + 10);
+
+	}
+
+	return value;
+}
+
+/* OF helpers for SLIMbus */
+static void of_register_slim_devices(struct slim_controller *ctrl)
+{
+	struct device *dev = &ctrl->dev;
+	struct device_node *node;
+
+	if (!ctrl->dev.of_node)
+		return;
+
+	for_each_child_of_node(ctrl->dev.of_node, node) {
+		struct slim_device *slim;
+		const char *compat = NULL;
+		char *p, *tok;
+		int reg[2], ret;
+
+		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
+		if (!slim)
+			continue;
+
+		slim->dev.of_node = of_node_get(node);
+
+		compat = of_get_property(node, "compatible", NULL);
+		if (!compat)
+			continue;
+
+		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
+
+		tok = strsep(&p, ",");
+		if (!tok) {
+			dev_err(dev, "No valid Manufacturer ID found\n");
+			kfree(p);
+			continue;
+		}
+		slim->e_addr.manf_id = str2hex(tok);
+
+		tok = strsep(&p, ",");
+		if (!tok) {
+			dev_err(dev, "No valid Product ID found\n");
+			kfree(p);
+			continue;
+		}
+		slim->e_addr.prod_code = str2hex(tok);
+		kfree(p);
+
+		ret = of_property_read_u32_array(node, "reg", reg, 2);
+		if (ret) {
+			dev_err(dev, "Device and Instance id not found:%d\n",
+				ret);
+			continue;
+		}
+		slim->e_addr.dev_index = reg[0];
+		slim->e_addr.instance = reg[1];
+
+		ret = slim_add_device(ctrl, slim);
+		if (ret)
+			dev_err(dev, "of_slim device register err:%d\n", ret);
+	}
+}
+
+/**
+ * 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->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;
+
+	of_register_slim_devices(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() */
+static void slim_remove_device(struct slim_device *sbdev)
+{
+	device_unregister(&sbdev->dev);
+}
+
+static int slim_ctrl_remove_device(struct device *dev, void *null)
+{
+	slim_remove_device(to_slim_device(dev));
+	return 0;
+}
+
+/**
+ * slim_del_controller: Controller tear-down.
+ * @ctrl: Controller to tear-down.
+ */
+int slim_del_controller(struct slim_controller *ctrl)
+{
+	struct slim_controller *found;
+
+	/* 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 */
+	device_for_each_child(&ctrl->dev, NULL, slim_ctrl_remove_device);
+
+
+	destroy_workqueue(ctrl->wq);
+
+	/* free bus id */
+	mutex_lock(&slim_lock);
+	idr_remove(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&slim_lock);
+
+	device_unregister(&ctrl->dev);
+	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;
+
+	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;
+
+	dev_info(&ctrl->dev, "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 = -ENODEV;
+	} 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 address.
+ * @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);
+}
+module_exit(slimbus_exit);
+
+static int __init slimbus_init(void)
+{
+	return bus_register(&slimbus_type);
+}
+postcore_initcall(slimbus_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+MODULE_DESCRIPTION("Slimbus module");
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 694cebb..015e5f6 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -448,6 +448,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..b5064b6
--- /dev/null
+++ b/include/linux/slimbus.h
@@ -0,0 +1,299 @@
+/* Copyright (c) 2011-2017, 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
+ * @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;
+	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;
+	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.
+ *  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
+ */
+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;
+};
+
+#define to_slim_device(d) container_of(d, struct slim_device, dev)
+
+/* Manager's logical address is set to 0xFF per spec */
+#define SLIM_LA_MANAGER 0xFF
+
+int slim_get_logical_addr(struct slim_device *sb,
+				 struct slim_eaddr *e_addr, u8 *laddr);
+
+/*
+ * use a macro to avoid include chaining to get THIS_MODULE
+ */
+#define slim_driver_register(drv) \
+	__slim_driver_register(drv, THIS_MODULE)
+
+int __slim_driver_register(struct slim_driver *drv, struct module *owner);
+
+void slim_driver_unregister(struct slim_driver *drv);
+
+/**
+ * module_slim_driver() - Helper macro for registering a slimbus driver
+ * @__slimbus_driver: slimbus_driver struct
+ *
+ * Helper macro for slimbus drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_slim_driver(__slim_driver) \
+	module_driver(__slim_driver, slim_driver_register, \
+			slim_driver_unregister)
+
+int slim_del_controller(struct slim_controller *ctrl);
+int slim_assign_laddr(struct slim_controller *ctrl,
+		      struct slim_eaddr *e_addr, u8 *laddr, bool valid);
+void slim_report_absent(struct slim_device *sbdev);
+void slim_framer_booted(struct slim_controller *ctrl);
+int slim_register_controller(struct slim_controller *ctrl);
+
+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_devicedata(struct slim_device *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+#endif /* _LINUX_SLIMBUS_H */
-- 
2.9.3

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

* [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-06 15:51 [Patch v6 0/7] Introduce framework for SLIMbus device drivers srinivas.kandagatla
@ 2017-10-06 15:51     ` srinivas.kandagatla
  2017-10-06 15:51 ` [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver srinivas.kandagatla
                       ` (3 subsequent siblings)
  4 siblings, 0 replies; 87+ messages in thread
From: srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A @ 2017-10-06 15:51 UTC (permalink / raw)
  To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw
  Cc: sdharia-sgV2jX0FEOL9JmXXK+q4OQ, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, treding-DDmLM1+adcrQT0dZR+AlfA,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	jkosina-AlSwsSmVLrQ, sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kheitke-hxvC4TZJLZFWk0Htik3J/w,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4,
	Srinivas Kandagatla

From: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>

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-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Tested-by: Naveen Kaje <nkaje-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 drivers/slimbus/Makefile         |   2 +-
 drivers/slimbus/slim-core.c      |  15 ++
 drivers/slimbus/slim-messaging.c | 471 +++++++++++++++++++++++++++++++++++++++
 include/linux/slimbus.h          | 161 +++++++++++++
 4 files changed, 648 insertions(+), 1 deletion(-)
 create mode 100644 drivers/slimbus/slim-messaging.c

diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
index f580704..bd1d35e 100644
--- a/drivers/slimbus/Makefile
+++ b/drivers/slimbus/Makefile
@@ -2,4 +2,4 @@
 # Makefile for kernel slimbus framework.
 #
 obj-$(CONFIG_SLIMBUS)			+= slimbus.o
-slimbus-y				:= slim-core.o
+slimbus-y				:= slim-core.o slim-messaging.o
diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
index de3ef79..5b1da28 100644
--- a/drivers/slimbus/slim-core.c
+++ b/drivers/slimbus/slim-core.c
@@ -362,6 +362,19 @@ int slim_register_controller(struct slim_controller *ctrl)
 		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));
+
 	ret = device_register(&ctrl->dev);
 	if (ret)
 		goto dev_reg_failed;
@@ -380,6 +393,8 @@ int slim_register_controller(struct slim_controller *ctrl)
 err_workq_failed:
 	device_unregister(&ctrl->dev);
 dev_reg_failed:
+	kfree(ctrl->pending_wr);
+wr_alloc_failed:
 	mutex_lock(&slim_lock);
 	idr_remove(&ctrl_idr, ctrl->nr);
 	mutex_unlock(&slim_lock);
diff --git a/drivers/slimbus/slim-messaging.c b/drivers/slimbus/slim-messaging.c
new file mode 100644
index 0000000..adcba67
--- /dev/null
+++ b/drivers/slimbus/slim-messaging.c
@@ -0,0 +1,471 @@
+/* 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;
+	}
+	ctrl->tid_tbl[tid] = NULL;
+	spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+
+	memcpy(msg->rbuf, reply, len);
+	if (msg->comp_cb)
+		msg->comp_cb(msg->ctx, 0);
+}
+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);
+}
+
+/**
+ * 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)
+{
+	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));
+
+	switch (mc) {
+	case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
+	case SLIM_MSG_MC_CHANGE_VALUE:
+	case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
+	case SLIM_MSG_MC_CLEAR_INFORMATION:
+		txn->rl += msg->num_bytes;
+	default:
+		break;
+	}
+
+	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 */
+
+/*
+ * slim_request_val_element: request value element
+ * @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);
+
+/**
+ * slim_request_inf_element: Request a info element
+ * @sb: client handle requesting elemental message reads.
+ * @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)
+{
+	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);
+
+
+/*
+ * slim_change_val_element: change a given value element
+ * @sb: client handle requesting elemental message reads, writes.
+ * @msg: Input structure for start-offset, number of bytes to write.
+ * 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_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);
+
+/**
+ * slim_clear_inf_element: Clear info element
+ * @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_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);
+
+/*
+ * slim_request_val_element: change and request a given value element
+ * @sb: client handle requesting elemental message reads, writes.
+ * @msg: Input structure for start-offset, number of bytes to write.
+ * 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_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);
+
+/**
+ * slim_request_clear_inf_element: Clear and request info element
+ * @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_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. */
+
+/**
+ * slim_get_rx: To get RX buffers for messaging.
+ * @ctrl: Controller handle
+ * These functions are called by controller to process the RX buffers.
+ * 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)
+{
+	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);
+
+/**
+ * slim_get_tx: To get TX buffers for messaging.
+ * @ctrl: Controller handle
+ * These functions are called by controller to process the TX 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.
+ */
+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 b5064b6..080d86a 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
  * @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.
@@ -161,6 +278,15 @@ 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;
+	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,
@@ -295,5 +421,40 @@ static inline void slim_set_devicedata(struct slim_device *dev, void *data)
 {
 	dev_set_drvdata(&dev->dev, data);
 }
+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);
+
+
+
+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);
+void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid,
+				u8 len);
+
+int slim_processtxn(struct slim_controller *ctrl, struct slim_msg_txn *txn);
+
+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 */
-- 
2.9.3

--
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] 87+ messages in thread

* [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
@ 2017-10-06 15:51     ` srinivas.kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: srinivas.kandagatla @ 2017-10-06 15:51 UTC (permalink / raw)
  To: gregkh, broonie, alsa-devel
  Cc: sdharia, bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, jkosina, sharon.dvir1, joe, davem,
	james.hogan, michael.opdenacker, robh+dt, pawel.moll,
	mark.rutland, devicetree, linux-kernel, kheitke, linux-arm-msm,
	arnd, Srinivas Kandagatla

From: Sagar Dharia <sdharia@codeaurora.org>

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>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 drivers/slimbus/Makefile         |   2 +-
 drivers/slimbus/slim-core.c      |  15 ++
 drivers/slimbus/slim-messaging.c | 471 +++++++++++++++++++++++++++++++++++++++
 include/linux/slimbus.h          | 161 +++++++++++++
 4 files changed, 648 insertions(+), 1 deletion(-)
 create mode 100644 drivers/slimbus/slim-messaging.c

diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
index f580704..bd1d35e 100644
--- a/drivers/slimbus/Makefile
+++ b/drivers/slimbus/Makefile
@@ -2,4 +2,4 @@
 # Makefile for kernel slimbus framework.
 #
 obj-$(CONFIG_SLIMBUS)			+= slimbus.o
-slimbus-y				:= slim-core.o
+slimbus-y				:= slim-core.o slim-messaging.o
diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
index de3ef79..5b1da28 100644
--- a/drivers/slimbus/slim-core.c
+++ b/drivers/slimbus/slim-core.c
@@ -362,6 +362,19 @@ int slim_register_controller(struct slim_controller *ctrl)
 		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));
+
 	ret = device_register(&ctrl->dev);
 	if (ret)
 		goto dev_reg_failed;
@@ -380,6 +393,8 @@ int slim_register_controller(struct slim_controller *ctrl)
 err_workq_failed:
 	device_unregister(&ctrl->dev);
 dev_reg_failed:
+	kfree(ctrl->pending_wr);
+wr_alloc_failed:
 	mutex_lock(&slim_lock);
 	idr_remove(&ctrl_idr, ctrl->nr);
 	mutex_unlock(&slim_lock);
diff --git a/drivers/slimbus/slim-messaging.c b/drivers/slimbus/slim-messaging.c
new file mode 100644
index 0000000..adcba67
--- /dev/null
+++ b/drivers/slimbus/slim-messaging.c
@@ -0,0 +1,471 @@
+/* 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;
+	}
+	ctrl->tid_tbl[tid] = NULL;
+	spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+
+	memcpy(msg->rbuf, reply, len);
+	if (msg->comp_cb)
+		msg->comp_cb(msg->ctx, 0);
+}
+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);
+}
+
+/**
+ * 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)
+{
+	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));
+
+	switch (mc) {
+	case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
+	case SLIM_MSG_MC_CHANGE_VALUE:
+	case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
+	case SLIM_MSG_MC_CLEAR_INFORMATION:
+		txn->rl += msg->num_bytes;
+	default:
+		break;
+	}
+
+	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 */
+
+/*
+ * slim_request_val_element: request value element
+ * @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);
+
+/**
+ * slim_request_inf_element: Request a info element
+ * @sb: client handle requesting elemental message reads.
+ * @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)
+{
+	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);
+
+
+/*
+ * slim_change_val_element: change a given value element
+ * @sb: client handle requesting elemental message reads, writes.
+ * @msg: Input structure for start-offset, number of bytes to write.
+ * 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_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);
+
+/**
+ * slim_clear_inf_element: Clear info element
+ * @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_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);
+
+/*
+ * slim_request_val_element: change and request a given value element
+ * @sb: client handle requesting elemental message reads, writes.
+ * @msg: Input structure for start-offset, number of bytes to write.
+ * 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_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);
+
+/**
+ * slim_request_clear_inf_element: Clear and request info element
+ * @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_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. */
+
+/**
+ * slim_get_rx: To get RX buffers for messaging.
+ * @ctrl: Controller handle
+ * These functions are called by controller to process the RX buffers.
+ * 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)
+{
+	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);
+
+/**
+ * slim_get_tx: To get TX buffers for messaging.
+ * @ctrl: Controller handle
+ * These functions are called by controller to process the TX 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.
+ */
+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 b5064b6..080d86a 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
  * @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.
@@ -161,6 +278,15 @@ 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;
+	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,
@@ -295,5 +421,40 @@ static inline void slim_set_devicedata(struct slim_device *dev, void *data)
 {
 	dev_set_drvdata(&dev->dev, data);
 }
+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);
+
+
+
+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);
+void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid,
+				u8 len);
+
+int slim_processtxn(struct slim_controller *ctrl, struct slim_msg_txn *txn);
+
+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 */
-- 
2.9.3

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

* [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver
  2017-10-06 15:51 [Patch v6 0/7] Introduce framework for SLIMbus device drivers srinivas.kandagatla
  2017-10-06 15:51   ` srinivas.kandagatla
@ 2017-10-06 15:51 ` srinivas.kandagatla
  2017-10-07  7:45   ` Jonathan Neuschäfer
                     ` (2 more replies)
  2017-10-06 15:51 ` [Patch v6 4/7] slimbus: Add support for 'clock-pause' feature srinivas.kandagatla
                   ` (2 subsequent siblings)
  4 siblings, 3 replies; 87+ messages in thread
From: srinivas.kandagatla @ 2017-10-06 15:51 UTC (permalink / raw)
  To: gregkh, broonie, alsa-devel
  Cc: sdharia, bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, jkosina, sharon.dvir1, joe, davem,
	james.hogan, michael.opdenacker, robh+dt, pawel.moll,
	mark.rutland, devicetree, linux-kernel, kheitke, linux-arm-msm,
	arnd, Srinivas Kandagatla

From: Sagar Dharia <sdharia@codeaurora.org>

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>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 .../devicetree/bindings/slimbus/slim-qcom-ctrl.txt |  43 ++
 drivers/slimbus/Kconfig                            |   9 +
 drivers/slimbus/Makefile                           |   3 +
 drivers/slimbus/slim-qcom-ctrl.c                   | 594 +++++++++++++++++++++
 drivers/slimbus/slim-qcom.h                        |  63 +++
 5 files changed, 712 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..081110d
--- /dev/null
+++ b/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt
@@ -0,0 +1,43 @@
+Qualcomm SLIMBUS controller
+This controller is used if applications processor driver controls slimbus
+master component.
+
+Required properties:
+
+ - #address-cells - refer to Documentation/devicetree/bindings/slimbus/bus.txt
+ - #size-cells	- refer to Documentation/devicetree/bindings/slimbus/bus.txt
+
+ - 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:
+	 "ctrl": Physical adderess of controller register blocks
+ - compatible : should be "qcom,<SOC-NAME>-slim" for SOC specific compatible or
+ 		"qcom,slim" if using generic qcom SLIM IP.
+ - 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: "slew"
+
+Example:
+	slim@28080000 {
+		compatible = "qcom,slim";
+		#address-cells = <4>;
+		#size-cells = <0>;
+		reg = <0x28080000 0x2000>, <0x80207C 4>;
+		reg-names = "ctrl", "slew";
+		interrupts = <0 33 0>;
+		clocks = <&lcc SLIMBUS_SRC>, <&lcc AUDIO_SLIMBUS_CLK>;
+		clock-names = "iface_clk", "core_clk";
+
+		codec: wcd9310@1{
+			compatible = "slim217,60";
+			reg = <1 0>;
+		};
+	};
diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig
index f0b118a..438773e 100644
--- a/drivers/slimbus/Kconfig
+++ b/drivers/slimbus/Kconfig
@@ -9,3 +9,12 @@ menuconfig SLIMBUS
 
 	  If unsure, choose N.
 
+if SLIMBUS
+config SLIM_QCOM_CTRL
+	tristate "Qualcomm Slimbus Manager Component"
+	depends on SLIMBUS
+	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 bd1d35e..ed8521a 100644
--- a/drivers/slimbus/Makefile
+++ b/drivers/slimbus/Makefile
@@ -3,3 +3,6 @@
 #
 obj-$(CONFIG_SLIMBUS)			+= slimbus.o
 slimbus-y				:= slim-core.o slim-messaging.o
+
+#Controllers
+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..d0574e1
--- /dev/null
+++ b/drivers/slimbus/slim-qcom-ctrl.c
@@ -0,0 +1,594 @@
+/* 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/dma-mapping.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, ret = IRQ_NONE;
+
+	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);
+		ret = IRQ_HANDLED;
+	}
+	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);
+
+		ret = IRQ_HANDLED;
+	}
+	return ret;
+}
+
+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);
+	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,
+						     "slew");
+	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 slim_controller *ctrl;
+	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, "ctrl");
+	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;
+	ctrl = &dev->ctrl;
+	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;
+
+
+	ctrl->tx.base = dma_alloc_coherent(&pdev->dev,
+					   (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(&pdev->dev,
+					   (ctrl->rx.sl_sz * ctrl->rx.n),
+					   &ctrl->rx.phy, GFP_KERNEL);
+	if (!ctrl->rx.base) {
+		ret = -ENOMEM;
+		goto rx_alloc_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:
+	dma_free_coherent(&pdev->dev, (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:
+	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);
+	struct slim_controller *ctrl = to_slim_controller(&pdev->dev);
+
+	dma_free_coherent(&pdev->dev, (ctrl->rx.sl_sz * ctrl->rx.n),
+			  ctrl->rx.base, ctrl->rx.phy);
+	dma_free_coherent(&pdev->dev, (ctrl->tx.sl_sz * ctrl->tx.n),
+			  ctrl->tx.base, ctrl->tx.phy);
+
+	disable_irq(dev->irq);
+	clk_disable_unprepare(dev->rclk);
+	clk_disable_unprepare(dev->hclk);
+	slim_del_controller(&dev->ctrl);
+	destroy_workqueue(dev->rxwq);
+	return 0;
+}
+
+static const struct of_device_id msm_slim_dt_match[] = {
+	{
+		.compatible = "qcom,slim",
+	},
+	{}
+};
+
+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
-- 
2.9.3

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

* [Patch v6 4/7] slimbus: Add support for 'clock-pause' feature
  2017-10-06 15:51 [Patch v6 0/7] Introduce framework for SLIMbus device drivers srinivas.kandagatla
  2017-10-06 15:51   ` srinivas.kandagatla
  2017-10-06 15:51 ` [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver srinivas.kandagatla
@ 2017-10-06 15:51 ` srinivas.kandagatla
  2017-10-07  8:06   ` Jonathan Neuschäfer
       [not found] ` <20171006155136.4682-1-srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
  2017-10-06 15:51 ` [Patch v6 6/7] regmap: add SLIMBUS support srinivas.kandagatla
  4 siblings, 1 reply; 87+ messages in thread
From: srinivas.kandagatla @ 2017-10-06 15:51 UTC (permalink / raw)
  To: gregkh, broonie, alsa-devel
  Cc: sdharia, bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, jkosina, sharon.dvir1, joe, davem,
	james.hogan, michael.opdenacker, robh+dt, pawel.moll,
	mark.rutland, devicetree, linux-kernel, kheitke, linux-arm-msm,
	arnd, Srinivas Kandagatla

From: Sagar Dharia <sdharia@codeaurora.org>

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

Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 drivers/slimbus/Makefile         |   2 +-
 drivers/slimbus/slim-core.c      |  15 +++++
 drivers/slimbus/slim-messaging.c |  50 ++++++++++++++--
 drivers/slimbus/slim-sched.c     | 126 +++++++++++++++++++++++++++++++++++++++
 include/linux/slimbus.h          |  54 ++++++++++++++++-
 5 files changed, 239 insertions(+), 8 deletions(-)
 create mode 100644 drivers/slimbus/slim-sched.c

diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
index ed8521a..8fc8b50 100644
--- a/drivers/slimbus/Makefile
+++ b/drivers/slimbus/Makefile
@@ -2,7 +2,7 @@
 # Makefile for kernel slimbus framework.
 #
 obj-$(CONFIG_SLIMBUS)			+= slimbus.o
-slimbus-y				:= slim-core.o slim-messaging.o
+slimbus-y				:= slim-core.o slim-messaging.o slim-sched.o
 
 #Controllers
 obj-$(CONFIG_SLIM_QCOM_CTRL)		+= slim-qcom-ctrl.o
diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
index 5b1da28..a5cb31b 100644
--- a/drivers/slimbus/slim-core.c
+++ b/drivers/slimbus/slim-core.c
@@ -364,6 +364,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),
@@ -432,6 +434,8 @@ int slim_del_controller(struct slim_controller *ctrl)
 	/* Remove all clients */
 	device_for_each_child(&ctrl->dev, NULL, slim_ctrl_remove_device);
 
+	/* Enter clock pause */
+	slim_ctrl_clk_pause(ctrl, false, 0);
 
 	destroy_workqueue(ctrl->wq);
 
@@ -585,6 +589,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) {
@@ -655,6 +667,9 @@ int slim_assign_laddr(struct slim_controller *ctrl, struct slim_eaddr *e_addr,
 		}
 		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 adcba67..f6a0fa2 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>
 
 /**
@@ -43,6 +44,9 @@ void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid, u8 len)
 	memcpy(msg->rbuf, reply, len);
 	if (msg->comp_cb)
 		msg->comp_cb(msg->ctx, 0);
+	/* 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);
 
@@ -77,11 +81,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;
@@ -90,7 +104,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;
 
@@ -104,7 +118,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++;
 		}
@@ -429,6 +444,14 @@ void slim_return_tx(struct slim_controller *ctrl, int err)
 		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);
 
@@ -441,15 +464,25 @@ EXPORT_SYMBOL_GPL(slim_return_tx);
  * back to the pool.
  */
 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);
 
@@ -457,15 +490,20 @@ void *slim_get_tx(struct slim_controller *ctrl, struct slim_msg_txn *txn,
 		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..3ed075f
--- /dev/null
+++ b/drivers/slimbus/slim-sched.c
@@ -0,0 +1,126 @@
+/* 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 080d86a..549f78f 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
  * @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.
@@ -265,6 +311,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;
@@ -285,12 +334,14 @@ struct slim_controller {
 	struct slim_ctrl_buf	rx;
 	struct slim_pending	*pending_wr;
 	struct semaphore	tx_sem;
+	struct slim_sched	sched;
 	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,
 					     struct slim_eaddr *ea, u8 *laddr);
+	int			(*wakeup)(struct slim_controller *ctrl);
 };
 
 #define to_slim_controller(d) container_of(d, struct slim_controller, dev)
@@ -444,7 +495,7 @@ int slim_processtxn(struct slim_controller *ctrl, struct slim_msg_txn *txn);
 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);
 
 static inline bool slim_tid_txn(u8 mt, u8 mc)
@@ -456,5 +507,6 @@ static inline bool slim_tid_txn(u8 mt, u8 mc)
 		 mc == SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION));
 }
 /* end of message apis */
+int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart);
 
 #endif /* _LINUX_SLIMBUS_H */
-- 
2.9.3

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

* [Patch v6 5/7] slimbus: qcom: Add runtime-pm support using clock-pause feature
  2017-10-06 15:51 [Patch v6 0/7] Introduce framework for SLIMbus device drivers srinivas.kandagatla
@ 2017-10-06 15:51     ` srinivas.kandagatla
  2017-10-06 15:51 ` [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver srinivas.kandagatla
                       ` (3 subsequent siblings)
  4 siblings, 0 replies; 87+ messages in thread
From: srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A @ 2017-10-06 15:51 UTC (permalink / raw)
  To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw
  Cc: sdharia-sgV2jX0FEOL9JmXXK+q4OQ, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, treding-DDmLM1+adcrQT0dZR+AlfA,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	jkosina-AlSwsSmVLrQ, sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kheitke-hxvC4TZJLZFWk0Htik3J/w,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4,
	Srinivas Kandagatla

From: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>

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-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 drivers/slimbus/slim-qcom-ctrl.c | 128 +++++++++++++++++++++++++++++++++++++--
 drivers/slimbus/slim-qcom.h      |   1 +
 2 files changed, 125 insertions(+), 4 deletions(-)

diff --git a/drivers/slimbus/slim-qcom-ctrl.c b/drivers/slimbus/slim-qcom-ctrl.c
index d0574e1..05ba44f 100644
--- a/drivers/slimbus/slim-qcom-ctrl.c
+++ b/drivers/slimbus/slim-qcom-ctrl.c
@@ -21,6 +21,7 @@
 #include <linux/of.h>
 #include <linux/dma-mapping.h>
 #include <linux/slimbus.h>
+#include <linux/pm_runtime.h>
 #include "slim-qcom.h"
 
 #define MSM_SLIM_NAME	"msm_slim_ctrl"
@@ -217,6 +218,30 @@ static irqreturn_t msm_slim_interrupt(int irq, void *d)
 	return ret;
 }
 
+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);
+	enable_irq(dev->irq);
+
+	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)
 {
@@ -286,7 +311,6 @@ static int msm_set_laddr(struct slim_controller *ctrl,
 	 */
 	msg.wbuf = buf;
 	msg.num_bytes = 7;
-
 	ret = slim_processtxn(&dev->ctrl, &txn);
 
 	if (ret)
@@ -417,6 +441,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;
@@ -532,6 +558,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;
 
@@ -562,14 +594,101 @@ static int msm_slim_remove(struct platform_device *pdev)
 	dma_free_coherent(&pdev->dev, (ctrl->tx.sl_sz * ctrl->tx.n),
 			  ctrl->tx.base, ctrl->tx.phy);
 
-	disable_irq(dev->irq);
-	clk_disable_unprepare(dev->rclk);
-	clk_disable_unprepare(dev->hclk);
+	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 {
+		disable_irq(dev->irq);
+		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",
@@ -584,6 +703,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
-- 
2.9.3

--
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] 87+ messages in thread

* [Patch v6 5/7] slimbus: qcom: Add runtime-pm support using clock-pause feature
@ 2017-10-06 15:51     ` srinivas.kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: srinivas.kandagatla @ 2017-10-06 15:51 UTC (permalink / raw)
  To: gregkh, broonie, alsa-devel
  Cc: sdharia, bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, jkosina, sharon.dvir1, joe, davem,
	james.hogan, michael.opdenacker, robh+dt, pawel.moll,
	mark.rutland, devicetree, linux-kernel, kheitke, linux-arm-msm,
	arnd, Srinivas Kandagatla

From: Sagar Dharia <sdharia@codeaurora.org>

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>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 drivers/slimbus/slim-qcom-ctrl.c | 128 +++++++++++++++++++++++++++++++++++++--
 drivers/slimbus/slim-qcom.h      |   1 +
 2 files changed, 125 insertions(+), 4 deletions(-)

diff --git a/drivers/slimbus/slim-qcom-ctrl.c b/drivers/slimbus/slim-qcom-ctrl.c
index d0574e1..05ba44f 100644
--- a/drivers/slimbus/slim-qcom-ctrl.c
+++ b/drivers/slimbus/slim-qcom-ctrl.c
@@ -21,6 +21,7 @@
 #include <linux/of.h>
 #include <linux/dma-mapping.h>
 #include <linux/slimbus.h>
+#include <linux/pm_runtime.h>
 #include "slim-qcom.h"
 
 #define MSM_SLIM_NAME	"msm_slim_ctrl"
@@ -217,6 +218,30 @@ static irqreturn_t msm_slim_interrupt(int irq, void *d)
 	return ret;
 }
 
+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);
+	enable_irq(dev->irq);
+
+	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)
 {
@@ -286,7 +311,6 @@ static int msm_set_laddr(struct slim_controller *ctrl,
 	 */
 	msg.wbuf = buf;
 	msg.num_bytes = 7;
-
 	ret = slim_processtxn(&dev->ctrl, &txn);
 
 	if (ret)
@@ -417,6 +441,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;
@@ -532,6 +558,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;
 
@@ -562,14 +594,101 @@ static int msm_slim_remove(struct platform_device *pdev)
 	dma_free_coherent(&pdev->dev, (ctrl->tx.sl_sz * ctrl->tx.n),
 			  ctrl->tx.base, ctrl->tx.phy);
 
-	disable_irq(dev->irq);
-	clk_disable_unprepare(dev->rclk);
-	clk_disable_unprepare(dev->hclk);
+	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 {
+		disable_irq(dev->irq);
+		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",
@@ -584,6 +703,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
-- 
2.9.3

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

* [Patch v6 6/7] regmap: add SLIMBUS support
  2017-10-06 15:51 [Patch v6 0/7] Introduce framework for SLIMbus device drivers srinivas.kandagatla
                   ` (3 preceding siblings ...)
       [not found] ` <20171006155136.4682-1-srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
@ 2017-10-06 15:51 ` srinivas.kandagatla
  2017-10-07  5:02   ` Jonathan Neuschäfer
  2017-10-20  5:00     ` Bjorn Andersson
  4 siblings, 2 replies; 87+ messages in thread
From: srinivas.kandagatla @ 2017-10-06 15:51 UTC (permalink / raw)
  To: gregkh, broonie, alsa-devel
  Cc: sdharia, bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, jkosina, sharon.dvir1, joe, davem,
	james.hogan, michael.opdenacker, robh+dt, pawel.moll,
	mark.rutland, devicetree, linux-kernel, kheitke, linux-arm-msm,
	arnd, Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds support to read/write slimbus value elements.
Currently it only supports byte read/write. Adding this support in
regmap would give codec drivers more flexibility when there are more
than 2 control interfaces like slimbus, i2c.

Without this patch each codec driver has to directly call slimbus value
element apis, and this could would get messy once we want to add i2c
interface to it.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 drivers/base/regmap/Kconfig          |  4 ++
 drivers/base/regmap/Makefile         |  1 +
 drivers/base/regmap/regmap-slimbus.c | 89 ++++++++++++++++++++++++++++++++++++
 include/linux/regmap.h               | 18 ++++++++
 4 files changed, 112 insertions(+)
 create mode 100644 drivers/base/regmap/regmap-slimbus.c

diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index 073c0b7..b79de7c 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -19,6 +19,10 @@ config REGMAP_I2C
 	tristate
 	depends on I2C
 
+config REGMAP_SLIMBUS
+	tristate
+	depends on SLIMBUS
+
 config REGMAP_SPI
 	tristate
 	depends on SPI
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index 0cf4abc..b16d367 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o
 obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
 obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
 obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
+obj-$(CONFIG_REGMAP_SLIMBUS) += regmap-slimbus.o
 obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
 obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
 obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o
diff --git a/drivers/base/regmap/regmap-slimbus.c b/drivers/base/regmap/regmap-slimbus.c
new file mode 100644
index 0000000..5ae24ab
--- /dev/null
+++ b/drivers/base/regmap/regmap-slimbus.c
@@ -0,0 +1,89 @@
+/*
+ * Register map access API - slimbus support
+ *
+ * Copyright 2017 Linaro Inc
+ *
+ * Author: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/regmap.h>
+#include <linux/slimbus.h>
+#include <linux/module.h>
+
+#include "internal.h"
+
+static int regmap_slimbus_byte_reg_read(void *context, unsigned int reg,
+					unsigned int *val)
+{
+	struct slim_device *slim = context;
+	struct slim_val_inf msg = {0,};
+
+	msg.start_offset = reg;
+	msg.num_bytes = 1;
+	msg.rbuf = (void *)val;
+
+	return slim_request_val_element(slim, &msg);
+}
+
+static int regmap_slimbus_byte_reg_write(void *context, unsigned int reg,
+					 unsigned int val)
+{
+	struct slim_device *slim = context;
+	struct slim_val_inf msg = {0,};
+
+	msg.start_offset = reg;
+	msg.num_bytes = 1;
+	msg.wbuf = (void *)&val;
+
+	return slim_change_val_element(slim, &msg);
+}
+
+static struct regmap_bus regmap_slimbus_bus = {
+	.reg_write = regmap_slimbus_byte_reg_write,
+	.reg_read = regmap_slimbus_byte_reg_read,
+};
+
+static const struct regmap_bus *regmap_get_slimbus(struct slim_device *slim,
+					const struct regmap_config *config)
+{
+	if (config->val_bits == 8 && config->reg_bits == 8)
+		return &regmap_slimbus_bus;
+
+	return ERR_PTR(-ENOTSUPP);
+}
+
+struct regmap *__regmap_init_slimbus(struct slim_device *slimbus,
+				     const struct regmap_config *config,
+				     struct lock_class_key *lock_key,
+				     const char *lock_name)
+{
+	const struct regmap_bus *bus = regmap_get_slimbus(slimbus, config);
+
+	if (IS_ERR(bus))
+		return ERR_CAST(bus);
+
+	return __regmap_init(&slimbus->dev, bus, &slimbus->dev, config,
+			     lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__regmap_init_slimbus);
+
+struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus,
+					  const struct regmap_config *config,
+					  struct lock_class_key *lock_key,
+					  const char *lock_name)
+{
+	const struct regmap_bus *bus = regmap_get_slimbus(slimbus, config);
+
+	if (IS_ERR(bus))
+		return ERR_CAST(bus);
+
+	return __devm_regmap_init(&slimbus->dev, bus, &slimbus, config,
+				  lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__devm_regmap_init_slimbus);
+
+MODULE_LICENSE("GPL");
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 978abfb..49c8db1 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -24,6 +24,7 @@ struct module;
 struct device;
 struct i2c_client;
 struct irq_domain;
+struct slim_device;
 struct spi_device;
 struct spmi_device;
 struct regmap;
@@ -449,6 +450,10 @@ struct regmap *__regmap_init_i2c(struct i2c_client *i2c,
 				 const struct regmap_config *config,
 				 struct lock_class_key *lock_key,
 				 const char *lock_name);
+struct regmap *__regmap_init_slimbus(struct slim_device *slimbus,
+				 const struct regmap_config *config,
+				 struct lock_class_key *lock_key,
+				 const char *lock_name);
 struct regmap *__regmap_init_spi(struct spi_device *dev,
 				 const struct regmap_config *config,
 				 struct lock_class_key *lock_key,
@@ -566,6 +571,19 @@ int regmap_attach_dev(struct device *dev, struct regmap *map,
 				i2c, config)
 
 /**
+ * regmap_init_slimbus() - Initialise register map
+ *
+ * @slimbus: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+#define regmap_init_slimbus(slimbus, config)				\
+	__regmap_lockdep_wrapper(__regmap_init_slimbus, #config,	\
+				slimbus, config)
+
+/**
  * regmap_init_spi() - Initialise register map
  *
  * @dev: Device that will be interacted with
-- 
2.9.3

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

* [Patch v6 7/7] MAINTAINERS: Add SLIMbus maintainer
  2017-10-06 15:51 [Patch v6 0/7] Introduce framework for SLIMbus device drivers srinivas.kandagatla
@ 2017-10-06 15:51     ` srinivas.kandagatla
  2017-10-06 15:51 ` [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver srinivas.kandagatla
                       ` (3 subsequent siblings)
  4 siblings, 0 replies; 87+ messages in thread
From: srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A @ 2017-10-06 15:51 UTC (permalink / raw)
  To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw
  Cc: sdharia-sgV2jX0FEOL9JmXXK+q4OQ, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, treding-DDmLM1+adcrQT0dZR+AlfA,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	jkosina-AlSwsSmVLrQ, sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kheitke-hxvC4TZJLZFWk0Htik3J/w,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4,
	Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Add myself as maintainer for slimbus.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 2281af4..014f74b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12320,6 +12320,14 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
 F:	include/linux/srcu.h
 F:	kernel/rcu/srcu.c
 
+SERIAL LOW-POWER INTER-CHIP MEDIA BUS (SLIMbus)
+M:	Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
+L:	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw@public.gmane.org (moderated for non-subscribers)
+S:	Maintained
+F:	drivers/slimbus/
+F:	Documentation/devicetree/bindings/slimbus/
+F:	include/linux/slimbus.h
+
 SMACK SECURITY MODULE
 M:	Casey Schaufler <casey-iSGtlc1asvQWG2LlvL+J4A@public.gmane.org>
 L:	linux-security-module-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
-- 
2.9.3

--
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] 87+ messages in thread

* [Patch v6 7/7] MAINTAINERS: Add SLIMbus maintainer
@ 2017-10-06 15:51     ` srinivas.kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: srinivas.kandagatla @ 2017-10-06 15:51 UTC (permalink / raw)
  To: gregkh, broonie, alsa-devel
  Cc: sdharia, bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, jkosina, sharon.dvir1, joe, davem,
	james.hogan, michael.opdenacker, robh+dt, pawel.moll,
	mark.rutland, devicetree, linux-kernel, kheitke, linux-arm-msm,
	arnd, Srinivas Kandagatla

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

Add myself as maintainer for slimbus.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 2281af4..014f74b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12320,6 +12320,14 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
 F:	include/linux/srcu.h
 F:	kernel/rcu/srcu.c
 
+SERIAL LOW-POWER INTER-CHIP MEDIA BUS (SLIMbus)
+M:	Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
+S:	Maintained
+F:	drivers/slimbus/
+F:	Documentation/devicetree/bindings/slimbus/
+F:	include/linux/slimbus.h
+
 SMACK SECURITY MODULE
 M:	Casey Schaufler <casey@schaufler-ca.com>
 L:	linux-security-module@vger.kernel.org
-- 
2.9.3

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-06 15:51   ` srinivas.kandagatla
@ 2017-10-07  4:14       ` Jonathan Neuschäfer
  -1 siblings, 0 replies; 87+ messages in thread
From: Jonathan Neuschäfer @ 2017-10-07  4:14 UTC (permalink / raw)
  To: srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A
  Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	sdharia-sgV2jX0FEOL9JmXXK+q4OQ, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, treding-DDmLM1+adcrQT0dZR+AlfA,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	jkosina-AlSwsSmVLrQ, sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kheitke-hxvC4TZJLZFWk0Htik3J/w,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4

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

Hi, I have some more or less trivial comments below.

On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org wrote:
> From: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> 
> 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). 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>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
[...]
> +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";
> +		#address-cells = <2>;
> +		#size-cells = <0>;
> +
> +		codec: wcd9310@1{
> +			compatible = "slim217,60"";
			                        ^ spurious quote?

> +			reg = <1 0>;
> +		};
> +	};
> diff --git a/Documentation/slimbus/summary b/Documentation/slimbus/summary
> new file mode 100644
> index 0000000..e7f90bb
> --- /dev/null
> +++ b/Documentation/slimbus/summary

Should this file have a .rst extension, like other Restructured Text
files?

> @@ -0,0 +1,109 @@
> +Overview of Linux kernel SLIMbus support
> +========================================
[...]
> +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

s/devie/device/ I guess

> +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 controller.
[...]
> +/**
> + * 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;
> +};

I wonder if valid should be moved after eaddr, to reduce the need for
padding. AFAICS, struct slim_eaddr is 6 bytes long and requires 2-byte
alignment, so if valid is one byte long, there would be one byte of
padding after it, slightly bloating struct slim_addrt, unnecessarily.

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

s/if a device/of 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

I don't see list in the struct.

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

I don't see xfer_msg in the struct.

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


Thanks,
Jonathan Neuschäfer

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

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-07  4:14       ` Jonathan Neuschäfer
  0 siblings, 0 replies; 87+ messages in thread
From: Jonathan Neuschäfer @ 2017-10-07  4:14 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

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

Hi, I have some more or less trivial comments below.

On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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). 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@codeaurora.org>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
[...]
> +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";
> +		#address-cells = <2>;
> +		#size-cells = <0>;
> +
> +		codec: wcd9310@1{
> +			compatible = "slim217,60"";
			                        ^ spurious quote?

> +			reg = <1 0>;
> +		};
> +	};
> diff --git a/Documentation/slimbus/summary b/Documentation/slimbus/summary
> new file mode 100644
> index 0000000..e7f90bb
> --- /dev/null
> +++ b/Documentation/slimbus/summary

Should this file have a .rst extension, like other Restructured Text
files?

> @@ -0,0 +1,109 @@
> +Overview of Linux kernel SLIMbus support
> +========================================
[...]
> +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

s/devie/device/ I guess

> +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 controller.
[...]
> +/**
> + * 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;
> +};

I wonder if valid should be moved after eaddr, to reduce the need for
padding. AFAICS, struct slim_eaddr is 6 bytes long and requires 2-byte
alignment, so if valid is one byte long, there would be one byte of
padding after it, slightly bloating struct slim_addrt, unnecessarily.

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

s/if a device/of 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

I don't see list in the struct.

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

I don't see xfer_msg in the struct.

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


Thanks,
Jonathan Neuschäfer

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

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

* Re: [Patch v6 6/7] regmap: add SLIMBUS support
  2017-10-06 15:51 ` [Patch v6 6/7] regmap: add SLIMBUS support srinivas.kandagatla
@ 2017-10-07  5:02   ` Jonathan Neuschäfer
  2017-10-07 10:25     ` Srinivas Kandagatla
  2017-10-20  5:00     ` Bjorn Andersson
  1 sibling, 1 reply; 87+ messages in thread
From: Jonathan Neuschäfer @ 2017-10-07  5:02 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

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

Hi,

On Fri, Oct 06, 2017 at 05:51:35PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> 
> This patch adds support to read/write slimbus value elements.
> Currently it only supports byte read/write. Adding this support in
> regmap would give codec drivers more flexibility when there are more
> than 2 control interfaces like slimbus, i2c.
> 
> Without this patch each codec driver has to directly call slimbus value
> element apis, and this could would get messy once we want to add i2c
> interface to it.
> 
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
[...]
> +static int regmap_slimbus_byte_reg_read(void *context, unsigned int reg,
> +					unsigned int *val)
> +{
> +	struct slim_device *slim = context;
> +	struct slim_val_inf msg = {0,};
> +
> +	msg.start_offset = reg;
> +	msg.num_bytes = 1;
> +	msg.rbuf = (void *)val;
> +
> +	return slim_request_val_element(slim, &msg);
> +}

This looks like it won't work on big-endian systems. I know big endian
is pretty uncommon in devices that will likely have SLIMBus, but it's
better to be endian-independent.

> +static int regmap_slimbus_byte_reg_write(void *context, unsigned int reg,
> +					 unsigned int val)
> +{
> +	struct slim_device *slim = context;
> +	struct slim_val_inf msg = {0,};
> +
> +	msg.start_offset = reg;
> +	msg.num_bytes = 1;
> +	msg.wbuf = (void *)&val;
> +
> +	return slim_change_val_element(slim, &msg);
> +}

dito

> +static struct regmap_bus regmap_slimbus_bus = {
> +	.reg_write = regmap_slimbus_byte_reg_write,
> +	.reg_read = regmap_slimbus_byte_reg_read,
> +};


Thanks,
Jonathan Neuschäfer

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

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

* Re: [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-06 15:51     ` srinivas.kandagatla
  (?)
@ 2017-10-07  6:42     ` Jonathan Neuschäfer
  2017-10-07 10:24         ` Srinivas Kandagatla
  -1 siblings, 1 reply; 87+ messages in thread
From: Jonathan Neuschäfer @ 2017-10-07  6:42 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

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

Hi,

On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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

s/based of/based on/
s/coerently/coherently/

> 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>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
[...]
> +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));

Shouldn't this be (cur | (1 << 3)?
(Also, what does cur mean? Cursor? Current?)

> +
> +	switch (mc) {
> +	case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
> +	case SLIM_MSG_MC_CHANGE_VALUE:
> +	case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
> +	case SLIM_MSG_MC_CLEAR_INFORMATION:
> +		txn->rl += msg->num_bytes;
> +	default:
> +		break;
> +	}
> +
> +	if (slim_tid_txn(txn->mt, txn->mc))
> +		txn->rl++;
> +
> +	return slim_processtxn(ctrl, txn);
> +}
> +EXPORT_SYMBOL_GPL(slim_xfer_msg);
[...]
> +/*
> + * slim_request_val_element: change and request a given value element
> + * @sb: client handle requesting elemental message reads, writes.
> + * @msg: Input structure for start-offset, number of bytes to write.
> + * 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.

Does rbuf contain the old value after this function finishes?

> + */
> +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);
[...]
> +/**
> + * struct slim_pending: context of pending transfers
> + * @cb: callback for this transfer
> + * @ctx: contex for the callback function

s/contex/context/

> + * @need_tid: True if this transfer need Transaction ID
> + */
> +struct slim_pending {
> +	void (*cb)(void *ctx, int err);
> +	void *ctx;
> +	bool need_tid;
> +};


Thanks,
Jonathan Neuschäfer

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

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

* Re: [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver
  2017-10-06 15:51 ` [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver srinivas.kandagatla
@ 2017-10-07  7:45   ` Jonathan Neuschäfer
  2017-10-07 10:24       ` Srinivas Kandagatla
  2017-10-13 19:17   ` Rob Herring
  2017-10-18  7:27   ` Bjorn Andersson
  2 siblings, 1 reply; 87+ messages in thread
From: Jonathan Neuschäfer @ 2017-10-07  7:45 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

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

Hi,

On Fri, Oct 06, 2017 at 05:51:32PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  .../devicetree/bindings/slimbus/slim-qcom-ctrl.txt |  43 ++
>  drivers/slimbus/Kconfig                            |   9 +
>  drivers/slimbus/Makefile                           |   3 +
>  drivers/slimbus/slim-qcom-ctrl.c                   | 594 +++++++++++++++++++++
>  drivers/slimbus/slim-qcom.h                        |  63 +++
>  5 files changed, 712 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..081110d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt
> @@ -0,0 +1,43 @@
> +Qualcomm SLIMBUS controller
> +This controller is used if applications processor driver controls slimbus
> +master component.
> +
> +Required properties:
> +
> + - #address-cells - refer to Documentation/devicetree/bindings/slimbus/bus.txt
> + - #size-cells	- refer to Documentation/devicetree/bindings/slimbus/bus.txt
> +
> + - 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:
> +	 "ctrl": Physical adderess of controller register blocks

s/adderess/address/

> + - compatible : should be "qcom,<SOC-NAME>-slim" for SOC specific compatible or
> + 		"qcom,slim" if using generic qcom SLIM IP.
> + - 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
[...]

> +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, ret = IRQ_NONE;
> +
> +	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;
> +		}
> +		/**

This isn't really a kerneldoc comment…

> +		 * Guarantee that interrupt clear bit write goes through before
> +		 * signalling completion/exiting ISR
> +		 */
> +		mb();
> +		slim_return_tx(&dev->ctrl, err);
> +		ret = IRQ_HANDLED;
> +	}
> +	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);
> +
> +		/**

ditto

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

ditto

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

ditto

> +		 * Guarantee that CLR bit write goes through
> +		 * before exiting
> +		 */
> +		mb();
> +		if (q_rx)
> +			queue_work(dev->rxwq, &dev->wd);
> +
> +		ret = IRQ_HANDLED;
> +	}
> +	return ret;
> +}
[...]
> +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;
> +
> +	/**

ditto

> +	 * 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_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,
> +						     "slew");
> +	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));

How often will the driver program a slew rate?

If it's often, you'll have a "soft" memory leak over the life time of a
SLIM controller instance, because the mappings for slew_reg will
accumulate in the driver instance's devm area until they are all freed
in the end (If I'm reading the code correctly). I think you'll either
have to unmap slew_reg when this function returns (and not use devm), or
cache slew_reg so that subsequent calls to msm_slim_prg_slew won't
create more mappings.

> +	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 slim_controller *ctrl;
> +	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, "ctrl");
> +	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;
> +	ctrl = &dev->ctrl;
> +	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;
> +
> +
> +	ctrl->tx.base = dma_alloc_coherent(&pdev->dev,
> +					   (ctrl->tx.sl_sz * ctrl->tx.n),
> +					   &ctrl->tx.phy, GFP_KERNEL);

Use dmam_alloc_coherent?

> +	if (!ctrl->tx.base) {
> +		ret = -ENOMEM;
> +		goto tx_alloc_failed;
> +	}
> +
> +	ctrl->rx.base = dma_alloc_coherent(&pdev->dev,
> +					   (ctrl->rx.sl_sz * ctrl->rx.n),
> +					   &ctrl->rx.phy, GFP_KERNEL);

ditto

> +	if (!ctrl->rx.base) {
> +		ret = -ENOMEM;
> +		goto rx_alloc_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:
> +	dma_free_coherent(&pdev->dev, (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:
> +	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;
> +}


Thanks,
Jonathan Neuschäfer

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

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

* Re: [Patch v6 4/7] slimbus: Add support for 'clock-pause' feature
  2017-10-06 15:51 ` [Patch v6 4/7] slimbus: Add support for 'clock-pause' feature srinivas.kandagatla
@ 2017-10-07  8:06   ` Jonathan Neuschäfer
  2017-10-07 10:24     ` Srinivas Kandagatla
  0 siblings, 1 reply; 87+ messages in thread
From: Jonathan Neuschäfer @ 2017-10-07  8:06 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

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

Hi, some trivial comments below.

On Fri, Oct 06, 2017 at 05:51:33PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> Per slimbus specification, a reconfiguration sequence known as
> 'clock pause' needs to be broadcast over the bus while entering low-
> power mode. Clock-pause is initiated by the controller driver.
> To exit clock-pause, controller typically wakes up the framer device.
> Since wakeup precedure is controller-specific, framework calls it via
> controller's function pointer to invoke it.
> 
> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
[...]
> @@ -429,6 +444,14 @@ void slim_return_tx(struct slim_controller *ctrl, int err)
>  		cur.cb(cur.ctx, err);
>  
>  	up(&ctrl->tx_sem);
> +	if (!cur.clk_pause && (!cur.need_tid || err)) {
> +		/**

This isn't really a kerneldoc comment.

> +		 * 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);
>  
[...]
> +/**
> + * 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;
> +		}
> +
> +		/**

ditto

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

ditto

> +		 * 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;
> +	}
[...]


Thanks,
Jonathan Neuschäfer

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

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

* Re: [Patch v6 5/7] slimbus: qcom: Add runtime-pm support using clock-pause feature
  2017-10-06 15:51     ` srinivas.kandagatla
  (?)
@ 2017-10-07  8:22     ` Jonathan Neuschäfer
  2017-10-07 10:25       ` Srinivas Kandagatla
  -1 siblings, 1 reply; 87+ messages in thread
From: Jonathan Neuschäfer @ 2017-10-07  8:22 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

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

Hi, some more trivial comments below.

On Fri, Oct 06, 2017 at 05:51:34PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
[...]
> +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);
> +	enable_irq(dev->irq);
> +
> +	writel_relaxed(1, dev->base + FRM_WAKEUP);
> +	/* Make sure framer wakeup write goes through before ISR fires */
> +	mb();
> +	/**

This isn't really a kerneldoc comment.

> +	 * 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;
> +}
[...]
> +#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) {
> +	/**

ditto.
Also, it looks misindented.

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


Thanks,
Jonathan Neuschäfer

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

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-07  4:14       ` Jonathan Neuschäfer
@ 2017-10-07 10:24         ` Srinivas Kandagatla
  -1 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-07 10:24 UTC (permalink / raw)
  To: Jonathan Neuschäfer
  Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	sdharia-sgV2jX0FEOL9JmXXK+q4OQ, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, treding-DDmLM1+adcrQT0dZR+AlfA,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	jkosina-AlSwsSmVLrQ, sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kheitke-hxvC4TZJLZFWk0Htik3J/w,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4

Thanks for the comments.

On 07/10/17 05:14, Jonathan Neuschäfer wrote:
> Hi, I have some more or less trivial comments below.
> 
> On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org wrote:
>> From: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>>
>> 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). 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>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> ---
> [...]
>> +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";
>> +		#address-cells = <2>;
>> +		#size-cells = <0>;
>> +
>> +		codec: wcd9310@1{
>> +			compatible = "slim217,60"";
> 			                        ^ spurious quote?
> 
>> +			reg = <1 0>;
>> +		};
>> +	};
>> diff --git a/Documentation/slimbus/summary b/Documentation/slimbus/summary
>> new file mode 100644
>> index 0000000..e7f90bb
>> --- /dev/null
>> +++ b/Documentation/slimbus/summary
> 
> Should this file have a .rst extension, like other Restructured Text
> files?

Will try to sort this out in next version.

> 
>> @@ -0,0 +1,109 @@
>> +Overview of Linux kernel SLIMbus support
>> +========================================
> [...]
>> +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
> 
> s/devie/device/ I guess
> 
>> +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 controller.
> [...]
>> +/**
>> + * 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;
>> +};
> 
> I wonder if valid should be moved after eaddr, to reduce the need for
> padding. AFAICS, struct slim_eaddr is 6 bytes long and requires 2-byte
> alignment, so if valid is one byte long, there would be one byte of
> padding after it, slightly bloating struct slim_addrt, unnecessarily.
Makes sense!!

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

> s/if a device/of a device/ ?

Yep, will fix this in next version.
> 
>> + *	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
> 
> I don't see list in the struct.
I think its a left over of the cleanup.. will fix this in next version.
> 
>> + * @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
>> + * @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.
> 
> I don't see xfer_msg in the struct.
Will add in next version.

> 
>> + * @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;
>> +	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;
>> +	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);
>> +};
> 
> 
> Thanks,
> Jonathan Neuschäfer
> 
--
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] 87+ messages in thread

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-07 10:24         ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-07 10:24 UTC (permalink / raw)
  To: Jonathan Neuschäfer
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

Thanks for the comments.

On 07/10/17 05:14, Jonathan Neuschäfer wrote:
> Hi, I have some more or less trivial comments below.
> 
> On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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). 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@codeaurora.org>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
> [...]
>> +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";
>> +		#address-cells = <2>;
>> +		#size-cells = <0>;
>> +
>> +		codec: wcd9310@1{
>> +			compatible = "slim217,60"";
> 			                        ^ spurious quote?
> 
>> +			reg = <1 0>;
>> +		};
>> +	};
>> diff --git a/Documentation/slimbus/summary b/Documentation/slimbus/summary
>> new file mode 100644
>> index 0000000..e7f90bb
>> --- /dev/null
>> +++ b/Documentation/slimbus/summary
> 
> Should this file have a .rst extension, like other Restructured Text
> files?

Will try to sort this out in next version.

> 
>> @@ -0,0 +1,109 @@
>> +Overview of Linux kernel SLIMbus support
>> +========================================
> [...]
>> +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
> 
> s/devie/device/ I guess
> 
>> +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 controller.
> [...]
>> +/**
>> + * 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;
>> +};
> 
> I wonder if valid should be moved after eaddr, to reduce the need for
> padding. AFAICS, struct slim_eaddr is 6 bytes long and requires 2-byte
> alignment, so if valid is one byte long, there would be one byte of
> padding after it, slightly bloating struct slim_addrt, unnecessarily.
Makes sense!!

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

> s/if a device/of a device/ ?

Yep, will fix this in next version.
> 
>> + *	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
> 
> I don't see list in the struct.
I think its a left over of the cleanup.. will fix this in next version.
> 
>> + * @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
>> + * @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.
> 
> I don't see xfer_msg in the struct.
Will add in next version.

> 
>> + * @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;
>> +	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;
>> +	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);
>> +};
> 
> 
> Thanks,
> Jonathan Neuschäfer
> 

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

* Re: [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-07  6:42     ` Jonathan Neuschäfer
@ 2017-10-07 10:24         ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-07 10:24 UTC (permalink / raw)
  To: Jonathan Neuschäfer
  Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	sdharia-sgV2jX0FEOL9JmXXK+q4OQ, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, treding-DDmLM1+adcrQT0dZR+AlfA,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	jkosina-AlSwsSmVLrQ, sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kheitke-hxvC4TZJLZFWk0Htik3J/w,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4

Thanks for the comments.

On 07/10/17 07:42, Jonathan Neuschäfer wrote:
> Hi,
> 
> On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org wrote:
>> From: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>>
>> 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
> 
> s/based of/based on/
> s/coerently/coherently/
Yep.. will fix this in next version.
> 
>> 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-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>> Tested-by: Naveen Kaje <nkaje-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> ---
> [...]
>> +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));
> 
> Shouldn't this be (cur | (1 << 3)?
cur seems to be redundant TBH, the only difference between cur and sl is 
that the slim_slicesize() can give slice size to program for any lengths 
between 1-16 bytes. However the slim_slicecodefromsize() can only 
handle 1,2,3,4, 6,8,12,16 byte sizes.

So we can delete slim_slicecodefromsize() call and function together.
looks like it was a leftover from downstream.

> (Also, what does cur mean? Cursor? Current?)
No Idea!! :-) it is supposed to return slice size as per number of bytes.

> 
>> +
>> +	switch (mc) {
>> +	case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
>> +	case SLIM_MSG_MC_CHANGE_VALUE:
>> +	case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
>> +	case SLIM_MSG_MC_CLEAR_INFORMATION:
>> +		txn->rl += msg->num_bytes;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	if (slim_tid_txn(txn->mt, txn->mc))
>> +		txn->rl++;
>> +
>> +	return slim_processtxn(ctrl, txn);
>> +}
>> +EXPORT_SYMBOL_GPL(slim_xfer_msg);
> [...]
>> +/*
>> + * slim_request_val_element: change and request a given value element

name should be fixed here..
>> + * @sb: client handle requesting elemental message reads, writes.
>> + * @msg: Input structure for start-offset, number of bytes to write.
>> + * 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.
> 
> Does rbuf contain the old value after this function finishes?
> 
Yep, device should send a reply value with the old value with matching tid.

>> + */
>> +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);
> [...]
>> +/**
>> + * struct slim_pending: context of pending transfers
>> + * @cb: callback for this transfer
>> + * @ctx: contex for the callback function
> 
> s/contex/context/
> 
Will fix all these instances.
>> + * @need_tid: True if this transfer need Transaction ID
>> + */
>> +struct slim_pending {
>> +	void (*cb)(void *ctx, int err);
>> +	void *ctx;
>> +	bool need_tid;
>> +};
> 
> 
> Thanks,
> Jonathan Neuschäfer
> 
--
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] 87+ messages in thread

* Re: [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
@ 2017-10-07 10:24         ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-07 10:24 UTC (permalink / raw)
  To: Jonathan Neuschäfer
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

Thanks for the comments.

On 07/10/17 07:42, Jonathan Neuschäfer wrote:
> Hi,
> 
> On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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
> 
> s/based of/based on/
> s/coerently/coherently/
Yep.. will fix this in next version.
> 
>> 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>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
> [...]
>> +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));
> 
> Shouldn't this be (cur | (1 << 3)?
cur seems to be redundant TBH, the only difference between cur and sl is 
that the slim_slicesize() can give slice size to program for any lengths 
between 1-16 bytes. However the slim_slicecodefromsize() can only 
handle 1,2,3,4, 6,8,12,16 byte sizes.

So we can delete slim_slicecodefromsize() call and function together.
looks like it was a leftover from downstream.

> (Also, what does cur mean? Cursor? Current?)
No Idea!! :-) it is supposed to return slice size as per number of bytes.

> 
>> +
>> +	switch (mc) {
>> +	case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
>> +	case SLIM_MSG_MC_CHANGE_VALUE:
>> +	case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
>> +	case SLIM_MSG_MC_CLEAR_INFORMATION:
>> +		txn->rl += msg->num_bytes;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	if (slim_tid_txn(txn->mt, txn->mc))
>> +		txn->rl++;
>> +
>> +	return slim_processtxn(ctrl, txn);
>> +}
>> +EXPORT_SYMBOL_GPL(slim_xfer_msg);
> [...]
>> +/*
>> + * slim_request_val_element: change and request a given value element

name should be fixed here..
>> + * @sb: client handle requesting elemental message reads, writes.
>> + * @msg: Input structure for start-offset, number of bytes to write.
>> + * 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.
> 
> Does rbuf contain the old value after this function finishes?
> 
Yep, device should send a reply value with the old value with matching tid.

>> + */
>> +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);
> [...]
>> +/**
>> + * struct slim_pending: context of pending transfers
>> + * @cb: callback for this transfer
>> + * @ctx: contex for the callback function
> 
> s/contex/context/
> 
Will fix all these instances.
>> + * @need_tid: True if this transfer need Transaction ID
>> + */
>> +struct slim_pending {
>> +	void (*cb)(void *ctx, int err);
>> +	void *ctx;
>> +	bool need_tid;
>> +};
> 
> 
> Thanks,
> Jonathan Neuschäfer
> 

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

* Re: [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver
  2017-10-07  7:45   ` Jonathan Neuschäfer
@ 2017-10-07 10:24       ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-07 10:24 UTC (permalink / raw)
  To: Jonathan Neuschäfer
  Cc: mark.rutland, alsa-devel, michael.opdenacker, poeschel,
	andreas.noever, gong.chen, arnd, kheitke, treding, devicetree,
	james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1, robh+dt,
	sdharia, alan, bp, mathieu.poirier, gregkh, linux-kernel,
	broonie, daniel, jkosina, joe, davem

Thanks for the review comments

On 07/10/17 08:45, Jonathan Neuschäfer wrote:
> Hi,
> 
> On Fri, Oct 06, 2017 at 05:51:32PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
>>   .../devicetree/bindings/slimbus/slim-qcom-ctrl.txt |  43 ++
>>   drivers/slimbus/Kconfig                            |   9 +
>>   drivers/slimbus/Makefile                           |   3 +
>>   drivers/slimbus/slim-qcom-ctrl.c                   | 594 +++++++++++++++++++++
>>   drivers/slimbus/slim-qcom.h                        |  63 +++
>>   5 files changed, 712 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..081110d
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt
>> @@ -0,0 +1,43 @@
>> +Qualcomm SLIMBUS controller
>> +This controller is used if applications processor driver controls slimbus
>> +master component.
>> +
>> +Required properties:
>> +
>> + - #address-cells - refer to Documentation/devicetree/bindings/slimbus/bus.txt
>> + - #size-cells	- refer to Documentation/devicetree/bindings/slimbus/bus.txt
>> +
>> + - 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:
>> +	 "ctrl": Physical adderess of controller register blocks
> 
> s/adderess/address/
Will fix this in next version.

> 
>> +}
> [...]
>> +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,
>> +						     "slew");
>> +	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));
> 
> How often will the driver program a slew rate?
> 
This should be programmed only once after power on.

> If it's often, you'll have a "soft" memory leak over the life time of a
> SLIM controller instance, because the mappings for slew_reg will
> accumulate in the driver instance's devm area until they are all freed
> in the end (If I'm reading the code correctly). I think you'll either
> have to unmap slew_reg when this function returns (and not use devm), or
> cache slew_reg so that subsequent calls to msm_slim_prg_slew won't
> create more mappings.
Yep .. I revisit this part once again before sending next version to see 
if we can do any better!
> 
>> +	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 slim_controller *ctrl;
>> +	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, "ctrl");
>> +	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;
>> +	ctrl = &dev->ctrl;
>> +	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;
>> +
>> +
>> +	ctrl->tx.base = dma_alloc_coherent(&pdev->dev,
>> +					   (ctrl->tx.sl_sz * ctrl->tx.n),
>> +					   &ctrl->tx.phy, GFP_KERNEL);
> 
> Use dmam_alloc_coherent?
> 
Yep, makes sense.. Will use it as suggested.

>> +	if (!ctrl->tx.base) {
>> +		ret = -ENOMEM;
>> +		goto tx_alloc_failed;
>> +	}
>> +
>> +	ctrl->rx.base = dma_alloc_coherent(&pdev->dev,
>> +					   (ctrl->rx.sl_sz * ctrl->rx.n),
>> +					   &ctrl->rx.phy, GFP_KERNEL);
> 
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

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

* Re: [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver
@ 2017-10-07 10:24       ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-07 10:24 UTC (permalink / raw)
  To: Jonathan Neuschäfer
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

Thanks for the review comments

On 07/10/17 08:45, Jonathan Neuschäfer wrote:
> Hi,
> 
> On Fri, Oct 06, 2017 at 05:51:32PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
>>   .../devicetree/bindings/slimbus/slim-qcom-ctrl.txt |  43 ++
>>   drivers/slimbus/Kconfig                            |   9 +
>>   drivers/slimbus/Makefile                           |   3 +
>>   drivers/slimbus/slim-qcom-ctrl.c                   | 594 +++++++++++++++++++++
>>   drivers/slimbus/slim-qcom.h                        |  63 +++
>>   5 files changed, 712 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..081110d
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt
>> @@ -0,0 +1,43 @@
>> +Qualcomm SLIMBUS controller
>> +This controller is used if applications processor driver controls slimbus
>> +master component.
>> +
>> +Required properties:
>> +
>> + - #address-cells - refer to Documentation/devicetree/bindings/slimbus/bus.txt
>> + - #size-cells	- refer to Documentation/devicetree/bindings/slimbus/bus.txt
>> +
>> + - 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:
>> +	 "ctrl": Physical adderess of controller register blocks
> 
> s/adderess/address/
Will fix this in next version.

> 
>> +}
> [...]
>> +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,
>> +						     "slew");
>> +	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));
> 
> How often will the driver program a slew rate?
> 
This should be programmed only once after power on.

> If it's often, you'll have a "soft" memory leak over the life time of a
> SLIM controller instance, because the mappings for slew_reg will
> accumulate in the driver instance's devm area until they are all freed
> in the end (If I'm reading the code correctly). I think you'll either
> have to unmap slew_reg when this function returns (and not use devm), or
> cache slew_reg so that subsequent calls to msm_slim_prg_slew won't
> create more mappings.
Yep .. I revisit this part once again before sending next version to see 
if we can do any better!
> 
>> +	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 slim_controller *ctrl;
>> +	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, "ctrl");
>> +	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;
>> +	ctrl = &dev->ctrl;
>> +	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;
>> +
>> +
>> +	ctrl->tx.base = dma_alloc_coherent(&pdev->dev,
>> +					   (ctrl->tx.sl_sz * ctrl->tx.n),
>> +					   &ctrl->tx.phy, GFP_KERNEL);
> 
> Use dmam_alloc_coherent?
> 
Yep, makes sense.. Will use it as suggested.

>> +	if (!ctrl->tx.base) {
>> +		ret = -ENOMEM;
>> +		goto tx_alloc_failed;
>> +	}
>> +
>> +	ctrl->rx.base = dma_alloc_coherent(&pdev->dev,
>> +					   (ctrl->rx.sl_sz * ctrl->rx.n),
>> +					   &ctrl->rx.phy, GFP_KERNEL);
> 

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

* Re: [Patch v6 4/7] slimbus: Add support for 'clock-pause' feature
  2017-10-07  8:06   ` Jonathan Neuschäfer
@ 2017-10-07 10:24     ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-07 10:24 UTC (permalink / raw)
  To: Jonathan Neuschäfer
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

Thanks for the review comments



On 07/10/17 09:06, Jonathan Neuschäfer wrote:
> Hi, some trivial comments below.
> 
> On Fri, Oct 06, 2017 at 05:51:33PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> Per slimbus specification, a reconfiguration sequence known as
>> 'clock pause' needs to be broadcast over the bus while entering low-
>> power mode. Clock-pause is initiated by the controller driver.
>> To exit clock-pause, controller typically wakes up the framer device.
>> Since wakeup precedure is controller-specific, framework calls it via
>> controller's function pointer to invoke it.
>>
>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
> [...]
>> @@ -429,6 +444,14 @@ void slim_return_tx(struct slim_controller *ctrl, int err)
>>   		cur.cb(cur.ctx, err);
>>   
>>   	up(&ctrl->tx_sem);
>> +	if (!cur.clk_pause && (!cur.need_tid || err)) {
>> +		/**
> 
> This isn't really a kerneldoc comment.
Will fix all such instances in next version.
> 
>> +		 * 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);
>>   
> [...]
>> +/**
>> + * 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;
>> +		}
>> +
>> +		/**
> 
> ditto
> 
>> +		 * 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;
>> +
>> +		/**
> 
> ditto
> 
>> +		 * 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;
>> +	}
> [...]
> 
> 
> Thanks,
> Jonathan Neuschäfer
> 

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

* Re: [Patch v6 5/7] slimbus: qcom: Add runtime-pm support using clock-pause feature
  2017-10-07  8:22     ` Jonathan Neuschäfer
@ 2017-10-07 10:25       ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-07 10:25 UTC (permalink / raw)
  To: Jonathan Neuschäfer
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

Thanks for the review comments

On 07/10/17 09:22, Jonathan Neuschäfer wrote:
> Hi, some more trivial comments below.
> 
> On Fri, Oct 06, 2017 at 05:51:34PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
> [...]
>> +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);
>> +	enable_irq(dev->irq);
>> +
>> +	writel_relaxed(1, dev->base + FRM_WAKEUP);
>> +	/* Make sure framer wakeup write goes through before ISR fires */
>> +	mb();
>> +	/**
> 
> This isn't really a kerneldoc comment.
Yep I agree will fix all such instances.

> 
>> +	 * 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;
>> +}
> [...]
>> +#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) {
>> +	/**
> 
> ditto.
> Also, it looks misindented.
Yep. will fix this too.

> 
>> +	 * 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;
>> +}
> 
> 
> Thanks,
> Jonathan Neuschäfer
> 

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

* Re: [Patch v6 6/7] regmap: add SLIMBUS support
  2017-10-07  5:02   ` Jonathan Neuschäfer
@ 2017-10-07 10:25     ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-07 10:25 UTC (permalink / raw)
  To: Jonathan Neuschäfer
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

Thanks for the review comments,

On 07/10/17 06:02, Jonathan Neuschäfer wrote:
> Hi,
> 
> On Fri, Oct 06, 2017 at 05:51:35PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>>
>> This patch adds support to read/write slimbus value elements.
>> Currently it only supports byte read/write. Adding this support in
>> regmap would give codec drivers more flexibility when there are more
>> than 2 control interfaces like slimbus, i2c.
>>
>> Without this patch each codec driver has to directly call slimbus value
>> element apis, and this could would get messy once we want to add i2c
>> interface to it.
>>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
> [...]
>> +static int regmap_slimbus_byte_reg_read(void *context, unsigned int reg,
>> +					unsigned int *val)
>> +{
>> +	struct slim_device *slim = context;
>> +	struct slim_val_inf msg = {0,};
>> +
>> +	msg.start_offset = reg;
>> +	msg.num_bytes = 1;
>> +	msg.rbuf = (void *)val;
>> +
>> +	return slim_request_val_element(slim, &msg);
>> +}
> 
> This looks like it won't work on big-endian systems. I know big endian
> is pretty uncommon in devices that will likely have SLIMBus, but it's
> better to be endian-independent.

Yep, I agree! will fix it in next version.

> 
>> +static int regmap_slimbus_byte_reg_write(void *context, unsigned int reg,
>> +					 unsigned int val)
>> +{
>> +	struct slim_device *slim = context;
>> +	struct slim_val_inf msg = {0,};
>> +
>> +	msg.start_offset = reg;
>> +	msg.num_bytes = 1;
>> +	msg.wbuf = (void *)&val;
>> +
>> +	return slim_change_val_element(slim, &msg);
>> +}
> 
> dito
> 
>> +static struct regmap_bus regmap_slimbus_bus = {
>> +	.reg_write = regmap_slimbus_byte_reg_write,
>> +	.reg_read = regmap_slimbus_byte_reg_read,
>> +};
> 
> 
> Thanks,
> Jonathan Neuschäfer
> 

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

* Re: [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-07 10:24         ` Srinivas Kandagatla
  (?)
@ 2017-10-07 12:29         ` Jonathan Neuschäfer
  -1 siblings, 0 replies; 87+ messages in thread
From: Jonathan Neuschäfer @ 2017-10-07 12:29 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Jonathan Neuschäfer, gregkh, broonie, alsa-devel, sdharia,
	bp, poeschel, treding, gong.chen, andreas.noever, alan,
	mathieu.poirier, daniel, jkosina, sharon.dvir1, joe, davem,
	james.hogan, michael.opdenacker, robh+dt, pawel.moll,
	mark.rutland, devicetree, linux-kernel, kheitke, linux-arm-msm,
	arnd

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

On Sat, Oct 07, 2017 at 11:24:33AM +0100, Srinivas Kandagatla wrote:
> Thanks for the comments.
> 
> On 07/10/17 07:42, Jonathan Neuschäfer wrote:
> > Hi,
> > 
> > On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:
> > > From: Sagar Dharia <sdharia@codeaurora.org>
[...]
> > > +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));
> > 
> > Shouldn't this be (cur | (1 << 3)?

I misread the code here: I thought that cur was assigned the
"compressed" message length (in the range 0..7) here, but that's not
true, as slim_slicecodefromsize returns an "uncompressed" number. Thus
cur is a "quantized"[1] version of msg->num_bytes.

> cur seems to be redundant TBH, the only difference between cur and sl is
> that the slim_slicesize() can give slice size to program for any lengths
> between 1-16 bytes. However the slim_slicecodefromsize() can only handle
> 1,2,3,4, 6,8,12,16 byte sizes.

In any case, cur is only assigned and not used, as the code currently
is.

> So we can delete slim_slicecodefromsize() call and function together.
> looks like it was a leftover from downstream.

I agree. I don't know how it *might* be used, because I haven't read the
SLIMbus spec, but it is unused here.

> > (Also, what does cur mean? Cursor? Current?)
> No Idea!! :-) it is supposed to return slice size as per number of bytes.

Another problem solved by deleting slim_slicecodefromsize :-)

(As a small side-note, I think slim_slicesize and slim_slicecodefromsize
are named backwards: I would call sl, as used above, a "slice code",
because it encodes the message length)


> > > +/*
> > > + * slim_request_val_element: change and request a given value element
> 
> name should be fixed here..

Good catch.

> > > + * @sb: client handle requesting elemental message reads, writes.
> > > + * @msg: Input structure for start-offset, number of bytes to write.
> > > + * 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.
> > 
> > Does rbuf contain the old value after this function finishes?
> > 
> Yep, device should send a reply value with the old value with matching tid.

I think you should document this in the comment to help readers.


Thanks,
Jonathan Neuschäfer

[1]: https://en.wikipedia.org/wiki/Quantization

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

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-06 15:51   ` srinivas.kandagatla
@ 2017-10-10 10:05     ` Charles Keepax
  -1 siblings, 0 replies; 87+ messages in thread
From: Charles Keepax @ 2017-10-10 10:05 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: mark.rutland, alsa-devel, michael.opdenacker, poeschel,
	andreas.noever, gong.chen, arnd, kheitke, treding, devicetree,
	james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1, robh+dt,
	sdharia, alan, bp, mathieu.poirier, jkosina, linux-kernel,
	broonie, daniel, gregkh, joe, davem

On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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). 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@codeaurora.org>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
>  Documentation/slimbus/summary                     | 109 ++++
>  drivers/Kconfig                                   |   2 +
>  drivers/Makefile                                  |   1 +
>  drivers/slimbus/Kconfig                           |  11 +
>  drivers/slimbus/Makefile                          |   5 +
>  drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
>  include/linux/mod_devicetable.h                   |  13 +
>  include/linux/slimbus.h                           | 299 ++++++++++
>  9 files changed, 1192 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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 include/linux/slimbus.h
> 
> diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
> new file mode 100644
> index 0000000..cb658bb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/slimbus/bus.txt
> @@ -0,0 +1,57 @@
> +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 2
> +- #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.
> +If child node is not present and it is instantiated after device
> +discovery (slave device reporting itself present).
> +
> +In some cases it may be necessary to describe non-probeable device
> +details such as non-standard ways of powering up a device. In
> +such cases, child nodes for those devices will be present as
> +slaves of the slimbus-controller, as detailed below.
> +
> +Required property for SLIMbus child node if it is present:
> +- reg		- Is Duplex (Device index, Instance ID) from Enumeration
> +		  Address.
> +		  Device Index Uniquely identifies multiple Devices within
> +		  a single Component.
> +		  Instance ID Is for the cases where multiple Devices of the
> +		  same type or Class are attached to the bus.
> +
> +- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
> +	 	  Product Code, shall be in lower case hexadecimal with leading
> +		  zeroes suppressed

This does sort of make sense but kinda makes the code a bit ugly
parsing the MID and PID. Some parts will support SLIMBus and also
other control interfaces, which means they would need to add an
additional compatible string just for SLIMBus. It also breaks
the normal conventions of vendor,part and finally it makes the
compatible strings really unreadable which will be a bit annoying
when looking at DTs.

I think the MID and PID should just be included in the reg field
and just leave this as a standard compatible.

> +/**

This doesn't appear to be a kernel doc comment, so only /*.

> + * Report callbacks(device_up, device_down) are implemented by slimbus-devices.
> + * The calls are scheduled into a workqueue to avoid holding up controller
> + * initialization/tear-down.
> + */
> +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->sbdev = 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);
> +	}
> +}
> +
> +/**
> + * slim_driver_register: Client driver registration with slimbus

A - after the function name usually works better, I think
kernel-doc doesn't support a colon after the function name.

> + * @drv:Client driver to be associated with client-device.
> + * @owner: owning module/driver
> + * This API will register the client driver with the slimbus
> + * It is called from the driver's module-init function.

If you don't put a blank line after the arguments kernel doc will
treat this as a run on for the description of owner rather than
the long description for the function you intended it to be.

> +
> +/* Helper to get hex Manufacturer ID and Product id from compatible */
> +static unsigned long str2hex(unsigned char *str)
> +{
> +	int value = 0;
> +
> +	while (*str) {
> +		char c = *str++;
> +
> +		value = value << 4;
> +		if (c >= '0' && c <= '9')
> +			value |= (c - '0');
> +		if (c >= 'a' && c <= 'f')
> +			value |= (c - 'a' + 10);
> +
> +	}
> +
> +	return value;
> +}

Isn't this just reimplementing kstrtoul?

> +
> +/* OF helpers for SLIMbus */
> +static void of_register_slim_devices(struct slim_controller *ctrl)
> +{
> +	struct device *dev = &ctrl->dev;
> +	struct device_node *node;
> +
> +	if (!ctrl->dev.of_node)
> +		return;
> +
> +	for_each_child_of_node(ctrl->dev.of_node, node) {
> +		struct slim_device *slim;
> +		const char *compat = NULL;
> +		char *p, *tok;
> +		int reg[2], ret;
> +
> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
> +		if (!slim)
> +			continue;
> +
> +		slim->dev.of_node = of_node_get(node);
> +
> +		compat = of_get_property(node, "compatible", NULL);
> +		if (!compat)
> +			continue;
> +
> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Manufacturer ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.manf_id = str2hex(tok);
> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Product ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.prod_code = str2hex(tok);
> +		kfree(p);
> +
> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
> +		if (ret) {
> +			dev_err(dev, "Device and Instance id not found:%d\n",
> +				ret);
> +			continue;
> +		}
> +		slim->e_addr.dev_index = reg[0];
> +		slim->e_addr.instance = reg[1];

As I said above, this feels a bit complex compared to just
reading the e_addr from reg.

> +
> +		ret = slim_add_device(ctrl, slim);
> +		if (ret)
> +			dev_err(dev, "of_slim device register err:%d\n", ret);
> +	}
> +}

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

Would it make sense to move this down to above slim_query_device,
that way all the related code is next to itself?
slim_boot_child/slim_framer_booted and
slim_match_dev/slim_query_device.

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

I would be inclined to remove the valid parameter and just
use get_laddr in here.  Feels weird to have two mechanisms
for specify the laddr and presumably if the controller wants
to specify the laddr it will need to support get_laddr.
Additionally, I would make get_laddr optional which feels more
sensible, indeed the code appears otherwise implements with that
assumption.

> +{
> +	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;
> +
> +	dev_info(&ctrl->dev, "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 = -ENODEV;
> +	} 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 address.
> + * @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);

I find the interface across these two functions
(assign_laddr/get_logical_addr) a little odd. Since
get_logical_addr calls assign_laddr if it couldn't find the
laddr and then assign_laddr actually does another search for the
laddr. Would it perhaps make sense to combine these into one
function?

Thanks,
Charles

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-10 10:05     ` Charles Keepax
  0 siblings, 0 replies; 87+ messages in thread
From: Charles Keepax @ 2017-10-10 10:05 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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). 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@codeaurora.org>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
>  Documentation/slimbus/summary                     | 109 ++++
>  drivers/Kconfig                                   |   2 +
>  drivers/Makefile                                  |   1 +
>  drivers/slimbus/Kconfig                           |  11 +
>  drivers/slimbus/Makefile                          |   5 +
>  drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
>  include/linux/mod_devicetable.h                   |  13 +
>  include/linux/slimbus.h                           | 299 ++++++++++
>  9 files changed, 1192 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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 include/linux/slimbus.h
> 
> diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
> new file mode 100644
> index 0000000..cb658bb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/slimbus/bus.txt
> @@ -0,0 +1,57 @@
> +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 2
> +- #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.
> +If child node is not present and it is instantiated after device
> +discovery (slave device reporting itself present).
> +
> +In some cases it may be necessary to describe non-probeable device
> +details such as non-standard ways of powering up a device. In
> +such cases, child nodes for those devices will be present as
> +slaves of the slimbus-controller, as detailed below.
> +
> +Required property for SLIMbus child node if it is present:
> +- reg		- Is Duplex (Device index, Instance ID) from Enumeration
> +		  Address.
> +		  Device Index Uniquely identifies multiple Devices within
> +		  a single Component.
> +		  Instance ID Is for the cases where multiple Devices of the
> +		  same type or Class are attached to the bus.
> +
> +- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
> +	 	  Product Code, shall be in lower case hexadecimal with leading
> +		  zeroes suppressed

This does sort of make sense but kinda makes the code a bit ugly
parsing the MID and PID. Some parts will support SLIMBus and also
other control interfaces, which means they would need to add an
additional compatible string just for SLIMBus. It also breaks
the normal conventions of vendor,part and finally it makes the
compatible strings really unreadable which will be a bit annoying
when looking at DTs.

I think the MID and PID should just be included in the reg field
and just leave this as a standard compatible.

> +/**

This doesn't appear to be a kernel doc comment, so only /*.

> + * Report callbacks(device_up, device_down) are implemented by slimbus-devices.
> + * The calls are scheduled into a workqueue to avoid holding up controller
> + * initialization/tear-down.
> + */
> +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->sbdev = 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);
> +	}
> +}
> +
> +/**
> + * slim_driver_register: Client driver registration with slimbus

A - after the function name usually works better, I think
kernel-doc doesn't support a colon after the function name.

> + * @drv:Client driver to be associated with client-device.
> + * @owner: owning module/driver
> + * This API will register the client driver with the slimbus
> + * It is called from the driver's module-init function.

If you don't put a blank line after the arguments kernel doc will
treat this as a run on for the description of owner rather than
the long description for the function you intended it to be.

> +
> +/* Helper to get hex Manufacturer ID and Product id from compatible */
> +static unsigned long str2hex(unsigned char *str)
> +{
> +	int value = 0;
> +
> +	while (*str) {
> +		char c = *str++;
> +
> +		value = value << 4;
> +		if (c >= '0' && c <= '9')
> +			value |= (c - '0');
> +		if (c >= 'a' && c <= 'f')
> +			value |= (c - 'a' + 10);
> +
> +	}
> +
> +	return value;
> +}

Isn't this just reimplementing kstrtoul?

> +
> +/* OF helpers for SLIMbus */
> +static void of_register_slim_devices(struct slim_controller *ctrl)
> +{
> +	struct device *dev = &ctrl->dev;
> +	struct device_node *node;
> +
> +	if (!ctrl->dev.of_node)
> +		return;
> +
> +	for_each_child_of_node(ctrl->dev.of_node, node) {
> +		struct slim_device *slim;
> +		const char *compat = NULL;
> +		char *p, *tok;
> +		int reg[2], ret;
> +
> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
> +		if (!slim)
> +			continue;
> +
> +		slim->dev.of_node = of_node_get(node);
> +
> +		compat = of_get_property(node, "compatible", NULL);
> +		if (!compat)
> +			continue;
> +
> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Manufacturer ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.manf_id = str2hex(tok);
> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Product ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.prod_code = str2hex(tok);
> +		kfree(p);
> +
> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
> +		if (ret) {
> +			dev_err(dev, "Device and Instance id not found:%d\n",
> +				ret);
> +			continue;
> +		}
> +		slim->e_addr.dev_index = reg[0];
> +		slim->e_addr.instance = reg[1];

As I said above, this feels a bit complex compared to just
reading the e_addr from reg.

> +
> +		ret = slim_add_device(ctrl, slim);
> +		if (ret)
> +			dev_err(dev, "of_slim device register err:%d\n", ret);
> +	}
> +}

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

Would it make sense to move this down to above slim_query_device,
that way all the related code is next to itself?
slim_boot_child/slim_framer_booted and
slim_match_dev/slim_query_device.

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

I would be inclined to remove the valid parameter and just
use get_laddr in here.  Feels weird to have two mechanisms
for specify the laddr and presumably if the controller wants
to specify the laddr it will need to support get_laddr.
Additionally, I would make get_laddr optional which feels more
sensible, indeed the code appears otherwise implements with that
assumption.

> +{
> +	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;
> +
> +	dev_info(&ctrl->dev, "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 = -ENODEV;
> +	} 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 address.
> + * @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);

I find the interface across these two functions
(assign_laddr/get_logical_addr) a little odd. Since
get_logical_addr calls assign_laddr if it couldn't find the
laddr and then assign_laddr actually does another search for the
laddr. Would it perhaps make sense to combine these into one
function?

Thanks,
Charles

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-06 15:51   ` srinivas.kandagatla
@ 2017-10-10 10:45       ` Vinod Koul
  -1 siblings, 0 replies; 87+ messages in thread
From: Vinod Koul @ 2017-10-10 10:45 UTC (permalink / raw)
  To: srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A
  Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, mark.rutland-5wv7dgnIgG8,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	gong.chen-VuQAYsv1563Yd54FQh9/CA, arnd-r2nGTMty4D4,
	kheitke-hxvC4TZJLZFWk0Htik3J/w, bp-l3A5Bk7waGM,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA, pawel.moll-5wv7dgnIgG8,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, sdharia-sgV2jX0FEOL9JmXXK+q4OQ,
	alan-VuQAYsv1563Yd54FQh9/CA, treding-DDmLM1+adcrQT0dZR+AlfA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, jkosina-AlSwsSmVLrQ,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, daniel-/w4YWyX8dFk,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q

On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org wrote:
> From: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> 
> 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). 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>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>  Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
>  Documentation/slimbus/summary                     | 109 ++++
>  drivers/Kconfig                                   |   2 +
>  drivers/Makefile                                  |   1 +
>  drivers/slimbus/Kconfig                           |  11 +
>  drivers/slimbus/Makefile                          |   5 +
>  drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
>  include/linux/mod_devicetable.h                   |  13 +
>  include/linux/slimbus.h                           | 299 ++++++++++
>  9 files changed, 1192 insertions(+)

thats a lot of code for review, consider splitting it up further for better
reviews

>  create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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

how about core.c (https://lkml.org/lkml/2017/7/12/430)

> +static const struct slim_device_id *slim_match(const struct slim_device_id *id,
> +					       const struct slim_device *sbdev)
> +{
> +	while (id->manf_id != 0 || id->prod_code != 0) {
> +		if (id->manf_id == sbdev->e_addr.manf_id &&
> +		    id->prod_code == sbdev->e_addr.prod_code &&
> +		    id->dev_index == sbdev->e_addr.dev_index)
> +			return id;
> +		id++;
> +	}
> +	return NULL;
> +}
> +
> +static int slim_device_match(struct device *dev, struct device_driver *drv)
> +{
> +	struct slim_device *sbdev = to_slim_device(dev);
> +	struct slim_driver *sbdrv = to_slim_driver(drv);
> +
> +	/* Attempt an OF style match first */
> +	if (of_driver_match_device(dev, drv))
> +		return 1;

is of_driver_match_device() a must have here? (I dont completely understand
DT so pardon my ignorance). Since we have devices with ids can we use that
alone for matching?

> +
> +	/* Then try to match against the id table */
> +	if (sbdrv->id_table)
> +		return slim_match(sbdrv->id_table, sbdev) != NULL;
> +
> +	return 0;
> +}
> +

rather than jumping now to reporting APIs, can we club all bus_type parts to
one place (patch) so that it is easier to review logically

> +struct sb_report_wd {
> +	struct work_struct wd;
> +	struct slim_device *sbdev;
> +	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->sbdev;
> +
> +	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
> +	 */

I think ppl commented about this style, so lets fix those issues

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

what do the device_up/down calls signify here?

> +static int slim_device_probe(struct device *dev)
> +{
> +	struct slim_device	*sbdev;
> +	struct slim_driver	*sbdrv;
> +	int status = 0;
> +
> +	sbdev = to_slim_device(dev);
> +	sbdrv = to_slim_driver(dev->driver);
> +
> +	sbdev->driver = sbdrv;
> +
> +	if (sbdrv->probe)
> +		status = sbdrv->probe(sbdev);
> +
> +	if (status)
> +		sbdev->driver = NULL;
> +	else if (sbdrv->device_up)
> +		schedule_slim_report(sbdev->ctrl, sbdev, true);

can you please explain what this is trying to do?

> +int __slim_driver_register(struct slim_driver *drv, struct module *owner)
> +{
> +	drv->driver.bus = &slimbus_type;
> +	drv->driver.owner = owner;
> +	return driver_register(&drv->driver);
> +}
> +EXPORT_SYMBOL_GPL(__slim_driver_register);

any reason to use __ for this API?

> +static int slim_add_device(struct slim_controller *ctrl,
> +			   struct slim_device *sbdev)
> +{
> +	sbdev->dev.bus = &slimbus_type;
> +	sbdev->dev.parent = &ctrl->dev;
> +	sbdev->dev.release = slim_dev_release;
> +	sbdev->dev.driver = NULL;
> +	sbdev->ctrl = ctrl;
> +
> +	slim_ctrl_get(ctrl);
> +	sbdev->name = kasprintf(GFP_KERNEL, "%x:%x:%x:%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);

I dont think the comment is quite correct, you register a device not probe!

> +/* OF helpers for SLIMbus */
> +static void of_register_slim_devices(struct slim_controller *ctrl)
> +{
> +	struct device *dev = &ctrl->dev;
> +	struct device_node *node;
> +
> +	if (!ctrl->dev.of_node)
> +		return;
> +
> +	for_each_child_of_node(ctrl->dev.of_node, node) {
> +		struct slim_device *slim;
> +		const char *compat = NULL;
> +		char *p, *tok;
> +		int reg[2], ret;
> +
> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
> +		if (!slim)
> +			continue;
> +
> +		slim->dev.of_node = of_node_get(node);
> +
> +		compat = of_get_property(node, "compatible", NULL);
> +		if (!compat)
> +			continue;
> +
> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Manufacturer ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.manf_id = str2hex(tok);
> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Product ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.prod_code = str2hex(tok);
> +		kfree(p);
> +
> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
> +		if (ret) {
> +			dev_err(dev, "Device and Instance id not found:%d\n",
> +				ret);
> +			continue;
> +		}
> +		slim->e_addr.dev_index = reg[0];
> +		slim->e_addr.instance = reg[1];
> +
> +		ret = slim_add_device(ctrl, slim);

okay this is good stuff. So we scan the DT for slimbus devices and register
them here. Same stuff we can do with ACPI :)

then why do we need the of register stuff I commented earlier. A Slimbus
device can work irrespective of firmware type and registers using various
ids. The platform will scan firmware (dt/acpi) create devices and load
drivers against them generically.  Apart from this code we ideally should
not have any DT parts in the bus, do you agree?

> +		if (ret)
> +			dev_err(dev, "of_slim device register err:%d\n", ret);
> +	}
> +}
> +
> +/**
> + * 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);

what are these ids used for?

> +	mutex_unlock(&slim_lock);
> +
> +	if (id < 0)
> +		return id;
> +
> +	ctrl->nr = id;
> +
> +	dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr);
> +	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);

one more device_register?? Can you explain why

> +/**
> + * 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 {

addrt? why not just addr?

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

BIT() GENMASK() please here and other places where they define bits in spec

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

do we need two callback, why not a status or notify callback with argument
for up/down?

-- 
~Vinod
--
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] 87+ messages in thread

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-10 10:45       ` Vinod Koul
  0 siblings, 0 replies; 87+ messages in thread
From: Vinod Koul @ 2017-10-10 10:45 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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). 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@codeaurora.org>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
>  Documentation/slimbus/summary                     | 109 ++++
>  drivers/Kconfig                                   |   2 +
>  drivers/Makefile                                  |   1 +
>  drivers/slimbus/Kconfig                           |  11 +
>  drivers/slimbus/Makefile                          |   5 +
>  drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
>  include/linux/mod_devicetable.h                   |  13 +
>  include/linux/slimbus.h                           | 299 ++++++++++
>  9 files changed, 1192 insertions(+)

thats a lot of code for review, consider splitting it up further for better
reviews

>  create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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

how about core.c (https://lkml.org/lkml/2017/7/12/430)

> +static const struct slim_device_id *slim_match(const struct slim_device_id *id,
> +					       const struct slim_device *sbdev)
> +{
> +	while (id->manf_id != 0 || id->prod_code != 0) {
> +		if (id->manf_id == sbdev->e_addr.manf_id &&
> +		    id->prod_code == sbdev->e_addr.prod_code &&
> +		    id->dev_index == sbdev->e_addr.dev_index)
> +			return id;
> +		id++;
> +	}
> +	return NULL;
> +}
> +
> +static int slim_device_match(struct device *dev, struct device_driver *drv)
> +{
> +	struct slim_device *sbdev = to_slim_device(dev);
> +	struct slim_driver *sbdrv = to_slim_driver(drv);
> +
> +	/* Attempt an OF style match first */
> +	if (of_driver_match_device(dev, drv))
> +		return 1;

is of_driver_match_device() a must have here? (I dont completely understand
DT so pardon my ignorance). Since we have devices with ids can we use that
alone for matching?

> +
> +	/* Then try to match against the id table */
> +	if (sbdrv->id_table)
> +		return slim_match(sbdrv->id_table, sbdev) != NULL;
> +
> +	return 0;
> +}
> +

rather than jumping now to reporting APIs, can we club all bus_type parts to
one place (patch) so that it is easier to review logically

> +struct sb_report_wd {
> +	struct work_struct wd;
> +	struct slim_device *sbdev;
> +	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->sbdev;
> +
> +	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
> +	 */

I think ppl commented about this style, so lets fix those issues

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

what do the device_up/down calls signify here?

> +static int slim_device_probe(struct device *dev)
> +{
> +	struct slim_device	*sbdev;
> +	struct slim_driver	*sbdrv;
> +	int status = 0;
> +
> +	sbdev = to_slim_device(dev);
> +	sbdrv = to_slim_driver(dev->driver);
> +
> +	sbdev->driver = sbdrv;
> +
> +	if (sbdrv->probe)
> +		status = sbdrv->probe(sbdev);
> +
> +	if (status)
> +		sbdev->driver = NULL;
> +	else if (sbdrv->device_up)
> +		schedule_slim_report(sbdev->ctrl, sbdev, true);

can you please explain what this is trying to do?

> +int __slim_driver_register(struct slim_driver *drv, struct module *owner)
> +{
> +	drv->driver.bus = &slimbus_type;
> +	drv->driver.owner = owner;
> +	return driver_register(&drv->driver);
> +}
> +EXPORT_SYMBOL_GPL(__slim_driver_register);

any reason to use __ for this API?

> +static int slim_add_device(struct slim_controller *ctrl,
> +			   struct slim_device *sbdev)
> +{
> +	sbdev->dev.bus = &slimbus_type;
> +	sbdev->dev.parent = &ctrl->dev;
> +	sbdev->dev.release = slim_dev_release;
> +	sbdev->dev.driver = NULL;
> +	sbdev->ctrl = ctrl;
> +
> +	slim_ctrl_get(ctrl);
> +	sbdev->name = kasprintf(GFP_KERNEL, "%x:%x:%x:%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);

I dont think the comment is quite correct, you register a device not probe!

> +/* OF helpers for SLIMbus */
> +static void of_register_slim_devices(struct slim_controller *ctrl)
> +{
> +	struct device *dev = &ctrl->dev;
> +	struct device_node *node;
> +
> +	if (!ctrl->dev.of_node)
> +		return;
> +
> +	for_each_child_of_node(ctrl->dev.of_node, node) {
> +		struct slim_device *slim;
> +		const char *compat = NULL;
> +		char *p, *tok;
> +		int reg[2], ret;
> +
> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
> +		if (!slim)
> +			continue;
> +
> +		slim->dev.of_node = of_node_get(node);
> +
> +		compat = of_get_property(node, "compatible", NULL);
> +		if (!compat)
> +			continue;
> +
> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Manufacturer ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.manf_id = str2hex(tok);
> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Product ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.prod_code = str2hex(tok);
> +		kfree(p);
> +
> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
> +		if (ret) {
> +			dev_err(dev, "Device and Instance id not found:%d\n",
> +				ret);
> +			continue;
> +		}
> +		slim->e_addr.dev_index = reg[0];
> +		slim->e_addr.instance = reg[1];
> +
> +		ret = slim_add_device(ctrl, slim);

okay this is good stuff. So we scan the DT for slimbus devices and register
them here. Same stuff we can do with ACPI :)

then why do we need the of register stuff I commented earlier. A Slimbus
device can work irrespective of firmware type and registers using various
ids. The platform will scan firmware (dt/acpi) create devices and load
drivers against them generically.  Apart from this code we ideally should
not have any DT parts in the bus, do you agree?

> +		if (ret)
> +			dev_err(dev, "of_slim device register err:%d\n", ret);
> +	}
> +}
> +
> +/**
> + * 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);

what are these ids used for?

> +	mutex_unlock(&slim_lock);
> +
> +	if (id < 0)
> +		return id;
> +
> +	ctrl->nr = id;
> +
> +	dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr);
> +	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);

one more device_register?? Can you explain why

> +/**
> + * 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 {

addrt? why not just addr?

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

BIT() GENMASK() please here and other places where they define bits in spec

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

do we need two callback, why not a status or notify callback with argument
for up/down?

-- 
~Vinod

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

* Re: [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-06 15:51     ` srinivas.kandagatla
@ 2017-10-10 12:19       ` Charles Keepax
  -1 siblings, 0 replies; 87+ messages in thread
From: Charles Keepax @ 2017-10-10 12:19 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: mark.rutland, alsa-devel, michael.opdenacker, poeschel,
	andreas.noever, gong.chen, arnd, kheitke, treding, devicetree,
	james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1, robh+dt,
	sdharia, alan, bp, mathieu.poirier, jkosina, linux-kernel,
	broonie, daniel, gregkh, joe, davem

On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  drivers/slimbus/Makefile         |   2 +-
>  drivers/slimbus/slim-core.c      |  15 ++
>  drivers/slimbus/slim-messaging.c | 471 +++++++++++++++++++++++++++++++++++++++
>  include/linux/slimbus.h          | 161 +++++++++++++
>  4 files changed, 648 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/slimbus/slim-messaging.c
> 
> +/**
> + * 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)

Can all go on one line.

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

What is the intent of this if statement here? We set async
locally so this code only runs if we executed the else on the if
statement at the top. If its just clearing anything the user
might have put in these fields why not do it up there.

> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(slim_processtxn);
> +
> +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);

cur should probably be removed until it is needed.

> +	txn->ec = ((sl | (1 << 3)) | ((msg->start_offset & 0xFFF) << 4));
> +
> +	switch (mc) {
> +	case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
> +	case SLIM_MSG_MC_CHANGE_VALUE:
> +	case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
> +	case SLIM_MSG_MC_CLEAR_INFORMATION:
> +		txn->rl += msg->num_bytes;
> +	default:
> +		break;
> +	}
> +
> +	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 */
> +
> +/*
> + * slim_request_val_element: request value element
> + * @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;

You could put this check into slim_xfer_msg and save duplicating
it. Would also then cover cases that call that function directly,
also would let you make these functions either inlines or macros.

> +
> +	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_VALUE);
> +}
> +EXPORT_SYMBOL_GPL(slim_request_val_element);
> +
> +/* Functions to get/return TX, RX buffers for messaging. */
> +
> +/**
> + * slim_get_rx: To get RX buffers for messaging.
> + * @ctrl: Controller handle
> + * These functions are called by controller to process the RX buffers.
> + * 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)
> +{
> +	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);

I find the combination of get/return a bit odd, would get/put
maybe more idiomatic? Also the return could use some kernel doc.

> +
> +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);
> +
> +/**
> + * slim_get_tx: To get TX buffers for messaging.
> + * @ctrl: Controller handle
> + * These functions are called by controller to process the TX 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.
> + */
> +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);

The rx calls seem ok that is basically the controller's job to
call those, but with these two calls it seems sometimes the
framework calls them sometimes the controller driver has to. Is
there anyway we can simplify that a bit? Or at least include some
documentation as to when the controller should call them.

> diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h
> index b5064b6..080d86a 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) */

s/neededing/needing/

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

If the LA isn't used in broadcast messages wouldn't it be simpler
to set it to a fixed value in this macro?

> +
> +#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, }
> +

Also one final overall comment this commit contains a lot of two
and three letter abreviations that are not always clear. I would
probably suggest expanding a few of the less standard ones out to
make the code a little easier to follow.

Thanks,
Charles

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

* Re: [alsa-devel] [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
@ 2017-10-10 12:19       ` Charles Keepax
  0 siblings, 0 replies; 87+ messages in thread
From: Charles Keepax @ 2017-10-10 12:19 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  drivers/slimbus/Makefile         |   2 +-
>  drivers/slimbus/slim-core.c      |  15 ++
>  drivers/slimbus/slim-messaging.c | 471 +++++++++++++++++++++++++++++++++++++++
>  include/linux/slimbus.h          | 161 +++++++++++++
>  4 files changed, 648 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/slimbus/slim-messaging.c
> 
> +/**
> + * 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)

Can all go on one line.

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

What is the intent of this if statement here? We set async
locally so this code only runs if we executed the else on the if
statement at the top. If its just clearing anything the user
might have put in these fields why not do it up there.

> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(slim_processtxn);
> +
> +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);

cur should probably be removed until it is needed.

> +	txn->ec = ((sl | (1 << 3)) | ((msg->start_offset & 0xFFF) << 4));
> +
> +	switch (mc) {
> +	case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
> +	case SLIM_MSG_MC_CHANGE_VALUE:
> +	case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
> +	case SLIM_MSG_MC_CLEAR_INFORMATION:
> +		txn->rl += msg->num_bytes;
> +	default:
> +		break;
> +	}
> +
> +	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 */
> +
> +/*
> + * slim_request_val_element: request value element
> + * @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;

You could put this check into slim_xfer_msg and save duplicating
it. Would also then cover cases that call that function directly,
also would let you make these functions either inlines or macros.

> +
> +	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_VALUE);
> +}
> +EXPORT_SYMBOL_GPL(slim_request_val_element);
> +
> +/* Functions to get/return TX, RX buffers for messaging. */
> +
> +/**
> + * slim_get_rx: To get RX buffers for messaging.
> + * @ctrl: Controller handle
> + * These functions are called by controller to process the RX buffers.
> + * 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)
> +{
> +	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);

I find the combination of get/return a bit odd, would get/put
maybe more idiomatic? Also the return could use some kernel doc.

> +
> +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);
> +
> +/**
> + * slim_get_tx: To get TX buffers for messaging.
> + * @ctrl: Controller handle
> + * These functions are called by controller to process the TX 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.
> + */
> +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);

The rx calls seem ok that is basically the controller's job to
call those, but with these two calls it seems sometimes the
framework calls them sometimes the controller driver has to. Is
there anyway we can simplify that a bit? Or at least include some
documentation as to when the controller should call them.

> diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h
> index b5064b6..080d86a 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) */

s/neededing/needing/

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

If the LA isn't used in broadcast messages wouldn't it be simpler
to set it to a fixed value in this macro?

> +
> +#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, }
> +

Also one final overall comment this commit contains a lot of two
and three letter abreviations that are not always clear. I would
probably suggest expanding a few of the less standard ones out to
make the code a little easier to follow.

Thanks,
Charles

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-10 10:05     ` [alsa-devel] " Charles Keepax
@ 2017-10-10 12:34       ` Srinivas Kandagatla
  -1 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-10 12:34 UTC (permalink / raw)
  To: Charles Keepax
  Cc: mark.rutland, alsa-devel, michael.opdenacker, poeschel,
	andreas.noever, gong.chen, arnd, kheitke, treding, devicetree,
	james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1, robh+dt,
	sdharia, alan, bp, mathieu.poirier, jkosina, linux-kernel,
	broonie, daniel, gregkh, joe, davem

Thanks for the review comments.

On 10/10/17 11:05, Charles Keepax wrote:
> On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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). 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@codeaurora.org>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
>>   Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
>>   Documentation/slimbus/summary                     | 109 ++++
>>   drivers/Kconfig                                   |   2 +
>>   drivers/Makefile                                  |   1 +
>>   drivers/slimbus/Kconfig                           |  11 +
>>   drivers/slimbus/Makefile                          |   5 +
>>   drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
>>   include/linux/mod_devicetable.h                   |  13 +
>>   include/linux/slimbus.h                           | 299 ++++++++++
>>   9 files changed, 1192 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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 include/linux/slimbus.h
>>
>> diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
>> new file mode 100644
>> index 0000000..cb658bb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/slimbus/bus.txt
>> @@ -0,0 +1,57 @@
>> +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 2
>> +- #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.
>> +If child node is not present and it is instantiated after device
>> +discovery (slave device reporting itself present).
>> +
>> +In some cases it may be necessary to describe non-probeable device
>> +details such as non-standard ways of powering up a device. In
>> +such cases, child nodes for those devices will be present as
>> +slaves of the slimbus-controller, as detailed below.
>> +
>> +Required property for SLIMbus child node if it is present:
>> +- reg		- Is Duplex (Device index, Instance ID) from Enumeration
>> +		  Address.
>> +		  Device Index Uniquely identifies multiple Devices within
>> +		  a single Component.
>> +		  Instance ID Is for the cases where multiple Devices of the
>> +		  same type or Class are attached to the bus.
>> +
>> +- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
>> +	 	  Product Code, shall be in lower case hexadecimal with leading
>> +		  zeroes suppressed
> 
> This does sort of make sense but kinda makes the code a bit ugly
> parsing the MID and PID. Some parts will support SLIMBus and also
> other control interfaces, which means they would need to add an
> additional compatible string just for SLIMBus. It also breaks
> the normal conventions of vendor,part and finally it makes the
> compatible strings really unreadable which will be a bit annoying
> when looking at DTs.
> 
This change was made inline to the comments provided in previous version 
of the patch https://lkml.org/lkml/2016/5/3/576

> I think the MID and PID should just be included in the reg field
> and just leave this as a standard compatible.

AFAIK, reg field should only contain index and instance, which was also 
discussed  at https://lkml.org/lkml/2016/5/3/747

> 
>> +/**
> 
> This doesn't appear to be a kernel doc comment, so only /*.

Yep, I got similar comments from other reviewers too, so I will fix all 
such instances in next version.

> 
>> + * Report callbacks(device_up, device_down) are implemented by slimbus-devices.
>> + * The calls are scheduled into a workqueue to avoid holding up controller
>> + * initialization/tear-down.
>> + */
>> +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->sbdev = 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);
>> +	}
>> +}
>> +
>> +/**
>> + * slim_driver_register: Client driver registration with slimbus
> 
> A - after the function name usually works better, I think
> kernel-doc doesn't support a colon after the function name.

Will fix this in next version too.

> 
>> + * @drv:Client driver to be associated with client-device.
>> + * @owner: owning module/driver
>> + * This API will register the client driver with the slimbus
>> + * It is called from the driver's module-init function.
> 
> If you don't put a blank line after the arguments kernel doc will
> treat this as a run on for the description of owner rather than
> the long description for the function you intended it to be.

okay, I will fix such instances in the patchset in next version.

> 
>> +
>> +/* Helper to get hex Manufacturer ID and Product id from compatible */
>> +static unsigned long str2hex(unsigned char *str)
>> +{
>> +	int value = 0;
>> +
>> +	while (*str) {
>> +		char c = *str++;
>> +
>> +		value = value << 4;
>> +		if (c >= '0' && c <= '9')
>> +			value |= (c - '0');
>> +		if (c >= 'a' && c <= 'f')
>> +			value |= (c - 'a' + 10);
>> +
>> +	}
>> +
>> +	return value;
>> +}
> 
> Isn't this just reimplementing kstrtoul?
> 
I would say partly, I think kstrtoul will only parse string as hex if 
its prefixed with "0x" But the compatible does not have 0x prefix..
we could probably do some prefixing before passing to kstrtoul to remove 
above function.. I will try that and see!

>> +
>> +/* OF helpers for SLIMbus */
>> +static void of_register_slim_devices(struct slim_controller *ctrl)
>> +{
>> +	struct device *dev = &ctrl->dev;
>> +	struct device_node *node;
>> +
>> +	if (!ctrl->dev.of_node)
>> +		return;
>> +
>> +	for_each_child_of_node(ctrl->dev.of_node, node) {
>> +		struct slim_device *slim;
>> +		const char *compat = NULL;
>> +		char *p, *tok;
>> +		int reg[2], ret;
>> +
>> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
>> +		if (!slim)
>> +			continue;
>> +
>> +		slim->dev.of_node = of_node_get(node);
>> +
>> +		compat = of_get_property(node, "compatible", NULL);
>> +		if (!compat)
>> +			continue;
>> +
>> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
>> +
>> +		tok = strsep(&p, ",");
>> +		if (!tok) {
>> +			dev_err(dev, "No valid Manufacturer ID found\n");
>> +			kfree(p);
>> +			continue;
>> +		}
>> +		slim->e_addr.manf_id = str2hex(tok);
>> +
>> +		tok = strsep(&p, ",");
>> +		if (!tok) {
>> +			dev_err(dev, "No valid Product ID found\n");
>> +			kfree(p);
>> +			continue;
>> +		}
>> +		slim->e_addr.prod_code = str2hex(tok);
>> +		kfree(p);
>> +
>> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
>> +		if (ret) {
>> +			dev_err(dev, "Device and Instance id not found:%d\n",
>> +				ret);
>> +			continue;
>> +		}
>> +		slim->e_addr.dev_index = reg[0];
>> +		slim->e_addr.instance = reg[1];
> 
> As I said above, this feels a bit complex compared to just
> reading the e_addr from reg.

I agree, but as I said, its done as part of the review comments on v5 
patchset.
https://lkml.org/lkml/2016/5/3/747

> 
>> +
>> +		ret = slim_add_device(ctrl, slim);
>> +		if (ret)
>> +			dev_err(dev, "of_slim device register err:%d\n", ret);
>> +	}
>> +}
> 
>> +
>> +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);
>> +}
> 
> Would it make sense to move this down to above slim_query_device,
> that way all the related code is next to itself?
> slim_boot_child/slim_framer_booted and
> slim_match_dev/slim_query_device.
> 
Okay, Will give that a go.
>> +
>> +/**
>> + * 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;
>> +
>> +	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)
> 
> I would be inclined to remove the valid parameter and just
> use get_laddr in here.  Feels weird to have two mechanisms
> for specify the laddr and presumably if the controller wants
> to specify the laddr it will need to support get_laddr.
> Additionally, I would make get_laddr optional which feels more
> sensible, indeed the code appears otherwise implements with that
> assumption.
Yep makes sense, I will give it a go in next version.

> 
>> +{
>> +	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;
>> +
>> +	dev_info(&ctrl->dev, "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 = -ENODEV;
>> +	} 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 address.
>> + * @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);
> 
> I find the interface across these two functions
> (assign_laddr/get_logical_addr) a little odd. Since
> get_logical_addr calls assign_laddr if it couldn't find the
> laddr and then assign_laddr actually does another search for the
> laddr. Would it perhaps make sense to combine these into one
> function?
Yes these two functions can be combined, Will fix this in next version.
> 
> Thanks,
> Charles
> 

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-10 12:34       ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-10 12:34 UTC (permalink / raw)
  To: Charles Keepax
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

Thanks for the review comments.

On 10/10/17 11:05, Charles Keepax wrote:
> On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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). 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@codeaurora.org>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
>>   Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
>>   Documentation/slimbus/summary                     | 109 ++++
>>   drivers/Kconfig                                   |   2 +
>>   drivers/Makefile                                  |   1 +
>>   drivers/slimbus/Kconfig                           |  11 +
>>   drivers/slimbus/Makefile                          |   5 +
>>   drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
>>   include/linux/mod_devicetable.h                   |  13 +
>>   include/linux/slimbus.h                           | 299 ++++++++++
>>   9 files changed, 1192 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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 include/linux/slimbus.h
>>
>> diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
>> new file mode 100644
>> index 0000000..cb658bb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/slimbus/bus.txt
>> @@ -0,0 +1,57 @@
>> +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 2
>> +- #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.
>> +If child node is not present and it is instantiated after device
>> +discovery (slave device reporting itself present).
>> +
>> +In some cases it may be necessary to describe non-probeable device
>> +details such as non-standard ways of powering up a device. In
>> +such cases, child nodes for those devices will be present as
>> +slaves of the slimbus-controller, as detailed below.
>> +
>> +Required property for SLIMbus child node if it is present:
>> +- reg		- Is Duplex (Device index, Instance ID) from Enumeration
>> +		  Address.
>> +		  Device Index Uniquely identifies multiple Devices within
>> +		  a single Component.
>> +		  Instance ID Is for the cases where multiple Devices of the
>> +		  same type or Class are attached to the bus.
>> +
>> +- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
>> +	 	  Product Code, shall be in lower case hexadecimal with leading
>> +		  zeroes suppressed
> 
> This does sort of make sense but kinda makes the code a bit ugly
> parsing the MID and PID. Some parts will support SLIMBus and also
> other control interfaces, which means they would need to add an
> additional compatible string just for SLIMBus. It also breaks
> the normal conventions of vendor,part and finally it makes the
> compatible strings really unreadable which will be a bit annoying
> when looking at DTs.
> 
This change was made inline to the comments provided in previous version 
of the patch https://lkml.org/lkml/2016/5/3/576

> I think the MID and PID should just be included in the reg field
> and just leave this as a standard compatible.

AFAIK, reg field should only contain index and instance, which was also 
discussed  at https://lkml.org/lkml/2016/5/3/747

> 
>> +/**
> 
> This doesn't appear to be a kernel doc comment, so only /*.

Yep, I got similar comments from other reviewers too, so I will fix all 
such instances in next version.

> 
>> + * Report callbacks(device_up, device_down) are implemented by slimbus-devices.
>> + * The calls are scheduled into a workqueue to avoid holding up controller
>> + * initialization/tear-down.
>> + */
>> +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->sbdev = 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);
>> +	}
>> +}
>> +
>> +/**
>> + * slim_driver_register: Client driver registration with slimbus
> 
> A - after the function name usually works better, I think
> kernel-doc doesn't support a colon after the function name.

Will fix this in next version too.

> 
>> + * @drv:Client driver to be associated with client-device.
>> + * @owner: owning module/driver
>> + * This API will register the client driver with the slimbus
>> + * It is called from the driver's module-init function.
> 
> If you don't put a blank line after the arguments kernel doc will
> treat this as a run on for the description of owner rather than
> the long description for the function you intended it to be.

okay, I will fix such instances in the patchset in next version.

> 
>> +
>> +/* Helper to get hex Manufacturer ID and Product id from compatible */
>> +static unsigned long str2hex(unsigned char *str)
>> +{
>> +	int value = 0;
>> +
>> +	while (*str) {
>> +		char c = *str++;
>> +
>> +		value = value << 4;
>> +		if (c >= '0' && c <= '9')
>> +			value |= (c - '0');
>> +		if (c >= 'a' && c <= 'f')
>> +			value |= (c - 'a' + 10);
>> +
>> +	}
>> +
>> +	return value;
>> +}
> 
> Isn't this just reimplementing kstrtoul?
> 
I would say partly, I think kstrtoul will only parse string as hex if 
its prefixed with "0x" But the compatible does not have 0x prefix..
we could probably do some prefixing before passing to kstrtoul to remove 
above function.. I will try that and see!

>> +
>> +/* OF helpers for SLIMbus */
>> +static void of_register_slim_devices(struct slim_controller *ctrl)
>> +{
>> +	struct device *dev = &ctrl->dev;
>> +	struct device_node *node;
>> +
>> +	if (!ctrl->dev.of_node)
>> +		return;
>> +
>> +	for_each_child_of_node(ctrl->dev.of_node, node) {
>> +		struct slim_device *slim;
>> +		const char *compat = NULL;
>> +		char *p, *tok;
>> +		int reg[2], ret;
>> +
>> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
>> +		if (!slim)
>> +			continue;
>> +
>> +		slim->dev.of_node = of_node_get(node);
>> +
>> +		compat = of_get_property(node, "compatible", NULL);
>> +		if (!compat)
>> +			continue;
>> +
>> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
>> +
>> +		tok = strsep(&p, ",");
>> +		if (!tok) {
>> +			dev_err(dev, "No valid Manufacturer ID found\n");
>> +			kfree(p);
>> +			continue;
>> +		}
>> +		slim->e_addr.manf_id = str2hex(tok);
>> +
>> +		tok = strsep(&p, ",");
>> +		if (!tok) {
>> +			dev_err(dev, "No valid Product ID found\n");
>> +			kfree(p);
>> +			continue;
>> +		}
>> +		slim->e_addr.prod_code = str2hex(tok);
>> +		kfree(p);
>> +
>> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
>> +		if (ret) {
>> +			dev_err(dev, "Device and Instance id not found:%d\n",
>> +				ret);
>> +			continue;
>> +		}
>> +		slim->e_addr.dev_index = reg[0];
>> +		slim->e_addr.instance = reg[1];
> 
> As I said above, this feels a bit complex compared to just
> reading the e_addr from reg.

I agree, but as I said, its done as part of the review comments on v5 
patchset.
https://lkml.org/lkml/2016/5/3/747

> 
>> +
>> +		ret = slim_add_device(ctrl, slim);
>> +		if (ret)
>> +			dev_err(dev, "of_slim device register err:%d\n", ret);
>> +	}
>> +}
> 
>> +
>> +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);
>> +}
> 
> Would it make sense to move this down to above slim_query_device,
> that way all the related code is next to itself?
> slim_boot_child/slim_framer_booted and
> slim_match_dev/slim_query_device.
> 
Okay, Will give that a go.
>> +
>> +/**
>> + * 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;
>> +
>> +	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)
> 
> I would be inclined to remove the valid parameter and just
> use get_laddr in here.  Feels weird to have two mechanisms
> for specify the laddr and presumably if the controller wants
> to specify the laddr it will need to support get_laddr.
> Additionally, I would make get_laddr optional which feels more
> sensible, indeed the code appears otherwise implements with that
> assumption.
Yep makes sense, I will give it a go in next version.

> 
>> +{
>> +	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;
>> +
>> +	dev_info(&ctrl->dev, "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 = -ENODEV;
>> +	} 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 address.
>> + * @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);
> 
> I find the interface across these two functions
> (assign_laddr/get_logical_addr) a little odd. Since
> get_logical_addr calls assign_laddr if it couldn't find the
> laddr and then assign_laddr actually does another search for the
> laddr. Would it perhaps make sense to combine these into one
> function?
Yes these two functions can be combined, Will fix this in next version.
> 
> Thanks,
> Charles
> 

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-10 10:45       ` Vinod Koul
  (?)
@ 2017-10-10 12:34       ` Srinivas Kandagatla
  2017-10-10 16:49           ` [alsa-devel] " Vinod Koul
  -1 siblings, 1 reply; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-10 12:34 UTC (permalink / raw)
  To: Vinod Koul
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

Thanks for your review comments,

On 10/10/17 11:45, Vinod Koul wrote:
> On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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). 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@codeaurora.org>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
>>   Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
>>   Documentation/slimbus/summary                     | 109 ++++
>>   drivers/Kconfig                                   |   2 +
>>   drivers/Makefile                                  |   1 +
>>   drivers/slimbus/Kconfig                           |  11 +
>>   drivers/slimbus/Makefile                          |   5 +
>>   drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
>>   include/linux/mod_devicetable.h                   |  13 +
>>   include/linux/slimbus.h                           | 299 ++++++++++
>>   9 files changed, 1192 insertions(+)
> 
> thats a lot of code for review, consider splitting it up further for better
> reviews

Its was suggested that parts of dtbindings and of_* wrapper merged into 
this patch.  In V5 review comments. https://lkml.org/lkml/2016/4/28/175


> 
>>   create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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
> 
> how about core.c (https://lkml.org/lkml/2017/7/12/430)
> 
Makes sense, will do that in next version.

>> +static const struct slim_device_id *slim_match(const struct slim_device_id *id,
>> +					       const struct slim_device *sbdev)
>> +{
>> +	while (id->manf_id != 0 || id->prod_code != 0) {
>> +		if (id->manf_id == sbdev->e_addr.manf_id &&
>> +		    id->prod_code == sbdev->e_addr.prod_code &&
>> +		    id->dev_index == sbdev->e_addr.dev_index)
>> +			return id;
>> +		id++;
>> +	}
>> +	return NULL;
>> +}
>> +
>> +static int slim_device_match(struct device *dev, struct device_driver *drv)
>> +{
>> +	struct slim_device *sbdev = to_slim_device(dev);
>> +	struct slim_driver *sbdrv = to_slim_driver(drv);
>> +
>> +	/* Attempt an OF style match first */
>> +	if (of_driver_match_device(dev, drv))
>> +		return 1;
> 
> is of_driver_match_device() a must have here? (I dont completely understand
Yes, we need this to match the compatible string from device tree vs 
driver itself, most of the bus driver do this in bus match functions.


> DT so pardon my ignorance). Since we have devices with ids can we use that
> alone for matching?

Two cases to consider here,
1> If the device is up and discoverable.
2> Device is not discoverable yet, as it needs some power up sequence.


In first case comparing with ID table makes sense.

But second case we would want to probe the device(for power sequencing) 
before we can discover the device on bus.


This code as it is supports both DT and id_table.

>> +
>> +	/* Then try to match against the id table */
>> +	if (sbdrv->id_table)
>> +		return slim_match(sbdrv->id_table, sbdev) != NULL;
>> +
>> +	return 0;
>> +}
>> +
> 
> rather than jumping now to reporting APIs, can we club all bus_type parts to
> one place (patch) so that it is easier to review logically
> 
Let me try that in next version.

>> +struct sb_report_wd {
>> +	struct work_struct wd;
>> +	struct slim_device *sbdev;
>> +	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->sbdev;
>> +
>> +	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
>> +	 */
> 
> I think ppl commented about this style, so lets fix those issues
> 

sure.

>> +	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);
> 
> what do the device_up/down calls signify here?
> 
up would be called when a device is discovered on the bus, and down on 
when the device disappeared on slimbus.

>> +static int slim_device_probe(struct device *dev)
>> +{
>> +	struct slim_device	*sbdev;
>> +	struct slim_driver	*sbdrv;
>> +	int status = 0;
>> +
>> +	sbdev = to_slim_device(dev);
>> +	sbdrv = to_slim_driver(dev->driver);
>> +
>> +	sbdev->driver = sbdrv;
>> +
>> +	if (sbdrv->probe)
>> +		status = sbdrv->probe(sbdev);
>> +
>> +	if (status)
>> +		sbdev->driver = NULL;
>> +	else if (sbdrv->device_up)
>> +		schedule_slim_report(sbdev->ctrl, sbdev, true);
> 
> can you please explain what this is trying to do?

It is scheduling a device_up() callback in workqueue for reporting 
discovered device.


> 
>> +int __slim_driver_register(struct slim_driver *drv, struct module *owner)
>> +{
>> +	drv->driver.bus = &slimbus_type;
>> +	drv->driver.owner = owner;
>> +	return driver_register(&drv->driver);
>> +}
>> +EXPORT_SYMBOL_GPL(__slim_driver_register);
> 
> any reason to use __ for this API?

This is made inline with __platfrom_driver_register() suggested in v5 
review.

> 
>> +static int slim_add_device(struct slim_controller *ctrl,
>> +			   struct slim_device *sbdev)
>> +{
>> +	sbdev->dev.bus = &slimbus_type;
>> +	sbdev->dev.parent = &ctrl->dev;
>> +	sbdev->dev.release = slim_dev_release;
>> +	sbdev->dev.driver = NULL;
>> +	sbdev->ctrl = ctrl;
>> +
>> +	slim_ctrl_get(ctrl);
>> +	sbdev->name = kasprintf(GFP_KERNEL, "%x:%x:%x:%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);
> 
> I dont think the comment is quite correct, you register a device not probe!
> 
Will fix this in next version.

>> +/* OF helpers for SLIMbus */
>> +static void of_register_slim_devices(struct slim_controller *ctrl)
>> +{
>> +	struct device *dev = &ctrl->dev;
>> +	struct device_node *node;
>> +
>> +	if (!ctrl->dev.of_node)
>> +		return;
>> +
>> +	for_each_child_of_node(ctrl->dev.of_node, node) {
>> +		struct slim_device *slim;
>> +		const char *compat = NULL;
>> +		char *p, *tok;
>> +		int reg[2], ret;
>> +
>> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
>> +		if (!slim)
>> +			continue;
>> +
>> +		slim->dev.of_node = of_node_get(node);
>> +
>> +		compat = of_get_property(node, "compatible", NULL);
>> +		if (!compat)
>> +			continue;
>> +
>> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
>> +
>> +		tok = strsep(&p, ",");
>> +		if (!tok) {
>> +			dev_err(dev, "No valid Manufacturer ID found\n");
>> +			kfree(p);
>> +			continue;
>> +		}
>> +		slim->e_addr.manf_id = str2hex(tok);
>> +
>> +		tok = strsep(&p, ",");
>> +		if (!tok) {
>> +			dev_err(dev, "No valid Product ID found\n");
>> +			kfree(p);
>> +			continue;
>> +		}
>> +		slim->e_addr.prod_code = str2hex(tok);
>> +		kfree(p);
>> +
>> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
>> +		if (ret) {
>> +			dev_err(dev, "Device and Instance id not found:%d\n",
>> +				ret);
>> +			continue;
>> +		}
>> +		slim->e_addr.dev_index = reg[0];
>> +		slim->e_addr.instance = reg[1];
>> +
>> +		ret = slim_add_device(ctrl, slim);
> 
> okay this is good stuff. So we scan the DT for slimbus devices and register
> them here. Same stuff we can do with ACPI :)
> 
> then why do we need the of register stuff I commented earlier. A Slimbus
> device can work irrespective of firmware type and registers using various
> ids. The platform will scan firmware (dt/acpi) create devices and load
> drivers against them generically.  Apart from this code we ideally should
> not have any DT parts in the bus, do you agree?

I partly agree with you, as all the devices on slimbus might not be in a 
discoverable state. Such devices would need some sort of power up 
sequence which what the of_wrapper and the match function are trying to 
achieve. Driver probe will be called based on the compatible match which 
would then power up/reset the device so that it can announce itself and 
the device_up() would be called at that point.

Your comment is 100% true, If the devices are in discoverable state, in 
such case we would not need any DT entires as you said.

> 
>> +		if (ret)
>> +			dev_err(dev, "of_slim device register err:%d\n", ret);
>> +	}
>> +}
>> +
>> +/**
>> + * 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);
> 
> what are these ids used for?

I think these are the controller ids, just to create a proper name space 
for each controller.

> 
>> +	mutex_unlock(&slim_lock);
>> +
>> +	if (id < 0)
>> +		return id;
>> +
>> +	ctrl->nr = id;
>> +
>> +	dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr);
>> +	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);
> 
> one more device_register?? Can you explain why
> 

This is a device for each controller.

>> +/**
>> + * 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 {
> 
> addrt? why not just addr?
yes, it can be done! will fix this in next version.
> 
>> +	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
> 
> BIT() GENMASK() please here and other places where they define bits in spec
> 
>> +/**
>> + * 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
> 
> do we need two callback, why not a status or notify callback with argument
> for up/down?

I will give it a try and see how it looks!

> 

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-10 12:34       ` [alsa-devel] " Srinivas Kandagatla
@ 2017-10-10 12:56         ` Charles Keepax
  -1 siblings, 0 replies; 87+ messages in thread
From: Charles Keepax @ 2017-10-10 12:56 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: mark.rutland, alsa-devel, michael.opdenacker, poeschel,
	andreas.noever, gong.chen, arnd, kheitke, treding, devicetree,
	james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1, robh+dt,
	sdharia, alan, bp, mathieu.poirier, jkosina, linux-kernel,
	broonie, daniel, gregkh, joe, davem

On Tue, Oct 10, 2017 at 01:34:48PM +0100, Srinivas Kandagatla wrote:
> Thanks for the review comments.
> 
> On 10/10/17 11:05, Charles Keepax wrote:
> > On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
> > > +Required property for SLIMbus child node if it is present:
> > > +- reg		- Is Duplex (Device index, Instance ID) from Enumeration
> > > +		  Address.
> > > +		  Device Index Uniquely identifies multiple Devices within
> > > +		  a single Component.
> > > +		  Instance ID Is for the cases where multiple Devices of the
> > > +		  same type or Class are attached to the bus.
> > > +
> > > +- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
> > > +	 	  Product Code, shall be in lower case hexadecimal with leading
> > > +		  zeroes suppressed
> > 
> > This does sort of make sense but kinda makes the code a bit ugly
> > parsing the MID and PID. Some parts will support SLIMBus and also
> > other control interfaces, which means they would need to add an
> > additional compatible string just for SLIMBus. It also breaks
> > the normal conventions of vendor,part and finally it makes the
> > compatible strings really unreadable which will be a bit annoying
> > when looking at DTs.
> > 
> This change was made inline to the comments provided in previous version of
> the patch https://lkml.org/lkml/2016/5/3/576
> 
> > I think the MID and PID should just be included in the reg field
> > and just leave this as a standard compatible.
> 
> AFAIK, reg field should only contain index and instance, which was also
> discussed  at https://lkml.org/lkml/2016/5/3/747
> 

Thanks for the links, if people are determined this is the way
to go I can live with it. I guess my primary objection is the
fact my parts are going to have different compatibles depending
on if they are on I2C/SPI or on SLIMBus (we have many parts that
support all three on the same chip) which feels like it violates
the principle of least surprise. Will wait to see if Arnd or Rob
have anymore thoughts to offer.

> > > +/* Helper to get hex Manufacturer ID and Product id from compatible */
> > > +static unsigned long str2hex(unsigned char *str)
> > > +{
> > > +	int value = 0;
> > > +
> > > +	while (*str) {
> > > +		char c = *str++;
> > > +
> > > +		value = value << 4;
> > > +		if (c >= '0' && c <= '9')
> > > +			value |= (c - '0');
> > > +		if (c >= 'a' && c <= 'f')
> > > +			value |= (c - 'a' + 10);
> > > +
> > > +	}
> > > +
> > > +	return value;
> > > +}
> > 
> > Isn't this just reimplementing kstrtoul?
> > 
> I would say partly, I think kstrtoul will only parse string as hex if its
> prefixed with "0x" But the compatible does not have 0x prefix..
> we could probably do some prefixing before passing to kstrtoul to remove
> above function.. I will try that and see!
> 

I am pretty sure kstrtoul is happy without the 0x as long as you
specify the base as 16 which I guess you should be doing here.

Thanks,
Charles

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-10 12:56         ` Charles Keepax
  0 siblings, 0 replies; 87+ messages in thread
From: Charles Keepax @ 2017-10-10 12:56 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

On Tue, Oct 10, 2017 at 01:34:48PM +0100, Srinivas Kandagatla wrote:
> Thanks for the review comments.
> 
> On 10/10/17 11:05, Charles Keepax wrote:
> > On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
> > > +Required property for SLIMbus child node if it is present:
> > > +- reg		- Is Duplex (Device index, Instance ID) from Enumeration
> > > +		  Address.
> > > +		  Device Index Uniquely identifies multiple Devices within
> > > +		  a single Component.
> > > +		  Instance ID Is for the cases where multiple Devices of the
> > > +		  same type or Class are attached to the bus.
> > > +
> > > +- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
> > > +	 	  Product Code, shall be in lower case hexadecimal with leading
> > > +		  zeroes suppressed
> > 
> > This does sort of make sense but kinda makes the code a bit ugly
> > parsing the MID and PID. Some parts will support SLIMBus and also
> > other control interfaces, which means they would need to add an
> > additional compatible string just for SLIMBus. It also breaks
> > the normal conventions of vendor,part and finally it makes the
> > compatible strings really unreadable which will be a bit annoying
> > when looking at DTs.
> > 
> This change was made inline to the comments provided in previous version of
> the patch https://lkml.org/lkml/2016/5/3/576
> 
> > I think the MID and PID should just be included in the reg field
> > and just leave this as a standard compatible.
> 
> AFAIK, reg field should only contain index and instance, which was also
> discussed  at https://lkml.org/lkml/2016/5/3/747
> 

Thanks for the links, if people are determined this is the way
to go I can live with it. I guess my primary objection is the
fact my parts are going to have different compatibles depending
on if they are on I2C/SPI or on SLIMBus (we have many parts that
support all three on the same chip) which feels like it violates
the principle of least surprise. Will wait to see if Arnd or Rob
have anymore thoughts to offer.

> > > +/* Helper to get hex Manufacturer ID and Product id from compatible */
> > > +static unsigned long str2hex(unsigned char *str)
> > > +{
> > > +	int value = 0;
> > > +
> > > +	while (*str) {
> > > +		char c = *str++;
> > > +
> > > +		value = value << 4;
> > > +		if (c >= '0' && c <= '9')
> > > +			value |= (c - '0');
> > > +		if (c >= 'a' && c <= 'f')
> > > +			value |= (c - 'a' + 10);
> > > +
> > > +	}
> > > +
> > > +	return value;
> > > +}
> > 
> > Isn't this just reimplementing kstrtoul?
> > 
> I would say partly, I think kstrtoul will only parse string as hex if its
> prefixed with "0x" But the compatible does not have 0x prefix..
> we could probably do some prefixing before passing to kstrtoul to remove
> above function.. I will try that and see!
> 

I am pretty sure kstrtoul is happy without the 0x as long as you
specify the base as 16 which I guess you should be doing here.

Thanks,
Charles

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

* Re: [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-10 12:19       ` [alsa-devel] " Charles Keepax
@ 2017-10-10 13:01         ` Srinivas Kandagatla
  -1 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-10 13:01 UTC (permalink / raw)
  To: Charles Keepax
  Cc: mark.rutland, alsa-devel, michael.opdenacker, poeschel,
	andreas.noever, gong.chen, arnd, kheitke, treding, devicetree,
	james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1, robh+dt,
	sdharia, alan, bp, mathieu.poirier, jkosina, linux-kernel,
	broonie, daniel, gregkh, joe, davem

Thanks for the review comments,

On 10/10/17 13:19, Charles Keepax wrote:
> On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
>>   drivers/slimbus/Makefile         |   2 +-
>>   drivers/slimbus/slim-core.c      |  15 ++
>>   drivers/slimbus/slim-messaging.c | 471 +++++++++++++++++++++++++++++++++++++++
>>   include/linux/slimbus.h          | 161 +++++++++++++
>>   4 files changed, 648 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/slimbus/slim-messaging.c
>>
>> +/**
>> + * 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)
> 
> Can all go on one line.
Thats true, Will fix it in next version.
> 
>> +{
>> +	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;
>> +	}
> 
> What is the intent of this if statement here? We set async
> locally so this code only runs if we executed the else on the if
> statement at the top. If its just clearing anything the user
> might have put in these fields why not do it up there.
Its clearing the temporary callback and context field when user wants to 
read/write on simbus synchronously.

If async is false user should not put anything in comp_cb or ctx.
comp_cb and ctx are only used when drivers are doing asynchronous 
read/writes on slimbus. Completion of those are indicated by comp_cb() 
with context.



> 
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(slim_processtxn);
>> +
>> +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);
> 
> cur should probably be removed until it is needed.
Yep.

> 
>> +	txn->ec = ((sl | (1 << 3)) | ((msg->start_offset & 0xFFF) << 4));
>> +
>> +	switch (mc) {
>> +	case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
>> +	case SLIM_MSG_MC_CHANGE_VALUE:
>> +	case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
>> +	case SLIM_MSG_MC_CLEAR_INFORMATION:
>> +		txn->rl += msg->num_bytes;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	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 */
>> +
>> +/*
>> + * slim_request_val_element: request value element
>> + * @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;
> 
> You could put this check into slim_xfer_msg and save duplicating
> it. Would also then cover cases that call that function directly,
> also would let you make these functions either inlines or macros.

I will give that a try in next version.

> 
>> +
>> +	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_VALUE);
>> +}
>> +EXPORT_SYMBOL_GPL(slim_request_val_element);
>> +
>> +/* Functions to get/return TX, RX buffers for messaging. */
>> +
>> +/**
>> + * slim_get_rx: To get RX buffers for messaging.
>> + * @ctrl: Controller handle
>> + * These functions are called by controller to process the RX buffers.
>> + * 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)
>> +{
>> +	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);
> 
> I find the combination of get/return a bit odd, would get/put
> maybe more idiomatic? Also the return could use some kernel doc.

If that makes it more readable I can rename the functions as suggested, 
and I will also add kernel doc in next version.

> 
>> +
>> +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);
>> +
>> +/**
>> + * slim_get_tx: To get TX buffers for messaging.
>> + * @ctrl: Controller handle
>> + * These functions are called by controller to process the TX 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.
>> + */
>> +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);
> 
> The rx calls seem ok that is basically the controller's job to
> call those, but with these two calls it seems sometimes the
> framework calls them sometimes the controller driver has to. Is
> there anyway we can simplify that a bit? Or at least include some
> documentation as to when the controller should call them.

I will try to do add some details in the document in next version.

> 
>> diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h
>> index b5064b6..080d86a 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) */
> 
> s/neededing/needing/
> 
Will fix this in next version.
>> +
>> +/* 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, }
> 
> If the LA isn't used in broadcast messages wouldn't it be simpler
> to set it to a fixed value in this macro?
> 
Yep, if the destination type is broadcast we should not set la or ea in 
the header. may be set 0 here.

>> +
>> +#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, }
>> +
> 
> Also one final overall comment this commit contains a lot of two
> and three letter abreviations that are not always clear. I would
> probably suggest expanding a few of the less standard ones out to
> make the code a little easier to follow.
Will do that!!

thanks
srini
> 
> Thanks,
> Charles
> 

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

* Re: [alsa-devel] [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
@ 2017-10-10 13:01         ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-10 13:01 UTC (permalink / raw)
  To: Charles Keepax
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

Thanks for the review comments,

On 10/10/17 13:19, Charles Keepax wrote:
> On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
>>   drivers/slimbus/Makefile         |   2 +-
>>   drivers/slimbus/slim-core.c      |  15 ++
>>   drivers/slimbus/slim-messaging.c | 471 +++++++++++++++++++++++++++++++++++++++
>>   include/linux/slimbus.h          | 161 +++++++++++++
>>   4 files changed, 648 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/slimbus/slim-messaging.c
>>
>> +/**
>> + * 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)
> 
> Can all go on one line.
Thats true, Will fix it in next version.
> 
>> +{
>> +	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;
>> +	}
> 
> What is the intent of this if statement here? We set async
> locally so this code only runs if we executed the else on the if
> statement at the top. If its just clearing anything the user
> might have put in these fields why not do it up there.
Its clearing the temporary callback and context field when user wants to 
read/write on simbus synchronously.

If async is false user should not put anything in comp_cb or ctx.
comp_cb and ctx are only used when drivers are doing asynchronous 
read/writes on slimbus. Completion of those are indicated by comp_cb() 
with context.



> 
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(slim_processtxn);
>> +
>> +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);
> 
> cur should probably be removed until it is needed.
Yep.

> 
>> +	txn->ec = ((sl | (1 << 3)) | ((msg->start_offset & 0xFFF) << 4));
>> +
>> +	switch (mc) {
>> +	case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
>> +	case SLIM_MSG_MC_CHANGE_VALUE:
>> +	case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
>> +	case SLIM_MSG_MC_CLEAR_INFORMATION:
>> +		txn->rl += msg->num_bytes;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	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 */
>> +
>> +/*
>> + * slim_request_val_element: request value element
>> + * @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;
> 
> You could put this check into slim_xfer_msg and save duplicating
> it. Would also then cover cases that call that function directly,
> also would let you make these functions either inlines or macros.

I will give that a try in next version.

> 
>> +
>> +	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_VALUE);
>> +}
>> +EXPORT_SYMBOL_GPL(slim_request_val_element);
>> +
>> +/* Functions to get/return TX, RX buffers for messaging. */
>> +
>> +/**
>> + * slim_get_rx: To get RX buffers for messaging.
>> + * @ctrl: Controller handle
>> + * These functions are called by controller to process the RX buffers.
>> + * 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)
>> +{
>> +	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);
> 
> I find the combination of get/return a bit odd, would get/put
> maybe more idiomatic? Also the return could use some kernel doc.

If that makes it more readable I can rename the functions as suggested, 
and I will also add kernel doc in next version.

> 
>> +
>> +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);
>> +
>> +/**
>> + * slim_get_tx: To get TX buffers for messaging.
>> + * @ctrl: Controller handle
>> + * These functions are called by controller to process the TX 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.
>> + */
>> +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);
> 
> The rx calls seem ok that is basically the controller's job to
> call those, but with these two calls it seems sometimes the
> framework calls them sometimes the controller driver has to. Is
> there anyway we can simplify that a bit? Or at least include some
> documentation as to when the controller should call them.

I will try to do add some details in the document in next version.

> 
>> diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h
>> index b5064b6..080d86a 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) */
> 
> s/neededing/needing/
> 
Will fix this in next version.
>> +
>> +/* 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, }
> 
> If the LA isn't used in broadcast messages wouldn't it be simpler
> to set it to a fixed value in this macro?
> 
Yep, if the destination type is broadcast we should not set la or ea in 
the header. may be set 0 here.

>> +
>> +#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, }
>> +
> 
> Also one final overall comment this commit contains a lot of two
> and three letter abreviations that are not always clear. I would
> probably suggest expanding a few of the less standard ones out to
> make the code a little easier to follow.
Will do that!!

thanks
srini
> 
> Thanks,
> Charles
> 

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-10 12:34       ` Srinivas Kandagatla
@ 2017-10-10 16:49           ` Vinod Koul
  0 siblings, 0 replies; 87+ messages in thread
From: Vinod Koul @ 2017-10-10 16:49 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: mark.rutland, alsa-devel, michael.opdenacker, poeschel,
	andreas.noever, gong.chen, arnd, kheitke, treding, devicetree,
	james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1, robh+dt,
	sdharia, alan, bp, mathieu.poirier, jkosina, linux-kernel,
	broonie, daniel, gregkh, joe, davem

On Tue, Oct 10, 2017 at 01:34:55PM +0100, Srinivas Kandagatla wrote:
> >>  9 files changed, 1192 insertions(+)
> >
> >thats a lot of code for review, consider splitting it up further for better
> >reviews
> 
> Its was suggested that parts of dtbindings and of_* wrapper merged into this
> patch.  In V5 review comments. https://lkml.org/lkml/2016/4/28/175

yes but it can still be split :)


> >>+static const struct slim_device_id *slim_match(const struct slim_device_id *id,
> >>+					       const struct slim_device *sbdev)
> >>+{
> >>+	while (id->manf_id != 0 || id->prod_code != 0) {
> >>+		if (id->manf_id == sbdev->e_addr.manf_id &&
> >>+		    id->prod_code == sbdev->e_addr.prod_code &&
> >>+		    id->dev_index == sbdev->e_addr.dev_index)
> >>+			return id;
> >>+		id++;
> >>+	}
> >>+	return NULL;
> >>+}
> >>+
> >>+static int slim_device_match(struct device *dev, struct device_driver *drv)
> >>+{
> >>+	struct slim_device *sbdev = to_slim_device(dev);
> >>+	struct slim_driver *sbdrv = to_slim_driver(drv);
> >>+
> >>+	/* Attempt an OF style match first */
> >>+	if (of_driver_match_device(dev, drv))
> >>+		return 1;
> >
> >is of_driver_match_device() a must have here? (I dont completely understand
> Yes, we need this to match the compatible string from device tree vs driver
> itself, most of the bus driver do this in bus match functions.
> 
> 
> >DT so pardon my ignorance). Since we have devices with ids can we use that
> >alone for matching?
> 
> Two cases to consider here,
> 1> If the device is up and discoverable.
> 2> Device is not discoverable yet, as it needs some power up sequence.
> 
> 
> In first case comparing with ID table makes sense.
> 
> But second case we would want to probe the device(for power sequencing)
> before we can discover the device on bus.
> 
> 
> This code as it is supports both DT and id_table.

Why not only id_table, see below:

> >>+	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);
> >
> >what do the device_up/down calls signify here?
> >
> up would be called when a device is discovered on the bus, and down on when
> the device disappeared on slimbus.
> 
> >>+static int slim_device_probe(struct device *dev)
> >>+{
> >>+	struct slim_device	*sbdev;
> >>+	struct slim_driver	*sbdrv;
> >>+	int status = 0;
> >>+
> >>+	sbdev = to_slim_device(dev);
> >>+	sbdrv = to_slim_driver(dev->driver);
> >>+
> >>+	sbdev->driver = sbdrv;
> >>+
> >>+	if (sbdrv->probe)
> >>+		status = sbdrv->probe(sbdev);
> >>+
> >>+	if (status)
> >>+		sbdev->driver = NULL;
> >>+	else if (sbdrv->device_up)
> >>+		schedule_slim_report(sbdev->ctrl, sbdev, true);
> >
> >can you please explain what this is trying to do?
> 
> It is scheduling a device_up() callback in workqueue for reporting
> discovered device.

any reason for that? Would the device not announce itself on the bus and
then you can synchronously update the device.

> >>+int __slim_driver_register(struct slim_driver *drv, struct module *owner)
> >>+{
> >>+	drv->driver.bus = &slimbus_type;
> >>+	drv->driver.owner = owner;
> >>+	return driver_register(&drv->driver);
> >>+}
> >>+EXPORT_SYMBOL_GPL(__slim_driver_register);
> >
> >any reason to use __ for this API?
> 
> This is made inline with __platfrom_driver_register() suggested in v5
> review.

I guess Greg is best person to make this call :)

> >>+static void of_register_slim_devices(struct slim_controller *ctrl)
> >>+{
> >>+	struct device *dev = &ctrl->dev;
> >>+	struct device_node *node;
> >>+
> >>+	if (!ctrl->dev.of_node)
> >>+		return;
> >>+
> >>+	for_each_child_of_node(ctrl->dev.of_node, node) {
> >>+		struct slim_device *slim;
> >>+		const char *compat = NULL;
> >>+		char *p, *tok;
> >>+		int reg[2], ret;
> >>+
> >>+		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
> >>+		if (!slim)
> >>+			continue;
> >>+
> >>+		slim->dev.of_node = of_node_get(node);
> >>+
> >>+		compat = of_get_property(node, "compatible", NULL);
> >>+		if (!compat)
> >>+			continue;
> >>+
> >>+		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
> >>+
> >>+		tok = strsep(&p, ",");
> >>+		if (!tok) {
> >>+			dev_err(dev, "No valid Manufacturer ID found\n");
> >>+			kfree(p);
> >>+			continue;
> >>+		}
> >>+		slim->e_addr.manf_id = str2hex(tok);
> >>+
> >>+		tok = strsep(&p, ",");
> >>+		if (!tok) {
> >>+			dev_err(dev, "No valid Product ID found\n");
> >>+			kfree(p);
> >>+			continue;
> >>+		}
> >>+		slim->e_addr.prod_code = str2hex(tok);
> >>+		kfree(p);
> >>+
> >>+		ret = of_property_read_u32_array(node, "reg", reg, 2);
> >>+		if (ret) {
> >>+			dev_err(dev, "Device and Instance id not found:%d\n",
> >>+				ret);
> >>+			continue;
> >>+		}
> >>+		slim->e_addr.dev_index = reg[0];
> >>+		slim->e_addr.instance = reg[1];
> >>+
> >>+		ret = slim_add_device(ctrl, slim);
> >
> >okay this is good stuff. So we scan the DT for slimbus devices and register
> >them here. Same stuff we can do with ACPI :)
> >
> >then why do we need the of register stuff I commented earlier. A Slimbus
> >device can work irrespective of firmware type and registers using various
> >ids. The platform will scan firmware (dt/acpi) create devices and load
> >drivers against them generically.  Apart from this code we ideally should
> >not have any DT parts in the bus, do you agree?
> 
> I partly agree with you, as all the devices on slimbus might not be in a
> discoverable state. Such devices would need some sort of power up sequence
> which what the of_wrapper and the match function are trying to achieve.
> Driver probe will be called based on the compatible match which would then
> power up/reset the device so that it can announce itself and the device_up()
> would be called at that point.
> 
> Your comment is 100% true, If the devices are in discoverable state, in such
> case we would not need any DT entires as you said.

Yes you are right. Since the device need to be powered up thru side band we
cannot live without using firmware (acpi/dt)

Now consider the below scheme:
 - The Bus scans device node of the controller (acpi/dt), as
   above and find the slim devices and adds them. The ID needs to be
   extracted from firmware (acpi/dt), we sure need to set the right firmware
   node for the device
 - Drivers register based on ID, no need to make it DT/ACPI aware
 - Match function is invoked and driver probed
 - Sideband mechanism kick in and power up device and it announces on the
   bus

In case it is already powered up, it can announce being up earlier.

FWIW i am using above method with ACPI on SoundWire.

> 
> >
> >>+		if (ret)
> >>+			dev_err(dev, "of_slim device register err:%d\n", ret);
> >>+	}
> >>+}
> >>+
> >>+/**
> >>+ * 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);
> >
> >what are these ids used for?
> 
> I think these are the controller ids, just to create a proper name space for
> each controller.
> 
> >
> >>+	mutex_unlock(&slim_lock);
> >>+
> >>+	if (id < 0)
> >>+		return id;
> >>+
> >>+	ctrl->nr = id;
> >>+
> >>+	dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr);
> >>+	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);
> >
> >one more device_register?? Can you explain why
> >
> 
> This is a device for each controller.

wont the controller have its own platform_device?

-- 
~Vinod

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-10 16:49           ` Vinod Koul
  0 siblings, 0 replies; 87+ messages in thread
From: Vinod Koul @ 2017-10-10 16:49 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

On Tue, Oct 10, 2017 at 01:34:55PM +0100, Srinivas Kandagatla wrote:
> >>  9 files changed, 1192 insertions(+)
> >
> >thats a lot of code for review, consider splitting it up further for better
> >reviews
> 
> Its was suggested that parts of dtbindings and of_* wrapper merged into this
> patch.  In V5 review comments. https://lkml.org/lkml/2016/4/28/175

yes but it can still be split :)


> >>+static const struct slim_device_id *slim_match(const struct slim_device_id *id,
> >>+					       const struct slim_device *sbdev)
> >>+{
> >>+	while (id->manf_id != 0 || id->prod_code != 0) {
> >>+		if (id->manf_id == sbdev->e_addr.manf_id &&
> >>+		    id->prod_code == sbdev->e_addr.prod_code &&
> >>+		    id->dev_index == sbdev->e_addr.dev_index)
> >>+			return id;
> >>+		id++;
> >>+	}
> >>+	return NULL;
> >>+}
> >>+
> >>+static int slim_device_match(struct device *dev, struct device_driver *drv)
> >>+{
> >>+	struct slim_device *sbdev = to_slim_device(dev);
> >>+	struct slim_driver *sbdrv = to_slim_driver(drv);
> >>+
> >>+	/* Attempt an OF style match first */
> >>+	if (of_driver_match_device(dev, drv))
> >>+		return 1;
> >
> >is of_driver_match_device() a must have here? (I dont completely understand
> Yes, we need this to match the compatible string from device tree vs driver
> itself, most of the bus driver do this in bus match functions.
> 
> 
> >DT so pardon my ignorance). Since we have devices with ids can we use that
> >alone for matching?
> 
> Two cases to consider here,
> 1> If the device is up and discoverable.
> 2> Device is not discoverable yet, as it needs some power up sequence.
> 
> 
> In first case comparing with ID table makes sense.
> 
> But second case we would want to probe the device(for power sequencing)
> before we can discover the device on bus.
> 
> 
> This code as it is supports both DT and id_table.

Why not only id_table, see below:

> >>+	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);
> >
> >what do the device_up/down calls signify here?
> >
> up would be called when a device is discovered on the bus, and down on when
> the device disappeared on slimbus.
> 
> >>+static int slim_device_probe(struct device *dev)
> >>+{
> >>+	struct slim_device	*sbdev;
> >>+	struct slim_driver	*sbdrv;
> >>+	int status = 0;
> >>+
> >>+	sbdev = to_slim_device(dev);
> >>+	sbdrv = to_slim_driver(dev->driver);
> >>+
> >>+	sbdev->driver = sbdrv;
> >>+
> >>+	if (sbdrv->probe)
> >>+		status = sbdrv->probe(sbdev);
> >>+
> >>+	if (status)
> >>+		sbdev->driver = NULL;
> >>+	else if (sbdrv->device_up)
> >>+		schedule_slim_report(sbdev->ctrl, sbdev, true);
> >
> >can you please explain what this is trying to do?
> 
> It is scheduling a device_up() callback in workqueue for reporting
> discovered device.

any reason for that? Would the device not announce itself on the bus and
then you can synchronously update the device.

> >>+int __slim_driver_register(struct slim_driver *drv, struct module *owner)
> >>+{
> >>+	drv->driver.bus = &slimbus_type;
> >>+	drv->driver.owner = owner;
> >>+	return driver_register(&drv->driver);
> >>+}
> >>+EXPORT_SYMBOL_GPL(__slim_driver_register);
> >
> >any reason to use __ for this API?
> 
> This is made inline with __platfrom_driver_register() suggested in v5
> review.

I guess Greg is best person to make this call :)

> >>+static void of_register_slim_devices(struct slim_controller *ctrl)
> >>+{
> >>+	struct device *dev = &ctrl->dev;
> >>+	struct device_node *node;
> >>+
> >>+	if (!ctrl->dev.of_node)
> >>+		return;
> >>+
> >>+	for_each_child_of_node(ctrl->dev.of_node, node) {
> >>+		struct slim_device *slim;
> >>+		const char *compat = NULL;
> >>+		char *p, *tok;
> >>+		int reg[2], ret;
> >>+
> >>+		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
> >>+		if (!slim)
> >>+			continue;
> >>+
> >>+		slim->dev.of_node = of_node_get(node);
> >>+
> >>+		compat = of_get_property(node, "compatible", NULL);
> >>+		if (!compat)
> >>+			continue;
> >>+
> >>+		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
> >>+
> >>+		tok = strsep(&p, ",");
> >>+		if (!tok) {
> >>+			dev_err(dev, "No valid Manufacturer ID found\n");
> >>+			kfree(p);
> >>+			continue;
> >>+		}
> >>+		slim->e_addr.manf_id = str2hex(tok);
> >>+
> >>+		tok = strsep(&p, ",");
> >>+		if (!tok) {
> >>+			dev_err(dev, "No valid Product ID found\n");
> >>+			kfree(p);
> >>+			continue;
> >>+		}
> >>+		slim->e_addr.prod_code = str2hex(tok);
> >>+		kfree(p);
> >>+
> >>+		ret = of_property_read_u32_array(node, "reg", reg, 2);
> >>+		if (ret) {
> >>+			dev_err(dev, "Device and Instance id not found:%d\n",
> >>+				ret);
> >>+			continue;
> >>+		}
> >>+		slim->e_addr.dev_index = reg[0];
> >>+		slim->e_addr.instance = reg[1];
> >>+
> >>+		ret = slim_add_device(ctrl, slim);
> >
> >okay this is good stuff. So we scan the DT for slimbus devices and register
> >them here. Same stuff we can do with ACPI :)
> >
> >then why do we need the of register stuff I commented earlier. A Slimbus
> >device can work irrespective of firmware type and registers using various
> >ids. The platform will scan firmware (dt/acpi) create devices and load
> >drivers against them generically.  Apart from this code we ideally should
> >not have any DT parts in the bus, do you agree?
> 
> I partly agree with you, as all the devices on slimbus might not be in a
> discoverable state. Such devices would need some sort of power up sequence
> which what the of_wrapper and the match function are trying to achieve.
> Driver probe will be called based on the compatible match which would then
> power up/reset the device so that it can announce itself and the device_up()
> would be called at that point.
> 
> Your comment is 100% true, If the devices are in discoverable state, in such
> case we would not need any DT entires as you said.

Yes you are right. Since the device need to be powered up thru side band we
cannot live without using firmware (acpi/dt)

Now consider the below scheme:
 - The Bus scans device node of the controller (acpi/dt), as
   above and find the slim devices and adds them. The ID needs to be
   extracted from firmware (acpi/dt), we sure need to set the right firmware
   node for the device
 - Drivers register based on ID, no need to make it DT/ACPI aware
 - Match function is invoked and driver probed
 - Sideband mechanism kick in and power up device and it announces on the
   bus

In case it is already powered up, it can announce being up earlier.

FWIW i am using above method with ACPI on SoundWire.

> 
> >
> >>+		if (ret)
> >>+			dev_err(dev, "of_slim device register err:%d\n", ret);
> >>+	}
> >>+}
> >>+
> >>+/**
> >>+ * 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);
> >
> >what are these ids used for?
> 
> I think these are the controller ids, just to create a proper name space for
> each controller.
> 
> >
> >>+	mutex_unlock(&slim_lock);
> >>+
> >>+	if (id < 0)
> >>+		return id;
> >>+
> >>+	ctrl->nr = id;
> >>+
> >>+	dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr);
> >>+	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);
> >
> >one more device_register?? Can you explain why
> >
> 
> This is a device for each controller.

wont the controller have its own platform_device?

-- 
~Vinod

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-10 16:49           ` [alsa-devel] " Vinod Koul
  (?)
@ 2017-10-10 17:21           ` Srinivas Kandagatla
  2017-10-11  4:07             ` Vinod Koul
  -1 siblings, 1 reply; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-10 17:21 UTC (permalink / raw)
  To: Vinod Koul
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem



On 10/10/17 17:49, Vinod Koul wrote:
> On Tue, Oct 10, 2017 at 01:34:55PM +0100, Srinivas Kandagatla wrote:
>>>>   9 files changed, 1192 insertions(+)
>>>
>>> thats a lot of code for review, consider splitting it up further for better
>>> reviews
>>
>> Its was suggested that parts of dtbindings and of_* wrapper merged into this
>> patch.  In V5 review comments. https://lkml.org/lkml/2016/4/28/175
> 
> yes but it can still be split :)
> 
Will give it a go in next version!!
> 
>>>> +static const struct slim_device_id *slim_match(const struct slim_device_id *id,
>>>> +					       const struct slim_device *sbdev)
>>>> +{
>>>> +	while (id->manf_id != 0 || id->prod_code != 0) {
>>>> +		if (id->manf_id == sbdev->e_addr.manf_id &&
>>>> +		    id->prod_code == sbdev->e_addr.prod_code &&
>>>> +		    id->dev_index == sbdev->e_addr.dev_index)
>>>> +			return id;
>>>> +		id++;
>>>> +	}
>>>> +	return NULL;
>>>> +}
>>>> +
>>>> +static int slim_device_match(struct device *dev, struct device_driver *drv)
>>>> +{
>>>> +	struct slim_device *sbdev = to_slim_device(dev);
>>>> +	struct slim_driver *sbdrv = to_slim_driver(drv);
>>>> +
>>>> +	/* Attempt an OF style match first */
>>>> +	if (of_driver_match_device(dev, drv))
>>>> +		return 1;
>>>
>>> is of_driver_match_device() a must have here? (I dont completely understand
>> Yes, we need this to match the compatible string from device tree vs driver
>> itself, most of the bus driver do this in bus match functions.
>>
>>
>>> DT so pardon my ignorance). Since we have devices with ids can we use that
>>> alone for matching?
>>
>> Two cases to consider here,
>> 1> If the device is up and discoverable.
>> 2> Device is not discoverable yet, as it needs some power up sequence.
>>
>>
>> In first case comparing with ID table makes sense.
>>
>> But second case we would want to probe the device(for power sequencing)
>> before we can discover the device on bus.
>>
>>
>> This code as it is supports both DT and id_table.
> 
> Why not only id_table, see below:
> 


Yes, we make id_table as mandatory field for all slimbus drivers.


>>>> +	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);
>>>
>>> what do the device_up/down calls signify here?
>>>
>> up would be called when a device is discovered on the bus, and down on when
>> the device disappeared on slimbus.
>>
>>>> +static int slim_device_probe(struct device *dev)
>>>> +{
>>>> +	struct slim_device	*sbdev;
>>>> +	struct slim_driver	*sbdrv;
>>>> +	int status = 0;
>>>> +
>>>> +	sbdev = to_slim_device(dev);
>>>> +	sbdrv = to_slim_driver(dev->driver);
>>>> +
>>>> +	sbdev->driver = sbdrv;
>>>> +
>>>> +	if (sbdrv->probe)
>>>> +		status = sbdrv->probe(sbdev);
>>>> +
>>>> +	if (status)
>>>> +		sbdev->driver = NULL;
>>>> +	else if (sbdrv->device_up)
>>>> +		schedule_slim_report(sbdev->ctrl, sbdev, true);
>>>
>>> can you please explain what this is trying to do?
>>
>> It is scheduling a device_up() callback in workqueue for reporting
>> discovered device.
> 
> any reason for that? Would the device not announce itself on the bus and
> then you can synchronously update the device.
You are correct,  Device should announce itself in all cases. core 
should only call this callback only when device is announced, it does 
not make sense for this call in slim_device_probe(). Will remove it from 
here in next version.


> 
>>>> +int __slim_driver_register(struct slim_driver *drv, struct module *owner)
>>>> +{
>>>> +	drv->driver.bus = &slimbus_type;
>>>> +	drv->driver.owner = owner;
>>>> +	return driver_register(&drv->driver);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(__slim_driver_register);
>>>
>>> any reason to use __ for this API?
>>
>> This is made inline with __platfrom_driver_register() suggested in v5
>> review.
> 
> I guess Greg is best person to make this call :)
> 

Forgot to put original comment in v5 by Arnd: 
https://lkml.org/lkml/2016/4/28/179


>>>> +static void of_register_slim_devices(struct slim_controller *ctrl)
>>>> +{
>>>> +	struct device *dev = &ctrl->dev;
>>>> +	struct device_node *node;
>>>> +
>>>> +	if (!ctrl->dev.of_node)
>>>> +		return;
>>>> +
>>>> +	for_each_child_of_node(ctrl->dev.of_node, node) {
>>>> +		struct slim_device *slim;
>>>> +		const char *compat = NULL;
>>>> +		char *p, *tok;
>>>> +		int reg[2], ret;
>>>> +
>>>> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
>>>> +		if (!slim)
>>>> +			continue;
>>>> +
>>>> +		slim->dev.of_node = of_node_get(node);
>>>> +
>>>> +		compat = of_get_property(node, "compatible", NULL);
>>>> +		if (!compat)
>>>> +			continue;
>>>> +
>>>> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
>>>> +
>>>> +		tok = strsep(&p, ",");
>>>> +		if (!tok) {
>>>> +			dev_err(dev, "No valid Manufacturer ID found\n");
>>>> +			kfree(p);
>>>> +			continue;
>>>> +		}
>>>> +		slim->e_addr.manf_id = str2hex(tok);
>>>> +
>>>> +		tok = strsep(&p, ",");
>>>> +		if (!tok) {
>>>> +			dev_err(dev, "No valid Product ID found\n");
>>>> +			kfree(p);
>>>> +			continue;
>>>> +		}
>>>> +		slim->e_addr.prod_code = str2hex(tok);
>>>> +		kfree(p);
>>>> +
>>>> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
>>>> +		if (ret) {
>>>> +			dev_err(dev, "Device and Instance id not found:%d\n",
>>>> +				ret);
>>>> +			continue;
>>>> +		}
>>>> +		slim->e_addr.dev_index = reg[0];
>>>> +		slim->e_addr.instance = reg[1];
>>>> +
>>>> +		ret = slim_add_device(ctrl, slim);
>>>
>>> okay this is good stuff. So we scan the DT for slimbus devices and register
>>> them here. Same stuff we can do with ACPI :)
>>>
>>> then why do we need the of register stuff I commented earlier. A Slimbus
>>> device can work irrespective of firmware type and registers using various
>>> ids. The platform will scan firmware (dt/acpi) create devices and load
>>> drivers against them generically.  Apart from this code we ideally should
>>> not have any DT parts in the bus, do you agree?
>>
>> I partly agree with you, as all the devices on slimbus might not be in a
>> discoverable state. Such devices would need some sort of power up sequence
>> which what the of_wrapper and the match function are trying to achieve.
>> Driver probe will be called based on the compatible match which would then
>> power up/reset the device so that it can announce itself and the device_up()
>> would be called at that point.
>>
>> Your comment is 100% true, If the devices are in discoverable state, in such
>> case we would not need any DT entires as you said.
> 
> Yes you are right. Since the device need to be powered up thru side band we
> cannot live without using firmware (acpi/dt)
> 
> Now consider the below scheme:
>   - The Bus scans device node of the controller (acpi/dt), as
>     above and find the slim devices and adds them. The ID needs to be
>     extracted from firmware (acpi/dt), we sure need to set the right firmware
>     node for the device
>   - Drivers register based on ID, no need to make it DT/ACPI aware
>   - Match function is invoked and driver probed
>   - Sideband mechanism kick in and power up device and it announces on the
>     bus
> 

I will make id_table mandatory in next version, which should remove the 
of_matching function and code should look as you suggested.

> In case it is already powered up, it can announce being up earlier.
> 
> FWIW i am using above method with ACPI on SoundWire.
Ah..okay

> 
>>
>>>
>>>> +		if (ret)
>>>> +			dev_err(dev, "of_slim device register err:%d\n", ret);
>>>> +	}
>>>> +}
>>>> +
>>>> +/**
>>>> + * slim_register_controller: Controller bring-up and registration.
...
>>>> +
>>>> +	mutex_init(&ctrl->m_ctrl);
>>>> +	ret = device_register(&ctrl->dev);
>>>
>>> one more device_register?? Can you explain why
>>>
>>
>> This is a device for each controller.
> 
> wont the controller have its own platform_device?

Reason could be that slim_register controller can be called from any 
code not just platform devices..


> 

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-10 17:21           ` Srinivas Kandagatla
@ 2017-10-11  4:07             ` Vinod Koul
  2017-10-11  9:42               ` Srinivas Kandagatla
  0 siblings, 1 reply; 87+ messages in thread
From: Vinod Koul @ 2017-10-11  4:07 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

On Tue, Oct 10, 2017 at 06:21:34PM +0100, Srinivas Kandagatla wrote:
> On 10/10/17 17:49, Vinod Koul wrote:

> >>>>+static int slim_device_probe(struct device *dev)
> >>>>+{
> >>>>+	struct slim_device	*sbdev;
> >>>>+	struct slim_driver	*sbdrv;
> >>>>+	int status = 0;
> >>>>+
> >>>>+	sbdev = to_slim_device(dev);
> >>>>+	sbdrv = to_slim_driver(dev->driver);
> >>>>+
> >>>>+	sbdev->driver = sbdrv;
> >>>>+
> >>>>+	if (sbdrv->probe)
> >>>>+		status = sbdrv->probe(sbdev);
> >>>>+
> >>>>+	if (status)
> >>>>+		sbdev->driver = NULL;
> >>>>+	else if (sbdrv->device_up)
> >>>>+		schedule_slim_report(sbdev->ctrl, sbdev, true);
> >>>
> >>>can you please explain what this is trying to do?
> >>
> >>It is scheduling a device_up() callback in workqueue for reporting
> >>discovered device.
> >
> >any reason for that? Would the device not announce itself on the bus and
> >then you can synchronously update the device.
> You are correct,  Device should announce itself in all cases. core should
> only call this callback only when device is announced, it does not make
> sense for this call in slim_device_probe(). Will remove it from here in next
> version.

Okay great. Btw do you need a notify being scheduled in those cases? I guess
your controller would get an interrupt and you will handle that in bottom
half and then cll this, so why not call in the bottom half?

> >>>>+/**
> >>>>+ * slim_register_controller: Controller bring-up and registration.
> ...
> >>>>+
> >>>>+	mutex_init(&ctrl->m_ctrl);
> >>>>+	ret = device_register(&ctrl->dev);
> >>>
> >>>one more device_register?? Can you explain why
> >>>
> >>
> >>This is a device for each controller.
> >
> >wont the controller have its own platform_device?
> 
> Reason could be that slim_register controller can be called from any code
> not just platform devices..

ah which cases would those be. I was expecting that you would have a
platform_device as a slimbus controller which would call slim_register?

-- 
~Vinod

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

* Re: [alsa-devel] [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-06 15:51     ` srinivas.kandagatla
                       ` (2 preceding siblings ...)
  (?)
@ 2017-10-11  4:38     ` Vinod Koul
  2017-10-11  7:53         ` Arnd Bergmann
  2017-10-11  9:42         ` [alsa-devel] " Srinivas Kandagatla
  -1 siblings, 2 replies; 87+ messages in thread
From: Vinod Koul @ 2017-10-11  4:38 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:

>  	mutex_init(&ctrl->m_ctrl);
> +	spin_lock_init(&ctrl->tx.lock);
> +	spin_lock_init(&ctrl->rx.lock);

locks galore :) My assumption is that you want to optimize these? But given
that audio user is going to be serialized do we practically need two locks?

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

i though v5 comment from Arnd was not to use semaphores..

> +/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.

2017?

> +int slim_processtxn(struct slim_controller *ctrl,

slim_process_txn seems more readable to me

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

that is interesting, I was expecting this to be internal API. So users are
expected to use this which is not very convenient IMO. Can we hide the gory
details and give users simple tx/rx or read/write APIs. FWIW most of the
usage would be thru regmap where people would call regmap_read/write()

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

line break here

> +	switch (mc) {
> +	case SLIM_MSG_MC_REQUEST_VALUE:
> +	case SLIM_MSG_MC_REQUEST_INFORMATION:

what does MC refer to?

> +		if (msg->rbuf != NULL)
> +			return 0;
> +		break;

after each break too

> +static u16 slim_slicecodefromsize(u16 req)

hmmm Linux code doesnt prefernamesnames like this :)

> +EXPORT_SYMBOL_GPL(slim_request_inf_element);
> +
> +

unnecessary double space

> +struct slim_val_inf {
> +	u16			start_offset;
> +	u8			num_bytes;
> +	u8			*rbuf;
> +	const u8		*wbuf;

can we do read and write, if not it can be a buf which maybe rbuf or wbug
based on type

-- 
~Vinod

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

* Re: [alsa-devel] [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-11  4:38     ` Vinod Koul
@ 2017-10-11  7:53         ` Arnd Bergmann
  2017-10-11  9:42         ` [alsa-devel] " Srinivas Kandagatla
  1 sibling, 0 replies; 87+ messages in thread
From: Arnd Bergmann @ 2017-10-11  7:53 UTC (permalink / raw)
  To: Vinod Koul
  Cc: Srinivas Kandagatla, gregkh, Mark Brown,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Mark Rutland,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	gong.chen-VuQAYsv1563Yd54FQh9/CA, kheitke-hxvC4TZJLZFWk0Htik3J/w,
	Borislav Petkov, DTML, James Hogan, Pawel Moll,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA, Rob Herring,
	sdharia-sgV2jX0FEOL9JmXXK+q4OQ, Alan Cox, Thierry Reding,
	Mathieu Poirier, jkosina-AlSwsSmVLrQ, Linux Kernel Mailing List,
	Dani

On Wed, Oct 11, 2017 at 6:38 AM, Vinod Koul <vinod.koul-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> wrote:
> On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org wrote:
>> +     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));
>
> i though v5 comment from Arnd was not to use semaphores..

Right, these need to go away. Thanks for spotting it!

     Arnd
--
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] 87+ messages in thread

* Re: [alsa-devel] [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
@ 2017-10-11  7:53         ` Arnd Bergmann
  0 siblings, 0 replies; 87+ messages in thread
From: Arnd Bergmann @ 2017-10-11  7:53 UTC (permalink / raw)
  To: Vinod Koul
  Cc: Srinivas Kandagatla, gregkh, Mark Brown, alsa-devel,
	Mark Rutland, michael.opdenacker, poeschel, andreas.noever,
	gong.chen, kheitke, Borislav Petkov, DTML, James Hogan,
	Pawel Moll, linux-arm-msm, sharon.dvir1, Rob Herring, sdharia,
	Alan Cox, Thierry Reding, Mathieu Poirier, jkosina,
	Linux Kernel Mailing List, Daniel Vetter, Joe Perches,
	David Miller

On Wed, Oct 11, 2017 at 6:38 AM, Vinod Koul <vinod.koul@intel.com> wrote:
> On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:
>> +     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));
>
> i though v5 comment from Arnd was not to use semaphores..

Right, these need to go away. Thanks for spotting it!

     Arnd

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-11  4:07             ` Vinod Koul
@ 2017-10-11  9:42               ` Srinivas Kandagatla
  2017-10-11 10:21                 ` Vinod Koul
  0 siblings, 1 reply; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-11  9:42 UTC (permalink / raw)
  To: Vinod Koul
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem



On 11/10/17 05:07, Vinod Koul wrote:
> On Tue, Oct 10, 2017 at 06:21:34PM +0100, Srinivas Kandagatla wrote:
>> On 10/10/17 17:49, Vinod Koul wrote:
> 
>>>>>> +static int slim_device_probe(struct device *dev)
>>>>>> +{
>>>>>> +	struct slim_device	*sbdev;
>>>>>> +	struct slim_driver	*sbdrv;
>>>>>> +	int status = 0;
>>>>>> +
>>>>>> +	sbdev = to_slim_device(dev);
>>>>>> +	sbdrv = to_slim_driver(dev->driver);
>>>>>> +
>>>>>> +	sbdev->driver = sbdrv;
>>>>>> +
>>>>>> +	if (sbdrv->probe)
>>>>>> +		status = sbdrv->probe(sbdev);
>>>>>> +
>>>>>> +	if (status)
>>>>>> +		sbdev->driver = NULL;
>>>>>> +	else if (sbdrv->device_up)
>>>>>> +		schedule_slim_report(sbdev->ctrl, sbdev, true);
>>>>>
>>>>> can you please explain what this is trying to do?
>>>>
>>>> It is scheduling a device_up() callback in workqueue for reporting
>>>> discovered device.
>>>
>>> any reason for that? Would the device not announce itself on the bus and
>>> then you can synchronously update the device.
>> You are correct,  Device should announce itself in all cases. core should
>> only call this callback only when device is announced, it does not make
>> sense for this call in slim_device_probe(). Will remove it from here in next
>> version.
> 
> Okay great. Btw do you need a notify being scheduled in those cases? I guess
> your controller would get an interrupt and you will handle that in bottom
> half and then cll this, so why not call in the bottom half?
> 
That makes sense, I will optimize this path, It looks like there are 2 
workqueues in this path. We should be able to get rid of one work-queue.



>>>>>> +/**
>>>>>> + * slim_register_controller: Controller bring-up and registration.
>> ...
>>>>>> +
>>>>>> +	mutex_init(&ctrl->m_ctrl);
>>>>>> +	ret = device_register(&ctrl->dev);
>>>>>
>>>>> one more device_register?? Can you explain why
>>>>>
>>>>
>>>> This is a device for each controller.
>>>
>>> wont the controller have its own platform_device?
>>
>> Reason could be that slim_register controller can be called from any code
>> not just platform devices..
> 
> ah which cases would those be. I was expecting that you would have a
> platform_device as a slimbus controller which would call slim_register?
As of now there is only one controller which uses platform driver, but 
in future there might be more, but this is something which makes the 
slimbus core more flexible.


> 

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

* Re: [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-11  4:38     ` Vinod Koul
@ 2017-10-11  9:42         ` Srinivas Kandagatla
  2017-10-11  9:42         ` [alsa-devel] " Srinivas Kandagatla
  1 sibling, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-11  9:42 UTC (permalink / raw)
  To: Vinod Koul
  Cc: mark.rutland, alsa-devel, michael.opdenacker, poeschel,
	andreas.noever, gong.chen, arnd, kheitke, treding, devicetree,
	james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1, robh+dt,
	sdharia, alan, bp, mathieu.poirier, jkosina, linux-kernel,
	broonie, daniel, gregkh, joe, davem



On 11/10/17 05:38, Vinod Koul wrote:
> On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:
> 
>>   	mutex_init(&ctrl->m_ctrl);
>> +	spin_lock_init(&ctrl->tx.lock);
>> +	spin_lock_init(&ctrl->rx.lock);
> 
> locks galore :) My assumption is that you want to optimize these? But given
> that audio user is going to be serialized do we practically need two locks?
> 
If we remove the locking, It will be issue if we have multiple devices 
in a component, which is common atleast with the codec which am looking at.


>> +
>> +	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));
> 
> i though v5 comment from Arnd was not to use semaphores..

I will revisit this area once again and get rid of this semaphore all 
together in next version.

> 
>> +/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
> 
> 2017?
Yep.
> 
>> +int slim_processtxn(struct slim_controller *ctrl,
> 
> slim_process_txn seems more readable to me
> 
I can change it in next version.

>> +				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);
> 
> that is interesting, I was expecting this to be internal API. So users are
> expected to use this which is not very convenient IMO. Can we hide the gory
> details and give users simple tx/rx or read/write APIs. FWIW most of the
> usage would be thru regmap where people would call regmap_read/write()
> 

Currently the only user of the api is qcom slim controller. May be the 
reason for it to use this api is to fit in with all the locking and 
sequencing mechanism with in the core.

I will be revisiting the semaphore thingy before I send next version, 
hopefully I can to align with your above comments.



>> +
>> +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;
> 
> line break here

I agree.

> 
>> +	switch (mc) {
>> +	case SLIM_MSG_MC_REQUEST_VALUE:
>> +	case SLIM_MSG_MC_REQUEST_INFORMATION:
> 
> what does MC refer to?

Message Code.

> 
>> +		if (msg->rbuf != NULL)
>> +			return 0;
>> +		break;
> 
> after each break too
> 
Sure, will fix it in next version.

>> +static u16 slim_slicecodefromsize(u16 req)
> 
> hmmm Linux code doesnt prefernamesnames like this :)
Yep, this function is unused, am going to delete this in next version.
> 
>> +EXPORT_SYMBOL_GPL(slim_request_inf_element);
>> +
>> +
> 
> unnecessary double space
> 
>> +struct slim_val_inf {
>> +	u16			start_offset;
>> +	u8			num_bytes;
>> +	u8			*rbuf;
>> +	const u8		*wbuf;
> 
> can we do read and write, if not it can be a buf which maybe rbuf or wbug
> based on type
With REQUEST_CHANGE_VALUE single command we can read old value at the 
same time we can write new value.

> 

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

* Re: [alsa-devel] [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
@ 2017-10-11  9:42         ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-11  9:42 UTC (permalink / raw)
  To: Vinod Koul
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem



On 11/10/17 05:38, Vinod Koul wrote:
> On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:
> 
>>   	mutex_init(&ctrl->m_ctrl);
>> +	spin_lock_init(&ctrl->tx.lock);
>> +	spin_lock_init(&ctrl->rx.lock);
> 
> locks galore :) My assumption is that you want to optimize these? But given
> that audio user is going to be serialized do we practically need two locks?
> 
If we remove the locking, It will be issue if we have multiple devices 
in a component, which is common atleast with the codec which am looking at.


>> +
>> +	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));
> 
> i though v5 comment from Arnd was not to use semaphores..

I will revisit this area once again and get rid of this semaphore all 
together in next version.

> 
>> +/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
> 
> 2017?
Yep.
> 
>> +int slim_processtxn(struct slim_controller *ctrl,
> 
> slim_process_txn seems more readable to me
> 
I can change it in next version.

>> +				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);
> 
> that is interesting, I was expecting this to be internal API. So users are
> expected to use this which is not very convenient IMO. Can we hide the gory
> details and give users simple tx/rx or read/write APIs. FWIW most of the
> usage would be thru regmap where people would call regmap_read/write()
> 

Currently the only user of the api is qcom slim controller. May be the 
reason for it to use this api is to fit in with all the locking and 
sequencing mechanism with in the core.

I will be revisiting the semaphore thingy before I send next version, 
hopefully I can to align with your above comments.



>> +
>> +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;
> 
> line break here

I agree.

> 
>> +	switch (mc) {
>> +	case SLIM_MSG_MC_REQUEST_VALUE:
>> +	case SLIM_MSG_MC_REQUEST_INFORMATION:
> 
> what does MC refer to?

Message Code.

> 
>> +		if (msg->rbuf != NULL)
>> +			return 0;
>> +		break;
> 
> after each break too
> 
Sure, will fix it in next version.

>> +static u16 slim_slicecodefromsize(u16 req)
> 
> hmmm Linux code doesnt prefernamesnames like this :)
Yep, this function is unused, am going to delete this in next version.
> 
>> +EXPORT_SYMBOL_GPL(slim_request_inf_element);
>> +
>> +
> 
> unnecessary double space
> 
>> +struct slim_val_inf {
>> +	u16			start_offset;
>> +	u8			num_bytes;
>> +	u8			*rbuf;
>> +	const u8		*wbuf;
> 
> can we do read and write, if not it can be a buf which maybe rbuf or wbug
> based on type
With REQUEST_CHANGE_VALUE single command we can read old value at the 
same time we can write new value.

> 

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-11  9:42               ` Srinivas Kandagatla
@ 2017-10-11 10:21                 ` Vinod Koul
  2017-10-11 11:23                   ` Srinivas Kandagatla
  0 siblings, 1 reply; 87+ messages in thread
From: Vinod Koul @ 2017-10-11 10:21 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

On Wed, Oct 11, 2017 at 10:42:23AM +0100, Srinivas Kandagatla wrote:
> On 11/10/17 05:07, Vinod Koul wrote:
> >On Tue, Oct 10, 2017 at 06:21:34PM +0100, Srinivas Kandagatla wrote:
> >>On 10/10/17 17:49, Vinod Koul wrote:

> >>>>>>+/**
> >>>>>>+ * slim_register_controller: Controller bring-up and registration.
> >>...
> >>>>>>+
> >>>>>>+	mutex_init(&ctrl->m_ctrl);
> >>>>>>+	ret = device_register(&ctrl->dev);
> >>>>>
> >>>>>one more device_register?? Can you explain why
> >>>>>
> >>>>
> >>>>This is a device for each controller.
> >>>
> >>>wont the controller have its own platform_device?
> >>
> >>Reason could be that slim_register controller can be called from any code
> >>not just platform devices..
> >
> >ah which cases would those be. I was expecting that you would have a
> >platform_device as a slimbus controller which would call slim_register?
> As of now there is only one controller which uses platform driver, but in
> future there might be more, but this is something which makes the slimbus
> core more flexible.

even if you have more controllers wouldn't we have similar number of platform
devices. Each instance of the link/controller would have its device node.

Thanks
-- 
~Vinod

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-10 12:34       ` [alsa-devel] " Srinivas Kandagatla
@ 2017-10-11 10:23         ` Mark Brown
  -1 siblings, 0 replies; 87+ messages in thread
From: Mark Brown @ 2017-10-11 10:23 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: mark.rutland, alsa-devel, michael.opdenacker, poeschel,
	andreas.noever, gong.chen, arnd, kheitke, treding, devicetree,
	james.hogan, Charles Keepax, pawel.moll, linux-arm-msm,
	sharon.dvir1, robh+dt, sdharia, alan, bp, mathieu.poirier,
	jkosina, linux-kernel, daniel, gregkh, joe, davem


[-- Attachment #1.1: Type: text/plain, Size: 1163 bytes --]

On Tue, Oct 10, 2017 at 01:34:48PM +0100, Srinivas Kandagatla wrote:
> On 10/10/17 11:05, Charles Keepax wrote:

Please delete unneeded context from mails when replying.  Doing this
makes it much easier to find your reply in the message, helping ensure
it won't be missed by people scrolling through the irrelevant quoted
material.

> > This does sort of make sense but kinda makes the code a bit ugly
> > parsing the MID and PID. Some parts will support SLIMBus and also
> > other control interfaces, which means they would need to add an
> > additional compatible string just for SLIMBus. It also breaks
> > the normal conventions of vendor,part and finally it makes the
> > compatible strings really unreadable which will be a bit annoying
> > when looking at DTs.

> This change was made inline to the comments provided in previous version of
> the patch https://lkml.org/lkml/2016/5/3/576

I'm not sure I really agree with Rob here - while Slimbus is notionally
discoverable I don't think I ever saw a practical system which relied on
that for enumeration.  In real systems the discoverability is more of a
complexity to be worked around than anything else.

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

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-11 10:23         ` Mark Brown
  0 siblings, 0 replies; 87+ messages in thread
From: Mark Brown @ 2017-10-11 10:23 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Charles Keepax, gregkh, alsa-devel, mark.rutland,
	michael.opdenacker, poeschel, andreas.noever, gong.chen, arnd,
	kheitke, bp, devicetree, james.hogan, pawel.moll, linux-arm-msm,
	sharon.dvir1, robh+dt, sdharia, alan, treding, mathieu.poirier,
	jkosina, linux-kernel, daniel, joe, davem

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

On Tue, Oct 10, 2017 at 01:34:48PM +0100, Srinivas Kandagatla wrote:
> On 10/10/17 11:05, Charles Keepax wrote:

Please delete unneeded context from mails when replying.  Doing this
makes it much easier to find your reply in the message, helping ensure
it won't be missed by people scrolling through the irrelevant quoted
material.

> > This does sort of make sense but kinda makes the code a bit ugly
> > parsing the MID and PID. Some parts will support SLIMBus and also
> > other control interfaces, which means they would need to add an
> > additional compatible string just for SLIMBus. It also breaks
> > the normal conventions of vendor,part and finally it makes the
> > compatible strings really unreadable which will be a bit annoying
> > when looking at DTs.

> This change was made inline to the comments provided in previous version of
> the patch https://lkml.org/lkml/2016/5/3/576

I'm not sure I really agree with Rob here - while Slimbus is notionally
discoverable I don't think I ever saw a practical system which relied on
that for enumeration.  In real systems the discoverability is more of a
complexity to be worked around than anything else.

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

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

* Re: [alsa-devel] [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-11  9:42         ` [alsa-devel] " Srinivas Kandagatla
@ 2017-10-11 10:24             ` Vinod Koul
  -1 siblings, 0 replies; 87+ messages in thread
From: Vinod Koul @ 2017-10-11 10:24 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, mark.rutland-5wv7dgnIgG8,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	gong.chen-VuQAYsv1563Yd54FQh9/CA, arnd-r2nGTMty4D4,
	kheitke-hxvC4TZJLZFWk0Htik3J/w, bp-l3A5Bk7waGM,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA, pawel.moll-5wv7dgnIgG8,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, sdharia-sgV2jX0FEOL9JmXXK+q4OQ,
	alan-VuQAYsv1563Yd54FQh9/CA, treding-DDmLM1+adcrQT0dZR+AlfA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, jkosina-AlSwsSmVLrQ,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, daniel-/w4YWyX8dFk,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q

On Wed, Oct 11, 2017 at 10:42:28AM +0100, Srinivas Kandagatla wrote:
> On 11/10/17 05:38, Vinod Koul wrote:
> >On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org wrote:
> >
> >>  	mutex_init(&ctrl->m_ctrl);
> >>+	spin_lock_init(&ctrl->tx.lock);
> >>+	spin_lock_init(&ctrl->rx.lock);
> >
> >locks galore :) My assumption is that you want to optimize these? But given
> >that audio user is going to be serialized do we practically need two locks?
> >
> If we remove the locking, It will be issue if we have multiple devices in a
> component, which is common atleast with the codec which am looking at.

can you explian what you mean by a "device" here?

> >>+	switch (mc) {
> >>+	case SLIM_MSG_MC_REQUEST_VALUE:
> >>+	case SLIM_MSG_MC_REQUEST_INFORMATION:
> >
> >what does MC refer to?
> 
> Message Code.

isnt SLIM_MSG enough :D I think we cna get rid of MC here..

> >>+struct slim_val_inf {
> >>+	u16			start_offset;
> >>+	u8			num_bytes;
> >>+	u8			*rbuf;
> >>+	const u8		*wbuf;
> >
> >can we do read and write, if not it can be a buf which maybe rbuf or wbug
> >based on type
> With REQUEST_CHANGE_VALUE single command we can read old value at the same
> time we can write new value.

so that is a read modify write, correct? Is that implemented in HW, if so we
need to provide only write value

-- 
~Vinod
--
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] 87+ messages in thread

* Re: [alsa-devel] [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
@ 2017-10-11 10:24             ` Vinod Koul
  0 siblings, 0 replies; 87+ messages in thread
From: Vinod Koul @ 2017-10-11 10:24 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

On Wed, Oct 11, 2017 at 10:42:28AM +0100, Srinivas Kandagatla wrote:
> On 11/10/17 05:38, Vinod Koul wrote:
> >On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:
> >
> >>  	mutex_init(&ctrl->m_ctrl);
> >>+	spin_lock_init(&ctrl->tx.lock);
> >>+	spin_lock_init(&ctrl->rx.lock);
> >
> >locks galore :) My assumption is that you want to optimize these? But given
> >that audio user is going to be serialized do we practically need two locks?
> >
> If we remove the locking, It will be issue if we have multiple devices in a
> component, which is common atleast with the codec which am looking at.

can you explian what you mean by a "device" here?

> >>+	switch (mc) {
> >>+	case SLIM_MSG_MC_REQUEST_VALUE:
> >>+	case SLIM_MSG_MC_REQUEST_INFORMATION:
> >
> >what does MC refer to?
> 
> Message Code.

isnt SLIM_MSG enough :D I think we cna get rid of MC here..

> >>+struct slim_val_inf {
> >>+	u16			start_offset;
> >>+	u8			num_bytes;
> >>+	u8			*rbuf;
> >>+	const u8		*wbuf;
> >
> >can we do read and write, if not it can be a buf which maybe rbuf or wbug
> >based on type
> With REQUEST_CHANGE_VALUE single command we can read old value at the same
> time we can write new value.

so that is a read modify write, correct? Is that implemented in HW, if so we
need to provide only write value

-- 
~Vinod

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

* Re: [alsa-devel] [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-11 10:24             ` Vinod Koul
  (?)
@ 2017-10-11 11:12             ` Srinivas Kandagatla
  -1 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-11 11:12 UTC (permalink / raw)
  To: Vinod Koul
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem



On 11/10/17 11:24, Vinod Koul wrote:
> On Wed, Oct 11, 2017 at 10:42:28AM +0100, Srinivas Kandagatla wrote:
>> On 11/10/17 05:38, Vinod Koul wrote:
>>> On Fri, Oct 06, 2017 at 05:51:31PM +0200, srinivas.kandagatla@linaro.org wrote:
>>>
>>>>   	mutex_init(&ctrl->m_ctrl);
>>>> +	spin_lock_init(&ctrl->tx.lock);
>>>> +	spin_lock_init(&ctrl->rx.lock);
>>>
>>> locks galore :) My assumption is that you want to optimize these? But given
>>> that audio user is going to be serialized do we practically need two locks?
>>>
>> If we remove the locking, It will be issue if we have multiple devices in a
>> component, which is common atleast with the codec which am looking at.
> 
> can you explian what you mean by a "device" here?

SLIMbus component contain two or more SLIMbus devices,
like function(Generic) device, interface device.
Interface device provides bus management services, where as generic 
device provides more of application specific functionality like ADC/DAC..


> 
>>>> +	switch (mc) {
>>>> +	case SLIM_MSG_MC_REQUEST_VALUE:
>>>> +	case SLIM_MSG_MC_REQUEST_INFORMATION:
>>>
>>> what does MC refer to?
>>
>> Message Code.
> 
> isnt SLIM_MSG enough :D I think we cna get rid of MC here..
> 
>>>> +struct slim_val_inf {
>>>> +	u16			start_offset;
>>>> +	u8			num_bytes;
>>>> +	u8			*rbuf;
>>>> +	const u8		*wbuf;
>>>
>>> can we do read and write, if not it can be a buf which maybe rbuf or wbug
>>> based on type
>> With REQUEST_CHANGE_VALUE single command we can read old value at the same
>> time we can write new value.
> 
> so that is a read modify write, correct? Is that implemented in HW, if so we
> need to provide only write value

Its not really a read-modify-write,
REQUEST_CHANGE_VALUE/REQUEST_CLEAR_INFORMATION commands are part of 
SLIMbus Spec.
We need provide write value + buffer for read value to store.

all REQUEST_CHANGE_VALUE cmd do is this in single operation:

1> save the old value
2> update new value from wbuf
3> return the saved value from step 1, into rbuf

Not sure what is the real usecase for this, I have not seen its usage in 
any Qualcomm downstream code.

May be it can be used to implement some class of atomic ops.

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-11 10:21                 ` Vinod Koul
@ 2017-10-11 11:23                   ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-11 11:23 UTC (permalink / raw)
  To: Vinod Koul
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem



On 11/10/17 11:21, Vinod Koul wrote:
>>> ah which cases would those be. I was expecting that you would have a
>>> platform_device as a slimbus controller which would call slim_register?
>> As of now there is only one controller which uses platform driver, but in
>> future there might be more, but this is something which makes the slimbus
>> core more flexible.
> even if you have more controllers wouldn't we have similar number of platform
> devices. Each instance of the link/controller would have its device node.
>
Yep, I will give at try and see in next version!


thanks,
srini


> Thanks
> -- ~Vinod

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-06 15:51   ` srinivas.kandagatla
                     ` (2 preceding siblings ...)
  (?)
@ 2017-10-12 11:01   ` Sanyog Kale
  2017-10-12 13:26       ` Srinivas Kandagatla
  -1 siblings, 1 reply; 87+ messages in thread
From: Sanyog Kale @ 2017-10-12 11:01 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem

On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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). 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@codeaurora.org>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
>  Documentation/slimbus/summary                     | 109 ++++
>  drivers/Kconfig                                   |   2 +
>  drivers/Makefile                                  |   1 +
>  drivers/slimbus/Kconfig                           |  11 +
>  drivers/slimbus/Makefile                          |   5 +
>  drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
>  include/linux/mod_devicetable.h                   |  13 +
>  include/linux/slimbus.h                           | 299 ++++++++++
>  9 files changed, 1192 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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 include/linux/slimbus.h
> 
> diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
> new file mode 100644
> index 0000000..cb658bb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/slimbus/bus.txt
> @@ -0,0 +1,57 @@
> +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 2
> +- #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.
> +If child node is not present and it is instantiated after device
> +discovery (slave device reporting itself present).
> +
> +In some cases it may be necessary to describe non-probeable device
> +details such as non-standard ways of powering up a device. In
> +such cases, child nodes for those devices will be present as
> +slaves of the slimbus-controller, as detailed below.
> +
> +Required property for SLIMbus child node if it is present:
> +- reg		- Is Duplex (Device index, Instance ID) from Enumeration
> +		  Address.
> +		  Device Index Uniquely identifies multiple Devices within
> +		  a single Component.
> +		  Instance ID Is for the cases where multiple Devices of the
> +		  same type or Class are attached to the bus.
> +
> +- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
> +	 	  Product Code, shall be in lower case hexadecimal with leading
> +		  zeroes suppressed
> +
> +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";
> +		#address-cells = <2>;
> +		#size-cells = <0>;
> +
> +		codec: wcd9310@1{
> +			compatible = "slim217,60"";
> +			reg = <1 0>;
> +		};
> +	};
> diff --git a/Documentation/slimbus/summary b/Documentation/slimbus/summary
> new file mode 100644
> index 0000000..e7f90bb
> --- /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.

Does the spec mandate 10 clock gears or its controller property?

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

Is the same logical address assign when it reports present again?

> +
> +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 505c676..8010c67 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -208,4 +208,6 @@ source "drivers/tee/Kconfig"
>  
>  source "drivers/mux/Kconfig"
>  
> +source "drivers/slimbus/Kconfig"
> +
>  endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index d90fdc4..0449c7c 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -86,6 +86,7 @@ obj-$(CONFIG_MTD)		+= mtd/
>  obj-$(CONFIG_SPI)		+= spi/
>  obj-$(CONFIG_SPMI)		+= spmi/
>  obj-$(CONFIG_HSI)		+= hsi/
> +obj-$(CONFIG_SLIMBUS)		+= slimbus/
>  obj-y				+= net/
>  obj-$(CONFIG_ATM)		+= atm/
>  obj-$(CONFIG_FUSION)		+= message/
> 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..f580704
> --- /dev/null
> +++ b/drivers/slimbus/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for kernel slimbus framework.
> +#
> +obj-$(CONFIG_SLIMBUS)			+= slimbus.o
> +slimbus-y				:= slim-core.o
> diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
> new file mode 100644
> index 0000000..de3ef79
> --- /dev/null
> +++ b/drivers/slimbus/slim-core.c
> @@ -0,0 +1,695 @@
> +/* Copyright (c) 2011-2017, 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>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +
> +static DEFINE_MUTEX(slim_lock);
> +static DEFINE_IDR(ctrl_idr);
> +
> +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 *sbdev)
> +{
> +	while (id->manf_id != 0 || id->prod_code != 0) {
> +		if (id->manf_id == sbdev->e_addr.manf_id &&
> +		    id->prod_code == sbdev->e_addr.prod_code &&
> +		    id->dev_index == sbdev->e_addr.dev_index)
> +			return id;
> +		id++;
> +	}
> +	return NULL;
> +}
> +
> +static int slim_device_match(struct device *dev, struct device_driver *drv)
> +{
> +	struct slim_device *sbdev = to_slim_device(dev);
> +	struct slim_driver *sbdrv = to_slim_driver(drv);
> +
> +	/* Attempt an OF style match first */
> +	if (of_driver_match_device(dev, drv))
> +		return 1;
> +
> +	/* Then try to match against the id table */
> +	if (sbdrv->id_table)
> +		return slim_match(sbdrv->id_table, sbdev) != NULL;
> +
> +	return 0;
> +}
> +
> +struct sb_report_wd {
> +	struct work_struct wd;
> +	struct slim_device *sbdev;
> +	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->sbdev;
> +
> +	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);
> +}
> +
> +/**
> + * Report callbacks(device_up, device_down) are implemented by slimbus-devices.
> + * The calls are scheduled into a workqueue to avoid holding up controller
> + * initialization/tear-down.
> + */
> +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->sbdev = 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	*sbdev;
> +	struct slim_driver	*sbdrv;
> +	int status = 0;
> +
> +	sbdev = to_slim_device(dev);
> +	sbdrv = to_slim_driver(dev->driver);
> +
> +	sbdev->driver = sbdrv;
> +
> +	if (sbdrv->probe)
> +		status = sbdrv->probe(sbdev);
> +
> +	if (status)
> +		sbdev->driver = NULL;
> +	else if (sbdrv->device_up)
> +		schedule_slim_report(sbdev->ctrl, sbdev, true);
> +
> +	return status;
> +}
> +
> +static int slim_device_remove(struct device *dev)
> +{
> +	struct slim_device *sbdev;
> +	struct slim_driver *sbdrv;
> +	int status = 0;
> +
> +	sbdev = to_slim_device(dev);
> +	if (!dev->driver)
> +		return 0;
> +
> +	sbdrv = to_slim_driver(dev->driver);
> +	if (sbdrv->remove)
> +		status = sbdrv->remove(sbdev);
> +
> +	mutex_lock(&sbdev->report_lock);
> +	sbdev->notified = false;
> +	if (status == 0)
> +		sbdev->driver = NULL;
> +	mutex_unlock(&sbdev->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.
> + * @owner: owning module/driver
> + * 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, struct module *owner)
> +{
> +	drv->driver.bus = &slimbus_type;
> +	drv->driver.owner = owner;
> +	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);
> +}
> +EXPORT_SYMBOL_GPL(slim_driver_unregister);
> +
> +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);
> +	kfree(sbdev->name);
> +	kfree(sbdev);
> +}
> +
> +static int slim_add_device(struct slim_controller *ctrl,
> +			   struct slim_device *sbdev)
> +{
> +	sbdev->dev.bus = &slimbus_type;
> +	sbdev->dev.parent = &ctrl->dev;
> +	sbdev->dev.release = slim_dev_release;
> +	sbdev->dev.driver = NULL;
> +	sbdev->ctrl = ctrl;
> +
> +	slim_ctrl_get(ctrl);
> +	sbdev->name = kasprintf(GFP_KERNEL, "%x:%x:%x:%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);
> +}
> +
> +/* Helper to get hex Manufacturer ID and Product id from compatible */
> +static unsigned long str2hex(unsigned char *str)
> +{
> +	int value = 0;
> +
> +	while (*str) {
> +		char c = *str++;
> +
> +		value = value << 4;
> +		if (c >= '0' && c <= '9')
> +			value |= (c - '0');
> +		if (c >= 'a' && c <= 'f')
> +			value |= (c - 'a' + 10);
> +
> +	}
> +
> +	return value;
> +}
> +
> +/* OF helpers for SLIMbus */
> +static void of_register_slim_devices(struct slim_controller *ctrl)
> +{
> +	struct device *dev = &ctrl->dev;
> +	struct device_node *node;
> +
> +	if (!ctrl->dev.of_node)
> +		return;
> +
> +	for_each_child_of_node(ctrl->dev.of_node, node) {
> +		struct slim_device *slim;
> +		const char *compat = NULL;
> +		char *p, *tok;
> +		int reg[2], ret;
> +
> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
> +		if (!slim)
> +			continue;
> +
> +		slim->dev.of_node = of_node_get(node);
> +
> +		compat = of_get_property(node, "compatible", NULL);
> +		if (!compat)
> +			continue;
> +
> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Manufacturer ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.manf_id = str2hex(tok);
> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Product ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.prod_code = str2hex(tok);
> +		kfree(p);
> +
> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
> +		if (ret) {
> +			dev_err(dev, "Device and Instance id not found:%d\n",
> +				ret);
> +			continue;
> +		}
> +		slim->e_addr.dev_index = reg[0];
> +		slim->e_addr.instance = reg[1];
> +
> +		ret = slim_add_device(ctrl, slim);
> +		if (ret)
> +			dev_err(dev, "of_slim device register err:%d\n", ret);
> +	}
> +}
> +
> +/**
> + * 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->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;
> +
> +	of_register_slim_devices(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() */
> +static void slim_remove_device(struct slim_device *sbdev)
> +{
> +	device_unregister(&sbdev->dev);
> +}
> +
> +static int slim_ctrl_remove_device(struct device *dev, void *null)
> +{
> +	slim_remove_device(to_slim_device(dev));
> +	return 0;
> +}
> +
> +/**
> + * slim_del_controller: Controller tear-down.
> + * @ctrl: Controller to tear-down.
> + */
> +int slim_del_controller(struct slim_controller *ctrl)
> +{
> +	struct slim_controller *found;
> +
> +	/* 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 */
> +	device_for_each_child(&ctrl->dev, NULL, slim_ctrl_remove_device);
> +
> +
> +	destroy_workqueue(ctrl->wq);
> +
> +	/* free bus id */
> +	mutex_lock(&slim_lock);
> +	idr_remove(&ctrl_idr, ctrl->nr);
> +	mutex_unlock(&slim_lock);
> +
> +	device_unregister(&ctrl->dev);
> +	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;
> +
> +	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;
> +
> +	dev_info(&ctrl->dev, "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 = -ENODEV;
> +	} 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 address.
> + * @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);
> +}
> +module_exit(slimbus_exit);
> +
> +static int __init slimbus_init(void)
> +{
> +	return bus_register(&slimbus_type);
> +}
> +postcore_initcall(slimbus_init);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_VERSION("0.1");
> +MODULE_DESCRIPTION("Slimbus module");
> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
> index 694cebb..015e5f6 100644
> --- a/include/linux/mod_devicetable.h
> +++ b/include/linux/mod_devicetable.h
> @@ -448,6 +448,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..b5064b6
> --- /dev/null
> +++ b/include/linux/slimbus.h
> @@ -0,0 +1,299 @@
> +/* Copyright (c) 2011-2017, 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
> + * @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.

xfer_msg element is not present in structure.

> + * @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;
> +	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;
> +	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.
> + *  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
> + */
> +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;
> +};
> +
> +#define to_slim_device(d) container_of(d, struct slim_device, dev)
> +
> +/* Manager's logical address is set to 0xFF per spec */
> +#define SLIM_LA_MANAGER 0xFF
> +
> +int slim_get_logical_addr(struct slim_device *sb,
> +				 struct slim_eaddr *e_addr, u8 *laddr);
> +
> +/*
> + * use a macro to avoid include chaining to get THIS_MODULE
> + */
> +#define slim_driver_register(drv) \
> +	__slim_driver_register(drv, THIS_MODULE)
> +
> +int __slim_driver_register(struct slim_driver *drv, struct module *owner);
> +
> +void slim_driver_unregister(struct slim_driver *drv);
> +
> +/**
> + * module_slim_driver() - Helper macro for registering a slimbus driver
> + * @__slimbus_driver: slimbus_driver struct
> + *
> + * Helper macro for slimbus drivers which do not do anything special in module
> + * init/exit. This eliminates a lot of boilerplate. Each module may only
> + * use this macro once, and calling it replaces module_init() and module_exit()
> + */
> +#define module_slim_driver(__slim_driver) \
> +	module_driver(__slim_driver, slim_driver_register, \
> +			slim_driver_unregister)
> +
> +int slim_del_controller(struct slim_controller *ctrl);
> +int slim_assign_laddr(struct slim_controller *ctrl,
> +		      struct slim_eaddr *e_addr, u8 *laddr, bool valid);
> +void slim_report_absent(struct slim_device *sbdev);
> +void slim_framer_booted(struct slim_controller *ctrl);
> +int slim_register_controller(struct slim_controller *ctrl);
> +
> +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_devicedata(struct slim_device *dev, void *data)
> +{
> +	dev_set_drvdata(&dev->dev, data);
> +}
> +
> +#endif /* _LINUX_SLIMBUS_H */
> -- 
> 2.9.3
> 
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

-- 

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

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-12 11:01   ` [alsa-devel] " Sanyog Kale
@ 2017-10-12 13:26       ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-12 13:26 UTC (permalink / raw)
  To: Sanyog Kale
  Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, mark.rutland-5wv7dgnIgG8,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	gong.chen-VuQAYsv1563Yd54FQh9/CA, arnd-r2nGTMty4D4,
	kheitke-hxvC4TZJLZFWk0Htik3J/w, bp-l3A5Bk7waGM,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA, pawel.moll-5wv7dgnIgG8,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, sdharia-sgV2jX0FEOL9JmXXK+q4OQ,
	alan-VuQAYsv1563Yd54FQh9/CA, treding-DDmLM1+adcrQT0dZR+AlfA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, jkosina-AlSwsSmVLrQ,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, daniel-/w4YWyX8dFk,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q



On 12/10/17 12:01, Sanyog Kale wrote:
> On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org wrote:
>> From: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
>>
>> +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.
> 
> Does the spec mandate 10 clock gears or its controller property?

Clock Gear Construct is part of SLIMbus Specs to alter clk frequency.


>> +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 controller.
>> +
>> +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.
> 
> Is the same logical address assign when it reports present again?

Currently, Code as it is will pick the first available logical address. 
If required we can add logic in future to assign same logical address.
For now the code is simple.



>> + *
>> + *	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
>> + * @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.
> 
> xfer_msg element is not present in structure.
> 

Thanks, it will be fixed in next version.

thanks,
Srini


--
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] 87+ messages in thread

* Re: [alsa-devel] [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-12 13:26       ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-12 13:26 UTC (permalink / raw)
  To: Sanyog Kale
  Cc: gregkh, broonie, alsa-devel, mark.rutland, michael.opdenacker,
	poeschel, andreas.noever, gong.chen, arnd, kheitke, bp,
	devicetree, james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1,
	robh+dt, sdharia, alan, treding, mathieu.poirier, jkosina,
	linux-kernel, daniel, joe, davem



On 12/10/17 12:01, Sanyog Kale wrote:
> On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> +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.
> 
> Does the spec mandate 10 clock gears or its controller property?

Clock Gear Construct is part of SLIMbus Specs to alter clk frequency.


>> +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 controller.
>> +
>> +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.
> 
> Is the same logical address assign when it reports present again?

Currently, Code as it is will pick the first available logical address. 
If required we can add logic in future to assign same logical address.
For now the code is simple.



>> + *
>> + *	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
>> + * @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.
> 
> xfer_msg element is not present in structure.
> 

Thanks, it will be fixed in next version.

thanks,
Srini

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

* Re: [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver
  2017-10-06 15:51 ` [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver srinivas.kandagatla
  2017-10-07  7:45   ` Jonathan Neuschäfer
@ 2017-10-13 19:17   ` Rob Herring
  2017-10-16  9:28     ` Srinivas Kandagatla
  2017-10-18  7:27   ` Bjorn Andersson
  2 siblings, 1 reply; 87+ messages in thread
From: Rob Herring @ 2017-10-13 19:17 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, pawel.moll, mark.rutland, devicetree,
	linux-kernel, kheitke, linux-arm-msm, arnd

On Fri, Oct 06, 2017 at 05:51:32PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  .../devicetree/bindings/slimbus/slim-qcom-ctrl.txt |  43 ++

Version 6 and this is the first I see it? Where's the version history? 

Please split to separate patch.

>  drivers/slimbus/Kconfig                            |   9 +
>  drivers/slimbus/Makefile                           |   3 +
>  drivers/slimbus/slim-qcom-ctrl.c                   | 594 +++++++++++++++++++++
>  drivers/slimbus/slim-qcom.h                        |  63 +++
>  5 files changed, 712 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..081110d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt
> @@ -0,0 +1,43 @@
> +Qualcomm SLIMBUS controller
> +This controller is used if applications processor driver controls slimbus
> +master component.
> +
> +Required properties:
> +
> + - #address-cells - refer to Documentation/devicetree/bindings/slimbus/bus.txt
> + - #size-cells	- refer to Documentation/devicetree/bindings/slimbus/bus.txt
> +
> + - 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:
> +	 "ctrl": Physical adderess of controller register blocks
> + - compatible : should be "qcom,<SOC-NAME>-slim" for SOC specific compatible or
> + 		"qcom,slim" if using generic qcom SLIM IP.

No such thing as generic IP.

Fine as a fallback, but not by itself.

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

_clk is redundant.

> +
> +
> +Optional property:
> + - reg entry for slew rate : If slew rate control register is provided, this
> +	entry should be used.
> + - reg-name for slew rate: "slew"

Pointless to have -name when there is only one.

> +
> +Example:
> +	slim@28080000 {
> +		compatible = "qcom,slim";
> +		#address-cells = <4>;
> +		#size-cells = <0>;
> +		reg = <0x28080000 0x2000>, <0x80207C 4>;
> +		reg-names = "ctrl", "slew";
> +		interrupts = <0 33 0>;
> +		clocks = <&lcc SLIMBUS_SRC>, <&lcc AUDIO_SLIMBUS_CLK>;
> +		clock-names = "iface_clk", "core_clk";
> +
> +		codec: wcd9310@1{
> +			compatible = "slim217,60";
> +			reg = <1 0>;

This would not compile. You don't have 4 cells here.

> +		};
> +	};

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-06 15:51   ` srinivas.kandagatla
@ 2017-10-13 19:26       ` Rob Herring
  -1 siblings, 0 replies; 87+ messages in thread
From: Rob Herring @ 2017-10-13 19:26 UTC (permalink / raw)
  To: srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A
  Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	sdharia-sgV2jX0FEOL9JmXXK+q4OQ, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, treding-DDmLM1+adcrQT0dZR+AlfA,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	jkosina-AlSwsSmVLrQ, sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kheitke-hxvC4TZJLZFWk0Htik3J/w,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4

On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org wrote:
> From: Sagar Dharia <sdharia-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
> 
> 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). 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>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>  Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++

Split to separate patch.

>  Documentation/slimbus/summary                     | 109 ++++
>  drivers/Kconfig                                   |   2 +
>  drivers/Makefile                                  |   1 +
>  drivers/slimbus/Kconfig                           |  11 +
>  drivers/slimbus/Makefile                          |   5 +
>  drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
>  include/linux/mod_devicetable.h                   |  13 +
>  include/linux/slimbus.h                           | 299 ++++++++++
>  9 files changed, 1192 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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 include/linux/slimbus.h
> 
> diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
> new file mode 100644
> index 0000000..cb658bb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/slimbus/bus.txt
> @@ -0,0 +1,57 @@
> +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).

I can't have a PCI based slimbus controller? "platform bus" is a 
Linuxism.

> +Required property for SLIMbus controller node:
> +- compatible	- name of SLIMbus controller following generic names
> +		recommended practice.

generic names aren't recommended. Allowed with some conditons, yes.

> +- #address-cells - should be 2

You used 4 for your controller.

> +- #size-cells	- should be 0
> +
> +No other properties are required in the SLIMbus controller bus node.

That's not a useful statement. Almost every controller probably has 
other required properties.

> +
> +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.
> +If child node is not present and it is instantiated after device
> +discovery (slave device reporting itself present).
> +
> +In some cases it may be necessary to describe non-probeable device
> +details such as non-standard ways of powering up a device. In
> +such cases, child nodes for those devices will be present as
> +slaves of the slimbus-controller, as detailed below.
> +
> +Required property for SLIMbus child node if it is present:
> +- reg		- Is Duplex (Device index, Instance ID) from Enumeration
> +		  Address.
> +		  Device Index Uniquely identifies multiple Devices within
> +		  a single Component.
> +		  Instance ID Is for the cases where multiple Devices of the
> +		  same type or Class are attached to the bus.
> +
> +- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
> +	 	  Product Code, shall be in lower case hexadecimal with leading
> +		  zeroes suppressed
> +
> +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";
> +		#address-cells = <2>;
> +		#size-cells = <0>;
> +
> +		codec: wcd9310@1{

Is '1' by itself unique enough because you have 2 address cells. The 
unit-address is typically split up into fields with commas unless it's 
just a memory address. Anyway, you need to define the unit address 
format in this doc.

> +			compatible = "slim217,60"";
> +			reg = <1 0>;
> +		};
> +	};
--
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] 87+ messages in thread

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-13 19:26       ` Rob Herring
  0 siblings, 0 replies; 87+ messages in thread
From: Rob Herring @ 2017-10-13 19:26 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, pawel.moll, mark.rutland, devicetree,
	linux-kernel, kheitke, linux-arm-msm, arnd

On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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). 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@codeaurora.org>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++

Split to separate patch.

>  Documentation/slimbus/summary                     | 109 ++++
>  drivers/Kconfig                                   |   2 +
>  drivers/Makefile                                  |   1 +
>  drivers/slimbus/Kconfig                           |  11 +
>  drivers/slimbus/Makefile                          |   5 +
>  drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
>  include/linux/mod_devicetable.h                   |  13 +
>  include/linux/slimbus.h                           | 299 ++++++++++
>  9 files changed, 1192 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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 include/linux/slimbus.h
> 
> diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
> new file mode 100644
> index 0000000..cb658bb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/slimbus/bus.txt
> @@ -0,0 +1,57 @@
> +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).

I can't have a PCI based slimbus controller? "platform bus" is a 
Linuxism.

> +Required property for SLIMbus controller node:
> +- compatible	- name of SLIMbus controller following generic names
> +		recommended practice.

generic names aren't recommended. Allowed with some conditons, yes.

> +- #address-cells - should be 2

You used 4 for your controller.

> +- #size-cells	- should be 0
> +
> +No other properties are required in the SLIMbus controller bus node.

That's not a useful statement. Almost every controller probably has 
other required properties.

> +
> +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.
> +If child node is not present and it is instantiated after device
> +discovery (slave device reporting itself present).
> +
> +In some cases it may be necessary to describe non-probeable device
> +details such as non-standard ways of powering up a device. In
> +such cases, child nodes for those devices will be present as
> +slaves of the slimbus-controller, as detailed below.
> +
> +Required property for SLIMbus child node if it is present:
> +- reg		- Is Duplex (Device index, Instance ID) from Enumeration
> +		  Address.
> +		  Device Index Uniquely identifies multiple Devices within
> +		  a single Component.
> +		  Instance ID Is for the cases where multiple Devices of the
> +		  same type or Class are attached to the bus.
> +
> +- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
> +	 	  Product Code, shall be in lower case hexadecimal with leading
> +		  zeroes suppressed
> +
> +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";
> +		#address-cells = <2>;
> +		#size-cells = <0>;
> +
> +		codec: wcd9310@1{

Is '1' by itself unique enough because you have 2 address cells. The 
unit-address is typically split up into fields with commas unless it's 
just a memory address. Anyway, you need to define the unit address 
format in this doc.

> +			compatible = "slim217,60"";
> +			reg = <1 0>;
> +		};
> +	};

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-13 19:26       ` Rob Herring
@ 2017-10-16  9:28         ` Srinivas Kandagatla
  -1 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-16  9:28 UTC (permalink / raw)
  To: Rob Herring
  Cc: mark.rutland, alsa-devel, michael.opdenacker, poeschel,
	andreas.noever, gong.chen, arnd, kheitke, treding, devicetree,
	james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1, broonie,
	sdharia, alan, bp, mathieu.poirier, gregkh, linux-kernel, daniel,
	jkosina, joe, davem



On 13/10/17 20:26, Rob Herring wrote:
> On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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). 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@codeaurora.org>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
>>   Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
> 
> Split to separate patch.

Will do this in next patch.
It was suggested by Arnd in v5, may be just of_parse code can be merged 
in this patch and the bindings made as separate patch.


> 
>>   Documentation/slimbus/summary                     | 109 ++++
>>   drivers/Kconfig                                   |   2 +
>>   drivers/Makefile                                  |   1 +
>>   drivers/slimbus/Kconfig                           |  11 +
>>   drivers/slimbus/Makefile                          |   5 +
>>   drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
>>   include/linux/mod_devicetable.h                   |  13 +
>>   include/linux/slimbus.h                           | 299 ++++++++++
>>   9 files changed, 1192 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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 include/linux/slimbus.h
>>
>> diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
>> new file mode 100644
>> index 0000000..cb658bb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/slimbus/bus.txt
>> @@ -0,0 +1,57 @@
>> +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).
> 
> I can't have a PCI based slimbus controller? "platform bus" is a
> Linuxism.
I agree, Will remove this Linuxism from the bindings.

> 
>> +Required property for SLIMbus controller node:
>> +- compatible	- name of SLIMbus controller following generic names
>> +		recommended practice.
> 
> generic names aren't recommended. Allowed with some conditons, yes.
> 
>> +- #address-cells - should be 2
> 
> You used 4 for your controller.

It should be actually 2. I will fix such mismatches in the other patches.

> 
>> +- #size-cells	- should be 0
>> +
>> +No other properties are required in the SLIMbus controller bus node.
> 
> That's not a useful statement. Almost every controller probably has
> other required properties.
Yep, will get rid of this in next patch.
> 
>> +
>> +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.
>> +If child node is not present and it is instantiated after device
>> +discovery (slave device reporting itself present).
>> +
>> +In some cases it may be necessary to describe non-probeable device
>> +details such as non-standard ways of powering up a device. In
>> +such cases, child nodes for those devices will be present as
>> +slaves of the slimbus-controller, as detailed below.
>> +
>> +Required property for SLIMbus child node if it is present:
>> +- reg		- Is Duplex (Device index, Instance ID) from Enumeration
>> +		  Address.
>> +		  Device Index Uniquely identifies multiple Devices within
>> +		  a single Component.
>> +		  Instance ID Is for the cases where multiple Devices of the
>> +		  same type or Class are attached to the bus.
>> +
>> +- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
>> +	 	  Product Code, shall be in lower case hexadecimal with leading
>> +		  zeroes suppressed
>> +
>> +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";
>> +		#address-cells = <2>;
>> +		#size-cells = <0>;
>> +
>> +		codec: wcd9310@1{
> 
> Is '1' by itself unique enough because you have 2 address cells. The
> unit-address is typically split up into fields with commas unless it's
> just a memory address. Anyway, you need to define the unit address
> format in this doc.
> 
Yep, this examples should be fixed with proper unit address which is 
formed by "device-id,instance-id"

device id + Instance ID makes it unique, I though I already added 
details the reg property.




>> +			compatible = "slim217,60"";
>> +			reg = <1 0>;
>> +		};
>> +	};

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-16  9:28         ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-16  9:28 UTC (permalink / raw)
  To: Rob Herring
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, pawel.moll, mark.rutland, devicetree,
	linux-kernel, kheitke, linux-arm-msm, arnd



On 13/10/17 20:26, Rob Herring wrote:
> On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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). 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@codeaurora.org>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
>>   Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
> 
> Split to separate patch.

Will do this in next patch.
It was suggested by Arnd in v5, may be just of_parse code can be merged 
in this patch and the bindings made as separate patch.


> 
>>   Documentation/slimbus/summary                     | 109 ++++
>>   drivers/Kconfig                                   |   2 +
>>   drivers/Makefile                                  |   1 +
>>   drivers/slimbus/Kconfig                           |  11 +
>>   drivers/slimbus/Makefile                          |   5 +
>>   drivers/slimbus/slim-core.c                       | 695 ++++++++++++++++++++++
>>   include/linux/mod_devicetable.h                   |  13 +
>>   include/linux/slimbus.h                           | 299 ++++++++++
>>   9 files changed, 1192 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/slimbus/bus.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 include/linux/slimbus.h
>>
>> diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt
>> new file mode 100644
>> index 0000000..cb658bb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/slimbus/bus.txt
>> @@ -0,0 +1,57 @@
>> +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).
> 
> I can't have a PCI based slimbus controller? "platform bus" is a
> Linuxism.
I agree, Will remove this Linuxism from the bindings.

> 
>> +Required property for SLIMbus controller node:
>> +- compatible	- name of SLIMbus controller following generic names
>> +		recommended practice.
> 
> generic names aren't recommended. Allowed with some conditons, yes.
> 
>> +- #address-cells - should be 2
> 
> You used 4 for your controller.

It should be actually 2. I will fix such mismatches in the other patches.

> 
>> +- #size-cells	- should be 0
>> +
>> +No other properties are required in the SLIMbus controller bus node.
> 
> That's not a useful statement. Almost every controller probably has
> other required properties.
Yep, will get rid of this in next patch.
> 
>> +
>> +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.
>> +If child node is not present and it is instantiated after device
>> +discovery (slave device reporting itself present).
>> +
>> +In some cases it may be necessary to describe non-probeable device
>> +details such as non-standard ways of powering up a device. In
>> +such cases, child nodes for those devices will be present as
>> +slaves of the slimbus-controller, as detailed below.
>> +
>> +Required property for SLIMbus child node if it is present:
>> +- reg		- Is Duplex (Device index, Instance ID) from Enumeration
>> +		  Address.
>> +		  Device Index Uniquely identifies multiple Devices within
>> +		  a single Component.
>> +		  Instance ID Is for the cases where multiple Devices of the
>> +		  same type or Class are attached to the bus.
>> +
>> +- compatible	-"slimMID,PID". The textual representation of Manufacturer ID,
>> +	 	  Product Code, shall be in lower case hexadecimal with leading
>> +		  zeroes suppressed
>> +
>> +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";
>> +		#address-cells = <2>;
>> +		#size-cells = <0>;
>> +
>> +		codec: wcd9310@1{
> 
> Is '1' by itself unique enough because you have 2 address cells. The
> unit-address is typically split up into fields with commas unless it's
> just a memory address. Anyway, you need to define the unit address
> format in this doc.
> 
Yep, this examples should be fixed with proper unit address which is 
formed by "device-id,instance-id"

device id + Instance ID makes it unique, I though I already added 
details the reg property.




>> +			compatible = "slim217,60"";
>> +			reg = <1 0>;
>> +		};
>> +	};

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

* Re: [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver
  2017-10-13 19:17   ` Rob Herring
@ 2017-10-16  9:28     ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-16  9:28 UTC (permalink / raw)
  To: Rob Herring
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, pawel.moll, mark.rutland, devicetree,
	linux-kernel, kheitke, linux-arm-msm, arnd



On 13/10/17 20:17, Rob Herring wrote:
> On Fri, Oct 06, 2017 at 05:51:32PM +0200, srinivas.kandagatla@linaro.org wrote:
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
>>   .../devicetree/bindings/slimbus/slim-qcom-ctrl.txt |  43 ++
> 
> Version 6 and this is the first I see it? Where's the version history?

Its been a very long time (18 months) from v5-v6, I will try see if I 
can pull up some version history from past few year patches :-) before i 
send next version. If it helps review process.

> 
> Please split to separate patch.

Will do that in next version.

> 
>>   drivers/slimbus/Kconfig                            |   9 +
>>   drivers/slimbus/Makefile                           |   3 +
>>   drivers/slimbus/slim-qcom-ctrl.c                   | 594 +++++++++++++++++++++
>>   drivers/slimbus/slim-qcom.h                        |  63 +++
>>   5 files changed, 712 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..081110d
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt
>> @@ -0,0 +1,43 @@
>> +Qualcomm SLIMBUS controller
>> +This controller is used if applications processor driver controls slimbus
>> +master component.
>> +
>> +Required properties:
>> +
>> + - #address-cells - refer to Documentation/devicetree/bindings/slimbus/bus.txt
>> + - #size-cells	- refer to Documentation/devicetree/bindings/slimbus/bus.txt
>> +
>> + - 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:
>> +	 "ctrl": Physical adderess of controller register blocks
>> + - compatible : should be "qcom,<SOC-NAME>-slim" for SOC specific compatible or
>> + 		"qcom,slim" if using generic qcom SLIM IP.
> 
> No such thing as generic IP.Yep, i though I removed such instances, but it looks like there are 
still in more places, I will rescan for them before sending next version.
> 
> Fine as a fallback, but not by itself.
> 
>> + - 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
> 
> _clk is redundant.
I agree, I will remove this suffixes in next version.

> 
>> +
>> +
>> +Optional property:
>> + - reg entry for slew rate : If slew rate control register is provided, this
>> +	entry should be used.
>> + - reg-name for slew rate: "slew"
> 
> Pointless to have -name when there is only one.
> 
I agree if it was just one.

Slew is optional resource, but as a whole there are 2 resources for the 
controller, so we need -name as there are 2 resources.

>> +
>> +Example:
>> +	slim@28080000 {
>> +		compatible = "qcom,slim";
>> +		#address-cells = <4>;
>> +		#size-cells = <0>;
>> +		reg = <0x28080000 0x2000>, <0x80207C 4>;
>> +		reg-names = "ctrl", "slew";
>> +		interrupts = <0 33 0>;
>> +		clocks = <&lcc SLIMBUS_SRC>, <&lcc AUDIO_SLIMBUS_CLK>;
>> +		clock-names = "iface_clk", "core_clk";
>> +
>> +		codec: wcd9310@1{
>> +			compatible = "slim217,60";
>> +			reg = <1 0>;
> 
> This would not compile. You don't have 4 cells here.

Yep, the example needs fixing.. address cells are actually 2.

> 
>> +		};
>> +	};

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-06 15:51   ` srinivas.kandagatla
@ 2017-10-17  6:23       ` Bjorn Andersson
  -1 siblings, 0 replies; 87+ messages in thread
From: Bjorn Andersson @ 2017-10-17  6:23 UTC (permalink / raw)
  To: srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A
  Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	sdharia-sgV2jX0FEOL9JmXXK+q4OQ, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, treding-DDmLM1+adcrQT0dZR+AlfA,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	jkosina-AlSwsSmVLrQ, sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kheitke-hxvC4TZJLZFWk0Htik3J/w,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4

On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org wrote:
[..]
> diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
[..]
> +/**
> + * Report callbacks(device_up, device_down) are implemented by slimbus-devices.
> + * The calls are scheduled into a workqueue to avoid holding up controller
> + * initialization/tear-down.
> + */
> +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);

This is the only place where sb->name is used in this driver. If you
instead invoke dev_*() on &sb->dev you should get prettier output and
can drop the double storage of the device name.

> +
> +	sbw = kmalloc(sizeof(*sbw), GFP_KERNEL);
> +	if (!sbw)
> +		return;
> +
> +	INIT_WORK(&sbw->wd, slim_report);
> +	sbw->sbdev = sb;
> +	sbw->report = report;
> +	if (!queue_work(ctrl->wq, &sbw->wd)) {

When a controller is torn down destroy_workqueue() is called after all
child devices has been unregistered, so this work might be scheduled
after "sb" is gone, if I get this properly.

> +		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	*sbdev;
> +	struct slim_driver	*sbdrv;
> +	int status = 0;
> +
> +	sbdev = to_slim_device(dev);
> +	sbdrv = to_slim_driver(dev->driver);
> +
> +	sbdev->driver = sbdrv;
> +
> +	if (sbdrv->probe)
> +		status = sbdrv->probe(sbdev);

So a driver can have a probe() and device_up() or just any one of them?

And probe() is called when the controller enumerates all devices
mentioned in DT and then device_up() is called at that point in time and
when it's advertised on the bus?

Is there a reason for this split model?

> +
> +	if (status)
> +		sbdev->driver = NULL;
> +	else if (sbdrv->device_up)
> +		schedule_slim_report(sbdev->ctrl, sbdev, true);
> +
> +	return status;
> +}
> +
> +static int slim_device_remove(struct device *dev)
> +{
> +	struct slim_device *sbdev;
> +	struct slim_driver *sbdrv;
> +	int status = 0;
> +
> +	sbdev = to_slim_device(dev);
> +	if (!dev->driver)
> +		return 0;
> +
> +	sbdrv = to_slim_driver(dev->driver);
> +	if (sbdrv->remove)
> +		status = sbdrv->remove(sbdev);
> +
> +	mutex_lock(&sbdev->report_lock);
> +	sbdev->notified = false;
> +	if (status == 0)
> +		sbdev->driver = NULL;
> +	mutex_unlock(&sbdev->report_lock);
> +	return status;

device_unregister() will call device_del() which will end up in
__device_release_driver() which will call this function. Upon returning
from this function the core expect the bus to have cleaned up after the
dev (normally by calling drv->remove(dev)).

It will completely ignore the return value and continue tearing down the
rest of the core resources, e.g. three lines down it will
devres_release_all().


So you have the option of sleeping, while waiting for stuff to be
aborted/finished and then you need to clean things up.

The slim_device object itself will stick around until all references are
dropped though.

> +}
> +
> +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.
> + * @owner: owning module/driver
> + * 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, struct module *owner)
> +{
> +	drv->driver.bus = &slimbus_type;
> +	drv->driver.owner = owner;
> +	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)

A driver invoking slim_driver_unregister(NULL) is broken, drop this
check and let it oops on the dereference instead.

> +		driver_unregister(&drv->driver);
> +}
> +EXPORT_SYMBOL_GPL(slim_driver_unregister);
> +
> +static struct slim_controller *slim_ctrl_get(struct slim_controller *ctrl)
> +{
> +	if (!ctrl || !get_device(&ctrl->dev))

ctrl can't be NULL here. In all code paths leading here it's
dereferenced multiple times already.

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

As far as I can see there's no case where sbdev->ctrl will ever be NULL,
so yo can just replace this with
	put_device(&ctrl->dev);

And drop slim_ctrl_put().

> +	kfree(sbdev->name);
> +	kfree(sbdev);
> +}
> +
> +static int slim_add_device(struct slim_controller *ctrl,
> +			   struct slim_device *sbdev)
> +{
> +	sbdev->dev.bus = &slimbus_type;
> +	sbdev->dev.parent = &ctrl->dev;
> +	sbdev->dev.release = slim_dev_release;
> +	sbdev->dev.driver = NULL;
> +	sbdev->ctrl = ctrl;
> +
> +	slim_ctrl_get(ctrl);

Unfolding slim_ctrl_get(), with ctrl != NULL, gives us a container_of(),
so this can't fail. Which is good because then an error check would have
been nice.

But it also means that you can replace this with just:
	get_device(&ctrl->dev);

And drop slim_ctrl_get()

> +	sbdev->name = kasprintf(GFP_KERNEL, "%x:%x:%x:%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);

This will create another copy of the same string and as noted above
there seems to be only one consumer, which could be switched over. So
you can drop above kasprintf() and just use dev_set_name to format the
name.

An added benefit is that you're not leaking the device reference from
slim_ctrl_get() on the line before.

> +	mutex_init(&sbdev->report_lock);
> +
> +	/* probe slave on this controller */
> +	return device_register(&sbdev->dev);
> +}
> +
> +/* Helper to get hex Manufacturer ID and Product id from compatible */
> +static unsigned long str2hex(unsigned char *str)

The caller of this passes char *, so you can drop the unsigned. And add
"const" while you're at it.

> +{
> +	int value = 0;
> +
> +	while (*str) {
> +		char c = *str++;
> +
> +		value = value << 4;
> +		if (c >= '0' && c <= '9')
> +			value |= (c - '0');
> +		if (c >= 'a' && c <= 'f')
> +			value |= (c - 'a' + 10);

At the cost of one more check here you can drop the line in the
documentation about this only working for lower-case hex digits.

> +
> +	}
> +
> +	return value;
> +}
> +
> +/* OF helpers for SLIMbus */
> +static void of_register_slim_devices(struct slim_controller *ctrl)
> +{
> +	struct device *dev = &ctrl->dev;
> +	struct device_node *node;
> +
> +	if (!ctrl->dev.of_node)
> +		return;
> +
> +	for_each_child_of_node(ctrl->dev.of_node, node) {
> +		struct slim_device *slim;
> +		const char *compat = NULL;
> +		char *p, *tok;
> +		int reg[2], ret;
> +
> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);

This is leaked in several places below.

> +		if (!slim)
> +			continue;
> +
> +		slim->dev.of_node = of_node_get(node);

Dito.

> +
> +		compat = of_get_property(node, "compatible", NULL);
> +		if (!compat)
> +			continue;
> +
> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));

Allocating a new string using string formatting based on an offset from
a string we don't know the size of, just to tokenize it does not seem
like the most efficient (nor safe) way of doing this.


How about:
		ret = sscanf(compat, "slim%x,%x", &manf_id, &prod_code);
		if (ret != 2)
			error();

> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Manufacturer ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.manf_id = str2hex(tok);
> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Product ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.prod_code = str2hex(tok);
> +		kfree(p);
> +
> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
> +		if (ret) {
> +			dev_err(dev, "Device and Instance id not found:%d\n",
> +				ret);
> +			continue;
> +		}
> +		slim->e_addr.dev_index = reg[0];
> +		slim->e_addr.instance = reg[1];
> +
> +		ret = slim_add_device(ctrl, slim);
> +		if (ret)
> +			dev_err(dev, "of_slim device register err:%d\n", ret);

Cleanup if this fails.

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

The purpose of ctrl_idr is to generate unique ids for the name and to
check that slim_del_controller() is only called on valid
slim_controllers.

The latter is okay to just expect the controller drivers to do right and
the prior is better done with an ida.


Also, the lower boundary should be 0, not ctrl->nr.

> +	mutex_unlock(&slim_lock);
> +
> +	if (id < 0)
> +		return id;
> +
> +	ctrl->nr = id;
> +
> +	dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr);

This name is used in a lot of debug prints, can we do better?

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

This is the only place ctrl->name is used. Perhaps it would make more
sense to base the dev_name off this string, to make all these dev_*()
more useful.

> +
> +	ctrl->wq = create_singlethread_workqueue(dev_name(&ctrl->dev));
> +	if (!ctrl->wq)
> +		goto err_workq_failed;
> +
> +	of_register_slim_devices(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() */
> +static void slim_remove_device(struct slim_device *sbdev)
> +{
> +	device_unregister(&sbdev->dev);
> +}
> +
> +static int slim_ctrl_remove_device(struct device *dev, void *null)
> +{
> +	slim_remove_device(to_slim_device(dev));
> +	return 0;
> +}
> +
> +/**
> + * slim_del_controller: Controller tear-down.
> + * @ctrl: Controller to tear-down.
> + */
> +int slim_del_controller(struct slim_controller *ctrl)

This is the opposite of slim_register_controller() so should it perhaps
be called slim_unregister_controller() ?

> +{
> +	struct slim_controller *found;
> +
> +	/* 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;

Just rely on the caller doing the right thing and just
	ida_remove(&ctrl_ida, ctrl->nr);

> +
> +	/* Remove all clients */
> +	device_for_each_child(&ctrl->dev, NULL, slim_ctrl_remove_device);
> +

As stated above there might be work items left in flight here, after the
slim_devices are released.

> +
> +	destroy_workqueue(ctrl->wq);
> +
> +	/* free bus id */
> +	mutex_lock(&slim_lock);
> +	idr_remove(&ctrl_idr, ctrl->nr);
> +	mutex_unlock(&slim_lock);
> +
> +	device_unregister(&ctrl->dev);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(slim_del_controller);
[..]
> +/**
> + * 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;

This will be written before read, so please don't initialize.

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

!slim

> +		return NULL;
> +
> +	slim->e_addr = *e_addr;
> +	if (slim_add_device(ctrl, slim) != 0) {

The idiomatic way is:

ret = fn();
if (ret) {
	failure...
}

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

i will be >= 0, so it can easily be distinguished from -ENXIO. So you
could return that directly instead of passing "i" as a reference and to
get the value.

On the other hand the return value is only used as an offset in addrt[]
to read out addrt[i].laddr, so perhaps you should just return that
instead (as an int).

> +			return 0;
> +		}
> +	}
> +	return -ENXIO;
> +}
> +
> +/**
> + * slim_assign_laddr: Assign logical address to a device enumerated.

So I presume this will either report a new (not seen before) e_addr
which should cause a new device to be spawned (although it might be
already mentioned in DT) or it might be called for an existing device to
update the logical address.

Can you describe when the latter is the case? Or is this a side effect
of the code, rather than slimbus?

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

How do you ensure this laddr is unique?

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

So I presume this updates an existing e_addr -> laddr mapping. But this
should imply that there is an associated slim_device with this e_addr
and laddr. Shouldn't said slim_device have its laddr updated then?

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

This seems better handled by a list than an array that we realloc. But
better yet, this array seems to mirror the list of registered devices.

So why doesn't this function just try to resolve the slim_device with
e_addr and if not allocate a new slim_device with a free laddr. Using an
ida to keep track of used logical addresses would make this much
cleaner.

Or a idr keyed by the laddr, it would still be O(n) to scan based on
e_addr, but this logic as well as lookups by laddr would be cleaner.

> +		ctrl->addrt[i].eaddr = *e_addr;
> +		ctrl->addrt[i].valid = true;
> +
> +		/* Preferred address is index into table */
> +		if (!valid)
> +			*laddr = i;

Is this laddr available?

> +	}
> +
> +	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;
> +
> +	dev_info(&ctrl->dev, "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 = -ENODEV;
> +	} 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);

I can't help feeling that this is the one and only point where you
should call probe() on the slim_device. Are there some funky
dependencies or protocol issues that makes this infeasible?

> +	}
> +	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 address.
> + * @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)

In what case would e_addr != sb->e_addr and why can't this function just
be return sb->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);
[..]
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_VERSION("0.1");

Who will ever update this version number? It's probably better to just
omit it.

> +MODULE_DESCRIPTION("Slimbus module");

Rather than "Slimbus module", this is actually the "Slimbus core".

> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
[..]
> +extern struct bus_type slimbus_type;

The device struct has a "struct device_type *type", which causes this
name to be confusing. Please rename it slimbus_bus instead.

Why does this bus_type have to be known to the world?

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

Keep the min/max here and move the rest to the patches that introduce
consumers of them.

[..]
> +/* 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

These are not currently used, move them to the patch that actually use
them.

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

Dito

> +
> +/* Destination type Values */
> +#define SLIM_MSG_DEST_LOGICALADDR	0
> +#define SLIM_MSG_DEST_ENUMADDR		1
> +#define	SLIM_MSG_DEST_BROADCAST		3

Dito

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

This mutex protects operations on the addrt array, so both name and
documentation can be improved.

> + * @addrt: Logical address table

Consider replacing with a idr, keyed by laddr. If there's actually a
point in having this list...

> + * @num_dev: Number of active slimbus slaves on this bus

This is not so much "number of devices", but rather the length of @addrt.

> + * @wq: Workqueue per controller used to notify devices when they report present
> + * @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.

Can you describe the use case for get_laddr() a little bit more?

> + */
> +struct slim_controller {
> +	struct device		dev;
> +	unsigned int		nr;
> +	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;
> +	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);

If nothing else I think this should return the laddr, rather than pass
it back into the referenced u8.

> +};
> +

Regards,
Bjorn
--
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] 87+ messages in thread

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-17  6:23       ` Bjorn Andersson
  0 siblings, 0 replies; 87+ messages in thread
From: Bjorn Andersson @ 2017-10-17  6:23 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:
[..]
> diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
[..]
> +/**
> + * Report callbacks(device_up, device_down) are implemented by slimbus-devices.
> + * The calls are scheduled into a workqueue to avoid holding up controller
> + * initialization/tear-down.
> + */
> +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);

This is the only place where sb->name is used in this driver. If you
instead invoke dev_*() on &sb->dev you should get prettier output and
can drop the double storage of the device name.

> +
> +	sbw = kmalloc(sizeof(*sbw), GFP_KERNEL);
> +	if (!sbw)
> +		return;
> +
> +	INIT_WORK(&sbw->wd, slim_report);
> +	sbw->sbdev = sb;
> +	sbw->report = report;
> +	if (!queue_work(ctrl->wq, &sbw->wd)) {

When a controller is torn down destroy_workqueue() is called after all
child devices has been unregistered, so this work might be scheduled
after "sb" is gone, if I get this properly.

> +		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	*sbdev;
> +	struct slim_driver	*sbdrv;
> +	int status = 0;
> +
> +	sbdev = to_slim_device(dev);
> +	sbdrv = to_slim_driver(dev->driver);
> +
> +	sbdev->driver = sbdrv;
> +
> +	if (sbdrv->probe)
> +		status = sbdrv->probe(sbdev);

So a driver can have a probe() and device_up() or just any one of them?

And probe() is called when the controller enumerates all devices
mentioned in DT and then device_up() is called at that point in time and
when it's advertised on the bus?

Is there a reason for this split model?

> +
> +	if (status)
> +		sbdev->driver = NULL;
> +	else if (sbdrv->device_up)
> +		schedule_slim_report(sbdev->ctrl, sbdev, true);
> +
> +	return status;
> +}
> +
> +static int slim_device_remove(struct device *dev)
> +{
> +	struct slim_device *sbdev;
> +	struct slim_driver *sbdrv;
> +	int status = 0;
> +
> +	sbdev = to_slim_device(dev);
> +	if (!dev->driver)
> +		return 0;
> +
> +	sbdrv = to_slim_driver(dev->driver);
> +	if (sbdrv->remove)
> +		status = sbdrv->remove(sbdev);
> +
> +	mutex_lock(&sbdev->report_lock);
> +	sbdev->notified = false;
> +	if (status == 0)
> +		sbdev->driver = NULL;
> +	mutex_unlock(&sbdev->report_lock);
> +	return status;

device_unregister() will call device_del() which will end up in
__device_release_driver() which will call this function. Upon returning
from this function the core expect the bus to have cleaned up after the
dev (normally by calling drv->remove(dev)).

It will completely ignore the return value and continue tearing down the
rest of the core resources, e.g. three lines down it will
devres_release_all().


So you have the option of sleeping, while waiting for stuff to be
aborted/finished and then you need to clean things up.

The slim_device object itself will stick around until all references are
dropped though.

> +}
> +
> +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.
> + * @owner: owning module/driver
> + * 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, struct module *owner)
> +{
> +	drv->driver.bus = &slimbus_type;
> +	drv->driver.owner = owner;
> +	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)

A driver invoking slim_driver_unregister(NULL) is broken, drop this
check and let it oops on the dereference instead.

> +		driver_unregister(&drv->driver);
> +}
> +EXPORT_SYMBOL_GPL(slim_driver_unregister);
> +
> +static struct slim_controller *slim_ctrl_get(struct slim_controller *ctrl)
> +{
> +	if (!ctrl || !get_device(&ctrl->dev))

ctrl can't be NULL here. In all code paths leading here it's
dereferenced multiple times already.

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

As far as I can see there's no case where sbdev->ctrl will ever be NULL,
so yo can just replace this with
	put_device(&ctrl->dev);

And drop slim_ctrl_put().

> +	kfree(sbdev->name);
> +	kfree(sbdev);
> +}
> +
> +static int slim_add_device(struct slim_controller *ctrl,
> +			   struct slim_device *sbdev)
> +{
> +	sbdev->dev.bus = &slimbus_type;
> +	sbdev->dev.parent = &ctrl->dev;
> +	sbdev->dev.release = slim_dev_release;
> +	sbdev->dev.driver = NULL;
> +	sbdev->ctrl = ctrl;
> +
> +	slim_ctrl_get(ctrl);

Unfolding slim_ctrl_get(), with ctrl != NULL, gives us a container_of(),
so this can't fail. Which is good because then an error check would have
been nice.

But it also means that you can replace this with just:
	get_device(&ctrl->dev);

And drop slim_ctrl_get()

> +	sbdev->name = kasprintf(GFP_KERNEL, "%x:%x:%x:%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);

This will create another copy of the same string and as noted above
there seems to be only one consumer, which could be switched over. So
you can drop above kasprintf() and just use dev_set_name to format the
name.

An added benefit is that you're not leaking the device reference from
slim_ctrl_get() on the line before.

> +	mutex_init(&sbdev->report_lock);
> +
> +	/* probe slave on this controller */
> +	return device_register(&sbdev->dev);
> +}
> +
> +/* Helper to get hex Manufacturer ID and Product id from compatible */
> +static unsigned long str2hex(unsigned char *str)

The caller of this passes char *, so you can drop the unsigned. And add
"const" while you're at it.

> +{
> +	int value = 0;
> +
> +	while (*str) {
> +		char c = *str++;
> +
> +		value = value << 4;
> +		if (c >= '0' && c <= '9')
> +			value |= (c - '0');
> +		if (c >= 'a' && c <= 'f')
> +			value |= (c - 'a' + 10);

At the cost of one more check here you can drop the line in the
documentation about this only working for lower-case hex digits.

> +
> +	}
> +
> +	return value;
> +}
> +
> +/* OF helpers for SLIMbus */
> +static void of_register_slim_devices(struct slim_controller *ctrl)
> +{
> +	struct device *dev = &ctrl->dev;
> +	struct device_node *node;
> +
> +	if (!ctrl->dev.of_node)
> +		return;
> +
> +	for_each_child_of_node(ctrl->dev.of_node, node) {
> +		struct slim_device *slim;
> +		const char *compat = NULL;
> +		char *p, *tok;
> +		int reg[2], ret;
> +
> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);

This is leaked in several places below.

> +		if (!slim)
> +			continue;
> +
> +		slim->dev.of_node = of_node_get(node);

Dito.

> +
> +		compat = of_get_property(node, "compatible", NULL);
> +		if (!compat)
> +			continue;
> +
> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));

Allocating a new string using string formatting based on an offset from
a string we don't know the size of, just to tokenize it does not seem
like the most efficient (nor safe) way of doing this.


How about:
		ret = sscanf(compat, "slim%x,%x", &manf_id, &prod_code);
		if (ret != 2)
			error();

> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Manufacturer ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.manf_id = str2hex(tok);
> +
> +		tok = strsep(&p, ",");
> +		if (!tok) {
> +			dev_err(dev, "No valid Product ID found\n");
> +			kfree(p);
> +			continue;
> +		}
> +		slim->e_addr.prod_code = str2hex(tok);
> +		kfree(p);
> +
> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
> +		if (ret) {
> +			dev_err(dev, "Device and Instance id not found:%d\n",
> +				ret);
> +			continue;
> +		}
> +		slim->e_addr.dev_index = reg[0];
> +		slim->e_addr.instance = reg[1];
> +
> +		ret = slim_add_device(ctrl, slim);
> +		if (ret)
> +			dev_err(dev, "of_slim device register err:%d\n", ret);

Cleanup if this fails.

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

The purpose of ctrl_idr is to generate unique ids for the name and to
check that slim_del_controller() is only called on valid
slim_controllers.

The latter is okay to just expect the controller drivers to do right and
the prior is better done with an ida.


Also, the lower boundary should be 0, not ctrl->nr.

> +	mutex_unlock(&slim_lock);
> +
> +	if (id < 0)
> +		return id;
> +
> +	ctrl->nr = id;
> +
> +	dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr);

This name is used in a lot of debug prints, can we do better?

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

This is the only place ctrl->name is used. Perhaps it would make more
sense to base the dev_name off this string, to make all these dev_*()
more useful.

> +
> +	ctrl->wq = create_singlethread_workqueue(dev_name(&ctrl->dev));
> +	if (!ctrl->wq)
> +		goto err_workq_failed;
> +
> +	of_register_slim_devices(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() */
> +static void slim_remove_device(struct slim_device *sbdev)
> +{
> +	device_unregister(&sbdev->dev);
> +}
> +
> +static int slim_ctrl_remove_device(struct device *dev, void *null)
> +{
> +	slim_remove_device(to_slim_device(dev));
> +	return 0;
> +}
> +
> +/**
> + * slim_del_controller: Controller tear-down.
> + * @ctrl: Controller to tear-down.
> + */
> +int slim_del_controller(struct slim_controller *ctrl)

This is the opposite of slim_register_controller() so should it perhaps
be called slim_unregister_controller() ?

> +{
> +	struct slim_controller *found;
> +
> +	/* 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;

Just rely on the caller doing the right thing and just
	ida_remove(&ctrl_ida, ctrl->nr);

> +
> +	/* Remove all clients */
> +	device_for_each_child(&ctrl->dev, NULL, slim_ctrl_remove_device);
> +

As stated above there might be work items left in flight here, after the
slim_devices are released.

> +
> +	destroy_workqueue(ctrl->wq);
> +
> +	/* free bus id */
> +	mutex_lock(&slim_lock);
> +	idr_remove(&ctrl_idr, ctrl->nr);
> +	mutex_unlock(&slim_lock);
> +
> +	device_unregister(&ctrl->dev);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(slim_del_controller);
[..]
> +/**
> + * 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;

This will be written before read, so please don't initialize.

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

!slim

> +		return NULL;
> +
> +	slim->e_addr = *e_addr;
> +	if (slim_add_device(ctrl, slim) != 0) {

The idiomatic way is:

ret = fn();
if (ret) {
	failure...
}

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

i will be >= 0, so it can easily be distinguished from -ENXIO. So you
could return that directly instead of passing "i" as a reference and to
get the value.

On the other hand the return value is only used as an offset in addrt[]
to read out addrt[i].laddr, so perhaps you should just return that
instead (as an int).

> +			return 0;
> +		}
> +	}
> +	return -ENXIO;
> +}
> +
> +/**
> + * slim_assign_laddr: Assign logical address to a device enumerated.

So I presume this will either report a new (not seen before) e_addr
which should cause a new device to be spawned (although it might be
already mentioned in DT) or it might be called for an existing device to
update the logical address.

Can you describe when the latter is the case? Or is this a side effect
of the code, rather than slimbus?

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

How do you ensure this laddr is unique?

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

So I presume this updates an existing e_addr -> laddr mapping. But this
should imply that there is an associated slim_device with this e_addr
and laddr. Shouldn't said slim_device have its laddr updated then?

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

This seems better handled by a list than an array that we realloc. But
better yet, this array seems to mirror the list of registered devices.

So why doesn't this function just try to resolve the slim_device with
e_addr and if not allocate a new slim_device with a free laddr. Using an
ida to keep track of used logical addresses would make this much
cleaner.

Or a idr keyed by the laddr, it would still be O(n) to scan based on
e_addr, but this logic as well as lookups by laddr would be cleaner.

> +		ctrl->addrt[i].eaddr = *e_addr;
> +		ctrl->addrt[i].valid = true;
> +
> +		/* Preferred address is index into table */
> +		if (!valid)
> +			*laddr = i;

Is this laddr available?

> +	}
> +
> +	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;
> +
> +	dev_info(&ctrl->dev, "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 = -ENODEV;
> +	} 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);

I can't help feeling that this is the one and only point where you
should call probe() on the slim_device. Are there some funky
dependencies or protocol issues that makes this infeasible?

> +	}
> +	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 address.
> + * @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)

In what case would e_addr != sb->e_addr and why can't this function just
be return sb->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);
[..]
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_VERSION("0.1");

Who will ever update this version number? It's probably better to just
omit it.

> +MODULE_DESCRIPTION("Slimbus module");

Rather than "Slimbus module", this is actually the "Slimbus core".

> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
[..]
> +extern struct bus_type slimbus_type;

The device struct has a "struct device_type *type", which causes this
name to be confusing. Please rename it slimbus_bus instead.

Why does this bus_type have to be known to the world?

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

Keep the min/max here and move the rest to the patches that introduce
consumers of them.

[..]
> +/* 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

These are not currently used, move them to the patch that actually use
them.

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

Dito

> +
> +/* Destination type Values */
> +#define SLIM_MSG_DEST_LOGICALADDR	0
> +#define SLIM_MSG_DEST_ENUMADDR		1
> +#define	SLIM_MSG_DEST_BROADCAST		3

Dito

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

This mutex protects operations on the addrt array, so both name and
documentation can be improved.

> + * @addrt: Logical address table

Consider replacing with a idr, keyed by laddr. If there's actually a
point in having this list...

> + * @num_dev: Number of active slimbus slaves on this bus

This is not so much "number of devices", but rather the length of @addrt.

> + * @wq: Workqueue per controller used to notify devices when they report present
> + * @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.

Can you describe the use case for get_laddr() a little bit more?

> + */
> +struct slim_controller {
> +	struct device		dev;
> +	unsigned int		nr;
> +	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;
> +	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);

If nothing else I think this should return the laddr, rather than pass
it back into the referenced u8.

> +};
> +

Regards,
Bjorn

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

* Re: [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-06 15:51     ` srinivas.kandagatla
                       ` (3 preceding siblings ...)
  (?)
@ 2017-10-18  6:15     ` Bjorn Andersson
  2017-10-18 16:39       ` Srinivas Kandagatla
  -1 siblings, 1 reply; 87+ messages in thread
From: Bjorn Andersson @ 2017-10-18  6:15 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:

> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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).

I think we can implement this "optimization" with less complex code,
regardless I don't think we need to mention this in the commit
message...

[..]
> diff --git a/drivers/slimbus/slim-messaging.c b/drivers/slimbus/slim-messaging.c
[..]
> +/**
> + * 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)

Even if tid and len comes from the spec I recommend you making them int
and size_t.

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

if (!msg || !msg->rbuf)


When is it valid to add a transaction to tid_tbl with msg->rbuf = NULL?
Should we reject it earlier?

> +		spin_unlock_irqrestore(&ctrl->txn_lock, flags);
> +		dev_err(&ctrl->dev, "Got response to invalid TID:%d, len:%d\n",
> +				tid, len);
> +		return;
> +	}
> +	ctrl->tid_tbl[tid] = NULL;
> +	spin_unlock_irqrestore(&ctrl->txn_lock, flags);
> +
> +	memcpy(msg->rbuf, reply, len);
> +	if (msg->comp_cb)
> +		msg->comp_cb(msg->ctx, 0);
> +}
> +EXPORT_SYMBOL_GPL(slim_msg_response);
[..]
> +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 (ret) {
	if (need_tid)
		drop();
	
	dev_err();
}

Would probably make this a little bit cleaner...

> +	if (!async) {
> +		txn->msg->comp_cb = NULL;
> +		txn->msg->ctx = NULL;

I believe txn->msg is always required, so you don't need to do this
contidionally.

> +	}
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(slim_processtxn);
[..]
> +int slim_request_val_element(struct slim_device *sb,
> +				struct slim_val_inf *msg)
> +{
> +	struct slim_controller *ctrl = sb->ctrl;
> +
> +	if (!ctrl)
> +		return -EINVAL;

>From patch 1 I believe it's invalid for sb->ctrl to be NULL, so there
shouldn't be a need to check this.

> +
> +	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_VALUE);
> +}
> +EXPORT_SYMBOL_GPL(slim_request_val_element);
[..]
> +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);
> +

Please provide kerneldoc for exported symbols.

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

Why is this doing struct copy?

> +	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);
[..]
>  /**
> + * 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;

This is not mentioned in the kerneldoc. Use void * for data buffers.

> +	const u8		*wbuf;

Can a message ever be read and write? Otherwise it should be sufficient
to only have one data pointer.

> +	void			(*comp_cb)(void *ctx, int err);
> +	void			*ctx;
> +};
> +
[..]
> +/**
> + * 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
> + */

Is this ringbuffer mechanism defined in the slimbus specification? Looks
like something specific to the Qualcomm controller, rather than
something that should be enforced in the framework.

> +struct slim_ctrl_buf {
> +	void		*base;
> +	phys_addr_t	phy;
> +	spinlock_t	lock;
> +	int		head;
> +	int		tail;
> +	int		sl_sz;
> +	int		n;
> +};
[..]
> +/**
>   * 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

This is out list of pending write requests, yet it's implemented as an
array used in a complex ring buffer fashion. Wouldn't it be easier to
just have this as a linked list of slim_pending struct?

> + * @tx_sem: Semaphore for available TX buffers for this controller
> + * @last_tid: Last used entry for TID transactions
>   * @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.
> @@ -161,6 +278,15 @@ 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;

I suggest that you replace these two with an idr, rather than having a
fixed size array and then last_tid as an optimization to limit how far
you linear search for an empty space.

> +	spinlock_t		txn_lock;
> +	struct slim_ctrl_buf	tx;
> +	struct slim_ctrl_buf	rx;
> +	struct slim_pending	*pending_wr;
> +	struct semaphore	tx_sem;

Please don't use semaphores. If you keep pending_wr as a list you can
use list_empty() instead...

> +	int			(*xfer_msg)(struct slim_controller *ctrl,
> +					    struct slim_msg_txn *tx, void *buf);

I believe buf has fixed size, so please document this.

>  	int			(*set_laddr)(struct slim_controller *ctrl,
>  					     struct slim_eaddr *ea, u8 laddr);
>  	int			(*get_laddr)(struct slim_controller *ctrl,
> @@ -295,5 +421,40 @@ static inline void slim_set_devicedata(struct slim_device *dev, void *data)
>  {
>  	dev_set_drvdata(&dev->dev, data);
>  }

Regards,
Bjorn

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

* Re: [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver
  2017-10-06 15:51 ` [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver srinivas.kandagatla
  2017-10-07  7:45   ` Jonathan Neuschäfer
  2017-10-13 19:17   ` Rob Herring
@ 2017-10-18  7:27   ` Bjorn Andersson
  2017-10-18 16:39     ` Srinivas Kandagatla
  2 siblings, 1 reply; 87+ messages in thread
From: Bjorn Andersson @ 2017-10-18  7:27 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:

> From: Sagar Dharia <sdharia@codeaurora.org>
> 
> 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>
> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  .../devicetree/bindings/slimbus/slim-qcom-ctrl.txt |  43 ++
>  drivers/slimbus/Kconfig                            |   9 +
>  drivers/slimbus/Makefile                           |   3 +
>  drivers/slimbus/slim-qcom-ctrl.c                   | 594 +++++++++++++++++++++
>  drivers/slimbus/slim-qcom.h                        |  63 +++
>  5 files changed, 712 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..081110d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt
> @@ -0,0 +1,43 @@
> +Qualcomm SLIMBUS controller
> +This controller is used if applications processor driver controls slimbus
> +master component.
> +
> +Required properties:
> +
> + - #address-cells - refer to Documentation/devicetree/bindings/slimbus/bus.txt
> + - #size-cells	- refer to Documentation/devicetree/bindings/slimbus/bus.txt
> +
> + - 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:
> +	 "ctrl": Physical adderess of controller register blocks
> + - compatible : should be "qcom,<SOC-NAME>-slim" for SOC specific compatible or

As you implementation is only compatible with "qcom,slim" this should be
"and" not "or".

> + 		"qcom,slim" if using generic qcom SLIM IP.
> + - 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: "slew"
> +
> +Example:
> +	slim@28080000 {
> +		compatible = "qcom,slim";
> +		#address-cells = <4>;
> +		#size-cells = <0>;
> +		reg = <0x28080000 0x2000>, <0x80207C 4>;
> +		reg-names = "ctrl", "slew";
> +		interrupts = <0 33 0>;
> +		clocks = <&lcc SLIMBUS_SRC>, <&lcc AUDIO_SLIMBUS_CLK>;
> +		clock-names = "iface_clk", "core_clk";
> +
> +		codec: wcd9310@1{
> +			compatible = "slim217,60";
> +			reg = <1 0>;
> +		};
> +	};
> diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig
> index f0b118a..438773e 100644
> --- a/drivers/slimbus/Kconfig
> +++ b/drivers/slimbus/Kconfig
> @@ -9,3 +9,12 @@ menuconfig SLIMBUS
>  
>  	  If unsure, choose N.
>  
> +if SLIMBUS
> +config SLIM_QCOM_CTRL
> +	tristate "Qualcomm Slimbus Manager Component"
> +	depends on SLIMBUS
> +	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 bd1d35e..ed8521a 100644
> --- a/drivers/slimbus/Makefile
> +++ b/drivers/slimbus/Makefile
> @@ -3,3 +3,6 @@
>  #
>  obj-$(CONFIG_SLIMBUS)			+= slimbus.o
>  slimbus-y				:= slim-core.o slim-messaging.o
> +
> +#Controllers
> +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..d0574e1
> --- /dev/null
> +++ b/drivers/slimbus/slim-qcom-ctrl.c
> @@ -0,0 +1,594 @@
> +/* 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/dma-mapping.h>
> +#include <linux/slimbus.h>
> +#include "slim-qcom.h"
> +
> +#define MSM_SLIM_NAME	"msm_slim_ctrl"

Just put this string in the driver struct.

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

Use void * for buf.

> +{
> +	int i;
> +
> +	for (i = 0; i < (len + 3) >> 2; i++) {

If len is in bytes then this looks like you will read outside the
buffer. If buf is guaranteed to be 4-byte aligned make "len" number of
32-bit entries in the array.

> +		dev_dbg(dev->dev, "AHB TX data:0x%x\n", buf[i]);

Drop the debug print, if anything use print_hex_dump to get the whole
chunk.

> +		writel_relaxed(buf[i], dev->base + tx_reg + (i * 4));
> +	}

Replace this loop with:

	__iowrite32_copy(dev->base + tx_reg, buf, len / sizeof(u32));

> +	/* Guarantee that message is sent before returning */

"Ensure ordering of subsequent writes", there's no guarantees that the
write is done at the end of this function.

> +	mb();
> +	return 0;

Why return an int when there are not other return paths?

> +}
> +
> +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, ret = IRQ_NONE;
> +

If you split the two large parts of this function into two functions
things will be cleaner and you'll save yourself one level of
indentation.

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

I'm not sure why this is necessary, but this is not "guaranteed" by the
mb().

> +		mb();
> +		slim_return_tx(&dev->ctrl, err);
> +		ret = IRQ_HANDLED;
> +	}
> +	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;

The way this function deals with cryptic variable names and cryptic
bit shifts is quite hard to follow. Perhaps it makes sense if you know
exactly what's going on?

A comment describing the fields in the read information would be quite
helpful, at least.

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

if (!rx_buf)

> +			dev_err(dev->dev, "dropping RX:0x%x due to RX full\n",
> +						pkt[0]);

Why do we need to drop this incoming message? Will things recover from
that?

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

Drop this debug message, if you really want to show this use
print_hex_dump() after reading the entire thing.


And you should be able to replace this loop with __ioread32_copy()

> +		}
> +
> +		switch (mc) {
> +			u8 *buf, la;
> +			u16 ele;

What?!

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

To whom is this cryptic dump valuable? Please use print_hex_dump()
rather than rolling your own print-loops.

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

"Ensure ordering of CLR bit write with subsequent writes" although I'm
wondering how useful this is...

> +		mb();
> +		if (q_rx)
> +			queue_work(dev->rxwq, &dev->wd);
> +
> +		ret = IRQ_HANDLED;
> +	}
> +	return ret;
> +}
> +
> +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);
> +	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);

Put "dt" (whatever that is?!) in a variable and then do this line once,
saves you from the line break.

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

This would be easier to deal with in the form of:

struct foo {
	__le16 manf_id;
	__le16 prod_code;
	u8 dev_idx;
	u8 instance;
	u8 laddr;
};

And it doesn't look like a "buf", it looks like a packet of some
specific type, so it could be given a better name.

> +
> +	/**
> +	 * Retries are needed since bus may lose sync when multiple devices
> +	 * are coming up and reporting present
> +	 */

Is this a todo? I'm not sure how this relates to the code.

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

40? Are you perhaps trying to say SLIM_MSGQ_BUF_LEN?

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

How about describing this like:

struct some_pkt {
	u8 len:5;
	u8 mt:3;
	u8 mc;

	__le16 manf_id;
	__le16 prod_code;
	u8 dev_idx;
	u8 instance;
};

But perhaps manf_id and prod_code are big endian here? I'm getting
confused from this code.

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

The idiomatic way is to deal with errors first and then you don't have
to indent the normal code path.

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

This is a local variable...

> +						     "slew");
> +	if (!dev->slew_mem) {
> +		dev_warn(&pdev->dev, "no slimbus slew resource\n");
> +		return;
> +	}
> +

It's idiomatic to do:

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "slew");
	slew_reg = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(slew_reg))
		...


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

Is this not a problem? Is it okay to silently discard this error.

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

This is not what wmb() does. If you want to ensure that it's written do
a readback.

> +	wmb();

There's no reason to keep the slew register mapped beyond this function,
so you could drop the devm_ and iounmap() slew_reg here again.


If there's a reason to touch this in the future, then keep track of
slew_reg rather than the slew_mem...

> +}
> +
> +static int msm_slim_probe(struct platform_device *pdev)
> +{
> +	struct msm_slim_ctrl *dev;

"dev" is not the best name for this (dev->dev?!)

> +	struct slim_controller *ctrl;
> +	struct resource *slim_mem;
> +	struct resource *irq;
> +	struct clk *hclk, *rclk;

Why the odd names?

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

Do this properly.

> +		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, "ctrl");

Move this down to the devm_ioremap() and skip the error check.

> +	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 you allocate this earlier you can reduce the number of local
variables.

> +	if (!dev)
> +		return -ENOMEM;
> +
> +	dev->hclk = hclk;
> +	dev->rclk = rclk;
> +	ctrl = &dev->ctrl;
> +	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));

devm_ioremap_resource()

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

Use platform_get_irq() instead and you don't need to do this.

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

Dividing by 8 makes the code readable, compilers will make sure it's
still super fast.

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

At this point you might get interrupts, which I presume will try to
access the registers which you start clocking on the next line. Please
reorder these.

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

Use the clk_bulk api instead.

> +
> +
> +	ctrl->tx.base = dma_alloc_coherent(&pdev->dev,
> +					   (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(&pdev->dev,
> +					   (ctrl->rx.sl_sz * ctrl->rx.n),
> +					   &ctrl->rx.phy, GFP_KERNEL);
> +	if (!ctrl->rx.base) {
> +		ret = -ENOMEM;
> +		goto rx_alloc_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));

Use non-relaxed writel, unless there's a good reason not to.

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

Drop this comment and just use a non-relaxed writel for the framer
registration initialization.

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

Drop this mb and use writel()

> +
> +	writel_relaxed(MGR_CFG_ENABLE, dev->base + MGR_CFG);
> +	/*
> +	 * Make sure that manager-enable is written through before interface
> +	 * device is enabled

Dito

> +	 */
> +	mb();
> +	writel_relaxed(1, dev->base + INTF_CFG);
> +	/*
> +	 * Make sure that interface-enable is written through before enabling
> +	 * ported generic device inside MSM manager
> +	 */

Dito

> +	mb();
> +
> +	writel_relaxed(1, dev->base + CFG_PORT(COMP_CFG, dev->ver));
> +	/*
> +	 * Make sure that all writes have gone through before exiting this
> +	 * function
> +	 */

Dito

> +	mb();
> +
> +	dev_dbg(dev->dev, "MSM SB controller is up:ver:0x%x!\n", dev->ver);
> +	return 0;
> +
> +err_ctrl_failed:
> +	dma_free_coherent(&pdev->dev, (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:
> +	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);
> +	struct slim_controller *ctrl = to_slim_controller(&pdev->dev);
> +
> +	dma_free_coherent(&pdev->dev, (ctrl->rx.sl_sz * ctrl->rx.n),
> +			  ctrl->rx.base, ctrl->rx.phy);
> +	dma_free_coherent(&pdev->dev, (ctrl->tx.sl_sz * ctrl->tx.n),
> +			  ctrl->tx.base, ctrl->tx.phy);

Don't you at least want to disable irqs before you free this memory?

> +
> +	disable_irq(dev->irq);
> +	clk_disable_unprepare(dev->rclk);
> +	clk_disable_unprepare(dev->hclk);
> +	slim_del_controller(&dev->ctrl);
> +	destroy_workqueue(dev->rxwq);
> +	return 0;
> +}
> +
> +static const struct of_device_id msm_slim_dt_match[] = {
> +	{
> +		.compatible = "qcom,slim",
> +	},
> +	{}
> +};
> +
> +static struct platform_driver msm_slim_driver = {
> +	.probe = msm_slim_probe,
> +	.remove = msm_slim_remove,
> +	.driver	= {
> +		.name = MSM_SLIM_NAME,
> +		.owner = THIS_MODULE,

No "owner"

> +		.of_match_table = msm_slim_dt_match,
> +	},
> +};
> +module_platform_driver(msm_slim_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_VERSION("0.1");

Skip the version, we'll never update it anyways.

> +MODULE_DESCRIPTION("Qualcomm Slimbus controller");
> +MODULE_ALIAS("platform:qcom-slim");

No-one will request the module by this alias, so skip it.

> 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

As far as I can see this information is all local to lim-qcom-ctrl.c, so
put it there.

> @@ -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

Unused.

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

"ver" is only used within probe(), use a local variable instead.

> +};
> +
> +#endif

Regards,
Bjorn

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-17  6:23       ` Bjorn Andersson
@ 2017-10-18 16:38         ` Srinivas Kandagatla
  -1 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-18 16:38 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: mark.rutland, alsa-devel, michael.opdenacker, poeschel,
	andreas.noever, gong.chen, arnd, kheitke, treding, devicetree,
	james.hogan, pawel.moll, linux-arm-msm, sharon.dvir1, robh+dt,
	sdharia, alan, bp, mathieu.poirier, gregkh, linux-kernel,
	broonie, daniel, jkosina, joe, davem

Thanks for the Review Bjorn,

On 17/10/17 07:23, Bjorn Andersson wrote:
> On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:
> [..]
>> diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
> [..]
>> +/**
>> + * Report callbacks(device_up, device_down) are implemented by slimbus-devices.
>> + * The calls are scheduled into a workqueue to avoid holding up controller
>> + * initialization/tear-down.
>> + */
>> +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);
> 
> This is the only place where sb->name is used in this driver. If you
> instead invoke dev_*() on &sb->dev you should get prettier output and
> can drop the double storage of the device name.

Makes sense, we could get rid of sb->name storage too.

> 
>> +
>> +	sbw = kmalloc(sizeof(*sbw), GFP_KERNEL);
>> +	if (!sbw)
>> +		return;
>> +
>> +	INIT_WORK(&sbw->wd, slim_report);
>> +	sbw->sbdev = sb;
>> +	sbw->report = report;
>> +	if (!queue_work(ctrl->wq, &sbw->wd)) {
> 
> When a controller is torn down destroy_workqueue() is called after all
> child devices has been unregistered, so this work might be scheduled
> after "sb" is gone, if I get this properly.

I agree, That is possible!
We should probably flush the workqueue before we start removing the clients.

> 
>> +		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	*sbdev;
>> +	struct slim_driver	*sbdrv;
>> +	int status = 0;
>> +
>> +	sbdev = to_slim_device(dev);
>> +	sbdrv = to_slim_driver(dev->driver);
>> +
>> +	sbdev->driver = sbdrv;
>> +
>> +	if (sbdrv->probe)
>> +		status = sbdrv->probe(sbdev);
> 
> So a driver can have a probe() and device_up() or just any one of them?
> 
> And probe() is called when the controller enumerates all devices
> mentioned in DT and then device_up() is called at that point in time and
> when it's advertised on the bus?
> 
> Is there a reason for this split model?
> 
yes, Some of the devices need to be powered up before they become 
usable, so probe is used to do the initial power up of the device.


>> +
>> +	if (status)
>> +		sbdev->driver = NULL;
>> +	else if (sbdrv->device_up)
>> +		schedule_slim_report(sbdev->ctrl, sbdev, true);
>> +
>> +	return status;
>> +}
>> +
>> +static int slim_device_remove(struct device *dev)
>> +{
>> +	struct slim_device *sbdev;
>> +	struct slim_driver *sbdrv;
>> +	int status = 0;
>> +
>> +	sbdev = to_slim_device(dev);
>> +	if (!dev->driver)
>> +		return 0;
>> +
>> +	sbdrv = to_slim_driver(dev->driver);
>> +	if (sbdrv->remove)
>> +		status = sbdrv->remove(sbdev);
>> +
>> +	mutex_lock(&sbdev->report_lock);
>> +	sbdev->notified = false;
>> +	if (status == 0)
>> +		sbdev->driver = NULL;
>> +	mutex_unlock(&sbdev->report_lock);
>> +	return status;
> 
> device_unregister() will call device_del() which will end up in
> __device_release_driver() which will call this function. Upon returning
> from this function the core expect the bus to have cleaned up after the
> dev (normally by calling drv->remove(dev)).
> 
> It will completely ignore the return value and continue tearing down the
> rest of the core resources, e.g. three lines down it will
> devres_release_all().
> 
> 
> So you have the option of sleeping, while waiting for stuff to be
> aborted/finished and then you need to clean things up.
> 
> The slim_device object itself will stick around until all references are
> dropped though.

So you are suggesting that we make slim_driver remove not return anything?

> 
>> +}
>> +
>> +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.
>> + * @owner: owning module/driver
>> + * 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, struct module *owner)
>> +{
>> +	drv->driver.bus = &slimbus_type;
>> +	drv->driver.owner = owner;
>> +	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)
> 
> A driver invoking slim_driver_unregister(NULL) is broken, drop this
> check and let it oops on the dereference instead.

Yep.

> 
>> +		driver_unregister(&drv->driver);
>> +}
>> +EXPORT_SYMBOL_GPL(slim_driver_unregister);
>> +
>> +static struct slim_controller *slim_ctrl_get(struct slim_controller *ctrl)
>> +{
>> +	if (!ctrl || !get_device(&ctrl->dev))
> 
> ctrl can't be NULL here. In all code paths leading here it's
> dereferenced multiple times already.

I agree.

> 
>> +		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);
> 
> As far as I can see there's no case where sbdev->ctrl will ever be NULL,
> so yo can just replace this with
> 	put_device(&ctrl->dev);
> 
> And drop slim_ctrl_put().


Yes..

> 
>> +	kfree(sbdev->name);
>> +	kfree(sbdev);
>> +}
>> +
>> +static int slim_add_device(struct slim_controller *ctrl,
>> +			   struct slim_device *sbdev)
>> +{
>> +	sbdev->dev.bus = &slimbus_type;
>> +	sbdev->dev.parent = &ctrl->dev;
>> +	sbdev->dev.release = slim_dev_release;
>> +	sbdev->dev.driver = NULL;
>> +	sbdev->ctrl = ctrl;
>> +
>> +	slim_ctrl_get(ctrl);
> 
> Unfolding slim_ctrl_get(), with ctrl != NULL, gives us a container_of(),
> so this can't fail. Which is good because then an error check would have
> been nice.
> 
> But it also means that you can replace this with just:
> 	get_device(&ctrl->dev);
> 
> And drop slim_ctrl_get()
> 

ya.

>> +	sbdev->name = kasprintf(GFP_KERNEL, "%x:%x:%x:%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);
> 
> This will create another copy of the same string and as noted above
> there seems to be only one consumer, which could be switched over. So
> you can drop above kasprintf() and just use dev_set_name to format the
> name.
> 
> An added benefit is that you're not leaking the device reference from
> slim_ctrl_get() on the line before.

I agree, will fix this in next version.

> 
>> +	mutex_init(&sbdev->report_lock);
>> +
>> +	/* probe slave on this controller */
>> +	return device_register(&sbdev->dev);
>> +}
>> +
>> +/* Helper to get hex Manufacturer ID and Product id from compatible */
>> +static unsigned long str2hex(unsigned char *str)
> 
> The caller of this passes char *, so you can drop the unsigned. And add
> "const" while you're at it.

I will be removing this function in next version, replacing it with 
kstrtoul()

> 
>> +{
>> +	int value = 0;
>> +
>> +	while (*str) {
>> +		char c = *str++;
>> +
>> +		value = value << 4;
>> +		if (c >= '0' && c <= '9')
>> +			value |= (c - '0');
>> +		if (c >= 'a' && c <= 'f')
>> +			value |= (c - 'a' + 10);
> 
> At the cost of one more check here you can drop the line in the
> documentation about this only working for lower-case hex digits.
> 
>> +
>> +	}
>> +
>> +	return value;
>> +}
>> +
>> +/* OF helpers for SLIMbus */
>> +static void of_register_slim_devices(struct slim_controller *ctrl)
>> +{
>> +	struct device *dev = &ctrl->dev;
>> +	struct device_node *node;
>> +
>> +	if (!ctrl->dev.of_node)
>> +		return;
>> +
>> +	for_each_child_of_node(ctrl->dev.of_node, node) {
>> +		struct slim_device *slim;
>> +		const char *compat = NULL;
>> +		char *p, *tok;
>> +		int reg[2], ret;
>> +
>> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
> 
> This is leaked in several places below.
> 
>> +		if (!slim)
>> +			continue;
>> +
>> +		slim->dev.of_node = of_node_get(node);
> 
> Dito.
> 
>> +
>> +		compat = of_get_property(node, "compatible", NULL);
>> +		if (!compat)
>> +			continue;
>> +
>> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
> 
> Allocating a new string using string formatting based on an offset from
> a string we don't know the size of, just to tokenize it does not seem
> like the most efficient (nor safe) way of doing this.
> 
> 
> How about:
> 		ret = sscanf(compat, "slim%x,%x", &manf_id, &prod_code);
> 		if (ret != 2)
> 			error();
> 
Looks much better!


>> +
>> +		tok = strsep(&p, ",");
>> +		if (!tok) {
>> +			dev_err(dev, "No valid Manufacturer ID found\n");
>> +			kfree(p);
>> +			continue;
>> +		}
>> +		slim->e_addr.manf_id = str2hex(tok);
>> +
>> +		tok = strsep(&p, ",");
>> +		if (!tok) {
>> +			dev_err(dev, "No valid Product ID found\n");
>> +			kfree(p);
>> +			continue;
>> +		}
>> +		slim->e_addr.prod_code = str2hex(tok);
>> +		kfree(p);
>> +
>> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
>> +		if (ret) {
>> +			dev_err(dev, "Device and Instance id not found:%d\n",
>> +				ret);
>> +			continue;
>> +		}
>> +		slim->e_addr.dev_index = reg[0];
>> +		slim->e_addr.instance = reg[1];
>> +
>> +		ret = slim_add_device(ctrl, slim);
>> +		if (ret)
>> +			dev_err(dev, "of_slim device register err:%d\n", ret);
> 
> Cleanup if this fails.
Yes, we are leaking memory here, will fix this in next version.
> 
>> +	}
>> +}
>> +
>> +/**
>> + * 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);
> 
> The purpose of ctrl_idr is to generate unique ids for the name and to
> check that slim_del_controller() is only called on valid
> slim_controllers.
> 
> The latter is okay to just expect the controller drivers to do right and
> the prior is better done with an ida.
> 

I agree, IDA seems to be better fit here.

> 
> Also, the lower boundary should be 0, not ctrl->nr.
> 
>> +	mutex_unlock(&slim_lock);
>> +
>> +	if (id < 0)
>> +		return id;
>> +
>> +	ctrl->nr = id;
>> +
>> +	dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr);
> 
> This name is used in a lot of debug prints, can we do better?
> 
In some of the previous discussions with Vinod, it was suggested that 
slim_register_controller should not create an additional device here, 
which is redundant to the actual controller device itself.

So this name would actually come from the controller driver in next 
version of patches.

>> +	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);
> 
> This is the only place ctrl->name is used. Perhaps it would make more
> sense to base the dev_name off this string, to make all these dev_*()
> more useful.

Agreed.
> 
>> +
>> +	ctrl->wq = create_singlethread_workqueue(dev_name(&ctrl->dev));
>> +	if (!ctrl->wq)
>> +		goto err_workq_failed;
>> +
>> +	of_register_slim_devices(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() */
>> +static void slim_remove_device(struct slim_device *sbdev)
>> +{
>> +	device_unregister(&sbdev->dev);
>> +}
>> +
>> +static int slim_ctrl_remove_device(struct device *dev, void *null)
>> +{
>> +	slim_remove_device(to_slim_device(dev));
>> +	return 0;
>> +}
>> +
>> +/**
>> + * slim_del_controller: Controller tear-down.
>> + * @ctrl: Controller to tear-down.
>> + */
>> +int slim_del_controller(struct slim_controller *ctrl)
> 
> This is the opposite of slim_register_controller() so should it perhaps
> be called slim_unregister_controller() ?

makes sense..
> 
>> +{
>> +	struct slim_controller *found;
>> +
>> +	/* 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;
> 
> Just rely on the caller doing the right thing and just
> 	ida_remove(&ctrl_ida, ctrl->nr);

Moving to ida is best

> 
>> +
>> +	/* Remove all clients */
>> +	device_for_each_child(&ctrl->dev, NULL, slim_ctrl_remove_device);
>> +
> 
> As stated above there might be work items left in flight here, after the
> slim_devices are released.
> 
We should flush the workqueue before we remove the devices.

>> 
>> +	destroy_workqueue(ctrl->wq);
>> +
>> +	/* free bus id */
>> +	mutex_lock(&slim_lock);
>> +	idr_remove(&ctrl_idr, ctrl->nr);
>> +	mutex_unlock(&slim_lock);
>> +
>> +	device_unregister(&ctrl->dev);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(slim_del_controller);
> [..]
>> +/**
>> + * 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;
> 
> This will be written before read, so please don't initialize.
> 
yep.

>> +
>> +	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))
> 
> !slim
yep.

> 
>> +		return NULL;
>> +
>> +	slim->e_addr = *e_addr;
>> +	if (slim_add_device(ctrl, slim) != 0) {
> 
> The idiomatic way is:
> 
> ret = fn();
> if (ret) {
> 	failure...
> }
> 
okay
>> +		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;
> 
> i will be >= 0, so it can easily be distinguished from -ENXIO. So you
> could return that directly instead of passing "i" as a reference and to
> get the value.
> 
> On the other hand the return value is only used as an offset in addrt[]
> to read out addrt[i].laddr, so perhaps you should just return that
> instead (as an int).
> 
sounds sensible!

>> +			return 0;
>> +		}
>> +	}
>> +	return -ENXIO;
>> +}
>> +
>> +/**
>> + * slim_assign_laddr: Assign logical address to a device enumerated.
> 
> So I presume this will either report a new (not seen before) e_addr
> which should cause a new device to be spawned (although it might be
> already mentioned in DT) or it might be called for an existing device to
> update the logical address.
> 
> Can you describe when the latter is the case? Or is this a side effect
> of the code, rather than slimbus?

If this function is called for an existing device with logical address, 
it would give the same logical address, if it finds a vaild one in the 
table.
Other case is when it does not find a valid on then a new address is 
assigned depending on the valid flag passed to the function.
This case is more to do with the Qualcomm B family Slim controller, 
where linux side can not assign logical address, As DSP would only be 
able to assign this address.

>> + * @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.
> 
> How do you ensure this laddr is unique?
Its index into a table which is based on unique enumeration 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) {
> 
> So I presume this updates an existing e_addr -> laddr mapping. But this
> should imply that there is an associated slim_device with this e_addr
> and laddr. Shouldn't said slim_device have its laddr updated then?

If we find a vaild an matching entry in the list, we assume that the 
slim device is already aware of this laddr.


> 
>> +		*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++;
>> +		}
> 
> This seems better handled by a list than an array that we realloc. But
> better yet, this array seems to mirror the list of registered devices.
> 
> So why doesn't this function just try to resolve the slim_device with
> e_addr and if not allocate a new slim_device with a free laddr. Using an
> ida to keep track of used logical addresses would make this much
> cleaner.
> 
> Or a idr keyed by the laddr, it would still be O(n) to scan based on
> e_addr, but this logic as well as lookups by laddr would be cleaner.
> 
I will give this a try before sending next version and see how it looks!

>> +		ctrl->addrt[i].eaddr = *e_addr;
>> +		ctrl->addrt[i].valid = true;
>> +
>> +		/* Preferred address is index into table */
>> +		if (!valid)
>> +			*laddr = i;
> 
> Is this laddr available?
this is the new index or first instance of invalid entry in the table, 
so it should be available.
> 
>> +	}
>> +
>> +	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;
>> +
>> +	dev_info(&ctrl->dev, "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 = -ENODEV;
>> +	} 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);
> 
> I can't help feeling that this is the one and only point where you
> should call probe() on the slim_device. Are there some funky
> dependencies or protocol issues that makes this infeasible?

The reason probe is called earlier because the slim device needs 
power-up or reset sequence to make it discoverable on the bus.

> 
>> +	}
>> +	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 address.
>> + * @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)
> 
> In what case would e_addr != sb->e_addr and why can't this function just
> be return sb->laddr?
> 
I don't think e_addr and sb->eaddr should be different in this case.

In usecase like B family Qualcomm SOCs, where linux cannot assign 
logical address, It needs to get logical address from ADSP and then use 
that. Current model of get_logical_addr provides abstraction so that 
this ADSP communication to get logical addr is hidden from client.


>> +{
>> +	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);
> [..]
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_VERSION("0.1");
> 
> Who will ever update this version number? It's probably better to just
> omit it.
>
Yep.

>> +MODULE_DESCRIPTION("Slimbus module");
> 
> Rather than "Slimbus module", this is actually the "Slimbus core".
> 
Yep.
>> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
> [..]
>> +extern struct bus_type slimbus_type;
> 
> The device struct has a "struct device_type *type", which causes this
> name to be confusing. Please rename it slimbus_bus instead.
> 
> Why does this bus_type have to be known to the world?
May be not. can be removed.
> 
>> +
>> +/* 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
> 
> Keep the min/max here and move the rest to the patches that introduce
> consumers of them.

makes sense!

> 
> [..]
>> +/* 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
> 
> These are not currently used, move them to the patch that actually use
> them.
> 
>> +
>> +/* 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
> 
> Dito
> 
>> +
>> +/* Destination type Values */
>> +#define SLIM_MSG_DEST_LOGICALADDR	0
>> +#define SLIM_MSG_DEST_ENUMADDR		1
>> +#define	SLIM_MSG_DEST_BROADCAST		3
> 
> Dito

will do it in next version.


> 
>> +
>> +/**
>> + * 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
> 
> This mutex protects operations on the addrt array, so both name and
> documentation can be improved.
Agreed, will address this in next version.

> 
>> + * @addrt: Logical address table
> 
> Consider replacing with a idr, keyed by laddr. If there's actually a
> point in having this list...
> 
>> + * @num_dev: Number of active slimbus slaves on this bus
> 
> This is not so much "number of devices", but rather the length of @addrt.

lenght of addrt might be more than active devices as some of the entries 
in addrt are not valid after the device goes down.


> 
>> + * @wq: Workqueue per controller used to notify devices when they report present
>> + * @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.
> 
> Can you describe the use case for get_laddr() a little bit more?

Usecase is  for  B family Qualcomm SOCs, where linux cannot assign 
logical address, It needs to get logical address from ADSP and then use 
that. Current model of get_logical_addr provides abstraction so that 
this ADSP communication to get logical addr is hidden from client.

> 
>> + */
>> +struct slim_controller {
>> +	struct device		dev;
>> +	unsigned int		nr;
>> +	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;
>> +	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);
> 
> If nothing else I think this should return the laddr, rather than pass
> it back into the referenced u8.

I will give it a go and see how it fits with every thing around it.

> 
>> +};
>> +
> 
> Regards,
> Bjorn
> 

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-18 16:38         ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-18 16:38 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

Thanks for the Review Bjorn,

On 17/10/17 07:23, Bjorn Andersson wrote:
> On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:
> [..]
>> diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c
> [..]
>> +/**
>> + * Report callbacks(device_up, device_down) are implemented by slimbus-devices.
>> + * The calls are scheduled into a workqueue to avoid holding up controller
>> + * initialization/tear-down.
>> + */
>> +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);
> 
> This is the only place where sb->name is used in this driver. If you
> instead invoke dev_*() on &sb->dev you should get prettier output and
> can drop the double storage of the device name.

Makes sense, we could get rid of sb->name storage too.

> 
>> +
>> +	sbw = kmalloc(sizeof(*sbw), GFP_KERNEL);
>> +	if (!sbw)
>> +		return;
>> +
>> +	INIT_WORK(&sbw->wd, slim_report);
>> +	sbw->sbdev = sb;
>> +	sbw->report = report;
>> +	if (!queue_work(ctrl->wq, &sbw->wd)) {
> 
> When a controller is torn down destroy_workqueue() is called after all
> child devices has been unregistered, so this work might be scheduled
> after "sb" is gone, if I get this properly.

I agree, That is possible!
We should probably flush the workqueue before we start removing the clients.

> 
>> +		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	*sbdev;
>> +	struct slim_driver	*sbdrv;
>> +	int status = 0;
>> +
>> +	sbdev = to_slim_device(dev);
>> +	sbdrv = to_slim_driver(dev->driver);
>> +
>> +	sbdev->driver = sbdrv;
>> +
>> +	if (sbdrv->probe)
>> +		status = sbdrv->probe(sbdev);
> 
> So a driver can have a probe() and device_up() or just any one of them?
> 
> And probe() is called when the controller enumerates all devices
> mentioned in DT and then device_up() is called at that point in time and
> when it's advertised on the bus?
> 
> Is there a reason for this split model?
> 
yes, Some of the devices need to be powered up before they become 
usable, so probe is used to do the initial power up of the device.


>> +
>> +	if (status)
>> +		sbdev->driver = NULL;
>> +	else if (sbdrv->device_up)
>> +		schedule_slim_report(sbdev->ctrl, sbdev, true);
>> +
>> +	return status;
>> +}
>> +
>> +static int slim_device_remove(struct device *dev)
>> +{
>> +	struct slim_device *sbdev;
>> +	struct slim_driver *sbdrv;
>> +	int status = 0;
>> +
>> +	sbdev = to_slim_device(dev);
>> +	if (!dev->driver)
>> +		return 0;
>> +
>> +	sbdrv = to_slim_driver(dev->driver);
>> +	if (sbdrv->remove)
>> +		status = sbdrv->remove(sbdev);
>> +
>> +	mutex_lock(&sbdev->report_lock);
>> +	sbdev->notified = false;
>> +	if (status == 0)
>> +		sbdev->driver = NULL;
>> +	mutex_unlock(&sbdev->report_lock);
>> +	return status;
> 
> device_unregister() will call device_del() which will end up in
> __device_release_driver() which will call this function. Upon returning
> from this function the core expect the bus to have cleaned up after the
> dev (normally by calling drv->remove(dev)).
> 
> It will completely ignore the return value and continue tearing down the
> rest of the core resources, e.g. three lines down it will
> devres_release_all().
> 
> 
> So you have the option of sleeping, while waiting for stuff to be
> aborted/finished and then you need to clean things up.
> 
> The slim_device object itself will stick around until all references are
> dropped though.

So you are suggesting that we make slim_driver remove not return anything?

> 
>> +}
>> +
>> +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.
>> + * @owner: owning module/driver
>> + * 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, struct module *owner)
>> +{
>> +	drv->driver.bus = &slimbus_type;
>> +	drv->driver.owner = owner;
>> +	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)
> 
> A driver invoking slim_driver_unregister(NULL) is broken, drop this
> check and let it oops on the dereference instead.

Yep.

> 
>> +		driver_unregister(&drv->driver);
>> +}
>> +EXPORT_SYMBOL_GPL(slim_driver_unregister);
>> +
>> +static struct slim_controller *slim_ctrl_get(struct slim_controller *ctrl)
>> +{
>> +	if (!ctrl || !get_device(&ctrl->dev))
> 
> ctrl can't be NULL here. In all code paths leading here it's
> dereferenced multiple times already.

I agree.

> 
>> +		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);
> 
> As far as I can see there's no case where sbdev->ctrl will ever be NULL,
> so yo can just replace this with
> 	put_device(&ctrl->dev);
> 
> And drop slim_ctrl_put().


Yes..

> 
>> +	kfree(sbdev->name);
>> +	kfree(sbdev);
>> +}
>> +
>> +static int slim_add_device(struct slim_controller *ctrl,
>> +			   struct slim_device *sbdev)
>> +{
>> +	sbdev->dev.bus = &slimbus_type;
>> +	sbdev->dev.parent = &ctrl->dev;
>> +	sbdev->dev.release = slim_dev_release;
>> +	sbdev->dev.driver = NULL;
>> +	sbdev->ctrl = ctrl;
>> +
>> +	slim_ctrl_get(ctrl);
> 
> Unfolding slim_ctrl_get(), with ctrl != NULL, gives us a container_of(),
> so this can't fail. Which is good because then an error check would have
> been nice.
> 
> But it also means that you can replace this with just:
> 	get_device(&ctrl->dev);
> 
> And drop slim_ctrl_get()
> 

ya.

>> +	sbdev->name = kasprintf(GFP_KERNEL, "%x:%x:%x:%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);
> 
> This will create another copy of the same string and as noted above
> there seems to be only one consumer, which could be switched over. So
> you can drop above kasprintf() and just use dev_set_name to format the
> name.
> 
> An added benefit is that you're not leaking the device reference from
> slim_ctrl_get() on the line before.

I agree, will fix this in next version.

> 
>> +	mutex_init(&sbdev->report_lock);
>> +
>> +	/* probe slave on this controller */
>> +	return device_register(&sbdev->dev);
>> +}
>> +
>> +/* Helper to get hex Manufacturer ID and Product id from compatible */
>> +static unsigned long str2hex(unsigned char *str)
> 
> The caller of this passes char *, so you can drop the unsigned. And add
> "const" while you're at it.

I will be removing this function in next version, replacing it with 
kstrtoul()

> 
>> +{
>> +	int value = 0;
>> +
>> +	while (*str) {
>> +		char c = *str++;
>> +
>> +		value = value << 4;
>> +		if (c >= '0' && c <= '9')
>> +			value |= (c - '0');
>> +		if (c >= 'a' && c <= 'f')
>> +			value |= (c - 'a' + 10);
> 
> At the cost of one more check here you can drop the line in the
> documentation about this only working for lower-case hex digits.
> 
>> +
>> +	}
>> +
>> +	return value;
>> +}
>> +
>> +/* OF helpers for SLIMbus */
>> +static void of_register_slim_devices(struct slim_controller *ctrl)
>> +{
>> +	struct device *dev = &ctrl->dev;
>> +	struct device_node *node;
>> +
>> +	if (!ctrl->dev.of_node)
>> +		return;
>> +
>> +	for_each_child_of_node(ctrl->dev.of_node, node) {
>> +		struct slim_device *slim;
>> +		const char *compat = NULL;
>> +		char *p, *tok;
>> +		int reg[2], ret;
>> +
>> +		slim = kzalloc(sizeof(*slim), GFP_KERNEL);
> 
> This is leaked in several places below.
> 
>> +		if (!slim)
>> +			continue;
>> +
>> +		slim->dev.of_node = of_node_get(node);
> 
> Dito.
> 
>> +
>> +		compat = of_get_property(node, "compatible", NULL);
>> +		if (!compat)
>> +			continue;
>> +
>> +		p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim"));
> 
> Allocating a new string using string formatting based on an offset from
> a string we don't know the size of, just to tokenize it does not seem
> like the most efficient (nor safe) way of doing this.
> 
> 
> How about:
> 		ret = sscanf(compat, "slim%x,%x", &manf_id, &prod_code);
> 		if (ret != 2)
> 			error();
> 
Looks much better!


>> +
>> +		tok = strsep(&p, ",");
>> +		if (!tok) {
>> +			dev_err(dev, "No valid Manufacturer ID found\n");
>> +			kfree(p);
>> +			continue;
>> +		}
>> +		slim->e_addr.manf_id = str2hex(tok);
>> +
>> +		tok = strsep(&p, ",");
>> +		if (!tok) {
>> +			dev_err(dev, "No valid Product ID found\n");
>> +			kfree(p);
>> +			continue;
>> +		}
>> +		slim->e_addr.prod_code = str2hex(tok);
>> +		kfree(p);
>> +
>> +		ret = of_property_read_u32_array(node, "reg", reg, 2);
>> +		if (ret) {
>> +			dev_err(dev, "Device and Instance id not found:%d\n",
>> +				ret);
>> +			continue;
>> +		}
>> +		slim->e_addr.dev_index = reg[0];
>> +		slim->e_addr.instance = reg[1];
>> +
>> +		ret = slim_add_device(ctrl, slim);
>> +		if (ret)
>> +			dev_err(dev, "of_slim device register err:%d\n", ret);
> 
> Cleanup if this fails.
Yes, we are leaking memory here, will fix this in next version.
> 
>> +	}
>> +}
>> +
>> +/**
>> + * 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);
> 
> The purpose of ctrl_idr is to generate unique ids for the name and to
> check that slim_del_controller() is only called on valid
> slim_controllers.
> 
> The latter is okay to just expect the controller drivers to do right and
> the prior is better done with an ida.
> 

I agree, IDA seems to be better fit here.

> 
> Also, the lower boundary should be 0, not ctrl->nr.
> 
>> +	mutex_unlock(&slim_lock);
>> +
>> +	if (id < 0)
>> +		return id;
>> +
>> +	ctrl->nr = id;
>> +
>> +	dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr);
> 
> This name is used in a lot of debug prints, can we do better?
> 
In some of the previous discussions with Vinod, it was suggested that 
slim_register_controller should not create an additional device here, 
which is redundant to the actual controller device itself.

So this name would actually come from the controller driver in next 
version of patches.

>> +	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);
> 
> This is the only place ctrl->name is used. Perhaps it would make more
> sense to base the dev_name off this string, to make all these dev_*()
> more useful.

Agreed.
> 
>> +
>> +	ctrl->wq = create_singlethread_workqueue(dev_name(&ctrl->dev));
>> +	if (!ctrl->wq)
>> +		goto err_workq_failed;
>> +
>> +	of_register_slim_devices(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() */
>> +static void slim_remove_device(struct slim_device *sbdev)
>> +{
>> +	device_unregister(&sbdev->dev);
>> +}
>> +
>> +static int slim_ctrl_remove_device(struct device *dev, void *null)
>> +{
>> +	slim_remove_device(to_slim_device(dev));
>> +	return 0;
>> +}
>> +
>> +/**
>> + * slim_del_controller: Controller tear-down.
>> + * @ctrl: Controller to tear-down.
>> + */
>> +int slim_del_controller(struct slim_controller *ctrl)
> 
> This is the opposite of slim_register_controller() so should it perhaps
> be called slim_unregister_controller() ?

makes sense..
> 
>> +{
>> +	struct slim_controller *found;
>> +
>> +	/* 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;
> 
> Just rely on the caller doing the right thing and just
> 	ida_remove(&ctrl_ida, ctrl->nr);

Moving to ida is best

> 
>> +
>> +	/* Remove all clients */
>> +	device_for_each_child(&ctrl->dev, NULL, slim_ctrl_remove_device);
>> +
> 
> As stated above there might be work items left in flight here, after the
> slim_devices are released.
> 
We should flush the workqueue before we remove the devices.

>> 
>> +	destroy_workqueue(ctrl->wq);
>> +
>> +	/* free bus id */
>> +	mutex_lock(&slim_lock);
>> +	idr_remove(&ctrl_idr, ctrl->nr);
>> +	mutex_unlock(&slim_lock);
>> +
>> +	device_unregister(&ctrl->dev);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(slim_del_controller);
> [..]
>> +/**
>> + * 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;
> 
> This will be written before read, so please don't initialize.
> 
yep.

>> +
>> +	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))
> 
> !slim
yep.

> 
>> +		return NULL;
>> +
>> +	slim->e_addr = *e_addr;
>> +	if (slim_add_device(ctrl, slim) != 0) {
> 
> The idiomatic way is:
> 
> ret = fn();
> if (ret) {
> 	failure...
> }
> 
okay
>> +		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;
> 
> i will be >= 0, so it can easily be distinguished from -ENXIO. So you
> could return that directly instead of passing "i" as a reference and to
> get the value.
> 
> On the other hand the return value is only used as an offset in addrt[]
> to read out addrt[i].laddr, so perhaps you should just return that
> instead (as an int).
> 
sounds sensible!

>> +			return 0;
>> +		}
>> +	}
>> +	return -ENXIO;
>> +}
>> +
>> +/**
>> + * slim_assign_laddr: Assign logical address to a device enumerated.
> 
> So I presume this will either report a new (not seen before) e_addr
> which should cause a new device to be spawned (although it might be
> already mentioned in DT) or it might be called for an existing device to
> update the logical address.
> 
> Can you describe when the latter is the case? Or is this a side effect
> of the code, rather than slimbus?

If this function is called for an existing device with logical address, 
it would give the same logical address, if it finds a vaild one in the 
table.
Other case is when it does not find a valid on then a new address is 
assigned depending on the valid flag passed to the function.
This case is more to do with the Qualcomm B family Slim controller, 
where linux side can not assign logical address, As DSP would only be 
able to assign this address.

>> + * @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.
> 
> How do you ensure this laddr is unique?
Its index into a table which is based on unique enumeration 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) {
> 
> So I presume this updates an existing e_addr -> laddr mapping. But this
> should imply that there is an associated slim_device with this e_addr
> and laddr. Shouldn't said slim_device have its laddr updated then?

If we find a vaild an matching entry in the list, we assume that the 
slim device is already aware of this laddr.


> 
>> +		*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++;
>> +		}
> 
> This seems better handled by a list than an array that we realloc. But
> better yet, this array seems to mirror the list of registered devices.
> 
> So why doesn't this function just try to resolve the slim_device with
> e_addr and if not allocate a new slim_device with a free laddr. Using an
> ida to keep track of used logical addresses would make this much
> cleaner.
> 
> Or a idr keyed by the laddr, it would still be O(n) to scan based on
> e_addr, but this logic as well as lookups by laddr would be cleaner.
> 
I will give this a try before sending next version and see how it looks!

>> +		ctrl->addrt[i].eaddr = *e_addr;
>> +		ctrl->addrt[i].valid = true;
>> +
>> +		/* Preferred address is index into table */
>> +		if (!valid)
>> +			*laddr = i;
> 
> Is this laddr available?
this is the new index or first instance of invalid entry in the table, 
so it should be available.
> 
>> +	}
>> +
>> +	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;
>> +
>> +	dev_info(&ctrl->dev, "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 = -ENODEV;
>> +	} 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);
> 
> I can't help feeling that this is the one and only point where you
> should call probe() on the slim_device. Are there some funky
> dependencies or protocol issues that makes this infeasible?

The reason probe is called earlier because the slim device needs 
power-up or reset sequence to make it discoverable on the bus.

> 
>> +	}
>> +	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 address.
>> + * @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)
> 
> In what case would e_addr != sb->e_addr and why can't this function just
> be return sb->laddr?
> 
I don't think e_addr and sb->eaddr should be different in this case.

In usecase like B family Qualcomm SOCs, where linux cannot assign 
logical address, It needs to get logical address from ADSP and then use 
that. Current model of get_logical_addr provides abstraction so that 
this ADSP communication to get logical addr is hidden from client.


>> +{
>> +	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);
> [..]
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_VERSION("0.1");
> 
> Who will ever update this version number? It's probably better to just
> omit it.
>
Yep.

>> +MODULE_DESCRIPTION("Slimbus module");
> 
> Rather than "Slimbus module", this is actually the "Slimbus core".
> 
Yep.
>> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
> [..]
>> +extern struct bus_type slimbus_type;
> 
> The device struct has a "struct device_type *type", which causes this
> name to be confusing. Please rename it slimbus_bus instead.
> 
> Why does this bus_type have to be known to the world?
May be not. can be removed.
> 
>> +
>> +/* 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
> 
> Keep the min/max here and move the rest to the patches that introduce
> consumers of them.

makes sense!

> 
> [..]
>> +/* 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
> 
> These are not currently used, move them to the patch that actually use
> them.
> 
>> +
>> +/* 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
> 
> Dito
> 
>> +
>> +/* Destination type Values */
>> +#define SLIM_MSG_DEST_LOGICALADDR	0
>> +#define SLIM_MSG_DEST_ENUMADDR		1
>> +#define	SLIM_MSG_DEST_BROADCAST		3
> 
> Dito

will do it in next version.


> 
>> +
>> +/**
>> + * 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
> 
> This mutex protects operations on the addrt array, so both name and
> documentation can be improved.
Agreed, will address this in next version.

> 
>> + * @addrt: Logical address table
> 
> Consider replacing with a idr, keyed by laddr. If there's actually a
> point in having this list...
> 
>> + * @num_dev: Number of active slimbus slaves on this bus
> 
> This is not so much "number of devices", but rather the length of @addrt.

lenght of addrt might be more than active devices as some of the entries 
in addrt are not valid after the device goes down.


> 
>> + * @wq: Workqueue per controller used to notify devices when they report present
>> + * @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.
> 
> Can you describe the use case for get_laddr() a little bit more?

Usecase is  for  B family Qualcomm SOCs, where linux cannot assign 
logical address, It needs to get logical address from ADSP and then use 
that. Current model of get_logical_addr provides abstraction so that 
this ADSP communication to get logical addr is hidden from client.

> 
>> + */
>> +struct slim_controller {
>> +	struct device		dev;
>> +	unsigned int		nr;
>> +	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;
>> +	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);
> 
> If nothing else I think this should return the laddr, rather than pass
> it back into the referenced u8.

I will give it a go and see how it fits with every thing around it.

> 
>> +};
>> +
> 
> Regards,
> Bjorn
> 

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

* Re: [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-18  6:15     ` Bjorn Andersson
@ 2017-10-18 16:39       ` Srinivas Kandagatla
  2017-10-20  5:00           ` Bjorn Andersson
  0 siblings, 1 reply; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-18 16:39 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

Thanks for Review Comments,


On 18/10/17 07:15, Bjorn Andersson wrote:
> On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:
> 
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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).
> 
> I think we can implement this "optimization" with less complex code,
> regardless I don't think we need to mention this in the commit
> message...
> 
> [..]
>> diff --git a/drivers/slimbus/slim-messaging.c b/drivers/slimbus/slim-messaging.c
> [..]
>> +/**
>> + * 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)
> 
> Even if tid and len comes from the spec I recommend you making them int
> and size_t.
okay, will give that a go.
> 
>> +{
>> +	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) {
> 
> if (!msg || !msg->rbuf)
> 
> 
> When is it valid to add a transaction to tid_tbl with msg->rbuf = NULL?
> Should we reject it earlier?

We do sanity checks before posting the request, however there are cases 
where this checks are not in place like calling slim_processtxn() directly.

May be we should add this check all the case
> 
>> +		spin_unlock_irqrestore(&ctrl->txn_lock, flags);
>> +		dev_err(&ctrl->dev, "Got response to invalid TID:%d, len:%d\n",
>> +				tid, len);
>> +		return;
>> +	}
>> +	ctrl->tid_tbl[tid] = NULL;
>> +	spin_unlock_irqrestore(&ctrl->txn_lock, flags);
>> +
>> +	memcpy(msg->rbuf, reply, len);
>> +	if (msg->comp_cb)
>> +		msg->comp_cb(msg->ctx, 0);
>> +}
>> +EXPORT_SYMBOL_GPL(slim_msg_response);
> [..]
>> +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 (ret) {
> 	if (need_tid)
> 		drop();
> 	
> 	dev_err();
> }
> 
> Would probably make this a little bit cleaner...

I agree.

> 
>> +	if (!async) {
>> +		txn->msg->comp_cb = NULL;
>> +		txn->msg->ctx = NULL;
> 
> I believe txn->msg is always required, so you don't need to do this
> contidionally.

I don't get this, why do you want to set comp_cb to NULL unconditionally?


> 
>> +	}
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(slim_processtxn);
> [..]
>> +int slim_request_val_element(struct slim_device *sb,
>> +				struct slim_val_inf *msg)
>> +{
>> +	struct slim_controller *ctrl = sb->ctrl;
>> +
>> +	if (!ctrl)
>> +		return -EINVAL;
> 
>  From patch 1 I believe it's invalid for sb->ctrl to be NULL, so there
> shouldn't be a need to check this.
> 
yep.

>> +
>> +	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_VALUE);
>> +}
>> +EXPORT_SYMBOL_GPL(slim_request_val_element);
> [..]
>> +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);
>> +
> 
> Please provide kerneldoc for exported symbols.
> 

Yes, I will fix this in next version.

>> +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];
> 
> Why is this doing struct copy?
> 
Not sure, do you see any issue with this?

>> +	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);
> [..]
>>   /**
>> + * 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;
> 
> This is not mentioned in the kerneldoc. Use void * for data buffers.
> 
>> +	const u8		*wbuf;
> 
> Can a message ever be read and write? Otherwise it should be sufficient
> to only have one data pointer.

some of the SLIMBus commands are request_response type, meaning the old 
value is returned and at the same time the new value is updated.
> 
>> +	void			(*comp_cb)(void *ctx, int err);
>> +	void			*ctx;
>> +};
>> +
> [..]
>> +/**
>> + * 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
>> + */
> 
> Is this ringbuffer mechanism defined in the slimbus specification? Looks
> like something specific to the Qualcomm controller, rather than
> something that should be enforced in the framework.
> 

Yes, this is not part of the slimbus specs, but Qcom SOCs have concept 
of Message Queues.

Are you suggesting that this buffer handling has to be moved out of core 
into controller driver?


>> +struct slim_ctrl_buf {
>> +	void		*base;
>> +	phys_addr_t	phy;
>> +	spinlock_t	lock;
>> +	int		head;
>> +	int		tail;
>> +	int		sl_sz;
>> +	int		n;
>> +};
> [..]
>> +/**
>>    * 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
> 
> This is out list of pending write requests, yet it's implemented as an
> array used in a complex ring buffer fashion. Wouldn't it be easier to
> just have this as a linked list of slim_pending struct?

Yes, its possible to implement this as list, i will give that a try.

> 
>> + * @tx_sem: Semaphore for available TX buffers for this controller
>> + * @last_tid: Last used entry for TID transactions
>>    * @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.
>> @@ -161,6 +278,15 @@ 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;
> 
> I suggest that you replace these two with an idr, rather than having a
> fixed size array and then last_tid as an optimization to limit how far
> you linear search for an empty space.

Will try that and see how it looks!


> 
>> +	spinlock_t		txn_lock;
>> +	struct slim_ctrl_buf	tx;
>> +	struct slim_ctrl_buf	rx;
>> +	struct slim_pending	*pending_wr;
>> +	struct semaphore	tx_sem;
> 
> Please don't use semaphores. If you keep pending_wr as a list you can
> use list_empty() instead...

will give that a go.

> 
>> +	int			(*xfer_msg)(struct slim_controller *ctrl,
>> +					    struct slim_msg_txn *tx, void *buf);
> 
> I believe buf has fixed size, so please document this.
Yep. Will do that in next version.
> 
>>   	int			(*set_laddr)(struct slim_controller *ctrl,
>>   					     struct slim_eaddr *ea, u8 laddr);
>>   	int			(*get_laddr)(struct slim_controller *ctrl,
>> @@ -295,5 +421,40 @@ static inline void slim_set_devicedata(struct slim_device *dev, void *data)
>>   {
>>   	dev_set_drvdata(&dev->dev, data);
>>   }
> 
> Regards,
> Bjorn
> 

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

* Re: [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver
  2017-10-18  7:27   ` Bjorn Andersson
@ 2017-10-18 16:39     ` Srinivas Kandagatla
  0 siblings, 0 replies; 87+ messages in thread
From: Srinivas Kandagatla @ 2017-10-18 16:39 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

Thanks for Review Comments.

On 18/10/17 08:27, Bjorn Andersson wrote:
> On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:
> 
>> From: Sagar Dharia <sdharia@codeaurora.org>
>>
>> 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>
>> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>> ---
>>   .../devicetree/bindings/slimbus/slim-qcom-ctrl.txt |  43 ++
>>   drivers/slimbus/Kconfig                            |   9 +
>>   drivers/slimbus/Makefile                           |   3 +
>>   drivers/slimbus/slim-qcom-ctrl.c                   | 594 +++++++++++++++++++++
>>   drivers/slimbus/slim-qcom.h                        |  63 +++
>>   5 files changed, 712 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..081110d
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/slimbus/slim-qcom-ctrl.txt
>> @@ -0,0 +1,43 @@
>> +Qualcomm SLIMBUS controller
>> +This controller is used if applications processor driver controls slimbus
>> +master component.
>> +
>> +Required properties:
>> +
>> + - #address-cells - refer to Documentation/devicetree/bindings/slimbus/bus.txt
>> + - #size-cells	- refer to Documentation/devicetree/bindings/slimbus/bus.txt
>> +
>> + - 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:
>> +	 "ctrl": Physical adderess of controller register blocks
>> + - compatible : should be "qcom,<SOC-NAME>-slim" for SOC specific compatible or
> 
> As you implementation is only compatible with "qcom,slim" this should be
> "and" not "or".
yep,

> 
>> + 		"qcom,slim" if using generic qcom SLIM IP.
>> + - 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: "slew"
>> +
>> +Example:
>> +	slim@28080000 {
>> +		compatible = "qcom,slim";
>> +		#address-cells = <4>;
>> +		#size-cells = <0>;
>> +		reg = <0x28080000 0x2000>, <0x80207C 4>;
>> +		reg-names = "ctrl", "slew";
>> +		interrupts = <0 33 0>;
>> +		clocks = <&lcc SLIMBUS_SRC>, <&lcc AUDIO_SLIMBUS_CLK>;
>> +		clock-names = "iface_clk", "core_clk";
>> +
>> +		codec: wcd9310@1{
>> +			compatible = "slim217,60";
>> +			reg = <1 0>;
>> +		};
>> +	};
>> diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig
>> index f0b118a..438773e 100644
>> --- a/drivers/slimbus/Kconfig
>> +++ b/drivers/slimbus/Kconfig
>> @@ -9,3 +9,12 @@ menuconfig SLIMBUS
>>   
>>   	  If unsure, choose N.
yep.

>>   
>> +if SLIMBUS
>> +config SLIM_QCOM_CTRL
>> +	tristate "Qualcomm Slimbus Manager Component"
>> +	depends on SLIMBUS
>> +	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 bd1d35e..ed8521a 100644
>> --- a/drivers/slimbus/Makefile
>> +++ b/drivers/slimbus/Makefile
>> @@ -3,3 +3,6 @@
>>   #
>>   obj-$(CONFIG_SLIMBUS)			+= slimbus.o
>>   slimbus-y				:= slim-core.o slim-messaging.o
>> +
>> +#Controllers
>> +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..d0574e1
>> --- /dev/null
>> +++ b/drivers/slimbus/slim-qcom-ctrl.c
..
>> +#include "slim-qcom.h"
>> +
>> +#define MSM_SLIM_NAME	"msm_slim_ctrl"
> 
> Just put this string in the driver struct.

yes..

> 
>> +
>> +/* Manager registers */
>> +#define	MGR_CFG		0x200
>...

>> +
>> +static int msm_slim_queue_tx(struct msm_slim_ctrl *dev, u32 *buf, u8 len,
>> +			     u32 tx_reg)
> 
> Use void * for buf.
> 
okay.

>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < (len + 3) >> 2; i++) {
> 
> If len is in bytes then this looks like you will read outside the
> buffer. If buf is guaranteed to be 4-byte aligned make "len" number of
> 32-bit entries in the array.
> 
>> +		dev_dbg(dev->dev, "AHB TX data:0x%x\n", buf[i]);
> 
> Drop the debug print, if anything use print_hex_dump to get the whole
> chunk.
> 
makes sense.
>> +		writel_relaxed(buf[i], dev->base + tx_reg + (i * 4));
>> +	}
> 
> Replace this loop with:
> 
> 	__iowrite32_copy(dev->base + tx_reg, buf, len / sizeof(u32));
> 
>> +	/* Guarantee that message is sent before returning */
> 
> "Ensure ordering of subsequent writes", there's no guarantees that the
> write is done at the end of this function.
> 
will update the comment!

>> +	mb();
>> +	return 0;
> 
> Why return an int when there are not other return paths?
> 
I agree.
>> +}
>> +
>> +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, ret = IRQ_NONE;
>> +
> 
> If you split the two large parts of this function into two functions
> things will be cleaner and you'll save yourself one level of
> indentation.
Will give it a try.

> 
>> +	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
>> +		 */
> 
> I'm not sure why this is necessary, but this is not "guaranteed" by the
> mb().

Will update the comment.

> 
>> +		mb();
>> +		slim_return_tx(&dev->ctrl, err);
>> +		ret = IRQ_HANDLED;
>> +	}
>> +	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;
> 
> The way this function deals with cryptic variable names and cryptic
> bit shifts is quite hard to follow. Perhaps it makes sense if you know
> exactly what's going on?
> 
> A comment describing the fields in the read information would be quite
> helpful, at least.
> 
I will try to add some notes here to make it easy for readers.

>> +		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) {
> 
> if (!rx_buf)
> 
>> +			dev_err(dev->dev, "dropping RX:0x%x due to RX full\n",
>> +						pkt[0]);
> 
> Why do we need to drop this incoming message? Will things recover from
> that?

Caller might timeout due to this.


> 
>> +			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]);
> 
> Drop this debug message, if you really want to show this use
> print_hex_dump() after reading the entire thing.
> 
> 
> And you should be able to replace this loop with __ioread32_copy()
> 
yep.

>> +		}
>> +
>> +		switch (mc) {
>> +			u8 *buf, la;
>> +			u16 ele;
> 
> What?!
It does not look very good!!

Its should be legal as it is within the block with automatic storage 
duration. But it would never be evaluated if we try to initialize these.

I will move them to better place in next version.

> 
>> +
>> +		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]);
> 
> To whom is this cryptic dump valuable? Please use print_hex_dump()
> rather than rolling your own print-loops.
> 
Yep.. Will cleanup all such instances.
>> +			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
>> +		 */
> 
> "Ensure ordering of CLR bit write with subsequent writes" although I'm
> wondering how useful this is...
> 
>> +		mb();
>> +		if (q_rx)
>> +			queue_work(dev->rxwq, &dev->wd);
>> +
>> +		ret = IRQ_HANDLED;
>> +	}
>> +	return ret;
>> +}
>> +
>> +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);
>> +	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);
> 
> Put "dt" (whatever that is?!) in a variable and then do this line once,
> saves you from the line break.

> 
>> +
>> +	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;
> 
> This would be easier to deal with in the form of:
> 
> struct foo {
> 	__le16 manf_id;
> 	__le16 prod_code;
> 	u8 dev_idx;
> 	u8 instance;
> 	u8 laddr;
> };
> 
> And it doesn't look like a "buf", it looks like a packet of some
> specific type, so it could be given a better name.
I agree.
> 
>> +
>> +	/**
>> +	 * Retries are needed since bus may lose sync when multiple devices
>> +	 * are coming up and reporting present
>> +	 */
> 
> Is this a todo? I'm not sure how this relates to the code.
> 
not sure. but can be removed from here.

>> +	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];
> 
> 40? Are you perhaps trying to say SLIM_MSGQ_BUF_LEN?
> 
yes.

>> +	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];
> 
> How about describing this like:
> 
> struct some_pkt {
> 	u8 len:5;
> 	u8 mt:3;
> 	u8 mc;
> 
> 	__le16 manf_id;
> 	__le16 prod_code;
> 	u8 dev_idx;
> 	u8 instance;
> };
> 
> But perhaps manf_id and prod_code are big endian here? I'm getting
> confused from this code.
Its little endian.
> 
>> +		if (mt == SLIM_MSG_MT_CORE &&
>> +			mc == SLIM_MSG_MC_REPORT_PRESENT) {
>> +			u8 laddr;
>> +			struct slim_eaddr ea;
>> +			u8 e_addr[6];>> +/* 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;
>> +
>> +			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);
> 
> The idiomatic way is to deal with errors first and then you don't have
> to indent the normal code path.
> 
>> +
>> +		}
>> +
>> +	}
>> +}
>> +
>> +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,
> 
> This is a local variable...
> 
>> +						     "slew");
>> +	if (!dev->slew_mem) {
>> +		dev_warn(&pdev->dev, "no slimbus slew resource\n");
>> +		return;
>> +	}
>> +
> 
> It's idiomatic to do:
> 
> 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "slew");
> 	slew_reg = devm_ioremap_resource(&pdev->dev, res);
> 	if (IS_ERR(slew_reg))
> 		...
> 
yep.
> 
>> +	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");
> 
> Is this not a problem? Is it okay to silently discard this error.
> 
>> +		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 */
> 
> This is not what wmb() does. If you want to ensure that it's written do
> a readback.
> 
>> +	wmb();
> 
> There's no reason to keep the slew register mapped beyond this function,
> so you could drop the devm_ and iounmap() slew_reg here again.
> 
> 
> If there's a reason to touch this in the future, then keep track of
> slew_reg rather than the slew_mem...
> 
I agree, we can clean this part up.

>> +}
>> +
>> +static int msm_slim_probe(struct platform_device *pdev)
>> +{
>> +	struct msm_slim_ctrl *dev;
> 
> "dev" is not the best name for this (dev->dev?!)
> 
>> +	struct slim_controller *ctrl;
>> +	struct resource *slim_mem;
>> +	struct resource *irq;
>> +	struct clk *hclk, *rclk;
> 
> Why the odd names?
> 
yes, I was bit confused when i started reading this part
Will fix this.
>> +	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 */
> 
> Do this properly.
> 
this is a carry over.. i will remove unnecessary commetns.

>> +		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, "ctrl");
> 
> Move this down to the devm_ioremap() and skip the error check.
yep.
> 
>> +	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 you allocate this earlier you can reduce the number of local
> variables.
> 
possibly..
>> +	if (!dev)
>> +		return -ENOMEM;
>> +
>> +	dev->hclk = hclk;
>> +	dev->rclk = rclk;
>> +	ctrl = &dev->ctrl;
>> +	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));
> 
> devm_ioremap_resource()
> 
>> +	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;
> 
> Use platform_get_irq() instead and you don't need to do this.
> 
yep,

>> +
>> +	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;
> 
> Dividing by 8 makes the code readable, compilers will make sure it's
> still super fast.
> 
yep..
>> +	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;
>> +	}
> 
> At this point you might get interrupts, which I presume will try to
> access the registers which you start clocking on the next line. Please
> reorder these.
> 
makes sense..
>> +
>> +	ret = clk_prepare_enable(hclk);
>> +	if (ret)
>> +		goto err_hclk_enable_failed;
>> +
>> +	ret = clk_prepare_enable(rclk);
>> +	if (ret)
>> +		goto err_rclk_enable_failed;
> 
> Use the clk_bulk api instead.
> 
yep.
>> +
>> +
>> +	ctrl->tx.base = dma_alloc_coherent(&pdev->dev,
>> +					   (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(&pdev->dev,
>> +					   (ctrl->rx.sl_sz * ctrl->rx.n),
>> +					   &ctrl->rx.phy, GFP_KERNEL);
>> +	if (!ctrl->rx.base) {
>> +		ret = -ENOMEM;
>> +		goto rx_alloc_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));
> 
> Use non-relaxed writel, unless there's a good reason not to.
> 
>> +	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
>> +	 */
> 
> Drop this comment and just use a non-relaxed writel for the framer
> registration initialization.
> 
>> +	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();
> 
> Drop this mb and use writel()
> 
>> +
>> +	writel_relaxed(MGR_CFG_ENABLE, dev->base + MGR_CFG);
>> +	/*
>> +	 * Make sure that manager-enable is written through before interface
>> +	 * device is enabled
> 
> Dito
> 
>> +	 */
>> +	mb();
>> +	writel_relaxed(1, dev->base + INTF_CFG);
>> +	/*
>> +	 * Make sure that interface-enable is written through before enabling
>> +	 * ported generic device inside MSM manager
>> +	 */
> 
> Dito
> 
>> +	mb();
>> +
>> +	writel_relaxed(1, dev->base + CFG_PORT(COMP_CFG, dev->ver));
>> +	/*
>> +	 * Make sure that all writes have gone through before exiting this
>> +	 * function
>> +	 */
> 
> Dito
I agree, we could just do writel here.

> 
>> +	mb();
>> +
>> +	dev_dbg(dev->dev, "MSM SB controller is up:ver:0x%x!\n", dev->ver);
>> +	return 0;
>> +
>> +err_ctrl_failed:
>> +	dma_free_coherent(&pdev->dev, (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:
>> +	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);
>> +	struct slim_controller *ctrl = to_slim_controller(&pdev->dev);
>> +
>> +	dma_free_coherent(&pdev->dev, (ctrl->rx.sl_sz * ctrl->rx.n),
>> +			  ctrl->rx.base, ctrl->rx.phy);
>> +	dma_free_coherent(&pdev->dev, (ctrl->tx.sl_sz * ctrl->tx.n),
>> +			  ctrl->tx.base, ctrl->tx.phy);
> 
> Don't you at least want to disable irqs before you free this memory?
> 
yes.. makes sense..
>> +
>> +	disable_irq(dev->irq);
>> +	clk_disable_unprepare(dev->rclk);
>> +	clk_disable_unprepare(dev->hclk);
>> +	slim_del_controller(&dev->ctrl);
>> +	destroy_workqueue(dev->rxwq);
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id msm_slim_dt_match[] = {
>> +	{
>> +		.compatible = "qcom,slim",
>> +	},
>> +	{}
>> +};
>> +
>> +static struct platform_driver msm_slim_driver = {
>> +	.probe = msm_slim_probe,
>> +	.remove = msm_slim_remove,
>> +	.driver	= {
>> +		.name = MSM_SLIM_NAME,
>> +		.owner = THIS_MODULE,
> 
> No "owner"
> 

y

>> +		.of_match_table = msm_slim_dt_match,
>> +	},
>> +};
>> +module_platform_driver(msm_slim_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_VERSION("0.1");
> 
> Skip the version, we'll never update it anyways.
> 
Yep.

>> +MODULE_DESCRIPTION("Qualcomm Slimbus controller");
>> +MODULE_ALIAS("platform:qcom-slim");
> 
> No-one will request the module by this alias, so skip it.
> 
yep.

>> 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
> 
> As far as I can see this information is all local to lim-qcom-ctrl.c, so
> put it there.

Yes, as of today, but its possible that this header can be shared across 
other slim controller in Qcom B Family.


> 
>> @@ -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
> 
> Unused.

Will remove this in next version.

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

>> +	int			irq;
>> +	struct workqueue_struct *rxwq;
>> +	struct work_struct	wd;
>> +	struct clk		*rclk;
>> +	struct clk		*hclk;
>> +	u32			ver;
> 
> "ver" is only used within probe(), use a local variable instead.
yep.

> 
>> +};
>> +
>> +#endif
> 
> Regards,
> Bjorn
> 

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

* Re: [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
  2017-10-18 16:39       ` Srinivas Kandagatla
@ 2017-10-20  5:00           ` Bjorn Andersson
  0 siblings, 0 replies; 87+ messages in thread
From: Bjorn Andersson @ 2017-10-20  5:00 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Greg Kroah-Hartman, Mark Brown, alsa-devel, Sagar Dharia, bp,
	poeschel, Thierry Reding, gong.chen, andreas.noever, alan,
	Mathieu Poirier, daniel, Jiri Kosina, sharon.dvir1, joe,
	David Miller, james.hogan, michael.opdenacker, Rob Herring,
	Pawel Moll, Mark Rutland, devicetree, lkml, kheitke,
	linux-arm-msm

On Wed 18 Oct 09:39 PDT 2017, Srinivas Kandagatla wrote:

> Thanks for Review Comments,
>
>
> On 18/10/17 07:15, Bjorn Andersson wrote:
> > On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:
[..]
> >
> > > + if (!async) {
> > > + txn->msg->comp_cb = NULL;
> > > + txn->msg->ctx = NULL;
> >
> > I believe txn->msg is always required, so you don't need to do this
> > contidionally.
>
> I don't get this, why do you want to set comp_cb to NULL unconditionally?
>

I'm just not happy about the complexity of this function, but perhaps
it's confusing to always set them, regardless of them being used. Feel
free to keep it.

[..]
> > > +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];
> >
> > Why is this doing struct copy?
> >
> Not sure, do you see any issue with this?
>

It's a rarely used feature and I don't see a reason for using it here.

It's probably better to make a copy of cur.cb and cur.ctx to make their
use after the spin-unlock more obvious (but should be fine as the
spinlock is for the pending_wr array.

> > > + 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);
[..]
> > > +/**
> > > + * 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
> > > + */
> >
> > Is this ringbuffer mechanism defined in the slimbus specification? Looks
> > like something specific to the Qualcomm controller, rather than
> > something that should be enforced in the framework.
> >
>
> Yes, this is not part of the slimbus specs, but Qcom SOCs have concept of
> Message Queues.
>
> Are you suggesting that this buffer handling has to be moved out of core
> into controller driver?
>

The fact that this seems to describe a physical ring buffer, with some
set of properties that are related to how a ring buffer works in the
Qualcomm hardware and it carries a notion of physical mapping, all
indicates to me that this describes some Qualcomm hardware interface.

I believe this is a hardware implementation detail that should reside in
the hardware part of the implementation (i.e. the Qualcomm driver).

>
> > > +struct slim_ctrl_buf {
> > > + void *base;
> > > + phys_addr_t phy;
> > > + spinlock_t lock;
> > > + int head;
> > > + int tail;
> > > + int sl_sz;
> > > + int n;
> > > +};

Regards,
Bjorn

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

* Re: [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework
@ 2017-10-20  5:00           ` Bjorn Andersson
  0 siblings, 0 replies; 87+ messages in thread
From: Bjorn Andersson @ 2017-10-20  5:00 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Greg Kroah-Hartman, Mark Brown, alsa-devel, Sagar Dharia, bp,
	poeschel, Thierry Reding, gong.chen, andreas.noever, alan,
	Mathieu Poirier, daniel, Jiri Kosina, sharon.dvir1, joe,
	David Miller, james.hogan, michael.opdenacker, Rob Herring,
	Pawel Moll, Mark Rutland, devicetree, lkml, kheitke,
	linux-arm-msm, Arnd Bergmann

On Wed 18 Oct 09:39 PDT 2017, Srinivas Kandagatla wrote:

> Thanks for Review Comments,
>
>
> On 18/10/17 07:15, Bjorn Andersson wrote:
> > On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:
[..]
> >
> > > + if (!async) {
> > > + txn->msg->comp_cb = NULL;
> > > + txn->msg->ctx = NULL;
> >
> > I believe txn->msg is always required, so you don't need to do this
> > contidionally.
>
> I don't get this, why do you want to set comp_cb to NULL unconditionally?
>

I'm just not happy about the complexity of this function, but perhaps
it's confusing to always set them, regardless of them being used. Feel
free to keep it.

[..]
> > > +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];
> >
> > Why is this doing struct copy?
> >
> Not sure, do you see any issue with this?
>

It's a rarely used feature and I don't see a reason for using it here.

It's probably better to make a copy of cur.cb and cur.ctx to make their
use after the spin-unlock more obvious (but should be fine as the
spinlock is for the pending_wr array.

> > > + 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);
[..]
> > > +/**
> > > + * 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
> > > + */
> >
> > Is this ringbuffer mechanism defined in the slimbus specification? Looks
> > like something specific to the Qualcomm controller, rather than
> > something that should be enforced in the framework.
> >
>
> Yes, this is not part of the slimbus specs, but Qcom SOCs have concept of
> Message Queues.
>
> Are you suggesting that this buffer handling has to be moved out of core
> into controller driver?
>

The fact that this seems to describe a physical ring buffer, with some
set of properties that are related to how a ring buffer works in the
Qualcomm hardware and it carries a notion of physical mapping, all
indicates to me that this describes some Qualcomm hardware interface.

I believe this is a hardware implementation detail that should reside in
the hardware part of the implementation (i.e. the Qualcomm driver).

>
> > > +struct slim_ctrl_buf {
> > > + void *base;
> > > + phys_addr_t phy;
> > > + spinlock_t lock;
> > > + int head;
> > > + int tail;
> > > + int sl_sz;
> > > + int n;
> > > +};

Regards,
Bjorn

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

* Re: [Patch v6 6/7] regmap: add SLIMBUS support
  2017-10-06 15:51 ` [Patch v6 6/7] regmap: add SLIMBUS support srinivas.kandagatla
@ 2017-10-20  5:00     ` Bjorn Andersson
  2017-10-20  5:00     ` Bjorn Andersson
  1 sibling, 0 replies; 87+ messages in thread
From: Bjorn Andersson @ 2017-10-20  5:00 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Rutland, alsa-devel, Michael Opdenacker, poeschel,
	Andreas Noever, gong.chen, Arnd Bergmann, kheitke,
	Thierry Reding, devicetree, james.hogan, Pawel Moll,
	linux-arm-msm, sharon.dvir1, Rob Herring, Sagar Dharia, alan, bp,
	Mathieu Poirier, Greg Kroah-Hartman, lkml, Mark Brown,
	Daniel Vetter, Jiri Kosina, joe, David Miller

On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:

> diff --git a/drivers/base/regmap/regmap-slimbus.c b/drivers/base/regmap/regmap-slimbus.c
[..]
> +static int regmap_slimbus_byte_reg_read(void *context, unsigned int reg,
> + unsigned int *val)
> +{
> + struct slim_device *slim = context;
> + struct slim_val_inf msg = {0,};
> +
> + msg.start_offset = reg;
> + msg.num_bytes = 1;
> + msg.rbuf = (void *)val;

Turn rbuf into a void * and you don't need this cast (think I commented
on this on a previous patch as well).

> +
> + return slim_request_val_element(slim, &msg);
> +}
> +
> +static int regmap_slimbus_byte_reg_write(void *context, unsigned int reg,
> + unsigned int val)
> +{
> + struct slim_device *slim = context;
> + struct slim_val_inf msg = {0,};
> +
> + msg.start_offset = reg;
> + msg.num_bytes = 1;
> + msg.wbuf = (void *)&val;

Dito

> +
> + return slim_change_val_element(slim, &msg);
> +}

Regards,
Bjorn

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

* Re: [Patch v6 6/7] regmap: add SLIMBUS support
@ 2017-10-20  5:00     ` Bjorn Andersson
  0 siblings, 0 replies; 87+ messages in thread
From: Bjorn Andersson @ 2017-10-20  5:00 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Greg Kroah-Hartman, Mark Brown, alsa-devel, Sagar Dharia, bp,
	poeschel, Thierry Reding, gong.chen, Andreas Noever, alan,
	Mathieu Poirier, Daniel Vetter, Jiri Kosina, sharon.dvir1, joe,
	David Miller, james.hogan, Michael Opdenacker, Rob Herring,
	Pawel Moll, Mark Rutland, devicetree, lkml, kheitke,
	linux-arm-msm, Arnd Bergmann

On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:

> diff --git a/drivers/base/regmap/regmap-slimbus.c b/drivers/base/regmap/regmap-slimbus.c
[..]
> +static int regmap_slimbus_byte_reg_read(void *context, unsigned int reg,
> + unsigned int *val)
> +{
> + struct slim_device *slim = context;
> + struct slim_val_inf msg = {0,};
> +
> + msg.start_offset = reg;
> + msg.num_bytes = 1;
> + msg.rbuf = (void *)val;

Turn rbuf into a void * and you don't need this cast (think I commented
on this on a previous patch as well).

> +
> + return slim_request_val_element(slim, &msg);
> +}
> +
> +static int regmap_slimbus_byte_reg_write(void *context, unsigned int reg,
> + unsigned int val)
> +{
> + struct slim_device *slim = context;
> + struct slim_val_inf msg = {0,};
> +
> + msg.start_offset = reg;
> + msg.num_bytes = 1;
> + msg.wbuf = (void *)&val;

Dito

> +
> + return slim_change_val_element(slim, &msg);
> +}

Regards,
Bjorn

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

* Re: [Patch v6 7/7] MAINTAINERS: Add SLIMbus maintainer
  2017-10-06 15:51     ` srinivas.kandagatla
@ 2017-10-20  5:00       ` Bjorn Andersson
  -1 siblings, 0 replies; 87+ messages in thread
From: Bjorn Andersson @ 2017-10-20  5:00 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Mark Rutland, alsa-devel, Michael Opdenacker, poeschel,
	Andreas Noever, gong.chen, Arnd Bergmann, kheitke,
	Thierry Reding, devicetree, james.hogan, Pawel Moll,
	linux-arm-msm, sharon.dvir1, Rob Herring, Sagar Dharia, alan, bp,
	Mathieu Poirier, Greg Kroah-Hartman, lkml, Mark Brown,
	Daniel Vetter, Jiri Kosina, joe, David Miller

On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:

> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>
> Add myself as maintainer for slimbus.
>

Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org>

Regards,
Bjorn

> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  MAINTAINERS | 8 ++++++++
>  1 file changed, 8 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 2281af4..014f74b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12320,6 +12320,14 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
>  F: include/linux/srcu.h
>  F: kernel/rcu/srcu.c
>
> +SERIAL LOW-POWER INTER-CHIP MEDIA BUS (SLIMbus)
> +M: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> +L: alsa-devel@alsa-project.org (moderated for non-subscribers)
> +S: Maintained
> +F: drivers/slimbus/
> +F: Documentation/devicetree/bindings/slimbus/
> +F: include/linux/slimbus.h
> +
>  SMACK SECURITY MODULE
>  M: Casey Schaufler <casey@schaufler-ca.com>
>  L: linux-security-module@vger.kernel.org
> --
> 2.9.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [Patch v6 7/7] MAINTAINERS: Add SLIMbus maintainer
@ 2017-10-20  5:00       ` Bjorn Andersson
  0 siblings, 0 replies; 87+ messages in thread
From: Bjorn Andersson @ 2017-10-20  5:00 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: Greg Kroah-Hartman, Mark Brown, alsa-devel, Sagar Dharia, bp,
	poeschel, Thierry Reding, gong.chen, Andreas Noever, alan,
	Mathieu Poirier, Daniel Vetter, Jiri Kosina, sharon.dvir1, joe,
	David Miller, james.hogan, Michael Opdenacker, Rob Herring,
	Pawel Moll, Mark Rutland, devicetree, lkml, kheitke,
	linux-arm-msm, Arnd Bergmann

On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:

> From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
>
> Add myself as maintainer for slimbus.
>

Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org>

Regards,
Bjorn

> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> ---
>  MAINTAINERS | 8 ++++++++
>  1 file changed, 8 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 2281af4..014f74b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -12320,6 +12320,14 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git
>  F: include/linux/srcu.h
>  F: kernel/rcu/srcu.c
>
> +SERIAL LOW-POWER INTER-CHIP MEDIA BUS (SLIMbus)
> +M: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> +L: alsa-devel@alsa-project.org (moderated for non-subscribers)
> +S: Maintained
> +F: drivers/slimbus/
> +F: Documentation/devicetree/bindings/slimbus/
> +F: include/linux/slimbus.h
> +
>  SMACK SECURITY MODULE
>  M: Casey Schaufler <casey@schaufler-ca.com>
>  L: linux-security-module@vger.kernel.org
> --
> 2.9.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-06 15:51   ` srinivas.kandagatla
                     ` (3 preceding siblings ...)
  (?)
@ 2017-10-23  9:06   ` Mark Brown
  -1 siblings, 0 replies; 87+ messages in thread
From: Mark Brown @ 2017-10-23  9:06 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, alsa-devel, sdharia, bp, poeschel, treding, gong.chen,
	andreas.noever, alan, mathieu.poirier, daniel, jkosina,
	sharon.dvir1, joe, davem, james.hogan, michael.opdenacker,
	robh+dt, pawel.moll, mark.rutland, devicetree, linux-kernel,
	kheitke, linux-arm-msm, arnd

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

On Fri, Oct 06, 2017 at 05:51:30PM +0200, srinivas.kandagatla@linaro.org wrote:

> ---
>  Documentation/devicetree/bindings/slimbus/bus.txt |  57 ++
>  Documentation/slimbus/summary                     | 109 ++++

This is a 40k patch which is a bit offputting for review.  Splitting the
docs out would help this a bit.

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

We silently don't boot a device if it hasn't got a driver - is that the
right thing?  It feels like the silencing should be in the calling
function.

> +ret_assigned_laddr:
> +	mutex_unlock(&ctrl->m_ctrl);
> +	if (exists || ret)
> +		return ret;
> +
> +	dev_info(&ctrl->dev, "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);

dev_dbg()?

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

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-06 15:51   ` srinivas.kandagatla
@ 2017-10-25  0:16       ` Stephen Boyd
  -1 siblings, 0 replies; 87+ messages in thread
From: Stephen Boyd @ 2017-10-25  0:16 UTC (permalink / raw)
  To: srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A
  Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	sdharia-sgV2jX0FEOL9JmXXK+q4OQ, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, treding-DDmLM1+adcrQT0dZR+AlfA,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	jkosina-AlSwsSmVLrQ, sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kheitke-hxvC4TZJLZFWk0Htik3J/w,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4

On 10/06, srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org wrote:
> +
> +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";

Please remove "_clk" from here.

> +		#address-cells = <2>;
> +		#size-cells = <0>;
> +
> +		codec: wcd9310@1{
> +			compatible = "slim217,60"";
> +			reg = <1 0>;
> +		};
> +	};

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
--
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] 87+ messages in thread

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-10-25  0:16       ` Stephen Boyd
  0 siblings, 0 replies; 87+ messages in thread
From: Stephen Boyd @ 2017-10-25  0:16 UTC (permalink / raw)
  To: srinivas.kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

On 10/06, srinivas.kandagatla@linaro.org wrote:
> +
> +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";

Please remove "_clk" from here.

> +		#address-cells = <2>;
> +		#size-cells = <0>;
> +
> +		codec: wcd9310@1{
> +			compatible = "slim217,60"";
> +			reg = <1 0>;
> +		};
> +	};

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

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

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
  2017-10-18 16:38         ` Srinivas Kandagatla
@ 2017-11-01 23:08             ` Bjorn Andersson
  -1 siblings, 0 replies; 87+ messages in thread
From: Bjorn Andersson @ 2017-11-01 23:08 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	broonie-DgEjT+Ai2ygdnm+yROfE0A,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	sdharia-sgV2jX0FEOL9JmXXK+q4OQ, bp-l3A5Bk7waGM,
	poeschel-Xtl8qvBWbHwb1SvskN2V4Q, treding-DDmLM1+adcrQT0dZR+AlfA,
	gong.chen-VuQAYsv1563Yd54FQh9/CA,
	andreas.noever-Re5JQEeQqe8AvxtiuMwx3w,
	alan-VuQAYsv1563Yd54FQh9/CA,
	mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A, daniel-/w4YWyX8dFk,
	jkosina-AlSwsSmVLrQ, sharon.dvir1-MQgwKvJRKlGYZoqfULhbRA,
	joe-6d6DIl74uiNBDgjK7y7TUQ, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	james.hogan-1AXoQHu6uovQT0dZR+AlfA,
	michael.opdenacker-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kheitke-hxvC4TZJLZFWk0Htik3J/w,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4

On Wed 18 Oct 09:38 PDT 2017, Srinivas Kandagatla wrote:
> On 17/10/17 07:23, Bjorn Andersson wrote:
> > On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org wrote:
[..]
> > > +static int slim_device_remove(struct device *dev)
> > > +{
> > > +	struct slim_device *sbdev;
> > > +	struct slim_driver *sbdrv;
> > > +	int status = 0;
> > > +
> > > +	sbdev = to_slim_device(dev);
> > > +	if (!dev->driver)
> > > +		return 0;
> > > +
> > > +	sbdrv = to_slim_driver(dev->driver);
> > > +	if (sbdrv->remove)
> > > +		status = sbdrv->remove(sbdev);
> > > +
> > > +	mutex_lock(&sbdev->report_lock);
> > > +	sbdev->notified = false;
> > > +	if (status == 0)
> > > +		sbdev->driver = NULL;
> > > +	mutex_unlock(&sbdev->report_lock);
> > > +	return status;
> > 
> > device_unregister() will call device_del() which will end up in
> > __device_release_driver() which will call this function. Upon returning
> > from this function the core expect the bus to have cleaned up after the
> > dev (normally by calling drv->remove(dev)).
> > 
> > It will completely ignore the return value and continue tearing down the
> > rest of the core resources, e.g. three lines down it will
> > devres_release_all().
> > 
> > 
> > So you have the option of sleeping, while waiting for stuff to be
> > aborted/finished and then you need to clean things up.
> > 
> > The slim_device object itself will stick around until all references are
> > dropped though.
> 
> So you are suggesting that we make slim_driver remove not return anything?
> 

Yes.

Having the opportunity of failing remove() causes driver writers to
expect that they can fail, with the result of things not being torn
down properly.

Note that right after remove() returns devm_* resources will be
released, so anything that is not torn down and for some reason later
access allocated resources would cause issues.

Regards,
Bjorn
--
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] 87+ messages in thread

* Re: [Patch v6 1/7] slimbus: Device management on SLIMbus
@ 2017-11-01 23:08             ` Bjorn Andersson
  0 siblings, 0 replies; 87+ messages in thread
From: Bjorn Andersson @ 2017-11-01 23:08 UTC (permalink / raw)
  To: Srinivas Kandagatla
  Cc: gregkh, broonie, alsa-devel, sdharia, bp, poeschel, treding,
	gong.chen, andreas.noever, alan, mathieu.poirier, daniel,
	jkosina, sharon.dvir1, joe, davem, james.hogan,
	michael.opdenacker, robh+dt, pawel.moll, mark.rutland,
	devicetree, linux-kernel, kheitke, linux-arm-msm, arnd

On Wed 18 Oct 09:38 PDT 2017, Srinivas Kandagatla wrote:
> On 17/10/17 07:23, Bjorn Andersson wrote:
> > On Fri 06 Oct 08:51 PDT 2017, srinivas.kandagatla@linaro.org wrote:
[..]
> > > +static int slim_device_remove(struct device *dev)
> > > +{
> > > +	struct slim_device *sbdev;
> > > +	struct slim_driver *sbdrv;
> > > +	int status = 0;
> > > +
> > > +	sbdev = to_slim_device(dev);
> > > +	if (!dev->driver)
> > > +		return 0;
> > > +
> > > +	sbdrv = to_slim_driver(dev->driver);
> > > +	if (sbdrv->remove)
> > > +		status = sbdrv->remove(sbdev);
> > > +
> > > +	mutex_lock(&sbdev->report_lock);
> > > +	sbdev->notified = false;
> > > +	if (status == 0)
> > > +		sbdev->driver = NULL;
> > > +	mutex_unlock(&sbdev->report_lock);
> > > +	return status;
> > 
> > device_unregister() will call device_del() which will end up in
> > __device_release_driver() which will call this function. Upon returning
> > from this function the core expect the bus to have cleaned up after the
> > dev (normally by calling drv->remove(dev)).
> > 
> > It will completely ignore the return value and continue tearing down the
> > rest of the core resources, e.g. three lines down it will
> > devres_release_all().
> > 
> > 
> > So you have the option of sleeping, while waiting for stuff to be
> > aborted/finished and then you need to clean things up.
> > 
> > The slim_device object itself will stick around until all references are
> > dropped though.
> 
> So you are suggesting that we make slim_driver remove not return anything?
> 

Yes.

Having the opportunity of failing remove() causes driver writers to
expect that they can fail, with the result of things not being torn
down properly.

Note that right after remove() returns devm_* resources will be
released, so anything that is not torn down and for some reason later
access allocated resources would cause issues.

Regards,
Bjorn

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

end of thread, other threads:[~2017-11-01 23:08 UTC | newest]

Thread overview: 87+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-06 15:51 [Patch v6 0/7] Introduce framework for SLIMbus device drivers srinivas.kandagatla
2017-10-06 15:51 ` [Patch v6 1/7] slimbus: Device management on SLIMbus srinivas.kandagatla
2017-10-06 15:51   ` srinivas.kandagatla
2017-10-10 10:05   ` Charles Keepax
2017-10-10 10:05     ` [alsa-devel] " Charles Keepax
2017-10-10 12:34     ` Srinivas Kandagatla
2017-10-10 12:34       ` [alsa-devel] " Srinivas Kandagatla
2017-10-10 12:56       ` Charles Keepax
2017-10-10 12:56         ` [alsa-devel] " Charles Keepax
2017-10-11 10:23       ` Mark Brown
2017-10-11 10:23         ` [alsa-devel] " Mark Brown
     [not found]   ` <20171006155136.4682-2-srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2017-10-07  4:14     ` Jonathan Neuschäfer
2017-10-07  4:14       ` Jonathan Neuschäfer
2017-10-07 10:24       ` Srinivas Kandagatla
2017-10-07 10:24         ` Srinivas Kandagatla
2017-10-10 10:45     ` [alsa-devel] " Vinod Koul
2017-10-10 10:45       ` Vinod Koul
2017-10-10 12:34       ` Srinivas Kandagatla
2017-10-10 16:49         ` Vinod Koul
2017-10-10 16:49           ` [alsa-devel] " Vinod Koul
2017-10-10 17:21           ` Srinivas Kandagatla
2017-10-11  4:07             ` Vinod Koul
2017-10-11  9:42               ` Srinivas Kandagatla
2017-10-11 10:21                 ` Vinod Koul
2017-10-11 11:23                   ` Srinivas Kandagatla
2017-10-13 19:26     ` Rob Herring
2017-10-13 19:26       ` Rob Herring
2017-10-16  9:28       ` Srinivas Kandagatla
2017-10-16  9:28         ` Srinivas Kandagatla
2017-10-17  6:23     ` Bjorn Andersson
2017-10-17  6:23       ` Bjorn Andersson
2017-10-18 16:38       ` Srinivas Kandagatla
2017-10-18 16:38         ` Srinivas Kandagatla
     [not found]         ` <1a1d2777-be69-98ca-afba-0ffd0e3dd80f-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2017-11-01 23:08           ` Bjorn Andersson
2017-11-01 23:08             ` Bjorn Andersson
2017-10-25  0:16     ` Stephen Boyd
2017-10-25  0:16       ` Stephen Boyd
2017-10-12 11:01   ` [alsa-devel] " Sanyog Kale
2017-10-12 13:26     ` Srinivas Kandagatla
2017-10-12 13:26       ` Srinivas Kandagatla
2017-10-23  9:06   ` Mark Brown
2017-10-06 15:51 ` [Patch v6 3/7] slimbus: qcom: Add Qualcomm Slimbus controller driver srinivas.kandagatla
2017-10-07  7:45   ` Jonathan Neuschäfer
2017-10-07 10:24     ` Srinivas Kandagatla
2017-10-07 10:24       ` Srinivas Kandagatla
2017-10-13 19:17   ` Rob Herring
2017-10-16  9:28     ` Srinivas Kandagatla
2017-10-18  7:27   ` Bjorn Andersson
2017-10-18 16:39     ` Srinivas Kandagatla
2017-10-06 15:51 ` [Patch v6 4/7] slimbus: Add support for 'clock-pause' feature srinivas.kandagatla
2017-10-07  8:06   ` Jonathan Neuschäfer
2017-10-07 10:24     ` Srinivas Kandagatla
     [not found] ` <20171006155136.4682-1-srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2017-10-06 15:51   ` [Patch v6 2/7] slimbus: Add messaging APIs to slimbus framework srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A
2017-10-06 15:51     ` srinivas.kandagatla
2017-10-07  6:42     ` Jonathan Neuschäfer
2017-10-07 10:24       ` Srinivas Kandagatla
2017-10-07 10:24         ` Srinivas Kandagatla
2017-10-07 12:29         ` Jonathan Neuschäfer
2017-10-10 12:19     ` Charles Keepax
2017-10-10 12:19       ` [alsa-devel] " Charles Keepax
2017-10-10 13:01       ` Srinivas Kandagatla
2017-10-10 13:01         ` [alsa-devel] " Srinivas Kandagatla
2017-10-11  4:38     ` Vinod Koul
2017-10-11  7:53       ` Arnd Bergmann
2017-10-11  7:53         ` Arnd Bergmann
2017-10-11  9:42       ` Srinivas Kandagatla
2017-10-11  9:42         ` [alsa-devel] " Srinivas Kandagatla
     [not found]         ` <aa117cb8-ba59-894c-5a82-1b38facfa841-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2017-10-11 10:24           ` Vinod Koul
2017-10-11 10:24             ` Vinod Koul
2017-10-11 11:12             ` Srinivas Kandagatla
2017-10-18  6:15     ` Bjorn Andersson
2017-10-18 16:39       ` Srinivas Kandagatla
2017-10-20  5:00         ` Bjorn Andersson
2017-10-20  5:00           ` Bjorn Andersson
2017-10-06 15:51   ` [Patch v6 5/7] slimbus: qcom: Add runtime-pm support using clock-pause feature srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A
2017-10-06 15:51     ` srinivas.kandagatla
2017-10-07  8:22     ` Jonathan Neuschäfer
2017-10-07 10:25       ` Srinivas Kandagatla
2017-10-06 15:51   ` [Patch v6 7/7] MAINTAINERS: Add SLIMbus maintainer srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A
2017-10-06 15:51     ` srinivas.kandagatla
2017-10-20  5:00     ` Bjorn Andersson
2017-10-20  5:00       ` Bjorn Andersson
2017-10-06 15:51 ` [Patch v6 6/7] regmap: add SLIMBUS support srinivas.kandagatla
2017-10-07  5:02   ` Jonathan Neuschäfer
2017-10-07 10:25     ` Srinivas Kandagatla
2017-10-20  5:00   ` Bjorn Andersson
2017-10-20  5:00     ` Bjorn Andersson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.