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

Commit 408fb7f3 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'acpi-hotplug' into linux-next

* acpi-hotplug:
  ACPI / memhotplug: Remove info->failed bit
  ACPI / memhotplug: set info->enabled for memory present at boot time
  ACPI: Verify device status after eject
  acpi: remove reference to ACPI_HOTPLUG_IO
  ACPI: Update _OST handling for notify
  ACPI: Update PNPID match handling for notify
  ACPI: Update PNPID set/free interfaces
  ACPI: Remove acpi_device dependency in acpi_device_set_id()
  ACPI / hotplug: Make acpi_hotplug_profile_ktype static
  ACPI / scan: Make memory hotplug driver use struct acpi_scan_handler
  ACPI / container: Use hotplug profile user space interface
  ACPI / hotplug: Introduce user space interface for hotplug profiles
  ACPI / scan: Introduce acpi_scan_handler_matching()
  ACPI / container: Use common hotplug code
  ACPI / scan: Introduce common code for ACPI-based device hotplug
  ACPI / scan: Introduce acpi_scan_match_handler()
parents 31880c37 fd4655c2
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -18,6 +18,32 @@ Description:
		yoffset: The number of pixels between the top of the screen
			 and the top edge of the image.

What:		/sys/firmware/acpi/hotplug/
Date:		February 2013
Contact:	Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Description:
		There are separate hotplug profiles for different classes of
		devices supported by ACPI, such as containers, memory modules,
		processors, PCI root bridges etc.  A hotplug profile for a given
		class of devices is a collection of settings defining the way
		that class of devices will be handled by the ACPI core hotplug
		code.  Those profiles are represented in sysfs as subdirectories
		of /sys/firmware/acpi/hotplug/.

		The following setting is available to user space for each
		hotplug profile:

		enabled: If set, the ACPI core will handle notifications of
			hotplug events associated with the given class of
			devices and will allow those devices to be ejected with
			the help of the _EJ0 control method.  Unsetting it
			effectively disables hotplug for the correspoinding
			class of devices.

		The value of the above attribute is an integer number: 1 (set)
		or 0 (unset).  Attempts to write any other values to it will
		cause -EINVAL to be returned.

What:		/sys/firmware/acpi/interrupts/
Date:		February 2008
Contact:	Len Brown <lenb@kernel.org>
+2 −3
Original line number Diff line number Diff line
@@ -334,7 +334,7 @@ config X86_PM_TIMER

config ACPI_CONTAINER
	bool "Container and Module Devices"
	default (ACPI_HOTPLUG_MEMORY || ACPI_HOTPLUG_CPU || ACPI_HOTPLUG_IO)
	default (ACPI_HOTPLUG_MEMORY || ACPI_HOTPLUG_CPU)
	help
	  This driver supports ACPI Container and Module devices (IDs
	  ACPI0004, PNP0A05, and PNP0A06).
@@ -345,9 +345,8 @@ config ACPI_CONTAINER
	  the module will be called container.

config ACPI_HOTPLUG_MEMORY
	tristate "Memory Hotplug"
	bool "Memory Hotplug"
	depends on MEMORY_HOTPLUG
	default n
	help
	  This driver supports ACPI memory hotplug.  The driver
	  fields notifications on ACPI memory devices (PNP0C80),
+42 −286
Original line number Diff line number Diff line
/*
 * Copyright (C) 2004 Intel Corporation <naveen.b.s@intel.com>
 * Copyright (C) 2004, 2013 Intel Corporation
 * Author: Naveen B S <naveen.b.s@intel.com>
 * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
 *
 * All rights reserved.
 *
@@ -25,14 +27,10 @@
 * ranges.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/memory_hotplug.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <acpi/acpi_drivers.h>
#include <linux/memory_hotplug.h>

#include "internal.h"

#define ACPI_MEMORY_DEVICE_CLASS		"memory"
#define ACPI_MEMORY_DEVICE_HID			"PNP0C80"
@@ -44,31 +42,27 @@
#define 	PREFIX		"ACPI:memory_hp:"

ACPI_MODULE_NAME("acpi_memhotplug");
MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>");
MODULE_DESCRIPTION("Hotplug Mem Driver");
MODULE_LICENSE("GPL");

/* Memory Device States */
#define MEMORY_INVALID_STATE	0
#define MEMORY_POWER_ON_STATE	1
#define MEMORY_POWER_OFF_STATE	2

static int acpi_memory_device_add(struct acpi_device *device);
static int acpi_memory_device_remove(struct acpi_device *device);
static int acpi_memory_device_add(struct acpi_device *device,
				  const struct acpi_device_id *not_used);
static void acpi_memory_device_remove(struct acpi_device *device);

static const struct acpi_device_id memory_device_ids[] = {
	{ACPI_MEMORY_DEVICE_HID, 0},
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, memory_device_ids);

static struct acpi_driver acpi_memory_device_driver = {
	.name = "acpi_memhotplug",
	.class = ACPI_MEMORY_DEVICE_CLASS,
static struct acpi_scan_handler memory_device_handler = {
	.ids = memory_device_ids,
	.ops = {
		.add = acpi_memory_device_add,
		.remove = acpi_memory_device_remove,
	.attach = acpi_memory_device_add,
	.detach = acpi_memory_device_remove,
	.hotplug = {
		.enabled = true,
	},
};

@@ -79,7 +73,6 @@ struct acpi_memory_info {
	unsigned short caching;	/* memory cache attribute */
	unsigned short write_protect;	/* memory read/write attribute */
	unsigned int enabled:1;
	unsigned int failed:1;
};

struct acpi_memory_device {
@@ -153,48 +146,6 @@ acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
	return 0;
}

static int acpi_memory_get_device(acpi_handle handle,
				  struct acpi_memory_device **mem_device)
{
	struct acpi_device *device = NULL;
	int result = 0;

	acpi_scan_lock_acquire();

	acpi_bus_get_device(handle, &device);
	if (device)
		goto end;

	/*
	 * Now add the notified device.  This creates the acpi_device
	 * and invokes .add function
	 */
	result = acpi_bus_scan(handle);
	if (result) {
		acpi_handle_warn(handle, "ACPI namespace scan failed\n");
		result = -EINVAL;
		goto out;
	}
	result = acpi_bus_get_device(handle, &device);
	if (result) {
		acpi_handle_warn(handle, "Missing device object\n");
		result = -EINVAL;
		goto out;
	}

 end:
	*mem_device = acpi_driver_data(device);
	if (!(*mem_device)) {
		dev_err(&device->dev, "driver data not found\n");
		result = -ENODEV;
		goto out;
	}

 out:
	acpi_scan_lock_release();
	return result;
}

static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
{
	unsigned long long current_status;
@@ -249,13 +200,11 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
		 * returns -EEXIST. If add_memory() returns the other error, it
		 * means that this memory block is not used by the kernel.
		 */
		if (result && result != -EEXIST) {
			info->failed = 1;
		if (result && result != -EEXIST)
			continue;
		}

		if (!result)
		info->enabled = 1;

		/*
		 * Add num_enable even if add_memory() returns -EEXIST, so the
		 * device is bound to this driver.
@@ -286,16 +235,8 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
	nid = acpi_get_node(mem_device->device->handle);

	list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
		if (info->failed)
			/* The kernel does not use this memory block */
			continue;

		if (!info->enabled)
			/*
			 * The kernel uses this memory block, but it may be not
			 * managed by us.
			 */
			return -EBUSY;
			continue;

		if (nid < 0)
			nid = memory_add_physaddr_to_nid(info->start_addr);
@@ -310,95 +251,21 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
	return result;
}

static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
{
	struct acpi_memory_device *mem_device;
	struct acpi_device *device;
	struct acpi_eject_event *ej_event = NULL;
	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
	acpi_status status;

	switch (event) {
	case ACPI_NOTIFY_BUS_CHECK:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "\nReceived BUS CHECK notification for device\n"));
		/* Fall Through */
	case ACPI_NOTIFY_DEVICE_CHECK:
		if (event == ACPI_NOTIFY_DEVICE_CHECK)
			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
					  "\nReceived DEVICE CHECK notification for device\n"));
		if (acpi_memory_get_device(handle, &mem_device)) {
			acpi_handle_err(handle, "Cannot find driver data\n");
			break;
		}

		ost_code = ACPI_OST_SC_SUCCESS;
		break;

	case ACPI_NOTIFY_EJECT_REQUEST:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "\nReceived EJECT REQUEST notification for device\n"));

		status = AE_ERROR;
		acpi_scan_lock_acquire();

		if (acpi_bus_get_device(handle, &device)) {
			acpi_handle_err(handle, "Device doesn't exist\n");
			goto unlock;
		}
		mem_device = acpi_driver_data(device);
		if (!mem_device) {
			acpi_handle_err(handle, "Driver Data is NULL\n");
			goto unlock;
		}

		ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL);
		if (!ej_event) {
			pr_err(PREFIX "No memory, dropping EJECT\n");
			goto unlock;
		}

		get_device(&device->dev);
		ej_event->device = device;
		ej_event->event = ACPI_NOTIFY_EJECT_REQUEST;
		/* The eject is carried out asynchronously. */
		status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device,
						 ej_event);
		if (ACPI_FAILURE(status)) {
			put_device(&device->dev);
			kfree(ej_event);
		}

 unlock:
		acpi_scan_lock_release();
		if (ACPI_SUCCESS(status))
			return;
	default:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Unsupported event [0x%x]\n", event));

		/* non-hotplug event; possibly handled by other handler */
		return;
	}

	/* Inform firmware that the hotplug operation has completed */
	(void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL);
}

static void acpi_memory_device_free(struct acpi_memory_device *mem_device)
{
	if (!mem_device)
		return;

	acpi_memory_free_device_resources(mem_device);
	mem_device->device->driver_data = NULL;
	kfree(mem_device);
}

static int acpi_memory_device_add(struct acpi_device *device)
static int acpi_memory_device_add(struct acpi_device *device,
				  const struct acpi_device_id *not_used)
{
	struct acpi_memory_device *mem_device;
	int result;
	struct acpi_memory_device *mem_device = NULL;


	if (!device)
		return -EINVAL;
@@ -423,147 +290,36 @@ static int acpi_memory_device_add(struct acpi_device *device)
	/* Set the device state */
	mem_device->state = MEMORY_POWER_ON_STATE;

	pr_debug("%s\n", acpi_device_name(device));
	result = acpi_memory_check_device(mem_device);
	if (result) {
		acpi_memory_device_free(mem_device);
		return 0;
	}

	if (!acpi_memory_check_device(mem_device)) {
		/* call add_memory func */
	result = acpi_memory_enable_device(mem_device);
	if (result) {
			dev_err(&device->dev,
				"Error in acpi_memory_enable_device\n");
		dev_err(&device->dev, "acpi_memory_enable_device() error\n");
		acpi_memory_device_free(mem_device);
		return -ENODEV;
	}
	}
	return result;

	dev_dbg(&device->dev, "Memory device configured by ACPI\n");
	return 1;
}

static int acpi_memory_device_remove(struct acpi_device *device)
static void acpi_memory_device_remove(struct acpi_device *device)
{
	struct acpi_memory_device *mem_device = NULL;
	int result;
	struct acpi_memory_device *mem_device;

	if (!device || !acpi_driver_data(device))
		return -EINVAL;
		return;

	mem_device = acpi_driver_data(device);

	result = acpi_memory_remove_memory(mem_device);
	if (result)
		return result;

	acpi_memory_remove_memory(mem_device);
	acpi_memory_device_free(mem_device);

	return 0;
}

/*
 * Helper function to check for memory device
 */
static acpi_status is_memory_device(acpi_handle handle)
void __init acpi_memory_hotplug_init(void)
{
	char *hardware_id;
	acpi_status status;
	struct acpi_device_info *info;

	status = acpi_get_object_info(handle, &info);
	if (ACPI_FAILURE(status))
		return status;

	if (!(info->valid & ACPI_VALID_HID)) {
		kfree(info);
		return AE_ERROR;
	acpi_scan_add_handler_with_hotplug(&memory_device_handler, "memory");
}

	hardware_id = info->hardware_id.string;
	if ((hardware_id == NULL) ||
	    (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
		status = AE_ERROR;

	kfree(info);
	return status;
}

static acpi_status
acpi_memory_register_notify_handler(acpi_handle handle,
				    u32 level, void *ctxt, void **retv)
{
	acpi_status status;


	status = is_memory_device(handle);
	if (ACPI_FAILURE(status))
		return AE_OK;	/* continue */

	status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
					     acpi_memory_device_notify, NULL);
	/* continue */
	return AE_OK;
}

static acpi_status
acpi_memory_deregister_notify_handler(acpi_handle handle,
				      u32 level, void *ctxt, void **retv)
{
	acpi_status status;


	status = is_memory_device(handle);
	if (ACPI_FAILURE(status))
		return AE_OK;	/* continue */

	status = acpi_remove_notify_handler(handle,
					    ACPI_SYSTEM_NOTIFY,
					    acpi_memory_device_notify);

	return AE_OK;	/* continue */
}

static int __init acpi_memory_device_init(void)
{
	int result;
	acpi_status status;


	result = acpi_bus_register_driver(&acpi_memory_device_driver);

	if (result < 0)
		return -ENODEV;

	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
				     ACPI_UINT32_MAX,
				     acpi_memory_register_notify_handler, NULL,
				     NULL, NULL);

	if (ACPI_FAILURE(status)) {
		ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
		acpi_bus_unregister_driver(&acpi_memory_device_driver);
		return -ENODEV;
	}

	return 0;
}

static void __exit acpi_memory_device_exit(void)
{
	acpi_status status;


	/*
	 * Adding this to un-install notification handlers for all the device
	 * handles.
	 */
	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
				     ACPI_UINT32_MAX,
				     acpi_memory_deregister_notify_handler, NULL,
				     NULL, NULL);

	if (ACPI_FAILURE(status))
		ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));

	acpi_bus_unregister_driver(&acpi_memory_device_driver);

	return;
}

module_init(acpi_memory_device_init);
module_exit(acpi_memory_device_exit);
+12 −138
Original line number Diff line number Diff line
/*
 * acpi_container.c  - ACPI Generic Container Driver
 * ($Revision: )
 * container.c  - ACPI Generic Container Driver
 *
 * Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com)
 * Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com)
 * Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com)
 * Copyright (C) 2004 Intel Corp.
 * Copyright (C) 2004 FUJITSU LIMITED
 * Copyright (C) 2004, 2013 Intel Corp.
 * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
@@ -26,14 +26,9 @@
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>

#include "internal.h"

#define PREFIX "ACPI: "

@@ -50,141 +45,20 @@ static const struct acpi_device_id container_device_ids[] = {
static int container_device_attach(struct acpi_device *device,
				   const struct acpi_device_id *not_used)
{
	/*
	 * FIXME: This is necessary, so that acpi_eject_store() doesn't return
	 * -ENODEV for containers.
	 */
	/* This is necessary for container hotplug to work. */
	return 1;
}

static struct acpi_scan_handler container_device_handler = {
static struct acpi_scan_handler container_handler = {
	.ids = container_device_ids,
	.attach = container_device_attach,
	.hotplug = {
		.enabled = true,
		.mode = AHM_CONTAINER,
	},
};

static int is_device_present(acpi_handle handle)
{
	acpi_handle temp;
	acpi_status status;
	unsigned long long sta;


	status = acpi_get_handle(handle, "_STA", &temp);
	if (ACPI_FAILURE(status))
		return 1;	/* _STA not found, assume device present */

	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
	if (ACPI_FAILURE(status))
		return 0;	/* Firmware error */

	return ((sta & ACPI_STA_DEVICE_PRESENT) == ACPI_STA_DEVICE_PRESENT);
}

static void container_notify_cb(acpi_handle handle, u32 type, void *context)
{
	struct acpi_device *device = NULL;
	int result;
	int present;
	acpi_status status;
	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */

	acpi_scan_lock_acquire();

	switch (type) {
	case ACPI_NOTIFY_BUS_CHECK:
		/* Fall through */
	case ACPI_NOTIFY_DEVICE_CHECK:
		pr_debug("Container driver received %s event\n",
		       (type == ACPI_NOTIFY_BUS_CHECK) ?
		       "ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK");

		present = is_device_present(handle);
		status = acpi_bus_get_device(handle, &device);
		if (!present) {
			if (ACPI_SUCCESS(status)) {
				/* device exist and this is a remove request */
				device->flags.eject_pending = 1;
				kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
				goto out;
			}
			break;
		}

		if (!ACPI_FAILURE(status) || device)
			break;

		result = acpi_bus_scan(handle);
		if (result) {
			acpi_handle_warn(handle, "Failed to add container\n");
			break;
		}
		result = acpi_bus_get_device(handle, &device);
		if (result) {
			acpi_handle_warn(handle, "Missing device object\n");
			break;
		}

		kobject_uevent(&device->dev.kobj, KOBJ_ONLINE);
		ost_code = ACPI_OST_SC_SUCCESS;
		break;

	case ACPI_NOTIFY_EJECT_REQUEST:
		if (!acpi_bus_get_device(handle, &device) && device) {
			device->flags.eject_pending = 1;
			kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
			goto out;
		}
		break;

	default:
		/* non-hotplug event; possibly handled by other handler */
		goto out;
	}

	/* Inform firmware that the hotplug operation has completed */
	(void) acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);

 out:
	acpi_scan_lock_release();
}

static bool is_container(acpi_handle handle)
{
	struct acpi_device_info *info;
	bool ret = false;

	if (ACPI_FAILURE(acpi_get_object_info(handle, &info)))
		return false;

	if (info->valid & ACPI_VALID_HID) {
		const struct acpi_device_id *id;

		for (id = container_device_ids; id->id[0]; id++) {
			ret = !strcmp((char *)id->id, info->hardware_id.string);
			if (ret)
				break;
		}
	}
	kfree(info);
	return ret;
}

static acpi_status acpi_container_register_notify_handler(acpi_handle handle,
							  u32 lvl, void *ctxt,
							  void **retv)
{
	if (is_container(handle))
		acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
					    container_notify_cb, NULL);

	return AE_OK;
}

void __init acpi_container_init(void)
{
	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
			    acpi_container_register_notify_handler, NULL,
			    NULL, NULL);

	acpi_scan_add_handler(&container_device_handler);
	acpi_scan_add_handler_with_hotplug(&container_handler, "container");
}
+12 −1
Original line number Diff line number Diff line
@@ -41,6 +41,17 @@ void acpi_container_init(void);
#else
static inline void acpi_container_init(void) {}
#endif
#ifdef CONFIG_ACPI_HOTPLUG_MEMORY
void acpi_memory_hotplug_init(void);
#else
static inline void acpi_memory_hotplug_init(void) {}
#endif

void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug,
				    const char *name);
int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
				       const char *hotplug_profile_name);
void acpi_scan_hotplug_enabled(struct acpi_hotplug_profile *hotplug, bool val);

#ifdef CONFIG_DEBUG_FS
extern struct dentry *acpi_debugfs_dir;
@@ -60,7 +71,7 @@ int acpi_device_add(struct acpi_device *device,
void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
			     int type, unsigned long long sta);
void acpi_device_add_finalize(struct acpi_device *device);
void acpi_free_ids(struct acpi_device *device);
void acpi_free_pnp_ids(struct acpi_device_pnp *pnp);

/* --------------------------------------------------------------------------
                                  Power Resource
Loading