diff options
-rw-r--r-- | drivers/acpi/scan.c | 103 | ||||
-rw-r--r-- | include/acpi/acpi_bus.h | 1 |
2 files changed, 73 insertions, 31 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 53502d1bbf26..726f0d1ace4b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -494,7 +494,8 @@ static int acpi_bus_match(struct device *dev, struct device_driver *drv) | |||
494 | struct acpi_device *acpi_dev = to_acpi_device(dev); | 494 | struct acpi_device *acpi_dev = to_acpi_device(dev); |
495 | struct acpi_driver *acpi_drv = to_acpi_driver(drv); | 495 | struct acpi_driver *acpi_drv = to_acpi_driver(drv); |
496 | 496 | ||
497 | return !acpi_match_device_ids(acpi_dev, acpi_drv->ids); | 497 | return acpi_dev->bus_ops.acpi_op_match |
498 | && !acpi_match_device_ids(acpi_dev, acpi_drv->ids); | ||
498 | } | 499 | } |
499 | 500 | ||
500 | static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) | 501 | static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) |
@@ -1418,6 +1419,17 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) | |||
1418 | return 0; | 1419 | return 0; |
1419 | } | 1420 | } |
1420 | 1421 | ||
1422 | /* | ||
1423 | * acpi_hot_add_bind - Bind _ADR-based devices on hot-add. | ||
1424 | * @device: ACPI device node to bind. | ||
1425 | */ | ||
1426 | static void acpi_hot_add_bind(struct acpi_device *device) | ||
1427 | { | ||
1428 | if (device->flags.bus_address | ||
1429 | && device->parent && device->parent->ops.bind) | ||
1430 | device->parent->ops.bind(device); | ||
1431 | } | ||
1432 | |||
1421 | static int acpi_add_single_object(struct acpi_device **child, | 1433 | static int acpi_add_single_object(struct acpi_device **child, |
1422 | acpi_handle handle, int type, | 1434 | acpi_handle handle, int type, |
1423 | unsigned long long sta, | 1435 | unsigned long long sta, |
@@ -1490,13 +1502,8 @@ static int acpi_add_single_object(struct acpi_device **child, | |||
1490 | 1502 | ||
1491 | result = acpi_device_register(device); | 1503 | result = acpi_device_register(device); |
1492 | 1504 | ||
1493 | /* | 1505 | if (device->bus_ops.acpi_op_match) |
1494 | * Bind _ADR-Based Devices when hot add | 1506 | acpi_hot_add_bind(device); |
1495 | */ | ||
1496 | if (device->flags.bus_address) { | ||
1497 | if (device->parent && device->parent->ops.bind) | ||
1498 | device->parent->ops.bind(device); | ||
1499 | } | ||
1500 | 1507 | ||
1501 | end: | 1508 | end: |
1502 | if (!result) { | 1509 | if (!result) { |
@@ -1522,6 +1529,7 @@ static void acpi_bus_add_power_resource(acpi_handle handle) | |||
1522 | struct acpi_bus_ops ops = { | 1529 | struct acpi_bus_ops ops = { |
1523 | .acpi_op_add = 1, | 1530 | .acpi_op_add = 1, |
1524 | .acpi_op_start = 1, | 1531 | .acpi_op_start = 1, |
1532 | .acpi_op_match = 1, | ||
1525 | }; | 1533 | }; |
1526 | struct acpi_device *device = NULL; | 1534 | struct acpi_device *device = NULL; |
1527 | 1535 | ||
@@ -1574,9 +1582,9 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, | |||
1574 | void *context, void **return_value) | 1582 | void *context, void **return_value) |
1575 | { | 1583 | { |
1576 | struct acpi_bus_ops *ops = context; | 1584 | struct acpi_bus_ops *ops = context; |
1585 | struct acpi_device *device = NULL; | ||
1577 | int type; | 1586 | int type; |
1578 | unsigned long long sta; | 1587 | unsigned long long sta; |
1579 | struct acpi_device *device; | ||
1580 | acpi_status status; | 1588 | acpi_status status; |
1581 | int result; | 1589 | int result; |
1582 | 1590 | ||
@@ -1596,52 +1604,84 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, | |||
1596 | return AE_CTRL_DEPTH; | 1604 | return AE_CTRL_DEPTH; |
1597 | } | 1605 | } |
1598 | 1606 | ||
1599 | /* | ||
1600 | * We may already have an acpi_device from a previous enumeration. If | ||
1601 | * so, we needn't add it again, but we may still have to start it. | ||
1602 | */ | ||
1603 | device = NULL; | ||
1604 | acpi_bus_get_device(handle, &device); | 1607 | acpi_bus_get_device(handle, &device); |
1605 | if (ops->acpi_op_add && !device) { | 1608 | if (ops->acpi_op_add && !device) { |
1606 | acpi_add_single_object(&device, handle, type, sta, ops); | 1609 | struct acpi_bus_ops add_ops = *ops; |
1607 | /* Is the device a known good platform device? */ | ||
1608 | if (device | ||
1609 | && !acpi_match_device_ids(device, acpi_platform_device_ids)) | ||
1610 | acpi_create_platform_device(device); | ||
1611 | } | ||
1612 | 1610 | ||
1613 | if (!device) | 1611 | add_ops.acpi_op_match = 0; |
1614 | return AE_CTRL_DEPTH; | 1612 | acpi_add_single_object(&device, handle, type, sta, &add_ops); |
1615 | 1613 | if (!device) | |
1616 | if (ops->acpi_op_start && !(ops->acpi_op_add)) { | ||
1617 | status = acpi_start_single_object(device); | ||
1618 | if (ACPI_FAILURE(status)) | ||
1619 | return AE_CTRL_DEPTH; | 1614 | return AE_CTRL_DEPTH; |
1615 | |||
1616 | device->bus_ops.acpi_op_match = 1; | ||
1617 | acpi_hot_add_bind(device); | ||
1620 | } | 1618 | } |
1621 | 1619 | ||
1622 | if (!*return_value) | 1620 | if (!*return_value) |
1623 | *return_value = device; | 1621 | *return_value = device; |
1622 | |||
1624 | return AE_OK; | 1623 | return AE_OK; |
1625 | } | 1624 | } |
1626 | 1625 | ||
1626 | static acpi_status acpi_bus_probe_start(acpi_handle handle, u32 lvl, | ||
1627 | void *context, void **not_used) | ||
1628 | { | ||
1629 | struct acpi_bus_ops *ops = context; | ||
1630 | acpi_status status = AE_OK; | ||
1631 | struct acpi_device *device; | ||
1632 | unsigned long long sta_not_used; | ||
1633 | int type_not_used; | ||
1634 | |||
1635 | /* | ||
1636 | * Ignore errors ignored by acpi_bus_check_add() to avoid terminating | ||
1637 | * namespace walks prematurely. | ||
1638 | */ | ||
1639 | if (acpi_bus_type_and_status(handle, &type_not_used, &sta_not_used)) | ||
1640 | return AE_OK; | ||
1641 | |||
1642 | if (acpi_bus_get_device(handle, &device)) | ||
1643 | return AE_CTRL_DEPTH; | ||
1644 | |||
1645 | if (ops->acpi_op_add) { | ||
1646 | if (!acpi_match_device_ids(device, acpi_platform_device_ids)) { | ||
1647 | /* This is a known good platform device. */ | ||
1648 | acpi_create_platform_device(device); | ||
1649 | } else if (device_attach(&device->dev)) { | ||
1650 | status = AE_CTRL_DEPTH; | ||
1651 | } | ||
1652 | } else if (ops->acpi_op_start) { | ||
1653 | if (ACPI_FAILURE(acpi_start_single_object(device))) | ||
1654 | status = AE_CTRL_DEPTH; | ||
1655 | } | ||
1656 | return status; | ||
1657 | } | ||
1658 | |||
1627 | static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops, | 1659 | static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops, |
1628 | struct acpi_device **child) | 1660 | struct acpi_device **child) |
1629 | { | 1661 | { |
1630 | acpi_status status; | ||
1631 | void *device = NULL; | 1662 | void *device = NULL; |
1663 | acpi_status status; | ||
1664 | int ret = -ENODEV; | ||
1632 | 1665 | ||
1633 | status = acpi_bus_check_add(handle, 0, ops, &device); | 1666 | status = acpi_bus_check_add(handle, 0, ops, &device); |
1634 | if (ACPI_SUCCESS(status)) | 1667 | if (ACPI_SUCCESS(status)) |
1635 | acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, | 1668 | acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, |
1636 | acpi_bus_check_add, NULL, ops, &device); | 1669 | acpi_bus_check_add, NULL, ops, &device); |
1637 | 1670 | ||
1671 | if (!device) | ||
1672 | goto out; | ||
1673 | |||
1674 | ret = 0; | ||
1675 | status = acpi_bus_probe_start(handle, 0, ops, NULL); | ||
1676 | if (ACPI_SUCCESS(status)) | ||
1677 | acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, | ||
1678 | acpi_bus_probe_start, NULL, ops, NULL); | ||
1679 | |||
1680 | out: | ||
1638 | if (child) | 1681 | if (child) |
1639 | *child = device; | 1682 | *child = device; |
1640 | 1683 | ||
1641 | if (device) | 1684 | return ret; |
1642 | return 0; | ||
1643 | else | ||
1644 | return -ENODEV; | ||
1645 | } | 1685 | } |
1646 | 1686 | ||
1647 | /* | 1687 | /* |
@@ -1752,6 +1792,7 @@ static int acpi_bus_scan_fixed(void) | |||
1752 | memset(&ops, 0, sizeof(ops)); | 1792 | memset(&ops, 0, sizeof(ops)); |
1753 | ops.acpi_op_add = 1; | 1793 | ops.acpi_op_add = 1; |
1754 | ops.acpi_op_start = 1; | 1794 | ops.acpi_op_start = 1; |
1795 | ops.acpi_op_match = 1; | ||
1755 | 1796 | ||
1756 | /* | 1797 | /* |
1757 | * Enumerate all fixed-feature devices. | 1798 | * Enumerate all fixed-feature devices. |
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 7ced5dc20dd3..016918c16c98 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h | |||
@@ -98,6 +98,7 @@ typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event); | |||
98 | struct acpi_bus_ops { | 98 | struct acpi_bus_ops { |
99 | u32 acpi_op_add:1; | 99 | u32 acpi_op_add:1; |
100 | u32 acpi_op_start:1; | 100 | u32 acpi_op_start:1; |
101 | u32 acpi_op_match:1; | ||
101 | }; | 102 | }; |
102 | 103 | ||
103 | struct acpi_device_ops { | 104 | struct acpi_device_ops { |