Add code allowing to control the power domain of some i.MX8 socs. Signed-off-by: Adrien Grassein <adrien.grassein@xxxxxxxxx> --- MAINTAINERS | 2 + drivers/soc/imx/Kconfig | 7 + drivers/soc/imx/Makefile | 1 + drivers/soc/imx/imx8m_pm_domains.c | 233 +++++++++++++++++++++++++++++ include/soc/imx/imx_sip.h | 12 ++ 5 files changed, 255 insertions(+) create mode 100644 drivers/soc/imx/imx8m_pm_domains.c create mode 100644 include/soc/imx/imx_sip.h diff --git a/MAINTAINERS b/MAINTAINERS index 97536afca0e1..06e1568d003b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13014,9 +13014,11 @@ NXP i.MX 8M(M|N|P) POWER DOMAIN DRIVER M: Adrien Grassein <adrien.grassein@xxxxxxxxx> S: Maintained F: Documentation/devicetree/bindings/power/fsl,imx-power-domain.yaml +F: drivers/soc/imx/imx8m_pm_domains.c F: include/dt-bindings/power/imx8mm-power.h F: include/dt-bindings/power/imx8mn-power.h F: include/dt-bindings/power/imx8mp-power.h +F: include/soc/imx/imx_sip.h NXP i.MX 8MQ DCSS DRIVER M: Laurentiu Palcu <laurentiu.palcu@xxxxxxxxxxx> diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig index 05812f8ae734..02d93346fd4b 100644 --- a/drivers/soc/imx/Kconfig +++ b/drivers/soc/imx/Kconfig @@ -8,6 +8,13 @@ config IMX_GPCV2_PM_DOMAINS select PM_GENERIC_DOMAINS default y if SOC_IMX7D +config IMX8M_PM_DOMAINS + bool "i.MX8M PM domains" + depends on ARCH_MXC || (COMPILE_TEST && OF) + depends on PM + select PM_GENERIC_DOMAINS + default y if SOC_IMX8M + config SOC_IMX8M bool "i.MX8M SoC family support" depends on ARCH_MXC || COMPILE_TEST diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile index 078dc918f4f3..7387239aecec 100644 --- a/drivers/soc/imx/Makefile +++ b/drivers/soc/imx/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_ARCH_MXC) += soc-imx.o endif obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o +obj-$(CONFIG_IMX8M_PM_DOMAINS) += imx8m_pm_domains.o obj-$(CONFIG_SOC_IMX8M) += soc-imx8m.o diff --git a/drivers/soc/imx/imx8m_pm_domains.c b/drivers/soc/imx/imx8m_pm_domains.c new file mode 100644 index 000000000000..3a039572b734 --- /dev/null +++ b/drivers/soc/imx/imx8m_pm_domains.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/arm-smccc.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/regulator/consumer.h> + +#include <soc/imx/imx_sip.h> + +#define MAX_CLK_NUM 6 +#define to_imx8m_pm_domain(_genpd) container_of(_genpd, struct imx8m_pm_domain, pd) + +struct imx8m_pm_domain { + struct device *dev; + struct generic_pm_domain pd; + u32 domain_index; + struct clk *clk[MAX_CLK_NUM]; + unsigned int num_clks; + struct regulator *reg; +}; + +enum imx8m_pm_domain_state { + PD_STATE_OFF, + PD_STATE_ON, +}; + +static DEFINE_MUTEX(gpc_pd_mutex); + +static int imx8m_pd_power_on(struct generic_pm_domain *genpd) +{ + struct imx8m_pm_domain *domain = to_imx8m_pm_domain(genpd); + struct arm_smccc_res res; + int index, ret = 0; + + /* power on the external supply */ + if (!IS_ERR(domain->reg)) { + ret = regulator_enable(domain->reg); + if (ret) { + dev_warn(domain->dev, "failed to power up the reg%d\n", ret); + return ret; + } + } + + /* enable the necessary clks needed by the power domain */ + if (domain->num_clks) { + for (index = 0; index < domain->num_clks; index++) + clk_prepare_enable(domain->clk[index]); + } + + mutex_lock(&gpc_pd_mutex); + arm_smccc_smc(IMX_SIP_GPC, IMX_SIP_CONFIG_GPC_PM_DOMAIN, domain->domain_index, + PD_STATE_ON, 0, 0, 0, 0, &res); + mutex_unlock(&gpc_pd_mutex); + + return 0; +} + +static int imx8m_pd_power_off(struct generic_pm_domain *genpd) +{ + struct imx8m_pm_domain *domain = to_imx8m_pm_domain(genpd); + struct arm_smccc_res res; + int index, ret = 0; + + mutex_lock(&gpc_pd_mutex); + arm_smccc_smc(IMX_SIP_GPC, IMX_SIP_CONFIG_GPC_PM_DOMAIN, domain->domain_index, + PD_STATE_OFF, 0, 0, 0, 0, &res); + mutex_unlock(&gpc_pd_mutex); + + /* power off the external supply */ + if (!IS_ERR(domain->reg)) { + ret = regulator_disable(domain->reg); + if (ret) { + dev_warn(domain->dev, "failed to power off the reg%d\n", ret); + return ret; + } + } + + /* disable clks when power domain is off */ + if (domain->num_clks) { + for (index = 0; index < domain->num_clks; index++) + clk_disable_unprepare(domain->clk[index]); + } + + return ret; +}; + +static int imx8m_pd_get_clocks(struct imx8m_pm_domain *domain) +{ + int i, ret; + + for (i = 0; ; i++) { + struct clk *clk = of_clk_get(domain->dev->of_node, i); + + if (IS_ERR(clk)) + break; + if (i >= MAX_CLK_NUM) { + dev_err(domain->dev, "more than %d clocks\n", + MAX_CLK_NUM); + ret = -EINVAL; + goto clk_err; + } + domain->clk[i] = clk; + } + domain->num_clks = i; + + return 0; + +clk_err: + while (i--) + clk_put(domain->clk[i]); + + return ret; +} + +static void imx8m_pd_put_clocks(struct imx8m_pm_domain *domain) +{ + int i; + + for (i = domain->num_clks - 1; i >= 0; i--) + clk_put(domain->clk[i]); +} + +static const struct of_device_id imx8m_pm_domain_ids[] = { + {.compatible = "fsl,imx8mm-pm-domain"}, + {.compatible = "fsl,imx8mn-pm-domain"}, + {.compatible = "fsl,imx8mn-pm-domain"}, + {}, +}; + +static int imx8m_pm_domain_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct imx8m_pm_domain *domain; + struct of_phandle_args parent, child; + int ret; + + domain = devm_kzalloc(dev, sizeof(*domain), GFP_KERNEL); + if (!domain) + return -ENOMEM; + + child.np = np; + domain->dev = dev; + + ret = of_property_read_string(np, "domain-name", &domain->pd.name); + if (ret) { + dev_err(dev, "failed to get the domain name\n"); + return -EINVAL; + } + + ret = of_property_read_u32(np, "domain-index", &domain->domain_index); + if (ret) { + dev_err(dev, "failed to get the domain index\n"); + return -EINVAL; + } + + domain->reg = devm_regulator_get_optional(dev, "power"); + if (IS_ERR(domain->reg)) { + if (PTR_ERR(domain->reg) != -ENODEV) { + if (PTR_ERR(domain->reg) != -EPROBE_DEFER) + dev_err(dev, "failed to get domain's regulator\n"); + return PTR_ERR(domain->reg); + } + } + + ret = imx8m_pd_get_clocks(domain); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get domain's clocks\n"); + return ret; + } + + domain->pd.power_off = imx8m_pd_power_off; + domain->pd.power_on = imx8m_pd_power_on; + if (of_property_read_bool(np, "fsl,active-wakeup")) + domain->pd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; + if (of_property_read_bool(np, "fsl,rpm-always-on")) + domain->pd.flags |= GENPD_FLAG_RPM_ALWAYS_ON; + + pm_genpd_init(&domain->pd, NULL, !(domain->pd.flags & GENPD_FLAG_RPM_ALWAYS_ON)); + + ret = of_genpd_add_provider_simple(np, &domain->pd); + if (ret) { + dev_err(dev, "failed to add the domain provider\n"); + pm_genpd_remove(&domain->pd); + imx8m_pd_put_clocks(domain); + return ret; + } + + /* add it as subdomain if necessary */ + if (!of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", + 0, &parent)) { + ret = of_genpd_add_subdomain(&parent, &child); + of_node_put(parent.np); + + if (ret < 0) { + dev_dbg(dev, "failed to add the subdomain: %s: %d", + domain->pd.name, ret); + of_genpd_del_provider(np); + pm_genpd_remove(&domain->pd); + imx8m_pd_put_clocks(domain); + return driver_deferred_probe_check_state(dev); + } + } + + return 0; +} + +static struct platform_driver imx8m_pm_domain_driver = { + .driver = { + .name = "imx8m_pm_domain", + .owner = THIS_MODULE, + .of_match_table = imx8m_pm_domain_ids, + }, + .probe = imx8m_pm_domain_probe, +}; +module_platform_driver(imx8m_pm_domain_driver); + +MODULE_AUTHOR("NXP"); +/* Just for mainstreaming */ +MODULE_AUTHOR("Adrien Grassein <adrien.grassein@xxxxxxxxx>"); +MODULE_DESCRIPTION("NXP i.MX8M power domain driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/soc/imx/imx_sip.h b/include/soc/imx/imx_sip.h new file mode 100644 index 000000000000..6b96b33c870e --- /dev/null +++ b/include/soc/imx/imx_sip.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2019 NXP + */ + +#ifndef __IMX_SIP_H__ +#define __IMX_SIP_H__ + +#define IMX_SIP_GPC 0xC2000000 +#define IMX_SIP_CONFIG_GPC_PM_DOMAIN 0x03 + +#endif /* __IMX_SIP_H__ */ -- 2.25.1