mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 19:51:31 -04:00
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
This commit is contained in:
commit
6621c5a69a
10 changed files with 295 additions and 505 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
/*
|
||||
* 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)
|
||||
{
|
||||
struct dock_dependent_device *dd, *aux;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
dd->hp_ops = NULL;
|
||||
dd->hp_context = NULL;
|
||||
dd->hp_release = NULL;
|
||||
if (release)
|
||||
release(context);
|
||||
}
|
||||
mutex_unlock(&hotplug_lock);
|
||||
}
|
||||
|
||||
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;
|
||||
struct acpi_device *adev = dd->adev;
|
||||
|
||||
mutex_lock(&hotplug_lock);
|
||||
acpi_lock_hp_context();
|
||||
|
||||
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;
|
||||
}
|
||||
if (!adev->hp)
|
||||
goto out;
|
||||
|
||||
if (cb_type == DOCK_CALL_FIXUP) {
|
||||
void (*fixup)(struct acpi_device *);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void register_dock_dependent_device(struct acpi_device *adev,
|
||||
acpi_handle dshandle)
|
||||
{
|
||||
struct dock_station *ds = find_dock_station(dshandle);
|
||||
|
||||
if (ds && !find_dock_dependent_device(ds, adev))
|
||||
add_dock_dependent_device(ds, adev);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Dock functions *
|
||||
*****************************************************************************/
|
||||
static int __init is_battery(acpi_handle handle)
|
||||
{
|
||||
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);
|
||||
|
||||
kfree(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -71,6 +71,17 @@ 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)
|
||||
|
|
@ -470,10 +481,9 @@ static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
void acpi_device_hotplug(void *data, u32 src)
|
||||
void acpi_device_hotplug(struct acpi_device *adev, u32 src)
|
||||
{
|
||||
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
|
||||
struct acpi_device *adev = data;
|
||||
int error = -ENODEV;
|
||||
|
||||
lock_device_hotplug();
|
||||
|
|
@ -487,24 +497,26 @@ void acpi_device_hotplug(void *data, u32 src)
|
|||
if (adev->handle == INVALID_ACPI_HANDLE)
|
||||
goto err_out;
|
||||
|
||||
if (adev->flags.hotplug_notify) {
|
||||
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 (*event)(struct acpi_device *, u32);
|
||||
int (*notify)(struct acpi_device *, u32);
|
||||
|
||||
acpi_lock_hp_context();
|
||||
event = adev->hp ? adev->hp->event : NULL;
|
||||
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 (event)
|
||||
error = event(adev, src);
|
||||
if (notify)
|
||||
error = notify(adev, src);
|
||||
else
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -566,8 +578,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
|
|||
return -ENODEV;
|
||||
|
||||
get_device(&acpi_device->dev);
|
||||
status = acpi_hotplug_execute(acpi_device_hotplug, acpi_device,
|
||||
ACPI_OST_EC_OSPM_EJECT);
|
||||
status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return count;
|
||||
|
||||
|
|
@ -1660,6 +1671,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
|
||||
*/
|
||||
|
|
@ -1964,6 +1996,10 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev)
|
|||
{
|
||||
struct acpi_hardware_id *hwid;
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -2035,8 +2071,12 @@ static int acpi_scan_attach_handler(struct acpi_device *device)
|
|||
static void acpi_bus_attach(struct acpi_device *device)
|
||||
{
|
||||
struct acpi_device *child;
|
||||
acpi_handle ejd;
|
||||
int ret;
|
||||
|
||||
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)) {
|
||||
|
|
@ -2189,7 +2229,6 @@ int __init acpi_scan_init(void)
|
|||
acpi_cmos_rtc_init();
|
||||
acpi_container_init();
|
||||
acpi_memory_hotplug_init();
|
||||
acpi_dock_init();
|
||||
|
||||
mutex_lock(&acpi_scan_lock);
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue