@@ -183,4 +183,6 @@ config DA8XX_MSTPRI
source "drivers/bus/fsl-mc/Kconfig"
+source "drivers/bus/domains/Kconfig"
+
endmenu
@@ -32,3 +32,5 @@ obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
obj-$(CONFIG_DA8XX_MSTPRI) += da8xx-mstpri.o
+
+obj-$(CONFIG_DOMAINS_CONTROLLERS) += domains/
new file mode 100644
@@ -0,0 +1,7 @@
+menu "Bus Domains Controllers"
+
+config DOMAINS_CONTROLLERS
+ bool "Support of bus domains controllers"
+ depends on OF
+
+endmenu
new file mode 100644
@@ -0,0 +1 @@
+obj-$(CONFIG_DOMAINS_CONTROLLERS) += domainsctrl.o
new file mode 100644
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ */
+
+#include <linux/device.h>
+#include <linux/domainsctrl.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+/* Mutex taken to protect domainctrl_list */
+static DEFINE_MUTEX(domainctrl_list_mutex);
+
+/* Global list of domain control devices (struct domains_ctrl) */
+static LIST_HEAD(domainctrl_list);
+
+struct domains_ctrl {
+ struct list_head node;
+ struct device *dev;
+ struct domains_ctrl_ops *ops;
+};
+
+static struct domains_ctrl *get_domainctrl_from_node(struct device_node *np)
+{
+ struct domains_ctrl *ctrl;
+
+ mutex_lock(&domainctrl_list_mutex);
+
+ list_for_each_entry(ctrl, &domainctrl_list, node) {
+ if (ctrl->dev->of_node == np) {
+ mutex_unlock(&domainctrl_list_mutex);
+ return ctrl;
+ }
+ }
+
+ mutex_unlock(&domainctrl_list_mutex);
+
+ return NULL;
+}
+
+/**
+ * domainsctrl_set_config_by_index
+ *
+ * Set a domains controller configuration based on given index.
+ *
+ * @dev: device with domain configuration to apply.
+ * @index: the index of the configuration in device node.
+ *
+ * Returns 0 if OK, -EPROBE_DEFER if waiting for domains controller to be
+ * registered or negative value on other errors.
+ */
+int domainsctrl_set_config_by_index(struct device *dev, int index)
+{
+ struct device_node *np = dev->of_node;
+ char *propname;
+ int configs, i, err = 0;
+
+ if (!np)
+ return 0;
+
+ propname = kasprintf(GFP_KERNEL, "domainctrl-%d", index);
+ configs = of_count_phandle_with_args(np, propname, "#domainctrl-cells");
+ if (configs < 0) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ for (i = 0; i < configs; i++) {
+ struct domains_ctrl *ctrl;
+ struct of_phandle_args args;
+
+ err = of_parse_phandle_with_args(np, propname,
+ "#domainctrl-cells",
+ i, &args);
+ if (err)
+ goto error;
+
+ /* Test if the controller is (or will be) available */
+ if (!of_device_is_available(args.np)) {
+ of_node_put(args.np);
+ continue;
+ }
+
+ ctrl = get_domainctrl_from_node(args.np);
+ of_node_put(args.np);
+
+ /* Controller is not yet registered */
+ if (!ctrl) {
+ err = -EPROBE_DEFER;
+ goto error;
+ }
+
+ err = ctrl->ops->set_config(ctrl->dev, &args);
+ if (err)
+ goto error;
+ }
+
+error:
+ kfree(propname);
+ return err;
+}
+EXPORT_SYMBOL_GPL(domainsctrl_set_config_by_index);
+
+/**
+ * domainsctrl_set_config_by_name
+ *
+ * Set a domains controller configuration based on given name.
+ *
+ * @dev: device with domain configuration to apply.
+ * @name: the name of the configuration in device node.
+ *
+ * Returns 0 if OK, -EPROBE_DEFER if waiting for domains controller to be
+ * registered or negative value on other errors.
+ */
+int domainsctrl_set_config_by_name(struct device *dev, char *name)
+{
+ const char *configname;
+ int count, i;
+
+ count = of_property_count_strings(dev->of_node, "domainctrl-names");
+ for (i = 0; i < count; i++) {
+ int err;
+
+ err = of_property_read_string_index(dev->of_node,
+ "domainctrl-names",
+ i, &configname);
+ if (err)
+ return err;
+
+ if (strcmp(name, configname))
+ continue;
+
+ return domainsctrl_set_config_by_index(dev, i);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(domainsctrl_set_config_by_name);
+
+/**
+ * domainsctrl_set_default_config
+ *
+ * Set the default configuration for device.
+ * First try to apply configuration named "default", if it fails
+ * or doesn't exist, try to apply domainsctrl-0 configuration.
+ *
+ * @dev: device with domain configuration to apply.
+ *
+ * Returns 0 if OK, -EPROBE_DEFER if waiting for domains controller to be
+ * registered or negative value on other errors.
+ */
+int domainsctrl_set_default_config(struct device *dev)
+{
+ int ret;
+
+ ret = domainsctrl_set_config_by_name(dev, "default");
+ if (!ret || (ret == -EPROBE_DEFER))
+ return ret;
+
+ return domainsctrl_set_config_by_index(dev, 0);
+}
+EXPORT_SYMBOL_GPL(domainsctrl_set_default_config);
+
+/**
+ * domainsctrl_register
+ *
+ * Register a domains controller device.
+ *
+ * @dev: device implementing domains controller.
+ * @ops: domains controller operations.
+ *
+ * Returns 0 if OK or negative value on error.
+ */
+int domainsctrl_register(struct device *dev,
+ struct domains_ctrl_ops *ops)
+{
+ struct domains_ctrl *ctrl;
+
+ if (!dev || !ops || !ops->set_config)
+ return -EINVAL;
+
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&ctrl->node);
+
+ ctrl->dev = dev;
+ ctrl->ops = ops;
+
+ mutex_lock(&domainctrl_list_mutex);
+ list_add_tail(&ctrl->node, &domainctrl_list);
+ mutex_unlock(&domainctrl_list_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(domainsctrl_register);
+
+/**
+ * domainsctrl_unregister
+ *
+ * Unregister a domains controller device.
+ *
+ * @dev: device implementing domains controller.
+ */
+void domainsctrl_unregister(struct device *dev)
+{
+ struct domains_ctrl *ctrl;
+
+ ctrl = get_domainctrl_from_node(dev->of_node);
+ if (!ctrl)
+ return;
+
+ mutex_lock(&domainctrl_list_mutex);
+ list_del(&ctrl->node);
+ mutex_unlock(&domainctrl_list_mutex);
+
+ kfree(ctrl);
+}
+EXPORT_SYMBOL_GPL(domainsctrl_unregister);
+
+static int __init domainsctrl_init(void)
+{
+ pr_info("initialized bus domain controller subsystem\n");
+ return 0;
+}
+
+/* Init early since drivers really need to configure domains early */
+core_initcall(domainsctrl_init);
new file mode 100644
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ */
+
+#ifndef _DOMAINSCTRL_H_
+#define _DOMAINSCTRL_H_
+
+#include <linux/device.h>
+#include <linux/of.h>
+
+/**
+ * struct domains_ctrl_ops
+ *
+ * Domains controller operations structure to be filled by drivers.
+ */
+struct domains_ctrl_ops {
+ /**
+ * @set_config:
+ *
+ * Driver callback to set a domain configuration on a domain controller.
+ * Configuration arguments are provided in out_args parameter.
+ *
+ * Returns 0 on success, a negative error code on failure.
+ */
+ int (*set_config)(struct device *dev, struct of_phandle_args *out_args);
+};
+
+#ifdef CONFIG_DOMAINS_CONTROLLERS
+
+int domainsctrl_set_config_by_index(struct device *dev, int index);
+int domainsctrl_set_config_by_name(struct device *dev, char *name);
+int domainsctrl_set_default_config(struct device *dev);
+
+int domainsctrl_register(struct device *dev, struct domains_ctrl_ops *ops);
+
+void domainsctrl_unregister(struct device *dev);
+
+#else
+
+static inline int domainsctrl_set_config_by_index(struct device *dev, int index)
+{
+ return 0;
+}
+
+static inline int domainsctrl_set_config_by_name(struct device *dev, char *name)
+{
+ return 0;
+}
+
+static inline int domainsctrl_set_default_config(struct device *dev)
+{
+ return 0;
+}
+
+static inline int domainsctrl_register(struct device *dev,
+ struct domains_ctrl_ops *ops)
+{
+ return 0;
+}
+
+static inline void domainsctrl_unregister(struct device *dev)
+{
+ /* Empty */
+}
+
+#endif
+
+#endif /* _DOMAINSCTRL_H_ */