Commit abe5430e authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'acpi-pci-hotplug'

* acpi-pci-hotplug: (34 commits)
  ACPI / PM: Hold acpi_scan_lock over system PM transitions
  ACPI / hotplug / PCI: Fix NULL pointer dereference in cleanup_bridge()
  PCI / ACPI: Use dev_dbg() instead of dev_info() in acpi_pci_set_power_state()
  ACPI / hotplug / PCI: Get rid of check_sub_bridges()
  ACPI / hotplug / PCI: Clean up bridge_mutex usage
  ACPI / hotplug / PCI: Redefine enable_device() and disable_device()
  ACPI / hotplug / PCI: Sanitize acpiphp_get_(latch)|(adapter)_status()
  ACPI / hotplug / PCI: Get rid of unused constants in acpiphp.h
  ACPI / hotplug / PCI: Check for new devices on enabled slots
  ACPI / hotplug / PCI: Allow slots without new devices to be rescanned
  ACPI / hotplug / PCI: Do not check SLOT_ENABLED in enable_device()
  ACPI / hotplug / PCI: Do not exectute _PS0 and _PS3 directly
  ACPI / hotplug / PCI: Do not queue up event handling work items in vain
  ACPI / hotplug / PCI: Consolidate slot disabling and ejecting
  ACPI / hotplug / PCI: Drop redundant checks from check_hotplug_bridge()
  ACPI / hotplug / PCI: Rework namespace scanning and trimming routines
  ACPI / hotplug / PCI: Store parent in functions and bus in slots
  ACPI / hotplug / PCI: Drop handle field from struct acpiphp_bridge
  ACPI / hotplug / PCI: Drop handle field from struct acpiphp_func
  ACPI / hotplug / PCI: Embed function struct into struct acpiphp_context
  ...
parents c92f56cb ad07277e
......@@ -210,6 +210,8 @@ static void pcibios_allocate_bridge_resources(struct pci_dev *dev)
r = &dev->resource[idx];
if (!r->flags)
continue;
if (r->parent) /* Already allocated */
continue;
if (!r->start || pci_claim_resource(dev, idx) < 0) {
/*
* Something is wrong with the region.
......@@ -318,6 +320,8 @@ static void pcibios_allocate_dev_rom_resource(struct pci_dev *dev)
r = &dev->resource[PCI_ROM_RESOURCE];
if (!r->flags || !r->start)
return;
if (r->parent) /* Already allocated */
return;
if (pci_claim_resource(dev, PCI_ROM_RESOURCE) < 0) {
r->end -= r->start;
......
......@@ -159,12 +159,16 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
return AE_OK;
}
void acpi_pci_slot_enumerate(struct pci_bus *bus, acpi_handle handle)
void acpi_pci_slot_enumerate(struct pci_bus *bus)
{
mutex_lock(&slot_list_lock);
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
register_slot, NULL, bus, NULL);
mutex_unlock(&slot_list_lock);
acpi_handle handle = ACPI_HANDLE(bus->bridge);
if (handle) {
mutex_lock(&slot_list_lock);
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
register_slot, NULL, bus, NULL);
mutex_unlock(&slot_list_lock);
}
}
void acpi_pci_slot_remove(struct pci_bus *bus)
......
......@@ -420,10 +420,21 @@ static void acpi_pm_finish(void)
}
/**
* acpi_pm_end - Finish up suspend sequence.
* acpi_pm_start - Start system PM transition.
*/
static void acpi_pm_start(u32 acpi_state)
{
acpi_target_sleep_state = acpi_state;
acpi_sleep_tts_switch(acpi_target_sleep_state);
acpi_scan_lock_acquire();
}
/**
* acpi_pm_end - Finish up system PM transition.
*/
static void acpi_pm_end(void)
{
acpi_scan_lock_release();
/*
* This is necessary in case acpi_pm_finish() is not called during a
* failing transition to a sleep state.
......@@ -451,21 +462,19 @@ static u32 acpi_suspend_states[] = {
static int acpi_suspend_begin(suspend_state_t pm_state)
{
u32 acpi_state = acpi_suspend_states[pm_state];
int error = 0;
int error;
error = (nvs_nosave || nvs_nosave_s3) ? 0 : suspend_nvs_alloc();
if (error)
return error;
if (sleep_states[acpi_state]) {
acpi_target_sleep_state = acpi_state;
acpi_sleep_tts_switch(acpi_target_sleep_state);
} else {
printk(KERN_ERR "ACPI does not support this state: %d\n",
pm_state);
error = -ENOSYS;
if (!sleep_states[acpi_state]) {
pr_err("ACPI does not support sleep state S%u\n", acpi_state);
return -ENOSYS;
}
return error;
acpi_pm_start(acpi_state);
return 0;
}
/**
......@@ -631,10 +640,8 @@ static int acpi_hibernation_begin(void)
int error;
error = nvs_nosave ? 0 : suspend_nvs_alloc();
if (!error) {
acpi_target_sleep_state = ACPI_STATE_S4;
acpi_sleep_tts_switch(acpi_target_sleep_state);
}
if (!error)
acpi_pm_start(ACPI_STATE_S4);
return error;
}
......@@ -713,8 +720,10 @@ static int acpi_hibernation_begin_old(void)
if (!error) {
if (!nvs_nosave)
error = suspend_nvs_alloc();
if (!error)
if (!error) {
acpi_target_sleep_state = ACPI_STATE_S4;
acpi_scan_lock_acquire();
}
}
return error;
}
......
......@@ -49,6 +49,7 @@
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
struct acpiphp_context;
struct acpiphp_bridge;
struct acpiphp_slot;
......@@ -59,6 +60,7 @@ struct slot {
struct hotplug_slot *hotplug_slot;
struct acpiphp_slot *acpi_slot;
struct hotplug_slot_info info;
unsigned int sun; /* ACPI _SUN (Slot User Number) value */
};
static inline const char *slot_name(struct slot *slot)
......@@ -75,15 +77,11 @@ struct acpiphp_bridge {
struct list_head list;
struct list_head slots;
struct kref ref;
acpi_handle handle;
/* Ejectable PCI-to-PCI bridge (PCI bridge and PCI function) */
struct acpiphp_func *func;
struct acpiphp_context *context;
int nr_slots;
u32 flags;
/* This bus (host bridge) or Secondary bus (PCI-to-PCI bridge) */
struct pci_bus *pci_bus;
......@@ -99,15 +97,13 @@ struct acpiphp_bridge {
*/
struct acpiphp_slot {
struct list_head node;
struct acpiphp_bridge *bridge; /* parent */
struct pci_bus *bus;
struct list_head funcs; /* one slot may have different
objects (i.e. for each function) */
struct slot *slot;
struct mutex crit_sect;
u8 device; /* pci device# */
unsigned long long sun; /* ACPI _SUN (slot unique number) */
u32 flags; /* see below */
};
......@@ -119,15 +115,32 @@ struct acpiphp_slot {
* typically 8 objects per slot (i.e. for each PCI function)
*/
struct acpiphp_func {
struct acpiphp_slot *slot; /* parent */
struct acpiphp_bridge *parent;
struct acpiphp_slot *slot;
struct list_head sibling;
acpi_handle handle;
u8 function; /* pci function# */
u32 flags; /* see below */
};
struct acpiphp_context {
acpi_handle handle;
struct acpiphp_func func;
struct acpiphp_bridge *bridge;
unsigned int refcount;
};
static inline struct acpiphp_context *func_to_context(struct acpiphp_func *func)
{
return container_of(func, struct acpiphp_context, func);
}
static inline acpi_handle func_to_handle(struct acpiphp_func *func)
{
return func_to_context(func)->handle;
}
/*
* struct acpiphp_attention_info - device specific attention registration
*
......@@ -141,45 +154,32 @@ struct acpiphp_attention_info
struct module *owner;
};
/* PCI bus bridge HID */
#define ACPI_PCI_HOST_HID "PNP0A03"
/* ACPI _STA method value (ignore bit 4; battery present) */
#define ACPI_STA_ALL (0x0000000f)
/* bridge flags */
#define BRIDGE_HAS_EJ0 (0x00000001)
/* slot flags */
#define SLOT_POWEREDON (0x00000001)
#define SLOT_ENABLED (0x00000002)
#define SLOT_MULTIFUNCTION (0x00000004)
#define SLOT_ENABLED (0x00000001)
/* function flags */
#define FUNC_HAS_STA (0x00000001)
#define FUNC_HAS_EJ0 (0x00000002)
#define FUNC_HAS_PS0 (0x00000010)
#define FUNC_HAS_PS1 (0x00000020)
#define FUNC_HAS_PS2 (0x00000040)
#define FUNC_HAS_PS3 (0x00000080)
#define FUNC_HAS_DCK (0x00000100)
#define FUNC_HAS_DCK (0x00000004)
/* function prototypes */
/* acpiphp_core.c */
int acpiphp_register_attention(struct acpiphp_attention_info*info);
int acpiphp_unregister_attention(struct acpiphp_attention_info *info);
int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot);
int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot, unsigned int sun);
void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot);
/* acpiphp_glue.c */
typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data);
int acpiphp_enable_slot(struct acpiphp_slot *slot);
int acpiphp_disable_slot(struct acpiphp_slot *slot);
int acpiphp_eject_slot(struct acpiphp_slot *slot);
int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot);
u8 acpiphp_get_power_status(struct acpiphp_slot *slot);
u8 acpiphp_get_attention_status(struct acpiphp_slot *slot);
u8 acpiphp_get_latch_status(struct acpiphp_slot *slot);
......
......@@ -155,15 +155,11 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
static int disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
int retval;
dbg("%s - physical_slot = %s\n", __func__, slot_name(slot));
/* disable the specified slot */
retval = acpiphp_disable_slot(slot->acpi_slot);
if (!retval)
retval = acpiphp_eject_slot(slot->acpi_slot);
return retval;
return acpiphp_disable_and_eject_slot(slot->acpi_slot);
}
......@@ -290,7 +286,8 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
}
/* callback routine to initialize 'struct slot' for each slot */
int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
unsigned int sun)
{
struct slot *slot;
int retval = -ENOMEM;
......@@ -317,12 +314,11 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
acpiphp_slot->slot = slot;
snprintf(name, SLOT_NAME_SIZE, "%llu", slot->acpi_slot->sun);
slot->sun = sun;
snprintf(name, SLOT_NAME_SIZE, "%u", sun);
retval = pci_hp_register(slot->hotplug_slot,
acpiphp_slot->bridge->pci_bus,
acpiphp_slot->device,
name);
retval = pci_hp_register(slot->hotplug_slot, acpiphp_slot->bus,
acpiphp_slot->device, name);
if (retval == -EBUSY)
goto error_hpslot;
if (retval) {
......
......@@ -46,6 +46,7 @@
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <linux/pci-acpi.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/acpi.h>
......@@ -55,28 +56,82 @@
static LIST_HEAD(bridge_list);
static DEFINE_MUTEX(bridge_mutex);
static DEFINE_MUTEX(acpiphp_context_lock);
#define MY_NAME "acpiphp_glue"
static void handle_hotplug_event_bridge (acpi_handle, u32, void *);
static void handle_hotplug_event(acpi_handle handle, u32 type, void *data);
static void acpiphp_sanitize_bus(struct pci_bus *bus);
static void acpiphp_set_hpp_values(struct pci_bus *bus);
static void hotplug_event_func(acpi_handle handle, u32 type, void *context);
static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context);
static void hotplug_event(acpi_handle handle, u32 type, void *data);
static void free_bridge(struct kref *kref);
/* callback routine to check for the existence of a pci dock device */
static acpi_status
is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv)
static void acpiphp_context_handler(acpi_handle handle, void *context)
{
int *count = (int *)context;
/* Intentionally empty. */
}
if (is_dock_device(handle)) {
(*count)++;
return AE_CTRL_TERMINATE;
} else {
return AE_OK;
/**
* acpiphp_init_context - Create hotplug context and grab a reference to it.
* @handle: ACPI object handle to create the context for.
*
* Call under acpiphp_context_lock.
*/
static struct acpiphp_context *acpiphp_init_context(acpi_handle handle)
{
struct acpiphp_context *context;
acpi_status status;
context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return NULL;
context->handle = handle;
context->refcount = 1;
status = acpi_attach_data(handle, acpiphp_context_handler, context);
if (ACPI_FAILURE(status)) {
kfree(context);
return NULL;
}
return context;
}
/**
* acpiphp_get_context - Get hotplug context and grab a reference to it.
* @handle: ACPI object handle to get the context for.
*
* Call under acpiphp_context_lock.
*/
static struct acpiphp_context *acpiphp_get_context(acpi_handle handle)
{
struct acpiphp_context *context = NULL;
acpi_status status;
void *data;
status = acpi_get_data(handle, acpiphp_context_handler, &data);
if (ACPI_SUCCESS(status)) {
context = data;
context->refcount++;
}
return context;
}
/**
* acpiphp_put_context - Drop a reference to ACPI hotplug context.
* @handle: ACPI object handle to put the context for.
*
* The context object is removed if there are no more references to it.
*
* Call under acpiphp_context_lock.
*/
static void acpiphp_put_context(struct acpiphp_context *context)
{
if (--context->refcount)
return;
WARN_ON(context->bridge);
acpi_detach_data(context->handle, acpiphp_context_handler);
kfree(context);
}
static inline void get_bridge(struct acpiphp_bridge *bridge)
......@@ -91,25 +146,36 @@ static inline void put_bridge(struct acpiphp_bridge *bridge)
static void free_bridge(struct kref *kref)
{
struct acpiphp_context *context;
struct acpiphp_bridge *bridge;
struct acpiphp_slot *slot, *next;
struct acpiphp_func *func, *tmp;
mutex_lock(&acpiphp_context_lock);
bridge = container_of(kref, struct acpiphp_bridge, ref);
list_for_each_entry_safe(slot, next, &bridge->slots, node) {
list_for_each_entry_safe(func, tmp, &slot->funcs, sibling) {
kfree(func);
}
list_for_each_entry_safe(func, tmp, &slot->funcs, sibling)
acpiphp_put_context(func_to_context(func));
kfree(slot);
}
/* Release reference acquired by acpiphp_bridge_handle_to_function() */
if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func)
put_bridge(bridge->func->slot->bridge);
context = bridge->context;
/* Root bridges will not have hotplug context. */
if (context) {
/* Release the reference taken by acpiphp_enumerate_slots(). */
put_bridge(context->func.parent);
context->bridge = NULL;
acpiphp_put_context(context);
}
put_device(&bridge->pci_bus->dev);
pci_dev_put(bridge->pci_dev);
kfree(bridge);
mutex_unlock(&acpiphp_context_lock);
}
/*
......@@ -121,8 +187,8 @@ static void free_bridge(struct kref *kref)
*/
static void post_dock_fixups(acpi_handle not_used, u32 event, void *data)
{
struct acpiphp_func *func = data;
struct pci_bus *bus = func->slot->bridge->pci_bus;
struct acpiphp_context *context = data;
struct pci_bus *bus = context->func.slot->bus;
u32 buses;
if (!bus->self)
......@@ -147,7 +213,7 @@ static void post_dock_fixups(acpi_handle not_used, u32 event, void *data)
static const struct acpi_dock_ops acpiphp_dock_ops = {
.fixup = post_dock_fixups,
.handler = hotplug_event_func,
.handler = hotplug_event,
};
/* Check whether the PCI device is managed by native PCIe hotplug driver */
......@@ -181,54 +247,56 @@ static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
static void acpiphp_dock_init(void *data)
{
struct acpiphp_func *func = data;
struct acpiphp_context *context = data;
get_bridge(func->slot->bridge);
get_bridge(context->func.parent);
}
static void acpiphp_dock_release(void *data)
{
struct acpiphp_func *func = data;
struct acpiphp_context *context = data;
put_bridge(func->slot->bridge);
put_bridge(context->func.parent);
}
/* callback routine to register each ACPI PCI slot object */
static acpi_status
register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
static acpi_status register_slot(acpi_handle handle, u32 lvl, void *data,
void **rv)
{
struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context;
struct acpiphp_bridge *bridge = data;
struct acpiphp_context *context;
struct acpiphp_slot *slot;
struct acpiphp_func *newfunc;
acpi_status status = AE_OK;
unsigned long long adr, sun;
int device, function, retval, found = 0;
unsigned long long adr;
int device, function;
struct pci_bus *pbus = bridge->pci_bus;
struct pci_dev *pdev;
struct pci_dev *pdev = bridge->pci_dev;
u32 val;
if (!acpi_pci_check_ejectable(pbus, handle) && !is_dock_device(handle))
if (pdev && device_is_managed_by_native_pciehp(pdev))
return AE_OK;
status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
if (ACPI_FAILURE(status)) {
warn("can't evaluate _ADR (%#x)\n", status);
acpi_handle_warn(handle, "can't evaluate _ADR (%#x)\n", status);
return AE_OK;
}
device = (adr >> 16) & 0xffff;
function = adr & 0xffff;
pdev = bridge->pci_dev;
if (pdev && device_is_managed_by_native_pciehp(pdev))
return AE_OK;
newfunc = kzalloc(sizeof(struct acpiphp_func), GFP_KERNEL);
if (!newfunc)
return AE_NO_MEMORY;
newfunc->handle = handle;
mutex_lock(&acpiphp_context_lock);
context = acpiphp_init_context(handle);
if (!context) {
mutex_unlock(&acpiphp_context_lock);
acpi_handle_err(handle, "No hotplug context\n");
return AE_NOT_EXIST;
}
newfunc = &context->func;
newfunc->function = function;
newfunc->parent = bridge;
mutex_unlock(&acpiphp_context_lock);
if (acpi_has_method(handle, "_EJ0"))
newfunc->flags = FUNC_HAS_EJ0;
......@@ -236,73 +304,61 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
if (acpi_has_method(handle, "_STA"))
newfunc->flags |= FUNC_HAS_STA;
if (acpi_has_method(handle, "_PS0"))
newfunc->flags |= FUNC_HAS_PS0;
if (acpi_has_method(handle, "_PS3"))
newfunc->flags |= FUNC_HAS_PS3;
if (acpi_has_method(handle, "_DCK"))
newfunc->flags |= FUNC_HAS_DCK;
status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
if (ACPI_FAILURE(status)) {
/*
* use the count of the number of slots we've found
* for the number of the slot
*/
sun = bridge->nr_slots+1;
}
/* search for objects that share the same slot */
list_for_each_entry(slot, &bridge->slots, node)
if (slot->device == device) {
if (slot->sun != sun)
warn("sibling found, but _SUN doesn't match!\n");
found = 1;
break;
}
if (slot->device == device)
goto slot_found;
if (!found) {
slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
if (!slot) {
kfree(newfunc);
return AE_NO_MEMORY;
}
slot = kzalloc(sizeof(struct acpiphp_slot), GFP_KERNEL);
if (!slot) {
status = AE_NO_MEMORY;
goto err;
}
slot->bus = bridge->pci_bus;
slot->device = device;
INIT_LIST_HEAD(&slot->funcs);
mutex_init(&slot->crit_sect);
list_add_tail(&slot->node, &bridge->slots);
slot->bridge = bridge;
slot->device = device;
slot->sun = sun;
INIT_LIST_HEAD(&slot->funcs);
mutex_init(&slot->crit_sect);
/* Register slots for ejectable funtions only. */
if (acpi_pci_check_ejectable(pbus, handle) || is_dock_device(handle)) {
unsigned long long sun;
int retval;
mutex_lock(&bridge_mutex);
list_add_tail(&slot->node, &bridge->slots);
mutex_unlock(&bridge_mutex);
bridge->nr_slots++;
status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
if (ACPI_FAILURE(status))
sun = bridge->nr_slots;
dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n",
slot->sun, pci_domain_nr(pbus), pbus->number, device);
retval = acpiphp_register_hotplug_slot(slot);
sun, pci_domain_nr(pbus), pbus->number, device);
retval = acpiphp_register_hotplug_slot(slot, sun);
if (retval) {
slot->slot = NULL;
bridge->nr_slots--;
if (retval == -EBUSY)
warn("Slot %llu already registered by another "
"hotplug driver\n", slot->sun);
"hotplug driver\n", sun);
else
warn("acpiphp_register_hotplug_slot failed "
"(err code = 0x%x)\n", retval);
goto err_exit;
}
/* Even if the slot registration fails, we can still use it. */
}
slot_found:
newfunc->slot = slot;
mutex_lock(&bridge_mutex);
list_add_tail(&newfunc->sibling, &slot->funcs);
mutex_unlock(&bridge_mutex);
if (pci_bus_read_dev_vendor_id(pbus, PCI_DEVFN(device, function),
&val, 60*1000))
slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON);
slot->flags |= SLOT_ENABLED;
if (is_dock_device(handle)) {
/* we don't want to call this device's _EJ0
......@@ -311,128 +367,46 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
*/
newfunc->flags &= ~FUNC_HAS_EJ0;
if (register_hotplug_dock_device(handle,
&acpiphp_dock_ops, newfunc,
&acpiphp_dock_ops, context,
acpiphp_dock_init, acpiphp_dock_release))
dbg("failed to register dock device\n");
}
/* install notify handler */
if (!(newfunc->flags & FUNC_HAS_DCK)) {
status = acpi_install_notify_handler(handle,
ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_func,
newfunc);
status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
handle_hotplug_event,
context);
if (ACPI_FAILURE(status))
err("failed to register interrupt notify handler\n");
} else
status = AE_OK;
return status;
err_exit:
bridge->nr_slots--;
mutex_lock(&bridge_mutex);
list_del(&slot->node);
mutex_unlock(&bridge_mutex);
kfree(slot);
kfree(newfunc);
return AE_OK;
}
/* see if it's worth looking at this bridge */
static int detect_ejectable_slots(acpi_handle handle)
{
int found = acpi_pci_detect_ejectable(handle);
if (!found) {
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
is_pci_dock_device, NULL, (void *)&found, NULL);
}
return found;
}
/* initialize miscellaneous stuff for both root and PCI-to-PCI bridge */
static void init_bridge_misc(struct acpiphp_bridge *bridge)
{
acpi_status status;
/* must be added to the list prior to calling register_slot */
mutex_lock(&bridge_mutex);
list_add(&bridge->list, &bridge_list);
mutex_unlock(&bridge_mutex);
/* register all slot objects under this bridge */
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1,
register_slot, NULL, bridge, NULL);
if (ACPI_FAILURE(status)) {
mutex_lock(&bridge_mutex);
list_del(&bridge->list);
mutex_unlock(&bridge_mutex);
return;
}
/* install notify handler for P2P bridges */
if (!pci_is_root_bus(bridge->pci_bus)) {
if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {
status = acpi_remove_notify_handler(bridge->func->handle,
ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_func);
if (ACPI_FAILURE(status))
err("failed to remove notify handler\n");
}
status = acpi_install_notify_handler(bridge->handle,
ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_bridge,
bridge);
if (ACPI_FAILURE(status)) {
err("failed to register interrupt notify handler\n");
}
acpi_handle_err(handle,
"failed to install notify handler\n");
}
}
/* find acpiphp_func from acpiphp_bridge */
static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle)
{
struct acpiphp_bridge *bridge;
struct acpiphp_slot *slot;
struct acpiphp_func *func = NULL;
mutex_lock(&bridge_mutex);
list_for_each_entry(bridge, &bridge_list, list) {
list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
if (func->handle == handle) {
get_bridge(func->slot->bridge);
mutex_unlock(&bridge_mutex);
return func;
}
}
}
}
mutex_unlock(&bridge_mutex);
return AE_OK;
return NULL;
err:
mutex_lock(&acpiphp_context_lock);
acpiphp_put_context(context);
mutex_unlock(&acpiphp_context_lock);
return status;
}
static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle)
{
struct acpiphp_bridge *bridge;
mutex_lock(&bridge_mutex);
list_for_each_entry(bridge, &bridge_list, list)
if (bridge->handle == handle) {
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL;
mutex_lock(&acpiphp_context_lock);
context = acpiphp_get_context(handle);
if (context) {
bridge = context->bridge;
if (bridge)
get_bridge(bridge);
mutex_unlock(&bridge_mutex);
return bridge;
}
mutex_unlock(&bridge_mutex);
return NULL;
acpiphp_put_context(context);
}
mutex_unlock(&acpiphp_context_lock);
return bridge;
}
static void cleanup_bridge(struct acpiphp_bridge *bridge)
......@@ -440,39 +414,24 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
struct acpiphp_slot *slot;
struct acpiphp_func *func;
acpi_status status;
acpi_handle handle = bridge->handle;
if (!pci_is_root_bus(bridge->pci_bus)) {
status = acpi_remove_notify_handler(handle,
ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_bridge);
if (ACPI_FAILURE(status))
err("failed to remove notify handler\n");
}
if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {
status = acpi_install_notify_handler(bridge->func->handle,
ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_func,
bridge->func);
if (ACPI_FAILURE(status))
err("failed to install interrupt notify handler\n");
}
list_for_each_entry(slot, &bridge->slots, node) {
list_for_each_entry(func, &slot->funcs, sibling) {
if (is_dock_device(func->handle)) {
unregister_hotplug_dock_device(func->handle);
}
acpi_handle handle = func_to_handle(func);
if (is_dock_device(handle))
unregister_hotplug_dock_device(handle);
if (!(func->flags & FUNC_HAS_DCK)) {
status = acpi_remove_notify_handler(func->handle,
ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_func);
status = acpi_remove_notify_handler(handle,
ACPI_SYSTEM_NOTIFY,
handle_hotplug_event);
if (ACPI_FAILURE(status))
err("failed to remove notify handler\n");
}
}
acpiphp_unregister_hotplug_slot(slot);
if (slot->slot)
acpiphp_unregister_hotplug_slot(slot);
}
mutex_lock(&bridge_mutex);
......@@ -480,71 +439,6 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
mutex_unlock(&bridge_mutex);
}
static int power_on_slot(struct acpiphp_slot *slot)
{
acpi_status status;
struct acpiphp_func *func;
int retval = 0;
/* if already enabled, just skip */
if (slot->flags & SLOT_POWEREDON)
goto err_exit;
list_for_each_entry(func, &slot->funcs, sibling) {
if (func->flags & FUNC_HAS_PS0) {
dbg("%s: executing _PS0\n", __func__);
status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL);
if (ACPI_FAILURE(status)) {
warn("%s: _PS0 failed\n", __func__);
retval = -1;
goto err_exit;
} else
break;
}
}
/* TBD: evaluate _STA to check if the slot is enabled */
slot->flags |= SLOT_POWEREDON;
err_exit:
return retval;
}
static int power_off_slot(struct acpiphp_slot *slot)
{
acpi_status status;
struct acpiphp_func *func;
int retval = 0;
/* if already disabled, just skip */
if ((slot->flags & SLOT_POWEREDON) == 0)
goto err_exit;
list_for_each_entry(func, &slot->funcs, sibling) {
if (func->flags & FUNC_HAS_PS3) {
status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL);
if (ACPI_FAILURE(status)) {
warn("%s: _PS3 failed\n", __func__);
retval = -1;
goto err_exit;
} else
break;
}
}
/* TBD: evaluate _STA to check if the slot is disabled */
slot->flags &= (~SLOT_POWEREDON);
err_exit:
return retval;
}
/**
* acpiphp_max_busnr - return the highest reserved bus number under the given bus.
* @bus: bus to start search with
......@@ -572,52 +466,32 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus)
return max;
}
/**
* acpiphp_bus_add - add a new bus to acpi subsystem
* @func: acpiphp_func of the bridge
* acpiphp_bus_trim - Trim device objects in an ACPI namespace subtree.
* @handle: ACPI device object handle to start from.
*/
static int acpiphp_bus_add(struct acpiphp_func *func)
static void acpiphp_bus_trim(acpi_handle handle)
{
struct acpi_device *device;
int ret_val;
struct acpi_device *adev = NULL;
if (!acpi_bus_get_device(func->handle, &device)) {
dbg("bus exists... trim\n");
/* this shouldn't be in here, so remove
* the bus then re-add it...
*/
acpi_bus_trim(device);
}
ret_val = acpi_bus_scan(func->handle);
if (!ret_val)
ret_val = acpi_bus_get_device(func->handle, &device);
if (ret_val)
dbg("error adding bus, %x\n", -ret_val);
return ret_val;
acpi_bus_get_device(handle, &adev);
if (adev)
acpi_bus_trim(adev);
}
/**
* acpiphp_bus_trim - trim a bus from acpi subsystem
* @handle: handle to acpi namespace
* acpiphp_bus_add - Scan ACPI namespace subtree.
* @handle: ACPI object handle to start the scan from.
*/
static int acpiphp_bus_trim(acpi_handle handle)
static void acpiphp_bus_add(acpi_handle handle)
{
struct acpi_device *device;
int retval;
retval = acpi_bus_get_device(handle, &device);
if (retval) {
dbg("acpi_device not found\n");
return retval;
}
struct acpi_device *adev = NULL;
acpi_bus_trim(device);
return 0;
acpiphp_bus_trim(handle);
acpi_bus_scan(handle);
acpi_bus_get_device(handle, &adev);
if (adev)
acpi_device_set_power(adev, ACPI_STATE_D0);
}
static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)
......@@ -634,7 +508,8 @@ static void acpiphp_set_acpi_region(struct acpiphp_slot *slot)
params[1].type = ACPI_TYPE_INTEGER;
params[1].integer.value = 1;
/* _REG is optional, we don't care about if there is failure */
acpi_evaluate_object(func->handle, "_REG", &arg_list, NULL);
acpi_evaluate_object(func_to_handle(func), "_REG", &arg_list,
NULL);
}
}
......@@ -642,59 +517,44 @@ static void check_hotplug_bridge(struct acpiphp_slot *slot, struct pci_dev *dev)
{
struct acpiphp_func *func;
if (!dev->subordinate)
return;
/* quirk, or pcie could set it already */
if (dev->is_hotplug_bridge)
return;
if (PCI_SLOT(dev->devfn) != slot->device)
return;
list_for_each_entry(func, &slot->funcs, sibling) {
if (PCI_FUNC(dev->devfn) == func->function) {
/* check if this bridge has ejectable slots */
if ((detect_ejectable_slots(func->handle) > 0))
dev->is_hotplug_bridge = 1;
dev->is_hotplug_bridge = 1;
break;
}
}
}
/**
* enable_device - enable, configure a slot
* enable_slot - enable, configure a slot
* @slot: slot to be enabled
*
* This function should be called per *physical slot*,
* not per each slot object in ACPI namespace.
*/
static int __ref enable_device(struct acpiphp_slot *slot)
static void __ref enable_slot(struct acpiphp_slot *slot)
{
struct pci_dev *dev;
struct pci_bus *bus = slot->bridge->pci_bus;
struct pci_bus *bus = slot->bus;
struct acpiphp_func *func;
int num, max, pass;
int max, pass;
LIST_HEAD(add_list);
if (slot->flags & SLOT_ENABLED)
goto err_exit;
list_for_each_entry(func, &slot->funcs, sibling)
acpiphp_bus_add(func);
acpiphp_bus_add(func_to_handle(func));
num = pci_scan_slot(bus, PCI_DEVFN(slot->device, 0));
if (num == 0) {
/* Maybe only part of funcs are added. */
dbg("No new device found\n");
goto err_exit;
}
pci_scan_slot(bus, PCI_DEVFN(slot->device, 0));
max = acpiphp_max_busnr(bus);
for (pass = 0; pass < 2; pass++) {
list_for_each_entry(dev, &bus->devices, bus_list) {
if (PCI_SLOT(dev->devfn) != slot->device)
continue;
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
max = pci_scan_bridge(bus, dev, max, pass);
......@@ -733,16 +593,12 @@ static int __ref enable_device(struct acpiphp_slot *slot)
continue;
}
}
err_exit:
return 0;
}
/* return first device in slot, acquiring a reference on it */
static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot)
{
struct pci_bus *bus = slot->bridge->pci_bus;
struct pci_bus *bus = slot->bus;
struct pci_dev *dev;
struct pci_dev *ret = NULL;
......@@ -758,16 +614,16 @@ static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot)
}
/**
* disable_device - disable a slot
* disable_slot - disable a slot
* @slot: ACPI PHP slot
*/
static int disable_device(struct acpiphp_slot *slot)
static void disable_slot(struct acpiphp_slot *slot)
{
struct acpiphp_func *func;
struct pci_dev *pdev;
/*
* enable_device() enumerates all functions in this device via
* enable_slot() enumerates all functions in this device via
* pci_scan_slot(), whether they have associated ACPI hotplug
* methods (_EJ0, etc.) or not. Therefore, we remove all functions
* here.
......@@ -777,13 +633,10 @@ static int disable_device(struct acpiphp_slot *slot)
pci_dev_put(pdev);
}
list_for_each_entry(func, &slot->funcs, sibling) {
acpiphp_bus_trim(func->handle);
}
list_for_each_entry(func, &slot->funcs, sibling)
acpiphp_bus_trim(func_to_handle(func));
slot->flags &= (~SLOT_ENABLED);
return 0;
}
......@@ -801,18 +654,21 @@ static int disable_device(struct acpiphp_slot *slot)
*/
static unsigned int get_slot_status(struct acpiphp_slot *slot)
{
acpi_status status;
unsigned long long sta = 0;
u32 dvid;
struct acpiphp_func *func;
list_for_each_entry(func, &slot->funcs, sibling) {
if (func->flags & FUNC_HAS_STA) {
status = acpi_evaluate_integer(func->handle, "_STA", NULL, &sta);
acpi_status status;
status = acpi_evaluate_integer(func_to_handle(func),
"_STA", NULL, &sta);
if (ACPI_SUCCESS(status) && sta)
break;
} else {
pci_bus_read_config_dword(slot->bridge->pci_bus,
u32 dvid;
pci_bus_read_config_dword(slot->bus,
PCI_DEVFN(slot->device,
func->function),
PCI_VENDOR_ID, &dvid);
......@@ -827,23 +683,42 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
}
/**
* acpiphp_eject_slot - physically eject the slot
* @slot: ACPI PHP slot
* trim_stale_devices - remove PCI devices that are not responding.
* @dev: PCI device to start walking the hierarchy from.
*/
int acpiphp_eject_slot(struct acpiphp_slot *slot)
static void trim_stale_devices(struct pci_dev *dev)
{
struct acpiphp_func *func;
acpi_handle handle = ACPI_HANDLE(&dev->dev);
struct pci_bus *bus = dev->subordinate;
bool alive = false;
list_for_each_entry(func, &slot->funcs, sibling) {
/* We don't want to call _EJ0 on non-existing functions. */
if ((func->flags & FUNC_HAS_EJ0)) {
if (ACPI_FAILURE(acpi_evaluate_ej0(func->handle)))
return -1;
else
break;
}
if (handle) {
acpi_status status;
unsigned long long sta;
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
alive = ACPI_SUCCESS(status) && sta == ACPI_STA_ALL;
}
if (!alive) {
u32 v;
/* Check if the device responds. */
alive = pci_bus_read_dev_vendor_id(dev->bus, dev->devfn, &v, 0);
}
if (!alive) {
pci_stop_and_remove_bus_device(dev);
if (handle)
acpiphp_bus_trim(handle);
} else if (bus) {
struct pci_dev *child, *tmp;
/* The device is a bridge. so check the bus below it. */
pm_runtime_get_sync(&dev->dev);
list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
trim_stale_devices(child);
pm_runtime_put(&dev->dev);
}
return 0;
}
/**
......@@ -853,43 +728,30 @@ int acpiphp_eject_slot(struct acpiphp_slot *slot)
* Iterate over all slots under this bridge and make sure that if a
* card is present they are enabled, and if not they are disabled.
*/
static int acpiphp_check_bridge(struct acpiphp_bridge *bridge)
static void acpiphp_check_bridge(struct acpiphp_bridge *bridge)
{
struct acpiphp_slot *slot;
int retval = 0;
int enabled, disabled;
enabled = disabled = 0;
list_for_each_entry(slot, &bridge->slots, node) {
unsigned int status = get_slot_status(slot);
if (slot->flags & SLOT_ENABLED) {
if (status == ACPI_STA_ALL)
continue;
retval = acpiphp_disable_slot(slot);
if (retval) {
err("Error occurred in disabling\n");
goto err_exit;
} else {
acpiphp_eject_slot(slot);
}
disabled++;
struct pci_bus *bus = slot->bus;
struct pci_dev *dev, *tmp;
mutex_lock(&slot->crit_sect);
/* wake up all functions */
if (get_slot_status(slot) == ACPI_STA_ALL) {
/* remove stale devices if any */
list_for_each_entry_safe(dev, tmp, &bus->devices,
bus_list)
if (PCI_SLOT(dev->devfn) == slot->device)
trim_stale_devices(dev);
/* configure all functions */
enable_slot(slot);
} else {
if (status != ACPI_STA_ALL)
continue;
retval = acpiphp_enable_slot(slot);
if (retval) {
err("Error occurred in enabling\n");
goto err_exit;
}
enabled++;
disable_slot(slot);
}
mutex_unlock(&slot->crit_sect);
}
dbg("%s: %d enabled, %d disabled\n", __func__, enabled, disabled);
err_exit:
return retval;
}
static void acpiphp_set_hpp_values(struct pci_bus *bus)
......@@ -928,25 +790,6 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus)
* ACPI event handlers
*/
static acpi_status
check_sub_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
{
struct acpiphp_bridge *bridge;
char objname[64];
struct acpi_buffer buffer = { .length = sizeof(objname),
.pointer = objname };
bridge = acpiphp_handle_to_bridge(handle);
if (bridge) {
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
dbg("%s: re-enumerating slots under %s\n",
__func__, objname);
acpiphp_check_bridge(bridge);
put_bridge(bridge);
}
return AE_OK ;
}
void acpiphp_check_host_bridge(acpi_handle handle)
{
struct acpiphp_bridge *bridge;
......@@ -956,27 +799,23 @@ void acpiphp_check_host_bridge(acpi_handle handle)
acpiphp_check_bridge(bridge);
put_bridge(bridge);
}
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL, NULL);
}
static void _handle_hotplug_event_bridge(struct work_struct *work)
static void hotplug_event(acpi_handle handle, u32 type, void *data)
{
struct acpiphp_context *context = data;
struct acpiphp_func *func = &context->func;
struct acpiphp_bridge *bridge;
char objname[64];
struct acpi_buffer buffer = { .length = sizeof(objname),
.pointer = objname };
struct acpi_hp_work *hp_work;
acpi_handle handle;
u32 type;
hp_work = container_of(work, struct acpi_hp_work, work);
handle = hp_work->handle;
type = hp_work->type;
bridge = (struct acpiphp_bridge *)hp_work->context;
mutex_lock(&acpiphp_context_lock);
bridge = context->bridge;
if (bridge)
get_bridge(bridge);
acpi_scan_lock_acquire();
mutex_unlock(&acpiphp_context_lock);
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
......@@ -985,187 +824,129 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)
/* bus re-enumerate */
dbg("%s: Bus check notify on %s\n", __func__, objname);
dbg("%s: re-enumerating slots under %s\n", __func__, objname);
acpiphp_check_bridge(bridge);
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL, NULL);
if (bridge) {
acpiphp_check_bridge(bridge);
} else {
struct acpiphp_slot *slot = func->slot;
mutex_lock(&slot->crit_sect);
enable_slot(slot);
mutex_unlock(&slot->crit_sect);
}
break;
case ACPI_NOTIFY_DEVICE_CHECK:
/* device check */
dbg("%s: Device check notify on %s\n", __func__, objname);
acpiphp_check_bridge(bridge);
break;
if (bridge)
acpiphp_check_bridge(bridge);
else
acpiphp_check_bridge(func->parent);
case ACPI_NOTIFY_DEVICE_WAKE:
/* wake event */
dbg("%s: Device wake notify on %s\n", __func__, objname);
break;
case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */
dbg("%s: Device eject notify on %s\n", __func__, objname);
if ((bridge->flags & BRIDGE_HAS_EJ0) && bridge->func) {
struct acpiphp_slot *slot;
slot = bridge->func->slot;
if (!acpiphp_disable_slot(slot))
acpiphp_eject_slot(slot);
}
acpiphp_disable_and_eject_slot(func->slot);
break;
}
case ACPI_NOTIFY_FREQUENCY_MISMATCH:
printk(KERN_ERR "Device %s cannot be configured due"
" to a frequency mismatch\n", objname);
break;
if (bridge)
put_bridge(bridge);
}
case ACPI_NOTIFY_BUS_MODE_MISMATCH:
printk(KERN_ERR "Device %s cannot be configured due"
" to a bus mode mismatch\n", objname);
break;
static void hotplug_event_work(struct work_struct *work)
{
struct acpiphp_context *context;
struct acpi_hp_work *hp_work;
case ACPI_NOTIFY_POWER_FAULT:
printk(KERN_ERR "Device %s has suffered a power fault\n",
objname);
break;
hp_work = container_of(work, struct acpi_hp_work, work);
context = hp_work->context;
acpi_scan_lock_acquire();
default:
warn("notify_handler: unknown event type 0x%x for %s\n", type, objname);
break;
}
hotplug_event(hp_work->handle, hp_work->type, context);
acpi_scan_lock_release();
kfree(hp_work); /* allocated in handle_hotplug_event_bridge */
put_bridge(bridge);
kfree(hp_work); /* allocated in handle_hotplug_event() */
put_bridge(context->func.parent);
}
/**
* handle_hotplug_event_bridge - handle ACPI event on bridges
* handle_hotplug_event - handle ACPI hotplug event
* @handle: Notify()'ed acpi_handle
* @type: Notify code
* @context: pointer to acpiphp_bridge structure
* @data: pointer to acpiphp_context structure
*
* Handles ACPI event notification on {host,p2p} bridges.
* Handles ACPI event notification on slots.
*/
static void handle_hotplug_event_bridge(acpi_handle handle, u32 type,
void *context)
{
struct acpiphp_bridge *bridge = context;
/*
* Currently the code adds all hotplug events to the kacpid_wq
* queue when it should add hotplug events to the kacpi_hotplug_wq.
* The proper way to fix this is to reorganize the code so that
* drivers (dock, etc.) do not call acpi_os_execute(), etc.
* For now just re-add this work to the kacpi_hotplug_wq so we
* don't deadlock on hotplug actions.
*/
get_bridge(bridge);
alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_bridge);
}
static void hotplug_event_func(acpi_handle handle, u32 type, void *context)
static void handle_hotplug_event(acpi_handle handle, u32 type, void *data)
{
struct acpiphp_func *func = context;
char objname[64];
struct acpi_buffer buffer = { .length = sizeof(objname),
.pointer = objname };
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
struct acpiphp_context *context;
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
/* bus re-enumerate */
dbg("%s: Bus check notify on %s\n", __func__, objname);
acpiphp_enable_slot(func->slot);
break;
case ACPI_NOTIFY_DEVICE_CHECK:
/* device check : re-enumerate from parent bus */
dbg("%s: Device check notify on %s\n", __func__, objname);
acpiphp_check_bridge(func->slot->bridge);
break;
case ACPI_NOTIFY_DEVICE_WAKE:
/* wake event */
dbg("%s: Device wake notify on %s\n", __func__, objname);
break;
case ACPI_NOTIFY_EJECT_REQUEST:
/* request device eject */
dbg("%s: Device eject notify on %s\n", __func__, objname);
if (!(acpiphp_disable_slot(func->slot)))
acpiphp_eject_slot(func->slot);
break;
default:
warn("notify_handler: unknown event type 0x%x for %s\n", type, objname);
break;
}
}
static void _handle_hotplug_event_func(struct work_struct *work)
{
struct acpi_hp_work *hp_work;
struct acpiphp_func *func;
case ACPI_NOTIFY_DEVICE_WAKE:
return;
hp_work = container_of(work, struct acpi_hp_work, work);
func = hp_work->context;
acpi_scan_lock_acquire();
case ACPI_NOTIFY_FREQUENCY_MISMATCH:
acpi_handle_err(handle, "Device cannot be configured due "
"to a frequency mismatch\n");
return;
hotplug_event_func(hp_work->handle, hp_work->type, func);
case ACPI_NOTIFY_BUS_MODE_MISMATCH:
acpi_handle_err(handle, "Device cannot be configured due "
"to a bus mode mismatch\n");
return;
acpi_scan_lock_release();
kfree(hp_work); /* allocated in handle_hotplug_event_func */
put_bridge(func->slot->bridge);
}
case ACPI_NOTIFY_POWER_FAULT:
acpi_handle_err(handle, "Device has suffered a power fault\n");
return;
/**
* handle_hotplug_event_func - handle ACPI event on functions (i.e. slots)
* @handle: Notify()'ed acpi_handle
* @type: Notify code
* @context: pointer to acpiphp_func structure
*
* Handles ACPI event notification on slots.
*/
static void handle_hotplug_event_func(acpi_handle handle, u32 type,
void *context)
{
struct acpiphp_func *func = context;
default:
acpi_handle_warn(handle, "Unsupported event type 0x%x\n", type);
return;
}
/*
* Currently the code adds all hotplug events to the kacpid_wq
* queue when it should add hotplug events to the kacpi_hotplug_wq.
* The proper way to fix this is to reorganize the code so that
* drivers (dock, etc.) do not call acpi_os_execute(), etc.
* For now just re-add this work to the kacpi_hotplug_wq so we
* don't deadlock on hotplug actions.
*/
get_bridge(func->slot->bridge);
alloc_acpi_hp_work(handle, type, context, _handle_hotplug_event_func);
mutex_lock(&acpiphp_context_lock);
context = acpiphp_get_context(handle);
if (context) {
get_bridge(context->func.parent);
acpiphp_put_context(context);
alloc_acpi_hp_work(handle, type, context, hotplug_event_work);
}
mutex_unlock(&acpiphp_context_lock);
}
/*
* Create hotplug slots for the PCI bus.
* It should always return 0 to avoid skipping following notifiers.
*/
void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle)
void acpiphp_enumerate_slots(struct pci_bus *bus)
{
struct acpiphp_bridge *bridge;
acpi_handle handle;
acpi_status status;
if (acpiphp_disabled)
return;
if (detect_ejectable_slots(handle) <= 0)
handle = ACPI_HANDLE(bus->bridge);
if (!handle)
return;
bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
if (bridge == NULL) {
err("out of memory\n");
if (!bridge) {
acpi_handle_err(handle, "No memory for bridge object\n");
return;
}
INIT_LIST_HEAD(&bridge->slots);
kref_init(&bridge->ref);
bridge->handle = handle;
bridge->pci_dev = pci_dev_get(bus->self);
bridge->pci_bus = bus;
......@@ -1176,30 +957,62 @@ void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle)
*/
get_device(&bus->dev);
if (!pci_is_root_bus(bridge->pci_bus) &&
acpi_has_method(bridge->handle, "_EJ0")) {
dbg("found ejectable p2p bridge\n");
bridge->flags |= BRIDGE_HAS_EJ0;
bridge->func = acpiphp_bridge_handle_to_function(handle);
if (!pci_is_root_bus(bridge->pci_bus)) {
struct acpiphp_context *context;
/*
* This bridge should have been registered as a hotplug function
* under its parent, so the context has to be there. If not, we
* are in deep goo.
*/
mutex_lock(&acpiphp_context_lock);
context = acpiphp_get_context(handle);
if (WARN_ON(!context)) {
mutex_unlock(&acpiphp_context_lock);
put_device(&bus->dev);
kfree(bridge);
return;
}
bridge->context = context;
context->bridge = bridge;
/* Get a reference to the parent bridge. */
get_bridge(context->func.parent);
mutex_unlock(&acpiphp_context_lock);
}
init_bridge_misc(bridge);
/* must be added to the list prior to calling register_slot */
mutex_lock(&bridge_mutex);
list_add(&bridge->list, &bridge_list);
mutex_unlock(&bridge_mutex);
/* register all slot objects under this bridge */
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
register_slot, NULL, bridge, NULL);
if (ACPI_FAILURE(status)) {
acpi_handle_err(handle, "failed to register slots\n");
cleanup_bridge(bridge);
put_bridge(bridge);
}
}
/* Destroy hotplug slots associated with the PCI bus */
void acpiphp_remove_slots(struct pci_bus *bus)
{
struct acpiphp_bridge *bridge, *tmp;
struct acpiphp_bridge *bridge;
if (acpiphp_disabled)
return;
list_for_each_entry_safe(bridge, tmp, &bridge_list, list)
mutex_lock(&bridge_mutex);
list_for_each_entry(bridge, &bridge_list, list)
if (bridge->pci_bus == bus) {
mutex_unlock(&bridge_mutex);
cleanup_bridge(bridge);
put_bridge(bridge);
break;
return;
}
mutex_unlock(&bridge_mutex);
}
/**
......@@ -1208,51 +1021,39 @@ void acpiphp_remove_slots(struct pci_bus *bus)
*/
int acpiphp_enable_slot(struct acpiphp_slot *slot)
{
int retval;
mutex_lock(&slot->crit_sect);
/* configure all functions */
if (!(slot->flags & SLOT_ENABLED))
enable_slot(slot);
/* wake up all functions */
retval = power_on_slot(slot);
if (retval)
goto err_exit;
if (get_slot_status(slot) == ACPI_STA_ALL) {
/* configure all functions */
retval = enable_device(slot);
if (retval)
power_off_slot(slot);
} else {
dbg("%s: Slot status is not ACPI_STA_ALL\n", __func__);
power_off_slot(slot);
}
err_exit:
mutex_unlock(&slot->crit_sect);
return retval;
return 0;
}
/**
* acpiphp_disable_slot - power off slot
* acpiphp_disable_and_eject_slot - power off and eject slot
* @slot: ACPI PHP slot
*/
int acpiphp_disable_slot(struct acpiphp_slot *slot)
int acpiphp_disable_and_eject_slot(struct acpiphp_slot *slot)
{
struct acpiphp_func *func;
int retval = 0;
mutex_lock(&slot->crit_sect);
/* unconfigure all functions */
retval = disable_device(slot);
if (retval)
goto err_exit;
disable_slot(slot);
list_for_each_entry(func, &slot->funcs, sibling)
if (func->flags & FUNC_HAS_EJ0) {
acpi_handle handle = func_to_handle(func);
/* power off all functions */
retval = power_off_slot(slot);
if (retval)
goto err_exit;
if (ACPI_FAILURE(acpi_evaluate_ej0(handle)))
acpi_handle_err(handle, "_EJ0 failed\n");
break;
}
err_exit:
mutex_unlock(&slot->crit_sect);
return retval;
}
......@@ -1264,7 +1065,7 @@ int acpiphp_disable_slot(struct acpiphp_slot *slot)
*/
u8 acpiphp_get_power_status(struct acpiphp_slot *slot)
{
return (slot->flags & SLOT_POWEREDON);
return (slot->flags & SLOT_ENABLED);
}
......@@ -1274,11 +1075,7 @@ u8 acpiphp_get_power_status(struct acpiphp_slot *slot)
*/
u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
{
unsigned int sta;
sta = get_slot_status(slot);
return (sta & ACPI_STA_DEVICE_UI) ? 0 : 1;
return !(get_slot_status(slot) & ACPI_STA_DEVICE_UI);
}
......@@ -1288,9 +1085,5 @@ u8 acpiphp_get_latch_status(struct acpiphp_slot *slot)
*/
u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot)
{
unsigned int sta;
sta = get_slot_status(slot);
return (sta == 0) ? 0 : 1;
return !!get_slot_status(slot);
}
......@@ -66,7 +66,7 @@ do { \
#define IBM_HARDWARE_ID1 "IBM37D0"
#define IBM_HARDWARE_ID2 "IBM37D4"
#define hpslot_to_sun(A) (((struct slot *)((A)->private))->acpi_slot->sun)
#define hpslot_to_sun(A) (((struct slot *)((A)->private))->sun)
/* union apci_descriptor - allows access to the
* various device descriptors that are embedded in the
......
......@@ -210,7 +210,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
}
if (!error)
dev_info(&dev->dev, "power state changed by ACPI to %s\n",
dev_dbg(&dev->dev, "power state changed by ACPI to %s\n",
acpi_power_state_string(state_conv[state]));
return error;
......@@ -290,24 +290,16 @@ static struct pci_platform_pm_ops acpi_pci_platform_pm = {
void acpi_pci_add_bus(struct pci_bus *bus)
{
acpi_handle handle = NULL;
if (bus->bridge)
handle = ACPI_HANDLE(bus->bridge);
if (acpi_pci_disabled || handle == NULL)
if (acpi_pci_disabled || !bus->bridge)
return;
acpi_pci_slot_enumerate(bus, handle);
acpiphp_enumerate_slots(bus, handle);
acpi_pci_slot_enumerate(bus);
acpiphp_enumerate_slots(bus);
}
void acpi_pci_remove_bus(struct pci_bus *bus)
{
/*
* bus->bridge->acpi_node.handle has already been reset to NULL
* when acpi_pci_remove_bus() is called, so don't check ACPI handle.
*/
if (acpi_pci_disabled)
if (acpi_pci_disabled || !bus->bridge)
return;
acpiphp_remove_slots(bus);
......
......@@ -47,24 +47,22 @@ void acpi_pci_remove_bus(struct pci_bus *bus);
#ifdef CONFIG_ACPI_PCI_SLOT
void acpi_pci_slot_init(void);
void acpi_pci_slot_enumerate(struct pci_bus *bus, acpi_handle handle);
void acpi_pci_slot_enumerate(struct pci_bus *bus);
void acpi_pci_slot_remove(struct pci_bus *bus);
#else
static inline void acpi_pci_slot_init(void) { }
static inline void acpi_pci_slot_enumerate(struct pci_bus *bus,
acpi_handle handle) { }
static inline void acpi_pci_slot_enumerate(struct pci_bus *bus) { }
static inline void acpi_pci_slot_remove(struct pci_bus *bus) { }
#endif
#ifdef CONFIG_HOTPLUG_PCI_ACPI
void acpiphp_init(void);
void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle);
void acpiphp_enumerate_slots(struct pci_bus *bus);
void acpiphp_remove_slots(struct pci_bus *bus);
void acpiphp_check_host_bridge(acpi_handle handle);
#else
static inline void acpiphp_init(void) { }
static inline void acpiphp_enumerate_slots(struct pci_bus *bus,
acpi_handle handle) { }
static inline void acpiphp_enumerate_slots(struct pci_bus *bus) { }
static inline void acpiphp_remove_slots(struct pci_bus *bus) { }
static inline void acpiphp_check_host_bridge(acpi_handle handle) { }
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment