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

Commit 96c9ddae authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'acpi-fixes'

* 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
parents 9e895ace 44521527
Loading
Loading
Loading
Loading
+96 −83
Original line number Diff line number Diff line
@@ -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
@@ -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);
@@ -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);
}

/**
@@ -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
@@ -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);
@@ -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;

@@ -597,13 +655,9 @@ 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;
}
@@ -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);
@@ -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);

@@ -993,30 +1046,6 @@ err_unregister:
	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
@@ -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;
@@ -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 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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);
	/*
+36 −1
Original line number Diff line number Diff line
@@ -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)
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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];
Loading