blob: 30862abee611d745fd3182722c2c23e73bda488f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001#include <linux/kernel.h>
2#include <linux/init.h>
3#include <linux/pci.h>
4#include <linux/slab.h>
5#include <asm/oplib.h>
6#include <asm/isa.h>
7
8struct sparc_isa_bridge *isa_chain;
9
10static void __init fatal_err(const char *reason)
11{
12 prom_printf("ISA: fatal error, %s.\n", reason);
13}
14
15static void __init report_dev(struct sparc_isa_device *isa_dev, int child)
16{
17 if (child)
18 printk(" (%s)", isa_dev->prom_name);
19 else
20 printk(" [%s", isa_dev->prom_name);
21}
22
23static void __init isa_dev_get_resource(struct sparc_isa_device *isa_dev,
24 struct linux_prom_registers *pregs,
25 int pregs_size)
26{
27 unsigned long base, len;
28 int prop_len;
29
30 prop_len = prom_getproperty(isa_dev->prom_node, "reg",
31 (char *) pregs, pregs_size);
32
33 if (prop_len <= 0)
34 return;
35
36 /* Only the first one is interesting. */
37 len = pregs[0].reg_size;
38 base = (((unsigned long)pregs[0].which_io << 32) |
39 (unsigned long)pregs[0].phys_addr);
40 base += isa_dev->bus->parent->io_space.start;
41
42 isa_dev->resource.start = base;
43 isa_dev->resource.end = (base + len - 1UL);
44 isa_dev->resource.flags = IORESOURCE_IO;
45 isa_dev->resource.name = isa_dev->prom_name;
46
47 request_resource(&isa_dev->bus->parent->io_space,
48 &isa_dev->resource);
49}
50
51/* I can't believe they didn't put a real INO in the isa device
52 * interrupts property. The whole point of the OBP properties
53 * is to shield the kernel from IRQ routing details.
54 *
55 * The P1275 standard for ISA devices seems to also have been
56 * totally ignored.
57 *
58 * On later systems, an interrupt-map and interrupt-map-mask scheme
59 * akin to EBUS is used.
60 */
61static struct {
62 int obp_irq;
63 int pci_ino;
64} grover_irq_table[] = {
65 { 1, 0x00 }, /* dma, unknown ino at this point */
66 { 2, 0x27 }, /* floppy */
67 { 3, 0x22 }, /* parallel */
68 { 4, 0x2b }, /* serial */
69 { 5, 0x25 }, /* acpi power management */
70
71 { 0, 0x00 } /* end of table */
72};
73
74static int __init isa_dev_get_irq_using_imap(struct sparc_isa_device *isa_dev,
75 struct sparc_isa_bridge *isa_br,
76 int *interrupt,
77 struct linux_prom_registers *pregs)
78{
79 unsigned int hi, lo, irq;
80 int i;
81
82 hi = pregs->which_io & isa_br->isa_intmask.phys_hi;
83 lo = pregs->phys_addr & isa_br->isa_intmask.phys_lo;
84 irq = *interrupt & isa_br->isa_intmask.interrupt;
85 for (i = 0; i < isa_br->num_isa_intmap; i++) {
86 if ((isa_br->isa_intmap[i].phys_hi == hi) &&
87 (isa_br->isa_intmap[i].phys_lo == lo) &&
88 (isa_br->isa_intmap[i].interrupt == irq)) {
89 *interrupt = isa_br->isa_intmap[i].cinterrupt;
90 return 0;
91 }
92 }
93 return -1;
94}
95
96static void __init isa_dev_get_irq(struct sparc_isa_device *isa_dev,
97 struct linux_prom_registers *pregs)
98{
99 int irq_prop;
100
101 irq_prop = prom_getintdefault(isa_dev->prom_node,
102 "interrupts", -1);
103 if (irq_prop <= 0) {
104 goto no_irq;
105 } else {
106 struct pci_controller_info *pcic;
107 struct pci_pbm_info *pbm;
108 int i;
109
110 if (isa_dev->bus->num_isa_intmap) {
111 if (!isa_dev_get_irq_using_imap(isa_dev,
112 isa_dev->bus,
113 &irq_prop,
114 pregs))
115 goto route_irq;
116 }
117
118 for (i = 0; grover_irq_table[i].obp_irq != 0; i++) {
119 if (grover_irq_table[i].obp_irq == irq_prop) {
120 int ino = grover_irq_table[i].pci_ino;
121
122 if (ino == 0)
123 goto no_irq;
124
125 irq_prop = ino;
126 goto route_irq;
127 }
128 }
129 goto no_irq;
130
131route_irq:
132 pbm = isa_dev->bus->parent;
133 pcic = pbm->parent;
134 isa_dev->irq = pcic->irq_build(pbm, NULL, irq_prop);
135 return;
136 }
137
138no_irq:
139 isa_dev->irq = PCI_IRQ_NONE;
140}
141
142static void __init isa_fill_children(struct sparc_isa_device *parent_isa_dev)
143{
144 int node = prom_getchild(parent_isa_dev->prom_node);
145
146 if (node == 0)
147 return;
148
149 printk(" ->");
150 while (node != 0) {
151 struct linux_prom_registers regs[PROMREG_MAX];
152 struct sparc_isa_device *isa_dev;
153 int prop_len;
154
155 isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL);
156 if (!isa_dev) {
157 fatal_err("cannot allocate child isa_dev");
158 prom_halt();
159 }
160
161 memset(isa_dev, 0, sizeof(*isa_dev));
162
163 /* Link it in to parent. */
164 isa_dev->next = parent_isa_dev->child;
165 parent_isa_dev->child = isa_dev;
166
167 isa_dev->bus = parent_isa_dev->bus;
168 isa_dev->prom_node = node;
169 prop_len = prom_getproperty(node, "name",
170 (char *) isa_dev->prom_name,
171 sizeof(isa_dev->prom_name));
172 if (prop_len <= 0) {
173 fatal_err("cannot get child isa_dev OBP node name");
174 prom_halt();
175 }
176
177 prop_len = prom_getproperty(node, "compatible",
178 (char *) isa_dev->compatible,
179 sizeof(isa_dev->compatible));
180
181 /* Not having this is OK. */
182 if (prop_len <= 0)
183 isa_dev->compatible[0] = '\0';
184
185 isa_dev_get_resource(isa_dev, regs, sizeof(regs));
186 isa_dev_get_irq(isa_dev, regs);
187
188 report_dev(isa_dev, 1);
189
190 node = prom_getsibling(node);
191 }
192}
193
194static void __init isa_fill_devices(struct sparc_isa_bridge *isa_br)
195{
196 int node = prom_getchild(isa_br->prom_node);
197
198 while (node != 0) {
199 struct linux_prom_registers regs[PROMREG_MAX];
200 struct sparc_isa_device *isa_dev;
201 int prop_len;
202
203 isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL);
204 if (!isa_dev) {
205 fatal_err("cannot allocate isa_dev");
206 prom_halt();
207 }
208
209 memset(isa_dev, 0, sizeof(*isa_dev));
210
211 /* Link it in. */
212 isa_dev->next = NULL;
213 if (isa_br->devices == NULL) {
214 isa_br->devices = isa_dev;
215 } else {
216 struct sparc_isa_device *tmp = isa_br->devices;
217
218 while (tmp->next)
219 tmp = tmp->next;
220
221 tmp->next = isa_dev;
222 }
223
224 isa_dev->bus = isa_br;
225 isa_dev->prom_node = node;
226 prop_len = prom_getproperty(node, "name",
227 (char *) isa_dev->prom_name,
228 sizeof(isa_dev->prom_name));
229 if (prop_len <= 0) {
230 fatal_err("cannot get isa_dev OBP node name");
231 prom_halt();
232 }
233
234 prop_len = prom_getproperty(node, "compatible",
235 (char *) isa_dev->compatible,
236 sizeof(isa_dev->compatible));
237
238 /* Not having this is OK. */
239 if (prop_len <= 0)
240 isa_dev->compatible[0] = '\0';
241
242 isa_dev_get_resource(isa_dev, regs, sizeof(regs));
243 isa_dev_get_irq(isa_dev, regs);
244
245 report_dev(isa_dev, 0);
246
247 isa_fill_children(isa_dev);
248
249 printk("]");
250
251 node = prom_getsibling(node);
252 }
253}
254
255void __init isa_init(void)
256{
257 struct pci_dev *pdev;
258 unsigned short vendor, device;
259 int index = 0;
260
261 vendor = PCI_VENDOR_ID_AL;
262 device = PCI_DEVICE_ID_AL_M1533;
263
264 pdev = NULL;
265 while ((pdev = pci_get_device(vendor, device, pdev)) != NULL) {
266 struct pcidev_cookie *pdev_cookie;
267 struct pci_pbm_info *pbm;
268 struct sparc_isa_bridge *isa_br;
269 int prop_len;
270
271 pdev_cookie = pdev->sysdata;
272 if (!pdev_cookie) {
273 printk("ISA: Warning, ISA bridge ignored due to "
274 "lack of OBP data.\n");
275 continue;
276 }
277 pbm = pdev_cookie->pbm;
278
279 isa_br = kmalloc(sizeof(*isa_br), GFP_KERNEL);
280 if (!isa_br) {
281 fatal_err("cannot allocate sparc_isa_bridge");
282 prom_halt();
283 }
284
285 memset(isa_br, 0, sizeof(*isa_br));
286
287 /* Link it in. */
288 isa_br->next = isa_chain;
289 isa_chain = isa_br;
290
291 isa_br->parent = pbm;
292 isa_br->self = pdev;
293 isa_br->index = index++;
294 isa_br->prom_node = pdev_cookie->prom_node;
295 strncpy(isa_br->prom_name, pdev_cookie->prom_name,
296 sizeof(isa_br->prom_name));
297
298 prop_len = prom_getproperty(isa_br->prom_node,
299 "ranges",
300 (char *) isa_br->isa_ranges,
301 sizeof(isa_br->isa_ranges));
302 if (prop_len <= 0)
303 isa_br->num_isa_ranges = 0;
304 else
305 isa_br->num_isa_ranges =
306 (prop_len / sizeof(struct linux_prom_isa_ranges));
307
308 prop_len = prom_getproperty(isa_br->prom_node,
309 "interrupt-map",
310 (char *) isa_br->isa_intmap,
311 sizeof(isa_br->isa_intmap));
312 if (prop_len <= 0)
313 isa_br->num_isa_intmap = 0;
314 else
315 isa_br->num_isa_intmap =
316 (prop_len / sizeof(struct linux_prom_isa_intmap));
317
318 prop_len = prom_getproperty(isa_br->prom_node,
319 "interrupt-map-mask",
320 (char *) &(isa_br->isa_intmask),
321 sizeof(isa_br->isa_intmask));
322
323 printk("isa%d:", isa_br->index);
324
325 isa_fill_devices(isa_br);
326
327 printk("\n");
328 }
329}