Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drivers: amba: properly handle devices with power domains

To read pid/cid registers, the probed device need to be properly turned on.
When it is inside a power domain, the bus code should ensure that the
given power domain is enabled before trying to access device's registers.
However in some cases power domain (or clocks) might not be yet available.
Returning -EPROBE_DEFER is not a solution in such case, because callers
don't handle this special error code. Instead such devices are added to the
special list and their registration is retried from periodic worker until
all resources are available.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Marek Szyprowski and committed by
Greg Kroah-Hartman
3385af8c 8d026465

+90 -10
+90 -10
drivers/amba/bus.c
··· 336 336 kfree(d); 337 337 } 338 338 339 - /** 340 - * amba_device_add - add a previously allocated AMBA device structure 341 - * @dev: AMBA device allocated by amba_device_alloc 342 - * @parent: resource parent for this devices resources 343 - * 344 - * Claim the resource, and read the device cell ID if not already 345 - * initialized. Register the AMBA device with the Linux device 346 - * manager. 347 - */ 348 - int amba_device_add(struct amba_device *dev, struct resource *parent) 339 + static int amba_device_try_add(struct amba_device *dev, struct resource *parent) 349 340 { 350 341 u32 size; 351 342 void __iomem *tmp; ··· 361 370 tmp = ioremap(dev->res.start, size); 362 371 if (!tmp) { 363 372 ret = -ENOMEM; 373 + goto err_release; 374 + } 375 + 376 + ret = dev_pm_domain_attach(&dev->dev, true); 377 + if (ret == -EPROBE_DEFER) { 378 + iounmap(tmp); 364 379 goto err_release; 365 380 } 366 381 ··· 395 398 } 396 399 397 400 iounmap(tmp); 401 + dev_pm_domain_detach(&dev->dev, true); 398 402 399 403 if (ret) 400 404 goto err_release; ··· 417 419 err_release: 418 420 release_resource(&dev->res); 419 421 err_out: 422 + return ret; 423 + } 424 + 425 + /* 426 + * Registration of AMBA device require reading its pid and cid registers. 427 + * To do this, the device must be turned on (if it is a part of power domain) 428 + * and have clocks enabled. However in some cases those resources might not be 429 + * yet available. Returning EPROBE_DEFER is not a solution in such case, 430 + * because callers don't handle this special error code. Instead such devices 431 + * are added to the special list and their registration is retried from 432 + * periodic worker, until all resources are available and registration succeeds. 433 + */ 434 + struct deferred_device { 435 + struct amba_device *dev; 436 + struct resource *parent; 437 + struct list_head node; 438 + }; 439 + 440 + static LIST_HEAD(deferred_devices); 441 + static DEFINE_MUTEX(deferred_devices_lock); 442 + 443 + static void amba_deferred_retry_func(struct work_struct *dummy); 444 + static DECLARE_DELAYED_WORK(deferred_retry_work, amba_deferred_retry_func); 445 + 446 + #define DEFERRED_DEVICE_TIMEOUT (msecs_to_jiffies(5 * 1000)) 447 + 448 + static void amba_deferred_retry_func(struct work_struct *dummy) 449 + { 450 + struct deferred_device *ddev, *tmp; 451 + 452 + mutex_lock(&deferred_devices_lock); 453 + 454 + list_for_each_entry_safe(ddev, tmp, &deferred_devices, node) { 455 + int ret = amba_device_try_add(ddev->dev, ddev->parent); 456 + 457 + if (ret == -EPROBE_DEFER) 458 + continue; 459 + 460 + list_del_init(&ddev->node); 461 + kfree(ddev); 462 + } 463 + 464 + if (!list_empty(&deferred_devices)) 465 + schedule_delayed_work(&deferred_retry_work, 466 + DEFERRED_DEVICE_TIMEOUT); 467 + 468 + mutex_unlock(&deferred_devices_lock); 469 + } 470 + 471 + /** 472 + * amba_device_add - add a previously allocated AMBA device structure 473 + * @dev: AMBA device allocated by amba_device_alloc 474 + * @parent: resource parent for this devices resources 475 + * 476 + * Claim the resource, and read the device cell ID if not already 477 + * initialized. Register the AMBA device with the Linux device 478 + * manager. 479 + */ 480 + int amba_device_add(struct amba_device *dev, struct resource *parent) 481 + { 482 + int ret = amba_device_try_add(dev, parent); 483 + 484 + if (ret == -EPROBE_DEFER) { 485 + struct deferred_device *ddev; 486 + 487 + ddev = kmalloc(sizeof(*ddev), GFP_KERNEL); 488 + if (!ddev) 489 + return -ENOMEM; 490 + 491 + ddev->dev = dev; 492 + ddev->parent = parent; 493 + ret = 0; 494 + 495 + mutex_lock(&deferred_devices_lock); 496 + 497 + if (list_empty(&deferred_devices)) 498 + schedule_delayed_work(&deferred_retry_work, 499 + DEFERRED_DEVICE_TIMEOUT); 500 + list_add_tail(&ddev->node, &deferred_devices); 501 + 502 + mutex_unlock(&deferred_devices_lock); 503 + } 420 504 return ret; 421 505 } 422 506 EXPORT_SYMBOL_GPL(amba_device_add);