OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [arch/] [sparc64/] [kernel/] [ebus.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/* $Id: ebus.c,v 1.1.1.1 2004-04-15 01:34:32 phoenix Exp $
2
 * ebus.c: PCI to EBus bridge device.
3
 *
4
 * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
5
 * Copyright (C) 1999  David S. Miller (davem@redhat.com)
6
 */
7
 
8
#include <linux/config.h>
9
#include <linux/kernel.h>
10
#include <linux/types.h>
11
#include <linux/init.h>
12
#include <linux/slab.h>
13
#include <linux/string.h>
14
 
15
#include <asm/system.h>
16
#include <asm/page.h>
17
#include <asm/pbm.h>
18
#include <asm/ebus.h>
19
#include <asm/oplib.h>
20
#include <asm/bpp.h>
21
#include <asm/irq.h>
22
 
23
struct linux_ebus *ebus_chain = 0;
24
 
25
#ifdef CONFIG_SUN_AUXIO
26
extern void auxio_probe(void);
27
#endif
28
 
29
static inline void *ebus_alloc(size_t size)
30
{
31
        void *mem;
32
 
33
        mem = kmalloc(size, GFP_ATOMIC);
34
        if (!mem)
35
                panic("ebus_alloc: out of memory");
36
        memset((char *)mem, 0, size);
37
        return mem;
38
}
39
 
40
static void __init ebus_ranges_init(struct linux_ebus *ebus)
41
{
42
        int success;
43
 
44
        ebus->num_ebus_ranges = 0;
45
        success = prom_getproperty(ebus->prom_node, "ranges",
46
                                   (char *)ebus->ebus_ranges,
47
                                   sizeof(ebus->ebus_ranges));
48
        if (success != -1)
49
                ebus->num_ebus_ranges = (success/sizeof(struct linux_prom_ebus_ranges));
50
}
51
 
52
static void __init ebus_intmap_init(struct linux_ebus *ebus)
53
{
54
        int success;
55
 
56
        ebus->num_ebus_intmap = 0;
57
        success = prom_getproperty(ebus->prom_node, "interrupt-map",
58
                                   (char *)ebus->ebus_intmap,
59
                                   sizeof(ebus->ebus_intmap));
60
        if (success == -1)
61
                return;
62
 
63
        ebus->num_ebus_intmap = (success/sizeof(struct linux_prom_ebus_intmap));
64
 
65
        success = prom_getproperty(ebus->prom_node, "interrupt-map-mask",
66
                                   (char *)&ebus->ebus_intmask,
67
                                   sizeof(ebus->ebus_intmask));
68
        if (success == -1) {
69
                prom_printf("ebus: can't get interrupt-map-mask\n");
70
                prom_halt();
71
        }
72
}
73
 
74
int __init ebus_intmap_match(struct linux_ebus *ebus,
75
                             struct linux_prom_registers *reg,
76
                             int *interrupt)
77
{
78
        unsigned int hi, lo, irq;
79
        int i;
80
 
81
        if (!ebus->num_ebus_intmap)
82
                return 0;
83
 
84
        hi = reg->which_io & ebus->ebus_intmask.phys_hi;
85
        lo = reg->phys_addr & ebus->ebus_intmask.phys_lo;
86
        irq = *interrupt & ebus->ebus_intmask.interrupt;
87
        for (i = 0; i < ebus->num_ebus_intmap; i++) {
88
                if ((ebus->ebus_intmap[i].phys_hi == hi) &&
89
                    (ebus->ebus_intmap[i].phys_lo == lo) &&
90
                    (ebus->ebus_intmap[i].interrupt == irq)) {
91
                        *interrupt = ebus->ebus_intmap[i].cinterrupt;
92
                        return 0;
93
                }
94
        }
95
        return -1;
96
}
97
 
98
void __init fill_ebus_child(int node, struct linux_prom_registers *preg,
99
                            struct linux_ebus_child *dev, int non_standard_regs)
100
{
101
        int regs[PROMREG_MAX];
102
        int irqs[PROMREG_MAX];
103
        int i, len;
104
 
105
        dev->prom_node = node;
106
        prom_getstring(node, "name", dev->prom_name, sizeof(dev->prom_name));
107
        printk(" (%s)", dev->prom_name);
108
 
109
        len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs));
110
        dev->num_addrs = len / sizeof(regs[0]);
111
 
112
        if (non_standard_regs) {
113
                /* This is to handle reg properties which are not
114
                 * in the parent relative format.  One example are
115
                 * children of the i2c device on CompactPCI systems.
116
                 *
117
                 * So, for such devices we just record the property
118
                 * raw in the child resources.
119
                 */
120
                for (i = 0; i < dev->num_addrs; i++)
121
                        dev->resource[i].start = regs[i];
122
        } else {
123
                for (i = 0; i < dev->num_addrs; i++) {
124
                        int rnum = regs[i];
125
                        if (rnum >= dev->parent->num_addrs) {
126
                                prom_printf("UGH: property for %s was %d, need < %d\n",
127
                                            dev->prom_name, len, dev->parent->num_addrs);
128
                                panic("fill_ebus_child");
129
                        }
130
                        dev->resource[i].start = dev->parent->resource[i].start;
131
                        dev->resource[i].end = dev->parent->resource[i].end;
132
                        dev->resource[i].flags = IORESOURCE_MEM;
133
                        dev->resource[i].name = dev->prom_name;
134
                }
135
        }
136
 
137
        for (i = 0; i < PROMINTR_MAX; i++)
138
                dev->irqs[i] = PCI_IRQ_NONE;
139
 
140
        len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs));
141
        if ((len == -1) || (len == 0)) {
142
                dev->num_irqs = 0;
143
                /*
144
                 * Oh, well, some PROMs don't export interrupts
145
                 * property to children of EBus devices...
146
                 *
147
                 * Be smart about PS/2 keyboard and mouse.
148
                 */
149
                if (!strcmp(dev->parent->prom_name, "8042")) {
150
                        if (!strcmp(dev->prom_name, "kb_ps2")) {
151
                                dev->num_irqs = 1;
152
                                dev->irqs[0] = dev->parent->irqs[0];
153
                        } else {
154
                                dev->num_irqs = 1;
155
                                dev->irqs[0] = dev->parent->irqs[1];
156
                        }
157
                }
158
        } else {
159
                dev->num_irqs = len / sizeof(irqs[0]);
160
                for (i = 0; i < dev->num_irqs; i++) {
161
                        struct pci_pbm_info *pbm = dev->bus->parent;
162
                        struct pci_controller_info *p = pbm->parent;
163
 
164
                        if (ebus_intmap_match(dev->bus, preg, &irqs[i]) != -1) {
165
                                dev->irqs[i] = p->irq_build(pbm,
166
                                                            dev->bus->self,
167
                                                            irqs[i]);
168
                        } else {
169
                                /* If we get a bogus interrupt property, just
170
                                 * record the raw value instead of punting.
171
                                 */
172
                                dev->irqs[i] = irqs[i];
173
                        }
174
                }
175
        }
176
}
177
 
178
static int __init child_regs_nonstandard(struct linux_ebus_device *dev)
179
{
180
        if (!strcmp(dev->prom_name, "i2c") ||
181
            !strcmp(dev->prom_name, "SUNW,lombus"))
182
                return 1;
183
        return 0;
184
}
185
 
186
void __init fill_ebus_device(int node, struct linux_ebus_device *dev)
187
{
188
        struct linux_prom_registers regs[PROMREG_MAX];
189
        struct linux_ebus_child *child;
190
        int irqs[PROMINTR_MAX];
191
        int i, n, len;
192
 
193
        dev->prom_node = node;
194
        prom_getstring(node, "name", dev->prom_name, sizeof(dev->prom_name));
195
        printk(" [%s", dev->prom_name);
196
 
197
        len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs));
198
        if (len == -1) {
199
                dev->num_addrs = 0;
200
                goto probe_interrupts;
201
        }
202
 
203
        if (len % sizeof(struct linux_prom_registers)) {
204
                prom_printf("UGH: proplen for %s was %d, need multiple of %d\n",
205
                            dev->prom_name, len,
206
                            (int)sizeof(struct linux_prom_registers));
207
                prom_halt();
208
        }
209
        dev->num_addrs = len / sizeof(struct linux_prom_registers);
210
 
211
        for (i = 0; i < dev->num_addrs; i++) {
212
                /* XXX Learn how to interpret ebus ranges... -DaveM */
213
                if (regs[i].which_io >= 0x10)
214
                        n = (regs[i].which_io - 0x10) >> 2;
215
                else
216
                        n = regs[i].which_io;
217
 
218
                dev->resource[i].start  = dev->bus->self->resource[n].start;
219
                dev->resource[i].start += (unsigned long)regs[i].phys_addr;
220
                dev->resource[i].end    =
221
                        (dev->resource[i].start + (unsigned long)regs[i].reg_size - 1UL);
222
                dev->resource[i].flags  = IORESOURCE_MEM;
223
                dev->resource[i].name   = dev->prom_name;
224
                request_resource(&dev->bus->self->resource[n],
225
                                 &dev->resource[i]);
226
        }
227
 
228
probe_interrupts:
229
        for (i = 0; i < PROMINTR_MAX; i++)
230
                dev->irqs[i] = PCI_IRQ_NONE;
231
 
232
        len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs));
233
        if ((len == -1) || (len == 0)) {
234
                dev->num_irqs = 0;
235
        } else {
236
                dev->num_irqs = len / sizeof(irqs[0]);
237
                for (i = 0; i < dev->num_irqs; i++) {
238
                        struct pci_pbm_info *pbm = dev->bus->parent;
239
                        struct pci_controller_info *p = pbm->parent;
240
 
241
                        if (ebus_intmap_match(dev->bus, &regs[0], &irqs[i]) != -1) {
242
                                dev->irqs[i] = p->irq_build(pbm,
243
                                                            dev->bus->self,
244
                                                            irqs[i]);
245
                        } else {
246
                                /* If we get a bogus interrupt property, just
247
                                 * record the raw value instead of punting.
248
                                 */
249
                                dev->irqs[i] = irqs[i];
250
                        }
251
                }
252
        }
253
 
254
        if ((node = prom_getchild(node))) {
255
                printk(" ->");
256
                dev->children = ebus_alloc(sizeof(struct linux_ebus_child));
257
 
258
                child = dev->children;
259
                child->next = 0;
260
                child->parent = dev;
261
                child->bus = dev->bus;
262
                fill_ebus_child(node, &regs[0],
263
                                child, child_regs_nonstandard(dev));
264
 
265
                while ((node = prom_getsibling(node))) {
266
                        child->next = ebus_alloc(sizeof(struct linux_ebus_child));
267
 
268
                        child = child->next;
269
                        child->next = 0;
270
                        child->parent = dev;
271
                        child->bus = dev->bus;
272
                        fill_ebus_child(node, &regs[0],
273
                                        child, child_regs_nonstandard(dev));
274
                }
275
        }
276
        printk("]");
277
}
278
 
279
static struct pci_dev *find_next_ebus(struct pci_dev *start, int *is_rio_p)
280
{
281
        struct pci_dev *pdev = start;
282
 
283
        do {
284
                pdev = pci_find_device(PCI_VENDOR_ID_SUN, PCI_ANY_ID, pdev);
285
                if (pdev &&
286
                    (pdev->device == PCI_DEVICE_ID_SUN_EBUS ||
287
                     pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS))
288
                        break;
289
        } while (pdev != NULL);
290
 
291
        if (pdev && (pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS))
292
                *is_rio_p = 1;
293
        else
294
                *is_rio_p = 0;
295
 
296
        return pdev;
297
}
298
 
299
void __init ebus_init(void)
300
{
301
        struct pci_pbm_info *pbm;
302
        struct linux_ebus_device *dev;
303
        struct linux_ebus *ebus;
304
        struct pci_dev *pdev;
305
        struct pcidev_cookie *cookie;
306
        int nd, ebusnd, is_rio;
307
        int num_ebus = 0;
308
 
309
        if (!pci_present())
310
                return;
311
 
312
        pdev = find_next_ebus(NULL, &is_rio);
313
        if (!pdev) {
314
                printk("ebus: No EBus's found.\n");
315
                return;
316
        }
317
 
318
        cookie = pdev->sysdata;
319
        ebusnd = cookie->prom_node;
320
 
321
        ebus_chain = ebus = ebus_alloc(sizeof(struct linux_ebus));
322
        ebus->next = 0;
323
        ebus->is_rio = is_rio;
324
 
325
        while (ebusnd) {
326
                /* SUNW,pci-qfe uses four empty ebuses on it.
327
                   I think we should not consider them here,
328
                   as they have half of the properties this
329
                   code expects and once we do PCI hot-plug,
330
                   we'd have to tweak with the ebus_chain
331
                   in the runtime after initialization. -jj */
332
                if (!prom_getchild (ebusnd)) {
333
                        pdev = find_next_ebus(pdev, &is_rio);
334
                        if (!pdev) {
335
                                if (ebus == ebus_chain) {
336
                                        ebus_chain = NULL;
337
                                        printk("ebus: No EBus's found.\n");
338
                                        return;
339
                                }
340
                                break;
341
                        }
342
                        ebus->is_rio = is_rio;
343
                        cookie = pdev->sysdata;
344
                        ebusnd = cookie->prom_node;
345
                        continue;
346
                }
347
                printk("ebus%d:", num_ebus);
348
 
349
                prom_getstring(ebusnd, "name", ebus->prom_name, sizeof(ebus->prom_name));
350
                ebus->index = num_ebus;
351
                ebus->prom_node = ebusnd;
352
                ebus->self = pdev;
353
                ebus->parent = pbm = cookie->pbm;
354
 
355
                ebus_ranges_init(ebus);
356
                ebus_intmap_init(ebus);
357
 
358
                nd = prom_getchild(ebusnd);
359
                if (!nd)
360
                        goto next_ebus;
361
 
362
                ebus->devices = ebus_alloc(sizeof(struct linux_ebus_device));
363
 
364
                dev = ebus->devices;
365
                dev->next = 0;
366
                dev->children = 0;
367
                dev->bus = ebus;
368
                fill_ebus_device(nd, dev);
369
 
370
                while ((nd = prom_getsibling(nd))) {
371
                        dev->next = ebus_alloc(sizeof(struct linux_ebus_device));
372
 
373
                        dev = dev->next;
374
                        dev->next = 0;
375
                        dev->children = 0;
376
                        dev->bus = ebus;
377
                        fill_ebus_device(nd, dev);
378
                }
379
 
380
        next_ebus:
381
                printk("\n");
382
 
383
                pdev = find_next_ebus(pdev, &is_rio);
384
                if (!pdev)
385
                        break;
386
 
387
                cookie = pdev->sysdata;
388
                ebusnd = cookie->prom_node;
389
 
390
                ebus->next = ebus_alloc(sizeof(struct linux_ebus));
391
                ebus = ebus->next;
392
                ebus->next = 0;
393
                ebus->is_rio = is_rio;
394
                ++num_ebus;
395
        }
396
 
397
#ifdef CONFIG_SUN_AUXIO
398
        auxio_probe();
399
#endif
400
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.