/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 2006 Silicon Graphics, Inc. All rights reserved. */ #include #include #include #include #include #include "xtalk/hubdev.h" #include /* * The code in this file will only be executed when running with * a PROM that has ACPI IO support. (i.e., SN_ACPI_BASE_SUPPORT() == 1) */ /* * This value must match the UUID the PROM uses * (io/acpi/defblk.c) when building a vendor descriptor. */ struct acpi_vendor_uuid sn_uuid = { .subtype = 0, .data = { 0x2c, 0xc6, 0xa6, 0xfe, 0x9c, 0x44, 0xda, 0x11, 0xa2, 0x7c, 0x08, 0x00, 0x69, 0x13, 0xea, 0x51 }, }; /* * Perform the early IO init in PROM. */ static s64 sal_ioif_init(u64 *result) { struct ia64_sal_retval isrv = {0,0,0,0}; SAL_CALL_NOLOCK(isrv, SN_SAL_IOIF_INIT, 0, 0, 0, 0, 0, 0, 0); *result = isrv.v0; return isrv.status; } /* * sn_hubdev_add - The 'add' function of the acpi_sn_hubdev_driver. * Called for every "SGIHUB" or "SGITIO" device defined * in the ACPI namespace. */ static int __init sn_hubdev_add(struct acpi_device *device) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; u64 addr; struct hubdev_info *hubdev; struct hubdev_info *hubdev_ptr; int i; u64 nasid; struct acpi_resource *resource; int ret = 0; acpi_status status; struct acpi_resource_vendor_typed *vendor; extern void sn_common_hubdev_init(struct hubdev_info *); status = acpi_get_vendor_resource(device->handle, METHOD_NAME__CRS, &sn_uuid, &buffer); if (ACPI_FAILURE(status)) { printk(KERN_ERR "sn_hubdev_add: acpi_get_vendor_resource() failed: %d\n", status); return 1; } resource = buffer.pointer; vendor = &resource->data.vendor_typed; if ((vendor->byte_length - sizeof(struct acpi_vendor_uuid)) != sizeof(struct hubdev_info *)) { printk(KERN_ERR "sn_hubdev_add: Invalid vendor data length: %d\n", vendor->byte_length); ret = 1; goto exit; } memcpy(&addr, vendor->byte_data, sizeof(struct hubdev_info *)); hubdev_ptr = __va((struct hubdev_info *) addr); nasid = hubdev_ptr->hdi_nasid; i = nasid_to_cnodeid(nasid); hubdev = (struct hubdev_info *)(NODEPDA(i)->pdinfo); *hubdev = *hubdev_ptr; sn_common_hubdev_init(hubdev); exit: kfree(buffer.pointer); return ret; } /* * sn_get_bussoft_ptr() - The pcibus_bussoft pointer is found in * the ACPI Vendor resource for this bus. */ static struct pcibus_bussoft * sn_get_bussoft_ptr(struct pci_bus *bus) { u64 addr; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_handle handle; struct pcibus_bussoft *prom_bussoft_ptr; struct acpi_resource *resource; acpi_status status; struct acpi_resource_vendor_typed *vendor; handle = PCI_CONTROLLER(bus)->acpi_handle; status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS, &sn_uuid, &buffer); if (ACPI_FAILURE(status)) { printk(KERN_ERR "get_acpi_pcibus_ptr: " "get_acpi_bussoft_info() failed: %d\n", status); return NULL; } resource = buffer.pointer; vendor = &resource->data.vendor_typed; if ((vendor->byte_length - sizeof(struct acpi_vendor_uuid)) != sizeof(struct pcibus_bussoft *)) { printk(KERN_ERR "get_acpi_bussoft_ptr: Invalid vendor data " "length %d\n", vendor->byte_length); kfree(buffer.pointer); return NULL; } memcpy(&addr, vendor->byte_data, sizeof(struct pcibus_bussoft *)); prom_bussoft_ptr = __va((struct pcibus_bussoft *) addr); kfree(buffer.pointer); return prom_bussoft_ptr; } /* * sn_acpi_bus_fixup */ void sn_acpi_bus_fixup(struct pci_bus *bus) { struct pci_dev *pci_dev = NULL; struct pcibus_bussoft *prom_bussoft_ptr; extern void sn_common_bus_fixup(struct pci_bus *, struct pcibus_bussoft *); if (!bus->parent) { /* If root bus */ prom_bussoft_ptr = sn_get_bussoft_ptr(bus); if (prom_bussoft_ptr == NULL) { printk(KERN_ERR "sn_pci_fixup_bus: 0x%04x:0x%02x Unable to " "obtain prom_bussoft_ptr\n", pci_domain_nr(bus), bus->number); return; } sn_common_bus_fixup(bus, prom_bussoft_ptr); } list_for_each_entry(pci_dev, &bus->devices, bus_list) { sn_pci_fixup_slot(pci_dev); } } /* * sn_acpi_slot_fixup - Perform any SN specific slot fixup. * At present there does not appear to be * any generic way to handle a ROM image * that has been shadowed by the PROM, so * we pass a pointer to it within the * pcidev_info structure. */ void sn_acpi_slot_fixup(struct pci_dev *dev, struct pcidev_info *pcidev_info) { void __iomem *addr; size_t size; if (pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE]) { /* * A valid ROM image exists and has been shadowed by the * PROM. Setup the pci_dev ROM resource to point to * the shadowed copy. */ size = dev->resource[PCI_ROM_RESOURCE].end - dev->resource[PCI_ROM_RESOURCE].start; addr = ioremap(pcidev_info->pdi_pio_mapped_addr[PCI_ROM_RESOURCE], size); dev->resource[PCI_ROM_RESOURCE].start = (unsigned long) addr; dev->resource[PCI_ROM_RESOURCE].end = (unsigned long) addr + size; dev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_BIOS_COPY; } } static struct acpi_driver acpi_sn_hubdev_driver = { .name = "SGI HUBDEV Driver", .ids = "SGIHUB,SGITIO", .ops = { .add = sn_hubdev_add, }, }; /* * sn_io_acpi_init - PROM has ACPI support for IO, defining at a minimum the * nodes and root buses in the DSDT. As a result, bus scanning * will be initiated by the Linux ACPI code. */ void __init sn_io_acpi_init(void) { u64 result; s64 status; /* SN Altix does not follow the IOSAPIC IRQ routing model */ acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM; acpi_bus_register_driver(&acpi_sn_hubdev_driver); status = sal_ioif_init(&result); if (status || result) panic("sal_ioif_init failed: [%lx] %s\n", status, ia64_sal_strerror(status)); }