mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-16 13:34:30 +00:00
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:
|
||||
|
||||
- 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:
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
* Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
@ -98,10 +99,14 @@
|
||||
|
||||
#define GPC_PGC_CTRL_PCR BIT(0)
|
||||
|
||||
#define GPC_CLK_MAX 6
|
||||
|
||||
struct imx_pgc_domain {
|
||||
struct generic_pm_domain genpd;
|
||||
struct regmap *regmap;
|
||||
struct regulator *regulator;
|
||||
struct clk *clk[GPC_CLK_MAX];
|
||||
int num_clks;
|
||||
|
||||
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 has_regulator = !IS_ERR(domain->regulator);
|
||||
unsigned long deadline;
|
||||
int ret = 0;
|
||||
int i, ret = 0;
|
||||
|
||||
regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING,
|
||||
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)
|
||||
regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc),
|
||||
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),
|
||||
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;
|
||||
|
||||
@ -440,6 +453,41 @@ static const struct imx_pgc_domain_data imx8m_pgc_domain_data = {
|
||||
.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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
if (ret) {
|
||||
dev_err(domain->dev, "Failed to init power domain\n");
|
||||
imx_pgc_put_clocks(domain);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -470,6 +526,7 @@ static int imx_pgc_domain_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(domain->dev, "Failed to add genpd provider\n");
|
||||
pm_genpd_remove(&domain->genpd);
|
||||
imx_pgc_put_clocks(domain);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -481,6 +538,7 @@ static int imx_pgc_domain_remove(struct platform_device *pdev)
|
||||
|
||||
of_genpd_del_provider(domain->dev->of_node);
|
||||
pm_genpd_remove(&domain->genpd);
|
||||
imx_pgc_put_clocks(domain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user