From 3a3e0b0e7de69486aac8a76237de7c4276f36797 Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Fri, 13 Jun 2003 17:39:46 +0000 Subject: 2003-06-13 Greg Menke PR 405/bsps * bootloader/pci.c: Added support for configuring devices for pci busses > 0 * pci/pci.c, pci/pci.h: Added FixupPCI() to store vectors in the INTERRUPT_LINE register of pci devices any # of hops away from the host processor. * motorola/motorola.c, motorola/motorola.h: Added interrupt routing tables in support of FixupPCI. This is board-specific, each board will have to supply information for FixupPCI() to do anything for it. * startup/bspstart.c: Extended bat2 to cover entire PCI address space. * irq/irq.c, irq/irq.h: Added support for shared interrupts. Existing single hander vectors are undisturbed, a new function added to allow adding/removing handlers from a vector. --- c/src/lib/libbsp/powerpc/shared/ChangeLog | 17 + c/src/lib/libbsp/powerpc/shared/bootloader/README | 8 + c/src/lib/libbsp/powerpc/shared/bootloader/pci.c | 1904 ++++++++++++-------- c/src/lib/libbsp/powerpc/shared/irq/irq.c | 208 ++- c/src/lib/libbsp/powerpc/shared/irq/irq.h | 73 +- .../lib/libbsp/powerpc/shared/motorola/motorola.c | 197 +- .../lib/libbsp/powerpc/shared/motorola/motorola.h | 10 + c/src/lib/libbsp/powerpc/shared/pci/pci.c | 315 ++++ c/src/lib/libbsp/powerpc/shared/pci/pci.h | 14 + .../lib/libbsp/powerpc/shared/pci/pcifinddevice.c | 2 +- c/src/lib/libbsp/powerpc/shared/startup/bspstart.c | 17 +- 11 files changed, 1985 insertions(+), 780 deletions(-) (limited to 'c/src/lib/libbsp/powerpc') diff --git a/c/src/lib/libbsp/powerpc/shared/ChangeLog b/c/src/lib/libbsp/powerpc/shared/ChangeLog index f292edd127..066a7699ef 100644 --- a/c/src/lib/libbsp/powerpc/shared/ChangeLog +++ b/c/src/lib/libbsp/powerpc/shared/ChangeLog @@ -1,3 +1,20 @@ +2003-06-13 Greg Menke + + PR 405/bsps + * bootloader/pci.c: Added support for configuring devices for pci + busses > 0 + * pci/pci.c, pci/pci.h: Added FixupPCI() to store vectors in the + INTERRUPT_LINE register of pci devices any # of hops away + from the host processor. + * motorola/motorola.c, motorola/motorola.h: Added interrupt + routing tables in support of FixupPCI. This is board-specific, + each board will have to supply information for FixupPCI() to do + anything for it. + * startup/bspstart.c: Extended bat2 to cover entire PCI address space. + * irq/irq.c, irq/irq.h: Added support for shared interrupts. + Existing single hander vectors are undisturbed, a new function + added to allow adding/removing handlers from a vector. + 2003-06-13 Till Straumann PR 415/bsps diff --git a/c/src/lib/libbsp/powerpc/shared/bootloader/README b/c/src/lib/libbsp/powerpc/shared/bootloader/README index 6d36a152ba..b4984e190f 100644 --- a/c/src/lib/libbsp/powerpc/shared/bootloader/README +++ b/c/src/lib/libbsp/powerpc/shared/bootloader/README @@ -39,3 +39,11 @@ initialization (e.g printk, ...). Eric Valette (valette@crf.canon.fr) +************************************************** +2003/5/7, Greg Menke, gregory.menke@gsfc.nasa.gov + +Reworked the pci bus 0 initialization a little and added support for +configuring an arbitrary number of other busses & their respective +bridges. Also added support for configuring IO ranges below 0x10000, +which I think is reasonable given this is a PowerPC bsp. + diff --git a/c/src/lib/libbsp/powerpc/shared/bootloader/pci.c b/c/src/lib/libbsp/powerpc/shared/bootloader/pci.c index d698a31c56..4c0b4ce707 100644 --- a/c/src/lib/libbsp/powerpc/shared/bootloader/pci.c +++ b/c/src/lib/libbsp/powerpc/shared/bootloader/pci.c @@ -22,8 +22,11 @@ #include "bootldr.h" #include "pci.h" #include +#include #include + + typedef unsigned int u32; /*#define DEBUG*/ @@ -34,27 +37,27 @@ typedef unsigned int u32; */ typedef struct _pci_resource { - struct _pci_resource *next; - struct pci_dev *dev; - u_long base; /* will be 64 bits on 64 bits machines */ - u_long size; - u_char type; /* 1 is I/O else low order 4 bits of the memory type */ - u_char reg; /* Register # in conf space header */ - u_short cmd; /* Original cmd byte */ + struct _pci_resource *next; + struct pci_dev *dev; + u_long base; /* will be 64 bits on 64 bits machines */ + u_long size; + u_char type; /* 1 is I/O else low order 4 bits of the memory type */ + u_char reg; /* Register # in conf space header */ + u_short cmd; /* Original cmd byte */ } pci_resource; typedef struct _pci_area { - struct _pci_area *next; - u_long start; - u_long end; - struct pci_bus *bus; - u_int flags; + struct _pci_area *next; + u_long start; + u_long end; + struct pci_bus *bus; + u_int flags; } pci_area; typedef struct _pci_area_head { - pci_area *head; - u_long mask; - int high; /* To allocate from top */ + pci_area *head; + u_long mask; + int high; /* To allocate from top */ } pci_area_head; #define PCI_AREA_PREFETCHABLE 0 @@ -62,20 +65,20 @@ typedef struct _pci_area_head { #define PCI_AREA_IO 2 struct _pci_private { - volatile u_int * config_addr; - volatile u_char * config_data; - struct pci_dev **last_dev_p; - struct pci_bus pci_root; - pci_resource *resources; - pci_area_head io, mem; + volatile u_int * config_addr; + volatile u_char * config_data; + struct pci_dev **last_dev_p; + struct pci_bus pci_root; + pci_resource *resources; + pci_area_head io, mem; } pci_private = { - config_addr: NULL, - config_data: (volatile u_char *) 0x80800000, - last_dev_p: NULL, - resources: NULL, - io: {NULL, 0xfff, 0}, - mem: {NULL, 0xfffff, 0} + config_addr: NULL, + config_data: (volatile u_char *) 0x80800000, + last_dev_p: NULL, + resources: NULL, + io: {NULL, 0xfff, 0}, + mem: {NULL, 0xfffff, 0} }; #define pci ((struct _pci_private *)(bd->pci_private)) @@ -92,33 +95,44 @@ struct _pci_private { #if defined(PCI_DEBUG) static void print_pci_resources(const char *s) { - pci_resource *p; - printk("%s", s); - for (p=pci->resources; p; p=p->next) { - printk(" %p:%p %06x %08lx %08lx %d\n", - p, p->next, - (p->dev->devfn<<8)+(p->dev->bus->number<<16) - +0x10+p->reg*4, - p->base, - p->size, - p->type); - } + pci_resource *p; + printk("%s", s); + for (p=pci->resources; p; p=p->next) { +/* + printk(" %p:%p %06x %08lx %08lx %d\n", + p, p->next, + (p->dev->devfn<<8)+(p->dev->bus->number<<16) + +0x10+p->reg*4, + p->base, + p->size, + p->type); +*/ + + printk(" %p:%p %d:%02x (%04x:%04x) %08lx %08lx %d\n", + p, p->next, + p->dev->bus->number, PCI_SLOT(p->dev->devfn), + p->dev->vendor, p->dev->device, + p->base, + p->size, + p->type); + + } } static void print_pci_area(pci_area *p) { - for (; p; p=p->next) { - printk(" %p:%p %p %08lx %08lx\n", - p, p->next, p->bus, p->start, p->end); - } + for (; p; p=p->next) { + printk(" %p:%p %p %08lx %08lx\n", + p, p->next, p->bus, p->start, p->end); + } } static void print_pci_areas(const char *s) { - printk("%s PCI I/O areas:\n",s); - print_pci_area(pci->io.head); - printk(" PCI memory areas:\n"); - print_pci_area(pci->mem.head); + printk("%s PCI I/O areas:\n",s); + print_pci_area(pci->io.head); + printk(" PCI memory areas:\n"); + print_pci_area(pci->mem.head); } #else #define print_pci_areas(x) @@ -132,17 +146,17 @@ print_pci_areas(const char *s) { */ struct blacklist_entry { - u_short vendor, device; - u_char reg; - u_long actual_size; + u_short vendor, device; + u_char reg; + u_long actual_size; }; #define BLACKLIST(vid, did, breg, actual_size) \ {PCI_VENDOR_ID_##vid, PCI_DEVICE_ID_##vid##_##did, breg, actual_size} static struct blacklist_entry blacklist[] = { - BLACKLIST(S3, TRIO, 0, 0x04000000), - {0xffff, 0, 0, 0} + BLACKLIST(S3, TRIO, 0, 0x04000000), + {0xffff, 0, 0, 0} }; @@ -156,76 +170,88 @@ static struct blacklist_entry blacklist[] = { ((r->type&PCI_BASE_ADDRESS_MEM_PREFETCH) ? PCI_AREA_PREFETCHABLE :\ PCI_AREA_MEMORY)) + + static int insert_before(pci_resource *e, pci_resource *t) { - if (e->dev->bus->number != t->dev->bus->number) - return e->dev->bus->number > t->dev->bus->number; - if (AREA(e) != AREA(t)) return AREA(e)size > t->size); + if (e->dev->bus->number != t->dev->bus->number) + return e->dev->bus->number > t->dev->bus->number; + if (AREA(e) != AREA(t)) return AREA(e)size > t->size); } + + + + static void insert_resource(pci_resource *r) { - struct blacklist_entry *b; - pci_resource *p; - if (!r) return; - - /* First fixup in case we have a blacklist entry. Note that this - * may temporarily leave a resource in an inconsistent state: with - * (base & (size-1)) !=0. This is harmless. - */ - for (b=blacklist; b->vendor!=0xffff; b++) { - if ((r->dev->vendor==b->vendor) && - (r->dev->device==b->device) && - (r->reg==b->reg)) { - r->size=b->actual_size; - break; - } - } + struct blacklist_entry *b; + pci_resource *p; + if (!r) return; + + /* First fixup in case we have a blacklist entry. Note that this + * may temporarily leave a resource in an inconsistent state: with + * (base & (size-1)) !=0. This is harmless. + */ + for (b=blacklist; b->vendor!=0xffff; b++) { + if ((r->dev->vendor==b->vendor) && + (r->dev->device==b->device) && + (r->reg==b->reg)) { + r->size=b->actual_size; + break; + } + } - /* Motorola NT firmware does not configure pci devices which are not - * required for booting, others do. For now: - * - allocated devices in the ISA range (64kB I/O, 16Mb memory) - * but non zero base registers are left as is. - * - all other registers, whether already allocated or not, are - * reallocated unless they require an inordinate amount of - * resources (>256 Mb for memory >64kB for I/O). These - * devices with too large mapping requirements are simply ignored - * and their bases are set to 0. This should disable the - * corresponding decoders according to the PCI specification. - * Many devices are buggy in this respect, however, but the - * limits have hopefully been set high enough to avoid problems. - */ - - if ((r->type==PCI_BASE_ADDRESS_SPACE_IO) - ? (r->base && r->base <0x10000) - : (r->base && r->base <0x1000000)) { - sfree(r); - return; - } - - if ((r->type==PCI_BASE_ADDRESS_SPACE_IO) - ? (r->size >= 0x10000) - : (r->size >= 0x10000000)) { - r->size = 0; - r->base = 0; - } - - /* Now insert into the list sorting by - * 1) decreasing bus number - * 2) space: prefetchable memory, non-prefetchable and finally I/O - * 3) decreasing size - */ - if (!pci->resources || insert_before(r, pci->resources)) { - r->next = pci->resources; - pci->resources=r; - } else { - for (p=pci->resources; p->next; p=p->next) { - if (insert_before(r, p->next)) break; - } - r->next=p->next; - p->next=r; - } + /* Motorola NT firmware does not configure pci devices which are not + * required for booting, others do. For now: + * - allocated devices in the ISA range (64kB I/O, 16Mb memory) + * but non zero base registers are left as is. + * - all other registers, whether already allocated or not, are + * reallocated unless they require an inordinate amount of + * resources (>256 Mb for memory >64kB for I/O). These + * devices with too large mapping requirements are simply ignored + * and their bases are set to 0. This should disable the + * corresponding decoders according to the PCI specification. + * Many devices are buggy in this respect, however, but the + * limits have hopefully been set high enough to avoid problems. + */ + +#if 0 + if ((r->type==PCI_BASE_ADDRESS_SPACE_IO) + ? (r->base && r->base <0x10000) + : (r->base && r->base <0x1000000)) { + sfree(r); + return; + } +#endif + + if ((r->type==PCI_BASE_ADDRESS_SPACE_IO) + ? (r->size >= 0x10000) + : (r->size >= 0x10000000)) { + r->size = 0; + r->base = 0; + } + + /* Now insert into the list sorting by + * 1) decreasing bus number + * 2) space: prefetchable memory, non-prefetchable and finally I/O + * 3) decreasing size + */ + if (!pci->resources || insert_before(r, pci->resources)) { + r->next = pci->resources; + pci->resources=r; + } else { + for (p=pci->resources; p->next; p=p->next) { + if (insert_before(r, p->next)) break; + } + r->next=p->next; + p->next=r; + } } + + + + /* This version only works for bus 0. I don't have any P2P bridges to test * a more sophisticated version which has therefore not been implemented. * Prefetchable memory is not yet handled correctly either. @@ -234,699 +260,1155 @@ static void insert_resource(pci_resource *r) { */ static u_long find_range(u_char bus, u_char type, - pci_resource **first, - pci_resource **past, u_int *flags) { - pci_resource *p; - u_long total=0; - u_int fl=0; - - for (p=pci->resources; p; p=p->next) { - if ((p->dev->bus->number == bus) && - AREA(p)==type) break; - } - *first = p; - for (; p; p=p->next) { - if ((p->dev->bus->number != bus) || - AREA(p)!=type || p->size == 0) break; - total = total+p->size; - fl |= 1<type; - } - *past = p; - /* This will be used later to tell whether there are any 32 bit - * devices in an area which could be mapped higher than 4Gb - * on 64 bits architectures - */ - *flags = fl; - return total; + pci_resource **first, + pci_resource **past, u_int *flags) { + pci_resource *p; + u_long total=0; + u_int fl=0; + + for (p=pci->resources; p; p=p->next) + { + if ((p->dev->bus->number == bus) && + AREA(p)==type) break; + } + + *first = p; + + for (; p; p=p->next) + { + if ((p->dev->bus->number != bus) || + AREA(p)!=type || p->size == 0) break; + total = total+p->size; + fl |= 1<type; + } + + *past = p; + /* This will be used later to tell whether there are any 32 bit + * devices in an area which could be mapped higher than 4Gb + * on 64 bits architectures + */ + *flags = fl; + return total; } + + + + + static inline void init_free_area(pci_area_head *h, u_long start, - u_long end, u_int mask, int high) { - pci_area *p; - p = salloc(sizeof(pci_area)); - if (!p) return; - h->head = p; - p->next = NULL; - p->start = (start+mask)&~mask; - p->end = (end-mask)|mask; - p->bus = NULL; - h->mask = mask; - h->high = high; + u_long end, u_int mask, int high) { + pci_area *p; + p = salloc(sizeof(pci_area)); + if (!p) return; + h->head = p; + p->next = NULL; + p->start = (start+mask)&~mask; + p->end = (end-mask)|mask; + p->bus = NULL; + h->mask = mask; + h->high = high; } + + + + + static void insert_area(pci_area_head *h, pci_area *p) { - pci_area *q = h->head; - if (!p) return; - if (q && (q->start< p->start)) { - for(;q->next && q->next->startstart; q = q->next); - if ((q->end >= p->start) || - (q->next && p->end>=q->next->start)) { - sfree(p); - printk("Overlapping pci areas!\n"); - return; - } - p->next = q->next; - q->next = p; - } else { /* Insert at head */ - if (q && (p->end >= q->start)) { - sfree(p); - printk("Overlapping pci areas!\n"); - return; - } - p->next = q; - h->head = p; - } + pci_area *q = h->head; + if (!p) return; + if (q && (q->start< p->start)) { + for(;q->next && q->next->startstart; q = q->next); + if ((q->end >= p->start) || + (q->next && p->end>=q->next->start)) { + sfree(p); + printk("Overlapping pci areas!\n"); + return; + } + p->next = q->next; + q->next = p; + } else { /* Insert at head */ + if (q && (p->end >= q->start)) { + sfree(p); + printk("Overlapping pci areas!\n"); + return; + } + p->next = q; + h->head = p; + } } -static -void remove_area(pci_area_head *h, pci_area *p) { - pci_area *q = h->head; - if (!p || !q) return; - if (q==p) { - h->head = q->next; - return; - } - for(;q && q->next!=p; q=q->next); - if (q) q->next=p->next; + + + +static +void remove_area(pci_area_head *h, pci_area *p) +{ + pci_area *q = h->head; + + if (!p || !q) return; + if (q==p) + { + h->head = q->next; + return; + } + for(;q && q->next!=p; q=q->next); + if (q) q->next=p->next; } + + + + + static pci_area * alloc_area(pci_area_head *h, struct pci_bus *bus, - u_long required, u_long mask, u_int flags) { - pci_area *p; - pci_area *from, *split, *new; - - required = (required+h->mask) & ~h->mask; - for (p=h->head, from=NULL; p; p=p->next) { - u_long l1 = ((p->start+required+mask)&~mask)-1; - u_long l2 = ((p->start+mask)&~mask)+required-1; - /* Allocated areas point to the bus to which they pertain */ - if (p->bus) continue; - if ((p->end)>=l1 || (p->end)>=l2) from=p; - if (from && !h->high) break; - } - if (!from) return NULL; - - split = salloc(sizeof(pci_area)); - new = salloc(sizeof(pci_area)); - /* If allocation of new succeeds then allocation of split has - * also been successful (given the current mm algorithms) ! - */ - if (!new) { - sfree(split); - return NULL; - } - new->bus = bus; - new->flags = flags; - /* Now allocate pci_space taking alignment into account ! */ - if (h->high) { - u_long l1 = ((from->end+1)&~mask)-required; - u_long l2 = (from->end+1-required)&~mask; - new->start = (l1>l2) ? l1 : l2; - split->end = from->end; - from->end = new->start-1; - split->start = new->start+required; - new->end = new->start+required-1; - } else { - u_long l1 = ((from->start+mask)&~mask)+required-1; - u_long l2 = ((from->start+required+mask)&~mask)-1; - new->end = (l1start = from->start; - from->start = new->end+1; - new->start = new->end+1-required; - split->end = new->start-1; - } + u_long required, u_long mask, u_int flags) { + pci_area *p; + pci_area *from, *split, *new; + + required = (required+h->mask) & ~h->mask; + for (p=h->head, from=NULL; p; p=p->next) + { + u_long l1 = ((p->start+required+mask)&~mask)-1; + u_long l2 = ((p->start+mask)&~mask)+required-1; + /* Allocated areas point to the bus to which they pertain */ + if (p->bus) continue; + if ((p->end)>=l1 || (p->end)>=l2) from=p; + if (from && !h->high) break; + } + if (!from) return NULL; + + split = salloc(sizeof(pci_area)); + new = salloc(sizeof(pci_area)); + /* If allocation of new succeeds then allocation of split has + * also been successful (given the current mm algorithms) ! + */ + if (!new) { + sfree(split); + return NULL; + } + new->bus = bus; + new->flags = flags; + /* Now allocate pci_space taking alignment into account ! */ + if (h->high) + { + u_long l1 = ((from->end+1)&~mask)-required; + u_long l2 = (from->end+1-required)&~mask; + new->start = (l1>l2) ? l1 : l2; + split->end = from->end; + from->end = new->start-1; + split->start = new->start+required; + new->end = new->start+required-1; + } + else + { + u_long l1 = ((from->start+mask)&~mask)+required-1; + u_long l2 = ((from->start+required+mask)&~mask)-1; + new->end = (l1start = from->start; + from->start = new->end+1; + new->start = new->end+1-required; + split->end = new->start-1; + } - if (from->end+1 == from->start) remove_area(h, from); - if (split->end+1 != split->start) { - split->bus = NULL; - insert_area(h, split); - } else { - sfree(split); - } - insert_area(h, new); - print_pci_areas("alloc_area called:\n"); - return new; + if (from->end+1 == from->start) remove_area(h, from); + if (split->end+1 != split->start) + { + split->bus = NULL; + insert_area(h, split); + } + else + { + sfree(split); + } + insert_area(h, new); + print_pci_areas("alloc_area called:\n"); + return new; } + + + + static inline -void alloc_space(pci_area *p, pci_resource *r) { - if (p->start & (r->size-1)) { - r->base = p->end+1-r->size; - p->end -= r->size; - } else { - r->base = p->start; - p->start += r->size; - } -} - -static void reconfigure_bus_space(u_char bus, u_char type, pci_area_head *h) { - pci_resource *first, *past, *r; - pci_area *area, tmp; - u_int flags; - u_int required = find_range(bus, type, &first, &past, &flags); - - if (required==0) return; - area = alloc_area(h, first->dev->bus, required, first->size-1, flags); - if (!area) return; - tmp = *area; - for (r=first; r!=past; r=r->next) { - alloc_space(&tmp, r); - } +void alloc_space(pci_area *p, pci_resource *r) +{ + if (p->start & (r->size-1)) { + r->base = p->end+1-r->size; + p->end -= r->size; + } else { + r->base = p->start; + p->start += r->size; + } } + + + + +static void reconfigure_bus_space(u_char bus, u_char type, pci_area_head *h) +{ + pci_resource *first, *past, *r; + pci_area *area, tmp; + u_int flags; + u_int required = find_range(bus, type, &first, &past, &flags); + + if (required==0) return; + + area = alloc_area(h, first->dev->bus, required, first->size-1, flags); + + if (!area) return; + + tmp = *area; + for (r=first; r!=past; r=r->next) + { + alloc_space(&tmp, r); + } +} + + + + + + + +#define BUS0_IO_START 0x10000 +#define BUS0_IO_END 0x1ffff +#define BUS0_MEM_START 0x1000000 +#define BUS0_MEM_END 0xaffffff + +#define BUSREST_IO_START 0x20000 +#define BUSREST_IO_END 0x7ffff +#define BUSREST_MEM_START 0xb000000 +#define BUSREST_MEM_END 0x30000000 + + + + + static void reconfigure_pci(void) { - pci_resource *r; - struct pci_dev *dev; - /* FIXME: for now memory is relocated from low, it's better - * to start from higher addresses. - */ - init_free_area(&pci->io, 0x10000, 0x7fffff, 0xfff, 0); - init_free_area(&pci->mem, 0x1000000, 0x3cffffff, 0xfffff, 0); - - /* First reconfigure the I/O space, this will be more - * complex when there is more than 1 bus. And 64 bits - * devices are another kind of problems. - */ - reconfigure_bus_space(0, PCI_AREA_IO, &pci->io); - reconfigure_bus_space(0, PCI_AREA_MEMORY, &pci->mem); - reconfigure_bus_space(0, PCI_AREA_PREFETCHABLE, &pci->mem); - - /* Now we have to touch the configuration space of all - * the devices to remap them better than they are right now. - * This is done in 3 steps: - * 1) first disable I/O and memory response of all devices - * 2) modify the base registers - * 3) restore the original PCI_COMMAND register. - */ - for (r=pci->resources; r; r= r->next) { - if (!r->dev->sysdata) { - r->dev->sysdata=r; - pci_read_config_word(r->dev, PCI_COMMAND, &r->cmd); - pci_write_config_word(r->dev, PCI_COMMAND, - r->cmd & ~(PCI_COMMAND_IO| - PCI_COMMAND_MEMORY)); - } - } - - for (r=pci->resources; r; r= r->next) { - pci_write_config_dword(r->dev, - PCI_BASE_ADDRESS_0+(r->reg<<2), - r->base); - if ((r->type& - (PCI_BASE_ADDRESS_SPACE| - PCI_BASE_ADDRESS_MEM_TYPE_MASK)) == - (PCI_BASE_ADDRESS_SPACE_MEMORY| - PCI_BASE_ADDRESS_MEM_TYPE_64)) { - pci_write_config_dword(r->dev, - PCI_BASE_ADDRESS_1+ - (r->reg<<2), - 0); - } - } - for (dev=bd->pci_devices; dev; dev= dev->next) { - if (dev->sysdata) { - pci_write_config_word(dev, PCI_COMMAND, - ((pci_resource *)dev->sysdata) - ->cmd); - dev->sysdata=NULL; - } - } + pci_resource *r; + struct pci_dev *dev; + + /* FIXME: for now memory is relocated from low, it's better + * to start from higher addresses. + */ + /* + init_free_area(&pci->io, 0x10000, 0x7fffff, 0xfff, 0); + init_free_area(&pci->mem, 0x1000000, 0x3cffffff, 0xfffff, 0); + */ + + init_free_area(&pci->io, BUS0_IO_START, BUS0_IO_END, 0xfff, 0); + init_free_area(&pci->mem, BUS0_MEM_START, BUS0_MEM_END, 0xfffff, 0); + + + /* First reconfigure the I/O space, this will be more + * complex when there is more than 1 bus. And 64 bits + * devices are another kind of problems. + */ + reconfigure_bus_space(0, PCI_AREA_IO, &pci->io); + reconfigure_bus_space(0, PCI_AREA_MEMORY, &pci->mem); + reconfigure_bus_space(0, PCI_AREA_PREFETCHABLE, &pci->mem); + + /* Now we have to touch the configuration space of all + * the devices to remap them better than they are right now. + * This is done in 3 steps: + * 1) first disable I/O and memory response of all devices + * 2) modify the base registers + * 3) restore the original PCI_COMMAND register. + */ + for (r=pci->resources; r; r= r->next) { + if (!r->dev->sysdata) { + r->dev->sysdata=r; + pci_read_config_word(r->dev, PCI_COMMAND, &r->cmd); + pci_write_config_word(r->dev, PCI_COMMAND, + r->cmd & ~(PCI_COMMAND_IO| + PCI_COMMAND_MEMORY)); + } + } + + for (r=pci->resources; r; r= r->next) { + pci_write_config_dword(r->dev, + PCI_BASE_ADDRESS_0+(r->reg<<2), + r->base); + if ((r->type& + (PCI_BASE_ADDRESS_SPACE| + PCI_BASE_ADDRESS_MEM_TYPE_MASK)) == + (PCI_BASE_ADDRESS_SPACE_MEMORY| + PCI_BASE_ADDRESS_MEM_TYPE_64)) { + pci_write_config_dword(r->dev, + PCI_BASE_ADDRESS_1+ + (r->reg<<2), + 0); + } + } + for (dev=bd->pci_devices; dev; dev= dev->next) { + if (dev->sysdata) { + pci_write_config_word(dev, PCI_COMMAND, + ((pci_resource *)dev->sysdata) + ->cmd); + dev->sysdata=NULL; + } + } } + + + + + static int indirect_pci_read_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char *val) { - out_be32(pci->config_addr, - 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); - *val=in_8(pci->config_data + (offset&3)); - return PCIBIOS_SUCCESSFUL; + out_be32(pci->config_addr, + 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); + *val=in_8(pci->config_data + (offset&3)); + return PCIBIOS_SUCCESSFUL; } static int indirect_pci_read_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short *val) { - *val = 0xffff; - if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER; - out_be32(pci->config_addr, - 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); - *val=in_le16((volatile u_short *)(pci->config_data + (offset&3))); - return PCIBIOS_SUCCESSFUL; + *val = 0xffff; + if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER; + out_be32(pci->config_addr, + 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); + *val=in_le16((volatile u_short *)(pci->config_data + (offset&3))); + return PCIBIOS_SUCCESSFUL; } static int indirect_pci_read_config_dword(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned int *val) { - *val = 0xffffffff; - if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER; - out_be32(pci->config_addr, - 0x80|(bus<<8)|(dev_fn<<16)|(offset<<24)); - *val=in_le32((volatile u_int *)pci->config_data); - return PCIBIOS_SUCCESSFUL; + unsigned char offset, unsigned int *val) { + *val = 0xffffffff; + if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER; + out_be32(pci->config_addr, + 0x80|(bus<<8)|(dev_fn<<16)|(offset<<24)); + *val=in_le32((volatile u_int *)pci->config_data); + return PCIBIOS_SUCCESSFUL; } static int indirect_pci_write_config_byte(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned char val) { - out_be32(pci->config_addr, - 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); - out_8(pci->config_data + (offset&3), val); - return PCIBIOS_SUCCESSFUL; + out_be32(pci->config_addr, + 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); + out_8(pci->config_data + (offset&3), val); + return PCIBIOS_SUCCESSFUL; } static int indirect_pci_write_config_word(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned short val) { - if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER; - out_be32(pci->config_addr, - 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); - out_le16((volatile u_short *)(pci->config_data + (offset&3)), val); - return PCIBIOS_SUCCESSFUL; + if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER; + out_be32(pci->config_addr, + 0x80|(bus<<8)|(dev_fn<<16)|((offset&~3)<<24)); + out_le16((volatile u_short *)(pci->config_data + (offset&3)), val); + return PCIBIOS_SUCCESSFUL; } static int indirect_pci_write_config_dword(unsigned char bus, unsigned char dev_fn, unsigned char offset, unsigned int val) { - if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER; - out_be32(pci->config_addr, - 0x80|(bus<<8)|(dev_fn<<16)|(offset<<24)); - out_le32((volatile u_int *)pci->config_data, val); - return PCIBIOS_SUCCESSFUL; + if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER; + out_be32(pci->config_addr, + 0x80|(bus<<8)|(dev_fn<<16)|(offset<<24)); + out_le32((volatile u_int *)pci->config_data, val); + return PCIBIOS_SUCCESSFUL; } static const struct pci_config_access_functions indirect_functions = { - indirect_pci_read_config_byte, - indirect_pci_read_config_word, - indirect_pci_read_config_dword, - indirect_pci_write_config_byte, - indirect_pci_write_config_word, - indirect_pci_write_config_dword + indirect_pci_read_config_byte, + indirect_pci_read_config_word, + indirect_pci_read_config_dword, + indirect_pci_write_config_byte, + indirect_pci_write_config_word, + indirect_pci_write_config_dword }; static int direct_pci_read_config_byte(unsigned char bus, unsigned char dev_fn, - unsigned char offset, unsigned char *val) { - if (bus != 0 || (1<config_data + ((1<config_data + ((1<config_data + ((1<config_data + ((1<config_data + ((1<config_data + ((1<config_data + ((1<config_data + ((1<config_data + ((1<config_data + ((1<config_data + ((1<config_data + ((1<base_address[reg] = l; - r = salloc(sizeof(pci_resource)); - if (!r) { - printk("Error allocating pci_resource struct.\n"); - continue; - } - r->dev = dev; - r->reg = reg; - if ((l&PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { - r->type = l&~PCI_BASE_ADDRESS_IO_MASK; - r->base = l&PCI_BASE_ADDRESS_IO_MASK; - r->size = ~(ml&PCI_BASE_ADDRESS_IO_MASK)+1; - } else { - r->type = l&~PCI_BASE_ADDRESS_MEM_MASK; - r->base = l&PCI_BASE_ADDRESS_MEM_MASK; - r->size = ~(ml&PCI_BASE_ADDRESS_MEM_MASK)+1; - } - /* Check for the blacklisted entries */ - insert_resource(r); - } + /* Note that disabling the memory response of a host bridge + * would lose data if a DMA transfer were in progress. In a + * bootloader we don't care however. Also we can't print any + * message for a while since we might just disable the console. + */ + pci_write_config_word(dev, PCI_COMMAND, cmd & + ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY)); + pci_write_config_dword(dev, REG, ~0); + pci_read_config_dword(dev, REG, &ml); + pci_write_config_dword(dev, REG, l); + + /* Reenable the device now that we've played with + * base registers. + */ + pci_write_config_word(dev, PCI_COMMAND, cmd); + + /* seems to be an unused entry skip it */ + if ( ml == 0 || ml == 0xffffffff ) continue; + + if ((l & + (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) + == (PCI_BASE_ADDRESS_MEM_TYPE_64 + |PCI_BASE_ADDRESS_SPACE_MEMORY)) { + nextreg=reg+2; + } + dev->base_address[reg] = l; + r = salloc(sizeof(pci_resource)); + if (!r) { + printk("Error allocating pci_resource struct.\n"); + continue; + } + r->dev = dev; + r->reg = reg; + if ((l&PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { + r->type = l&~PCI_BASE_ADDRESS_IO_MASK; + r->base = l&PCI_BASE_ADDRESS_IO_MASK; + r->size = ~(ml&PCI_BASE_ADDRESS_IO_MASK)+1; + } else { + r->type = l&~PCI_BASE_ADDRESS_MEM_MASK; + r->base = l&PCI_BASE_ADDRESS_MEM_MASK; + r->size = ~(ml&PCI_BASE_ADDRESS_MEM_MASK)+1; + } + /* Check for the blacklisted entries */ + insert_resource(r); + } } + + + + u_int pci_scan_bus(struct pci_bus *bus) { - unsigned int devfn, l, max, class; - unsigned char irq, hdr_type, is_multi = 0; - struct pci_dev *dev, **bus_last; - struct pci_bus *child; - - bus_last = &bus->devices; - max = bus->secondary; - for (devfn = 0; devfn < 0xff; ++devfn) { - if (PCI_FUNC(devfn) && !is_multi) { - /* not a multi-function device */ - continue; - } - if (pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type)) - continue; - if (!PCI_FUNC(devfn)) - is_multi = hdr_type & 0x80; - - if (pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l) || - /* some broken boards return 0 if a slot is empty: */ - l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) { - is_multi = 0; - continue; - } - - dev = salloc(sizeof(*dev)); - dev->bus = bus; - dev->devfn = devfn; - dev->vendor = l & 0xffff; - dev->device = (l >> 16) & 0xffff; - - pcibios_read_config_dword(bus->number, devfn, - PCI_CLASS_REVISION, &class); - class >>= 8; /* upper 3 bytes */ - dev->class = class; - class >>= 8; - dev->hdr_type = hdr_type; - - switch (hdr_type & 0x7f) { /* header type */ - case PCI_HEADER_TYPE_NORMAL: /* standard header */ - if (class == PCI_CLASS_BRIDGE_PCI) - goto bad; - /* - * If the card generates interrupts, read IRQ number - * (some architectures change it during pcibios_fixup()) - */ - pcibios_read_config_byte(bus->number, dev->devfn, PCI_INTERRUPT_PIN, &irq); - if (irq) - pcibios_read_config_byte(bus->number, dev->devfn, PCI_INTERRUPT_LINE, &irq); - dev->irq = irq; - /* - * read base address registers, again pcibios_fixup() can - * tweak these - */ - pci_read_bases(dev, 6); - pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS, &l); - dev->rom_address = (l == 0xffffffff) ? 0 : l; - break; - case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ - if (class != PCI_CLASS_BRIDGE_PCI) - goto bad; - pci_read_bases(dev, 2); - pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS1, &l); - dev->rom_address = (l == 0xffffffff) ? 0 : l; - break; - case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */ - if (class != PCI_CLASS_BRIDGE_CARDBUS) - goto bad; - pci_read_bases(dev, 1); - break; - default: /* unknown header */ - bad: - printk("PCI device with unknown " - "header type %d ignored.\n", - hdr_type&0x7f); - continue; - } - - /* - * Put it into the global PCI device chain. It's used to - * find devices once everything is set up. - */ - *pci->last_dev_p = dev; - pci->last_dev_p = &dev->next; - - /* - * Now insert it into the list of devices held - * by the parent bus. - */ - *bus_last = dev; - bus_last = &dev->sibling; - - } - - /* - * After performing arch-dependent fixup of the bus, look behind - * all PCI-to-PCI bridges on this bus. - */ - for(dev=bus->devices; dev; dev=dev->sibling) - /* - * If it's a bridge, scan the bus behind it. - */ - if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { - unsigned int buses; - unsigned int devfn = dev->devfn; - unsigned short cr; - - /* - * Insert it into the tree of buses. - */ - child = salloc(sizeof(*child)); - child->next = bus->children; - bus->children = child; - child->self = dev; - child->parent = bus; - - /* - * Set up the primary, secondary and subordinate - * bus numbers. - */ - child->number = child->secondary = ++max; - child->primary = bus->secondary; - child->subordinate = 0xff; - /* - * Clear all status bits and turn off memory, - * I/O and master enables. - */ - pcibios_read_config_word(bus->number, devfn, PCI_COMMAND, &cr); - pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, 0x0000); - pcibios_write_config_word(bus->number, devfn, PCI_STATUS, 0xffff); - /* - * Read the existing primary/secondary/subordinate bus - * number configuration to determine if the PCI bridge - * has already been configured by the system. If so, - * do not modify the configuration, merely note it. - */ - pcibios_read_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, &buses); - if ((buses & 0xFFFFFF) != 0) - { - unsigned int cmax; - - child->primary = buses & 0xFF; - child->secondary = (buses >> 8) & 0xFF; - child->subordinate = (buses >> 16) & 0xFF; - child->number = child->secondary; - cmax = pci_scan_bus(child); - if (cmax > max) max = cmax; - } - else - { - /* - * Configure the bus numbers for this bridge: - */ - buses &= 0xff000000; - buses |= - (((unsigned int)(child->primary) << 0) | - ((unsigned int)(child->secondary) << 8) | - ((unsigned int)(child->subordinate) << 16)); - pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses); - /* - * Now we can scan all subordinate buses: - */ - max = pci_scan_bus(child); - /* - * Set the subordinate bus number to its real - * value: - */ - child->subordinate = max; - buses = (buses & 0xff00ffff) - | ((unsigned int)(child->subordinate) << 16); - pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses); - } - pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, cr); - } - - /* - * We've scanned the bus and so we know all about what's on - * the other side of any bridges that may be on this bus plus - * any devices. - * - * Return how far we've got finding sub-buses. - */ - return max; + unsigned int devfn, l, max, class; + unsigned char irq, hdr_type, is_multi = 0; + struct pci_dev *dev, **bus_last; + struct pci_bus *child; + +#if 0 + printk("scanning pci bus %d\n", bus->number ); +#endif + + bus_last = &bus->devices; + max = bus->secondary; + for (devfn = 0; devfn < 0xff; ++devfn) { + if (PCI_FUNC(devfn) && !is_multi) { + /* not a multi-function device */ + continue; + } + if (pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type)) + continue; + if (!PCI_FUNC(devfn)) + is_multi = hdr_type & 0x80; + + if (pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l) || + /* some broken boards return 0 if a slot is empty: */ + l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) { + is_multi = 0; + continue; + } + + dev = salloc(sizeof(*dev)); + dev->bus = bus; + dev->devfn = devfn; + dev->vendor = l & 0xffff; + dev->device = (l >> 16) & 0xffff; + + pcibios_read_config_dword(bus->number, devfn, + PCI_CLASS_REVISION, &class); + class >>= 8; /* upper 3 bytes */ + dev->class = class; + class >>= 8; + dev->hdr_type = hdr_type; + + switch (hdr_type & 0x7f) { /* header type */ + case PCI_HEADER_TYPE_NORMAL: /* standard header */ + if (class == PCI_CLASS_BRIDGE_PCI) + goto bad; + /* + * If the card generates interrupts, read IRQ number + * (some architectures change it during pcibios_fixup()) + */ + pcibios_read_config_byte(bus->number, dev->devfn, PCI_INTERRUPT_PIN, &irq); + if (irq) + pcibios_read_config_byte(bus->number, dev->devfn, PCI_INTERRUPT_LINE, &irq); + dev->irq = irq; + /* + * read base address registers, again pcibios_fixup() can + * tweak these + */ + pci_read_bases(dev, 6); + pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS, &l); + dev->rom_address = (l == 0xffffffff) ? 0 : l; + break; + case PCI_HEADER_TYPE_BRIDGE: /* bridge header */ + if (class != PCI_CLASS_BRIDGE_PCI) + goto bad; + pci_read_bases(dev, 2); + pcibios_read_config_dword(bus->number, devfn, PCI_ROM_ADDRESS1, &l); + dev->rom_address = (l == 0xffffffff) ? 0 : l; + break; + case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */ + if (class != PCI_CLASS_BRIDGE_CARDBUS) + goto bad; + pci_read_bases(dev, 1); + break; + + default: /* unknown header */ + bad: + printk("PCI device with unknown header type %d ignored.\n", + hdr_type&0x7f); + continue; + } + + /* + * Put it into the global PCI device chain. It's used to + * find devices once everything is set up. + */ + *pci->last_dev_p = dev; + pci->last_dev_p = &dev->next; + + /* + * Now insert it into the list of devices held + * by the parent bus. + */ + *bus_last = dev; + bus_last = &dev->sibling; + + } + + /* + * After performing arch-dependent fixup of the bus, look behind + * all PCI-to-PCI bridges on this bus. + */ + for(dev=bus->devices; dev; dev=dev->sibling) + /* + * If it's a bridge, scan the bus behind it. + */ + if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + unsigned int buses; + unsigned int devfn = dev->devfn; + unsigned short cr; + + /* + * Insert it into the tree of buses. + */ + child = salloc(sizeof(*child)); + child->next = bus->children; + bus->children = child; + child->self = dev; + child->parent = bus; + + /* + * Set up the primary, secondary and subordinate + * bus numbers. + */ + child->number = child->secondary = ++max; + child->primary = bus->secondary; + child->subordinate = 0xff; + /* + * Clear all status bits and turn off memory, + * I/O and master enables. + */ + pcibios_read_config_word(bus->number, devfn, PCI_COMMAND, &cr); + pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, 0x0000); + pcibios_write_config_word(bus->number, devfn, PCI_STATUS, 0xffff); + /* + * Read the existing primary/secondary/subordinate bus + * number configuration to determine if the PCI bridge + * has already been configured by the system. If so, + * do not modify the configuration, merely note it. + */ + pcibios_read_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, &buses); + if ((buses & 0xFFFFFF) != 0) + { + unsigned int cmax; + + child->primary = buses & 0xFF; + child->secondary = (buses >> 8) & 0xFF; + child->subordinate = (buses >> 16) & 0xFF; + child->number = child->secondary; + cmax = pci_scan_bus(child); + if (cmax > max) max = cmax; + } + else + { + /* + * Configure the bus numbers for this bridge: + */ + buses &= 0xff000000; + buses |= + (((unsigned int)(child->primary) << 0) | + ((unsigned int)(child->secondary) << 8) | + ((unsigned int)(child->subordinate) << 16)); + pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses); + /* + * Now we can scan all subordinate buses: + */ + max = pci_scan_bus(child); + /* + * Set the subordinate bus number to its real + * value: + */ + child->subordinate = max; + buses = (buses & 0xff00ffff) + | ((unsigned int)(child->subordinate) << 16); + pcibios_write_config_dword(bus->number, devfn, PCI_PRIMARY_BUS, buses); + } + pcibios_write_config_word(bus->number, devfn, PCI_COMMAND, cr ); + } + + /* + * We've scanned the bus and so we know all about what's on + * the other side of any bridges that may be on this bus plus + * any devices. + * + * Return how far we've got finding sub-buses. + */ + return max; } + + + + + + + + + +#if 0 + void -pci_fixup(void) { - struct pci_dev *p; - struct pci_bus *bus; - for (bus = &pci_root; bus; bus=bus->next) { - - for (p=bus->devices; p; p=p->sibling) { - } - } -} - -void pci_init(void) { - PPC_DEVICE *hostbridge; - - if (pci->last_dev_p) { - printk("Two or more calls to pci_init!\n"); - return; - } - pci->last_dev_p = &(bd->pci_devices); - hostbridge=residual_find_device(PROCESSORDEVICE, NULL, - BridgeController, - PCIBridge, -1, 0); - if (hostbridge) { - if (hostbridge->DeviceId.Interface==PCIBridgeIndirect) { - bd->pci_functions=&indirect_functions; - /* Should be extracted from residual data, - * indeed MPC106 in CHRP mode is different, - * but we should not use residual data in - * this case anyway. - */ - pci->config_addr = ((volatile u_int *) - (ptr_mem_map->io_base+0xcf8)); - pci->config_data = ptr_mem_map->io_base+0xcfc; - } else if(hostbridge->DeviceId.Interface==PCIBridgeDirect) { - bd->pci_functions=&direct_functions; - pci->config_data=(u_char *) 0x80800000; - } else { - } - } else { - /* Let us try by experimentation at our own risk! */ - u_int id0; - bd->pci_functions = &direct_functions; - /* On all direct bridges I know the host bridge itself - * appears as device 0 function 0. - */ - pcibios_read_config_dword(0, 0, PCI_VENDOR_ID, &id0); - if (id0==~0U) { - bd->pci_functions = &indirect_functions; - pci->config_addr = ((volatile u_int *) - (ptr_mem_map->io_base+0xcf8)); - pci->config_data = ptr_mem_map->io_base+0xcfc; - } - /* Here we should check that the host bridge is actually - * present, but if it not, we are in such a desperate - * situation, that we probably can't even tell it. - */ - } - /* Now build a small database of all found PCI devices */ - printk("\nPCI: Probing PCI hardware\n"); - pci_root.subordinate=pci_scan_bus(&pci_root); - print_pci_resources("Configurable PCI resources:\n"); - reconfigure_pci(); - print_pci_resources("Allocated PCI resources:\n"); +pci_fixup(void) +{ + struct pci_dev *p; + struct pci_bus *bus; + + for (bus = &pci_root; bus; bus=bus->next) + { + for (p=bus->devices; p; p=p->sibling) + { + } + } +} + + + + + + +static void print_pci_info() +{ + pci_resource *r; + struct pci_bus *pb = &pci_root; + + printk("\n"); + printk("PCI busses:\n"); + + for(pb= &pci_root; pb; pb=pb->children ) + { + printk(" number %d, primary %d, secondary %d, subordinate %d\n", + pb->number, + pb->primary, + pb->secondary, + pb->subordinate ); + printk(" bridge; vendor %04x, device %04x\n", + pb->self->vendor, + pb->self->device ); + + { + struct pci_dev *pd; + + for(pd= pb->devices; pd; pd=pd->sibling ) + { + printk(" vendor %04x, device %04x, irq %d\n", + pd->vendor, + pd->device, + pd->irq ); + + } + printk("\n"); + } + + } + printk("\n"); + + printk("PCI resources:\n"); + for (r=pci->resources; r; r= r->next) + { + printk(" bus %d, vendor %04x, device %04x, base %08x, size %08x, type %d\n", + r->dev->bus->number, + r->dev->vendor, + r->dev->device, + r->base, + r->size, + r->type ); + } + printk("\n"); + + return; +} + +#endif + + + + + + + + + + + + +static struct _addr_start +{ + unsigned32 start_pcimem; + unsigned32 start_pciio; + unsigned32 start_prefetch; +} astart; + + + + + +static pci_resource *enum_device_resources( struct pci_dev *pdev, int i ) +{ + pci_resource *r; + + for(r= pci->resources; r; r= r->next ) + { + if( r->dev == pdev ) + { + if( i-- == 0 ) break; + } + } + return r; } + + + + + +#define MEMORY_IO_GRANULARITY 256 + + + +static void recursive_bus_reconfigure( struct pci_bus *pbus ) +{ + struct pci_dev *pdev; + struct pci_bus *childbus; + int isroot = 0; + + + if( !pbus ) + { + /* start with the root bus */ + astart.start_pcimem = BUSREST_MEM_START; + astart.start_pciio = BUSREST_IO_START; + astart.start_prefetch = ((BUSREST_MEM_END >> 16) << 16); + + pbus = &pci_root; + isroot = -1; + } + + +#define WRITE_BRIDGE_IO +#define WRITE_BRIDGE_MEM +#define WRITE_BRIDGE_PF +#define WRITE_BRIDGE_ENABLE + + +/* +** Run thru the p2p bridges on this bus and recurse into subordinate busses +*/ + for( childbus= pbus->children; childbus; childbus= childbus->next ) + { + pdev= childbus->self; + + { + struct _addr_start addrhold; + unsigned8 base8, limit8; + unsigned16 base16, limit16, ubase16, ulimit16; + + /* save the base address values */ + memcpy( &addrhold, &astart, sizeof(struct _addr_start)); + + recursive_bus_reconfigure( childbus ); + +#ifdef PCI_DEBUG + printk("pci: configuring bus %d bridge (%04x:%04x), bus %d : (%d-%d)\n", + pdev->bus->number, + pdev->vendor, + pdev->device, + childbus->primary, + childbus->secondary, + childbus->subordinate ); +#endif + + + + /* + **use the current values & the saved ones to figure out + ** the address spaces for the bridge + */ + + if( addrhold.start_pciio == astart.start_pciio ) + { + base8 = limit8 = 0xff; + ubase16 = ulimit16 = 0xffff; + } + else + { + base8 = (unsigned8) ((addrhold.start_pciio >> 8) & 0xf0); + ubase16 = (unsigned16)(addrhold.start_pciio >> 16); + limit8 = (unsigned8) ((astart.start_pciio >> 8 ) & 0xf0); + ulimit16 = (unsigned16)(astart.start_pciio >> 16); + astart.start_pciio += 0x1000; + } + +#ifdef PCI_DEBUG + printk("pci: io base %08x limit %08x\n", (base8<<8)+(ubase16<<16), (limit8<<8)+(ulimit16<<16)); +#endif +#ifdef WRITE_BRIDGE_IO + pcibios_write_config_word(pdev->bus->number, pdev->devfn, PCI_IO_BASE_UPPER16, ubase16 ); + pcibios_write_config_byte(pdev->bus->number, pdev->devfn, PCI_IO_BASE, base8 ); + + pcibios_write_config_word(pdev->bus->number, pdev->devfn, PCI_IO_LIMIT_UPPER16, ulimit16 ); + pcibios_write_config_byte(pdev->bus->number, pdev->devfn, PCI_IO_LIMIT, limit8 ); +#endif + + + + + if( addrhold.start_pcimem == astart.start_pcimem ) + { + limit16 = 0; + base16 = 0xffff; + } + else + { + limit16= (unsigned16)((astart.start_pcimem >> 16) & 0xfff0); + base16 = (unsigned16)((addrhold.start_pcimem >> 16) & 0xfff0); + astart.start_pcimem += 0x100000; + } +#ifdef PCI_DEBUG + printk("pci: memory %04x, limit %04x\n", base16, limit16); +#endif +#ifdef WRITE_BRIDGE_MEM + pcibios_write_config_word(pdev->bus->number, pdev->devfn, PCI_MEMORY_BASE, base16 ); + pcibios_write_config_word(pdev->bus->number, pdev->devfn, PCI_MEMORY_LIMIT, limit16 ); +#endif + + + + if( astart.start_prefetch == addrhold.start_prefetch ) + { + limit16 = 0; + base16 = 0xffff; + } + else + { + limit16= (unsigned16)((addrhold.start_prefetch >> 16) & 0xfff0); + base16 = (unsigned16)((astart.start_prefetch >> 16) & 0xfff0); + astart.start_prefetch -= 0x100000; + } +#ifdef PCI_DEBUG + printk("pci: pf memory %04x, limit %04x\n", base16, limit16); +#endif +#ifdef WRITE_BRIDGE_PF + pcibios_write_config_dword(pdev->bus->number, pdev->devfn, PCI_PREF_BASE_UPPER32, 0); + pcibios_write_config_word(pdev->bus->number, pdev->devfn, PCI_PREF_MEMORY_BASE, base16 ); + pcibios_write_config_dword(pdev->bus->number, pdev->devfn, PCI_PREF_LIMIT_UPPER32, 0); + pcibios_write_config_word(pdev->bus->number, pdev->devfn, PCI_PREF_MEMORY_LIMIT, limit16 ); + +#endif + +#ifdef WRITE_BRIDGE_ENABLE + pcibios_write_config_word(pdev->bus->number, pdev->devfn, PCI_BRIDGE_CONTROL, (unsigned16)( PCI_BRIDGE_CTL_PARITY | + PCI_BRIDGE_CTL_SERR | + PCI_BRIDGE_CTL_FAST_BACK)); + + pcibios_write_config_word(pdev->bus->number, pdev->devfn, PCI_COMMAND, (unsigned16)( PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | + PCI_COMMAND_PARITY | + PCI_COMMAND_WAIT | + PCI_COMMAND_SERR | + PCI_COMMAND_FAST_BACK )); +#endif + } + } + + + + + + + if( !isroot ) + { +#ifdef PCI_DEBUG + printk("pci: Configuring devices on bus %d\n", pbus->number); +#endif + /* + ** Run thru this bus and set up addresses for all the non-bridge devices + */ + for( pdev = pbus->devices; pdev; pdev= pdev->sibling ) + { + if( (pdev->class >> 8) != PCI_CLASS_BRIDGE_PCI ) + { + pci_resource *r; + int i = 0; + unsigned alloc; + + /* enumerate all the resources defined by this device & reserve space + ** for each of their defined regions. + */ + +#ifdef PCI_DEBUG + printk("pci: configuring; vendor %04x, device %04x\n", pdev->vendor, pdev->device ); +#endif + + while( (r= enum_device_resources( pdev, i++ )) ) + { + if( r->type & PCI_BASE_ADDRESS_MEM_PREFETCH ) + { + /* prefetchable space */ + + /* shift base pointer down to an integer multiple of the size of the desired region */ + astart.start_prefetch -= (alloc= ((r->size / PAGE_SIZE) + 1) * PAGE_SIZE); + /* shift base pointer down to an integer multiple of the size of the desired region */ + astart.start_prefetch = (astart.start_prefetch / r->size) * r->size; + + r->base = astart.start_prefetch; +#ifdef PCI_DEBUG + printk("pci: pf %08X, size %08X, alloc %08X\n", r->base, r->size, alloc ); +#endif + } + else if( r->type & PCI_BASE_ADDRESS_SPACE_IO ) + { + /* io space */ + + /* shift base pointer up to an integer multiple of the size of the desired region */ + if( astart.start_pciio % r->size ) + astart.start_pciio = (((astart.start_pciio / r->size) + 1) * r->size); + + r->base = astart.start_pciio; + astart.start_pciio += (alloc= ((r->size / MEMORY_IO_GRANULARITY) + 1) * MEMORY_IO_GRANULARITY); +#ifdef PCI_DEBUG + printk("pci: io %08X, size %08X, alloc %08X\n", r->base, r->size, alloc ); +#endif + } + else + { + /* memory space */ + + /* shift base pointer up to an integer multiple of the size of the desired region */ + if( astart.start_pcimem % r->size ) + astart.start_pcimem = (((astart.start_pcimem / r->size) + 1) * r->size); + + r->base = astart.start_pcimem; + astart.start_pcimem += (alloc= ((r->size / MEMORY_IO_GRANULARITY) + 1) * MEMORY_IO_GRANULARITY); +#ifdef PCI_DEBUG + printk("pci: mem %08X, size %08X, alloc %08X\n", r->base, r->size, alloc ); +#endif + } + } + + } + } + } + +} + + + + + + + + + + +void pci_init(void) +{ + PPC_DEVICE *hostbridge; + + if (pci->last_dev_p) { + printk("Two or more calls to pci_init!\n"); + return; + } + pci->last_dev_p = &(bd->pci_devices); + hostbridge=residual_find_device(PROCESSORDEVICE, NULL, + BridgeController, + PCIBridge, -1, 0); + if (hostbridge) { + if (hostbridge->DeviceId.Interface==PCIBridgeIndirect) { + bd->pci_functions=&indirect_functions; + /* Should be extracted from residual data, + * indeed MPC106 in CHRP mode is different, + * but we should not use residual data in + * this case anyway. + */ + pci->config_addr = ((volatile u_int *) + (ptr_mem_map->io_base+0xcf8)); + pci->config_data = ptr_mem_map->io_base+0xcfc; + } else if(hostbridge->DeviceId.Interface==PCIBridgeDirect) { + bd->pci_functions=&direct_functions; + pci->config_data=(u_char *) 0x80800000; + } else { + } + } else { + /* Let us try by experimentation at our own risk! */ + u_int id0; + bd->pci_functions = &direct_functions; + /* On all direct bridges I know the host bridge itself + * appears as device 0 function 0. + */ + pcibios_read_config_dword(0, 0, PCI_VENDOR_ID, &id0); + if (id0==~0U) { + bd->pci_functions = &indirect_functions; + pci->config_addr = ((volatile u_int *) + (ptr_mem_map->io_base+0xcf8)); + pci->config_data = ptr_mem_map->io_base+0xcfc; + } + /* Here we should check that the host bridge is actually + * present, but if it not, we are in such a desperate + * situation, that we probably can't even tell it. + */ + } + /* Now build a small database of all found PCI devices */ + printk("\nPCI: Probing PCI hardware\n"); + pci_root.subordinate=pci_scan_bus(&pci_root); + + print_pci_resources("Installed PCI resources:\n"); + + recursive_bus_reconfigure(NULL); + reconfigure_pci(); + + print_pci_resources("Allocated PCI resources:\n"); +} + + +/* eof */ diff --git a/c/src/lib/libbsp/powerpc/shared/irq/irq.c b/c/src/lib/libbsp/powerpc/shared/irq/irq.c index d45242529b..dbb5e72f77 100644 --- a/c/src/lib/libbsp/powerpc/shared/irq/irq.c +++ b/c/src/lib/libbsp/powerpc/shared/irq/irq.c @@ -118,6 +118,70 @@ static int isValidInterrupt(int irq) return 1; } + +/* + * ------------------------ RTEMS Shared Irq Handler Mngt Routines ---------------- + */ +int BSP_install_rtems_shared_irq_handler (const rtems_irq_connect_data* irq) +{ + unsigned int level; + rtems_irq_connect_data* vchain; + + if (!isValidInterrupt(irq->name)) { + printk("Invalid interrupt vector %i\n",irq->name); + return 0; + } + if ( (int)rtems_hdl_tbl[irq->name].next_handler == -1 ) { + printk("IRQ vector %i already connected to an unshared handler\n",irq->name); + return 0; + } + _CPU_ISR_Disable(level); + + + vchain = (rtems_irq_connect_data*)malloc(sizeof(rtems_irq_connect_data)); + + /* save off topmost handler */ + vchain[0]= rtems_hdl_tbl[irq->name]; + + /* + * store the data provided by user + */ + rtems_hdl_tbl[irq->name] = *irq; + + /* link chain to new topmost handler */ + rtems_hdl_tbl[irq->name].next_handler = (void *)vchain; + + + if (is_isa_irq(irq->name)) { + /* + * Enable interrupt at PIC level + */ + BSP_irq_enable_at_i8259s (irq->name); + } + + if (is_pci_irq(irq->name)) { + /* + * Enable interrupt at OPENPIC level + */ + openpic_enable_irq ((int) irq->name - BSP_PCI_IRQ_LOWEST_OFFSET); + } + + if (is_processor_irq(irq->name)) { + /* + * Enable exception at processor level + */ + } + /* + * Enable interrupt on device + */ + irq->on(irq); + + _CPU_ISR_Enable(level); + + return 1; +} + + /* * ------------------------ RTEMS Single Irq Handler Mngt Routines ---------------- */ @@ -147,6 +211,7 @@ int BSP_install_rtems_irq_handler (const rtems_irq_connect_data* irq) * store the data provided by user */ rtems_hdl_tbl[irq->name] = *irq; + rtems_hdl_tbl[irq->name].next_handler = (void *)-1; if (is_isa_irq(irq->name)) { /* @@ -189,6 +254,7 @@ int BSP_get_current_rtems_irq_handler (rtems_irq_connect_data* irq) int BSP_remove_rtems_irq_handler (const rtems_irq_connect_data* irq) { + rtems_irq_connect_data *pchain= NULL, *vchain = NULL; unsigned int level; if (!isValidInterrupt(irq->name)) { @@ -206,6 +272,35 @@ int BSP_remove_rtems_irq_handler (const rtems_irq_connect_data* irq) } _CPU_ISR_Disable(level); + if( (int)rtems_hdl_tbl[irq->name].next_handler != -1 ) + { + int found = 0; + + for( (pchain= NULL, vchain = &rtems_hdl_tbl[irq->name]); + (vchain->hdl != default_rtems_entry.hdl); + (pchain= vchain, vchain = (rtems_irq_connect_data*)vchain->next_handler) ) + { + if( vchain->hdl == irq->hdl ) + { + found= -1; break; + } + } + + if( !found ) + { + _CPU_ISR_Enable(level); + return 0; + } + } + else + { + if (rtems_hdl_tbl[irq->name].hdl != irq->hdl) + { + _CPU_ISR_Enable(level); + return 0; + } + } + if (is_isa_irq(irq->name)) { /* * disable interrupt at PIC level @@ -232,7 +327,27 @@ int BSP_remove_rtems_irq_handler (const rtems_irq_connect_data* irq) /* * restore the default irq value */ - rtems_hdl_tbl[irq->name] = default_rtems_entry; + if( !vchain ) + { + /* single handler vector... */ + rtems_hdl_tbl[irq->name] = default_rtems_entry; + } + else + { + if( pchain ) + { + /* non-first handler being removed */ + pchain->next_handler = vchain->next_handler; + } + else + { + /* first handler isn't malloc'ed, so just overwrite it. Since + the contents of vchain are being struct copied, vchain itself + goes away */ + rtems_hdl_tbl[irq->name]= *vchain; + } + free(vchain); + } _CPU_ISR_Enable(level); @@ -265,12 +380,31 @@ int BSP_rtems_irq_mngt_set(rtems_irq_global_settings* config) for (i=BSP_ISA_IRQ_LOWEST_OFFSET; i < BSP_ISA_IRQ_LOWEST_OFFSET + BSP_ISA_IRQ_NUMBER; i++) { if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) { - BSP_irq_enable_at_i8259s (i); - rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]); + BSP_irq_enable_at_i8259s (i); + + /* rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]); */ + { + rtems_irq_connect_data* vchain; + for( vchain = &rtems_hdl_tbl[i]; + ((int)vchain != -1 && vchain->hdl != default_rtems_entry.hdl); + vchain = (rtems_irq_connect_data*)vchain->next_handler ) + { + vchain->on(vchain); + } + } } else { - rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]); - BSP_irq_disable_at_i8259s (i); + /* rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]); */ + { + rtems_irq_connect_data* vchain; + for( vchain = &rtems_hdl_tbl[i]; + ((int)vchain != -1 && vchain->hdl != default_rtems_entry.hdl); + vchain = (rtems_irq_connect_data*)vchain->next_handler ) + { + vchain->off(vchain); + } + } + BSP_irq_disable_at_i8259s (i); } } /* @@ -287,12 +421,32 @@ int BSP_rtems_irq_mngt_set(rtems_irq_global_settings* config) openpic_set_source_priority(i - BSP_PCI_IRQ_LOWEST_OFFSET, internal_config->irqPrioTbl[i]); if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) { - openpic_enable_irq ((int) i - BSP_PCI_IRQ_LOWEST_OFFSET); - rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]); + openpic_enable_irq ((int) i - BSP_PCI_IRQ_LOWEST_OFFSET); + /* rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]); */ + { + rtems_irq_connect_data* vchain; + for( vchain = &rtems_hdl_tbl[i]; + ((int)vchain != -1 && vchain->hdl != default_rtems_entry.hdl); + vchain = (rtems_irq_connect_data*)vchain->next_handler ) + { + vchain->on(vchain); + } + } + } else { - rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]); - openpic_disable_irq ((int) i - BSP_PCI_IRQ_LOWEST_OFFSET); + /* rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]); */ + { + rtems_irq_connect_data* vchain; + for( vchain = &rtems_hdl_tbl[i]; + ((int)vchain != -1 && vchain->hdl != default_rtems_entry.hdl); + vchain = (rtems_irq_connect_data*)vchain->next_handler ) + { + vchain->off(vchain); + } + } + + openpic_disable_irq ((int) i - BSP_PCI_IRQ_LOWEST_OFFSET); } } /* @@ -304,10 +458,30 @@ int BSP_rtems_irq_mngt_set(rtems_irq_global_settings* config) */ for (i=BSP_PROCESSOR_IRQ_LOWEST_OFFSET; i < BSP_PROCESSOR_IRQ_LOWEST_OFFSET + BSP_PROCESSOR_IRQ_NUMBER; i++) { if (rtems_hdl_tbl[i].hdl != default_rtems_entry.hdl) { - rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]); + /* rtems_hdl_tbl[i].on(&rtems_hdl_tbl[i]); */ + { + rtems_irq_connect_data* vchain; + for( vchain = &rtems_hdl_tbl[i]; + ((int)vchain != -1 && vchain->hdl != default_rtems_entry.hdl); + vchain = (rtems_irq_connect_data*)vchain->next_handler ) + { + vchain->on(vchain); + } + } + } else { - rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]); + /* rtems_hdl_tbl[i].off(&rtems_hdl_tbl[i]); */ + { + rtems_irq_connect_data* vchain; + for( vchain = &rtems_hdl_tbl[i]; + ((int)vchain != -1 && vchain->hdl != default_rtems_entry.hdl); + vchain = (rtems_irq_connect_data*)vchain->next_handler ) + { + vchain->off(vchain); + } + } + } } _CPU_ISR_Enable(level); @@ -373,7 +547,17 @@ void C_dispatch_irq_handler (CPU_Interrupt_frame *frame, unsigned int excNum) new_msr = msr | MSR_EE; _CPU_MSR_SET(new_msr); - rtems_hdl_tbl[irq].hdl(); + /* rtems_hdl_tbl[irq].hdl(); */ + { + rtems_irq_connect_data* vchain; + for( vchain = &rtems_hdl_tbl[irq]; + ((int)vchain != -1 && vchain->hdl != default_rtems_entry.hdl); + vchain = (rtems_irq_connect_data*)vchain->next_handler ) + { + vchain->hdl(); + } + } + _CPU_MSR_SET(msr); diff --git a/c/src/lib/libbsp/powerpc/shared/irq/irq.h b/c/src/lib/libbsp/powerpc/shared/irq/irq.h index a356762e4f..7d54a6eff7 100644 --- a/c/src/lib/libbsp/powerpc/shared/irq/irq.h +++ b/c/src/lib/libbsp/powerpc/shared/irq/irq.h @@ -143,38 +143,43 @@ typedef void (*rtems_irq_disable) (const struct __rtems_irq_connect_data__*); typedef int (*rtems_irq_is_enabled) (const struct __rtems_irq_connect_data__*); typedef struct __rtems_irq_connect_data__ { - /* - * IRQ line - */ - rtems_irq_symbolic_name name; - /* - * handler. See comment on handler properties below in function prototype. - */ - rtems_irq_hdl hdl; - /* - * function for enabling interrupts at device level (ONLY!). - * The BSP code will automatically enable it at i8259s level and openpic level. - * RATIONALE : anyway such code has to exist in current driver code. - * It is usually called immediately AFTER connecting the interrupt handler. - * RTEMS may well need such a function when restoring normal interrupt - * processing after a debug session. - * - */ - rtems_irq_enable on; - /* - * function for disabling interrupts at device level (ONLY!). - * The code will disable it at i8259s level. RATIONALE : anyway - * such code has to exist for clean shutdown. It is usually called - * BEFORE disconnecting the interrupt. RTEMS may well need such - * a function when disabling normal interrupt processing for - * a debug session. May well be a NOP function. - */ - rtems_irq_disable off; - /* - * function enabling to know what interrupt may currently occur - * if someone manipulates the i8259s interrupt mask without care... - */ - rtems_irq_is_enabled isOn; + /* + * IRQ line + */ + rtems_irq_symbolic_name name; + /* + * handler. See comment on handler properties below in function prototype. + */ + rtems_irq_hdl hdl; + /* + * function for enabling interrupts at device level (ONLY!). + * The BSP code will automatically enable it at i8259s level and openpic level. + * RATIONALE : anyway such code has to exist in current driver code. + * It is usually called immediately AFTER connecting the interrupt handler. + * RTEMS may well need such a function when restoring normal interrupt + * processing after a debug session. + * + */ + rtems_irq_enable on; + /* + * function for disabling interrupts at device level (ONLY!). + * The code will disable it at i8259s level. RATIONALE : anyway + * such code has to exist for clean shutdown. It is usually called + * BEFORE disconnecting the interrupt. RTEMS may well need such + * a function when disabling normal interrupt processing for + * a debug session. May well be a NOP function. + */ + rtems_irq_disable off; + /* + * function enabling to know what interrupt may currently occur + * if someone manipulates the i8259s interrupt mask without care... + */ + rtems_irq_is_enabled isOn; + /* + * Set to -1 for vectors forced to have only 1 handler + */ + void *next_handler; + }rtems_irq_connect_data; typedef struct { @@ -276,6 +281,10 @@ int BSP_irq_enabled_at_i8259s (const rtems_irq_symbolic_name irqLine); * */ int BSP_install_rtems_irq_handler (const rtems_irq_connect_data*); +int BSP_install_rtems_shared_irq_handler (const rtems_irq_connect_data*); + +#define BSP_SHARED_HANDLER_SUPPORT 1 + /* * function to get the current RTEMS irq handler for ptr->name. It enables to * define hanlder chain... diff --git a/c/src/lib/libbsp/powerpc/shared/motorola/motorola.c b/c/src/lib/libbsp/powerpc/shared/motorola/motorola.c index d45e09044b..a6a4ce9868 100644 --- a/c/src/lib/libbsp/powerpc/shared/motorola/motorola.c +++ b/c/src/lib/libbsp/powerpc/shared/motorola/motorola.c @@ -18,42 +18,178 @@ #include #include + +/* +** Board-specific table that maps interrupt names to onboard pci +** peripherals as well as local pci busses. This table is used at +** bspstart() to configure the interrupt name & pin for all devices that +** do not have it already specified. If the device is already +** configured, we leave it alone but sanity check & print a warning if +** we don't know about the pin/line the card gives us. +** +** bus = the bus number of the slot/device in question +** +** slot : +** +** If slot != -1, it indicates a device on the given bus in that slot +** is to use one of the listed interrupt names given an interrupt pin. +** +** If slot == -1, it means devices on this bus can occupy any slot- +** and for pci, this means the particular interrupt pin that the +** device signals is therefore dependent on the particular slot. To +** work from the slot to the interrupt pin, the swizzle table is used. +** Once the bus and interrupt pin is known, the correct interrupt name +** can be pulled from the table. The swizzle table relates the +** interrupt pin from the device to the particular interrupt +** controller interrupt pin- so it is quite reasonable for a device on +** bus 1 signalling interrupt pin 1 to show up at the interrupt +** controller as pin 4- this is why the int pin field varies for +** bridged pci busses. +** +** +** opts = bitmap of options that control the configuration of this +** slot/bus. +** +** pin_routes[] = array of pin & vectors that may serve this slot; +** +** pin = the pin # which delivers an interrupt on this route, A=1, +** B=2, C=3, D=4 +** +** int_name[4] = an array of up to 4 bsp-specific interrupt name +** that can be used by this route. Unused entries should be -1. +** The array is of primary use for slots that can be vectored thru +** multiple interrupt lines over the interrupt pin supplied by the +** record. If more than one entry is present, the most preferable +** should supplied first. +** +*/ + +#define NULL_PINMAP {-1,{-1,-1,-1,-1}} +#define NULL_INTMAP {-1,-1,-1,{}} + + + +static struct _int_map mcp750_intmap[] = { + + { 0, 16, 0, {{1, {5, 19,-1,-1}}, /* pmc slot */ + NULL_PINMAP}}, + + { 0, 14, 0, {{1, {10,18,-1,-1}}, /* onboard ethernet */ + NULL_PINMAP}}, + + { 1, -1, 0, {{1, {24,-1,-1,-1}}, + {2, {25,-1,-1,-1}}, + {3, {26,-1,-1,-1}}, + {4, {27,-1,-1,-1}}, + NULL_PINMAP}}, + + NULL_INTMAP }; + + + + +static struct _int_map mtx603_intmap[] = { + + {0, 14, 0, {{1, {10,16,-1,-1}}, /* onboard ethernet */ + NULL_PINMAP}}, + + {0, 12, 0, {{1, {14,18,-1,-1}}, /* onboard scsi */ + NULL_PINMAP}}, + + {0, 16, 0, {{1, {25,-1,-1,-1}}, /* pci/pmc slot 1 */ + {2, {26,-1,-1,-1}}, + {3, {27,-1,-1,-1}}, + {4, {28,-1,-1,-1}}, + NULL_PINMAP}}, + + {0, 17, 0, {{1, {26,-1,-1,-1}}, /* pci/pmc slot 2 */ + {2, {27,-1,-1,-1}}, + {3, {28,-1,-1,-1}}, + {4, {25,-1,-1,-1}}, + NULL_PINMAP}}, + + {0, 18, 0, {{1, {27,-1,-1,-1}}, /* pci slot 3 */ + {2, {28,-1,-1,-1}}, + {3, {25,-1,-1,-1}}, + {4, {26,-1,-1,-1}}, + NULL_PINMAP}}, + + NULL_INTMAP }; + + + + + + + + +/* + * This table represents the standard PCI swizzle defined in the + * PCI bus specification. Table taken from Linux 2.4.18, prep_pci.c, + * the values in this table are interrupt_pin values (1 based). + */ +static unsigned char prep_pci_intpins[4][4] = +{ + { 1, 2, 3, 4 }, /* Buses 0, 4, 8, ... */ + { 2, 3, 4, 1 }, /* Buses 1, 5, 9, ... */ + { 3, 4, 1, 2 }, /* Buses 2, 6, 10 ... */ + { 4, 1, 2, 3 }, /* Buses 3, 7, 11 ... */ +}; + +static int prep_pci_swizzle(int slot, int pin) +{ + return prep_pci_intpins[ slot % 4 ][ pin-1 ]; +} + + + + + + + + + typedef struct { /* * 0x100 mask assumes for Raven and Hawk boards * that the level/edge are set. * 0x200 if this board has a Hawk chip. */ - int cpu_type; - int base_type; - const char *name; + int cpu_type; + int base_type; + const char *name; + + struct _int_map *intmap; + int (*swizzler)(int, int); } mot_info_t; static const mot_info_t mot_boards[] = { - {0x300, 0x00, "MVME 2400"}, - {0x010, 0x00, "Genesis"}, - {0x020, 0x00, "Powerstack (Series E)"}, - {0x040, 0x00, "Blackhawk (Powerstack)"}, - {0x050, 0x00, "Omaha (PowerStack II Pro3000)"}, - {0x060, 0x00, "Utah (Powerstack II Pro4000)"}, - {0x0A0, 0x00, "Powerstack (Series EX)"}, - {0x1E0, 0xE0, "Mesquite cPCI (MCP750)"}, - {0x1E0, 0xE1, "Sitka cPCI (MCPN750)"}, - {0x1E0, 0xE2, "Mesquite cPCI (MCP750) w/ HAC"}, - {0x1E0, 0xF6, "MTX Plus"}, - {0x1E0, 0xF7, "MTX w/o Parallel Port"}, - {0x1E0, 0xF8, "MTX w/ Parallel Port"}, - {0x1E0, 0xF9, "MVME 2300"}, - {0x1E0, 0xFA, "MVME 2300SC/2600"}, - {0x1E0, 0xFB, "MVME 2600 with MVME712M"}, - {0x1E0, 0xFC, "MVME 2600/2700 with MVME761"}, - {0x1E0, 0xFD, "MVME 3600 with MVME712M"}, - {0x1E0, 0xFE, "MVME 3600 with MVME761"}, - {0x1E0, 0xFF, "MVME 1600-001 or 1600-011"}, + {0x300, 0x00, "MVME 2400", NULL, NULL}, + {0x010, 0x00, "Genesis", NULL, NULL}, + {0x020, 0x00, "Powerstack (Series E)", NULL, NULL}, + {0x040, 0x00, "Blackhawk (Powerstack)", NULL, NULL}, + {0x050, 0x00, "Omaha (PowerStack II Pro3000)", NULL, NULL}, + {0x060, 0x00, "Utah (Powerstack II Pro4000)", NULL, NULL}, + {0x0A0, 0x00, "Powerstack (Series EX)", NULL, NULL}, + {0x1E0, 0xE0, "Mesquite cPCI (MCP750)", mcp750_intmap, prep_pci_swizzle}, + {0x1E0, 0xE1, "Sitka cPCI (MCPN750)", mcp750_intmap, prep_pci_swizzle}, + {0x1E0, 0xE2, "Mesquite cPCI (MCP750) w/ HAC", mcp750_intmap, prep_pci_swizzle}, + {0x1E0, 0xF6, "MTX Plus", NULL, NULL}, + {0x1E0, 0xF7, "MTX w/o Parallel Port", mtx603_intmap, prep_pci_swizzle}, + {0x1E0, 0xF8, "MTX w/ Parallel Port", mtx603_intmap, prep_pci_swizzle}, + {0x1E0, 0xF9, "MVME 2300", NULL, NULL}, + {0x1E0, 0xFA, "MVME 2300SC/2600", NULL, NULL}, + {0x1E0, 0xFB, "MVME 2600 with MVME712M", NULL, NULL}, + {0x1E0, 0xFC, "MVME 2600/2700 with MVME761", NULL, NULL}, + {0x1E0, 0xFD, "MVME 3600 with MVME712M", NULL, NULL}, + {0x1E0, 0xFE, "MVME 3600 with MVME761", NULL, NULL}, + {0x1E0, 0xFF, "MVME 1600-001 or 1600-011", NULL, NULL}, {0x000, 0x00, ""} }; + + prep_t currentPrepType; motorolaBoard currentBoard; prep_t checkPrepBoardType(RESIDUAL *res) @@ -114,9 +250,24 @@ motorolaBoard getMotorolaBoard() return currentBoard; } + const char* motorolaBoardToString(motorolaBoard board) { if (board == MOTOROLA_UNKNOWN) return "Unknown motorola board"; return (mot_boards[board].name); } + +const struct _int_map *motorolaIntMap(motorolaBoard board) +{ + if (board == MOTOROLA_UNKNOWN) return NULL; + return mot_boards[board].intmap; +} + + +const void *motorolaIntSwizzle(motorolaBoard board) +{ + if (board == MOTOROLA_UNKNOWN) return NULL; + return (void *)mot_boards[board].swizzler; +} + diff --git a/c/src/lib/libbsp/powerpc/shared/motorola/motorola.h b/c/src/lib/libbsp/powerpc/shared/motorola/motorola.h index 585d1d62dc..c538bd2f74 100644 --- a/c/src/lib/libbsp/powerpc/shared/motorola/motorola.h +++ b/c/src/lib/libbsp/powerpc/shared/motorola/motorola.h @@ -16,6 +16,14 @@ #define LIBBSP_POWERPC_SHARED_MOTOROLA_MOTOROLA_H #include +#include + + + + + + + typedef enum { PREP_IBM = 0, @@ -61,6 +69,8 @@ extern prep_t currentPrepType; extern motorolaBoard getMotorolaBoard(); extern motorolaBoard currentBoard; extern const char* motorolaBoardToString(motorolaBoard); +extern const struct _int_map *motorolaIntMap(motorolaBoard board); +extern const void *motorolaIntSwizzle(motorolaBoard board); #endif /* LIBBSP_POWERPC_SHARED_MOTOROLA_MOTOROLA_H */ diff --git a/c/src/lib/libbsp/powerpc/shared/pci/pci.c b/c/src/lib/libbsp/powerpc/shared/pci/pci.c index a6d615cea6..0661a8a60e 100644 --- a/c/src/lib/libbsp/powerpc/shared/pci/pci.c +++ b/c/src/lib/libbsp/powerpc/shared/pci/pci.c @@ -216,6 +216,320 @@ const pci_config_access_functions pci_direct_functions = { }; + + + + + + + + + + + +#define PRINT_MSG() \ + printk("pci : Device %d:%02x routed to interrupt_line %d\n", pbus, pslot, int_name ) + + +/* +** Validate a test interrupt name and print a warning if its not one of +** the names defined in the routing record. +*/ +static int test_intname( struct _int_map *row, int pbus, int pslot, int int_pin, int int_name ) +{ + int j,k; + int _nopin= -1, _noname= -1; + + for(j=0; row->pin_route[j].pin > -1; j++) + { + if( row->pin_route[j].pin == int_pin ) + { + _nopin = 0; + + for(k=0; k<4 && row->pin_route[j].int_name[k] > -1; k++ ) + { + if( row->pin_route[j].int_name[k] == int_name ){ _noname=0; break; } + } + break; + } + } + + if( _nopin ) + { + printk("pci : Device %d:%02x supplied a bogus interrupt_pin %d\n", pbus, pslot, int_pin ); + return -1; + } + else + { + if( _noname ) + printk("pci : Device %d:%02x supplied a suspicious interrupt_line %d, using it anyway\n", pbus, pslot, int_name ); + } + return 0; +} + + + + + +struct pcibridge +{ + int bus,slot; +}; + + +static int FindPCIbridge( int mybus, struct pcibridge *pb ) +{ + int pbus, pslot; + unsigned8 bussec, buspri; + unsigned16 devid, vendorid, dclass; + + for(pbus=0; pbus< BusCountPCI(); pbus++) + { + for(pslot=0; pslot< PCI_MAX_DEVICES; pslot++) + { + pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &devid); + if( devid == 0xffff ) continue; + + pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &vendorid); + if( vendorid == 0xffff ) continue; + + pci_read_config_word(pbus, pslot, 0, PCI_CLASS_DEVICE, &dclass); + + if( dclass == PCI_CLASS_BRIDGE_PCI ) + { + pci_read_config_byte(pbus, pslot, 0, PCI_PRIMARY_BUS, &buspri); + pci_read_config_byte(pbus, pslot, 0, PCI_SECONDARY_BUS, &bussec); + +#if 0 + printk("pci : Found bridge at %d:%d, mybus %d, pribus %d, secbus %d ", pbus, pslot, mybus, buspri, bussec ); +#endif + if( bussec == mybus ) + { +#if 0 + printk("match\n"); +#endif + /* found our nearest bridge going towards the root */ + pb->bus = pbus; + pb->slot = pslot; + + return 0; + } +#if 0 + printk("no match\n"); +#endif + } + + + } + } + return -1; +} + + + + + + + + +void FixupPCI( struct _int_map *bspmap, int (*swizzler)(int,int) ) +{ + unsigned char cvalue; + unsigned16 devid; + int ismatch, i, j, pbus, pslot, int_pin, int_name; + + /* + ** If the device has a non-zero INTERRUPT_PIN, assign a bsp-specific + ** INTERRUPT_NAME if one isn't already in place. Then, drivers can + ** trivially use INTERRUPT_NAME to hook up with devices. + */ + + for(pbus=0; pbus< BusCountPCI(); pbus++) + { + for(pslot=0; pslot< PCI_MAX_DEVICES; pslot++) + { + pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &devid); + if( devid == 0xffff ) continue; + + /* got a device */ + + pci_read_config_byte( pbus, pslot, 0, PCI_INTERRUPT_PIN, &cvalue); + int_pin = cvalue; + + pci_read_config_byte( pbus, pslot, 0, PCI_INTERRUPT_LINE, &cvalue); + int_name = cvalue; + +/* printk("pci : device %d:%02x devid %04x, intpin %d, intline %d\n", pbus, pslot, devid, int_pin, int_name ); */ + + if( int_pin > 0 ) + { + ismatch = 0; + + /* + ** first run thru the bspmap table and see if we have an explicit configuration + */ + for(i=0; bspmap[i].bus > -1; i++) + { + if( bspmap[i].bus == pbus && bspmap[i].slot == pslot ) + { + ismatch = -1; + /* we have a record in the table that gives specific + * pins and interrupts for devices in this slot */ + if( int_name == 255 ) + { + /* find the vector associated with whatever pin the device gives us */ + for( int_name=-1, j=0; bspmap[i].pin_route[j].pin > -1; j++ ) + { + if( bspmap[i].pin_route[j].pin == int_pin ) + { + int_name = bspmap[i].pin_route[j].int_name[0]; + break; + } + } + if( int_name == -1 ) + { + printk("pci : Unable to resolve device %d:%d w/ swizzled int pin %i to an interrupt_line.\n", pbus, pslot, int_pin ); + } + else + { + PRINT_MSG(); + pci_write_config_byte(pbus,pslot,0,PCI_INTERRUPT_LINE,(cvalue= int_name, cvalue)); + } + } + else + { + test_intname( &bspmap[i],pbus,pslot,int_pin,int_name); + } + break; + } + } + + + if( !ismatch ) + { + /* + ** no match, which means we're on a bus someplace. Work + ** backwards from it to one of our defined busses, + ** swizzling thru each bridge on the way. + */ + + /* keep pbus, pslot pointed to the device being + configured while we track down the bridges using + tbus,tslot. We keep searching the routing table because + we may end up finding our bridge in it */ + + int tbus= pbus, tslot= pslot; + + for(;;) + { + + for(i=0; bspmap[i].bus > -1; i++) + { + if( bspmap[i].bus == tbus && (bspmap[i].slot == tslot || bspmap[i].slot == -1) ) + { + ismatch = -1; + /* found a record for this bus, so swizzle the + * int_pin which we then use to find the + * interrupt_name. + */ + + if( int_name == 255 ) + { + /* + ** FIXME. I can't believe this little hack + ** is right. It does not yield an error in + ** convienently simple situations. + */ + if( tbus ) int_pin = (*swizzler)(tslot,int_pin); + + + /* + ** int_pin points to the interrupt channel + ** this card ends up delivering interrupts + ** on. Find the int_name servicing it. + */ + for( int_name=-1, j=0; bspmap[i].pin_route[j].pin > -1; j++ ) + { + if( bspmap[i].pin_route[j].pin == int_pin ) + { + int_name = bspmap[i].pin_route[j].int_name[0]; + break; + } + } + if( int_name == -1 ) + { + printk("pci : Unable to resolve device %d:%d w/ swizzled int pin %i to an interrupt_line.\n", pbus, pslot, int_pin ); + } + else + { + PRINT_MSG(); + pci_write_config_byte(pbus,pslot,0,PCI_INTERRUPT_LINE,(cvalue= int_name, cvalue)); + } + } + else + { + test_intname(&bspmap[i],pbus,pslot,int_pin,int_name); + } + goto donesearch; + } + } + + + if( !ismatch ) + { + struct pcibridge pb; + + /* + ** Haven't found our bus in the int map, so work + ** upwards thru the bridges till we find it. + */ + + if( FindPCIbridge( tbus, &pb )== 0 ) + { + int_pin = (*swizzler)(tslot,int_pin); + + /* our next bridge up is on pb.bus, pb.slot- now + ** instead of pointing to the device we're + ** trying to configure, we move from bridge to + ** bridge. + */ + + tbus = pb.bus; + tslot = pb.slot; + } + else + { + printk("pci : No bridge from bus %i towards root found\n", tbus ); + goto donesearch; + } + + } + + } + } + donesearch: + + + if( !ismatch && int_pin != 0 && int_name == 255 ) + { + printk("pci : Unable to match device %d:%d with an int routing table entry\n", pbus, pslot ); + } + + + } + } + } +} + + + + + + + + + + /* * This routine determines the maximum bus number in the system */ @@ -228,6 +542,7 @@ void InitializePCI() unsigned int ulClass, ulDeviceID; detect_host_bridge(); + /* * Scan PCI bus 0 looking for PCI-PCI bridges */ diff --git a/c/src/lib/libbsp/powerpc/shared/pci/pci.h b/c/src/lib/libbsp/powerpc/shared/pci/pci.h index 7327a6afbc..bacf8d51b5 100644 --- a/c/src/lib/libbsp/powerpc/shared/pci/pci.h +++ b/c/src/lib/libbsp/powerpc/shared/pci/pci.h @@ -1153,6 +1153,20 @@ pci_write_config_dword(unsigned char bus, unsigned char slot, unsigned char func extern unsigned char BusCountPCI(); extern void InitializePCI(); + +struct _pin_routes +{ + int pin, int_name[4]; +}; +struct _int_map +{ + int bus, slot, opts; + struct _pin_routes pin_route[5]; +}; + +void FixupPCI( struct _int_map *, int (*swizzler)(int,int) ); + + /* scan for a specific device */ /* find a particular PCI device * (currently, only bus0 is scanned for device/fun0) diff --git a/c/src/lib/libbsp/powerpc/shared/pci/pcifinddevice.c b/c/src/lib/libbsp/powerpc/shared/pci/pcifinddevice.c index a1d2a0d3ad..4414c5755e 100644 --- a/c/src/lib/libbsp/powerpc/shared/pci/pcifinddevice.c +++ b/c/src/lib/libbsp/powerpc/shared/pci/pcifinddevice.c @@ -37,7 +37,7 @@ unsigned char bus,dev,fun,hd; if (PCI_INVALID_VENDORDEVICEID == d) continue; #ifdef PCI_DEBUG - printk("BSP_pciFindDevice: found 0x%08x at %i/%i/%i\n",d,bus,dev,fun); + printk("BSP_pciFindDevice: found 0x%08x at %d/%d/%d\n",d,bus,dev,fun); #endif (void) pci_read_config_word(bus,dev,fun,PCI_VENDOR_ID,&s); if (vendorid != s) diff --git a/c/src/lib/libbsp/powerpc/shared/startup/bspstart.c b/c/src/lib/libbsp/powerpc/shared/startup/bspstart.c index a865742dad..5fc61e65bb 100644 --- a/c/src/lib/libbsp/powerpc/shared/startup/bspstart.c +++ b/c/src/lib/libbsp/powerpc/shared/startup/bspstart.c @@ -253,7 +253,7 @@ void bsp_start( void ) * provided by the RAVEN */ /* T. Straumann: give more PCI address space */ - setdbat(2, PCI_MEM_BASE, PCI_MEM_BASE, 0x10000000, IO_PAGE); + setdbat(2, PCI_MEM_BASE, PCI_MEM_BASE, 0x30000000, IO_PAGE); /* * Must have acces to open pic PCI ACK registers * provided by the RAVEN @@ -300,6 +300,21 @@ void bsp_start( void ) printk("Going to start PCI buses scanning and initialization\n"); #endif InitializePCI(); + + { + struct _int_map *bspmap = motorolaIntMap(currentBoard); + if( bspmap ) + { + printk("pci : Configuring interrupt routing for '%s'\n", motorolaBoardToString(currentBoard)); + FixupPCI(bspmap, motorolaIntSwizzle(currentBoard) ); + } + else + printk("pci : Interrupt routing not available for this bsp\n"); + + } + + + #ifdef SHOW_MORE_INIT_SETTINGS printk("Number of PCI buses found is : %d\n", BusCountPCI()); #endif -- cgit v1.2.3