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