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

Subversion Repositories openrisc_me

[/] [openrisc/] [trunk/] [rtos/] [rtems/] [c/] [src/] [lib/] [libbsp/] [powerpc/] [shared/] [bootloader/] [pci.c] - Rev 30

Go to most recent revision | Compare with Previous | Blame | View Log

/*
 *  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.OARcorp.com/rtems/license.html.
 *
 * $Id: pci.c,v 1.2 2001-09-27 12:01:06 chris Exp $
 */
 
 
#include <sys/types.h>
#include <libcpu/spr.h>
#include "bootldr.h"
#include "pci.h"
#include <libcpu/io.h>
#include <bsp/consoleIo.h>
 
typedef unsigned int u32;
 
/*#define 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 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); 
	}
}
 
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.
	 */
 
	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;
	}
}
 
/* 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);
	}
}
 
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;
		}
	}
}
 
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;
}
 
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;
}
 
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;
}
 
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;
}
 
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;
}
 
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;
}
 
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
};
 
 
static int
direct_pci_read_config_byte(unsigned char bus, unsigned char dev_fn, 
			      unsigned char offset, unsigned char *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, unsigned short *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 u_short *)
		     (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, unsigned int *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 u_int *)
		     (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, unsigned char 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, unsigned short 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 u_short *)
		 (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, unsigned int 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 u_int *)
		 (pci->config_data + ((1<<PCI_SLOT(dev_fn))&~1)
		  + (PCI_FUNC(dev_fn)<<8) + offset),
		 val);
	return PCIBIOS_SUCCESSFUL;
}
 
static const struct pci_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
};
 
 
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;
	u32 l, ml;
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
 
	for(reg=0; reg<howmany; reg=nextreg) {
	  	pci_resource *r;
		nextreg=reg+1;
		pci_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_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;
}
 
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");
}
 
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

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