Merge git://git.skbuff.net/gitroot/yoshfuji/linux-2.6-git-rfc3542
[linux-2.6] / arch / sparc64 / kernel / isa.c
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 }