Commit 4e8662bb authored by Kristen Accardi's avatar Kristen Accardi Committed by Len Brown

ACPIPHP: use ACPI dock driver

Modify the acpiphp driver to use the ACPI dock driver for dock
notifications.  Only load the acpiphp driver if we find we have pci dock
devices.
Signed-off-by: default avatarKristen Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: default avatarAdrian Bunk <bunk@stusta.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent a5e1b940
......@@ -40,8 +40,7 @@ ibmphp-objs := ibmphp_core.o \
ibmphp_hpc.o
acpiphp-objs := acpiphp_core.o \
acpiphp_glue.o \
acpiphp_dock.o
acpiphp_glue.o
rpaphp-objs := rpaphp_core.o \
rpaphp_pci.o \
......
......@@ -130,7 +130,7 @@ struct acpiphp_func {
struct list_head sibling;
struct pci_dev *pci_dev;
struct notifier_block nb;
acpi_handle handle;
u8 function; /* pci function# */
......@@ -151,24 +151,6 @@ struct acpiphp_attention_info
};
struct dependent_device {
struct list_head device_list;
struct list_head pci_list;
acpi_handle handle;
struct acpiphp_func *func;
};
struct acpiphp_dock_station {
acpi_handle handle;
u32 last_dock_time;
u32 flags;
struct acpiphp_func *dock_bridge;
struct list_head dependent_devices;
struct list_head pci_dependent_devices;
};
/* PCI bus bridge HID */
#define ACPI_PCI_HOST_HID "PNP0A03"
......@@ -207,11 +189,6 @@ struct acpiphp_dock_station {
#define FUNC_HAS_PS2 (0x00000040)
#define FUNC_HAS_PS3 (0x00000080)
#define FUNC_HAS_DCK (0x00000100)
#define FUNC_IS_DD (0x00000200)
/* dock station flags */
#define DOCK_DOCKING (0x00000001)
#define DOCK_HAS_BRIDGE (0x00000002)
/* function prototypes */
......@@ -236,16 +213,6 @@ extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot);
extern u32 acpiphp_get_address (struct acpiphp_slot *slot);
/* acpiphp_dock.c */
extern int find_dock_station(void);
extern void remove_dock_station(void);
extern void add_dependent_device(struct dependent_device *new_dd);
extern void add_pci_dependent_device(struct dependent_device *new_dd);
extern struct dependent_device *get_dependent_device(acpi_handle handle);
extern int is_dependent_device(acpi_handle handle);
extern int detect_dependent_devices(acpi_handle *bridge_handle);
extern struct dependent_device *alloc_dependent_device(acpi_handle handle);
/* variables */
extern int acpiphp_debug;
......
......@@ -416,27 +416,12 @@ void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
static int __init acpiphp_init(void)
{
int retval;
int docking_station;
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
acpiphp_debug = debug;
docking_station = find_dock_station();
/* read all the ACPI info from the system */
retval = init_acpi();
/* if we have found a docking station, we should
* go ahead and load even if init_acpi has found
* no slots. This handles the case when the _DCK
* method not defined under the actual dock bridge
*/
if (docking_station)
return 0;
else
return retval;
return init_acpi();
}
......@@ -444,8 +429,6 @@ static void __exit acpiphp_exit(void)
{
/* deallocate internal data structures etc. */
acpiphp_glue_exit();
remove_dock_station();
}
module_init(acpiphp_init);
......
/*
* ACPI PCI HotPlug dock functions to ACPI CA subsystem
*
* Copyright (C) 2006 Kristen Carlson Accardi (kristen.c.accardi@intel.com)
* Copyright (C) 2006 Intel Corporation
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Send feedback to <kristen.c.accardi@intel.com>
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/smp_lock.h>
#include <linux/mutex.h>
#include "../pci.h"
#include "pci_hotplug.h"
#include "acpiphp.h"
static struct acpiphp_dock_station *ds;
#define MY_NAME "acpiphp_dock"
int is_dependent_device(acpi_handle handle)
{
return (get_dependent_device(handle) ? 1 : 0);
}
static acpi_status
find_dependent_device(acpi_handle handle, u32 lvl, void *context, void **rv)
{
int *count = (int *)context;
if (is_dependent_device(handle)) {
(*count)++;
return AE_CTRL_TERMINATE;
} else {
return AE_OK;
}
}
void add_dependent_device(struct dependent_device *new_dd)
{
list_add_tail(&new_dd->device_list, &ds->dependent_devices);
}
void add_pci_dependent_device(struct dependent_device *new_dd)
{
list_add_tail(&new_dd->pci_list, &ds->pci_dependent_devices);
}
struct dependent_device * get_dependent_device(acpi_handle handle)
{
struct dependent_device *dd;
if (!ds)
return NULL;
list_for_each_entry(dd, &ds->dependent_devices, device_list) {
if (handle == dd->handle)
return dd;
}
return NULL;
}
struct dependent_device *alloc_dependent_device(acpi_handle handle)
{
struct dependent_device *dd;
dd = kzalloc(sizeof(*dd), GFP_KERNEL);
if (dd) {
INIT_LIST_HEAD(&dd->pci_list);
INIT_LIST_HEAD(&dd->device_list);
dd->handle = handle;
}
return dd;
}
static int is_dock(acpi_handle handle)
{
acpi_status status;
acpi_handle tmp;
status = acpi_get_handle(handle, "_DCK", &tmp);
if (ACPI_FAILURE(status)) {
return 0;
}
return 1;
}
static int dock_present(void)
{
unsigned long sta;
acpi_status status;
if (ds) {
status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
if (ACPI_SUCCESS(status) && sta)
return 1;
}
return 0;
}
static void eject_dock(void)
{
struct acpi_object_list arg_list;
union acpi_object arg;
arg_list.count = 1;
arg_list.pointer = &arg;
arg.type = ACPI_TYPE_INTEGER;
arg.integer.value = 1;
if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
&arg_list, NULL)) || dock_present())
warn("%s: failed to eject dock!\n", __FUNCTION__);
return;
}
static acpi_status handle_dock(int dock)
{
acpi_status status;
struct acpi_object_list arg_list;
union acpi_object arg;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
dbg("%s: %s\n", __FUNCTION__, dock ? "docking" : "undocking");
/* _DCK method has one argument */
arg_list.count = 1;
arg_list.pointer = &arg;
arg.type = ACPI_TYPE_INTEGER;
arg.integer.value = dock;
status = acpi_evaluate_object(ds->handle, "_DCK",
&arg_list, &buffer);
if (ACPI_FAILURE(status))
err("%s: failed to execute _DCK\n", __FUNCTION__);
acpi_os_free(buffer.pointer);
return status;
}
static inline void dock(void)
{
handle_dock(1);
}
static inline void undock(void)
{
handle_dock(0);
}
/*
* the _DCK method can do funny things... and sometimes not
* hah-hah funny.
*
* TBD - figure out a way to only call fixups for
* systems that require them.
*/
static void post_dock_fixups(void)
{
struct pci_bus *bus;
u32 buses;
struct dependent_device *dd;
list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list) {
bus = dd->func->slot->bridge->pci_bus;
/* fixup bad _DCK function that rewrites
* secondary bridge on slot
*/
pci_read_config_dword(bus->self,
PCI_PRIMARY_BUS,
&buses);
if (((buses >> 8) & 0xff) != bus->secondary) {
buses = (buses & 0xff000000)
| ((unsigned int)(bus->primary) << 0)
| ((unsigned int)(bus->secondary) << 8)
| ((unsigned int)(bus->subordinate) << 16);
pci_write_config_dword(bus->self,
PCI_PRIMARY_BUS,
buses);
}
}
}
static void hotplug_pci(u32 type)
{
struct dependent_device *dd;
list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list)
handle_hotplug_event_func(dd->handle, type, dd->func);
}
static inline void begin_dock(void)
{
ds->flags |= DOCK_DOCKING;
}
static inline void complete_dock(void)
{
ds->flags &= ~(DOCK_DOCKING);
ds->last_dock_time = jiffies;
}
static int dock_in_progress(void)
{
if (ds->flags & DOCK_DOCKING ||
ds->last_dock_time == jiffies) {
dbg("dock in progress\n");
return 1;
}
return 0;
}
static void
handle_hotplug_event_dock(acpi_handle handle, u32 type, void *context)
{
dbg("%s: enter\n", __FUNCTION__);
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
dbg("BUS Check\n");
if (!dock_in_progress() && dock_present()) {
begin_dock();
dock();
if (!dock_present()) {
err("Unable to dock!\n");
break;
}
post_dock_fixups();
hotplug_pci(type);
complete_dock();
}
break;
case ACPI_NOTIFY_EJECT_REQUEST:
dbg("EJECT request\n");
if (!dock_in_progress() && dock_present()) {
hotplug_pci(type);
undock();
eject_dock();
if (dock_present())
err("Unable to undock!\n");
}
break;
}
}
static acpi_status
find_dock_ejd(acpi_handle handle, u32 lvl, void *context, void **rv)
{
acpi_status status;
acpi_handle tmp;
acpi_handle dck_handle = (acpi_handle) context;
char objname[64];
struct acpi_buffer buffer = { .length = sizeof(objname),
.pointer = objname };
struct acpi_buffer ejd_buffer = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *ejd_obj;
status = acpi_get_handle(handle, "_EJD", &tmp);
if (ACPI_FAILURE(status))
return AE_OK;
/* make sure we are dependent on the dock device,
* by executing the _EJD method, then getting a handle
* to the device referenced by that name. If that
* device handle is the same handle as the dock station
* handle, then we are a device dependent on the dock station
*/
acpi_get_name(dck_handle, ACPI_FULL_PATHNAME, &buffer);
status = acpi_evaluate_object(handle, "_EJD", NULL, &ejd_buffer);
if (ACPI_FAILURE(status)) {
err("Unable to execute _EJD!\n");
goto find_ejd_out;
}
ejd_obj = ejd_buffer.pointer;
status = acpi_get_handle(NULL, ejd_obj->string.pointer, &tmp);
if (ACPI_FAILURE(status))
goto find_ejd_out;
if (tmp == dck_handle) {
struct dependent_device *dd;
dbg("%s: found device dependent on dock\n", __FUNCTION__);
dd = alloc_dependent_device(handle);
if (!dd) {
err("Can't allocate memory for dependent device!\n");
goto find_ejd_out;
}
add_dependent_device(dd);
}
find_ejd_out:
acpi_os_free(ejd_buffer.pointer);
return AE_OK;
}
int detect_dependent_devices(acpi_handle *bridge_handle)
{
acpi_status status;
int count;
count = 0;
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle,
(u32)1, find_dependent_device,
(void *)&count, NULL);
return count;
}
static acpi_status
find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
{
int *count = (int *)context;
if (is_dock(handle)) {
dbg("%s: found dock\n", __FUNCTION__);
ds = kzalloc(sizeof(*ds), GFP_KERNEL);
ds->handle = handle;
INIT_LIST_HEAD(&ds->dependent_devices);
INIT_LIST_HEAD(&ds->pci_dependent_devices);
/* look for devices dependent on dock station */
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, find_dock_ejd, handle, NULL);
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
handle_hotplug_event_dock, ds);
(*count)++;
}
return AE_OK;
}
int find_dock_station(void)
{
int num = 0;
ds = NULL;
/* start from the root object, because some laptops define
* _DCK methods outside the scope of PCI (IBM x-series laptop)
*/
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, find_dock, &num, NULL);
return num;
}
void remove_dock_station(void)
{
struct dependent_device *dd, *tmp;
if (ds) {
if (ACPI_FAILURE(acpi_remove_notify_handler(ds->handle,
ACPI_SYSTEM_NOTIFY, handle_hotplug_event_dock)))
err("failed to remove dock notify handler\n");
/* free all dependent devices */
list_for_each_entry_safe(dd, tmp, &ds->dependent_devices,
device_list)
kfree(dd);
/* no need to touch the pci_dependent_device list,
* cause all memory was freed above
*/
kfree(ds);
}
}
......@@ -116,6 +116,59 @@ is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
}
}
/* callback routine to check for the existance of a pci dock device */
static acpi_status
is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv)
{
int *count = (int *)context;
if (is_dock_device(handle)) {
(*count)++;
return AE_CTRL_TERMINATE;
} else {
return AE_OK;
}
}
/*
* the _DCK method can do funny things... and sometimes not
* hah-hah funny.
*
* TBD - figure out a way to only call fixups for
* systems that require them.
*/
static int post_dock_fixups(struct notifier_block *nb, unsigned long val,
void *v)
{
struct acpiphp_func *func = container_of(nb, struct acpiphp_func, nb);
struct pci_bus *bus = func->slot->bridge->pci_bus;
u32 buses;
if (!bus->self)
return NOTIFY_OK;
/* fixup bad _DCK function that rewrites
* secondary bridge on slot
*/
pci_read_config_dword(bus->self,
PCI_PRIMARY_BUS,
&buses);
if (((buses >> 8) & 0xff) != bus->secondary) {
buses = (buses & 0xff000000)
| ((unsigned int)(bus->primary) << 0)
| ((unsigned int)(bus->secondary) << 8)
| ((unsigned int)(bus->subordinate) << 16);
pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses);
}
return NOTIFY_OK;
}
/* callback routine to register each ACPI PCI slot object */
static acpi_status
......@@ -124,7 +177,6 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
struct acpiphp_bridge *bridge = (struct acpiphp_bridge *)context;
struct acpiphp_slot *slot;
struct acpiphp_func *newfunc;
struct dependent_device *dd;
acpi_handle tmp;
acpi_status status = AE_OK;
unsigned long adr, sun;
......@@ -137,7 +189,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
status = acpi_get_handle(handle, "_EJ0", &tmp);
if (ACPI_FAILURE(status) && !(is_dependent_device(handle)))
if (ACPI_FAILURE(status) && !(is_dock_device(handle)))
return AE_OK;
device = (adr >> 16) & 0xffff;
......@@ -162,18 +214,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp)))
newfunc->flags |= FUNC_HAS_PS3;
if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp))) {
if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp)))
newfunc->flags |= FUNC_HAS_DCK;
/* add to devices dependent on dock station,
* because this may actually be the dock bridge
*/
dd = alloc_dependent_device(handle);
if (!dd)
err("Can't allocate memory for "
"new dependent device!\n");
else
add_dependent_device(dd);
}
status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
if (ACPI_FAILURE(status))
......@@ -225,20 +267,23 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON);
}
/* if this is a device dependent on a dock station,
* associate the acpiphp_func to the dependent_device
* struct.
*/
if ((dd = get_dependent_device(handle))) {
newfunc->flags |= FUNC_IS_DD;
/*
* we don't want any devices which is dependent
* on the dock to have it's _EJ0 method executed.
* because we need to run _DCK first.
if (is_dock_device(handle)) {
/* we don't want to call this device's _EJ0
* because we want the dock notify handler
* to call it after it calls _DCK
*/
newfunc->flags &= ~FUNC_HAS_EJ0;
dd->func = newfunc;
add_pci_dependent_device(dd);
if (register_hotplug_dock_device(handle,
handle_hotplug_event_func, newfunc))
dbg("failed to register dock device\n");
/* we need to be notified when dock events happen
* outside of the hotplug operation, since we may
* need to do fixups before we can hotplug.
*/
newfunc->nb.notifier_call = post_dock_fixups;
if (register_dock_notifier(&newfunc->nb))
dbg("failed to register a dock notifier");
}
/* install notify handler */
......@@ -277,6 +322,15 @@ static int detect_ejectable_slots(acpi_handle *bridge_handle)
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1,
is_ejectable_slot, (void *)&count, NULL);
/*
* we also need to add this bridge if there is a dock bridge or
* other pci device on a dock station (removable)
*/
if (!count)
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle,
(u32)1, is_pci_dock_device, (void *)&count,
NULL);
return count;
}
......@@ -487,8 +541,7 @@ find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
goto out;
/* check if this bridge has ejectable slots */
if ((detect_ejectable_slots(handle) > 0) ||
(detect_dependent_devices(handle) > 0)) {
if ((detect_ejectable_slots(handle) > 0)) {
dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev));
add_p2p_bridge(handle, dev);
}
......@@ -605,6 +658,10 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge)
list_for_each_safe (list, tmp, &slot->funcs) {
struct acpiphp_func *func;
func = list_entry(list, struct acpiphp_func, sibling);
if (is_dock_device(func->handle)) {
unregister_hotplug_dock_device(func->handle);
unregister_dock_notifier(&func->nb);
}
if (!(func->flags & FUNC_HAS_DCK)) {
status = acpi_remove_notify_handler(func->handle,
ACPI_SYSTEM_NOTIFY,
......
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