[PATCH 2/2] soc: imx: add SCU power domains driver
Dong Aisheng
aisheng.dong at nxp.com
Fri Apr 27 11:51:03 PDT 2018
Some i.MX SoCs contain a system controller that is responsible for
controlling the state of the IPs that are present. Communication
between the host processor running an OS and the system controller
happens through a SCU protocol. This patch adds SCU protocol based
power domains drivers.
Cc: Shawn Guo <shawnguo at kernel.org>
Cc: Sascha Hauer <kernel at pengutronix.de>
Cc: Fabio Estevam <fabio.estevam at nxp.com>
Cc: "Rafael J. Wysocki" <rjw at rjwysocki.net>
Cc: Kevin Hilman <khilman at kernel.org>
Cc: Ulf Hansson <ulf.hansson at linaro.org>
Cc: linux-pm at vger.kernel.org
Signed-off-by: Dong Aisheng <aisheng.dong at nxp.com>
---
drivers/soc/imx/Kconfig | 4 ++
drivers/soc/imx/Makefile | 1 +
drivers/soc/imx/sc-pd.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 156 insertions(+)
create mode 100644 drivers/soc/imx/sc-pd.c
diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig
index 4cad3c0..8d56e9b 100644
--- a/drivers/soc/imx/Kconfig
+++ b/drivers/soc/imx/Kconfig
@@ -14,4 +14,8 @@ config HAVE_IMX_SCU
bool
depends on HAVE_IMX_MU
+config HAVE_IMX_SC_PD
+ bool
+ depends on HAVE_IMX_SCU
+
endmenu
diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile
index f931984..40ad2a5 100644
--- a/drivers/soc/imx/Makefile
+++ b/drivers/soc/imx/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
obj-$(CONFIG_IMX7_PM_DOMAINS) += gpcv2.o
obj-$(CONFIG_HAVE_IMX_MU) += imx_mu.o
obj-$(CONFIG_HAVE_IMX_SCU) += sc/
+obj-$(CONFIG_HAVE_IMX_SC_PD) += sc-pd.o
diff --git a/drivers/soc/imx/sc-pd.c b/drivers/soc/imx/sc-pd.c
new file mode 100644
index 0000000..ecbfd2c
--- /dev/null
+++ b/drivers/soc/imx/sc-pd.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2017~2018 NXP
+ * Dong Aisheng <aisheng.dong at nxp.com>
+ *
+ * Implementation of the SCU based Power Domains
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+
+#include <soc/imx/sc/sci.h>
+
+struct imx_sc_pm_domain {
+ struct generic_pm_domain pd;
+ sc_rsrc_t rsrc_id;
+};
+
+static sc_ipc_t pm_ipc_handle;
+
+static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on)
+{
+ struct imx_sc_pm_domain *pd;
+ sc_err_t sci_err;
+
+ pd = container_of(domain, struct imx_sc_pm_domain, pd);
+
+ sci_err = sc_pm_set_resource_power_mode(pm_ipc_handle, pd->rsrc_id,
+ power_on ? SC_PM_PW_MODE_ON :
+ SC_PM_PW_MODE_LP);
+ if (sci_err) {
+ pr_err("imx_sc_pm: failed to power %s resource %d ret %d\n",
+ power_on ? "up" : "off", pd->rsrc_id, sci_err);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static int imx_sc_pd_power_on(struct generic_pm_domain *domain)
+{
+ return imx_sc_pd_power(domain, true);
+}
+
+static int imx_sc_pd_power_off(struct generic_pm_domain *domain)
+{
+ return imx_sc_pd_power(domain, false);
+}
+
+static struct __init generic_pm_domain * imx_sc_pm_add_one_domain(struct device_node *np,
+ struct generic_pm_domain *genpd_parent)
+{
+ struct imx_sc_pm_domain *imx_sc_pd;
+ sc_rsrc_t rsrc_id;
+ int ret;
+
+ imx_sc_pd = kzalloc(sizeof(*imx_sc_pd), GFP_KERNEL);
+ if (!imx_sc_pd)
+ return ERR_PTR(-ENOMEM);
+
+ if (!of_property_read_u32(np, "reg", &rsrc_id)) {
+ if (rsrc_id > SC_R_LAST) {
+ pr_warn("%pOF: invalid rsrc id %d found", np, rsrc_id);
+ ret = -EINVAL;
+ goto err;
+ }
+ imx_sc_pd->rsrc_id = rsrc_id;
+ } else {
+ imx_sc_pd->rsrc_id = SC_R_LAST;
+ }
+
+ if (imx_sc_pd->rsrc_id != SC_R_LAST) {
+ imx_sc_pd->pd.power_off = imx_sc_pd_power_off;
+ imx_sc_pd->pd.power_on = imx_sc_pd_power_on;
+ }
+
+ imx_sc_pd->pd.name = np->name;
+
+ ret = pm_genpd_init(&imx_sc_pd->pd, NULL, true);
+ if (ret < 0)
+ goto err;
+
+ if (genpd_parent) {
+ ret = pm_genpd_add_subdomain(genpd_parent, &imx_sc_pd->pd);
+ if (ret)
+ goto err;
+ }
+
+ ret = of_genpd_add_provider_simple(np, &imx_sc_pd->pd);
+ if (!ret)
+ return &imx_sc_pd->pd;
+
+ pm_genpd_remove_subdomain(genpd_parent, &imx_sc_pd->pd);
+err:
+ pr_warn("imx_sc_pm: failed to add PM domain %pOF: %d\n", np, ret);
+ kfree(imx_sc_pd);
+ return ERR_PTR(ret);
+}
+
+static void __init imx_sc_pm_add_subdomains(struct device_node *parent,
+ struct generic_pm_domain *genpd_parent)
+{
+ struct generic_pm_domain *pd;
+ struct device_node *np;
+
+ for_each_child_of_node(parent, np) {
+ pd = imx_sc_pm_add_one_domain(np, genpd_parent);
+ if (!IS_ERR(pd))
+ imx_sc_pm_add_subdomains(np, pd);
+ }
+}
+
+static const struct of_device_id imx_sc_pm_domain_of_match[] __initconst = {
+ {
+ .compatible = "nxp,imx8qxp-pd",
+ },
+ { },
+};
+
+static int __init imx_sc_init_pm_domains(void)
+{
+ struct generic_pm_domain *pd;
+ struct device_node *np;
+ sc_err_t sci_err;
+
+ if (!of_machine_is_compatible("fsl,imx8qxp"))
+ return 0;
+
+ sci_err = sc_ipc_get_handle(&pm_ipc_handle);
+ if (sci_err != SC_ERR_NONE) {
+ pr_err("imx_sc_pd: can't get sc ipc handle\n");
+ return -ENODEV;
+ }
+
+ for_each_matching_node(np, imx_sc_pm_domain_of_match) {
+ pd = imx_sc_pm_add_one_domain(np, NULL);
+ if (!IS_ERR(pd))
+ imx_sc_pm_add_subdomains(np, pd);
+ }
+
+ return 0;
+}
+early_initcall(imx_sc_init_pm_domains);
--
2.7.4
More information about the linux-arm-kernel
mailing list