Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
acpiphp_ibm.c
Go to the documentation of this file.
1 /*
2  * ACPI PCI Hot Plug IBM Extension
3  *
4  * Copyright (C) 2004 Vernon Mauery <[email protected]>
5  * Copyright (C) 2004 IBM Corp.
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 <[email protected]>
25  *
26  */
27 
28 #include <linux/init.h>
29 #include <linux/slab.h>
30 #include <linux/module.h>
31 #include <linux/kernel.h>
32 #include <acpi/acpi_bus.h>
33 #include <linux/sysfs.h>
34 #include <linux/kobject.h>
35 #include <asm/uaccess.h>
36 #include <linux/moduleparam.h>
37 #include <linux/pci.h>
38 
39 #include "acpiphp.h"
40 #include "../pci.h"
41 
42 #define DRIVER_VERSION "1.0.1"
43 #define DRIVER_AUTHOR "Irene Zubarev <[email protected]>, Vernon Mauery <[email protected]>"
44 #define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver IBM extension"
45 
46 static bool debug;
47 
50 MODULE_LICENSE("GPL");
52 module_param(debug, bool, 0644);
53 MODULE_PARM_DESC(debug, " Debugging mode enabled or not");
54 #define MY_NAME "acpiphp_ibm"
55 
56 #undef dbg
57 #define dbg(format, arg...) \
58 do { \
59  if (debug) \
60  printk(KERN_DEBUG "%s: " format, \
61  MY_NAME , ## arg); \
62 } while (0)
63 
64 #define FOUND_APCI 0x61504349
65 /* these are the names for the IBM ACPI pseudo-device */
66 #define IBM_HARDWARE_ID1 "IBM37D0"
67 #define IBM_HARDWARE_ID2 "IBM37D4"
68 
69 #define hpslot_to_sun(A) (((struct slot *)((A)->private))->acpi_slot->sun)
70 
71 /* union apci_descriptor - allows access to the
72  * various device descriptors that are embedded in the
73  * aPCI table
74  */
76  struct {
77  char sig[4];
79  } header;
80  struct {
82  u8 len;
89  u8 status[2];
91  u8 res[3];
92  } slot;
93  struct {
94  u8 type;
95  u8 len;
96  } generic;
97 };
98 
99 /* struct notification - keeps info about the device
100  * that cause the ACPI notification event
101  */
102 struct notification {
103  struct acpi_device *device;
105 };
106 
107 static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status);
108 static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status);
109 static void ibm_handle_events(acpi_handle handle, u32 event, void *context);
110 static int ibm_get_table_from_acpi(char **bufp);
111 static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj,
112  struct bin_attribute *bin_attr,
113  char *buffer, loff_t pos, size_t size);
114 static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
115  u32 lvl, void *context, void **rv);
116 static int __init ibm_acpiphp_init(void);
117 static void __exit ibm_acpiphp_exit(void);
118 
119 static acpi_handle ibm_acpi_handle;
120 static struct notification ibm_note;
121 static struct bin_attribute ibm_apci_table_attr = {
122  .attr = {
123  .name = "apci_table",
124  .mode = S_IRUGO,
125  },
126  .read = ibm_read_apci_table,
127  .write = NULL,
128 };
129 static struct acpiphp_attention_info ibm_attention_info =
130 {
131  .set_attn = ibm_set_attention_status,
132  .get_attn = ibm_get_attention_status,
133  .owner = THIS_MODULE,
134 };
135 
145 static union apci_descriptor *ibm_slot_from_id(int id)
146 {
147  int ind = 0, size;
148  union apci_descriptor *ret = NULL, *des;
149  char *table;
150 
151  size = ibm_get_table_from_acpi(&table);
152  des = (union apci_descriptor *)table;
153  if (memcmp(des->header.sig, "aPCI", 4) != 0)
154  goto ibm_slot_done;
155 
156  des = (union apci_descriptor *)&table[ind += des->header.len];
157  while (ind < size && (des->generic.type != 0x82 ||
158  des->slot.slot_num != id)) {
159  des = (union apci_descriptor *)&table[ind += des->generic.len];
160  }
161 
162  if (ind < size && des->slot.slot_num == id)
163  ret = des;
164 
165 ibm_slot_done:
166  if (ret) {
167  ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL);
168  memcpy(ret, des, sizeof(union apci_descriptor));
169  }
170  kfree(table);
171  return ret;
172 }
173 
182 static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
183 {
184  union acpi_object args[2];
185  struct acpi_object_list params = { .pointer = args, .count = 2 };
186  acpi_status stat;
187  unsigned long long rc;
188  union apci_descriptor *ibm_slot;
189 
190  ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
191 
192  dbg("%s: set slot %d (%d) attention status to %d\n", __func__,
193  ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
194  (status ? 1 : 0));
195 
196  args[0].type = ACPI_TYPE_INTEGER;
197  args[0].integer.value = ibm_slot->slot.slot_id;
198  args[1].type = ACPI_TYPE_INTEGER;
199  args[1].integer.value = (status) ? 1 : 0;
200 
201  kfree(ibm_slot);
202 
203  stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", &params, &rc);
204  if (ACPI_FAILURE(stat)) {
205  err("APLS evaluation failed: 0x%08x\n", stat);
206  return -ENODEV;
207  } else if (!rc) {
208  err("APLS method failed: 0x%08llx\n", rc);
209  return -ERANGE;
210  }
211  return 0;
212 }
213 
226 static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status)
227 {
228  union apci_descriptor *ibm_slot;
229 
230  ibm_slot = ibm_slot_from_id(hpslot_to_sun(slot));
231 
232  if (ibm_slot->slot.attn & 0xa0 || ibm_slot->slot.status[1] & 0x08)
233  *status = 1;
234  else
235  *status = 0;
236 
237  dbg("%s: get slot %d (%d) attention status is %d\n", __func__,
238  ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
239  *status);
240 
241  kfree(ibm_slot);
242  return 0;
243 }
244 
263 static void ibm_handle_events(acpi_handle handle, u32 event, void *context)
264 {
265  u8 detail = event & 0x0f;
266  u8 subevent = event & 0xf0;
267  struct notification *note = context;
268 
269  dbg("%s: Received notification %02x\n", __func__, event);
270 
271  if (subevent == 0x80) {
272  dbg("%s: generationg bus event\n", __func__);
273  acpi_bus_generate_proc_event(note->device, note->event, detail);
274  acpi_bus_generate_netlink_event(note->device->pnp.device_class,
275  dev_name(&note->device->dev),
276  note->event, detail);
277  } else
278  note->event = event;
279 }
280 
295 static int ibm_get_table_from_acpi(char **bufp)
296 {
297  union acpi_object *package;
300  char *lbuf = NULL;
301  int i, size = -EIO;
302 
303  status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer);
304  if (ACPI_FAILURE(status)) {
305  err("%s: APCI evaluation failed\n", __func__);
306  return -ENODEV;
307  }
308 
309  package = (union acpi_object *) buffer.pointer;
310  if (!(package) ||
311  (package->type != ACPI_TYPE_PACKAGE) ||
312  !(package->package.elements)) {
313  err("%s: Invalid APCI object\n", __func__);
314  goto read_table_done;
315  }
316 
317  for(size = 0, i = 0; i < package->package.count; i++) {
318  if (package->package.elements[i].type != ACPI_TYPE_BUFFER) {
319  err("%s: Invalid APCI element %d\n", __func__, i);
320  goto read_table_done;
321  }
322  size += package->package.elements[i].buffer.length;
323  }
324 
325  if (bufp == NULL)
326  goto read_table_done;
327 
328  lbuf = kzalloc(size, GFP_KERNEL);
329  dbg("%s: element count: %i, ASL table size: %i, &table = 0x%p\n",
330  __func__, package->package.count, size, lbuf);
331 
332  if (lbuf) {
333  *bufp = lbuf;
334  } else {
335  size = -ENOMEM;
336  goto read_table_done;
337  }
338 
339  size = 0;
340  for (i=0; i<package->package.count; i++) {
341  memcpy(&lbuf[size],
342  package->package.elements[i].buffer.pointer,
343  package->package.elements[i].buffer.length);
344  size += package->package.elements[i].buffer.length;
345  }
346 
347 read_table_done:
348  kfree(buffer.pointer);
349  return size;
350 }
351 
368 static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj,
369  struct bin_attribute *bin_attr,
370  char *buffer, loff_t pos, size_t size)
371 {
372  int bytes_read = -EINVAL;
373  char *table = NULL;
374 
375  dbg("%s: pos = %d, size = %zd\n", __func__, (int)pos, size);
376 
377  if (pos == 0) {
378  bytes_read = ibm_get_table_from_acpi(&table);
379  if (bytes_read > 0 && bytes_read <= size)
380  memcpy(buffer, table, bytes_read);
381  kfree(table);
382  }
383  return bytes_read;
384 }
385 
397 static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
398  u32 lvl, void *context, void **rv)
399 {
400  acpi_handle *phandle = (acpi_handle *)context;
402  struct acpi_device_info *info;
403  int retval = 0;
404 
405  status = acpi_get_object_info(handle, &info);
406  if (ACPI_FAILURE(status)) {
407  err("%s: Failed to get device information status=0x%x\n",
408  __func__, status);
409  return retval;
410  }
411 
412  if (info->current_status && (info->valid & ACPI_VALID_HID) &&
413  (!strcmp(info->hardware_id.string, IBM_HARDWARE_ID1) ||
414  !strcmp(info->hardware_id.string, IBM_HARDWARE_ID2))) {
415  dbg("found hardware: %s, handle: %p\n",
416  info->hardware_id.string, handle);
417  *phandle = handle;
418  /* returning non-zero causes the search to stop
419  * and returns this value to the caller of
420  * acpi_walk_namespace, but it also causes some warnings
421  * in the acpi debug code to print...
422  */
423  retval = FOUND_APCI;
424  }
425  kfree(info);
426  return retval;
427 }
428 
429 static int __init ibm_acpiphp_init(void)
430 {
431  int retval = 0;
433  struct acpi_device *device;
434  struct kobject *sysdir = &pci_slots_kset->kobj;
435 
436  dbg("%s\n", __func__);
437 
439  ACPI_UINT32_MAX, ibm_find_acpi_device, NULL,
440  &ibm_acpi_handle, NULL) != FOUND_APCI) {
441  err("%s: acpi_walk_namespace failed\n", __func__);
442  retval = -ENODEV;
443  goto init_return;
444  }
445  dbg("%s: found IBM aPCI device\n", __func__);
446  if (acpi_bus_get_device(ibm_acpi_handle, &device)) {
447  err("%s: acpi_bus_get_device failed\n", __func__);
448  retval = -ENODEV;
449  goto init_return;
450  }
451  if (acpiphp_register_attention(&ibm_attention_info)) {
452  retval = -ENODEV;
453  goto init_return;
454  }
455 
456  ibm_note.device = device;
457  status = acpi_install_notify_handler(ibm_acpi_handle,
458  ACPI_DEVICE_NOTIFY, ibm_handle_events,
459  &ibm_note);
460  if (ACPI_FAILURE(status)) {
461  err("%s: Failed to register notification handler\n",
462  __func__);
463  retval = -EBUSY;
464  goto init_cleanup;
465  }
466 
467  ibm_apci_table_attr.size = ibm_get_table_from_acpi(NULL);
468  retval = sysfs_create_bin_file(sysdir, &ibm_apci_table_attr);
469 
470  return retval;
471 
472 init_cleanup:
473  acpiphp_unregister_attention(&ibm_attention_info);
474 init_return:
475  return retval;
476 }
477 
478 static void __exit ibm_acpiphp_exit(void)
479 {
481  struct kobject *sysdir = &pci_slots_kset->kobj;
482 
483  dbg("%s\n", __func__);
484 
485  if (acpiphp_unregister_attention(&ibm_attention_info))
486  err("%s: attention info deregistration failed", __func__);
487 
489  ibm_acpi_handle,
491  ibm_handle_events);
492  if (ACPI_FAILURE(status))
493  err("%s: Notification handler removal failed\n", __func__);
494  /* remove the /sys entries */
495  sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
496 }
497 
498 module_init(ibm_acpiphp_init);
499 module_exit(ibm_acpiphp_exit);