acpiphp_dock.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  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. #include <linux/kernel.h>
  30. #include <linux/pci.h>
  31. #include <linux/smp_lock.h>
  32. #include <linux/mutex.h>
  33. #include "../pci.h"
  34. #include "pci_hotplug.h"
  35. #include "acpiphp.h"
  36. static struct acpiphp_dock_station *ds;
  37. #define MY_NAME "acpiphp_dock"
  38. int is_dependent_device(acpi_handle handle)
  39. {
  40. return (get_dependent_device(handle) ? 1 : 0);
  41. }
  42. static acpi_status
  43. find_dependent_device(acpi_handle handle, u32 lvl, void *context, void **rv)
  44. {
  45. int *count = (int *)context;
  46. if (is_dependent_device(handle)) {
  47. (*count)++;
  48. return AE_CTRL_TERMINATE;
  49. } else {
  50. return AE_OK;
  51. }
  52. }
  53. void add_dependent_device(struct dependent_device *new_dd)
  54. {
  55. list_add_tail(&new_dd->device_list, &ds->dependent_devices);
  56. }
  57. void add_pci_dependent_device(struct dependent_device *new_dd)
  58. {
  59. list_add_tail(&new_dd->pci_list, &ds->pci_dependent_devices);
  60. }
  61. struct dependent_device * get_dependent_device(acpi_handle handle)
  62. {
  63. struct dependent_device *dd;
  64. if (!ds)
  65. return NULL;
  66. list_for_each_entry(dd, &ds->dependent_devices, device_list) {
  67. if (handle == dd->handle)
  68. return dd;
  69. }
  70. return NULL;
  71. }
  72. struct dependent_device *alloc_dependent_device(acpi_handle handle)
  73. {
  74. struct dependent_device *dd;
  75. dd = kzalloc(sizeof(*dd), GFP_KERNEL);
  76. if (dd) {
  77. INIT_LIST_HEAD(&dd->pci_list);
  78. INIT_LIST_HEAD(&dd->device_list);
  79. dd->handle = handle;
  80. }
  81. return dd;
  82. }
  83. static int is_dock(acpi_handle handle)
  84. {
  85. acpi_status status;
  86. acpi_handle tmp;
  87. status = acpi_get_handle(handle, "_DCK", &tmp);
  88. if (ACPI_FAILURE(status)) {
  89. return 0;
  90. }
  91. return 1;
  92. }
  93. static int dock_present(void)
  94. {
  95. unsigned long sta;
  96. acpi_status status;
  97. if (ds) {
  98. status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
  99. if (ACPI_SUCCESS(status) && sta)
  100. return 1;
  101. }
  102. return 0;
  103. }
  104. static void eject_dock(void)
  105. {
  106. struct acpi_object_list arg_list;
  107. union acpi_object arg;
  108. arg_list.count = 1;
  109. arg_list.pointer = &arg;
  110. arg.type = ACPI_TYPE_INTEGER;
  111. arg.integer.value = 1;
  112. if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
  113. &arg_list, NULL)) || dock_present())
  114. warn("%s: failed to eject dock!\n", __FUNCTION__);
  115. return;
  116. }
  117. static acpi_status handle_dock(int dock)
  118. {
  119. acpi_status status;
  120. struct acpi_object_list arg_list;
  121. union acpi_object arg;
  122. struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
  123. dbg("%s: %s\n", __FUNCTION__, dock ? "docking" : "undocking");
  124. /* _DCK method has one argument */
  125. arg_list.count = 1;
  126. arg_list.pointer = &arg;
  127. arg.type = ACPI_TYPE_INTEGER;
  128. arg.integer.value = dock;
  129. status = acpi_evaluate_object(ds->handle, "_DCK",
  130. &arg_list, &buffer);
  131. if (ACPI_FAILURE(status))
  132. err("%s: failed to execute _DCK\n", __FUNCTION__);
  133. acpi_os_free(buffer.pointer);
  134. return status;
  135. }
  136. static inline void dock(void)
  137. {
  138. handle_dock(1);
  139. }
  140. static inline void undock(void)
  141. {
  142. handle_dock(0);
  143. }
  144. /*
  145. * the _DCK method can do funny things... and sometimes not
  146. * hah-hah funny.
  147. *
  148. * TBD - figure out a way to only call fixups for
  149. * systems that require them.
  150. */
  151. static void post_dock_fixups(void)
  152. {
  153. struct pci_bus *bus;
  154. u32 buses;
  155. struct dependent_device *dd;
  156. list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list) {
  157. bus = dd->func->slot->bridge->pci_bus;
  158. /* fixup bad _DCK function that rewrites
  159. * secondary bridge on slot
  160. */
  161. pci_read_config_dword(bus->self,
  162. PCI_PRIMARY_BUS,
  163. &buses);
  164. if (((buses >> 8) & 0xff) != bus->secondary) {
  165. buses = (buses & 0xff000000)
  166. | ((unsigned int)(bus->primary) << 0)
  167. | ((unsigned int)(bus->secondary) << 8)
  168. | ((unsigned int)(bus->subordinate) << 16);
  169. pci_write_config_dword(bus->self,
  170. PCI_PRIMARY_BUS,
  171. buses);
  172. }
  173. }
  174. }
  175. static void hotplug_pci(u32 type)
  176. {
  177. struct dependent_device *dd;
  178. list_for_each_entry(dd, &ds->pci_dependent_devices, pci_list)
  179. handle_hotplug_event_func(dd->handle, type, dd->func);
  180. }
  181. static inline void begin_dock(void)
  182. {
  183. ds->flags |= DOCK_DOCKING;
  184. }
  185. static inline void complete_dock(void)
  186. {
  187. ds->flags &= ~(DOCK_DOCKING);
  188. ds->last_dock_time = jiffies;
  189. }
  190. static int dock_in_progress(void)
  191. {
  192. if (ds->flags & DOCK_DOCKING ||
  193. ds->last_dock_time == jiffies) {
  194. dbg("dock in progress\n");
  195. return 1;
  196. }
  197. return 0;
  198. }
  199. static void
  200. handle_hotplug_event_dock(acpi_handle handle, u32 type, void *context)
  201. {
  202. dbg("%s: enter\n", __FUNCTION__);
  203. switch (type) {
  204. case ACPI_NOTIFY_BUS_CHECK:
  205. dbg("BUS Check\n");
  206. if (!dock_in_progress() && dock_present()) {
  207. begin_dock();
  208. dock();
  209. if (!dock_present()) {
  210. err("Unable to dock!\n");
  211. break;
  212. }
  213. post_dock_fixups();
  214. hotplug_pci(type);
  215. complete_dock();
  216. }
  217. break;
  218. case ACPI_NOTIFY_EJECT_REQUEST:
  219. dbg("EJECT request\n");
  220. if (!dock_in_progress() && dock_present()) {
  221. hotplug_pci(type);
  222. undock();
  223. eject_dock();
  224. if (dock_present())
  225. err("Unable to undock!\n");
  226. }
  227. break;
  228. }
  229. }
  230. static acpi_status
  231. find_dock_ejd(acpi_handle handle, u32 lvl, void *context, void **rv)
  232. {
  233. acpi_status status;
  234. acpi_handle tmp;
  235. acpi_handle dck_handle = (acpi_handle) context;
  236. char objname[64];
  237. struct acpi_buffer buffer = { .length = sizeof(objname),
  238. .pointer = objname };
  239. struct acpi_buffer ejd_buffer = {ACPI_ALLOCATE_BUFFER, NULL};
  240. union acpi_object *ejd_obj;
  241. status = acpi_get_handle(handle, "_EJD", &tmp);
  242. if (ACPI_FAILURE(status))
  243. return AE_OK;
  244. /* make sure we are dependent on the dock device,
  245. * by executing the _EJD method, then getting a handle
  246. * to the device referenced by that name. If that
  247. * device handle is the same handle as the dock station
  248. * handle, then we are a device dependent on the dock station
  249. */
  250. acpi_get_name(dck_handle, ACPI_FULL_PATHNAME, &buffer);
  251. status = acpi_evaluate_object(handle, "_EJD", NULL, &ejd_buffer);
  252. if (ACPI_FAILURE(status)) {
  253. err("Unable to execute _EJD!\n");
  254. goto find_ejd_out;
  255. }
  256. ejd_obj = ejd_buffer.pointer;
  257. status = acpi_get_handle(NULL, ejd_obj->string.pointer, &tmp);
  258. if (ACPI_FAILURE(status))
  259. goto find_ejd_out;
  260. if (tmp == dck_handle) {
  261. struct dependent_device *dd;
  262. dbg("%s: found device dependent on dock\n", __FUNCTION__);
  263. dd = alloc_dependent_device(handle);
  264. if (!dd) {
  265. err("Can't allocate memory for dependent device!\n");
  266. goto find_ejd_out;
  267. }
  268. add_dependent_device(dd);
  269. }
  270. find_ejd_out:
  271. acpi_os_free(ejd_buffer.pointer);
  272. return AE_OK;
  273. }
  274. int detect_dependent_devices(acpi_handle *bridge_handle)
  275. {
  276. acpi_status status;
  277. int count;
  278. count = 0;
  279. status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle,
  280. (u32)1, find_dependent_device,
  281. (void *)&count, NULL);
  282. return count;
  283. }
  284. static acpi_status
  285. find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
  286. {
  287. int *count = (int *)context;
  288. if (is_dock(handle)) {
  289. dbg("%s: found dock\n", __FUNCTION__);
  290. ds = kzalloc(sizeof(*ds), GFP_KERNEL);
  291. ds->handle = handle;
  292. INIT_LIST_HEAD(&ds->dependent_devices);
  293. INIT_LIST_HEAD(&ds->pci_dependent_devices);
  294. /* look for devices dependent on dock station */
  295. acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
  296. ACPI_UINT32_MAX, find_dock_ejd, handle, NULL);
  297. acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
  298. handle_hotplug_event_dock, ds);
  299. (*count)++;
  300. }
  301. return AE_OK;
  302. }
  303. int find_dock_station(void)
  304. {
  305. int num = 0;
  306. ds = NULL;
  307. /* start from the root object, because some laptops define
  308. * _DCK methods outside the scope of PCI (IBM x-series laptop)
  309. */
  310. acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
  311. ACPI_UINT32_MAX, find_dock, &num, NULL);
  312. return num;
  313. }
  314. void remove_dock_station(void)
  315. {
  316. struct dependent_device *dd, *tmp;
  317. if (ds) {
  318. if (ACPI_FAILURE(acpi_remove_notify_handler(ds->handle,
  319. ACPI_SYSTEM_NOTIFY, handle_hotplug_event_dock)))
  320. err("failed to remove dock notify handler\n");
  321. /* free all dependent devices */
  322. list_for_each_entry_safe(dd, tmp, &ds->dependent_devices,
  323. device_list)
  324. kfree(dd);
  325. /* no need to touch the pci_dependent_device list,
  326. * cause all memory was freed above
  327. */
  328. kfree(ds);
  329. }
  330. }