Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 05/20] firmware: arm_scmi: add scmi protocol bus to enumerate protocol devices
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

The SCMI specification encompasses various protocols. However, not every
protocol has to be present on a given platform/implementation as not
every protocol is relevant for it.

Furthermore, the platform chooses which protocols it exposes to a given
agent. The only protocol that must be implemented is the base protocol.
The base protocol is used by an agent to discover which protocols are
available to it.

In order to enumerate the discovered implemented protocols, this patch
adds support for a separate scmi protocol bus. It also adds mechanism to
register support for different protocols.

Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/Makefile |   3 +-
 drivers/firmware/arm_scmi/bus.c    | 232 +++++++++++++++++++++++++++++++++++++
 drivers/firmware/arm_scmi/common.h |   1 +
 include/linux/scmi_protocol.h      |  62 ++++++++++
 4 files changed, 297 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/arm_scmi/bus.c

diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 5d9c7ef35f0f..5f4ec2613db6 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,3 +1,4 @@
-obj-y	= scmi-driver.o scmi-protocols.o
+obj-y	= scmi-bus.o scmi-driver.o scmi-protocols.o
+scmi-bus-y = bus.o
 scmi-driver-y = driver.o
 scmi-protocols-y = base.o
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
new file mode 100644
index 000000000000..cf1c2464cb0f
--- /dev/null
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -0,0 +1,232 @@
+/*
+ * System Control and Management Interface (SCMI) Message Protocol bus layer
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#include "common.h"
+
+static DEFINE_IDA(scmi_bus_id);
+static DEFINE_IDR(scmi_protocols);
+static DEFINE_SPINLOCK(protocol_lock);
+
+static const struct scmi_device_id *
+scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
+{
+	const struct scmi_device_id *id = scmi_drv->id_table;
+
+	if (!id)
+		return NULL;
+
+	for (; id->protocol_id; id++)
+		if (id->protocol_id == scmi_dev->protocol_id)
+			return id;
+
+	return NULL;
+}
+
+static int scmi_dev_match(struct device *dev, struct device_driver *drv)
+{
+	struct scmi_driver *scmi_drv = to_scmi_driver(drv);
+	struct scmi_device *scmi_dev = to_scmi_dev(dev);
+	const struct scmi_device_id *id;
+
+	id = scmi_dev_match_id(scmi_dev, scmi_drv);
+	if (id)
+		return 1;
+
+	return 0;
+}
+
+static int scmi_protocol_init(int protocol_id, struct scmi_handle *handle)
+{
+	scmi_prot_init_fn_t fn = idr_find(&scmi_protocols, protocol_id);
+
+	if (unlikely(!fn))
+		return -EINVAL;
+	return fn(handle);
+}
+
+static int scmi_dev_probe(struct device *dev)
+{
+	struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
+	struct scmi_device *scmi_dev = to_scmi_dev(dev);
+	const struct scmi_device_id *id;
+	int ret;
+
+	id = scmi_dev_match_id(scmi_dev, scmi_drv);
+	if (!id)
+		return -ENODEV;
+
+	if (!scmi_dev->handle)
+		return -EPROBE_DEFER;
+
+	ret = scmi_protocol_init(scmi_dev->protocol_id, scmi_dev->handle);
+	if (ret)
+		return ret;
+
+	return scmi_drv->probe(scmi_dev);
+}
+
+static int scmi_dev_remove(struct device *dev)
+{
+	struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
+	struct scmi_device *scmi_dev = to_scmi_dev(dev);
+
+	if (scmi_drv->remove)
+		scmi_drv->remove(scmi_dev);
+
+	return 0;
+}
+
+static struct bus_type scmi_bus_type = {
+	.name =	"scmi_protocol",
+	.match = scmi_dev_match,
+	.probe = scmi_dev_probe,
+	.remove = scmi_dev_remove,
+};
+
+int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
+			 const char *mod_name)
+{
+	int retval;
+
+	driver->driver.bus = &scmi_bus_type;
+	driver->driver.name = driver->name;
+	driver->driver.owner = owner;
+	driver->driver.mod_name = mod_name;
+
+	retval = driver_register(&driver->driver);
+	if (!retval)
+		pr_debug("registered new scmi driver %s\n", driver->name);
+
+	return retval;
+}
+EXPORT_SYMBOL_GPL(scmi_driver_register);
+
+void scmi_driver_unregister(struct scmi_driver *driver)
+{
+	driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL_GPL(scmi_driver_unregister);
+
+struct scmi_device *
+scmi_device_create(struct device_node *np, struct device *parent, int protocol)
+{
+	int id, retval;
+	struct scmi_device *scmi_dev;
+
+	id = ida_simple_get(&scmi_bus_id, 1, 0, GFP_KERNEL);
+	if (id < 0)
+		return NULL;
+
+	scmi_dev = kzalloc(sizeof(*scmi_dev), GFP_KERNEL);
+	if (!scmi_dev)
+		goto no_mem;
+
+	scmi_dev->id = id;
+	scmi_dev->protocol_id = protocol;
+	scmi_dev->dev.parent = parent;
+	scmi_dev->dev.of_node = np;
+	scmi_dev->dev.bus = &scmi_bus_type;
+	dev_set_name(&scmi_dev->dev, "scmi_dev.%d", id);
+
+	retval = device_register(&scmi_dev->dev);
+	if (!retval)
+		return scmi_dev;
+
+	put_device(&scmi_dev->dev);
+	kfree(scmi_dev);
+no_mem:
+	ida_simple_remove(&scmi_bus_id, id);
+	return NULL;
+}
+
+void scmi_device_destroy(struct scmi_device *scmi_dev)
+{
+	scmi_handle_put(scmi_dev->handle);
+	device_unregister(&scmi_dev->dev);
+	ida_simple_remove(&scmi_bus_id, scmi_dev->id);
+	kfree(scmi_dev);
+}
+
+void scmi_set_handle(struct scmi_device *scmi_dev)
+{
+	scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
+}
+
+int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn)
+{
+	int ret;
+
+	spin_lock(&protocol_lock);
+	ret = idr_alloc(&scmi_protocols, fn, protocol_id, protocol_id + 1,
+			GFP_KERNEL);
+	if (ret != protocol_id)
+		pr_err("unable to allocate SCMI idr slot, err %d\n", ret);
+	spin_unlock(&protocol_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(scmi_protocol_register);
+
+void scmi_protocol_unregister(int protocol_id)
+{
+	spin_lock(&protocol_lock);
+	idr_remove(&scmi_protocols, protocol_id);
+	spin_unlock(&protocol_lock);
+}
+EXPORT_SYMBOL_GPL(scmi_protocol_unregister);
+
+static int __scmi_devices_unregister(struct device *dev, void *data)
+{
+	struct scmi_device *scmi_dev = to_scmi_dev(dev);
+
+	scmi_device_destroy(scmi_dev);
+	return 0;
+}
+
+static void scmi_devices_unregister(void)
+{
+	bus_for_each_dev(&scmi_bus_type, NULL, NULL, __scmi_devices_unregister);
+}
+
+static int __init scmi_bus_init(void)
+{
+	int retval;
+
+	retval = bus_register(&scmi_bus_type);
+	if (retval)
+		pr_err("scmi protocol bus register failed (%d)\n", retval);
+
+	return retval;
+}
+subsys_initcall(scmi_bus_init);
+
+static void __exit scmi_bus_exit(void)
+{
+	scmi_devices_unregister();
+	bus_unregister(&scmi_bus_type);
+	ida_destroy(&scmi_bus_id);
+}
+module_exit(scmi_bus_exit);
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index bc767f4997e0..f1eeacaef57b 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -107,6 +107,7 @@ int scmi_one_xfer_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id,
 		       size_t tx_size, size_t rx_size, struct scmi_xfer **p);
 int scmi_handle_put(const struct scmi_handle *handle);
 struct scmi_handle *scmi_handle_get(struct device *dev);
+void scmi_set_handle(struct scmi_device *scmi_dev);
 int scmi_version_get(const struct scmi_handle *h, u8 protocol, u32 *version);
 void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
 				     u8 *prot_imp);
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 664da3d763f2..7b72c75cb560 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -15,6 +15,7 @@
  * 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/device.h>
 #include <linux/types.h>
 
 #define SCMI_MAX_STR_SIZE	16
@@ -62,3 +63,64 @@ enum scmi_std_protocol {
 	SCMI_PROTOCOL_CLOCK = 0x14,
 	SCMI_PROTOCOL_SENSOR = 0x15,
 };
+
+struct scmi_device {
+	u32 id;
+	u8 protocol_id;
+	struct device dev;
+	struct scmi_handle *handle;
+};
+
+#define to_scmi_dev(d) container_of(d, struct scmi_device, dev)
+
+struct scmi_device *
+scmi_device_create(struct device_node *np, struct device *parent, int protocol);
+void scmi_device_destroy(struct scmi_device *scmi_dev);
+
+struct scmi_device_id {
+	u8 protocol_id;
+};
+
+struct scmi_driver {
+	const char *name;
+	int (*probe)(struct scmi_device *);
+	void (*remove)(struct scmi_device *);
+	const struct scmi_device_id *id_table;
+
+	struct device_driver driver;
+};
+
+#define to_scmi_driver(d) container_of(d, struct scmi_driver, driver)
+
+#ifdef CONFIG_ARM_SCMI_PROTOCOL
+int scmi_driver_register(struct scmi_driver *driver,
+			 struct module *owner, const char *mod_name);
+void scmi_driver_unregister(struct scmi_driver *driver);
+#else
+static int scmi_driver_register(struct scmi_driver *driver,
+				struct module *owner, const char *mod_name)
+{
+	return -EINVAL;
+}
+static void scmi_driver_unregister(struct scmi_driver *driver) {}
+#endif /* CONFIG_ARM_SCMI_PROTOCOL */
+
+#define scmi_register(driver) \
+	scmi_driver_register(driver, THIS_MODULE, KBUILD_MODNAME)
+#define scmi_unregister(driver) \
+	scmi_driver_unregister(driver)
+
+/**
+ * module_scmi_driver() - Helper macro for registering a scmi driver
+ * @__scmi_driver: scmi_driver structure
+ *
+ * Helper macro for scmi drivers to set up proper module init / exit
+ * functions.  Replaces module_init() and module_exit() and keeps people from
+ * printing pointless things to the kernel log when their driver is loaded.
+ */
+#define module_scmi_driver(__scmi_driver)	\
+	module_driver(__scmi_driver, scmi_register, scmi_unregister)
+
+typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *);
+int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn);
+void scmi_protocol_unregister(int protocol_id);
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 06/20] firmware: arm_scmi: add initial support for performance protocol
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

The performance protocol is intended for the performance management of
group(s) of device(s) that run in the same performance domain. It
includes even the CPUs. A performance domain is defined by a set of
devices that always have to run at the same performance level.
For example, a set of CPUs that share a voltage domain, and have a
common frequency control, is said to be in the same performance domain.

The commands in this protocol provide functionality to describe the
protocol version, describe various attribute flags, set and get the
performance level of a domain. It also supports discovery of the list
of performance levels supported by a performance domain, and the
properties of each performance level.

This patch adds basic support for the performance protocol.

Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/Makefile |   2 +-
 drivers/firmware/arm_scmi/common.h |   1 +
 drivers/firmware/arm_scmi/perf.c   | 527 +++++++++++++++++++++++++++++++++++++
 include/linux/scmi_protocol.h      |  34 +++
 4 files changed, 563 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/arm_scmi/perf.c

diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 5f4ec2613db6..687cbbfb3af6 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,4 +1,4 @@
 obj-y	= scmi-bus.o scmi-driver.o scmi-protocols.o
 scmi-bus-y = bus.o
 scmi-driver-y = driver.o
-scmi-protocols-y = base.o
+scmi-protocols-y = base.o perf.o
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index f1eeacaef57b..b045a3f268e7 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -30,6 +30,7 @@
 #define PROTOCOL_REV_MAJOR(x)	((x) >> PROTOCOL_REV_MINOR_BITS)
 #define PROTOCOL_REV_MINOR(x)	((x) & PROTOCOL_REV_MINOR_MASK)
 #define MAX_PROTOCOLS_IMP	16
+#define MAX_OPPS		16
 
 enum scmi_common_cmd {
 	PROTOCOL_VERSION = 0x0,
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
new file mode 100644
index 000000000000..a1f5cf136748
--- /dev/null
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -0,0 +1,527 @@
+/*
+ * System Control and Management Interface (SCMI) Performance Protocol
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/sort.h>
+
+#include "common.h"
+
+enum scmi_performance_protocol_cmd {
+	PERF_DOMAIN_ATTRIBUTES = 0x3,
+	PERF_DESCRIBE_LEVELS = 0x4,
+	PERF_LIMITS_SET = 0x5,
+	PERF_LIMITS_GET = 0x6,
+	PERF_LEVEL_SET = 0x7,
+	PERF_LEVEL_GET = 0x8,
+	PERF_NOTIFY_LIMITS = 0x9,
+	PERF_NOTIFY_LEVEL = 0xa,
+};
+
+struct scmi_opp {
+	u32 perf;
+	u32 power;
+	u32 trans_latency_us;
+};
+
+struct scmi_msg_resp_perf_attributes {
+	__le16 num_domains;
+	__le16 flags;
+#define POWER_SCALE_IN_MILLIWATT(x)	((x) & BIT(0))
+	__le32 stats_addr_low;
+	__le32 stats_addr_high;
+	__le32 stats_size;
+};
+
+struct scmi_msg_resp_perf_domain_attributes {
+	__le32 flags;
+#define SUPPORTS_SET_LIMITS(x)		((x) & BIT(31))
+#define SUPPORTS_SET_PERF_LVL(x)	((x) & BIT(30))
+#define SUPPORTS_PERF_LIMIT_NOTIFY(x)	((x) & BIT(29))
+#define SUPPORTS_PERF_LEVEL_NOTIFY(x)	((x) & BIT(28))
+	__le32 rate_limit_us;
+	__le32 sustained_freq_khz;
+	__le32 sustained_perf_level;
+	    u8 name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_msg_perf_describe_levels {
+	__le32 domain;
+	__le32 level_index;
+};
+
+struct scmi_perf_set_limits {
+	__le32 domain;
+	__le32 max_level;
+	__le32 min_level;
+};
+
+struct scmi_perf_get_limits {
+	__le32 max_level;
+	__le32 min_level;
+};
+
+struct scmi_perf_set_level {
+	__le32 domain;
+	__le32 level;
+};
+
+struct scmi_perf_notify_level_or_limits {
+	__le32 domain;
+	__le32 notify_enable;
+};
+
+struct scmi_msg_resp_perf_describe_levels {
+	__le16 num_returned;
+	__le16 num_remaining;
+	struct {
+		__le32 perf_val;
+		__le32 power;
+		__le16 transition_latency_us;
+		__le16 reserved;
+	} opp[0];
+};
+
+struct perf_dom_info {
+	bool set_limits;
+	bool set_perf;
+	bool perf_limit_notify;
+	bool perf_level_notify;
+	u32 opp_count;
+	u32 sustained_freq_khz;
+	u32 sustained_perf_level;
+	u32 mult_factor;
+	char name[SCMI_MAX_STR_SIZE];
+	struct scmi_opp opp[MAX_OPPS];
+};
+
+struct scmi_perf_info {
+	int num_domains;
+	bool power_scale_mw;
+	u64 stats_addr;
+	u32 stats_size;
+	struct perf_dom_info *dom_info;
+};
+
+static int scmi_perf_attributes_get(const struct scmi_handle *handle,
+				    struct scmi_perf_info *pi)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_perf_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+				 SCMI_PROTOCOL_PERF, 0, sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	attr = t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		u16 flags = le16_to_cpu(attr->flags);
+
+		pi->num_domains = le16_to_cpu(attr->num_domains);
+		pi->power_scale_mw = POWER_SCALE_IN_MILLIWATT(flags);
+		pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
+				(u64)le32_to_cpu(attr->stats_addr_high) << 32;
+		pi->stats_size = le32_to_cpu(attr->stats_size);
+	}
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int
+scmi_perf_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
+				struct perf_dom_info *dom_info)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_perf_domain_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, PERF_DOMAIN_ATTRIBUTES,
+				 SCMI_PROTOCOL_PERF, sizeof(domain),
+				 sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(domain);
+	attr = t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		u32 flags = le32_to_cpu(attr->flags);
+
+		dom_info->set_limits = SUPPORTS_SET_LIMITS(flags);
+		dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags);
+		dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags);
+		dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags);
+		dom_info->sustained_freq_khz =
+					le32_to_cpu(attr->sustained_freq_khz);
+		dom_info->sustained_perf_level =
+					le32_to_cpu(attr->sustained_perf_level);
+		dom_info->mult_factor =	(dom_info->sustained_freq_khz * 1000) /
+					dom_info->sustained_perf_level;
+		memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
+	}
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int opp_cmp_func(const void *opp1, const void *opp2)
+{
+	const struct scmi_opp *t1 = opp1, *t2 = opp2;
+
+	return t1->perf - t2->perf;
+}
+
+static int
+scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain,
+			      struct perf_dom_info *perf_dom)
+{
+	int ret, cnt;
+	u32 tot_opp_cnt = 0;
+	u16 num_returned, num_remaining;
+	struct scmi_xfer *t;
+	struct scmi_opp *opp;
+	struct scmi_msg_perf_describe_levels *dom_info;
+	struct scmi_msg_resp_perf_describe_levels *level_info;
+
+	ret = scmi_one_xfer_init(handle, PERF_DESCRIBE_LEVELS,
+				 SCMI_PROTOCOL_PERF, sizeof(*dom_info), 0, &t);
+	if (ret)
+		return ret;
+
+	dom_info = t->tx.buf;
+	level_info = t->rx.buf;
+
+	do {
+		dom_info->domain = cpu_to_le32(domain);
+		/* Set the number of OPPs to be skipped/already read */
+		dom_info->level_index = cpu_to_le32(tot_opp_cnt);
+
+		ret = scmi_do_xfer(handle, t);
+		if (ret)
+			break;
+
+		num_returned = le16_to_cpu(level_info->num_returned);
+		num_remaining = le16_to_cpu(level_info->num_remaining);
+		if (tot_opp_cnt + num_returned > MAX_OPPS) {
+			dev_err(handle->dev, "No. of OPPs exceeded MAX_OPPS");
+			break;
+		}
+
+		opp = &perf_dom->opp[tot_opp_cnt];
+		for (cnt = 0; cnt < num_returned; cnt++, opp++) {
+			opp->perf = le32_to_cpu(level_info->opp[cnt].perf_val);
+			opp->power = le32_to_cpu(level_info->opp[cnt].power);
+			opp->trans_latency_us = le16_to_cpu(
+				level_info->opp[cnt].transition_latency_us);
+
+			dev_dbg(handle->dev, "Level %d Power %d Latency %dus\n",
+				opp->perf, opp->power, opp->trans_latency_us);
+		}
+
+		tot_opp_cnt += num_returned;
+		/*
+		 * check for both returned and remaining to avoid infinite
+		 * loop due to buggy firmware
+		 */
+	} while (num_returned && num_remaining);
+
+	perf_dom->opp_count = tot_opp_cnt;
+	scmi_one_xfer_put(handle, t);
+
+	sort(perf_dom->opp, tot_opp_cnt, sizeof(*opp), opp_cmp_func, NULL);
+	return ret;
+}
+
+static int scmi_perf_limits_set(const struct scmi_handle *handle, u32 domain,
+				u32 max_perf, u32 min_perf)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_perf_set_limits *limits;
+
+	ret = scmi_one_xfer_init(handle, PERF_LIMITS_SET, SCMI_PROTOCOL_PERF,
+				 sizeof(*limits), 0, &t);
+	if (ret)
+		return ret;
+
+	limits = t->tx.buf;
+	limits->domain = cpu_to_le32(domain);
+	limits->max_level = cpu_to_le32(max_perf);
+	limits->min_level = cpu_to_le32(min_perf);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
+				u32 *max_perf, u32 *min_perf)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_perf_get_limits *limits;
+
+	ret = scmi_one_xfer_init(handle, PERF_LIMITS_GET, SCMI_PROTOCOL_PERF,
+				 sizeof(__le32), 0, &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(domain);
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		limits = t->rx.buf;
+
+		*max_perf = le32_to_cpu(limits->max_level);
+		*min_perf = le32_to_cpu(limits->min_level);
+	}
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int
+scmi_perf_level_set(const struct scmi_handle *handle, u32 domain, u32 level)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_perf_set_level *lvl;
+
+	ret = scmi_one_xfer_init(handle, PERF_LEVEL_SET, SCMI_PROTOCOL_PERF,
+				 sizeof(*lvl), 0, &t);
+	if (ret)
+		return ret;
+
+	lvl = t->tx.buf;
+	lvl->domain = cpu_to_le32(domain);
+	lvl->level = cpu_to_le32(level);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int
+scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, u32 *level)
+{
+	int ret;
+	struct scmi_xfer *t;
+
+	ret = scmi_one_xfer_init(handle, PERF_LEVEL_GET, SCMI_PROTOCOL_PERF,
+				 sizeof(u32), sizeof(u32), &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(domain);
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret)
+		*level = le32_to_cpu(*(__le32 *)t->rx.buf);
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int __scmi_perf_notify_enable(const struct scmi_handle *handle, u32 cmd,
+				     u32 domain, bool enable)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_perf_notify_level_or_limits *notify;
+
+	ret = scmi_one_xfer_init(handle, cmd, SCMI_PROTOCOL_PERF,
+				 sizeof(*notify), 0, &t);
+	if (ret)
+		return ret;
+
+	notify = t->tx.buf;
+	notify->domain = cpu_to_le32(domain);
+	notify->notify_enable = cpu_to_le32(enable & BIT(0));
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int scmi_perf_limits_notify_enable(const struct scmi_handle *handle,
+					  u32 domain, bool enable)
+{
+	return __scmi_perf_notify_enable(handle, PERF_NOTIFY_LIMITS,
+					 domain, enable);
+}
+
+static int scmi_perf_level_notify_enable(const struct scmi_handle *handle,
+					 u32 domain, bool enable)
+{
+	return __scmi_perf_notify_enable(handle, PERF_NOTIFY_LEVEL,
+					 domain, enable);
+}
+
+/* Device specific ops */
+static int scmi_dev_domain_id(struct device *dev)
+{
+	struct of_phandle_args clkspec;
+
+	if (of_parse_phandle_with_args(dev->of_node, "clocks", "#clock-cells",
+				       0, &clkspec))
+		return -EINVAL;
+
+	return clkspec.args[0];
+}
+
+static int scmi_dvfs_add_opps_to_device(const struct scmi_handle *handle,
+					struct device *dev)
+{
+	int idx, ret, domain;
+	unsigned long freq;
+	struct scmi_opp *opp;
+	struct perf_dom_info *dom;
+	struct scmi_perf_info *pi = handle->perf_priv;
+
+	domain = scmi_dev_domain_id(dev);
+	if (domain < 0)
+		return domain;
+
+	dom = pi->dom_info + domain;
+	if (!dom)
+		return -EIO;
+
+	for (opp = dom->opp, idx = 0; idx < dom->opp_count; idx++, opp++) {
+		freq = opp->perf * dom->mult_factor;
+
+		ret = dev_pm_opp_add(dev, freq, 0);
+		if (ret) {
+			dev_warn(dev, "failed to add opp %luHz\n", freq);
+
+			while (idx-- > 0) {
+				freq = (--opp)->perf * dom->mult_factor;
+				dev_pm_opp_remove(dev, freq);
+			}
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static int scmi_dvfs_get_transition_latency(const struct scmi_handle *handle,
+					    struct device *dev)
+{
+	struct perf_dom_info *dom;
+	struct scmi_perf_info *pi = handle->perf_priv;
+	int domain = scmi_dev_domain_id(dev);
+
+	if (domain < 0)
+		return domain;
+
+	dom = pi->dom_info + domain;
+	if (!dom)
+		return -EIO;
+
+	/* uS to nS */
+	return dom->opp[dom->opp_count - 1].trans_latency_us * 1000;
+}
+
+static int scmi_dvfs_freq_set(const struct scmi_handle *handle, u32 domain,
+			      unsigned long freq)
+{
+	struct scmi_perf_info *pi = handle->perf_priv;
+	struct perf_dom_info *dom = pi->dom_info + domain;
+
+	return scmi_perf_level_set(handle, domain, freq / dom->mult_factor);
+}
+
+static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain,
+			      unsigned long *freq)
+{
+	int ret;
+	u32 level;
+	struct scmi_perf_info *pi = handle->perf_priv;
+	struct perf_dom_info *dom = pi->dom_info + domain;
+
+	ret = scmi_perf_level_get(handle, domain, &level);
+	if (!ret)
+		*freq = level * dom->mult_factor;
+
+	return ret;
+}
+
+static struct scmi_perf_ops perf_ops = {
+	.limits_set = scmi_perf_limits_set,
+	.limits_get = scmi_perf_limits_get,
+	.level_set = scmi_perf_level_set,
+	.level_get = scmi_perf_level_get,
+	.limits_notify_enable = scmi_perf_limits_notify_enable,
+	.level_notify_enable = scmi_perf_level_notify_enable,
+	.device_domain_id = scmi_dev_domain_id,
+	.get_transition_latency = scmi_dvfs_get_transition_latency,
+	.add_opps_to_device = scmi_dvfs_add_opps_to_device,
+	.freq_set = scmi_dvfs_freq_set,
+	.freq_get = scmi_dvfs_freq_get,
+};
+
+static int scmi_perf_protocol_init(struct scmi_handle *handle)
+{
+	int domain;
+	u32 version;
+	struct scmi_perf_info *pinfo;
+
+	scmi_version_get(handle, SCMI_PROTOCOL_PERF, &version);
+
+	dev_dbg(handle->dev, "Performance Version %d.%d\n",
+		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+	pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
+	if (!pinfo)
+		return -ENOMEM;
+
+	scmi_perf_attributes_get(handle, pinfo);
+
+	pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains,
+				       sizeof(*pinfo->dom_info), GFP_KERNEL);
+	if (!pinfo->dom_info)
+		return -ENOMEM;
+
+	for (domain = 0; domain < pinfo->num_domains; domain++) {
+		struct perf_dom_info *dom = pinfo->dom_info + domain;
+
+		scmi_perf_domain_attributes_get(handle, domain, dom);
+		scmi_perf_describe_levels_get(handle, domain, dom);
+	}
+
+	handle->perf_ops = &perf_ops;
+	handle->perf_priv = pinfo;
+
+	return 0;
+}
+
+static int __init scmi_perf_init(void)
+{
+	return scmi_protocol_register(SCMI_PROTOCOL_PERF,
+				      &scmi_perf_protocol_init);
+}
+subsys_initcall(scmi_perf_init);
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 7b72c75cb560..8fa189ca0fed 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -44,15 +44,49 @@ struct scmi_revision_info {
 	char sub_vendor_id[SCMI_MAX_STR_SIZE];
 };
 
+struct scmi_handle;
+
+/**
+ * struct scmi_perf_ops - represents the various operations provided
+ *	by SCMI Performance Protocol
+ *
+ * @limits_set: sets limits on the performance level of a domain
+ * @limits_get: gets limits on the performance level of a domain
+ * @level_set: sets the performance level of a domain
+ * @level_get: gets the performance level of a domain
+ * @limits_notify_enable: requests notifications from the platform for changes
+ *	in the allowed maximum and minimum performance levels
+ * @level_notify_enable: requests notifications from the platform when the
+ *	performance level for a domain changes in value
+ */
+struct scmi_perf_ops {
+	int (*limits_set)(const struct scmi_handle *, u32, u32, u32);
+	int (*limits_get)(const struct scmi_handle *, u32, u32 *, u32 *);
+	int (*level_set)(const struct scmi_handle *, u32, u32);
+	int (*level_get)(const struct scmi_handle *, u32, u32 *);
+	int (*limits_notify_enable)(const struct scmi_handle *, u32, bool);
+	int (*level_notify_enable)(const struct scmi_handle *, u32, bool);
+	int (*device_domain_id)(struct device *);
+	int (*get_transition_latency)(const struct scmi_handle *,
+				      struct device *);
+	int (*add_opps_to_device)(const struct scmi_handle *, struct device *);
+	int (*freq_set)(const struct scmi_handle *, u32, unsigned long);
+	int (*freq_get)(const struct scmi_handle *, u32, unsigned long *);
+};
+
 /**
  * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
  *
  * @dev: pointer to the SCMI device
  * @version: pointer to the structure containing SCMI version information
+ * @perf_ops: pointer to set of performance protocol operations
  */
 struct scmi_handle {
 	struct device *dev;
 	struct scmi_revision_info *version;
+	struct scmi_perf_ops *perf_ops;
+	/* for protocol internal use */
+	void *perf_priv;
 };
 
 enum scmi_std_protocol {
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 07/20] firmware: arm_scmi: add initial support for clock protocol
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

The clock protocol is intended for management of clocks. It is used to
enable or disable clocks, and to set and get the clock rates. This
protocol provides commands to describe the protocol version, discover
various implementation specific attributes, describe a clock, enable
and disable a clock and get/set the rate of the clock synchronously or
asynchronously.

This patch adds initial support for the clock protocol.

Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/Makefile |   2 +-
 drivers/firmware/arm_scmi/clock.c  | 353 +++++++++++++++++++++++++++++++++++++
 include/linux/scmi_protocol.h      |  41 +++++
 3 files changed, 395 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/arm_scmi/clock.c

diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 687cbbfb3af6..2130ee9ac825 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,4 +1,4 @@
 obj-y	= scmi-bus.o scmi-driver.o scmi-protocols.o
 scmi-bus-y = bus.o
 scmi-driver-y = driver.o
-scmi-protocols-y = base.o perf.o
+scmi-protocols-y = base.o clock.o perf.o
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
new file mode 100644
index 000000000000..0fac1d4b377b
--- /dev/null
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -0,0 +1,353 @@
+/*
+ * System Control and Management Interface (SCMI) Clock Protocol
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "common.h"
+
+enum scmi_clock_protocol_cmd {
+	CLOCK_ATTRIBUTES = 0x3,
+	CLOCK_DESCRIBE_RATES = 0x4,
+	CLOCK_RATE_SET = 0x5,
+	CLOCK_RATE_GET = 0x6,
+	CLOCK_CONFIG_SET = 0x7,
+};
+
+struct scmi_msg_resp_clock_protocol_attributes {
+	__le16 num_clocks;
+	    u8 max_async_req;
+	    u8 reserved;
+};
+
+struct scmi_msg_resp_clock_attributes {
+	__le32 attributes;
+#define	CLOCK_ENABLE	BIT(0)
+	    u8 name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_clock_set_config {
+	__le32 id;
+	__le32 attributes;
+};
+
+struct scmi_msg_clock_describe_rates {
+	__le32 id;
+	__le32 rate_index;
+};
+
+struct scmi_msg_resp_clock_describe_rates {
+	__le32 num_rates_flags;
+#define NUM_RETURNED(x)		((x) & 0xfff)
+#define RATE_DISCRETE(x)	!((x) & BIT(12))
+#define NUM_REMAINING(x)	((x) >> 16)
+	struct {
+		__le32 value_low;
+		__le32 value_high;
+	} rate[0];
+#define RATE_TO_U64(X)		\
+({				\
+	typeof(X) x = (X);	\
+	le32_to_cpu((x).value_low) | (u64)le32_to_cpu((x).value_high) << 32; \
+})
+};
+
+struct scmi_clock_set_rate {
+	__le32 flags;
+#define CLOCK_SET_ASYNC		BIT(0)
+#define CLOCK_SET_DELAYED	BIT(1)
+#define CLOCK_SET_ROUND_UP	BIT(2)
+#define CLOCK_SET_ROUND_AUTO	BIT(3)
+	__le32 id;
+	__le32 value_low;
+	__le32 value_high;
+};
+
+struct clock_info {
+	int num_clocks;
+	int max_async_req;
+	struct scmi_clock_info *clk;
+};
+
+static int scmi_clock_protocol_attributes_get(const struct scmi_handle *handle,
+					      struct clock_info *ci)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_clock_protocol_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+				 SCMI_PROTOCOL_CLOCK, 0, sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	attr = t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		ci->num_clocks = le16_to_cpu(attr->num_clocks);
+		ci->max_async_req = attr->max_async_req;
+	}
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int scmi_clock_attributes_get(const struct scmi_handle *handle,
+				     u32 clk_id, struct scmi_clock_info *clk)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_clock_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, CLOCK_ATTRIBUTES, SCMI_PROTOCOL_CLOCK,
+				 sizeof(clk_id), sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(clk_id);
+	attr = t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret)
+		memcpy(clk->name, attr->name, SCMI_MAX_STR_SIZE);
+	else
+		clk->name[0] = '\0';
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int
+scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id,
+			      struct scmi_clock_info *clk)
+{
+	u64 *rate;
+	int ret, cnt;
+	bool rate_discrete;
+	u32 tot_rate_cnt = 0, rates_flag;
+	u16 num_returned, num_remaining;
+	struct scmi_xfer *t;
+	struct scmi_msg_clock_describe_rates *clk_desc;
+	struct scmi_msg_resp_clock_describe_rates *rlist;
+
+	ret = scmi_one_xfer_init(handle, CLOCK_DESCRIBE_RATES,
+				 SCMI_PROTOCOL_CLOCK, sizeof(*clk_desc), 0, &t);
+	if (ret)
+		return ret;
+
+	clk_desc = t->tx.buf;
+	rlist = t->rx.buf;
+
+	do {
+		clk_desc->id = cpu_to_le32(clk_id);
+		/* Set the number of rates to be skipped/already read */
+		clk_desc->rate_index = cpu_to_le32(tot_rate_cnt);
+
+		ret = scmi_do_xfer(handle, t);
+		if (ret)
+			break;
+
+		rates_flag = le32_to_cpu(rlist->num_rates_flags);
+		num_remaining = NUM_REMAINING(rates_flag);
+		rate_discrete = RATE_DISCRETE(rates_flag);
+		num_returned = NUM_RETURNED(rates_flag);
+
+		if (tot_rate_cnt + num_returned > SCMI_MAX_NUM_RATES) {
+			dev_err(handle->dev, "No. of rates > MAX_NUM_RATES");
+			break;
+		}
+
+		if (!rate_discrete) {
+			clk->range.min_rate = RATE_TO_U64(rlist->rate[0]);
+			clk->range.max_rate = RATE_TO_U64(rlist->rate[1]);
+			clk->range.step_size = RATE_TO_U64(rlist->rate[2]);
+			dev_dbg(handle->dev, "Min %llu Max %llu Step %llu Hz\n",
+				clk->range.min_rate, clk->range.max_rate,
+				clk->range.step_size);
+			break;
+		}
+
+		rate = &clk->list.rates[tot_rate_cnt];
+		for (cnt = 0; cnt < num_returned; cnt++, rate++) {
+			*rate = RATE_TO_U64(rlist->rate[cnt]);
+			dev_dbg(handle->dev, "Rate %llu Hz\n", *rate);
+		}
+
+		tot_rate_cnt += num_returned;
+		/*
+		 * check for both returned and remaining to avoid infinite
+		 * loop due to buggy firmware
+		 */
+	} while (num_returned && num_remaining);
+
+	if (rate_discrete)
+		clk->list.num_rates = tot_rate_cnt;
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int
+scmi_clock_rate_get(const struct scmi_handle *handle, u32 clk_id, u64 *value)
+{
+	int ret;
+	struct scmi_xfer *t;
+
+	ret = scmi_one_xfer_init(handle, CLOCK_RATE_GET, SCMI_PROTOCOL_CLOCK,
+				 sizeof(__le32), sizeof(u64), &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(clk_id);
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		__le32 *pval = t->rx.buf;
+
+		*value = le32_to_cpu(*pval);
+		*value |= (u64)le32_to_cpu(*(pval + 1)) << 32;
+	}
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int scmi_clock_rate_set(const struct scmi_handle *handle, u32 clk_id,
+			       u32 config, u64 rate)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_clock_set_rate *cfg;
+
+	ret = scmi_one_xfer_init(handle, CLOCK_RATE_SET, SCMI_PROTOCOL_CLOCK,
+				 sizeof(*cfg), 0, &t);
+	if (ret)
+		return ret;
+
+	cfg = t->tx.buf;
+	cfg->flags = cpu_to_le32(config);
+	cfg->id = cpu_to_le32(clk_id);
+	cfg->value_low = cpu_to_le32(rate & 0xffffffff);
+	cfg->value_high = cpu_to_le32(rate >> 32);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int
+scmi_clock_config_set(const struct scmi_handle *handle, u32 clk_id, u32 config)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_clock_set_config *cfg;
+
+	ret = scmi_one_xfer_init(handle, CLOCK_CONFIG_SET, SCMI_PROTOCOL_CLOCK,
+				 sizeof(*cfg), 0, &t);
+	if (ret)
+		return ret;
+
+	cfg = t->tx.buf;
+	cfg->id = cpu_to_le32(clk_id);
+	cfg->attributes = cpu_to_le32(config);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int scmi_clock_enable(const struct scmi_handle *handle, u32 clk_id)
+{
+	return scmi_clock_config_set(handle, clk_id, CLOCK_ENABLE);
+}
+
+static int scmi_clock_disable(const struct scmi_handle *handle, u32 clk_id)
+{
+	return scmi_clock_config_set(handle, clk_id, 0);
+}
+
+static int scmi_clock_count_get(const struct scmi_handle *handle)
+{
+	struct clock_info *ci = handle->clk_priv;
+
+	return ci->num_clocks;
+}
+
+static const struct scmi_clock_info *
+scmi_clock_info_get(const struct scmi_handle *handle, u32 clk_id)
+{
+	struct clock_info *ci = handle->clk_priv;
+	struct scmi_clock_info *clk = ci->clk + clk_id;
+
+	if (!clk->name || !clk->name[0])
+		return NULL;
+
+	return clk;
+}
+
+static struct scmi_clk_ops clk_ops = {
+	.count_get = scmi_clock_count_get,
+	.info_get = scmi_clock_info_get,
+	.rate_get = scmi_clock_rate_get,
+	.rate_set = scmi_clock_rate_set,
+	.enable = scmi_clock_enable,
+	.disable = scmi_clock_disable,
+};
+
+static int scmi_clock_protocol_init(struct scmi_handle *handle)
+{
+	u32 version;
+	int clkid, ret;
+	struct clock_info *cinfo;
+
+	scmi_version_get(handle, SCMI_PROTOCOL_CLOCK, &version);
+
+	dev_dbg(handle->dev, "Clock Version %d.%d\n",
+		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+	cinfo = devm_kzalloc(handle->dev, sizeof(*cinfo), GFP_KERNEL);
+	if (!cinfo)
+		return -ENOMEM;
+
+	scmi_clock_protocol_attributes_get(handle, cinfo);
+
+	cinfo->clk = devm_kcalloc(handle->dev, cinfo->num_clocks,
+				  sizeof(*cinfo->clk), GFP_KERNEL);
+	if (!cinfo->clk)
+		return -ENOMEM;
+
+	for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
+		struct scmi_clock_info *clk = cinfo->clk + clkid;
+
+		ret = scmi_clock_attributes_get(handle, clkid, clk);
+		if (!ret)
+			scmi_clock_describe_rates_get(handle, clkid, clk);
+	}
+
+	handle->clk_ops = &clk_ops;
+	handle->clk_priv = cinfo;
+
+	return 0;
+}
+
+static int __init scmi_clock_init(void)
+{
+	return scmi_protocol_register(SCMI_PROTOCOL_CLOCK,
+				      &scmi_clock_protocol_init);
+}
+subsys_initcall(scmi_clock_init);
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 8fa189ca0fed..0c2fd3ab464c 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -19,6 +19,7 @@
 #include <linux/types.h>
 
 #define SCMI_MAX_STR_SIZE	16
+#define SCMI_MAX_NUM_RATES	16
 
 /**
  * struct scmi_revision_info - version information structure
@@ -44,9 +45,46 @@ struct scmi_revision_info {
 	char sub_vendor_id[SCMI_MAX_STR_SIZE];
 };
 
+struct scmi_clock_info {
+	char name[SCMI_MAX_STR_SIZE];
+	bool rate_discrete;
+	union {
+		struct {
+			int num_rates;
+			u64 rates[SCMI_MAX_NUM_RATES];
+		} list;
+		struct {
+			u64 min_rate;
+			u64 max_rate;
+			u64 step_size;
+		} range;
+	};
+};
+
 struct scmi_handle;
 
 /**
+ * struct scmi_clk_ops - represents the various operations provided
+ *	by SCMI Clock Protocol
+ *
+ * @count_get: get the count of clocks provided by SCMI
+ * @info_get: get the information of the specified clock
+ * @rate_get: request the current clock rate of a clock
+ * @rate_set: set the clock rate of a clock
+ * @enable: enables the specified clock
+ * @disable: disables the specified clock
+ */
+struct scmi_clk_ops {
+	int (*count_get)(const struct scmi_handle *);
+	const struct scmi_clock_info *(*info_get)(const struct scmi_handle *,
+						  u32);
+	int (*rate_get)(const struct scmi_handle *, u32, u64*);
+	int (*rate_set)(const struct scmi_handle *, u32, u32, u64);
+	int (*enable)(const struct scmi_handle *, u32);
+	int (*disable)(const struct scmi_handle *, u32);
+};
+
+/**
  * struct scmi_perf_ops - represents the various operations provided
  *	by SCMI Performance Protocol
  *
@@ -80,13 +118,16 @@ struct scmi_perf_ops {
  * @dev: pointer to the SCMI device
  * @version: pointer to the structure containing SCMI version information
  * @perf_ops: pointer to set of performance protocol operations
+ * @clk_ops: pointer to set of clock protocol operations
  */
 struct scmi_handle {
 	struct device *dev;
 	struct scmi_revision_info *version;
 	struct scmi_perf_ops *perf_ops;
+	struct scmi_clk_ops *clk_ops;
 	/* for protocol internal use */
 	void *perf_priv;
+	void *clk_priv;
 };
 
 enum scmi_std_protocol {
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 08/20] firmware: arm_scmi: add initial support for power protocol
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

The power protocol is intended for management of power states of various
power domains. The power domain management protocol provides commands to
describe the protocol version, discover the implementation specific
attributes, set and get the power state of a domain.

This patch adds support for the above mention features of the protocol.

Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
--
 drivers/firmware/arm_scmi/Makefile |   2 +-
 drivers/firmware/arm_scmi/power.c  | 242 +++++++++++++++++++++++++++++++++++++
 include/linux/scmi_protocol.h      |  28 +++++
 3 files changed, 271 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/arm_scmi/power.c
---
 drivers/firmware/arm_scmi/Makefile |   2 +-
 drivers/firmware/arm_scmi/power.c  | 255 +++++++++++++++++++++++++++++++++++++
 include/linux/scmi_protocol.h      |  29 +++++
 3 files changed, 285 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/arm_scmi/power.c

diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 2130ee9ac825..420c761ced94 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,4 +1,4 @@
 obj-y	= scmi-bus.o scmi-driver.o scmi-protocols.o
 scmi-bus-y = bus.o
 scmi-driver-y = driver.o
-scmi-protocols-y = base.o clock.o perf.o
+scmi-protocols-y = base.o clock.o perf.o power.o
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
new file mode 100644
index 000000000000..50195bd6bbd2
--- /dev/null
+++ b/drivers/firmware/arm_scmi/power.c
@@ -0,0 +1,255 @@
+/*
+ * System Control and Management Interface (SCMI) Power Protocol
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "common.h"
+
+enum scmi_power_protocol_cmd {
+	POWER_DOMAIN_ATTRIBUTES = 0x3,
+	POWER_STATE_SET = 0x4,
+	POWER_STATE_GET = 0x5,
+	POWER_STATE_NOTIFY = 0x6,
+};
+
+struct scmi_msg_resp_power_attributes {
+	__le16 num_domains;
+	__le16 reserved;
+	__le32 stats_addr_low;
+	__le32 stats_addr_high;
+	__le32 stats_size;
+};
+
+struct scmi_msg_resp_power_domain_attributes {
+	__le32 flags;
+#define SUPPORTS_STATE_SET_NOTIFY(x)	((x) & BIT(31))
+#define SUPPORTS_STATE_SET_ASYNC(x)	((x) & BIT(30))
+#define SUPPORTS_STATE_SET_SYNC(x)	((x) & BIT(29))
+	    u8 name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_power_set_state {
+	__le32 flags;
+#define STATE_SET_ASYNC		BIT(0)
+	__le32 domain;
+	__le32 state;
+};
+
+struct scmi_power_state_notify {
+	__le32 domain;
+	__le32 notify_enable;
+};
+
+struct power_dom_info {
+	bool state_set_sync;
+	bool state_set_async;
+	bool state_set_notify;
+	char name[SCMI_MAX_STR_SIZE];
+};
+
+struct scmi_power_info {
+	int num_domains;
+	u64 stats_addr;
+	u32 stats_size;
+	struct power_dom_info *dom_info;
+};
+
+static int scmi_power_attributes_get(const struct scmi_handle *handle,
+				     struct scmi_power_info *pi)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_power_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+				 SCMI_PROTOCOL_POWER, 0, sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	attr = t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		pi->num_domains = le16_to_cpu(attr->num_domains);
+		pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
+				(u64)le32_to_cpu(attr->stats_addr_high) << 32;
+		pi->stats_size = le32_to_cpu(attr->stats_size);
+	}
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int
+scmi_power_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
+				 struct power_dom_info *dom_info)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_power_domain_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, POWER_DOMAIN_ATTRIBUTES,
+				 SCMI_PROTOCOL_POWER, sizeof(domain),
+				 sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(domain);
+	attr = t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		u32 flags = le32_to_cpu(attr->flags);
+
+		dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
+		dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
+		dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
+		memcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
+	}
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int
+scmi_power_state_set(const struct scmi_handle *handle, u32 domain, u32 state)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_power_set_state *st;
+
+	ret = scmi_one_xfer_init(handle, POWER_STATE_SET, SCMI_PROTOCOL_POWER,
+				 sizeof(*st), 0, &t);
+	if (ret)
+		return ret;
+
+	st = t->tx.buf;
+	st->flags = cpu_to_le32(0);
+	st->domain = cpu_to_le32(domain);
+	st->state = cpu_to_le32(state);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int
+scmi_power_state_get(const struct scmi_handle *handle, u32 domain, u32 *state)
+{
+	int ret;
+	struct scmi_xfer *t;
+
+	ret = scmi_one_xfer_init(handle, POWER_STATE_GET, SCMI_PROTOCOL_POWER,
+				 sizeof(u32), sizeof(u32), &t);
+	if (ret)
+		return ret;
+
+	*(__le32 *)t->tx.buf = cpu_to_le32(domain);
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret)
+		*state = le32_to_cpu(*(__le32 *)t->rx.buf);
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int scmi_power_state_notify_enable(const struct scmi_handle *handle,
+					  u32 domain, bool enable)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_power_state_notify *notify;
+
+	ret = scmi_one_xfer_init(handle, POWER_STATE_NOTIFY,
+				 SCMI_PROTOCOL_POWER, sizeof(*notify), 0, &t);
+	if (ret)
+		return ret;
+
+	notify = t->tx.buf;
+	notify->domain = cpu_to_le32(domain);
+	notify->notify_enable = cpu_to_le32(enable & BIT(0));
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int scmi_power_num_domains_get(const struct scmi_handle *handle)
+{
+	struct scmi_power_info *pi = handle->power_priv;
+
+	return pi->num_domains;
+}
+
+static char *scmi_power_name_get(const struct scmi_handle *handle, u32 domain)
+{
+	struct scmi_power_info *pi = handle->power_priv;
+	struct power_dom_info *dom = pi->dom_info + domain;
+
+	return dom->name;
+}
+
+static struct scmi_power_ops power_ops = {
+	.num_domains_get = scmi_power_num_domains_get,
+	.name_get = scmi_power_name_get,
+	.state_set = scmi_power_state_set,
+	.state_get = scmi_power_state_get,
+	.state_notify_enable = scmi_power_state_notify_enable,
+};
+
+static int scmi_power_protocol_init(struct scmi_handle *handle)
+{
+	int domain;
+	u32 version;
+	struct scmi_power_info *pinfo;
+
+	scmi_version_get(handle, SCMI_PROTOCOL_POWER, &version);
+
+	dev_dbg(handle->dev, "Power Version %d.%d\n",
+		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+	pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
+	if (!pinfo)
+		return -ENOMEM;
+
+	scmi_power_attributes_get(handle, pinfo);
+
+	pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains,
+				       sizeof(*pinfo->dom_info), GFP_KERNEL);
+	if (!pinfo->dom_info)
+		return -ENOMEM;
+
+	for (domain = 0; domain < pinfo->num_domains; domain++) {
+		struct power_dom_info *dom = pinfo->dom_info + domain;
+
+		scmi_power_domain_attributes_get(handle, domain, dom);
+	}
+
+	handle->power_ops = &power_ops;
+	handle->power_priv = pinfo;
+
+	return 0;
+}
+
+static int __init scmi_power_init(void)
+{
+	return scmi_protocol_register(SCMI_PROTOCOL_POWER,
+				      &scmi_power_protocol_init);
+}
+subsys_initcall(scmi_power_init);
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 0c2fd3ab464c..95a645f9039d 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -113,10 +113,37 @@ struct scmi_perf_ops {
 };
 
 /**
+ * struct scmi_power_ops - represents the various operations provided
+ *	by SCMI Power Protocol
+ *
+ * @num_domains_get: get the count of power domains provided by SCMI
+ * @name_get: gets the name of a power domain
+ * @state_set: sets the power state of a power domain
+ * @state_get: gets the power state of a power domain
+ * @state_notify_enable: request notifications from the platform for
+ *	state changes in a specific power domain
+ */
+struct scmi_power_ops {
+	int (*num_domains_get)(const struct scmi_handle *);
+	char *(*name_get)(const struct scmi_handle *, u32);
+#define SCMI_POWER_STATE_TYPE_SHIFT	30
+#define SCMI_POWER_STATE_ID_MASK	(BIT(28) - 1)
+#define SCMI_POWER_STATE_PARAM(type, id) \
+	((((type) & BIT(0)) << SCMI_POWER_STATE_TYPE_SHIFT) | \
+		((id) & SCMI_POWER_STATE_ID_MASK))
+#define SCMI_POWER_STATE_GENERIC_ON	SCMI_POWER_STATE_PARAM(0, 0)
+#define SCMI_POWER_STATE_GENERIC_OFF	SCMI_POWER_STATE_PARAM(1, 0)
+	int (*state_set)(const struct scmi_handle *, u32, u32);
+	int (*state_get)(const struct scmi_handle *, u32, u32 *);
+	int (*state_notify_enable)(const struct scmi_handle *, u32, bool);
+};
+
+/**
  * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
  *
  * @dev: pointer to the SCMI device
  * @version: pointer to the structure containing SCMI version information
+ * @power_ops: pointer to set of power protocol operations
  * @perf_ops: pointer to set of performance protocol operations
  * @clk_ops: pointer to set of clock protocol operations
  */
@@ -125,9 +152,11 @@ struct scmi_handle {
 	struct scmi_revision_info *version;
 	struct scmi_perf_ops *perf_ops;
 	struct scmi_clk_ops *clk_ops;
+	struct scmi_power_ops *power_ops;
 	/* for protocol internal use */
 	void *perf_priv;
 	void *clk_priv;
+	void *power_priv;
 };
 
 enum scmi_std_protocol {
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 09/20] firmware: arm_scmi: add initial support for sensor protocol
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

The sensor protocol provides functions to manage platform sensors, and
provides the commands to describe the protocol version and the various
attribute flags. It also provides commands to discover various sensors
implemented and managed by the platform, read any sensor synchronously
or asynchronously as allowed by the platform, program sensor attributes
and/or configurations, if applicable.

This patch adds support for most of the above features.

Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/Makefile  |   2 +-
 drivers/firmware/arm_scmi/sensors.c | 302 ++++++++++++++++++++++++++++++++++++
 include/linux/scmi_protocol.h       |  42 +++++
 3 files changed, 345 insertions(+), 1 deletion(-)
 create mode 100644 drivers/firmware/arm_scmi/sensors.c

diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 420c761ced94..3236890905b9 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,4 +1,4 @@
 obj-y	= scmi-bus.o scmi-driver.o scmi-protocols.o
 scmi-bus-y = bus.o
 scmi-driver-y = driver.o
-scmi-protocols-y = base.o clock.o perf.o power.o
+scmi-protocols-y = base.o clock.o perf.o power.o sensors.o
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
new file mode 100644
index 000000000000..aee38d3d1c2d
--- /dev/null
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -0,0 +1,302 @@
+/*
+ * System Control and Management Interface (SCMI) Sensor Protocol
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "common.h"
+
+enum scmi_sensor_protocol_cmd {
+	SENSOR_DESCRIPTION_GET = 0x3,
+	SENSOR_CONFIG_SET = 0x4,
+	SENSOR_TRIP_POINT_SET = 0x5,
+	SENSOR_READING_GET = 0x6,
+};
+
+struct scmi_msg_resp_sensor_attributes {
+	__le16 num_sensors;
+	    u8 max_requests;
+	    u8 reserved;
+	__le32 reg_addr_low;
+	__le32 reg_addr_high;
+	__le32 reg_size;
+};
+
+struct scmi_msg_resp_sensor_description {
+	__le16 num_returned;
+	__le16 num_remaining;
+	struct {
+		__le32 id;
+		__le32 attributes_low;
+#define SUPPORTS_ASYNC_READ(x)	((x) & BIT(31))
+#define NUM_TRIP_POINTS(x)	(((x) >> 4) & 0xff)
+		__le32 attributes_high;
+#define SENSOR_TYPE(x)		((x) & 0xff)
+#define SENSOR_SCALE(x)		(((x) >> 11) & 0x3f)
+#define SENSOR_UPDATE_SCALE(x)	(((x) >> 22) & 0x1f)
+#define SENSOR_UPDATE_BASE(x)	(((x) >> 27) & 0x1f)
+		    u8 name[SCMI_MAX_STR_SIZE];
+	} desc[0];
+};
+
+struct scmi_msg_set_sensor_config {
+	__le32 id;
+	__le32 event_control;
+};
+
+struct scmi_msg_set_sensor_trip_point {
+	__le32 id;
+	__le32 event_control;
+#define SENSOR_TP_EVENT_MASK	(0x3)
+#define SENSOR_TP_DISABLED	0x0
+#define SENSOR_TP_POSITIVE	0x1
+#define SENSOR_TP_NEGATIVE	0x2
+#define SENSOR_TP_BOTH		0x3
+#define SENSOR_TP_ID(x)		(((x) & 0xff) << 4)
+	__le32 value_low;
+	__le32 value_high;
+};
+
+struct scmi_msg_sensor_reading_get {
+	__le32 id;
+	__le32 flags;
+#define SENSOR_READ_ASYNC	BIT(0)
+};
+
+struct sensors_info {
+	int num_sensors;
+	int max_requests;
+	u64 reg_addr;
+	u32 reg_size;
+	struct scmi_sensor_info *sensors;
+};
+
+static int scmi_sensor_attributes_get(const struct scmi_handle *handle,
+				      struct sensors_info *si)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_sensor_attributes *attr;
+
+	ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
+				 SCMI_PROTOCOL_SENSOR, 0, sizeof(*attr), &t);
+	if (ret)
+		return ret;
+
+	attr = t->rx.buf;
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		si->num_sensors = le16_to_cpu(attr->num_sensors);
+		si->max_requests = attr->max_requests;
+		si->reg_addr = le32_to_cpu(attr->reg_addr_low) |
+				(u64)le32_to_cpu(attr->reg_addr_high) << 32;
+		si->reg_size = le32_to_cpu(attr->reg_size);
+	}
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int scmi_sensor_description_get(const struct scmi_handle *handle,
+				       struct sensors_info *si)
+{
+	int ret, cnt;
+	u32 desc_index = 0;
+	u16 num_returned, num_remaining;
+	struct scmi_xfer *t;
+	struct scmi_msg_resp_sensor_description *buf;
+
+	ret = scmi_one_xfer_init(handle, SENSOR_DESCRIPTION_GET,
+				 SCMI_PROTOCOL_SENSOR, sizeof(__le32), 0, &t);
+	if (ret)
+		return ret;
+
+	buf = t->rx.buf;
+
+	do {
+		/* Set the number of sensors to be skipped/already read */
+		*(__le32 *)t->tx.buf = cpu_to_le32(desc_index);
+
+		ret = scmi_do_xfer(handle, t);
+		if (ret)
+			break;
+
+		num_returned = le16_to_cpu(buf->num_returned);
+		num_remaining = le16_to_cpu(buf->num_remaining);
+
+		if (desc_index + num_returned > si->num_sensors) {
+			dev_err(handle->dev, "No. of sensors can't exceed %d",
+				si->num_sensors);
+			break;
+		}
+
+		for (cnt = 0; cnt < num_returned; cnt++) {
+			u32 attrh;
+			struct scmi_sensor_info *s;
+
+			attrh = le32_to_cpu(buf->desc[cnt].attributes_high);
+			s = &si->sensors[desc_index + cnt];
+			s->id = le32_to_cpu(buf->desc[cnt].id);
+			s->type = SENSOR_TYPE(attrh);
+			memcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE);
+		}
+
+		desc_index += num_returned;
+		/*
+		 * check for both returned and remaining to avoid infinite
+		 * loop due to buggy firmware
+		 */
+	} while (num_returned && num_remaining);
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int
+scmi_sensor_configuration_set(const struct scmi_handle *handle, u32 sensor_id)
+{
+	int ret;
+	u32 evt_cntl = BIT(0);
+	struct scmi_xfer *t;
+	struct scmi_msg_set_sensor_config *cfg;
+
+	ret = scmi_one_xfer_init(handle, SENSOR_CONFIG_SET,
+				 SCMI_PROTOCOL_SENSOR, sizeof(*cfg), 0, &t);
+	if (ret)
+		return ret;
+
+	cfg = t->tx.buf;
+	cfg->id = cpu_to_le32(sensor_id);
+	cfg->event_control = cpu_to_le32(evt_cntl);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int scmi_sensor_trip_point_set(const struct scmi_handle *handle,
+				      u32 sensor_id, u8 trip_id, u64 trip_value)
+{
+	int ret;
+	u32 evt_cntl = SENSOR_TP_BOTH;
+	struct scmi_xfer *t;
+	struct scmi_msg_set_sensor_trip_point *trip;
+
+	ret = scmi_one_xfer_init(handle, SENSOR_TRIP_POINT_SET,
+				 SCMI_PROTOCOL_SENSOR, sizeof(*trip), 0, &t);
+	if (ret)
+		return ret;
+
+	trip = t->tx.buf;
+	trip->id = cpu_to_le32(sensor_id);
+	trip->event_control = cpu_to_le32(evt_cntl | SENSOR_TP_ID(trip_id));
+	trip->value_low = cpu_to_le32(trip_value & 0xffffffff);
+	trip->value_high = cpu_to_le32(trip_value >> 32);
+
+	ret = scmi_do_xfer(handle, t);
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static int scmi_sensor_reading_get(const struct scmi_handle *handle,
+				   u32 sensor_id, bool async, u64 *value)
+{
+	int ret;
+	struct scmi_xfer *t;
+	struct scmi_msg_sensor_reading_get *sensor;
+
+	ret = scmi_one_xfer_init(handle, SENSOR_READING_GET,
+				 SCMI_PROTOCOL_SENSOR, sizeof(*sensor),
+				 sizeof(u64), &t);
+	if (ret)
+		return ret;
+
+	sensor = t->tx.buf;
+	sensor->id = cpu_to_le32(sensor_id);
+	sensor->flags = cpu_to_le32(async ? SENSOR_READ_ASYNC : 0);
+
+	ret = scmi_do_xfer(handle, t);
+	if (!ret) {
+		__le32 *pval = t->rx.buf;
+
+		*value = le32_to_cpu(*pval);
+		*value |= (u64)le32_to_cpu(*(pval + 1)) << 32;
+	}
+
+	scmi_one_xfer_put(handle, t);
+	return ret;
+}
+
+static const struct scmi_sensor_info *
+scmi_sensor_info_get(const struct scmi_handle *handle, u32 sensor_id)
+{
+	struct sensors_info *si = handle->sensor_priv;
+
+	return si->sensors + sensor_id;
+}
+
+static int scmi_sensor_count_get(const struct scmi_handle *handle)
+{
+	struct sensors_info *si = handle->sensor_priv;
+
+	return si->num_sensors;
+}
+
+static struct scmi_sensor_ops sensor_ops = {
+	.count_get = scmi_sensor_count_get,
+	.info_get = scmi_sensor_info_get,
+	.configuration_set = scmi_sensor_configuration_set,
+	.trip_point_set = scmi_sensor_trip_point_set,
+	.reading_get = scmi_sensor_reading_get,
+};
+
+static int scmi_sensors_protocol_init(struct scmi_handle *handle)
+{
+	u32 version;
+	struct sensors_info *sinfo;
+
+	scmi_version_get(handle, SCMI_PROTOCOL_SENSOR, &version);
+
+	dev_dbg(handle->dev, "Sensor Version %d.%d\n",
+		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
+
+	sinfo = devm_kzalloc(handle->dev, sizeof(*sinfo), GFP_KERNEL);
+	if (!sinfo)
+		return -ENOMEM;
+
+	scmi_sensor_attributes_get(handle, sinfo);
+
+	sinfo->sensors = devm_kcalloc(handle->dev, sinfo->num_sensors,
+				      sizeof(*sinfo->sensors), GFP_KERNEL);
+	if (!sinfo->sensors)
+		return -ENOMEM;
+
+	scmi_sensor_description_get(handle, sinfo);
+
+	handle->sensor_ops = &sensor_ops;
+	handle->sensor_priv = sinfo;
+
+	return 0;
+}
+
+static int __init scmi_sensors_init(void)
+{
+	return scmi_protocol_register(SCMI_PROTOCOL_SENSOR,
+				      &scmi_sensors_protocol_init);
+}
+subsys_initcall(scmi_sensors_init);
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 95a645f9039d..70e2993f6d48 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -138,6 +138,45 @@ struct scmi_power_ops {
 	int (*state_notify_enable)(const struct scmi_handle *, u32, bool);
 };
 
+struct scmi_sensor_info {
+	u32 id;
+	u8 type;
+	char name[SCMI_MAX_STR_SIZE];
+};
+
+/*
+ * Partial list from Distributed Management Task Force (DMTF) specification:
+ * DSP0249 (Platform Level Data Model specification)
+ */
+enum scmi_sensor_class {
+	NONE = 0x0,
+	TEMPERATURE_C = 0x2,
+	VOLTAGE = 0x5,
+	CURRENT = 0x6,
+	POWER = 0x7,
+	ENERGY = 0x8,
+};
+
+/**
+ * struct scmi_sensor_ops - represents the various operations provided
+ *	by SCMI Sensor Protocol
+ *
+ * @count_get: get the count of sensors provided by SCMI
+ * @info_get: get the information of the specified sensor
+ * @configuration_set: control notifications on cross-over events for
+ *	the trip-points
+ * @trip_point_set: selects and configures a trip-point of interest
+ * @reading_get: gets the current value of the sensor
+ */
+struct scmi_sensor_ops {
+	int (*count_get)(const struct scmi_handle *);
+	const struct scmi_sensor_info *(*info_get)(const struct scmi_handle *,
+						   u32);
+	int (*configuration_set)(const struct scmi_handle *, u32);
+	int (*trip_point_set)(const struct scmi_handle *, u32, u8, u64);
+	int (*reading_get)(const struct scmi_handle *, u32, bool, u64 *);
+};
+
 /**
  * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
  *
@@ -146,6 +185,7 @@ struct scmi_power_ops {
  * @power_ops: pointer to set of power protocol operations
  * @perf_ops: pointer to set of performance protocol operations
  * @clk_ops: pointer to set of clock protocol operations
+ * @sensor_ops: pointer to set of sensor protocol operations
  */
 struct scmi_handle {
 	struct device *dev;
@@ -153,10 +193,12 @@ struct scmi_handle {
 	struct scmi_perf_ops *perf_ops;
 	struct scmi_clk_ops *clk_ops;
 	struct scmi_power_ops *power_ops;
+	struct scmi_sensor_ops *sensor_ops;
 	/* for protocol internal use */
 	void *perf_priv;
 	void *clk_priv;
 	void *power_priv;
+	void *sensor_priv;
 };
 
 enum scmi_std_protocol {
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 10/20] firmware: arm_scmi: probe and initialise all the supported protocols
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

Now that we have basic support for all the protocols in the
specification, let's probe them individually and initialise them.

Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/driver.c | 51 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 50 insertions(+), 1 deletion(-)

diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 20d083be474c..11c18ac9816f 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -494,6 +494,21 @@ void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
 	info->protocols_imp = prot_imp;
 }
 
+static bool
+scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id)
+{
+	int i;
+	struct scmi_info *info = handle_to_scmi_info(handle);
+
+	if (!info->protocols_imp)
+		return false;
+
+	for (i = 0; i < MAX_PROTOCOLS_IMP; i++)
+		if (info->protocols_imp[i] == prot_id)
+			return true;
+	return false;
+}
+
 /**
  * scmi_handle_get() - Get the  SCMI handle for a device
  *
@@ -691,6 +706,23 @@ static inline int scmi_mbox_chan_setup(struct scmi_info *info)
 	return 0;
 }
 
+static inline void
+scmi_create_protocol_device(struct device_node *np, struct scmi_info *info,
+			    int prot_id)
+{
+	struct scmi_device *sdev;
+
+	sdev = scmi_device_create(np, info->dev, prot_id);
+	if (!sdev) {
+		dev_err(info->dev, "failed to create %d protocol device\n",
+			prot_id);
+		return;
+	}
+
+	/* setup handle now as the transport is ready */
+	scmi_set_handle(sdev);
+}
+
 static int scmi_probe(struct platform_device *pdev)
 {
 	int ret;
@@ -698,7 +730,7 @@ static int scmi_probe(struct platform_device *pdev)
 	const struct scmi_desc *desc;
 	struct scmi_info *info;
 	struct device *dev = &pdev->dev;
-	struct device_node *np = dev->of_node;
+	struct device_node *child, *np = dev->of_node;
 
 	/* Only mailbox method supported, check for the presence of one */
 	if (scmi_mailbox_check(np)) {
@@ -741,6 +773,23 @@ static int scmi_probe(struct platform_device *pdev)
 	list_add_tail(&info->node, &scmi_list);
 	mutex_unlock(&scmi_list_mutex);
 
+	for_each_available_child_of_node(np, child) {
+		u32 prot_id;
+
+		if (of_property_read_u32(child, "reg", &prot_id))
+			continue;
+
+		prot_id &= MSG_PROTOCOL_ID_MASK;
+
+		if (!scmi_is_protocol_implemented(handle, prot_id)) {
+			dev_err(dev, "SCMI protocol %d not implemented\n",
+				prot_id);
+			continue;
+		}
+
+		scmi_create_protocol_device(child, info, prot_id);
+	}
+
 	return 0;
 }
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 11/20] firmware: arm_scmi: add support for polling based SCMI transfers
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

It would be useful to have options to perform some SCMI transfers
atomically by polling for the completion flag instead of interrupt
driven. The SCMI specification has option to disable the interrupt and
poll for the completion flag in the shared memory.

This patch adds support for polling based SCMI transfers using that
option. This might be used for uninterrupted/atomic DVFS operations
from the scheduler context.

Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/driver.c | 49 +++++++++++++++++++++++++++++++-------
 1 file changed, 41 insertions(+), 8 deletions(-)

diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 11c18ac9816f..0c9dda72f10c 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -26,9 +26,11 @@
  */
 
 #include <linux/bitmap.h>
+#include <linux/delay.h>
 #include <linux/export.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/ktime.h>
 #include <linux/mailbox_client.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
@@ -363,6 +365,21 @@ void scmi_one_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer)
 	up(&minfo->sem_xfer_count);
 }
 
+static bool
+scmi_xfer_poll_done(const struct scmi_info *info, struct scmi_xfer *xfer)
+{
+	struct scmi_shared_mem *mem = info->tx_payload;
+	u16 xfer_id = MSG_XTRACT_TOKEN(le32_to_cpu(mem->msg_header));
+
+	if (xfer->hdr.seq != xfer_id)
+		return false;
+
+	return le32_to_cpu(mem->channel_status) &
+		(SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR |
+		SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
+}
+
+#define SCMI_MAX_POLLING_TIMEOUT_NS	(100 * NSEC_PER_USEC)
 /**
  * scmi_do_xfer() - Do one transfer
  *
@@ -389,14 +406,30 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
 	/* mbox_send_message returns non-negative value on success, so reset */
 	ret = 0;
 
-	/* And we wait for the response. */
-	timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
-	if (!wait_for_completion_timeout(&xfer->done, timeout)) {
-		dev_err(dev, "mbox timed out in resp(caller: %pF)\n",
-			(void *)_RET_IP_);
-		ret = -ETIMEDOUT;
-	} else if (xfer->hdr.status) {
-		ret = scmi_to_linux_errno(xfer->hdr.status);
+	if (xfer->hdr.poll_completion) {
+		ktime_t stop, cur;
+
+		stop = ktime_add_ns(ktime_get(), SCMI_MAX_POLLING_TIMEOUT_NS);
+		do {
+			udelay(5);
+			cur = ktime_get();
+		} while (!scmi_xfer_poll_done(info, xfer) &&
+			 ktime_before(cur, stop));
+
+		if (ktime_before(cur, stop))
+			scmi_fetch_response(xfer, info->tx_payload);
+		else
+			ret = -ETIMEDOUT;
+	} else {
+		/* And we wait for the response. */
+		timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
+		if (!wait_for_completion_timeout(&xfer->done, timeout)) {
+			dev_err(dev, "mbox timed out in resp(caller: %pF)\n",
+				(void *)_RET_IP_);
+			ret = -ETIMEDOUT;
+		} else if (xfer->hdr.status) {
+			ret = scmi_to_linux_errno(xfer->hdr.status);
+		}
 	}
 	/*
 	 * NOTE: we might prefer not to need the mailbox ticker to manage the
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 12/20] firmware: arm_scmi: add option for polling based performance domain operations
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

In order to implement fast CPU DVFS switching, we need to perform all
DVFS operations atomically. Since SCMI transfer already provide option
to choose between pooling vs interrupt driven(default), we can opt for
polling based transfers for set,get performance domain operations.

This patch adds option to choose between polling vs interrupt driven
SCMI transfers for set,get performance level operations.

Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/perf.c | 19 +++++++++++--------
 include/linux/scmi_protocol.h    |  8 ++++----
 2 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index a1f5cf136748..ad73ef6b8d7d 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -303,8 +303,8 @@ static int scmi_perf_limits_get(const struct scmi_handle *handle, u32 domain,
 	return ret;
 }
 
-static int
-scmi_perf_level_set(const struct scmi_handle *handle, u32 domain, u32 level)
+static int scmi_perf_level_set(const struct scmi_handle *handle, u32 domain,
+			       u32 level, bool poll)
 {
 	int ret;
 	struct scmi_xfer *t;
@@ -315,6 +315,7 @@ scmi_perf_level_set(const struct scmi_handle *handle, u32 domain, u32 level)
 	if (ret)
 		return ret;
 
+	t->hdr.poll_completion = poll;
 	lvl = t->tx.buf;
 	lvl->domain = cpu_to_le32(domain);
 	lvl->level = cpu_to_le32(level);
@@ -325,8 +326,8 @@ scmi_perf_level_set(const struct scmi_handle *handle, u32 domain, u32 level)
 	return ret;
 }
 
-static int
-scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, u32 *level)
+static int scmi_perf_level_get(const struct scmi_handle *handle, u32 domain,
+			       u32 *level, bool poll)
 {
 	int ret;
 	struct scmi_xfer *t;
@@ -336,6 +337,7 @@ scmi_perf_level_get(const struct scmi_handle *handle, u32 domain, u32 *level)
 	if (ret)
 		return ret;
 
+	t->hdr.poll_completion = poll;
 	*(__le32 *)t->tx.buf = cpu_to_le32(domain);
 
 	ret = scmi_do_xfer(handle, t);
@@ -447,23 +449,24 @@ static int scmi_dvfs_get_transition_latency(const struct scmi_handle *handle,
 }
 
 static int scmi_dvfs_freq_set(const struct scmi_handle *handle, u32 domain,
-			      unsigned long freq)
+			      unsigned long freq, bool poll)
 {
 	struct scmi_perf_info *pi = handle->perf_priv;
 	struct perf_dom_info *dom = pi->dom_info + domain;
 
-	return scmi_perf_level_set(handle, domain, freq / dom->mult_factor);
+	return scmi_perf_level_set(handle, domain, freq / dom->mult_factor,
+				   poll);
 }
 
 static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain,
-			      unsigned long *freq)
+			      unsigned long *freq, bool poll)
 {
 	int ret;
 	u32 level;
 	struct scmi_perf_info *pi = handle->perf_priv;
 	struct perf_dom_info *dom = pi->dom_info + domain;
 
-	ret = scmi_perf_level_get(handle, domain, &level);
+	ret = scmi_perf_level_get(handle, domain, &level, poll);
 	if (!ret)
 		*freq = level * dom->mult_factor;
 
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 70e2993f6d48..3b378af1badd 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -100,16 +100,16 @@ struct scmi_clk_ops {
 struct scmi_perf_ops {
 	int (*limits_set)(const struct scmi_handle *, u32, u32, u32);
 	int (*limits_get)(const struct scmi_handle *, u32, u32 *, u32 *);
-	int (*level_set)(const struct scmi_handle *, u32, u32);
-	int (*level_get)(const struct scmi_handle *, u32, u32 *);
+	int (*level_set)(const struct scmi_handle *, u32, u32, bool);
+	int (*level_get)(const struct scmi_handle *, u32, u32 *, bool);
 	int (*limits_notify_enable)(const struct scmi_handle *, u32, bool);
 	int (*level_notify_enable)(const struct scmi_handle *, u32, bool);
 	int (*device_domain_id)(struct device *);
 	int (*get_transition_latency)(const struct scmi_handle *,
 				      struct device *);
 	int (*add_opps_to_device)(const struct scmi_handle *, struct device *);
-	int (*freq_set)(const struct scmi_handle *, u32, unsigned long);
-	int (*freq_get)(const struct scmi_handle *, u32, unsigned long *);
+	int (*freq_set)(const struct scmi_handle *, u32, unsigned long, bool);
+	int (*freq_get)(const struct scmi_handle *, u32, unsigned long *, bool);
 };
 
 /**
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 13/20] firmware: arm_scmi: refactor in preparation to support per-protocol channels
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

In order to support per-protocol channels if available, we need to
factor out all the mailbox channel information(Tx/Rx payload and
channel handle) out of the main SCMI instance information structure.

This patch refactors the existing channel information into a separate
chan_info structure.

Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/driver.c | 86 ++++++++++++++++++++++++--------------
 1 file changed, 54 insertions(+), 32 deletions(-)

diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 0c9dda72f10c..24acb421208c 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -105,6 +105,22 @@ struct scmi_desc {
 };
 
 /**
+ * struct scmi_chan_info - Structure representing a SCMI channel informfation
+ *
+ * @cl: Mailbox Client
+ * @chan: Transmit/Receive mailbox channel
+ * @payload: Transmit/Receive mailbox channel payload area
+ * @dev: Reference to device in the SCMI hierarchy corresponding to this
+ *	 channel
+ */
+struct scmi_chan_info {
+	struct mbox_client cl;
+	struct mbox_chan *chan;
+	void __iomem *payload;
+	struct device *dev;
+};
+
+/**
  * struct scmi_info - Structure representing a  SCMI instance
  *
  * @dev: Device pointer
@@ -112,10 +128,8 @@ struct scmi_desc {
  * @handle: Instance of SCMI handle to send to clients
  * @version: SCMI revision information containing protocol version,
  *	implementation version and (sub-)vendor identification.
- * @cl: Mailbox Client
- * @tx_chan: Transmit mailbox channel
- * @tx_payload: Transmit mailbox channel payload area
  * @minfo: Message info
+ * @tx_cinfo: Reference to SCMI channel information
  * @protocols_imp: list of protocols implemented, currently maximum of
  *	MAX_PROTOCOLS_IMP elements allocated by the base protocol
  * @node: list head
@@ -126,16 +140,14 @@ struct scmi_info {
 	const struct scmi_desc *desc;
 	struct scmi_revision_info version;
 	struct scmi_handle handle;
-	struct mbox_client cl;
-	struct mbox_chan *tx_chan;
-	void __iomem *tx_payload;
 	struct scmi_xfers_info minfo;
+	struct scmi_chan_info *tx_cinfo;
 	u8 *protocols_imp;
 	struct list_head node;
 	int users;
 };
 
-#define client_to_scmi_info(c)	container_of(c, struct scmi_info, cl)
+#define client_to_scmi_chan_info(c) container_of(c, struct scmi_chan_info, cl)
 #define handle_to_scmi_info(h)	container_of(h, struct scmi_info, handle)
 
 /*
@@ -218,10 +230,11 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m)
 {
 	u16 xfer_id;
 	struct scmi_xfer *xfer;
-	struct scmi_info *info = client_to_scmi_info(cl);
+	struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl);
+	struct device *dev = cinfo->dev;
+	struct scmi_info *info = dev_get_drvdata(dev);
 	struct scmi_xfers_info *minfo = &info->minfo;
-	struct device *dev = info->dev;
-	struct scmi_shared_mem __iomem *mem = info->tx_payload;
+	struct scmi_shared_mem __iomem *mem = cinfo->payload;
 
 	xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header));
 
@@ -272,8 +285,8 @@ static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr)
 static void scmi_tx_prepare(struct mbox_client *cl, void *m)
 {
 	struct scmi_xfer *t = m;
-	struct scmi_info *info = client_to_scmi_info(cl);
-	struct scmi_shared_mem __iomem *mem = info->tx_payload;
+	struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl);
+	struct scmi_shared_mem __iomem *mem = cinfo->payload;
 
 	/* Mark channel busy + clear error */
 	iowrite32(0x0, &mem->channel_status);
@@ -366,15 +379,15 @@ void scmi_one_xfer_put(const struct scmi_handle *handle, struct scmi_xfer *xfer)
 }
 
 static bool
-scmi_xfer_poll_done(const struct scmi_info *info, struct scmi_xfer *xfer)
+scmi_xfer_poll_done(const struct scmi_chan_info *cinfo, struct scmi_xfer *xfer)
 {
-	struct scmi_shared_mem *mem = info->tx_payload;
-	u16 xfer_id = MSG_XTRACT_TOKEN(le32_to_cpu(mem->msg_header));
+	struct scmi_shared_mem __iomem *mem = cinfo->payload;
+	u16 xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header));
 
 	if (xfer->hdr.seq != xfer_id)
 		return false;
 
-	return le32_to_cpu(mem->channel_status) &
+	return ioread32(&mem->channel_status) &
 		(SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR |
 		SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
 }
@@ -396,8 +409,9 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
 	int timeout;
 	struct scmi_info *info = handle_to_scmi_info(handle);
 	struct device *dev = info->dev;
+	struct scmi_chan_info *cinfo = info->tx_cinfo;
 
-	ret = mbox_send_message(info->tx_chan, xfer);
+	ret = mbox_send_message(cinfo->chan, xfer);
 	if (ret < 0) {
 		dev_dbg(dev, "mbox send fail %d\n", ret);
 		return ret;
@@ -413,11 +427,11 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
 		do {
 			udelay(5);
 			cur = ktime_get();
-		} while (!scmi_xfer_poll_done(info, xfer) &&
+		} while (!scmi_xfer_poll_done(cinfo, xfer) &&
 			 ktime_before(cur, stop));
 
 		if (ktime_before(cur, stop))
-			scmi_fetch_response(xfer, info->tx_payload);
+			scmi_fetch_response(xfer, cinfo->payload);
 		else
 			ret = -ETIMEDOUT;
 	} else {
@@ -437,7 +451,7 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
 	 * Unfortunately, we have to kick the mailbox framework after we have
 	 * received our message.
 	 */
-	mbox_client_txdone(info->tx_chan, ret);
+	mbox_client_txdone(cinfo->chan, ret);
 
 	return ret;
 }
@@ -667,11 +681,11 @@ static int scmi_mailbox_check(struct device_node *np)
 	return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, &arg);
 }
 
-static int scmi_mbox_free_channel(struct scmi_info *info)
+static int scmi_mbox_free_channel(struct scmi_chan_info *cinfo)
 {
-	if (!IS_ERR_OR_NULL(info->tx_chan)) {
-		mbox_free_channel(info->tx_chan);
-		info->tx_chan = NULL;
+	if (!IS_ERR_OR_NULL(cinfo->chan)) {
+		mbox_free_channel(cinfo->chan);
+		cinfo->chan = NULL;
 	}
 
 	return 0;
@@ -691,7 +705,7 @@ static int scmi_remove(struct platform_device *pdev)
 
 	if (!ret)
 		/* Safe to free channels since no more users */
-		return scmi_mbox_free_channel(info);
+		return scmi_mbox_free_channel(info->tx_cinfo);
 
 	return ret;
 }
@@ -703,9 +717,17 @@ static inline int scmi_mbox_chan_setup(struct scmi_info *info)
 	resource_size_t size;
 	struct device *dev = info->dev;
 	struct device_node *shmem, *np = dev->of_node;
+	struct scmi_chan_info *cinfo;
 	struct mbox_client *cl;
 
-	cl = &info->cl;
+	cinfo = devm_kzalloc(info->dev, sizeof(*cinfo), GFP_KERNEL);
+	if (!cinfo)
+		return -ENOMEM;
+
+	info->tx_cinfo = cinfo;
+	cinfo->dev = dev;
+
+	cl = &cinfo->cl;
 	cl->dev = dev;
 	cl->rx_callback = scmi_rx_callback;
 	cl->tx_prepare = scmi_tx_prepare;
@@ -721,16 +743,16 @@ static inline int scmi_mbox_chan_setup(struct scmi_info *info)
 	}
 
 	size = resource_size(&res);
-	info->tx_payload = devm_ioremap(dev, res.start, size);
-	if (!info->tx_payload) {
+	cinfo->payload = devm_ioremap(info->dev, res.start, size);
+	if (!cinfo->payload) {
 		dev_err(dev, "failed to ioremap SCMI Tx payload\n");
 		return -EADDRNOTAVAIL;
 	}
 
 	/* Transmit channel is first entry i.e. index 0 */
-	info->tx_chan = mbox_request_channel(cl, 0);
-	if (IS_ERR(info->tx_chan)) {
-		ret = PTR_ERR(info->tx_chan);
+	cinfo->chan = mbox_request_channel(cl, 0);
+	if (IS_ERR(cinfo->chan)) {
+		ret = PTR_ERR(cinfo->chan);
 		if (ret != -EPROBE_DEFER)
 			dev_err(dev, "failed to request SCMI Tx mailbox\n");
 		return ret;
@@ -798,7 +820,7 @@ static int scmi_probe(struct platform_device *pdev)
 	ret = scmi_base_protocol_init(handle);
 	if (ret) {
 		dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
-		scmi_mbox_free_channel(info);
+		scmi_mbox_free_channel(info->tx_cinfo);
 		return ret;
 	}
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 14/20] firmware: arm_scmi: add per-protocol channels support using idr objects
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

In order to maintain the channel information per protocol, we need
some sort of list or hashtable to hold all this information. IDR
provides sparse array mapping of small integer ID numbers onto arbitrary
pointers. In this case the arbitrary pointers can be pointers to the
channel information.

This patch adds support for per-protocol channels using those idr
objects.

Cc: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/arm_scmi/driver.c | 54 +++++++++++++++++++++++++++++---------
 1 file changed, 42 insertions(+), 12 deletions(-)

diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 24acb421208c..6734a035bcc6 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -118,6 +118,7 @@ struct scmi_chan_info {
 	struct mbox_chan *chan;
 	void __iomem *payload;
 	struct device *dev;
+	struct scmi_handle *handle;
 };
 
 /**
@@ -129,7 +130,7 @@ struct scmi_chan_info {
  * @version: SCMI revision information containing protocol version,
  *	implementation version and (sub-)vendor identification.
  * @minfo: Message info
- * @tx_cinfo: Reference to SCMI channel information
+ * @tx_idr: IDR object to map protocol id to channel info pointer
  * @protocols_imp: list of protocols implemented, currently maximum of
  *	MAX_PROTOCOLS_IMP elements allocated by the base protocol
  * @node: list head
@@ -141,7 +142,7 @@ struct scmi_info {
 	struct scmi_revision_info version;
 	struct scmi_handle handle;
 	struct scmi_xfers_info minfo;
-	struct scmi_chan_info *tx_cinfo;
+	struct idr tx_idr;
 	u8 *protocols_imp;
 	struct list_head node;
 	int users;
@@ -232,7 +233,7 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m)
 	struct scmi_xfer *xfer;
 	struct scmi_chan_info *cinfo = client_to_scmi_chan_info(cl);
 	struct device *dev = cinfo->dev;
-	struct scmi_info *info = dev_get_drvdata(dev);
+	struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
 	struct scmi_xfers_info *minfo = &info->minfo;
 	struct scmi_shared_mem __iomem *mem = cinfo->payload;
 
@@ -409,7 +410,11 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer)
 	int timeout;
 	struct scmi_info *info = handle_to_scmi_info(handle);
 	struct device *dev = info->dev;
-	struct scmi_chan_info *cinfo = info->tx_cinfo;
+	struct scmi_chan_info *cinfo;
+
+	cinfo = idr_find(&info->tx_idr, xfer->hdr.protocol_id);
+	if (unlikely(!cinfo))
+		return -EINVAL;
 
 	ret = mbox_send_message(cinfo->chan, xfer);
 	if (ret < 0) {
@@ -681,13 +686,18 @@ static int scmi_mailbox_check(struct device_node *np)
 	return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, &arg);
 }
 
-static int scmi_mbox_free_channel(struct scmi_chan_info *cinfo)
+static int scmi_mbox_free_channel(int id, void *p, void *data)
 {
+	struct scmi_chan_info *cinfo = p;
+	struct idr *idr = data;
+
 	if (!IS_ERR_OR_NULL(cinfo->chan)) {
 		mbox_free_channel(cinfo->chan);
 		cinfo->chan = NULL;
 	}
 
+	idr_remove(idr, id);
+
 	return 0;
 }
 
@@ -695,6 +705,7 @@ static int scmi_remove(struct platform_device *pdev)
 {
 	int ret = 0;
 	struct scmi_info *info = platform_get_drvdata(pdev);
+	struct idr *idr = &info->tx_idr;
 
 	mutex_lock(&scmi_list_mutex);
 	if (info->users)
@@ -703,28 +714,34 @@ static int scmi_remove(struct platform_device *pdev)
 		list_del(&info->node);
 	mutex_unlock(&scmi_list_mutex);
 
-	if (!ret)
+	if (!ret) {
 		/* Safe to free channels since no more users */
-		return scmi_mbox_free_channel(info->tx_cinfo);
+		ret = idr_for_each(idr, scmi_mbox_free_channel, idr);
+		idr_destroy(&info->tx_idr);
+	}
 
 	return ret;
 }
 
-static inline int scmi_mbox_chan_setup(struct scmi_info *info)
+static inline int
+scmi_mbox_chan_setup(struct scmi_info *info, struct device *dev, int prot_id)
 {
 	int ret;
 	struct resource res;
 	resource_size_t size;
-	struct device *dev = info->dev;
 	struct device_node *shmem, *np = dev->of_node;
 	struct scmi_chan_info *cinfo;
 	struct mbox_client *cl;
 
+	if (scmi_mailbox_check(np)) {
+		cinfo = idr_find(&info->tx_idr, SCMI_PROTOCOL_BASE);
+		goto idr_alloc;
+	}
+
 	cinfo = devm_kzalloc(info->dev, sizeof(*cinfo), GFP_KERNEL);
 	if (!cinfo)
 		return -ENOMEM;
 
-	info->tx_cinfo = cinfo;
 	cinfo->dev = dev;
 
 	cl = &cinfo->cl;
@@ -758,6 +775,14 @@ static inline int scmi_mbox_chan_setup(struct scmi_info *info)
 		return ret;
 	}
 
+idr_alloc:
+	ret = idr_alloc(&info->tx_idr, cinfo, prot_id, prot_id + 1, GFP_KERNEL);
+	if (ret != prot_id) {
+		dev_err(dev, "unable to allocate SCMI idr slot err %d\n", ret);
+		return ret;
+	}
+
+	cinfo->handle = &info->handle;
 	return 0;
 }
 
@@ -774,6 +799,11 @@ scmi_create_protocol_device(struct device_node *np, struct scmi_info *info,
 		return;
 	}
 
+	if (scmi_mbox_chan_setup(info, &sdev->dev, prot_id)) {
+		dev_err(&sdev->dev, "failed to setup transport\n");
+		scmi_device_destroy(sdev);
+	}
+
 	/* setup handle now as the transport is ready */
 	scmi_set_handle(sdev);
 }
@@ -808,19 +838,19 @@ static int scmi_probe(struct platform_device *pdev)
 		return ret;
 
 	platform_set_drvdata(pdev, info);
+	idr_init(&info->tx_idr);
 
 	handle = &info->handle;
 	handle->dev = info->dev;
 	handle->version = &info->version;
 
-	ret = scmi_mbox_chan_setup(info);
+	ret = scmi_mbox_chan_setup(info, dev, SCMI_PROTOCOL_BASE);
 	if (ret)
 		return ret;
 
 	ret = scmi_base_protocol_init(handle);
 	if (ret) {
 		dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
-		scmi_mbox_free_channel(info->tx_cinfo);
 		return ret;
 	}
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 15/20] firmware: arm_scmi: add device power domain support using genpd
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

This patch hooks up the support for device power domain provided by
SCMI using the Linux generic power domain infrastructure.

Cc: Kevin Hilman <khilman@baylibre.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/firmware/Kconfig                   |  13 +++
 drivers/firmware/arm_scmi/Makefile         |   1 +
 drivers/firmware/arm_scmi/scmi_pm_domain.c | 140 +++++++++++++++++++++++++++++
 3 files changed, 154 insertions(+)
 create mode 100644 drivers/firmware/arm_scmi/scmi_pm_domain.c

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 10b917d32087..a0a2d100e28e 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -40,6 +40,19 @@ config ARM_SCMI_PROTOCOL
 	  This protocol library provides interface for all the client drivers
 	  making use of the features offered by the SCMI.
 
+config ARM_SCMI_POWER_DOMAIN
+	tristate "SCMI power domain driver"
+	depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
+	default y
+	select PM_GENERIC_DOMAINS if PM
+	help
+	  This enables support for the SCMI power domains which can be
+	  enabled or disabled via the SCP firmware
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called scmi_pm_domain. Note this may needed early in boot
+	  before rootfs may be available.
+
 config ARM_SCPI_PROTOCOL
 	tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
 	depends on ARM || ARM64 || COMPILE_TEST
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 3236890905b9..99e36c580fbc 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -2,3 +2,4 @@ obj-y	= scmi-bus.o scmi-driver.o scmi-protocols.o
 scmi-bus-y = bus.o
 scmi-driver-y = driver.o
 scmi-protocols-y = base.o clock.o perf.o power.o sensors.o
+obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
diff --git a/drivers/firmware/arm_scmi/scmi_pm_domain.c b/drivers/firmware/arm_scmi/scmi_pm_domain.c
new file mode 100644
index 000000000000..c03c06d3840a
--- /dev/null
+++ b/drivers/firmware/arm_scmi/scmi_pm_domain.c
@@ -0,0 +1,140 @@
+/*
+ * SCMI Generic power domain support.
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pm_domain.h>
+#include <linux/scmi_protocol.h>
+
+struct scmi_pm_domain {
+	struct generic_pm_domain genpd;
+	const struct scmi_handle *handle;
+	const char *name;
+	u32 domain;
+};
+
+#define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd)
+
+static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on)
+{
+	int ret;
+	u32 state, ret_state;
+	struct scmi_pm_domain *pd = to_scmi_pd(domain);
+	const struct scmi_power_ops *ops = pd->handle->power_ops;
+
+	if (power_on)
+		state = SCMI_POWER_STATE_GENERIC_ON;
+	else
+		state = SCMI_POWER_STATE_GENERIC_OFF;
+
+	ret = ops->state_set(pd->handle, pd->domain, state);
+	if (!ret)
+		ret = ops->state_get(pd->handle, pd->domain, &ret_state);
+	if (!ret && state != ret_state)
+		return -EIO;
+
+	return ret;
+}
+
+static int scmi_pd_power_on(struct generic_pm_domain *domain)
+{
+	return scmi_pd_power(domain, true);
+}
+
+static int scmi_pd_power_off(struct generic_pm_domain *domain)
+{
+	return scmi_pd_power(domain, false);
+}
+
+static int scmi_pm_domain_probe(struct scmi_device *sdev)
+{
+	int num_domains, i;
+	struct device *dev = &sdev->dev;
+	struct device_node *np = dev->of_node;
+	struct scmi_pm_domain *scmi_pd;
+	struct genpd_onecell_data *scmi_pd_data;
+	struct generic_pm_domain **domains;
+	const struct scmi_handle *handle = sdev->handle;
+
+	if (!handle || !handle->power_ops)
+		return -ENODEV;
+
+	num_domains = handle->power_ops->num_domains_get(handle);
+	if (num_domains < 0) {
+		dev_err(dev, "number of domains not found\n");
+		return num_domains;
+	}
+
+	scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL);
+	if (!scmi_pd)
+		return -ENOMEM;
+
+	scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL);
+	if (!scmi_pd_data)
+		return -ENOMEM;
+
+	domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
+	if (!domains)
+		return -ENOMEM;
+
+	for (i = 0; i < num_domains; i++, scmi_pd++) {
+		u32 state;
+
+		domains[i] = &scmi_pd->genpd;
+
+		scmi_pd->domain = i;
+		scmi_pd->handle = handle;
+		scmi_pd->name = handle->power_ops->name_get(handle, i);
+		scmi_pd->genpd.name = scmi_pd->name;
+		scmi_pd->genpd.power_off = scmi_pd_power_off;
+		scmi_pd->genpd.power_on = scmi_pd_power_on;
+
+		if (handle->power_ops->state_get(handle, i, &state)) {
+			dev_warn(dev, "failed to get state for domain %d\n", i);
+			continue;
+		}
+
+		pm_genpd_init(&scmi_pd->genpd, NULL,
+			      state == SCMI_POWER_STATE_GENERIC_OFF);
+	}
+
+	scmi_pd_data->domains = domains;
+	scmi_pd_data->num_domains = num_domains;
+
+	of_genpd_add_provider_onecell(np, scmi_pd_data);
+
+	return 0;
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+	{ SCMI_PROTOCOL_POWER },
+	{ },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_power_domain_driver = {
+	.name = "scmi-power-domain",
+	.probe = scmi_pm_domain_probe,
+	.id_table = scmi_id_table,
+};
+module_scmi_driver(scmi_power_domain_driver);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI power domain driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 16/20] clk: add support for clocks provided by SCMI
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

On some ARM based systems, a separate Cortex-M based System Control
Processor(SCP) provides the overall power, clock, reset and system
control. System Control and Management Interface(SCMI) Message Protocol
is defined for the communication between the Application Cores(AP)
and the SCP.

This patch adds support for the clocks provided by SCP using SCMI
protocol.

Cc: linux-clk at vger.kernel.org
Cc: Michael Turquette <mturquette@baylibre.com>
Acked-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 MAINTAINERS            |   2 +-
 drivers/clk/Kconfig    |  10 +++
 drivers/clk/Makefile   |   1 +
 drivers/clk/clk-scmi.c | 213 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 225 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/clk-scmi.c

diff --git a/MAINTAINERS b/MAINTAINERS
index e7526be3a05b..d961de6aa0fd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13154,7 +13154,7 @@ M:	Sudeep Holla <sudeep.holla@arm.com>
 L:	linux-arm-kernel at lists.infradead.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/arm/arm,sc[mp]i.txt
-F:	drivers/clk/clk-scpi.c
+F:	drivers/clk/clk-sc[mp]i.c
 F:	drivers/cpufreq/scpi-cpufreq.c
 F:	drivers/firmware/arm_scpi.c
 F:	drivers/firmware/arm_scmi/
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 1c4e1aa6767e..57c66b22eab8 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -62,6 +62,16 @@ config COMMON_CLK_HI655X
 	  multi-function device has one fixed-rate oscillator, clocked
 	  at 32KHz.
 
+config COMMON_CLK_SCMI
+	tristate "Clock driver controlled via SCMI interface"
+	depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
+	  ---help---
+	  This driver provides support for clocks that are controlled
+	  by firmware that implements the SCMI interface.
+
+	  This driver uses SCMI Message Protocol to interact with the
+	  firmware providing all the clock controls.
+
 config COMMON_CLK_SCPI
 	tristate "Clock driver controlled via SCPI interface"
 	depends on ARM_SCPI_PROTOCOL || COMPILE_TEST
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f7f761b02bed..da622c01526c 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_CLK_QORIQ)			+= clk-qoriq.o
 obj-$(CONFIG_COMMON_CLK_RK808)		+= clk-rk808.o
 obj-$(CONFIG_COMMON_CLK_HI655X)		+= clk-hi655x.o
 obj-$(CONFIG_COMMON_CLK_S2MPS11)	+= clk-s2mps11.o
+obj-$(CONFIG_COMMON_CLK_SCMI)           += clk-scmi.o
 obj-$(CONFIG_COMMON_CLK_SCPI)           += clk-scpi.o
 obj-$(CONFIG_COMMON_CLK_SI5351)		+= clk-si5351.o
 obj-$(CONFIG_COMMON_CLK_SI514)		+= clk-si514.o
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
new file mode 100644
index 000000000000..1e4d7a57779b
--- /dev/null
+++ b/drivers/clk/clk-scmi.c
@@ -0,0 +1,213 @@
+/*
+ * System Control and Power Interface (SCMI) Protocol based clock driver
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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-provider.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/scmi_protocol.h>
+#include <asm/div64.h>
+
+struct scmi_clk {
+	u32 id;
+	struct clk_hw hw;
+	const struct scmi_clock_info *info;
+	const struct scmi_handle *handle;
+};
+
+#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw)
+
+static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw,
+					  unsigned long parent_rate)
+{
+	int ret;
+	u64 rate;
+	struct scmi_clk *clk = to_scmi_clk(hw);
+
+	ret = clk->handle->clk_ops->rate_get(clk->handle, clk->id, &rate);
+	if (ret)
+		return 0;
+	return rate;
+}
+
+static long scmi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *parent_rate)
+{
+	int step;
+	u64 fmin, fmax, ftmp;
+	struct scmi_clk *clk = to_scmi_clk(hw);
+
+	/*
+	 * We can't figure out what rate it will be, so just return the
+	 * rate back to the caller. scmi_clk_recalc_rate() will be called
+	 * after the rate is set and we'll know what rate the clock is
+	 * running at then.
+	 */
+	if (clk->info->rate_discrete)
+		return rate;
+
+	fmin = clk->info->range.min_rate;
+	fmax = clk->info->range.max_rate;
+	if (rate <= fmin)
+		return fmin;
+	else if (rate >= fmax)
+		return fmax;
+
+	ftmp = rate - fmin;
+	ftmp += clk->info->range.step_size - 1; /* to round up */
+	step = do_div(ftmp, clk->info->range.step_size);
+
+	return step * clk->info->range.step_size + fmin;
+}
+
+static int scmi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+			     unsigned long parent_rate)
+{
+	struct scmi_clk *clk = to_scmi_clk(hw);
+
+	return clk->handle->clk_ops->rate_set(clk->handle, clk->id, 0, rate);
+}
+
+static int scmi_clk_enable(struct clk_hw *hw)
+{
+	struct scmi_clk *clk = to_scmi_clk(hw);
+
+	return clk->handle->clk_ops->enable(clk->handle, clk->id);
+}
+
+static void scmi_clk_disable(struct clk_hw *hw)
+{
+	struct scmi_clk *clk = to_scmi_clk(hw);
+
+	clk->handle->clk_ops->disable(clk->handle, clk->id);
+}
+
+static const struct clk_ops scmi_clk_ops = {
+	.recalc_rate = scmi_clk_recalc_rate,
+	.round_rate = scmi_clk_round_rate,
+	.set_rate = scmi_clk_set_rate,
+	/*
+	 * We can't provide enable/disable callback as we can't perform the same
+	 * in atomic context. Since the clock framework provides standard API
+	 * clk_prepare_enable that helps cases using clk_enable in non-atomic
+	 * context, it should be fine providing prepare/unprepare.
+	 */
+	.prepare = scmi_clk_enable,
+	.unprepare = scmi_clk_disable,
+};
+
+static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk)
+{
+	int ret;
+	struct clk_init_data init = {
+		.flags = CLK_GET_RATE_NOCACHE,
+		.num_parents = 0,
+		.ops = &scmi_clk_ops,
+		.name = sclk->info->name,
+	};
+
+	sclk->hw.init = &init;
+	ret = devm_clk_hw_register(dev, &sclk->hw);
+	if (!ret)
+		clk_hw_set_rate_range(&sclk->hw, sclk->info->range.min_rate,
+				      sclk->info->range.max_rate);
+	return ret;
+}
+
+static int scmi_clocks_probe(struct scmi_device *sdev)
+{
+	int idx, count, err;
+	struct clk_hw **hws;
+	struct clk_hw_onecell_data *clk_data;
+	struct device *dev = &sdev->dev;
+	struct device_node *np = dev->of_node;
+	const struct scmi_handle *handle = sdev->handle;
+
+	if (!handle || !handle->clk_ops)
+		return -ENODEV;
+
+	count = handle->clk_ops->count_get(handle);
+	if (count < 0) {
+		dev_err(dev, "%s: invalid clock output count\n", np->name);
+		return -EINVAL;
+	}
+
+	clk_data = devm_kzalloc(dev, sizeof(*clk_data) +
+				sizeof(*clk_data->hws) * count, GFP_KERNEL);
+	if (!clk_data)
+		return -ENOMEM;
+
+	clk_data->num = count;
+	hws = clk_data->hws;
+
+	for (idx = 0; idx < count; idx++) {
+		struct scmi_clk *sclk;
+
+		sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL);
+		if (!sclk)
+			return -ENOMEM;
+
+		sclk->info = handle->clk_ops->info_get(handle, idx);
+		if (!sclk->info) {
+			dev_dbg(dev, "invalid clock info for idx %d\n", idx);
+			continue;
+		}
+
+		sclk->id = idx;
+		sclk->handle = handle;
+
+		err = scmi_clk_ops_init(dev, sclk);
+		if (err) {
+			dev_err(dev, "failed to register clock %d\n", idx);
+			devm_kfree(dev, sclk);
+			hws[idx] = NULL;
+		} else {
+			dev_dbg(dev, "Registered clock:%s\n", sclk->info->name);
+			hws[idx] = &sclk->hw;
+		}
+	}
+
+	return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
+}
+
+static void scmi_clocks_remove(struct scmi_device *sdev)
+{
+	struct device *dev = &sdev->dev;
+	struct device_node *np = dev->of_node;
+
+	of_clk_del_provider(np);
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+	{ SCMI_PROTOCOL_CLOCK },
+	{ },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_clocks_driver = {
+	.name = "scmi-clocks",
+	.probe = scmi_clocks_probe,
+	.remove = scmi_clocks_remove,
+	.id_table = scmi_id_table,
+};
+module_scmi_driver(scmi_clocks_driver);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI clock driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 17/20] hwmon: (core) Add hwmon_max to hwmon_sensor_types enumeration
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

It's useful to know the maximum types of sensor supported by hwmon
framework. It can be used to allocate some data structures when sorting
the monitors based on their type.

This will be used by scmi hwmon support.

Cc: linux-hwmon at vger.kernel.org
Acked-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 include/linux/hwmon.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
index ceb751987c40..e5fd2707b6df 100644
--- a/include/linux/hwmon.h
+++ b/include/linux/hwmon.h
@@ -29,6 +29,7 @@ enum hwmon_sensor_types {
 	hwmon_humidity,
 	hwmon_fan,
 	hwmon_pwm,
+	hwmon_max,
 };
 
 enum hwmon_chip_attributes {
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 18/20] hwmon: add support for sensors exported via ARM SCMI
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

Create a driver to add support for SoC sensors exported by the System
Control Processor (SCP) via the System Control and Management Interface
(SCMI). The supported sensor types is one of voltage, temperature,
current, and power.

The sensor labels and values provided by the SCP are exported via the
hwmon sysfs interface.

Cc: linux-hwmon at vger.kernel.org
Acked-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/hwmon/Kconfig      |  12 +++
 drivers/hwmon/Makefile     |   1 +
 drivers/hwmon/scmi-hwmon.c | 233 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 246 insertions(+)
 create mode 100644 drivers/hwmon/scmi-hwmon.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 7ad017690e3a..518483386bd2 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -321,6 +321,18 @@ config SENSORS_APPLESMC
 	  Say Y here if you have an applicable laptop and want to experience
 	  the awesome power of applesmc.
 
+config SENSORS_ARM_SCMI
+	tristate "ARM SCMI Sensors"
+	depends on ARM_SCMI_PROTOCOL
+	depends on THERMAL || !THERMAL_OF
+	help
+	  This driver provides support for temperature, voltage, current
+	  and power sensors available on SCMI based platforms. The actual
+	  number and type of sensors exported depend on the platform.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called scmi-hwmon.
+
 config SENSORS_ARM_SCPI
 	tristate "ARM SCPI Sensors"
 	depends on ARM_SCPI_PROTOCOL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 0fe489fab663..86ae4011021f 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_SENSORS_ADT7462)	+= adt7462.o
 obj-$(CONFIG_SENSORS_ADT7470)	+= adt7470.o
 obj-$(CONFIG_SENSORS_ADT7475)	+= adt7475.o
 obj-$(CONFIG_SENSORS_APPLESMC)	+= applesmc.o
+obj-$(CONFIG_SENSORS_ARM_SCMI)	+= scmi-hwmon.o
 obj-$(CONFIG_SENSORS_ARM_SCPI)	+= scpi-hwmon.o
 obj-$(CONFIG_SENSORS_ASC7621)	+= asc7621.o
 obj-$(CONFIG_SENSORS_ASPEED)	+= aspeed-pwm-tacho.o
diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c
new file mode 100644
index 000000000000..7a0ab768d4b5
--- /dev/null
+++ b/drivers/hwmon/scmi-hwmon.c
@@ -0,0 +1,233 @@
+/*
+ * System Control and Management Interface(SCMI) based hwmon sensor driver
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ * Sudeep Holla <sudeep.holla@arm.com>
+ *
+ * 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 "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/scmi_protocol.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+
+struct scmi_sensors {
+	const struct scmi_handle *handle;
+	const struct scmi_sensor_info **info[hwmon_max];
+};
+
+static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+			   u32 attr, int channel, long *val)
+{
+	int ret;
+	u64 value;
+	const struct scmi_sensor_info *sensor;
+	struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
+	const struct scmi_handle *h = scmi_sensors->handle;
+
+	sensor = *(scmi_sensors->info[type] + channel);
+	ret = h->sensor_ops->reading_get(h, sensor->id, false, &value);
+	if (!ret)
+		*val = value;
+
+	return ret;
+}
+
+static int
+scmi_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
+		       u32 attr, int channel, const char **str)
+{
+	const struct scmi_sensor_info *sensor;
+	struct scmi_sensors *scmi_sensors = dev_get_drvdata(dev);
+
+	sensor = *(scmi_sensors->info[type] + channel);
+	*str = sensor->name;
+
+	return 0;
+}
+
+static umode_t
+scmi_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
+		      u32 attr, int channel)
+{
+	const struct scmi_sensor_info *sensor;
+	const struct scmi_sensors *scmi_sensors = drvdata;
+
+	sensor = *(scmi_sensors->info[type] + channel);
+	if (sensor && sensor->name)
+		return S_IRUGO;
+
+	return 0;
+}
+
+static const struct hwmon_ops scmi_hwmon_ops = {
+	.is_visible = scmi_hwmon_is_visible,
+	.read = scmi_hwmon_read,
+	.read_string = scmi_hwmon_read_string,
+};
+
+static struct hwmon_chip_info scmi_chip_info = {
+	.ops = &scmi_hwmon_ops,
+	.info = NULL,
+};
+
+static int scmi_hwmon_add_chan_info(struct hwmon_channel_info *scmi_hwmon_chan,
+				    struct device *dev, int num,
+				    enum hwmon_sensor_types type, u32 config)
+{
+	int i;
+	u32 *cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL);
+
+	if (!cfg)
+		return -ENOMEM;
+
+	scmi_hwmon_chan->type = type;
+	scmi_hwmon_chan->config = cfg;
+	for (i = 0; i < num; i++, cfg++)
+		*cfg = config;
+
+	return 0;
+}
+
+static enum hwmon_sensor_types scmi_types[] = {
+	[TEMPERATURE_C] = hwmon_temp,
+	[VOLTAGE] = hwmon_in,
+	[CURRENT] = hwmon_curr,
+	[POWER] = hwmon_power,
+	[ENERGY] = hwmon_energy,
+};
+
+static u32 hwmon_attributes[] = {
+	[hwmon_chip] = HWMON_C_REGISTER_TZ,
+	[hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL,
+	[hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL,
+	[hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL,
+	[hwmon_power] = HWMON_P_INPUT | HWMON_P_LABEL,
+	[hwmon_energy] = HWMON_E_INPUT | HWMON_E_LABEL,
+};
+
+static int scmi_hwmon_probe(struct scmi_device *sdev)
+{
+	int i, idx;
+	u16 nr_sensors;
+	enum hwmon_sensor_types type;
+	struct scmi_sensors *scmi_sensors;
+	const struct scmi_sensor_info *sensor;
+	int nr_count[hwmon_max] = {0}, nr_types = 0;
+	const struct hwmon_chip_info *chip_info;
+	struct device *hwdev, *dev = &sdev->dev;
+	struct hwmon_channel_info *scmi_hwmon_chan;
+	const struct hwmon_channel_info **ptr_scmi_ci;
+	const struct scmi_handle *handle = sdev->handle;
+
+	if (!handle || !handle->sensor_ops)
+		return -ENODEV;
+
+	nr_sensors = handle->sensor_ops->count_get(handle);
+	if (!nr_sensors)
+		return -EIO;
+
+	scmi_sensors = devm_kzalloc(dev, sizeof(*scmi_sensors), GFP_KERNEL);
+	if (!scmi_sensors)
+		return -ENOMEM;
+
+	scmi_sensors->handle = handle;
+
+	for (i = 0; i < nr_sensors; i++) {
+		sensor = handle->sensor_ops->info_get(handle, i);
+		if (!sensor)
+			return PTR_ERR(sensor);
+
+		switch (sensor->type) {
+		case TEMPERATURE_C:
+		case VOLTAGE:
+		case CURRENT:
+		case POWER:
+		case ENERGY:
+			type = scmi_types[sensor->type];
+			if (!nr_count[type])
+				nr_types++;
+			nr_count[type]++;
+			break;
+		}
+	}
+
+	if (nr_count[hwmon_temp])
+		nr_count[hwmon_chip]++, nr_types++;
+
+	scmi_hwmon_chan = devm_kcalloc(dev, nr_types, sizeof(*scmi_hwmon_chan),
+				       GFP_KERNEL);
+	if (!scmi_hwmon_chan)
+		return -ENOMEM;
+
+	ptr_scmi_ci = devm_kcalloc(dev, nr_types + 1, sizeof(*ptr_scmi_ci),
+				   GFP_KERNEL);
+	if (!ptr_scmi_ci)
+		return -ENOMEM;
+
+	scmi_chip_info.info = ptr_scmi_ci;
+	chip_info = &scmi_chip_info;
+
+	for (type = 0; type < hwmon_max && nr_count[type]; type++) {
+		scmi_hwmon_add_chan_info(scmi_hwmon_chan, dev, nr_count[type],
+					 type, hwmon_attributes[type]);
+		*ptr_scmi_ci++ = scmi_hwmon_chan++;
+
+		scmi_sensors->info[type] =
+			devm_kcalloc(dev, nr_count[type],
+				     sizeof(*scmi_sensors->info), GFP_KERNEL);
+		if (!scmi_sensors->info[type])
+			return -ENOMEM;
+	}
+
+	for (i = nr_sensors - 1; i >= 0 ; i--) {
+		sensor = handle->sensor_ops->info_get(handle, i);
+		if (!sensor)
+			continue;
+
+		switch (sensor->type) {
+		case TEMPERATURE_C:
+		case VOLTAGE:
+		case CURRENT:
+		case POWER:
+		case ENERGY:
+			type = scmi_types[sensor->type];
+			idx = --nr_count[type];
+			*(scmi_sensors->info[type] + idx) = sensor;
+			break;
+		}
+	}
+
+	hwdev = devm_hwmon_device_register_with_info(dev, "scmi_sensors",
+						     scmi_sensors, chip_info,
+						     NULL);
+
+	return PTR_ERR_OR_ZERO(hwdev);
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+	{ SCMI_PROTOCOL_SENSOR },
+	{ },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_hwmon_drv = {
+	.name		= "scmi-hwmon",
+	.probe		= scmi_hwmon_probe,
+	.id_table	= scmi_id_table,
+};
+module_scmi_driver(scmi_hwmon_drv);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI HWMON interface driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 19/20] cpufreq: add support for CPU DVFS based on SCMI message protocol
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

On some ARM based systems, a separate Cortex-M based System Control
Processor(SCP) provides the overall power, clock, reset and system
control including CPU DVFS. SCMI Message Protocol is used to
communicate with the SCP.

This patch adds a cpufreq driver for such systems using SCMI interface
to drive CPU DVFS.

Cc: linux-pm at vger.kernel.org
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 MAINTAINERS                    |   2 +-
 drivers/cpufreq/Kconfig.arm    |  11 ++
 drivers/cpufreq/Makefile       |   1 +
 drivers/cpufreq/scmi-cpufreq.c | 270 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 283 insertions(+), 1 deletion(-)
 create mode 100644 drivers/cpufreq/scmi-cpufreq.c

diff --git a/MAINTAINERS b/MAINTAINERS
index d961de6aa0fd..d885824346a0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13155,7 +13155,7 @@ L:	linux-arm-kernel at lists.infradead.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/arm/arm,sc[mp]i.txt
 F:	drivers/clk/clk-sc[mp]i.c
-F:	drivers/cpufreq/scpi-cpufreq.c
+F:	drivers/cpufreq/sc[mp]i-cpufreq.c
 F:	drivers/firmware/arm_scpi.c
 F:	drivers/firmware/arm_scmi/
 F:	include/linux/sc[mp]i_protocol.h
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index bdce4488ded1..e21f84cbd9b4 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -205,6 +205,17 @@ config ARM_SA1100_CPUFREQ
 config ARM_SA1110_CPUFREQ
 	bool
 
+config ARM_SCMI_CPUFREQ
+	tristate "SCMI based CPUfreq driver"
+	depends on ARM_SCMI_PROTOCOL || COMPILE_TEST
+	select PM_OPP
+	help
+	  This adds the CPUfreq driver support for ARM platforms using SCMI
+	  protocol for CPU power management.
+
+	  This driver uses SCMI Message Protocol driver to interact with the
+	  firmware providing the CPU DVFS functionality.
+
 config ARM_SCPI_CPUFREQ
         tristate "SCPI based CPUfreq driver"
 	depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL && COMMON_CLK_SCPI
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 812f9e0d01a3..21ed72b78c84 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_ARM_S3C64XX_CPUFREQ)	+= s3c64xx-cpufreq.o
 obj-$(CONFIG_ARM_S5PV210_CPUFREQ)	+= s5pv210-cpufreq.o
 obj-$(CONFIG_ARM_SA1100_CPUFREQ)	+= sa1100-cpufreq.o
 obj-$(CONFIG_ARM_SA1110_CPUFREQ)	+= sa1110-cpufreq.o
+obj-$(CONFIG_ARM_SCMI_CPUFREQ)		+= scmi-cpufreq.o
 obj-$(CONFIG_ARM_SCPI_CPUFREQ)		+= scpi-cpufreq.o
 obj-$(CONFIG_ARM_SPEAR_CPUFREQ)		+= spear-cpufreq.o
 obj-$(CONFIG_ARM_STI_CPUFREQ)		+= sti-cpufreq.o
diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c
new file mode 100644
index 000000000000..0ee9335d0063
--- /dev/null
+++ b/drivers/cpufreq/scmi-cpufreq.c
@@ -0,0 +1,270 @@
+/*
+ * System Control and Power Interface (SCMI) based CPUFreq Interface driver
+ *
+ * Copyright (C) 2017 ARM Ltd.
+ * Sudeep Holla <sudeep.holla@arm.com>
+ *
+ * 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 "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/cpu_cooling.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/scmi_protocol.h>
+#include <linux/types.h>
+
+struct scmi_data {
+	int domain_id;
+	struct device *cpu_dev;
+	struct thermal_cooling_device *cdev;
+};
+
+static const struct scmi_handle *handle;
+
+static unsigned int scmi_cpufreq_get_rate(unsigned int cpu)
+{
+	struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
+	struct scmi_perf_ops *perf_ops = handle->perf_ops;
+	struct scmi_data *priv = policy->driver_data;
+	unsigned long rate;
+	int ret;
+
+	ret = perf_ops->freq_get(handle, priv->domain_id, &rate, false);
+	if (ret)
+		return 0;
+	return rate / 1000;
+}
+
+/*
+ * perf_ops->freq_set is not a synchronous, the actual OPP change will
+ * happen asynchronously and can get notified if the events are
+ * subscribed for by the SCMI firmware
+ */
+static int
+scmi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
+{
+	struct scmi_data *priv = policy->driver_data;
+	struct scmi_perf_ops *perf_ops = handle->perf_ops;
+	u64 freq = policy->freq_table[index].frequency * 1000;
+
+	return perf_ops->freq_set(handle, priv->domain_id, freq, false);
+}
+
+static int
+scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
+{
+	int cpu, domain, tdomain;
+	struct device *tcpu_dev;
+
+	domain = handle->perf_ops->device_domain_id(cpu_dev);
+	if (domain < 0)
+		return domain;
+
+	for_each_possible_cpu(cpu) {
+		if (cpu == cpu_dev->id)
+			continue;
+
+		tcpu_dev = get_cpu_device(cpu);
+		if (!tcpu_dev)
+			continue;
+
+		tdomain = handle->perf_ops->device_domain_id(tcpu_dev);
+		if (tdomain == domain)
+			cpumask_set_cpu(cpu, cpumask);
+	}
+
+	return 0;
+}
+
+static int scmi_cpufreq_init(struct cpufreq_policy *policy)
+{
+	int ret;
+	unsigned int latency;
+	struct device *cpu_dev;
+	struct scmi_data *priv;
+	struct cpufreq_frequency_table *freq_table;
+
+	cpu_dev = get_cpu_device(policy->cpu);
+	if (!cpu_dev) {
+		pr_err("failed to get cpu%d device\n", policy->cpu);
+		return -ENODEV;
+	}
+
+	ret = handle->perf_ops->add_opps_to_device(handle, cpu_dev);
+	if (ret) {
+		dev_warn(cpu_dev, "failed to add opps to the device\n");
+		return ret;
+	}
+
+	ret = scmi_get_sharing_cpus(cpu_dev, policy->cpus);
+	if (ret) {
+		dev_warn(cpu_dev, "failed to get sharing cpumask\n");
+		return ret;
+	}
+
+	ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
+	if (ret) {
+		dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	ret = dev_pm_opp_get_opp_count(cpu_dev);
+	if (ret <= 0) {
+		dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
+		ret = -EPROBE_DEFER;
+		goto out_free_opp;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		ret = -ENOMEM;
+		goto out_free_opp;
+	}
+
+	ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
+	if (ret) {
+		dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
+		goto out_free_priv;
+	}
+
+	priv->cpu_dev = cpu_dev;
+	priv->domain_id = handle->perf_ops->device_domain_id(cpu_dev);
+
+	policy->driver_data = priv;
+
+	ret = cpufreq_table_validate_and_show(policy, freq_table);
+	if (ret) {
+		dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__,
+			ret);
+		goto out_free_cpufreq_table;
+	}
+
+	/* SCMI allows DVFS request for any domain from any CPU */
+	policy->dvfs_possible_from_any_cpu = true;
+
+	latency = handle->perf_ops->get_transition_latency(handle, cpu_dev);
+	if (!latency)
+		latency = CPUFREQ_ETERNAL;
+
+	policy->cpuinfo.transition_latency = latency;
+
+	return 0;
+
+out_free_cpufreq_table:
+	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
+out_free_priv:
+	kfree(priv);
+out_free_opp:
+	dev_pm_opp_cpumask_remove_table(policy->cpus);
+
+	return ret;
+}
+
+static int scmi_cpufreq_exit(struct cpufreq_policy *policy)
+{
+	struct scmi_data *priv = policy->driver_data;
+
+	cpufreq_cooling_unregister(priv->cdev);
+	dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
+	kfree(priv);
+	dev_pm_opp_cpumask_remove_table(policy->related_cpus);
+
+	return 0;
+}
+
+static void scmi_cpufreq_ready(struct cpufreq_policy *policy)
+{
+	struct scmi_data *priv = policy->driver_data;
+	struct device_node *np = of_node_get(priv->cpu_dev->of_node);
+
+	if (WARN_ON(!np))
+		return;
+
+	if (of_find_property(np, "#cooling-cells", NULL)) {
+		u32 pcoeff = 0;
+
+		of_property_read_u32(np, "dynamic-power-coefficient",
+				     &pcoeff);
+
+		priv->cdev = of_cpufreq_power_cooling_register(np, policy,
+							       pcoeff, NULL);
+		if (IS_ERR(priv->cdev)) {
+			dev_err(priv->cpu_dev,
+				"running cpufreq without cooling device: %ld\n",
+				PTR_ERR(priv->cdev));
+
+			priv->cdev = NULL;
+		}
+	}
+
+	of_node_put(np);
+}
+
+static struct cpufreq_driver scmi_cpufreq_driver = {
+	.name	= "scmi",
+	.flags	= CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
+		  CPUFREQ_NEED_INITIAL_FREQ_CHECK,
+	.verify	= cpufreq_generic_frequency_table_verify,
+	.attr	= cpufreq_generic_attr,
+	.target_index	= scmi_cpufreq_set_target,
+	.get	= scmi_cpufreq_get_rate,
+	.init	= scmi_cpufreq_init,
+	.exit	= scmi_cpufreq_exit,
+	.ready	= scmi_cpufreq_ready,
+};
+
+static int scmi_cpufreq_probe(struct scmi_device *sdev)
+{
+	int ret;
+
+	handle = sdev->handle;
+
+	if (!handle || !handle->perf_ops)
+		return -ENODEV;
+
+	ret = cpufreq_register_driver(&scmi_cpufreq_driver);
+	if (ret) {
+		dev_err(&sdev->dev, "%s: registering cpufreq failed, err: %d\n",
+			__func__, ret);
+	}
+
+	return ret;
+}
+
+static void scmi_cpufreq_remove(struct scmi_device *sdev)
+{
+	cpufreq_unregister_driver(&scmi_cpufreq_driver);
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+	{ SCMI_PROTOCOL_PERF },
+	{ },
+};
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_cpufreq_drv = {
+	.name		= "scmi-cpufreq",
+	.probe		= scmi_cpufreq_probe,
+	.remove		= scmi_cpufreq_remove,
+	.id_table	= scmi_id_table,
+};
+module_scmi_driver(scmi_cpufreq_drv);
+
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("ARM SCMI CPUFreq interface driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 20/20] cpufreq: scmi: add support for fast frequency switching
From: Sudeep Holla @ 2018-01-02 14:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1514904162-11201-1-git-send-email-sudeep.holla@arm.com>

The cpufreq core provides option for drivers to implement fast_switch
callback which is invoked for frequency switching from interrupt context.

This patch adds support for fast_switch callback in SCMI cpufreq driver
by making use of polling based SCMI transfer. It also sets the flag
fast_switch_possible.

Cc: linux-pm at vger.kernel.org
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
 drivers/cpufreq/scmi-cpufreq.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c
index 0ee9335d0063..d0a82d7c6fd4 100644
--- a/drivers/cpufreq/scmi-cpufreq.c
+++ b/drivers/cpufreq/scmi-cpufreq.c
@@ -64,6 +64,19 @@ scmi_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index)
 	return perf_ops->freq_set(handle, priv->domain_id, freq, false);
 }
 
+static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy,
+					     unsigned int target_freq)
+{
+	struct scmi_data *priv = policy->driver_data;
+	struct scmi_perf_ops *perf_ops = handle->perf_ops;
+
+	if (!perf_ops->freq_set(handle, priv->domain_id,
+				target_freq * 1000, true))
+		return target_freq;
+
+	return 0;
+}
+
 static int
 scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
 {
@@ -163,6 +176,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
 
 	policy->cpuinfo.transition_latency = latency;
 
+	policy->fast_switch_possible = true;
 	return 0;
 
 out_free_cpufreq_table:
@@ -222,6 +236,7 @@ static struct cpufreq_driver scmi_cpufreq_driver = {
 	.verify	= cpufreq_generic_frequency_table_verify,
 	.attr	= cpufreq_generic_attr,
 	.target_index	= scmi_cpufreq_set_target,
+	.fast_switch	= scmi_cpufreq_fast_switch,
 	.get	= scmi_cpufreq_get_rate,
 	.init	= scmi_cpufreq_init,
 	.exit	= scmi_cpufreq_exit,
-- 
2.7.4

^ permalink raw reply related

* [PATCH] soc: renesas: rcar-sysc: Mark rcar_sysc_matches[] __initconst
From: Geert Uytterhoeven @ 2018-01-02 14:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171220082527.k7wldjwjxut3j4xz@verge.net.au>

Hi Simon,

On Wed, Dec 20, 2017 at 9:25 AM, Simon Horman <horms@verge.net.au> wrote:
> On Tue, Dec 19, 2017 at 04:54:44PM +0100, Geert Uytterhoeven wrote:
>> rcar_sysc_matches[] is used only by rcar_sysc_pd_init(), which is
>> __init.  Hence mark rcar_sysc_matches[] __initconst.
>>
>> This frees another 1764 bytes (arm32/shmobile_defconfig) or 1000 bytes
>> (arm64/renesas_defconfig) of memory after kernel init.
>>
>> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
>
> Reviewed-by: Simon Horman <simon.horman@netronome.com>

Thank you.
Please note this is a patch intended for your soc-for-v4.16 branch ;-)

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* [PATCH v2 0/8] Armada 7K/8K CP110 DT de-duplication
From: Thomas Petazzoni @ 2018-01-02 14:55 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

This series aims at de-duplicating the Armada CP110 Device Tree
description, which is currently duplicated between
armada-cp110-master.dtsi and armada-cp110-slave.dtsi, even though they
are almost identical. Indeed, one concept of Marvell SoCs is that they
are made of HW blocks composed of a variety of IPs (network, PCIe,
SATA, XOR, SPI, I2C, etc.), and those HW blocks can be duplicated
several times within a given SoC. The Armada 7K SoC has a single CP110
(so no duplication), while the Armada 8K SoC has two CP110. In the
future, SoCs with more than 2 CP110s will be introduced.

This duplication issue has been discussed at the DT workshop [1] in
Prague last October, and I presented on this topic [2]. The solution
of using the C pre-processor to avoid this duplication has been
validated by the people present in this DT workshop, and this patch
series simply submits what has been presented.

 - The first four patches are fixes for existing
   issues/inconsistencies in the Device Tree files. Since they don't
   fix any visible problems, they are not marked for -stable.

 - The fifth patch is a minor improvement.

 - The sixth patch making use of aliases for SPI busses simply aims at
   reducing the number of changes between the CP110 master and CP110
   slave description, by avoiding the need for the cell-index property
   in the SPI controller DT nodes.

 - The seventh patch implements the de-duplication itself, by
   introducing an armada-cp110.dtsi file included twice on Armada 8K
   platforms, once for the master CP110 and once for the slave CP110.

 - The last patch renames cpm to cp0 and cps to cp1, as the concept of
   master/slave CPs does not apply to future SoCs that have more than
   2 CPs.

Changes since v1:

 - Rebase on top of mvebu/dt64, since the NAND controller changes will
   only be submitted for 4.17.

 - Add patches fixing NAND related typos/inconsistencies:
     arm64: dts: marvell: fix typos in comment describing the NAND controller
     arm64: dts: marvell: fix compatible string list for Armada CP110 slave NAND

 - Improve the de-duplication patch by removing
   armada-cp110-master.dtsi and armada-cp110-slave.dtsi, since the
   concept of master/slave will no longer exist when we will have more
   than 2 CPs.

 - Add a patch renaming cpm -> cp0, cps -> cp1.

Best regards,

Thomas

[1] https://elinux.org/Device_tree_kernel_summit_2017_etherpad
[2] https://elinux.org/images/1/14/DTWorkshop2017-duplicate-data.pdf

Thomas Petazzoni (8):
  arm64: dts: marvell: fix watchdog unit address in Armada AP806
  arm64: dts: marvell: use lower case for unit address and reg property
  arm64: dts: marvell: fix typos in comment describing the NAND
    controller
  arm64: dts: marvell: fix compatible string list for Armada CP110 slave
    NAND
  arm64: dts: marvell: use mvebu-icu.h where possible
  arm64: dts: marvell: use aliases for SPI busses on Armada 7K/8K
  arm64: dts: marvell: de-duplicate CP110 description
  arm64: dts: marvell: replace cpm by cp0, cps by cp1

 arch/arm64/boot/dts/marvell/armada-7040-db.dts     |  46 +--
 arch/arm64/boot/dts/marvell/armada-70x0.dtsi       |  37 +-
 arch/arm64/boot/dts/marvell/armada-8020.dtsi       |   2 +-
 arch/arm64/boot/dts/marvell/armada-8040-db.dts     |  80 ++--
 arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts  |  76 ++--
 arch/arm64/boot/dts/marvell/armada-8040.dtsi       |   2 +-
 arch/arm64/boot/dts/marvell/armada-80x0.dtsi       |  80 +++-
 arch/arm64/boot/dts/marvell/armada-ap806.dtsi      |   8 +-
 arch/arm64/boot/dts/marvell/armada-common.dtsi     |  10 +
 .../boot/dts/marvell/armada-cp110-master.dtsi      | 449 ---------------------
 .../arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 448 --------------------
 arch/arm64/boot/dts/marvell/armada-cp110.dtsi      | 422 +++++++++++++++++++
 12 files changed, 635 insertions(+), 1025 deletions(-)
 create mode 100644 arch/arm64/boot/dts/marvell/armada-common.dtsi
 delete mode 100644 arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
 delete mode 100644 arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
 create mode 100644 arch/arm64/boot/dts/marvell/armada-cp110.dtsi

-- 
2.14.3

^ permalink raw reply

* [PATCH v2 1/8] arm64: dts: marvell: fix watchdog unit address in Armada AP806
From: Thomas Petazzoni @ 2018-01-02 14:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180102145558.9773-1-thomas.petazzoni@free-electrons.com>

This fixes the following DTC warning:

  Warning (simple_bus_reg): Node /ap806/config-space at f0000000/watchdog at 600000 simple-bus unit address format error, expected "610000"

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 arch/arm64/boot/dts/marvell/armada-ap806.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
index bbc5a4d3acac..36f6d7fbb310 100644
--- a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
@@ -241,7 +241,7 @@
 
 			};
 
-			watchdog: watchdog at 600000 {
+			watchdog: watchdog at 610000 {
 				compatible = "arm,sbsa-gwdt";
 				reg = <0x610000 0x1000>, <0x600000 0x1000>;
 				interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
-- 
2.14.3

^ permalink raw reply related

* [PATCH v2 2/8] arm64: dts: marvell: use lower case for unit address and reg property
From: Thomas Petazzoni @ 2018-01-02 14:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180102145558.9773-1-thomas.petazzoni@free-electrons.com>

This fixes the following DTC warning:

  <stdout>: Warning (simple_bus_reg): Node /ap806/config-space at f0000000/thermal at 6f808C simple-bus unit address format error, expected "6f808c"

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 arch/arm64/boot/dts/marvell/armada-ap806.dtsi | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
index 36f6d7fbb310..0575207cafee 100644
--- a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
@@ -286,9 +286,9 @@
 				};
 			};
 
-			ap_thermal: thermal at 6f808C {
+			ap_thermal: thermal at 6f808c {
 				compatible = "marvell,armada-ap806-thermal";
-				reg = <0x6f808C 0x4>,
+				reg = <0x6f808c 0x4>,
 				      <0x6f8084 0x8>;
 			};
 		};
-- 
2.14.3

^ permalink raw reply related

* [PATCH v2 3/8] arm64: dts: marvell: fix typos in comment describing the NAND controller
From: Thomas Petazzoni @ 2018-01-02 14:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180102145558.9773-1-thomas.petazzoni@free-electrons.com>

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi | 2 +-
 arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
index ecbc76d26dff..a0b2cec2823f 100644
--- a/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
@@ -317,7 +317,7 @@
 
 			cpm_nand: nand at 720000 {
 				/*
-				 * Due to the limiation of the pin available
+				 * Due to the limitation of the pins available
 				 * this controller is only usable on the CPM
 				 * for A7K and on the CPS for A8K.
 				 */
diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
index 6a07c786b788..0624ec2de496 100644
--- a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
@@ -318,7 +318,7 @@
 
 			cps_nand: nand at 720000 {
 				/*
-				 * Due to the limiation of the pin available
+				 * Due to the limitation of the pins available
 				 * this controller is only usable on the CPM
 				 * for A7K and on the CPS for A8K.
 				 */
-- 
2.14.3

^ permalink raw reply related

* [PATCH v2 4/8] arm64: dts: marvell: fix compatible string list for Armada CP110 slave NAND
From: Thomas Petazzoni @ 2018-01-02 14:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180102145558.9773-1-thomas.petazzoni@free-electrons.com>

The Armada CP110 slave NAND controller Device Tree description lists
the compatible string in the wrong order: marvell,armada-8k-nand
should come first. This commit alignes the slave CP110 description
with the master CP110 description from that respect.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
index 0624ec2de496..3eda62d482fc 100644
--- a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
@@ -322,8 +322,8 @@
 				 * this controller is only usable on the CPM
 				 * for A7K and on the CPS for A8K.
 				 */
-				compatible = "marvell,armada370-nand",
-					     "marvell,armada-8k-nand";
+				compatible = "marvell,armada-8k-nand",
+					     "marvell,armada370-nand";
 				reg = <0x720000 0x54>;
 				#address-cells = <1>;
 				#size-cells = <1>;
-- 
2.14.3

^ permalink raw reply related

* [PATCH v2 5/8] arm64: dts: marvell: use mvebu-icu.h where possible
From: Thomas Petazzoni @ 2018-01-02 14:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180102145558.9773-1-thomas.petazzoni@free-electrons.com>

Back when the ICU Device Tree binding was introduced, we could not use
mvebu-icu.h from the Device Tree files, because the DT files and
mvebu-icu.h were following different merge routes towards Linus
tree. Now that both have been merged, we can switch the Marvell Armada
CP110 Device Tree files to use the mvebu-icu.h header instead of
duplicating the ICU_GRP_NSR definition.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi | 2 +-
 arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
index a0b2cec2823f..f89053577bcc 100644
--- a/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
@@ -44,7 +44,7 @@
  * Device Tree file for Marvell Armada CP110 Master.
  */
 
-#define ICU_GRP_NSR 0x0
+#include <dt-bindings/interrupt-controller/mvebu-icu.h>
 
 / {
 	cp110-master {
diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
index 3eda62d482fc..a658b72b0229 100644
--- a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
@@ -44,7 +44,7 @@
  * Device Tree file for Marvell Armada CP110 Slave.
  */
 
-#define ICU_GRP_NSR 0x0
+#include <dt-bindings/interrupt-controller/mvebu-icu.h>
 
 / {
 	cp110-slave {
-- 
2.14.3

^ permalink raw reply related

* [PATCH v2 6/8] arm64: dts: marvell: use aliases for SPI busses on Armada 7K/8K
From: Thomas Petazzoni @ 2018-01-02 14:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180102145558.9773-1-thomas.petazzoni@free-electrons.com>

We are currently using the cell-index DT property to assign SPI bus
numbers. This property is specific to the spi-orion driver, and
requires each SPI controller to have a unique ID defined in the Device
Tree.

As we are about to merge armada-cp110-master.dtsi and
armada-cp110-slave.dtsi into a single file, those cell-index
properties that differ between the master CP110 and the slave CP110
are a difference that would have to be handled.

In order to avoid this, we switch to using the "aliases" DT node to
assign a unique number to each SPI controller. This is more generic,
and directly handled by the SPI core.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 arch/arm64/boot/dts/marvell/armada-70x0.dtsi         | 2 ++
 arch/arm64/boot/dts/marvell/armada-80x0.dtsi         | 4 ++++
 arch/arm64/boot/dts/marvell/armada-ap806.dtsi        | 2 +-
 arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi | 2 --
 arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi  | 2 --
 5 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/boot/dts/marvell/armada-70x0.dtsi b/arch/arm64/boot/dts/marvell/armada-70x0.dtsi
index 0e1a1e5be399..815e64b3a874 100644
--- a/arch/arm64/boot/dts/marvell/armada-70x0.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-70x0.dtsi
@@ -50,6 +50,8 @@
 	aliases {
 		gpio1 = &cpm_gpio1;
 		gpio2 = &cpm_gpio2;
+		spi1 = &cpm_spi0;
+		spi2 = &cpm_spi1;
 	};
 };
 
diff --git a/arch/arm64/boot/dts/marvell/armada-80x0.dtsi b/arch/arm64/boot/dts/marvell/armada-80x0.dtsi
index b280ddd3c397..de9c34333cd4 100644
--- a/arch/arm64/boot/dts/marvell/armada-80x0.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-80x0.dtsi
@@ -51,6 +51,10 @@
 	aliases {
 		gpio1 = &cps_gpio1;
 		gpio2 = &cpm_gpio2;
+		spi1 = &cpm_spi0;
+		spi2 = &cpm_spi1;
+		spi3 = &cps_spi0;
+		spi4 = &cps_spi1;
 	};
 };
 
diff --git a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
index 0575207cafee..f9b66b81f9fc 100644
--- a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
@@ -58,6 +58,7 @@
 		serial0 = &uart0;
 		serial1 = &uart1;
 		gpio0 = &ap_gpio;
+		spi0 = &spi0;
 	};
 
 	psci {
@@ -203,7 +204,6 @@
 				reg = <0x510600 0x50>;
 				#address-cells = <1>;
 				#size-cells = <0>;
-				cell-index = <0>;
 				interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
 				clocks = <&ap_clk 3>;
 				status = "disabled";
diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
index f89053577bcc..162e6c228bd9 100644
--- a/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
@@ -280,7 +280,6 @@
 				reg = <0x700600 0x50>;
 				#address-cells = <0x1>;
 				#size-cells = <0x0>;
-				cell-index = <1>;
 				clocks = <&cpm_clk 1 21>;
 				status = "disabled";
 			};
@@ -290,7 +289,6 @@
 				reg = <0x700680 0x50>;
 				#address-cells = <1>;
 				#size-cells = <0>;
-				cell-index = <2>;
 				clocks = <&cpm_clk 1 21>;
 				status = "disabled";
 			};
diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
index a658b72b0229..207b6f444e24 100644
--- a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
@@ -281,7 +281,6 @@
 				reg = <0x700600 0x50>;
 				#address-cells = <0x1>;
 				#size-cells = <0x0>;
-				cell-index = <3>;
 				clocks = <&cps_clk 1 21>;
 				status = "disabled";
 			};
@@ -291,7 +290,6 @@
 				reg = <0x700680 0x50>;
 				#address-cells = <1>;
 				#size-cells = <0>;
-				cell-index = <4>;
 				clocks = <&cps_clk 1 21>;
 				status = "disabled";
 			};
-- 
2.14.3

^ permalink raw reply related

* [PATCH v2 7/8] arm64: dts: marvell: de-duplicate CP110 description
From: Thomas Petazzoni @ 2018-01-02 14:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180102145558.9773-1-thomas.petazzoni@free-electrons.com>

One concept of Marvell Armada 7K/8K SoCs is that they are made of HW
blocks composed of a variety of IPs (network, PCIe, SATA, XOR, SPI,
I2C, etc.), and those HW blocks can be duplicated several times within
a given SoC. The Armada 7K SoC has a single CP110 (so no duplication),
while the Armada 8K SoC has two CP110. In the future, SoCs with more
than 2 CP110s will be introduced.

In current kernel versions, the master CP110 is described in
armada-cp110-master.dtsi and the slave CP110 is described in
armada-cp110-slave.dtsi. Those files are basically exactly the same,
since they describe the same hardware. They only have a few
differences:

 - Base address of the registers is different for the "config-space"

 - Base address of the PCIe registers, MEM, CONF and IO areas were
   different

 - Labels (and phandles pointing to them) of the nodes were different
   ("cpm" prefix in the master CP, "cps" prefix in the slave CP)

This duplication issue has been discussed at the DT workshop [1] in
Prague last October, and we presented on this topic [2]. The solution
of using the C pre-processor to avoid this duplication has been
validated by the people present in this DT workshop, and this patch
simply implements what has been presented.

We handle differences between the master CP and slave CP description
using the C pre-processor, by defining a set of macros with different
values armada-cp110.dtsi is included to instantiate one of the master
or slave CP110.

There are a few aspects that deserve additional explanations:

 - PCIe needs to be handled separately because it is not part of the
   config-space {...} node, since it has registers outside of the
   range covered by config-space {...}.

 - We need to defined CP110_BASE, CP110_PCIEx_BASE without 0x, because
   they are used for the unit address part of some DT nodes. But since
   they are also used for the "reg" property of the same nodes, we
   have an ADDRESSIFY() macro that prepends 0x to those values.

We compared the resulting .dtb for armada-8040-db.dtb before and after
this patch is applied, and the result is exactly the same, except for
a few differences:

 - the SDHCI controller that was only described in the master CP110 is
   now also described in the slave CP110. Even though the SDHCI
   controller from the slave CP110 is indeed not usable (as it isn't
   wired to the outside world) it is technically part of the silicon,
   and therefore it is reasonable to also describe it to be part of
   the slave CP110. In addition, if we wanted to get this correct for
   the SDHCI controller, we should also do it for the NAND controller,
   for which the situation is even more complicated: in a single CP110
   configuration (Armada 7K), the usable NAND controller is in the
   master CP110, while in a dual CP110 configuration (Armada 8K), the
   usable NAND controller is in the slave CP110. Since that would add
   a lot of additional complexity for no good reason, and since the IP
   blocks are in fact really present in both CPs, we simply describe
   them in both CPs at the DT level.

 - the cp110-master and cp110-slave nodes are now named cpm and
   cps. We could have kept cp110-master and cp110-slave, but that
   would have required adding another CP110_xyz define, which didn't
   seem very useful.

Note that this commit also gets rid of the armada-cp110-master.dtsi
and armada-cp110-slave.dtsi files, as future SoCs will have more than
2 CPs. Instead, we instantiate the CPs directly from the SoC-specific
.dtsi files, i.e armada-70x0.dtsi and armada-80x0.dtsi.

[1] https://elinux.org/Device_tree_kernel_summit_2017_etherpad
[2] https://elinux.org/images/1/14/DTWorkshop2017-duplicate-data.pdf

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
 arch/arm64/boot/dts/marvell/armada-70x0.dtsi       |  23 +-
 arch/arm64/boot/dts/marvell/armada-80x0.dtsi       |  56 ++-
 arch/arm64/boot/dts/marvell/armada-common.dtsi     |  10 +
 .../boot/dts/marvell/armada-cp110-master.dtsi      | 447 ---------------------
 .../arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 446 --------------------
 arch/arm64/boot/dts/marvell/armada-cp110.dtsi      | 422 +++++++++++++++++++
 6 files changed, 506 insertions(+), 898 deletions(-)
 create mode 100644 arch/arm64/boot/dts/marvell/armada-common.dtsi
 delete mode 100644 arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
 delete mode 100644 arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
 create mode 100644 arch/arm64/boot/dts/marvell/armada-cp110.dtsi

diff --git a/arch/arm64/boot/dts/marvell/armada-70x0.dtsi b/arch/arm64/boot/dts/marvell/armada-70x0.dtsi
index 815e64b3a874..9917cff3dae6 100644
--- a/arch/arm64/boot/dts/marvell/armada-70x0.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-70x0.dtsi
@@ -44,8 +44,6 @@
  * Device Tree file for the Armada 70x0 SoC
  */
 
-#include "armada-cp110-master.dtsi"
-
 / {
 	aliases {
 		gpio1 = &cpm_gpio1;
@@ -55,6 +53,27 @@
 	};
 };
 
+/*
+ * Instantiate the CP110
+ */
+#define CP110_NAME		cpm
+#define CP110_BASE		f2000000
+#define CP110_PCIE_IO_BASE	0xf9000000
+#define CP110_PCIE_MEM_BASE	0xf6000000
+#define CP110_PCIE0_BASE	f2600000
+#define CP110_PCIE1_BASE	f2620000
+#define CP110_PCIE2_BASE	f2640000
+
+#include "armada-cp110.dtsi"
+
+#undef CP110_NAME
+#undef CP110_BASE
+#undef CP110_PCIE_IO_BASE
+#undef CP110_PCIE_MEM_BASE
+#undef CP110_PCIE0_BASE
+#undef CP110_PCIE1_BASE
+#undef CP110_PCIE2_BASE
+
 &cpm_gpio1 {
 	status = "okay";
 };
diff --git a/arch/arm64/boot/dts/marvell/armada-80x0.dtsi b/arch/arm64/boot/dts/marvell/armada-80x0.dtsi
index de9c34333cd4..5e038e7b7b30 100644
--- a/arch/arm64/boot/dts/marvell/armada-80x0.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-80x0.dtsi
@@ -44,9 +44,6 @@
  * Device Tree file for the Armada 80x0 SoC family
  */
 
-#include "armada-cp110-master.dtsi"
-#include "armada-cp110-slave.dtsi"
-
 / {
 	aliases {
 		gpio1 = &cps_gpio1;
@@ -58,6 +55,48 @@
 	};
 };
 
+/*
+ * Instantiate the master CP110
+ */
+#define CP110_NAME		cpm
+#define CP110_BASE		f2000000
+#define CP110_PCIE_IO_BASE	0xf9000000
+#define CP110_PCIE_MEM_BASE	0xf6000000
+#define CP110_PCIE0_BASE	f2600000
+#define CP110_PCIE1_BASE	f2620000
+#define CP110_PCIE2_BASE	f2640000
+
+#include "armada-cp110.dtsi"
+
+#undef CP110_NAME
+#undef CP110_BASE
+#undef CP110_PCIE_IO_BASE
+#undef CP110_PCIE_MEM_BASE
+#undef CP110_PCIE0_BASE
+#undef CP110_PCIE1_BASE
+#undef CP110_PCIE2_BASE
+
+/*
+ * Instantiate the slave CP110
+ */
+#define CP110_NAME		cps
+#define CP110_BASE		f4000000
+#define CP110_PCIE_IO_BASE	0xfd000000
+#define CP110_PCIE_MEM_BASE	0xfa000000
+#define CP110_PCIE0_BASE	f4600000
+#define CP110_PCIE1_BASE	f4620000
+#define CP110_PCIE2_BASE	f4640000
+
+#include "armada-cp110.dtsi"
+
+#undef CP110_NAME
+#undef CP110_BASE
+#undef CP110_PCIE_IO_BASE
+#undef CP110_PCIE_MEM_BASE
+#undef CP110_PCIE0_BASE
+#undef CP110_PCIE1_BASE
+#undef CP110_PCIE2_BASE
+
 /* The 80x0 has two CP blocks, but uses only one block from each. */
 &cps_gpio1 {
 	status = "okay";
@@ -95,3 +134,14 @@
 		};
 	};
 };
+
+&cps_crypto {
+	/*
+	 * The cryptographic engine found on the cp110
+	 * master is enabled by default at the SoC
+	 * level. Because it is not possible as of now
+	 * to enable two cryptographic engines in
+	 * parallel, disable this one by default.
+	 */
+	status = "disabled";
+};
diff --git a/arch/arm64/boot/dts/marvell/armada-common.dtsi b/arch/arm64/boot/dts/marvell/armada-common.dtsi
new file mode 100644
index 000000000000..c6dd1d81c68d
--- /dev/null
+++ b/arch/arm64/boot/dts/marvell/armada-common.dtsi
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR X11)
+/*
+ * Copyright (C) 2016 Marvell Technology Group Ltd.
+ */
+
+/* Common definitions used by Armada 7K/8K DTs */
+#define PASTER(x, y) x ## y
+#define EVALUATOR(x, y) PASTER(x, y)
+#define CP110_LABEL(name) EVALUATOR(CP110_NAME, EVALUATOR(_, name))
+#define ADDRESSIFY(addr) EVALUATOR(0x, addr)
diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
deleted file mode 100644
index 162e6c228bd9..000000000000
--- a/arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * Copyright (C) 2016 Marvell Technology Group Ltd.
- *
- * This file is dual-licensed: you can use it either under the terms
- * of the GPLv2 or the X11 license, at your option. Note that this dual
- * licensing only applies to this file, and not this project as a
- * whole.
- *
- *  a) This library is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of the
- *     License, or (at your option) any later version.
- *
- *     This library 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.
- *
- * Or, alternatively,
- *
- *  b) Permission is hereby granted, free of charge, to any person
- *     obtaining a copy of this software and associated documentation
- *     files (the "Software"), to deal in the Software without
- *     restriction, including without limitation the rights to use,
- *     copy, modify, merge, publish, distribute, sublicense, and/or
- *     sell copies of the Software, and to permit persons to whom the
- *     Software is furnished to do so, subject to the following
- *     conditions:
- *
- *     The above copyright notice and this permission notice shall be
- *     included in all copies or substantial portions of the Software.
- *
- *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- *     OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/*
- * Device Tree file for Marvell Armada CP110 Master.
- */
-
-#include <dt-bindings/interrupt-controller/mvebu-icu.h>
-
-/ {
-	cp110-master {
-		#address-cells = <2>;
-		#size-cells = <2>;
-		compatible = "simple-bus";
-		interrupt-parent = <&cpm_icu>;
-		ranges;
-
-		config-space at f2000000 {
-			#address-cells = <1>;
-			#size-cells = <1>;
-			compatible = "simple-bus";
-			ranges = <0x0 0x0 0xf2000000 0x2000000>;
-
-			cpm_ethernet: ethernet at 0 {
-				compatible = "marvell,armada-7k-pp22";
-				reg = <0x0 0x100000>, <0x129000 0xb000>;
-				clocks = <&cpm_clk 1 3>, <&cpm_clk 1 9>, <&cpm_clk 1 5>;
-				clock-names = "pp_clk", "gop_clk", "mg_clk";
-				marvell,system-controller = <&cpm_syscon0>;
-				status = "disabled";
-				dma-coherent;
-
-				cpm_eth0: eth0 {
-					interrupts = <ICU_GRP_NSR 39 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 43 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 47 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 51 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 55 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 129 IRQ_TYPE_LEVEL_HIGH>;
-					interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2",
-							  "tx-cpu3", "rx-shared", "link";
-					port-id = <0>;
-					gop-port-id = <0>;
-					status = "disabled";
-				};
-
-				cpm_eth1: eth1 {
-					interrupts = <ICU_GRP_NSR 40 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 44 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 48 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 52 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 56 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 128 IRQ_TYPE_LEVEL_HIGH>;
-					interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2",
-							  "tx-cpu3", "rx-shared", "link";
-					port-id = <1>;
-					gop-port-id = <2>;
-					status = "disabled";
-				};
-
-				cpm_eth2: eth2 {
-					interrupts = <ICU_GRP_NSR 41 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 45 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 49 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 53 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 57 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 127 IRQ_TYPE_LEVEL_HIGH>;
-					interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2",
-							  "tx-cpu3", "rx-shared", "link";
-					port-id = <2>;
-					gop-port-id = <3>;
-					status = "disabled";
-				};
-			};
-
-			cpm_comphy: phy at 120000 {
-				compatible = "marvell,comphy-cp110";
-				reg = <0x120000 0x6000>;
-				marvell,system-controller = <&cpm_syscon0>;
-				#address-cells = <1>;
-				#size-cells = <0>;
-
-				cpm_comphy0: phy at 0 {
-					reg = <0>;
-					#phy-cells = <1>;
-				};
-
-				cpm_comphy1: phy at 1 {
-					reg = <1>;
-					#phy-cells = <1>;
-				};
-
-				cpm_comphy2: phy at 2 {
-					reg = <2>;
-					#phy-cells = <1>;
-				};
-
-				cpm_comphy3: phy at 3 {
-					reg = <3>;
-					#phy-cells = <1>;
-				};
-
-				cpm_comphy4: phy at 4 {
-					reg = <4>;
-					#phy-cells = <1>;
-				};
-
-				cpm_comphy5: phy at 5 {
-					reg = <5>;
-					#phy-cells = <1>;
-				};
-			};
-
-			cpm_mdio: mdio at 12a200 {
-				#address-cells = <1>;
-				#size-cells = <0>;
-				compatible = "marvell,orion-mdio";
-				reg = <0x12a200 0x10>;
-				clocks = <&cpm_clk 1 9>, <&cpm_clk 1 5>;
-				status = "disabled";
-			};
-
-			cpm_xmdio: mdio at 12a600 {
-				#address-cells = <1>;
-				#size-cells = <0>;
-				compatible = "marvell,xmdio";
-				reg = <0x12a600 0x10>;
-				status = "disabled";
-			};
-
-			cpm_icu: interrupt-controller at 1e0000 {
-				compatible = "marvell,cp110-icu";
-				reg = <0x1e0000 0x10>;
-				#interrupt-cells = <3>;
-				interrupt-controller;
-				msi-parent = <&gicp>;
-			};
-
-			cpm_rtc: rtc at 284000 {
-				compatible = "marvell,armada-8k-rtc";
-				reg = <0x284000 0x20>, <0x284080 0x24>;
-				reg-names = "rtc", "rtc-soc";
-				interrupts = <ICU_GRP_NSR 77 IRQ_TYPE_LEVEL_HIGH>;
-			};
-
-			cpm_thermal: thermal at 400078 {
-				compatible = "marvell,armada-cp110-thermal";
-				reg = <0x400078 0x4>,
-				      <0x400070 0x8>;
-			};
-
-			cpm_syscon0: system-controller at 440000 {
-				compatible = "syscon", "simple-mfd";
-				reg = <0x440000 0x2000>;
-
-				cpm_clk: clock {
-					compatible = "marvell,cp110-clock";
-					#clock-cells = <2>;
-				};
-
-				cpm_gpio1: gpio at 100 {
-					compatible = "marvell,armada-8k-gpio";
-					offset = <0x100>;
-					ngpios = <32>;
-					gpio-controller;
-					#gpio-cells = <2>;
-					gpio-ranges = <&cpm_pinctrl 0 0 32>;
-					interrupt-controller;
-					interrupts = <ICU_GRP_NSR 86 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 85 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 84 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 83 IRQ_TYPE_LEVEL_HIGH>;
-					status = "disabled";
-				};
-
-				cpm_gpio2: gpio at 140 {
-					compatible = "marvell,armada-8k-gpio";
-					offset = <0x140>;
-					ngpios = <31>;
-					gpio-controller;
-					#gpio-cells = <2>;
-					gpio-ranges = <&cpm_pinctrl 0 32 31>;
-					interrupt-controller;
-					interrupts = <ICU_GRP_NSR 82 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 81 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 80 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 79 IRQ_TYPE_LEVEL_HIGH>;
-					status = "disabled";
-				};
-			};
-
-			cpm_usb3_0: usb3 at 500000 {
-				compatible = "marvell,armada-8k-xhci",
-					     "generic-xhci";
-				reg = <0x500000 0x4000>;
-				dma-coherent;
-				interrupts = <ICU_GRP_NSR 106 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cpm_clk 1 22>;
-				status = "disabled";
-			};
-
-			cpm_usb3_1: usb3 at 510000 {
-				compatible = "marvell,armada-8k-xhci",
-					     "generic-xhci";
-				reg = <0x510000 0x4000>;
-				dma-coherent;
-				interrupts = <ICU_GRP_NSR 105 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cpm_clk 1 23>;
-				status = "disabled";
-			};
-
-			cpm_sata0: sata at 540000 {
-				compatible = "marvell,armada-8k-ahci",
-					     "generic-ahci";
-				reg = <0x540000 0x30000>;
-				interrupts = <ICU_GRP_NSR 107 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cpm_clk 1 15>;
-				status = "disabled";
-			};
-
-			cpm_xor0: xor at 6a0000 {
-				compatible = "marvell,armada-7k-xor", "marvell,xor-v2";
-				reg = <0x6a0000 0x1000>,
-				      <0x6b0000 0x1000>;
-				dma-coherent;
-				msi-parent = <&gic_v2m0>;
-				clocks = <&cpm_clk 1 8>;
-			};
-
-			cpm_xor1: xor at 6c0000 {
-				compatible = "marvell,armada-7k-xor", "marvell,xor-v2";
-				reg = <0x6c0000 0x1000>,
-				      <0x6d0000 0x1000>;
-				dma-coherent;
-				msi-parent = <&gic_v2m0>;
-				clocks = <&cpm_clk 1 7>;
-			};
-
-			cpm_spi0: spi at 700600 {
-				compatible = "marvell,armada-380-spi";
-				reg = <0x700600 0x50>;
-				#address-cells = <0x1>;
-				#size-cells = <0x0>;
-				clocks = <&cpm_clk 1 21>;
-				status = "disabled";
-			};
-
-			cpm_spi1: spi at 700680 {
-				compatible = "marvell,armada-380-spi";
-				reg = <0x700680 0x50>;
-				#address-cells = <1>;
-				#size-cells = <0>;
-				clocks = <&cpm_clk 1 21>;
-				status = "disabled";
-			};
-
-			cpm_i2c0: i2c at 701000 {
-				compatible = "marvell,mv78230-i2c";
-				reg = <0x701000 0x20>;
-				#address-cells = <1>;
-				#size-cells = <0>;
-				interrupts = <ICU_GRP_NSR 120 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cpm_clk 1 21>;
-				status = "disabled";
-			};
-
-			cpm_i2c1: i2c at 701100 {
-				compatible = "marvell,mv78230-i2c";
-				reg = <0x701100 0x20>;
-				#address-cells = <1>;
-				#size-cells = <0>;
-				interrupts = <ICU_GRP_NSR 121 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cpm_clk 1 21>;
-				status = "disabled";
-			};
-
-			cpm_nand: nand at 720000 {
-				/*
-				 * Due to the limitation of the pins available
-				 * this controller is only usable on the CPM
-				 * for A7K and on the CPS for A8K.
-				 */
-				compatible = "marvell,armada-8k-nand",
-					     "marvell,armada370-nand";
-				reg = <0x720000 0x54>;
-				#address-cells = <1>;
-				#size-cells = <1>;
-				interrupts = <ICU_GRP_NSR 115 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cpm_clk 1 2>;
-				marvell,system-controller = <&cpm_syscon0>;
-				status = "disabled";
-			};
-
-			cpm_trng: trng at 760000 {
-				compatible = "marvell,armada-8k-rng", "inside-secure,safexcel-eip76";
-				reg = <0x760000 0x7d>;
-				interrupts = <ICU_GRP_NSR 95 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cpm_clk 1 25>;
-				status = "okay";
-			};
-
-			cpm_sdhci0: sdhci at 780000 {
-				compatible = "marvell,armada-cp110-sdhci";
-				reg = <0x780000 0x300>;
-				interrupts = <ICU_GRP_NSR 27 IRQ_TYPE_LEVEL_HIGH>;
-				clock-names = "core";
-				clocks = <&cpm_clk 1 4>;
-				dma-coherent;
-				status = "disabled";
-			};
-
-			cpm_crypto: crypto at 800000 {
-				compatible = "inside-secure,safexcel-eip197";
-				reg = <0x800000 0x200000>;
-				interrupts = <ICU_GRP_NSR 87 IRQ_TYPE_LEVEL_HIGH>,
-					     <ICU_GRP_NSR 88 IRQ_TYPE_LEVEL_HIGH>,
-					     <ICU_GRP_NSR 89 IRQ_TYPE_LEVEL_HIGH>,
-					     <ICU_GRP_NSR 90 IRQ_TYPE_LEVEL_HIGH>,
-					     <ICU_GRP_NSR 91 IRQ_TYPE_LEVEL_HIGH>,
-					     <ICU_GRP_NSR 92 IRQ_TYPE_LEVEL_HIGH>;
-				interrupt-names = "mem", "ring0", "ring1",
-				"ring2", "ring3", "eip";
-				clocks = <&cpm_clk 1 26>;
-				dma-coherent;
-			};
-		};
-
-		cpm_pcie0: pcie at f2600000 {
-			compatible = "marvell,armada8k-pcie", "snps,dw-pcie";
-			reg = <0 0xf2600000 0 0x10000>,
-			      <0 0xf6f00000 0 0x80000>;
-			reg-names = "ctrl", "config";
-			#address-cells = <3>;
-			#size-cells = <2>;
-			#interrupt-cells = <1>;
-			device_type = "pci";
-			dma-coherent;
-			msi-parent = <&gic_v2m0>;
-
-			bus-range = <0 0xff>;
-			ranges =
-				/* downstream I/O */
-				<0x81000000 0 0xf9000000 0  0xf9000000 0 0x10000
-				/* non-prefetchable memory */
-				0x82000000 0 0xf6000000 0  0xf6000000 0 0xf00000>;
-			interrupt-map-mask = <0 0 0 0>;
-			interrupt-map = <0 0 0 0 &cpm_icu ICU_GRP_NSR 22 IRQ_TYPE_LEVEL_HIGH>;
-			interrupts = <ICU_GRP_NSR 22 IRQ_TYPE_LEVEL_HIGH>;
-			num-lanes = <1>;
-			clocks = <&cpm_clk 1 13>;
-			status = "disabled";
-		};
-
-		cpm_pcie1: pcie at f2620000 {
-			compatible = "marvell,armada8k-pcie", "snps,dw-pcie";
-			reg = <0 0xf2620000 0 0x10000>,
-			      <0 0xf7f00000 0 0x80000>;
-			reg-names = "ctrl", "config";
-			#address-cells = <3>;
-			#size-cells = <2>;
-			#interrupt-cells = <1>;
-			device_type = "pci";
-			dma-coherent;
-			msi-parent = <&gic_v2m0>;
-
-			bus-range = <0 0xff>;
-			ranges =
-				/* downstream I/O */
-				<0x81000000 0 0xf9010000 0  0xf9010000 0 0x10000
-				/* non-prefetchable memory */
-				0x82000000 0 0xf7000000 0  0xf7000000 0 0xf00000>;
-			interrupt-map-mask = <0 0 0 0>;
-			interrupt-map = <0 0 0 0 &cpm_icu ICU_GRP_NSR 24 IRQ_TYPE_LEVEL_HIGH>;
-			interrupts = <ICU_GRP_NSR 24 IRQ_TYPE_LEVEL_HIGH>;
-
-			num-lanes = <1>;
-			clocks = <&cpm_clk 1 11>;
-			status = "disabled";
-		};
-
-		cpm_pcie2: pcie at f2640000 {
-			compatible = "marvell,armada8k-pcie", "snps,dw-pcie";
-			reg = <0 0xf2640000 0 0x10000>,
-			      <0 0xf8f00000 0 0x80000>;
-			reg-names = "ctrl", "config";
-			#address-cells = <3>;
-			#size-cells = <2>;
-			#interrupt-cells = <1>;
-			device_type = "pci";
-			dma-coherent;
-			msi-parent = <&gic_v2m0>;
-
-			bus-range = <0 0xff>;
-			ranges =
-				/* downstream I/O */
-				<0x81000000 0 0xf9020000 0  0xf9020000 0 0x10000
-				/* non-prefetchable memory */
-				0x82000000 0 0xf8000000 0  0xf8000000 0 0xf00000>;
-			interrupt-map-mask = <0 0 0 0>;
-			interrupt-map = <0 0 0 0 &cpm_icu ICU_GRP_NSR 23 IRQ_TYPE_LEVEL_HIGH>;
-			interrupts = <ICU_GRP_NSR 23 IRQ_TYPE_LEVEL_HIGH>;
-
-			num-lanes = <1>;
-			clocks = <&cpm_clk 1 12>;
-			status = "disabled";
-		};
-	};
-};
diff --git a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
deleted file mode 100644
index 207b6f444e24..000000000000
--- a/arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi
+++ /dev/null
@@ -1,446 +0,0 @@
-/*
- * Copyright (C) 2016 Marvell Technology Group Ltd.
- *
- * This file is dual-licensed: you can use it either under the terms
- * of the GPLv2 or the X11 license, at your option. Note that this dual
- * licensing only applies to this file, and not this project as a
- * whole.
- *
- *  a) This library is free software; you can redistribute it and/or
- *     modify it under the terms of the GNU General Public License as
- *     published by the Free Software Foundation; either version 2 of the
- *     License, or (at your option) any later version.
- *
- *     This library 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.
- *
- * Or, alternatively,
- *
- *  b) Permission is hereby granted, free of charge, to any person
- *     obtaining a copy of this software and associated documentation
- *     files (the "Software"), to deal in the Software without
- *     restriction, including without limitation the rights to use,
- *     copy, modify, merge, publish, distribute, sublicense, and/or
- *     sell copies of the Software, and to permit persons to whom the
- *     Software is furnished to do so, subject to the following
- *     conditions:
- *
- *     The above copyright notice and this permission notice shall be
- *     included in all copies or substantial portions of the Software.
- *
- *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- *     OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/*
- * Device Tree file for Marvell Armada CP110 Slave.
- */
-
-#include <dt-bindings/interrupt-controller/mvebu-icu.h>
-
-/ {
-	cp110-slave {
-		#address-cells = <2>;
-		#size-cells = <2>;
-		compatible = "simple-bus";
-		interrupt-parent = <&cps_icu>;
-		ranges;
-
-		config-space at f4000000 {
-			#address-cells = <1>;
-			#size-cells = <1>;
-			compatible = "simple-bus";
-			ranges = <0x0 0x0 0xf4000000 0x2000000>;
-
-			cps_ethernet: ethernet at 0 {
-				compatible = "marvell,armada-7k-pp22";
-				reg = <0x0 0x100000>, <0x129000 0xb000>;
-				clocks = <&cps_clk 1 3>, <&cps_clk 1 9>, <&cps_clk 1 5>;
-				clock-names = "pp_clk", "gop_clk", "mg_clk";
-				marvell,system-controller = <&cps_syscon0>;
-				status = "disabled";
-				dma-coherent;
-
-				cps_eth0: eth0 {
-					interrupts = <ICU_GRP_NSR 39 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 43 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 47 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 51 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 55 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 129 IRQ_TYPE_LEVEL_HIGH>;
-					interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2",
-							  "tx-cpu3", "rx-shared", "link";
-					port-id = <0>;
-					gop-port-id = <0>;
-					status = "disabled";
-				};
-
-				cps_eth1: eth1 {
-					interrupts = <ICU_GRP_NSR 40 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 44 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 48 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 52 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 56 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 128 IRQ_TYPE_LEVEL_HIGH>;
-					interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2",
-							  "tx-cpu3", "rx-shared", "link";
-					port-id = <1>;
-					gop-port-id = <2>;
-					status = "disabled";
-				};
-
-				cps_eth2: eth2 {
-					interrupts = <ICU_GRP_NSR 41 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 45 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 49 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 53 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 57 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 127 IRQ_TYPE_LEVEL_HIGH>;
-					interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2",
-							  "tx-cpu3", "rx-shared", "link";
-					port-id = <2>;
-					gop-port-id = <3>;
-					status = "disabled";
-				};
-			};
-
-			cps_comphy: phy at 120000 {
-				compatible = "marvell,comphy-cp110";
-				reg = <0x120000 0x6000>;
-				marvell,system-controller = <&cps_syscon0>;
-				#address-cells = <1>;
-				#size-cells = <0>;
-
-				cps_comphy0: phy at 0 {
-					reg = <0>;
-					#phy-cells = <1>;
-				};
-
-				cps_comphy1: phy at 1 {
-					reg = <1>;
-					#phy-cells = <1>;
-				};
-
-				cps_comphy2: phy at 2 {
-					reg = <2>;
-					#phy-cells = <1>;
-				};
-
-				cps_comphy3: phy at 3 {
-					reg = <3>;
-					#phy-cells = <1>;
-				};
-
-				cps_comphy4: phy at 4 {
-					reg = <4>;
-					#phy-cells = <1>;
-				};
-
-				cps_comphy5: phy at 5 {
-					reg = <5>;
-					#phy-cells = <1>;
-				};
-			};
-
-			cps_mdio: mdio at 12a200 {
-				#address-cells = <1>;
-				#size-cells = <0>;
-				compatible = "marvell,orion-mdio";
-				reg = <0x12a200 0x10>;
-				clocks = <&cps_clk 1 9>, <&cps_clk 1 5>;
-				status = "disabled";
-			};
-
-			cps_xmdio: mdio at 12a600 {
-				#address-cells = <1>;
-				#size-cells = <0>;
-				compatible = "marvell,xmdio";
-				reg = <0x12a600 0x10>;
-				status = "disabled";
-			};
-
-			cps_icu: interrupt-controller at 1e0000 {
-				compatible = "marvell,cp110-icu";
-				reg = <0x1e0000 0x10>;
-				#interrupt-cells = <3>;
-				interrupt-controller;
-				msi-parent = <&gicp>;
-			};
-
-			cps_rtc: rtc at 284000 {
-				compatible = "marvell,armada-8k-rtc";
-				reg = <0x284000 0x20>, <0x284080 0x24>;
-				reg-names = "rtc", "rtc-soc";
-				interrupts = <ICU_GRP_NSR 77 IRQ_TYPE_LEVEL_HIGH>;
-			};
-
-			cps_thermal: thermal at 400078 {
-				compatible = "marvell,armada-cp110-thermal";
-				reg = <0x400078 0x4>,
-				      <0x400070 0x8>;
-			};
-
-			cps_syscon0: system-controller at 440000 {
-				compatible = "syscon", "simple-mfd";
-				reg = <0x440000 0x2000>;
-
-				cps_clk: clock {
-					compatible = "marvell,cp110-clock";
-					#clock-cells = <2>;
-				};
-
-				cps_gpio1: gpio at 100 {
-					compatible = "marvell,armada-8k-gpio";
-					offset = <0x100>;
-					ngpios = <32>;
-					gpio-controller;
-					#gpio-cells = <2>;
-					gpio-ranges = <&cps_pinctrl 0 0 32>;
-					interrupt-controller;
-					interrupts = <ICU_GRP_NSR 86 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 85 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 84 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 83 IRQ_TYPE_LEVEL_HIGH>;
-					status = "disabled";
-				};
-
-				cps_gpio2: gpio at 140 {
-					compatible = "marvell,armada-8k-gpio";
-					offset = <0x140>;
-					ngpios = <31>;
-					gpio-controller;
-					#gpio-cells = <2>;
-					gpio-ranges = <&cps_pinctrl 0 32 31>;
-					interrupt-controller;
-					interrupts = <ICU_GRP_NSR 82 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 81 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 80 IRQ_TYPE_LEVEL_HIGH>,
-						     <ICU_GRP_NSR 79 IRQ_TYPE_LEVEL_HIGH>;
-					status = "disabled";
-				};
-
-			};
-
-			cps_usb3_0: usb3 at 500000 {
-				compatible = "marvell,armada-8k-xhci",
-					     "generic-xhci";
-				reg = <0x500000 0x4000>;
-				dma-coherent;
-				interrupts = <ICU_GRP_NSR 106 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cps_clk 1 22>;
-				status = "disabled";
-			};
-
-			cps_usb3_1: usb3 at 510000 {
-				compatible = "marvell,armada-8k-xhci",
-					     "generic-xhci";
-				reg = <0x510000 0x4000>;
-				dma-coherent;
-				interrupts = <ICU_GRP_NSR 105 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cps_clk 1 23>;
-				status = "disabled";
-			};
-
-			cps_sata0: sata at 540000 {
-				compatible = "marvell,armada-8k-ahci",
-					     "generic-ahci";
-				reg = <0x540000 0x30000>;
-				interrupts = <ICU_GRP_NSR 107 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cps_clk 1 15>;
-				status = "disabled";
-			};
-
-			cps_xor0: xor at 6a0000 {
-				compatible = "marvell,armada-7k-xor", "marvell,xor-v2";
-				reg = <0x6a0000 0x1000>,
-				      <0x6b0000 0x1000>;
-				dma-coherent;
-				msi-parent = <&gic_v2m0>;
-				clocks = <&cps_clk 1 8>;
-			};
-
-			cps_xor1: xor at 6c0000 {
-				compatible = "marvell,armada-7k-xor", "marvell,xor-v2";
-				reg = <0x6c0000 0x1000>,
-				      <0x6d0000 0x1000>;
-				dma-coherent;
-				msi-parent = <&gic_v2m0>;
-				clocks = <&cps_clk 1 7>;
-			};
-
-			cps_spi0: spi at 700600 {
-				compatible = "marvell,armada-380-spi";
-				reg = <0x700600 0x50>;
-				#address-cells = <0x1>;
-				#size-cells = <0x0>;
-				clocks = <&cps_clk 1 21>;
-				status = "disabled";
-			};
-
-			cps_spi1: spi at 700680 {
-				compatible = "marvell,armada-380-spi";
-				reg = <0x700680 0x50>;
-				#address-cells = <1>;
-				#size-cells = <0>;
-				clocks = <&cps_clk 1 21>;
-				status = "disabled";
-			};
-
-			cps_i2c0: i2c at 701000 {
-				compatible = "marvell,mv78230-i2c";
-				reg = <0x701000 0x20>;
-				#address-cells = <1>;
-				#size-cells = <0>;
-				interrupts = <ICU_GRP_NSR 120 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cps_clk 1 21>;
-				status = "disabled";
-			};
-
-			cps_i2c1: i2c at 701100 {
-				compatible = "marvell,mv78230-i2c";
-				reg = <0x701100 0x20>;
-				#address-cells = <1>;
-				#size-cells = <0>;
-				interrupts = <ICU_GRP_NSR 121 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cps_clk 1 21>;
-				status = "disabled";
-			};
-
-			cps_nand: nand at 720000 {
-				/*
-				 * Due to the limitation of the pins available
-				 * this controller is only usable on the CPM
-				 * for A7K and on the CPS for A8K.
-				 */
-				compatible = "marvell,armada-8k-nand",
-					     "marvell,armada370-nand";
-				reg = <0x720000 0x54>;
-				#address-cells = <1>;
-				#size-cells = <1>;
-				interrupts = <ICU_GRP_NSR 115 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cps_clk 1 2>;
-				marvell,system-controller = <&cpm_syscon0>;
-				status = "disabled";
-			};
-
-			cps_trng: trng at 760000 {
-				compatible = "marvell,armada-8k-rng", "inside-secure,safexcel-eip76";
-				reg = <0x760000 0x7d>;
-				interrupts = <ICU_GRP_NSR 95 IRQ_TYPE_LEVEL_HIGH>;
-				clocks = <&cps_clk 1 25>;
-				status = "okay";
-			};
-
-			cps_crypto: crypto at 800000 {
-				compatible = "inside-secure,safexcel-eip197";
-				reg = <0x800000 0x200000>;
-				interrupts = <ICU_GRP_NSR 87 IRQ_TYPE_LEVEL_HIGH>,
-					     <ICU_GRP_NSR 88 IRQ_TYPE_LEVEL_HIGH>,
-					     <ICU_GRP_NSR 89 IRQ_TYPE_LEVEL_HIGH>,
-					     <ICU_GRP_NSR 90 IRQ_TYPE_LEVEL_HIGH>,
-					     <ICU_GRP_NSR 91 IRQ_TYPE_LEVEL_HIGH>,
-					     <ICU_GRP_NSR 92 IRQ_TYPE_LEVEL_HIGH>;
-				interrupt-names = "mem", "ring0", "ring1",
-						  "ring2", "ring3", "eip";
-				clocks = <&cps_clk 1 26>;
-				dma-coherent;
-				/*
-				 * The cryptographic engine found on the cp110
-				 * master is enabled by default at the SoC
-				 * level. Because it is not possible as of now
-				 * to enable two cryptographic engines in
-				 * parallel, disable this one by default.
-				 */
-				status = "disabled";
-			};
-		};
-
-		cps_pcie0: pcie at f4600000 {
-			compatible = "marvell,armada8k-pcie", "snps,dw-pcie";
-			reg = <0 0xf4600000 0 0x10000>,
-			      <0 0xfaf00000 0 0x80000>;
-			reg-names = "ctrl", "config";
-			#address-cells = <3>;
-			#size-cells = <2>;
-			#interrupt-cells = <1>;
-			device_type = "pci";
-			dma-coherent;
-			msi-parent = <&gic_v2m0>;
-
-			bus-range = <0 0xff>;
-			ranges =
-				/* downstream I/O */
-				<0x81000000 0 0xfd000000 0  0xfd000000 0 0x10000
-				/* non-prefetchable memory */
-				0x82000000 0 0xfa000000 0  0xfa000000 0 0xf00000>;
-			interrupt-map-mask = <0 0 0 0>;
-			interrupt-map = <0 0 0 0 &cps_icu ICU_GRP_NSR 22 IRQ_TYPE_LEVEL_HIGH>;
-			interrupts = <ICU_GRP_NSR 22 IRQ_TYPE_LEVEL_HIGH>;
-			num-lanes = <1>;
-			clocks = <&cps_clk 1 13>;
-			status = "disabled";
-		};
-
-		cps_pcie1: pcie at f4620000 {
-			compatible = "marvell,armada8k-pcie", "snps,dw-pcie";
-			reg = <0 0xf4620000 0 0x10000>,
-			      <0 0xfbf00000 0 0x80000>;
-			reg-names = "ctrl", "config";
-			#address-cells = <3>;
-			#size-cells = <2>;
-			#interrupt-cells = <1>;
-			device_type = "pci";
-			dma-coherent;
-			msi-parent = <&gic_v2m0>;
-
-			bus-range = <0 0xff>;
-			ranges =
-				/* downstream I/O */
-				<0x81000000 0 0xfd010000 0  0xfd010000 0 0x10000
-				/* non-prefetchable memory */
-				0x82000000 0 0xfb000000 0  0xfb000000 0 0xf00000>;
-			interrupt-map-mask = <0 0 0 0>;
-			interrupt-map = <0 0 0 0 &cps_icu ICU_GRP_NSR 24 IRQ_TYPE_LEVEL_HIGH>;
-			interrupts = <ICU_GRP_NSR 24 IRQ_TYPE_LEVEL_HIGH>;
-
-			num-lanes = <1>;
-			clocks = <&cps_clk 1 11>;
-			status = "disabled";
-		};
-
-		cps_pcie2: pcie at f4640000 {
-			compatible = "marvell,armada8k-pcie", "snps,dw-pcie";
-			reg = <0 0xf4640000 0 0x10000>,
-			      <0 0xfcf00000 0 0x80000>;
-			reg-names = "ctrl", "config";
-			#address-cells = <3>;
-			#size-cells = <2>;
-			#interrupt-cells = <1>;
-			device_type = "pci";
-			dma-coherent;
-			msi-parent = <&gic_v2m0>;
-
-			bus-range = <0 0xff>;
-			ranges =
-				/* downstream I/O */
-				<0x81000000 0 0xfd020000 0  0xfd020000 0 0x10000
-				/* non-prefetchable memory */
-				0x82000000 0 0xfc000000 0  0xfc000000 0 0xf00000>;
-			interrupt-map-mask = <0 0 0 0>;
-			interrupt-map = <0 0 0 0 &cps_icu ICU_GRP_NSR 23 IRQ_TYPE_LEVEL_HIGH>;
-			interrupts = <ICU_GRP_NSR 23 IRQ_TYPE_LEVEL_HIGH>;
-
-			num-lanes = <1>;
-			clocks = <&cps_clk 1 12>;
-			status = "disabled";
-		};
-	};
-};
diff --git a/arch/arm64/boot/dts/marvell/armada-cp110.dtsi b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi
new file mode 100644
index 000000000000..08989a158578
--- /dev/null
+++ b/arch/arm64/boot/dts/marvell/armada-cp110.dtsi
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR X11)
+/*
+ * Copyright (C) 2016 Marvell Technology Group Ltd.
+ */
+
+/*
+ * Device Tree file for Marvell Armada CP110.
+ */
+
+#include <dt-bindings/interrupt-controller/mvebu-icu.h>
+
+#include "armada-common.dtsi"
+
+#define CP110_PCIEx_IO_BASE(iface)	(CP110_PCIE_IO_BASE + (iface *  0x10000))
+#define CP110_PCIEx_MEM_BASE(iface)	(CP110_PCIE_MEM_BASE + (iface *  0x1000000))
+#define CP110_PCIEx_CONF_BASE(iface)	(CP110_PCIEx_MEM_BASE(iface) + 0xf00000)
+
+/ {
+	/*
+	 * The contents of the node are defined below, in order to
+	 * save one indentation level
+	 */
+	CP110_NAME: CP110_NAME { };
+};
+
+&CP110_NAME {
+	#address-cells = <2>;
+	#size-cells = <2>;
+	compatible = "simple-bus";
+	interrupt-parent = <&CP110_LABEL(icu)>;
+	ranges;
+
+	config-space at CP110_BASE {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "simple-bus";
+		ranges = <0x0 0x0 ADDRESSIFY(CP110_BASE) 0x2000000>;
+
+		CP110_LABEL(ethernet): ethernet at 0 {
+			compatible = "marvell,armada-7k-pp22";
+			reg = <0x0 0x100000>, <0x129000 0xb000>;
+			clocks = <&CP110_LABEL(clk) 1 3>, <&CP110_LABEL(clk) 1 9>,
+				 <&CP110_LABEL(clk) 1 5>;
+			clock-names = "pp_clk", "gop_clk", "mg_clk";
+			marvell,system-controller = <&CP110_LABEL(syscon0)>;
+			status = "disabled";
+			dma-coherent;
+
+			CP110_LABEL(eth0): eth0 {
+				interrupts = <ICU_GRP_NSR 39 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 43 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 47 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 51 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 55 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 129 IRQ_TYPE_LEVEL_HIGH>;
+				interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2",
+					"tx-cpu3", "rx-shared", "link";
+				port-id = <0>;
+				gop-port-id = <0>;
+				status = "disabled";
+			};
+
+			CP110_LABEL(eth1): eth1 {
+				interrupts = <ICU_GRP_NSR 40 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 44 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 48 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 52 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 56 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 128 IRQ_TYPE_LEVEL_HIGH>;
+				interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2",
+					"tx-cpu3", "rx-shared", "link";
+				port-id = <1>;
+				gop-port-id = <2>;
+				status = "disabled";
+			};
+
+			CP110_LABEL(eth2): eth2 {
+				interrupts = <ICU_GRP_NSR 41 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 45 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 49 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 53 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 57 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 127 IRQ_TYPE_LEVEL_HIGH>;
+				interrupt-names = "tx-cpu0", "tx-cpu1", "tx-cpu2",
+					"tx-cpu3", "rx-shared", "link";
+				port-id = <2>;
+				gop-port-id = <3>;
+				status = "disabled";
+			};
+		};
+
+		CP110_LABEL(comphy): phy at 120000 {
+			compatible = "marvell,comphy-cp110";
+			reg = <0x120000 0x6000>;
+			marvell,system-controller = <&CP110_LABEL(syscon0)>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			CP110_LABEL(comphy0): phy at 0 {
+				reg = <0>;
+				#phy-cells = <1>;
+			};
+
+			CP110_LABEL(comphy1): phy at 1 {
+				reg = <1>;
+				#phy-cells = <1>;
+			};
+
+			CP110_LABEL(comphy2): phy at 2 {
+				reg = <2>;
+				#phy-cells = <1>;
+			};
+
+			CP110_LABEL(comphy3): phy at 3 {
+				reg = <3>;
+				#phy-cells = <1>;
+			};
+
+			CP110_LABEL(comphy4): phy at 4 {
+				reg = <4>;
+				#phy-cells = <1>;
+			};
+
+			CP110_LABEL(comphy5): phy at 5 {
+				reg = <5>;
+				#phy-cells = <1>;
+			};
+		};
+
+		CP110_LABEL(mdio): mdio at 12a200 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "marvell,orion-mdio";
+			reg = <0x12a200 0x10>;
+			clocks = <&CP110_LABEL(clk) 1 9>, <&CP110_LABEL(clk) 1 5>;
+			status = "disabled";
+		};
+
+		CP110_LABEL(xmdio): mdio at 12a600 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "marvell,xmdio";
+			reg = <0x12a600 0x10>;
+			status = "disabled";
+		};
+
+		CP110_LABEL(icu): interrupt-controller at 1e0000 {
+			compatible = "marvell,cp110-icu";
+			reg = <0x1e0000 0x10>;
+			#interrupt-cells = <3>;
+			interrupt-controller;
+			msi-parent = <&gicp>;
+		};
+
+		CP110_LABEL(rtc): rtc at 284000 {
+			compatible = "marvell,armada-8k-rtc";
+			reg = <0x284000 0x20>, <0x284080 0x24>;
+			reg-names = "rtc", "rtc-soc";
+			interrupts = <ICU_GRP_NSR 77 IRQ_TYPE_LEVEL_HIGH>;
+		};
+
+		CP110_LABEL(thermal): thermal at 400078 {
+			compatible = "marvell,armada-cp110-thermal";
+			reg = <0x400078 0x4>,
+			<0x400070 0x8>;
+		};
+
+		CP110_LABEL(syscon0): system-controller at 440000 {
+			compatible = "syscon", "simple-mfd";
+			reg = <0x440000 0x2000>;
+
+			CP110_LABEL(clk): clock {
+				compatible = "marvell,cp110-clock";
+				#clock-cells = <2>;
+			};
+
+			CP110_LABEL(gpio1): gpio at 100 {
+				compatible = "marvell,armada-8k-gpio";
+				offset = <0x100>;
+				ngpios = <32>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				gpio-ranges = <&CP110_LABEL(pinctrl) 0 0 32>;
+				interrupt-controller;
+				interrupts = <ICU_GRP_NSR 86 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 85 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 84 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 83 IRQ_TYPE_LEVEL_HIGH>;
+				status = "disabled";
+			};
+
+			CP110_LABEL(gpio2): gpio at 140 {
+				compatible = "marvell,armada-8k-gpio";
+				offset = <0x140>;
+				ngpios = <31>;
+				gpio-controller;
+				#gpio-cells = <2>;
+				gpio-ranges = <&CP110_LABEL(pinctrl) 0 32 31>;
+				interrupt-controller;
+				interrupts = <ICU_GRP_NSR 82 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 81 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 80 IRQ_TYPE_LEVEL_HIGH>,
+					<ICU_GRP_NSR 79 IRQ_TYPE_LEVEL_HIGH>;
+				status = "disabled";
+			};
+		};
+
+		CP110_LABEL(usb3_0): usb3 at 500000 {
+			compatible = "marvell,armada-8k-xhci",
+			"generic-xhci";
+			reg = <0x500000 0x4000>;
+			dma-coherent;
+			interrupts = <ICU_GRP_NSR 106 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&CP110_LABEL(clk) 1 22>;
+			status = "disabled";
+		};
+
+		CP110_LABEL(usb3_1): usb3 at 510000 {
+			compatible = "marvell,armada-8k-xhci",
+			"generic-xhci";
+			reg = <0x510000 0x4000>;
+			dma-coherent;
+			interrupts = <ICU_GRP_NSR 105 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&CP110_LABEL(clk) 1 23>;
+			status = "disabled";
+		};
+
+		CP110_LABEL(sata0): sata at 540000 {
+			compatible = "marvell,armada-8k-ahci",
+			"generic-ahci";
+			reg = <0x540000 0x30000>;
+			interrupts = <ICU_GRP_NSR 107 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&CP110_LABEL(clk) 1 15>;
+			status = "disabled";
+		};
+
+		CP110_LABEL(xor0): xor at 6a0000 {
+			compatible = "marvell,armada-7k-xor", "marvell,xor-v2";
+			reg = <0x6a0000 0x1000>, <0x6b0000 0x1000>;
+			dma-coherent;
+			msi-parent = <&gic_v2m0>;
+			clocks = <&CP110_LABEL(clk) 1 8>;
+		};
+
+		CP110_LABEL(xor1): xor at 6c0000 {
+			compatible = "marvell,armada-7k-xor", "marvell,xor-v2";
+			reg = <0x6c0000 0x1000>, <0x6d0000 0x1000>;
+			dma-coherent;
+			msi-parent = <&gic_v2m0>;
+			clocks = <&CP110_LABEL(clk) 1 7>;
+		};
+
+		CP110_LABEL(spi0): spi at 700600 {
+			compatible = "marvell,armada-380-spi";
+			reg = <0x700600 0x50>;
+			#address-cells = <0x1>;
+			#size-cells = <0x0>;
+			clocks = <&CP110_LABEL(clk) 1 21>;
+			status = "disabled";
+		};
+
+		CP110_LABEL(spi1): spi at 700680 {
+			compatible = "marvell,armada-380-spi";
+			reg = <0x700680 0x50>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			clocks = <&CP110_LABEL(clk) 1 21>;
+			status = "disabled";
+		};
+
+		CP110_LABEL(i2c0): i2c at 701000 {
+			compatible = "marvell,mv78230-i2c";
+			reg = <0x701000 0x20>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <ICU_GRP_NSR 120 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&CP110_LABEL(clk) 1 21>;
+			status = "disabled";
+		};
+
+		CP110_LABEL(i2c1): i2c at 701100 {
+			compatible = "marvell,mv78230-i2c";
+			reg = <0x701100 0x20>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <ICU_GRP_NSR 121 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&CP110_LABEL(clk) 1 21>;
+			status = "disabled";
+		};
+
+		CP110_LABEL(nand): nand at 720000 {
+			/*
+			* Due to the limitation of the pins available
+			* this controller is only usable on the CPM
+			* for A7K and on the CPS for A8K.
+			*/
+			compatible = "marvell,armada-8k-nand",
+			"marvell,armada370-nand";
+			reg = <0x720000 0x54>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			interrupts = <ICU_GRP_NSR 115 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&CP110_LABEL(clk) 1 2>;
+			marvell,system-controller = <&CP110_LABEL(syscon0)>;
+			status = "disabled";
+		};
+
+		CP110_LABEL(trng): trng at 760000 {
+			compatible = "marvell,armada-8k-rng",
+			"inside-secure,safexcel-eip76";
+			reg = <0x760000 0x7d>;
+			interrupts = <ICU_GRP_NSR 95 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&CP110_LABEL(clk) 1 25>;
+			status = "okay";
+		};
+
+		CP110_LABEL(sdhci0): sdhci at 780000 {
+			compatible = "marvell,armada-cp110-sdhci";
+			reg = <0x780000 0x300>;
+			interrupts = <ICU_GRP_NSR 27 IRQ_TYPE_LEVEL_HIGH>;
+			clock-names = "core";
+			clocks = <&CP110_LABEL(clk) 1 4>;
+			dma-coherent;
+			status = "disabled";
+		};
+
+		CP110_LABEL(crypto): crypto at 800000 {
+			compatible = "inside-secure,safexcel-eip197";
+			reg = <0x800000 0x200000>;
+			interrupts = <ICU_GRP_NSR 87 IRQ_TYPE_LEVEL_HIGH>,
+				<ICU_GRP_NSR 88 IRQ_TYPE_LEVEL_HIGH>,
+				<ICU_GRP_NSR 89 IRQ_TYPE_LEVEL_HIGH>,
+				<ICU_GRP_NSR 90 IRQ_TYPE_LEVEL_HIGH>,
+				<ICU_GRP_NSR 91 IRQ_TYPE_LEVEL_HIGH>,
+				<ICU_GRP_NSR 92 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "mem", "ring0", "ring1",
+				"ring2", "ring3", "eip";
+			clocks = <&CP110_LABEL(clk) 1 26>;
+			dma-coherent;
+		};
+	};
+
+	CP110_LABEL(pcie0): pcie at CP110_PCIE0_BASE {
+		compatible = "marvell,armada8k-pcie", "snps,dw-pcie";
+		reg = <0 ADDRESSIFY(CP110_PCIE0_BASE) 0 0x10000>,
+		      <0 CP110_PCIEx_CONF_BASE(0) 0 0x80000>;
+		reg-names = "ctrl", "config";
+		#address-cells = <3>;
+		#size-cells = <2>;
+		#interrupt-cells = <1>;
+		device_type = "pci";
+		dma-coherent;
+		msi-parent = <&gic_v2m0>;
+
+		bus-range = <0 0xff>;
+		ranges =
+		/* downstream I/O */
+		<0x81000000 0 CP110_PCIEx_IO_BASE(0) 0  CP110_PCIEx_IO_BASE(0) 0 0x10000
+		/* non-prefetchable memory */
+		0x82000000 0 CP110_PCIEx_MEM_BASE(0) 0  CP110_PCIEx_MEM_BASE(0) 0 0xf00000>;
+		interrupt-map-mask = <0 0 0 0>;
+		interrupt-map = <0 0 0 0 &CP110_LABEL(icu) ICU_GRP_NSR 22 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <ICU_GRP_NSR 22 IRQ_TYPE_LEVEL_HIGH>;
+		num-lanes = <1>;
+		clocks = <&CP110_LABEL(clk) 1 13>;
+		status = "disabled";
+	};
+
+	CP110_LABEL(pcie1): pcie at CP110_PCIE1_BASE {
+		compatible = "marvell,armada8k-pcie", "snps,dw-pcie";
+		reg = <0 ADDRESSIFY(CP110_PCIE1_BASE) 0 0x10000>,
+		      <0 CP110_PCIEx_CONF_BASE(1) 0 0x80000>;
+		reg-names = "ctrl", "config";
+		#address-cells = <3>;
+		#size-cells = <2>;
+		#interrupt-cells = <1>;
+		device_type = "pci";
+		dma-coherent;
+		msi-parent = <&gic_v2m0>;
+
+		bus-range = <0 0xff>;
+		ranges =
+		/* downstream I/O */
+		<0x81000000 0 CP110_PCIEx_IO_BASE(1) 0  CP110_PCIEx_IO_BASE(1) 0 0x10000
+		/* non-prefetchable memory */
+		0x82000000 0 CP110_PCIEx_MEM_BASE(1) 0  CP110_PCIEx_MEM_BASE(1) 0 0xf00000>;
+		interrupt-map-mask = <0 0 0 0>;
+		interrupt-map = <0 0 0 0 &CP110_LABEL(icu) ICU_GRP_NSR 24 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <ICU_GRP_NSR 24 IRQ_TYPE_LEVEL_HIGH>;
+
+		num-lanes = <1>;
+		clocks = <&CP110_LABEL(clk) 1 11>;
+		status = "disabled";
+	};
+
+	CP110_LABEL(pcie2): pcie at CP110_PCIE2_BASE {
+		compatible = "marvell,armada8k-pcie", "snps,dw-pcie";
+		reg = <0 ADDRESSIFY(CP110_PCIE2_BASE) 0 0x10000>,
+		      <0 CP110_PCIEx_CONF_BASE(2) 0 0x80000>;
+		reg-names = "ctrl", "config";
+		#address-cells = <3>;
+		#size-cells = <2>;
+		#interrupt-cells = <1>;
+		device_type = "pci";
+		dma-coherent;
+		msi-parent = <&gic_v2m0>;
+
+		bus-range = <0 0xff>;
+		ranges =
+		/* downstream I/O */
+		<0x81000000 0 CP110_PCIEx_IO_BASE(2) 0  CP110_PCIEx_IO_BASE(2) 0 0x10000
+		/* non-prefetchable memory */
+		0x82000000 0 CP110_PCIEx_MEM_BASE(2) 0  CP110_PCIEx_MEM_BASE(2) 0 0xf00000>;
+		interrupt-map-mask = <0 0 0 0>;
+		interrupt-map = <0 0 0 0 &CP110_LABEL(icu) ICU_GRP_NSR 23 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <ICU_GRP_NSR 23 IRQ_TYPE_LEVEL_HIGH>;
+
+		num-lanes = <1>;
+		clocks = <&CP110_LABEL(clk) 1 12>;
+		status = "disabled";
+	};
+};
-- 
2.14.3

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox