|
156 | 156 | * }, |
157 | 157 | * .ops = my_custom_ops, |
158 | 158 | * }; |
| 159 | + * |
| 160 | + * Please note that such custom ops approach is valid, but it is hard to implement |
| 161 | + * it right without global locks per-device to protect from auxiliary_drv removal |
| 162 | + * during call to that ops. In addition, this implementation lacks proper module |
| 163 | + * dependency, which causes to load/unload races between auxiliary parent and devices |
| 164 | + * modules. |
| 165 | + * |
| 166 | + * The most easiest way to provide these ops reliably without needing to |
| 167 | + * have a lock is to EXPORT_SYMBOL*() them and rely on already existing |
| 168 | + * modules infrastructure for validity and correct dependencies chains. |
159 | 169 | */ |
160 | 170 |
|
161 | 171 | static const struct auxiliary_device_id *auxiliary_match_id(const struct auxiliary_device_id *id, |
@@ -207,7 +217,7 @@ static int auxiliary_bus_probe(struct device *dev) |
207 | 217 | struct auxiliary_device *auxdev = to_auxiliary_dev(dev); |
208 | 218 | int ret; |
209 | 219 |
|
210 | | - ret = dev_pm_domain_attach(dev, true); |
| 220 | + ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON); |
211 | 221 | if (ret) { |
212 | 222 | dev_warn(dev, "Failed to attach to PM Domain : %d\n", ret); |
213 | 223 | return ret; |
@@ -385,6 +395,116 @@ void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv) |
385 | 395 | } |
386 | 396 | EXPORT_SYMBOL_GPL(auxiliary_driver_unregister); |
387 | 397 |
|
| 398 | +static void auxiliary_device_release(struct device *dev) |
| 399 | +{ |
| 400 | + struct auxiliary_device *auxdev = to_auxiliary_dev(dev); |
| 401 | + |
| 402 | + of_node_put(dev->of_node); |
| 403 | + kfree(auxdev); |
| 404 | +} |
| 405 | + |
| 406 | +/** |
| 407 | + * auxiliary_device_create - create a device on the auxiliary bus |
| 408 | + * @dev: parent device |
| 409 | + * @modname: module name used to create the auxiliary driver name. |
| 410 | + * @devname: auxiliary bus device name |
| 411 | + * @platform_data: auxiliary bus device platform data |
| 412 | + * @id: auxiliary bus device id |
| 413 | + * |
| 414 | + * Helper to create an auxiliary bus device. |
| 415 | + * The device created matches driver 'modname.devname' on the auxiliary bus. |
| 416 | + */ |
| 417 | +struct auxiliary_device *auxiliary_device_create(struct device *dev, |
| 418 | + const char *modname, |
| 419 | + const char *devname, |
| 420 | + void *platform_data, |
| 421 | + int id) |
| 422 | +{ |
| 423 | + struct auxiliary_device *auxdev; |
| 424 | + int ret; |
| 425 | + |
| 426 | + auxdev = kzalloc(sizeof(*auxdev), GFP_KERNEL); |
| 427 | + if (!auxdev) |
| 428 | + return NULL; |
| 429 | + |
| 430 | + auxdev->id = id; |
| 431 | + auxdev->name = devname; |
| 432 | + auxdev->dev.parent = dev; |
| 433 | + auxdev->dev.platform_data = platform_data; |
| 434 | + auxdev->dev.release = auxiliary_device_release; |
| 435 | + device_set_of_node_from_dev(&auxdev->dev, dev); |
| 436 | + |
| 437 | + ret = auxiliary_device_init(auxdev); |
| 438 | + if (ret) { |
| 439 | + of_node_put(auxdev->dev.of_node); |
| 440 | + kfree(auxdev); |
| 441 | + return NULL; |
| 442 | + } |
| 443 | + |
| 444 | + ret = __auxiliary_device_add(auxdev, modname); |
| 445 | + if (ret) { |
| 446 | + /* |
| 447 | + * It may look odd but auxdev should not be freed here. |
| 448 | + * auxiliary_device_uninit() calls device_put() which call |
| 449 | + * the device release function, freeing auxdev. |
| 450 | + */ |
| 451 | + auxiliary_device_uninit(auxdev); |
| 452 | + return NULL; |
| 453 | + } |
| 454 | + |
| 455 | + return auxdev; |
| 456 | +} |
| 457 | +EXPORT_SYMBOL_GPL(auxiliary_device_create); |
| 458 | + |
| 459 | +/** |
| 460 | + * auxiliary_device_destroy - remove an auxiliary device |
| 461 | + * @auxdev: pointer to the auxdev to be removed |
| 462 | + * |
| 463 | + * Helper to remove an auxiliary device created with |
| 464 | + * auxiliary_device_create() |
| 465 | + */ |
| 466 | +void auxiliary_device_destroy(void *auxdev) |
| 467 | +{ |
| 468 | + struct auxiliary_device *_auxdev = auxdev; |
| 469 | + |
| 470 | + auxiliary_device_delete(_auxdev); |
| 471 | + auxiliary_device_uninit(_auxdev); |
| 472 | +} |
| 473 | +EXPORT_SYMBOL_GPL(auxiliary_device_destroy); |
| 474 | + |
| 475 | +/** |
| 476 | + * __devm_auxiliary_device_create - create a managed device on the auxiliary bus |
| 477 | + * @dev: parent device |
| 478 | + * @modname: module name used to create the auxiliary driver name. |
| 479 | + * @devname: auxiliary bus device name |
| 480 | + * @platform_data: auxiliary bus device platform data |
| 481 | + * @id: auxiliary bus device id |
| 482 | + * |
| 483 | + * Device managed helper to create an auxiliary bus device. |
| 484 | + * The device created matches driver 'modname.devname' on the auxiliary bus. |
| 485 | + */ |
| 486 | +struct auxiliary_device *__devm_auxiliary_device_create(struct device *dev, |
| 487 | + const char *modname, |
| 488 | + const char *devname, |
| 489 | + void *platform_data, |
| 490 | + int id) |
| 491 | +{ |
| 492 | + struct auxiliary_device *auxdev; |
| 493 | + int ret; |
| 494 | + |
| 495 | + auxdev = auxiliary_device_create(dev, modname, devname, platform_data, id); |
| 496 | + if (!auxdev) |
| 497 | + return NULL; |
| 498 | + |
| 499 | + ret = devm_add_action_or_reset(dev, auxiliary_device_destroy, |
| 500 | + auxdev); |
| 501 | + if (ret) |
| 502 | + return NULL; |
| 503 | + |
| 504 | + return auxdev; |
| 505 | +} |
| 506 | +EXPORT_SYMBOL_GPL(__devm_auxiliary_device_create); |
| 507 | + |
388 | 508 | void __init auxiliary_bus_init(void) |
389 | 509 | { |
390 | 510 | WARN_ON(bus_register(&auxiliary_bus_type)); |
|
0 commit comments