io_acpi_init.c 6.0 KB
Newer Older
J
John Keller 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
/*
 * 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 <asm/sn/types.h>
#include <asm/sn/addrs.h>
#include <asm/sn/pcidev.h>
#include <asm/sn/pcibus_provider_defs.h>
#include <asm/sn/sn_sal.h>
#include "xtalk/hubdev.h"
#include <linux/acpi.h>


/*
 * 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);
	}
}

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
/*
 * 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;
	}
}

J
John Keller 已提交
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
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;

J
John Keller 已提交
226 227 228
	/* SN Altix does not follow the IOSAPIC IRQ routing model */
	acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM;

J
John Keller 已提交
229 230 231 232 233 234
	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));
}