diff options
author | Adrian Hunter <adrian.hunter@intel.com> | 2017-12-15 07:48:47 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2017-12-15 20:42:57 -0500 |
commit | e6ce0ce34f657795fef6536edd24df81d6f2ba81 (patch) | |
tree | ed34e71a4a15c8cb8c5b6daf8079b97e31512340 | |
parent | 50c4c4e268a2d7a3e58ebb698ac74da0de40ae36 (diff) |
ACPI / LPSS: Add device link for CHT SD card dependency on I2C
Some Cherry Trail boards have a dependency between the SDHCI host
controller used for SD cards and an external PMIC accessed via I2C. Add a
device link between the SDHCI host controller (consumer) and the I2C
adapter (supplier).
This patch depends on a fix to devices links, namely commit 0ff26c662d5f
("driver core: Fix device link deferred probe"). And also either,
commit 126dbc6b49c8 ("PM: i2c-designware-platdrv: Clean up PM handling in
probe"), or patch "PM / runtime: Fix handling of suppliers with disabled
runtime PM".
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/acpi/acpi_lpss.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 7f2b02cc8ea1..d78c57a95b86 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c | |||
@@ -427,6 +427,142 @@ out: | |||
427 | return 0; | 427 | return 0; |
428 | } | 428 | } |
429 | 429 | ||
430 | struct lpss_device_links { | ||
431 | const char *supplier_hid; | ||
432 | const char *supplier_uid; | ||
433 | const char *consumer_hid; | ||
434 | const char *consumer_uid; | ||
435 | u32 flags; | ||
436 | }; | ||
437 | |||
438 | /* | ||
439 | * The _DEP method is used to identify dependencies but instead of creating | ||
440 | * device links for every handle in _DEP, only links in the following list are | ||
441 | * created. That is necessary because, in the general case, _DEP can refer to | ||
442 | * devices that might not have drivers, or that are on different buses, or where | ||
443 | * the supplier is not enumerated until after the consumer is probed. | ||
444 | */ | ||
445 | static const struct lpss_device_links lpss_device_links[] = { | ||
446 | {"808622C1", "7", "80860F14", "3", DL_FLAG_PM_RUNTIME}, | ||
447 | }; | ||
448 | |||
449 | static bool hid_uid_match(const char *hid1, const char *uid1, | ||
450 | const char *hid2, const char *uid2) | ||
451 | { | ||
452 | return !strcmp(hid1, hid2) && uid1 && uid2 && !strcmp(uid1, uid2); | ||
453 | } | ||
454 | |||
455 | static bool acpi_lpss_is_supplier(struct acpi_device *adev, | ||
456 | const struct lpss_device_links *link) | ||
457 | { | ||
458 | return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev), | ||
459 | link->supplier_hid, link->supplier_uid); | ||
460 | } | ||
461 | |||
462 | static bool acpi_lpss_is_consumer(struct acpi_device *adev, | ||
463 | const struct lpss_device_links *link) | ||
464 | { | ||
465 | return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev), | ||
466 | link->consumer_hid, link->consumer_uid); | ||
467 | } | ||
468 | |||
469 | struct hid_uid { | ||
470 | const char *hid; | ||
471 | const char *uid; | ||
472 | }; | ||
473 | |||
474 | static int match_hid_uid(struct device *dev, void *data) | ||
475 | { | ||
476 | struct acpi_device *adev = ACPI_COMPANION(dev); | ||
477 | struct hid_uid *id = data; | ||
478 | |||
479 | if (!adev) | ||
480 | return 0; | ||
481 | |||
482 | return hid_uid_match(acpi_device_hid(adev), acpi_device_uid(adev), | ||
483 | id->hid, id->uid); | ||
484 | } | ||
485 | |||
486 | static struct device *acpi_lpss_find_device(const char *hid, const char *uid) | ||
487 | { | ||
488 | struct hid_uid data = { | ||
489 | .hid = hid, | ||
490 | .uid = uid, | ||
491 | }; | ||
492 | |||
493 | return bus_find_device(&platform_bus_type, NULL, &data, match_hid_uid); | ||
494 | } | ||
495 | |||
496 | static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle) | ||
497 | { | ||
498 | struct acpi_handle_list dep_devices; | ||
499 | acpi_status status; | ||
500 | int i; | ||
501 | |||
502 | if (!acpi_has_method(adev->handle, "_DEP")) | ||
503 | return false; | ||
504 | |||
505 | status = acpi_evaluate_reference(adev->handle, "_DEP", NULL, | ||
506 | &dep_devices); | ||
507 | if (ACPI_FAILURE(status)) { | ||
508 | dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n"); | ||
509 | return false; | ||
510 | } | ||
511 | |||
512 | for (i = 0; i < dep_devices.count; i++) { | ||
513 | if (dep_devices.handles[i] == handle) | ||
514 | return true; | ||
515 | } | ||
516 | |||
517 | return false; | ||
518 | } | ||
519 | |||
520 | static void acpi_lpss_link_consumer(struct device *dev1, | ||
521 | const struct lpss_device_links *link) | ||
522 | { | ||
523 | struct device *dev2; | ||
524 | |||
525 | dev2 = acpi_lpss_find_device(link->consumer_hid, link->consumer_uid); | ||
526 | if (!dev2) | ||
527 | return; | ||
528 | |||
529 | if (acpi_lpss_dep(ACPI_COMPANION(dev2), ACPI_HANDLE(dev1))) | ||
530 | device_link_add(dev2, dev1, link->flags); | ||
531 | |||
532 | put_device(dev2); | ||
533 | } | ||
534 | |||
535 | static void acpi_lpss_link_supplier(struct device *dev1, | ||
536 | const struct lpss_device_links *link) | ||
537 | { | ||
538 | struct device *dev2; | ||
539 | |||
540 | dev2 = acpi_lpss_find_device(link->supplier_hid, link->supplier_uid); | ||
541 | if (!dev2) | ||
542 | return; | ||
543 | |||
544 | if (acpi_lpss_dep(ACPI_COMPANION(dev1), ACPI_HANDLE(dev2))) | ||
545 | device_link_add(dev1, dev2, link->flags); | ||
546 | |||
547 | put_device(dev2); | ||
548 | } | ||
549 | |||
550 | static void acpi_lpss_create_device_links(struct acpi_device *adev, | ||
551 | struct platform_device *pdev) | ||
552 | { | ||
553 | int i; | ||
554 | |||
555 | for (i = 0; i < ARRAY_SIZE(lpss_device_links); i++) { | ||
556 | const struct lpss_device_links *link = &lpss_device_links[i]; | ||
557 | |||
558 | if (acpi_lpss_is_supplier(adev, link)) | ||
559 | acpi_lpss_link_consumer(&pdev->dev, link); | ||
560 | |||
561 | if (acpi_lpss_is_consumer(adev, link)) | ||
562 | acpi_lpss_link_supplier(&pdev->dev, link); | ||
563 | } | ||
564 | } | ||
565 | |||
430 | static int acpi_lpss_create_device(struct acpi_device *adev, | 566 | static int acpi_lpss_create_device(struct acpi_device *adev, |
431 | const struct acpi_device_id *id) | 567 | const struct acpi_device_id *id) |
432 | { | 568 | { |
@@ -500,6 +636,7 @@ static int acpi_lpss_create_device(struct acpi_device *adev, | |||
500 | adev->driver_data = pdata; | 636 | adev->driver_data = pdata; |
501 | pdev = acpi_create_platform_device(adev, dev_desc->properties); | 637 | pdev = acpi_create_platform_device(adev, dev_desc->properties); |
502 | if (!IS_ERR_OR_NULL(pdev)) { | 638 | if (!IS_ERR_OR_NULL(pdev)) { |
639 | acpi_lpss_create_device_links(adev, pdev); | ||
503 | return 1; | 640 | return 1; |
504 | } | 641 | } |
505 | 642 | ||