Skip to content

Commit

Permalink
Merge branch 'acpi-fixes'
Browse files Browse the repository at this point in the history
* acpi-fixes:
  libata-acpi: add back ACPI based hotplug functionality
  ACPI / dock / PCI: Synchronous handling of dock events for PCI devices
  PCI / ACPI: Use boot-time resource allocation rules during hotplug
  ACPI / dock: Initialize ACPI dock subsystem upfront
  • Loading branch information
Rafael J. Wysocki committed Jun 25, 2013
2 parents 9e895ac + 4452152 commit 96c9dda
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 106 deletions.
179 changes: 96 additions & 83 deletions drivers/acpi/dock.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,21 @@ struct dock_station {
spinlock_t dd_lock;
struct mutex hp_lock;
struct list_head dependent_devices;
struct list_head hotplug_devices;

struct list_head sibling;
struct platform_device *dock_device;
};
static LIST_HEAD(dock_stations);
static int dock_station_count;
static DEFINE_MUTEX(hotplug_lock);

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

#define DOCK_DOCKING 0x00000001
Expand Down Expand Up @@ -111,7 +112,6 @@ add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)

dd->handle = handle;
INIT_LIST_HEAD(&dd->list);
INIT_LIST_HEAD(&dd->hotplug_list);

spin_lock(&ds->dd_lock);
list_add_tail(&dd->list, &ds->dependent_devices);
Expand All @@ -121,35 +121,90 @@ add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
}

/**
* dock_add_hotplug_device - associate a hotplug handler with the dock station
* @ds: The dock station
* @dd: The dependent device struct
*
* Add the dependent device to the dock's hotplug device list
* 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 void
dock_add_hotplug_device(struct dock_station *ds,
struct dock_dependent_device *dd)
static int dock_init_hotplug(struct dock_dependent_device *dd,
const struct acpi_dock_ops *ops, void *context,
void (*init)(void *), void (*release)(void *))
{
mutex_lock(&ds->hp_lock);
list_add_tail(&dd->hotplug_list, &ds->hotplug_devices);
mutex_unlock(&ds->hp_lock);
int ret = 0;

mutex_lock(&hotplug_lock);

if (dd->hp_context) {
ret = -EEXIST;
} else {
dd->hp_refcount = 1;
dd->hp_ops = ops;
dd->hp_context = context;
dd->hp_release = release;
}

if (!WARN_ON(ret) && init)
init(context);

mutex_unlock(&hotplug_lock);
return ret;
}

/**
* dock_del_hotplug_device - remove a hotplug handler from the dock station
* @ds: The dock station
* @dd: the dependent device struct
* dock_release_hotplug - Decrement hotplug reference counter of dock device.
* @dd: Dock-dependent device.
*
* Delete the dependent device from the dock's hotplug device list
* 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_del_hotplug_device(struct dock_station *ds,
struct dock_dependent_device *dd)
static void dock_release_hotplug(struct dock_dependent_device *dd)
{
mutex_lock(&ds->hp_lock);
list_del(&dd->hotplug_list);
mutex_unlock(&ds->hp_lock);
void (*release)(void *) = NULL;
void *context = NULL;

mutex_lock(&hotplug_lock);

if (dd->hp_context && !--dd->hp_refcount) {
dd->hp_ops = NULL;
context = dd->hp_context;
dd->hp_context = NULL;
release = dd->hp_release;
dd->hp_release = NULL;
}

if (release && context)
release(context);

mutex_unlock(&hotplug_lock);
}

static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
bool uevent)
{
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)
cb = uevent ? dd->hp_ops->uevent : dd->hp_ops->handler;
}

mutex_unlock(&hotplug_lock);

if (!run)
return;

if (cb)
cb(dd->handle, event, dd->hp_context);

dock_release_hotplug(dd);
}

/**
Expand Down Expand Up @@ -360,9 +415,8 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event)
/*
* First call driver specific hotplug functions
*/
list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
if (dd->ops && dd->ops->handler)
dd->ops->handler(dd->handle, event, dd->context);
list_for_each_entry(dd, &ds->dependent_devices, list)
dock_hotplug_event(dd, event, false);

/*
* Now make sure that an acpi_device is created for each
Expand Down Expand Up @@ -398,9 +452,8 @@ static void dock_event(struct dock_station *ds, u32 event, int num)
if (num == DOCK_EVENT)
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);

list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
if (dd->ops && dd->ops->uevent)
dd->ops->uevent(dd->handle, event, dd->context);
list_for_each_entry(dd, &ds->dependent_devices, list)
dock_hotplug_event(dd, event, true);

if (num != DOCK_EVENT)
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
Expand Down Expand Up @@ -570,19 +623,24 @@ EXPORT_SYMBOL_GPL(unregister_dock_notifier);
* @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)
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;

Expand All @@ -597,12 +655,8 @@ register_hotplug_dock_device(acpi_handle handle, const struct acpi_dock_ops *ops
* ops
*/
dd = find_dock_dependent_device(dock_station, handle);
if (dd) {
dd->ops = ops;
dd->context = context;
dock_add_hotplug_device(dock_station, dd);
if (dd && !dock_init_hotplug(dd, ops, context, init, release))
ret = 0;
}
}

return ret;
Expand All @@ -624,7 +678,7 @@ void unregister_hotplug_dock_device(acpi_handle handle)
list_for_each_entry(dock_station, &dock_stations, sibling) {
dd = find_dock_dependent_device(dock_station, handle);
if (dd)
dock_del_hotplug_device(dock_station, dd);
dock_release_hotplug(dd);
}
}
EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
Expand Down Expand Up @@ -953,7 +1007,6 @@ static int __init dock_add(acpi_handle handle)
mutex_init(&dock_station->hp_lock);
spin_lock_init(&dock_station->dd_lock);
INIT_LIST_HEAD(&dock_station->sibling);
INIT_LIST_HEAD(&dock_station->hotplug_devices);
ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
INIT_LIST_HEAD(&dock_station->dependent_devices);

Expand Down Expand Up @@ -993,30 +1046,6 @@ static int __init dock_add(acpi_handle handle)
return ret;
}

/**
* dock_remove - free up resources related to the dock station
*/
static int dock_remove(struct dock_station *ds)
{
struct dock_dependent_device *dd, *tmp;
struct platform_device *dock_device = ds->dock_device;

if (!dock_station_count)
return 0;

/* remove dependent devices */
list_for_each_entry_safe(dd, tmp, &ds->dependent_devices, list)
kfree(dd);

list_del(&ds->sibling);

/* cleanup sysfs */
sysfs_remove_group(&dock_device->dev.kobj, &dock_attribute_group);
platform_device_unregister(dock_device);

return 0;
}

/**
* find_dock_and_bay - look for dock stations and bays
* @handle: acpi handle of a device
Expand All @@ -1035,7 +1064,7 @@ find_dock_and_bay(acpi_handle handle, u32 lvl, void *context, void **rv)
return AE_OK;
}

static int __init dock_init(void)
int __init acpi_dock_init(void)
{
if (acpi_disabled)
return 0;
Expand All @@ -1054,19 +1083,3 @@ static int __init dock_init(void)
ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count);
return 0;
}

static void __exit dock_exit(void)
{
struct dock_station *tmp, *dock_station;

unregister_acpi_bus_notifier(&dock_acpi_notifier);
list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibling)
dock_remove(dock_station);
}

/*
* Must be called before drivers of devices in dock, otherwise we can't know
* which devices are in a dock
*/
subsys_initcall(dock_init);
module_exit(dock_exit);
5 changes: 5 additions & 0 deletions drivers/acpi/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ void acpi_container_init(void);
#else
static inline void acpi_container_init(void) {}
#endif
#ifdef CONFIG_ACPI_DOCK
void acpi_dock_init(void);
#else
static inline void acpi_dock_init(void) {}
#endif
#ifdef CONFIG_ACPI_HOTPLUG_MEMORY
void acpi_memory_hotplug_init(void);
#else
Expand Down
1 change: 1 addition & 0 deletions drivers/acpi/scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,7 @@ int __init acpi_scan_init(void)
acpi_lpss_init();
acpi_container_init();
acpi_memory_hotplug_init();
acpi_dock_init();

mutex_lock(&acpi_scan_lock);
/*
Expand Down
37 changes: 36 additions & 1 deletion drivers/ata/libata-acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,10 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev,

spin_unlock_irqrestore(ap->lock, flags);

if (wait)
if (wait) {
ata_port_wait_eh(ap);
flush_work(&ap->hotplug_task.work);
}
}

static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data)
Expand Down Expand Up @@ -214,6 +216,39 @@ static const struct acpi_dock_ops ata_acpi_ap_dock_ops = {
.uevent = ata_acpi_ap_uevent,
};

void ata_acpi_hotplug_init(struct ata_host *host)
{
int i;

for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
acpi_handle handle;
struct ata_device *dev;

if (!ap)
continue;

handle = ata_ap_acpi_handle(ap);
if (handle) {
/* we might be on a docking station */
register_hotplug_dock_device(handle,
&ata_acpi_ap_dock_ops, ap,
NULL, NULL);
}

ata_for_each_dev(dev, &ap->link, ALL) {
handle = ata_dev_acpi_handle(dev);
if (!handle)
continue;

/* we might be on a docking station */
register_hotplug_dock_device(handle,
&ata_acpi_dev_dock_ops,
dev, NULL, NULL);
}
}
}

/**
* ata_acpi_dissociate - dissociate ATA host from ACPI objects
* @host: target ATA host
Expand Down
2 changes: 2 additions & 0 deletions drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -6148,6 +6148,8 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht)
if (rc)
goto err_tadd;

ata_acpi_hotplug_init(host);

/* set cable, sata_spd_limit and report */
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
Expand Down
2 changes: 2 additions & 0 deletions drivers/ata/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ extern int ata_acpi_register(void);
extern void ata_acpi_unregister(void);
extern void ata_acpi_bind(struct ata_device *dev);
extern void ata_acpi_unbind(struct ata_device *dev);
extern void ata_acpi_hotplug_init(struct ata_host *host);
#else
static inline void ata_acpi_dissociate(struct ata_host *host) { }
static inline int ata_acpi_on_suspend(struct ata_port *ap) { return 0; }
Expand All @@ -134,6 +135,7 @@ static inline int ata_acpi_register(void) { return 0; }
static inline void ata_acpi_unregister(void) { }
static inline void ata_acpi_bind(struct ata_device *dev) { }
static inline void ata_acpi_unbind(struct ata_device *dev) { }
static inline void ata_acpi_hotplug_init(struct ata_host *host) {}
#endif

/* libata-scsi.c */
Expand Down
Loading

0 comments on commit 96c9dda

Please sign in to comment.