1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * ACPI PCI Hot Plug IBM Extension
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2004 Vernon Mauery <vernux@us.ibm.com>
6*4882a593Smuzhiyun * Copyright (C) 2004 IBM Corp.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * All rights reserved.
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * Send feedback to <vernux@us.ibm.com>
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #define pr_fmt(fmt) "acpiphp_ibm: " fmt
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <linux/init.h>
17*4882a593Smuzhiyun #include <linux/slab.h>
18*4882a593Smuzhiyun #include <linux/module.h>
19*4882a593Smuzhiyun #include <linux/kernel.h>
20*4882a593Smuzhiyun #include <linux/sysfs.h>
21*4882a593Smuzhiyun #include <linux/kobject.h>
22*4882a593Smuzhiyun #include <linux/moduleparam.h>
23*4882a593Smuzhiyun #include <linux/pci.h>
24*4882a593Smuzhiyun #include <linux/uaccess.h>
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #include "acpiphp.h"
27*4882a593Smuzhiyun #include "../pci.h"
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun #define DRIVER_VERSION "1.0.1"
30*4882a593Smuzhiyun #define DRIVER_AUTHOR "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
31*4882a593Smuzhiyun #define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver IBM extension"
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun MODULE_AUTHOR(DRIVER_AUTHOR);
35*4882a593Smuzhiyun MODULE_DESCRIPTION(DRIVER_DESC);
36*4882a593Smuzhiyun MODULE_LICENSE("GPL");
37*4882a593Smuzhiyun MODULE_VERSION(DRIVER_VERSION);
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun #define FOUND_APCI 0x61504349
40*4882a593Smuzhiyun /* these are the names for the IBM ACPI pseudo-device */
41*4882a593Smuzhiyun #define IBM_HARDWARE_ID1 "IBM37D0"
42*4882a593Smuzhiyun #define IBM_HARDWARE_ID2 "IBM37D4"
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun #define hpslot_to_sun(A) (to_slot(A)->sun)
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun /* union apci_descriptor - allows access to the
47*4882a593Smuzhiyun * various device descriptors that are embedded in the
48*4882a593Smuzhiyun * aPCI table
49*4882a593Smuzhiyun */
50*4882a593Smuzhiyun union apci_descriptor {
51*4882a593Smuzhiyun struct {
52*4882a593Smuzhiyun char sig[4];
53*4882a593Smuzhiyun u8 len;
54*4882a593Smuzhiyun } header;
55*4882a593Smuzhiyun struct {
56*4882a593Smuzhiyun u8 type;
57*4882a593Smuzhiyun u8 len;
58*4882a593Smuzhiyun u16 slot_id;
59*4882a593Smuzhiyun u8 bus_id;
60*4882a593Smuzhiyun u8 dev_num;
61*4882a593Smuzhiyun u8 slot_num;
62*4882a593Smuzhiyun u8 slot_attr[2];
63*4882a593Smuzhiyun u8 attn;
64*4882a593Smuzhiyun u8 status[2];
65*4882a593Smuzhiyun u8 sun;
66*4882a593Smuzhiyun u8 res[3];
67*4882a593Smuzhiyun } slot;
68*4882a593Smuzhiyun struct {
69*4882a593Smuzhiyun u8 type;
70*4882a593Smuzhiyun u8 len;
71*4882a593Smuzhiyun } generic;
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun /* struct notification - keeps info about the device
75*4882a593Smuzhiyun * that cause the ACPI notification event
76*4882a593Smuzhiyun */
77*4882a593Smuzhiyun struct notification {
78*4882a593Smuzhiyun struct acpi_device *device;
79*4882a593Smuzhiyun u8 event;
80*4882a593Smuzhiyun };
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status);
83*4882a593Smuzhiyun static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status);
84*4882a593Smuzhiyun static void ibm_handle_events(acpi_handle handle, u32 event, void *context);
85*4882a593Smuzhiyun static int ibm_get_table_from_acpi(char **bufp);
86*4882a593Smuzhiyun static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj,
87*4882a593Smuzhiyun struct bin_attribute *bin_attr,
88*4882a593Smuzhiyun char *buffer, loff_t pos, size_t size);
89*4882a593Smuzhiyun static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
90*4882a593Smuzhiyun u32 lvl, void *context, void **rv);
91*4882a593Smuzhiyun static int __init ibm_acpiphp_init(void);
92*4882a593Smuzhiyun static void __exit ibm_acpiphp_exit(void);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun static acpi_handle ibm_acpi_handle;
95*4882a593Smuzhiyun static struct notification ibm_note;
96*4882a593Smuzhiyun static struct bin_attribute ibm_apci_table_attr __ro_after_init = {
97*4882a593Smuzhiyun .attr = {
98*4882a593Smuzhiyun .name = "apci_table",
99*4882a593Smuzhiyun .mode = S_IRUGO,
100*4882a593Smuzhiyun },
101*4882a593Smuzhiyun .read = ibm_read_apci_table,
102*4882a593Smuzhiyun .write = NULL,
103*4882a593Smuzhiyun };
104*4882a593Smuzhiyun static struct acpiphp_attention_info ibm_attention_info =
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun .set_attn = ibm_set_attention_status,
107*4882a593Smuzhiyun .get_attn = ibm_get_attention_status,
108*4882a593Smuzhiyun .owner = THIS_MODULE,
109*4882a593Smuzhiyun };
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun /**
112*4882a593Smuzhiyun * ibm_slot_from_id - workaround for bad ibm hardware
113*4882a593Smuzhiyun * @id: the slot number that linux refers to the slot by
114*4882a593Smuzhiyun *
115*4882a593Smuzhiyun * Description: This method returns the aCPI slot descriptor
116*4882a593Smuzhiyun * corresponding to the Linux slot number. This descriptor
117*4882a593Smuzhiyun * has info about the aPCI slot id and attention status.
118*4882a593Smuzhiyun * This descriptor must be freed using kfree when done.
119*4882a593Smuzhiyun */
ibm_slot_from_id(int id)120*4882a593Smuzhiyun static union apci_descriptor *ibm_slot_from_id(int id)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun int ind = 0, size;
123*4882a593Smuzhiyun union apci_descriptor *ret = NULL, *des;
124*4882a593Smuzhiyun char *table;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun size = ibm_get_table_from_acpi(&table);
127*4882a593Smuzhiyun if (size < 0)
128*4882a593Smuzhiyun return NULL;
129*4882a593Smuzhiyun des = (union apci_descriptor *)table;
130*4882a593Smuzhiyun if (memcmp(des->header.sig, "aPCI", 4) != 0)
131*4882a593Smuzhiyun goto ibm_slot_done;
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun des = (union apci_descriptor *)&table[ind += des->header.len];
134*4882a593Smuzhiyun while (ind < size && (des->generic.type != 0x82 ||
135*4882a593Smuzhiyun des->slot.slot_num != id)) {
136*4882a593Smuzhiyun des = (union apci_descriptor *)&table[ind += des->generic.len];
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun if (ind < size && des->slot.slot_num == id)
140*4882a593Smuzhiyun ret = des;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun ibm_slot_done:
143*4882a593Smuzhiyun if (ret) {
144*4882a593Smuzhiyun ret = kmalloc(sizeof(union apci_descriptor), GFP_KERNEL);
145*4882a593Smuzhiyun if (ret)
146*4882a593Smuzhiyun memcpy(ret, des, sizeof(union apci_descriptor));
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun kfree(table);
149*4882a593Smuzhiyun return ret;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun /**
153*4882a593Smuzhiyun * ibm_set_attention_status - callback method to set the attention LED
154*4882a593Smuzhiyun * @slot: the hotplug_slot to work with
155*4882a593Smuzhiyun * @status: what to set the LED to (0 or 1)
156*4882a593Smuzhiyun *
157*4882a593Smuzhiyun * Description: This method is registered with the acpiphp module as a
158*4882a593Smuzhiyun * callback to do the device specific task of setting the LED status.
159*4882a593Smuzhiyun */
ibm_set_attention_status(struct hotplug_slot * slot,u8 status)160*4882a593Smuzhiyun static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun union acpi_object args[2];
163*4882a593Smuzhiyun struct acpi_object_list params = { .pointer = args, .count = 2 };
164*4882a593Smuzhiyun acpi_status stat;
165*4882a593Smuzhiyun unsigned long long rc;
166*4882a593Smuzhiyun union apci_descriptor *ibm_slot;
167*4882a593Smuzhiyun int id = hpslot_to_sun(slot);
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun ibm_slot = ibm_slot_from_id(id);
170*4882a593Smuzhiyun if (!ibm_slot) {
171*4882a593Smuzhiyun pr_err("APLS null ACPI descriptor for slot %d\n", id);
172*4882a593Smuzhiyun return -ENODEV;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun pr_debug("%s: set slot %d (%d) attention status to %d\n", __func__,
176*4882a593Smuzhiyun ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
177*4882a593Smuzhiyun (status ? 1 : 0));
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun args[0].type = ACPI_TYPE_INTEGER;
180*4882a593Smuzhiyun args[0].integer.value = ibm_slot->slot.slot_id;
181*4882a593Smuzhiyun args[1].type = ACPI_TYPE_INTEGER;
182*4882a593Smuzhiyun args[1].integer.value = (status) ? 1 : 0;
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun kfree(ibm_slot);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun stat = acpi_evaluate_integer(ibm_acpi_handle, "APLS", ¶ms, &rc);
187*4882a593Smuzhiyun if (ACPI_FAILURE(stat)) {
188*4882a593Smuzhiyun pr_err("APLS evaluation failed: 0x%08x\n", stat);
189*4882a593Smuzhiyun return -ENODEV;
190*4882a593Smuzhiyun } else if (!rc) {
191*4882a593Smuzhiyun pr_err("APLS method failed: 0x%08llx\n", rc);
192*4882a593Smuzhiyun return -ERANGE;
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun return 0;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun /**
198*4882a593Smuzhiyun * ibm_get_attention_status - callback method to get attention LED status
199*4882a593Smuzhiyun * @slot: the hotplug_slot to work with
200*4882a593Smuzhiyun * @status: returns what the LED is set to (0 or 1)
201*4882a593Smuzhiyun *
202*4882a593Smuzhiyun * Description: This method is registered with the acpiphp module as a
203*4882a593Smuzhiyun * callback to do the device specific task of getting the LED status.
204*4882a593Smuzhiyun *
205*4882a593Smuzhiyun * Because there is no direct method of getting the LED status directly
206*4882a593Smuzhiyun * from an ACPI call, we read the aPCI table and parse out our
207*4882a593Smuzhiyun * slot descriptor to read the status from that.
208*4882a593Smuzhiyun */
ibm_get_attention_status(struct hotplug_slot * slot,u8 * status)209*4882a593Smuzhiyun static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun union apci_descriptor *ibm_slot;
212*4882a593Smuzhiyun int id = hpslot_to_sun(slot);
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun ibm_slot = ibm_slot_from_id(id);
215*4882a593Smuzhiyun if (!ibm_slot) {
216*4882a593Smuzhiyun pr_err("APLS null ACPI descriptor for slot %d\n", id);
217*4882a593Smuzhiyun return -ENODEV;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun if (ibm_slot->slot.attn & 0xa0 || ibm_slot->slot.status[1] & 0x08)
221*4882a593Smuzhiyun *status = 1;
222*4882a593Smuzhiyun else
223*4882a593Smuzhiyun *status = 0;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun pr_debug("%s: get slot %d (%d) attention status is %d\n", __func__,
226*4882a593Smuzhiyun ibm_slot->slot.slot_num, ibm_slot->slot.slot_id,
227*4882a593Smuzhiyun *status);
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun kfree(ibm_slot);
230*4882a593Smuzhiyun return 0;
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun /**
234*4882a593Smuzhiyun * ibm_handle_events - listens for ACPI events for the IBM37D0 device
235*4882a593Smuzhiyun * @handle: an ACPI handle to the device that caused the event
236*4882a593Smuzhiyun * @event: the event info (device specific)
237*4882a593Smuzhiyun * @context: passed context (our notification struct)
238*4882a593Smuzhiyun *
239*4882a593Smuzhiyun * Description: This method is registered as a callback with the ACPI
240*4882a593Smuzhiyun * subsystem it is called when this device has an event to notify the OS of.
241*4882a593Smuzhiyun *
242*4882a593Smuzhiyun * The events actually come from the device as two events that get
243*4882a593Smuzhiyun * synthesized into one event with data by this function. The event
244*4882a593Smuzhiyun * ID comes first and then the slot number that caused it. We report
245*4882a593Smuzhiyun * this as one event to the OS.
246*4882a593Smuzhiyun *
247*4882a593Smuzhiyun * From section 5.6.2.2 of the ACPI 2.0 spec, I understand that the OSPM will
248*4882a593Smuzhiyun * only re-enable the interrupt that causes this event AFTER this method
249*4882a593Smuzhiyun * has returned, thereby enforcing serial access for the notification struct.
250*4882a593Smuzhiyun */
ibm_handle_events(acpi_handle handle,u32 event,void * context)251*4882a593Smuzhiyun static void ibm_handle_events(acpi_handle handle, u32 event, void *context)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun u8 detail = event & 0x0f;
254*4882a593Smuzhiyun u8 subevent = event & 0xf0;
255*4882a593Smuzhiyun struct notification *note = context;
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun pr_debug("%s: Received notification %02x\n", __func__, event);
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun if (subevent == 0x80) {
260*4882a593Smuzhiyun pr_debug("%s: generating bus event\n", __func__);
261*4882a593Smuzhiyun acpi_bus_generate_netlink_event(note->device->pnp.device_class,
262*4882a593Smuzhiyun dev_name(¬e->device->dev),
263*4882a593Smuzhiyun note->event, detail);
264*4882a593Smuzhiyun } else
265*4882a593Smuzhiyun note->event = event;
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun /**
269*4882a593Smuzhiyun * ibm_get_table_from_acpi - reads the APLS buffer from ACPI
270*4882a593Smuzhiyun * @bufp: address to pointer to allocate for the table
271*4882a593Smuzhiyun *
272*4882a593Smuzhiyun * Description: This method reads the APLS buffer in from ACPI and
273*4882a593Smuzhiyun * stores the "stripped" table into a single buffer
274*4882a593Smuzhiyun * it allocates and passes the address back in bufp.
275*4882a593Smuzhiyun *
276*4882a593Smuzhiyun * If NULL is passed in as buffer, this method only calculates
277*4882a593Smuzhiyun * the size of the table and returns that without filling
278*4882a593Smuzhiyun * in the buffer.
279*4882a593Smuzhiyun *
280*4882a593Smuzhiyun * Returns < 0 on error or the size of the table on success.
281*4882a593Smuzhiyun */
ibm_get_table_from_acpi(char ** bufp)282*4882a593Smuzhiyun static int ibm_get_table_from_acpi(char **bufp)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun union acpi_object *package;
285*4882a593Smuzhiyun struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
286*4882a593Smuzhiyun acpi_status status;
287*4882a593Smuzhiyun char *lbuf = NULL;
288*4882a593Smuzhiyun int i, size = -EIO;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun status = acpi_evaluate_object(ibm_acpi_handle, "APCI", NULL, &buffer);
291*4882a593Smuzhiyun if (ACPI_FAILURE(status)) {
292*4882a593Smuzhiyun pr_err("%s: APCI evaluation failed\n", __func__);
293*4882a593Smuzhiyun return -ENODEV;
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun package = (union acpi_object *) buffer.pointer;
297*4882a593Smuzhiyun if (!(package) ||
298*4882a593Smuzhiyun (package->type != ACPI_TYPE_PACKAGE) ||
299*4882a593Smuzhiyun !(package->package.elements)) {
300*4882a593Smuzhiyun pr_err("%s: Invalid APCI object\n", __func__);
301*4882a593Smuzhiyun goto read_table_done;
302*4882a593Smuzhiyun }
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun for (size = 0, i = 0; i < package->package.count; i++) {
305*4882a593Smuzhiyun if (package->package.elements[i].type != ACPI_TYPE_BUFFER) {
306*4882a593Smuzhiyun pr_err("%s: Invalid APCI element %d\n", __func__, i);
307*4882a593Smuzhiyun goto read_table_done;
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun size += package->package.elements[i].buffer.length;
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun if (bufp == NULL)
313*4882a593Smuzhiyun goto read_table_done;
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun lbuf = kzalloc(size, GFP_KERNEL);
316*4882a593Smuzhiyun pr_debug("%s: element count: %i, ASL table size: %i, &table = 0x%p\n",
317*4882a593Smuzhiyun __func__, package->package.count, size, lbuf);
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun if (lbuf) {
320*4882a593Smuzhiyun *bufp = lbuf;
321*4882a593Smuzhiyun } else {
322*4882a593Smuzhiyun size = -ENOMEM;
323*4882a593Smuzhiyun goto read_table_done;
324*4882a593Smuzhiyun }
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun size = 0;
327*4882a593Smuzhiyun for (i = 0; i < package->package.count; i++) {
328*4882a593Smuzhiyun memcpy(&lbuf[size],
329*4882a593Smuzhiyun package->package.elements[i].buffer.pointer,
330*4882a593Smuzhiyun package->package.elements[i].buffer.length);
331*4882a593Smuzhiyun size += package->package.elements[i].buffer.length;
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun read_table_done:
335*4882a593Smuzhiyun kfree(buffer.pointer);
336*4882a593Smuzhiyun return size;
337*4882a593Smuzhiyun }
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun /**
340*4882a593Smuzhiyun * ibm_read_apci_table - callback for the sysfs apci_table file
341*4882a593Smuzhiyun * @filp: the open sysfs file
342*4882a593Smuzhiyun * @kobj: the kobject this binary attribute is a part of
343*4882a593Smuzhiyun * @bin_attr: struct bin_attribute for this file
344*4882a593Smuzhiyun * @buffer: the kernel space buffer to fill
345*4882a593Smuzhiyun * @pos: the offset into the file
346*4882a593Smuzhiyun * @size: the number of bytes requested
347*4882a593Smuzhiyun *
348*4882a593Smuzhiyun * Description: Gets registered with sysfs as the reader callback
349*4882a593Smuzhiyun * to be executed when /sys/bus/pci/slots/apci_table gets read.
350*4882a593Smuzhiyun *
351*4882a593Smuzhiyun * Since we don't get notified on open and close for this file,
352*4882a593Smuzhiyun * things get really tricky here...
353*4882a593Smuzhiyun * our solution is to only allow reading the table in all at once.
354*4882a593Smuzhiyun */
ibm_read_apci_table(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buffer,loff_t pos,size_t size)355*4882a593Smuzhiyun static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj,
356*4882a593Smuzhiyun struct bin_attribute *bin_attr,
357*4882a593Smuzhiyun char *buffer, loff_t pos, size_t size)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun int bytes_read = -EINVAL;
360*4882a593Smuzhiyun char *table = NULL;
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun pr_debug("%s: pos = %d, size = %zd\n", __func__, (int)pos, size);
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun if (pos == 0) {
365*4882a593Smuzhiyun bytes_read = ibm_get_table_from_acpi(&table);
366*4882a593Smuzhiyun if (bytes_read > 0 && bytes_read <= size)
367*4882a593Smuzhiyun memcpy(buffer, table, bytes_read);
368*4882a593Smuzhiyun kfree(table);
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun return bytes_read;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun /**
374*4882a593Smuzhiyun * ibm_find_acpi_device - callback to find our ACPI device
375*4882a593Smuzhiyun * @handle: the ACPI handle of the device we are inspecting
376*4882a593Smuzhiyun * @lvl: depth into the namespace tree
377*4882a593Smuzhiyun * @context: a pointer to our handle to fill when we find the device
378*4882a593Smuzhiyun * @rv: a return value to fill if desired
379*4882a593Smuzhiyun *
380*4882a593Smuzhiyun * Description: Used as a callback when calling acpi_walk_namespace
381*4882a593Smuzhiyun * to find our device. When this method returns non-zero
382*4882a593Smuzhiyun * acpi_walk_namespace quits its search and returns our value.
383*4882a593Smuzhiyun */
ibm_find_acpi_device(acpi_handle handle,u32 lvl,void * context,void ** rv)384*4882a593Smuzhiyun static acpi_status __init ibm_find_acpi_device(acpi_handle handle,
385*4882a593Smuzhiyun u32 lvl, void *context, void **rv)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun acpi_handle *phandle = (acpi_handle *)context;
388*4882a593Smuzhiyun unsigned long long current_status = 0;
389*4882a593Smuzhiyun acpi_status status;
390*4882a593Smuzhiyun struct acpi_device_info *info;
391*4882a593Smuzhiyun int retval = 0;
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun status = acpi_get_object_info(handle, &info);
394*4882a593Smuzhiyun if (ACPI_FAILURE(status)) {
395*4882a593Smuzhiyun pr_err("%s: Failed to get device information status=0x%x\n",
396*4882a593Smuzhiyun __func__, status);
397*4882a593Smuzhiyun return retval;
398*4882a593Smuzhiyun }
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun acpi_bus_get_status_handle(handle, ¤t_status);
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun if (current_status && (info->valid & ACPI_VALID_HID) &&
403*4882a593Smuzhiyun (!strcmp(info->hardware_id.string, IBM_HARDWARE_ID1) ||
404*4882a593Smuzhiyun !strcmp(info->hardware_id.string, IBM_HARDWARE_ID2))) {
405*4882a593Smuzhiyun pr_debug("found hardware: %s, handle: %p\n",
406*4882a593Smuzhiyun info->hardware_id.string, handle);
407*4882a593Smuzhiyun *phandle = handle;
408*4882a593Smuzhiyun /* returning non-zero causes the search to stop
409*4882a593Smuzhiyun * and returns this value to the caller of
410*4882a593Smuzhiyun * acpi_walk_namespace, but it also causes some warnings
411*4882a593Smuzhiyun * in the acpi debug code to print...
412*4882a593Smuzhiyun */
413*4882a593Smuzhiyun retval = FOUND_APCI;
414*4882a593Smuzhiyun }
415*4882a593Smuzhiyun kfree(info);
416*4882a593Smuzhiyun return retval;
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun
ibm_acpiphp_init(void)419*4882a593Smuzhiyun static int __init ibm_acpiphp_init(void)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun int retval = 0;
422*4882a593Smuzhiyun acpi_status status;
423*4882a593Smuzhiyun struct acpi_device *device;
424*4882a593Smuzhiyun struct kobject *sysdir = &pci_slots_kset->kobj;
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun pr_debug("%s\n", __func__);
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
429*4882a593Smuzhiyun ACPI_UINT32_MAX, ibm_find_acpi_device, NULL,
430*4882a593Smuzhiyun &ibm_acpi_handle, NULL) != FOUND_APCI) {
431*4882a593Smuzhiyun pr_err("%s: acpi_walk_namespace failed\n", __func__);
432*4882a593Smuzhiyun retval = -ENODEV;
433*4882a593Smuzhiyun goto init_return;
434*4882a593Smuzhiyun }
435*4882a593Smuzhiyun pr_debug("%s: found IBM aPCI device\n", __func__);
436*4882a593Smuzhiyun if (acpi_bus_get_device(ibm_acpi_handle, &device)) {
437*4882a593Smuzhiyun pr_err("%s: acpi_bus_get_device failed\n", __func__);
438*4882a593Smuzhiyun retval = -ENODEV;
439*4882a593Smuzhiyun goto init_return;
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun if (acpiphp_register_attention(&ibm_attention_info)) {
442*4882a593Smuzhiyun retval = -ENODEV;
443*4882a593Smuzhiyun goto init_return;
444*4882a593Smuzhiyun }
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun ibm_note.device = device;
447*4882a593Smuzhiyun status = acpi_install_notify_handler(ibm_acpi_handle,
448*4882a593Smuzhiyun ACPI_DEVICE_NOTIFY, ibm_handle_events,
449*4882a593Smuzhiyun &ibm_note);
450*4882a593Smuzhiyun if (ACPI_FAILURE(status)) {
451*4882a593Smuzhiyun pr_err("%s: Failed to register notification handler\n",
452*4882a593Smuzhiyun __func__);
453*4882a593Smuzhiyun retval = -EBUSY;
454*4882a593Smuzhiyun goto init_cleanup;
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun ibm_apci_table_attr.size = ibm_get_table_from_acpi(NULL);
458*4882a593Smuzhiyun retval = sysfs_create_bin_file(sysdir, &ibm_apci_table_attr);
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun return retval;
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun init_cleanup:
463*4882a593Smuzhiyun acpiphp_unregister_attention(&ibm_attention_info);
464*4882a593Smuzhiyun init_return:
465*4882a593Smuzhiyun return retval;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun
ibm_acpiphp_exit(void)468*4882a593Smuzhiyun static void __exit ibm_acpiphp_exit(void)
469*4882a593Smuzhiyun {
470*4882a593Smuzhiyun acpi_status status;
471*4882a593Smuzhiyun struct kobject *sysdir = &pci_slots_kset->kobj;
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun pr_debug("%s\n", __func__);
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun if (acpiphp_unregister_attention(&ibm_attention_info))
476*4882a593Smuzhiyun pr_err("%s: attention info deregistration failed", __func__);
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun status = acpi_remove_notify_handler(
479*4882a593Smuzhiyun ibm_acpi_handle,
480*4882a593Smuzhiyun ACPI_DEVICE_NOTIFY,
481*4882a593Smuzhiyun ibm_handle_events);
482*4882a593Smuzhiyun if (ACPI_FAILURE(status))
483*4882a593Smuzhiyun pr_err("%s: Notification handler removal failed\n", __func__);
484*4882a593Smuzhiyun /* remove the /sys entries */
485*4882a593Smuzhiyun sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
486*4882a593Smuzhiyun }
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun module_init(ibm_acpiphp_init);
489*4882a593Smuzhiyun module_exit(ibm_acpiphp_exit);
490