diff options
-rw-r--r-- | drivers/soc/imx/gpcv2.c | 149 |
1 files changed, 95 insertions, 54 deletions
diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index dc420734b16c..f91063c9fb92 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -127,20 +127,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 i, ret; + + if (!IS_ERR(domain->regulator)) { ret = regulator_enable(domain->regulator); if (ret) { dev_err(domain->dev, "failed to enable regulator\n"); @@ -149,69 +148,111 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, } /* Enable reset clocks for all devices in the domain */ + for (i = 0; i < domain->num_clks; i++) { + ret = clk_prepare_enable(domain->clk[i]); + if (ret) { + dev_err(domain->dev, "failed to enable clock %d\n", i); + goto out_clk_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 */ for (i = 0; i < domain->num_clks; i++) - clk_prepare_enable(domain->clk[i]); + clk_disable_unprepare(domain->clk[i]); + + return 0; - if (enable_power_control) - regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc), - GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR); +out_clk_disable: + for (i--; i >= 0; i--) + clk_disable_unprepare(domain->clk[i]); + 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 i, ret; + /* Enable reset clocks for all devices in the domain */ + for (i = 0; i < domain->num_clks; i++) { + ret = clk_prepare_enable(domain->clk[i]); + if (ret) { + dev_err(domain->dev, "failed to enable clock %d\n", i); + goto out_clk_disable; + } + } + + /* 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 */ for (i = 0; i < domain->num_clks; i++) clk_disable_unprepare(domain->clk[i]); - 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: + for (i--; i >= 0; i--) + clk_disable_unprepare(domain->clk[i]); -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 0; } static const struct imx_pgc_domain imx7_pgc_domains[] = { @@ -631,8 +672,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; |