diff options
Diffstat (limited to 'drivers/acpi/scan.c')
| -rw-r--r-- | drivers/acpi/scan.c | 963 | 
1 files changed, 599 insertions, 364 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index fbdb82e70d1..f775fa0d850 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -12,13 +12,12 @@  #include <linux/dmi.h>  #include <linux/nls.h> -#include <acpi/acpi_drivers.h> +#include <asm/pgtable.h>  #include "internal.h"  #define _COMPONENT		ACPI_BUS_COMPONENT  ACPI_MODULE_NAME("scan"); -#define STRUCT_TO_INT(s)	(*((int*)&s))  extern struct acpi_device *acpi_root;  #define ACPI_BUS_CLASS			"system_bus" @@ -27,6 +26,8 @@ extern struct acpi_device *acpi_root;  #define ACPI_IS_ROOT_DEVICE(device)    (!(device)->parent) +#define INVALID_ACPI_HANDLE	((acpi_handle)empty_zero_page) +  /*   * If set, devices will be hot-removed even if they cannot be put offline   * gracefully (from the kernel's standpoint). @@ -40,6 +41,7 @@ static DEFINE_MUTEX(acpi_scan_lock);  static LIST_HEAD(acpi_scan_handlers_list);  DEFINE_MUTEX(acpi_device_lock);  LIST_HEAD(acpi_wakeup_device_list); +static DEFINE_MUTEX(acpi_hp_context_lock);  struct acpi_device_bus_id{  	char bus_id[15]; @@ -59,9 +61,30 @@ void acpi_scan_lock_release(void)  }  EXPORT_SYMBOL_GPL(acpi_scan_lock_release); +void acpi_lock_hp_context(void) +{ +	mutex_lock(&acpi_hp_context_lock); +} + +void acpi_unlock_hp_context(void) +{ +	mutex_unlock(&acpi_hp_context_lock); +} + +void acpi_initialize_hp_context(struct acpi_device *adev, +				struct acpi_hotplug_context *hp, +				int (*notify)(struct acpi_device *, u32), +				void (*uevent)(struct acpi_device *, u32)) +{ +	acpi_lock_hp_context(); +	acpi_set_hp_context(adev, hp, notify, uevent, NULL); +	acpi_unlock_hp_context(); +} +EXPORT_SYMBOL_GPL(acpi_initialize_hp_context); +  int acpi_scan_add_handler(struct acpi_scan_handler *handler)  { -	if (!handler || !handler->attach) +	if (!handler)  		return -EINVAL;  	list_add_tail(&handler->list_node, &acpi_scan_handlers_list); @@ -85,6 +108,9 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,   * Creates hid/cid(s) string needed for modalias and uevent   * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:   * char *modalias: "acpi:IBM0001:ACPI0001" + * Return: 0: no _HID and no _CID + *         -EINVAL: output error + *         -ENOMEM: output is truncated  */  static int create_modalias(struct acpi_device *acpi_dev, char *modalias,  			   int size) @@ -101,8 +127,10 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,  	list_for_each_entry(id, &acpi_dev->pnp.ids, list) {  		count = snprintf(&modalias[len], size, "%s:", id->id); -		if (count < 0 || count >= size) -			return -EINVAL; +		if (count < 0) +			return EINVAL; +		if (count >= size) +			return -ENOMEM;  		len += count;  		size -= count;  	} @@ -111,22 +139,98 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,  	return len;  } +/* + * Creates uevent modalias field for ACPI enumerated devices. + * Because the other buses does not support ACPI HIDs & CIDs. + * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: + * "acpi:IBM0001:ACPI0001" + */ +int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) +{ +	struct acpi_device *acpi_dev; +	int len; + +	acpi_dev = ACPI_COMPANION(dev); +	if (!acpi_dev) +		return -ENODEV; + +	/* Fall back to bus specific way of modalias exporting */ +	if (list_empty(&acpi_dev->pnp.ids)) +		return -ENODEV; + +	if (add_uevent_var(env, "MODALIAS=")) +		return -ENOMEM; +	len = create_modalias(acpi_dev, &env->buf[env->buflen - 1], +				sizeof(env->buf) - env->buflen); +	if (len <= 0) +		return len; +	env->buflen += len; +	return 0; +} +EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); + +/* + * Creates modalias sysfs attribute for ACPI enumerated devices. + * Because the other buses does not support ACPI HIDs & CIDs. + * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: + * "acpi:IBM0001:ACPI0001" + */ +int acpi_device_modalias(struct device *dev, char *buf, int size) +{ +	struct acpi_device *acpi_dev; +	int len; + +	acpi_dev = ACPI_COMPANION(dev); +	if (!acpi_dev) +		return -ENODEV; + +	/* Fall back to bus specific way of modalias exporting */ +	if (list_empty(&acpi_dev->pnp.ids)) +		return -ENODEV; + +	len = create_modalias(acpi_dev, buf, size -1); +	if (len <= 0) +		return len; +	buf[len++] = '\n'; +	return len; +} +EXPORT_SYMBOL_GPL(acpi_device_modalias); +  static ssize_t  acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) {  	struct acpi_device *acpi_dev = to_acpi_device(dev);  	int len; -	/* Device has no HID and no CID or string is >1024 */  	len = create_modalias(acpi_dev, buf, 1024);  	if (len <= 0) -		return 0; +		return len;  	buf[len++] = '\n';  	return len;  }  static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl, -					       void *data, void **ret_p) +bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) +{ +	struct acpi_device_physical_node *pn; +	bool offline = true; + +	mutex_lock(&adev->physical_node_lock); + +	list_for_each_entry(pn, &adev->physical_node_list, node) +		if (device_supports_offline(pn->dev) && !pn->dev->offline) { +			if (uevent) +				kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE); + +			offline = false; +			break; +		} + +	mutex_unlock(&adev->physical_node_lock); +	return offline; +} + +static acpi_status acpi_bus_offline(acpi_handle handle, u32 lvl, void *data, +				    void **ret_p)  {  	struct acpi_device *device = NULL;  	struct acpi_device_physical_node *pn; @@ -136,6 +240,11 @@ static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl,  	if (acpi_bus_get_device(handle, &device))  		return AE_OK; +	if (device->handler && !device->handler->hotplug.enabled) { +		*ret_p = &device->dev; +		return AE_SUPPORT; +	} +  	mutex_lock(&device->physical_node_lock);  	list_for_each_entry(pn, &device->physical_node_list, node) { @@ -168,8 +277,8 @@ static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl,  	return status;  } -static acpi_status acpi_bus_online_companions(acpi_handle handle, u32 lvl, -					      void *data, void **ret_p) +static acpi_status acpi_bus_online(acpi_handle handle, u32 lvl, void *data, +				   void **ret_p)  {  	struct acpi_device *device = NULL;  	struct acpi_device_physical_node *pn; @@ -190,19 +299,11 @@ static acpi_status acpi_bus_online_companions(acpi_handle handle, u32 lvl,  	return AE_OK;  } -static int acpi_scan_hot_remove(struct acpi_device *device) +static int acpi_scan_try_to_offline(struct acpi_device *device)  {  	acpi_handle handle = device->handle; -	struct device *errdev; +	struct device *errdev = NULL;  	acpi_status status; -	unsigned long long sta; - -	/* If there is no handle, the device node has been unregistered. */ -	if (!handle) { -		dev_dbg(&device->dev, "ACPI handle missing\n"); -		put_device(&device->dev); -		return -EINVAL; -	}  	/*  	 * Carry out two passes here and ignore errors in the first pass, @@ -213,41 +314,57 @@ static int acpi_scan_hot_remove(struct acpi_device *device)  	 *  	 * If the first pass is successful, the second one isn't needed, though.  	 */ -	errdev = NULL; -	acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, -			    NULL, acpi_bus_offline_companions, -			    (void *)false, (void **)&errdev); -	acpi_bus_offline_companions(handle, 0, (void *)false, (void **)&errdev); +	status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, +				     NULL, acpi_bus_offline, (void *)false, +				     (void **)&errdev); +	if (status == AE_SUPPORT) { +		dev_warn(errdev, "Offline disabled.\n"); +		acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, +				    acpi_bus_online, NULL, NULL, NULL); +		return -EPERM; +	} +	acpi_bus_offline(handle, 0, (void *)false, (void **)&errdev);  	if (errdev) {  		errdev = NULL;  		acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, -				    NULL, acpi_bus_offline_companions, -				    (void *)true , (void **)&errdev); +				    NULL, acpi_bus_offline, (void *)true, +				    (void **)&errdev);  		if (!errdev || acpi_force_hot_remove) -			acpi_bus_offline_companions(handle, 0, (void *)true, -						    (void **)&errdev); +			acpi_bus_offline(handle, 0, (void *)true, +					 (void **)&errdev);  		if (errdev && !acpi_force_hot_remove) {  			dev_warn(errdev, "Offline failed.\n"); -			acpi_bus_online_companions(handle, 0, NULL, NULL); +			acpi_bus_online(handle, 0, NULL, NULL);  			acpi_walk_namespace(ACPI_TYPE_ANY, handle, -					    ACPI_UINT32_MAX, -					    acpi_bus_online_companions, NULL, -					    NULL, NULL); -			put_device(&device->dev); +					    ACPI_UINT32_MAX, acpi_bus_online, +					    NULL, NULL, NULL);  			return -EBUSY;  		}  	} +	return 0; +} + +static int acpi_scan_hot_remove(struct acpi_device *device) +{ +	acpi_handle handle = device->handle; +	unsigned long long sta; +	acpi_status status; + +	if (device->handler->hotplug.demand_offline && !acpi_force_hot_remove) { +		if (!acpi_scan_is_offline(device, true)) +			return -EBUSY; +	} else { +		int error = acpi_scan_try_to_offline(device); +		if (error) +			return error; +	}  	ACPI_DEBUG_PRINT((ACPI_DB_INFO,  		"Hot-removing device %s...\n", dev_name(&device->dev)));  	acpi_bus_trim(device); -	/* Device node has been unregistered. */ -	put_device(&device->dev); -	device = NULL; -  	acpi_evaluate_lck(handle, 0);  	/*  	 * TBD: _EJD support. @@ -274,187 +391,146 @@ static int acpi_scan_hot_remove(struct acpi_device *device)  	return 0;  } -static void acpi_bus_device_eject(void *context) +static int acpi_scan_device_not_present(struct acpi_device *adev)  { -	acpi_handle handle = context; -	struct acpi_device *device = NULL; -	struct acpi_scan_handler *handler; -	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; -	int error; - -	lock_device_hotplug(); -	mutex_lock(&acpi_scan_lock); - -	acpi_bus_get_device(handle, &device); -	if (!device) -		goto err_out; - -	handler = device->handler; -	if (!handler || !handler->hotplug.enabled) { -		ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; -		goto err_out; +	if (!acpi_device_enumerated(adev)) { +		dev_warn(&adev->dev, "Still not present\n"); +		return -EALREADY;  	} -	acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, -				  ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); -	if (handler->hotplug.mode == AHM_CONTAINER) -		kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); - -	get_device(&device->dev); -	error = acpi_scan_hot_remove(device); -	if (error) -		goto err_out; - - out: -	mutex_unlock(&acpi_scan_lock); -	unlock_device_hotplug(); -	return; - - err_out: -	acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, ost_code, -				  NULL); -	goto out; +	acpi_bus_trim(adev); +	return 0;  } -static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source) +static int acpi_scan_device_check(struct acpi_device *adev)  { -	struct acpi_device *device = NULL; -	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;  	int error; -	lock_device_hotplug(); -	mutex_lock(&acpi_scan_lock); - -	if (ost_source != ACPI_NOTIFY_BUS_CHECK) { -		acpi_bus_get_device(handle, &device); -		if (device) { -			dev_warn(&device->dev, "Attempt to re-insert\n"); -			goto out; +	acpi_bus_get_status(adev); +	if (adev->status.present || adev->status.functional) { +		/* +		 * This function is only called for device objects for which +		 * matching scan handlers exist.  The only situation in which +		 * the scan handler is not attached to this device object yet +		 * is when the device has just appeared (either it wasn't +		 * present at all before or it was removed and then added +		 * again). +		 */ +		if (adev->handler) { +			dev_warn(&adev->dev, "Already enumerated\n"); +			return -EALREADY;  		} +		error = acpi_bus_scan(adev->handle); +		if (error) { +			dev_warn(&adev->dev, "Namespace scan failure\n"); +			return error; +		} +		if (!adev->handler) { +			dev_warn(&adev->dev, "Enumeration failure\n"); +			error = -ENODEV; +		} +	} else { +		error = acpi_scan_device_not_present(adev);  	} -	acpi_evaluate_hotplug_ost(handle, ost_source, -				  ACPI_OST_SC_INSERT_IN_PROGRESS, NULL); -	error = acpi_bus_scan(handle); -	if (error) { -		acpi_handle_warn(handle, "Namespace scan failure\n"); -		goto out; -	} -	error = acpi_bus_get_device(handle, &device); -	if (error) { -		acpi_handle_warn(handle, "Missing device node object\n"); -		goto out; -	} -	ost_code = ACPI_OST_SC_SUCCESS; -	if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER) -		kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); - - out: -	acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL); -	mutex_unlock(&acpi_scan_lock); -	unlock_device_hotplug(); -} - -static void acpi_scan_bus_check(void *context) -{ -	acpi_scan_bus_device_check((acpi_handle)context, -				   ACPI_NOTIFY_BUS_CHECK); -} - -static void acpi_scan_device_check(void *context) -{ -	acpi_scan_bus_device_check((acpi_handle)context, -				   ACPI_NOTIFY_DEVICE_CHECK); +	return error;  } -static void acpi_hotplug_unsupported(acpi_handle handle, u32 type) +static int acpi_scan_bus_check(struct acpi_device *adev)  { -	u32 ost_status; +	struct acpi_scan_handler *handler = adev->handler; +	struct acpi_device *child; +	int error; -	switch (type) { -	case ACPI_NOTIFY_BUS_CHECK: -		acpi_handle_debug(handle, -			"ACPI_NOTIFY_BUS_CHECK event: unsupported\n"); -		ost_status = ACPI_OST_SC_INSERT_NOT_SUPPORTED; -		break; -	case ACPI_NOTIFY_DEVICE_CHECK: -		acpi_handle_debug(handle, -			"ACPI_NOTIFY_DEVICE_CHECK event: unsupported\n"); -		ost_status = ACPI_OST_SC_INSERT_NOT_SUPPORTED; -		break; -	case ACPI_NOTIFY_EJECT_REQUEST: -		acpi_handle_debug(handle, -			"ACPI_NOTIFY_EJECT_REQUEST event: unsupported\n"); -		ost_status = ACPI_OST_SC_EJECT_NOT_SUPPORTED; -		break; -	default: -		/* non-hotplug event; possibly handled by other handler */ -		return; +	acpi_bus_get_status(adev); +	if (!(adev->status.present || adev->status.functional)) { +		acpi_scan_device_not_present(adev); +		return 0;  	} +	if (handler && handler->hotplug.scan_dependent) +		return handler->hotplug.scan_dependent(adev); -	acpi_evaluate_hotplug_ost(handle, type, ost_status, NULL); +	error = acpi_bus_scan(adev->handle); +	if (error) { +		dev_warn(&adev->dev, "Namespace scan failure\n"); +		return error; +	} +	list_for_each_entry(child, &adev->children, node) { +		error = acpi_scan_bus_check(child); +		if (error) +			return error; +	} +	return 0;  } -static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data) +static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)  { -	acpi_osd_exec_callback callback; -	struct acpi_scan_handler *handler = data; -	acpi_status status; - -	if (!handler->hotplug.enabled) -		return acpi_hotplug_unsupported(handle, type); -  	switch (type) {  	case ACPI_NOTIFY_BUS_CHECK: -		acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n"); -		callback = acpi_scan_bus_check; -		break; +		return acpi_scan_bus_check(adev);  	case ACPI_NOTIFY_DEVICE_CHECK: -		acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n"); -		callback = acpi_scan_device_check; -		break; +		return acpi_scan_device_check(adev);  	case ACPI_NOTIFY_EJECT_REQUEST: -		acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n"); -		callback = acpi_bus_device_eject; -		break; -	default: -		/* non-hotplug event; possibly handled by other handler */ -		return; +	case ACPI_OST_EC_OSPM_EJECT: +		if (adev->handler && !adev->handler->hotplug.enabled) { +			dev_info(&adev->dev, "Eject disabled\n"); +			return -EPERM; +		} +		acpi_evaluate_ost(adev->handle, ACPI_NOTIFY_EJECT_REQUEST, +				  ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); +		return acpi_scan_hot_remove(adev);  	} -	status = acpi_os_hotplug_execute(callback, handle); -	if (ACPI_FAILURE(status)) -		acpi_evaluate_hotplug_ost(handle, type, -					  ACPI_OST_SC_NON_SPECIFIC_FAILURE, -					  NULL); +	return -EINVAL;  } -/** - * acpi_bus_hot_remove_device: hot-remove a device and its children - * @context: struct acpi_eject_event pointer (freed in this func) - * - * Hot-remove a device and its children. This function frees up the - * memory space passed by arg context, so that the caller may call - * this function asynchronously through acpi_os_hotplug_execute(). - */ -void acpi_bus_hot_remove_device(void *context) +void acpi_device_hotplug(struct acpi_device *adev, u32 src)  { -	struct acpi_eject_event *ej_event = context; -	struct acpi_device *device = ej_event->device; -	acpi_handle handle = device->handle; -	int error; +	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; +	int error = -ENODEV;  	lock_device_hotplug();  	mutex_lock(&acpi_scan_lock); -	error = acpi_scan_hot_remove(device); -	if (error && handle) -		acpi_evaluate_hotplug_ost(handle, ej_event->event, -					  ACPI_OST_SC_NON_SPECIFIC_FAILURE, -					  NULL); +	/* +	 * The device object's ACPI handle cannot become invalid as long as we +	 * are holding acpi_scan_lock, but it might have become invalid before +	 * that lock was acquired. +	 */ +	if (adev->handle == INVALID_ACPI_HANDLE) +		goto err_out; + +	if (adev->flags.is_dock_station) { +		error = dock_notify(adev, src); +	} else if (adev->flags.hotplug_notify) { +		error = acpi_generic_hotplug_event(adev, src); +		if (error == -EPERM) { +			ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; +			goto err_out; +		} +	} else { +		int (*notify)(struct acpi_device *, u32); + +		acpi_lock_hp_context(); +		notify = adev->hp ? adev->hp->notify : NULL; +		acpi_unlock_hp_context(); +		/* +		 * There may be additional notify handlers for device objects +		 * without the .event() callback, so ignore them here. +		 */ +		if (notify) +			error = notify(adev, src); +		else +			goto out; +	} +	if (!error) +		ost_code = ACPI_OST_SC_SUCCESS; + err_out: +	acpi_evaluate_ost(adev->handle, src, ost_code, NULL); + + out: +	acpi_bus_put_acpi_device(adev);  	mutex_unlock(&acpi_scan_lock);  	unlock_device_hotplug(); -	kfree(context);  } -EXPORT_SYMBOL(acpi_bus_hot_remove_device);  static ssize_t real_power_state_show(struct device *dev,  				     struct device_attribute *attr, char *buf) @@ -487,10 +563,8 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,  		const char *buf, size_t count)  {  	struct acpi_device *acpi_device = to_acpi_device(d); -	struct acpi_eject_event *ej_event;  	acpi_object_type not_used;  	acpi_status status; -	int ret;  	if (!count || buf[0] != '1')  		return -EINVAL; @@ -503,28 +577,15 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,  	if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable)  		return -ENODEV; -	ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); -	if (!ej_event) { -		ret = -ENOMEM; -		goto err_out; -	} -	acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, -				  ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); -	ej_event->device = acpi_device; -	ej_event->event = ACPI_OST_EC_OSPM_EJECT;  	get_device(&acpi_device->dev); -	status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event); +	status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT);  	if (ACPI_SUCCESS(status))  		return count;  	put_device(&acpi_device->dev); -	kfree(ej_event); -	ret = status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN; - - err_out: -	acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, -				  ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); -	return ret; +	acpi_evaluate_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, +			  ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); +	return status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN;  }  static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); @@ -608,6 +669,20 @@ acpi_device_sun_show(struct device *dev, struct device_attribute *attr,  }  static DEVICE_ATTR(sun, 0444, acpi_device_sun_show, NULL); +static ssize_t status_show(struct device *dev, struct device_attribute *attr, +				char *buf) { +	struct acpi_device *acpi_dev = to_acpi_device(dev); +	acpi_status status; +	unsigned long long sta; + +	status = acpi_evaluate_integer(acpi_dev->handle, "_STA", NULL, &sta); +	if (ACPI_FAILURE(status)) +		return -ENODEV; + +	return sprintf(buf, "%llu\n", sta); +} +static DEVICE_ATTR_RO(status); +  static int acpi_device_setup_files(struct acpi_device *dev)  {  	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; @@ -663,6 +738,12 @@ static int acpi_device_setup_files(struct acpi_device *dev)  		dev->pnp.sun = (unsigned long)-1;  	} +	if (acpi_has_method(dev->handle, "_STA")) { +		result = device_create_file(&dev->dev, &dev_attr_status); +		if (result) +			goto end; +	} +          /*           * If device has _EJ0, 'eject' file is created that is used to trigger           * hot-removal function from userland. @@ -718,6 +799,8 @@ static void acpi_device_remove_files(struct acpi_device *dev)  		device_remove_file(&dev->dev, &dev_attr_adr);  	device_remove_file(&dev->dev, &dev_attr_modalias);  	device_remove_file(&dev->dev, &dev_attr_hid); +	if (acpi_has_method(dev->handle, "_STA")) +		device_remove_file(&dev->dev, &dev_attr_status);  	if (dev->handle)  		device_remove_file(&dev->dev, &dev_attr_path);  } @@ -823,8 +906,8 @@ static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)  		return -ENOMEM;  	len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],  			      sizeof(env->buf) - env->buflen); -	if (len >= (sizeof(env->buf) - env->buflen)) -		return -ENOMEM; +	if (len <= 0) +		return len;  	env->buflen += len;  	return 0;  } @@ -948,19 +1031,103 @@ struct bus_type acpi_bus_type = {  	.uevent		= acpi_device_uevent,  }; -static void acpi_bus_data_handler(acpi_handle handle, void *context) +static void acpi_device_del(struct acpi_device *device)  { -	/* Intentionally empty. */ +	mutex_lock(&acpi_device_lock); +	if (device->parent) +		list_del(&device->node); + +	list_del(&device->wakeup_list); +	mutex_unlock(&acpi_device_lock); + +	acpi_power_add_remove_device(device, false); +	acpi_device_remove_files(device); +	if (device->remove) +		device->remove(device); + +	device_del(&device->dev);  } -int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) +static LIST_HEAD(acpi_device_del_list); +static DEFINE_MUTEX(acpi_device_del_lock); + +static void acpi_device_del_work_fn(struct work_struct *work_not_used) +{ +	for (;;) { +		struct acpi_device *adev; + +		mutex_lock(&acpi_device_del_lock); + +		if (list_empty(&acpi_device_del_list)) { +			mutex_unlock(&acpi_device_del_lock); +			break; +		} +		adev = list_first_entry(&acpi_device_del_list, +					struct acpi_device, del_list); +		list_del(&adev->del_list); + +		mutex_unlock(&acpi_device_del_lock); + +		acpi_device_del(adev); +		/* +		 * Drop references to all power resources that might have been +		 * used by the device. +		 */ +		acpi_power_transition(adev, ACPI_STATE_D3_COLD); +		put_device(&adev->dev); +	} +} + +/** + * acpi_scan_drop_device - Drop an ACPI device object. + * @handle: Handle of an ACPI namespace node, not used. + * @context: Address of the ACPI device object to drop. + * + * This is invoked by acpi_ns_delete_node() during the removal of the ACPI + * namespace node the device object pointed to by @context is attached to. + * + * The unregistration is carried out asynchronously to avoid running + * acpi_device_del() under the ACPICA's namespace mutex and the list is used to + * ensure the correct ordering (the device objects must be unregistered in the + * same order in which the corresponding namespace nodes are deleted). + */ +static void acpi_scan_drop_device(acpi_handle handle, void *context) +{ +	static DECLARE_WORK(work, acpi_device_del_work_fn); +	struct acpi_device *adev = context; + +	mutex_lock(&acpi_device_del_lock); + +	/* +	 * Use the ACPI hotplug workqueue which is ordered, so this work item +	 * won't run after any hotplug work items submitted subsequently.  That +	 * prevents attempts to register device objects identical to those being +	 * deleted from happening concurrently (such attempts result from +	 * hotplug events handled via the ACPI hotplug workqueue).  It also will +	 * run after all of the work items submitted previosuly, which helps +	 * those work items to ensure that they are not accessing stale device +	 * objects. +	 */ +	if (list_empty(&acpi_device_del_list)) +		acpi_queue_hotplug_work(&work); + +	list_add_tail(&adev->del_list, &acpi_device_del_list); +	/* Make acpi_ns_validate_handle() return NULL for this handle. */ +	adev->handle = INVALID_ACPI_HANDLE; + +	mutex_unlock(&acpi_device_del_lock); +} + +static int acpi_get_device_data(acpi_handle handle, struct acpi_device **device, +				void (*callback)(void *))  {  	acpi_status status;  	if (!device)  		return -EINVAL; -	status = acpi_get_data(handle, acpi_bus_data_handler, (void **)device); +	status = acpi_get_data_full(handle, acpi_scan_drop_device, +				    (void **)device, callback);  	if (ACPI_FAILURE(status) || !*device) {  		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",  				  handle)); @@ -968,7 +1135,31 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)  	}  	return 0;  } -EXPORT_SYMBOL_GPL(acpi_bus_get_device); + +int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) +{ +	return acpi_get_device_data(handle, device, NULL); +} +EXPORT_SYMBOL(acpi_bus_get_device); + +static void get_acpi_device(void *dev) +{ +	if (dev) +		get_device(&((struct acpi_device *)dev)->dev); +} + +struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle) +{ +	struct acpi_device *adev = NULL; + +	acpi_get_device_data(handle, &adev, get_acpi_device); +	return adev; +} + +void acpi_bus_put_acpi_device(struct acpi_device *adev) +{ +	put_device(&adev->dev); +}  int acpi_device_add(struct acpi_device *device,  		    void (*release)(struct device *)) @@ -980,7 +1171,7 @@ int acpi_device_add(struct acpi_device *device,  	if (device->handle) {  		acpi_status status; -		status = acpi_attach_data(device->handle, acpi_bus_data_handler, +		status = acpi_attach_data(device->handle, acpi_scan_drop_device,  					  device);  		if (ACPI_FAILURE(status)) {  			acpi_handle_err(device->handle, @@ -998,8 +1189,8 @@ int acpi_device_add(struct acpi_device *device,  	INIT_LIST_HEAD(&device->node);  	INIT_LIST_HEAD(&device->wakeup_list);  	INIT_LIST_HEAD(&device->physical_node_list); +	INIT_LIST_HEAD(&device->del_list);  	mutex_init(&device->physical_node_lock); -	INIT_LIST_HEAD(&device->power_dependent);  	new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL);  	if (!new_bus_id) { @@ -1062,37 +1253,10 @@ int acpi_device_add(struct acpi_device *device,  	mutex_unlock(&acpi_device_lock);   err_detach: -	acpi_detach_data(device->handle, acpi_bus_data_handler); +	acpi_detach_data(device->handle, acpi_scan_drop_device);  	return result;  } -static void acpi_device_unregister(struct acpi_device *device) -{ -	mutex_lock(&acpi_device_lock); -	if (device->parent) -		list_del(&device->node); - -	list_del(&device->wakeup_list); -	mutex_unlock(&acpi_device_lock); - -	acpi_detach_data(device->handle, acpi_bus_data_handler); - -	acpi_power_add_remove_device(device, false); -	acpi_device_remove_files(device); -	if (device->remove) -		device->remove(device); - -	device_del(&device->dev); -	/* -	 * Transition the device to D3cold to drop the reference counts of all -	 * power resources the device depends on and turn off the ones that have -	 * no more references. -	 */ -	acpi_device_set_power(device, ACPI_STATE_D3_COLD); -	device->handle = NULL; -	put_device(&device->dev); -} -  /* --------------------------------------------------------------------------                                   Driver Management     -------------------------------------------------------------------------- */ @@ -1121,7 +1285,7 @@ int acpi_bus_register_driver(struct acpi_driver *driver)  EXPORT_SYMBOL(acpi_bus_register_driver);  /** - * acpi_bus_unregister_driver - unregisters a driver with the APIC bus + * acpi_bus_unregister_driver - unregisters a driver with the ACPI bus   * @driver: driver to unregister   *   * Unregisters a driver with the ACPI bus.  Searches the namespace for all @@ -1387,9 +1551,13 @@ static void acpi_bus_get_power_flags(struct acpi_device *device)  	 */  	if (acpi_has_method(device->handle, "_PSC"))  		device->power.flags.explicit_get = 1; +  	if (acpi_has_method(device->handle, "_IRC"))  		device->power.flags.inrush_current = 1; +	if (acpi_has_method(device->handle, "_DSW")) +		device->power.flags.dsw_present = 1; +  	/*  	 * Enumerate supported power management states  	 */ @@ -1507,6 +1675,27 @@ bool acpi_bay_match(acpi_handle handle)  	return acpi_ata_match(phandle);  } +bool acpi_device_is_battery(struct acpi_device *adev) +{ +	struct acpi_hardware_id *hwid; + +	list_for_each_entry(hwid, &adev->pnp.ids, list) +		if (!strcmp("PNP0C0A", hwid->id)) +			return true; + +	return false; +} + +static bool is_ejectable_bay(struct acpi_device *adev) +{ +	acpi_handle handle = adev->handle; + +	if (acpi_has_method(handle, "_EJ0") && acpi_device_is_battery(adev)) +		return true; + +	return acpi_bay_match(handle); +} +  /*   * acpi_dock_match - see if an acpi object has a _DCK method   */ @@ -1572,6 +1761,20 @@ static bool acpi_ibm_smbus_match(acpi_handle handle)  	return false;  } +static bool acpi_object_is_system_bus(acpi_handle handle) +{ +	acpi_handle tmp; + +	if (ACPI_SUCCESS(acpi_get_handle(NULL, "\\_SB", &tmp)) && +	    tmp == handle) +		return true; +	if (ACPI_SUCCESS(acpi_get_handle(NULL, "\\_TZ", &tmp)) && +	    tmp == handle) +		return true; + +	return false; +} +  static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,  				int device_type)  { @@ -1594,8 +1797,10 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,  			return;  		} -		if (info->valid & ACPI_VALID_HID) +		if (info->valid & ACPI_VALID_HID) {  			acpi_add_id(pnp, info->hardware_id.string); +			pnp->type.platform_id = 1; +		}  		if (info->valid & ACPI_VALID_CID) {  			cid_list = &info->compatible_id_list;  			for (i = 0; i < cid_list->count; i++) @@ -1623,8 +1828,10 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,  			acpi_add_id(pnp, ACPI_DOCK_HID);  		else if (acpi_ibm_smbus_match(handle))  			acpi_add_id(pnp, ACPI_SMBUS_IBM_HID); -		else if (list_empty(&pnp->ids) && handle == ACPI_ROOT_OBJECT) { -			acpi_add_id(pnp, ACPI_BUS_HID); /* \_SB, LNXSYBUS */ +		else if (list_empty(&pnp->ids) && +			 acpi_object_is_system_bus(handle)) { +			/* \_SB, \_TZ, LNXSYBUS */ +			acpi_add_id(pnp, ACPI_BUS_HID);  			strcpy(pnp->device_name, ACPI_BUS_DEVICE_NAME);  			strcpy(pnp->device_class, ACPI_BUS_CLASS);  		} @@ -1666,18 +1873,19 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,  	device->device_type = type;  	device->handle = handle;  	device->parent = acpi_bus_get_parent(handle); -	STRUCT_TO_INT(device->status) = sta; +	acpi_set_device_status(device, sta);  	acpi_device_get_busid(device);  	acpi_set_pnp_ids(handle, &device->pnp, type);  	acpi_bus_get_flags(device);  	device->flags.match_driver = false; +	device->flags.initialized = true; +	device->flags.visited = false;  	device_initialize(&device->dev);  	dev_set_uevent_suppress(&device->dev, true);  }  void acpi_device_add_finalize(struct acpi_device *device)  { -	device->flags.match_driver = true;  	dev_set_uevent_suppress(&device->dev, false);  	kobject_uevent(&device->dev.kobj, KOBJ_ADD);  } @@ -1756,12 +1964,24 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type,  	return 0;  } +bool acpi_device_is_present(struct acpi_device *adev) +{ +	if (adev->status.present || adev->status.functional) +		return true; + +	adev->flags.initialized = false; +	return false; +} +  static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler,  				       char *idstr,  				       const struct acpi_device_id **matchid)  {  	const struct acpi_device_id *devid; +	if (handler->match) +		return handler->match(idstr, matchid); +  	for (devid = handler->ids; devid->id[0]; devid++)  		if (!strcmp((char *)devid->id, idstr)) {  			if (matchid) @@ -1797,33 +2017,23 @@ void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val)  	mutex_unlock(&acpi_scan_lock);  } -static void acpi_scan_init_hotplug(acpi_handle handle, int type) +static void acpi_scan_init_hotplug(struct acpi_device *adev)  { -	struct acpi_device_pnp pnp = {};  	struct acpi_hardware_id *hwid; -	struct acpi_scan_handler *handler; - -	INIT_LIST_HEAD(&pnp.ids); -	acpi_set_pnp_ids(handle, &pnp, type); -	if (!pnp.type.hardware_id) -		goto out; +	if (acpi_dock_match(adev->handle) || is_ejectable_bay(adev)) { +		acpi_dock_add(adev); +		return; +	} +	list_for_each_entry(hwid, &adev->pnp.ids, list) { +		struct acpi_scan_handler *handler; -	/* -	 * This relies on the fact that acpi_install_notify_handler() will not -	 * install the same notify handler routine twice for the same handle. -	 */ -	list_for_each_entry(hwid, &pnp.ids, list) {  		handler = acpi_scan_match_handler(hwid->id, NULL);  		if (handler) { -			acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, -					acpi_hotplug_notify_cb, handler); +			adev->flags.hotplug_notify = true;  			break;  		}  	} - -out: -	acpi_free_pnp_ids(&pnp);  }  static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, @@ -1847,24 +2057,12 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,  		return AE_OK;  	} -	acpi_scan_init_hotplug(handle, type); - -	if (!(sta & ACPI_STA_DEVICE_PRESENT) && -	    !(sta & ACPI_STA_DEVICE_FUNCTIONING)) { -		struct acpi_device_wakeup wakeup; - -		if (acpi_has_method(handle, "_PRW")) { -			acpi_bus_extract_wakeup_device_power_package(handle, -								     &wakeup); -			acpi_power_resources_list_free(&wakeup.resources); -		} -		return AE_CTRL_DEPTH; -	} -  	acpi_add_single_object(&device, handle, type, sta);  	if (!device)  		return AE_CTRL_DEPTH; +	acpi_scan_init_hotplug(device); +   out:  	if (!*return_value)  		*return_value = device; @@ -1872,6 +2070,44 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,  	return AE_OK;  } +static int acpi_check_spi_i2c_slave(struct acpi_resource *ares, void *data) +{ +	bool *is_spi_i2c_slave_p = data; + +	if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) +		return 1; + +	/* +	 * devices that are connected to UART still need to be enumerated to +	 * platform bus +	 */ +	if (ares->data.common_serial_bus.type != ACPI_RESOURCE_SERIAL_TYPE_UART) +		*is_spi_i2c_slave_p = true; + +	 /* no need to do more checking */ +	return -1; +} + +static void acpi_default_enumeration(struct acpi_device *device) +{ +	struct list_head resource_list; +	bool is_spi_i2c_slave = false; + +	if (!device->pnp.type.platform_id || device->handler) +		return; + +	/* +	 * Do not enemerate SPI/I2C slaves as they will be enuerated by their +	 * respective parents. +	 */ +	INIT_LIST_HEAD(&resource_list); +	acpi_dev_get_resources(device, &resource_list, acpi_check_spi_i2c_slave, +			       &is_spi_i2c_slave); +	acpi_dev_free_resource_list(&resource_list); +	if (!is_spi_i2c_slave) +		acpi_create_platform_device(device); +} +  static int acpi_scan_attach_handler(struct acpi_device *device)  {  	struct acpi_hardware_id *hwid; @@ -1883,44 +2119,64 @@ static int acpi_scan_attach_handler(struct acpi_device *device)  		handler = acpi_scan_match_handler(hwid->id, &devid);  		if (handler) { +			if (!handler->attach) { +				device->pnp.type.platform_id = 0; +				continue; +			} +			device->handler = handler;  			ret = handler->attach(device, devid); -			if (ret > 0) { -				device->handler = handler; +			if (ret > 0)  				break; -			} else if (ret < 0) { + +			device->handler = NULL; +			if (ret < 0)  				break; -			}  		}  	} +	if (!ret) +		acpi_default_enumeration(device); +  	return ret;  } -static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, -					  void *not_used, void **ret_not_used) +static void acpi_bus_attach(struct acpi_device *device)  { -	struct acpi_device *device; -	unsigned long long sta_not_used; +	struct acpi_device *child; +	acpi_handle ejd;  	int ret; -	/* -	 * Ignore errors ignored by acpi_bus_check_add() to avoid terminating -	 * namespace walks prematurely. -	 */ -	if (acpi_bus_type_and_status(handle, &ret, &sta_not_used)) -		return AE_OK; - -	if (acpi_bus_get_device(handle, &device)) -		return AE_CTRL_DEPTH; +	if (ACPI_SUCCESS(acpi_bus_get_ejd(device->handle, &ejd))) +		register_dock_dependent_device(device, ejd); +	acpi_bus_get_status(device); +	/* Skip devices that are not present. */ +	if (!acpi_device_is_present(device)) { +		device->flags.visited = false; +		return; +	}  	if (device->handler) -		return AE_OK; +		goto ok; +	if (!device->flags.initialized) { +		acpi_bus_update_power(device, NULL); +		device->flags.initialized = true; +	} +	device->flags.visited = false;  	ret = acpi_scan_attach_handler(device); -	if (ret) -		return ret > 0 ? AE_OK : AE_CTRL_DEPTH; +	if (ret < 0) +		return; + +	device->flags.match_driver = true; +	if (!ret) { +		ret = device_attach(&device->dev); +		if (ret < 0) +			return; +	} +	device->flags.visited = true; -	ret = device_attach(&device->dev); -	return ret >= 0 ? AE_OK : AE_CTRL_DEPTH; + ok: +	list_for_each_entry(child, &device->children, node) +		acpi_bus_attach(child);  }  /** @@ -1940,75 +2196,49 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used,  int acpi_bus_scan(acpi_handle handle)  {  	void *device = NULL; -	int error = 0;  	if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device)))  		acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,  				    acpi_bus_check_add, NULL, NULL, &device); -	if (!device) -		error = -ENODEV; -	else if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL))) -		acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, -				    acpi_bus_device_attach, NULL, NULL, NULL); - -	return error; -} -EXPORT_SYMBOL(acpi_bus_scan); - -static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used, -					  void *not_used, void **ret_not_used) -{ -	struct acpi_device *device = NULL; - -	if (!acpi_bus_get_device(handle, &device)) { -		struct acpi_scan_handler *dev_handler = device->handler; - -		if (dev_handler) { -			if (dev_handler->detach) -				dev_handler->detach(device); - -			device->handler = NULL; -		} else { -			device_release_driver(&device->dev); -		} +	if (device) { +		acpi_bus_attach(device); +		return 0;  	} -	return AE_OK; -} - -static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used, -				   void *not_used, void **ret_not_used) -{ -	struct acpi_device *device = NULL; - -	if (!acpi_bus_get_device(handle, &device)) -		acpi_device_unregister(device); - -	return AE_OK; +	return -ENODEV;  } +EXPORT_SYMBOL(acpi_bus_scan);  /** - * acpi_bus_trim - Remove ACPI device node and all of its descendants - * @start: Root of the ACPI device nodes subtree to remove. + * acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects. + * @adev: Root of the ACPI namespace scope to walk.   *   * Must be called under acpi_scan_lock.   */ -void acpi_bus_trim(struct acpi_device *start) +void acpi_bus_trim(struct acpi_device *adev)  { +	struct acpi_scan_handler *handler = adev->handler; +	struct acpi_device *child; + +	list_for_each_entry_reverse(child, &adev->children, node) +		acpi_bus_trim(child); + +	adev->flags.match_driver = false; +	if (handler) { +		if (handler->detach) +			handler->detach(adev); + +		adev->handler = NULL; +	} else { +		device_release_driver(&adev->dev); +	}  	/* -	 * Execute acpi_bus_device_detach() as a post-order callback to detach -	 * all ACPI drivers from the device nodes being removed. -	 */ -	acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL, -			    acpi_bus_device_detach, NULL, NULL); -	acpi_bus_device_detach(start->handle, 0, NULL, NULL); -	/* -	 * Execute acpi_bus_remove() as a post-order callback to remove device -	 * nodes in the given namespace scope. +	 * Most likely, the device is going away, so put it into D3cold before +	 * that.  	 */ -	acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL, -			    acpi_bus_remove, NULL, NULL); -	acpi_bus_remove(start->handle, 0, NULL, NULL); +	acpi_device_set_power(adev, ACPI_STATE_D3_COLD); +	adev->flags.initialized = false; +	adev->flags.visited = false;  }  EXPORT_SYMBOL_GPL(acpi_bus_trim); @@ -2028,6 +2258,7 @@ static int acpi_bus_scan_fixed(void)  		if (result)  			return result; +		device->flags.match_driver = true;  		result = device_attach(&device->dev);  		if (result < 0)  			return result; @@ -2044,6 +2275,7 @@ static int acpi_bus_scan_fixed(void)  		if (result)  			return result; +		device->flags.match_driver = true;  		result = device_attach(&device->dev);  	} @@ -2063,12 +2295,11 @@ int __init acpi_scan_init(void)  	acpi_pci_root_init();  	acpi_pci_link_init();  	acpi_processor_init(); -	acpi_platform_init();  	acpi_lpss_init();  	acpi_cmos_rtc_init();  	acpi_container_init();  	acpi_memory_hotplug_init(); -	acpi_dock_init(); +	acpi_pnp_init();  	mutex_lock(&acpi_scan_lock);  	/* @@ -2082,16 +2313,20 @@ int __init acpi_scan_init(void)  	if (result)  		goto out; -	result = acpi_bus_scan_fixed(); -	if (result) { -		acpi_device_unregister(acpi_root); -		goto out; +	/* Fixed feature devices do not exist on HW-reduced platform */ +	if (!acpi_gbl_reduced_hardware) { +		result = acpi_bus_scan_fixed(); +		if (result) { +			acpi_detach_data(acpi_root->handle, +					 acpi_scan_drop_device); +			acpi_device_del(acpi_root); +			put_device(&acpi_root->dev); +			goto out; +		}  	}  	acpi_update_all_gpes(); -	acpi_pci_root_hp_init(); -   out:  	mutex_unlock(&acpi_scan_lock);  	return result;  | 
