xref: /linux/drivers/pmdomain/renesas/r8a78000-mdlc.c (revision ee1b6365f07a14e987dd0f994fc7d46966037ea9)
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