📄 pci.c
字号:
/* * pci.c -- Crude pci handling for early boot. * * Copyright (C) 1998, 1999 Gabriel Paubert, paubert@iram.es * * Modified to compile in RTEMS development environment * by Eric Valette * * Copyright (C) 1999 Eric Valette. valette@crf.canon.fr * * The license and distribution terms for this file may be * found in found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. * * pci.c,v 1.3 2003/06/13 17:39:44 joel Exp */#include <sys/types.h>#include <rtems/bspIo.h>#include <libcpu/spr.h>#include "bootldr.h"#include "pci.h"#include <libcpu/io.h>#include <libcpu/page.h>#include <bsp/consoleIo.h>#include <string.h>typedef unsigned int u32;/*#define DEBUG#define PCI_DEBUG*//* Used to reorganize PCI space on stupid machines which spread resources * across a wide address space. This is bad when P2P bridges are present * or when it limits the mappings that a resource hog like a PCI<->VME * bridge can use. */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 */} pci_resource;typedef struct _pci_area { 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;#define PCI_AREA_PREFETCHABLE 0#define PCI_AREA_MEMORY 1#define PCI_AREA_IO 2struct _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;} 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}};#define pci ((struct _pci_private *)(bd->pci_private))#define pci_root pci->pci_root#if !defined(DEBUG)#undef PCI_DEBUG/* #else #define PCI_DEBUG*/#endif#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); */ 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); }}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);}#else#define print_pci_areas(x) #define print_pci_resources(x)#endif/* Maybe there are some devices who use a size different * from the alignment. For now we assume both are the same. * The blacklist might be used for other weird things in the future too, * since weird non PCI complying devices seem to proliferate these days. */struct blacklist_entry { 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}};/* This function filters resources and then inserts them into a list of * configurable pci resources. */#define AREA(r) \(((r->type&PCI_BASE_ADDRESS_SPACE)==PCI_BASE_ADDRESS_SPACE_IO) ? PCI_AREA_IO :\ ((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)<AREA(t); return (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; } } /* 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. */ /* ** This is little ugly below. It seems that at least on the MCP750, ** the PBC has some default IO space mappings that the bsp #defines ** that read/write to PCI I/O space assume, particuarly the i8259 ** manipulation code. So, if we allow the small IO spaces on PCI bus ** 0 and 1 to be remapped, the registers can shift out from under the ** #defines. This is particuarly awful, but short of redefining the ** PCI I/O primitives to be functions with base addresses read from ** the hardware, we are stuck with the kludge below. Note that ** everything is remapped on the CPCI backplane and any downstream ** hardware, its just the builtin stuff we're tiptoeing around. ** ** Gregm, 7/16/2003 ** ** Gregm, changed 11/2003 so IO devices only on bus 0 zero are not ** remapped. This covers the builtin pc-like io devices- but ** properly maps IO devices on higher busses. */ if( r->dev->bus->number == 0 ) { if ((r->type==PCI_BASE_ADDRESS_SPACE_IO) ? (r->base && r->base <0x10000) : (r->base && r->base <0x1000000)) {#ifdef PCI_DEBUG printk("freeing region; %p:%p %d:%02x (%04x:%04x) %08lx %08lx %d\n", r, r->next, r->dev->bus->number, PCI_SLOT(r->dev->devfn), r->dev->vendor, r->dev->device, r->base, r->size, r->type); #endif 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; }}/* 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. * And several levels of PCI bridges much less even since there must be * allocated together to be able to setup correctly the top bridge. */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<<p->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;}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->start<p->start; 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; }}staticvoid 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 = (l1<l2) ? l1 : l2; split->start = 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;}static inlinevoid 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -