// SPDX-License-Identifier: GPL-2.0+ /* * ACPI PCI Hot Plug IBM Extension * * Copyright (C) 2004 Vernon Mauery <[email protected]> * Copyright (C) 2004 IBM Corp. * * All rights reserved. * * Send feedback to <[email protected]> * */ #define pr_fmt(fmt) … #include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/sysfs.h> #include <linux/kobject.h> #include <linux/moduleparam.h> #include <linux/pci.h> #include <linux/uaccess.h> #include "acpiphp.h" #include "../pci.h" #define DRIVER_VERSION … #define DRIVER_AUTHOR … #define DRIVER_DESC … MODULE_AUTHOR(…); MODULE_DESCRIPTION(…); MODULE_LICENSE(…) …; MODULE_VERSION(…); #define FOUND_APCI … /* these are the names for the IBM ACPI pseudo-device */ #define IBM_HARDWARE_ID1 … #define IBM_HARDWARE_ID2 … #define hpslot_to_sun(A) … /* union apci_descriptor - allows access to the * various device descriptors that are embedded in the * aPCI table */ apci_descriptor; /* struct notification - keeps info about the device * that cause the ACPI notification event */ struct notification { … }; static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status); static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status); static void ibm_handle_events(acpi_handle handle, u32 event, void *context); static int ibm_get_table_from_acpi(char **bufp); static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t size); static acpi_status __init ibm_find_acpi_device(acpi_handle handle, u32 lvl, void *context, void **rv); static int __init ibm_acpiphp_init(void); static void __exit ibm_acpiphp_exit(void); static acpi_handle ibm_acpi_handle; static struct notification ibm_note; static struct bin_attribute ibm_apci_table_attr __ro_after_init = …; static struct acpiphp_attention_info ibm_attention_info = …; /** * ibm_slot_from_id - workaround for bad ibm hardware * @id: the slot number that linux refers to the slot by * * Description: This method returns the aCPI slot descriptor * corresponding to the Linux slot number. This descriptor * has info about the aPCI slot id and attention status. * This descriptor must be freed using kfree when done. */ static union apci_descriptor *ibm_slot_from_id(int id) { … } /** * ibm_set_attention_status - callback method to set the attention LED * @slot: the hotplug_slot to work with * @status: what to set the LED to (0 or 1) * * Description: This method is registered with the acpiphp module as a * callback to do the device specific task of setting the LED status. */ static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status) { … } /** * ibm_get_attention_status - callback method to get attention LED status * @slot: the hotplug_slot to work with * @status: returns what the LED is set to (0 or 1) * * Description: This method is registered with the acpiphp module as a * callback to do the device specific task of getting the LED status. * * Because there is no direct method of getting the LED status directly * from an ACPI call, we read the aPCI table and parse out our * slot descriptor to read the status from that. */ static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status) { … } /** * ibm_handle_events - listens for ACPI events for the IBM37D0 device * @handle: an ACPI handle to the device that caused the event * @event: the event info (device specific) * @context: passed context (our notification struct) * * Description: This method is registered as a callback with the ACPI * subsystem it is called when this device has an event to notify the OS of. * * The events actually come from the device as two events that get * synthesized into one event with data by this function. The event * ID comes first and then the slot number that caused it. We report * this as one event to the OS. * * From section 5.6.2.2 of the ACPI 2.0 spec, I understand that the OSPM will * only re-enable the interrupt that causes this event AFTER this method * has returned, thereby enforcing serial access for the notification struct. */ static void ibm_handle_events(acpi_handle handle, u32 event, void *context) { … } /** * ibm_get_table_from_acpi - reads the APLS buffer from ACPI * @bufp: address to pointer to allocate for the table * * Description: This method reads the APLS buffer in from ACPI and * stores the "stripped" table into a single buffer * it allocates and passes the address back in bufp. * * If NULL is passed in as buffer, this method only calculates * the size of the table and returns that without filling * in the buffer. * * Returns < 0 on error or the size of the table on success. */ static int ibm_get_table_from_acpi(char **bufp) { … } /** * ibm_read_apci_table - callback for the sysfs apci_table file * @filp: the open sysfs file * @kobj: the kobject this binary attribute is a part of * @bin_attr: struct bin_attribute for this file * @buffer: the kernel space buffer to fill * @pos: the offset into the file * @size: the number of bytes requested * * Description: Gets registered with sysfs as the reader callback * to be executed when /sys/bus/pci/slots/apci_table gets read. * * Since we don't get notified on open and close for this file, * things get really tricky here... * our solution is to only allow reading the table in all at once. */ static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t size) { … } /** * ibm_find_acpi_device - callback to find our ACPI device * @handle: the ACPI handle of the device we are inspecting * @lvl: depth into the namespace tree * @context: a pointer to our handle to fill when we find the device * @rv: a return value to fill if desired * * Description: Used as a callback when calling acpi_walk_namespace * to find our device. When this method returns non-zero * acpi_walk_namespace quits its search and returns our value. */ static acpi_status __init ibm_find_acpi_device(acpi_handle handle, u32 lvl, void *context, void **rv) { … } static int __init ibm_acpiphp_init(void) { … } static void __exit ibm_acpiphp_exit(void) { … } module_init(…) …; module_exit(ibm_acpiphp_exit);