aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/acpi/scan.c103
-rw-r--r--include/acpi/acpi_bus.h1
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)
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_driver *acpi_drv = to_acpi_driver(drv);
- return !acpi_match_device_ids(acpi_dev, acpi_drv->ids);
+ return acpi_dev->bus_ops.acpi_op_match
+ && !acpi_match_device_ids(acpi_dev, acpi_drv->ids);
}
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)
return 0;
}
+/*
+ * acpi_hot_add_bind - Bind _ADR-based devices on hot-add.
+ * @device: ACPI device node to bind.
+ */
+static void acpi_hot_add_bind(struct acpi_device *device)
+{
+ if (device->flags.bus_address
+ && device->parent && device->parent->ops.bind)
+ device->parent->ops.bind(device);
+}
+
static int acpi_add_single_object(struct acpi_device **child,
acpi_handle handle, int type,
unsigned long long sta,
@@ -1490,13 +1502,8 @@ static int acpi_add_single_object(struct acpi_device **child,
result = acpi_device_register(device);
- /*
- * Bind _ADR-Based Devices when hot add
- */
- if (device->flags.bus_address) {
- if (device->parent && device->parent->ops.bind)
- device->parent->ops.bind(device);
- }
+ if (device->bus_ops.acpi_op_match)
+ acpi_hot_add_bind(device);
end:
if (!result) {
@@ -1522,6 +1529,7 @@ static void acpi_bus_add_power_resource(acpi_handle handle)
struct acpi_bus_ops ops = {
.acpi_op_add = 1,
.acpi_op_start = 1,
+ .acpi_op_match = 1,
};
struct acpi_device *device = NULL;
@@ -1574,9 +1582,9 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl,
void *context, void **return_value)
{
struct acpi_bus_ops *ops = context;
+ struct acpi_device *device = NULL;
int type;
unsigned long long sta;
- struct acpi_device *device;
acpi_status status;
int result;
@@ -1596,52 +1604,84 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl,
return AE_CTRL_DEPTH;
}
- /*
- * We may already have an acpi_device from a previous enumeration. If
- * so, we needn't add it again, but we may still have to start it.
- */
- device = NULL;
acpi_bus_get_device(handle, &device);
if (ops->acpi_op_add && !device) {
- acpi_add_single_object(&device, handle, type, sta, ops);
- /* Is the device a known good platform device? */
- if (device
- && !acpi_match_device_ids(device, acpi_platform_device_ids))
- acpi_create_platform_device(device);
- }
+ struct acpi_bus_ops add_ops = *ops;
- if (!device)
- return AE_CTRL_DEPTH;
-
- if (ops->acpi_op_start && !(ops->acpi_op_add)) {
- status = acpi_start_single_object(device);
- if (ACPI_FAILURE(status))
+ add_ops.acpi_op_match = 0;
+ acpi_add_single_object(&device, handle, type, sta, &add_ops);
+ if (!device)
return AE_CTRL_DEPTH;
+
+ device->bus_ops.acpi_op_match = 1;
+ acpi_hot_add_bind(device);
}
if (!*return_value)
*return_value = device;
+
return AE_OK;
}
+static acpi_status acpi_bus_probe_start(acpi_handle handle, u32 lvl,
+ void *context, void **not_used)
+{
+ struct acpi_bus_ops *ops = context;
+ acpi_status status = AE_OK;
+ struct acpi_device *device;
+ unsigned long long sta_not_used;
+ int type_not_used;
+
+ /*
+ * Ignore errors ignored by acpi_bus_check_add() to avoid terminating
+ * namespace walks prematurely.
+ */
+ if (acpi_bus_type_and_status(handle, &type_not_used, &sta_not_used))
+ return AE_OK;
+
+ if (acpi_bus_get_device(handle, &device))
+ return AE_CTRL_DEPTH;
+
+ if (ops->acpi_op_add) {
+ if (!acpi_match_device_ids(device, acpi_platform_device_ids)) {
+ /* This is a known good platform device. */
+ acpi_create_platform_device(device);
+ } else if (device_attach(&device->dev)) {
+ status = AE_CTRL_DEPTH;
+ }
+ } else if (ops->acpi_op_start) {
+ if (ACPI_FAILURE(acpi_start_single_object(device)))
+ status = AE_CTRL_DEPTH;
+ }
+ return status;
+}
+
static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops,
struct acpi_device **child)
{
- acpi_status status;
void *device = NULL;
+ acpi_status status;
+ int ret = -ENODEV;
status = acpi_bus_check_add(handle, 0, ops, &device);
if (ACPI_SUCCESS(status))
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
acpi_bus_check_add, NULL, ops, &device);
+ if (!device)
+ goto out;
+
+ ret = 0;
+ status = acpi_bus_probe_start(handle, 0, ops, NULL);
+ if (ACPI_SUCCESS(status))
+ acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
+ acpi_bus_probe_start, NULL, ops, NULL);
+
+ out:
if (child)
*child = device;
- if (device)
- return 0;
- else
- return -ENODEV;
+ return ret;
}
/*
@@ -1752,6 +1792,7 @@ static int acpi_bus_scan_fixed(void)
memset(&ops, 0, sizeof(ops));
ops.acpi_op_add = 1;
ops.acpi_op_start = 1;
+ ops.acpi_op_match = 1;
/*
* 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);
struct acpi_bus_ops {
u32 acpi_op_add:1;
u32 acpi_op_start:1;
+ u32 acpi_op_match:1;
};
struct acpi_device_ops {