Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6621c5a6 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'acpi-hotplug'

* acpi-hotplug:
  ACPI / hotplug: Rework deferred execution of acpi_device_hotplug()
  ACPI / dock: Update copyright notice
  ACPI / dock: Drop remove_dock_dependent_devices()
  ACPI / dock: Drop struct acpi_dock_ops and all code related to it
  ACPI / ATA: Add hotplug contexts to ACPI companions of SATA devices
  ACPI / dock: Add .uevent() callback to struct acpi_hotplug_context
  ACPI / dock: Use callback pointers from devices' ACPI hotplug contexts
  ACPI / dock: Use ACPI device object pointers instead of ACPI handles
  ACPI / hotplug: Add .fixup() callback to struct acpi_hotplug_context
  ACPI / hotplug / PCI: Do not clear event callback pointer for docks
  ACPI / dock: Associate dock platform devices with ACPI device objects
  ACPI / dock: Pass ACPI device pointer to acpi_device_is_battery()
  ACPI / dock: Dispatch dock notifications from the global notify handler
parents d983f933 d901188f
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -400,7 +400,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
	case ACPI_NOTIFY_BUS_CHECK:
	case ACPI_NOTIFY_DEVICE_CHECK:
	case ACPI_NOTIFY_EJECT_REQUEST:
		status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
		status = acpi_hotplug_schedule(adev, type);
		if (ACPI_SUCCESS(status))
			return;
	default:
+3 −0
Original line number Diff line number Diff line
@@ -68,6 +68,9 @@ static int container_device_attach(struct acpi_device *adev,
	struct device *dev;
	int ret;

	if (adev->flags.is_dock_station)
		return 0;

	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
	if (!cdev)
		return -ENOMEM;
+109 −343
Original line number Diff line number Diff line
/*
 *  dock.c - ACPI dock station driver
 *
 *  Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@intel.com>
 *  Copyright (C) 2006, 2014, Intel Corp.
 *  Author: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
 *          Rafael J. Wysocki <rafael.j.wysocki@intel.com>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
@@ -68,15 +70,10 @@ struct dock_station {
};
static LIST_HEAD(dock_stations);
static int dock_station_count;
static DEFINE_MUTEX(hotplug_lock);

struct dock_dependent_device {
	struct list_head list;
	acpi_handle handle;
	const struct acpi_dock_ops *hp_ops;
	void *hp_context;
	unsigned int hp_refcount;
	void (*hp_release)(void *);
	struct acpi_device *adev;
};

#define DOCK_DOCKING	0x00000001
@@ -98,13 +95,13 @@ enum dock_callback_type {
 *****************************************************************************/
/**
 * add_dock_dependent_device - associate a device with the dock station
 * @ds: The dock station
 * @handle: handle of the dependent device
 * @ds: Dock station.
 * @adev: Dependent ACPI device object.
 *
 * Add the dependent device to the dock's dependent device list.
 */
static int __init
add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
static int add_dock_dependent_device(struct dock_station *ds,
				     struct acpi_device *adev)
{
	struct dock_dependent_device *dd;

@@ -112,180 +109,120 @@ add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
	if (!dd)
		return -ENOMEM;

	dd->handle = handle;
	dd->adev = adev;
	INIT_LIST_HEAD(&dd->list);
	list_add_tail(&dd->list, &ds->dependent_devices);

	return 0;
}

static void remove_dock_dependent_devices(struct dock_station *ds)
static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
			       enum dock_callback_type cb_type)
{
	struct dock_dependent_device *dd, *aux;
	struct acpi_device *adev = dd->adev;

	list_for_each_entry_safe(dd, aux, &ds->dependent_devices, list) {
		list_del(&dd->list);
		kfree(dd);
	}
}

/**
 * dock_init_hotplug - Initialize a hotplug device on a docking station.
 * @dd: Dock-dependent device.
 * @ops: Dock operations to attach to the dependent device.
 * @context: Data to pass to the @ops callbacks and @release.
 * @init: Optional initialization routine to run after setting up context.
 * @release: Optional release routine to run on removal.
 */
static int dock_init_hotplug(struct dock_dependent_device *dd,
			     const struct acpi_dock_ops *ops, void *context,
			     void (*init)(void *), void (*release)(void *))
{
	int ret = 0;
	acpi_lock_hp_context();

	mutex_lock(&hotplug_lock);
	if (WARN_ON(dd->hp_context)) {
		ret = -EEXIST;
	} else {
		dd->hp_refcount = 1;
		dd->hp_ops = ops;
		dd->hp_context = context;
		dd->hp_release = release;
		if (init)
			init(context);
	}
	mutex_unlock(&hotplug_lock);
	return ret;
}
	if (!adev->hp)
		goto out;

/**
 * dock_release_hotplug - Decrement hotplug reference counter of dock device.
 * @dd: Dock-dependent device.
 *
 * Decrement the reference counter of @dd and if 0, detach its hotplug
 * operations from it, reset its context pointer and run the optional release
 * routine if present.
 */
static void dock_release_hotplug(struct dock_dependent_device *dd)
{
	mutex_lock(&hotplug_lock);
	if (dd->hp_context && !--dd->hp_refcount) {
		void (*release)(void *) = dd->hp_release;
		void *context = dd->hp_context;
	if (cb_type == DOCK_CALL_FIXUP) {
		void (*fixup)(struct acpi_device *);

		dd->hp_ops = NULL;
		dd->hp_context = NULL;
		dd->hp_release = NULL;
		if (release)
			release(context);
	}
	mutex_unlock(&hotplug_lock);
		fixup = adev->hp->fixup;
		if (fixup) {
			acpi_unlock_hp_context();
			fixup(adev);
			return;
		}
	} else if (cb_type == DOCK_CALL_UEVENT) {
		void (*uevent)(struct acpi_device *, u32);

static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
			       enum dock_callback_type cb_type)
{
	acpi_notify_handler cb = NULL;
	bool run = false;

	mutex_lock(&hotplug_lock);

	if (dd->hp_context) {
		run = true;
		dd->hp_refcount++;
		if (dd->hp_ops) {
			switch (cb_type) {
			case DOCK_CALL_FIXUP:
				cb = dd->hp_ops->fixup;
				break;
			case DOCK_CALL_UEVENT:
				cb = dd->hp_ops->uevent;
				break;
			default:
				cb = dd->hp_ops->handler;
		uevent = adev->hp->uevent;
		if (uevent) {
			acpi_unlock_hp_context();
			uevent(adev, event);
			return;
		}
	} else {
		int (*notify)(struct acpi_device *, u32);

		notify = adev->hp->notify;
		if (notify) {
			acpi_unlock_hp_context();
			notify(adev, event);
			return;
		}
	}

	mutex_unlock(&hotplug_lock);
 out:
	acpi_unlock_hp_context();
}

	if (!run)
		return;
static struct dock_station *find_dock_station(acpi_handle handle)
{
	struct dock_station *ds;

	if (cb)
		cb(dd->handle, event, dd->hp_context);
	list_for_each_entry(ds, &dock_stations, sibling)
		if (ds->handle == handle)
			return ds;

	dock_release_hotplug(dd);
	return NULL;
}

/**
 * find_dock_dependent_device - get a device dependent on this dock
 * @ds: the dock station
 * @handle: the acpi_handle of the device we want
 * @adev: ACPI device object to find.
 *
 * iterate over the dependent device list for this dock.  If the
 * dependent device matches the handle, return.
 */
static struct dock_dependent_device *
find_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
find_dock_dependent_device(struct dock_station *ds, struct acpi_device *adev)
{
	struct dock_dependent_device *dd;

	list_for_each_entry(dd, &ds->dependent_devices, list)
		if (handle == dd->handle)
		if (adev == dd->adev)
			return dd;

	return NULL;
}

/*****************************************************************************
 *                         Dock functions                                    *
 *****************************************************************************/
static int __init is_battery(acpi_handle handle)
void register_dock_dependent_device(struct acpi_device *adev,
				    acpi_handle dshandle)
{
	struct acpi_device_info *info;
	int ret = 1;

	if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info)))
		return 0;
	if (!(info->valid & ACPI_VALID_HID))
		ret = 0;
	else
		ret = !strcmp("PNP0C0A", info->hardware_id.string);
	struct dock_station *ds = find_dock_station(dshandle);

	kfree(info);
	return ret;
	if (ds && !find_dock_dependent_device(ds, adev))
		add_dock_dependent_device(ds, adev);
}

/* Check whether ACPI object is an ejectable battery or disk bay */
static bool __init is_ejectable_bay(acpi_handle handle)
{
	if (acpi_has_method(handle, "_EJ0") && is_battery(handle))
		return true;

	return acpi_bay_match(handle);
}
/*****************************************************************************
 *                         Dock functions                                    *
 *****************************************************************************/

/**
 * is_dock_device - see if a device is on a dock station
 * @handle: acpi handle of the device
 * @adev: ACPI device object to check.
 *
 * If this device is either the dock station itself,
 * or is a device dependent on the dock station, then it
 * is a dock device
 */
int is_dock_device(acpi_handle handle)
int is_dock_device(struct acpi_device *adev)
{
	struct dock_station *dock_station;

	if (!dock_station_count)
		return 0;

	if (acpi_dock_match(handle))
	if (acpi_dock_match(adev->handle))
		return 1;

	list_for_each_entry(dock_station, &dock_stations, sibling)
		if (find_dock_dependent_device(dock_station, handle))
		if (find_dock_dependent_device(dock_station, adev))
			return 1;

	return 0;
@@ -312,43 +249,6 @@ static int dock_present(struct dock_station *ds)
	return 0;
}

/**
 * dock_create_acpi_device - add new devices to acpi
 * @handle - handle of the device to add
 *
 *  This function will create a new acpi_device for the given
 *  handle if one does not exist already.  This should cause
 *  acpi to scan for drivers for the given devices, and call
 *  matching driver's add routine.
 */
static void dock_create_acpi_device(acpi_handle handle)
{
	struct acpi_device *device = NULL;
	int ret;

	acpi_bus_get_device(handle, &device);
	if (!acpi_device_enumerated(device)) {
		ret = acpi_bus_scan(handle);
		if (ret)
			pr_debug("error adding bus, %x\n", -ret);
	}
}

/**
 * dock_remove_acpi_device - remove the acpi_device struct from acpi
 * @handle - the handle of the device to remove
 *
 *  Tell acpi to remove the acpi_device.  This should cause any loaded
 *  driver to have it's remove routine called.
 */
static void dock_remove_acpi_device(acpi_handle handle)
{
	struct acpi_device *device;

	if (!acpi_bus_get_device(handle, &device))
		acpi_bus_trim(device);
}

/**
 * hot_remove_dock_devices - Remove dock station devices.
 * @ds: Dock station.
@@ -366,7 +266,7 @@ static void hot_remove_dock_devices(struct dock_station *ds)
		dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, false);

	list_for_each_entry_reverse(dd, &ds->dependent_devices, list)
		dock_remove_acpi_device(dd->handle);
		acpi_bus_trim(dd->adev);
}

/**
@@ -392,12 +292,20 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event)
		dock_hotplug_event(dd, event, DOCK_CALL_HANDLER);

	/*
	 * Now make sure that an acpi_device is created for each dependent
	 * device.  That will cause scan handlers to be attached to device
	 * objects or acpi_drivers to be stopped/started if they are present.
	 * Check if all devices have been enumerated already.  If not, run
	 * acpi_bus_scan() for them and that will cause scan handlers to be
	 * attached to device objects or acpi_drivers to be stopped/started if
	 * they are present.
	 */
	list_for_each_entry(dd, &ds->dependent_devices, list)
		dock_create_acpi_device(dd->handle);
	list_for_each_entry(dd, &ds->dependent_devices, list) {
		struct acpi_device *adev = dd->adev;

		if (!acpi_device_enumerated(adev)) {
			int ret = acpi_bus_scan(adev->handle);
			if (ret)
				dev_dbg(&adev->dev, "scan error %d\n", -ret);
		}
	}
}

static void dock_event(struct dock_station *ds, u32 event, int num)
@@ -500,71 +408,6 @@ static int dock_in_progress(struct dock_station *ds)
	return 0;
}

/**
 * register_hotplug_dock_device - register a hotplug function
 * @handle: the handle of the device
 * @ops: handlers to call after docking
 * @context: device specific data
 * @init: Optional initialization routine to run after registration
 * @release: Optional release routine to run on unregistration
 *
 * If a driver would like to perform a hotplug operation after a dock
 * event, they can register an acpi_notifiy_handler to be called by
 * the dock driver after _DCK is executed.
 */
int register_hotplug_dock_device(acpi_handle handle,
				 const struct acpi_dock_ops *ops, void *context,
				 void (*init)(void *), void (*release)(void *))
{
	struct dock_dependent_device *dd;
	struct dock_station *dock_station;
	int ret = -EINVAL;

	if (WARN_ON(!context))
		return -EINVAL;

	if (!dock_station_count)
		return -ENODEV;

	/*
	 * make sure this handle is for a device dependent on the dock,
	 * this would include the dock station itself
	 */
	list_for_each_entry(dock_station, &dock_stations, sibling) {
		/*
		 * An ATA bay can be in a dock and itself can be ejected
		 * separately, so there are two 'dock stations' which need the
		 * ops
		 */
		dd = find_dock_dependent_device(dock_station, handle);
		if (dd && !dock_init_hotplug(dd, ops, context, init, release))
			ret = 0;
	}

	return ret;
}
EXPORT_SYMBOL_GPL(register_hotplug_dock_device);

/**
 * unregister_hotplug_dock_device - remove yourself from the hotplug list
 * @handle: the acpi handle of the device
 */
void unregister_hotplug_dock_device(acpi_handle handle)
{
	struct dock_dependent_device *dd;
	struct dock_station *dock_station;

	if (!dock_station_count)
		return;

	list_for_each_entry(dock_station, &dock_stations, sibling) {
		dd = find_dock_dependent_device(dock_station, handle);
		if (dd)
			dock_release_hotplug(dd);
	}
}
EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);

/**
 * handle_eject_request - handle an undock request checking for error conditions
 *
@@ -598,20 +441,23 @@ static int handle_eject_request(struct dock_station *ds, u32 event)
}

/**
 * dock_notify - act upon an acpi dock notification
 * @ds: dock station
 * @event: the acpi event
 * dock_notify - Handle ACPI dock notification.
 * @adev: Dock station's ACPI device object.
 * @event: Event code.
 *
 * If we are notified to dock, then check to see if the dock is
 * present and then dock.  Notify all drivers of the dock event,
 * and then hotplug and devices that may need hotplugging.
 */
static void dock_notify(struct dock_station *ds, u32 event)
int dock_notify(struct acpi_device *adev, u32 event)
{
	acpi_handle handle = ds->handle;
	struct acpi_device *adev = NULL;
	acpi_handle handle = adev->handle;
	struct dock_station *ds = find_dock_station(handle);
	int surprise_removal = 0;

	if (!ds)
		return -ENODEV;

	/*
	 * According to acpi spec 3.0a, if a DEVICE_CHECK notification
	 * is sent and _DCK is present, it is assumed to mean an undock
@@ -632,7 +478,6 @@ static void dock_notify(struct dock_station *ds, u32 event)
	switch (event) {
	case ACPI_NOTIFY_BUS_CHECK:
	case ACPI_NOTIFY_DEVICE_CHECK:
		acpi_bus_get_device(handle, &adev);
		if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) {
			begin_dock(ds);
			dock(ds);
@@ -662,49 +507,8 @@ static void dock_notify(struct dock_station *ds, u32 event)
		else
			dock_event(ds, event, UNDOCK_EVENT);
		break;
	default:
		acpi_handle_err(handle, "Unknown dock event %d\n", event);
	}
}

static void acpi_dock_deferred_cb(void *data, u32 event)
{
	acpi_scan_lock_acquire();
	dock_notify(data, event);
	acpi_scan_lock_release();
	}

static void dock_notify_handler(acpi_handle handle, u32 event, void *data)
{
	if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK
	   && event != ACPI_NOTIFY_EJECT_REQUEST)
		return;

	acpi_hotplug_execute(acpi_dock_deferred_cb, data, event);
}

/**
 * find_dock_devices - find devices on the dock station
 * @handle: the handle of the device we are examining
 * @lvl: unused
 * @context: the dock station private data
 * @rv: unused
 *
 * This function is called by acpi_walk_namespace.  It will
 * check to see if an object has an _EJD method.  If it does, then it
 * will see if it is dependent on the dock station.
 */
static acpi_status __init find_dock_devices(acpi_handle handle, u32 lvl,
					    void *context, void **rv)
{
	struct dock_station *ds = context;
	acpi_handle ejd = NULL;

	acpi_bus_get_ejd(handle, &ejd);
	if (ejd == ds->handle)
		add_dock_dependent_device(ds, handle);

	return AE_OK;
	return 0;
}

/*
@@ -803,23 +607,28 @@ static struct attribute_group dock_attribute_group = {
};

/**
 * dock_add - add a new dock station
 * @handle: the dock station handle
 * acpi_dock_add - Add a new dock station
 * @adev: Dock station ACPI device object.
 *
 * allocated and initialize a new dock station device.  Find all devices
 * that are on the dock station, and register for dock event notifications.
 * allocated and initialize a new dock station device.
 */
static int __init dock_add(acpi_handle handle)
void acpi_dock_add(struct acpi_device *adev)
{
	struct dock_station *dock_station, ds = { NULL, };
	struct platform_device_info pdevinfo;
	acpi_handle handle = adev->handle;
	struct platform_device *dd;
	acpi_status status;
	int ret;

	dd = platform_device_register_data(NULL, "dock", dock_station_count,
					   &ds, sizeof(ds));
	memset(&pdevinfo, 0, sizeof(pdevinfo));
	pdevinfo.name = "dock";
	pdevinfo.id = dock_station_count;
	pdevinfo.acpi_node.companion = adev;
	pdevinfo.data = &ds;
	pdevinfo.size_data = sizeof(ds);
	dd = platform_device_register_full(&pdevinfo);
	if (IS_ERR(dd))
		return PTR_ERR(dd);
		return;

	dock_station = dd->dev.platform_data;

@@ -837,72 +646,29 @@ static int __init dock_add(acpi_handle handle)
		dock_station->flags |= DOCK_IS_DOCK;
	if (acpi_ata_match(handle))
		dock_station->flags |= DOCK_IS_ATA;
	if (is_battery(handle))
	if (acpi_device_is_battery(adev))
		dock_station->flags |= DOCK_IS_BAT;

	ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);
	if (ret)
		goto err_unregister;

	/* Find dependent devices */
	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
			    ACPI_UINT32_MAX, find_dock_devices, NULL,
			    dock_station, NULL);

	/* add the dock station as a device dependent on itself */
	ret = add_dock_dependent_device(dock_station, handle);
	ret = add_dock_dependent_device(dock_station, adev);
	if (ret)
		goto err_rmgroup;

	status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
					     dock_notify_handler, dock_station);
	if (ACPI_FAILURE(status)) {
		ret = -ENODEV;
		goto err_rmgroup;
	}

	dock_station_count++;
	list_add(&dock_station->sibling, &dock_stations);
	return 0;
	adev->flags.is_dock_station = true;
	dev_info(&adev->dev, "ACPI dock station (docks/bays count: %d)\n",
		 dock_station_count);
	return;

err_rmgroup:
	remove_dock_dependent_devices(dock_station);
	sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group);

err_unregister:
	platform_device_unregister(dd);
	acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret);
	return ret;
}

/**
 * find_dock_and_bay - look for dock stations and bays
 * @handle: acpi handle of a device
 * @lvl: unused
 * @context: unused
 * @rv: unused
 *
 * This is called by acpi_walk_namespace to look for dock stations and bays.
 */
static acpi_status __init
find_dock_and_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
{
	if (acpi_dock_match(handle) || is_ejectable_bay(handle))
		dock_add(handle);

	return AE_OK;
}

void __init acpi_dock_init(void)
{
	/* look for dock stations and bays */
	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
		ACPI_UINT32_MAX, find_dock_and_bay, NULL, NULL, NULL);

	if (!dock_station_count) {
		pr_info(PREFIX "No dock devices found.\n");
		return;
	}

	pr_info(PREFIX "%s: %d docks/bays found\n",
		ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count);
}
+11 −3
Original line number Diff line number Diff line
@@ -37,9 +37,15 @@ void acpi_container_init(void);
static inline void acpi_container_init(void) {}
#endif
#ifdef CONFIG_ACPI_DOCK
void acpi_dock_init(void);
void register_dock_dependent_device(struct acpi_device *adev,
				    acpi_handle dshandle);
int dock_notify(struct acpi_device *adev, u32 event);
void acpi_dock_add(struct acpi_device *adev);
#else
static inline void acpi_dock_init(void) {}
static inline void register_dock_dependent_device(struct acpi_device *adev,
						  acpi_handle dshandle) {}
static inline int dock_notify(struct acpi_device *adev, u32 event) { return -ENODEV; }
static inline void acpi_dock_add(struct acpi_device *adev) {}
#endif
#ifdef CONFIG_ACPI_HOTPLUG_MEMORY
void acpi_memory_hotplug_init(void);
@@ -72,8 +78,9 @@ void acpi_lpss_init(void);
static inline void acpi_lpss_init(void) {}
#endif

acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src);
bool acpi_queue_hotplug_work(struct work_struct *work);
void acpi_device_hotplug(void *data, u32 src);
void acpi_device_hotplug(struct acpi_device *adev, u32 src);
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);

/* --------------------------------------------------------------------------
@@ -91,6 +98,7 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);
int acpi_bind_one(struct device *dev, struct acpi_device *adev);
int acpi_unbind_one(struct device *dev);
bool acpi_device_is_present(struct acpi_device *adev);
bool acpi_device_is_battery(struct acpi_device *adev);

/* --------------------------------------------------------------------------
                                  Power Resource
+6 −8
Original line number Diff line number Diff line
@@ -1168,8 +1168,7 @@ void acpi_os_wait_events_complete(void)

struct acpi_hp_work {
	struct work_struct work;
	acpi_hp_callback func;
	void *data;
	struct acpi_device *adev;
	u32 src;
};

@@ -1178,25 +1177,24 @@ static void acpi_hotplug_work_fn(struct work_struct *work)
	struct acpi_hp_work *hpw = container_of(work, struct acpi_hp_work, work);

	acpi_os_wait_events_complete();
	hpw->func(hpw->data, hpw->src);
	acpi_device_hotplug(hpw->adev, hpw->src);
	kfree(hpw);
}

acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src)
acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src)
{
	struct acpi_hp_work *hpw;

	ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
		  "Scheduling function [%p(%p, %u)] for deferred execution.\n",
		  func, data, src));
		  "Scheduling hotplug event (%p, %u) for deferred execution.\n",
		  adev, src));

	hpw = kmalloc(sizeof(*hpw), GFP_KERNEL);
	if (!hpw)
		return AE_NO_MEMORY;

	INIT_WORK(&hpw->work, acpi_hotplug_work_fn);
	hpw->func = func;
	hpw->data = data;
	hpw->adev = adev;
	hpw->src = src;
	/*
	 * We can't run hotplug code in kacpid_wq/kacpid_notify_wq etc., because
Loading