@@ -333,6 +333,17 @@ menuconfig ACPI_HOTPLUG
If your hardware and firmware do not support adding or removing
of system devices at runtime, you need not to enable this option.
+config ACPI_HOTPLUG_ENUM
+ tristate "ACPI Hotplug Slot Enumerator"
+ depends on ACPI_HOTPLUG
+ default y
+ help
+ This driver enumerates ACPI hotplug slots for ACPI based system
+ device hotplug.
+
+ To compile this driver as a module, choose M here:
+ the module will be called acpihp_enum.
+
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_ENUM) += acpihp_enum.o
+acpihp_enum-y = slot_enum.o
new file mode 100644
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2011 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2011 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2011 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 <acpi/acpi.h>
+#include <acpi/acpi_hotplug.h>
+
+static LIST_HEAD(slot_list);
+static LIST_HEAD(slot_id_list);
+
+struct acpihp_slot_id {
+ struct list_head node;
+ unsigned long instance_id;
+ enum acpihp_slot_type type;
+};
+
+static struct acpihp_slot_ops *slot_ops_curr;
+
+/*
+ * Array of platform specific enumeration methods.
+ * Entries in the array should be sorted by descending priority order.
+ */
+static struct acpihp_slot_ops *slot_ops_array[] = {
+ NULL
+};
+
+static void acpihp_enum_cleanup_slots(void);
+
+static int __init acpihp_get_parent_slot(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_WARN("fails to get ACPI root device.\n");
+ return -EINVAL;
+ }
+
+ do {
+ if (ACPI_FAILURE(acpi_get_parent(handle, &handle))) {
+ ACPIHP_DEBUG("fails to get parent device handle.\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_get_slot_state(struct acpihp_slot *slot)
+{
+ unsigned long long sta;
+
+ /* An hotplug slot must implement _STA method. */
+ if (ACPI_FAILURE(acpi_evaluate_integer(slot->handle, METHOD_NAME__STA,
+ NULL, &sta))) {
+ ACPIHP_DEBUG("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_enum_create_slot(acpi_handle handle)
+{
+ struct acpihp_slot *slot;
+
+ slot = acpihp_create_slot(handle, "TEMP");
+ if (!slot) {
+ ACPIHP_DEBUG("fails to allocate memory for hotplug slot.\n");
+ return -ENOMEM;
+ }
+
+ slot->slot_ops = slot_ops_curr;
+
+ if (acpihp_get_parent_slot(slot))
+ goto out;
+ if (acpihp_get_slot_state(slot))
+ goto out;
+ if (ACPI_FAILURE(acpihp_slot_get_capabilities(slot,
+ &slot->capabilities))) {
+ ACPIHP_DEBUG("fails to get slot capabilities.\n");
+ goto out;
+ }
+ if (ACPI_FAILURE(acpihp_mark_slot(handle, slot))) {
+ ACPIHP_DEBUG("fails to attach slot to ACPI device object.\n");
+ goto out;
+ }
+
+ list_add_tail(&slot->slot_list, &slot_list);
+
+ return 0;
+out:
+ acpihp_slot_put(slot);
+ return -EINVAL;
+}
+
+/*
+ * Scan hotplug slots for ACPI based system device hotplug.
+ * We only care about processor, memory, PCI host bridge and CONTAINER.
+ */
+static acpi_status __init acpihp_enum_scan_slot(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_enum_create_slot(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;
+}
+
+/*
+ * Get types of child devices connected to this slot.
+ * We only care about CPU, memory, PCI host bridge and CONTAINER here.
+ * Values used here must be in consistence with acpihp_enum_get_slot_type().
+ */
+static acpi_status __init
+acpihp_enum_get_dev_type(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ acpi_status status = AE_OK;
+ enum acpihp_dev_type type;
+ u32 *tp = (u32 *)rv;
+
+ 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;
+}
+
+/*
+ * Guess type of a hotplug slot according to child devices connecting to it.
+ */
+static enum acpihp_slot_type __init acpihp_enum_get_slot_type(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;
+ }
+}
+
+/*
+ * Guess type of a hotplug slot according to the device type of the
+ * corresponding ACPI object itself.
+ */
+static enum acpihp_slot_type __init
+acpihp_enum_check_slot_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;
+ }
+}
+
+static int __init acpihp_enum_generate_slot_name(struct acpihp_slot *slot)
+{
+ int found = 0;
+ struct list_head *list;
+ struct acpihp_slot_id *slot_id;
+ unsigned long long uid;
+
+ /* Respect firmware settings if _UID return an integer. */
+ if (ACPI_SUCCESS(acpi_evaluate_integer(slot->handle, METHOD_NAME__UID,
+ NULL, &uid)))
+ goto set_name;
+
+ if (slot->parent)
+ list = &slot->parent->slot_id_list;
+ else
+ list = &slot_id_list;
+
+ list_for_each_entry(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_DEBUG("fails to allocate slot instance ID.\n");
+ return -ENOMEM;
+ }
+ slot_id->type = slot->type;
+ list_add_tail(&slot_id->node, 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;
+}
+
+/*
+ * Generate a meaningful name for the slot according to devices connecting
+ * to this slot
+ */
+static int __init acpihp_enum_rename_slot(struct acpihp_slot *slot)
+{
+ u32 child_types = 0;
+
+ slot->type = acpihp_enum_check_slot_self(slot);
+ if (slot->type == ACPIHP_SLOT_TYPE_UNKNOWN) {
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, slot->handle,
+ ACPI_UINT32_MAX, acpihp_enum_get_dev_type,
+ NULL, NULL, (void **)&child_types);
+ acpi_walk_namespace(ACPI_TYPE_PROCESSOR, slot->handle,
+ ACPI_UINT32_MAX, acpihp_enum_get_dev_type,
+ NULL, NULL, (void **)&child_types);
+ slot->type = acpihp_enum_get_slot_type(child_types);
+ }
+
+ if (acpihp_enum_generate_slot_name(slot))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void __init acpihp_enum_rename_and_register_slots(void)
+{
+ struct acpihp_slot *slot;
+
+ list_for_each_entry(slot, &slot_list, slot_list) {
+ /* generate a meaningful name for this slot */
+ if (acpihp_enum_rename_slot(slot))
+ continue;
+
+ if (acpihp_register_slot(slot))
+ ACPIHP_DEBUG("fails to register slot %s.\n",
+ slot->name);
+ }
+}
+
+static int __init acpihp_enum_generate_slots(void)
+{
+ acpi_status status;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpihp_enum_scan_slot,
+ NULL, NULL, NULL);
+ if (!ACPI_SUCCESS(status))
+ goto out_err;
+
+ status = acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpihp_enum_scan_slot,
+ NULL, NULL, NULL);
+ if (!ACPI_SUCCESS(status))
+ goto out_err;
+
+ acpihp_enum_rename_and_register_slots();
+
+ return 0;
+
+out_err:
+ ACPIHP_DEBUG("fails to scan hotplug slots.\n");
+ acpihp_enum_cleanup_slots();
+
+ return -ENOTSUPP;
+}
+
+static void acpihp_enum_unregister_slots(void)
+{
+ struct acpihp_slot *slot, *tmp;
+ struct acpihp_slot_id *slot_id, *slot_id_safe;
+
+ list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
+ acpihp_unregister_slot(slot);
+ list_del_init(&slot->slot_list);
+ acpihp_unmark_slot(slot->handle);
+ list_for_each_entry_safe(slot_id, slot_id_safe,
+ &slot->slot_id_list, node) {
+ list_del(&slot_id->node);
+ kfree(slot_id);
+ }
+ acpihp_slot_put(slot);
+ }
+}
+
+static void acpihp_enum_cleanup_slots(void)
+{
+ struct acpihp_slot_id *slot_id, *tmp;
+
+ acpihp_enum_unregister_slots();
+ list_for_each_entry_safe(slot_id, tmp, &slot_id_list, node) {
+ list_del(&slot_id->node);
+ kfree(slot_id);
+ }
+}
+
+static int __init acpihp_enum_init(void)
+{
+ int i;
+ int retval;
+
+ /* probe for suitable enumerator. */
+ 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 ACPI hotplug slot found.\n");
+ return -ENXIO;
+ }
+
+ retval = acpihp_register_class();
+ if (retval != 0) {
+ ACPIHP_DEBUG("fails to register ACPI hotplug slot class.\n");
+ goto out_fini;
+ }
+
+ retval = acpihp_enum_generate_slots();
+ if (retval != 0) {
+ ACPIHP_DEBUG("fails to enumerate ACPI hotplug slots.\n");
+ goto out_unregister_class;
+ }
+
+ /* Back out if no ACPI hotplug slot found. */
+ if (list_empty(&slot_list)) {
+ ACPIHP_DEBUG("no ACPI hotplug slot found.\n");
+ retval = -ENODEV;
+ goto out_unregister_class;
+ }
+
+ return 0;
+
+out_unregister_class:
+ acpihp_unregister_class();
+out_fini:
+ slot_ops_curr->fini();
+ ACPIHP_DEBUG("fails to initialize hotplug slot enumerator.\n");
+
+ return retval;
+}
+
+static void __exit acpihp_enum_exit(void)
+{
+ acpihp_enum_cleanup_slots();
+ acpihp_unregister_class();
+ slot_ops_curr->fini();
+}
+
+module_init(acpihp_enum_init);
+module_exit(acpihp_enum_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiang Liu <jiang.liu@huawei.com>");
+MODULE_AUTHOR("Gaohuai Han <hangaohuai@huawei.com>");