Re: [PATCH v2 4/6] pmdomain: renesas: Add R-Car X5H MDLC driver

From: Geert Uytterhoeven

Date: Wed Jul 15 2026 - 05:14:57 EST


Hi Ulf,

On Tue, 14 Jul 2026 at 14:04, Ulf Hansson <ulf.hansson@xxxxxxxxxxxxxxxx> wrote:
> On Wed, Jul 8, 2026 at 12:15 PM Geert Uytterhoeven
> <geert+renesas@xxxxxxxxx> wrote:
> >
> > Add a minimal Module Controller driver for the R-Car X5H (R8A78000) SoC.
> > For now this just supports the always-on power domains, and dummy clocks
> > and resets for the serial console (which is enabled by the boot loader).
> >
> > Signed-off-by: Geert Uytterhoeven <geert+renesas@xxxxxxxxx>

> > --- /dev/null
> > +++ b/drivers/pmdomain/renesas/r8a78000-mdlc.c

> > +static struct generic_pm_domain *r8a78000_genpd_xlate(
> > + const struct of_phandle_args *spec, void *data)
> > +{
> > + struct r8a78000_mdlc_priv *priv = container_of(data,
> > + struct r8a78000_mdlc_priv, genpd_data);
> > + struct device *dev = priv->dev;
> > + u32 id;
> > +
> > + if (spec->args_count != 2)
> > + return ERR_PTR(-EINVAL);
> > +
> > + id = spec->args[0];
> > +
> > + if (id >= R8A78000_MDLC_PD_AON) {
> > + dev_dbg(dev,
> > + "Mapping HW power domain 0x%x to always-on domain\n",
> > + id);
> > + return r8a78000_genpd_always_on;
>
> This looks odd, but perhaps it's just a temporary mapping that you
> intend to change later, no?

The hardware has multiple always-on domains, but on the software side,
using only one is sufficient. When needed, it can be changed later.
Other domains will be added later, in the else branch.

> > + }
> > +
> > + dev_err(dev, "Unknown power domain 0x%x\n", id);
> > + return ERR_PTR(-ENOENT);
> > +}

> > +static int r8a78000_genpd_always_on_singleton(struct device *dev)
> > +{
> > + struct generic_pm_domain *genpd;
> > + int ret;
> > +
> > + guard(mutex)(&r8a78000_mdlc_lock);
> > +
> > + if (r8a78000_genpd_always_on)
> > + return 0;
> > +
>
> I guess the mutex is used to protect the global
> r8a78000_genpd_always_on, but it looks like that isn't really needed
> based upon how things are being called during the probe.

Why would it not be needed? There are multiple instances of the MDLC
device, but only one instance of the always-on domain.

> > + genpd = kzalloc_obj(*genpd);
> > + if (!genpd)
> > + return -ENOMEM;
> > +
> > + genpd->name = "always-on";
> > + genpd->attach_dev = r8a78000_mdlc_attach_dev;
> > +
> > + ret = pm_genpd_init(genpd, &pm_domain_always_on_gov, false);
> > + if (ret) {
> > + kfree(genpd);
> > + return dev_err_probe(dev, ret,
> > + "Failed to create always-on domain\n");
> > + }
> > +
> > + r8a78000_genpd_always_on = genpd;
> > + return 0;
> > +}
> > +
> > +static int r8a78000_mdlc_probe(struct platform_device *pdev)
> > +{
> > + struct device *dev = &pdev->dev;
> > + struct device_node *np = dev->of_node;
> > + struct r8a78000_mdlc_priv *priv;
> > + const struct mdlc_info *info;
> > + struct resource *res;
> > + int ret;
> > +
> > + ret = r8a78000_genpd_always_on_singleton(dev);
> > + if (ret)
> > + return ret;
> > +
> > + info = of_device_get_match_data(dev);
> > + if (!info)
> > + return -ENODEV;
> > +
> > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > + if (!priv)
> > + return -ENOMEM;
> > +
> > + priv->dev = dev;
> > + priv->np = np;
> > +
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + if (!res)
> > + return -ENODEV;
> > +
> > + for (; info->base; info++) {
> > + if (info->base == res->start)
> > + break;
> > + }
> > +
> > + if (!info->base) {
> > + dev_dbg(dev, "Unsupported MDLC instance 0x%pa\n", &res->start);
> > + return -ENODEV;
> > + }
> > +
> > + priv->mod_map = info->mod_map;
> > +
> > + scoped_guard(mutex, &r8a78000_mdlc_lock) {
> > + hlist_add_head(&priv->link, &r8a78000_mdlc_list);
> > + }
> > +
> > + ret = devm_add_action_or_reset(dev, r8a78000_mdlc_unlink, priv);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "failed to add action\n");
> > +
> > + /* Note that no actual domains are registered, just need translation */
> > + priv->genpd_data.xlate = r8a78000_genpd_xlate;
> > + ret = of_genpd_add_provider_onecell(np, &priv->genpd_data);
> > + if (ret)
> > + return dev_err_probe(dev, ret,
> > + "Failed to register genpd provider\n");
> > +
> > + ret = devm_add_action_or_reset(dev, r8a78000_genpd_del_provider, np);
> > + if (ret)
> > + return dev_err_probe(dev, ret,
> > + "failed to add unregister action\n");
> > +
> > + priv->rcdev.ops = &r8a78000_mdlc_reset_ops;
> > + priv->rcdev.of_node = np;
> > + priv->rcdev.of_reset_n_cells = 1;
> > + priv->rcdev.of_xlate = r8a78000_mdlc_reset_xlate;
> > +
> > + ret = devm_reset_controller_register(dev, &priv->rcdev);
> > + if (ret)
> > + return dev_err_probe(dev, ret,
> > + "Failed to register reset controller\n");
> > +
>
> In some of the error paths above it looks like we end up leaking the
> data allocated for r8a78000_genpd_always_on. Perhaps clean up that
> somewhere here.

That is intentional: the always-on domain is shared by all MDLC
instances. If one MDLC fails to probe, the MDLC instances that did
succeed still need the always-on domain.

>
> > + return 0;
> > +}
>
> [...]
>
> In regards to the merge strategy, I intend to pick patch 2 (shared via
> the immutable dt branch) and $subject patch. Please let me know if
> there are any issues with that.

That is fine, thanks!

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@xxxxxxxxxxxxxx

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds