@@ -334,6 +334,25 @@ menuconfig ACPI_HOTPLUG
If your hardware platform does not support system device dynamic
reconfiguration at runtime, you need not to enable this option.
+config ACPI_HOTPLUG_SLOT
+ tristate "System Device Hotplug Slot Enumerator"
+ depends on ACPI_HOTPLUG
+ default m
+ help
+ ACPI system device hotplug slot is an abstraction of ACPI based
+ system device dynamic reconfiguration control points. On load,
+ this driver enumerates system device hotplug slots by wakling the
+ ACPI namespace and provides platform specific methods to control
+ those hotplug slots.
+
+ By default, this driver detects system device hotplug slots by
+ checking avaliability of ACPI _EJ0 method. You may pass a module
+ parameter "fake_slot=0xf" to enable faking hotplug slots on
+ platforms without hardware dynamic reconfiguration capabilities.
+
+ To compile this driver as a module, choose M here:
+ the module will be called acpihp_slot.
+
config ACPI_CONTAINER
tristate "Container and Module Devices (EXPERIMENTAL)"
depends on EXPERIMENTAL
@@ -4,3 +4,6 @@
obj-$(CONFIG_ACPI_HOTPLUG) += acpihp.o
acpihp-y = core.o
+
+obj-$(CONFIG_ACPI_HOTPLUG_SLOT) += acpihp_slot.o
+acpihp_slot-y = slot.o
new file mode 100644
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Gaohuai Han <hangaohuai@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include "acpihp.h"
+
+struct acpihp_slot_id {
+ struct list_head node;
+ enum acpihp_slot_type type;
+ unsigned long instance_id;
+};
+
+static LIST_HEAD(slot_list);
+static LIST_HEAD(slot_id_list);
+static struct acpihp_slot_ops *slot_ops_curr;
+
+/*
+ * Platform dependent slot drivers should be sorted in descending order.
+ * The first entry whose init() method returns success will be used.
+ */
+static struct acpihp_slot_ops *slot_ops_array[] = {
+ NULL
+};
+
+static acpi_status __init
+acpihp_slot_get_dev_type(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ u32 *tp = (u32 *)rv;
+ acpi_status status = AE_OK;
+ enum acpihp_dev_type type;
+
+ /* Only care about CPU, memory, PCI host bridge and CONTAINER */
+ if (!acpihp_dev_get_type(handle, &type)) {
+ switch (type) {
+ case ACPIHP_DEV_TYPE_CPU:
+ *tp |= 0x0001;
+ status = AE_CTRL_DEPTH;
+ break;
+ case ACPIHP_DEV_TYPE_MEM:
+ *tp |= 0x0002;
+ status = AE_CTRL_DEPTH;
+ break;
+ case ACPIHP_DEV_TYPE_HOST_BRIDGE:
+ *tp |= 0x0004;
+ status = AE_CTRL_DEPTH;
+ break;
+ case ACPIHP_DEV_TYPE_CONTAINER:
+ *tp |= 0x0008;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return status;
+}
+
+static enum acpihp_slot_type __init acpihp_slot_get_type_child(u32 dev_types)
+{
+ BUG_ON(dev_types > 15);
+
+ switch (dev_types) {
+ case 0:
+ /* Generic CONTAINER */
+ return ACPIHP_SLOT_TYPE_COMMON;
+ case 1:
+ /* Physical processor with logical CPUs */
+ return ACPIHP_SLOT_TYPE_CPU;
+ case 2:
+ /* Memory board/box with memory devices */
+ return ACPIHP_SLOT_TYPE_MEM;
+ case 3:
+ /* Physical processor with CPUs and memory controllers */
+ return ACPIHP_SLOT_TYPE_CPU;
+ case 4:
+ /* IO eXtension board/box with IO host bridges */
+ return ACPIHP_SLOT_TYPE_IOX;
+ case 7:
+ /* Physical processor with CPUs, IO host bridges and MCs. */
+ return ACPIHP_SLOT_TYPE_CPU;
+ case 8:
+ /* Generic CONTAINER */
+ return ACPIHP_SLOT_TYPE_COMMON;
+ case 9:
+ /* System board with physical processors */
+ return ACPIHP_SLOT_TYPE_SYSTEM_BOARD;
+ case 11:
+ /* System board with physical processors and memory */
+ return ACPIHP_SLOT_TYPE_SYSTEM_BOARD;
+ case 15:
+ /* Node with processor, memory and IO host bridge */
+ return ACPIHP_SLOT_TYPE_NODE;
+ default:
+ return ACPIHP_SLOT_TYPE_UNKNOWN;
+ }
+}
+
+static enum acpihp_slot_type __init
+acpihp_slot_get_type_self(struct acpihp_slot *slot)
+{
+ enum acpihp_dev_type type;
+
+ if (acpihp_dev_get_type(slot->handle, &type))
+ return ACPIHP_SLOT_TYPE_UNKNOWN;
+
+ switch (type) {
+ case ACPIHP_DEV_TYPE_CPU:
+ /* Logical CPU used in virtualization environment */
+ return ACPIHP_SLOT_TYPE_CPU;
+ case ACPIHP_DEV_TYPE_MEM:
+ /* Memory board with single memory device */
+ return ACPIHP_SLOT_TYPE_MEM;
+ case ACPIHP_DEV_TYPE_HOST_BRIDGE:
+ /* IO eXtension board/box with single IO host bridge */
+ return ACPIHP_SLOT_TYPE_IOX;
+ default:
+ return ACPIHP_SLOT_TYPE_UNKNOWN;
+ }
+}
+
+/*
+ * Generate a meaningful name for a hotplug slot according to types of ACPI
+ * devices which could be attached to the slot.
+ */
+static int __init acpihp_slot_generate_name(struct acpihp_slot *slot)
+{
+ int found = 0;
+ u32 child_types = 0;
+ unsigned long long uid;
+ struct acpihp_slot_id *slot_id;
+
+ /*
+ * Figure out slot type by checking types of ACPI devices which could
+ * be attached to the slot.
+ */
+ slot->type = acpihp_slot_get_type_self(slot);
+ if (slot->type == ACPIHP_SLOT_TYPE_UNKNOWN) {
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, slot->handle,
+ ACPI_UINT32_MAX, acpihp_slot_get_dev_type,
+ NULL, NULL, (void **)&child_types);
+ acpi_walk_namespace(ACPI_TYPE_PROCESSOR, slot->handle,
+ ACPI_UINT32_MAX, acpihp_slot_get_dev_type,
+ NULL, NULL, (void **)&child_types);
+ slot->type = acpihp_slot_get_type_child(child_types);
+ }
+
+ /* Respect firmware settings if ACPI _UID returns a valid value. */
+ if (ACPI_SUCCESS(acpi_evaluate_integer(slot->handle, METHOD_NAME__UID,
+ NULL, &uid)))
+ goto set_name;
+
+ /* Generate a unique pseudo instance ID */
+ list_for_each_entry(slot_id, &slot_id_list, node)
+ if (slot_id->type == slot->type) {
+ found = 1;
+ break;
+ }
+ if (!found) {
+ slot_id = kzalloc(sizeof(struct acpihp_slot_id), GFP_KERNEL);
+ if (!slot_id) {
+ ACPIHP_SLOT_WARN(slot,
+ "fails to allocate slot instance ID.\n");
+ return -ENOMEM;
+ }
+ slot_id->type = slot->type;
+ list_add_tail(&slot_id->node, &slot_id_list);
+ }
+ uid = slot_id->instance_id++;
+
+set_name:
+ snprintf(slot->name, sizeof(slot->name) - 1, "%s%02llx",
+ acpihp_get_slot_type_name(slot->type), uid);
+ dev_set_name(&slot->dev, "%s", slot->name);
+
+ return 0;
+}
+
+static int __init acpihp_slot_get_parent(struct acpihp_slot *slot)
+{
+ acpi_handle handle, root_handle;
+ struct acpihp_slot *tmp;
+
+ slot->parent = NULL;
+ handle = slot->handle;
+ if (ACPI_FAILURE(acpi_get_handle(NULL, ACPI_NS_ROOT_PATH,
+ &root_handle))) {
+ ACPIHP_SLOT_DEBUG(slot, "fails to get ACPI root device.\n");
+ return -ENODEV;
+ }
+
+ do {
+ if (ACPI_FAILURE(acpi_get_parent(handle, &handle))) {
+ ACPIHP_SLOT_DEBUG(slot,
+ "fails to get parent device.\n");
+ return -ENODEV;
+ }
+ list_for_each_entry(tmp, &slot_list, slot_list)
+ if (tmp->handle == handle) {
+ slot->parent = tmp;
+ return 0;
+ }
+ } while (handle != root_handle);
+
+ return 0;
+}
+
+static int __init acpihp_slot_get_state(struct acpihp_slot *slot)
+{
+ unsigned long long sta;
+
+ /* Hotplug slots must implement ACPI _STA method. */
+ if (ACPI_FAILURE(acpihp_slot_get_status(slot, &sta))) {
+ ACPIHP_SLOT_DEBUG(slot, "fails to execute _STA method.\n");
+ return -EINVAL;
+ }
+
+ if (!(sta & ACPI_STA_DEVICE_PRESENT))
+ slot->state = ACPIHP_SLOT_STATE_ABSENT;
+ else if ((sta & ACPI_STA_DEVICE_ENABLED) ||
+ (sta & ACPI_STA_DEVICE_FUNCTIONING))
+ slot->state = ACPIHP_SLOT_STATE_POWERED;
+ else
+ slot->state = ACPIHP_SLOT_STATE_PRESENT;
+
+ return 0;
+}
+
+static int __init acpihp_slot_create(acpi_handle handle)
+{
+ struct acpihp_slot *slot;
+
+ slot = acpihp_alloc_slot(handle, NULL);
+ if (!slot) {
+ ACPIHP_DEBUG("fails to allocate memory for slot %p.\n", handle);
+ return -ENOMEM;
+ }
+
+ slot->slot_ops = slot_ops_curr;
+ if (ACPI_FAILURE(slot_ops_curr->create(slot))) {
+ ACPIHP_DEBUG("fails to create slot for %p.\n", handle);
+ goto out_free;
+ }
+ if (acpihp_slot_get_parent(slot))
+ goto out_destroy;
+ if (acpihp_slot_get_state(slot))
+ goto out_destroy;
+ if (acpihp_slot_generate_name(slot))
+ goto out_destroy;
+
+ if (acpihp_register_slot(slot)) {
+ ACPIHP_SLOT_DEBUG(slot, "fails to register slot.\n");
+ goto out_destroy;
+ }
+ if (ACPI_FAILURE(acpihp_mark_slot(handle, slot))) {
+ ACPIHP_SLOT_DEBUG(slot,
+ "fails to attach slot to ACPI device object.\n");
+ goto out_unregister;
+ }
+
+ list_add_tail(&slot->slot_list, &slot_list);
+
+ return 0;
+
+out_unregister:
+ acpihp_unregister_slot(slot);
+out_destroy:
+ if (slot_ops_curr->destroy)
+ slot_ops_curr->destroy(slot);
+out_free:
+ acpihp_slot_put(slot);
+ return -ENODEV;
+}
+
+static acpi_status __init acpihp_slot_scan(acpi_handle handle, u32 lvl,
+ void *context, void **rv)
+{
+ enum acpihp_dev_type type;
+
+ if (acpihp_dev_get_type(handle, &type) ||
+ type == ACPIHP_DEV_TYPE_UNKNOWN)
+ return AE_OK;
+
+ if (ACPI_SUCCESS(slot_ops_curr->check(handle)))
+ acpihp_slot_create(handle);
+
+ /*
+ * Don't scan hotplug slots under PCI host bridges, they should be
+ * handled by acpiphp or pciehp drivers.
+ */
+ if (type == ACPIHP_DEV_TYPE_HOST_BRIDGE)
+ return AE_CTRL_DEPTH;
+
+ return AE_OK;
+}
+
+static int __init acpihp_slot_scan_slots(void)
+{
+ acpi_status status;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpihp_slot_scan,
+ NULL, NULL, NULL);
+ if (!ACPI_SUCCESS(status))
+ goto out_err;
+
+ status = acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpihp_slot_scan,
+ NULL, NULL, NULL);
+ if (ACPI_SUCCESS(status))
+ return 0;
+
+out_err:
+ ACPIHP_DEBUG("fails to scan ACPI hotplug slots.\n");
+
+ return -ENODEV;
+}
+
+static void acpihp_slot_cleanup(void)
+{
+ struct acpihp_slot *slot, *tmp;
+ struct acpihp_slot_id *slot_id, *tmp_id;
+
+ list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
+ acpihp_unmark_slot(slot->handle);
+ acpihp_unregister_slot(slot);
+ if (slot->slot_ops && slot->slot_ops->destroy)
+ slot->slot_ops->destroy(slot);
+ acpihp_slot_put(slot);
+ }
+
+ list_for_each_entry_safe(slot_id, tmp_id, &slot_id_list, node) {
+ list_del(&slot_id->node);
+ kfree(slot_id);
+ }
+}
+
+static int __init acpihp_slot_init(void)
+{
+ int i, retval;
+
+ /* probe for available platform specific slot driver. */
+ for (i = 0; slot_ops_array[i]; i++)
+ if (ACPI_SUCCESS(slot_ops_array[i]->init())) {
+ slot_ops_curr = slot_ops_array[i];
+ break;
+ }
+ if (slot_ops_curr == NULL) {
+ ACPIHP_DEBUG("no platform specific slot driver available.\n");
+ return -ENODEV;
+ }
+
+ retval = acpihp_core_init();
+ if (retval != 0) {
+ ACPIHP_DEBUG("fails to initialize ACPIHP core.\n");
+ goto out_fini;
+ }
+
+ if (acpihp_slot_scan_slots() != 0)
+ ACPIHP_DEBUG("fails to enumerate ACPI hotplug slots.\n");
+
+ /* Back out if no ACPI hotplug slot found. */
+ if (list_empty(&slot_list)) {
+ ACPIHP_DEBUG("no ACPI hotplug slot available.\n");
+ retval = -ENODEV;
+ goto out_core_fini;
+ }
+
+ return 0;
+
+out_core_fini:
+ acpihp_core_fini();
+out_fini:
+ if (slot_ops_curr && slot_ops_curr->fini)
+ slot_ops_curr->fini();
+ ACPIHP_DEBUG("fails to initialize ACPI hotplug slot driver.\n");
+
+ return retval;
+}
+
+static void __exit acpihp_slot_exit(void)
+{
+ acpihp_slot_cleanup();
+ acpihp_core_fini();
+ if (slot_ops_curr && slot_ops_curr->fini)
+ slot_ops_curr->fini();
+}
+
+module_init(acpihp_slot_init);
+module_exit(acpihp_slot_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiang Liu <jiang.liu@huawei.com>");
+MODULE_AUTHOR("Gaohuai Han <hangaohuai@huawei.com>");