diff options
Diffstat (limited to 'arch/sparc64/kernel/isa.c')
-rw-r--r-- | arch/sparc64/kernel/isa.c | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/arch/sparc64/kernel/isa.c b/arch/sparc64/kernel/isa.c new file mode 100644 index 000000000000..30862abee611 --- /dev/null +++ b/arch/sparc64/kernel/isa.c | |||
@@ -0,0 +1,329 @@ | |||
1 | #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 | |||
8 | struct sparc_isa_bridge *isa_chain; | ||
9 | |||
10 | static void __init fatal_err(const char *reason) | ||
11 | { | ||
12 | prom_printf("ISA: fatal error, %s.\n", reason); | ||
13 | } | ||
14 | |||
15 | static 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 | |||
23 | static 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 | */ | ||
61 | static 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 | |||
74 | static 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 | |||
96 | static 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 | |||
131 | route_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 | |||
138 | no_irq: | ||
139 | isa_dev->irq = PCI_IRQ_NONE; | ||
140 | } | ||
141 | |||
142 | static 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 | |||
194 | static 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 | |||
255 | void __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 | } | ||