1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * System Control and Management Interface (SCMI) Message Protocol bus layer |
4 | * |
5 | * Copyright (C) 2018-2021 ARM Ltd. |
6 | */ |
7 | |
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
9 | |
10 | #include <linux/atomic.h> |
11 | #include <linux/types.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/device.h> |
17 | |
18 | #include "common.h" |
19 | |
20 | BLOCKING_NOTIFIER_HEAD(scmi_requested_devices_nh); |
21 | EXPORT_SYMBOL_GPL(scmi_requested_devices_nh); |
22 | |
23 | static DEFINE_IDA(scmi_bus_id); |
24 | |
25 | static DEFINE_IDR(scmi_requested_devices); |
26 | /* Protect access to scmi_requested_devices */ |
27 | static DEFINE_MUTEX(scmi_requested_devices_mtx); |
28 | |
29 | struct scmi_requested_dev { |
30 | const struct scmi_device_id *id_table; |
31 | struct list_head node; |
32 | }; |
33 | |
34 | /* Track globally the creation of SCMI SystemPower related devices */ |
35 | static atomic_t scmi_syspower_registered = ATOMIC_INIT(0); |
36 | |
37 | /** |
38 | * scmi_protocol_device_request - Helper to request a device |
39 | * |
40 | * @id_table: A protocol/name pair descriptor for the device to be created. |
41 | * |
42 | * This helper let an SCMI driver request specific devices identified by the |
43 | * @id_table to be created for each active SCMI instance. |
44 | * |
45 | * The requested device name MUST NOT be already existent for any protocol; |
46 | * at first the freshly requested @id_table is annotated in the IDR table |
47 | * @scmi_requested_devices and then the requested device is advertised to any |
48 | * registered party via the @scmi_requested_devices_nh notification chain. |
49 | * |
50 | * Return: 0 on Success |
51 | */ |
52 | static int scmi_protocol_device_request(const struct scmi_device_id *id_table) |
53 | { |
54 | int ret = 0; |
55 | unsigned int id = 0; |
56 | struct list_head *head, *phead = NULL; |
57 | struct scmi_requested_dev *rdev; |
58 | |
59 | pr_debug("Requesting SCMI device (%s) for protocol %x\n" , |
60 | id_table->name, id_table->protocol_id); |
61 | |
62 | if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) && |
63 | !IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT_COEX)) { |
64 | pr_warn("SCMI Raw mode active. Rejecting '%s'/0x%02X\n" , |
65 | id_table->name, id_table->protocol_id); |
66 | return -EINVAL; |
67 | } |
68 | |
69 | /* |
70 | * Search for the matching protocol rdev list and then search |
71 | * of any existent equally named device...fails if any duplicate found. |
72 | */ |
73 | mutex_lock(&scmi_requested_devices_mtx); |
74 | idr_for_each_entry(&scmi_requested_devices, head, id) { |
75 | if (!phead) { |
76 | /* A list found registered in the IDR is never empty */ |
77 | rdev = list_first_entry(head, struct scmi_requested_dev, |
78 | node); |
79 | if (rdev->id_table->protocol_id == |
80 | id_table->protocol_id) |
81 | phead = head; |
82 | } |
83 | list_for_each_entry(rdev, head, node) { |
84 | if (!strcmp(rdev->id_table->name, id_table->name)) { |
85 | pr_err("Ignoring duplicate request [%d] %s\n" , |
86 | rdev->id_table->protocol_id, |
87 | rdev->id_table->name); |
88 | ret = -EINVAL; |
89 | goto out; |
90 | } |
91 | } |
92 | } |
93 | |
94 | /* |
95 | * No duplicate found for requested id_table, so let's create a new |
96 | * requested device entry for this new valid request. |
97 | */ |
98 | rdev = kzalloc(size: sizeof(*rdev), GFP_KERNEL); |
99 | if (!rdev) { |
100 | ret = -ENOMEM; |
101 | goto out; |
102 | } |
103 | rdev->id_table = id_table; |
104 | |
105 | /* |
106 | * Append the new requested device table descriptor to the head of the |
107 | * related protocol list, eventually creating such head if not already |
108 | * there. |
109 | */ |
110 | if (!phead) { |
111 | phead = kzalloc(size: sizeof(*phead), GFP_KERNEL); |
112 | if (!phead) { |
113 | kfree(objp: rdev); |
114 | ret = -ENOMEM; |
115 | goto out; |
116 | } |
117 | INIT_LIST_HEAD(list: phead); |
118 | |
119 | ret = idr_alloc(&scmi_requested_devices, ptr: (void *)phead, |
120 | start: id_table->protocol_id, |
121 | end: id_table->protocol_id + 1, GFP_KERNEL); |
122 | if (ret != id_table->protocol_id) { |
123 | pr_err("Failed to save SCMI device - ret:%d\n" , ret); |
124 | kfree(objp: rdev); |
125 | kfree(objp: phead); |
126 | ret = -EINVAL; |
127 | goto out; |
128 | } |
129 | ret = 0; |
130 | } |
131 | list_add(new: &rdev->node, head: phead); |
132 | |
133 | out: |
134 | mutex_unlock(lock: &scmi_requested_devices_mtx); |
135 | |
136 | if (!ret) |
137 | blocking_notifier_call_chain(nh: &scmi_requested_devices_nh, |
138 | SCMI_BUS_NOTIFY_DEVICE_REQUEST, |
139 | v: (void *)rdev->id_table); |
140 | |
141 | return ret; |
142 | } |
143 | |
144 | static int scmi_protocol_table_register(const struct scmi_device_id *id_table) |
145 | { |
146 | int ret = 0; |
147 | const struct scmi_device_id *entry; |
148 | |
149 | for (entry = id_table; entry->name && ret == 0; entry++) |
150 | ret = scmi_protocol_device_request(id_table: entry); |
151 | |
152 | return ret; |
153 | } |
154 | |
155 | /** |
156 | * scmi_protocol_device_unrequest - Helper to unrequest a device |
157 | * |
158 | * @id_table: A protocol/name pair descriptor for the device to be unrequested. |
159 | * |
160 | * The unrequested device, described by the provided id_table, is at first |
161 | * removed from the IDR @scmi_requested_devices and then the removal is |
162 | * advertised to any registered party via the @scmi_requested_devices_nh |
163 | * notification chain. |
164 | */ |
165 | static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table) |
166 | { |
167 | struct list_head *phead; |
168 | |
169 | pr_debug("Unrequesting SCMI device (%s) for protocol %x\n" , |
170 | id_table->name, id_table->protocol_id); |
171 | |
172 | mutex_lock(&scmi_requested_devices_mtx); |
173 | phead = idr_find(&scmi_requested_devices, id: id_table->protocol_id); |
174 | if (phead) { |
175 | struct scmi_requested_dev *victim, *tmp; |
176 | |
177 | list_for_each_entry_safe(victim, tmp, phead, node) { |
178 | if (!strcmp(victim->id_table->name, id_table->name)) { |
179 | list_del(entry: &victim->node); |
180 | |
181 | mutex_unlock(lock: &scmi_requested_devices_mtx); |
182 | blocking_notifier_call_chain(nh: &scmi_requested_devices_nh, |
183 | SCMI_BUS_NOTIFY_DEVICE_UNREQUEST, |
184 | v: (void *)victim->id_table); |
185 | kfree(objp: victim); |
186 | mutex_lock(&scmi_requested_devices_mtx); |
187 | break; |
188 | } |
189 | } |
190 | |
191 | if (list_empty(head: phead)) { |
192 | idr_remove(&scmi_requested_devices, |
193 | id: id_table->protocol_id); |
194 | kfree(objp: phead); |
195 | } |
196 | } |
197 | mutex_unlock(lock: &scmi_requested_devices_mtx); |
198 | } |
199 | |
200 | static void |
201 | scmi_protocol_table_unregister(const struct scmi_device_id *id_table) |
202 | { |
203 | const struct scmi_device_id *entry; |
204 | |
205 | for (entry = id_table; entry->name; entry++) |
206 | scmi_protocol_device_unrequest(id_table: entry); |
207 | } |
208 | |
209 | static const struct scmi_device_id * |
210 | scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv) |
211 | { |
212 | const struct scmi_device_id *id = scmi_drv->id_table; |
213 | |
214 | if (!id) |
215 | return NULL; |
216 | |
217 | for (; id->protocol_id; id++) |
218 | if (id->protocol_id == scmi_dev->protocol_id) { |
219 | if (!id->name) |
220 | return id; |
221 | else if (!strcmp(id->name, scmi_dev->name)) |
222 | return id; |
223 | } |
224 | |
225 | return NULL; |
226 | } |
227 | |
228 | static int scmi_dev_match(struct device *dev, struct device_driver *drv) |
229 | { |
230 | struct scmi_driver *scmi_drv = to_scmi_driver(drv); |
231 | struct scmi_device *scmi_dev = to_scmi_dev(dev); |
232 | const struct scmi_device_id *id; |
233 | |
234 | id = scmi_dev_match_id(scmi_dev, scmi_drv); |
235 | if (id) |
236 | return 1; |
237 | |
238 | return 0; |
239 | } |
240 | |
241 | static int scmi_match_by_id_table(struct device *dev, void *data) |
242 | { |
243 | struct scmi_device *sdev = to_scmi_dev(dev); |
244 | struct scmi_device_id *id_table = data; |
245 | |
246 | return sdev->protocol_id == id_table->protocol_id && |
247 | (id_table->name && !strcmp(sdev->name, id_table->name)); |
248 | } |
249 | |
250 | static struct scmi_device *scmi_child_dev_find(struct device *parent, |
251 | int prot_id, const char *name) |
252 | { |
253 | struct scmi_device_id id_table; |
254 | struct device *dev; |
255 | |
256 | id_table.protocol_id = prot_id; |
257 | id_table.name = name; |
258 | |
259 | dev = device_find_child(dev: parent, data: &id_table, match: scmi_match_by_id_table); |
260 | if (!dev) |
261 | return NULL; |
262 | |
263 | return to_scmi_dev(dev); |
264 | } |
265 | |
266 | static int scmi_dev_probe(struct device *dev) |
267 | { |
268 | struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver); |
269 | struct scmi_device *scmi_dev = to_scmi_dev(dev); |
270 | |
271 | if (!scmi_dev->handle) |
272 | return -EPROBE_DEFER; |
273 | |
274 | return scmi_drv->probe(scmi_dev); |
275 | } |
276 | |
277 | static void scmi_dev_remove(struct device *dev) |
278 | { |
279 | struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver); |
280 | struct scmi_device *scmi_dev = to_scmi_dev(dev); |
281 | |
282 | if (scmi_drv->remove) |
283 | scmi_drv->remove(scmi_dev); |
284 | } |
285 | |
286 | const struct bus_type scmi_bus_type = { |
287 | .name = "scmi_protocol" , |
288 | .match = scmi_dev_match, |
289 | .probe = scmi_dev_probe, |
290 | .remove = scmi_dev_remove, |
291 | }; |
292 | EXPORT_SYMBOL_GPL(scmi_bus_type); |
293 | |
294 | int scmi_driver_register(struct scmi_driver *driver, struct module *owner, |
295 | const char *mod_name) |
296 | { |
297 | int retval; |
298 | |
299 | if (!driver->probe) |
300 | return -EINVAL; |
301 | |
302 | retval = scmi_protocol_table_register(id_table: driver->id_table); |
303 | if (retval) |
304 | return retval; |
305 | |
306 | driver->driver.bus = &scmi_bus_type; |
307 | driver->driver.name = driver->name; |
308 | driver->driver.owner = owner; |
309 | driver->driver.mod_name = mod_name; |
310 | |
311 | retval = driver_register(drv: &driver->driver); |
312 | if (!retval) |
313 | pr_debug("Registered new scmi driver %s\n" , driver->name); |
314 | |
315 | return retval; |
316 | } |
317 | EXPORT_SYMBOL_GPL(scmi_driver_register); |
318 | |
319 | void scmi_driver_unregister(struct scmi_driver *driver) |
320 | { |
321 | driver_unregister(drv: &driver->driver); |
322 | scmi_protocol_table_unregister(id_table: driver->id_table); |
323 | } |
324 | EXPORT_SYMBOL_GPL(scmi_driver_unregister); |
325 | |
326 | static void scmi_device_release(struct device *dev) |
327 | { |
328 | kfree(to_scmi_dev(dev)); |
329 | } |
330 | |
331 | static void __scmi_device_destroy(struct scmi_device *scmi_dev) |
332 | { |
333 | pr_debug("(%s) Destroying SCMI device '%s' for protocol 0x%x (%s)\n" , |
334 | of_node_full_name(scmi_dev->dev.parent->of_node), |
335 | dev_name(&scmi_dev->dev), scmi_dev->protocol_id, |
336 | scmi_dev->name); |
337 | |
338 | if (scmi_dev->protocol_id == SCMI_PROTOCOL_SYSTEM) |
339 | atomic_set(v: &scmi_syspower_registered, i: 0); |
340 | |
341 | kfree_const(x: scmi_dev->name); |
342 | ida_free(&scmi_bus_id, id: scmi_dev->id); |
343 | device_unregister(dev: &scmi_dev->dev); |
344 | } |
345 | |
346 | static struct scmi_device * |
347 | __scmi_device_create(struct device_node *np, struct device *parent, |
348 | int protocol, const char *name) |
349 | { |
350 | int id, retval; |
351 | struct scmi_device *scmi_dev; |
352 | |
353 | /* |
354 | * If the same protocol/name device already exist under the same parent |
355 | * (i.e. SCMI instance) just return the existent device. |
356 | * This avoids any race between the SCMI driver, creating devices for |
357 | * each DT defined protocol at probe time, and the concurrent |
358 | * registration of SCMI drivers. |
359 | */ |
360 | scmi_dev = scmi_child_dev_find(parent, prot_id: protocol, name); |
361 | if (scmi_dev) |
362 | return scmi_dev; |
363 | |
364 | /* |
365 | * Ignore any possible subsequent failures while creating the device |
366 | * since we are doomed anyway at that point; not using a mutex which |
367 | * spans across this whole function to keep things simple and to avoid |
368 | * to serialize all the __scmi_device_create calls across possibly |
369 | * different SCMI server instances (parent) |
370 | */ |
371 | if (protocol == SCMI_PROTOCOL_SYSTEM && |
372 | atomic_cmpxchg(v: &scmi_syspower_registered, old: 0, new: 1)) { |
373 | dev_warn(parent, |
374 | "SCMI SystemPower protocol device must be unique !\n" ); |
375 | return NULL; |
376 | } |
377 | |
378 | scmi_dev = kzalloc(size: sizeof(*scmi_dev), GFP_KERNEL); |
379 | if (!scmi_dev) |
380 | return NULL; |
381 | |
382 | scmi_dev->name = kstrdup_const(s: name ?: "unknown" , GFP_KERNEL); |
383 | if (!scmi_dev->name) { |
384 | kfree(objp: scmi_dev); |
385 | return NULL; |
386 | } |
387 | |
388 | id = ida_alloc_min(ida: &scmi_bus_id, min: 1, GFP_KERNEL); |
389 | if (id < 0) { |
390 | kfree_const(x: scmi_dev->name); |
391 | kfree(objp: scmi_dev); |
392 | return NULL; |
393 | } |
394 | |
395 | scmi_dev->id = id; |
396 | scmi_dev->protocol_id = protocol; |
397 | scmi_dev->dev.parent = parent; |
398 | device_set_node(dev: &scmi_dev->dev, of_fwnode_handle(np)); |
399 | scmi_dev->dev.bus = &scmi_bus_type; |
400 | scmi_dev->dev.release = scmi_device_release; |
401 | dev_set_name(dev: &scmi_dev->dev, name: "scmi_dev.%d" , id); |
402 | |
403 | retval = device_register(dev: &scmi_dev->dev); |
404 | if (retval) |
405 | goto put_dev; |
406 | |
407 | pr_debug("(%s) Created SCMI device '%s' for protocol 0x%x (%s)\n" , |
408 | of_node_full_name(parent->of_node), |
409 | dev_name(&scmi_dev->dev), protocol, name); |
410 | |
411 | return scmi_dev; |
412 | put_dev: |
413 | kfree_const(x: scmi_dev->name); |
414 | put_device(dev: &scmi_dev->dev); |
415 | ida_free(&scmi_bus_id, id); |
416 | return NULL; |
417 | } |
418 | |
419 | /** |
420 | * scmi_device_create - A method to create one or more SCMI devices |
421 | * |
422 | * @np: A reference to the device node to use for the new device(s) |
423 | * @parent: The parent device to use identifying a specific SCMI instance |
424 | * @protocol: The SCMI protocol to be associated with this device |
425 | * @name: The requested-name of the device to be created; this is optional |
426 | * and if no @name is provided, all the devices currently known to |
427 | * be requested on the SCMI bus for @protocol will be created. |
428 | * |
429 | * This method can be invoked to create a single well-defined device (like |
430 | * a transport device or a device requested by an SCMI driver loaded after |
431 | * the core SCMI stack has been probed), or to create all the devices currently |
432 | * known to have been requested by the loaded SCMI drivers for a specific |
433 | * protocol (typically during SCMI core protocol enumeration at probe time). |
434 | * |
435 | * Return: The created device (or one of them if @name was NOT provided and |
436 | * multiple devices were created) or NULL if no device was created; |
437 | * note that NULL indicates an error ONLY in case a specific @name |
438 | * was provided: when @name param was not provided, a number of devices |
439 | * could have been potentially created for a whole protocol, unless no |
440 | * device was found to have been requested for that specific protocol. |
441 | */ |
442 | struct scmi_device *scmi_device_create(struct device_node *np, |
443 | struct device *parent, int protocol, |
444 | const char *name) |
445 | { |
446 | struct list_head *phead; |
447 | struct scmi_requested_dev *rdev; |
448 | struct scmi_device *scmi_dev = NULL; |
449 | |
450 | if (name) |
451 | return __scmi_device_create(np, parent, protocol, name); |
452 | |
453 | mutex_lock(&scmi_requested_devices_mtx); |
454 | phead = idr_find(&scmi_requested_devices, id: protocol); |
455 | /* Nothing to do. */ |
456 | if (!phead) { |
457 | mutex_unlock(lock: &scmi_requested_devices_mtx); |
458 | return NULL; |
459 | } |
460 | |
461 | /* Walk the list of requested devices for protocol and create them */ |
462 | list_for_each_entry(rdev, phead, node) { |
463 | struct scmi_device *sdev; |
464 | |
465 | sdev = __scmi_device_create(np, parent, |
466 | protocol: rdev->id_table->protocol_id, |
467 | name: rdev->id_table->name); |
468 | /* Report errors and carry on... */ |
469 | if (sdev) |
470 | scmi_dev = sdev; |
471 | else |
472 | pr_err("(%s) Failed to create device for protocol 0x%x (%s)\n" , |
473 | of_node_full_name(parent->of_node), |
474 | rdev->id_table->protocol_id, |
475 | rdev->id_table->name); |
476 | } |
477 | mutex_unlock(lock: &scmi_requested_devices_mtx); |
478 | |
479 | return scmi_dev; |
480 | } |
481 | EXPORT_SYMBOL_GPL(scmi_device_create); |
482 | |
483 | void scmi_device_destroy(struct device *parent, int protocol, const char *name) |
484 | { |
485 | struct scmi_device *scmi_dev; |
486 | |
487 | scmi_dev = scmi_child_dev_find(parent, prot_id: protocol, name); |
488 | if (scmi_dev) |
489 | __scmi_device_destroy(scmi_dev); |
490 | } |
491 | EXPORT_SYMBOL_GPL(scmi_device_destroy); |
492 | |
493 | static int __scmi_devices_unregister(struct device *dev, void *data) |
494 | { |
495 | struct scmi_device *scmi_dev = to_scmi_dev(dev); |
496 | |
497 | __scmi_device_destroy(scmi_dev); |
498 | return 0; |
499 | } |
500 | |
501 | static void scmi_devices_unregister(void) |
502 | { |
503 | bus_for_each_dev(bus: &scmi_bus_type, NULL, NULL, fn: __scmi_devices_unregister); |
504 | } |
505 | |
506 | static int __init scmi_bus_init(void) |
507 | { |
508 | int retval; |
509 | |
510 | retval = bus_register(bus: &scmi_bus_type); |
511 | if (retval) |
512 | pr_err("SCMI protocol bus register failed (%d)\n" , retval); |
513 | |
514 | pr_info("SCMI protocol bus registered\n" ); |
515 | |
516 | return retval; |
517 | } |
518 | subsys_initcall(scmi_bus_init); |
519 | |
520 | static void __exit scmi_bus_exit(void) |
521 | { |
522 | /* |
523 | * Destroy all remaining devices: just in case the drivers were |
524 | * manually unbound and at first and then the modules unloaded. |
525 | */ |
526 | scmi_devices_unregister(); |
527 | bus_unregister(bus: &scmi_bus_type); |
528 | ida_destroy(ida: &scmi_bus_id); |
529 | } |
530 | module_exit(scmi_bus_exit); |
531 | |
532 | MODULE_ALIAS("scmi-core" ); |
533 | MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>" ); |
534 | MODULE_DESCRIPTION("ARM SCMI protocol bus" ); |
535 | MODULE_LICENSE("GPL" ); |
536 | |