blob: 2693f2de549c00d22f42dd6f27cdf836a6e76863 [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)
David S. Miller690c8fd2006-06-22 19:12:03 -070018 printk(" (%s)", isa_dev->prom_node->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -070019 else
David S. Miller690c8fd2006-06-22 19:12:03 -070020 printk(" [%s", isa_dev->prom_node->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -070021}
22
David S. Miller690c8fd2006-06-22 19:12:03 -070023static struct linux_prom_registers * __init
24isa_dev_get_resource(struct sparc_isa_device *isa_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -070025{
David S. Miller690c8fd2006-06-22 19:12:03 -070026 struct linux_prom_registers *pregs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070027 unsigned long base, len;
28 int prop_len;
29
David S. Miller690c8fd2006-06-22 19:12:03 -070030 pregs = of_get_property(isa_dev->prom_node, "reg", &prop_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32 /* Only the first one is interesting. */
33 len = pregs[0].reg_size;
34 base = (((unsigned long)pregs[0].which_io << 32) |
35 (unsigned long)pregs[0].phys_addr);
36 base += isa_dev->bus->parent->io_space.start;
37
38 isa_dev->resource.start = base;
39 isa_dev->resource.end = (base + len - 1UL);
40 isa_dev->resource.flags = IORESOURCE_IO;
David S. Miller690c8fd2006-06-22 19:12:03 -070041 isa_dev->resource.name = isa_dev->prom_node->name;
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
43 request_resource(&isa_dev->bus->parent->io_space,
44 &isa_dev->resource);
David S. Miller690c8fd2006-06-22 19:12:03 -070045
46 return pregs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047}
48
49/* I can't believe they didn't put a real INO in the isa device
50 * interrupts property. The whole point of the OBP properties
51 * is to shield the kernel from IRQ routing details.
52 *
53 * The P1275 standard for ISA devices seems to also have been
54 * totally ignored.
55 *
56 * On later systems, an interrupt-map and interrupt-map-mask scheme
57 * akin to EBUS is used.
58 */
59static struct {
60 int obp_irq;
61 int pci_ino;
62} grover_irq_table[] = {
63 { 1, 0x00 }, /* dma, unknown ino at this point */
64 { 2, 0x27 }, /* floppy */
65 { 3, 0x22 }, /* parallel */
66 { 4, 0x2b }, /* serial */
67 { 5, 0x25 }, /* acpi power management */
68
69 { 0, 0x00 } /* end of table */
70};
71
72static int __init isa_dev_get_irq_using_imap(struct sparc_isa_device *isa_dev,
73 struct sparc_isa_bridge *isa_br,
74 int *interrupt,
David S. Miller9c10a582006-06-22 19:31:11 -070075 struct linux_prom_registers *reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -070076{
David S. Miller9c10a582006-06-22 19:31:11 -070077 struct linux_prom_ebus_intmap *imap;
78 struct linux_prom_ebus_intmap *imask;
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 unsigned int hi, lo, irq;
David S. Miller9c10a582006-06-22 19:31:11 -070080 int i, len, n_imap;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
David S. Miller9c10a582006-06-22 19:31:11 -070082 imap = of_get_property(isa_br->prom_node, "interrupt-map", &len);
83 if (!imap)
84 return 0;
85 n_imap = len / sizeof(imap[0]);
86
87 imask = of_get_property(isa_br->prom_node, "interrupt-map-mask", NULL);
88 if (!imask)
89 return 0;
90
91 hi = reg->which_io & imask->phys_hi;
92 lo = reg->phys_addr & imask->phys_lo;
93 irq = *interrupt & imask->interrupt;
94 for (i = 0; i < n_imap; i++) {
95 if ((imap[i].phys_hi == hi) &&
96 (imap[i].phys_lo == lo) &&
97 (imap[i].interrupt == irq)) {
98 *interrupt = imap[i].cinterrupt;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 return 0;
100 }
101 }
102 return -1;
103}
104
105static void __init isa_dev_get_irq(struct sparc_isa_device *isa_dev,
106 struct linux_prom_registers *pregs)
107{
108 int irq_prop;
109
David S. Miller690c8fd2006-06-22 19:12:03 -0700110 irq_prop = of_getintprop_default(isa_dev->prom_node,
111 "interrupts", -1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 if (irq_prop <= 0) {
113 goto no_irq;
114 } else {
115 struct pci_controller_info *pcic;
116 struct pci_pbm_info *pbm;
117 int i;
118
David S. Miller9c10a582006-06-22 19:31:11 -0700119 if (of_find_property(isa_dev->bus->prom_node,
120 "interrupt-map", NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 if (!isa_dev_get_irq_using_imap(isa_dev,
122 isa_dev->bus,
123 &irq_prop,
124 pregs))
125 goto route_irq;
126 }
127
128 for (i = 0; grover_irq_table[i].obp_irq != 0; i++) {
129 if (grover_irq_table[i].obp_irq == irq_prop) {
130 int ino = grover_irq_table[i].pci_ino;
131
132 if (ino == 0)
133 goto no_irq;
134
135 irq_prop = ino;
136 goto route_irq;
137 }
138 }
139 goto no_irq;
140
141route_irq:
142 pbm = isa_dev->bus->parent;
143 pcic = pbm->parent;
144 isa_dev->irq = pcic->irq_build(pbm, NULL, irq_prop);
145 return;
146 }
147
148no_irq:
149 isa_dev->irq = PCI_IRQ_NONE;
150}
151
152static void __init isa_fill_children(struct sparc_isa_device *parent_isa_dev)
153{
David S. Miller690c8fd2006-06-22 19:12:03 -0700154 struct device_node *dp = parent_isa_dev->prom_node->child;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155
David S. Miller690c8fd2006-06-22 19:12:03 -0700156 if (!dp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 return;
158
159 printk(" ->");
David S. Miller690c8fd2006-06-22 19:12:03 -0700160 while (dp) {
161 struct linux_prom_registers *regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 struct sparc_isa_device *isa_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
164 isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL);
165 if (!isa_dev) {
166 fatal_err("cannot allocate child isa_dev");
167 prom_halt();
168 }
169
170 memset(isa_dev, 0, sizeof(*isa_dev));
171
172 /* Link it in to parent. */
173 isa_dev->next = parent_isa_dev->child;
174 parent_isa_dev->child = isa_dev;
175
176 isa_dev->bus = parent_isa_dev->bus;
David S. Miller690c8fd2006-06-22 19:12:03 -0700177 isa_dev->prom_node = dp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178
David S. Miller690c8fd2006-06-22 19:12:03 -0700179 regs = isa_dev_get_resource(isa_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 isa_dev_get_irq(isa_dev, regs);
181
182 report_dev(isa_dev, 1);
183
David S. Miller690c8fd2006-06-22 19:12:03 -0700184 dp = dp->sibling;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 }
186}
187
188static void __init isa_fill_devices(struct sparc_isa_bridge *isa_br)
189{
David S. Miller690c8fd2006-06-22 19:12:03 -0700190 struct device_node *dp = isa_br->prom_node->child;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
David S. Miller690c8fd2006-06-22 19:12:03 -0700192 while (dp) {
193 struct linux_prom_registers *regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 struct sparc_isa_device *isa_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
196 isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL);
197 if (!isa_dev) {
198 fatal_err("cannot allocate isa_dev");
199 prom_halt();
200 }
201
202 memset(isa_dev, 0, sizeof(*isa_dev));
203
204 /* Link it in. */
205 isa_dev->next = NULL;
206 if (isa_br->devices == NULL) {
207 isa_br->devices = isa_dev;
208 } else {
209 struct sparc_isa_device *tmp = isa_br->devices;
210
211 while (tmp->next)
212 tmp = tmp->next;
213
214 tmp->next = isa_dev;
215 }
216
217 isa_dev->bus = isa_br;
David S. Miller690c8fd2006-06-22 19:12:03 -0700218 isa_dev->prom_node = dp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
David S. Miller690c8fd2006-06-22 19:12:03 -0700220 regs = isa_dev_get_resource(isa_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 isa_dev_get_irq(isa_dev, regs);
222
223 report_dev(isa_dev, 0);
224
225 isa_fill_children(isa_dev);
226
227 printk("]");
228
David S. Miller690c8fd2006-06-22 19:12:03 -0700229 dp = dp->sibling;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 }
231}
232
233void __init isa_init(void)
234{
235 struct pci_dev *pdev;
236 unsigned short vendor, device;
237 int index = 0;
238
239 vendor = PCI_VENDOR_ID_AL;
240 device = PCI_DEVICE_ID_AL_M1533;
241
242 pdev = NULL;
243 while ((pdev = pci_get_device(vendor, device, pdev)) != NULL) {
244 struct pcidev_cookie *pdev_cookie;
245 struct pci_pbm_info *pbm;
246 struct sparc_isa_bridge *isa_br;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
248 pdev_cookie = pdev->sysdata;
249 if (!pdev_cookie) {
250 printk("ISA: Warning, ISA bridge ignored due to "
251 "lack of OBP data.\n");
252 continue;
253 }
254 pbm = pdev_cookie->pbm;
255
256 isa_br = kmalloc(sizeof(*isa_br), GFP_KERNEL);
257 if (!isa_br) {
258 fatal_err("cannot allocate sparc_isa_bridge");
259 prom_halt();
260 }
261
262 memset(isa_br, 0, sizeof(*isa_br));
263
264 /* Link it in. */
265 isa_br->next = isa_chain;
266 isa_chain = isa_br;
267
268 isa_br->parent = pbm;
269 isa_br->self = pdev;
270 isa_br->index = index++;
David S. Miller690c8fd2006-06-22 19:12:03 -0700271 isa_br->prom_node = pdev_cookie->prom_node;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 printk("isa%d:", isa_br->index);
274
275 isa_fill_devices(isa_br);
276
277 printk("\n");
278 }
279}