summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/powerpc/shared/bootloader/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libbsp/powerpc/shared/bootloader/pci.c')
-rw-r--r--c/src/lib/libbsp/powerpc/shared/bootloader/pci.c1374
1 files changed, 0 insertions, 1374 deletions
diff --git a/c/src/lib/libbsp/powerpc/shared/bootloader/pci.c b/c/src/lib/libbsp/powerpc/shared/bootloader/pci.c
deleted file mode 100644
index c9daca419c..0000000000
--- a/c/src/lib/libbsp/powerpc/shared/bootloader/pci.c
+++ /dev/null
@@ -1,1374 +0,0 @@
-/*
- * 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 the file LICENSE in this distribution or at
- * http://www.rtems.org/license/LICENSE.
- */
-
-#include <sys/types.h>
-#include <rtems/bspIo.h>
-#include <libcpu/spr.h>
-#include "bootldr.h"
-#include "pci.h"
-#include <libcpu/io.h>
-#include <bsp/consoleIo.h>
-#include <string.h>
-#include <bsp.h>
-
-#include <string.h>
-
-
-/*
-#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 2
-
-struct _pci_private {
- volatile void * 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;
- }
- }
-
-
- /* 2004/11/30, PR 729 fix is removing the r->size=0 and r->base=0
- * assignement which makes too-large regions conflict with onboard
- * hardware, replacing it with sfree which deletes the memory region
- * from the setup code, leaving it disabled. */
- if ((r->type==PCI_BASE_ADDRESS_SPACE_IO)
- ? (r->size > 0x10000)
- : (r->size > 0x18000000)) {
- sfree(r);
- return;
- }
-
- /* 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;
- }
-}
-
-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 = (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 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);
- }
-}
-
-#define BUS0_IO_START 0x10000
-#define BUS0_IO_END 0x1ffff
-#define BUS0_MEM_START 0x1000000
-#define BUS0_MEM_END 0x3f00000
-
-#define BUSREST_IO_START 0x20000
-#define BUSREST_IO_END 0x7ffff
-#define BUSREST_MEM_START 0x4000000
-#define BUSREST_MEM_END 0x10000000
-
-static void reconfigure_pci(void) {
- pci_resource *r;
- struct pci_dev *dev;
-
- u_long bus0_mem_start = BUS0_MEM_START;
- u_long bus0_mem_end = BUS0_MEM_END;
-
- if ( residual_fw_is_qemu( bd->residual ) ) {
- bus0_mem_start += PREP_ISA_MEM_BASE;
- bus0_mem_end += PREP_ISA_MEM_BASE;
- }
-
- /* 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_bootloader_read_config_word(r->dev, PCI_COMMAND, &r->cmd);
- pci_bootloader_write_config_word(r->dev, PCI_COMMAND,
- r->cmd & ~(PCI_COMMAND_IO|
- PCI_COMMAND_MEMORY));
- }
- }
-
- for (r=pci->resources; r; r= r->next) {
- pci_bootloader_write_config_dword(r->dev,
- PCI_BASE_ADDRESS_0+(r->reg<<2),
- r->base);
-
- if ( residual_fw_is_qemu( bd->residual ) && r->dev->sysdata ) {
- if ( PCI_BASE_ADDRESS_SPACE_IO == (r->type & PCI_BASE_ADDRESS_SPACE) )
- ((pci_resource*)r->dev->sysdata)->cmd |= PCI_COMMAND_IO;
- else
- ((pci_resource*)r->dev->sysdata)->cmd |= PCI_COMMAND_MEMORY;
- }
-
- 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_bootloader_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_bootloader_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, uint8_t *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;
-}
-
-static int
-indirect_pci_read_config_word(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, uint16_t *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 uint16_t *)(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, uint32_t *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 uint32_t *)pci->config_data);
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int
-indirect_pci_write_config_byte(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, uint8_t 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;
-}
-
-static int
-indirect_pci_write_config_word(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, uint16_t 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 uint16_t *)(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, uint32_t 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 uint32_t *)pci->config_data, val);
- return PCIBIOS_SUCCESSFUL;
-}
-
-static const struct pci_bootloader_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
-};
-
-static int
-direct_pci_read_config_byte(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, uint8_t *val) {
- if (bus != 0 || (1<<PCI_SLOT(dev_fn) & 0xff8007fe)) {
- *val=0xff;
- return PCIBIOS_DEVICE_NOT_FOUND;
- }
- *val=in_8(pci->config_data + ((1<<PCI_SLOT(dev_fn))&~1)
- + (PCI_FUNC(dev_fn)<<8) + offset);
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int
-direct_pci_read_config_word(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, uint16_t *val) {
- *val = 0xffff;
- if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER;
- if (bus != 0 || (1<<PCI_SLOT(dev_fn) & 0xff8007fe)) {
- return PCIBIOS_DEVICE_NOT_FOUND;
- }
- *val=in_le16((volatile uint16_t *)
- (pci->config_data + ((1<<PCI_SLOT(dev_fn))&~1)
- + (PCI_FUNC(dev_fn)<<8) + offset));
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int
-direct_pci_read_config_dword(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, uint32_t *val) {
- *val = 0xffffffff;
- if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER;
- if (bus != 0 || (1<<PCI_SLOT(dev_fn) & 0xff8007fe)) {
- return PCIBIOS_DEVICE_NOT_FOUND;
- }
- *val=in_le32((volatile uint32_t *)
- (pci->config_data + ((1<<PCI_SLOT(dev_fn))&~1)
- + (PCI_FUNC(dev_fn)<<8) + offset));
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int
-direct_pci_write_config_byte(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, uint8_t val) {
- if (bus != 0 || (1<<PCI_SLOT(dev_fn) & 0xff8007fe)) {
- return PCIBIOS_DEVICE_NOT_FOUND;
- }
- out_8(pci->config_data + ((1<<PCI_SLOT(dev_fn))&~1)
- + (PCI_FUNC(dev_fn)<<8) + offset,
- val);
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int
-direct_pci_write_config_word(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, uint16_t val) {
- if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER;
- if (bus != 0 || (1<<PCI_SLOT(dev_fn) & 0xff8007fe)) {
- return PCIBIOS_DEVICE_NOT_FOUND;
- }
- out_le16((volatile uint16_t *)
- (pci->config_data + ((1<<PCI_SLOT(dev_fn))&~1)
- + (PCI_FUNC(dev_fn)<<8) + offset),
- val);
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int
-direct_pci_write_config_dword(unsigned char bus, unsigned char dev_fn,
- unsigned char offset, uint32_t val) {
- if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER;
- if (bus != 0 || (1<<PCI_SLOT(dev_fn) & 0xff8007fe)) {
- return PCIBIOS_DEVICE_NOT_FOUND;
- }
- out_le32((volatile uint32_t *)
- (pci->config_data + ((1<<PCI_SLOT(dev_fn))&~1)
- + (PCI_FUNC(dev_fn)<<8) + offset),
- val);
- return PCIBIOS_SUCCESSFUL;
-}
-
-static const struct pci_bootloader_config_access_functions direct_functions = {
- direct_pci_read_config_byte,
- direct_pci_read_config_word,
- direct_pci_read_config_dword,
- direct_pci_write_config_byte,
- direct_pci_write_config_word,
- direct_pci_write_config_dword
-};
-
-static void pci_read_bases(struct pci_dev *dev, unsigned int howmany)
-{
- unsigned int reg, nextreg;
-
-#define REG (PCI_BASE_ADDRESS_0 + (reg<<2))
-
- u_short cmd;
- uint32_t l, ml;
- pci_bootloader_read_config_word(dev, PCI_COMMAND, &cmd);
-
- for(reg=0; reg<howmany; reg=nextreg)
- {
- pci_resource *r;
-
- nextreg=reg+1;
- pci_bootloader_read_config_dword(dev, REG, &l);
-#if 0
- if (l == 0xffffffff /*AJF || !l*/) continue;
-#endif
- /* 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_bootloader_write_config_word(dev, PCI_COMMAND, cmd &
- ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY));
- pci_bootloader_write_config_dword(dev, REG, ~0);
- pci_bootloader_read_config_dword(dev, REG, &ml);
- pci_bootloader_write_config_dword(dev, REG, l);
-
- /* Reenable the device now that we've played with
- * base registers.
- */
- pci_bootloader_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; */
- }
-
- /* find the first bit set to one after the base
- address type bits to find length of region */
- {
- unsigned int c= 16 , val= 0;
- while( !(val= ml & c) ) c <<= 1;
- r->size = val;
- }
-
-#ifdef PCI_DEBUG
- printk(" readbase bus %d, (%04x:%04x), base %08x, size %08x, type %d\n",
- r->dev->bus->number,
- r->dev->vendor,
- r->dev->device,
- r->base,
- r->size,
- r->type );
-#endif
-
- /* Check for the blacklisted entries */
- insert_resource(r);
- }
-}
-
-static u_int pci_scan_bus(struct pci_bus *bus)
-{
- unsigned int devfn, max;
- uint32_t class;
- uint32_t l;
- 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) {
- uint32_t 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)
- {
- }
- }
-}
-
-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
-{
- uint32_t start_pcimem;
- uint32_t start_pciio;
- uint32_t 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;
-}
-
-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;
-
- pcibios_write_config_byte(pdev->bus->number, pdev->devfn, PCI_LATENCY_TIMER, 0x80 );
- pcibios_write_config_byte(pdev->bus->number, pdev->devfn, PCI_SEC_LATENCY_TIMER, 0x80 );
-
- {
- struct _addr_start addrhold;
- uint8_t base8, limit8;
- uint16_t 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 = (uint8_t) ((addrhold.start_pciio >> 8) & 0xf0);
- ubase16 = (uint16_t)(addrhold.start_pciio >> 16);
- limit8 = (uint8_t) ((astart.start_pciio >> 8 ) & 0xf0);
- ulimit16 = (uint16_t)(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= (uint16_t)((astart.start_pcimem >> 16) & 0xfff0);
- base16 = (uint16_t)((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= (uint16_t)((addrhold.start_prefetch >> 16) & 0xfff0);
- base16 = (uint16_t)((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,
- (uint16_t)( 0 ));
-
- pcibios_write_config_word(pdev->bus->number,
- pdev->devfn,
- PCI_COMMAND,
- (uint16_t)( PCI_COMMAND_IO |
- PCI_COMMAND_MEMORY |
- PCI_COMMAND_MASTER ));
-#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++ )) )
- {
- /*
- ** Force all memory spaces to be non-prefetchable because
- ** on the pci bus, byte-wise reads against prefetchable
- ** memory are applied as 32 bit reads, which is a pain
- ** when you're trying to talk to hardware. This is a
- ** little sub-optimal because the algorithm doesn't sort
- ** the address regions to pack them in, OTOH, perhaps its
- ** not so bad because the inefficient packing will help
- ** avoid buffer overflow/underflow problems.
- */
-#if 0
- 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
- }
-#endif
- 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 / PAGE_SIZE) + 1) * PAGE_SIZE);
-#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 / PAGE_SIZE) + 1) * PAGE_SIZE);
-#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 void *)
- (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! */
- uint32_t 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");
-
-#if 0
- print_pci_info();
-#endif
-}
-
-/* eof */