aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64/sn/kernel/io_acpi_init.c
diff options
context:
space:
mode:
authorJohn Keller <jpk@sgi.com>2007-01-30 01:17:37 -0500
committerLen Brown <len.brown@intel.com>2007-02-02 22:14:35 -0500
commit6f09a9250a5d76c0765cd51a33e0a042e9761cfc (patch)
tree764db1dcd196fc1c272a8c6930924a5bc60623bc /arch/ia64/sn/kernel/io_acpi_init.c
parent647fb47dfabeffd2f1706013ebf5cfc92b70d273 (diff)
Altix: ACPI SSDT PCI device support
Add SN platform support for running with an ACPI capable PROM that defines PCI devices in SSDT tables. There is a SSDT table for every occupied slot on a root bus, containing info for every PPB and/or device on the bus. The SSDTs will be dynamically loaded/unloaded at hotplug enable/disable. Platform specific information that is currently passed via a SAL call, will now be passed via the Vendor resource in the ACPI Device object(s) defined in each SSDT. Signed-off-by: John Keller <jpk@sgi.com> Cc: Greg KH <greg@kroah.com> Cc: "Luck, Tony" <tony.luck@intel.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'arch/ia64/sn/kernel/io_acpi_init.c')
-rw-r--r--arch/ia64/sn/kernel/io_acpi_init.c314
1 files changed, 290 insertions, 24 deletions
diff --git a/arch/ia64/sn/kernel/io_acpi_init.c b/arch/ia64/sn/kernel/io_acpi_init.c
index cb96b4ea7df..8c331ca6e5c 100644
--- a/arch/ia64/sn/kernel/io_acpi_init.c
+++ b/arch/ia64/sn/kernel/io_acpi_init.c
@@ -13,6 +13,7 @@
13#include <asm/sn/sn_sal.h> 13#include <asm/sn/sn_sal.h>
14#include "xtalk/hubdev.h" 14#include "xtalk/hubdev.h"
15#include <linux/acpi.h> 15#include <linux/acpi.h>
16#include <acpi/acnamesp.h>
16 17
17 18
18/* 19/*
@@ -31,6 +32,12 @@ struct acpi_vendor_uuid sn_uuid = {
31 0xa2, 0x7c, 0x08, 0x00, 0x69, 0x13, 0xea, 0x51 }, 32 0xa2, 0x7c, 0x08, 0x00, 0x69, 0x13, 0xea, 0x51 },
32}; 33};
33 34
35struct sn_pcidev_match {
36 u8 bus;
37 unsigned int devfn;
38 acpi_handle handle;
39};
40
34/* 41/*
35 * Perform the early IO init in PROM. 42 * Perform the early IO init in PROM.
36 */ 43 */
@@ -119,9 +126,11 @@ sn_get_bussoft_ptr(struct pci_bus *bus)
119 status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS, 126 status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS,
120 &sn_uuid, &buffer); 127 &sn_uuid, &buffer);
121 if (ACPI_FAILURE(status)) { 128 if (ACPI_FAILURE(status)) {
122 printk(KERN_ERR "get_acpi_pcibus_ptr: " 129 printk(KERN_ERR "%s: "
123 "get_acpi_bussoft_info() failed: %d\n", 130 "acpi_get_vendor_resource() failed (0x%x) for: ",
124 status); 131 __FUNCTION__, status);
132 acpi_ns_print_node_pathname(handle, NULL);
133 printk("\n");
125 return NULL; 134 return NULL;
126 } 135 }
127 resource = buffer.pointer; 136 resource = buffer.pointer;
@@ -130,8 +139,8 @@ sn_get_bussoft_ptr(struct pci_bus *bus)
130 if ((vendor->byte_length - sizeof(struct acpi_vendor_uuid)) != 139 if ((vendor->byte_length - sizeof(struct acpi_vendor_uuid)) !=
131 sizeof(struct pcibus_bussoft *)) { 140 sizeof(struct pcibus_bussoft *)) {
132 printk(KERN_ERR 141 printk(KERN_ERR
133 "get_acpi_bussoft_ptr: Invalid vendor data " 142 "%s: Invalid vendor data length %d\n",
134 "length %d\n", vendor->byte_length); 143 __FUNCTION__, vendor->byte_length);
135 kfree(buffer.pointer); 144 kfree(buffer.pointer);
136 return NULL; 145 return NULL;
137 } 146 }
@@ -143,34 +152,254 @@ sn_get_bussoft_ptr(struct pci_bus *bus)
143} 152}
144 153
145/* 154/*
146 * sn_acpi_bus_fixup 155 * sn_extract_device_info - Extract the pcidev_info and the sn_irq_info
156 * pointers from the vendor resource using the
157 * provided acpi handle, and copy the structures
158 * into the argument buffers.
147 */ 159 */
148void 160static int
149sn_acpi_bus_fixup(struct pci_bus *bus) 161sn_extract_device_info(acpi_handle handle, struct pcidev_info **pcidev_info,
162 struct sn_irq_info **sn_irq_info)
150{ 163{
151 struct pci_dev *pci_dev = NULL; 164 u64 addr;
152 struct pcibus_bussoft *prom_bussoft_ptr; 165 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
153 extern void sn_common_bus_fixup(struct pci_bus *, 166 struct sn_irq_info *irq_info, *irq_info_prom;
154 struct pcibus_bussoft *); 167 struct pcidev_info *pcidev_ptr, *pcidev_prom_ptr;
168 struct acpi_resource *resource;
169 int ret = 0;
170 acpi_status status;
171 struct acpi_resource_vendor_typed *vendor;
155 172
156 if (!bus->parent) { /* If root bus */ 173 /*
157 prom_bussoft_ptr = sn_get_bussoft_ptr(bus); 174 * The pointer to this device's pcidev_info structure in
158 if (prom_bussoft_ptr == NULL) { 175 * the PROM, is in the vendor resource.
176 */
177 status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS,
178 &sn_uuid, &buffer);
179 if (ACPI_FAILURE(status)) {
180 printk(KERN_ERR
181 "%s: acpi_get_vendor_resource() failed (0x%x) for: ",
182 __FUNCTION__, status);
183 acpi_ns_print_node_pathname(handle, NULL);
184 printk("\n");
185 return 1;
186 }
187
188 resource = buffer.pointer;
189 vendor = &resource->data.vendor_typed;
190 if ((vendor->byte_length - sizeof(struct acpi_vendor_uuid)) !=
191 sizeof(struct pci_devdev_info *)) {
192 printk(KERN_ERR
193 "%s: Invalid vendor data length: %d for: ",
194 __FUNCTION__, vendor->byte_length);
195 acpi_ns_print_node_pathname(handle, NULL);
196 printk("\n");
197 ret = 1;
198 goto exit;
199 }
200
201 pcidev_ptr = kzalloc(sizeof(struct pcidev_info), GFP_KERNEL);
202 if (!pcidev_ptr)
203 panic("%s: Unable to alloc memory for pcidev_info", __FUNCTION__);
204
205 memcpy(&addr, vendor->byte_data, sizeof(struct pcidev_info *));
206 pcidev_prom_ptr = __va(addr);
207 memcpy(pcidev_ptr, pcidev_prom_ptr, sizeof(struct pcidev_info));
208
209 /* Get the IRQ info */
210 irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
211 if (!irq_info)
212 panic("%s: Unable to alloc memory for sn_irq_info", __FUNCTION__);
213
214 if (pcidev_ptr->pdi_sn_irq_info) {
215 irq_info_prom = __va(pcidev_ptr->pdi_sn_irq_info);
216 memcpy(irq_info, irq_info_prom, sizeof(struct sn_irq_info));
217 }
218
219 *pcidev_info = pcidev_ptr;
220 *sn_irq_info = irq_info;
221
222exit:
223 kfree(buffer.pointer);
224 return ret;
225}
226
227static unsigned int
228get_host_devfn(acpi_handle device_handle, acpi_handle rootbus_handle)
229{
230 unsigned long adr;
231 acpi_handle child;
232 unsigned int devfn;
233 int function;
234 acpi_handle parent;
235 int slot;
236 acpi_status status;
237
238 /*
239 * Do an upward search to find the root bus device, and
240 * obtain the host devfn from the previous child device.
241 */
242 child = device_handle;
243 while (child) {
244 status = acpi_get_parent(child, &parent);
245 if (ACPI_FAILURE(status)) {
246 printk(KERN_ERR "%s: acpi_get_parent() failed "
247 "(0x%x) for: ", __FUNCTION__, status);
248 acpi_ns_print_node_pathname(child, NULL);
249 printk("\n");
250 panic("%s: Unable to find host devfn\n", __FUNCTION__);
251 }
252 if (parent == rootbus_handle)
253 break;
254 child = parent;
255 }
256 if (!child) {
257 printk(KERN_ERR "%s: Unable to find root bus for: ",
258 __FUNCTION__);
259 acpi_ns_print_node_pathname(device_handle, NULL);
260 printk("\n");
261 BUG();
262 }
263
264 status = acpi_evaluate_integer(child, METHOD_NAME__ADR, NULL, &adr);
265 if (ACPI_FAILURE(status)) {
266 printk(KERN_ERR "%s: Unable to get _ADR (0x%x) for: ",
267 __FUNCTION__, status);
268 acpi_ns_print_node_pathname(child, NULL);
269 printk("\n");
270 panic("%s: Unable to find host devfn\n", __FUNCTION__);
271 }
272
273 slot = (adr >> 16) & 0xffff;
274 function = adr & 0xffff;
275 devfn = PCI_DEVFN(slot, function);
276 return devfn;
277}
278
279/*
280 * find_matching_device - Callback routine to find the ACPI device
281 * that matches up with our pci_dev device.
282 * Matching is done on bus number and devfn.
283 * To find the bus number for a particular
284 * ACPI device, we must look at the _BBN method
285 * of its parent.
286 */
287static acpi_status
288find_matching_device(acpi_handle handle, u32 lvl, void *context, void **rv)
289{
290 unsigned long bbn = -1;
291 unsigned long adr;
292 acpi_handle parent = NULL;
293 acpi_status status;
294 unsigned int devfn;
295 int function;
296 int slot;
297 struct sn_pcidev_match *info = context;
298
299 status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL,
300 &adr);
301 if (ACPI_SUCCESS(status)) {
302 status = acpi_get_parent(handle, &parent);
303 if (ACPI_FAILURE(status)) {
159 printk(KERN_ERR 304 printk(KERN_ERR
160 "sn_pci_fixup_bus: 0x%04x:0x%02x Unable to " 305 "%s: acpi_get_parent() failed (0x%x) for: ",
161 "obtain prom_bussoft_ptr\n", 306 __FUNCTION__, status);
162 pci_domain_nr(bus), bus->number); 307 acpi_ns_print_node_pathname(handle, NULL);
163 return; 308 printk("\n");
309 return AE_OK;
310 }
311 status = acpi_evaluate_integer(parent, METHOD_NAME__BBN,
312 NULL, &bbn);
313 if (ACPI_FAILURE(status)) {
314 printk(KERN_ERR
315 "%s: Failed to find _BBN in parent of: ",
316 __FUNCTION__);
317 acpi_ns_print_node_pathname(handle, NULL);
318 printk("\n");
319 return AE_OK;
320 }
321
322 slot = (adr >> 16) & 0xffff;
323 function = adr & 0xffff;
324 devfn = PCI_DEVFN(slot, function);
325 if ((info->devfn == devfn) && (info->bus == bbn)) {
326 /* We have a match! */
327 info->handle = handle;
328 return 1;
164 } 329 }
165 sn_common_bus_fixup(bus, prom_bussoft_ptr);
166 } 330 }
167 list_for_each_entry(pci_dev, &bus->devices, bus_list) { 331 return AE_OK;
168 sn_pci_fixup_slot(pci_dev); 332}
333
334/*
335 * sn_acpi_get_pcidev_info - Search ACPI namespace for the acpi
336 * device matching the specified pci_dev,
337 * and return the pcidev info and irq info.
338 */
339int
340sn_acpi_get_pcidev_info(struct pci_dev *dev, struct pcidev_info **pcidev_info,
341 struct sn_irq_info **sn_irq_info)
342{
343 unsigned int host_devfn;
344 struct sn_pcidev_match pcidev_match;
345 acpi_handle rootbus_handle;
346 unsigned long segment;
347 acpi_status status;
348
349 rootbus_handle = PCI_CONTROLLER(dev)->acpi_handle;
350 status = acpi_evaluate_integer(rootbus_handle, METHOD_NAME__SEG, NULL,
351 &segment);
352 if (ACPI_SUCCESS(status)) {
353 if (segment != pci_domain_nr(dev)) {
354 printk(KERN_ERR
355 "%s: Segment number mismatch, 0x%lx vs 0x%x for: ",
356 __FUNCTION__, segment, pci_domain_nr(dev));
357 acpi_ns_print_node_pathname(rootbus_handle, NULL);
358 printk("\n");
359 return 1;
360 }
361 } else {
362 printk(KERN_ERR "%s: Unable to get __SEG from: ",
363 __FUNCTION__);
364 acpi_ns_print_node_pathname(rootbus_handle, NULL);
365 printk("\n");
366 return 1;
367 }
368
369 /*
370 * We want to search all devices in this segment/domain
371 * of the ACPI namespace for the matching ACPI device,
372 * which holds the pcidev_info pointer in its vendor resource.
373 */
374 pcidev_match.bus = dev->bus->number;
375 pcidev_match.devfn = dev->devfn;
376 pcidev_match.handle = NULL;
377
378 acpi_walk_namespace(ACPI_TYPE_DEVICE, rootbus_handle, ACPI_UINT32_MAX,
379 find_matching_device, &pcidev_match, NULL);
380
381 if (!pcidev_match.handle) {
382 printk(KERN_ERR
383 "%s: Could not find matching ACPI device for %s.\n",
384 __FUNCTION__, pci_name(dev));
385 return 1;
169 } 386 }
387
388 if (sn_extract_device_info(pcidev_match.handle, pcidev_info, sn_irq_info))
389 return 1;
390
391 /* Build up the pcidev_info.pdi_slot_host_handle */
392 host_devfn = get_host_devfn(pcidev_match.handle, rootbus_handle);
393 (*pcidev_info)->pdi_slot_host_handle =
394 ((unsigned long) pci_domain_nr(dev) << 40) |
395 /* bus == 0 */
396 host_devfn;
397 return 0;
170} 398}
171 399
172/* 400/*
173 * sn_acpi_slot_fixup - Perform any SN specific slot fixup. 401 * sn_acpi_slot_fixup - Obtain the pcidev_info and sn_irq_info.
402 * Perform any SN specific slot fixup.
174 * At present there does not appear to be 403 * At present there does not appear to be
175 * any generic way to handle a ROM image 404 * any generic way to handle a ROM image
176 * that has been shadowed by the PROM, so 405 * that has been shadowed by the PROM, so
@@ -179,11 +408,18 @@ sn_acpi_bus_fixup(struct pci_bus *bus)
179 */ 408 */
180 409
181void 410void
182sn_acpi_slot_fixup(struct pci_dev *dev, struct pcidev_info *pcidev_info) 411sn_acpi_slot_fixup(struct pci_dev *dev)
183{ 412{
184 void __iomem *addr; 413 void __iomem *addr;
414 struct pcidev_info *pcidev_info = NULL;
415 struct sn_irq_info *sn_irq_info = NULL;
185 size_t size; 416 size_t size;
186 417
418 if (sn_acpi_get_pcidev_info(dev, &pcidev_info, &sn_irq_info)) {
419 panic("%s: Failure obtaining pcidev_info for %s\n",
420 __FUNCTION__, pci_name(dev));
421 }
422
187 if (pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE]) { 423 if (pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE]) {
188 /* 424 /*
189 * A valid ROM image exists and has been shadowed by the 425 * A valid ROM image exists and has been shadowed by the
@@ -200,8 +436,11 @@ sn_acpi_slot_fixup(struct pci_dev *dev, struct pcidev_info *pcidev_info)
200 (unsigned long) addr + size; 436 (unsigned long) addr + size;
201 dev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_BIOS_COPY; 437 dev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_BIOS_COPY;
202 } 438 }
439 sn_pci_fixup_slot(dev, pcidev_info, sn_irq_info);
203} 440}
204 441
442EXPORT_SYMBOL(sn_acpi_slot_fixup);
443
205static struct acpi_driver acpi_sn_hubdev_driver = { 444static struct acpi_driver acpi_sn_hubdev_driver = {
206 .name = "SGI HUBDEV Driver", 445 .name = "SGI HUBDEV Driver",
207 .ids = "SGIHUB,SGITIO", 446 .ids = "SGIHUB,SGITIO",
@@ -212,6 +451,33 @@ static struct acpi_driver acpi_sn_hubdev_driver = {
212 451
213 452
214/* 453/*
454 * sn_acpi_bus_fixup - Perform SN specific setup of software structs
455 * (pcibus_bussoft, pcidev_info) and hardware
456 * registers, for the specified bus and devices under it.
457 */
458void
459sn_acpi_bus_fixup(struct pci_bus *bus)
460{
461 struct pci_dev *pci_dev = NULL;
462 struct pcibus_bussoft *prom_bussoft_ptr;
463
464 if (!bus->parent) { /* If root bus */
465 prom_bussoft_ptr = sn_get_bussoft_ptr(bus);
466 if (prom_bussoft_ptr == NULL) {
467 printk(KERN_ERR
468 "%s: 0x%04x:0x%02x Unable to "
469 "obtain prom_bussoft_ptr\n",
470 __FUNCTION__, pci_domain_nr(bus), bus->number);
471 return;
472 }
473 sn_common_bus_fixup(bus, prom_bussoft_ptr);
474 }
475 list_for_each_entry(pci_dev, &bus->devices, bus_list) {
476 sn_acpi_slot_fixup(pci_dev);
477 }
478}
479
480/*
215 * sn_io_acpi_init - PROM has ACPI support for IO, defining at a minimum the 481 * sn_io_acpi_init - PROM has ACPI support for IO, defining at a minimum the
216 * nodes and root buses in the DSDT. As a result, bus scanning 482 * nodes and root buses in the DSDT. As a result, bus scanning
217 * will be initiated by the Linux ACPI code. 483 * will be initiated by the Linux ACPI code.