aboutsummaryrefslogtreecommitdiff
path: root/arch/ia64/sn/kernel/io_acpi_init.c
blob: a9dc36901b19852b0b6212ea9efe66303a0a7fac (plain)
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
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
/*
 * 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);
	}
}

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;

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