This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / pci / hotplug / acpiphp_dock.c
1 /*
2  * ACPI PCI HotPlug dock functions to ACPI CA subsystem
3  *
4  * Copyright (C) 2006 Kristen Carlson Accardi (kristen.c.accardi@intel.com)
5  * Copyright (C) 2006 Intel Corporation
6  *
7  * All rights reserved.
8  *
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.
13  *
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
18  * details.
19  *
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.
23  *
24  * Send feedback to <kristen.c.accardi@intel.com>
25  *
26  */
27 #include <linux/init.h>
28 #include <linux/module.h>
29
30 #include <linux/kernel.h>
31 #include <linux/pci.h>
32 #include <linux/smp_lock.h>
33 #include <linux/mutex.h>
34
35 #include "../pci.h"
36 #include "pci_hotplug.h"
37 #include "acpiphp.h"
38
39 static struct acpiphp_dock_station *ds;
40 #define MY_NAME "acpiphp_dock"
41
42
43 int is_dependent_device(acpi_handle handle)
44 {
45         return (get_dependent_device(handle) ? 1 : 0);
46 }
47
48
49 static acpi_status
50 find_dependent_device(acpi_handle handle, u32 lvl, void *context, void **rv)
51 {
52         int *count = (int *)context;
53
54         if (is_dependent_device(handle)) {
55                 (*count)++;
56                 return AE_CTRL_TERMINATE;
57         } else {
58                 return AE_OK;
59         }
60 }
61
62
63
64
65 void add_dependent_device(struct dependent_device *new_dd)
66 {
67         list_add_tail(&new_dd->device_list, &ds->dependent_devices);
68 }
69
70
71 void add_pci_dependent_device(struct dependent_device *new_dd)
72 {
73         list_add_tail(&new_dd->pci_list, &ds->pci_dependent_devices);
74 }
75
76
77
78 struct dependent_device * get_dependent_device(acpi_handle handle)
79 {
80         struct dependent_device *dd;
81
82         if (!ds)
83                 return NULL;
84
85         list_for_each_entry(dd, &ds->dependent_devices, device_list) {
86                 if (handle == dd->handle)
87                         return dd;
88         }
89         return NULL;
90 }
91
92
93
94 struct dependent_device *alloc_dependent_device(acpi_handle handle)
95 {
96         struct dependent_device *dd;
97
98         dd = kzalloc(sizeof(*dd), GFP_KERNEL);
99         if (dd) {
100                 INIT_LIST_HEAD(&dd->pci_list);
101                 INIT_LIST_HEAD(&dd->device_list);
102                 dd->handle = handle;
103         }
104         return dd;
105 }
106
107
108
109 static int is_dock(acpi_handle handle)
110 {
111         acpi_status status;
112         acpi_handle tmp;
113
114         status = acpi_get_handle(handle, "_DCK", &tmp);
115         if (ACPI_FAILURE(status)) {
116                 return 0;
117         }
118         return 1;
119 }
120
121
122
123 static int dock_present(void)
124 {
125         unsigned long sta;
126         acpi_status status;
127
128         if (ds) {
129                 status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
130                 if (ACPI_SUCCESS(status) && sta)
131                         return 1;
132         }
133         return 0;
134 }
135
136
137
138 static void eject_dock(void)
139 {
140         struct acpi_object_list arg_list;
141         union acpi_object arg;
142
143         arg_list.count = 1;
144         arg_list.pointer = &arg;
145         arg.type = ACPI_TYPE_INTEGER;
146         arg.integer.value = 1;
147
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__);
151
152         return;
153 }
154
155
156
157
158 static acpi_status handle_dock(int dock)
159 {
160         acpi_status status;
161         struct acpi_object_list arg_list;
162         union acpi_object arg;
163         struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
164
165         dbg("%s: %s\n", __FUNCTION__, dock ? "docking" : "undocking");
166
167         /* _DCK method has one argument */
168         arg_list.count = 1;
169         arg_list.pointer = &arg;
170         arg.type = ACPI_TYPE_INTEGER;
171         arg.integer.value = dock;
172         status = acpi_evaluate_object(ds->handle, "_DCK",
173                                         &arg_list, &buffer);
174         if (ACPI_FAILURE(status))
175                 err("%s: failed to execute _DCK\n", __FUNCTION__);
176         acpi_os_free(buffer.pointer);
177
178         return status;
179 }
180
181
182
183 static inline void dock(void)
184 {
185         handle_dock(1);
186 }
187
188
189
190 static inline void undock(void)
191 {
192         handle_dock(0);
193 }
194
195
196
197 /*
198  * the _DCK method can do funny things... and sometimes not
199  * hah-hah funny.
200  *
201  * TBD - figure out a way to only call fixups for
202  * systems that require them.
203  */
204 static void post_dock_fixups(void)
205 {
206         struct pci_bus *bus;
207         u32 buses;
208         struct dependent_device *dd;
209
210         list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list) {
211                 bus = dd->func->slot->bridge->pci_bus;
212
213                 /* fixup bad _DCK function that rewrites
214                  * secondary bridge on slot
215                  */
216                 pci_read_config_dword(bus->self,
217                                 PCI_PRIMARY_BUS,
218                                 &buses);
219
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,
226                                         PCI_PRIMARY_BUS,
227                                         buses);
228                 }
229         }
230 }
231
232
233
234 static void hotplug_pci(u32 type)
235 {
236         struct dependent_device *dd;
237
238         list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list)
239                 handle_hotplug_event_func(dd->handle, type, dd->func);
240 }
241
242
243
244 static inline void begin_dock(void)
245 {
246         ds->flags |= DOCK_DOCKING;
247 }
248
249
250 static inline void complete_dock(void)
251 {
252         ds->flags &= ~(DOCK_DOCKING);
253         ds->last_dock_time = jiffies;
254 }
255
256
257 static int dock_in_progress(void)
258 {
259         if (ds->flags & DOCK_DOCKING ||
260                 ds->last_dock_time == jiffies) {
261                 dbg("dock in progress\n");
262                 return 1;
263         }
264         return 0;
265 }
266
267
268
269 static void
270 handle_hotplug_event_dock(acpi_handle handle, u32 type, void *context)
271 {
272         dbg("%s: enter\n", __FUNCTION__);
273
274         switch (type) {
275                 case ACPI_NOTIFY_BUS_CHECK:
276                         dbg("BUS Check\n");
277                         if (!dock_in_progress() && dock_present()) {
278                                 begin_dock();
279                                 dock();
280                                 if (!dock_present()) {
281                                         err("Unable to dock!\n");
282                                         break;
283                                 }
284                                 post_dock_fixups();
285                                 hotplug_pci(type);
286                                 complete_dock();
287                         }
288                         break;
289                 case ACPI_NOTIFY_EJECT_REQUEST:
290                         dbg("EJECT request\n");
291                         if (!dock_in_progress() && dock_present()) {
292                                 hotplug_pci(type);
293                                 undock();
294                                 eject_dock();
295                                 if (dock_present())
296                                         err("Unable to undock!\n");
297                         }
298                         break;
299         }
300 }
301
302
303
304
305 static acpi_status
306 find_dock_ejd(acpi_handle handle, u32 lvl, void *context, void **rv)
307 {
308         acpi_status status;
309         acpi_handle tmp;
310         acpi_handle dck_handle = (acpi_handle) context;
311         char objname[64];
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;
316
317         status = acpi_get_handle(handle, "_EJD", &tmp);
318         if (ACPI_FAILURE(status))
319                 return AE_OK;
320
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
326          */
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");
331                 goto find_ejd_out;
332         }
333         ejd_obj = ejd_buffer.pointer;
334         status = acpi_get_handle(NULL, ejd_obj->string.pointer, &tmp);
335         if (ACPI_FAILURE(status))
336                 goto find_ejd_out;
337
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);
342                 if (!dd) {
343                         err("Can't allocate memory for dependent device!\n");
344                         goto find_ejd_out;
345                 }
346                 add_dependent_device(dd);
347         }
348
349 find_ejd_out:
350         acpi_os_free(ejd_buffer.pointer);
351         return AE_OK;
352 }
353
354
355
356 int detect_dependent_devices(acpi_handle *bridge_handle)
357 {
358         acpi_status status;
359         int count;
360
361         count = 0;
362
363         status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle,
364                                         (u32)1, find_dependent_device,
365                                         (void *)&count, NULL);
366
367         return count;
368 }
369
370
371
372
373
374 static acpi_status
375 find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
376 {
377         int *count = (int *)context;
378
379         if (is_dock(handle)) {
380                 dbg("%s: found dock\n", __FUNCTION__);
381                 ds = kzalloc(sizeof(*ds), GFP_KERNEL);
382                 ds->handle = handle;
383                 INIT_LIST_HEAD(&ds->dependent_devices);
384                 INIT_LIST_HEAD(&ds->pci_dependent_devices);
385
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);
389
390                 acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
391                         handle_hotplug_event_dock, ds);
392                 (*count)++;
393         }
394
395         return AE_OK;
396 }
397
398
399
400
401 int find_dock_station(void)
402 {
403         int num = 0;
404
405         ds = NULL;
406
407         /* start from the root object, because some laptops define
408          * _DCK methods outside the scope of PCI (IBM x-series laptop)
409          */
410         acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
411                         ACPI_UINT32_MAX, find_dock, &num, NULL);
412
413         return num;
414 }
415
416
417
418 void remove_dock_station(void)
419 {
420         struct dependent_device *dd, *tmp;
421         if (ds) {
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");
425
426                 /* free all dependent devices */
427                 list_for_each_entry_safe(dd, tmp, &ds->dependent_devices,
428                                 device_list)
429                         kfree(dd);
430
431                 /* no need to touch the pci_dependent_device list,
432                  * cause all memory was freed above
433                  */
434                 kfree(ds);
435         }
436 }
437
438