1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * drivers/mfd/mfd-core.c |
4 | * |
5 | * core MFD support |
6 | * Copyright (c) 2006 Ian Molton |
7 | * Copyright (c) 2007,2008 Dmitry Baryshkov |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/acpi.h> |
13 | #include <linux/list.h> |
14 | #include <linux/property.h> |
15 | #include <linux/mfd/core.h> |
16 | #include <linux/pm_runtime.h> |
17 | #include <linux/slab.h> |
18 | #include <linux/module.h> |
19 | #include <linux/irqdomain.h> |
20 | #include <linux/of.h> |
21 | #include <linux/of_address.h> |
22 | #include <linux/regulator/consumer.h> |
23 | |
24 | static LIST_HEAD(mfd_of_node_list); |
25 | |
26 | struct mfd_of_node_entry { |
27 | struct list_head list; |
28 | struct device *dev; |
29 | struct device_node *np; |
30 | }; |
31 | |
32 | static struct device_type mfd_dev_type = { |
33 | .name = "mfd_device" , |
34 | }; |
35 | |
36 | #if IS_ENABLED(CONFIG_ACPI) |
37 | struct match_ids_walk_data { |
38 | struct acpi_device_id *ids; |
39 | struct acpi_device *adev; |
40 | }; |
41 | |
42 | static int match_device_ids(struct acpi_device *adev, void *data) |
43 | { |
44 | struct match_ids_walk_data *wd = data; |
45 | |
46 | if (!acpi_match_device_ids(device: adev, ids: wd->ids)) { |
47 | wd->adev = adev; |
48 | return 1; |
49 | } |
50 | |
51 | return 0; |
52 | } |
53 | |
54 | static void mfd_acpi_add_device(const struct mfd_cell *cell, |
55 | struct platform_device *pdev) |
56 | { |
57 | const struct mfd_cell_acpi_match *match = cell->acpi_match; |
58 | struct acpi_device *adev = NULL; |
59 | struct acpi_device *parent; |
60 | |
61 | parent = ACPI_COMPANION(pdev->dev.parent); |
62 | if (!parent) |
63 | return; |
64 | |
65 | /* |
66 | * MFD child device gets its ACPI handle either from the ACPI device |
67 | * directly under the parent that matches the either _HID or _CID, or |
68 | * _ADR or it will use the parent handle if is no ID is given. |
69 | * |
70 | * Note that use of _ADR is a grey area in the ACPI specification, |
71 | * though at least Intel Galileo Gen 2 is using it to distinguish |
72 | * the children devices. |
73 | */ |
74 | if (match) { |
75 | if (match->pnpid) { |
76 | struct acpi_device_id ids[2] = {}; |
77 | struct match_ids_walk_data wd = { |
78 | .adev = NULL, |
79 | .ids = ids, |
80 | }; |
81 | |
82 | strscpy(p: ids[0].id, q: match->pnpid, size: sizeof(ids[0].id)); |
83 | acpi_dev_for_each_child(adev: parent, fn: match_device_ids, data: &wd); |
84 | adev = wd.adev; |
85 | } else { |
86 | adev = acpi_find_child_device(parent, address: match->adr, check_children: false); |
87 | } |
88 | } |
89 | |
90 | ACPI_COMPANION_SET(&pdev->dev, adev ?: parent); |
91 | } |
92 | #else |
93 | static inline void mfd_acpi_add_device(const struct mfd_cell *cell, |
94 | struct platform_device *pdev) |
95 | { |
96 | } |
97 | #endif |
98 | |
99 | static int mfd_match_of_node_to_dev(struct platform_device *pdev, |
100 | struct device_node *np, |
101 | const struct mfd_cell *cell) |
102 | { |
103 | #if IS_ENABLED(CONFIG_OF) |
104 | struct mfd_of_node_entry *of_entry; |
105 | u64 of_node_addr; |
106 | |
107 | /* Skip if OF node has previously been allocated to a device */ |
108 | list_for_each_entry(of_entry, &mfd_of_node_list, list) |
109 | if (of_entry->np == np) |
110 | return -EAGAIN; |
111 | |
112 | if (!cell->use_of_reg) |
113 | /* No of_reg defined - allocate first free compatible match */ |
114 | goto allocate_of_node; |
115 | |
116 | /* We only care about each node's first defined address */ |
117 | if (of_property_read_reg(np, idx: 0, addr: &of_node_addr, NULL)) |
118 | /* OF node does not contatin a 'reg' property to match to */ |
119 | return -EAGAIN; |
120 | |
121 | if (cell->of_reg != of_node_addr) |
122 | /* No match */ |
123 | return -EAGAIN; |
124 | |
125 | allocate_of_node: |
126 | of_entry = kzalloc(size: sizeof(*of_entry), GFP_KERNEL); |
127 | if (!of_entry) |
128 | return -ENOMEM; |
129 | |
130 | of_entry->dev = &pdev->dev; |
131 | of_entry->np = np; |
132 | list_add_tail(new: &of_entry->list, head: &mfd_of_node_list); |
133 | |
134 | pdev->dev.of_node = np; |
135 | pdev->dev.fwnode = &np->fwnode; |
136 | #endif |
137 | return 0; |
138 | } |
139 | |
140 | static int mfd_add_device(struct device *parent, int id, |
141 | const struct mfd_cell *cell, |
142 | struct resource *mem_base, |
143 | int irq_base, struct irq_domain *domain) |
144 | { |
145 | struct resource *res; |
146 | struct platform_device *pdev; |
147 | struct device_node *np = NULL; |
148 | struct mfd_of_node_entry *of_entry, *tmp; |
149 | bool disabled = false; |
150 | int ret = -ENOMEM; |
151 | int platform_id; |
152 | int r; |
153 | |
154 | if (id == PLATFORM_DEVID_AUTO) |
155 | platform_id = id; |
156 | else |
157 | platform_id = id + cell->id; |
158 | |
159 | pdev = platform_device_alloc(name: cell->name, id: platform_id); |
160 | if (!pdev) |
161 | goto fail_alloc; |
162 | |
163 | pdev->mfd_cell = kmemdup(p: cell, size: sizeof(*cell), GFP_KERNEL); |
164 | if (!pdev->mfd_cell) |
165 | goto fail_device; |
166 | |
167 | res = kcalloc(n: cell->num_resources, size: sizeof(*res), GFP_KERNEL); |
168 | if (!res) |
169 | goto fail_device; |
170 | |
171 | pdev->dev.parent = parent; |
172 | pdev->dev.type = &mfd_dev_type; |
173 | pdev->dev.dma_mask = parent->dma_mask; |
174 | pdev->dev.dma_parms = parent->dma_parms; |
175 | pdev->dev.coherent_dma_mask = parent->coherent_dma_mask; |
176 | |
177 | ret = regulator_bulk_register_supply_alias( |
178 | dev: &pdev->dev, id: cell->parent_supplies, |
179 | alias_dev: parent, alias_id: cell->parent_supplies, |
180 | num_id: cell->num_parent_supplies); |
181 | if (ret < 0) |
182 | goto fail_res; |
183 | |
184 | if (IS_ENABLED(CONFIG_OF) && parent->of_node && cell->of_compatible) { |
185 | for_each_child_of_node(parent->of_node, np) { |
186 | if (of_device_is_compatible(device: np, cell->of_compatible)) { |
187 | /* Skip 'disabled' devices */ |
188 | if (!of_device_is_available(device: np)) { |
189 | disabled = true; |
190 | continue; |
191 | } |
192 | |
193 | ret = mfd_match_of_node_to_dev(pdev, np, cell); |
194 | if (ret == -EAGAIN) |
195 | continue; |
196 | of_node_put(node: np); |
197 | if (ret) |
198 | goto fail_alias; |
199 | |
200 | goto match; |
201 | } |
202 | } |
203 | |
204 | if (disabled) { |
205 | /* Ignore 'disabled' devices error free */ |
206 | ret = 0; |
207 | goto fail_alias; |
208 | } |
209 | |
210 | match: |
211 | if (!pdev->dev.of_node) |
212 | pr_warn("%s: Failed to locate of_node [id: %d]\n" , |
213 | cell->name, platform_id); |
214 | } |
215 | |
216 | mfd_acpi_add_device(cell, pdev); |
217 | |
218 | if (cell->pdata_size) { |
219 | ret = platform_device_add_data(pdev, |
220 | data: cell->platform_data, size: cell->pdata_size); |
221 | if (ret) |
222 | goto fail_of_entry; |
223 | } |
224 | |
225 | if (cell->swnode) { |
226 | ret = device_add_software_node(dev: &pdev->dev, node: cell->swnode); |
227 | if (ret) |
228 | goto fail_of_entry; |
229 | } |
230 | |
231 | for (r = 0; r < cell->num_resources; r++) { |
232 | res[r].name = cell->resources[r].name; |
233 | res[r].flags = cell->resources[r].flags; |
234 | |
235 | /* Find out base to use */ |
236 | if ((cell->resources[r].flags & IORESOURCE_MEM) && mem_base) { |
237 | res[r].parent = mem_base; |
238 | res[r].start = mem_base->start + |
239 | cell->resources[r].start; |
240 | res[r].end = mem_base->start + |
241 | cell->resources[r].end; |
242 | } else if (cell->resources[r].flags & IORESOURCE_IRQ) { |
243 | if (domain) { |
244 | /* Unable to create mappings for IRQ ranges. */ |
245 | WARN_ON(cell->resources[r].start != |
246 | cell->resources[r].end); |
247 | res[r].start = res[r].end = irq_create_mapping( |
248 | host: domain, hwirq: cell->resources[r].start); |
249 | } else { |
250 | res[r].start = irq_base + |
251 | cell->resources[r].start; |
252 | res[r].end = irq_base + |
253 | cell->resources[r].end; |
254 | } |
255 | } else { |
256 | res[r].parent = cell->resources[r].parent; |
257 | res[r].start = cell->resources[r].start; |
258 | res[r].end = cell->resources[r].end; |
259 | } |
260 | |
261 | if (!cell->ignore_resource_conflicts) { |
262 | if (has_acpi_companion(dev: &pdev->dev)) { |
263 | ret = acpi_check_resource_conflict(res: &res[r]); |
264 | if (ret) |
265 | goto fail_res_conflict; |
266 | } |
267 | } |
268 | } |
269 | |
270 | ret = platform_device_add_resources(pdev, res, num: cell->num_resources); |
271 | if (ret) |
272 | goto fail_res_conflict; |
273 | |
274 | ret = platform_device_add(pdev); |
275 | if (ret) |
276 | goto fail_res_conflict; |
277 | |
278 | if (cell->pm_runtime_no_callbacks) |
279 | pm_runtime_no_callbacks(dev: &pdev->dev); |
280 | |
281 | kfree(objp: res); |
282 | |
283 | return 0; |
284 | |
285 | fail_res_conflict: |
286 | if (cell->swnode) |
287 | device_remove_software_node(dev: &pdev->dev); |
288 | fail_of_entry: |
289 | list_for_each_entry_safe(of_entry, tmp, &mfd_of_node_list, list) |
290 | if (of_entry->dev == &pdev->dev) { |
291 | list_del(entry: &of_entry->list); |
292 | kfree(objp: of_entry); |
293 | } |
294 | fail_alias: |
295 | regulator_bulk_unregister_supply_alias(dev: &pdev->dev, |
296 | id: cell->parent_supplies, |
297 | num_id: cell->num_parent_supplies); |
298 | fail_res: |
299 | kfree(objp: res); |
300 | fail_device: |
301 | platform_device_put(pdev); |
302 | fail_alloc: |
303 | return ret; |
304 | } |
305 | |
306 | /** |
307 | * mfd_add_devices - register child devices |
308 | * |
309 | * @parent: Pointer to parent device. |
310 | * @id: Can be PLATFORM_DEVID_AUTO to let the Platform API take care |
311 | * of device numbering, or will be added to a device's cell_id. |
312 | * @cells: Array of (struct mfd_cell)s describing child devices. |
313 | * @n_devs: Number of child devices to register. |
314 | * @mem_base: Parent register range resource for child devices. |
315 | * @irq_base: Base of the range of virtual interrupt numbers allocated for |
316 | * this MFD device. Unused if @domain is specified. |
317 | * @domain: Interrupt domain to create mappings for hardware interrupts. |
318 | */ |
319 | int mfd_add_devices(struct device *parent, int id, |
320 | const struct mfd_cell *cells, int n_devs, |
321 | struct resource *mem_base, |
322 | int irq_base, struct irq_domain *domain) |
323 | { |
324 | int i; |
325 | int ret; |
326 | |
327 | for (i = 0; i < n_devs; i++) { |
328 | ret = mfd_add_device(parent, id, cell: cells + i, mem_base, |
329 | irq_base, domain); |
330 | if (ret) |
331 | goto fail; |
332 | } |
333 | |
334 | return 0; |
335 | |
336 | fail: |
337 | if (i) |
338 | mfd_remove_devices(parent); |
339 | |
340 | return ret; |
341 | } |
342 | EXPORT_SYMBOL(mfd_add_devices); |
343 | |
344 | static int mfd_remove_devices_fn(struct device *dev, void *data) |
345 | { |
346 | struct platform_device *pdev; |
347 | const struct mfd_cell *cell; |
348 | struct mfd_of_node_entry *of_entry, *tmp; |
349 | int *level = data; |
350 | |
351 | if (dev->type != &mfd_dev_type) |
352 | return 0; |
353 | |
354 | pdev = to_platform_device(dev); |
355 | cell = mfd_get_cell(pdev); |
356 | |
357 | if (level && cell->level > *level) |
358 | return 0; |
359 | |
360 | if (cell->swnode) |
361 | device_remove_software_node(dev: &pdev->dev); |
362 | |
363 | list_for_each_entry_safe(of_entry, tmp, &mfd_of_node_list, list) |
364 | if (of_entry->dev == &pdev->dev) { |
365 | list_del(entry: &of_entry->list); |
366 | kfree(objp: of_entry); |
367 | } |
368 | |
369 | regulator_bulk_unregister_supply_alias(dev, id: cell->parent_supplies, |
370 | num_id: cell->num_parent_supplies); |
371 | |
372 | platform_device_unregister(pdev); |
373 | return 0; |
374 | } |
375 | |
376 | void mfd_remove_devices_late(struct device *parent) |
377 | { |
378 | int level = MFD_DEP_LEVEL_HIGH; |
379 | |
380 | device_for_each_child_reverse(dev: parent, data: &level, fn: mfd_remove_devices_fn); |
381 | } |
382 | EXPORT_SYMBOL(mfd_remove_devices_late); |
383 | |
384 | void mfd_remove_devices(struct device *parent) |
385 | { |
386 | int level = MFD_DEP_LEVEL_NORMAL; |
387 | |
388 | device_for_each_child_reverse(dev: parent, data: &level, fn: mfd_remove_devices_fn); |
389 | } |
390 | EXPORT_SYMBOL(mfd_remove_devices); |
391 | |
392 | static void devm_mfd_dev_release(struct device *dev, void *res) |
393 | { |
394 | mfd_remove_devices(dev); |
395 | } |
396 | |
397 | /** |
398 | * devm_mfd_add_devices - Resource managed version of mfd_add_devices() |
399 | * |
400 | * Returns 0 on success or an appropriate negative error number on failure. |
401 | * All child-devices of the MFD will automatically be removed when it gets |
402 | * unbinded. |
403 | * |
404 | * @dev: Pointer to parent device. |
405 | * @id: Can be PLATFORM_DEVID_AUTO to let the Platform API take care |
406 | * of device numbering, or will be added to a device's cell_id. |
407 | * @cells: Array of (struct mfd_cell)s describing child devices. |
408 | * @n_devs: Number of child devices to register. |
409 | * @mem_base: Parent register range resource for child devices. |
410 | * @irq_base: Base of the range of virtual interrupt numbers allocated for |
411 | * this MFD device. Unused if @domain is specified. |
412 | * @domain: Interrupt domain to create mappings for hardware interrupts. |
413 | */ |
414 | int devm_mfd_add_devices(struct device *dev, int id, |
415 | const struct mfd_cell *cells, int n_devs, |
416 | struct resource *mem_base, |
417 | int irq_base, struct irq_domain *domain) |
418 | { |
419 | struct device **ptr; |
420 | int ret; |
421 | |
422 | ptr = devres_alloc(devm_mfd_dev_release, sizeof(*ptr), GFP_KERNEL); |
423 | if (!ptr) |
424 | return -ENOMEM; |
425 | |
426 | ret = mfd_add_devices(dev, id, cells, n_devs, mem_base, |
427 | irq_base, domain); |
428 | if (ret < 0) { |
429 | devres_free(res: ptr); |
430 | return ret; |
431 | } |
432 | |
433 | *ptr = dev; |
434 | devres_add(dev, res: ptr); |
435 | |
436 | return ret; |
437 | } |
438 | EXPORT_SYMBOL(devm_mfd_add_devices); |
439 | |
440 | MODULE_LICENSE("GPL" ); |
441 | MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov" ); |
442 | |