All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC v2 0/5] System resource management
@ 2018-06-27  8:06 Fabien Dessenne
  2018-06-27  8:06 ` [RFC v2 1/5] dt-bindings: remoteproc: add system resource manager (SRM) Fabien Dessenne
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Fabien Dessenne @ 2018-06-27  8:06 UTC (permalink / raw)
  To: Bjorn Andersson, linux-remoteproc, Arnaud Pouliquen,
	Loic Pallardy, Oleksij
  Cc: Fabien Dessenne, Suman Anna, Alex Elder

This RFC follows discussions in OpenAMP meeting around a resource manager 
for the remote processor.
Here is a patchset that implements a system resource management.
By 'system resource' we speak about resource that is requested to operate
a peripheral, used by a remote processor.
Note: 'shared resource' may be a more appropriate term than 'system resource'.
These 'system resources' can introduce potential contention if they are not
controlled by a master core.

The system resource manager configures and enables the following identified
'system resources':
- Clocks
- GPIOs
- Regulators
- Interrupts

Theses 'system resources' are declared in devicetree sub-nodes of the resource
manager. A subnode corresponds to one peripheral used by the remote processor.
For each peripheral a generic driver can be probed to handle the 'system
resource'. Developers can also define their own driver for more complexe use
cases.

In this second patchset the configuration of the 'system resources' is not only
static : it can be updated during runtime ('dynamic' resource management).
The implementation of this part relies on an an RPMSG service and offers the
remote processor the ability to get and set the following configurations:
- Clock enable / disable
- Clock rate
- Regulator enable / disable
- Regulator voltage (min/max, current)
- Pin state configuration
The proposed implementation allows to extend the list of 'system resources' and
configurations.

Notes:
1- This patchset depends on the "remoteproc: updates for new events" patchset
   submitted by Alex Elder [https://lkml.org/lkml/2018/6/25/1081]
2- This proposed implementation deals with 'system resources', not with
   peripheral: hence it does not propose to assign a peripheral from one core
   to another core during runtime.
3- See also TI proposed solution:
   [http://git.omapzoom.org/?p=repo/sysbios-rpmsg.git;a=summary]

v2 changes :
- Add interrupts as a 'system resource'
- Add RPMSG-based 'dynamic' resource management

Fabien Dessenne (5):
  dt-bindings: remoteproc: add system resource manager (SRM)
  remoteproc: add system resource manager core
  remoteproc: add system resource manager device
  remoteproc: probe the system resource manager
  remoteproc: srm: introduce dynamic resource manager

 .../devicetree/bindings/remoteproc/rproc-srm.txt   |  54 ++
 Documentation/remoteproc.txt                       |  23 +
 drivers/remoteproc/Kconfig                         |  18 +
 drivers/remoteproc/Makefile                        |   2 +
 drivers/remoteproc/remoteproc_core.c               |   8 +
 drivers/remoteproc/rproc_srm_core.c                | 295 +++++++
 drivers/remoteproc/rproc_srm_core.h                | 113 +++
 drivers/remoteproc/rproc_srm_dev.c                 | 890 +++++++++++++++++++++
 8 files changed, 1403 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/remoteproc/rproc-srm.txt
 create mode 100644 drivers/remoteproc/rproc_srm_core.c
 create mode 100644 drivers/remoteproc/rproc_srm_core.h
 create mode 100644 drivers/remoteproc/rproc_srm_dev.c

-- 
2.7.4

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

* [RFC v2 1/5] dt-bindings: remoteproc: add system resource manager (SRM)
  2018-06-27  8:06 [RFC v2 0/5] System resource management Fabien Dessenne
@ 2018-06-27  8:06 ` Fabien Dessenne
  2018-06-27  8:06 ` [RFC v2 2/5] remoteproc: add system resource manager core Fabien Dessenne
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Fabien Dessenne @ 2018-06-27  8:06 UTC (permalink / raw)
  To: Bjorn Andersson, linux-remoteproc, Arnaud Pouliquen,
	Loic Pallardy, Oleksij
  Cc: Fabien Dessenne, Suman Anna, Alex Elder

Add Documentation of devicetree bindings to support the remote processor
system resource manager.

Signed-off-by: Fabien Dessenne <fabien.dessenne@st.com>
---
 .../devicetree/bindings/remoteproc/rproc-srm.txt   | 54 ++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/remoteproc/rproc-srm.txt

diff --git a/Documentation/devicetree/bindings/remoteproc/rproc-srm.txt b/Documentation/devicetree/bindings/remoteproc/rproc-srm.txt
new file mode 100644
index 0000000..a17208e
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/rproc-srm.txt
@@ -0,0 +1,54 @@
+Remoteproc System Resource Manager
+----------------------------------
+
+The remoteproc SRM (System Resource Manager) handles resources allocated
+to remote processors.
+This makes it possible for remote proc to reserve and initialize system
+resources for a peripheral assigned to a coprocessor.
+
+The devices are grouped in a core node
+
+Core
+====
+Required properties:
+- compatible: should be "rproc-srm-core"
+
+Dev
+===
+Required properties:
+- compatible: should be "rproc-srm-dev"
+
+Optional properties:
+- clocks: clocks required by the coprocessor
+- clock-names: see clock-bindings.txt
+- assigned-clocks: see clock-bindings.txt
+- assigned-clock-parents: see clock-bindings.txt
+- assigned-clock-rates: see clock-bindings.txt
+- pinctrl-x: pins configurations required by the coprocessor
+- pinctrl-names: see pinctrl-bindings.txt
+- x-supply: power supplies required by the coprocessor
+- interrupts: see interrupts.txt
+- interrupt-parent: see interrupts.txt
+- interrupt-names: see interrupts.txt
+
+Example:
+	system_resources {
+		compatible = "rproc-srm-core";
+
+		mmc0: sdhci@09060000 {
+			compatible = "rproc-srm-dev";
+			pinctrl-names = "default", "idle";
+			pinctrl-0 = <&pinctrl_mmc0>;
+			pinctrl-1 = <&pinctrl_mmc1>;
+			clock-names = "mmc", "icn";
+			clocks = <&clk_s_c0_flexgen CLK_MMC_0>,
+				 <&clk_s_c0_flexgen CLK_RX_ICN_HVA>;
+			vdda-supply = <&vdda>;
+		};
+		button {
+			compatible = "rproc-srm-dev";
+			interrupt-parent = <&gpioa>;
+			interrupts = <5 1>;
+			interrupt-names = "gpio_key";
+		};
+	};
-- 
2.7.4

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

* [RFC v2 2/5] remoteproc: add system resource manager core
  2018-06-27  8:06 [RFC v2 0/5] System resource management Fabien Dessenne
  2018-06-27  8:06 ` [RFC v2 1/5] dt-bindings: remoteproc: add system resource manager (SRM) Fabien Dessenne
@ 2018-06-27  8:06 ` Fabien Dessenne
  2018-06-27  8:06 ` [RFC v2 3/5] remoteproc: add system resource manager device Fabien Dessenne
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Fabien Dessenne @ 2018-06-27  8:06 UTC (permalink / raw)
  To: Bjorn Andersson, linux-remoteproc, Arnaud Pouliquen,
	Loic Pallardy, Oleksij
  Cc: Fabien Dessenne, Suman Anna, Alex Elder

The remoteproc SRM (System Resource Manager) handles resources allocated
to remote processors.
This makes it possible for remote proc to reserve and initialize system
resources for a peripheral assigned to a coprocessor.
This is the core part which is in charge of controlling the device
children.

Signed-off-by: Fabien Dessenne <fabien.dessenne@st.com>
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Signed-off-by: Loic Pallardy <loic.pallardy@st.com>
---
 Documentation/remoteproc.txt        |  23 ++++
 drivers/remoteproc/Kconfig          |   8 ++
 drivers/remoteproc/Makefile         |   1 +
 drivers/remoteproc/rproc_srm_core.c | 205 ++++++++++++++++++++++++++++++++++++
 4 files changed, 237 insertions(+)
 create mode 100644 drivers/remoteproc/rproc_srm_core.c

diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt
index 77fb03a..bec2177 100644
--- a/Documentation/remoteproc.txt
+++ b/Documentation/remoteproc.txt
@@ -353,3 +353,26 @@ Of course, RSC_VDEV resource entries are only good enough for static
 allocation of virtio devices. Dynamic allocations will also be made possible
 using the rpmsg bus (similar to how we already do dynamic allocations of
 rpmsg channels; read more about it in rpmsg.txt).
+
+8. System Resource Manager (SRM)
+
+Since some resources are shared (directly or not) between the processors, a
+processor cannot manage such resources without potentially impacting the other
+processors : as an example, if a processor changes the frequency of a clock, the
+frequency of another clock managed by another processor may be updated too.
+
+The System Resource Manager prevents such resource conflicts between the
+processors : it reserves and initializes the system resources of the peripherals
+assigned to a remote processor.
+
+As of today the following resources are controlled by the SRM:
+- clocks
+- gpios (pinctrl)
+- regulators (power supplies)
+
+The SRM is implemented as an 'rproc_subdev' and registered to remoteproc_core.
+Unlike the virtio device (vdev), the SRM subdev is probed *before* the rproc
+boots, ensuring the availability of the resources before the remoteproc starts.
+
+The resources handled by the SRM are defined in the DeviceTree: please read
+Documentation/devicetree/bindings/remoteproc/rproc-srm.txt for details.
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index cd1c168..e981831 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -13,6 +13,14 @@ config REMOTEPROC
 
 if REMOTEPROC
 
+config REMOTEPROC_SRM_CORE
+	tristate "Remoteproc System Resource Manager core"
+	help
+	  Say y here to enable the core driver of the remoteproc System Resource
+	  Manager (SRM).
+	  The SRM handles resources allocated to remote processors.
+	  The core part is in charge of controlling the device children.
+
 config IMX_REMOTEPROC
 	tristate "IMX6/7 remoteproc support"
 	depends on SOC_IMX6SX || SOC_IMX7D
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 02627ed..2be447a 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -9,6 +9,7 @@ remoteproc-y				+= remoteproc_debugfs.o
 remoteproc-y				+= remoteproc_sysfs.o
 remoteproc-y				+= remoteproc_virtio.o
 remoteproc-y				+= remoteproc_elf_loader.o
+obj-$(CONFIG_REMOTEPROC_SRM_CORE)	+= rproc_srm_core.o
 obj-$(CONFIG_IMX_REMOTEPROC)		+= imx_rproc.o
 obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
 obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
diff --git a/drivers/remoteproc/rproc_srm_core.c b/drivers/remoteproc/rproc_srm_core.c
new file mode 100644
index 0000000..29fcc73
--- /dev/null
+++ b/drivers/remoteproc/rproc_srm_core.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author: Fabien Dessenne <fabien.dessenne@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/component.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/remoteproc.h>
+
+#define BIND_TIMEOUT 10000
+
+struct rproc_srm_core {
+	struct device *dev;
+	struct completion all_bound;
+	int bind_status;
+	atomic_t prepared;
+	struct rproc_subdev subdev;
+};
+
+#define to_rproc_srm_core(s) container_of(s, struct rproc_srm_core, subdev)
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static void release_of(struct device *dev, void *data)
+{
+	of_node_put(data);
+}
+
+static void rproc_srm_core_unbind(struct device *dev)
+{
+	component_unbind_all(dev, NULL);
+}
+
+static int rproc_srm_core_bind(struct device *dev)
+{
+	struct rproc_srm_core *rproc_srm_core = dev_get_drvdata(dev);
+
+	rproc_srm_core->bind_status = component_bind_all(dev, NULL);
+	complete(&rproc_srm_core->all_bound);
+
+	return rproc_srm_core->bind_status;
+}
+
+static const struct component_master_ops srm_comp_ops = {
+	.bind = rproc_srm_core_bind,
+	.unbind = rproc_srm_core_unbind,
+};
+
+static int rproc_srm_core_prepare(struct rproc_subdev *subdev)
+{
+	struct rproc_srm_core *rproc_srm_core = to_rproc_srm_core(subdev);
+	struct device *dev = rproc_srm_core->dev;
+	struct device_node *node = dev->of_node;
+	struct device_node *child_np;
+	struct component_match *match = NULL;
+	int ret;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	init_completion(&rproc_srm_core->all_bound);
+
+	ret = devm_of_platform_populate(dev);
+	if (ret) {
+		dev_err(dev, "cannot populate node (%d)\n", ret);
+		return ret;
+	}
+
+	child_np = of_get_next_available_child(node, NULL);
+
+	while (child_np) {
+		of_node_get(child_np);
+		component_match_add_release(dev, &match, release_of, compare_of,
+					    child_np);
+		child_np = of_get_next_available_child(node, child_np);
+	}
+
+	if (!match) {
+		dev_dbg(dev, "No available child\n");
+		goto done;
+	}
+
+	ret = component_master_add_with_match(dev, &srm_comp_ops, match);
+	if (ret)
+		goto depopulate;
+
+	/* Wait for every child to be bound */
+	if (!wait_for_completion_timeout(&rproc_srm_core->all_bound,
+					 msecs_to_jiffies(BIND_TIMEOUT))) {
+		dev_err(dev, "bind timeout\n");
+		ret = -ETIMEDOUT;
+		goto master;
+	}
+
+	ret = rproc_srm_core->bind_status;
+	if (ret) {
+		dev_err(dev, "failed to bind\n");
+		goto master;
+	}
+done:
+	atomic_inc(&rproc_srm_core->prepared);
+
+	return 0;
+
+master:
+	component_master_del(dev, &srm_comp_ops);
+depopulate:
+	devm_of_platform_depopulate(dev);
+	return ret;
+}
+
+static void rproc_srm_core_do_unprepare(struct rproc_subdev *subdev)
+{
+	struct rproc_srm_core *rproc_srm_core = to_rproc_srm_core(subdev);
+	struct device *dev = rproc_srm_core->dev;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	atomic_dec(&rproc_srm_core->prepared);
+
+	component_master_del(dev, &srm_comp_ops);
+	devm_of_platform_depopulate(dev);
+}
+
+static int rproc_srm_core_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rproc *rproc = dev_get_drvdata(dev->parent);
+	struct rproc_srm_core *rproc_srm_core;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	rproc_srm_core = devm_kzalloc(dev, sizeof(struct rproc_srm_core),
+				      GFP_KERNEL);
+	if (!rproc_srm_core)
+		return -ENOMEM;
+
+	rproc_srm_core->dev = dev;
+
+	/* Register rproc subdevice with (un)prepare ops */
+	rproc_srm_core->subdev.prepare = rproc_srm_core_prepare;
+	rproc_srm_core->subdev.unprepare = rproc_srm_core_unprepare;
+	rproc_add_subdev(rproc, &rproc_srm_core->subdev);
+
+	dev_set_drvdata(dev, rproc_srm_core);
+
+	return 0;
+}
+
+static int rproc_srm_core_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rproc_srm_core *rproc_srm_core = dev_get_drvdata(dev);
+	struct rproc *rproc = dev_get_drvdata(dev->parent);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	if (atomic_read(&rproc->power) > 0)
+		dev_warn(dev, "Releasing resources while firmware running!\n");
+
+	if (atomic_read(&rproc_srm_core->prepared))
+		rproc_srm_core_unprepare(&rproc_srm_core->subdev);
+
+	return 0;
+}
+
+static const struct of_device_id rproc_srm_core_match[] = {
+	{ .compatible = "rproc-srm-core", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, rproc_srm_core_match);
+
+static struct platform_driver rproc_srm_core_driver = {
+	.probe = rproc_srm_core_probe,
+	.remove = rproc_srm_core_remove,
+	.driver = {
+		.name = "rproc-srm-core",
+		.of_match_table = of_match_ptr(rproc_srm_core_match),
+	},
+};
+
+module_platform_driver(rproc_srm_core_driver);
+
+MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
+MODULE_DESCRIPTION("Remoteproc System Resource Manager driver - core");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

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

* [RFC v2 3/5] remoteproc: add system resource manager device
  2018-06-27  8:06 [RFC v2 0/5] System resource management Fabien Dessenne
  2018-06-27  8:06 ` [RFC v2 1/5] dt-bindings: remoteproc: add system resource manager (SRM) Fabien Dessenne
  2018-06-27  8:06 ` [RFC v2 2/5] remoteproc: add system resource manager core Fabien Dessenne
@ 2018-06-27  8:06 ` Fabien Dessenne
  2018-06-27  8:06 ` [RFC v2 4/5] remoteproc: probe the system resource manager Fabien Dessenne
  2018-06-27  8:06 ` [RFC v2 5/5] remoteproc: srm: introduce dynamic " Fabien Dessenne
  4 siblings, 0 replies; 6+ messages in thread
From: Fabien Dessenne @ 2018-06-27  8:06 UTC (permalink / raw)
  To: Bjorn Andersson, linux-remoteproc, Arnaud Pouliquen,
	Loic Pallardy, Oleksij
  Cc: Fabien Dessenne, Suman Anna, Alex Elder

The SRM device reserves and enables the resources needed by a remote
processor.
In this initial commit the only managed resources are:
- clocks which are prepared and enabled
- pins which are configured
- regulators which are enabled
- interrupts which are prepared

Signed-off-by: Fabien Dessenne <fabien.dessenne@st.com>
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Signed-off-by: Loic Pallardy <loic.pallardy@st.com>
---
 drivers/remoteproc/Kconfig         |  10 +
 drivers/remoteproc/Makefile        |   1 +
 drivers/remoteproc/rproc_srm_dev.c | 570 +++++++++++++++++++++++++++++++++++++
 3 files changed, 581 insertions(+)
 create mode 100644 drivers/remoteproc/rproc_srm_dev.c

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index e981831..5d5cec9 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -21,6 +21,16 @@ config REMOTEPROC_SRM_CORE
 	  The SRM handles resources allocated to remote processors.
 	  The core part is in charge of controlling the device children.
 
+config REMOTEPROC_SRM_DEV
+	tristate "Remoteproc System Resource Manager device"
+	depends on REMOTEPROC_SRM_CORE
+	help
+	  Say y here to enable the device driver of the remoteproc System
+	  Resource Manager (SRM).
+	  The SRM handles resources allocated to remote processors.
+	  The device part is in charge of reserving and initializing resources
+	  for a peripheral assigned to a coprocessor.
+
 config IMX_REMOTEPROC
 	tristate "IMX6/7 remoteproc support"
 	depends on SOC_IMX6SX || SOC_IMX7D
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 2be447a..7cb767b 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -10,6 +10,7 @@ remoteproc-y				+= remoteproc_sysfs.o
 remoteproc-y				+= remoteproc_virtio.o
 remoteproc-y				+= remoteproc_elf_loader.o
 obj-$(CONFIG_REMOTEPROC_SRM_CORE)	+= rproc_srm_core.o
+obj-$(CONFIG_REMOTEPROC_SRM_DEV)	+= rproc_srm_dev.o
 obj-$(CONFIG_IMX_REMOTEPROC)		+= imx_rproc.o
 obj-$(CONFIG_OMAP_REMOTEPROC)		+= omap_remoteproc.o
 obj-$(CONFIG_WKUP_M3_RPROC)		+= wkup_m3_rproc.o
diff --git a/drivers/remoteproc/rproc_srm_dev.c b/drivers/remoteproc/rproc_srm_dev.c
new file mode 100644
index 0000000..4b2e6ac
--- /dev/null
+++ b/drivers/remoteproc/rproc_srm_dev.c
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author: Fabien Dessenne <fabien.dessenne@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/component.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#define SUPPLY_SUFFIX   "-supply"
+
+struct rproc_srm_clk_info {
+	struct list_head list;
+	unsigned int index;
+	struct clk *clk;
+	const char *name;
+	bool enabled;
+};
+
+struct rproc_srm_pin_info {
+	struct list_head list;
+	unsigned int index;
+	char *name;
+};
+
+struct rproc_srm_regu_info {
+	struct list_head list;
+	unsigned int index;
+	struct regulator *regu;
+	const char *name;
+	bool enabled;
+};
+
+struct rproc_srm_irq_info {
+	struct list_head list;
+	unsigned int index;
+	char *name;
+	int irq;
+	bool enabled;
+};
+
+struct rproc_srm_dev {
+	struct device *dev;
+	struct pinctrl *pctrl;
+
+	struct list_head clk_list_head;
+	struct list_head regu_list_head;
+	struct list_head pin_list_head;
+	struct list_head irq_list_head;
+};
+
+/* irqs */
+static void rproc_srm_dev_put_irqs(struct rproc_srm_dev *rproc_srm_dev)
+{
+	struct device *dev = rproc_srm_dev->dev;
+	struct rproc_srm_irq_info *info, *tmp;
+	struct list_head *irq_head = &rproc_srm_dev->irq_list_head;
+
+	list_for_each_entry_safe(info, tmp, irq_head, list) {
+		devm_free_irq(dev, info->irq, NULL);
+		dev_dbg(dev, "Put irq %d (%s)\n", info->irq, info->name);
+		list_del(&info->list);
+	}
+}
+
+static irqreturn_t rproc_srm_dev_irq_handler(int irq, void *dev)
+{
+	dev_warn(dev, "Spurious interrupt\n");
+	return IRQ_HANDLED;
+}
+
+static int rproc_srm_dev_get_irqs(struct rproc_srm_dev *rproc_srm_dev)
+{
+	struct device *dev = rproc_srm_dev->dev;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct device_node *np = dev->of_node;
+	struct rproc_srm_irq_info *info;
+	struct list_head *irq_head = &rproc_srm_dev->irq_list_head;
+	const char *name;
+	int nr, ret, irq;
+	unsigned int i;
+
+	if (!np)
+		return 0;
+
+	nr = platform_irq_count(pdev);
+	dev_dbg(dev, "irq count = %d\n", nr);
+	if (!nr)
+		return 0;
+
+	for (i = 0; i < nr; i++) {
+		info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+		if (!info) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		irq = platform_get_irq(pdev, i);
+		if (irq <= 0) {
+			ret = irq;
+			dev_err(dev, "Failed to get irq (%d)\n", ret);
+			goto err;
+		}
+
+		info->irq = irq;
+
+		/* Register a dummy irq handleras not used by Linux */
+		ret = devm_request_irq(dev, info->irq,
+				       rproc_srm_dev_irq_handler, 0,
+				       dev_name(dev), NULL);
+		if (ret) {
+			dev_err(dev, "Failed to request irq (%d)\n", ret);
+			goto err;
+		}
+
+		/*
+		 * Disable IRQ. Since it is used by the remote processor we
+		 * must not use the 'irq lazy disable' optimization
+		 */
+		irq_set_status_flags(info->irq, IRQ_DISABLE_UNLAZY);
+
+		if (!of_property_read_string_index(np, "interrupt-names", i,
+						   &name))
+			info->name = devm_kstrdup(dev, name, GFP_KERNEL);
+
+		info->index = i;
+
+		list_add_tail(&info->list, irq_head);
+		dev_dbg(dev, "Got irq %d (%s)\n", info->irq, info->name);
+	}
+
+	return 0;
+
+err:
+	rproc_srm_dev_put_irqs(rproc_srm_dev);
+
+	return ret;
+}
+
+/* Clocks */
+static void rproc_srm_dev_deconfig_clocks(struct rproc_srm_dev *rproc_srm_dev)
+{
+	struct rproc_srm_clk_info *c;
+	struct list_head *clk_head = &rproc_srm_dev->clk_list_head;
+
+	list_for_each_entry(c, clk_head, list) {
+		if (!c->enabled)
+			continue;
+
+		clk_disable_unprepare(c->clk);
+		c->enabled = false;
+		dev_dbg(rproc_srm_dev->dev, "clk %d (%s) deconfigured\n",
+			c->index, c->name);
+	}
+}
+
+static int rproc_srm_dev_config_clocks(struct rproc_srm_dev *rproc_srm_dev)
+{
+	struct rproc_srm_clk_info *c;
+	struct list_head *clk_head = &rproc_srm_dev->clk_list_head;
+	int ret;
+
+	/* Note: not only configuring, but also enabling */
+
+	list_for_each_entry(c, clk_head, list) {
+		if (c->enabled)
+			continue;
+
+		ret = clk_prepare_enable(c->clk);
+		if (ret) {
+			dev_err(rproc_srm_dev->dev, "clk %d (%s) cfg failed\n",
+				c->index, c->name);
+			rproc_srm_dev_deconfig_clocks(rproc_srm_dev);
+			return ret;
+		}
+		c->enabled = true;
+		dev_dbg(rproc_srm_dev->dev, "clk %d (%s) configured\n",
+			c->index, c->name);
+	}
+
+	return 0;
+}
+
+static void rproc_srm_dev_put_clocks(struct rproc_srm_dev *rproc_srm_dev)
+{
+	struct device *dev = rproc_srm_dev->dev;
+	struct rproc_srm_clk_info *c, *tmp;
+	struct list_head *clk_head = &rproc_srm_dev->clk_list_head;
+
+	list_for_each_entry_safe(c, tmp, clk_head, list) {
+		clk_put(c->clk);
+		dev_dbg(dev, "put clock %d (%s)\n", c->index, c->name);
+		list_del(&c->list);
+	}
+}
+
+static int rproc_srm_dev_get_clocks(struct rproc_srm_dev *rproc_srm_dev)
+{
+	struct device *dev = rproc_srm_dev->dev;
+	struct device_node *np = dev->of_node;
+	struct rproc_srm_clk_info *c;
+	struct list_head *clk_head = &rproc_srm_dev->clk_list_head;
+	const char *name;
+	int nb_c, ret;
+	unsigned int i;
+
+	if (!np)
+		return 0;
+
+	nb_c = of_clk_get_parent_count(np);
+	if (!nb_c)
+		return 0;
+
+	for (i = 0; i < nb_c; i++) {
+		c = devm_kzalloc(dev, sizeof(*c), GFP_KERNEL);
+		if (!c) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		c->clk = of_clk_get(np, i);
+		if (IS_ERR(c->clk)) {
+			dev_err(dev, "clock %d KO (%ld)\n", i,
+				PTR_ERR(c->clk));
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		if (!of_property_read_string_index(np, "clock-names", i,
+						   &name))
+			c->name = devm_kstrdup(dev, name, GFP_KERNEL);
+
+		c->index = i;
+
+		list_add_tail(&c->list, clk_head);
+		dev_dbg(dev, "got clock %d (%s)\n", c->index, c->name);
+	}
+
+	return 0;
+
+err:
+	rproc_srm_dev_put_clocks(rproc_srm_dev);
+	return ret;
+}
+
+/* Regulators */
+static void rproc_srm_dev_deconfig_regus(struct rproc_srm_dev *rproc_srm_dev)
+{
+	struct rproc_srm_regu_info *r;
+	struct list_head *regu_head = &rproc_srm_dev->regu_list_head;
+
+	list_for_each_entry(r, regu_head, list) {
+		if (!r->enabled)
+			continue;
+
+		regulator_disable(r->regu);
+		r->enabled = false;
+		dev_dbg(rproc_srm_dev->dev, "regu %d (%s) disabled\n",
+			r->index, r->name);
+	}
+}
+
+static int rproc_srm_dev_config_regus(struct rproc_srm_dev *rproc_srm_dev)
+{
+	struct rproc_srm_regu_info *r;
+	struct list_head *regu_head = &rproc_srm_dev->regu_list_head;
+	int ret;
+
+	/* Enable all the regulators */
+	list_for_each_entry(r, regu_head, list) {
+		if (r->enabled)
+			continue;
+
+		ret = regulator_enable(r->regu);
+		if (ret) {
+			dev_err(rproc_srm_dev->dev, "regu %d (%s) failed\n",
+				r->index, r->name);
+			rproc_srm_dev_deconfig_regus(rproc_srm_dev);
+			return ret;
+		}
+		r->enabled = true;
+		dev_dbg(rproc_srm_dev->dev, "regu %d (%s) enabled\n",
+			r->index, r->name);
+	}
+
+	return 0;
+}
+
+static void rproc_srm_dev_put_regus(struct rproc_srm_dev *rproc_srm_dev)
+{
+	struct device *dev = rproc_srm_dev->dev;
+	struct rproc_srm_regu_info *r, *tmp;
+	struct list_head *regu_head = &rproc_srm_dev->regu_list_head;
+
+	list_for_each_entry_safe(r, tmp, regu_head, list) {
+		devm_regulator_put(r->regu);
+		dev_dbg(dev, "put regu %d (%s)\n", r->index, r->name);
+		list_del(&r->list);
+	}
+}
+
+static int rproc_srm_dev_get_regus(struct rproc_srm_dev *rproc_srm_dev)
+{
+	struct device *dev = rproc_srm_dev->dev;
+	struct device_node *np = dev->of_node;
+	struct property *p;
+	const char *n;
+	char *name;
+	struct rproc_srm_regu_info *r;
+	struct list_head *regu_head = &rproc_srm_dev->regu_list_head;
+	int ret, nb_s = 0;
+
+	if (!np)
+		return 0;
+
+	for_each_property_of_node(np, p) {
+		n = strstr(p->name, SUPPLY_SUFFIX);
+		if (!n || n == p->name)
+			continue;
+
+		r = devm_kzalloc(dev, sizeof(*r), GFP_KERNEL);
+		if (!r) {
+			ret = -ENOMEM;
+			goto err_list;
+		}
+
+		name = devm_kstrdup(dev, p->name, GFP_KERNEL);
+		name[strlen(p->name) - strlen(SUPPLY_SUFFIX)] = '\0';
+		r->name = name;
+
+		r->regu = devm_regulator_get(dev, r->name);
+		if (IS_ERR(r->regu)) {
+			dev_err(dev, "cannot get regu %s\n", r->name);
+			ret = -EINVAL;
+			goto err_list;
+		}
+
+		r->index = nb_s++;
+
+		list_add_tail(&r->list, regu_head);
+		dev_dbg(dev, "got regu %d (%s)\n", r->index, r->name);
+	}
+
+	return 0;
+
+err_list:
+	rproc_srm_dev_put_regus(rproc_srm_dev);
+	return ret;
+}
+
+/* Pins */
+static void rproc_srm_dev_put_pins(struct rproc_srm_dev *rproc_srm_dev)
+{
+	struct device *dev = rproc_srm_dev->dev;
+	struct rproc_srm_pin_info *p, *tmp;
+	struct list_head *pin_head = &rproc_srm_dev->pin_list_head;
+
+	list_for_each_entry_safe(p, tmp, pin_head, list) {
+		devm_kfree(dev, p->name);
+		devm_kfree(dev, p);
+		dev_dbg(dev, "remove pin cfg %d (%s)\n", p->index, p->name);
+		list_del(&p->list);
+	}
+
+	if (!IS_ERR_OR_NULL(rproc_srm_dev->pctrl)) {
+		devm_pinctrl_put(rproc_srm_dev->pctrl);
+		rproc_srm_dev->pctrl = NULL;
+	}
+}
+
+static int rproc_srm_dev_get_pins(struct rproc_srm_dev *rproc_srm_dev)
+{
+	struct device *dev = rproc_srm_dev->dev;
+	struct device_node *np = dev->of_node;
+	struct rproc_srm_pin_info *p;
+	struct list_head *pin_head = &rproc_srm_dev->pin_list_head;
+	int ret, nb_p;
+	unsigned int i;
+	const char *name;
+
+	if (!np)
+		return 0;
+
+	/* Assumption here is that "default" pinctrl applied before probe */
+
+	rproc_srm_dev->pctrl = devm_pinctrl_get(dev);
+	if (IS_ERR(rproc_srm_dev->pctrl))
+		return 0;
+
+	nb_p = of_property_count_strings(np, "pinctrl-names");
+	if (nb_p <= 0) {
+		dev_err(dev, "pinctrl-names not defined\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	for (i = 0; i < nb_p; i++) {
+		p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
+		if (!p) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		if (of_property_read_string_index(np, "pinctrl-names", i,
+						  &name)) {
+			dev_err(dev, "no pinctrl-names (pin %d)\n", i);
+			ret = -EINVAL;
+			goto err;
+		}
+		p->name = devm_kstrdup(dev, name, GFP_KERNEL);
+
+		p->index = i;
+
+		list_add_tail(&p->list, pin_head);
+		dev_dbg(dev, "found pin cfg %d (%s)\n", p->index, p->name);
+	}
+	return 0;
+
+err:
+	rproc_srm_dev_put_pins(rproc_srm_dev);
+	return ret;
+}
+
+/* Core */
+static void
+rproc_srm_dev_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct rproc_srm_dev *rproc_srm_dev = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	rproc_srm_dev_deconfig_regus(rproc_srm_dev);
+	rproc_srm_dev_deconfig_clocks(rproc_srm_dev);
+
+	/* For pins and IRQs: nothing to deconfigure */
+}
+
+static int
+rproc_srm_dev_bind(struct device *dev, struct device *master, void *data)
+{
+	struct rproc_srm_dev *rproc_srm_dev = dev_get_drvdata(dev);
+	int ret;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	ret = rproc_srm_dev_config_clocks(rproc_srm_dev);
+	if (ret)
+		return ret;
+
+	ret = rproc_srm_dev_config_regus(rproc_srm_dev);
+	if (ret)
+		return ret;
+
+	/* For pins and IRQs: nothing to configure */
+	return 0;
+}
+
+static const struct component_ops rproc_srm_dev_ops = {
+	.bind = rproc_srm_dev_bind,
+	.unbind = rproc_srm_dev_unbind,
+};
+
+static int rproc_srm_dev_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rproc_srm_dev *rproc_srm_dev;
+	int ret;
+
+	dev_dbg(dev, "%s for node %s\n", __func__, dev->of_node->name);
+
+	rproc_srm_dev = devm_kzalloc(dev, sizeof(struct rproc_srm_dev),
+				     GFP_KERNEL);
+	if (!rproc_srm_dev)
+		return -ENOMEM;
+
+	rproc_srm_dev->dev = dev;
+	INIT_LIST_HEAD(&rproc_srm_dev->clk_list_head);
+	INIT_LIST_HEAD(&rproc_srm_dev->pin_list_head);
+	INIT_LIST_HEAD(&rproc_srm_dev->regu_list_head);
+	INIT_LIST_HEAD(&rproc_srm_dev->irq_list_head);
+
+	/* Get clocks, regu, irqs and pinctrl */
+	ret = rproc_srm_dev_get_clocks(rproc_srm_dev);
+	if (ret)
+		return ret;
+
+	ret = rproc_srm_dev_get_regus(rproc_srm_dev);
+	if (ret)
+		goto err;
+
+	ret = rproc_srm_dev_get_pins(rproc_srm_dev);
+	if (ret)
+		goto err;
+
+	ret = rproc_srm_dev_get_irqs(rproc_srm_dev);
+	if (ret)
+		goto err;
+
+	dev_set_drvdata(dev, rproc_srm_dev);
+
+	return  component_add(dev, &rproc_srm_dev_ops);
+
+err:
+	rproc_srm_dev_put_irqs(rproc_srm_dev);
+	rproc_srm_dev_put_pins(rproc_srm_dev);
+	rproc_srm_dev_put_regus(rproc_srm_dev);
+	rproc_srm_dev_put_clocks(rproc_srm_dev);
+	return ret;
+}
+
+static int rproc_srm_dev_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rproc_srm_dev *rproc_srm_dev = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	component_del(dev, &rproc_srm_dev_ops);
+
+	rproc_srm_dev_put_irqs(rproc_srm_dev);
+	rproc_srm_dev_put_regus(rproc_srm_dev);
+	rproc_srm_dev_put_pins(rproc_srm_dev);
+	rproc_srm_dev_put_clocks(rproc_srm_dev);
+
+	return 0;
+}
+
+static const struct of_device_id rproc_srm_dev_match[] = {
+	{ .compatible = "rproc-srm-dev", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, rproc_srm_dev_match);
+
+static struct platform_driver rproc_srm_dev_driver = {
+	.probe = rproc_srm_dev_probe,
+	.remove = rproc_srm_dev_remove,
+	.driver = {
+		.name = "rproc-srm-dev",
+		.of_match_table = of_match_ptr(rproc_srm_dev_match),
+	},
+};
+
+module_platform_driver(rproc_srm_dev_driver);
+
+MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
+MODULE_DESCRIPTION("Remoteproc System Resource Manager driver - dev");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

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

* [RFC v2 4/5] remoteproc: probe the system resource manager
  2018-06-27  8:06 [RFC v2 0/5] System resource management Fabien Dessenne
                   ` (2 preceding siblings ...)
  2018-06-27  8:06 ` [RFC v2 3/5] remoteproc: add system resource manager device Fabien Dessenne
@ 2018-06-27  8:06 ` Fabien Dessenne
  2018-06-27  8:06 ` [RFC v2 5/5] remoteproc: srm: introduce dynamic " Fabien Dessenne
  4 siblings, 0 replies; 6+ messages in thread
From: Fabien Dessenne @ 2018-06-27  8:06 UTC (permalink / raw)
  To: Bjorn Andersson, linux-remoteproc, Arnaud Pouliquen,
	Loic Pallardy, Oleksij
  Cc: Fabien Dessenne, Suman Anna, Alex Elder

When a remote processor registers, populate the device tree to probe
the SRM core driver

Signed-off-by: Fabien Dessenne <fabien.dessenne@st.com>
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Signed-off-by: Loic Pallardy <loic.pallardy@st.com>
---
 drivers/remoteproc/remoteproc_core.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 283b258..a67f918 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -41,6 +41,7 @@
 #include <linux/crc32.h>
 #include <linux/virtio_ids.h>
 #include <linux/virtio_ring.h>
+#include <linux/of_platform.h>
 #include <asm/byteorder.h>
 
 #include "remoteproc_internal.h"
@@ -1485,6 +1486,11 @@ int rproc_add(struct rproc *rproc)
 			return ret;
 	}
 
+	/* add resource manager device */
+	ret = devm_of_platform_populate(dev->parent);
+	if (ret < 0)
+		return ret;
+
 	/* expose to rproc_get_by_phandle users */
 	mutex_lock(&rproc_list_mutex);
 	list_add(&rproc->node, &rproc_list);
@@ -1705,6 +1711,8 @@ int rproc_del(struct rproc *rproc)
 	list_del(&rproc->node);
 	mutex_unlock(&rproc_list_mutex);
 
+	of_platform_depopulate(rproc->dev.parent);
+
 	device_del(&rproc->dev);
 
 	return 0;
-- 
2.7.4

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

* [RFC v2 5/5] remoteproc: srm: introduce dynamic resource manager
  2018-06-27  8:06 [RFC v2 0/5] System resource management Fabien Dessenne
                   ` (3 preceding siblings ...)
  2018-06-27  8:06 ` [RFC v2 4/5] remoteproc: probe the system resource manager Fabien Dessenne
@ 2018-06-27  8:06 ` Fabien Dessenne
  4 siblings, 0 replies; 6+ messages in thread
From: Fabien Dessenne @ 2018-06-27  8:06 UTC (permalink / raw)
  To: Bjorn Andersson, linux-remoteproc, Arnaud Pouliquen,
	Loic Pallardy, Oleksij
  Cc: Fabien Dessenne, Suman Anna, Alex Elder

The dynamic resource manager allows the remote processor to configure
the system resources that were statically allocated and setup before
the processor was started.

It relies on an rpmsg link and offers the remote processor the ability
to get and set the following configurations:
- Clock enable / disable
- Clock rate
- Regulator enable / disable
- Regulator voltage (min/max, current)
- Pin state configuration
The proposed implementation allows to extend the list of resources and
configurations.

Signed-off-by: Fabien Dessenne <fabien.dessenne@st.com>
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Signed-off-by: Loic Pallardy <loic.pallardy@st.com>
---
 drivers/remoteproc/rproc_srm_core.c | 124 +++++++--
 drivers/remoteproc/rproc_srm_core.h | 113 ++++++++
 drivers/remoteproc/rproc_srm_dev.c  | 518 +++++++++++++++++++++++++++++-------
 3 files changed, 639 insertions(+), 116 deletions(-)
 create mode 100644 drivers/remoteproc/rproc_srm_core.h

diff --git a/drivers/remoteproc/rproc_srm_core.c b/drivers/remoteproc/rproc_srm_core.c
index 29fcc73..55eb13b 100644
--- a/drivers/remoteproc/rproc_srm_core.c
+++ b/drivers/remoteproc/rproc_srm_core.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
- * Author: Fabien Dessenne <fabien.dessenne@st.com>.
- *
- * License type: GPLv2
- *
- * 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.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
  */
 
 #include <linux/component.h>
@@ -22,6 +9,9 @@
 #include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/remoteproc.h>
+#include <linux/rpmsg.h>
+
+#include "rproc_srm_core.h"
 
 #define BIND_TIMEOUT 10000
 
@@ -31,10 +21,98 @@ struct rproc_srm_core {
 	int bind_status;
 	atomic_t prepared;
 	struct rproc_subdev subdev;
+	struct rpmsg_driver rpdrv;
+	struct blocking_notifier_head notifier;
 };
 
 #define to_rproc_srm_core(s) container_of(s, struct rproc_srm_core, subdev)
 
+static struct rproc_srm_core *rpmsg_srm_to_core(struct rpmsg_device *rpdev)
+{
+	struct rpmsg_driver *rpdrv;
+	struct rproc_srm_core *core;
+
+	rpdrv = container_of(rpdev->dev.driver, struct rpmsg_driver, drv);
+	core = container_of(rpdrv, struct rproc_srm_core, rpdrv);
+
+	return core;
+}
+
+int rpmsg_srm_send(struct rpmsg_endpoint *ept, struct rpmsg_srm_msg *msg)
+{
+	int ret;
+
+	ret = rpmsg_send(ept, (void *)msg, sizeof(*msg));
+	if (ret)
+		dev_err(&ept->rpdev->dev, "rpmsg_send failed: %d\n", ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(rpmsg_srm_send);
+
+static int rpmsg_srm_cb(struct rpmsg_device *rpdev, void *data, int len,
+			void *priv, u32 src)
+{
+	struct rproc_srm_core *core = rpmsg_srm_to_core(rpdev);
+	struct rpmsg_srm_msg_desc desc;
+	int ret;
+
+	desc.ept = rpdev->ept;
+	desc.msg = data;
+
+	ret = blocking_notifier_call_chain(&core->notifier, 0, &desc);
+
+	if (!(ret & NOTIFY_STOP_MASK)) {
+		dev_warn(&rpdev->dev, "unknown device\n");
+		desc.msg->message_type = RPROC_SRM_MSG_ERROR;
+		rpmsg_srm_send(desc.ept, desc.msg);
+	}
+
+	return 0;
+}
+
+static int rpmsg_srm_probe(struct rpmsg_device *rpdev)
+{
+	/* Note : the probe ops is mandatory */
+	dev_dbg(&rpdev->dev, "%s\n", __func__);
+
+	return 0;
+}
+
+static void rpmsg_srm_remove(struct rpmsg_device *rpdev)
+{
+	/* Note : the remove ops is mandatory */
+	dev_dbg(&rpdev->dev, "%s\n", __func__);
+}
+
+static struct rpmsg_device_id rpmsg_srm_id_table[] = {
+	{ .name	= "rproc-srm" },
+	{ },
+};
+MODULE_DEVICE_TABLE(rpmsg, rpmsg_srm_id_table);
+
+static struct rpmsg_driver rpmsg_srm_drv = {
+	.drv.name	= "rpmsg_srm",
+	.id_table	= rpmsg_srm_id_table,
+	.probe		= rpmsg_srm_probe,
+	.callback	= rpmsg_srm_cb,
+	.remove		= rpmsg_srm_remove,
+};
+
+int rproc_srm_core_register_notifier(struct rproc_srm_core *core,
+				     struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&core->notifier, nb);
+}
+EXPORT_SYMBOL(rproc_srm_core_register_notifier);
+
+int rproc_srm_core_unregister_notifier(struct rproc_srm_core *core,
+				       struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&core->notifier, nb);
+}
+EXPORT_SYMBOL(rproc_srm_core_unregister_notifier);
+
 static int compare_of(struct device *dev, void *data)
 {
 	return dev->of_node == data;
@@ -115,6 +193,15 @@ static int rproc_srm_core_prepare(struct rproc_subdev *subdev)
 		dev_err(dev, "failed to bind\n");
 		goto master;
 	}
+
+	/* Register rpmsg driver for dynamic management */
+	rproc_srm_core->rpdrv = rpmsg_srm_drv;
+	ret = register_rpmsg_driver(&rproc_srm_core->rpdrv);
+	if (ret) {
+		dev_err(dev, "failed to register rpmsg drv\n");
+		goto master;
+	}
+
 done:
 	atomic_inc(&rproc_srm_core->prepared);
 
@@ -127,7 +214,7 @@ static int rproc_srm_core_prepare(struct rproc_subdev *subdev)
 	return ret;
 }
 
-static void rproc_srm_core_do_unprepare(struct rproc_subdev *subdev)
+static void rproc_srm_core_unprepare(struct rproc_subdev *subdev)
 {
 	struct rproc_srm_core *rproc_srm_core = to_rproc_srm_core(subdev);
 	struct device *dev = rproc_srm_core->dev;
@@ -136,6 +223,8 @@ static void rproc_srm_core_do_unprepare(struct rproc_subdev *subdev)
 
 	atomic_dec(&rproc_srm_core->prepared);
 
+	unregister_rpmsg_driver(&rproc_srm_core->rpdrv);
+
 	component_master_del(dev, &srm_comp_ops);
 	devm_of_platform_depopulate(dev);
 }
@@ -154,6 +243,7 @@ static int rproc_srm_core_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	rproc_srm_core->dev = dev;
+	BLOCKING_INIT_NOTIFIER_HEAD(&rproc_srm_core->notifier);
 
 	/* Register rproc subdevice with (un)prepare ops */
 	rproc_srm_core->subdev.prepare = rproc_srm_core_prepare;
diff --git a/drivers/remoteproc/rproc_srm_core.h b/drivers/remoteproc/rproc_srm_core.h
new file mode 100644
index 0000000..7fe8a23
--- /dev/null
+++ b/drivers/remoteproc/rproc_srm_core.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
+ */
+
+#ifndef _RPROC_SRM_CORE_H_
+#define _RPROC_SRM_CORE_H_
+
+/**
+ * Message type used in resource manager rpmsg:
+ *  RPROC_SRM_MSG_GETCONFIG:    Request to get the configuration of a resource
+ *  RPROC_SRM_MSG_SETCONFIG:    Request to set the configuration of a resource
+ *  RPROC_SRM_MSG_ERROR:        Error when processing a request
+ */
+#define RPROC_SRM_MSG_GETCONFIG 0x00
+#define RPROC_SRM_MSG_SETCONFIG 0x01
+#define RPROC_SRM_MSG_ERROR     0xFF
+
+/**
+ * Resource type used in resource manager rpmsg:
+ *  RPROC_SRM_RSC_CLOCK:        clock resource
+ *  RPROC_SRM_RSC_PIN:          pin resource
+ *  RPROC_SRM_RSC_REGU:         regulator resource
+ */
+#define RPROC_SRM_RSC_CLOCK     0x00
+#define RPROC_SRM_RSC_PIN       0x01
+#define RPROC_SRM_RSC_REGU      0x02
+
+/**
+ * struct clock_cfg - clock configuration used in resource manager rpmsg
+ * @index:      clock index
+ * @name:       clock name
+ * @enable:     clock enable/disable request (in SetConfig message) or current
+ *              status (in GetConfig message)
+ * @rate:       clock rate request (in SetConfig message) or current status (in
+ *              GetConfig message)
+ */
+struct clock_cfg {
+	u32 index;
+	u8 name[16];
+	u32 enable;
+	u32 rate;
+};
+
+/**
+ * struct regu_cfg - regu configuration used in resource manager rpmsg
+ * @index:      regulator index
+ * @name:       regulator name
+ * @enable:     regulator enable/disable request (in SetConfig message) or
+ *              current status (in GetConfig message)
+ * @curr_voltage_mv: current regulator voltage in mV (meaningful in
+ *                   SetConfig message)
+ * @min_voltage_mv:  regulator min voltage request in mV (meaningful in
+ *                   SetConfig message)
+ * @max_voltage_mv:  regulator max voltage request in mV (meaningful in
+ *                   SetConfig message)
+ */
+struct regu_cfg {
+	u32 index;
+	u8 name[16];
+	u32 enable;
+	u32 curr_voltage_mv;
+	u32 min_voltage_mv;
+	u32 max_voltage_mv;
+};
+
+/**
+ * struct pin_cfg - pin configuration used in resource manager rpmsg
+ * @name:       current pin configuration name (meaningful in GetConfig message)
+ */
+struct pin_cfg {
+	u8 name[16];
+};
+
+/**
+ * struct rpmsg_srm_msg - message structure used between processors to
+ *                        dynamically update resources configuration
+ * @message_type: type of the message: see RPROC_SRM_MSG*
+ * @device_id:  an identifier specifying the device owning the resources.
+ *              This is implementation dependent. As example it may be the
+ *              device name or the device address.
+ * @rsc_type:   the type of the resource for which the configuration applies:
+ *              see RPROC_SRM_RSC*
+ * @clock_cfg:  clock config - relevant if &rsc_type is RPROC_SRM_RSC_CLOCK
+ * @regu_cfg:   regulator config - relevant if &rsc_type is RPROC_SRM_RSC_REGU
+ * @pin_cfg:    pin config - relevant if &rsc_type is RPROC_SRM_RSC_PIN
+ */
+struct rpmsg_srm_msg {
+	u32 message_type;
+	u8 device_id[32];
+	u32 rsc_type;
+	union {
+		struct clock_cfg clock_cfg;
+		struct regu_cfg regu_cfg;
+		struct pin_cfg pin_cfg;
+	};
+};
+
+struct rpmsg_srm_msg_desc {
+	struct rpmsg_endpoint *ept;
+	struct rpmsg_srm_msg *msg;
+};
+
+struct rproc_srm_core;
+
+int rproc_srm_core_register_notifier(struct rproc_srm_core *core,
+				     struct notifier_block *nb);
+int rproc_srm_core_unregister_notifier(struct rproc_srm_core *core,
+				       struct notifier_block *nb);
+int rpmsg_srm_send(struct rpmsg_endpoint *ept, struct rpmsg_srm_msg *msg);
+
+#endif
diff --git a/drivers/remoteproc/rproc_srm_dev.c b/drivers/remoteproc/rproc_srm_dev.c
index 4b2e6ac..b3ed733 100644
--- a/drivers/remoteproc/rproc_srm_dev.c
+++ b/drivers/remoteproc/rproc_srm_dev.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
- * Author: Fabien Dessenne <fabien.dessenne@st.com>.
- *
- * License type: GPLv2
- *
- * 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.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
  */
 
 #include <linux/clk.h>
@@ -29,7 +16,7 @@
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 
-#define SUPPLY_SUFFIX   "-supply"
+#include "rproc_srm_core.h"
 
 struct rproc_srm_clk_info {
 	struct list_head list;
@@ -43,6 +30,7 @@ struct rproc_srm_pin_info {
 	struct list_head list;
 	unsigned int index;
 	char *name;
+	bool selected;
 };
 
 struct rproc_srm_regu_info {
@@ -63,6 +51,8 @@ struct rproc_srm_irq_info {
 
 struct rproc_srm_dev {
 	struct device *dev;
+	struct rproc_srm_core *core;
+	struct notifier_block nb;
 	struct pinctrl *pctrl;
 
 	struct list_head clk_list_head;
@@ -71,17 +61,16 @@ struct rproc_srm_dev {
 	struct list_head irq_list_head;
 };
 
-/* irqs */
-static void rproc_srm_dev_put_irqs(struct rproc_srm_dev *rproc_srm_dev)
+/* Irqs */
+static void rproc_srm_dev_irqs_put(struct rproc_srm_dev *rproc_srm_dev)
 {
 	struct device *dev = rproc_srm_dev->dev;
-	struct rproc_srm_irq_info *info, *tmp;
-	struct list_head *irq_head = &rproc_srm_dev->irq_list_head;
+	struct rproc_srm_irq_info *i, *tmp;
 
-	list_for_each_entry_safe(info, tmp, irq_head, list) {
-		devm_free_irq(dev, info->irq, NULL);
-		dev_dbg(dev, "Put irq %d (%s)\n", info->irq, info->name);
-		list_del(&info->list);
+	list_for_each_entry_safe(i, tmp, &rproc_srm_dev->irq_list_head, list) {
+		devm_free_irq(dev, i->irq, NULL);
+		dev_dbg(dev, "Put irq %d (%s)\n", i->irq, i->name);
+		list_del(&i->list);
 	}
 }
 
@@ -91,13 +80,12 @@ static irqreturn_t rproc_srm_dev_irq_handler(int irq, void *dev)
 	return IRQ_HANDLED;
 }
 
-static int rproc_srm_dev_get_irqs(struct rproc_srm_dev *rproc_srm_dev)
+static int rproc_srm_dev_irqs_get(struct rproc_srm_dev *rproc_srm_dev)
 {
 	struct device *dev = rproc_srm_dev->dev;
 	struct platform_device *pdev = to_platform_device(dev);
 	struct device_node *np = dev->of_node;
 	struct rproc_srm_irq_info *info;
-	struct list_head *irq_head = &rproc_srm_dev->irq_list_head;
 	const char *name;
 	int nr, ret, irq;
 	unsigned int i;
@@ -141,87 +129,166 @@ static int rproc_srm_dev_get_irqs(struct rproc_srm_dev *rproc_srm_dev)
 		 */
 		irq_set_status_flags(info->irq, IRQ_DISABLE_UNLAZY);
 
+		/* Note: "interrupt-names" is optional */
 		if (!of_property_read_string_index(np, "interrupt-names", i,
 						   &name))
 			info->name = devm_kstrdup(dev, name, GFP_KERNEL);
+		else
+			info->name = devm_kstrdup(dev, "", GFP_KERNEL);
 
 		info->index = i;
 
-		list_add_tail(&info->list, irq_head);
+		list_add_tail(&info->list, &rproc_srm_dev->irq_list_head);
 		dev_dbg(dev, "Got irq %d (%s)\n", info->irq, info->name);
 	}
 
 	return 0;
 
 err:
-	rproc_srm_dev_put_irqs(rproc_srm_dev);
+	rproc_srm_dev_irqs_put(rproc_srm_dev);
 
 	return ret;
 }
 
 /* Clocks */
-static void rproc_srm_dev_deconfig_clocks(struct rproc_srm_dev *rproc_srm_dev)
+static void rproc_srm_dev_clocks_unsetup(struct rproc_srm_dev *rproc_srm_dev)
 {
 	struct rproc_srm_clk_info *c;
-	struct list_head *clk_head = &rproc_srm_dev->clk_list_head;
 
-	list_for_each_entry(c, clk_head, list) {
+	list_for_each_entry(c, &rproc_srm_dev->clk_list_head, list) {
 		if (!c->enabled)
 			continue;
 
 		clk_disable_unprepare(c->clk);
 		c->enabled = false;
-		dev_dbg(rproc_srm_dev->dev, "clk %d (%s) deconfigured\n",
+		dev_dbg(rproc_srm_dev->dev, "clk %d (%s) unsetup\n",
 			c->index, c->name);
 	}
 }
 
-static int rproc_srm_dev_config_clocks(struct rproc_srm_dev *rproc_srm_dev)
+static int rproc_srm_dev_clocks_setup(struct rproc_srm_dev *rproc_srm_dev)
 {
 	struct rproc_srm_clk_info *c;
-	struct list_head *clk_head = &rproc_srm_dev->clk_list_head;
 	int ret;
 
-	/* Note: not only configuring, but also enabling */
-
-	list_for_each_entry(c, clk_head, list) {
+	/* Prepare and enable clocks */
+	list_for_each_entry(c, &rproc_srm_dev->clk_list_head, list) {
 		if (c->enabled)
 			continue;
 
 		ret = clk_prepare_enable(c->clk);
 		if (ret) {
-			dev_err(rproc_srm_dev->dev, "clk %d (%s) cfg failed\n",
+			dev_err(rproc_srm_dev->dev,
+				"clk %d (%s) enable failed\n",
 				c->index, c->name);
-			rproc_srm_dev_deconfig_clocks(rproc_srm_dev);
+			rproc_srm_dev_clocks_unsetup(rproc_srm_dev);
 			return ret;
 		}
 		c->enabled = true;
-		dev_dbg(rproc_srm_dev->dev, "clk %d (%s) configured\n",
+		dev_dbg(rproc_srm_dev->dev, "clk %d (%s) enabled\n",
 			c->index, c->name);
 	}
 
 	return 0;
 }
 
-static void rproc_srm_dev_put_clocks(struct rproc_srm_dev *rproc_srm_dev)
+static struct rproc_srm_clk_info *rproc_srm_dev_clock_find(
+		struct rproc_srm_dev *rproc_srm_dev, struct clock_cfg *cfg)
+{
+	struct rproc_srm_clk_info *ci;
+
+	/* Search by index (if valid value) otherwise search by name */
+	list_for_each_entry(ci, &rproc_srm_dev->clk_list_head, list) {
+		if (cfg->index != U32_MAX) {
+			if (ci->index == cfg->index)
+				return ci;
+		} else {
+			if (!strcmp(ci->name, cfg->name))
+				return ci;
+		}
+	}
+
+	return NULL;
+}
+
+static int rproc_srm_dev_clock_set_cfg(struct rproc_srm_dev *rproc_srm_dev,
+				       struct clock_cfg *cfg)
+{
+	struct rproc_srm_clk_info *c;
+	struct device *dev = rproc_srm_dev->dev;
+	int ret;
+
+	c = rproc_srm_dev_clock_find(rproc_srm_dev, cfg);
+
+	if (!c) {
+		dev_err(dev, "unknown clock (id %d)\n", cfg->index);
+		return -EINVAL;
+	}
+
+	if (!c->enabled && cfg->enable) {
+		ret = clk_enable(c->clk);
+		if (ret) {
+			dev_err(dev, "clk enable failed\n");
+			return ret;
+		}
+		c->enabled = true;
+		dev_dbg(dev, "clk %d (%s) enabled\n", c->index, c->name);
+	} else if (c->enabled && !cfg->enable) {
+		clk_disable(c->clk);
+		c->enabled = false;
+		dev_dbg(dev, "clk %d (%s) disabled\n", c->index, c->name);
+	}
+
+	if (cfg->rate && clk_get_rate(c->clk) != cfg->rate) {
+		ret = clk_set_rate(c->clk, cfg->rate);
+		if (ret) {
+			dev_err(dev, "clk set rate failed\n");
+			return ret;
+		}
+
+		dev_dbg(dev, "clk %d (%s) rate = %d\n", c->index, c->name,
+			cfg->rate);
+	}
+
+	return 0;
+}
+
+static int rproc_srm_dev_clock_get_cfg(struct rproc_srm_dev *rproc_srm_dev,
+				       struct clock_cfg *cfg)
+{
+	struct rproc_srm_clk_info *c;
+
+	c = rproc_srm_dev_clock_find(rproc_srm_dev, cfg);
+	if (!c) {
+		dev_err(rproc_srm_dev->dev, "unknown clock (%d)\n", cfg->index);
+		return -EINVAL;
+	}
+
+	strlcpy(cfg->name, c->name, sizeof(cfg->name));
+	cfg->index = c->index;
+	cfg->enable = c->enabled;
+	cfg->rate = (u32)clk_get_rate(c->clk);
+
+	return 0;
+}
+
+static void rproc_srm_dev_clocks_put(struct rproc_srm_dev *rproc_srm_dev)
 {
 	struct device *dev = rproc_srm_dev->dev;
 	struct rproc_srm_clk_info *c, *tmp;
-	struct list_head *clk_head = &rproc_srm_dev->clk_list_head;
 
-	list_for_each_entry_safe(c, tmp, clk_head, list) {
+	list_for_each_entry_safe(c, tmp, &rproc_srm_dev->clk_list_head, list) {
 		clk_put(c->clk);
 		dev_dbg(dev, "put clock %d (%s)\n", c->index, c->name);
 		list_del(&c->list);
 	}
 }
 
-static int rproc_srm_dev_get_clocks(struct rproc_srm_dev *rproc_srm_dev)
+static int rproc_srm_dev_clocks_get(struct rproc_srm_dev *rproc_srm_dev)
 {
 	struct device *dev = rproc_srm_dev->dev;
 	struct device_node *np = dev->of_node;
 	struct rproc_srm_clk_info *c;
-	struct list_head *clk_head = &rproc_srm_dev->clk_list_head;
 	const char *name;
 	int nb_c, ret;
 	unsigned int i;
@@ -248,48 +315,53 @@ static int rproc_srm_dev_get_clocks(struct rproc_srm_dev *rproc_srm_dev)
 			goto err;
 		}
 
+		/* Note: "clock-names" is optional */
 		if (!of_property_read_string_index(np, "clock-names", i,
 						   &name))
 			c->name = devm_kstrdup(dev, name, GFP_KERNEL);
+		else
+			c->name = devm_kstrdup(dev, "", GFP_KERNEL);
 
 		c->index = i;
 
-		list_add_tail(&c->list, clk_head);
+		list_add_tail(&c->list, &rproc_srm_dev->clk_list_head);
 		dev_dbg(dev, "got clock %d (%s)\n", c->index, c->name);
 	}
 
 	return 0;
 
 err:
-	rproc_srm_dev_put_clocks(rproc_srm_dev);
+	rproc_srm_dev_clocks_put(rproc_srm_dev);
 	return ret;
 }
 
 /* Regulators */
-static void rproc_srm_dev_deconfig_regus(struct rproc_srm_dev *rproc_srm_dev)
+static void rproc_srm_dev_regus_unsetup(struct rproc_srm_dev *rproc_srm_dev)
 {
 	struct rproc_srm_regu_info *r;
-	struct list_head *regu_head = &rproc_srm_dev->regu_list_head;
+	struct device *dev = rproc_srm_dev->dev;
 
-	list_for_each_entry(r, regu_head, list) {
+	list_for_each_entry(r, &rproc_srm_dev->regu_list_head, list) {
 		if (!r->enabled)
 			continue;
 
-		regulator_disable(r->regu);
+		if (regulator_disable(r->regu)) {
+			dev_warn(dev, "regu %d disabled failed\n", r->index);
+			continue;
+		}
+
 		r->enabled = false;
-		dev_dbg(rproc_srm_dev->dev, "regu %d (%s) disabled\n",
-			r->index, r->name);
+		dev_dbg(dev, "regu %d (%s) disabled\n", r->index, r->name);
 	}
 }
 
-static int rproc_srm_dev_config_regus(struct rproc_srm_dev *rproc_srm_dev)
+static int rproc_srm_dev_regus_setup(struct rproc_srm_dev *rproc_srm_dev)
 {
 	struct rproc_srm_regu_info *r;
-	struct list_head *regu_head = &rproc_srm_dev->regu_list_head;
 	int ret;
 
 	/* Enable all the regulators */
-	list_for_each_entry(r, regu_head, list) {
+	list_for_each_entry(r, &rproc_srm_dev->regu_list_head, list) {
 		if (r->enabled)
 			continue;
 
@@ -297,7 +369,7 @@ static int rproc_srm_dev_config_regus(struct rproc_srm_dev *rproc_srm_dev)
 		if (ret) {
 			dev_err(rproc_srm_dev->dev, "regu %d (%s) failed\n",
 				r->index, r->name);
-			rproc_srm_dev_deconfig_regus(rproc_srm_dev);
+			rproc_srm_dev_regus_unsetup(rproc_srm_dev);
 			return ret;
 		}
 		r->enabled = true;
@@ -308,20 +380,113 @@ static int rproc_srm_dev_config_regus(struct rproc_srm_dev *rproc_srm_dev)
 	return 0;
 }
 
-static void rproc_srm_dev_put_regus(struct rproc_srm_dev *rproc_srm_dev)
+static struct rproc_srm_regu_info *rproc_srm_dev_regu_find(
+		struct rproc_srm_dev *rproc_srm_dev, struct regu_cfg *cfg)
+{
+	struct rproc_srm_regu_info *ri;
+
+	list_for_each_entry(ri, &rproc_srm_dev->regu_list_head, list) {
+		if (cfg->index != U32_MAX) {
+			if (ri->index == cfg->index)
+				return ri;
+		} else {
+			if (!strcmp(ri->name, cfg->name))
+				return ri;
+		}
+	}
+
+	return NULL;
+}
+
+static int rproc_srm_dev_regu_set_cfg(struct rproc_srm_dev *rproc_srm_dev,
+				      struct regu_cfg *cfg)
+{
+	struct rproc_srm_regu_info *r;
+	struct device *dev = rproc_srm_dev->dev;
+	int ret;
+
+	r = rproc_srm_dev_regu_find(rproc_srm_dev, cfg);
+	if (!r) {
+		dev_err(dev, "unknown regu (%d)\n", cfg->index);
+		return -EINVAL;
+	}
+
+	if (!r->enabled && cfg->enable) {
+		ret = regulator_enable(r->regu);
+		if (ret) {
+			dev_err(dev, "regu %d enable failed\n", r->index);
+			return ret;
+		}
+		r->enabled = true;
+		dev_dbg(dev, "regu %d (%s) enabled\n", r->index, r->name);
+	} else if (r->enabled && !cfg->enable) {
+		ret = regulator_disable(r->regu);
+		if (ret) {
+			dev_err(dev, "regu %d disable failed\n", r->index);
+			return ret;
+		}
+		r->enabled = false;
+		dev_dbg(dev, "regu %d (%s) disabled\n", r->index, r->name);
+	}
+
+	if (cfg->min_voltage_mv || cfg->max_voltage_mv) {
+		ret = regulator_set_voltage(r->regu, cfg->min_voltage_mv * 1000,
+					    cfg->max_voltage_mv * 1000);
+		if (ret) {
+			dev_err(dev, "regu %d set voltage failed\n", r->index);
+			return ret;
+		}
+
+		dev_dbg(dev, "regu %d (%s) voltage = [%d - %d] mv\n", r->index,
+			r->name, cfg->min_voltage_mv, cfg->max_voltage_mv);
+	}
+
+	return 0;
+}
+
+static int rproc_srm_dev_regu_get_cfg(struct rproc_srm_dev *rproc_srm_dev,
+				      struct regu_cfg *cfg)
+{
+	struct rproc_srm_regu_info *r;
+	struct device *dev = rproc_srm_dev->dev;
+	int v;
+
+	r = rproc_srm_dev_regu_find(rproc_srm_dev, cfg);
+	if (!r) {
+		dev_err(dev, "unknown regu (%d)\n", cfg->index);
+		return -EINVAL;
+	}
+
+	strlcpy(cfg->name, r->name, sizeof(cfg->name));
+	cfg->index = r->index;
+	cfg->enable = r->enabled;
+	cfg->min_voltage_mv = 0;
+	cfg->max_voltage_mv = 0;
+
+	v = regulator_get_voltage(r->regu);
+	if (v < 0) {
+		dev_warn(dev, "cannot get %s voltage\n", r->name);
+		cfg->curr_voltage_mv = 0;
+	} else {
+		cfg->curr_voltage_mv = v / 1000;
+	}
+
+	return 0;
+}
+
+static void rproc_srm_dev_regus_put(struct rproc_srm_dev *rproc_srm_dev)
 {
 	struct device *dev = rproc_srm_dev->dev;
 	struct rproc_srm_regu_info *r, *tmp;
-	struct list_head *regu_head = &rproc_srm_dev->regu_list_head;
 
-	list_for_each_entry_safe(r, tmp, regu_head, list) {
+	list_for_each_entry_safe(r, tmp, &rproc_srm_dev->regu_list_head, list) {
 		devm_regulator_put(r->regu);
 		dev_dbg(dev, "put regu %d (%s)\n", r->index, r->name);
 		list_del(&r->list);
 	}
 }
 
-static int rproc_srm_dev_get_regus(struct rproc_srm_dev *rproc_srm_dev)
+static int rproc_srm_dev_regus_get(struct rproc_srm_dev *rproc_srm_dev)
 {
 	struct device *dev = rproc_srm_dev->dev;
 	struct device_node *np = dev->of_node;
@@ -329,14 +494,13 @@ static int rproc_srm_dev_get_regus(struct rproc_srm_dev *rproc_srm_dev)
 	const char *n;
 	char *name;
 	struct rproc_srm_regu_info *r;
-	struct list_head *regu_head = &rproc_srm_dev->regu_list_head;
 	int ret, nb_s = 0;
 
 	if (!np)
 		return 0;
 
 	for_each_property_of_node(np, p) {
-		n = strstr(p->name, SUPPLY_SUFFIX);
+		n = strstr(p->name, "-supply");
 		if (!n || n == p->name)
 			continue;
 
@@ -347,7 +511,7 @@ static int rproc_srm_dev_get_regus(struct rproc_srm_dev *rproc_srm_dev)
 		}
 
 		name = devm_kstrdup(dev, p->name, GFP_KERNEL);
-		name[strlen(p->name) - strlen(SUPPLY_SUFFIX)] = '\0';
+		name[strlen(p->name) - strlen("-supply")] = '\0';
 		r->name = name;
 
 		r->regu = devm_regulator_get(dev, r->name);
@@ -359,25 +523,83 @@ static int rproc_srm_dev_get_regus(struct rproc_srm_dev *rproc_srm_dev)
 
 		r->index = nb_s++;
 
-		list_add_tail(&r->list, regu_head);
+		list_add_tail(&r->list, &rproc_srm_dev->regu_list_head);
 		dev_dbg(dev, "got regu %d (%s)\n", r->index, r->name);
 	}
 
 	return 0;
 
 err_list:
-	rproc_srm_dev_put_regus(rproc_srm_dev);
+	rproc_srm_dev_regus_put(rproc_srm_dev);
 	return ret;
 }
 
 /* Pins */
-static void rproc_srm_dev_put_pins(struct rproc_srm_dev *rproc_srm_dev)
+static int rproc_srm_dev_pin_set_cfg(struct rproc_srm_dev *rproc_srm_dev,
+				     struct pin_cfg *cfg)
+{
+	struct rproc_srm_pin_info *pi, *p = NULL;
+	struct device *dev = rproc_srm_dev->dev;
+	struct pinctrl_state *state;
+	int ret;
+
+	list_for_each_entry(pi, &rproc_srm_dev->pin_list_head, list) {
+		if (!strcmp(pi->name, cfg->name)) {
+			p = pi;
+			break;
+		}
+	}
+
+	if (!p) {
+		dev_err(dev, "unknown pin config (%s)\n", cfg->name);
+		return -EINVAL;
+	}
+
+	state = pinctrl_lookup_state(rproc_srm_dev->pctrl, cfg->name);
+	if (IS_ERR(state)) {
+		dev_err(dev, "cannot get pin config (%s)\n", cfg->name);
+		return -EINVAL;
+	}
+
+	ret = pinctrl_select_state(rproc_srm_dev->pctrl, state);
+	if (ret < 0) {
+		dev_err(dev, "cannot set pin config (%s)\n", cfg->name);
+		return ret;
+	}
+
+	list_for_each_entry(pi, &rproc_srm_dev->pin_list_head, list) {
+		pi->selected = (pi == p);
+	}
+
+	dev_dbg(dev, "pin config (%s) selected\n", p->name);
+
+	return 0;
+}
+
+static int rproc_srm_dev_pin_get_cfg(struct rproc_srm_dev *rproc_srm_dev,
+				     struct pin_cfg *cfg)
+{
+	struct rproc_srm_pin_info *p;
+
+	list_for_each_entry(p, &rproc_srm_dev->pin_list_head, list) {
+		if (p->selected) {
+			strlcpy(cfg->name, p->name, sizeof(cfg->name));
+			return 0;
+		}
+	}
+
+	dev_warn(rproc_srm_dev->dev, "cannot find selected pin state\n");
+	strcpy(cfg->name, "");
+
+	return 0;
+}
+
+static void rproc_srm_dev_pins_put(struct rproc_srm_dev *rproc_srm_dev)
 {
 	struct device *dev = rproc_srm_dev->dev;
 	struct rproc_srm_pin_info *p, *tmp;
-	struct list_head *pin_head = &rproc_srm_dev->pin_list_head;
 
-	list_for_each_entry_safe(p, tmp, pin_head, list) {
+	list_for_each_entry_safe(p, tmp, &rproc_srm_dev->pin_list_head, list) {
 		devm_kfree(dev, p->name);
 		devm_kfree(dev, p);
 		dev_dbg(dev, "remove pin cfg %d (%s)\n", p->index, p->name);
@@ -390,12 +612,11 @@ static void rproc_srm_dev_put_pins(struct rproc_srm_dev *rproc_srm_dev)
 	}
 }
 
-static int rproc_srm_dev_get_pins(struct rproc_srm_dev *rproc_srm_dev)
+static int rproc_srm_dev_pins_get(struct rproc_srm_dev *rproc_srm_dev)
 {
 	struct device *dev = rproc_srm_dev->dev;
 	struct device_node *np = dev->of_node;
 	struct rproc_srm_pin_info *p;
-	struct list_head *pin_head = &rproc_srm_dev->pin_list_head;
 	int ret, nb_p;
 	unsigned int i;
 	const char *name;
@@ -403,8 +624,6 @@ static int rproc_srm_dev_get_pins(struct rproc_srm_dev *rproc_srm_dev)
 	if (!np)
 		return 0;
 
-	/* Assumption here is that "default" pinctrl applied before probe */
-
 	rproc_srm_dev->pctrl = devm_pinctrl_get(dev);
 	if (IS_ERR(rproc_srm_dev->pctrl))
 		return 0;
@@ -430,20 +649,107 @@ static int rproc_srm_dev_get_pins(struct rproc_srm_dev *rproc_srm_dev)
 			goto err;
 		}
 		p->name = devm_kstrdup(dev, name, GFP_KERNEL);
+		if (!strcmp(p->name, PINCTRL_STATE_DEFAULT))
+			p->selected = true;
 
 		p->index = i;
 
-		list_add_tail(&p->list, pin_head);
+		list_add_tail(&p->list, &rproc_srm_dev->pin_list_head);
 		dev_dbg(dev, "found pin cfg %d (%s)\n", p->index, p->name);
 	}
 	return 0;
 
 err:
-	rproc_srm_dev_put_pins(rproc_srm_dev);
+	rproc_srm_dev_pins_put(rproc_srm_dev);
 	return ret;
 }
 
 /* Core */
+static int rproc_srm_dev_notify_cb(struct notifier_block *nb, unsigned long evt,
+				   void *data)
+{
+	struct rproc_srm_dev *rproc_srm_dev =
+			container_of(nb, struct rproc_srm_dev, nb);
+	struct device *dev = rproc_srm_dev->dev;
+	struct rpmsg_srm_msg_desc *desc;
+	struct rpmsg_srm_msg *i, o;
+	int ret = 0;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	desc = (struct rpmsg_srm_msg_desc *)data;
+	i = desc->msg;
+	o = *i;
+
+	/* Check if 'device_id' (name / addr ) matches this device */
+	if (!strstr(dev_name(dev), i->device_id))
+		return NOTIFY_DONE;
+
+	switch (i->message_type) {
+	case RPROC_SRM_MSG_SETCONFIG:
+		switch (i->rsc_type) {
+		case RPROC_SRM_RSC_CLOCK:
+			ret = rproc_srm_dev_clock_set_cfg(rproc_srm_dev,
+							  &i->clock_cfg);
+			if (!ret)
+				ret = rproc_srm_dev_clock_get_cfg(rproc_srm_dev,
+								  &o.clock_cfg);
+			break;
+		case RPROC_SRM_RSC_PIN:
+			ret = rproc_srm_dev_pin_set_cfg(rproc_srm_dev,
+							&i->pin_cfg);
+			if (!ret)
+				ret = rproc_srm_dev_pin_get_cfg(rproc_srm_dev,
+								&o.pin_cfg);
+			break;
+		case RPROC_SRM_RSC_REGU:
+			ret = rproc_srm_dev_regu_set_cfg(rproc_srm_dev,
+							 &i->regu_cfg);
+			if (!ret)
+				ret = rproc_srm_dev_regu_get_cfg(rproc_srm_dev,
+								 &o.regu_cfg);
+			break;
+		default:
+			dev_warn(dev, "bad rsc type (%d)\n", i->rsc_type);
+			ret = -EINVAL;
+			break;
+		}
+		break;
+	case RPROC_SRM_MSG_GETCONFIG:
+		switch (i->rsc_type) {
+		case RPROC_SRM_RSC_CLOCK:
+			ret = rproc_srm_dev_clock_get_cfg(rproc_srm_dev,
+							  &o.clock_cfg);
+			break;
+		case RPROC_SRM_RSC_PIN:
+			ret = rproc_srm_dev_pin_get_cfg(rproc_srm_dev,
+							&o.pin_cfg);
+			break;
+		case RPROC_SRM_RSC_REGU:
+			ret = rproc_srm_dev_regu_get_cfg(rproc_srm_dev,
+							 &o.regu_cfg);
+			break;
+		default:
+			dev_warn(dev, "bad rsc type (%d)\n", i->rsc_type);
+			ret = -EINVAL;
+			break;
+		}
+		break;
+	default:
+		dev_warn(dev, "bad msg type (%d)\n", i->message_type);
+		ret = -EINVAL;
+		break;
+	}
+
+	/* Send return msg */
+	if (ret)
+		o.message_type = RPROC_SRM_MSG_ERROR;
+
+	ret = rpmsg_srm_send(desc->ept, &o);
+
+	return ret ? NOTIFY_BAD : NOTIFY_STOP;
+}
+
 static void
 rproc_srm_dev_unbind(struct device *dev, struct device *master, void *data)
 {
@@ -451,10 +757,10 @@ rproc_srm_dev_unbind(struct device *dev, struct device *master, void *data)
 
 	dev_dbg(dev, "%s\n", __func__);
 
-	rproc_srm_dev_deconfig_regus(rproc_srm_dev);
-	rproc_srm_dev_deconfig_clocks(rproc_srm_dev);
+	rproc_srm_dev_regus_unsetup(rproc_srm_dev);
+	rproc_srm_dev_clocks_unsetup(rproc_srm_dev);
 
-	/* For pins and IRQs: nothing to deconfigure */
+	/* For pins and IRQs: nothing to unsetup */
 }
 
 static int
@@ -465,15 +771,15 @@ rproc_srm_dev_bind(struct device *dev, struct device *master, void *data)
 
 	dev_dbg(dev, "%s\n", __func__);
 
-	ret = rproc_srm_dev_config_clocks(rproc_srm_dev);
+	ret = rproc_srm_dev_clocks_setup(rproc_srm_dev);
 	if (ret)
 		return ret;
 
-	ret = rproc_srm_dev_config_regus(rproc_srm_dev);
+	ret = rproc_srm_dev_regus_setup(rproc_srm_dev);
 	if (ret)
 		return ret;
 
-	/* For pins and IRQs: nothing to configure */
+	/* For pins and IRQs: nothing to setup */
 	return 0;
 }
 
@@ -496,37 +802,48 @@ static int rproc_srm_dev_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	rproc_srm_dev->dev = dev;
+	rproc_srm_dev->core = dev_get_drvdata(dev->parent);
+
 	INIT_LIST_HEAD(&rproc_srm_dev->clk_list_head);
 	INIT_LIST_HEAD(&rproc_srm_dev->pin_list_head);
 	INIT_LIST_HEAD(&rproc_srm_dev->regu_list_head);
 	INIT_LIST_HEAD(&rproc_srm_dev->irq_list_head);
 
 	/* Get clocks, regu, irqs and pinctrl */
-	ret = rproc_srm_dev_get_clocks(rproc_srm_dev);
+	ret = rproc_srm_dev_clocks_get(rproc_srm_dev);
 	if (ret)
 		return ret;
 
-	ret = rproc_srm_dev_get_regus(rproc_srm_dev);
+	ret = rproc_srm_dev_regus_get(rproc_srm_dev);
 	if (ret)
-		goto err;
+		goto err_get;
 
-	ret = rproc_srm_dev_get_pins(rproc_srm_dev);
+	ret = rproc_srm_dev_pins_get(rproc_srm_dev);
 	if (ret)
-		goto err;
+		goto err_get;
 
-	ret = rproc_srm_dev_get_irqs(rproc_srm_dev);
+	ret = rproc_srm_dev_irqs_get(rproc_srm_dev);
 	if (ret)
-		goto err;
+		goto err_get;
+
+	rproc_srm_dev->nb.notifier_call = rproc_srm_dev_notify_cb;
+	ret = rproc_srm_core_register_notifier(rproc_srm_dev->core,
+					       &rproc_srm_dev->nb);
+	if (ret)
+		goto err_register;
 
 	dev_set_drvdata(dev, rproc_srm_dev);
 
-	return  component_add(dev, &rproc_srm_dev_ops);
+	return component_add(dev, &rproc_srm_dev_ops);
 
-err:
-	rproc_srm_dev_put_irqs(rproc_srm_dev);
-	rproc_srm_dev_put_pins(rproc_srm_dev);
-	rproc_srm_dev_put_regus(rproc_srm_dev);
-	rproc_srm_dev_put_clocks(rproc_srm_dev);
+err_register:
+	rproc_srm_core_unregister_notifier(rproc_srm_dev->core,
+					   &rproc_srm_dev->nb);
+err_get:
+	rproc_srm_dev_irqs_put(rproc_srm_dev);
+	rproc_srm_dev_pins_put(rproc_srm_dev);
+	rproc_srm_dev_regus_put(rproc_srm_dev);
+	rproc_srm_dev_clocks_put(rproc_srm_dev);
 	return ret;
 }
 
@@ -539,10 +856,13 @@ static int rproc_srm_dev_remove(struct platform_device *pdev)
 
 	component_del(dev, &rproc_srm_dev_ops);
 
-	rproc_srm_dev_put_irqs(rproc_srm_dev);
-	rproc_srm_dev_put_regus(rproc_srm_dev);
-	rproc_srm_dev_put_pins(rproc_srm_dev);
-	rproc_srm_dev_put_clocks(rproc_srm_dev);
+	rproc_srm_core_unregister_notifier(rproc_srm_dev->core,
+					   &rproc_srm_dev->nb);
+
+	rproc_srm_dev_irqs_put(rproc_srm_dev);
+	rproc_srm_dev_regus_put(rproc_srm_dev);
+	rproc_srm_dev_pins_put(rproc_srm_dev);
+	rproc_srm_dev_clocks_put(rproc_srm_dev);
 
 	return 0;
 }
-- 
2.7.4

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

end of thread, other threads:[~2018-06-27  8:06 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-27  8:06 [RFC v2 0/5] System resource management Fabien Dessenne
2018-06-27  8:06 ` [RFC v2 1/5] dt-bindings: remoteproc: add system resource manager (SRM) Fabien Dessenne
2018-06-27  8:06 ` [RFC v2 2/5] remoteproc: add system resource manager core Fabien Dessenne
2018-06-27  8:06 ` [RFC v2 3/5] remoteproc: add system resource manager device Fabien Dessenne
2018-06-27  8:06 ` [RFC v2 4/5] remoteproc: probe the system resource manager Fabien Dessenne
2018-06-27  8:06 ` [RFC v2 5/5] remoteproc: srm: introduce dynamic " Fabien Dessenne

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.