⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pci.c

📁 RTEMS (Real-Time Executive for Multiprocessor Systems) is a free open source real-time operating sys
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  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 + -