aboutsummaryrefslogtreecommitdiff
path: root/drivers/pci/hotplug
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/hotplug')
-rw-r--r--drivers/pci/hotplug/Makefile20
-rw-r--r--drivers/pci/hotplug/acpi_pcihp.c (renamed from drivers/pci/hotplug/shpchprm_acpi.c)145
-rw-r--r--drivers/pci/hotplug/acpiphp.h62
-rw-r--r--drivers/pci/hotplug/acpiphp_core.c134
-rw-r--r--drivers/pci/hotplug/acpiphp_dock.c438
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c356
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_core.c9
-rw-r--r--drivers/pci/hotplug/cpqphp.h3
-rw-r--r--drivers/pci/hotplug/cpqphp_core.c27
-rw-r--r--drivers/pci/hotplug/cpqphp_ctrl.c124
-rw-r--r--drivers/pci/hotplug/fakephp.c9
-rw-r--r--drivers/pci/hotplug/ibmphp.h2
-rw-r--r--drivers/pci/hotplug/ibmphp_core.c10
-rw-r--r--drivers/pci/hotplug/ibmphp_ebda.c59
-rw-r--r--drivers/pci/hotplug/ibmphp_hpc.c64
-rw-r--r--drivers/pci/hotplug/ibmphp_pci.c121
-rw-r--r--drivers/pci/hotplug/ibmphp_res.c81
-rw-r--r--drivers/pci/hotplug/pci_hotplug.h16
-rw-r--r--drivers/pci/hotplug/pciehp.h27
-rw-r--r--drivers/pci/hotplug/pciehp_core.c19
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c68
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c77
-rw-r--r--drivers/pci/hotplug/pciehprm_acpi.c257
-rw-r--r--drivers/pci/hotplug/pciehprm_nonacpi.c47
-rw-r--r--drivers/pci/hotplug/pcihp_skeleton.c33
-rw-r--r--drivers/pci/hotplug/rpaphp_slot.c9
-rw-r--r--drivers/pci/hotplug/sgi_hotplug.c16
-rw-r--r--drivers/pci/hotplug/shpchp.h108
-rw-r--r--drivers/pci/hotplug/shpchp_core.c331
-rw-r--r--drivers/pci/hotplug/shpchp_ctrl.c808
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c518
-rw-r--r--drivers/pci/hotplug/shpchp_pci.c10
-rw-r--r--drivers/pci/hotplug/shpchprm_legacy.c54
-rw-r--r--drivers/pci/hotplug/shpchprm_nonacpi.c57
34 files changed, 1879 insertions, 2240 deletions
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
index 3c71e3077ff..421cfffb175 100644
--- a/drivers/pci/hotplug/Makefile
+++ b/drivers/pci/hotplug/Makefile
@@ -22,6 +22,9 @@ ifdef CONFIG_HOTPLUG_PCI_CPCI
pci_hotplug-objs += cpci_hotplug_core.o \
cpci_hotplug_pci.o
endif
+ifdef CONFIG_ACPI
+pci_hotplug-objs += acpi_pcihp.o
+endif
cpqphp-objs := cpqphp_core.o \
cpqphp_ctrl.o \
@@ -37,7 +40,8 @@ ibmphp-objs := ibmphp_core.o \
ibmphp_hpc.o
acpiphp-objs := acpiphp_core.o \
- acpiphp_glue.o
+ acpiphp_glue.o \
+ acpiphp_dock.o
rpaphp-objs := rpaphp_core.o \
rpaphp_pci.o \
@@ -50,23 +54,9 @@ pciehp-objs := pciehp_core.o \
pciehp_ctrl.o \
pciehp_pci.o \
pciehp_hpc.o
-ifdef CONFIG_ACPI
- pciehp-objs += pciehprm_acpi.o
-else
- pciehp-objs += pciehprm_nonacpi.o
-endif
shpchp-objs := shpchp_core.o \
shpchp_ctrl.o \
shpchp_pci.o \
shpchp_sysfs.o \
shpchp_hpc.o
-ifdef CONFIG_ACPI
- shpchp-objs += shpchprm_acpi.o
-else
- ifdef CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY
- shpchp-objs += shpchprm_legacy.o
- else
- shpchp-objs += shpchprm_nonacpi.o
- endif
-endif
diff --git a/drivers/pci/hotplug/shpchprm_acpi.c b/drivers/pci/hotplug/acpi_pcihp.c
index 17145e52223..39af9c325f3 100644
--- a/drivers/pci/hotplug/shpchprm_acpi.c
+++ b/drivers/pci/hotplug/acpi_pcihp.c
@@ -1,7 +1,7 @@
/*
- * SHPCHPRM ACPI: PHP Resource Manager for ACPI platform
+ * Common ACPI functions for hot plug platforms
*
- * Copyright (C) 2003-2004 Intel Corporation
+ * Copyright (C) 2006 Intel Corporation
*
* All rights reserved.
*
@@ -31,26 +31,12 @@
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/actypes.h>
-#include "shpchp.h"
+#include "pci_hotplug.h"
#define METHOD_NAME__SUN "_SUN"
#define METHOD_NAME__HPP "_HPP"
#define METHOD_NAME_OSHP "OSHP"
-static u8 * acpi_path_name( acpi_handle handle)
-{
- acpi_status status;
- static u8 path_name[ACPI_PATHNAME_MAX];
- struct acpi_buffer ret_buf = { ACPI_PATHNAME_MAX, path_name };
-
- memset(path_name, 0, sizeof (path_name));
- status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf);
-
- if (ACPI_FAILURE(status))
- return NULL;
- else
- return path_name;
-}
static acpi_status
acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
@@ -58,18 +44,21 @@ acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
acpi_status status;
u8 nui[4];
struct acpi_buffer ret_buf = { 0, NULL};
+ struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *ext_obj, *package;
- u8 *path_name = acpi_path_name(handle);
int i, len = 0;
+ acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
+
/* get _hpp */
status = acpi_evaluate_object(handle, METHOD_NAME__HPP, NULL, &ret_buf);
switch (status) {
case AE_BUFFER_OVERFLOW:
ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL);
if (!ret_buf.pointer) {
- err ("%s:%s alloc for _HPP fail\n", __FUNCTION__,
- path_name);
+ printk(KERN_ERR "%s:%s alloc for _HPP fail\n",
+ __FUNCTION__, (char *)string.pointer);
+ acpi_os_free(string.pointer);
return AE_NO_MEMORY;
}
status = acpi_evaluate_object(handle, METHOD_NAME__HPP,
@@ -78,16 +67,17 @@ acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
break;
default:
if (ACPI_FAILURE(status)) {
- dbg("%s:%s _HPP fail=0x%x\n", __FUNCTION__,
- path_name, status);
+ pr_debug("%s:%s _HPP fail=0x%x\n", __FUNCTION__,
+ (char *)string.pointer, status);
+ acpi_os_free(string.pointer);
return status;
}
}
ext_obj = (union acpi_object *) ret_buf.pointer;
if (ext_obj->type != ACPI_TYPE_PACKAGE) {
- err ("%s:%s _HPP obj not a package\n", __FUNCTION__,
- path_name);
+ printk(KERN_ERR "%s:%s _HPP obj not a package\n", __FUNCTION__,
+ (char *)string.pointer);
status = AE_ERROR;
goto free_and_return;
}
@@ -101,8 +91,8 @@ acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
nui[i] = (u8)ext_obj->integer.value;
break;
default:
- err ("%s:%s _HPP obj type incorrect\n", __FUNCTION__,
- path_name);
+ printk(KERN_ERR "%s:%s _HPP obj type incorrect\n",
+ __FUNCTION__, (char *)string.pointer);
status = AE_ERROR;
goto free_and_return;
}
@@ -113,54 +103,52 @@ acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp)
hpp->enable_serr = nui[2];
hpp->enable_perr = nui[3];
- dbg(" _HPP: cache_line_size=0x%x\n", hpp->cache_line_size);
- dbg(" _HPP: latency timer =0x%x\n", hpp->latency_timer);
- dbg(" _HPP: enable SERR =0x%x\n", hpp->enable_serr);
- dbg(" _HPP: enable PERR =0x%x\n", hpp->enable_perr);
+ pr_debug(" _HPP: cache_line_size=0x%x\n", hpp->cache_line_size);
+ pr_debug(" _HPP: latency timer =0x%x\n", hpp->latency_timer);
+ pr_debug(" _HPP: enable SERR =0x%x\n", hpp->enable_serr);
+ pr_debug(" _HPP: enable PERR =0x%x\n", hpp->enable_perr);
free_and_return:
- kfree(ret_buf.pointer);
+ acpi_os_free(string.pointer);
+ acpi_os_free(ret_buf.pointer);
return status;
}
-static void acpi_run_oshp(acpi_handle handle)
+
+
+/* acpi_run_oshp - get control of hotplug from the firmware
+ *
+ * @handle - the handle of the hotplug controller.
+ */
+acpi_status acpi_run_oshp(acpi_handle handle)
{
acpi_status status;
- u8 *path_name = acpi_path_name(handle);
+ struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
/* run OSHP */
status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL);
- if (ACPI_FAILURE(status)) {
- err("%s:%s OSHP fails=0x%x\n", __FUNCTION__, path_name,
- status);
- } else {
- dbg("%s:%s OSHP passes\n", __FUNCTION__, path_name);
- }
-}
-
-int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
-{
- int offset = devnum - ctrl->slot_device_offset;
+ if (ACPI_FAILURE(status))
+ printk(KERN_ERR "%s:%s OSHP fails=0x%x\n", __FUNCTION__,
+ (char *)string.pointer, status);
+ else
+ pr_debug("%s:%s OSHP passes\n", __FUNCTION__,
+ (char *)string.pointer);
- dbg("%s: ctrl->slot_num_inc %d, offset %d\n", __FUNCTION__, ctrl->slot_num_inc, offset);
- *sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc *offset);
- return 0;
+ acpi_os_free(string.pointer);
+ return status;
}
+EXPORT_SYMBOL_GPL(acpi_run_oshp);
+
-void get_hp_hw_control_from_firmware(struct pci_dev *dev)
-{
- /*
- * OSHP is an optional ACPI firmware control method. If present,
- * we need to run it to inform BIOS that we will control SHPC
- * hardware from now on.
- */
- acpi_handle handle = DEVICE_ACPI_HANDLE(&(dev->dev));
- if (!handle)
- return;
- acpi_run_oshp(handle);
-}
-void get_hp_params_from_firmware(struct pci_dev *dev,
+/* acpi_get_hp_params_from_firmware
+ *
+ * @dev - the pci_dev of the newly added device
+ * @hpp - allocated by the caller
+ */
+acpi_status acpi_get_hp_params_from_firmware(struct pci_dev *dev,
struct hotplug_params *hpp)
{
acpi_status status = AE_NOT_FOUND;
@@ -182,5 +170,42 @@ void get_hp_params_from_firmware(struct pci_dev *dev,
/* Check if a parent object supports _HPP */
pdev = pdev->bus->parent->self;
}
+ return status;
}
+EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware);
+
+/* acpi_root_bridge - check to see if this acpi object is a root bridge
+ *
+ * @handle - the acpi object in question.
+ */
+int acpi_root_bridge(acpi_handle handle)
+{
+ acpi_status status;
+ struct acpi_device_info *info;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ int i;
+
+ status = acpi_get_object_info(handle, &buffer);
+ if (ACPI_SUCCESS(status)) {
+ info = buffer.pointer;
+ if ((info->valid & ACPI_VALID_HID) &&
+ !strcmp(PCI_ROOT_HID_STRING,
+ info->hardware_id.value)) {
+ acpi_os_free(buffer.pointer);
+ return 1;
+ }
+ if (info->valid & ACPI_VALID_CID) {
+ for (i=0; i < info->compatibility_id.count; i++) {
+ if (!strcmp(PCI_ROOT_HID_STRING,
+ info->compatibility_id.id[i].value)) {
+ acpi_os_free(buffer.pointer);
+ return 1;
+ }
+ }
+ }
+ acpi_os_free(buffer.pointer);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_root_bridge);
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index 293603e1b7c..467ac70a46f 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -37,6 +37,7 @@
#include <linux/acpi.h>
#include <linux/kobject.h> /* for KOBJ_NAME_LEN */
+#include <linux/mutex.h>
#include "pci_hotplug.h"
#define dbg(format, arg...) \
@@ -59,26 +60,10 @@ struct acpiphp_slot;
* struct slot - slot information for each *physical* slot
*/
struct slot {
- u8 number;
struct hotplug_slot *hotplug_slot;
- struct list_head slot_list;
-
struct acpiphp_slot *acpi_slot;
};
-/**
- * struct hpp_param - ACPI 2.0 _HPP Hot Plug Parameters
- * @cache_line_size in DWORD
- * @latency_timer in PCI clock
- * @enable_SERR 0 or 1
- * @enable_PERR 0 or 1
- */
-struct hpp_param {
- u8 cache_line_size;
- u8 latency_timer;
- u8 enable_SERR;
- u8 enable_PERR;
-};
/**
@@ -102,7 +87,7 @@ struct acpiphp_bridge {
struct pci_dev *pci_dev;
/* ACPI 2.0 _HPP parameters */
- struct hpp_param hpp;
+ struct hotplug_params hpp;
spinlock_t res_lock;
};
@@ -118,9 +103,9 @@ struct acpiphp_slot {
struct acpiphp_bridge *bridge; /* parent */
struct list_head funcs; /* one slot may have different
objects (i.e. for each function) */
- struct semaphore crit_sect;
+ struct slot *slot;
+ struct mutex crit_sect;
- u32 id; /* slot id (serial #) for hotplug core */
u8 device; /* pci device# */
u32 sun; /* ACPI _SUN (slot unique number) */
@@ -160,6 +145,25 @@ struct acpiphp_attention_info
struct module *owner;
};
+
+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"
@@ -197,19 +201,27 @@ struct acpiphp_attention_info
#define FUNC_HAS_PS1 (0x00000020)
#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 */
/* acpiphp_core.c */
extern int acpiphp_register_attention(struct acpiphp_attention_info*info);
extern int acpiphp_unregister_attention(struct acpiphp_attention_info *info);
+extern int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot);
+extern void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot);
/* acpiphp_glue.c */
extern int acpiphp_glue_init (void);
extern void acpiphp_glue_exit (void);
extern int acpiphp_get_num_slots (void);
-extern struct acpiphp_slot *get_slot_from_id (int id);
typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data);
+void handle_hotplug_event_func(acpi_handle, u32, void*);
extern int acpiphp_enable_slot (struct acpiphp_slot *slot);
extern int acpiphp_disable_slot (struct acpiphp_slot *slot);
@@ -219,6 +231,16 @@ 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;
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
index 60c4c38047a..4f1b0da8e47 100644
--- a/drivers/pci/hotplug/acpiphp_core.c
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -44,8 +44,6 @@
#include "pci_hotplug.h"
#include "acpiphp.h"
-static LIST_HEAD(slot_list);
-
#define MY_NAME "acpiphp"
static int debug;
@@ -341,62 +339,53 @@ static void release_slot(struct hotplug_slot *hotplug_slot)
kfree(slot);
}
-/**
- * init_slots - initialize 'struct slot' structures for each slot
- *
- */
-static int __init init_slots(void)
+/* callback routine to initialize 'struct slot' for each slot */
+int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
{
struct slot *slot;
+ struct hotplug_slot *hotplug_slot;
+ struct hotplug_slot_info *hotplug_slot_info;
int retval = -ENOMEM;
- int i;
-
- for (i = 0; i < num_slots; ++i) {
- slot = kmalloc(sizeof(struct slot), GFP_KERNEL);
- if (!slot)
- goto error;
- memset(slot, 0, sizeof(struct slot));
-
- slot->hotplug_slot = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
- if (!slot->hotplug_slot)
- goto error_slot;
- memset(slot->hotplug_slot, 0, sizeof(struct hotplug_slot));
-
- slot->hotplug_slot->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
- if (!slot->hotplug_slot->info)
- goto error_hpslot;
- memset(slot->hotplug_slot->info, 0, sizeof(struct hotplug_slot_info));
-
- slot->hotplug_slot->name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
- if (!slot->hotplug_slot->name)
- goto error_info;
-
- slot->number = i;
-
- slot->hotplug_slot->private = slot;
- slot->hotplug_slot->release = &release_slot;
- slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
-
- slot->acpi_slot = get_slot_from_id(i);
- slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot);
- slot->hotplug_slot->info->attention_status = 0;
- slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot);
- slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
- slot->hotplug_slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
- slot->hotplug_slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
-
- make_slot_name(slot);
-
- retval = pci_hp_register(slot->hotplug_slot);
- if (retval) {
- err("pci_hp_register failed with error %d\n", retval);
- goto error_name;
- }
-
- /* add slot to our internal list */
- list_add(&slot->slot_list, &slot_list);
- info("Slot [%s] registered\n", slot->hotplug_slot->name);
- }
+
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot)
+ goto error;
+
+ slot->hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL);
+ if (!slot->hotplug_slot)
+ goto error_slot;
+
+ slot->hotplug_slot->info = kzalloc(sizeof(*hotplug_slot_info),
+ GFP_KERNEL);
+ if (!slot->hotplug_slot->info)
+ goto error_hpslot;
+
+ slot->hotplug_slot->name = kzalloc(SLOT_NAME_SIZE, GFP_KERNEL);
+ if (!slot->hotplug_slot->name)
+ goto error_info;
+
+ slot->hotplug_slot->private = slot;
+ slot->hotplug_slot->release = &release_slot;
+ slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
+
+ slot->acpi_slot = acpiphp_slot;
+ slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot);
+ slot->hotplug_slot->info->attention_status = 0;
+ slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot);
+ slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
+ slot->hotplug_slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
+ slot->hotplug_slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
+
+ acpiphp_slot->slot = slot;
+ make_slot_name(slot);
+
+ retval = pci_hp_register(slot->hotplug_slot);
+ if (retval) {
+ err("pci_hp_register failed with error %d\n", retval);
+ goto error_name;
+ }
+
+ info("Slot [%s] registered\n", slot->hotplug_slot->name);
return 0;
error_name:
@@ -412,42 +401,51 @@ error:
}
-static void __exit cleanup_slots (void)
+void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
{
- struct list_head *tmp, *n;
- struct slot *slot;
+ struct slot *slot = acpiphp_slot->slot;
+ int retval = 0;
- list_for_each_safe (tmp, n, &slot_list) {
- /* memory will be freed in release_slot callback */
- slot = list_entry(tmp, struct slot, slot_list);
- list_del(&slot->slot_list);
- pci_hp_deregister(slot->hotplug_slot);
- }
+ info ("Slot [%s] unregistered\n", slot->hotplug_slot->name);
+
+ retval = pci_hp_deregister(slot->hotplug_slot);
+ if (retval)
+ err("pci_hp_deregister failed with error %d\n", retval);
}
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 (retval)
- return retval;
- return init_slots();
+ /* 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;
}
static void __exit acpiphp_exit(void)
{
- cleanup_slots();
/* deallocate internal data structures etc. */
acpiphp_glue_exit();
+
+ remove_dock_station();
}
module_init(acpiphp_init);
diff --git a/drivers/pci/hotplug/acpiphp_dock.c b/drivers/pci/hotplug/acpiphp_dock.c
new file mode 100644
index 00000000000..4f1aaf12831
--- /dev/null
+++ b/drivers/pci/hotplug/acpiphp_dock.c
@@ -0,0 +1,438 @@
+/*
+ * 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),