Skip to content

Commit 0c8a59b

Browse files
claudiubezneageertu
authored andcommitted
clk: renesas: rzg2l: Extend power domain support
RZ/{G2L, V2L, G3S}-based CPG versions have support for saving extra power when clocks are disabled by activating module standby. This is done through MSTOP-specific registers that are part of CPG. Each individual module has one or more bits associated with one MSTOP register (see table "Registers for Module Standby Mode" from HW manuals). Hardware manual associates modules' clocks with one or more MSTOP bits. There are 3 mappings available (identified by researching RZ/G2L, RZ/G3S, RZ/V2L HW manuals): case 1: N clocks mapped to N MSTOP bits (with N={0, ..., X}) case 2: N clocks mapped to 1 MSTOP bit (with N={0, ..., X}) case 3: N clocks mapped to M MSTOP bits (with N={0, ..., X}, M={0, ..., Y}) Case 3 has been currently identified on RZ/V2L for the VCPL4 module. To cover all three cases, the individual platform drivers will provide the clock driver with MSTOP register offsets and associated bits in this register as a bitmask, and the clock driver will apply this bitmask to the proper MSTOP register. The MSTOP support was implemented through power domains. Platform-specific clock drivers will register an array of type struct rzg2l_cpg_pm_domain_init_data, which will be used to instantiate properly the power domains. Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Link: https://lore.kernel.org/r/20240422105355.1622177-7-claudiu.beznea.uj@bp.renesas.com Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
1 parent f33dca9 commit 0c8a59b

File tree

2 files changed

+252
-14
lines changed

2 files changed

+252
-14
lines changed

drivers/clk/renesas/rzg2l-cpg.c

Lines changed: 185 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ struct rzg2l_pll5_mux_dsi_div_param {
139139
* @num_resets: Number of Module Resets in info->resets[]
140140
* @last_dt_core_clk: ID of the last Core Clock exported to DT
141141
* @info: Pointer to platform data
142-
* @genpd: PM domain
143142
* @mux_dsi_div_params: pll5 mux and dsi div parameters
144143
*/
145144
struct rzg2l_cpg_priv {
@@ -156,8 +155,6 @@ struct rzg2l_cpg_priv {
156155

157156
const struct rzg2l_cpg_info *info;
158157

159-
struct generic_pm_domain genpd;
160-
161158
struct rzg2l_pll5_mux_dsi_div_param mux_dsi_div_params;
162159
};
163160

@@ -1559,9 +1556,34 @@ static bool rzg2l_cpg_is_pm_clk(struct rzg2l_cpg_priv *priv,
15591556
return true;
15601557
}
15611558

1559+
/**
1560+
* struct rzg2l_cpg_pm_domains - RZ/G2L PM domains data structure
1561+
* @onecell_data: cell data
1562+
* @domains: generic PM domains
1563+
*/
1564+
struct rzg2l_cpg_pm_domains {
1565+
struct genpd_onecell_data onecell_data;
1566+
struct generic_pm_domain *domains[];
1567+
};
1568+
1569+
/**
1570+
* struct rzg2l_cpg_pd - RZ/G2L power domain data structure
1571+
* @genpd: generic PM domain
1572+
* @priv: pointer to CPG private data structure
1573+
* @conf: CPG PM domain configuration info
1574+
* @id: RZ/G2L power domain ID
1575+
*/
1576+
struct rzg2l_cpg_pd {
1577+
struct generic_pm_domain genpd;
1578+
struct rzg2l_cpg_priv *priv;
1579+
struct rzg2l_cpg_pm_domain_conf conf;
1580+
u16 id;
1581+
};
1582+
15621583
static int rzg2l_cpg_attach_dev(struct generic_pm_domain *domain, struct device *dev)
15631584
{
1564-
struct rzg2l_cpg_priv *priv = container_of(domain, struct rzg2l_cpg_priv, genpd);
1585+
struct rzg2l_cpg_pd *pd = container_of(domain, struct rzg2l_cpg_pd, genpd);
1586+
struct rzg2l_cpg_priv *priv = pd->priv;
15651587
struct device_node *np = dev->of_node;
15661588
struct of_phandle_args clkspec;
15671589
bool once = true;
@@ -1617,31 +1639,180 @@ static void rzg2l_cpg_detach_dev(struct generic_pm_domain *unused, struct device
16171639
}
16181640

16191641
static void rzg2l_cpg_genpd_remove(void *data)
1642+
{
1643+
struct genpd_onecell_data *celldata = data;
1644+
1645+
for (unsigned int i = 0; i < celldata->num_domains; i++)
1646+
pm_genpd_remove(celldata->domains[i]);
1647+
}
1648+
1649+
static void rzg2l_cpg_genpd_remove_simple(void *data)
16201650
{
16211651
pm_genpd_remove(data);
16221652
}
16231653

1654+
static int rzg2l_cpg_power_on(struct generic_pm_domain *domain)
1655+
{
1656+
struct rzg2l_cpg_pd *pd = container_of(domain, struct rzg2l_cpg_pd, genpd);
1657+
struct rzg2l_cpg_reg_conf mstop = pd->conf.mstop;
1658+
struct rzg2l_cpg_priv *priv = pd->priv;
1659+
1660+
/* Set MSTOP. */
1661+
if (mstop.mask)
1662+
writel(mstop.mask << 16, priv->base + mstop.off);
1663+
1664+
return 0;
1665+
}
1666+
1667+
static int rzg2l_cpg_power_off(struct generic_pm_domain *domain)
1668+
{
1669+
struct rzg2l_cpg_pd *pd = container_of(domain, struct rzg2l_cpg_pd, genpd);
1670+
struct rzg2l_cpg_reg_conf mstop = pd->conf.mstop;
1671+
struct rzg2l_cpg_priv *priv = pd->priv;
1672+
1673+
/* Set MSTOP. */
1674+
if (mstop.mask)
1675+
writel(mstop.mask | (mstop.mask << 16), priv->base + mstop.off);
1676+
1677+
return 0;
1678+
}
1679+
1680+
static int __init rzg2l_cpg_pd_setup(struct rzg2l_cpg_pd *pd, bool always_on)
1681+
{
1682+
struct dev_power_governor *governor;
1683+
1684+
pd->genpd.flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP;
1685+
pd->genpd.attach_dev = rzg2l_cpg_attach_dev;
1686+
pd->genpd.detach_dev = rzg2l_cpg_detach_dev;
1687+
if (always_on) {
1688+
pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
1689+
governor = &pm_domain_always_on_gov;
1690+
} else {
1691+
pd->genpd.power_on = rzg2l_cpg_power_on;
1692+
pd->genpd.power_off = rzg2l_cpg_power_off;
1693+
governor = &simple_qos_governor;
1694+
}
1695+
1696+
return pm_genpd_init(&pd->genpd, governor, !always_on);
1697+
}
1698+
16241699
static int __init rzg2l_cpg_add_clk_domain(struct rzg2l_cpg_priv *priv)
16251700
{
16261701
struct device *dev = priv->dev;
16271702
struct device_node *np = dev->of_node;
1628-
struct generic_pm_domain *genpd = &priv->genpd;
1703+
struct rzg2l_cpg_pd *pd;
1704+
int ret;
1705+
1706+
pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
1707+
if (!pd)
1708+
return -ENOMEM;
1709+
1710+
pd->genpd.name = np->name;
1711+
pd->priv = priv;
1712+
ret = rzg2l_cpg_pd_setup(pd, true);
1713+
if (ret)
1714+
return ret;
1715+
1716+
ret = devm_add_action_or_reset(dev, rzg2l_cpg_genpd_remove_simple, &pd->genpd);
1717+
if (ret)
1718+
return ret;
1719+
1720+
return of_genpd_add_provider_simple(np, &pd->genpd);
1721+
}
1722+
1723+
static struct generic_pm_domain *
1724+
rzg2l_cpg_pm_domain_xlate(const struct of_phandle_args *spec, void *data)
1725+
{
1726+
struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
1727+
struct genpd_onecell_data *genpd = data;
1728+
1729+
if (spec->args_count != 1)
1730+
return ERR_PTR(-EINVAL);
1731+
1732+
for (unsigned int i = 0; i < genpd->num_domains; i++) {
1733+
struct rzg2l_cpg_pd *pd = container_of(genpd->domains[i], struct rzg2l_cpg_pd,
1734+
genpd);
1735+
1736+
if (pd->id == spec->args[0]) {
1737+
domain = &pd->genpd;
1738+
break;
1739+
}
1740+
}
1741+
1742+
return domain;
1743+
}
1744+
1745+
static int __init rzg2l_cpg_add_pm_domains(struct rzg2l_cpg_priv *priv)
1746+
{
1747+
const struct rzg2l_cpg_info *info = priv->info;
1748+
struct device *dev = priv->dev;
1749+
struct device_node *np = dev->of_node;
1750+
struct rzg2l_cpg_pm_domains *domains;
1751+
struct generic_pm_domain *parent;
1752+
u32 ncells;
16291753
int ret;
16301754

1631-
genpd->name = np->name;
1632-
genpd->flags = GENPD_FLAG_PM_CLK | GENPD_FLAG_ALWAYS_ON |
1633-
GENPD_FLAG_ACTIVE_WAKEUP;
1634-
genpd->attach_dev = rzg2l_cpg_attach_dev;
1635-
genpd->detach_dev = rzg2l_cpg_detach_dev;
1636-
ret = pm_genpd_init(genpd, &pm_domain_always_on_gov, false);
1755+
ret = of_property_read_u32(np, "#power-domain-cells", &ncells);
1756+
if (ret)
1757+
return ret;
1758+
1759+
/* For backward compatibility. */
1760+
if (!ncells)
1761+
return rzg2l_cpg_add_clk_domain(priv);
1762+
1763+
domains = devm_kzalloc(dev, struct_size(domains, domains, info->num_pm_domains),
1764+
GFP_KERNEL);
1765+
if (!domains)
1766+
return -ENOMEM;
1767+
1768+
domains->onecell_data.domains = domains->domains;
1769+
domains->onecell_data.num_domains = info->num_pm_domains;
1770+
domains->onecell_data.xlate = rzg2l_cpg_pm_domain_xlate;
1771+
1772+
ret = devm_add_action_or_reset(dev, rzg2l_cpg_genpd_remove, &domains->onecell_data);
16371773
if (ret)
16381774
return ret;
16391775

1640-
ret = devm_add_action_or_reset(dev, rzg2l_cpg_genpd_remove, genpd);
1776+
for (unsigned int i = 0; i < info->num_pm_domains; i++) {
1777+
bool always_on = !!(info->pm_domains[i].flags & RZG2L_PD_F_ALWAYS_ON);
1778+
struct rzg2l_cpg_pd *pd;
1779+
1780+
pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
1781+
if (!pd)
1782+
return -ENOMEM;
1783+
1784+
pd->genpd.name = info->pm_domains[i].name;
1785+
pd->conf = info->pm_domains[i].conf;
1786+
pd->id = info->pm_domains[i].id;
1787+
pd->priv = priv;
1788+
1789+
ret = rzg2l_cpg_pd_setup(pd, always_on);
1790+
if (ret)
1791+
return ret;
1792+
1793+
if (always_on) {
1794+
ret = rzg2l_cpg_power_on(&pd->genpd);
1795+
if (ret)
1796+
return ret;
1797+
}
1798+
1799+
domains->domains[i] = &pd->genpd;
1800+
/* Parent should be on the very first entry of info->pm_domains[]. */
1801+
if (!i) {
1802+
parent = &pd->genpd;
1803+
continue;
1804+
}
1805+
1806+
ret = pm_genpd_add_subdomain(parent, &pd->genpd);
1807+
if (ret)
1808+
return ret;
1809+
}
1810+
1811+
ret = of_genpd_add_provider_onecell(np, &domains->onecell_data);
16411812
if (ret)
16421813
return ret;
16431814

1644-
return of_genpd_add_provider_simple(np, genpd);
1815+
return 0;
16451816
}
16461817

16471818
static int __init rzg2l_cpg_probe(struct platform_device *pdev)
@@ -1697,7 +1868,7 @@ static int __init rzg2l_cpg_probe(struct platform_device *pdev)
16971868
if (error)
16981869
return error;
16991870

1700-
error = rzg2l_cpg_add_clk_domain(priv);
1871+
error = rzg2l_cpg_add_pm_domains(priv);
17011872
if (error)
17021873
return error;
17031874

drivers/clk/renesas/rzg2l-cpg.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@
2727
#define CPG_PL6_ETH_SSEL (0x418)
2828
#define CPG_PL5_SDIV (0x420)
2929
#define CPG_RST_MON (0x680)
30+
#define CPG_BUS_ACPU_MSTOP (0xB60)
31+
#define CPG_BUS_MCPU1_MSTOP (0xB64)
32+
#define CPG_BUS_MCPU2_MSTOP (0xB68)
33+
#define CPG_BUS_PERI_COM_MSTOP (0xB6C)
34+
#define CPG_BUS_PERI_CPU_MSTOP (0xB70)
35+
#define CPG_BUS_PERI_DDR_MSTOP (0xB74)
36+
#define CPG_BUS_REG0_MSTOP (0xB7C)
37+
#define CPG_BUS_REG1_MSTOP (0xB80)
38+
#define CPG_BUS_TZCDDR_MSTOP (0xB84)
39+
#define CPG_MHU_MSTOP (0xB88)
40+
#define CPG_BUS_MCPU3_MSTOP (0xB90)
41+
#define CPG_BUS_PERI_CPU2_MSTOP (0xB94)
3042
#define CPG_OTHERFUNC1_REG (0xBE8)
3143

3244
#define CPG_SIPLL5_STBY_RESETB BIT(0)
@@ -234,6 +246,55 @@ struct rzg2l_reset {
234246
#define DEF_RST(_id, _off, _bit) \
235247
DEF_RST_MON(_id, _off, _bit, -1)
236248

249+
/**
250+
* struct rzg2l_cpg_reg_conf - RZ/G2L register configuration data structure
251+
* @off: register offset
252+
* @mask: register mask
253+
*/
254+
struct rzg2l_cpg_reg_conf {
255+
u16 off;
256+
u16 mask;
257+
};
258+
259+
#define DEF_REG_CONF(_off, _mask) ((struct rzg2l_cpg_reg_conf) { .off = (_off), .mask = (_mask) })
260+
261+
/**
262+
* struct rzg2l_cpg_pm_domain_conf - PM domain configuration data structure
263+
* @mstop: MSTOP register configuration
264+
*/
265+
struct rzg2l_cpg_pm_domain_conf {
266+
struct rzg2l_cpg_reg_conf mstop;
267+
};
268+
269+
/**
270+
* struct rzg2l_cpg_pm_domain_init_data - PM domain init data
271+
* @name: PM domain name
272+
* @conf: PM domain configuration
273+
* @flags: RZG2L PM domain flags (see RZG2L_PD_F_*)
274+
* @id: PM domain ID (similar to the ones defined in
275+
* include/dt-bindings/clock/<soc-id>-cpg.h)
276+
*/
277+
struct rzg2l_cpg_pm_domain_init_data {
278+
const char * const name;
279+
struct rzg2l_cpg_pm_domain_conf conf;
280+
u32 flags;
281+
u16 id;
282+
};
283+
284+
#define DEF_PD(_name, _id, _mstop_conf, _flags) \
285+
{ \
286+
.name = (_name), \
287+
.id = (_id), \
288+
.conf = { \
289+
.mstop = (_mstop_conf), \
290+
}, \
291+
.flags = (_flags), \
292+
}
293+
294+
/* Power domain flags. */
295+
#define RZG2L_PD_F_ALWAYS_ON BIT(0)
296+
#define RZG2L_PD_F_NONE (0)
297+
237298
/**
238299
* struct rzg2l_cpg_info - SoC-specific CPG Description
239300
*
@@ -252,6 +313,8 @@ struct rzg2l_reset {
252313
* @crit_mod_clks: Array with Module Clock IDs of critical clocks that
253314
* should not be disabled without a knowledgeable driver
254315
* @num_crit_mod_clks: Number of entries in crit_mod_clks[]
316+
* @pm_domains: PM domains init data array
317+
* @num_pm_domains: Number of PM domains
255318
* @has_clk_mon_regs: Flag indicating whether the SoC has CLK_MON registers
256319
*/
257320
struct rzg2l_cpg_info {
@@ -278,6 +341,10 @@ struct rzg2l_cpg_info {
278341
const unsigned int *crit_mod_clks;
279342
unsigned int num_crit_mod_clks;
280343

344+
/* Power domain. */
345+
const struct rzg2l_cpg_pm_domain_init_data *pm_domains;
346+
unsigned int num_pm_domains;
347+
281348
bool has_clk_mon_regs;
282349
};
283350

0 commit comments

Comments
 (0)