0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #define pr_fmt(fmt) "acpiphp_ibm: " fmt
0015
0016 #include <linux/init.h>
0017 #include <linux/slab.h>
0018 #include <linux/module.h>
0019 #include <linux/kernel.h>
0020 #include <linux/sysfs.h>
0021 #include <linux/kobject.h>
0022 #include <linux/moduleparam.h>
0023 #include <linux/pci.h>
0024 #include <linux/uaccess.h>
0025
0026 #include "acpiphp.h"
0027 #include "../pci.h"
0028
0029 #define DRIVER_VERSION "1.0.1"
0030 #define DRIVER_AUTHOR "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
0031 #define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver IBM extension"
0032
0033
0034 MODULE_AUTHOR(DRIVER_AUTHOR);
0035 MODULE_DESCRIPTION(DRIVER_DESC);
0036 MODULE_LICENSE("GPL");
0037 MODULE_VERSION(DRIVER_VERSION);
0038
0039 #define FOUND_APCI 0x61504349
0040
0041 #define IBM_HARDWARE_ID1 "IBM37D0"
0042 #define IBM_HARDWARE_ID2 "IBM37D4"
0043
0044 #define hpslot_to_sun(A) (to_slot(A)->sun)
0045
0046
0047
0048
0049
0050 union apci_descriptor {
0051 struct {
0052 char sig[4];
0053 u8 len;
0054 } header;
0055 struct {
0056 u8 type;
0057 u8 len;
0058 u16 slot_id;
0059 u8 bus_id;
0060 u8 dev_num;
0061 u8 slot_num;
0062 u8 slot_attr[2];
0063 u8 attn;
0064 u8 status[2];
0065 u8 sun;
0066 u8 res[3];
0067 } slot;
0068 struct {
0069 u8 type;
0070 u8 len;
0071 } generic;
0072 };
0073
0074
0075
0076
0077 struct notification {
0078 struct acpi_device *device;
0079 u8 event;
0080 };
0081
0082 static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status);
0083 static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status);
0084 static void ibm_handle_events(acpi_handle handle, u32 event, void *context);
0085 static int ibm_get_table_from_acpi(char **bufp);
0086 static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj,
0087 struct bin_attribute *bin_attr,
0088 char *buffer, loff_t pos, size_t size);
0089 static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
0090 u32 lvl, void *context, void **rv);
0091 static int __init ibm_acpiphp_init(void);
0092 static void __exit ibm_acpiphp_exit(void);
0093
0094 static acpi_handle ibm_acpi_handle;
0095 static struct notification ibm_note;
0096 static struct bin_attribute ibm_apci_table_attr __ro_after_init = {
0097 .attr = {
0098 .name = "apci_table",
0099 .mode = S_IRUGO,
0100 },
0101 .read = ibm_read_apci_table,
0102 .write = NULL,
0103 };
0104 static struct acpiphp_attention_info ibm_attention_info =
0105 {
0106 .set_attn = ibm_set_attention_status,
0107 .get_attn = ibm_get_attention_status,
0108 .owner = THIS_MODULE,
0109 };
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120 static union apci_descriptor *ibm_slot_from_id(int id)
0121 {
0122 int ind = 0, size;
0123 union apci_descriptor *ret = NULL, *des;
0124 char *table;
0125
0126 size = ibm_get_table_from_acpi(&table);
0127 if (size < 0)
0128 return NULL;
0129 des = (union apci_descriptor *)table;
0130 if (memcmp(des->header.sig, "aPCI", 4) != 0)
0131 goto ibm_slot_done;
0132
0133 des = (union apci_descriptor *)&table[ind += des->header.len];
0134 while (ind < size && (des->generic.type != 0x82 ||
0135 des->slot.slot_num != id)) {
0136 des = (union apci_descriptor *)&table[ind += des->generic.len];
0137 }
0138
0139 if (ind < size && des->slot.slot_num == id)
0140 ret = des;
0141
0142 ibm_slot_done:
0143 if (ret) {
0144 ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL);
0145 if (ret)
0146 memcpy(ret, des, sizeof(union apci_descriptor));
0147 }
0148 kfree(table);
0149 return ret;
0150 }
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160 static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
0161 {
0162 union acpi_object args[2];
0163 struct acpi_object_list params = { .pointer = args, .count = 2 };
0164 acpi_status stat;
0165 unsigned long long rc;
0166 union apci_descriptor *ibm_slot;
0167 int id = hpslot_to_sun(slot);
0168
0169 ibm_slot = ibm_slot_from_id(id);
0170 if (!ibm_slot) {
0171 pr_err("APLS null ACPI descriptor for slot %d\n", id);
0172 return -ENODEV;
0173 }
0174
0175 pr_debug("%s: set slot %d (%d) attention status to %d\n", __func__,
0176 ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
0177 (status ? 1 : 0));
0178
0179 args[0].type = ACPI_TYPE_INTEGER;
0180 args[0].integer.value = ibm_slot->slot.slot_id;
0181 args[1].type = ACPI_TYPE_INTEGER;
0182 args[1].integer.value = (status) ? 1 : 0;
0183
0184 kfree(ibm_slot);
0185
0186 stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", ¶ms, &rc);
0187 if (ACPI_FAILURE(stat)) {
0188 pr_err("APLS evaluation failed: 0x%08x\n", stat);
0189 return -ENODEV;
0190 } else if (!rc) {
0191 pr_err("APLS method failed: 0x%08llx\n", rc);
0192 return -ERANGE;
0193 }
0194 return 0;
0195 }
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209 static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status)
0210 {
0211 union apci_descriptor *ibm_slot;
0212 int id = hpslot_to_sun(slot);
0213
0214 ibm_slot = ibm_slot_from_id(id);
0215 if (!ibm_slot) {
0216 pr_err("APLS null ACPI descriptor for slot %d\n", id);
0217 return -ENODEV;
0218 }
0219
0220 if (ibm_slot->slot.attn & 0xa0 || ibm_slot->slot.status[1] & 0x08)
0221 *status = 1;
0222 else
0223 *status = 0;
0224
0225 pr_debug("%s: get slot %d (%d) attention status is %d\n", __func__,
0226 ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
0227 *status);
0228
0229 kfree(ibm_slot);
0230 return 0;
0231 }
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251 static void ibm_handle_events(acpi_handle handle, u32 event, void *context)
0252 {
0253 u8 detail = event & 0x0f;
0254 u8 subevent = event & 0xf0;
0255 struct notification *note = context;
0256
0257 pr_debug("%s: Received notification %02x\n", __func__, event);
0258
0259 if (subevent == 0x80) {
0260 pr_debug("%s: generating bus event\n", __func__);
0261 acpi_bus_generate_netlink_event(note->device->pnp.device_class,
0262 dev_name(¬e->device->dev),
0263 note->event, detail);
0264 } else
0265 note->event = event;
0266 }
0267
0268
0269
0270
0271
0272
0273
0274
0275
0276
0277
0278
0279
0280
0281
0282 static int ibm_get_table_from_acpi(char **bufp)
0283 {
0284 union acpi_object *package;
0285 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0286 acpi_status status;
0287 char *lbuf = NULL;
0288 int i, size = -EIO;
0289
0290 status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer);
0291 if (ACPI_FAILURE(status)) {
0292 pr_err("%s: APCI evaluation failed\n", __func__);
0293 return -ENODEV;
0294 }
0295
0296 package = (union acpi_object *) buffer.pointer;
0297 if (!(package) ||
0298 (package->type != ACPI_TYPE_PACKAGE) ||
0299 !(package->package.elements)) {
0300 pr_err("%s: Invalid APCI object\n", __func__);
0301 goto read_table_done;
0302 }
0303
0304 for (size = 0, i = 0; i < package->package.count; i++) {
0305 if (package->package.elements[i].type != ACPI_TYPE_BUFFER) {
0306 pr_err("%s: Invalid APCI element %d\n", __func__, i);
0307 goto read_table_done;
0308 }
0309 size += package->package.elements[i].buffer.length;
0310 }
0311
0312 if (bufp == NULL)
0313 goto read_table_done;
0314
0315 lbuf = kzalloc(size, GFP_KERNEL);
0316 pr_debug("%s: element count: %i, ASL table size: %i, &table = 0x%p\n",
0317 __func__, package->package.count, size, lbuf);
0318
0319 if (lbuf) {
0320 *bufp = lbuf;
0321 } else {
0322 size = -ENOMEM;
0323 goto read_table_done;
0324 }
0325
0326 size = 0;
0327 for (i = 0; i < package->package.count; i++) {
0328 memcpy(&lbuf[size],
0329 package->package.elements[i].buffer.pointer,
0330 package->package.elements[i].buffer.length);
0331 size += package->package.elements[i].buffer.length;
0332 }
0333
0334 read_table_done:
0335 kfree(buffer.pointer);
0336 return size;
0337 }
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355 static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj,
0356 struct bin_attribute *bin_attr,
0357 char *buffer, loff_t pos, size_t size)
0358 {
0359 int bytes_read = -EINVAL;
0360 char *table = NULL;
0361
0362 pr_debug("%s: pos = %d, size = %zd\n", __func__, (int)pos, size);
0363
0364 if (pos == 0) {
0365 bytes_read = ibm_get_table_from_acpi(&table);
0366 if (bytes_read > 0 && bytes_read <= size)
0367 memcpy(buffer, table, bytes_read);
0368 kfree(table);
0369 }
0370 return bytes_read;
0371 }
0372
0373
0374
0375
0376
0377
0378
0379
0380
0381
0382
0383
0384 static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
0385 u32 lvl, void *context, void **rv)
0386 {
0387 acpi_handle *phandle = (acpi_handle *)context;
0388 unsigned long long current_status = 0;
0389 acpi_status status;
0390 struct acpi_device_info *info;
0391 int retval = 0;
0392
0393 status = acpi_get_object_info(handle, &info);
0394 if (ACPI_FAILURE(status)) {
0395 pr_err("%s: Failed to get device information status=0x%x\n",
0396 __func__, status);
0397 return retval;
0398 }
0399
0400 acpi_bus_get_status_handle(handle, ¤t_status);
0401
0402 if (current_status && (info->valid & ACPI_VALID_HID) &&
0403 (!strcmp(info->hardware_id.string, IBM_HARDWARE_ID1) ||
0404 !strcmp(info->hardware_id.string, IBM_HARDWARE_ID2))) {
0405 pr_debug("found hardware: %s, handle: %p\n",
0406 info->hardware_id.string, handle);
0407 *phandle = handle;
0408
0409
0410
0411
0412
0413 retval = FOUND_APCI;
0414 }
0415 kfree(info);
0416 return retval;
0417 }
0418
0419 static int __init ibm_acpiphp_init(void)
0420 {
0421 int retval = 0;
0422 acpi_status status;
0423 struct acpi_device *device;
0424 struct kobject *sysdir = &pci_slots_kset->kobj;
0425
0426 pr_debug("%s\n", __func__);
0427
0428 if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
0429 ACPI_UINT32_MAX, ibm_find_acpi_device, NULL,
0430 &ibm_acpi_handle, NULL) != FOUND_APCI) {
0431 pr_err("%s: acpi_walk_namespace failed\n", __func__);
0432 retval = -ENODEV;
0433 goto init_return;
0434 }
0435 pr_debug("%s: found IBM aPCI device\n", __func__);
0436 device = acpi_fetch_acpi_dev(ibm_acpi_handle);
0437 if (!device) {
0438 pr_err("%s: acpi_fetch_acpi_dev failed\n", __func__);
0439 retval = -ENODEV;
0440 goto init_return;
0441 }
0442 if (acpiphp_register_attention(&ibm_attention_info)) {
0443 retval = -ENODEV;
0444 goto init_return;
0445 }
0446
0447 ibm_note.device = device;
0448 status = acpi_install_notify_handler(ibm_acpi_handle,
0449 ACPI_DEVICE_NOTIFY, ibm_handle_events,
0450 &ibm_note);
0451 if (ACPI_FAILURE(status)) {
0452 pr_err("%s: Failed to register notification handler\n",
0453 __func__);
0454 retval = -EBUSY;
0455 goto init_cleanup;
0456 }
0457
0458 ibm_apci_table_attr.size = ibm_get_table_from_acpi(NULL);
0459 retval = sysfs_create_bin_file(sysdir, &ibm_apci_table_attr);
0460
0461 return retval;
0462
0463 init_cleanup:
0464 acpiphp_unregister_attention(&ibm_attention_info);
0465 init_return:
0466 return retval;
0467 }
0468
0469 static void __exit ibm_acpiphp_exit(void)
0470 {
0471 acpi_status status;
0472 struct kobject *sysdir = &pci_slots_kset->kobj;
0473
0474 pr_debug("%s\n", __func__);
0475
0476 if (acpiphp_unregister_attention(&ibm_attention_info))
0477 pr_err("%s: attention info deregistration failed", __func__);
0478
0479 status = acpi_remove_notify_handler(
0480 ibm_acpi_handle,
0481 ACPI_DEVICE_NOTIFY,
0482 ibm_handle_events);
0483 if (ACPI_FAILURE(status))
0484 pr_err("%s: Notification handler removal failed\n", __func__);
0485
0486 sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
0487 }
0488
0489 module_init(ibm_acpiphp_init);
0490 module_exit(ibm_acpiphp_exit);