mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 22:51:35 +00:00
Merge branch 'acpi-hotplug'
* acpi-hotplug: ACPI / scan: ACPI device object sysfs attribute for _STA evaluation ACPI / hotplug / driver core: Handle containers in a special way ACPI / hotplug: Add demand_offline hotplug profile flag ACPI / bind: Move acpi_get_child() to drivers/ide/ide-acpi.c ACPI / bind: Pass struct acpi_device pointer to acpi_bind_one() ACPI / bind: Rework struct acpi_bus_type ACPI / bind: Redefine acpi_preset_companion() ACPI / bind: Redefine acpi_get_child() PCI / ACPI: Use acpi_find_child_device() for child devices lookup ACPI / bind: Simplify child device lookups ACPI / scan: Use direct recurrence for device hierarchy walks ACPI: Introduce acpi_set_device_status() ACPI / hotplug: Drop unfinished global notification handling routines ACPI / hotplug: Rework generic code to handle suprise removals ACPI / hotplug: Move container-specific code out of the core ACPI / hotplug: Make ACPI PCI root hotplug use common hotplug code ACPI / hotplug: Introduce common hotplug function acpi_device_hotplug() ACPI / hotplug: Do not fail bus and device checks for disabled hotplug ACPI / scan: Add acpi_device objects for all device nodes in the namespace ACPI / scan: Define non-empty device removal handler
This commit is contained in:
commit
25d412d932
@ -235,10 +235,6 @@ Wysocki <rafael.j.wysocki@intel.com>.
|
||||
named object's type in the second column). In that case the object's
|
||||
directory in sysfs will contain the 'path' attribute whose value is
|
||||
the full path to the node from the namespace root.
|
||||
struct acpi_device objects are created for the ACPI namespace nodes
|
||||
whose _STA control methods return PRESENT or FUNCTIONING. The power
|
||||
resource nodes or nodes without _STA are assumed to be both PRESENT
|
||||
and FUNCTIONING.
|
||||
F:
|
||||
The struct acpi_device object is created for a fixed hardware
|
||||
feature (as indicated by the fixed feature flag's name in the second
|
||||
@ -340,7 +336,7 @@ Wysocki <rafael.j.wysocki@intel.com>.
|
||||
| +-------------+-------+----------------+
|
||||
| |
|
||||
| | +- - - - - - - +- - - - - - +- - - - - - - -+
|
||||
| +-| * PNP0C0D:00 | \_SB_.LID0 | acpi:PNP0C0D: |
|
||||
| +-| PNP0C0D:00 | \_SB_.LID0 | acpi:PNP0C0D: |
|
||||
| | +- - - - - - - +- - - - - - +- - - - - - - -+
|
||||
| |
|
||||
| | +------------+------------+-----------------------+
|
||||
@ -390,6 +386,3 @@ Wysocki <rafael.j.wysocki@intel.com>.
|
||||
attribute (as described earlier in this document).
|
||||
NOTE: N/A indicates the device object does not have the 'path' or the
|
||||
'modalias' attribute.
|
||||
NOTE: The PNP0C0D device listed above is highlighted (marked by "*")
|
||||
to indicate it will be created only when its _STA methods return
|
||||
PRESENT or FUNCTIONING.
|
||||
|
@ -180,14 +180,14 @@ static unsigned long acpi_meminfo_end_pfn(struct acpi_memory_info *info)
|
||||
|
||||
static int acpi_bind_memblk(struct memory_block *mem, void *arg)
|
||||
{
|
||||
return acpi_bind_one(&mem->dev, (acpi_handle)arg);
|
||||
return acpi_bind_one(&mem->dev, arg);
|
||||
}
|
||||
|
||||
static int acpi_bind_memory_blocks(struct acpi_memory_info *info,
|
||||
acpi_handle handle)
|
||||
struct acpi_device *adev)
|
||||
{
|
||||
return walk_memory_range(acpi_meminfo_start_pfn(info),
|
||||
acpi_meminfo_end_pfn(info), (void *)handle,
|
||||
acpi_meminfo_end_pfn(info), adev,
|
||||
acpi_bind_memblk);
|
||||
}
|
||||
|
||||
@ -197,8 +197,7 @@ static int acpi_unbind_memblk(struct memory_block *mem, void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_unbind_memory_blocks(struct acpi_memory_info *info,
|
||||
acpi_handle handle)
|
||||
static void acpi_unbind_memory_blocks(struct acpi_memory_info *info)
|
||||
{
|
||||
walk_memory_range(acpi_meminfo_start_pfn(info),
|
||||
acpi_meminfo_end_pfn(info), NULL, acpi_unbind_memblk);
|
||||
@ -242,9 +241,9 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
|
||||
if (result && result != -EEXIST)
|
||||
continue;
|
||||
|
||||
result = acpi_bind_memory_blocks(info, handle);
|
||||
result = acpi_bind_memory_blocks(info, mem_device->device);
|
||||
if (result) {
|
||||
acpi_unbind_memory_blocks(info, handle);
|
||||
acpi_unbind_memory_blocks(info);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -285,7 +284,7 @@ static void acpi_memory_remove_memory(struct acpi_memory_device *mem_device)
|
||||
if (nid == NUMA_NO_NODE)
|
||||
nid = memory_add_physaddr_to_nid(info->start_addr);
|
||||
|
||||
acpi_unbind_memory_blocks(info, handle);
|
||||
acpi_unbind_memory_blocks(info);
|
||||
remove_memory(nid, info->start_addr, info->length);
|
||||
list_del(&info->list);
|
||||
kfree(info);
|
||||
|
@ -395,7 +395,7 @@ static int acpi_processor_add(struct acpi_device *device,
|
||||
goto err;
|
||||
}
|
||||
|
||||
result = acpi_bind_one(dev, pr->handle);
|
||||
result = acpi_bind_one(dev, device);
|
||||
if (result)
|
||||
goto err;
|
||||
|
||||
|
@ -50,9 +50,6 @@ struct acpi_device *acpi_root;
|
||||
struct proc_dir_entry *acpi_root_dir;
|
||||
EXPORT_SYMBOL(acpi_root_dir);
|
||||
|
||||
#define STRUCT_TO_INT(s) (*((int*)&s))
|
||||
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
static int set_copy_dsdt(const struct dmi_system_id *id)
|
||||
{
|
||||
@ -113,18 +110,16 @@ int acpi_bus_get_status(struct acpi_device *device)
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
STRUCT_TO_INT(device->status) = (int) sta;
|
||||
acpi_set_device_status(device, sta);
|
||||
|
||||
if (device->status.functional && !device->status.present) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]: "
|
||||
"functional but not present;\n",
|
||||
device->pnp.bus_id,
|
||||
(u32) STRUCT_TO_INT(device->status)));
|
||||
device->pnp.bus_id, (u32)sta));
|
||||
}
|
||||
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n",
|
||||
device->pnp.bus_id,
|
||||
(u32) STRUCT_TO_INT(device->status)));
|
||||
device->pnp.bus_id, (u32)sta));
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_bus_get_status);
|
||||
@ -337,58 +332,6 @@ static void acpi_bus_osc_support(void)
|
||||
Notification Handling
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
static void acpi_bus_check_device(acpi_handle handle)
|
||||
{
|
||||
struct acpi_device *device;
|
||||
acpi_status status;
|
||||
struct acpi_device_status old_status;
|
||||
|
||||
if (acpi_bus_get_device(handle, &device))
|
||||
return;
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
old_status = device->status;
|
||||
|
||||
/*
|
||||
* Make sure this device's parent is present before we go about
|
||||
* messing with the device.
|
||||
*/
|
||||
if (device->parent && !device->parent->status.present) {
|
||||
device->status = device->parent->status;
|
||||
return;
|
||||
}
|
||||
|
||||
status = acpi_bus_get_status(device);
|
||||
if (ACPI_FAILURE(status))
|
||||
return;
|
||||
|
||||
if (STRUCT_TO_INT(old_status) == STRUCT_TO_INT(device->status))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Device Insertion/Removal
|
||||
*/
|
||||
if ((device->status.present) && !(old_status.present)) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device insertion detected\n"));
|
||||
/* TBD: Handle device insertion */
|
||||
} else if (!(device->status.present) && (old_status.present)) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device removal detected\n"));
|
||||
/* TBD: Handle device removal */
|
||||
}
|
||||
}
|
||||
|
||||
static void acpi_bus_check_scope(acpi_handle handle)
|
||||
{
|
||||
/* Status Change? */
|
||||
acpi_bus_check_device(handle);
|
||||
|
||||
/*
|
||||
* TBD: Enumerate child devices within this device's scope and
|
||||
* run acpi_bus_check_device()'s on them.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_bus_notify
|
||||
* ---------------
|
||||
@ -405,19 +348,11 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
|
||||
switch (type) {
|
||||
|
||||
case ACPI_NOTIFY_BUS_CHECK:
|
||||
acpi_bus_check_scope(handle);
|
||||
/*
|
||||
* TBD: We'll need to outsource certain events to non-ACPI
|
||||
* drivers via the device manager (device.c).
|
||||
*/
|
||||
/* TBD */
|
||||
break;
|
||||
|
||||
case ACPI_NOTIFY_DEVICE_CHECK:
|
||||
acpi_bus_check_device(handle);
|
||||
/*
|
||||
* TBD: We'll need to outsource certain events to non-ACPI
|
||||
* drivers via the device manager (device.c).
|
||||
*/
|
||||
/* TBD */
|
||||
break;
|
||||
|
||||
case ACPI_NOTIFY_DEVICE_WAKE:
|
||||
|
@ -27,8 +27,7 @@
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include <linux/container.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@ -44,19 +43,65 @@ static const struct acpi_device_id container_device_ids[] = {
|
||||
{"", 0},
|
||||
};
|
||||
|
||||
static int container_device_attach(struct acpi_device *device,
|
||||
static int acpi_container_offline(struct container_dev *cdev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&cdev->dev);
|
||||
struct acpi_device *child;
|
||||
|
||||
/* Check all of the dependent devices' physical companions. */
|
||||
list_for_each_entry(child, &adev->children, node)
|
||||
if (!acpi_scan_is_offline(child, false))
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_container_release(struct device *dev)
|
||||
{
|
||||
kfree(to_container_dev(dev));
|
||||
}
|
||||
|
||||
static int container_device_attach(struct acpi_device *adev,
|
||||
const struct acpi_device_id *not_used)
|
||||
{
|
||||
/* This is necessary for container hotplug to work. */
|
||||
struct container_dev *cdev;
|
||||
struct device *dev;
|
||||
int ret;
|
||||
|
||||
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
|
||||
if (!cdev)
|
||||
return -ENOMEM;
|
||||
|
||||
cdev->offline = acpi_container_offline;
|
||||
dev = &cdev->dev;
|
||||
dev->bus = &container_subsys;
|
||||
dev_set_name(dev, "%s", dev_name(&adev->dev));
|
||||
ACPI_COMPANION_SET(dev, adev);
|
||||
dev->release = acpi_container_release;
|
||||
ret = device_register(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adev->driver_data = dev;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void container_device_detach(struct acpi_device *adev)
|
||||
{
|
||||
struct device *dev = acpi_driver_data(adev);
|
||||
|
||||
adev->driver_data = NULL;
|
||||
if (dev)
|
||||
device_unregister(dev);
|
||||
}
|
||||
|
||||
static struct acpi_scan_handler container_handler = {
|
||||
.ids = container_device_ids,
|
||||
.attach = container_device_attach,
|
||||
.detach = container_device_detach,
|
||||
.hotplug = {
|
||||
.enabled = true,
|
||||
.mode = AHM_CONTAINER,
|
||||
.demand_offline = true,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -256,6 +256,8 @@ int acpi_bus_init_power(struct acpi_device *device)
|
||||
return -EINVAL;
|
||||
|
||||
device->power.state = ACPI_STATE_UNKNOWN;
|
||||
if (!acpi_device_is_present(device))
|
||||
return 0;
|
||||
|
||||
result = acpi_device_get_power(device, &state);
|
||||
if (result)
|
||||
@ -302,15 +304,18 @@ int acpi_device_fix_up_power(struct acpi_device *device)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int acpi_bus_update_power(acpi_handle handle, int *state_p)
|
||||
int acpi_device_update_power(struct acpi_device *device, int *state_p)
|
||||
{
|
||||
struct acpi_device *device;
|
||||
int state;
|
||||
int result;
|
||||
|
||||
result = acpi_bus_get_device(handle, &device);
|
||||
if (result)
|
||||
if (device->power.state == ACPI_STATE_UNKNOWN) {
|
||||
result = acpi_bus_init_power(device);
|
||||
if (!result && state_p)
|
||||
*state_p = device->power.state;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
result = acpi_device_get_power(device, &state);
|
||||
if (result)
|
||||
@ -338,6 +343,15 @@ int acpi_bus_update_power(acpi_handle handle, int *state_p)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acpi_bus_update_power(acpi_handle handle, int *state_p)
|
||||
{
|
||||
struct acpi_device *device;
|
||||
int result;
|
||||
|
||||
result = acpi_bus_get_device(handle, &device);
|
||||
return result ? result : acpi_device_update_power(device, state_p);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_bus_update_power);
|
||||
|
||||
bool acpi_bus_power_manageable(acpi_handle handle)
|
||||
|
@ -323,14 +323,11 @@ static int dock_present(struct dock_station *ds)
|
||||
*/
|
||||
static void dock_create_acpi_device(acpi_handle handle)
|
||||
{
|
||||
struct acpi_device *device;
|
||||
struct acpi_device *device = NULL;
|
||||
int ret;
|
||||
|
||||
if (acpi_bus_get_device(handle, &device)) {
|
||||
/*
|
||||
* no device created for this object,
|
||||
* so we should create one.
|
||||
*/
|
||||
acpi_bus_get_device(handle, &device);
|
||||
if (!acpi_device_enumerated(device)) {
|
||||
ret = acpi_bus_scan(handle);
|
||||
if (ret)
|
||||
pr_debug("error adding bus, %x\n", -ret);
|
||||
|
@ -37,7 +37,7 @@ int register_acpi_bus_type(struct acpi_bus_type *type)
|
||||
{
|
||||
if (acpi_disabled)
|
||||
return -ENODEV;
|
||||
if (type && type->match && type->find_device) {
|
||||
if (type && type->match && type->find_companion) {
|
||||
down_write(&bus_type_sem);
|
||||
list_add_tail(&type->list, &bus_type_list);
|
||||
up_write(&bus_type_sem);
|
||||
@ -82,109 +82,74 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
|
||||
#define FIND_CHILD_MIN_SCORE 1
|
||||
#define FIND_CHILD_MAX_SCORE 2
|
||||
|
||||
static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used,
|
||||
void *not_used, void **ret_p)
|
||||
{
|
||||
struct acpi_device *adev = NULL;
|
||||
|
||||
acpi_bus_get_device(handle, &adev);
|
||||
if (adev) {
|
||||
*ret_p = handle;
|
||||
return AE_CTRL_TERMINATE;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static int do_find_child_checks(acpi_handle handle, bool is_bridge)
|
||||
static int find_child_checks(struct acpi_device *adev, bool check_children)
|
||||
{
|
||||
bool sta_present = true;
|
||||
unsigned long long sta;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
|
||||
status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
|
||||
if (status == AE_NOT_FOUND)
|
||||
sta_present = false;
|
||||
else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))
|
||||
return -ENODEV;
|
||||
|
||||
if (is_bridge) {
|
||||
void *test = NULL;
|
||||
if (check_children && list_empty(&adev->children))
|
||||
return -ENODEV;
|
||||
|
||||
/* Check if this object has at least one child device. */
|
||||
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
|
||||
acpi_dev_present, NULL, NULL, &test);
|
||||
if (!test)
|
||||
return -ENODEV;
|
||||
}
|
||||
return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE;
|
||||
}
|
||||
|
||||
struct find_child_context {
|
||||
u64 addr;
|
||||
bool is_bridge;
|
||||
acpi_handle ret;
|
||||
int ret_score;
|
||||
};
|
||||
|
||||
static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used,
|
||||
void *data, void **not_used)
|
||||
struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
|
||||
u64 address, bool check_children)
|
||||
{
|
||||
struct find_child_context *context = data;
|
||||
unsigned long long addr;
|
||||
acpi_status status;
|
||||
int score;
|
||||
struct acpi_device *adev, *ret = NULL;
|
||||
int ret_score = 0;
|
||||
|
||||
status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr);
|
||||
if (ACPI_FAILURE(status) || addr != context->addr)
|
||||
return AE_OK;
|
||||
if (!parent)
|
||||
return NULL;
|
||||
|
||||
if (!context->ret) {
|
||||
/* This is the first matching object. Save its handle. */
|
||||
context->ret = handle;
|
||||
return AE_OK;
|
||||
list_for_each_entry(adev, &parent->children, node) {
|
||||
unsigned long long addr;
|
||||
acpi_status status;
|
||||
int score;
|
||||
|
||||
status = acpi_evaluate_integer(adev->handle, METHOD_NAME__ADR,
|
||||
NULL, &addr);
|
||||
if (ACPI_FAILURE(status) || addr != address)
|
||||
continue;
|
||||
|
||||
if (!ret) {
|
||||
/* This is the first matching object. Save it. */
|
||||
ret = adev;
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* There is more than one matching device object with the same
|
||||
* _ADR value. That really is unexpected, so we are kind of
|
||||
* beyond the scope of the spec here. We have to choose which
|
||||
* one to return, though.
|
||||
*
|
||||
* First, check if the previously found object is good enough
|
||||
* and return it if so. Second, do the same for the object that
|
||||
* we've just found.
|
||||
*/
|
||||
if (!ret_score) {
|
||||
ret_score = find_child_checks(ret, check_children);
|
||||
if (ret_score == FIND_CHILD_MAX_SCORE)
|
||||
return ret;
|
||||
}
|
||||
score = find_child_checks(adev, check_children);
|
||||
if (score == FIND_CHILD_MAX_SCORE) {
|
||||
return adev;
|
||||
} else if (score > ret_score) {
|
||||
ret = adev;
|
||||
ret_score = score;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* There is more than one matching object with the same _ADR value.
|
||||
* That really is unexpected, so we are kind of beyond the scope of the
|
||||
* spec here. We have to choose which one to return, though.
|
||||
*
|
||||
* First, check if the previously found object is good enough and return
|
||||
* its handle if so. Second, check the same for the object that we've
|
||||
* just found.
|
||||
*/
|
||||
if (!context->ret_score) {
|
||||
score = do_find_child_checks(context->ret, context->is_bridge);
|
||||
if (score == FIND_CHILD_MAX_SCORE)
|
||||
return AE_CTRL_TERMINATE;
|
||||
else
|
||||
context->ret_score = score;
|
||||
}
|
||||
score = do_find_child_checks(handle, context->is_bridge);
|
||||
if (score == FIND_CHILD_MAX_SCORE) {
|
||||
context->ret = handle;
|
||||
return AE_CTRL_TERMINATE;
|
||||
} else if (score > context->ret_score) {
|
||||
context->ret = handle;
|
||||
context->ret_score = score;
|
||||
}
|
||||
return AE_OK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
acpi_handle acpi_find_child(acpi_handle parent, u64 addr, bool is_bridge)
|
||||
{
|
||||
if (parent) {
|
||||
struct find_child_context context = {
|
||||
.addr = addr,
|
||||
.is_bridge = is_bridge,
|
||||
};
|
||||
|
||||
acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, do_find_child,
|
||||
NULL, &context, NULL);
|
||||
return context.ret;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_find_child);
|
||||
EXPORT_SYMBOL_GPL(acpi_find_child_device);
|
||||
|
||||
static void acpi_physnode_link_name(char *buf, unsigned int node_id)
|
||||
{
|
||||
@ -195,9 +160,8 @@ static void acpi_physnode_link_name(char *buf, unsigned int node_id)
|
||||
strcpy(buf, PHYSICAL_NODE_STRING);
|
||||
}
|
||||
|
||||
int acpi_bind_one(struct device *dev, acpi_handle handle)
|
||||
int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
|
||||
{
|
||||
struct acpi_device *acpi_dev = NULL;
|
||||
struct acpi_device_physical_node *physical_node, *pn;
|
||||
char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
|
||||
struct list_head *physnode_list;
|
||||
@ -205,14 +169,12 @@ int acpi_bind_one(struct device *dev, acpi_handle handle)
|
||||
int retval = -EINVAL;
|
||||
|
||||
if (ACPI_COMPANION(dev)) {
|
||||
if (handle) {
|
||||
if (acpi_dev) {
|
||||
dev_warn(dev, "ACPI companion already set\n");
|
||||
return -EINVAL;
|
||||
} else {
|
||||
acpi_dev = ACPI_COMPANION(dev);
|
||||
}
|
||||
} else {
|
||||
acpi_bus_get_device(handle, &acpi_dev);
|
||||
}
|
||||
if (!acpi_dev)
|
||||
return -EINVAL;
|
||||
@ -322,29 +284,22 @@ int acpi_unbind_one(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_unbind_one);
|
||||
|
||||
void acpi_preset_companion(struct device *dev, acpi_handle parent, u64 addr)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
|
||||
if (!acpi_bus_get_device(acpi_get_child(parent, addr), &adev))
|
||||
ACPI_COMPANION_SET(dev, adev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_preset_companion);
|
||||
|
||||
static int acpi_platform_notify(struct device *dev)
|
||||
{
|
||||
struct acpi_bus_type *type = acpi_get_bus_type(dev);
|
||||
acpi_handle handle;
|
||||
int ret;
|
||||
|
||||
ret = acpi_bind_one(dev, NULL);
|
||||
if (ret && type) {
|
||||
ret = type->find_device(dev, &handle);
|
||||
if (ret) {
|
||||
struct acpi_device *adev;
|
||||
|
||||
adev = type->find_companion(dev);
|
||||
if (!adev) {
|
||||
DBG("Unable to get handle for %s\n", dev_name(dev));
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
ret = acpi_bind_one(dev, handle);
|
||||
ret = acpi_bind_one(dev, adev);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ int init_acpi_device_notify(void);
|
||||
int acpi_scan_init(void);
|
||||
void acpi_pci_root_init(void);
|
||||
void acpi_pci_link_init(void);
|
||||
void acpi_pci_root_hp_init(void);
|
||||
void acpi_processor_init(void);
|
||||
void acpi_platform_init(void);
|
||||
int acpi_sysfs_init(void);
|
||||
@ -73,6 +72,9 @@ void acpi_lpss_init(void);
|
||||
static inline void acpi_lpss_init(void) {}
|
||||
#endif
|
||||
|
||||
bool acpi_queue_hotplug_work(struct work_struct *work);
|
||||
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
Device Node Initialization / Removal
|
||||
-------------------------------------------------------------------------- */
|
||||
@ -85,9 +87,9 @@ 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_pnp_ids(struct acpi_device_pnp *pnp);
|
||||
int acpi_bind_one(struct device *dev, acpi_handle handle);
|
||||
int acpi_bind_one(struct device *dev, struct acpi_device *adev);
|
||||
int acpi_unbind_one(struct device *dev);
|
||||
void acpi_bus_device_eject(void *data, u32 ost_src);
|
||||
bool acpi_device_is_present(struct acpi_device *adev);
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
Power Resource
|
||||
@ -105,6 +107,8 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state);
|
||||
int acpi_power_on_resources(struct acpi_device *device, int state);
|
||||
int acpi_power_transition(struct acpi_device *device, int state);
|
||||
|
||||
int acpi_device_update_power(struct acpi_device *device, int *state_p);
|
||||
|
||||
int acpi_wakeup_device_init(void);
|
||||
void acpi_early_processor_set_pdc(void);
|
||||
|
||||
|
@ -1211,6 +1211,10 @@ acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src)
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
bool acpi_queue_hotplug_work(struct work_struct *work)
|
||||
{
|
||||
return queue_work(kacpi_hotplug_wq, work);
|
||||
}
|
||||
|
||||
acpi_status
|
||||
acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle)
|
||||
@ -1790,7 +1794,7 @@ acpi_status __init acpi_os_initialize1(void)
|
||||
{
|
||||
kacpid_wq = alloc_workqueue("kacpid", 0, 1);
|
||||
kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1);
|
||||
kacpi_hotplug_wq = alloc_workqueue("kacpi_hotplug", 0, 1);
|
||||
kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0);
|
||||
BUG_ON(!kacpid_wq);
|
||||
BUG_ON(!kacpi_notify_wq);
|
||||
BUG_ON(!kacpi_hotplug_wq);
|
||||
|
@ -49,6 +49,12 @@ static int acpi_pci_root_add(struct acpi_device *device,
|
||||
const struct acpi_device_id *not_used);
|
||||
static void acpi_pci_root_remove(struct acpi_device *device);
|
||||
|
||||
static int acpi_pci_root_scan_dependent(struct acpi_device *adev)
|
||||
{
|
||||
acpiphp_check_host_bridge(adev->handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ACPI_PCIE_REQ_SUPPORT (OSC_PCI_EXT_CONFIG_SUPPORT \
|
||||
| OSC_PCI_ASPM_SUPPORT \
|
||||
| OSC_PCI_CLOCK_PM_SUPPORT \
|
||||
@ -64,7 +70,8 @@ static struct acpi_scan_handler pci_root_handler = {
|
||||
.attach = acpi_pci_root_add,
|
||||
.detach = acpi_pci_root_remove,
|
||||
.hotplug = {
|
||||
.ignore = true,
|
||||
.enabled = true,
|
||||
.scan_dependent = acpi_pci_root_scan_dependent,
|
||||
},
|
||||
};
|
||||
|
||||
@ -622,116 +629,9 @@ static void acpi_pci_root_remove(struct acpi_device *device)
|
||||
void __init acpi_pci_root_init(void)
|
||||
{
|
||||
acpi_hest_init();
|
||||
|
||||
if (!acpi_pci_disabled) {
|
||||
pci_acpi_crs_quirks();
|
||||
acpi_scan_add_handler(&pci_root_handler);
|
||||
}
|
||||
}
|
||||
/* Support root bridge hotplug */
|
||||
|
||||
static void handle_root_bridge_insertion(acpi_handle handle)
|
||||
{
|
||||
struct acpi_device *device;
|
||||
|
||||
if (!acpi_bus_get_device(handle, &device)) {
|
||||
dev_printk(KERN_DEBUG, &device->dev,
|
||||
"acpi device already exists; ignoring notify\n");
|
||||
if (acpi_pci_disabled)
|
||||
return;
|
||||
}
|
||||
|
||||
if (acpi_bus_scan(handle))
|
||||
acpi_handle_err(handle, "cannot add bridge to acpi list\n");
|
||||
}
|
||||
|
||||
static void hotplug_event_root(void *data, u32 type)
|
||||
{
|
||||
acpi_handle handle = data;
|
||||
struct acpi_pci_root *root;
|
||||
|
||||
acpi_scan_lock_acquire();
|
||||
|
||||
root = acpi_pci_find_root(handle);
|
||||
|
||||
switch (type) {
|
||||
case ACPI_NOTIFY_BUS_CHECK:
|
||||
/* bus enumerate */
|
||||
acpi_handle_printk(KERN_DEBUG, handle,
|
||||
"Bus check notify on %s\n", __func__);
|
||||
if (root)
|
||||
acpiphp_check_host_bridge(handle);
|
||||
else
|
||||
handle_root_bridge_insertion(handle);
|
||||
|
||||
break;
|
||||
|
||||
case ACPI_NOTIFY_DEVICE_CHECK:
|
||||
/* device check */
|
||||
acpi_handle_printk(KERN_DEBUG, handle,
|
||||
"Device check notify on %s\n", __func__);
|
||||
if (!root)
|
||||
handle_root_bridge_insertion(handle);
|
||||
break;
|
||||
|
||||
case ACPI_NOTIFY_EJECT_REQUEST:
|
||||
/* request device eject */
|
||||
acpi_handle_printk(KERN_DEBUG, handle,
|
||||
"Device eject notify on %s\n", __func__);
|
||||
if (!root)
|
||||
break;
|
||||
|
||||
get_device(&root->device->dev);
|
||||
|
||||
acpi_scan_lock_release();
|
||||
|
||||
acpi_bus_device_eject(root->device, ACPI_NOTIFY_EJECT_REQUEST);
|
||||
return;
|
||||
default:
|
||||
acpi_handle_warn(handle,
|
||||
"notify_handler: unknown event type 0x%x\n",
|
||||
type);
|
||||
break;
|
||||
}
|
||||
|
||||
acpi_scan_lock_release();
|
||||
}
|
||||
|
||||
static void handle_hotplug_event_root(acpi_handle handle, u32 type,
|
||||
void *context)
|
||||
{
|
||||
acpi_hotplug_execute(hotplug_event_root, handle, type);
|
||||
}
|
||||
|
||||
static acpi_status __init
|
||||
find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
|
||||
{
|
||||
acpi_status status;
|
||||
int *count = (int *)context;
|
||||
|
||||
if (!acpi_is_root_bridge(handle))
|
||||
return AE_OK;
|
||||
|
||||
(*count)++;
|
||||
|
||||
status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
|
||||
handle_hotplug_event_root, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
acpi_handle_printk(KERN_DEBUG, handle,
|
||||
"notify handler is not installed, exit status: %u\n",
|
||||
(unsigned int)status);
|
||||
else
|
||||
acpi_handle_printk(KERN_DEBUG, handle,
|
||||
"notify handler is installed\n");
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
void __init acpi_pci_root_hp_init(void)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL);
|
||||
|
||||
printk(KERN_DEBUG "Found %d acpi root devices\n", num);
|
||||
pci_acpi_crs_quirks();
|
||||
acpi_scan_add_handler_with_hotplug(&pci_root_handler, "pci_root");
|
||||
}
|
||||
|
@ -12,11 +12,12 @@
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/nls.h>
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#define _COMPONENT ACPI_BUS_COMPONENT
|
||||
ACPI_MODULE_NAME("scan");
|
||||
#define STRUCT_TO_INT(s) (*((int*)&s))
|
||||
extern struct acpi_device *acpi_root;
|
||||
|
||||
#define ACPI_BUS_CLASS "system_bus"
|
||||
@ -25,6 +26,8 @@ extern struct acpi_device *acpi_root;
|
||||
|
||||
#define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent)
|
||||
|
||||
#define INVALID_ACPI_HANDLE ((acpi_handle)empty_zero_page)
|
||||
|
||||
/*
|
||||
* If set, devices will be hot-removed even if they cannot be put offline
|
||||
* gracefully (from the kernel's standpoint).
|
||||
@ -123,6 +126,26 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
|
||||
}
|
||||
static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
|
||||
|
||||
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent)
|
||||
{
|
||||
struct acpi_device_physical_node *pn;
|
||||
bool offline = true;
|
||||
|
||||
mutex_lock(&adev->physical_node_lock);
|
||||
|
||||
list_for_each_entry(pn, &adev->physical_node_list, node)
|
||||
if (device_supports_offline(pn->dev) && !pn->dev->offline) {
|
||||
if (uevent)
|
||||
kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE);
|
||||
|
||||
offline = false;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&adev->physical_node_lock);
|
||||
return offline;
|
||||
}
|
||||
|
||||
static acpi_status acpi_bus_offline(acpi_handle handle, u32 lvl, void *data,
|
||||
void **ret_p)
|
||||
{
|
||||
@ -193,19 +216,11 @@ static acpi_status acpi_bus_online(acpi_handle handle, u32 lvl, void *data,
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static int acpi_scan_hot_remove(struct acpi_device *device)
|
||||
static int acpi_scan_try_to_offline(struct acpi_device *device)
|
||||
{
|
||||
acpi_handle handle = device->handle;
|
||||
struct device *errdev;
|
||||
struct device *errdev = NULL;
|
||||
acpi_status status;
|
||||
unsigned long long sta;
|
||||
|
||||
/* If there is no handle, the device node has been unregistered. */
|
||||
if (!handle) {
|
||||
dev_dbg(&device->dev, "ACPI handle missing\n");
|
||||
put_device(&device->dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Carry out two passes here and ignore errors in the first pass,
|
||||
@ -216,7 +231,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
|
||||
*
|
||||
* If the first pass is successful, the second one isn't needed, though.
|
||||
*/
|
||||
errdev = NULL;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
|
||||
NULL, acpi_bus_offline, (void *)false,
|
||||
(void **)&errdev);
|
||||
@ -224,7 +238,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
|
||||
dev_warn(errdev, "Offline disabled.\n");
|
||||
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
|
||||
acpi_bus_online, NULL, NULL, NULL);
|
||||
put_device(&device->dev);
|
||||
return -EPERM;
|
||||
}
|
||||
acpi_bus_offline(handle, 0, (void *)false, (void **)&errdev);
|
||||
@ -243,20 +256,32 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
|
||||
acpi_walk_namespace(ACPI_TYPE_ANY, handle,
|
||||
ACPI_UINT32_MAX, acpi_bus_online,
|
||||
NULL, NULL, NULL);
|
||||
put_device(&device->dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_scan_hot_remove(struct acpi_device *device)
|
||||
{
|
||||
acpi_handle handle = device->handle;
|
||||
unsigned long long sta;
|
||||
acpi_status status;
|
||||
|
||||
if (device->handler->hotplug.demand_offline && !acpi_force_hot_remove) {
|
||||
if (!acpi_scan_is_offline(device, true))
|
||||
return -EBUSY;
|
||||
} else {
|
||||
int error = acpi_scan_try_to_offline(device);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||
"Hot-removing device %s...\n", dev_name(&device->dev)));
|
||||
|
||||
acpi_bus_trim(device);
|
||||
|
||||
/* Device node has been unregistered. */
|
||||
put_device(&device->dev);
|
||||
device = NULL;
|
||||
|
||||
acpi_evaluate_lck(handle, 0);
|
||||
/*
|
||||
* TBD: _EJD support.
|
||||
@ -283,115 +308,127 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void acpi_bus_device_eject(void *data, u32 ost_src)
|
||||
static int acpi_scan_device_not_present(struct acpi_device *adev)
|
||||
{
|
||||
struct acpi_device *device = data;
|
||||
acpi_handle handle = device->handle;
|
||||
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
|
||||
int error;
|
||||
|
||||
lock_device_hotplug();
|
||||
mutex_lock(&acpi_scan_lock);
|
||||
|
||||
if (ost_src == ACPI_NOTIFY_EJECT_REQUEST)
|
||||
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
|
||||
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
|
||||
|
||||
if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER)
|
||||
kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
|
||||
|
||||
error = acpi_scan_hot_remove(device);
|
||||
if (error == -EPERM) {
|
||||
goto err_support;
|
||||
} else if (error) {
|
||||
goto err_out;
|
||||
if (!acpi_device_enumerated(adev)) {
|
||||
dev_warn(&adev->dev, "Still not present\n");
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&acpi_scan_lock);
|
||||
unlock_device_hotplug();
|
||||
return;
|
||||
|
||||
err_support:
|
||||
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
|
||||
err_out:
|
||||
acpi_evaluate_hotplug_ost(handle, ost_src, ost_code, NULL);
|
||||
goto out;
|
||||
acpi_bus_trim(adev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_scan_bus_device_check(void *data, u32 ost_source)
|
||||
static int acpi_scan_device_check(struct acpi_device *adev)
|
||||
{
|
||||
acpi_handle handle = data;
|
||||
struct acpi_device *device = NULL;
|
||||
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
|
||||
int error;
|
||||
|
||||
lock_device_hotplug();
|
||||
mutex_lock(&acpi_scan_lock);
|
||||
|
||||
if (ost_source != ACPI_NOTIFY_BUS_CHECK) {
|
||||
acpi_bus_get_device(handle, &device);
|
||||
if (device) {
|
||||
dev_warn(&device->dev, "Attempt to re-insert\n");
|
||||
goto out;
|
||||
acpi_bus_get_status(adev);
|
||||
if (adev->status.present || adev->status.functional) {
|
||||
/*
|
||||
* This function is only called for device objects for which
|
||||
* matching scan handlers exist. The only situation in which
|
||||
* the scan handler is not attached to this device object yet
|
||||
* is when the device has just appeared (either it wasn't
|
||||
* present at all before or it was removed and then added
|
||||
* again).
|
||||
*/
|
||||
if (adev->handler) {
|
||||
dev_warn(&adev->dev, "Already enumerated\n");
|
||||
return -EALREADY;
|
||||
}
|
||||
error = acpi_bus_scan(adev->handle);
|
||||
if (error) {
|
||||
dev_warn(&adev->dev, "Namespace scan failure\n");
|
||||
return error;
|
||||
}
|
||||
if (!adev->handler) {
|
||||
dev_warn(&adev->dev, "Enumeration failure\n");
|
||||
error = -ENODEV;
|
||||
}
|
||||
} else {
|
||||
error = acpi_scan_device_not_present(adev);
|
||||
}
|
||||
error = acpi_bus_scan(handle);
|
||||
if (error) {
|
||||
acpi_handle_warn(handle, "Namespace scan failure\n");
|
||||
goto out;
|
||||
}
|
||||
error = acpi_bus_get_device(handle, &device);
|
||||
if (error) {
|
||||
acpi_handle_warn(handle, "Missing device node object\n");
|
||||
goto out;
|
||||
}
|
||||
ost_code = ACPI_OST_SC_SUCCESS;
|
||||
if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER)
|
||||
kobject_uevent(&device->dev.kobj, KOBJ_ONLINE);
|
||||
|
||||
out:
|
||||
acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL);
|
||||
mutex_unlock(&acpi_scan_lock);
|
||||
unlock_device_hotplug();
|
||||
return error;
|
||||
}
|
||||
|
||||
static void acpi_hotplug_unsupported(acpi_handle handle, u32 type)
|
||||
static int acpi_scan_bus_check(struct acpi_device *adev)
|
||||
{
|
||||
u32 ost_status;
|
||||
struct acpi_scan_handler *handler = adev->handler;
|
||||
struct acpi_device *child;
|
||||
int error;
|
||||
|
||||
switch (type) {
|
||||
acpi_bus_get_status(adev);
|
||||
if (!(adev->status.present || adev->status.functional)) {
|
||||
acpi_scan_device_not_present(adev);
|
||||
return 0;
|
||||
}
|
||||
if (handler && handler->hotplug.scan_dependent)
|
||||
return handler->hotplug.scan_dependent(adev);
|
||||
|
||||
error = acpi_bus_scan(adev->handle);
|
||||
if (error) {
|
||||
dev_warn(&adev->dev, "Namespace scan failure\n");
|
||||
return error;
|
||||
}
|
||||
list_for_each_entry(child, &adev->children, node) {
|
||||
error = acpi_scan_bus_check(child);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_device_hotplug(void *data, u32 src)
|
||||
{
|
||||
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
|
||||
struct acpi_device *adev = data;
|
||||
int error;
|
||||
|
||||
lock_device_hotplug();
|
||||
mutex_lock(&acpi_scan_lock);
|
||||
|
||||
/*
|
||||
* The device object's ACPI handle cannot become invalid as long as we
|
||||
* are holding acpi_scan_lock, but it may have become invalid before
|
||||
* that lock was acquired.
|
||||
*/
|
||||
if (adev->handle == INVALID_ACPI_HANDLE)
|
||||
goto out;
|
||||
|
||||
switch (src) {
|
||||
case ACPI_NOTIFY_BUS_CHECK:
|
||||
acpi_handle_debug(handle,
|
||||
"ACPI_NOTIFY_BUS_CHECK event: unsupported\n");
|
||||
ost_status = ACPI_OST_SC_INSERT_NOT_SUPPORTED;
|
||||
error = acpi_scan_bus_check(adev);
|
||||
break;
|
||||
case ACPI_NOTIFY_DEVICE_CHECK:
|
||||
acpi_handle_debug(handle,
|
||||
"ACPI_NOTIFY_DEVICE_CHECK event: unsupported\n");
|
||||
ost_status = ACPI_OST_SC_INSERT_NOT_SUPPORTED;
|
||||
error = acpi_scan_device_check(adev);
|
||||
break;
|
||||
case ACPI_NOTIFY_EJECT_REQUEST:
|
||||
acpi_handle_debug(handle,
|
||||
"ACPI_NOTIFY_EJECT_REQUEST event: unsupported\n");
|
||||
ost_status = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
|
||||
case ACPI_OST_EC_OSPM_EJECT:
|
||||
error = acpi_scan_hot_remove(adev);
|
||||
break;
|
||||
default:
|
||||
/* non-hotplug event; possibly handled by other handler */
|
||||
return;
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (!error)
|
||||
ost_code = ACPI_OST_SC_SUCCESS;
|
||||
|
||||
acpi_evaluate_hotplug_ost(handle, type, ost_status, NULL);
|
||||
out:
|
||||
acpi_evaluate_hotplug_ost(adev->handle, src, ost_code, NULL);
|
||||
put_device(&adev->dev);
|
||||
mutex_unlock(&acpi_scan_lock);
|
||||
unlock_device_hotplug();
|
||||
}
|
||||
|
||||
static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
|
||||
{
|
||||
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
|
||||
struct acpi_scan_handler *handler = data;
|
||||
struct acpi_device *adev;
|
||||
acpi_status status;
|
||||
|
||||
if (!handler->hotplug.enabled)
|
||||
return acpi_hotplug_unsupported(handle, type);
|
||||
if (acpi_bus_get_device(handle, &adev))
|
||||
goto err_out;
|
||||
|
||||
switch (type) {
|
||||
case ACPI_NOTIFY_BUS_CHECK:
|
||||
@ -402,27 +439,27 @@ static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *data)
|
||||
break;
|
||||
case ACPI_NOTIFY_EJECT_REQUEST:
|
||||
acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
|
||||
if (acpi_bus_get_device(handle, &adev))
|
||||
if (!handler->hotplug.enabled) {
|
||||
acpi_handle_err(handle, "Eject disabled\n");
|
||||
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
|
||||
goto err_out;
|
||||
|
||||
get_device(&adev->dev);
|
||||
status = acpi_hotplug_execute(acpi_bus_device_eject, adev, type);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return;
|
||||
|
||||
put_device(&adev->dev);
|
||||
goto err_out;
|
||||
}
|
||||
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
|
||||
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
|
||||
break;
|
||||
default:
|
||||
/* non-hotplug event; possibly handled by other handler */
|
||||
return;
|
||||
}
|
||||
status = acpi_hotplug_execute(acpi_scan_bus_device_check, handle, type);
|
||||
get_device(&adev->dev);
|
||||
status = acpi_hotplug_execute(acpi_device_hotplug, adev, type);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return;
|
||||
|
||||
put_device(&adev->dev);
|
||||
|
||||
err_out:
|
||||
acpi_evaluate_hotplug_ost(handle, type,
|
||||
ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);
|
||||
acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
|
||||
}
|
||||
|
||||
static ssize_t real_power_state_show(struct device *dev,
|
||||
@ -473,7 +510,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
|
||||
acpi_evaluate_hotplug_ost(acpi_device->handle, ACPI_OST_EC_OSPM_EJECT,
|
||||
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
|
||||
get_device(&acpi_device->dev);
|
||||
status = acpi_hotplug_execute(acpi_bus_device_eject, acpi_device,
|
||||
status = acpi_hotplug_execute(acpi_device_hotplug, acpi_device,
|
||||
ACPI_OST_EC_OSPM_EJECT);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return count;
|
||||
@ -565,6 +602,20 @@ acpi_device_sun_show(struct device *dev, struct device_attribute *attr,
|
||||
}
|
||||
static DEVICE_ATTR(sun, 0444, acpi_device_sun_show, NULL);
|
||||
|
||||
static ssize_t status_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf) {
|
||||
struct acpi_device *acpi_dev = to_acpi_device(dev);
|
||||
acpi_status status;
|
||||
unsigned long long sta;
|
||||
|
||||
status = acpi_evaluate_integer(acpi_dev->handle, "_STA", NULL, &sta);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
return sprintf(buf, "%llu\n", sta);
|
||||
}
|
||||
static DEVICE_ATTR_RO(status);
|
||||
|
||||
static int acpi_device_setup_files(struct acpi_device *dev)
|
||||
{
|
||||
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
@ -620,6 +671,12 @@ static int acpi_device_setup_files(struct acpi_device *dev)
|
||||
dev->pnp.sun = (unsigned long)-1;
|
||||
}
|
||||
|
||||
if (acpi_has_method(dev->handle, "_STA")) {
|
||||
result = device_create_file(&dev->dev, &dev_attr_status);
|
||||
if (result)
|
||||
goto end;
|
||||
}
|
||||
|
||||
/*
|
||||
* If device has _EJ0, 'eject' file is created that is used to trigger
|
||||
* hot-removal function from userland.
|
||||
@ -675,6 +732,8 @@ static void acpi_device_remove_files(struct acpi_device *dev)
|
||||
device_remove_file(&dev->dev, &dev_attr_adr);
|
||||
device_remove_file(&dev->dev, &dev_attr_modalias);
|
||||
device_remove_file(&dev->dev, &dev_attr_hid);
|
||||
if (acpi_has_method(dev->handle, "_STA"))
|
||||
device_remove_file(&dev->dev, &dev_attr_status);
|
||||
if (dev->handle)
|
||||
device_remove_file(&dev->dev, &dev_attr_path);
|
||||
}
|
||||
@ -905,9 +964,91 @@ struct bus_type acpi_bus_type = {
|
||||
.uevent = acpi_device_uevent,
|
||||
};
|
||||
|
||||
static void acpi_bus_data_handler(acpi_handle handle, void *context)
|
||||
static void acpi_device_del(struct acpi_device *device)
|
||||
{
|
||||
/* Intentionally empty. */
|
||||
mutex_lock(&acpi_device_lock);
|
||||
if (device->parent)
|
||||
list_del(&device->node);
|
||||
|
||||
list_del(&device->wakeup_list);
|
||||
mutex_unlock(&acpi_device_lock);
|
||||
|
||||
acpi_power_add_remove_device(device, false);
|
||||
acpi_device_remove_files(device);
|
||||
if (device->remove)
|
||||
device->remove(device);
|
||||
|
||||
device_del(&device->dev);
|
||||
}
|
||||
|
||||
static LIST_HEAD(acpi_device_del_list);
|
||||
static DEFINE_MUTEX(acpi_device_del_lock);
|
||||
|
||||
static void acpi_device_del_work_fn(struct work_struct *work_not_used)
|
||||
{
|
||||
for (;;) {
|
||||
struct acpi_device *adev;
|
||||
|
||||
mutex_lock(&acpi_device_del_lock);
|
||||
|
||||
if (list_empty(&acpi_device_del_list)) {
|
||||
mutex_unlock(&acpi_device_del_lock);
|
||||
break;
|
||||
}
|
||||
adev = list_first_entry(&acpi_device_del_list,
|
||||
struct acpi_device, del_list);
|
||||
list_del(&adev->del_list);
|
||||
|
||||
mutex_unlock(&acpi_device_del_lock);
|
||||
|
||||
acpi_device_del(adev);
|
||||
/*
|
||||
* Drop references to all power resources that might have been
|
||||
* used by the device.
|
||||
*/
|
||||
acpi_power_transition(adev, ACPI_STATE_D3_COLD);
|
||||
put_device(&adev->dev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_scan_drop_device - Drop an ACPI device object.
|
||||
* @handle: Handle of an ACPI namespace node, not used.
|
||||
* @context: Address of the ACPI device object to drop.
|
||||
*
|
||||
* This is invoked by acpi_ns_delete_node() during the removal of the ACPI
|
||||
* namespace node the device object pointed to by @context is attached to.
|
||||
*
|
||||
* The unregistration is carried out asynchronously to avoid running
|
||||
* acpi_device_del() under the ACPICA's namespace mutex and the list is used to
|
||||
* ensure the correct ordering (the device objects must be unregistered in the
|
||||
* same order in which the corresponding namespace nodes are deleted).
|
||||
*/
|
||||
static void acpi_scan_drop_device(acpi_handle handle, void *context)
|
||||
{
|
||||
static DECLARE_WORK(work, acpi_device_del_work_fn);
|
||||
struct acpi_device *adev = context;
|
||||
|
||||
mutex_lock(&acpi_device_del_lock);
|
||||
|
||||
/*
|
||||
* Use the ACPI hotplug workqueue which is ordered, so this work item
|
||||
* won't run after any hotplug work items submitted subsequently. That
|
||||
* prevents attempts to register device objects identical to those being
|
||||
* deleted from happening concurrently (such attempts result from
|
||||
* hotplug events handled via the ACPI hotplug workqueue). It also will
|
||||
* run after all of the work items submitted previosuly, which helps
|
||||
* those work items to ensure that they are not accessing stale device
|
||||
* objects.
|
||||
*/
|
||||
if (list_empty(&acpi_device_del_list))
|
||||
acpi_queue_hotplug_work(&work);
|
||||
|
||||
list_add_tail(&adev->del_list, &acpi_device_del_list);
|
||||
/* Make acpi_ns_validate_handle() return NULL for this handle. */
|
||||
adev->handle = INVALID_ACPI_HANDLE;
|
||||
|
||||
mutex_unlock(&acpi_device_del_lock);
|
||||
}
|
||||
|
||||
int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
|
||||
@ -917,7 +1058,7 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
|
||||
if (!device)
|
||||
return -EINVAL;
|
||||
|
||||
status = acpi_get_data(handle, acpi_bus_data_handler, (void **)device);
|
||||
status = acpi_get_data(handle, acpi_scan_drop_device, (void **)device);
|
||||
if (ACPI_FAILURE(status) || !*device) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
|
||||
handle));
|
||||
@ -937,7 +1078,7 @@ int acpi_device_add(struct acpi_device *device,
|
||||
if (device->handle) {
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_attach_data(device->handle, acpi_bus_data_handler,
|
||||
status = acpi_attach_data(device->handle, acpi_scan_drop_device,
|
||||
device);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
acpi_handle_err(device->handle,
|
||||
@ -955,6 +1096,7 @@ int acpi_device_add(struct acpi_device *device,
|
||||
INIT_LIST_HEAD(&device->node);
|
||||
INIT_LIST_HEAD(&device->wakeup_list);
|
||||
INIT_LIST_HEAD(&device->physical_node_list);
|
||||
INIT_LIST_HEAD(&device->del_list);
|
||||
mutex_init(&device->physical_node_lock);
|
||||
|
||||
new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL);
|
||||
@ -1018,37 +1160,10 @@ int acpi_device_add(struct acpi_device *device,
|
||||
mutex_unlock(&acpi_device_lock);
|
||||
|
||||
err_detach:
|
||||
acpi_detach_data(device->handle, acpi_bus_data_handler);
|
||||
acpi_detach_data(device->handle, acpi_scan_drop_device);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void acpi_device_unregister(struct acpi_device *device)
|
||||
{
|
||||
mutex_lock(&acpi_device_lock);
|
||||
if (device->parent)
|
||||
list_del(&device->node);
|
||||
|
||||
list_del(&device->wakeup_list);
|
||||
mutex_unlock(&acpi_device_lock);
|
||||
|
||||
acpi_detach_data(device->handle, acpi_bus_data_handler);
|
||||
|
||||
acpi_power_add_remove_device(device, false);
|
||||
acpi_device_remove_files(device);
|
||||
if (device->remove)
|
||||
device->remove(device);
|
||||
|
||||
device_del(&device->dev);
|
||||
/*
|
||||
* Transition the device to D3cold to drop the reference counts of all
|
||||
* power resources the device depends on and turn off the ones that have
|
||||
* no more references.
|
||||
*/
|
||||
acpi_device_set_power(device, ACPI_STATE_D3_COLD);
|
||||
device->handle = NULL;
|
||||
put_device(&device->dev);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
Driver Management
|
||||
-------------------------------------------------------------------------- */
|
||||
@ -1622,11 +1737,13 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
|
||||
device->device_type = type;
|
||||
device->handle = handle;
|
||||
device->parent = acpi_bus_get_parent(handle);
|
||||
STRUCT_TO_INT(device->status) = sta;
|
||||
acpi_set_device_status(device, sta);
|
||||
acpi_device_get_busid(device);
|
||||
acpi_set_pnp_ids(handle, &device->pnp, type);
|
||||
acpi_bus_get_flags(device);
|
||||
device->flags.match_driver = false;
|
||||
device->flags.initialized = true;
|
||||
device->flags.visited = false;
|
||||
device_initialize(&device->dev);
|
||||
dev_set_uevent_suppress(&device->dev, true);
|
||||
}
|
||||
@ -1711,6 +1828,15 @@ static int acpi_bus_type_and_status(acpi_handle handle, int *type,
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool acpi_device_is_present(struct acpi_device *adev)
|
||||
{
|
||||
if (adev->status.present || adev->status.functional)
|
||||
return true;
|
||||
|
||||
adev->flags.initialized = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler,
|
||||
char *idstr,
|
||||
const struct acpi_device_id **matchid)
|
||||
@ -1770,7 +1896,7 @@ static void acpi_scan_init_hotplug(acpi_handle handle, int type)
|
||||
*/
|
||||
list_for_each_entry(hwid, &pnp.ids, list) {
|
||||
handler = acpi_scan_match_handler(hwid->id, NULL);
|
||||
if (handler && !handler->hotplug.ignore) {
|
||||
if (handler) {
|
||||
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
|
||||
acpi_hotplug_notify_cb, handler);
|
||||
break;
|
||||
@ -1804,18 +1930,6 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
|
||||
|
||||
acpi_scan_init_hotplug(handle, type);
|
||||
|
||||
if (!(sta & ACPI_STA_DEVICE_PRESENT) &&
|
||||
!(sta & ACPI_STA_DEVICE_FUNCTIONING)) {
|
||||
struct acpi_device_wakeup wakeup;
|
||||
|
||||
if (acpi_has_method(handle, "_PRW")) {
|
||||
acpi_bus_extract_wakeup_device_power_package(handle,
|
||||
&wakeup);
|
||||
acpi_power_resources_list_free(&wakeup.resources);
|
||||
}
|
||||
return AE_CTRL_DEPTH;
|
||||
}
|
||||
|
||||
acpi_add_single_object(&device, handle, type, sta);
|
||||
if (!device)
|
||||
return AE_CTRL_DEPTH;
|
||||
@ -1850,36 +1964,40 @@ static int acpi_scan_attach_handler(struct acpi_device *device)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used,
|
||||
void *not_used, void **ret_not_used)
|
||||
static void acpi_bus_attach(struct acpi_device *device)
|
||||
{
|
||||
struct acpi_device *device;
|
||||
unsigned long long sta_not_used;
|
||||
struct acpi_device *child;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Ignore errors ignored by acpi_bus_check_add() to avoid terminating
|
||||
* namespace walks prematurely.
|
||||
*/
|
||||
if (acpi_bus_type_and_status(handle, &ret, &sta_not_used))
|
||||
return AE_OK;
|
||||
|
||||
if (acpi_bus_get_device(handle, &device))
|
||||
return AE_CTRL_DEPTH;
|
||||
|
||||
acpi_bus_get_status(device);
|
||||
/* Skip devices that are not present. */
|
||||
if (!acpi_device_is_present(device)) {
|
||||
device->flags.visited = false;
|
||||
return;
|
||||
}
|
||||
if (device->handler)
|
||||
return AE_OK;
|
||||
goto ok;
|
||||
|
||||
if (!device->flags.initialized) {
|
||||
acpi_bus_update_power(device, NULL);
|
||||
device->flags.initialized = true;
|
||||
}
|
||||
device->flags.visited = false;
|
||||
ret = acpi_scan_attach_handler(device);
|
||||
if (ret < 0)
|
||||
return AE_CTRL_DEPTH;
|
||||
return;
|
||||
|
||||
device->flags.match_driver = true;
|
||||
if (ret > 0)
|
||||
return AE_OK;
|
||||
if (!ret) {
|
||||
ret = device_attach(&device->dev);
|
||||
if (ret < 0)
|
||||
return;
|
||||
}
|
||||
device->flags.visited = true;
|
||||
|
||||
ret = device_attach(&device->dev);
|
||||
return ret >= 0 ? AE_OK : AE_CTRL_DEPTH;
|
||||
ok:
|
||||
list_for_each_entry(child, &device->children, node)
|
||||
acpi_bus_attach(child);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1899,75 +2017,48 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used,
|
||||
int acpi_bus_scan(acpi_handle handle)
|
||||
{
|
||||
void *device = NULL;
|
||||
int error = 0;
|
||||
|
||||
if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device)))
|
||||
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
|
||||
acpi_bus_check_add, NULL, NULL, &device);
|
||||
|
||||
if (!device)
|
||||
error = -ENODEV;
|
||||
else if (ACPI_SUCCESS(acpi_bus_device_attach(handle, 0, NULL, NULL)))
|
||||
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
|
||||
acpi_bus_device_attach, NULL, NULL, NULL);
|
||||
|
||||
return error;
|
||||
if (device) {
|
||||
acpi_bus_attach(device);
|
||||
return 0;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_bus_scan);
|
||||
|
||||
static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used,
|
||||
void *not_used, void **ret_not_used)
|
||||
{
|
||||
struct acpi_device *device = NULL;
|
||||
|
||||
if (!acpi_bus_get_device(handle, &device)) {
|
||||
struct acpi_scan_handler *dev_handler = device->handler;
|
||||
|
||||
if (dev_handler) {
|
||||
if (dev_handler->detach)
|
||||
dev_handler->detach(device);
|
||||
|
||||
device->handler = NULL;
|
||||
} else {
|
||||
device_release_driver(&device->dev);
|
||||
}
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used,
|
||||
void *not_used, void **ret_not_used)
|
||||
{
|
||||
struct acpi_device *device = NULL;
|
||||
|
||||
if (!acpi_bus_get_device(handle, &device))
|
||||
acpi_device_unregister(device);
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_bus_trim - Remove ACPI device node and all of its descendants
|
||||
* @start: Root of the ACPI device nodes subtree to remove.
|
||||
* acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects.
|
||||
* @adev: Root of the ACPI namespace scope to walk.
|
||||
*
|
||||
* Must be called under acpi_scan_lock.
|
||||
*/
|
||||
void acpi_bus_trim(struct acpi_device *start)
|
||||
void acpi_bus_trim(struct acpi_device *adev)
|
||||
{
|
||||
struct acpi_scan_handler *handler = adev->handler;
|
||||
struct acpi_device *child;
|
||||
|
||||
list_for_each_entry_reverse(child, &adev->children, node)
|
||||
acpi_bus_trim(child);
|
||||
|
||||
if (handler) {
|
||||
if (handler->detach)
|
||||
handler->detach(adev);
|
||||
|
||||
adev->handler = NULL;
|
||||
} else {
|
||||
device_release_driver(&adev->dev);
|
||||
}
|
||||
/*
|
||||
* Execute acpi_bus_device_detach() as a post-order callback to detach
|
||||
* all ACPI drivers from the device nodes being removed.
|
||||
* Most likely, the device is going away, so put it into D3cold before
|
||||
* that.
|
||||
*/
|
||||
acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL,
|
||||
acpi_bus_device_detach, NULL, NULL);
|
||||
acpi_bus_device_detach(start->handle, 0, NULL, NULL);
|
||||
/*
|
||||
* Execute acpi_bus_remove() as a post-order callback to remove device
|
||||
* nodes in the given namespace scope.
|
||||
*/
|
||||
acpi_walk_namespace(ACPI_TYPE_ANY, start->handle, ACPI_UINT32_MAX, NULL,
|
||||
acpi_bus_remove, NULL, NULL);
|
||||
acpi_bus_remove(start->handle, 0, NULL, NULL);
|
||||
acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
|
||||
adev->flags.initialized = false;
|
||||
adev->flags.visited = false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_bus_trim);
|
||||
|
||||
@ -2045,14 +2136,14 @@ int __init acpi_scan_init(void)
|
||||
|
||||
result = acpi_bus_scan_fixed();
|
||||
if (result) {
|
||||
acpi_device_unregister(acpi_root);
|
||||
acpi_detach_data(acpi_root->handle, acpi_scan_drop_device);
|
||||
acpi_device_del(acpi_root);
|
||||
put_device(&acpi_root->dev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
acpi_update_all_gpes();
|
||||
|
||||
acpi_pci_root_hp_init();
|
||||
|
||||
out:
|
||||
mutex_unlock(&acpi_scan_lock);
|
||||
return result;
|
||||
|
@ -178,12 +178,12 @@ static const struct acpi_dock_ops ata_acpi_ap_dock_ops = {
|
||||
/* bind acpi handle to pata port */
|
||||
void ata_acpi_bind_port(struct ata_port *ap)
|
||||
{
|
||||
acpi_handle host_handle = ACPI_HANDLE(ap->host->dev);
|
||||
struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev);
|
||||
|
||||
if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA || !host_handle)
|
||||
if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA || !host_companion)
|
||||
return;
|
||||
|
||||
acpi_preset_companion(&ap->tdev, host_handle, ap->port_no);
|
||||
acpi_preset_companion(&ap->tdev, host_companion, ap->port_no);
|
||||
|
||||
if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0)
|
||||
ap->pflags |= ATA_PFLAG_INIT_GTM_VALID;
|
||||
@ -196,17 +196,17 @@ void ata_acpi_bind_port(struct ata_port *ap)
|
||||
void ata_acpi_bind_dev(struct ata_device *dev)
|
||||
{
|
||||
struct ata_port *ap = dev->link->ap;
|
||||
acpi_handle port_handle = ACPI_HANDLE(&ap->tdev);
|
||||
acpi_handle host_handle = ACPI_HANDLE(ap->host->dev);
|
||||
acpi_handle parent_handle;
|
||||
struct acpi_device *port_companion = ACPI_COMPANION(&ap->tdev);
|
||||
struct acpi_device *host_companion = ACPI_COMPANION(ap->host->dev);
|
||||
struct acpi_device *parent;
|
||||
u64 adr;
|
||||
|
||||
/*
|
||||
* For both sata/pata devices, host handle is required.
|
||||
* For pata device, port handle is also required.
|
||||
* For both sata/pata devices, host companion device is required.
|
||||
* For pata device, port companion device is also required.
|
||||
*/
|
||||
if (libata_noacpi || !host_handle ||
|
||||
(!(ap->flags & ATA_FLAG_ACPI_SATA) && !port_handle))
|
||||
if (libata_noacpi || !host_companion ||
|
||||
(!(ap->flags & ATA_FLAG_ACPI_SATA) && !port_companion))
|
||||
return;
|
||||
|
||||
if (ap->flags & ATA_FLAG_ACPI_SATA) {
|
||||
@ -214,13 +214,13 @@ void ata_acpi_bind_dev(struct ata_device *dev)
|
||||
adr = SATA_ADR(ap->port_no, NO_PORT_MULT);
|
||||
else
|
||||
adr = SATA_ADR(ap->port_no, dev->link->pmp);
|
||||
parent_handle = host_handle;
|
||||
parent = host_companion;
|
||||
} else {
|
||||
adr = dev->devno;
|
||||
parent_handle = port_handle;
|
||||
parent = port_companion;
|
||||
}
|
||||
|
||||
acpi_preset_companion(&dev->tdev, parent_handle, adr);
|
||||
acpi_preset_companion(&dev->tdev, parent, adr);
|
||||
|
||||
register_hotplug_dock_device(ata_dev_acpi_handle(dev),
|
||||
&ata_acpi_dev_dock_ops, dev, NULL, NULL);
|
||||
|
@ -4,7 +4,7 @@ obj-y := core.o bus.o dd.o syscore.o \
|
||||
driver.o class.o platform.o \
|
||||
cpu.o firmware.o init.o map.o devres.o \
|
||||
attribute_container.o transport_class.o \
|
||||
topology.o
|
||||
topology.o container.o
|
||||
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
|
||||
obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
|
||||
obj-y += power/
|
||||
|
@ -100,6 +100,7 @@ static inline int hypervisor_init(void) { return 0; }
|
||||
#endif
|
||||
extern int platform_bus_init(void);
|
||||
extern void cpu_dev_init(void);
|
||||
extern void container_dev_init(void);
|
||||
|
||||
struct kobject *virtual_device_parent(struct device *dev);
|
||||
|
||||
|
44
drivers/base/container.c
Normal file
44
drivers/base/container.c
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* System bus type for containers.
|
||||
*
|
||||
* Copyright (C) 2013, Intel Corporation
|
||||
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/container.h>
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define CONTAINER_BUS_NAME "container"
|
||||
|
||||
static int trivial_online(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int container_offline(struct device *dev)
|
||||
{
|
||||
struct container_dev *cdev = to_container_dev(dev);
|
||||
|
||||
return cdev->offline ? cdev->offline(cdev) : 0;
|
||||
}
|
||||
|
||||
struct bus_type container_subsys = {
|
||||
.name = CONTAINER_BUS_NAME,
|
||||
.dev_name = CONTAINER_BUS_NAME,
|
||||
.online = trivial_online,
|
||||
.offline = container_offline,
|
||||
};
|
||||
|
||||
void __init container_dev_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = subsys_system_register(&container_subsys, NULL);
|
||||
if (ret)
|
||||
pr_err("%s() failed: %d\n", __func__, ret);
|
||||
}
|
@ -33,4 +33,5 @@ void __init driver_init(void)
|
||||
platform_bus_init();
|
||||
cpu_dev_init();
|
||||
memory_dev_init();
|
||||
container_dev_init();
|
||||
}
|
||||
|
@ -97,6 +97,17 @@ bool ide_port_acpi(ide_hwif_t *hwif)
|
||||
return ide_noacpi == 0 && hwif->acpidata;
|
||||
}
|
||||
|
||||
static acpi_handle acpi_get_child(acpi_handle handle, u64 addr)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
|
||||
if (!handle || acpi_bus_get_device(handle, &adev))
|
||||
return NULL;
|
||||
|
||||
adev = acpi_find_child_device(adev, addr, false);
|
||||
return adev ? adev->handle : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_get_dev_handle - finds acpi_handle and PCI device.function
|
||||
* @dev: device to locate
|
||||
|
@ -308,7 +308,7 @@ static void sdio_acpi_set_handle(struct sdio_func *func)
|
||||
struct mmc_host *host = func->card->host;
|
||||
u64 addr = (host->slotno << 16) | func->num;
|
||||
|
||||
acpi_preset_companion(&func->dev, ACPI_HANDLE(host->parent), addr);
|
||||
acpi_preset_companion(&func->dev, ACPI_COMPANION(host->parent), addr);
|
||||
}
|
||||
#else
|
||||
static inline void sdio_acpi_set_handle(struct sdio_func *func) {}
|
||||
|
@ -491,7 +491,7 @@ static void acpiphp_bus_add(acpi_handle handle)
|
||||
|
||||
acpi_bus_scan(handle);
|
||||
acpi_bus_get_device(handle, &adev);
|
||||
if (adev)
|
||||
if (acpi_device_enumerated(adev))
|
||||
acpi_device_set_power(adev, ACPI_STATE_D0);
|
||||
}
|
||||
|
||||
|
@ -303,10 +303,10 @@ void acpi_pci_remove_bus(struct pci_bus *bus)
|
||||
}
|
||||
|
||||
/* ACPI bus type */
|
||||
static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
|
||||
static struct acpi_device *acpi_pci_find_companion(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
bool is_bridge;
|
||||
bool check_children;
|
||||
u64 addr;
|
||||
|
||||
/*
|
||||
@ -314,14 +314,12 @@ static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
|
||||
* is set only after acpi_pci_find_device() has been called for the
|
||||
* given device.
|
||||
*/
|
||||
is_bridge = pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE
|
||||
check_children = pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE
|
||||
|| pci_dev->hdr_type == PCI_HEADER_TYPE_CARDBUS;
|
||||
/* Please ref to ACPI spec for the syntax of _ADR */
|
||||
addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);
|
||||
*handle = acpi_find_child(ACPI_HANDLE(dev->parent), addr, is_bridge);
|
||||
if (!*handle)
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
return acpi_find_child_device(ACPI_COMPANION(dev->parent), addr,
|
||||
check_children);
|
||||
}
|
||||
|
||||
static void pci_acpi_setup(struct device *dev)
|
||||
@ -364,7 +362,7 @@ static bool pci_acpi_bus_match(struct device *dev)
|
||||
static struct acpi_bus_type acpi_pci_bus = {
|
||||
.name = "PCI",
|
||||
.match = pci_acpi_bus_match,
|
||||
.find_device = acpi_pci_find_device,
|
||||
.find_companion = acpi_pci_find_companion,
|
||||
.setup = pci_acpi_setup,
|
||||
.cleanup = pci_acpi_cleanup,
|
||||
};
|
||||
|
@ -328,20 +328,15 @@ static int __init acpi_pnp_match(struct device *dev, void *_pnp)
|
||||
&& compare_pnp_id(pnp->id, acpi_device_hid(acpi));
|
||||
}
|
||||
|
||||
static int __init acpi_pnp_find_device(struct device *dev, acpi_handle * handle)
|
||||
static struct acpi_device * __init acpi_pnp_find_companion(struct device *dev)
|
||||
{
|
||||
struct device *adev;
|
||||
struct acpi_device *acpi;
|
||||
dev = bus_find_device(&acpi_bus_type, NULL, to_pnp_dev(dev),
|
||||
acpi_pnp_match);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
adev = bus_find_device(&acpi_bus_type, NULL,
|
||||
to_pnp_dev(dev), acpi_pnp_match);
|
||||
if (!adev)
|
||||
return -ENODEV;
|
||||
|
||||
acpi = to_acpi_device(adev);
|
||||
*handle = acpi->handle;
|
||||
put_device(adev);
|
||||
return 0;
|
||||
put_device(dev);
|
||||
return to_acpi_device(dev);
|
||||
}
|
||||
|
||||
/* complete initialization of a PNPACPI device includes having
|
||||
@ -355,7 +350,7 @@ static bool acpi_pnp_bus_match(struct device *dev)
|
||||
static struct acpi_bus_type __initdata acpi_pnp_bus = {
|
||||
.name = "PNP",
|
||||
.match = acpi_pnp_bus_match,
|
||||
.find_device = acpi_pnp_find_device,
|
||||
.find_companion = acpi_pnp_find_companion,
|
||||
};
|
||||
|
||||
int pnpacpi_disabled __initdata;
|
||||
|
@ -126,7 +126,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
|
||||
static struct acpi_device *usb_acpi_find_companion(struct device *dev)
|
||||
{
|
||||
struct usb_device *udev;
|
||||
acpi_handle *parent_handle;
|
||||
@ -168,16 +168,15 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
|
||||
break;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* root hub's parent is the usb hcd. */
|
||||
parent_handle = ACPI_HANDLE(dev->parent);
|
||||
*handle = acpi_get_child(parent_handle, udev->portnum);
|
||||
if (!*handle)
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
return acpi_find_child_device(ACPI_COMPANION(dev->parent),
|
||||
udev->portnum, false);
|
||||
} else if (is_usb_port(dev)) {
|
||||
struct acpi_device *adev = NULL;
|
||||
|
||||
sscanf(dev_name(dev), "port%d", &port_num);
|
||||
/* Get the struct usb_device point of port's hub */
|
||||
udev = to_usb_device(dev->parent->parent);
|
||||
@ -193,26 +192,27 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
|
||||
|
||||
raw_port_num = usb_hcd_find_raw_port_number(hcd,
|
||||
port_num);
|
||||
*handle = acpi_get_child(ACPI_HANDLE(&udev->dev),
|
||||
raw_port_num);
|
||||
if (!*handle)
|
||||
return -ENODEV;
|
||||
adev = acpi_find_child_device(ACPI_COMPANION(&udev->dev),
|
||||
raw_port_num, false);
|
||||
if (!adev)
|
||||
return NULL;
|
||||
} else {
|
||||
parent_handle =
|
||||
usb_get_hub_port_acpi_handle(udev->parent,
|
||||
udev->portnum);
|
||||
if (!parent_handle)
|
||||
return -ENODEV;
|
||||
return NULL;
|
||||
|
||||
*handle = acpi_get_child(parent_handle, port_num);
|
||||
if (!*handle)
|
||||
return -ENODEV;
|
||||
acpi_bus_get_device(parent_handle, &adev);
|
||||
adev = acpi_find_child_device(adev, port_num, false);
|
||||
if (!adev)
|
||||
return NULL;
|
||||
}
|
||||
usb_acpi_check_port_connect_type(udev, *handle, port_num);
|
||||
} else
|
||||
return -ENODEV;
|
||||
usb_acpi_check_port_connect_type(udev, adev->handle, port_num);
|
||||
return adev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool usb_acpi_bus_match(struct device *dev)
|
||||
@ -223,7 +223,7 @@ static bool usb_acpi_bus_match(struct device *dev)
|
||||
static struct acpi_bus_type usb_acpi_bus = {
|
||||
.name = "USB",
|
||||
.match = usb_acpi_bus_match,
|
||||
.find_device = usb_acpi_find_device,
|
||||
.find_companion = usb_acpi_find_companion,
|
||||
};
|
||||
|
||||
int usb_acpi_register(void)
|
||||
|
@ -266,7 +266,8 @@ static void acpi_processor_hotplug_notify(acpi_handle handle,
|
||||
if (!is_processor_present(handle))
|
||||
break;
|
||||
|
||||
if (!acpi_bus_get_device(handle, &device))
|
||||
acpi_bus_get_device(handle, &device);
|
||||
if (acpi_device_enumerated(device))
|
||||
break;
|
||||
|
||||
result = acpi_bus_scan(handle);
|
||||
@ -274,8 +275,9 @@ static void acpi_processor_hotplug_notify(acpi_handle handle,
|
||||
pr_err(PREFIX "Unable to add the device\n");
|
||||
break;
|
||||
}
|
||||
result = acpi_bus_get_device(handle, &device);
|
||||
if (result) {
|
||||
device = NULL;
|
||||
acpi_bus_get_device(handle, &device);
|
||||
if (!acpi_device_enumerated(device)) {
|
||||
pr_err(PREFIX "Missing device object\n");
|
||||
break;
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ static int acpi_memory_get_device(acpi_handle handle,
|
||||
acpi_scan_lock_acquire();
|
||||
|
||||
acpi_bus_get_device(handle, &device);
|
||||
if (device)
|
||||
if (acpi_device_enumerated(device))
|
||||
goto end;
|
||||
|
||||
/*
|
||||
@ -181,8 +181,9 @@ static int acpi_memory_get_device(acpi_handle handle,
|
||||
result = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
result = acpi_bus_get_device(handle, &device);
|
||||
if (result) {
|
||||
device = NULL;
|
||||
acpi_bus_get_device(handle, &device);
|
||||
if (!acpi_device_enumerated(device)) {
|
||||
pr_warn(PREFIX "Missing device object\n");
|
||||
result = -EINVAL;
|
||||
goto out;
|
||||
|
@ -89,17 +89,11 @@ struct acpi_device;
|
||||
* -----------------
|
||||
*/
|
||||
|
||||
enum acpi_hotplug_mode {
|
||||
AHM_GENERIC = 0,
|
||||
AHM_CONTAINER,
|
||||
AHM_COUNT
|
||||
};
|
||||
|
||||
struct acpi_hotplug_profile {
|
||||
struct kobject kobj;
|
||||
int (*scan_dependent)(struct acpi_device *adev);
|
||||
bool enabled:1;
|
||||
bool ignore:1;
|
||||
enum acpi_hotplug_mode mode;
|
||||
bool demand_offline:1;
|
||||
};
|
||||
|
||||
static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile(
|
||||
@ -167,8 +161,10 @@ struct acpi_device_flags {
|
||||
u32 ejectable:1;
|
||||
u32 power_manageable:1;
|
||||
u32 match_driver:1;
|
||||
u32 initialized:1;
|
||||
u32 visited:1;
|
||||
u32 no_hotplug:1;
|
||||
u32 reserved:26;
|
||||
u32 reserved:24;
|
||||
};
|
||||
|
||||
/* File System */
|
||||
@ -298,6 +294,7 @@ struct acpi_device {
|
||||
struct list_head children;
|
||||
struct list_head node;
|
||||
struct list_head wakeup_list;
|
||||
struct list_head del_list;
|
||||
struct acpi_device_status status;
|
||||
struct acpi_device_flags flags;
|
||||
struct acpi_device_pnp pnp;
|
||||
@ -323,6 +320,11 @@ static inline void *acpi_driver_data(struct acpi_device *d)
|
||||
#define to_acpi_device(d) container_of(d, struct acpi_device, dev)
|
||||
#define to_acpi_driver(d) container_of(d, struct acpi_driver, drv)
|
||||
|
||||
static inline void acpi_set_device_status(struct acpi_device *adev, u32 sta)
|
||||
{
|
||||
*((u32 *)&adev->status) = sta;
|
||||
}
|
||||
|
||||
/* acpi_device.dev.bus == &acpi_bus_type */
|
||||
extern struct bus_type acpi_bus_type;
|
||||
|
||||
@ -385,6 +387,11 @@ int acpi_match_device_ids(struct acpi_device *device,
|
||||
int acpi_create_dir(struct acpi_device *);
|
||||
void acpi_remove_dir(struct acpi_device *);
|
||||
|
||||
static inline bool acpi_device_enumerated(struct acpi_device *adev)
|
||||
{
|
||||
return adev && adev->flags.initialized && adev->flags.visited;
|
||||
}
|
||||
|
||||
typedef void (*acpi_hp_callback)(void *data, u32 src);
|
||||
|
||||
acpi_status acpi_hotplug_execute(acpi_hp_callback func, void *data, u32 src);
|
||||
@ -408,7 +415,7 @@ struct acpi_bus_type {
|
||||
struct list_head list;
|
||||
const char *name;
|
||||
bool (*match)(struct device *dev);
|
||||
int (*find_device) (struct device *, acpi_handle *);
|
||||
struct acpi_device * (*find_companion)(struct device *);
|
||||
void (*setup)(struct device *);
|
||||
void (*cleanup)(struct device *);
|
||||
};
|
||||
@ -427,12 +434,9 @@ struct acpi_pci_root {
|
||||
};
|
||||
|
||||
/* helper */
|
||||
acpi_handle acpi_find_child(acpi_handle, u64, bool);
|
||||
static inline acpi_handle acpi_get_child(acpi_handle handle, u64 addr)
|
||||
{
|
||||
return acpi_find_child(handle, addr, false);
|
||||
}
|
||||
void acpi_preset_companion(struct device *dev, acpi_handle parent, u64 addr);
|
||||
|
||||
struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
|
||||
u64 address, bool check_children);
|
||||
int acpi_is_root_bridge(acpi_handle);
|
||||
struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle);
|
||||
|
||||
|
@ -54,6 +54,12 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev)
|
||||
#define ACPI_COMPANION_SET(dev, adev) ACPI_COMPANION(dev) = (adev)
|
||||
#define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev))
|
||||
|
||||
static inline void acpi_preset_companion(struct device *dev,
|
||||
struct acpi_device *parent, u64 addr)
|
||||
{
|
||||
ACPI_COMPANION_SET(dev, acpi_find_child_device(parent, addr, NULL));
|
||||
}
|
||||
|
||||
static inline const char *acpi_dev_name(struct acpi_device *adev)
|
||||
{
|
||||
return dev_name(&adev->dev);
|
||||
|
25
include/linux/container.h
Normal file
25
include/linux/container.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Definitions for container bus type.
|
||||
*
|
||||
* Copyright (C) 2013, Intel Corporation
|
||||
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
/* drivers/base/power/container.c */
|
||||
extern struct bus_type container_subsys;
|
||||
|
||||
struct container_dev {
|
||||
struct device dev;
|
||||
int (*offline)(struct container_dev *cdev);
|
||||
};
|
||||
|
||||
static inline struct container_dev *to_container_dev(struct device *dev)
|
||||
{
|
||||
return container_of(dev, struct container_dev, dev);
|
||||
}
|
Loading…
Reference in New Issue
Block a user