soc: imx: gpcv2: handle reset clocks
Some power domains handled by the GPCv2 driver need to enable the clocks for devies inside the domain, so that the reset propagation and proper power-up sequencing happens. Handle them in the same way as on GPCv1. Signed-off-by: Lucas Stach <l.stach@pengutronix.de> Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Shawn Guo <shawnguo@kernel.org>
This commit is contained in:
parent
b798d5a1b0
commit
ae1d2add26
|
@ -32,6 +32,9 @@ Required properties:
|
||||||
Optional properties:
|
Optional properties:
|
||||||
|
|
||||||
- power-supply: Power supply used to power the domain
|
- power-supply: Power supply used to power the domain
|
||||||
|
- clocks: a number of phandles to clocks that need to be enabled during
|
||||||
|
domain power-up sequencing to ensure reset propagation into devices
|
||||||
|
located inside this power domain
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
* Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
|
* Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_domain.h>
|
#include <linux/pm_domain.h>
|
||||||
|
@ -98,10 +99,14 @@
|
||||||
|
|
||||||
#define GPC_PGC_CTRL_PCR BIT(0)
|
#define GPC_PGC_CTRL_PCR BIT(0)
|
||||||
|
|
||||||
|
#define GPC_CLK_MAX 6
|
||||||
|
|
||||||
struct imx_pgc_domain {
|
struct imx_pgc_domain {
|
||||||
struct generic_pm_domain genpd;
|
struct generic_pm_domain genpd;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct regulator *regulator;
|
struct regulator *regulator;
|
||||||
|
struct clk *clk[GPC_CLK_MAX];
|
||||||
|
int num_clks;
|
||||||
|
|
||||||
unsigned int pgc;
|
unsigned int pgc;
|
||||||
|
|
||||||
|
@ -132,7 +137,7 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd,
|
||||||
const bool enable_power_control = !on;
|
const bool enable_power_control = !on;
|
||||||
const bool has_regulator = !IS_ERR(domain->regulator);
|
const bool has_regulator = !IS_ERR(domain->regulator);
|
||||||
unsigned long deadline;
|
unsigned long deadline;
|
||||||
int ret = 0;
|
int i, ret = 0;
|
||||||
|
|
||||||
regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING,
|
regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING,
|
||||||
domain->bits.map, domain->bits.map);
|
domain->bits.map, domain->bits.map);
|
||||||
|
@ -145,6 +150,10 @@ 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++)
|
||||||
|
clk_prepare_enable(domain->clk[i]);
|
||||||
|
|
||||||
if (enable_power_control)
|
if (enable_power_control)
|
||||||
regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
|
regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
|
||||||
GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
|
GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
|
||||||
|
@ -190,6 +199,10 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd,
|
||||||
regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
|
regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
|
||||||
GPC_PGC_CTRL_PCR, 0);
|
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) {
|
if (has_regulator && !on) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -440,6 +453,41 @@ static const struct imx_pgc_domain_data imx8m_pgc_domain_data = {
|
||||||
.reg_access_table = &imx8m_access_table,
|
.reg_access_table = &imx8m_access_table,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int imx_pgc_get_clocks(struct imx_pgc_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 >= GPC_CLK_MAX) {
|
||||||
|
dev_err(domain->dev, "more than %d clocks\n",
|
||||||
|
GPC_CLK_MAX);
|
||||||
|
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 imx_pgc_put_clocks(struct imx_pgc_domain *domain)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = domain->num_clks - 1; i >= 0; i--)
|
||||||
|
clk_put(domain->clk[i]);
|
||||||
|
}
|
||||||
|
|
||||||
static int imx_pgc_domain_probe(struct platform_device *pdev)
|
static int imx_pgc_domain_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct imx_pgc_domain *domain = pdev->dev.platform_data;
|
struct imx_pgc_domain *domain = pdev->dev.platform_data;
|
||||||
|
@ -459,9 +507,17 @@ static int imx_pgc_domain_probe(struct platform_device *pdev)
|
||||||
domain->voltage, domain->voltage);
|
domain->voltage, domain->voltage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = imx_pgc_get_clocks(domain);
|
||||||
|
if (ret) {
|
||||||
|
if (ret != -EPROBE_DEFER)
|
||||||
|
dev_err(domain->dev, "Failed to get domain's clocks\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ret = pm_genpd_init(&domain->genpd, NULL, true);
|
ret = pm_genpd_init(&domain->genpd, NULL, true);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(domain->dev, "Failed to init power domain\n");
|
dev_err(domain->dev, "Failed to init power domain\n");
|
||||||
|
imx_pgc_put_clocks(domain);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,6 +526,7 @@ static int imx_pgc_domain_probe(struct platform_device *pdev)
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(domain->dev, "Failed to add genpd provider\n");
|
dev_err(domain->dev, "Failed to add genpd provider\n");
|
||||||
pm_genpd_remove(&domain->genpd);
|
pm_genpd_remove(&domain->genpd);
|
||||||
|
imx_pgc_put_clocks(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -481,6 +538,7 @@ static int imx_pgc_domain_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
of_genpd_del_provider(domain->dev->of_node);
|
of_genpd_del_provider(domain->dev->of_node);
|
||||||
pm_genpd_remove(&domain->genpd);
|
pm_genpd_remove(&domain->genpd);
|
||||||
|
imx_pgc_put_clocks(domain);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue