summaryrefslogtreecommitdiffstats
path: root/c
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>2007-09-06 13:24:03 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>2007-09-06 13:24:03 +0000
commitce40d306b32aaee573c401043af3ca7f61c8807b (patch)
tree5fe479bde678f025cd07954db8b0441d26d6bd9f /c
parent2007-09-06 Daniel Hellstrom <daniel@gaisler.com> (diff)
downloadrtems-ce40d306b32aaee573c401043af3ca7f61c8807b.tar.bz2
2007-09-06 Daniel Hellstrom <daniel@gaisler.com>
* pci/pci.c: New file missed on previous commit.
Diffstat (limited to 'c')
-rw-r--r--c/src/lib/libbsp/sparc/leon3/ChangeLog4
-rw-r--r--c/src/lib/libbsp/sparc/leon3/pci/pci.c577
2 files changed, 581 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/sparc/leon3/ChangeLog b/c/src/lib/libbsp/sparc/leon3/ChangeLog
index ef6e775e4e..e38d52c2a5 100644
--- a/c/src/lib/libbsp/sparc/leon3/ChangeLog
+++ b/c/src/lib/libbsp/sparc/leon3/ChangeLog
@@ -1,5 +1,9 @@
2007-09-06 Daniel Hellstrom <daniel@gaisler.com>
+ * pci/pci.c: New file missed on previous commit.
+
+2007-09-06 Daniel Hellstrom <daniel@gaisler.com>
+
* Makefile.am, preinstall.am: New files, split of printk.
* console/console.c, console/debugputs.c: Split printk support out.
* include/spacewire.h: Removed.
diff --git a/c/src/lib/libbsp/sparc/leon3/pci/pci.c b/c/src/lib/libbsp/sparc/leon3/pci/pci.c
new file mode 100644
index 0000000000..b959600fdf
--- /dev/null
+++ b/c/src/lib/libbsp/sparc/leon3/pci/pci.c
@@ -0,0 +1,577 @@
+/*
+ * 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 GRPCI
+ * Copyright (C) 2006 Gaisler Research
+ *
+ */
+
+#include <pci.h>
+#include <rtems/bspIo.h>
+
+#define PCI_ADDR 0x80000400
+#define DMAPCI_ADDR 0x80000500
+#define PCI_CONF 0xfff50000
+#define PCI_MEM_START 0xe0000000
+#define PCI_MEM_END 0xf0000000
+#define PCI_MEM_SIZE (PCI_MEM_START - PCI_MEM_END)
+
+/* If uncommented byte twisting is enabled */
+/*#define BT_ENABLED 1*/
+
+/* 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 cfg_stat;
+ volatile unsigned int bar0;
+ volatile unsigned int page0;
+ volatile unsigned int bar1;
+ volatile unsigned int page1;
+ volatile unsigned int iomap;
+ volatile unsigned int stat_cmd;
+} LEON3_GRPCI_Regs_Map;
+
+LEON3_GRPCI_Regs_Map *pcic = (LEON3_GRPCI_Regs_Map *) PCI_ADDR;
+unsigned int *pcidma = (unsigned int *)DMAPCI_ADDR;
+
+struct pci_res {
+ unsigned int size;
+ unsigned char bar;
+ unsigned char devfn;
+};
+
+static inline unsigned int flip_dword (unsigned int l)
+{
+ return ((l&0xff)<<24) | (((l>>8)&0xff)<<16) | (((l>>16)&0xff)<<8)| ((l>>24)&0xff);
+}
+
+
+/* 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 *pci_conf;
+
+ if (offset & 3) return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ if (slot > 21) {
+ *val = 0xffffffff;
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ pci_conf = PCI_CONF + ((slot<<11) | (function<<8) | offset);
+
+#ifdef BT_ENABLED
+ *val = flip_dword(*pci_conf);
+#else
+ *val = *pci_conf;
+#endif
+
+ if (pcic->cfg_stat & 0x100) {
+ *val = 0xffffffff;
+ }
+
+ 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) {
+
+ volatile unsigned int *pci_conf;
+ unsigned int value;
+
+ if (offset & 3 || bus != 0) return PCIBIOS_BAD_REGISTER_NUMBER;
+
+
+ pci_conf = PCI_CONF + ((slot<<11) | (function<<8) | (offset & ~3));
+
+#ifdef BT_ENABLED
+ value = flip_dword(val);
+#else
+ value = val;
+#endif
+
+ *pci_conf = value;
+
+ 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), value);
+
+ 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 };
+
+
+
+
+int init_grpci(void) {
+
+ volatile unsigned int *page0 = (unsigned volatile int *) PCI_MEM_START;
+ unsigned int data, addr;
+
+#ifndef BT_ENABLED
+ pci_write_config_dword(0,0,0,0x10, 0xffffffff);
+ pci_read_config_dword(0,0,0,0x10, &addr);
+ pci_write_config_dword(0,0,0,0x10, flip_dword(0x10000000)); /* Setup bar0 to nonzero value (grpci considers BAR==0 as invalid) */
+ addr = (~flip_dword(addr)+1)>>1; /* page0 is accessed through upper half of bar0 */
+ pcic->cfg_stat |= 0x10000000; /* Setup mmap reg so we can reach bar0 */
+ page0[addr/4] = 0; /* Disable bytetwisting ... */
+#endif
+
+ /* set 1:1 mapping between AHB -> PCI memory */
+ pcic->cfg_stat = (pcic->cfg_stat & 0x0fffffff) | PCI_MEM_START;
+
+ /* and map system RAM at pci address 0x40000000 */
+ pci_write_config_dword(0, 0, 0, 0x14, 0x40000000);
+ pcic->page1 = 0x40000000;
+
+ /* set as bus master and enable pci memory responses */
+ pci_read_config_dword(0, 0, 0, 0x4, &data);
+ pci_write_config_dword(0, 0, 0, 0x4, data | 0x6);
+
+ return 0;
+}
+
+/* DMA functions which uses GRPCIs optional DMA controller (len in words) */
+int dma_to_pci(unsigned int ahb_addr, unsigned int pci_addr, unsigned int len) {
+ int ret = 0;
+
+ pcidma[0] = 0x82;
+ pcidma[1] = ahb_addr;
+ pcidma[2] = pci_addr;
+ pcidma[3] = len;
+ pcidma[0] = 0x83;
+
+ while ( (pcidma[0] & 0x4) == 0)
+ ;
+
+ if (pcidma[0] & 0x8) { /* error */
+ ret = -1;
+ }
+
+ pcidma[0] |= 0xC;
+ return ret;
+
+}
+
+int dma_from_pci(unsigned int ahb_addr, unsigned int pci_addr, unsigned int len) {
+ int ret = 0;
+
+ pcidma[0] = 0x80;
+ pcidma[1] = ahb_addr;
+ pcidma[2] = pci_addr;
+ pcidma[3] = len;
+ pcidma[0] = 0x81;
+
+ while ( (pcidma[0] & 0x4) == 0)
+ ;
+
+ if (pcidma[0] & 0x8) { /* error */
+ ret = -1;
+ }
+
+ pcidma[0] |= 0xC;
+ return ret;
+
+}
+
+
+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 (that are at least 1 KB).
+ * 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 = 1; 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 & 0x3f1) != 0){
+ pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0 + (pos<<2), 0);
+ continue;
+
+ }else {
+ size &= 0xfffffff0;
+ 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 = 1; 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 & 0x3f1) == 0) {
+
+ printk("\tBAR %d: %x\n", pos, tmp);
+ }
+
+ }
+ printk("\n");
+
+ }
+
+ }
+ }
+ printk("\n");
+#endif
+
+ for (i = 0; i < 1536; i++) {
+ free(res[i]);
+ }
+ free(res);
+}
+
+
+
+int init_pci()
+{
+ unsigned char ucSlotNumber, ucFnNumber, ucNumFuncs;
+ unsigned char ucHeader;
+ unsigned char ucMaxSubordinate;
+ unsigned int ulClass, ulDeviceID;
+
+ DBG("Initializing PCI\n");
+ if ( init_grpci() ) {
+ return -1;
+ }
+ pci_allocate_resources();
+ DBG("PCI resource allocation done\n");
+/*
+ * 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);
+}