diff options
author | Joel Sherrill <joel.sherrill@OARcorp.com> | 2007-09-06 16:33:26 +0000 |
---|---|---|
committer | Joel Sherrill <joel.sherrill@OARcorp.com> | 2007-09-06 16:33:26 +0000 |
commit | 931e9cc00be093dbac74324c84973c377a1c6e27 (patch) | |
tree | 2d2dc4501007c4c9113f8edf81d76ec67cae21c0 /c/src/lib/libbsp/sparc/leon2/pci/pci.c | |
parent | 2007-09-06 Joel Sherrill <joel.sherrill@oarcorp.com> (diff) | |
download | rtems-931e9cc00be093dbac74324c84973c377a1c6e27.tar.bz2 |
2007-09-06 Daniel Hellstrom <daniel@gaisler.com>
* pci/pci.c: New file.
Diffstat (limited to 'c/src/lib/libbsp/sparc/leon2/pci/pci.c')
-rw-r--r-- | c/src/lib/libbsp/sparc/leon2/pci/pci.c | 675 |
1 files changed, 675 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/sparc/leon2/pci/pci.c b/c/src/lib/libbsp/sparc/leon2/pci/pci.c new file mode 100644 index 0000000000..0a8ebb5833 --- /dev/null +++ b/c/src/lib/libbsp/sparc/leon2/pci/pci.c @@ -0,0 +1,675 @@ +/* + * pci.c : this file contains basic PCI Io functions. + * + * Copyright (C) 1999 valette@crf.canon.fr + * + * This code is heavily inspired by the public specification of STREAM V2 + * that can be found at : + * + * <http://www.chorus.com/Documentation/index.html> by following + * the STREAM API Specification Document link. + * + * 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.2.4.4 2004/11/10 22:15:01 joel Exp + * + * Till Straumann, <strauman@slac.stanford.edu>, 1/2002 + * - separated bridge detection code out of this file + * + * + * Adapted to LEON2 AT697 PCI + * Copyright (C) 2006 Gaisler Research + * + */ + +#include <pci.h> +#include <rtems/bspIo.h> +#include <stdlib.h> + +/* Define PCI_INFO to get a listing of configured devices at boot time */ +#define PCI_INFO 1 + +/* #define DEBUG 1 */ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* allow for overriding these definitions */ +#ifndef PCI_CONFIG_ADDR +#define PCI_CONFIG_ADDR 0xcf8 +#endif +#ifndef PCI_CONFIG_DATA +#define PCI_CONFIG_DATA 0xcfc +#endif + +#define PCI_INVALID_VENDORDEVICEID 0xffffffff +#define PCI_MULTI_FUNCTION 0x80 + +/* define a shortcut */ +#define pci BSP_pci_configuration + +/* + * Bit encode for PCI_CONFIG_HEADER_TYPE register + */ +unsigned char ucMaxPCIBus; + +typedef struct { + volatile unsigned int pciid1; /* 0x80000100 - PCI Device identification register 1 */ + volatile unsigned int pcisc; /* 0x80000104 - PCI Status & Command */ + volatile unsigned int pciid2; /* 0x80000108 - PCI Device identification register 2 */ + volatile unsigned int pcibhlc; /* 0x8000010c - BIST, Header type, Cache line size register */ + volatile unsigned int mbar1; /* 0x80000110 - Memory Base Address Register 1 */ + volatile unsigned int mbar2; /* 0x80000114 - Memory Base Address Register 2 */ + volatile unsigned int iobar3; /* 0x80000118 - IO Base Address Register 3 */ + volatile unsigned int dummy1[4]; /* 0x8000011c - 0x80000128 */ + volatile unsigned int pcisid; /* 0x8000012c - Subsystem identification register */ + volatile unsigned int dummy2; /* 0x80000130 */ + volatile unsigned int pcicp; /* 0x80000134 - PCI capabilities pointer register */ + volatile unsigned int dummy3; /* 0x80000138 */ + volatile unsigned int pcili; /* 0x8000013c - PCI latency interrupt register */ + volatile unsigned int pcirt; /* 0x80000140 - PCI retry, trdy config */ + volatile unsigned int pcicw; /* 0x80000144 - PCI configuration write register */ + volatile unsigned int pcisa; /* 0x80000148 - PCI Initiator Start Address */ + volatile unsigned int pciiw; /* 0x8000014c - PCI Initiator Write Register */ + volatile unsigned int pcidma; /* 0x80000150 - PCI DMA configuration register */ + volatile unsigned int pciis; /* 0x80000154 - PCI Initiator Status Register */ + volatile unsigned int pciic; /* 0x80000158 - PCI Initiator Configuration */ + volatile unsigned int pcitpa; /* 0x8000015c - PCI Target Page Address Register */ + volatile unsigned int pcitsc; /* 0x80000160 - PCI Target Status-Command Register */ + volatile unsigned int pciite; /* 0x80000164 - PCI Interrupt Enable Register */ + volatile unsigned int pciitp; /* 0x80000168 - PCI Interrupt Pending Register */ + volatile unsigned int pciitf; /* 0x8000016c - PCI Interrupt Force Register */ + volatile unsigned int pcid; /* 0x80000170 - PCI Data Register */ + volatile unsigned int pcibe; /* 0x80000174 - PCI Burst End Register */ + volatile unsigned int pcidmaa; /* 0x80000178 - PCI DMA Address Register */ +} AT697_PCI_Map; + +AT697_PCI_Map *pcic = (AT697_PCI_Map *) 0x80000100; + +#define PCI_MEM_START 0xa0000000 +#define PCI_MEM_END 0xf0000000 +#define PCI_MEM_SIZE (PCI_MEM_START - PCI_MEM_END) + + +struct pci_res { + unsigned int size; + unsigned char bar; + unsigned char devfn; +}; + +/* The configuration access functions uses the DMA functionality of the + * AT697 pci controller to be able access all slots + */ + +int +pci_read_config_dword(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned int *val) { + + volatile unsigned int data; + + if (offset & 3) return PCIBIOS_BAD_REGISTER_NUMBER; + + pcic->pciitp = 0xff; /* clear interrupts */ + + pcic->pcisa = ( 1<<(11+slot) ) | ((function & 7)<<8) | (offset&0x3f); + pcic->pcidma = 0xa01; + pcic->pcidmaa = (unsigned int) &data; + + while (pcic->pciitp == 0) + ; + + pcic->pciitp = 0xff; /* clear interrupts */ + + if (pcic->pcisc & 0x20000000) { /* Master Abort */ + pcic->pcisc |= 0x20000000; + *val = 0xffffffff; + } + else + *val = data; + + DBG("pci_read - bus: %d, dev: %d, fn: %d, off: %d => addr: %x, val: %x\n", bus, slot, function, offset, (1<<(11+slot) ) | ((function & 7)<<8) | (offset&0x3f), *val); + + return PCIBIOS_SUCCESSFUL; +} + + +int +pci_read_config_word(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned short *val) { + unsigned int v; + + if (offset & 1) return PCIBIOS_BAD_REGISTER_NUMBER; + + pci_read_config_dword(bus, slot, function, offset&~3, &v); + *val = 0xffff & (v >> (8*(offset & 3))); + + return PCIBIOS_SUCCESSFUL; +} + + +int +pci_read_config_byte(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned char *val) { + unsigned int v; + + pci_read_config_dword(bus, slot, function, offset&~3, &v); + + *val = 0xff & (v >> (8*(offset & 3))); + + return PCIBIOS_SUCCESSFUL; +} + + +int +pci_write_config_dword(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned int val) { + + if (offset & 3) return PCIBIOS_BAD_REGISTER_NUMBER; + + pcic->pciitp = 0xff; /* clear interrupts */ + + pcic->pcisa = ( 1<<(11+slot) ) | ((function & 7)<<8) | (offset&0x3f); + pcic->pcidma = 0xb01; + pcic->pcidmaa = (unsigned int) &val; + + while (pcic->pciitp == 0) + ; + + if (pcic->pcisc & 0x20000000) { /* Master Abort */ + pcic->pcisc |= 0x20000000; + } + + pcic->pciitp = 0xff; /* clear interrupts */ + +/* DBG("pci write - bus: %d, dev: %d, fn: %d, off: %d => addr: %x, val: %x\n", bus, slot, function, offset, (1<<(11+slot) ) | ((function & 7)<<8) | (offset&0x3f), val); */ + + return PCIBIOS_SUCCESSFUL; +} + + +int +pci_write_config_word(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned short val) { + unsigned int v; + + if (offset & 1) return PCIBIOS_BAD_REGISTER_NUMBER; + + pci_read_config_dword(bus, slot, function, offset&~3, &v); + + v = (v & ~(0xffff << (8*(offset&3)))) | ((0xffff&val) << (8*(offset&3))); + + return pci_write_config_dword(bus, slot, function, offset&~3, v); +} + + +int +pci_write_config_byte(unsigned char bus, unsigned char slot, unsigned char function, unsigned char offset, unsigned char val) { + unsigned int v; + + pci_read_config_dword(bus, slot, function, offset&~3, &v); + + v = (v & ~(0xff << (8*(offset&3)))) | ((0xff&val) << (8*(offset&3))); + + return pci_write_config_dword(bus, slot, function, offset&~3, v); +} + + + +const pci_config_access_functions pci_access_functions = { + pci_read_config_byte, + pci_read_config_word, + pci_read_config_dword, + pci_write_config_byte, + pci_write_config_word, + pci_write_config_dword +}; + +pci_config BSP_pci_configuration = { (volatile unsigned char*)PCI_CONFIG_ADDR, + (volatile unsigned char*)PCI_CONFIG_DATA, + &pci_access_functions }; + + + + +void init_at697_pci(void) { + + /* Reset */ + pcic->pciic = 0xffffffff; + + /* Map system RAM at pci address 0x40000000 and system SDRAM to pci address 0x60000000 */ + pcic->mbar1 = 0x40000000; + pcic->mbar2 = 0x60000000; + pcic->pcitpa = 0x40006000; + + /* Enable PCI master and target memory command response */ + pcic->pcisc |= 0x40 | 0x6; + + /* Set latency timer to 64 */ + pcic->pcibhlc = 0x00004000; + + /* Set Inititator configuration so that AHB slave accesses generate memory read/write commands */ + pcic->pciic = 0x41; + + pcic->pciite = 0xff; + +} + +/* May not pass a 1k boundary */ +int dma_from_pci_1k(unsigned int addr, unsigned int paddr, unsigned char len) { + + int retval = 0; + + if (addr & 3) { + return -1; + } + + pcic->pciitp = 0xff; /* clear interrupts */ + + pcic->pcisa = paddr; + pcic->pcidma = 0xc00 | len; + pcic->pcidmaa = addr; + + while (pcic->pciitp == 0) + ; + + if (pcic->pciitp & 0x7F) { + retval = -1; + } + + pcic->pciitp = 0xff; /* clear interrupts */ + + if (pcic->pcisc & 0x20000000) { /* Master Abort */ + pcic->pcisc |= 0x20000000; + retval = -1; + } + + return retval; +} + +/* May not pass a 1k boundary */ +int dma_to_pci_1k(unsigned int addr, unsigned int paddr, unsigned char len) { + + int retval = 0; + + if (addr & 3) return -1; + + pcic->pciitp = 0xff; /* clear interrupts */ + + pcic->pcisa = paddr; + pcic->pcidma = 0x700 | len; + pcic->pcidmaa = addr; + + while (pcic->pciitp == 0) + ; + + if (pcic->pciitp & 0x7F) retval = -1; + + pcic->pciitp = 0xff; /* clear interrupts */ + + if (pcic->pcisc & 0x20000000) { /* Master Abort */ + pcic->pcisc |= 0x20000000; + retval = -1; + } + + return retval; +} + +/* Transfer len number of words from addr to paddr */ +int dma_to_pci(unsigned int addr, unsigned int paddr, unsigned int len) { + + int tmp_len; + + /* Align to 1k boundary */ + tmp_len = ((addr + 1024) & 0xfffffc00) - addr; + + tmp_len = (tmp_len/4 < len) ? tmp_len : (len*4); + + if (dma_to_pci_1k(addr, paddr, tmp_len/4) < 0) + return -1; + + addr += tmp_len; + paddr += tmp_len; + len -= tmp_len/4; + + /* Transfer all 1k blocks */ + while (len >= 128) { + + if (dma_to_pci_1k(addr, paddr, 128) < 0) + return -1; + + addr += 512; + paddr += 512; + len -= 128; + + } + + /* Transfer last words */ + if (len) return dma_to_pci_1k(addr, paddr, len); + + return 0; +} + +/* Transfer len number of words from paddr to addr */ +int dma_from_pci(unsigned int addr, unsigned int paddr, unsigned int len) { + + int tmp_len; + + /* Align to 1k boundary */ + tmp_len = ((addr + 1024) & 0xfffffc00) - addr; + + tmp_len = (tmp_len/4 < len) ? tmp_len : (len*4); + + if (dma_from_pci_1k(addr, paddr, tmp_len/4) < 0) + return -1; + + addr += tmp_len; + paddr += tmp_len; + len -= tmp_len/4; + + /* Transfer all 1k blocks */ + while (len >= 128) { + + if (dma_from_pci_1k(addr, paddr, 128) < 0) + return -1; + addr += 512; + paddr += 512; + len -= 128; + + } + + /* Transfer last words */ + if (len) return dma_from_pci_1k(addr, paddr, len); + + return 0; +} + +void pci_mem_enable(unsigned char bus, unsigned char slot, unsigned char function) { + unsigned int data; + + pci_read_config_dword(0, slot, function, PCI_COMMAND, &data); + pci_write_config_dword(0, slot, function, PCI_COMMAND, data | PCI_COMMAND_MEMORY); + +} + +void pci_master_enable(unsigned char bus, unsigned char slot, unsigned char function) { + unsigned int data; + + pci_read_config_dword(0, slot, function, PCI_COMMAND, &data); + pci_write_config_dword(0, slot, function, PCI_COMMAND, data | PCI_COMMAND_MASTER); + +} + +static inline void swap_res(struct pci_res **p1, struct pci_res **p2) { + + struct pci_res *tmp = *p1; + *p1 = *p2; + *p2 = tmp; + +} + +/* pci_allocate_resources + * + * This function scans the bus and assigns PCI addresses to all devices. It handles both + * single function and multi function devices. All allocated devices are enabled and + * latency timers are set to 40. + * + * NOTE that it only allocates PCI memory space devices. IO spaces are not enabled. + * Also, it does not handle pci-pci bridges. They are left disabled. + * + * +*/ +void pci_allocate_resources(void) { + + unsigned int slot, numfuncs, func, id, pos, size, tmp, i, swapped, addr, dev, fn; + unsigned char header; + struct pci_res **res; + + res = (struct pci_res **) malloc(sizeof(struct pci_res *)*32*8*6); + + for (i = 0; i < 32*8*6; i++) { + res[i] = (struct pci_res *) malloc(sizeof(struct pci_res)); + res[i]->size = 0; + res[i]->devfn = i; + } + + for(slot = 0; slot< PCI_MAX_DEVICES; slot++) { + + pci_read_config_dword(0, slot, 0, PCI_VENDOR_ID, &id); + + if(id == PCI_INVALID_VENDORDEVICEID || id == 0) { + /* + * This slot is empty + */ + continue; + } + + pci_read_config_byte(0, slot, 0, PCI_HEADER_TYPE, &header); + + if(header & PCI_MULTI_FUNCTION) { + numfuncs = PCI_MAX_FUNCTIONS; + } + else { + numfuncs = 1; + } + + for(func = 0; func < numfuncs; func++) { + + pci_read_config_dword(0, slot, func, PCI_VENDOR_ID, &id); + if(id == PCI_INVALID_VENDORDEVICEID || id == 0) { + continue; + } + + pci_read_config_dword(0, slot, func, PCI_CLASS_REVISION, &tmp); + tmp >>= 16; + if (tmp == PCI_CLASS_BRIDGE_PCI) { + continue; + } + + for (pos = 0; pos < 6; pos++) { + pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), 0xffffffff); + pci_read_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), &size); + + if (size == 0 || size == 0xffffffff || (size & 0xff) != 0) { + pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), 0); + continue; + } + + else { + res[slot*8*6+func*6+pos]->size = ~size+1; + res[slot*8*6+func*6+pos]->devfn = slot*8 + func; + res[slot*8*6+func*6+pos]->bar = pos; + + DBG("Slot: %d, function: %d, bar%d size: %x\n", slot, func, pos, ~size+1); + } + } + } + } + + + /* Sort the resources in descending order */ + + swapped = 1; + while (swapped == 1) { + swapped = 0; + for (i = 0; i < 32*8*6-1; i++) { + if (res[i]->size < res[i+1]->size) { + swap_res(&res[i], &res[i+1]); + swapped = 1; + } + } + i++; + } + + /* Assign the BARs */ + + addr = PCI_MEM_START; + for (i = 0; i < 32*8*6; i++) { + + if (res[i]->size == 0) { + goto done; + } + if ( (addr + res[i]->size) > PCI_MEM_END) { + printk("Out of PCI memory space, all devices not configured.\n"); + goto done; + } + + dev = res[i]->devfn >> 3; + fn = res[i]->devfn & 7; + + DBG("Assigning PCI addr %x to device %d, function %d, bar %d\n", addr, dev, fn, res[i]->bar); + pci_write_config_dword(0, dev, fn, PCI_BASE_ADDRESS_0+res[i]->bar*4, addr); + addr += res[i]->size; + + /* Set latency timer to 64 */ + pci_read_config_dword(0, dev, fn, 0xC, &tmp); + pci_write_config_dword(0, dev, fn, 0xC, tmp|0x4000); + + pci_mem_enable(0, dev, fn); + + } + + + +done: + +#ifdef PCI_INFO + printk("\nPCI devices found and configured:\n"); + for (slot = 0; slot < PCI_MAX_DEVICES; slot++) { + + pci_read_config_byte(0, slot, 0, PCI_HEADER_TYPE, &header); + + if(header & PCI_MULTI_FUNCTION) { + numfuncs = PCI_MAX_FUNCTIONS; + } + else { + numfuncs = 1; + } + + for (func = 0; func < numfuncs; func++) { + + pci_read_config_dword(0, slot, func, PCI_COMMAND, &tmp); + + if (tmp & PCI_COMMAND_MEMORY) { + + pci_read_config_dword(0, slot, func, PCI_VENDOR_ID, &id); + + if (id == PCI_INVALID_VENDORDEVICEID || id == 0) continue; + + printk("\nSlot %d function: %d\nVendor id: 0x%x, device id: 0x%x\n", slot, func, id & 0xffff, id>>16); + + for (pos = 0; pos < 6; pos++) { + pci_read_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + pos*4, &tmp); + + if (tmp != 0 && tmp != 0xffffffff && (tmp & 0xff) == 0) { + + printk("\tBAR %d: %x\n", pos, tmp); + } + + } + printk("\n"); + + } + + } + } + printk("\n"); +#endif + + for (i = 0; i < 1536; i++) { + free(res[i]); + } + free(res); +} + + + + + + + +/* + * This routine determines the maximum bus number in the system + */ +int init_pci() +{ + unsigned char ucSlotNumber, ucFnNumber, ucNumFuncs; + unsigned char ucHeader; + unsigned char ucMaxSubordinate; + unsigned int ulClass, ulDeviceID; + + init_at697_pci(); + pci_allocate_resources(); + +/* + * Scan PCI bus 0 looking for PCI-PCI bridges + */ + for(ucSlotNumber=0;ucSlotNumber<PCI_MAX_DEVICES;ucSlotNumber++) { + (void)pci_read_config_dword(0, + ucSlotNumber, + 0, + PCI_VENDOR_ID, + &ulDeviceID); + if(ulDeviceID==PCI_INVALID_VENDORDEVICEID) { +/* + * This slot is empty + */ + continue; + } + (void)pci_read_config_byte(0, + ucSlotNumber, + 0, + PCI_HEADER_TYPE, + &ucHeader); + if(ucHeader&PCI_MULTI_FUNCTION) { + ucNumFuncs=PCI_MAX_FUNCTIONS; + } + else { + ucNumFuncs=1; + } + for(ucFnNumber=0;ucFnNumber<ucNumFuncs;ucFnNumber++) { + (void)pci_read_config_dword(0, + ucSlotNumber, + ucFnNumber, + PCI_VENDOR_ID, + &ulDeviceID); + if(ulDeviceID==PCI_INVALID_VENDORDEVICEID) { +/* + * This slot/function is empty + */ + continue; + } + +/* + * This slot/function has a device fitted. + */ + (void)pci_read_config_dword(0, + ucSlotNumber, + ucFnNumber, + PCI_CLASS_REVISION, + &ulClass); + ulClass >>= 16; + if (ulClass == PCI_CLASS_BRIDGE_PCI) { +/* + * We have found a PCI-PCI bridge + */ + (void)pci_read_config_byte(0, + ucSlotNumber, + ucFnNumber, + PCI_SUBORDINATE_BUS, + &ucMaxSubordinate); + if(ucMaxSubordinate>ucMaxPCIBus) { + ucMaxPCIBus=ucMaxSubordinate; + } + } + } + } + return 0; +} + +/* + * Return the number of PCI busses in the system + */ +unsigned char BusCountPCI() +{ + return(ucMaxPCIBus+1); +} |