Re: [PATCH v3 6/9] clk: renesas: rzg2l: Extend power domain support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 10 Apr 2024 at 14:28, Claudiu <claudiu.beznea@xxxxxxxxx> wrote:
>
> From: Claudiu Beznea <claudiu.beznea.uj@xxxxxxxxxxxxxx>
>
> 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 to
> clock driver MSTOP register offset and associated bits in this register
> as a bitmask and the clock driver will apply this bitmask to proper
> MSTOP register.
>
> Apart from MSTOP support, RZ/G3S can save more power by powering down the
> individual IPs (after MSTOP has been set) if proper bits in
> CPG_PWRDN_IP{1,2} registers are set.
>
> The MSTOP and IP power down support were 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.
>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@xxxxxxxxx>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@xxxxxxxxxxxxxx>

This looks good to me!

However, as said elsewhere, I would like to encourage you to move the
genpd provider specific parts into drivers/pmdomain/*. Not saying it
should be a separate driver, but looking at the amount of genpd
specific code, I am a little worried about maintenance when moving
forward, if we keep this in drivers/clk/*. Nevertheless, feel free to
add:

Reviewed-by: Ulf Hansson <ulf.hansson@xxxxxxxxxx>

Kind regards
Uffe


> ---
>
> Changes in v3:
> - collected tags
>
> Changes in v2:
> - called pm_genpd_init() with proper value for is_off argument
> - fixed typos
> - used flexible array for struct rzg2l_cpg_pm_domains::domains member
> - moved genpd member of struct rzg2l_cpg_pd at the beginning of struct
> - didn't initialize the parent variable in rzg2l_cpg_add_pm_domains()
>   as it is already initialized in the for block from
>   rzg2l_cpg_add_pm_domains() and that initialization should be enough
> - dropped RZG2L_PD_F_PARENT flag
> - used datasheet naming for all MSTOP registers
> - added all MSTOP registers to rzg2l-cpg.h
> - reworked the code that initializes the register offset and bits for domains
> - dropped MSTOP*(), PWRDN*() macros and introduced struct rzg2l_cpg_reg_conf
>   and DEF_REG_CONF() for domain description
> - constified the 1st argument of rzg2l_cpg_pm_domain_xlate()
> - used dev instead of priv->dev where possible
> - dropped RZG2L_PD_F_PARENT
> - added RZG2L_PD_F_NONE for better description of domains in platform
>   specific clock drivers
>
>  drivers/clk/renesas/rzg2l-cpg.c | 213 +++++++++++++++++++++++++++++---
>  drivers/clk/renesas/rzg2l-cpg.h |  77 ++++++++++++
>  2 files changed, 276 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c
> index 3d2daa4ba2a4..b36700f4a9f5 100644
> --- a/drivers/clk/renesas/rzg2l-cpg.c
> +++ b/drivers/clk/renesas/rzg2l-cpg.c
> @@ -139,7 +139,6 @@ struct rzg2l_pll5_mux_dsi_div_param {
>   * @num_resets: Number of Module Resets in info->resets[]
>   * @last_dt_core_clk: ID of the last Core Clock exported to DT
>   * @info: Pointer to platform data
> - * @genpd: PM domain
>   * @mux_dsi_div_params: pll5 mux and dsi div parameters
>   */
>  struct rzg2l_cpg_priv {
> @@ -156,8 +155,6 @@ struct rzg2l_cpg_priv {
>
>         const struct rzg2l_cpg_info *info;
>
> -       struct generic_pm_domain genpd;
> -
>         struct rzg2l_pll5_mux_dsi_div_param mux_dsi_div_params;
>  };
>
> @@ -1559,9 +1556,34 @@ static bool rzg2l_cpg_is_pm_clk(struct rzg2l_cpg_priv *priv,
>         return true;
>  }
>
> +/**
> + * struct rzg2l_cpg_pm_domains - RZ/G2L PM domains data structure
> + * @onecell_data: cell data
> + * @domains: generic PM domains
> + */
> +struct rzg2l_cpg_pm_domains {
> +       struct genpd_onecell_data onecell_data;
> +       struct generic_pm_domain *domains[];
> +};
> +
> +/**
> + * struct rzg2l_cpg_pd - RZ/G2L power domain data structure
> + * @genpd: generic PM domain
> + * @priv: pointer to CPG private data structure
> + * @conf: CPG PM domain configuration info
> + * @id: RZ/G2L power domain ID
> + */
> +struct rzg2l_cpg_pd {
> +       struct generic_pm_domain genpd;
> +       struct rzg2l_cpg_priv *priv;
> +       struct rzg2l_cpg_pm_domain_conf conf;
> +       u16 id;
> +};
> +
>  static int rzg2l_cpg_attach_dev(struct generic_pm_domain *domain, struct device *dev)
>  {
> -       struct rzg2l_cpg_priv *priv = container_of(domain, struct rzg2l_cpg_priv, genpd);
> +       struct rzg2l_cpg_pd *pd = container_of(domain, struct rzg2l_cpg_pd, genpd);
> +       struct rzg2l_cpg_priv *priv = pd->priv;
>         struct device_node *np = dev->of_node;
>         struct of_phandle_args clkspec;
>         bool once = true;
> @@ -1617,31 +1639,194 @@ static void rzg2l_cpg_detach_dev(struct generic_pm_domain *unused, struct device
>  }
>
>  static void rzg2l_cpg_genpd_remove(void *data)
> +{
> +       struct genpd_onecell_data *celldata = data;
> +
> +       for (unsigned int i = 0; i < celldata->num_domains; i++)
> +               pm_genpd_remove(celldata->domains[i]);
> +}
> +
> +static void rzg2l_cpg_genpd_remove_simple(void *data)
>  {
>         pm_genpd_remove(data);
>  }
>
> +static int rzg2l_cpg_power_on(struct generic_pm_domain *domain)
> +{
> +       struct rzg2l_cpg_pd *pd = container_of(domain, struct rzg2l_cpg_pd, genpd);
> +       struct rzg2l_cpg_reg_conf mstop = pd->conf.mstop;
> +       struct rzg2l_cpg_reg_conf pwrdn = pd->conf.pwrdn;
> +       struct rzg2l_cpg_priv *priv = pd->priv;
> +
> +       /* Set PWRDN. */
> +       if (pwrdn.mask)
> +               writel(pwrdn.mask << 16, priv->base + pwrdn.off);
> +
> +       /* Set MSTOP. */
> +       if (mstop.mask)
> +               writel(mstop.mask << 16, priv->base + mstop.off);
> +
> +       return 0;
> +}
> +
> +static int rzg2l_cpg_power_off(struct generic_pm_domain *domain)
> +{
> +       struct rzg2l_cpg_pd *pd = container_of(domain, struct rzg2l_cpg_pd, genpd);
> +       struct rzg2l_cpg_reg_conf mstop = pd->conf.mstop;
> +       struct rzg2l_cpg_reg_conf pwrdn = pd->conf.pwrdn;
> +       struct rzg2l_cpg_priv *priv = pd->priv;
> +
> +       /* Set MSTOP. */
> +       if (mstop.mask)
> +               writel(mstop.mask | (mstop.mask << 16), priv->base + mstop.off);
> +
> +       /* Set PWRDN. */
> +       if (pwrdn.mask)
> +               writel(pwrdn.mask | (pwrdn.mask << 16), priv->base + pwrdn.off);
> +
> +       return 0;
> +}
> +
> +static int __init rzg2l_cpg_pd_setup(struct rzg2l_cpg_pd *pd, bool always_on)
> +{
> +       struct dev_power_governor *governor;
> +
> +       pd->genpd.flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP;
> +       pd->genpd.attach_dev = rzg2l_cpg_attach_dev;
> +       pd->genpd.detach_dev = rzg2l_cpg_detach_dev;
> +       if (always_on) {
> +               pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
> +               governor = &pm_domain_always_on_gov;
> +       } else {
> +               pd->genpd.power_on = rzg2l_cpg_power_on;
> +               pd->genpd.power_off = rzg2l_cpg_power_off;
> +               governor = &simple_qos_governor;
> +       }
> +
> +       return pm_genpd_init(&pd->genpd, governor, !always_on);
> +}
> +
>  static int __init rzg2l_cpg_add_clk_domain(struct rzg2l_cpg_priv *priv)
>  {
>         struct device *dev = priv->dev;
>         struct device_node *np = dev->of_node;
> -       struct generic_pm_domain *genpd = &priv->genpd;
> +       struct rzg2l_cpg_pd *pd;
>         int ret;
>
> -       genpd->name = np->name;
> -       genpd->flags = GENPD_FLAG_PM_CLK | GENPD_FLAG_ALWAYS_ON |
> -                      GENPD_FLAG_ACTIVE_WAKEUP;
> -       genpd->attach_dev = rzg2l_cpg_attach_dev;
> -       genpd->detach_dev = rzg2l_cpg_detach_dev;
> -       ret = pm_genpd_init(genpd, &pm_domain_always_on_gov, false);
> +       pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
> +       if (!pd)
> +               return -ENOMEM;
> +
> +       pd->genpd.name = np->name;
> +       pd->priv = priv;
> +       ret = rzg2l_cpg_pd_setup(pd, true);
>         if (ret)
>                 return ret;
>
> -       ret = devm_add_action_or_reset(dev, rzg2l_cpg_genpd_remove, genpd);
> +       ret = devm_add_action_or_reset(dev, rzg2l_cpg_genpd_remove_simple, &pd->genpd);
>         if (ret)
>                 return ret;
>
> -       return of_genpd_add_provider_simple(np, genpd);
> +       return of_genpd_add_provider_simple(np, &pd->genpd);
> +}
> +
> +static struct generic_pm_domain *
> +rzg2l_cpg_pm_domain_xlate(const struct of_phandle_args *spec, void *data)
> +{
> +       struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
> +       struct genpd_onecell_data *genpd = data;
> +
> +       if (spec->args_count != 1)
> +               return ERR_PTR(-EINVAL);
> +
> +       for (unsigned int i = 0; i < genpd->num_domains; i++) {
> +               struct rzg2l_cpg_pd *pd = container_of(genpd->domains[i], struct rzg2l_cpg_pd,
> +                                                      genpd);
> +
> +               if (pd->id == spec->args[0]) {
> +                       domain = &pd->genpd;
> +                       break;
> +               }
> +       }
> +
> +       return domain;
> +}
> +
> +static int __init rzg2l_cpg_add_pm_domains(struct rzg2l_cpg_priv *priv)
> +{
> +       const struct rzg2l_cpg_info *info = priv->info;
> +       struct device *dev = priv->dev;
> +       struct device_node *np = dev->of_node;
> +       struct rzg2l_cpg_pm_domains *domains;
> +       struct generic_pm_domain *parent;
> +       u32 ncells;
> +       int ret;
> +
> +       ret = of_property_read_u32(np, "#power-domain-cells", &ncells);
> +       if (ret)
> +               return ret;
> +
> +       /* For backward compatibility. */
> +       if (!ncells)
> +               return rzg2l_cpg_add_clk_domain(priv);
> +
> +       domains = devm_kzalloc(dev, struct_size(domains, domains, info->num_pm_domains),
> +                              GFP_KERNEL);
> +       if (!domains)
> +               return -ENOMEM;
> +
> +       domains->onecell_data.domains = domains->domains;
> +       domains->onecell_data.num_domains = info->num_pm_domains;
> +       domains->onecell_data.xlate = rzg2l_cpg_pm_domain_xlate;
> +
> +       ret = devm_add_action_or_reset(dev, rzg2l_cpg_genpd_remove, &domains->onecell_data);
> +       if (ret)
> +               return ret;
> +
> +       for (unsigned int i = 0; i < info->num_pm_domains; i++) {
> +               bool always_on = !!(info->pm_domains[i].flags & RZG2L_PD_F_ALWAYS_ON);
> +               struct rzg2l_cpg_pd *pd;
> +
> +               pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
> +               if (!pd)
> +                       return -ENOMEM;
> +
> +               pd->genpd.name = info->pm_domains[i].name;
> +               pd->conf = info->pm_domains[i].conf;
> +               pd->id = info->pm_domains[i].id;
> +               pd->priv = priv;
> +
> +               ret = rzg2l_cpg_pd_setup(pd, always_on);
> +               if (ret)
> +                       return ret;
> +
> +               if (always_on) {
> +                       ret = rzg2l_cpg_power_on(&pd->genpd);
> +                       if (ret)
> +                               return ret;
> +               }
> +
> +               domains->domains[i] = &pd->genpd;
> +               /* Parent should be on the very first entry of info->pm_domains[]. */
> +               if (!i) {
> +                       parent = &pd->genpd;
> +                       continue;
> +               }
> +
> +               ret = pm_genpd_add_subdomain(parent, &pd->genpd);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       ret = of_genpd_add_provider_onecell(np, &domains->onecell_data);
> +       if (ret)
> +               return ret;
> +
> +       /* Prepare for power down the BUSes in power down mode. */
> +       if (info->pm_domain_pwrdn_mstop)
> +               writel(CPG_PWRDN_MSTOP_ENABLE, priv->base + CPG_PWRDN_MSTOP);
> +
> +       return 0;
>  }
>
>  static int __init rzg2l_cpg_probe(struct platform_device *pdev)
> @@ -1697,7 +1882,7 @@ static int __init rzg2l_cpg_probe(struct platform_device *pdev)
>         if (error)
>                 return error;
>
> -       error = rzg2l_cpg_add_clk_domain(priv);
> +       error = rzg2l_cpg_add_pm_domains(priv);
>         if (error)
>                 return error;
>
> diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h
> index 6e38c8fc888c..d9a7357c4873 100644
> --- a/drivers/clk/renesas/rzg2l-cpg.h
> +++ b/drivers/clk/renesas/rzg2l-cpg.h
> @@ -27,6 +27,21 @@
>  #define CPG_PL6_ETH_SSEL       (0x418)
>  #define CPG_PL5_SDIV           (0x420)
>  #define CPG_RST_MON            (0x680)
> +#define CPG_BUS_ACPU_MSTOP     (0xB60)
> +#define CPG_BUS_MCPU1_MSTOP    (0xB64)
> +#define CPG_BUS_MCPU2_MSTOP    (0xB68)
> +#define CPG_BUS_PERI_COM_MSTOP (0xB6C)
> +#define CPG_BUS_PERI_CPU_MSTOP (0xB70)
> +#define CPG_BUS_PERI_DDR_MSTOP (0xB74)
> +#define CPG_BUS_REG0_MSTOP     (0xB7C)
> +#define CPG_BUS_REG1_MSTOP     (0xB80)
> +#define CPG_BUS_TZCDDR_MSTOP   (0xB84)
> +#define CPG_MHU_MSTOP          (0xB88)
> +#define CPG_BUS_MCPU3_MSTOP    (0xB90)
> +#define CPG_BUS_PERI_CPU2_MSTOP        (0xB94)
> +#define CPG_PWRDN_IP1          (0xBB0)
> +#define CPG_PWRDN_IP2          (0xBB4)
> +#define CPG_PWRDN_MSTOP                (0xBC0)
>  #define CPG_OTHERFUNC1_REG     (0xBE8)
>
>  #define CPG_SIPLL5_STBY_RESETB         BIT(0)
> @@ -70,6 +85,8 @@
>
>  #define EXTAL_FREQ_IN_MEGA_HZ  (24)
>
> +#define CPG_PWRDN_MSTOP_ENABLE         (BIT(16) | BIT(0))
> +
>  /**
>   * Definitions of CPG Core Clocks
>   *
> @@ -234,6 +251,58 @@ struct rzg2l_reset {
>  #define DEF_RST(_id, _off, _bit)       \
>         DEF_RST_MON(_id, _off, _bit, -1)
>
> +/**
> + * struct rzg2l_cpg_reg_conf - RZ/G2L register configuration data structure
> + * @off: register offset
> + * @mask: register mask
> + */
> +struct rzg2l_cpg_reg_conf {
> +       u16 off;
> +       u16 mask;
> +};
> +
> +#define DEF_REG_CONF(_off, _mask) ((struct rzg2l_cpg_reg_conf) { .off = (_off), .mask = (_mask) })
> +
> +/**
> + * struct rzg2l_cpg_pm_domain_conf - PM domain configuration data structure
> + * @mstop: MSTOP register configuration
> + * @pwrdn: PWRDN register configuration
> + */
> +struct rzg2l_cpg_pm_domain_conf {
> +       struct rzg2l_cpg_reg_conf mstop;
> +       struct rzg2l_cpg_reg_conf pwrdn;
> +};
> +
> +/**
> + * struct rzg2l_cpg_pm_domain_init_data - PM domain init data
> + * @name: PM domain name
> + * @conf: PM domain configuration
> + * @flags: RZG2L PM domain flags (see RZG2L_PD_F_*)
> + * @id: PM domain ID (similar to the ones defined in
> + *      include/dt-bindings/clock/<soc-id>-cpg.h)
> + */
> +struct rzg2l_cpg_pm_domain_init_data {
> +       const char * const name;
> +       struct rzg2l_cpg_pm_domain_conf conf;
> +       u32 flags;
> +       u16 id;
> +};
> +
> +#define DEF_PD(_name, _id, _mstop_conf, _pwrdn_conf, _flags) \
> +       { \
> +               .name = (_name), \
> +               .id = (_id), \
> +               .conf = { \
> +                       .mstop = (_mstop_conf), \
> +                       .pwrdn = (_pwrdn_conf), \
> +               }, \
> +               .flags = (_flags), \
> +       }
> +
> +/* Power domain flags. */
> +#define RZG2L_PD_F_ALWAYS_ON   BIT(0)
> +#define RZG2L_PD_F_NONE                (0)
> +
>  /**
>   * struct rzg2l_cpg_info - SoC-specific CPG Description
>   *
> @@ -252,6 +321,9 @@ struct rzg2l_reset {
>   * @crit_mod_clks: Array with Module Clock IDs of critical clocks that
>   *                 should not be disabled without a knowledgeable driver
>   * @num_crit_mod_clks: Number of entries in crit_mod_clks[]
> + * @pm_domains: PM domains init data array
> + * @num_pm_domains: Number of PM domains
> + * @pm_domain_pwrdn_mstop: Specifies if PWRDN MSTOP is supported
>   * @has_clk_mon_regs: Flag indicating whether the SoC has CLK_MON registers
>   */
>  struct rzg2l_cpg_info {
> @@ -278,6 +350,11 @@ struct rzg2l_cpg_info {
>         const unsigned int *crit_mod_clks;
>         unsigned int num_crit_mod_clks;
>
> +       /* Power domain. */
> +       const struct rzg2l_cpg_pm_domain_init_data *pm_domains;
> +       unsigned int num_pm_domains;
> +       bool pm_domain_pwrdn_mstop;
> +
>         bool has_clk_mon_regs;
>  };
>
> --
> 2.39.2
>
>




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux