xref: /OK3568_Linux_fs/kernel/drivers/pci/hotplug/acpiphp_ibm.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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", &params, &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(&note->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, &current_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