@@ -1,6 +1,7 @@
/*
* Copyright (C) 2012 Huawei Tech. Co., Ltd.
* Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Hanjun Guo <guohanjun@huawei.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
@@ -27,6 +28,98 @@
#include <acpi/acpi_hotplug.h>
#include "acpihp.h"
+int acpihp_dev_get_info(struct acpi_device *device,
+ struct acpihp_dev_info *info)
+{
+ int ret = -ENOSYS;
+
+ acpihp_dev_get_type(device->handle, &info->type);
+
+ device_lock(&device->dev);
+ if (device->driver && device->driver->ops.hp_ops &&
+ device->driver->ops.hp_ops->get_info)
+ ret = device->driver->ops.hp_ops->get_info(device, info);
+ else
+#if 0
+ /* Turn on this once all system devices have been converted
+ * to the new hotplug framework
+ */
+ info->status |= ACPIHP_DEV_STATUS_IRREMOVABLE;
+#else
+ ret = 0;
+#endif
+
+ if (device->driver)
+ info->status |= ACPIHP_DEV_STATUS_ATTACHED;
+ device_unlock(&device->dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpihp_dev_get_info);
+
+#define ACPIHP_DEFINE_FUNC1(method, def, err, type) \
+int acpihp_dev_##method(struct acpi_device *device, type val) \
+{ \
+ int ret; \
+ BUG_ON(device == NULL); \
+ device_lock(&device->dev); \
+ if (!device->driver || !device->driver->ops.hp_ops) \
+ ret = (err); \
+ else if (!device->driver->ops.hp_ops->method) \
+ ret = (def); \
+ else \
+ ret = device->driver->ops.hp_ops->method(device, val); \
+ device_unlock(&device->dev); \
+ return ret; \
+} \
+EXPORT_SYMBOL_GPL(acpihp_dev_##method)
+
+#define ACPIHP_DEFINE_FUNC2(method, def, err, type) \
+int acpihp_dev_##method(struct acpi_device *device, type val) \
+{ \
+ int ret = 0; \
+ BUG_ON(device == NULL); \
+ device_lock(&device->dev); \
+ if (!device->driver || !device->driver->ops.hp_ops) \
+ ret = (err); \
+ else if (!device->driver->ops.hp_ops->method) \
+ ret = (def); \
+ else \
+ device->driver->ops.hp_ops->method(device, val); \
+ device_unlock(&device->dev); \
+ return ret; \
+} \
+EXPORT_SYMBOL_GPL(acpihp_dev_##method)
+
+#define ACPIHP_DEFINE_FUNC3(method, def, err) \
+int acpihp_dev_##method(struct acpi_device *device) \
+{ \
+ int ret = 0; \
+ BUG_ON(device == NULL); \
+ device_lock(&device->dev); \
+ if (!device->driver || !device->driver->ops.hp_ops) \
+ ret = (err); \
+ else if (!device->driver->ops.hp_ops->method) \
+ ret = (def); \
+ else \
+ device->driver->ops.hp_ops->method(device);\
+ device_unlock(&device->dev); \
+ return ret; \
+} \
+EXPORT_SYMBOL_GPL(acpihp_dev_##method)
+
+ACPIHP_DEFINE_FUNC1(pre_configure, 0, 0, struct acpihp_cancel_context *);
+ACPIHP_DEFINE_FUNC1(configure, 0, -ENOSYS, struct acpihp_cancel_context *);
+ACPIHP_DEFINE_FUNC2(post_configure, 0, 0, enum acpihp_dev_post_cmd);
+
+ACPIHP_DEFINE_FUNC1(pre_release, 0, 0, struct acpihp_cancel_context *);
+ACPIHP_DEFINE_FUNC1(release, 0, 0, struct acpihp_cancel_context *);
+ACPIHP_DEFINE_FUNC2(post_release, 0, 0, enum acpihp_dev_post_cmd);
+
+ACPIHP_DEFINE_FUNC3(pre_unconfigure, 0, 0);
+ACPIHP_DEFINE_FUNC3(unconfigure, 0, -ENOSYS);
+ACPIHP_DEFINE_FUNC3(post_unconfigure, 0, 0);
+
/*
* When creating ACPI devices for hot-added system devices connecting to
* a slot, don't cross the slot boundary. Otherwise it will cause
@@ -109,6 +109,9 @@ struct acpi_device_ops {
acpi_op_bind bind;
acpi_op_unbind unbind;
acpi_op_notify notify;
+#ifdef CONFIG_ACPI_HOTPLUG
+ struct acpihp_dev_ops *hp_ops;
+#endif /* CONFIG_ACPI_HOTPLUG */
};
#define ACPI_DRIVER_ALL_NOTIFY_EVENTS 0x1 /* system AND device events */
@@ -63,6 +63,51 @@ struct acpihp_dev_node {
struct klist_node node;
};
+/* Status of ACPI system devices. */
+#define ACPIHP_DEV_STATUS_ATTACHED 0x1 /* Device driver attached */
+#define ACPIHP_DEV_STATUS_STARTED 0x2 /* Device started */
+#define ACPIHP_DEV_STATUS_IRREMOVABLE 0x10000 /* Device can't be removed */
+#define ACPIHP_DEV_STATUS_FAULT 0x20000 /* Device in fault state */
+
+struct acpihp_dev_info {
+ enum acpihp_dev_type type;
+ uint32_t status;
+};
+
+/* Rollback or commit changes in post_{confiure|release} */
+enum acpihp_dev_post_cmd {
+ ACPIHP_DEV_POST_CMD_ROLLBACK,
+ ACPIHP_DEV_POST_CMD_COMMIT
+};
+
+/*
+ * ACPI system device drivers may check cancellations of hotplug operations
+ * by invoking the callback.
+ */
+struct acpihp_cancel_context {
+ int (*check_cancel)(struct acpihp_cancel_context *ctx);
+};
+
+/*
+ * Callback hooks provided by ACPI device drivers to support system device
+ * hotplug. To support hotplug, an ACPI system device driver should implement
+ * configure(), unconfigure() and get_info() at a minimal.
+ */
+struct acpihp_dev_ops {
+ int (*get_info)(struct acpi_device *, struct acpihp_dev_info *info);
+ int (*pre_configure)(struct acpi_device *,
+ struct acpihp_cancel_context *);
+ int (*configure)(struct acpi_device *, struct acpihp_cancel_context *);
+ void (*post_configure)(struct acpi_device *, enum acpihp_dev_post_cmd);
+ int (*pre_release)(struct acpi_device *,
+ struct acpihp_cancel_context *);
+ int (*release)(struct acpi_device *, struct acpihp_cancel_context *);
+ void (*post_release)(struct acpi_device *, enum acpihp_dev_post_cmd);
+ void (*pre_unconfigure)(struct acpi_device *);
+ void (*unconfigure)(struct acpi_device *);
+ void (*post_unconfigure)(struct acpi_device *);
+};
+
/*
* ACPI hotplug slot is an abstraction of receptacles where a group of
* system devices could be attached, just like PCI slot in PCI hotplug.
@@ -173,6 +218,25 @@ extern int acpihp_core_init(void);
/* Deinitialize the ACPI based system device hotplug core logic */
extern void acpihp_core_fini(void);
+/* Interfaces to invoke ACPI device driver's hotplug callbacks. */
+extern int acpihp_dev_get_info(struct acpi_device *device,
+ struct acpihp_dev_info *info);
+extern int acpihp_dev_pre_configure(struct acpi_device *device,
+ struct acpihp_cancel_context *ctx);
+extern int acpihp_dev_configure(struct acpi_device *device,
+ struct acpihp_cancel_context *ctx);
+extern int acpihp_dev_post_configure(struct acpi_device *device,
+ enum acpihp_dev_post_cmd cmd);
+extern int acpihp_dev_pre_release(struct acpi_device *device,
+ struct acpihp_cancel_context *ctx);
+extern int acpihp_dev_release(struct acpi_device *device,
+ struct acpihp_cancel_context *ctx);
+extern int acpihp_dev_post_release(struct acpi_device *device,
+ enum acpihp_dev_post_cmd cmd);
+extern int acpihp_dev_pre_unconfigure(struct acpi_device *device);
+extern int acpihp_dev_unconfigure(struct acpi_device *device);
+extern int acpihp_dev_post_unconfigure(struct acpi_device *device);
+
/* Utility routines */
extern int acpihp_dev_get_type(acpi_handle handle, enum acpihp_dev_type *type);
extern bool acpihp_dev_match_ids(struct acpi_device_info *infop, char **ids);