Received: by 2002:a25:1985:0:0:0:0:0 with SMTP id 127csp2999131ybz; Mon, 27 Apr 2020 08:15:03 -0700 (PDT) X-Google-Smtp-Source: APiQypLhKfSSCJBy3g7oLvYSrBkgGsdsDjUrL3k1whWK9xyukd6ONFUvg01AZGmZeVfDznrL3ySJ X-Received: by 2002:a17:906:d0d2:: with SMTP id bq18mr19410928ejb.62.1588000503168; Mon, 27 Apr 2020 08:15:03 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1588000503; cv=none; d=google.com; s=arc-20160816; b=X1+vVFNax3qa+jE9tZhT8er9zO8tio/S1JUUJCQrDqbfnW+jR4LdY+Rm08LLy2L5DM 5xst7X/rAelciE20wOXVecb96oSQcLj8aFl0Uiqy1yDbCCUHdrgzNFUngvT9rGnG8Khm KQKFmMr3HBwt6kRuGZSoVG8eMnjxBZ5wIvorQETxV/MSfBHSfzexOnAvL+IHVN+xBgOo QdMWzHVDHxPsg4rA4xCCSu3+l3jRG1oy0f71Cu3IaWYLScw7vI8MHmYSkS83TjFpEykp ywWTRJwN0X2ixwNDIpwpZc/GpPr4uL8+h+j9Oyy1VQjYNZjqwu8CeJ87f72pg+5dvXYS 2jSA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:date:cc:to:from:subject :message-id; bh=8t5H7I87Arf9Emo+b/gdSSrsf5bQMsnbGMFQ7LUKmhQ=; b=jc+M7WTKnPBgB/WfBxN+4AgVwkurtVhwcM+CU0Z99e/fybRj7MG4PTp754fa073f65 LX5wU9bxn9nfrd+7VOQFGMLebNrhUmhy1F6QXcxniBEC0l8W+v+2pobFHAVZAIGg4XKO Q2LPVvIBI/foA3k8d+yP7xVVmlnw3vmIfsFLtI3rXDcrneFR+TN/BpvZMQQkxUbeT7dY 7AGZMYCPuGi3nxSQyZ6qohUst/gbX0GDYXzgerqycW9TvASmTn7MDLDQTpakMciMJK4V eSf8tBfFjWpluZhtQ8IW7lEPVXoUtPjNv0xgJvoDl+FAIMrVOG0hR0WS+3/4pux+UT+J zhmQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id z5si9207369edp.102.2020.04.27.08.14.36; Mon, 27 Apr 2020 08:15:03 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728157AbgD0PKy (ORCPT + 99 others); Mon, 27 Apr 2020 11:10:54 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40702 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1727807AbgD0PKx (ORCPT ); Mon, 27 Apr 2020 11:10:53 -0400 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A981FC0610D5 for ; Mon, 27 Apr 2020 08:10:52 -0700 (PDT) Received: from gallifrey.ext.pengutronix.de ([2001:67c:670:201:5054:ff:fe8d:eefb] helo=localhost) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1jT5PB-0008Di-4p; Mon, 27 Apr 2020 17:10:45 +0200 Message-ID: <0d301ed303faea4895d30b682133ec5c9d44bd8b.camel@pengutronix.de> Subject: Re: [PATCH] soc: imx: Add power domain driver support for i.mx8m family From: Lucas Stach To: Abel Vesa , Jacky Bai , Shawn Guo , Sascha Hauer , Liam Girdwood , Mark Brown Cc: Dong Aisheng , NXP Linux Team , linux-arm-kernel@lists.infradead.org, Linux Kernel Mailing List Date: Mon, 27 Apr 2020 17:10:44 +0200 In-Reply-To: <1587999532-30006-1-git-send-email-abel.vesa@nxp.com> References: <1587999532-30006-1-git-send-email-abel.vesa@nxp.com> Content-Type: text/plain; charset="UTF-8" User-Agent: Evolution 3.36.1 (3.36.1-1.fc32) MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-SA-Exim-Connect-IP: 2001:67c:670:201:5054:ff:fe8d:eefb X-SA-Exim-Mail-From: l.stach@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Am Montag, den 27.04.2020, 17:58 +0300 schrieb Abel Vesa: > From: Jacky Bai > > The i.MX8M family is a set of NXP product focus on delivering > the latest and greatest video and audio experience combining > state-of-the-art media-specific features with high-performance > processing while optimized for lowest power consumption. > > i.MX8MQ, i.MX8MM, i.MX8MN, even the furture i.MX8MP are all > belong to this family. A GPC module is used to manage all the > PU power domain on/off. But the situation is that the number of > power domains & the power up sequence has significate difference > on those SoCs. Even on the same SoC. The power up sequence still > has big difference. It makes us hard to reuse the GPCv2 driver to > cover the whole i.MX8M family. Each time a new SoC is supported in > the mainline kernel, we need to modify the GPCv2 driver to support > it. We need to add or modify hundred lines of code in worst case. > It is a bad practice for the driver maintainability. > > This driver add a more generic power domain driver that the actual > power on/off is done by TF-A code. the abstraction give us the > possibility that using one driver to cover the whole i.MX8M family > in kernel side. > Again: what does this driver bring to the table, other than moving a fraction of the power domain functionality into the firmware? The discussions on the last submissions of this driver already established that we can't move all functionality for the power domains into the firmware, as controlling regulators is probably not easy to do from this context. Also the TF-A side implementation of this driver is "interesting" IMHO, it does stuff like accessing the clock controller registers without any locking or other means of mutual exclusion with the Linux kernel clock controller driver. Why can't we just extend the existing GPCv2 driver with support for the other i.MX8M family members? Regards, Lucas > Signed-off-by: Jacky Bai > Signed-off-by: Abel Vesa > --- > drivers/soc/imx/Kconfig | 6 + > drivers/soc/imx/Makefile | 1 + > drivers/soc/imx/imx8m_pm_domains.c | 224 +++++++++++++++++++++++++++++++++++++ > include/soc/imx/imx_sip.h | 12 ++ > 4 files changed, 243 insertions(+) > create mode 100644 drivers/soc/imx/imx8m_pm_domains.c > create mode 100644 include/soc/imx/imx_sip.h > > diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig > index d515d2c..7837199 100644 > --- a/drivers/soc/imx/Kconfig > +++ b/drivers/soc/imx/Kconfig > @@ -27,4 +27,10 @@ config SOC_IMX8M > support, it will provide the SoC info like SoC family, > ID and revision etc. > > +config IMX8M_PM_DOMAINS > + bool "i.MX8M PM domains" > + depends on ARCH_MXC || (COMPILE_TEST && OF) > + depends on PM > + select PM_GENERIC_DOMAINS > + > endmenu > diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile > index 103e2c9..a22e24b 100644 > --- a/drivers/soc/imx/Makefile > +++ b/drivers/soc/imx/Makefile > @@ -3,3 +3,4 @@ obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o > obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o > obj-$(CONFIG_SOC_IMX8M) += soc-imx8m.o > obj-$(CONFIG_IMX_SCU_SOC) += soc-imx-scu.o > +obj-$(CONFIG_IMX8M_PM_DOMAINS) += imx8m_pm_domains.o > diff --git a/drivers/soc/imx/imx8m_pm_domains.c b/drivers/soc/imx/imx8m_pm_domains.c > new file mode 100644 > index 00000000..ce06a05 > --- /dev/null > +++ b/drivers/soc/imx/imx8m_pm_domains.c > @@ -0,0 +1,224 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright 2019 NXP. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +#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,imx8m-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; > + > + pm_genpd_init(&domain->pd, NULL, true); > + > + 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, "parent-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"); > +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 00000000..6b96b33 > --- /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__ */