18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ACPI PCI Hot Plug IBM Extension 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Vernon Mauery <vernux@us.ibm.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2004 IBM Corp. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * All rights reserved. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Send feedback to <vernux@us.ibm.com> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "acpiphp_ibm: " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 218c2ecf20Sopenharmony_ci#include <linux/kobject.h> 228c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 238c2ecf20Sopenharmony_ci#include <linux/pci.h> 248c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "acpiphp.h" 278c2ecf20Sopenharmony_ci#include "../pci.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define DRIVER_VERSION "1.0.1" 308c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>" 318c2ecf20Sopenharmony_ci#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver IBM extension" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 378c2ecf20Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define FOUND_APCI 0x61504349 408c2ecf20Sopenharmony_ci/* these are the names for the IBM ACPI pseudo-device */ 418c2ecf20Sopenharmony_ci#define IBM_HARDWARE_ID1 "IBM37D0" 428c2ecf20Sopenharmony_ci#define IBM_HARDWARE_ID2 "IBM37D4" 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define hpslot_to_sun(A) (to_slot(A)->sun) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* union apci_descriptor - allows access to the 478c2ecf20Sopenharmony_ci * various device descriptors that are embedded in the 488c2ecf20Sopenharmony_ci * aPCI table 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ciunion apci_descriptor { 518c2ecf20Sopenharmony_ci struct { 528c2ecf20Sopenharmony_ci char sig[4]; 538c2ecf20Sopenharmony_ci u8 len; 548c2ecf20Sopenharmony_ci } header; 558c2ecf20Sopenharmony_ci struct { 568c2ecf20Sopenharmony_ci u8 type; 578c2ecf20Sopenharmony_ci u8 len; 588c2ecf20Sopenharmony_ci u16 slot_id; 598c2ecf20Sopenharmony_ci u8 bus_id; 608c2ecf20Sopenharmony_ci u8 dev_num; 618c2ecf20Sopenharmony_ci u8 slot_num; 628c2ecf20Sopenharmony_ci u8 slot_attr[2]; 638c2ecf20Sopenharmony_ci u8 attn; 648c2ecf20Sopenharmony_ci u8 status[2]; 658c2ecf20Sopenharmony_ci u8 sun; 668c2ecf20Sopenharmony_ci u8 res[3]; 678c2ecf20Sopenharmony_ci } slot; 688c2ecf20Sopenharmony_ci struct { 698c2ecf20Sopenharmony_ci u8 type; 708c2ecf20Sopenharmony_ci u8 len; 718c2ecf20Sopenharmony_ci } generic; 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* struct notification - keeps info about the device 758c2ecf20Sopenharmony_ci * that cause the ACPI notification event 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_cistruct notification { 788c2ecf20Sopenharmony_ci struct acpi_device *device; 798c2ecf20Sopenharmony_ci u8 event; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int ibm_set_attention_status(struct hotplug_slot *slot, u8 status); 838c2ecf20Sopenharmony_cistatic int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status); 848c2ecf20Sopenharmony_cistatic void ibm_handle_events(acpi_handle handle, u32 event, void *context); 858c2ecf20Sopenharmony_cistatic int ibm_get_table_from_acpi(char **bufp); 868c2ecf20Sopenharmony_cistatic ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj, 878c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, 888c2ecf20Sopenharmony_ci char *buffer, loff_t pos, size_t size); 898c2ecf20Sopenharmony_cistatic acpi_status __init ibm_find_acpi_device(acpi_handle handle, 908c2ecf20Sopenharmony_ci u32 lvl, void *context, void **rv); 918c2ecf20Sopenharmony_cistatic int __init ibm_acpiphp_init(void); 928c2ecf20Sopenharmony_cistatic void __exit ibm_acpiphp_exit(void); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic acpi_handle ibm_acpi_handle; 958c2ecf20Sopenharmony_cistatic struct notification ibm_note; 968c2ecf20Sopenharmony_cistatic struct bin_attribute ibm_apci_table_attr __ro_after_init = { 978c2ecf20Sopenharmony_ci .attr = { 988c2ecf20Sopenharmony_ci .name = "apci_table", 998c2ecf20Sopenharmony_ci .mode = S_IRUGO, 1008c2ecf20Sopenharmony_ci }, 1018c2ecf20Sopenharmony_ci .read = ibm_read_apci_table, 1028c2ecf20Sopenharmony_ci .write = NULL, 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_cistatic struct acpiphp_attention_info ibm_attention_info = 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci .set_attn = ibm_set_attention_status, 1078c2ecf20Sopenharmony_ci .get_attn = ibm_get_attention_status, 1088c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/** 1128c2ecf20Sopenharmony_ci * ibm_slot_from_id - workaround for bad ibm hardware 1138c2ecf20Sopenharmony_ci * @id: the slot number that linux refers to the slot by 1148c2ecf20Sopenharmony_ci * 1158c2ecf20Sopenharmony_ci * Description: This method returns the aCPI slot descriptor 1168c2ecf20Sopenharmony_ci * corresponding to the Linux slot number. This descriptor 1178c2ecf20Sopenharmony_ci * has info about the aPCI slot id and attention status. 1188c2ecf20Sopenharmony_ci * This descriptor must be freed using kfree when done. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_cistatic union apci_descriptor *ibm_slot_from_id(int id) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int ind = 0, size; 1238c2ecf20Sopenharmony_ci union apci_descriptor *ret = NULL, *des; 1248c2ecf20Sopenharmony_ci char *table; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci size = ibm_get_table_from_acpi(&table); 1278c2ecf20Sopenharmony_ci if (size < 0) 1288c2ecf20Sopenharmony_ci return NULL; 1298c2ecf20Sopenharmony_ci des = (union apci_descriptor *)table; 1308c2ecf20Sopenharmony_ci if (memcmp(des->header.sig, "aPCI", 4) != 0) 1318c2ecf20Sopenharmony_ci goto ibm_slot_done; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci des = (union apci_descriptor *)&table[ind += des->header.len]; 1348c2ecf20Sopenharmony_ci while (ind < size && (des->generic.type != 0x82 || 1358c2ecf20Sopenharmony_ci des->slot.slot_num != id)) { 1368c2ecf20Sopenharmony_ci des = (union apci_descriptor *)&table[ind += des->generic.len]; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (ind < size && des->slot.slot_num == id) 1408c2ecf20Sopenharmony_ci ret = des; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciibm_slot_done: 1438c2ecf20Sopenharmony_ci if (ret) { 1448c2ecf20Sopenharmony_ci ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL); 1458c2ecf20Sopenharmony_ci if (ret) 1468c2ecf20Sopenharmony_ci memcpy(ret, des, sizeof(union apci_descriptor)); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci kfree(table); 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/** 1538c2ecf20Sopenharmony_ci * ibm_set_attention_status - callback method to set the attention LED 1548c2ecf20Sopenharmony_ci * @slot: the hotplug_slot to work with 1558c2ecf20Sopenharmony_ci * @status: what to set the LED to (0 or 1) 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * Description: This method is registered with the acpiphp module as a 1588c2ecf20Sopenharmony_ci * callback to do the device specific task of setting the LED status. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_cistatic int ibm_set_attention_status(struct hotplug_slot *slot, u8 status) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci union acpi_object args[2]; 1638c2ecf20Sopenharmony_ci struct acpi_object_list params = { .pointer = args, .count = 2 }; 1648c2ecf20Sopenharmony_ci acpi_status stat; 1658c2ecf20Sopenharmony_ci unsigned long long rc; 1668c2ecf20Sopenharmony_ci union apci_descriptor *ibm_slot; 1678c2ecf20Sopenharmony_ci int id = hpslot_to_sun(slot); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ibm_slot = ibm_slot_from_id(id); 1708c2ecf20Sopenharmony_ci if (!ibm_slot) { 1718c2ecf20Sopenharmony_ci pr_err("APLS null ACPI descriptor for slot %d\n", id); 1728c2ecf20Sopenharmony_ci return -ENODEV; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci pr_debug("%s: set slot %d (%d) attention status to %d\n", __func__, 1768c2ecf20Sopenharmony_ci ibm_slot->slot.slot_num, ibm_slot->slot.slot_id, 1778c2ecf20Sopenharmony_ci (status ? 1 : 0)); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci args[0].type = ACPI_TYPE_INTEGER; 1808c2ecf20Sopenharmony_ci args[0].integer.value = ibm_slot->slot.slot_id; 1818c2ecf20Sopenharmony_ci args[1].type = ACPI_TYPE_INTEGER; 1828c2ecf20Sopenharmony_ci args[1].integer.value = (status) ? 1 : 0; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci kfree(ibm_slot); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", ¶ms, &rc); 1878c2ecf20Sopenharmony_ci if (ACPI_FAILURE(stat)) { 1888c2ecf20Sopenharmony_ci pr_err("APLS evaluation failed: 0x%08x\n", stat); 1898c2ecf20Sopenharmony_ci return -ENODEV; 1908c2ecf20Sopenharmony_ci } else if (!rc) { 1918c2ecf20Sopenharmony_ci pr_err("APLS method failed: 0x%08llx\n", rc); 1928c2ecf20Sopenharmony_ci return -ERANGE; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/** 1988c2ecf20Sopenharmony_ci * ibm_get_attention_status - callback method to get attention LED status 1998c2ecf20Sopenharmony_ci * @slot: the hotplug_slot to work with 2008c2ecf20Sopenharmony_ci * @status: returns what the LED is set to (0 or 1) 2018c2ecf20Sopenharmony_ci * 2028c2ecf20Sopenharmony_ci * Description: This method is registered with the acpiphp module as a 2038c2ecf20Sopenharmony_ci * callback to do the device specific task of getting the LED status. 2048c2ecf20Sopenharmony_ci * 2058c2ecf20Sopenharmony_ci * Because there is no direct method of getting the LED status directly 2068c2ecf20Sopenharmony_ci * from an ACPI call, we read the aPCI table and parse out our 2078c2ecf20Sopenharmony_ci * slot descriptor to read the status from that. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_cistatic int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci union apci_descriptor *ibm_slot; 2128c2ecf20Sopenharmony_ci int id = hpslot_to_sun(slot); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci ibm_slot = ibm_slot_from_id(id); 2158c2ecf20Sopenharmony_ci if (!ibm_slot) { 2168c2ecf20Sopenharmony_ci pr_err("APLS null ACPI descriptor for slot %d\n", id); 2178c2ecf20Sopenharmony_ci return -ENODEV; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (ibm_slot->slot.attn & 0xa0 || ibm_slot->slot.status[1] & 0x08) 2218c2ecf20Sopenharmony_ci *status = 1; 2228c2ecf20Sopenharmony_ci else 2238c2ecf20Sopenharmony_ci *status = 0; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci pr_debug("%s: get slot %d (%d) attention status is %d\n", __func__, 2268c2ecf20Sopenharmony_ci ibm_slot->slot.slot_num, ibm_slot->slot.slot_id, 2278c2ecf20Sopenharmony_ci *status); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci kfree(ibm_slot); 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/** 2348c2ecf20Sopenharmony_ci * ibm_handle_events - listens for ACPI events for the IBM37D0 device 2358c2ecf20Sopenharmony_ci * @handle: an ACPI handle to the device that caused the event 2368c2ecf20Sopenharmony_ci * @event: the event info (device specific) 2378c2ecf20Sopenharmony_ci * @context: passed context (our notification struct) 2388c2ecf20Sopenharmony_ci * 2398c2ecf20Sopenharmony_ci * Description: This method is registered as a callback with the ACPI 2408c2ecf20Sopenharmony_ci * subsystem it is called when this device has an event to notify the OS of. 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * The events actually come from the device as two events that get 2438c2ecf20Sopenharmony_ci * synthesized into one event with data by this function. The event 2448c2ecf20Sopenharmony_ci * ID comes first and then the slot number that caused it. We report 2458c2ecf20Sopenharmony_ci * this as one event to the OS. 2468c2ecf20Sopenharmony_ci * 2478c2ecf20Sopenharmony_ci * From section 5.6.2.2 of the ACPI 2.0 spec, I understand that the OSPM will 2488c2ecf20Sopenharmony_ci * only re-enable the interrupt that causes this event AFTER this method 2498c2ecf20Sopenharmony_ci * has returned, thereby enforcing serial access for the notification struct. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_cistatic void ibm_handle_events(acpi_handle handle, u32 event, void *context) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci u8 detail = event & 0x0f; 2548c2ecf20Sopenharmony_ci u8 subevent = event & 0xf0; 2558c2ecf20Sopenharmony_ci struct notification *note = context; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci pr_debug("%s: Received notification %02x\n", __func__, event); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (subevent == 0x80) { 2608c2ecf20Sopenharmony_ci pr_debug("%s: generating bus event\n", __func__); 2618c2ecf20Sopenharmony_ci acpi_bus_generate_netlink_event(note->device->pnp.device_class, 2628c2ecf20Sopenharmony_ci dev_name(¬e->device->dev), 2638c2ecf20Sopenharmony_ci note->event, detail); 2648c2ecf20Sopenharmony_ci } else 2658c2ecf20Sopenharmony_ci note->event = event; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/** 2698c2ecf20Sopenharmony_ci * ibm_get_table_from_acpi - reads the APLS buffer from ACPI 2708c2ecf20Sopenharmony_ci * @bufp: address to pointer to allocate for the table 2718c2ecf20Sopenharmony_ci * 2728c2ecf20Sopenharmony_ci * Description: This method reads the APLS buffer in from ACPI and 2738c2ecf20Sopenharmony_ci * stores the "stripped" table into a single buffer 2748c2ecf20Sopenharmony_ci * it allocates and passes the address back in bufp. 2758c2ecf20Sopenharmony_ci * 2768c2ecf20Sopenharmony_ci * If NULL is passed in as buffer, this method only calculates 2778c2ecf20Sopenharmony_ci * the size of the table and returns that without filling 2788c2ecf20Sopenharmony_ci * in the buffer. 2798c2ecf20Sopenharmony_ci * 2808c2ecf20Sopenharmony_ci * Returns < 0 on error or the size of the table on success. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_cistatic int ibm_get_table_from_acpi(char **bufp) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci union acpi_object *package; 2858c2ecf20Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 2868c2ecf20Sopenharmony_ci acpi_status status; 2878c2ecf20Sopenharmony_ci char *lbuf = NULL; 2888c2ecf20Sopenharmony_ci int i, size = -EIO; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer); 2918c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 2928c2ecf20Sopenharmony_ci pr_err("%s: APCI evaluation failed\n", __func__); 2938c2ecf20Sopenharmony_ci return -ENODEV; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci package = (union acpi_object *) buffer.pointer; 2978c2ecf20Sopenharmony_ci if (!(package) || 2988c2ecf20Sopenharmony_ci (package->type != ACPI_TYPE_PACKAGE) || 2998c2ecf20Sopenharmony_ci !(package->package.elements)) { 3008c2ecf20Sopenharmony_ci pr_err("%s: Invalid APCI object\n", __func__); 3018c2ecf20Sopenharmony_ci goto read_table_done; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci for (size = 0, i = 0; i < package->package.count; i++) { 3058c2ecf20Sopenharmony_ci if (package->package.elements[i].type != ACPI_TYPE_BUFFER) { 3068c2ecf20Sopenharmony_ci pr_err("%s: Invalid APCI element %d\n", __func__, i); 3078c2ecf20Sopenharmony_ci goto read_table_done; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci size += package->package.elements[i].buffer.length; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (bufp == NULL) 3138c2ecf20Sopenharmony_ci goto read_table_done; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci lbuf = kzalloc(size, GFP_KERNEL); 3168c2ecf20Sopenharmony_ci pr_debug("%s: element count: %i, ASL table size: %i, &table = 0x%p\n", 3178c2ecf20Sopenharmony_ci __func__, package->package.count, size, lbuf); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (lbuf) { 3208c2ecf20Sopenharmony_ci *bufp = lbuf; 3218c2ecf20Sopenharmony_ci } else { 3228c2ecf20Sopenharmony_ci size = -ENOMEM; 3238c2ecf20Sopenharmony_ci goto read_table_done; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci size = 0; 3278c2ecf20Sopenharmony_ci for (i = 0; i < package->package.count; i++) { 3288c2ecf20Sopenharmony_ci memcpy(&lbuf[size], 3298c2ecf20Sopenharmony_ci package->package.elements[i].buffer.pointer, 3308c2ecf20Sopenharmony_ci package->package.elements[i].buffer.length); 3318c2ecf20Sopenharmony_ci size += package->package.elements[i].buffer.length; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ciread_table_done: 3358c2ecf20Sopenharmony_ci kfree(buffer.pointer); 3368c2ecf20Sopenharmony_ci return size; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/** 3408c2ecf20Sopenharmony_ci * ibm_read_apci_table - callback for the sysfs apci_table file 3418c2ecf20Sopenharmony_ci * @filp: the open sysfs file 3428c2ecf20Sopenharmony_ci * @kobj: the kobject this binary attribute is a part of 3438c2ecf20Sopenharmony_ci * @bin_attr: struct bin_attribute for this file 3448c2ecf20Sopenharmony_ci * @buffer: the kernel space buffer to fill 3458c2ecf20Sopenharmony_ci * @pos: the offset into the file 3468c2ecf20Sopenharmony_ci * @size: the number of bytes requested 3478c2ecf20Sopenharmony_ci * 3488c2ecf20Sopenharmony_ci * Description: Gets registered with sysfs as the reader callback 3498c2ecf20Sopenharmony_ci * to be executed when /sys/bus/pci/slots/apci_table gets read. 3508c2ecf20Sopenharmony_ci * 3518c2ecf20Sopenharmony_ci * Since we don't get notified on open and close for this file, 3528c2ecf20Sopenharmony_ci * things get really tricky here... 3538c2ecf20Sopenharmony_ci * our solution is to only allow reading the table in all at once. 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_cistatic ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj, 3568c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, 3578c2ecf20Sopenharmony_ci char *buffer, loff_t pos, size_t size) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci int bytes_read = -EINVAL; 3608c2ecf20Sopenharmony_ci char *table = NULL; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci pr_debug("%s: pos = %d, size = %zd\n", __func__, (int)pos, size); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (pos == 0) { 3658c2ecf20Sopenharmony_ci bytes_read = ibm_get_table_from_acpi(&table); 3668c2ecf20Sopenharmony_ci if (bytes_read > 0 && bytes_read <= size) 3678c2ecf20Sopenharmony_ci memcpy(buffer, table, bytes_read); 3688c2ecf20Sopenharmony_ci kfree(table); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci return bytes_read; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci/** 3748c2ecf20Sopenharmony_ci * ibm_find_acpi_device - callback to find our ACPI device 3758c2ecf20Sopenharmony_ci * @handle: the ACPI handle of the device we are inspecting 3768c2ecf20Sopenharmony_ci * @lvl: depth into the namespace tree 3778c2ecf20Sopenharmony_ci * @context: a pointer to our handle to fill when we find the device 3788c2ecf20Sopenharmony_ci * @rv: a return value to fill if desired 3798c2ecf20Sopenharmony_ci * 3808c2ecf20Sopenharmony_ci * Description: Used as a callback when calling acpi_walk_namespace 3818c2ecf20Sopenharmony_ci * to find our device. When this method returns non-zero 3828c2ecf20Sopenharmony_ci * acpi_walk_namespace quits its search and returns our value. 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_cistatic acpi_status __init ibm_find_acpi_device(acpi_handle handle, 3858c2ecf20Sopenharmony_ci u32 lvl, void *context, void **rv) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci acpi_handle *phandle = (acpi_handle *)context; 3888c2ecf20Sopenharmony_ci unsigned long long current_status = 0; 3898c2ecf20Sopenharmony_ci acpi_status status; 3908c2ecf20Sopenharmony_ci struct acpi_device_info *info; 3918c2ecf20Sopenharmony_ci int retval = 0; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci status = acpi_get_object_info(handle, &info); 3948c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 3958c2ecf20Sopenharmony_ci pr_err("%s: Failed to get device information status=0x%x\n", 3968c2ecf20Sopenharmony_ci __func__, status); 3978c2ecf20Sopenharmony_ci return retval; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci acpi_bus_get_status_handle(handle, ¤t_status); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (current_status && (info->valid & ACPI_VALID_HID) && 4038c2ecf20Sopenharmony_ci (!strcmp(info->hardware_id.string, IBM_HARDWARE_ID1) || 4048c2ecf20Sopenharmony_ci !strcmp(info->hardware_id.string, IBM_HARDWARE_ID2))) { 4058c2ecf20Sopenharmony_ci pr_debug("found hardware: %s, handle: %p\n", 4068c2ecf20Sopenharmony_ci info->hardware_id.string, handle); 4078c2ecf20Sopenharmony_ci *phandle = handle; 4088c2ecf20Sopenharmony_ci /* returning non-zero causes the search to stop 4098c2ecf20Sopenharmony_ci * and returns this value to the caller of 4108c2ecf20Sopenharmony_ci * acpi_walk_namespace, but it also causes some warnings 4118c2ecf20Sopenharmony_ci * in the acpi debug code to print... 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_ci retval = FOUND_APCI; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci kfree(info); 4168c2ecf20Sopenharmony_ci return retval; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic int __init ibm_acpiphp_init(void) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci int retval = 0; 4228c2ecf20Sopenharmony_ci acpi_status status; 4238c2ecf20Sopenharmony_ci struct acpi_device *device; 4248c2ecf20Sopenharmony_ci struct kobject *sysdir = &pci_slots_kset->kobj; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci pr_debug("%s\n", __func__); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 4298c2ecf20Sopenharmony_ci ACPI_UINT32_MAX, ibm_find_acpi_device, NULL, 4308c2ecf20Sopenharmony_ci &ibm_acpi_handle, NULL) != FOUND_APCI) { 4318c2ecf20Sopenharmony_ci pr_err("%s: acpi_walk_namespace failed\n", __func__); 4328c2ecf20Sopenharmony_ci retval = -ENODEV; 4338c2ecf20Sopenharmony_ci goto init_return; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci pr_debug("%s: found IBM aPCI device\n", __func__); 4368c2ecf20Sopenharmony_ci if (acpi_bus_get_device(ibm_acpi_handle, &device)) { 4378c2ecf20Sopenharmony_ci pr_err("%s: acpi_bus_get_device failed\n", __func__); 4388c2ecf20Sopenharmony_ci retval = -ENODEV; 4398c2ecf20Sopenharmony_ci goto init_return; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci if (acpiphp_register_attention(&ibm_attention_info)) { 4428c2ecf20Sopenharmony_ci retval = -ENODEV; 4438c2ecf20Sopenharmony_ci goto init_return; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci ibm_note.device = device; 4478c2ecf20Sopenharmony_ci status = acpi_install_notify_handler(ibm_acpi_handle, 4488c2ecf20Sopenharmony_ci ACPI_DEVICE_NOTIFY, ibm_handle_events, 4498c2ecf20Sopenharmony_ci &ibm_note); 4508c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 4518c2ecf20Sopenharmony_ci pr_err("%s: Failed to register notification handler\n", 4528c2ecf20Sopenharmony_ci __func__); 4538c2ecf20Sopenharmony_ci retval = -EBUSY; 4548c2ecf20Sopenharmony_ci goto init_cleanup; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci ibm_apci_table_attr.size = ibm_get_table_from_acpi(NULL); 4588c2ecf20Sopenharmony_ci retval = sysfs_create_bin_file(sysdir, &ibm_apci_table_attr); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci return retval; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ciinit_cleanup: 4638c2ecf20Sopenharmony_ci acpiphp_unregister_attention(&ibm_attention_info); 4648c2ecf20Sopenharmony_ciinit_return: 4658c2ecf20Sopenharmony_ci return retval; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic void __exit ibm_acpiphp_exit(void) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci acpi_status status; 4718c2ecf20Sopenharmony_ci struct kobject *sysdir = &pci_slots_kset->kobj; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci pr_debug("%s\n", __func__); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (acpiphp_unregister_attention(&ibm_attention_info)) 4768c2ecf20Sopenharmony_ci pr_err("%s: attention info deregistration failed", __func__); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci status = acpi_remove_notify_handler( 4798c2ecf20Sopenharmony_ci ibm_acpi_handle, 4808c2ecf20Sopenharmony_ci ACPI_DEVICE_NOTIFY, 4818c2ecf20Sopenharmony_ci ibm_handle_events); 4828c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 4838c2ecf20Sopenharmony_ci pr_err("%s: Notification handler removal failed\n", __func__); 4848c2ecf20Sopenharmony_ci /* remove the /sys entries */ 4858c2ecf20Sopenharmony_ci sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr); 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cimodule_init(ibm_acpiphp_init); 4898c2ecf20Sopenharmony_cimodule_exit(ibm_acpiphp_exit); 490