1*9f79f939SGeert Uytterhoeven // SPDX-License-Identifier: GPL-2.0 2*9f79f939SGeert Uytterhoeven /* 3*9f79f939SGeert Uytterhoeven * R-Car X5H Module Controller 4*9f79f939SGeert Uytterhoeven * 5*9f79f939SGeert Uytterhoeven * Copyright (C) 2026 Glider bv 6*9f79f939SGeert Uytterhoeven */ 7*9f79f939SGeert Uytterhoeven 8*9f79f939SGeert Uytterhoeven #include <linux/dev_printk.h> 9*9f79f939SGeert Uytterhoeven #include <linux/device-id/of.h> 10*9f79f939SGeert Uytterhoeven #include <linux/module.h> 11*9f79f939SGeert Uytterhoeven #include <linux/of.h> 12*9f79f939SGeert Uytterhoeven #include <linux/platform_device.h> 13*9f79f939SGeert Uytterhoeven #include <linux/pm_domain.h> 14*9f79f939SGeert Uytterhoeven #include <linux/reset-controller.h> 15*9f79f939SGeert Uytterhoeven #include <linux/slab.h> 16*9f79f939SGeert Uytterhoeven 17*9f79f939SGeert Uytterhoeven #include <dt-bindings/power/renesas,r8a78000-mdlc.h> 18*9f79f939SGeert Uytterhoeven 19*9f79f939SGeert Uytterhoeven struct mod_map { 20*9f79f939SGeert Uytterhoeven int hw_id; /* Hardware module ID or -1 sentinel */ 21*9f79f939SGeert Uytterhoeven }; 22*9f79f939SGeert Uytterhoeven 23*9f79f939SGeert Uytterhoeven struct mdlc_info { 24*9f79f939SGeert Uytterhoeven u32 base; 25*9f79f939SGeert Uytterhoeven const struct mod_map *mod_map; 26*9f79f939SGeert Uytterhoeven }; 27*9f79f939SGeert Uytterhoeven 28*9f79f939SGeert Uytterhoeven /** 29*9f79f939SGeert Uytterhoeven * struct r8a78000_mdlc_priv - Module Controller Private Data 30*9f79f939SGeert Uytterhoeven * 31*9f79f939SGeert Uytterhoeven * @link: Link into list of MDLC instances 32*9f79f939SGeert Uytterhoeven * @genpd_data: PM domain provider data 33*9f79f939SGeert Uytterhoeven * @rcdev: Reset controller entity 34*9f79f939SGeert Uytterhoeven * @dev: MDLC device 35*9f79f939SGeert Uytterhoeven * @np: Device node in DT representing the MDLC 36*9f79f939SGeert Uytterhoeven * @mod_map: Mapping from hardware module IDs 37*9f79f939SGeert Uytterhoeven */ 38*9f79f939SGeert Uytterhoeven struct r8a78000_mdlc_priv { 39*9f79f939SGeert Uytterhoeven struct hlist_node link; 40*9f79f939SGeert Uytterhoeven struct genpd_onecell_data genpd_data; 41*9f79f939SGeert Uytterhoeven struct reset_controller_dev rcdev; 42*9f79f939SGeert Uytterhoeven struct device *dev; 43*9f79f939SGeert Uytterhoeven struct device_node *np; 44*9f79f939SGeert Uytterhoeven const struct mod_map *mod_map; 45*9f79f939SGeert Uytterhoeven }; 46*9f79f939SGeert Uytterhoeven 47*9f79f939SGeert Uytterhoeven static struct generic_pm_domain *r8a78000_genpd_always_on; 48*9f79f939SGeert Uytterhoeven static HLIST_HEAD(r8a78000_mdlc_list); 49*9f79f939SGeert Uytterhoeven static DEFINE_MUTEX(r8a78000_mdlc_lock); /* protects the two above */ 50*9f79f939SGeert Uytterhoeven 51*9f79f939SGeert Uytterhoeven static struct generic_pm_domain *r8a78000_genpd_xlate( 52*9f79f939SGeert Uytterhoeven const struct of_phandle_args *spec, void *data) 53*9f79f939SGeert Uytterhoeven { 54*9f79f939SGeert Uytterhoeven struct r8a78000_mdlc_priv *priv = container_of(data, 55*9f79f939SGeert Uytterhoeven struct r8a78000_mdlc_priv, genpd_data); 56*9f79f939SGeert Uytterhoeven struct device *dev = priv->dev; 57*9f79f939SGeert Uytterhoeven u32 id; 58*9f79f939SGeert Uytterhoeven 59*9f79f939SGeert Uytterhoeven if (spec->args_count != 2) 60*9f79f939SGeert Uytterhoeven return ERR_PTR(-EINVAL); 61*9f79f939SGeert Uytterhoeven 62*9f79f939SGeert Uytterhoeven id = spec->args[0]; 63*9f79f939SGeert Uytterhoeven 64*9f79f939SGeert Uytterhoeven if (id >= R8A78000_MDLC_PD_AON) { 65*9f79f939SGeert Uytterhoeven dev_dbg(dev, 66*9f79f939SGeert Uytterhoeven "Mapping HW power domain 0x%x to always-on domain\n", 67*9f79f939SGeert Uytterhoeven id); 68*9f79f939SGeert Uytterhoeven return r8a78000_genpd_always_on; 69*9f79f939SGeert Uytterhoeven } 70*9f79f939SGeert Uytterhoeven 71*9f79f939SGeert Uytterhoeven /* For now only always-on domains are supported */ 72*9f79f939SGeert Uytterhoeven dev_err(dev, "Unknown power domain 0x%x\n", id); 73*9f79f939SGeert Uytterhoeven return ERR_PTR(-ENOENT); 74*9f79f939SGeert Uytterhoeven } 75*9f79f939SGeert Uytterhoeven 76*9f79f939SGeert Uytterhoeven #define rcdev_to_priv(_rcdev) \ 77*9f79f939SGeert Uytterhoeven container_of(_rcdev, struct r8a78000_mdlc_priv, rcdev) 78*9f79f939SGeert Uytterhoeven 79*9f79f939SGeert Uytterhoeven static const struct mod_map *mod_map_find(const struct mod_map *map, u32 id) 80*9f79f939SGeert Uytterhoeven { 81*9f79f939SGeert Uytterhoeven if (!map) 82*9f79f939SGeert Uytterhoeven return NULL; 83*9f79f939SGeert Uytterhoeven 84*9f79f939SGeert Uytterhoeven for (; map->hw_id >= 0; map++) { 85*9f79f939SGeert Uytterhoeven if (map->hw_id == id) 86*9f79f939SGeert Uytterhoeven return map; 87*9f79f939SGeert Uytterhoeven } 88*9f79f939SGeert Uytterhoeven 89*9f79f939SGeert Uytterhoeven return NULL; 90*9f79f939SGeert Uytterhoeven } 91*9f79f939SGeert Uytterhoeven 92*9f79f939SGeert Uytterhoeven static int r8a78000_mdlc_reset_xlate(struct reset_controller_dev *rcdev, 93*9f79f939SGeert Uytterhoeven const struct of_phandle_args *spec) 94*9f79f939SGeert Uytterhoeven { 95*9f79f939SGeert Uytterhoeven struct r8a78000_mdlc_priv *priv = rcdev_to_priv(rcdev); 96*9f79f939SGeert Uytterhoeven struct device *dev = priv->dev; 97*9f79f939SGeert Uytterhoeven const struct mod_map *map; 98*9f79f939SGeert Uytterhoeven u32 id; 99*9f79f939SGeert Uytterhoeven 100*9f79f939SGeert Uytterhoeven if (spec->args_count != 1) 101*9f79f939SGeert Uytterhoeven return -EINVAL; 102*9f79f939SGeert Uytterhoeven 103*9f79f939SGeert Uytterhoeven id = spec->args[0]; 104*9f79f939SGeert Uytterhoeven 105*9f79f939SGeert Uytterhoeven map = mod_map_find(priv->mod_map, id); 106*9f79f939SGeert Uytterhoeven if (!map) { 107*9f79f939SGeert Uytterhoeven dev_err(dev, "Unknown reset 0x%x\n", id); 108*9f79f939SGeert Uytterhoeven return -ENOENT; 109*9f79f939SGeert Uytterhoeven } 110*9f79f939SGeert Uytterhoeven 111*9f79f939SGeert Uytterhoeven dev_dbg(dev, "Ignoring HW reset 0x%x\n", id); 112*9f79f939SGeert Uytterhoeven return id; 113*9f79f939SGeert Uytterhoeven } 114*9f79f939SGeert Uytterhoeven 115*9f79f939SGeert Uytterhoeven #define DEFINE_MDLC_RESET_WRAPPER(op) \ 116*9f79f939SGeert Uytterhoeven static int r8a78000_mdlc_ ## op(struct reset_controller_dev *rcdev, \ 117*9f79f939SGeert Uytterhoeven unsigned long id) \ 118*9f79f939SGeert Uytterhoeven { \ 119*9f79f939SGeert Uytterhoeven struct r8a78000_mdlc_priv *priv = rcdev_to_priv(rcdev); \ 120*9f79f939SGeert Uytterhoeven \ 121*9f79f939SGeert Uytterhoeven dev_dbg(priv->dev, "%s: Ignoring\n", __func__); \ 122*9f79f939SGeert Uytterhoeven return 0; \ 123*9f79f939SGeert Uytterhoeven } 124*9f79f939SGeert Uytterhoeven 125*9f79f939SGeert Uytterhoeven DEFINE_MDLC_RESET_WRAPPER(reset) 126*9f79f939SGeert Uytterhoeven DEFINE_MDLC_RESET_WRAPPER(assert) 127*9f79f939SGeert Uytterhoeven DEFINE_MDLC_RESET_WRAPPER(deassert) 128*9f79f939SGeert Uytterhoeven DEFINE_MDLC_RESET_WRAPPER(status) 129*9f79f939SGeert Uytterhoeven 130*9f79f939SGeert Uytterhoeven static const struct reset_control_ops r8a78000_mdlc_reset_ops = { 131*9f79f939SGeert Uytterhoeven .reset = r8a78000_mdlc_reset, 132*9f79f939SGeert Uytterhoeven .assert = r8a78000_mdlc_assert, 133*9f79f939SGeert Uytterhoeven .deassert = r8a78000_mdlc_deassert, 134*9f79f939SGeert Uytterhoeven .status = r8a78000_mdlc_status, 135*9f79f939SGeert Uytterhoeven }; 136*9f79f939SGeert Uytterhoeven 137*9f79f939SGeert Uytterhoeven static int r8a78000_mdlc_attach_dev(struct generic_pm_domain *domain, 138*9f79f939SGeert Uytterhoeven struct device *dev) 139*9f79f939SGeert Uytterhoeven { 140*9f79f939SGeert Uytterhoeven struct device_node *np = dev->of_node; 141*9f79f939SGeert Uytterhoeven struct r8a78000_mdlc_priv *priv; 142*9f79f939SGeert Uytterhoeven struct of_phandle_args pd_spec; 143*9f79f939SGeert Uytterhoeven const struct mod_map *map; 144*9f79f939SGeert Uytterhoeven unsigned int id; 145*9f79f939SGeert Uytterhoeven int ret; 146*9f79f939SGeert Uytterhoeven 147*9f79f939SGeert Uytterhoeven ret = of_parse_phandle_with_args(np, "power-domains", 148*9f79f939SGeert Uytterhoeven "#power-domain-cells", 0, &pd_spec); 149*9f79f939SGeert Uytterhoeven if (ret < 0) 150*9f79f939SGeert Uytterhoeven return ret; 151*9f79f939SGeert Uytterhoeven 152*9f79f939SGeert Uytterhoeven scoped_guard(mutex, &r8a78000_mdlc_lock) { 153*9f79f939SGeert Uytterhoeven hlist_for_each_entry(priv, &r8a78000_mdlc_list, link) { 154*9f79f939SGeert Uytterhoeven if (priv->np == pd_spec.np) 155*9f79f939SGeert Uytterhoeven break; 156*9f79f939SGeert Uytterhoeven } 157*9f79f939SGeert Uytterhoeven } 158*9f79f939SGeert Uytterhoeven 159*9f79f939SGeert Uytterhoeven if (!priv) { 160*9f79f939SGeert Uytterhoeven dev_err(dev, "%s: MDLC %pOF not found\n", __func__, pd_spec.np); 161*9f79f939SGeert Uytterhoeven of_node_put(pd_spec.np); 162*9f79f939SGeert Uytterhoeven return -ENODEV; 163*9f79f939SGeert Uytterhoeven } 164*9f79f939SGeert Uytterhoeven 165*9f79f939SGeert Uytterhoeven id = pd_spec.args[1]; 166*9f79f939SGeert Uytterhoeven of_node_put(pd_spec.np); 167*9f79f939SGeert Uytterhoeven 168*9f79f939SGeert Uytterhoeven map = mod_map_find(priv->mod_map, id); 169*9f79f939SGeert Uytterhoeven if (!map) { 170*9f79f939SGeert Uytterhoeven dev_err(dev, "Unknown module 0x%x\n", id); 171*9f79f939SGeert Uytterhoeven return -ENOENT; 172*9f79f939SGeert Uytterhoeven } 173*9f79f939SGeert Uytterhoeven 174*9f79f939SGeert Uytterhoeven dev_dbg(dev, "Ignoring HW module 0x%x\n", id); 175*9f79f939SGeert Uytterhoeven return 0; 176*9f79f939SGeert Uytterhoeven } 177*9f79f939SGeert Uytterhoeven 178*9f79f939SGeert Uytterhoeven static void r8a78000_mdlc_unlink(void *data) 179*9f79f939SGeert Uytterhoeven { 180*9f79f939SGeert Uytterhoeven struct r8a78000_mdlc_priv *priv = data; 181*9f79f939SGeert Uytterhoeven 182*9f79f939SGeert Uytterhoeven scoped_guard(mutex, &r8a78000_mdlc_lock) { 183*9f79f939SGeert Uytterhoeven hlist_del(&priv->link); 184*9f79f939SGeert Uytterhoeven } 185*9f79f939SGeert Uytterhoeven } 186*9f79f939SGeert Uytterhoeven 187*9f79f939SGeert Uytterhoeven static void r8a78000_genpd_del_provider(void *data) 188*9f79f939SGeert Uytterhoeven { 189*9f79f939SGeert Uytterhoeven of_genpd_del_provider(data); 190*9f79f939SGeert Uytterhoeven } 191*9f79f939SGeert Uytterhoeven 192*9f79f939SGeert Uytterhoeven static int r8a78000_genpd_always_on_singleton(struct device *dev) 193*9f79f939SGeert Uytterhoeven { 194*9f79f939SGeert Uytterhoeven struct generic_pm_domain *genpd; 195*9f79f939SGeert Uytterhoeven int ret; 196*9f79f939SGeert Uytterhoeven 197*9f79f939SGeert Uytterhoeven guard(mutex)(&r8a78000_mdlc_lock); 198*9f79f939SGeert Uytterhoeven 199*9f79f939SGeert Uytterhoeven if (r8a78000_genpd_always_on) 200*9f79f939SGeert Uytterhoeven return 0; 201*9f79f939SGeert Uytterhoeven 202*9f79f939SGeert Uytterhoeven genpd = kzalloc_obj(*genpd); 203*9f79f939SGeert Uytterhoeven if (!genpd) 204*9f79f939SGeert Uytterhoeven return -ENOMEM; 205*9f79f939SGeert Uytterhoeven 206*9f79f939SGeert Uytterhoeven genpd->name = "always-on"; 207*9f79f939SGeert Uytterhoeven genpd->attach_dev = r8a78000_mdlc_attach_dev; 208*9f79f939SGeert Uytterhoeven 209*9f79f939SGeert Uytterhoeven ret = pm_genpd_init(genpd, &pm_domain_always_on_gov, false); 210*9f79f939SGeert Uytterhoeven if (ret) { 211*9f79f939SGeert Uytterhoeven kfree(genpd); 212*9f79f939SGeert Uytterhoeven return dev_err_probe(dev, ret, 213*9f79f939SGeert Uytterhoeven "Failed to create always-on domain\n"); 214*9f79f939SGeert Uytterhoeven } 215*9f79f939SGeert Uytterhoeven 216*9f79f939SGeert Uytterhoeven r8a78000_genpd_always_on = genpd; 217*9f79f939SGeert Uytterhoeven return 0; 218*9f79f939SGeert Uytterhoeven } 219*9f79f939SGeert Uytterhoeven 220*9f79f939SGeert Uytterhoeven static int r8a78000_mdlc_probe(struct platform_device *pdev) 221*9f79f939SGeert Uytterhoeven { 222*9f79f939SGeert Uytterhoeven struct device *dev = &pdev->dev; 223*9f79f939SGeert Uytterhoeven struct device_node *np = dev->of_node; 224*9f79f939SGeert Uytterhoeven struct r8a78000_mdlc_priv *priv; 225*9f79f939SGeert Uytterhoeven const struct mdlc_info *info; 226*9f79f939SGeert Uytterhoeven struct resource *res; 227*9f79f939SGeert Uytterhoeven int ret; 228*9f79f939SGeert Uytterhoeven 229*9f79f939SGeert Uytterhoeven ret = r8a78000_genpd_always_on_singleton(dev); 230*9f79f939SGeert Uytterhoeven if (ret) 231*9f79f939SGeert Uytterhoeven return ret; 232*9f79f939SGeert Uytterhoeven 233*9f79f939SGeert Uytterhoeven info = of_device_get_match_data(dev); 234*9f79f939SGeert Uytterhoeven if (!info) 235*9f79f939SGeert Uytterhoeven return -ENODEV; 236*9f79f939SGeert Uytterhoeven 237*9f79f939SGeert Uytterhoeven priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 238*9f79f939SGeert Uytterhoeven if (!priv) 239*9f79f939SGeert Uytterhoeven return -ENOMEM; 240*9f79f939SGeert Uytterhoeven 241*9f79f939SGeert Uytterhoeven priv->dev = dev; 242*9f79f939SGeert Uytterhoeven priv->np = np; 243*9f79f939SGeert Uytterhoeven 244*9f79f939SGeert Uytterhoeven res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 245*9f79f939SGeert Uytterhoeven if (!res) 246*9f79f939SGeert Uytterhoeven return -ENODEV; 247*9f79f939SGeert Uytterhoeven 248*9f79f939SGeert Uytterhoeven for (; info->base; info++) { 249*9f79f939SGeert Uytterhoeven if (info->base == res->start) 250*9f79f939SGeert Uytterhoeven break; 251*9f79f939SGeert Uytterhoeven } 252*9f79f939SGeert Uytterhoeven 253*9f79f939SGeert Uytterhoeven if (!info->base) { 254*9f79f939SGeert Uytterhoeven dev_dbg(dev, "Unsupported MDLC instance 0x%pa\n", &res->start); 255*9f79f939SGeert Uytterhoeven return -ENODEV; 256*9f79f939SGeert Uytterhoeven } 257*9f79f939SGeert Uytterhoeven 258*9f79f939SGeert Uytterhoeven priv->mod_map = info->mod_map; 259*9f79f939SGeert Uytterhoeven 260*9f79f939SGeert Uytterhoeven scoped_guard(mutex, &r8a78000_mdlc_lock) { 261*9f79f939SGeert Uytterhoeven hlist_add_head(&priv->link, &r8a78000_mdlc_list); 262*9f79f939SGeert Uytterhoeven } 263*9f79f939SGeert Uytterhoeven 264*9f79f939SGeert Uytterhoeven ret = devm_add_action_or_reset(dev, r8a78000_mdlc_unlink, priv); 265*9f79f939SGeert Uytterhoeven if (ret) 266*9f79f939SGeert Uytterhoeven return dev_err_probe(dev, ret, "failed to add action\n"); 267*9f79f939SGeert Uytterhoeven 268*9f79f939SGeert Uytterhoeven /* Note that no actual domains are registered, just need translation */ 269*9f79f939SGeert Uytterhoeven priv->genpd_data.xlate = r8a78000_genpd_xlate; 270*9f79f939SGeert Uytterhoeven ret = of_genpd_add_provider_onecell(np, &priv->genpd_data); 271*9f79f939SGeert Uytterhoeven if (ret) 272*9f79f939SGeert Uytterhoeven return dev_err_probe(dev, ret, 273*9f79f939SGeert Uytterhoeven "Failed to register genpd provider\n"); 274*9f79f939SGeert Uytterhoeven 275*9f79f939SGeert Uytterhoeven ret = devm_add_action_or_reset(dev, r8a78000_genpd_del_provider, np); 276*9f79f939SGeert Uytterhoeven if (ret) 277*9f79f939SGeert Uytterhoeven return dev_err_probe(dev, ret, 278*9f79f939SGeert Uytterhoeven "failed to add unregister action\n"); 279*9f79f939SGeert Uytterhoeven 280*9f79f939SGeert Uytterhoeven priv->rcdev.ops = &r8a78000_mdlc_reset_ops; 281*9f79f939SGeert Uytterhoeven priv->rcdev.of_node = np; 282*9f79f939SGeert Uytterhoeven priv->rcdev.of_reset_n_cells = 1; 283*9f79f939SGeert Uytterhoeven priv->rcdev.of_xlate = r8a78000_mdlc_reset_xlate; 284*9f79f939SGeert Uytterhoeven 285*9f79f939SGeert Uytterhoeven ret = devm_reset_controller_register(dev, &priv->rcdev); 286*9f79f939SGeert Uytterhoeven if (ret) 287*9f79f939SGeert Uytterhoeven return dev_err_probe(dev, ret, 288*9f79f939SGeert Uytterhoeven "Failed to register reset controller\n"); 289*9f79f939SGeert Uytterhoeven 290*9f79f939SGeert Uytterhoeven return 0; 291*9f79f939SGeert Uytterhoeven } 292*9f79f939SGeert Uytterhoeven 293*9f79f939SGeert Uytterhoeven static const struct mod_map r8a78000_mdlc_perw_mod_default[] = { 294*9f79f939SGeert Uytterhoeven { 0x54 }, /* HSCIF0 */ 295*9f79f939SGeert Uytterhoeven { -1 } 296*9f79f939SGeert Uytterhoeven }; 297*9f79f939SGeert Uytterhoeven 298*9f79f939SGeert Uytterhoeven static const struct mdlc_info r8a78000_mdlc_default[] = { 299*9f79f939SGeert Uytterhoeven { 300*9f79f939SGeert Uytterhoeven .base = 0xc05d0000 /* mdlc_perw */, 301*9f79f939SGeert Uytterhoeven .mod_map = r8a78000_mdlc_perw_mod_default, 302*9f79f939SGeert Uytterhoeven }, 303*9f79f939SGeert Uytterhoeven { /* sentinel */ } 304*9f79f939SGeert Uytterhoeven }; 305*9f79f939SGeert Uytterhoeven 306*9f79f939SGeert Uytterhoeven static const struct of_device_id r8a78000_mdlc_match[] = { 307*9f79f939SGeert Uytterhoeven { 308*9f79f939SGeert Uytterhoeven .compatible = "renesas,r8a78000-mdlc", 309*9f79f939SGeert Uytterhoeven .data = &r8a78000_mdlc_default, 310*9f79f939SGeert Uytterhoeven }, 311*9f79f939SGeert Uytterhoeven { /* sentinel */ } 312*9f79f939SGeert Uytterhoeven }; 313*9f79f939SGeert Uytterhoeven 314*9f79f939SGeert Uytterhoeven static struct platform_driver r8a78000_mdlc_driver = { 315*9f79f939SGeert Uytterhoeven .probe = r8a78000_mdlc_probe, 316*9f79f939SGeert Uytterhoeven .driver = { 317*9f79f939SGeert Uytterhoeven .name = "r8a78000-mdlc", 318*9f79f939SGeert Uytterhoeven .of_match_table = r8a78000_mdlc_match, 319*9f79f939SGeert Uytterhoeven .suppress_bind_attrs = true, 320*9f79f939SGeert Uytterhoeven }, 321*9f79f939SGeert Uytterhoeven }; 322*9f79f939SGeert Uytterhoeven 323*9f79f939SGeert Uytterhoeven builtin_platform_driver(r8a78000_mdlc_driver) 324*9f79f939SGeert Uytterhoeven 325*9f79f939SGeert Uytterhoeven MODULE_DESCRIPTION("R-Car X5H MDLC Driver"); 326