2 * ACPI PCI HotPlug dock functions to ACPI CA subsystem
4 * Copyright (C) 2006 Kristen Carlson Accardi (kristen.c.accardi@intel.com)
5 * Copyright (C) 2006 Intel Corporation
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or (at
12 * your option) any later version.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
17 * NON INFRINGEMENT. See the GNU General Public License for more
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 * Send feedback to <kristen.c.accardi@intel.com>
27 #include <linux/init.h>
28 #include <linux/module.h>
30 #include <linux/kernel.h>
31 #include <linux/pci.h>
32 #include <linux/smp_lock.h>
33 #include <linux/mutex.h>
36 #include "pci_hotplug.h"
39 static struct acpiphp_dock_station *ds;
40 #define MY_NAME "acpiphp_dock"
43 int is_dependent_device(acpi_handle handle)
45 return (get_dependent_device(handle) ? 1 : 0);
50 find_dependent_device(acpi_handle handle, u32 lvl, void *context, void **rv)
52 int *count = (int *)context;
54 if (is_dependent_device(handle)) {
56 return AE_CTRL_TERMINATE;
65 void add_dependent_device(struct dependent_device *new_dd)
67 list_add_tail(&new_dd->device_list, &ds->dependent_devices);
71 void add_pci_dependent_device(struct dependent_device *new_dd)
73 list_add_tail(&new_dd->pci_list, &ds->pci_dependent_devices);
78 struct dependent_device * get_dependent_device(acpi_handle handle)
80 struct dependent_device *dd;
85 list_for_each_entry(dd, &ds->dependent_devices, device_list) {
86 if (handle == dd->handle)
94 struct dependent_device *alloc_dependent_device(acpi_handle handle)
96 struct dependent_device *dd;
98 dd = kzalloc(sizeof(*dd), GFP_KERNEL);
100 INIT_LIST_HEAD(&dd->pci_list);
101 INIT_LIST_HEAD(&dd->device_list);
109 static int is_dock(acpi_handle handle)
114 status = acpi_get_handle(handle, "_DCK", &tmp);
115 if (ACPI_FAILURE(status)) {
123 static int dock_present(void)
129 status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
130 if (ACPI_SUCCESS(status) && sta)
138 static void eject_dock(void)
140 struct acpi_object_list arg_list;
141 union acpi_object arg;
144 arg_list.pointer = &arg;
145 arg.type = ACPI_TYPE_INTEGER;
146 arg.integer.value = 1;
148 if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
149 &arg_list, NULL)) || dock_present())
150 warn("%s: failed to eject dock!\n", __FUNCTION__);
158 static acpi_status handle_dock(int dock)
161 struct acpi_object_list arg_list;
162 union acpi_object arg;
163 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
165 dbg("%s: %s\n", __FUNCTION__, dock ? "docking" : "undocking");
167 /* _DCK method has one argument */
169 arg_list.pointer = &arg;
170 arg.type = ACPI_TYPE_INTEGER;
171 arg.integer.value = dock;
172 status = acpi_evaluate_object(ds->handle, "_DCK",
174 if (ACPI_FAILURE(status))
175 err("%s: failed to execute _DCK\n", __FUNCTION__);
176 acpi_os_free(buffer.pointer);
183 static inline void dock(void)
190 static inline void undock(void)
198 * the _DCK method can do funny things... and sometimes not
201 * TBD - figure out a way to only call fixups for
202 * systems that require them.
204 static void post_dock_fixups(void)
208 struct dependent_device *dd;
210 list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list) {
211 bus = dd->func->slot->bridge->pci_bus;
213 /* fixup bad _DCK function that rewrites
214 * secondary bridge on slot
216 pci_read_config_dword(bus->self,
220 if (((buses >> 8) & 0xff) != bus->secondary) {
221 buses = (buses & 0xff000000)
222 | ((unsigned int)(bus->primary) << 0)
223 | ((unsigned int)(bus->secondary) << 8)
224 | ((unsigned int)(bus->subordinate) << 16);
225 pci_write_config_dword(bus->self,
234 static void hotplug_pci(u32 type)
236 struct dependent_device *dd;
238 list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list)
239 handle_hotplug_event_func(dd->handle, type, dd->func);
244 static inline void begin_dock(void)
246 ds->flags |= DOCK_DOCKING;
250 static inline void complete_dock(void)
252 ds->flags &= ~(DOCK_DOCKING);
253 ds->last_dock_time = jiffies;
257 static int dock_in_progress(void)
259 if (ds->flags & DOCK_DOCKING ||
260 ds->last_dock_time == jiffies) {
261 dbg("dock in progress\n");
270 handle_hotplug_event_dock(acpi_handle handle, u32 type, void *context)
272 dbg("%s: enter\n", __FUNCTION__);
275 case ACPI_NOTIFY_BUS_CHECK:
277 if (!dock_in_progress() && dock_present()) {
280 if (!dock_present()) {
281 err("Unable to dock!\n");
289 case ACPI_NOTIFY_EJECT_REQUEST:
290 dbg("EJECT request\n");
291 if (!dock_in_progress() && dock_present()) {
296 err("Unable to undock!\n");
306 find_dock_ejd(acpi_handle handle, u32 lvl, void *context, void **rv)
310 acpi_handle dck_handle = (acpi_handle) context;
312 struct acpi_buffer buffer = { .length = sizeof(objname),
313 .pointer = objname };
314 struct acpi_buffer ejd_buffer = {ACPI_ALLOCATE_BUFFER, NULL};
315 union acpi_object *ejd_obj;
317 status = acpi_get_handle(handle, "_EJD", &tmp);
318 if (ACPI_FAILURE(status))
321 /* make sure we are dependent on the dock device,
322 * by executing the _EJD method, then getting a handle
323 * to the device referenced by that name. If that
324 * device handle is the same handle as the dock station
325 * handle, then we are a device dependent on the dock station
327 acpi_get_name(dck_handle, ACPI_FULL_PATHNAME, &buffer);
328 status = acpi_evaluate_object(handle, "_EJD", NULL, &ejd_buffer);
329 if (ACPI_FAILURE(status)) {
330 err("Unable to execute _EJD!\n");
333 ejd_obj = ejd_buffer.pointer;
334 status = acpi_get_handle(NULL, ejd_obj->string.pointer, &tmp);
335 if (ACPI_FAILURE(status))
338 if (tmp == dck_handle) {
339 struct dependent_device *dd;
340 dbg("%s: found device dependent on dock\n", __FUNCTION__);
341 dd = alloc_dependent_device(handle);
343 err("Can't allocate memory for dependent device!\n");
346 add_dependent_device(dd);
350 acpi_os_free(ejd_buffer.pointer);
356 int detect_dependent_devices(acpi_handle *bridge_handle)
363 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle,
364 (u32)1, find_dependent_device,
365 (void *)&count, NULL);
375 find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
377 int *count = (int *)context;
379 if (is_dock(handle)) {
380 dbg("%s: found dock\n", __FUNCTION__);
381 ds = kzalloc(sizeof(*ds), GFP_KERNEL);
383 INIT_LIST_HEAD(&ds->dependent_devices);
384 INIT_LIST_HEAD(&ds->pci_dependent_devices);
386 /* look for devices dependent on dock station */
387 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
388 ACPI_UINT32_MAX, find_dock_ejd, handle, NULL);
390 acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
391 handle_hotplug_event_dock, ds);
401 int find_dock_station(void)
407 /* start from the root object, because some laptops define
408 * _DCK methods outside the scope of PCI (IBM x-series laptop)
410 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
411 ACPI_UINT32_MAX, find_dock, &num, NULL);
418 void remove_dock_station(void)
420 struct dependent_device *dd, *tmp;
422 if (ACPI_FAILURE(acpi_remove_notify_handler(ds->handle,
423 ACPI_SYSTEM_NOTIFY, handle_hotplug_event_dock)))
424 err("failed to remove dock notify handler\n");
426 /* free all dependent devices */
427 list_for_each_entry_safe(dd, tmp, &ds->dependent_devices,
431 /* no need to touch the pci_dependent_device list,
432 * cause all memory was freed above