blob: 8c8c5a491ad6dbde47e81cc7e0ed16d63b838b5e [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,
75 struct linux_prom_registers *pregs)
76{
77 unsigned int hi, lo, irq;
78 int i;
79
80 hi = pregs->which_io & isa_br->isa_intmask.phys_hi;
81 lo = pregs->phys_addr & isa_br->isa_intmask.phys_lo;
82 irq = *interrupt & isa_br->isa_intmask.interrupt;
83 for (i = 0; i < isa_br->num_isa_intmap; i++) {
84 if ((isa_br->isa_intmap[i].phys_hi == hi) &&
85 (isa_br->isa_intmap[i].phys_lo == lo) &&
86 (isa_br->isa_intmap[i].interrupt == irq)) {
87 *interrupt = isa_br->isa_intmap[i].cinterrupt;
88 return 0;
89 }
90 }
91 return -1;
92}
93
94static void __init isa_dev_get_irq(struct sparc_isa_device *isa_dev,
95 struct linux_prom_registers *pregs)
96{
97 int irq_prop;
98
David S. Miller690c8fd2006-06-22 19:12:03 -070099 irq_prop = of_getintprop_default(isa_dev->prom_node,
100 "interrupts", -1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 if (irq_prop <= 0) {
102 goto no_irq;
103 } else {
104 struct pci_controller_info *pcic;
105 struct pci_pbm_info *pbm;
106 int i;
107
108 if (isa_dev->bus->num_isa_intmap) {
109 if (!isa_dev_get_irq_using_imap(isa_dev,
110 isa_dev->bus,
111 &irq_prop,
112 pregs))
113 goto route_irq;
114 }
115
116 for (i = 0; grover_irq_table[i].obp_irq != 0; i++) {
117 if (grover_irq_table[i].obp_irq == irq_prop) {
118 int ino = grover_irq_table[i].pci_ino;
119
120 if (ino == 0)
121 goto no_irq;
122
123 irq_prop = ino;
124 goto route_irq;
125 }
126 }
127 goto no_irq;
128
129route_irq:
130 pbm = isa_dev->bus->parent;
131 pcic = pbm->parent;
132 isa_dev->irq = pcic->irq_build(pbm, NULL, irq_prop);
133 return;
134 }
135
136no_irq:
137 isa_dev->irq = PCI_IRQ_NONE;
138}
139
140static void __init isa_fill_children(struct sparc_isa_device *parent_isa_dev)
141{
David S. Miller690c8fd2006-06-22 19:12:03 -0700142 struct device_node *dp = parent_isa_dev->prom_node->child;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
David S. Miller690c8fd2006-06-22 19:12:03 -0700144 if (!dp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 return;
146
147 printk(" ->");
David S. Miller690c8fd2006-06-22 19:12:03 -0700148 while (dp) {
149 struct linux_prom_registers *regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 struct sparc_isa_device *isa_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151
152 isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL);
153 if (!isa_dev) {
154 fatal_err("cannot allocate child isa_dev");
155 prom_halt();
156 }
157
158 memset(isa_dev, 0, sizeof(*isa_dev));
159
160 /* Link it in to parent. */
161 isa_dev->next = parent_isa_dev->child;
162 parent_isa_dev->child = isa_dev;
163
164 isa_dev->bus = parent_isa_dev->bus;
David S. Miller690c8fd2006-06-22 19:12:03 -0700165 isa_dev->prom_node = dp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166
David S. Miller690c8fd2006-06-22 19:12:03 -0700167 regs = isa_dev_get_resource(isa_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 isa_dev_get_irq(isa_dev, regs);
169
170 report_dev(isa_dev, 1);
171
David S. Miller690c8fd2006-06-22 19:12:03 -0700172 dp = dp->sibling;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 }
174}
175
176static void __init isa_fill_devices(struct sparc_isa_bridge *isa_br)
177{
David S. Miller690c8fd2006-06-22 19:12:03 -0700178 struct device_node *dp = isa_br->prom_node->child;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
David S. Miller690c8fd2006-06-22 19:12:03 -0700180 while (dp) {
181 struct linux_prom_registers *regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 struct sparc_isa_device *isa_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183
184 isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL);
185 if (!isa_dev) {
186 fatal_err("cannot allocate isa_dev");
187 prom_halt();
188 }
189
190 memset(isa_dev, 0, sizeof(*isa_dev));
191
192 /* Link it in. */
193 isa_dev->next = NULL;
194 if (isa_br->devices == NULL) {
195 isa_br->devices = isa_dev;
196 } else {
197 struct sparc_isa_device *tmp = isa_br->devices;
198
199 while (tmp->next)
200 tmp = tmp->next;
201
202 tmp->next = isa_dev;
203 }
204
205 isa_dev->bus = isa_br;
David S. Miller690c8fd2006-06-22 19:12:03 -0700206 isa_dev->prom_node = dp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207
David S. Miller690c8fd2006-06-22 19:12:03 -0700208 regs = isa_dev_get_resource(isa_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 isa_dev_get_irq(isa_dev, regs);
210
211 report_dev(isa_dev, 0);
212
213 isa_fill_children(isa_dev);
214
215 printk("]");
216
David S. Miller690c8fd2006-06-22 19:12:03 -0700217 dp = dp->sibling;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 }
219}
220
David S. Miller690c8fd2006-06-22 19:12:03 -0700221static void __init get_bridge_props(struct sparc_isa_bridge *isa_br)
222{
223 struct device_node *dp = isa_br->prom_node;
224 void *pval;
225 int len;
226
227 pval = of_get_property(dp, "ranges", &len);
228 if (pval) {
229 memcpy(isa_br->isa_ranges, pval, len);
230 isa_br->num_isa_ranges =
231 len / sizeof(struct linux_prom_isa_ranges);
232 } else {
233 isa_br->num_isa_ranges = 0;
234 }
235
236 pval = of_get_property(dp, "interrupt-map", &len);
237 if (pval) {
238 memcpy(isa_br->isa_intmap, pval, len);
239 isa_br->num_isa_intmap =
240 (len / sizeof(struct linux_prom_isa_intmap));
241 } else {
242 isa_br->num_isa_intmap = 0;
243 }
244
245 pval = of_get_property(dp, "interrupt-map-mask", &len);
246 if (pval)
247 memcpy(&isa_br->isa_intmask, pval,
248 sizeof(isa_br->isa_intmask));
249}
250
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251void __init isa_init(void)
252{
253 struct pci_dev *pdev;
254 unsigned short vendor, device;
255 int index = 0;
256
257 vendor = PCI_VENDOR_ID_AL;
258 device = PCI_DEVICE_ID_AL_M1533;
259
260 pdev = NULL;
261 while ((pdev = pci_get_device(vendor, device, pdev)) != NULL) {
262 struct pcidev_cookie *pdev_cookie;
263 struct pci_pbm_info *pbm;
264 struct sparc_isa_bridge *isa_br;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265
266 pdev_cookie = pdev->sysdata;
267 if (!pdev_cookie) {
268 printk("ISA: Warning, ISA bridge ignored due to "
269 "lack of OBP data.\n");
270 continue;
271 }
272 pbm = pdev_cookie->pbm;
273
274 isa_br = kmalloc(sizeof(*isa_br), GFP_KERNEL);
275 if (!isa_br) {
276 fatal_err("cannot allocate sparc_isa_bridge");
277 prom_halt();
278 }
279
280 memset(isa_br, 0, sizeof(*isa_br));
281
282 /* Link it in. */
283 isa_br->next = isa_chain;
284 isa_chain = isa_br;
285
286 isa_br->parent = pbm;
287 isa_br->self = pdev;
288 isa_br->index = index++;
David S. Miller690c8fd2006-06-22 19:12:03 -0700289 isa_br->prom_node = pdev_cookie->prom_node;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
David S. Miller690c8fd2006-06-22 19:12:03 -0700291 get_bridge_props(isa_br);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
293 printk("isa%d:", isa_br->index);
294
295 isa_fill_devices(isa_br);
296
297 printk("\n");
298 }
299}