linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Peng Fan (OSS)" <peng.fan@oss.nxp.com>
To: robh+dt@kernel.org, shawnguo@kernel.org, s.hauer@pengutronix.de
Cc: kernel@pengutronix.de, festevam@gmail.com, linux-imx@nxp.com,
	p.zabel@pengutronix.de, l.stach@pengutronix.de, krzk@kernel.org,
	agx@sigxcpu.org, marex@denx.de, andrew.smirnov@gmail.com,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, ping.bai@nxp.com,
	frieder.schrempf@kontron.de, aford173@gmail.com,
	abel.vesa@nxp.com
Subject: [PATCH 04/16] soc: imx: gpcv2: split power up and power down sequence control
Date: Thu, 29 Apr 2021 15:30:38 +0800	[thread overview]
Message-ID: <20210429073050.21039-5-peng.fan@oss.nxp.com> (raw)
In-Reply-To: <20210429073050.21039-1-peng.fan@oss.nxp.com>

From: Lucas Stach <l.stach@pengutronix.de>

The current mixed function to control both power up and power down
sequences is very hard to follow and already contains some sequence
errors like triggering the ADB400 handshake at the wrong time due to
this. Split the function into two, which results in slightly more
code, but is way easier to get right.

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 drivers/soc/imx/gpcv2.c | 141 ++++++++++++++++++++++++----------------
 1 file changed, 86 insertions(+), 55 deletions(-)

diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c
index 1d90c7802972..7356e48ebdad 100644
--- a/drivers/soc/imx/gpcv2.c
+++ b/drivers/soc/imx/gpcv2.c
@@ -125,20 +125,19 @@ struct imx_pgc_domain_data {
 	const struct regmap_access_table *reg_access_table;
 };
 
-static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd,
-				      bool on)
+static inline struct imx_pgc_domain *
+to_imx_pgc_domain(struct generic_pm_domain *genpd)
 {
-	struct imx_pgc_domain *domain = container_of(genpd,
-						      struct imx_pgc_domain,
-						      genpd);
-	unsigned int offset = on ?
-		GPC_PU_PGC_SW_PUP_REQ : GPC_PU_PGC_SW_PDN_REQ;
-	const bool enable_power_control = !on;
-	const bool has_regulator = !IS_ERR(domain->regulator);
-	int i, ret = 0;
-	u32 pxx_req;
-
-	if (has_regulator && on) {
+	return container_of(genpd, struct imx_pgc_domain, genpd);
+}
+
+static int imx_pgc_power_up(struct generic_pm_domain *genpd)
+{
+	struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd);
+	u32 reg_val;
+	int ret;
+
+	if (!IS_ERR(domain->regulator)) {
 		ret = regulator_enable(domain->regulator);
 		if (ret) {
 			dev_err(domain->dev, "failed to enable regulator\n");
@@ -147,72 +146,104 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd,
 	}
 
 	/* Enable reset clocks for all devices in the domain */
-	clk_bulk_prepare_enable(domain->num_clks, domain->clks);
+	ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
 	if (ret) {
 		dev_err(domain->dev, "failed to enable reset clocks\n");
+		goto out_regulator_disable;
+	}
+
+	/* request the domain to power up */
+	regmap_update_bits(domain->regmap, GPC_PU_PGC_SW_PUP_REQ,
+			   domain->bits.pxx, domain->bits.pxx);
+	/*
+	 * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
+	 * for PUP_REQ/PDN_REQ bit to be cleared
+	 */
+	ret = regmap_read_poll_timeout(domain->regmap, GPC_PU_PGC_SW_PUP_REQ,
+				       reg_val, !(reg_val & domain->bits.pxx),
+				       0, USEC_PER_MSEC);
+	if (ret) {
+		dev_err(domain->dev, "failed to command PGC\n");
+		goto out_clk_disable;
+	}
+
+	/* disable power control */
+	regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
+			   GPC_PGC_CTRL_PCR, 0);
+
+	/* request the ADB400 to power up */
+	if (domain->bits.hsk)
+		regmap_update_bits(domain->regmap, GPC_PU_PWRHSK,
+				   domain->bits.hsk, domain->bits.hsk);
+
+	/* Disable reset clocks for all devices in the domain */
+	clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
+
+	return 0;
+
+out_clk_disable:
+	clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
+out_regulator_disable:
+	if (!IS_ERR(domain->regulator))
 		regulator_disable(domain->regulator);
+
+	return ret;
+}
+
+static int imx_pgc_power_down(struct generic_pm_domain *genpd)
+{
+	struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd);
+	u32 reg_val;
+	int ret;
+
+	/* Enable reset clocks for all devices in the domain */
+	ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
+	if (ret) {
+		dev_err(domain->dev, "failed to enable reset clocks\n");
 		return ret;
 	}
 
-	if (enable_power_control)
-		regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
-				   GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
-
+	/* request the ADB400 to power down */
 	if (domain->bits.hsk)
 		regmap_update_bits(domain->regmap, GPC_PU_PWRHSK,
-				   domain->bits.hsk, on ? domain->bits.hsk : 0);
+				   domain->bits.hsk, 0);
 
-	regmap_update_bits(domain->regmap, offset,
-			   domain->bits.pxx, domain->bits.pxx);
+	/* enable power control */
+	regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
+			   GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
 
+	/* request the domain to power down */
+	regmap_update_bits(domain->regmap, GPC_PU_PGC_SW_PDN_REQ,
+			   domain->bits.pxx, domain->bits.pxx);
 	/*
 	 * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
 	 * for PUP_REQ/PDN_REQ bit to be cleared
 	 */
-	ret = regmap_read_poll_timeout(domain->regmap, offset, pxx_req,
-				       !(pxx_req & domain->bits.pxx),
+	ret = regmap_read_poll_timeout(domain->regmap, GPC_PU_PGC_SW_PDN_REQ,
+				       reg_val, !(reg_val & domain->bits.pxx),
 				       0, USEC_PER_MSEC);
 	if (ret) {
 		dev_err(domain->dev, "failed to command PGC\n");
-		/*
-		 * If we were in a process of enabling a
-		 * domain and failed we might as well disable
-		 * the regulator we just enabled. And if it
-		 * was the opposite situation and we failed to
-		 * power down -- keep the regulator on
-		 */
-		on = !on;
+		goto out_clk_disable;
 	}
 
-	if (enable_power_control)
-		regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
-				   GPC_PGC_CTRL_PCR, 0);
-
 	/* Disable reset clocks for all devices in the domain */
 	clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
 
-	if (has_regulator && !on) {
-		int err;
-
-		err = regulator_disable(domain->regulator);
-		if (err)
-			dev_err(domain->dev,
-				"failed to disable regulator: %d\n", err);
-		/* Preserve earlier error code */
-		ret = ret ?: err;
+	if (!IS_ERR(domain->regulator)) {
+		ret = regulator_disable(domain->regulator);
+		if (ret) {
+			dev_err(domain->dev, "failed to disable regulator\n");
+			return ret;
+		}
 	}
 
-	return ret;
-}
+	return 0;
 
-static int imx_gpc_pu_pgc_sw_pup_req(struct generic_pm_domain *genpd)
-{
-	return imx_gpc_pu_pgc_sw_pxx_req(genpd, true);
-}
+out_clk_disable:
+	clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
 
-static int imx_gpc_pu_pgc_sw_pdn_req(struct generic_pm_domain *genpd)
-{
-	return imx_gpc_pu_pgc_sw_pxx_req(genpd, false);
+	return ret;
 }
 
 static const struct imx_pgc_domain imx7_pgc_domains[] = {
@@ -590,8 +621,8 @@ static int imx_gpcv2_probe(struct platform_device *pdev)
 
 		domain = pd_pdev->dev.platform_data;
 		domain->regmap = regmap;
-		domain->genpd.power_on  = imx_gpc_pu_pgc_sw_pup_req;
-		domain->genpd.power_off = imx_gpc_pu_pgc_sw_pdn_req;
+		domain->genpd.power_on  = imx_pgc_power_up;
+		domain->genpd.power_off = imx_pgc_power_down;
 
 		pd_pdev->dev.parent = dev;
 		pd_pdev->dev.of_node = np;
-- 
2.30.0


  parent reply	other threads:[~2021-04-29  6:59 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-29  7:30 [PATCH 00/16] soc: imx: gpcv2: support i.MX8MM Peng Fan (OSS)
2021-04-29  7:30 ` [PATCH 01/16] soc: imx: gpcv2: move to more ideomatic error handling in probe Peng Fan (OSS)
2021-05-03 13:26   ` Frieder Schrempf
2021-04-29  7:30 ` [PATCH 02/16] soc: imx: gpcv2: move domain mapping to domain driver probe Peng Fan (OSS)
2021-05-03 13:27   ` Frieder Schrempf
2021-04-29  7:30 ` [PATCH 03/16] soc: imx: gpcv2: switch to clk_bulk_* API Peng Fan (OSS)
2021-05-03 13:37   ` Frieder Schrempf
2021-04-29  7:30 ` Peng Fan (OSS) [this message]
2021-05-03 14:13   ` [PATCH 04/16] soc: imx: gpcv2: split power up and power down sequence control Frieder Schrempf
2021-04-29  7:30 ` [PATCH 05/16] soc: imx: gpcv2: wait for ADB400 handshake Peng Fan (OSS)
2021-04-29  7:30 ` [PATCH 06/16] soc: imx: gpcv2: add runtime PM support for power-domains Peng Fan (OSS)
2021-05-03 14:18   ` Frieder Schrempf
2021-04-29  7:30 ` [PATCH 07/16] soc: imx: gpcv2: allow domains without power-sequence control Peng Fan (OSS)
2021-05-03 14:21   ` Frieder Schrempf
2021-04-29  7:30 ` [PATCH 08/16] dt-bindings: imx: gpcv2: add support for optional resets Peng Fan (OSS)
2021-05-03 14:27   ` Frieder Schrempf
2021-04-29  7:30 ` [PATCH 09/16] soc: " Peng Fan (OSS)
2021-05-03 14:30   ` Frieder Schrempf
2021-04-29  7:30 ` [PATCH 10/16] dt-bindings: power: add defines for i.MX8MM power domains Peng Fan (OSS)
2021-05-03 19:37   ` Rob Herring
2021-04-29  7:30 ` [PATCH 11/16] soc: imx: gpcv2: add support " Peng Fan (OSS)
2021-05-03 14:37   ` Frieder Schrempf
2021-04-29  7:30 ` [PATCH 12/16] soc: imx: gpcv2: Add support for missing i.MX8MM VPU/DISPMIX " Peng Fan (OSS)
2021-04-29  7:30 ` [PATCH 13/16] soc: imx: gpcv2: correct pm_runtime_get_sync usage Peng Fan (OSS)
2021-04-29 14:25   ` Lucas Stach
2021-04-30  4:43     ` Peng Fan (OSS)
2021-04-29  7:30 ` [PATCH 14/16] soc: imx: gpcv2: move reset assert after requesting domain power up Peng Fan (OSS)
2021-04-29 14:28   ` Lucas Stach
2021-04-30  4:51     ` Peng Fan (OSS)
2021-04-29  7:30 ` [PATCH 15/16] soc: imx: gpcv2: support reset defer probe Peng Fan (OSS)
2021-04-29  8:39   ` Ahmad Fatoum
2021-04-29 14:30   ` Lucas Stach
2021-04-30  4:51     ` Peng Fan (OSS)
2021-04-29  7:30 ` [PATCH 16/16] soc: imx: gpcv2: remove waiting handshake in power up Peng Fan (OSS)
2021-04-29 14:34   ` Lucas Stach
2021-04-30  4:52     ` Peng Fan (OSS)
2021-04-30  7:14     ` Peng Fan (OSS)
2021-04-30  8:47       ` Lucas Stach
2021-04-29 12:39 ` [PATCH 00/16] soc: imx: gpcv2: support i.MX8MM Adam Ford
2021-04-30  1:33   ` Peng Fan
2021-04-30  3:05     ` Adam Ford

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210429073050.21039-5-peng.fan@oss.nxp.com \
    --to=peng.fan@oss.nxp.com \
    --cc=abel.vesa@nxp.com \
    --cc=aford173@gmail.com \
    --cc=agx@sigxcpu.org \
    --cc=andrew.smirnov@gmail.com \
    --cc=devicetree@vger.kernel.org \
    --cc=festevam@gmail.com \
    --cc=frieder.schrempf@kontron.de \
    --cc=kernel@pengutronix.de \
    --cc=krzk@kernel.org \
    --cc=l.stach@pengutronix.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-imx@nxp.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=marex@denx.de \
    --cc=p.zabel@pengutronix.de \
    --cc=ping.bai@nxp.com \
    --cc=robh+dt@kernel.org \
    --cc=s.hauer@pengutronix.de \
    --cc=shawnguo@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).