diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 12:50:58 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 15:18:45 +0200 |
commit | 5a4e3dc0a5af631b1723858bc50a9af1545392fb (patch) | |
tree | f2825a86e99fbf1982fc484278483a03728ccc3a /bsps | |
parent | bsps/sh: Move console.c to bsps (diff) | |
download | rtems-5a4e3dc0a5af631b1723858bc50a9af1545392fb.tar.bz2 |
bsps: Move PCI drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps')
-rw-r--r-- | bsps/i386/shared/pci/pci_io.c | 213 | ||||
-rw-r--r-- | bsps/i386/shared/pci/pcibios.c | 479 | ||||
-rw-r--r-- | bsps/mips/malta/pci/pci.c | 1102 | ||||
-rw-r--r-- | bsps/powerpc/beatnik/pci/gt_pci_init.c | 270 | ||||
-rw-r--r-- | bsps/powerpc/beatnik/pci/motload_fixup.c | 180 | ||||
-rw-r--r-- | bsps/powerpc/beatnik/pci/pci_io_remap.c | 203 | ||||
-rw-r--r-- | bsps/powerpc/beatnik/pci/pci_io_remap.h | 66 | ||||
-rw-r--r-- | bsps/powerpc/mvme3100/pci/detect_host_bridge.c | 113 | ||||
-rw-r--r-- | bsps/powerpc/mvme5500/pci/detect_host_bridge.c | 72 | ||||
-rw-r--r-- | bsps/powerpc/mvme5500/pci/pci.c | 420 | ||||
-rw-r--r-- | bsps/powerpc/mvme5500/pci/pci_interface.c | 100 | ||||
-rw-r--r-- | bsps/powerpc/shared/pci/detect_raven_bridge.c | 197 | ||||
-rw-r--r-- | bsps/powerpc/shared/pci/generic_clear_hberrs.c | 63 | ||||
-rw-r--r-- | bsps/powerpc/shared/pci/pci.c | 640 | ||||
-rw-r--r-- | bsps/powerpc/shared/pci/pcifinddevice.c | 212 |
15 files changed, 4330 insertions, 0 deletions
diff --git a/bsps/i386/shared/pci/pci_io.c b/bsps/i386/shared/pci/pci_io.c new file mode 100644 index 0000000000..b6cd1005f0 --- /dev/null +++ b/bsps/i386/shared/pci/pci_io.c @@ -0,0 +1,213 @@ +/** + * @file + * + * PCI Support when Configuration Space is in I/O + */ + +/* + * COPYRIGHT (c) 2016. + * On-Line Applications Research Corporation (OAR). + * + * 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 <rtems.h> +#include <bsp.h> +#include <bsp/bspimpl.h> + +static int pci_io_initialized = 0; + +/* + * Forward reference. Initialized at bottom. + */ +static const pci_config_access_functions pci_io_indirect_functions; + +/* + * Detects presense of PCI Configuration is in I/O space. If so, return + * pointer to accessor methods. + * + * NOTE: TBD to determine if (a) PCI Bus exists and (b) this is the + * access method. + */ +const pci_config_access_functions *pci_io_initialize(void) +{ + pci_io_initialized = 1; + + printk( "PCI I/O Support Initialized\n" ); + + return &pci_io_indirect_functions; +} + +/* + * Build PCI Address + */ +static inline uint32_t pci_io_build_address( + uint16_t bus, + uint16_t slot, + uint16_t function, + uint16_t offset +) +{ + uint32_t bus_u32 = (uint32_t)bus; + uint32_t slot_u32 = (uint32_t)slot; + uint32_t function_u32 = (uint32_t)function; + uint32_t address; + + /* + * create configuration address as per figure at + * http://wiki.osdev.org/PCI#Configuration_Space_Access_Mechanism_.231 + */ + address = (uint32_t) 0x80000000; /* Bit 31 - Enable Bit */ + /* Bits 30-24 - Reserved */ + address |= bus_u32 << 16; /* Bits 23-16 - Bus Number */ + address |= slot_u32 << 11; /* Bits 15-11 - Device/Slot Number */ + address |= function_u32 << 8; /* Bits 10-8 - Function Number */ + address |= offset & 0xfc; /* Bits 7-2 - Offset/Register Number */ + /* Bits 1-0 - Reserved 0 */ + return address; +} + +static int BSP_pci_read_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + unsigned char *value +) +{ + uint32_t address; + uint32_t tmp; + + address = pci_io_build_address( bus, slot, function, offset ); + + /* write out the address */ + outport_long(0xCF8, address); + + /* read in the data */ + inport_long(0xCFC, tmp); + + /* (offset & 3) * 8) = 0 will choose the first byte of the 32 bits register */ + *value = (uint16_t)(tmp >> ((offset & 3) * 8)) & 0xff; + return PCIBIOS_SUCCESSFUL; +} + +static int BSP_pci_read_config_word( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + unsigned short *value +) +{ + uint32_t address; + uint32_t tmp; + + address = pci_io_build_address( bus, slot, function, offset ); + + /* write out the address */ + outport_long(0xCF8, address); + + /* read in the data */ + inport_long(0xCFC, tmp); + + /* (offset & 2) * 8) = 0 will choose the first word of the 32 bits register */ + *value = (uint16_t)(tmp >> ((offset & 2) * 8)) & 0xffff; + return PCIBIOS_SUCCESSFUL; +} + +static int BSP_pci_read_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint32_t *value +) +{ + uint32_t address; + uint32_t tmp; + + address = pci_io_build_address( bus, slot, function, offset ); + + /* write out the address */ + outport_long(0xCF8, address); + + /* read in the data */ + inport_long(0xCFC, tmp); + + *value = tmp; + return PCIBIOS_SUCCESSFUL; +} + +static int BSP_pci_write_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + unsigned char value +) +{ + uint32_t address; + + address = pci_io_build_address( bus, slot, function, offset ); + + /* write out the address */ + outport_long(0xCF8, address); + + /* read in the data */ + outport_byte(0xCFC, value); + + return PCIBIOS_SUCCESSFUL; +} + +static int BSP_pci_write_config_word( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + unsigned short value +) +{ + uint32_t address; + + address = pci_io_build_address( bus, slot, function, offset ); + + /* write out the address */ + outport_long(0xCF8, address); + + /* read in the data */ + outport_word(0xCFC, value); + + return PCIBIOS_SUCCESSFUL; +} + +static int BSP_pci_write_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint32_t value +) +{ + uint32_t address; + + address = pci_io_build_address( bus, slot, function, offset ); + + /* write out the address */ + outport_long(0xCF8, address); + + /* read in the data */ + outport_long(0xCFC, value); + + return PCIBIOS_SUCCESSFUL; +} + +static const pci_config_access_functions pci_io_indirect_functions = { + BSP_pci_read_config_byte, + BSP_pci_read_config_word, + BSP_pci_read_config_dword, + BSP_pci_write_config_byte, + BSP_pci_write_config_word, + BSP_pci_write_config_dword +}; diff --git a/bsps/i386/shared/pci/pcibios.c b/bsps/i386/shared/pci/pcibios.c new file mode 100644 index 0000000000..173c89f3f8 --- /dev/null +++ b/bsps/i386/shared/pci/pcibios.c @@ -0,0 +1,479 @@ +/** + * @file + * + * PCI Support when Configuration Space is accessed via BIOS + */ + +/* + * This software is Copyright (C) 1998 by T.sqware - all rights limited + * It is provided in to the public domain "as is", can be freely modified + * as far as this copyight notice is kept unchanged, but does not imply + * an endorsement by T.sqware of the product in which it is included. + */ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/pci.h> + +#include <string.h> /* memcpy */ + +/* + * This is simpliest possible PCI BIOS, it assumes that addressing + * is flat and that stack is big enough + */ +static int pcibInitialized = 0; +static unsigned int pcibEntry; + +/* + * Array to pass data between c and __asm__ parts, at the time of + * writing I am not yet that familiar with extended __asm__ feature + * of gcc. This code is not on performance path, so we can care + * relatively little about performance here + */ +static volatile unsigned int pcibExchg[5]; + +static int pcib_convert_err(int err); + +/** @brief + * Make device signature from bus number, device numebr and function + * number + */ +#define PCIB_DEVSIG_MAKE(b,d,f) ((b<<8)|(d<<3)|(f)) + +/** @brief + * Extract valrous part from device signature + */ +#define PCIB_DEVSIG_BUS(x) (((x)>>8) &0xff) +#define PCIB_DEVSIG_DEV(x) (((x)>>3) & 0x1f) +#define PCIB_DEVSIG_FUNC(x) ((x) & 0x7) + +/* + * Forward reference. Initialized at bottom. + */ +static const pci_config_access_functions pci_bios_indirect_functions; + +/* prototype before defining */ +const pci_config_access_functions *pci_bios_initialize(void); + +/* + * Detects presense of PCI BIOS, returns pointer to accessor methods. + */ +const pci_config_access_functions *pci_bios_initialize(void) +{ + unsigned char *ucp; + unsigned char sum; + int i; + + pcibInitialized = 0; + + /* First, we have to look for BIOS-32 */ + for (ucp = (unsigned char *)0xE0000; + ucp < (unsigned char *)0xFFFFF; + ucp += 0x10) { + if (memcmp(ucp, "_32_", 4) != 0) { + continue; + } + + /* Got signature, check length */ + if (*(ucp + 9) != 1) { + continue; + } + + /* Verify checksum */ + sum = 0; + for (i=0; i<16; i++) { + sum += *(ucp+i); + } + + if (sum == 0) { + /* found */ + break; + } + } + + if (ucp >= (unsigned char *)0xFFFFF) { + /* BIOS-32 not found */ + return NULL; + } + + /* BIOS-32 found, let us find PCI BIOS */ + ucp += 4; + + pcibExchg[0] = *(unsigned int *)ucp; + + __asm__ (" pusha"); /* Push all registers */ + __asm__ (" movl pcibExchg, %edi"); /* Move entry point to esi */ + __asm__ (" movl $0x49435024, %eax"); /* Move signature to eax */ + __asm__ (" xorl %ebx, %ebx"); /* Zero ebx */ + __asm__ (" pushl %cs"); + __asm__ (" call *%edi"); /* Call entry */ + __asm__ (" movl %eax, pcibExchg"); + __asm__ (" movl %ebx, pcibExchg+4"); + __asm__ (" movl %ecx, pcibExchg+8"); + __asm__ (" movl %edx, pcibExchg+12"); + __asm__ (" popa"); + + if ((pcibExchg[0] & 0xff) != 0) { + /* Not found */ + return NULL; + } + + /* Found PCI entry point */ + pcibEntry = pcibExchg[1] + pcibExchg[3]; + + /* Let us check whether PCI bios is present */ + pcibExchg[0] = pcibEntry; + + __asm__ (" pusha"); + __asm__ (" movl pcibExchg, %edi"); + __asm__ (" movb $0xb1, %ah"); + __asm__ (" movb $0x01, %al"); + __asm__ (" pushl %cs"); + __asm__ (" call *%edi"); + __asm__ (" movl %eax, pcibExchg"); + __asm__ (" movl %ebx, pcibExchg+4"); + __asm__ (" movl %ecx, pcibExchg+8"); + __asm__ (" movl %edx, pcibExchg+12"); + __asm__ (" popa"); + + if ((pcibExchg[0] & 0xff00) != 0) { + /* Not found */ + return NULL; + } + + if (pcibExchg[3] != 0x20494350) { + /* Signature does not match */ + return NULL; + } + + /* Success */ + pcibInitialized = 1; + + return &pci_bios_indirect_functions; +} + +/* + * Read byte from config space + */ +static int +pcib_conf_read8(int sig, int off, uint8_t *data) +{ + if (!pcibInitialized) { + return PCIB_ERR_UNINITIALIZED; + } + + pcibExchg[0] = pcibEntry; + pcibExchg[1] = sig; + pcibExchg[2] = off; + + __asm__ (" pusha"); + __asm__ (" movl pcibExchg, %esi"); + __asm__ (" movb $0xb1, %ah"); + __asm__ (" movb $0x08, %al"); + __asm__ (" movl pcibExchg+4, %ebx"); + __asm__ (" movl pcibExchg+8, %edi"); + __asm__ (" pushl %cs"); + __asm__ (" call *%esi"); + __asm__ (" movl %eax, pcibExchg"); + __asm__ (" movl %ecx, pcibExchg+4"); + __asm__ (" popa"); + + if ((pcibExchg[0] & 0xff00) != 0) { + return pcib_convert_err((pcibExchg[0] >> 8) & 0xff); + } + + *data = (unsigned char)pcibExchg[1] & 0xff; + + return PCIB_ERR_SUCCESS; +} + + +/* + * Read word from config space + */ +static int +pcib_conf_read16(int sig, int off, uint16_t *data) +{ + if (!pcibInitialized) { + return PCIB_ERR_UNINITIALIZED; + } + + pcibExchg[0] = pcibEntry; + pcibExchg[1] = sig; + pcibExchg[2] = off; + + __asm__ (" pusha"); + __asm__ (" movl pcibExchg, %esi"); + __asm__ (" movb $0xb1, %ah"); + __asm__ (" movb $0x09, %al"); + __asm__ (" movl pcibExchg+4, %ebx"); + __asm__ (" movl pcibExchg+8, %edi"); + __asm__ (" pushl %cs"); + __asm__ (" call *%esi"); + __asm__ (" movl %eax, pcibExchg"); + __asm__ (" movl %ecx, pcibExchg+4"); + __asm__ (" popa"); + + if ((pcibExchg[0] & 0xff00) != 0) { + return pcib_convert_err((pcibExchg[0] >> 8) & 0xff); + } + + *data = (unsigned short)pcibExchg[1] & 0xffff; + + return PCIB_ERR_SUCCESS; +} + + +/* + * Read dword from config space + */ +static int +pcib_conf_read32(int sig, int off, uint32_t *data) +{ + if (!pcibInitialized) { + return PCIB_ERR_UNINITIALIZED; + } + + pcibExchg[0] = pcibEntry; + pcibExchg[1] = sig; + pcibExchg[2] = off; + + __asm__ (" pusha"); + __asm__ (" movl pcibExchg, %esi"); + __asm__ (" movb $0xb1, %ah"); + __asm__ (" movb $0x0a, %al"); + __asm__ (" movl pcibExchg+4, %ebx"); + __asm__ (" movl pcibExchg+8, %edi"); + __asm__ (" pushl %cs"); + __asm__ (" call *%esi"); + __asm__ (" movl %eax, pcibExchg"); + __asm__ (" movl %ecx, pcibExchg+4"); + __asm__ (" popa"); + + if ((pcibExchg[0] & 0xff00) != 0) { + return pcib_convert_err((pcibExchg[0] >> 8) & 0xff); + } + + *data = (unsigned int)pcibExchg[1]; + + return PCIB_ERR_SUCCESS; +} + + +/* + * Write byte into config space + */ +static int +pcib_conf_write8(int sig, int off, uint8_t data) +{ + if (!pcibInitialized) { + return PCIB_ERR_UNINITIALIZED; + } + + pcibExchg[0] = pcibEntry; + pcibExchg[1] = sig; + pcibExchg[2] = off; + pcibExchg[3] = data & 0xff; + + __asm__ (" pusha"); + __asm__ (" movl pcibExchg, %esi"); + __asm__ (" movb $0xb1, %ah"); + __asm__ (" movb $0x0b, %al"); + __asm__ (" movl pcibExchg+4, %ebx"); + __asm__ (" movl pcibExchg+8, %edi"); + __asm__ (" movl pcibExchg+12, %ecx"); + __asm__ (" pushl %cs"); + __asm__ (" call *%esi"); + __asm__ (" movl %eax, pcibExchg"); + __asm__ (" popa"); + + return pcib_convert_err((pcibExchg[0] >> 8) & 0xff); +} + +/* + * Write word into config space + */ +static int +pcib_conf_write16(int sig, int off, uint16_t data) +{ + if (!pcibInitialized) { + return PCIB_ERR_UNINITIALIZED; + } + + pcibExchg[0] = pcibEntry; + pcibExchg[1] = sig; + pcibExchg[2] = off; + pcibExchg[3] = data & 0xffff; + + __asm__ (" pusha"); + __asm__ (" movl pcibExchg, %esi"); + __asm__ (" movb $0xb1, %ah"); + __asm__ (" movb $0x0c, %al"); + __asm__ (" movl pcibExchg+4, %ebx"); + __asm__ (" movl pcibExchg+8, %edi"); + __asm__ (" movl pcibExchg+12, %ecx"); + __asm__ (" pushl %cs"); + __asm__ (" call *%esi"); + __asm__ (" movl %eax, pcibExchg"); + __asm__ (" popa"); + + return pcib_convert_err((pcibExchg[0] >> 8) & 0xff); +} + + + +/* + * Write dword into config space + */ +static int +pcib_conf_write32(int sig, int off, uint32_t data) +{ + if (!pcibInitialized){ + return PCIB_ERR_UNINITIALIZED; + } + + pcibExchg[0] = pcibEntry; + pcibExchg[1] = sig; + pcibExchg[2] = off; + pcibExchg[3] = data; + + __asm__ (" pusha"); + __asm__ (" movl pcibExchg, %esi"); + __asm__ (" movb $0xb1, %ah"); + __asm__ (" movb $0x0d, %al"); + __asm__ (" movl pcibExchg+4, %ebx"); + __asm__ (" movl pcibExchg+8, %edi"); + __asm__ (" movl pcibExchg+12, %ecx"); + __asm__ (" pushl %cs"); + __asm__ (" call *%esi"); + __asm__ (" movl %eax, pcibExchg"); + __asm__ (" popa"); + + return pcib_convert_err((pcibExchg[0] >> 8) & 0xff); +} + + +static int +pcib_convert_err(int err) +{ + switch(err & 0xff){ + case 0: + return PCIB_ERR_SUCCESS; + case 0x81: + return PCIB_ERR_NOFUNC; + case 0x83: + return PCIB_ERR_BADVENDOR; + case 0x86: + return PCIB_ERR_DEVNOTFOUND; + case 0x87: + return PCIB_ERR_BADREG; + default: + break; + } + return PCIB_ERR_NOFUNC; +} + +static int +BSP_pci_read_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char fun, + unsigned char offset, + unsigned char *val +) +{ + int sig; + + sig = PCIB_DEVSIG_MAKE(bus,slot,fun); + pcib_conf_read8(sig, offset, val); + return PCIBIOS_SUCCESSFUL; +} + +static int +BSP_pci_read_config_word( + unsigned char bus, + unsigned char slot, + unsigned char fun, + unsigned char offset, + unsigned short *val +) +{ + int sig; + + sig = PCIB_DEVSIG_MAKE(bus,slot,fun); + pcib_conf_read16(sig, offset, val); + return PCIBIOS_SUCCESSFUL; +} + +static int +BSP_pci_read_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char fun, + unsigned char offset, + uint32_t *val +) +{ + int sig; + + sig = PCIB_DEVSIG_MAKE(bus,slot,fun); + pcib_conf_read32(sig, offset, val); + return PCIBIOS_SUCCESSFUL; +} + +static int +BSP_pci_write_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char fun, + unsigned char offset, + unsigned char val +) +{ + int sig; + + sig = PCIB_DEVSIG_MAKE(bus,slot,fun); + pcib_conf_write8(sig, offset, val); + return PCIBIOS_SUCCESSFUL; +} + +static int +BSP_pci_write_config_word( + unsigned char bus, + unsigned char slot, + unsigned char fun, + unsigned char offset, + unsigned short val +) +{ + int sig; + + sig = PCIB_DEVSIG_MAKE(bus,slot,fun); + pcib_conf_write16(sig, offset, val); + return PCIBIOS_SUCCESSFUL; +} + +static int +BSP_pci_write_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char fun, + unsigned char offset, + uint32_t val +) +{ + int sig; + + sig = PCIB_DEVSIG_MAKE(bus,slot,fun); + pcib_conf_write32(sig, offset, val); + return PCIBIOS_SUCCESSFUL; +} + +static const pci_config_access_functions pci_bios_indirect_functions = { + BSP_pci_read_config_byte, + BSP_pci_read_config_word, + BSP_pci_read_config_dword, + BSP_pci_write_config_byte, + BSP_pci_write_config_word, + BSP_pci_write_config_dword +}; diff --git a/bsps/mips/malta/pci/pci.c b/bsps/mips/malta/pci/pci.c new file mode 100644 index 0000000000..0932f07d35 --- /dev/null +++ b/bsps/mips/malta/pci/pci.c @@ -0,0 +1,1102 @@ +/** + * @file + * + * This file was based on the powerpc. + */ + +/* + * COPYRIGHT (c) 1989-2012. + * On-Line Applications Research Corporation (OAR). + * + * 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 <rtems.h> +#include <bsp.h> + +#include <bsp/pci.h> +#include <bsp/irq.h> +#include <rtems/bspIo.h> +#include <rtems/endian.h> + +/* + * DEFINES + */ + +#undef SHOW_PCI_SETTING + +// #define DEBUG_PCI 1 + +/* 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 a shortcut */ +#define pci BSP_pci_configuration + +#ifndef PCI_CONFIG_ADDR_VAL +#define PCI_CONFIG_ADDR_VAL(bus, slot, funcion, offset) \ + (0x80000000|((bus)<<16)|(PCI_DEVFN((slot),(function))<<8)|(((offset)&~3))) +#endif + +#ifdef DEBUG_PCI + #define JPRINTK(fmt, ...) printk("%s: " fmt, __FUNCTION__, ##__VA_ARGS__) +#else + #define JPRINTK(fmt, ...) +#endif + +#ifndef PCI_CONFIG_WR_ADDR +#define PCI_CONFIG_WR_ADDR( addr, val ) out_le32((uint32_t)(addr), (val)) +#endif + +/* Bit encode for PCI_CONFIG_HEADER_TYPE register */ +#define PCI_CONFIG_SET_ADDR(addr, bus, slot,function,offset) \ + PCI_CONFIG_WR_ADDR( \ + (addr), \ + PCI_CONFIG_ADDR_VAL((bus), (slot), (function), (offset))\ + ) + +#define PRINT_MSG() \ + printk("pci : Device %d:0x%02x:%d routed to interrupt_line %d\n", \ + pbus, pslot, pfun, int_name ) + +/* + * STRUCTURES + */ + +/* + * PROTOTYPES + */ +void print_bars( + unsigned char slot, + unsigned char func +); +int direct_pci_read_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint8_t *val +); +int direct_pci_read_config_word( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint16_t *val +); +int direct_pci_read_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint32_t *val +); +int direct_pci_write_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint8_t val +); +int direct_pci_write_config_word( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint16_t val +); +int direct_pci_write_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint32_t val +); +int test_intname( + const struct _int_map *row, + int pbus, + int pslot, + int pfun, + int int_pin, + int int_name +); +void pci_memory_enable( + unsigned char bus, + unsigned char slot, + unsigned char function +); +void pci_io_enable( + unsigned char bus, + unsigned char slot, + unsigned char function +); +void pci_busmaster_enable( + unsigned char bus, + unsigned char slot, + unsigned char function +); + +/* + * GLOBALS + */ +unsigned char ucMaxPCIBus; +const pci_config_access_functions pci_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 +}; + +rtems_pci_config_t BSP_pci_configuration = { + (volatile unsigned char*)PCI_CONFIG_ADDR, + (volatile unsigned char*)PCI_CONFIG_DATA, + &pci_indirect_functions +}; + +const pci_config_access_functions pci_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 +}; + +/* + * PCI specific accesses. Note these are made on 32 bit + * boundries. + */ +void pci_out_32(uint32_t base, uint32_t addr, uint32_t val) +{ + volatile uint32_t *ptr; + + ptr = (volatile uint32_t *) (base + addr); + *ptr = val; + + JPRINTK( "%p data: 0x%x\n", ptr, val); +} + +void pci_out_le32(uint32_t base, uint32_t addr, uint32_t val) +{ + volatile uint32_t *ptr; + uint32_t data = 0; + + ptr = (volatile uint32_t *) (base + addr); + rtems_uint32_to_little_endian( val, (uint8_t *) &data); + *ptr = data; + + JPRINTK( "%p data: 0x%x\n", ptr, data); +} + +uint8_t pci_in_8( uint32_t base, uint32_t addr ) { + volatile uint32_t *ptr; + uint8_t val; + uint32_t data; + + data = addr/4; + ptr = (volatile uint32_t *) (base + (data*4)); + data = *ptr; + + switch ( addr%4 ) { + case 0: val = (data & 0x000000ff) >> 0; break; + case 1: val = (data & 0x0000ff00) >> 8; break; + case 2: val = (data & 0x00ff0000) >> 16; break; + case 3: val = (data & 0xff000000) >> 24; break; + } + + JPRINTK( "0x%x data: 0x%x raw: 0x%x\n", ptr, val, data); + + return val; +} + +int16_t pci_in_le16( uint32_t base, uint32_t addr ) { + volatile uint32_t *ptr; + uint16_t val; + uint16_t rval; + uint32_t data; + + data = addr/4; + ptr = (volatile uint32_t *) (base + (data*4)); + data = *ptr; + if ( addr%4 == 0 ) + val = data & 0xffff; + else + val = (data>>16) & 0xffff; + + rval = rtems_uint16_from_little_endian( (uint8_t *) &val); + JPRINTK( "0x%x data: 0x%x raw: 0x%x\n", ptr, rval, data); + return rval; +} + +int16_t pci_in_16( uint32_t base, uint32_t addr ) { + volatile uint32_t *ptr; + uint16_t val; + uint32_t data; + + data = addr/4; + ptr = (volatile uint32_t *) (base + (data*4)); + data = *ptr; + if ( addr%4 == 0 ) + val = data & 0xffff; + else + val = (data>>16) & 0xffff; + + JPRINTK( "0x%x data: 0x%x raw: 0x%x\n", ptr, val, data); + return val; +} + +uint32_t pci_in_32( uint32_t base, uint32_t addr ) { + volatile uint32_t *ptr; + uint32_t val; + + ptr = (volatile uint32_t *) (base + addr); + val = *ptr; + + JPRINTK( "0x%x data: 0x%x raw: 0x%x\n", ptr, val, val); + return val; +} +uint32_t pci_in_le32( uint32_t base, uint32_t addr ) { + volatile uint32_t *ptr; + uint32_t val; + uint32_t rval; + + ptr = (volatile uint32_t *) (base + addr); + val = *ptr; + rval = rtems_uint32_from_little_endian( (uint8_t *) &val); + + JPRINTK( "0x%x data: 0x%x raw: 0x%x\n", ptr, rval, val); + return rval; +} + +void pci_out_8( uint32_t base, uint32_t addr, uint8_t val ) { + volatile uint32_t *ptr; + + ptr = (volatile uint32_t *) (base + addr); + JPRINTK("Address: %p\n", ptr); + *ptr = val; + JPRINTK( "%p data: 0x%x\n", ptr, val); +} + +void pci_out_le16( uint32_t base, uint32_t addr, uint16_t val ) { + volatile uint32_t *ptr; + uint32_t out_data; + uint32_t data; + + ptr = (volatile uint32_t *) (base + (addr & ~0x3)); + data = *ptr; + if ( addr%4 == 0 ) + out_data = (data & 0xffff0000) | val; + else + out_data = ((val << 16)&0xffff0000) | (data & 0xffff); + rtems_uint32_to_little_endian( out_data, (uint8_t *) &data); + *ptr = data; + + JPRINTK( "0x%x data: 0x%x\n", ptr, data); +} + +void pci_out_16( uint32_t base, uint32_t addr, uint16_t val ) { + volatile uint32_t *ptr; + uint32_t out_data; + uint32_t data; + + ptr = (volatile uint32_t *) (base + (addr & ~0x3)); + data = *ptr; + if ( addr%4 == 0 ) + out_data = (data & 0xffff0000) | val; + else + out_data = ((val << 16)&0xffff0000) | (data & 0xffff); + *ptr = out_data; + + JPRINTK( "0x%x data: 0x%x\n", ptr, out_data); +} + +/* + * INDIRECT PCI CONFIGURATION ACCESSES + */ +int indirect_pci_read_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint8_t *val +) { + + JPRINTK("==>\n"); + PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); + *val = in_8((uint32_t) (pci.pci_config_data + (offset&3)) ); + JPRINTK("\n\n"); + + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pci_read_config_word( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint16_t *val +) { + + JPRINTK("==>\n"); + + *val = 0xffff; + if (offset&1) + return PCIBIOS_BAD_REGISTER_NUMBER; + + PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); + *val = in_16((uint32_t)(pci.pci_config_data + (offset&3))); + + JPRINTK("\n\n"); + + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pci_read_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint32_t *val +) { + uint32_t v; + JPRINTK("==>\n"); + + *val = 0xffffffff; + if (offset&3) + return PCIBIOS_BAD_REGISTER_NUMBER; + PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); + v = in_32( (uint32_t) pci.pci_config_data ); + *val = v; + if ( offset == 0x0b ) + JPRINTK( "%x:%x %x ==> 0x%08x, 0x%08x\n", bus, slot, function, v, *val ); + + JPRINTK("\n\n"); + + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pci_write_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint8_t val +) { + + JPRINTK("==>\n"); + + PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); + out_8( (uint32_t) (pci.pci_config_data + (offset&3)), val); + + JPRINTK("\n\n"); + + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pci_write_config_word( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint16_t val +) { + + JPRINTK("==>\n"); + + if (offset&1) + return PCIBIOS_BAD_REGISTER_NUMBER; + + PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); + out_16((uint32_t)(pci.pci_config_data + (offset&3)), val); + + JPRINTK("\n\n"); + + return PCIBIOS_SUCCESSFUL; +} + +int indirect_pci_write_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint32_t val +) { + + if (offset&3) + return PCIBIOS_BAD_REGISTER_NUMBER; + + JPRINTK("==>\n"); + + /* + * The Base Address Registers get accessed big endian while the + * other registers are little endian. + */ + PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); + if ( bus == 0 && slot == 0x0b && + (offset >= PCI_BASE_ADDRESS_0 && offset <= PCI_BASE_ADDRESS_5) ) { + out_32((uint32_t)pci.pci_config_data, val); + } else { + out_le32((uint32_t)pci.pci_config_data, val); + } + + JPRINTK("\n\n"); + + return PCIBIOS_SUCCESSFUL; +} + +/* + * DIRECT CONFIGUREATION ACCESSES. + */ +int direct_pci_read_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint8_t *val +) { + if (bus != 0 || (1<<slot & 0xff8007fe)) { + *val=0xff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + JPRINTK("==>\n"); + + *val=in_8((uint32_t) (pci.pci_config_data + ((1<<slot)&~1) + + (function<<8) + offset)); + + JPRINTK("\n\n"); + + return PCIBIOS_SUCCESSFUL; +} + +int direct_pci_read_config_word( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint16_t *val +) { + *val = 0xffff; + if (offset&1) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (bus != 0 || (1<<slot & 0xff8007fe)) + return PCIBIOS_DEVICE_NOT_FOUND; + + JPRINTK("==>\n"); + + *val=in_le16((uint32_t) + (pci.pci_config_data + ((1<<slot)&~1) + + (function<<8) + offset)); + + JPRINTK("\n\n"); + + return PCIBIOS_SUCCESSFUL; +} + +int direct_pci_read_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint32_t *val +) { + *val = 0xffffffff; + if (offset&3) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (bus != 0 || (1<<slot & 0xff8007fe)) + return PCIBIOS_DEVICE_NOT_FOUND; + + JPRINTK("==>\n"); + + *val=in_le32((uint32_t)(pci.pci_config_data + + ((1<<slot)&~1)+(function<<8) + offset)); + + JPRINTK("\n\n"); + + return PCIBIOS_SUCCESSFUL; +} + +int direct_pci_write_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint8_t val +) { + if (bus != 0 || (1<<slot & 0xff8007fe)) + return PCIBIOS_DEVICE_NOT_FOUND; + + JPRINTK("==>\n"); + + out_8((uint32_t) (pci.pci_config_data + ((1<<slot)&~1) + + (function<<8) + offset), + val); + + JPRINTK("\n\n"); + + return PCIBIOS_SUCCESSFUL; +} + +int direct_pci_write_config_word( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint16_t val +) { + if (offset&1) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (bus != 0 || (1<<slot & 0xff8007fe)) + return PCIBIOS_DEVICE_NOT_FOUND; + + JPRINTK("==>\n"); + + out_le16((uint32_t)(pci.pci_config_data + ((1<<slot)&~1) + + (function<<8) + offset), + val); + + JPRINTK("\n\n"); + + return PCIBIOS_SUCCESSFUL; +} + +int direct_pci_write_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint32_t val +) { + if (offset&3) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (bus != 0 || (1<<slot & 0xff8007fe)) + return PCIBIOS_DEVICE_NOT_FOUND; + + JPRINTK("direct_pci_write_config_dword==>\n"); + + out_le32((uint32_t) + (pci.pci_config_data + ((1<<slot)&~1) + + (function<<8) + offset), + val); + + JPRINTK("\n\n"); + + return PCIBIOS_SUCCESSFUL; +} + +/* + * Validate a test interrupt name and print a warning if its not one of + * the names defined in the routing record. + */ +int test_intname( + const struct _int_map *row, + int pbus, + int pslot, + int pfun, + int int_pin, + int int_name +) { + int j, k; + int _nopin= -1, _noname= -1; + + for (j=0; row->pin_route[j].pin > -1; j++) { + if ( row->pin_route[j].pin == int_pin ) { + _nopin = 0; + + for (k=0; k<4 && row->pin_route[j].int_name[k] > -1; k++ ) { + if ( row->pin_route[j].int_name[k] == int_name ) { + _noname=0; break; + } + } + break; + } + } + + if( _nopin ) + { + printk( + "pci : Device %d:0x%02x:%d supplied a bogus interrupt_pin %d\n", + pbus, + pslot, + pfun, + int_pin + ); + return -1; + } + else + { + if( _noname ) { + unsigned char v = row->pin_route[j].int_name[0]; + printk( + "pci : Device %d:0x%02x:%d supplied a suspicious interrupt_line %d, ", + pbus, + pslot, + pfun, + int_name + ); + if ((row->opts & PCI_FIXUP_OPT_OVERRIDE_NAME) && 255 != + (v = row->pin_route[j].int_name[0]) + ) { + printk("OVERRIDING with %d from fixup table\n", v); + pci_write_config_byte(pbus,pslot,pfun,PCI_INTERRUPT_LINE,v); + } else { + printk("using it anyway\n"); + } + } + } + return 0; +} + +int FindPCIbridge( int mybus, struct pcibridge *pb ) +{ + int pbus, pslot; + uint8_t bussec, buspri; + uint16_t devid, vendorid, dclass; + + for(pbus=0; pbus< pci_bus_count(); pbus++) { + for(pslot=0; pslot< PCI_MAX_DEVICES; pslot++) { + pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &devid); + if ( devid == 0xffff ) continue; + + pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &vendorid); + if ( vendorid == 0xffff ) continue; + + pci_read_config_word(pbus, pslot, 0, PCI_CLASS_DEVICE, &dclass); + + if ( dclass == PCI_CLASS_BRIDGE_PCI ) { + pci_read_config_byte(pbus, pslot, 0, PCI_PRIMARY_BUS, &buspri); + pci_read_config_byte(pbus, pslot, 0, PCI_SECONDARY_BUS, &bussec); + + #ifdef SHOW_PCI_SETTING + JPRINTK( + "pci : Found bridge at %d:0x%02x, mybus %d, pribus %d, secbus %d ", + pbus, + pslot, + mybus, + buspri, + bussec + ); + #endif + if ( bussec == mybus ) { + #ifdef SHOW_PCI_SETTING + JPRINTK("match\n"); + #endif + /* found our nearest bridge going towards the root */ + pb->bus = pbus; + pb->slot = pslot; + return 0; + } + #ifdef SHOW_PCI_SETTING + JPRINTK("no match\n"); + #endif + } + + } + } + return -1; +} + +void FixupPCI( const struct _int_map *bspmap, int (*swizzler)(int,int) ) +{ + unsigned char cvalue; + uint16_t devid; + int ismatch, i, j, pbus, pslot, pfun, int_pin, int_name, nfuns; + + /* + * If the device has a non-zero INTERRUPT_PIN, assign a bsp-specific + * INTERRUPT_NAME if one isn't already in place. Then, drivers can + * trivially use INTERRUPT_NAME to hook up with devices. + */ + + for (pbus=0; pbus< pci_bus_count(); pbus++) { + for (pslot=0; pslot< PCI_MAX_DEVICES; pslot++) { + pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &devid); + if ( devid == 0xffff ) continue; + + /* got a device */ + pci_read_config_byte(pbus, pslot, 0, PCI_HEADER_TYPE, &cvalue); + nfuns = cvalue & PCI_HEADER_TYPE_MULTI_FUNCTION ? PCI_MAX_FUNCTIONS : 1; + for (pfun=0; pfun< nfuns; pfun++) { + + pci_read_config_word(pbus, pslot, pfun, PCI_DEVICE_ID, &devid); + if( devid == 0xffff ) continue; + + pci_read_config_byte( pbus, pslot, pfun, PCI_INTERRUPT_PIN, &cvalue); + int_pin = cvalue; + + pci_read_config_byte( pbus, pslot, pfun, PCI_INTERRUPT_LINE, &cvalue); + int_name = cvalue; + + #ifdef SHOW_PCI_SETTING + { + unsigned short cmd,stat; + unsigned char lat, seclat, csize; + + pci_read_config_word(pbus,pslot,pfun,PCI_COMMAND, &cmd ); + pci_read_config_word(pbus,pslot,pfun,PCI_STATUS, &stat ); + pci_read_config_byte(pbus,pslot,pfun,PCI_LATENCY_TIMER, &lat ); + pci_read_config_byte(pbus,pslot,pfun,PCI_SEC_LATENCY_TIMER, &seclat); + pci_read_config_byte(pbus,pslot,pfun,PCI_CACHE_LINE_SIZE, &csize ); + + JPRINTK( + "pci : device %d:0x%02x:%d cmd %04X, stat %04X, latency %d, " + " sec_latency %d, clsize %d\n", + pbus, + pslot, + pfun, + cmd, + stat, + lat, + seclat, + csize + ); + } + #endif + + if ( int_pin > 0 ) { + ismatch = 0; + + /* + * first run thru the bspmap table and see if we have an + * explicit configuration + */ + for (i=0; bspmap[i].bus > -1; i++) { + if ( bspmap[i].bus == pbus && bspmap[i].slot == pslot ) { + ismatch = -1; + + /* we have a record in the table that gives specific + * pins and interrupts for devices in this slot + */ + if ( int_name == 255 ) { + + /* find the vector associated with whatever pin the + * device gives us + */ + for ( int_name=-1, j=0; bspmap[i].pin_route[j].pin > -1; j++ ){ + if ( bspmap[i].pin_route[j].pin == int_pin ) { + int_name = bspmap[i].pin_route[j].int_name[0]; + break; + } + } + + if ( int_name == -1 ) { + printk( + "pci : Unable to resolve device %d:0x%02x:%d w/ " + "swizzled int pin %i to an interrupt_line.\n", + pbus, + pslot, + pfun, + int_pin + ); + } else { + PRINT_MSG(); + pci_write_config_byte( + pbus, + pslot, + pfun, + PCI_INTERRUPT_LINE,(cvalue= int_name, cvalue) + ); + } + } else { + test_intname( &bspmap[i],pbus,pslot,pfun,int_pin,int_name); + } + break; + } + } + + if ( !ismatch ) { + /* + * no match, which means we're on a bus someplace. Work + * backwards from it to one of our defined busses, + * swizzling thru each bridge on the way. + */ + + /* keep pbus, pslot pointed to the device being + * configured while we track down the bridges using + * tbus,tslot. We keep searching the routing table because + * we may end up finding our bridge in it + */ + + int tbus= pbus, tslot= pslot; + for (;;) { + for (i=0; bspmap[i].bus > -1; i++) { + if ( bspmap[i].bus == tbus && + (bspmap[i].slot == tslot || bspmap[i].slot == -1)) + { + ismatch = -1; + + /* found a record for this bus, so swizzle the + * int_pin which we then use to find the + * interrupt_name. + */ + if ( int_name == 255 ) { + /* + * FIXME. I can't believe this little hack + * is right. It does not yield an error in + * convienently simple situations. + */ + if ( tbus ) int_pin = (*swizzler)(tslot,int_pin); + + /* + * int_pin points to the interrupt channel + * this card ends up delivering interrupts + * on. Find the int_name servicing it. + */ + for (int_name=-1, j=0; + bspmap[i].pin_route[j].pin > -1; + j++) + { + if ( bspmap[i].pin_route[j].pin == int_pin ) { + int_name = bspmap[i].pin_route[j].int_name[0]; + break; + } + } + + if ( int_name == -1 ) { + printk( + "pci : Unable to resolve device %d:0x%02x:%d w/ " + "swizzled int pin %i to an interrupt_line.\n", + pbus, + pslot, + pfun, + int_pin + ); + } else { + PRINT_MSG(); + pci_write_config_byte(pbus,pslot,pfun, + PCI_INTERRUPT_LINE,(cvalue=int_name, cvalue)); + } + } else { + test_intname( + &bspmap[i], + pbus, + pslot, + pfun, + int_pin, + int_name + ); + } + goto donesearch; + } + } + if ( !ismatch ) { + struct pcibridge pb; + + /* + * Haven't found our bus in the int map, so work + * upwards thru the bridges till we find it. + */ + if ( FindPCIbridge( tbus, &pb )== 0 ) { + int_pin = (*swizzler)(tslot,int_pin); + + /* our next bridge up is on pb.bus, pb.slot- now + * instead of pointing to the device we're + * trying to configure, we move from bridge to + * bridge. + */ + tbus = pb.bus; + tslot = pb.slot; + } else { + printk( + "pci : No bridge from bus %i towards root found\n", + tbus + ); + goto donesearch; + } + } + } + } + + donesearch: + if ( !ismatch && int_pin != 0 && int_name == 255 ) { + printk( + "pci : Unable to match device %d:0x%02x:%d with an int " + "routing table entry\n", + pbus, + pslot, + pfun + ); + } + } + } + } + } +} + +void print_bars( + unsigned char slot, + unsigned char func +) +{ + uint32_t addr; + + printk( "*** BARs for slot=%d func=%d\n", slot, func ); + pci_read_config_dword (0, slot, func, PCI_BASE_ADDRESS_0, &addr); + printk("*** PCI DEVICE BAR0: 0x%lx\n", addr); + pci_read_config_dword (0, slot, func, PCI_BASE_ADDRESS_1, &addr); + printk("*** PCI DEVICE BAR1: 0x%lx\n", addr); + pci_read_config_dword (0, slot, func, PCI_BASE_ADDRESS_2, &addr); + printk("*** PCI DEVICE BAR2: 0x%lx\n", addr); + pci_read_config_dword (0, slot, func, PCI_BASE_ADDRESS_3, &addr); + printk("*** PCI DEVICE BAR3: 0x%lx\n", addr); + pci_read_config_dword (0, slot, func, PCI_BASE_ADDRESS_4, &addr); + printk("*** PCI DEVICE BAR4: 0x%lx\n", addr); + pci_read_config_dword (0, slot, func, PCI_BASE_ADDRESS_5, &addr); + printk("*** PCI DEVICE BAR5: 0x%lx\n", addr); +} + +void pci_memory_enable( + unsigned char bus, + unsigned char slot, + unsigned char function +) +{ + uint16_t data; + + pci_read_config_word(0, slot, function, PCI_COMMAND, &data); + data |= PCI_COMMAND_MEMORY; + pci_write_config_word(0, slot, function, PCI_COMMAND, data ); + pci_read_config_word(0, slot, function, PCI_COMMAND, &data); +} + +void pci_io_enable( + unsigned char bus, + unsigned char slot, + unsigned char function +) +{ + uint16_t data; + + pci_read_config_word(0, slot, function, PCI_COMMAND, &data); + data |= PCI_COMMAND_IO; + pci_write_config_word(0, slot, function, PCI_COMMAND, data ); +} + +void pci_busmaster_enable( + unsigned char bus, + unsigned char slot, + unsigned char function +) +{ + uint16_t data; + + pci_read_config_word(0, slot, function, PCI_COMMAND, &data); + data |= PCI_COMMAND_MASTER; + pci_write_config_word(0, slot, function, PCI_COMMAND, data ); +} + +/* + * This routine determines the maximum bus number in the system + */ +int pci_initialize(void) +{ + unsigned char slot, func, ucNumFuncs; + unsigned char ucHeader; + uint32_t class; + uint32_t device; + uint32_t vendor; + + /* + * Initialize GT_PCI0IOREMAP + */ + pci_out_32( BSP_PCI_BASE_ADDRESS, 0xf0, 0 ); + + /* + * According to Linux and BSD sources, this is needed to cover up a bug + * in some versions of the hardware. + */ + out_le32( PCI_CONFIG_ADDR, 0x80000020 ); + out_le32( PCI_CONFIG_DATA, 0x1be00000 ); + + /* + * Scan PCI bus 0 looking for the known Network devices and + * initializing the PCI for them. + */ + for (slot=0;slot<PCI_MAX_DEVICES;slot++) { + pci_read_config_dword(0, slot, 0, PCI_VENDOR_ID, &device); + if (device == PCI_INVALID_VENDORDEVICEID) { + /* This slot is empty */ + continue; + } + + pci_read_config_byte(0, slot, 0, PCI_HEADER_TYPE, &ucHeader); + if (ucHeader & PCI_HEADER_TYPE_MULTI_FUNCTION) { + ucNumFuncs = PCI_MAX_FUNCTIONS; + } else { + ucNumFuncs=1; + } + for (func=0;func<ucNumFuncs;func++) { + pci_read_config_dword(0, slot, func, PCI_VENDOR_ID, &device); + if (device==PCI_INVALID_VENDORDEVICEID) { + /* This slot/function is empty */ + continue; + } + vendor = device & 0xffff; + device = device >> 16; + + /* This slot/function has a device fitted. */ + pci_read_config_dword(0, slot, func, PCI_CLASS_REVISION, &class); + class >>= 16; + + // printk( "FOUND DEVICE 0x%04x/0x%04x class 0x%x\n", + // vendor, device, class ); + if (class == PCI_CLASS_NETWORK_ETHERNET) { + JPRINTK("FOUND ETHERNET\n"); + + pci_write_config_byte( + 0, slot, func, PCI_INTERRUPT_LINE, MALTA_IRQ_ETHERNET ); + + /* + * Rewrite BAR1 for RTL8139 + */ + if ( vendor == PCI_VENDOR_ID_REALTEK && + device == PCI_DEVICE_ID_REALTEK_8139 ) { + + pci_memory_enable(0, slot, func); + pci_io_enable(0, slot, func); + pci_busmaster_enable(0, slot, func); + + // BAR0: IO at 0x0000_1001 + pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0, 0x00001001); + + // BAR1: Memory at 0x1203_1000 + pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_1, 0x12031000); + + // print_bars( slot, func ); + } else if ( vendor == PCI_VENDOR_ID_AMD && + device == PCI_DEVICE_ID_AMD_LANCE ) { + print_bars( slot, func ); + pci_memory_enable(0, slot, func); + pci_io_enable(0, slot, func); + pci_busmaster_enable(0, slot, func); + + // BAR0: IO at 0x0000_1041 + pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_0, 0x00001041); + + // BAR1: Memory at 0x1201_1020 + pci_write_config_dword(0, slot, func, PCI_BASE_ADDRESS_1, 0x12011020); + print_bars( slot, func ); + } + + } + } + } + return PCIB_ERR_SUCCESS; +} + +/* + * Return the number of PCI busses in the system + */ +unsigned char pci_bus_count(void) +{ + return (ucMaxPCIBus+1); +} diff --git a/bsps/powerpc/beatnik/pci/gt_pci_init.c b/bsps/powerpc/beatnik/pci/gt_pci_init.c new file mode 100644 index 0000000000..68d7467b22 --- /dev/null +++ b/bsps/powerpc/beatnik/pci/gt_pci_init.c @@ -0,0 +1,270 @@ +/* PCI configuration space access */ + +/* + * Acknowledgements: + * Valuable information was obtained from the following drivers + * netbsd: (C) Allegro Networks Inc; Wasabi Systems Inc. + * linux: (C) MontaVista, Software, Inc; Chris Zankel, Mark A. Greer. + * rtems: (C) Brookhaven National Laboratory; K. Feng + */ + +/* + * Original file header of libbsp/shared/pci.c where this file is based upon. + * + * 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 the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * Till Straumann, <strauman@slac.stanford.edu>, 1/2002 + * - separated bridge detection code out of this file + */ + +#include <rtems.h> +#include <bsp.h> +#include <libcpu/io.h> +#include <bsp/pci.h> +#include <rtems/bspIo.h> +#include <stdint.h> + +/* set to max so we get early access to hose 0 */ +unsigned BSP_pci_hose1_bus_base = (unsigned)-1; + +#define MV64x60_PCI0_CONFIG_ADDR (BSP_MV64x60_BASE + 0xcf8) +#define MV64x60_PCI0_CONFIG_DATA (BSP_MV64x60_BASE + 0xcfc) +#define MV64x60_PCI1_CONFIG_ADDR (BSP_MV64x60_BASE + 0xc78) +#define MV64x60_PCI1_CONFIG_DATA (BSP_MV64x60_BASE + 0xc7c) + +#define PCI_BUS2HOSE(bus) (bus<BSP_pci_hose1_bus_base?0:1) + +void detect_host_bridge(void) +{ + +} + +typedef struct { + volatile unsigned char *pci_config_addr; + volatile unsigned char *pci_config_data; +} PciHoseCfg; + +static PciHoseCfg hoses[2] = { + { + pci_config_addr: (volatile unsigned char *)(MV64x60_PCI0_CONFIG_ADDR), + pci_config_data: (volatile unsigned char *)(MV64x60_PCI0_CONFIG_DATA), + }, + { + pci_config_addr: (volatile unsigned char *)(MV64x60_PCI1_CONFIG_ADDR), + pci_config_data: (volatile unsigned char *)(MV64x60_PCI1_CONFIG_DATA), + } +}; + +#define pci hoses[hose] + +#define HOSE_PREAMBLE \ + uint8_t hose; \ + if (bus < BSP_pci_hose1_bus_base) { \ + hose = 0; \ + } else { \ + hose = 1; \ + bus -= BSP_pci_hose1_bus_base; \ + } + + +/* Sigh; we have to copy those out from the shared area... */ +static int +indirect_pci_read_config_byte(unsigned char bus, unsigned char slot, + unsigned char function, + unsigned char offset, uint8_t *val) { +HOSE_PREAMBLE; + out_be32((volatile uint32_t *) pci.pci_config_addr, + 0x80|(bus<<8)|(PCI_DEVFN(slot,function)<<16)|((offset&~3)<<24)); + *val = in_8(pci.pci_config_data + (offset&3)); + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_pci_read_config_word(unsigned char bus, unsigned char slot, + unsigned char function, + unsigned char offset, uint16_t *val) { +HOSE_PREAMBLE; + *val = 0xffff; + if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER; + out_be32((uint32_t*) pci.pci_config_addr, + 0x80|(bus<<8)|(PCI_DEVFN(slot,function)<<16)|((offset&~3)<<24)); + *val = in_le16((volatile uint16_t *)(pci.pci_config_data + (offset&3))); + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_pci_read_config_dword(unsigned char bus, unsigned char slot, + unsigned char function, + unsigned char offset, uint32_t *val) { +HOSE_PREAMBLE; + *val = 0xffffffff; + if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER; + out_be32((uint32_t*) pci.pci_config_addr, + 0x80|(bus<<8)|(PCI_DEVFN(slot,function)<<16)|(offset<<24)); + *val = in_le32((volatile uint32_t *)pci.pci_config_data); + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_pci_write_config_byte(unsigned char bus, unsigned char slot, + unsigned char function, + unsigned char offset, uint8_t val) { +HOSE_PREAMBLE; + out_be32((uint32_t*) pci.pci_config_addr, + 0x80|(bus<<8)|(PCI_DEVFN(slot,function)<<16)|((offset&~3)<<24)); + out_8(pci.pci_config_data + (offset&3), val); + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_pci_write_config_word(unsigned char bus, unsigned char slot, + unsigned char function, + unsigned char offset, uint16_t val) { +HOSE_PREAMBLE; + if (offset&1) return PCIBIOS_BAD_REGISTER_NUMBER; + out_be32((uint32_t*) pci.pci_config_addr, + 0x80|(bus<<8)|(PCI_DEVFN(slot,function)<<16)|((offset&~3)<<24)); + out_le16((volatile uint16_t *)(pci.pci_config_data + (offset&3)), val); + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_pci_write_config_dword(unsigned char bus, unsigned char slot, + unsigned char function, + unsigned char offset, uint32_t val) { +HOSE_PREAMBLE; + if (offset&3) return PCIBIOS_BAD_REGISTER_NUMBER; + out_be32((uint32_t*) pci.pci_config_addr, + 0x80|(bus<<8)|(PCI_DEVFN(slot,function)<<16)|(offset<<24)); + out_le32((volatile uint32_t *)pci.pci_config_data, val); + return PCIBIOS_SUCCESSFUL; +} + +const pci_config_access_functions pci_hosed_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 +}; + + +extern unsigned char ucMaxPCIBus; /* importing this is ugly */ + +/* This is a very ugly hack. I don't want to change the shared + * code to support multiple hoses so we hide everything under + * the hood with horrible kludges for now. Sorry. + */ +void +BSP_pci_initialize(void) +{ + +#if 0 /* These values are already set up for the shared/pci.c code */ +{ +extern pci_config_access_functions pci_indirect_functions; + /* by means of the PCI_CONFIG_ADDR/PCI_CONFIG_DATA macros (bsp.h) */ + BSP_pci_configuration.pci_config_addr = hoses[0].pci_config_addr; + BSP_pci_configuration.pci_config_data = hoses[0].pci_config_data; + BSP_pci_configuration.pci_functions = &pci_indirect_functions; +} +#endif + /* initialize the first hose */ + /* scan hose 0 and sets the maximum bus number */ + pci_initialize(); + /* remember the boundary */ + BSP_pci_hose1_bus_base = pci_bus_count(); + /* so far, so good -- now comes the cludgy part: */ + /* hack/reset the bus count */ + ucMaxPCIBus = 0; + /* scan hose 1 */ + BSP_pci_configuration.pci_config_addr = hoses[1].pci_config_addr; + BSP_pci_configuration.pci_config_data = hoses[1].pci_config_data; + pci_initialize(); + /* check for overflow of an unsigned char */ + if ( BSP_pci_hose1_bus_base + pci_bus_count() > 255 ) { + rtems_panic("Too many PCI busses in the system"); + } + /* readjust total number */ + ucMaxPCIBus+=BSP_pci_hose1_bus_base; + + /* install new access functions that can hide the hoses */ + BSP_pci_configuration.pci_config_addr = (volatile unsigned char *)0xdeadbeef; + BSP_pci_configuration.pci_config_data = (volatile unsigned char *)0xdeadbeef; + BSP_pci_configuration.pci_functions = &pci_hosed_indirect_functions; +} + +#define PCI_ERR_BITS 0xf900 +#define PCI_STATUS_OK(x) (!((x)&PCI_ERR_BITS)) + +/* For now, just clear errors in the PCI status reg. + * + * Returns: (for diagnostic purposes) + * original settings (i.e. before applying the clearing + * sequence) + * (pci_status(hose_1)&0xff00) | ((pci_status(hose_2)>>8)&0xff) + */ + +static unsigned long clear_hose_errors(int bus, int quiet) +{ +unsigned long rval; +uint16_t pcistat; +int count; +int hose = PCI_BUS2HOSE(bus); + + /* read error status for info return */ + pci_read_config_word(bus,0,0,PCI_STATUS,&pcistat); + rval = pcistat; + + count=10; + do { + /* clear error reporting registers */ + + /* clear PCI status register */ + pci_write_config_word(bus,0,0,PCI_STATUS, PCI_ERR_BITS); + + /* read new status */ + pci_read_config_word(bus,0,0,PCI_STATUS, &pcistat); + + } while ( ! PCI_STATUS_OK(pcistat) && count-- ); + + if ( !PCI_STATUS_OK(rval) && !quiet) { + printk("Cleared PCI errors at discovery (hose %i): pci_stat was 0x%04lx\n", hose, rval); + } + if ( !PCI_STATUS_OK(pcistat) ) { + printk("Unable to clear PCI errors at discovery (hose %i) still 0x%04x after 10 attempts\n",hose, pcistat); + } + return rval; +} + +unsigned short +(*_BSP_clear_vmebridge_errors)(int) = 0; + +unsigned long +_BSP_clear_hostbridge_errors(int enableMCP, int quiet) +{ +unsigned long rval; + + /* MCP is not connected */ + if ( enableMCP ) + return -1; + + rval = (clear_hose_errors(0, quiet) & PCI_ERR_BITS)>>8; + rval |= clear_hose_errors(BSP_pci_hose1_bus_base, quiet) & PCI_ERR_BITS; + + /* Tsi148 doesn't propagate VME bus errors to PCI status reg. */ + if ( _BSP_clear_vmebridge_errors ) + rval |= _BSP_clear_vmebridge_errors(quiet)<<16; + + return rval; +} diff --git a/bsps/powerpc/beatnik/pci/motload_fixup.c b/bsps/powerpc/beatnik/pci/motload_fixup.c new file mode 100644 index 0000000000..21d55916cb --- /dev/null +++ b/bsps/powerpc/beatnik/pci/motload_fixup.c @@ -0,0 +1,180 @@ +/* remap the zero-based PCI IO spaces of both hoses to a single + * address space + * + * This must be called AFTER to BSP_pci_initialize() + */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ +#include <rtems.h> +#include <bsp.h> +#include <libcpu/io.h> +#include <bsp/pci.h> +#include <bsp/irq.h> +#include <rtems/bspIo.h> +#include <bsp/gtreg.h> +#include "pci_io_remap.h" + +static int +fixup_irq_line(int bus, int slot, int fun, void *uarg) +{ +unsigned char line; + pci_read_config_byte( bus, slot, fun, PCI_INTERRUPT_LINE, &line); + if ( line < BSP_IRQ_GPP_0 ) { + pci_write_config_byte( bus, slot, fun, PCI_INTERRUPT_LINE, line + BSP_IRQ_GPP_0 ); + } + + return 0; +} + +void BSP_motload_pci_fixup(void) +{ +uint32_t b0,b1,r0,r1,lim,dis; + + /* MotLoad on the mvme5500 and mvme6100 configures the PCI + * busses nicely, i.e., the values read from the memory address + * space BARs by means of PCI config cycles directly reflect the + * CPU memory map. Thus, the presence of two hoses is already hidden. + * + * Unfortunately, all PCI I/O addresses are 'zero-based' i.e., + * a hose-specific base address would have to be added to + * the values read from config space. + * + * We fix this here so I/O BARs also reflect the CPU memory map. + * + * Furthermore, the mvme5500 uses + * f000.0000 + * ..f07f.ffff for PCI-0 / hose0 + * + * and + * + * f080.0000 + * ..f0ff.0000 for PCI-1 / hose 0 + * + * whereas the mvme6100 does it the other way round... + */ + + b0 = in_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI0_IO_Low_Decode) ); + b1 = in_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI1_IO_Low_Decode) ); + + r0 = in_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI0_IO_Remap) ); + r1 = in_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI1_IO_Remap) ); + + switch ( BSP_getDiscoveryVersion(0) ) { + case MV_64360: + /* In case of the MV64360 the 'limit' is actually a 'size'! + * Disable by setting special bits in the 'BAR disable reg'. + */ + dis = in_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + MV_64360_BASE_ADDR_DISBL) ); + /* disable PCI0 I/O and PCI1 I/O */ + out_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + MV_64360_BASE_ADDR_DISBL), dis | (1<<9) | (1<<14) ); + /* remap busses on hose 0; if the remap register was already set, assume + * that someone else [such as the bootloader] already performed the fixup + */ + if ( (b0 & 0xffff) && 0 == (r0 & 0xffff) ) { + rtems_pci_io_remap( 0, BSP_pci_hose1_bus_base, (b0 & 0xffff)<<16 ); + out_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI0_IO_Remap), (b0 & 0xffff) ); + } + + /* remap busses on hose 1 */ + if ( (b1 & 0xffff) && 0 == (r1 & 0xffff) ) { + rtems_pci_io_remap( BSP_pci_hose1_bus_base, pci_bus_count(), (b1 & 0xffff)<<16 ); + out_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI1_IO_Remap), (b1 & 0xffff) ); + } + + /* re-enable */ + out_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + MV_64360_BASE_ADDR_DISBL), dis ); + break; + + case GT_64260_A: + case GT_64260_B: + + if ( (b0 & 0xfff) && 0 == (r0 & 0xfff) ) { /* base are only 12 bits */ + /* switch window off by setting the limit < base */ + lim = in_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI0_IO_High_Decode) ); + out_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI0_IO_High_Decode), 0 ); + /* remap busses on hose 0 */ + rtems_pci_io_remap( 0, BSP_pci_hose1_bus_base, (b0 & 0xfff)<<20 ); + + /* BTW: it seems that writing the base register also copies the + * value into the 'remap' register automatically (??) + */ + out_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI0_IO_Remap), (b0 & 0xfff) ); + + /* re-enable */ + out_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI0_IO_High_Decode), lim ); + } + + if ( (b1 & 0xfff) && 0 == (r1 & 0xfff) ) { /* base are only 12 bits */ + /* switch window off by setting the limit < base */ + lim = in_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI1_IO_High_Decode) ); + out_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI1_IO_High_Decode), 0 ); + + /* remap busses on hose 1 */ + rtems_pci_io_remap( BSP_pci_hose1_bus_base, pci_bus_count(), (b1 & 0xfff)<<20 ); + + out_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI1_IO_Remap), (b1 & 0xfff) ); + + /* re-enable */ + out_le32( (volatile uint32_t*)(BSP_MV64x60_BASE + GT_PCI1_IO_High_Decode), lim ); + } + break; + + default: + rtems_panic("Unknown discovery version; switch in file: "__FILE__" not implemented (yet)"); + break; /* never get here */ + } + + /* Fixup the IRQ lines; the mvme6100 maps them nicely into our scheme, i.e., GPP + * interrupts start at 64 upwards + * + * The mvme5500 is apparently initialized differently :-(. GPP interrupts start at 0 + * Since all PCI interrupts are wired to GPP we simply check for a value < 64 and + * reprogram the interrupt line register. + */ + BSP_pciScan(0, fixup_irq_line, 0); +} + + diff --git a/bsps/powerpc/beatnik/pci/pci_io_remap.c b/bsps/powerpc/beatnik/pci/pci_io_remap.c new file mode 100644 index 0000000000..56118d01d5 --- /dev/null +++ b/bsps/powerpc/beatnik/pci/pci_io_remap.c @@ -0,0 +1,203 @@ +/* Adjust a PCI bus range's I/O address space by adding an offset */ + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include <rtems.h> +#include <rtems/bspIo.h> +#include <bsp/pci.h> +#include <stdint.h> +#include "pci_io_remap.h" + +#ifndef PCI_MULTI_FUN +#define PCI_MULTI_FUN 0x80 +#endif + +#ifndef PCI_HEADER_TYPE_MSK +#define PCI_HEADER_TYPE_MSK 0x7f +#endif + +/* Reconfigure all I/O base address registers for a range of PCI busses + * (from and including 'bus_from' up to and not including 'bus_to'). + * adding an offset. This involves adjusting the base and limit registers + * of PCI-PCI bridges, too. + * + * RESTRICTIONS: 'offset' must be 4k aligned (PCI req.); no argument check + * on the bus numbers is done. + * + * RETURNS: 0 on success and a number > 0 indicating the number of + * non-32bit bridges found where the offset couldn't be added. + * Devices behind such a bridge are not accessible through I/O + * and should probably be switched off (not done by this code). + */ +int +rtems_pci_io_remap(int bus_from, int bus_to, uint32_t offset) +{ + int rval = 0; + int bus, dev, fun, maxf; + int bar, numBars = 0; + uint8_t b; + uint16_t s; + uint32_t d; + unsigned int bas, lim; + + if ( offset & ((1<<12)-1) ) { + rtems_panic("rtems_pci_io_remap(): offset must be 4k aligned"); + return -1; + } + + + for ( bus=bus_from; bus < bus_to; bus++ ) { + for ( dev = 0; dev<PCI_MAX_DEVICES; dev++ ) { + + maxf = 1; + + for ( fun = 0; fun < maxf; fun++ ) { + pci_read_config_word( bus, dev, fun, PCI_VENDOR_ID, &s ); + if ( 0xffff == s ) + continue; + + pci_read_config_byte( bus, dev, fun, PCI_HEADER_TYPE, &b ); + + /* readjust the max. function number to scan if this is a multi-function + * device. + */ + if ( 0 == fun && (PCI_MULTI_FUN & b) ) + maxf = PCI_MAX_FUNCTIONS; + + /* Check the header type; panic if unknown. + * header type 0 has 6 bars, header type 1 (PCI-PCI bridge) has 2 + */ + b &= PCI_HEADER_TYPE_MSK; + switch ( b ) { + default: + printk("PCI header type %i (@%i/%i/%i)\n", b, bus, dev, fun); + rtems_panic("rtems_pci_io_remap(): unknown PCI header type"); + return -1; /* keep compiler happy */ + + case PCI_HEADER_TYPE_CARDBUS: + printk("PCI header type %i (@%i/%i/%i)\n", b, bus, dev, fun); + rtems_panic("rtems_pci_io_remap(): don't know how to deal with Cardbus bridge"); + return -1; + + case PCI_HEADER_TYPE_NORMAL: + numBars = 6*4; /* loop below counts reg. offset in bytes */ + break; + + case PCI_HEADER_TYPE_BRIDGE: + numBars = 2*4; /* loop below counts reg. offset in bytes */ + break; + + } + + for ( bar = 0; bar < numBars; bar+=4 ) { + pci_read_config_dword( bus, dev, fun, PCI_BASE_ADDRESS_0 + bar, &d ); + if ( PCI_BASE_ADDRESS_SPACE_IO & d ) { + /* It's an I/O BAR; remap */ + d &= PCI_BASE_ADDRESS_IO_MASK; + if ( d ) { + /* IO bar was configured; add offset */ + d += offset; + pci_write_config_dword( bus, dev, fun, PCI_BASE_ADDRESS_0 + bar, d ); + } + } else { + /* skip upper half of 64-bit window */ + d &= PCI_BASE_ADDRESS_MEM_TYPE_MASK; + if ( PCI_BASE_ADDRESS_MEM_TYPE_64 == d ) + bar+=4; + } + } + + /* Now it's time to deal with bridges */ + if ( PCI_HEADER_TYPE_BRIDGE == b ) { + /* must adjust the limit registers */ + pci_read_config_byte( bus, dev, fun, PCI_IO_LIMIT, &b ); + pci_read_config_word( bus, dev, fun, PCI_IO_LIMIT_UPPER16, &s ); + lim = (s<<16) + (( b & PCI_IO_RANGE_MASK ) << 8); + lim += offset; + + pci_read_config_byte( bus, dev, fun, PCI_IO_BASE, &b ); + pci_read_config_word( bus, dev, fun, PCI_IO_BASE_UPPER16, &s ); + bas = (s<<16) + (( b & PCI_IO_RANGE_MASK ) << 8); + bas += offset; + + b &= PCI_IO_RANGE_TYPE_MASK; + switch ( b ) { + default: + printk("Unknown IO range type 0x%x (@%i/%i/%i)\n", b, bus, dev, fun); + rtems_panic("rtems_pci_io_remap(): unknown IO range type"); + return -1; + + case PCI_IO_RANGE_TYPE_16: + if ( bas > 0xffff || lim > 0xffff ) { + printk("PCI I/O range type 1 (16bit) bridge (@%i/%i/%i) found:\n", bus, dev, fun); + printk("WARNING: base (0x%08x) or limit (0x%08x) exceed 16-bit;\n", bas, lim); + printk(" devices behind this bridge are NOT accessible!\n"); + + /* FIXME: should we disable devices behind this bridge ? */ + bas = lim = 0; + } + break; + + case PCI_IO_RANGE_TYPE_32: + break; + } + + b = (uint8_t)((bas>>8) & PCI_IO_RANGE_MASK); + pci_write_config_byte( bus, dev, fun, PCI_IO_BASE, b ); + + s = (uint16_t)((bas>>16)&0xffff); + pci_write_config_word( bus, dev, fun, PCI_IO_BASE_UPPER16, s); + + b = (uint8_t)((lim>>8) & PCI_IO_RANGE_MASK); + pci_write_config_byte( bus, dev, fun, PCI_IO_LIMIT, b ); + s = (uint16_t)((lim>>16)&0xffff); + pci_write_config_word( bus, dev, fun, PCI_IO_LIMIT_UPPER16, s ); + } + } + } + } + return rval; +} diff --git a/bsps/powerpc/beatnik/pci/pci_io_remap.h b/bsps/powerpc/beatnik/pci/pci_io_remap.h new file mode 100644 index 0000000000..533519a2ae --- /dev/null +++ b/bsps/powerpc/beatnik/pci/pci_io_remap.h @@ -0,0 +1,66 @@ +#ifndef RTEMS_PCI_IO_REMAP_H +#define RTEMS_PCI_IO_REMAP_H +/* Reconfigure all I/O base address registers for a range of PCI busses + * (from and including 'bus_from' up to and not including 'bus_to'). + * adding an offset. This involves adjusting the base and limit registers + * of PCI-PCI bridges, too. + * + * RESTRICTIONS: 'offset' must be 4k aligned (PCI req.); no argument check + * on the bus numbers is done. + * + * RETURNS: 0 on success and a number > 0 indicating the number of + * non-32bit bridges found where the offset couldn't be added. + * Devices behind such a bridge are not accessible through I/O + * and should probably be switched off (not done by this code). + */ + +int +rtems_pci_io_remap(int bus_from, int bus_to, uint32_t offset); + +/* + * Authorship + * ---------- + * This software ('beatnik' RTEMS BSP for MVME6100 and MVME5500) was + * created by Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'beatnik' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#endif + diff --git a/bsps/powerpc/mvme3100/pci/detect_host_bridge.c b/bsps/powerpc/mvme3100/pci/detect_host_bridge.c new file mode 100644 index 0000000000..8488c99dc5 --- /dev/null +++ b/bsps/powerpc/mvme3100/pci/detect_host_bridge.c @@ -0,0 +1,113 @@ +/* PCI Initialization */ + +/* + * Authorship + * ---------- + * This software ('mvme3100' RTEMS BSP) was created by + * + * Till Straumann <strauman@slac.stanford.edu>, 2005-2007, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * The 'mvme3100' BSP was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include <bsp.h> +#include <bsp/pci.h> +#include <bsp/irq.h> +#include <bsp/openpic.h> +#include <inttypes.h> + +/* Motload configures PCI interrupts to start at 16 and up but + * we'd rather have them starting at 0. + * Use this callback to fix them up. + */ +static int +fixup_irq_line(int bus, int slot, int fun, void *uarg) +{ +unsigned char line; + pci_read_config_byte( bus, slot, fun, PCI_INTERRUPT_LINE, &line); + if ( line >= BSP_EXT_IRQ_NUMBER ) { + pci_write_config_byte( bus, slot, fun, PCI_INTERRUPT_LINE, line - BSP_EXT_IRQ_NUMBER ); + } + + return 0; +} + +void BSP_motload_pci_fixup(void) +{ + BSP_pciScan(0, fixup_irq_line, 0); +} + +void detect_host_bridge(void) +{ + OpenPIC = (volatile struct OpenPIC *) (BSP_8540_CCSR_BASE + BSP_OPEN_PIC_BASE_OFFSET); +} + +static int +dump_dev_cb( + int bus, + int dev, + int fun, + void *uarg +) +{ + uint16_t vi,di; + uint16_t cd,st; + uint32_t b1,b2; + uint8_t il,ip; + + pci_read_config_word (bus, dev, fun, PCI_VENDOR_ID, &vi); + pci_read_config_word (bus, dev, fun, PCI_DEVICE_ID, &di); + pci_read_config_word (bus, dev, fun, PCI_COMMAND, &cd); + pci_read_config_word (bus, dev, fun, PCI_STATUS, &st); + pci_read_config_dword(bus, dev, fun, PCI_BASE_ADDRESS_0, &b1); + pci_read_config_dword(bus, dev, fun, PCI_BASE_ADDRESS_1, &b2); + pci_read_config_byte (bus, dev, fun, PCI_INTERRUPT_LINE, &il); + pci_read_config_byte (bus, dev, fun, PCI_INTERRUPT_PIN, &ip); + + printk("%3d:0x%02x:%d 0x%04x-0x%04x: 0x%04x 0x%04x 0x%08" PRIx32 " 0x%08" PRIx32 " %d -> %3d (=0x%02x)\n", + bus, dev, fun, vi, di, cd, st, b1, b2, ip, il, il); + return 0; +} + +void +BSP_pciConfigDump_early(void) +{ + printk("BUS:SLOT:FUN VENDOR-DEV_ID: COMMAND STATUS BASE_ADDR0 BASE_ADDR1 IRQ_PIN -> IRQ_LINE\n"); + BSP_pciScan(0, dump_dev_cb, 0); +} diff --git a/bsps/powerpc/mvme5500/pci/detect_host_bridge.c b/bsps/powerpc/mvme5500/pci/detect_host_bridge.c new file mode 100644 index 0000000000..3bd5b00d7a --- /dev/null +++ b/bsps/powerpc/mvme5500/pci/detect_host_bridge.c @@ -0,0 +1,72 @@ +/* + * detect_host_bridge.c + * + * This code is inspired by detect_grackle_bridge.c of SVGM BSP + * written by Till Straumann + * Copyright (C) 2001, 2003 Till Straumann <strauman@slac.stanford.edu> + * + * Copyright (C) 2004 S. Kate Feng, <feng1@bnl.gov> + * wrote it to support the MVME5500 board. + * + */ +#include <libcpu/io.h> +#include <rtems/bspIo.h> /* printk */ + +#include <bsp/pci.h> +#include <bsp/gtreg.h> +#include <bsp/gtpcireg.h> + +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#define PCI_DEBUG 0 + +#define HOSTBRIDGET_ERROR 0xf0000000 + +unsigned long _BSP_clear_hostbridge_errors(int enableMCP, int quiet) +{ + uint32_t pcidata, pcidata1; + int PciLocal, busNumber=0; + + /* On the mvme5500 board, the GT64260B system controller had the MCP + * signal pulled up high. Thus, the MCP signal is not used as it is + * on other boards such as mvme2307. + */ + if (enableMCP) return(-1); + for (PciLocal=0; PciLocal<1; PciLocal++ ) { + pci_read_config_dword(busNumber, + 0, + 0, + PCI_COMMAND, + &pcidata); + + if (!quiet) + printk("Before _BSP_clear_hostbridge_errors(): 0x%" PRIx32 ", cause 0x%lx\n", + pcidata, inl(0x1d58)); + + outl(0,0x1d58); + + /* Clear the error on the host bridge */ + pcidata1= pcidata; + pcidata1 |= PCI_STATUS_CLRERR_MASK; + pcidata1 |= 0x140; + pci_write_config_dword(busNumber, + 0, + 0, + PCI_COMMAND, + pcidata1); + + pci_read_config_dword(busNumber, + 0, + 0, + PCI_COMMAND, + &pcidata1); + + if (!quiet) printk("After _BSP_clear_hostbridge_errors(): sts 0x%" PRIx32 "\n", + pcidata1); + if (pcidata1 & HOSTBRIDGET_ERROR) printk("BSP_clear_hostbridge_errors(): unable to clear pending hostbridge errors\n"); + busNumber += BSP_MAX_PCI_BUS_ON_PCI0; + } + return(pcidata & HOSTBRIDGET_ERROR); +} diff --git a/bsps/powerpc/mvme5500/pci/pci.c b/bsps/powerpc/mvme5500/pci/pci.c new file mode 100644 index 0000000000..8ef5876047 --- /dev/null +++ b/bsps/powerpc/mvme5500/pci/pci.c @@ -0,0 +1,420 @@ +/* + * pci.c : this file contains basic PCI Io functions. + * + * CopyRight (C) 1999 valette@crf.canon.fr + * + * This code is heavilly 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 the file LICENSE in this distribution or at + * http://www.rtems.org/rtems/license.html. + * + * Copyright 2004, 2008 Brookhaven National Laboratory and + * Shuchen K. Feng, <feng1@bnl.gov> + * + * - to be consistent with the original pci.c written by Eric Valette + * - added 2nd PCI support for discovery based PCI bridge (e.g. mvme5500/mvme6100) + * - added bus support for the expansion of PMCSpan as per request by Peter + */ +#define PCI_MAIN + +#include <libcpu/io.h> +#include <rtems/bspIo.h> /* printk */ + +#include <bsp/irq.h> +#include <bsp/pci.h> +#include <bsp/gtreg.h> +#include <bsp/gtpcireg.h> +#include <bsp.h> + +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#define PCI_DEBUG 0 +#define PCI_PRINT 1 + +/* 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 + +#ifndef PCI1_CONFIG_ADDR +#define PCI1_CONFIG_ADDR 0xc78 +#endif +#ifndef PCI1_CONFIG_DATA +#define PCI1_CONFIG_DATA 0xc7c +#endif + +#define HOSTBRIDGET_ERROR 0xf0000000 + +#define GT64x60_PCI_CONFIG_ADDR GT64x60_REG_BASE + PCI_CONFIG_ADDR +#define GT64x60_PCI_CONFIG_DATA GT64x60_REG_BASE + PCI_CONFIG_DATA + +#define GT64x60_PCI1_CONFIG_ADDR GT64x60_REG_BASE + PCI1_CONFIG_ADDR +#define GT64x60_PCI1_CONFIG_DATA GT64x60_REG_BASE + PCI1_CONFIG_DATA + +static int numPCIDevs=0; +static DiscoveryChipVersion BSP_sysControllerVersion = 0; +static BSP_VMEchipTypes BSP_VMEinterface = 0; +static rtems_pci_config_t BSP_pci[2]={ + {(volatile unsigned char*) (GT64x60_PCI_CONFIG_ADDR), + (volatile unsigned char*) (GT64x60_PCI_CONFIG_DATA), + 0 /* defined at BSP_pci_configuration */}, + {(volatile unsigned char*) (GT64x60_PCI1_CONFIG_ADDR), + (volatile unsigned char*) (GT64x60_PCI1_CONFIG_DATA), + 0 /* defined at BSP_pci_configuration */} +}; + +/* Pack RegNum,FuncNum,DevNum,BusNum,and ConfigEnable for + * PCI Configuration Address Register + */ +#define pciConfigPack(bus,dev,func,offset)\ +((offset&~3)<<24)|(PCI_DEVFN(dev,func)<<16)|(bus<<8)|0x80 + +/* + * Bit encode for PCI_CONFIG_HEADER_TYPE register + */ +static unsigned char ucMaxPCIBus=0; + +/* Please note that PCI0 and PCI1 does not correlate with the busNum 0 and 1. + */ +static int indirect_pci_read_config_byte(unsigned char bus,unsigned char dev,unsigned char func, +unsigned char offset, uint8_t *val) +{ + int n=0; + + if (bus>= BSP_MAX_PCI_BUS_ON_PCI0) { + bus-=BSP_MAX_PCI_BUS_ON_PCI0; + n=1; + } + + *val = 0xff; + if (offset & ~0xff) return PCIBIOS_BAD_REGISTER_NUMBER; +#if 0 + printk("addr %x, data %x, pack %x \n", BSP_pci[n].pci_config_addr), + BSP_pci[n].config_data,pciConfigPack(bus,dev,func,offset)); +#endif + + out_be32((volatile uint32_t *) BSP_pci[n].pci_config_addr, pciConfigPack(bus,dev,func,offset)); + *val = in_8(BSP_pci[n].pci_config_data + (offset&3)); + return PCIBIOS_SUCCESSFUL; +} + +static int indirect_pci_read_config_word(unsigned char bus, unsigned char dev, +unsigned char func, unsigned char offset, uint16_t *val) +{ + int n=0; + + if (bus>= BSP_MAX_PCI_BUS_ON_PCI0) { + bus-=BSP_MAX_PCI_BUS_ON_PCI0; + n=1; + } + + *val = 0xffff; + if ((offset&1)|| (offset & ~0xff)) return PCIBIOS_BAD_REGISTER_NUMBER; +#if 0 + printk("addr %x, data %x, pack %x \n", config_addr, + config_data,pciConfigPack(bus,dev,func,offset)); +#endif + out_be32((volatile uint32_t *) BSP_pci[n].pci_config_addr, pciConfigPack(bus,dev,func,offset)); + *val = in_le16((volatile uint16_t *) (BSP_pci[n].pci_config_data + (offset&2))); + return PCIBIOS_SUCCESSFUL; +} + +static int indirect_pci_read_config_dword(unsigned char bus, unsigned char dev, +unsigned char func, unsigned char offset, uint32_t *val) +{ + int n=0; + + if (bus>= BSP_MAX_PCI_BUS_ON_PCI0) { + bus-=BSP_MAX_PCI_BUS_ON_PCI0; + n=1; + } + + *val = 0xffffffff; + if ((offset&3)|| (offset & ~0xff)) return PCIBIOS_BAD_REGISTER_NUMBER; + + out_be32((volatile uint32_t *)BSP_pci[n].pci_config_addr, pciConfigPack(bus,dev,func,offset)); + *val = in_le32((volatile uint32_t *)BSP_pci[n].pci_config_data); + return PCIBIOS_SUCCESSFUL; +} + +static int indirect_pci_write_config_byte(unsigned char bus, unsigned char dev,unsigned char func, unsigned char offset, uint8_t val) +{ + int n=0; + + if (bus>= BSP_MAX_PCI_BUS_ON_PCI0) { + bus-=BSP_MAX_PCI_BUS_ON_PCI0; + n=1; + } + + if (offset & ~0xff) return PCIBIOS_BAD_REGISTER_NUMBER; + + out_be32((volatile uint32_t *)BSP_pci[n].pci_config_addr, pciConfigPack(bus,dev,func,offset)); + out_8((volatile uint8_t *) (BSP_pci[n].pci_config_data + (offset&3)), val); + return PCIBIOS_SUCCESSFUL; +} + +static int indirect_pci_write_config_word(unsigned char bus, unsigned char dev,unsigned char func, unsigned char offset, uint16_t val) +{ + int n=0; + + if (bus>= BSP_MAX_PCI_BUS_ON_PCI0) { + bus-=BSP_MAX_PCI_BUS_ON_PCI0; + n=1; + } + + if ((offset&1)|| (offset & ~0xff)) return PCIBIOS_BAD_REGISTER_NUMBER; + + out_be32((volatile uint32_t *)BSP_pci[n].pci_config_addr, pciConfigPack(bus,dev,func,offset)); + out_le16((volatile uint16_t *)(BSP_pci[n].pci_config_data + (offset&3)), val); + return PCIBIOS_SUCCESSFUL; +} + +static int indirect_pci_write_config_dword(unsigned char bus,unsigned char dev,unsigned char func, unsigned char offset, uint32_t val) +{ + int n=0; + + if (bus>= BSP_MAX_PCI_BUS_ON_PCI0) { + bus-=BSP_MAX_PCI_BUS_ON_PCI0; + n=1; + } + + if ((offset&3)|| (offset & ~0xff)) return PCIBIOS_BAD_REGISTER_NUMBER; + + out_be32((volatile uint32_t *)BSP_pci[n].pci_config_addr, pciConfigPack(bus,dev,func,offset)); + out_le32((volatile uint32_t *)BSP_pci[n].pci_config_data, val); + return PCIBIOS_SUCCESSFUL; +} + +const pci_config_access_functions pci_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 +}; + + +rtems_pci_config_t BSP_pci_configuration = { + (volatile unsigned char*) (GT64x60_PCI_CONFIG_ADDR), + (volatile unsigned char*) (GT64x60_PCI_CONFIG_DATA), + &pci_indirect_functions}; + +DiscoveryChipVersion BSP_getDiscoveryChipVersion(void) +{ + return(BSP_sysControllerVersion); +} + +BSP_VMEchipTypes BSP_getVMEchipType(void) +{ + return(BSP_VMEinterface); +} + +/* + * This routine determines the maximum bus number in the system. + * The PCI_SUBORDINATE_BUS is not supported in GT6426xAB. Thus, + * it's not used. + * + */ +int pci_initialize(void) +{ + int deviceFound; + unsigned char ucBusNumber, ucSlotNumber, ucFnNumber, ucNumFuncs, data8; + uint32_t ulHeader, ulClass, ulDeviceID; +#if PCI_DEBUG + uint32_t pcidata; +#endif + + /* + * Scan PCI0 and PCI1 buses + */ + for (ucBusNumber=0; ucBusNumber<BSP_MAX_PCI_BUS; ucBusNumber++) { + deviceFound=0; + for (ucSlotNumber=0;ucSlotNumber<PCI_MAX_DEVICES;ucSlotNumber++) { + ucFnNumber = 0; + pci_read_config_dword(ucBusNumber, + ucSlotNumber, + 0, + PCI_VENDOR_ID, + &ulDeviceID); + + if( ulDeviceID==PCI_INVALID_VENDORDEVICEID) { + /* This slot is empty */ + continue; + } + + if (++numPCIDevs > PCI_MAX_DEVICES) { + rtems_panic("Too many PCI devices found; increase PCI_MAX_DEVICES in pci.h\n"); + } + + if (!deviceFound) deviceFound=1; + switch(ulDeviceID) { + case (PCI_VENDOR_ID_MARVELL+(PCI_DEVICE_ID_MARVELL_GT6426xAB<<16)): + pci_read_config_byte(0,0,0,PCI_REVISION_ID, &data8); + switch(data8) { + case 0x10: + BSP_sysControllerVersion = GT64260A; +#if PCI_PRINT + printk("Marvell GT64260A (Discovery I) hostbridge detected at bus%d slot%d\n", + ucBusNumber,ucSlotNumber); +#endif + break; + case 0x20: + BSP_sysControllerVersion = GT64260B; +#if PCI_PRINT + printk("Marvell GT64260B (Discovery I) hostbridge detected at bus%d slot%d\n", + ucBusNumber,ucSlotNumber); +#endif + break; + default: + printk("Undefined revsion of GT64260 chip\n"); + break; + } + break; + case PCI_VENDOR_ID_TUNDRA: +#if PCI_PRINT + printk("TUNDRA PCI-VME bridge detected at bus%d slot%d\n", + ucBusNumber,ucSlotNumber); +#endif + break; + case (PCI_VENDOR_ID_DEC+(PCI_DEVICE_ID_DEC_21150<<16)): +#if PCI_PRINT + printk("DEC21150 PCI-PCI bridge detected at bus%d slot%d\n", + ucBusNumber,ucSlotNumber); +#endif + break; + default : +#if PCI_PRINT + printk("BSP unlisted vendor, Bus%d Slot%d DeviceID 0x%" PRIx32 "\n", + ucBusNumber,ucSlotNumber, ulDeviceID); +#endif + /* Kate Feng : device not supported by BSP needs to remap the IRQ line on mvme5500/mvme6100 */ + pci_read_config_byte(ucBusNumber,ucSlotNumber,0,PCI_INTERRUPT_LINE,&data8); + if (data8 < BSP_GPP_IRQ_LOWEST_OFFSET) pci_write_config_byte(ucBusNumber, + ucSlotNumber,0,PCI_INTERRUPT_LINE,BSP_GPP_IRQ_LOWEST_OFFSET+data8); + + break; + } + +#if PCI_DEBUG + pci_read_config_dword(ucBusNumber, + ucSlotNumber, + 0, + PCI_BASE_ADDRESS_0, + &data); + printk("Bus%d BASE_ADDRESS_0 0x%x \n",ucBusNumber, data); + pci_read_config_dword(ucBusNumber, + ucSlotNumber, + 0, + PCI_BASE_ADDRESS_1, + &data); + printk("Bus%d BASE_ADDRESS_1 0x%x \n",ucBusNumber, data); + pci_read_config_dword(ucBusNumber, + ucSlotNumber, + 0, + PCI_BASE_ADDRESS_2, + &data); + printk("Bus%d BASE_ADDRESS_2 0x%x \n", ucBusNumber, data); + + pci_read_config_dword(ucBusNumber, + ucSlotNumber, + 0, + PCI_BASE_ADDRESS_3, + &data); + printk("Bus%d BASE_ADDRESS_3 0x%x \n", ucBusNumber, data); + + pci_read_config_word(ucBusNumber, + ucSlotNumber, + 0, + PCI_INTERRUPT_LINE, + &sdata); + printk("Bus%d INTERRUPT_LINE 0x%x \n", ucBusNumber, sdata); + + /* We always enable internal memory. */ + pci_read_config_dword(ucBusNumber, + ucSlotNumber, + 0, + PCI_MEM_BASE_ADDR, + &pcidata); + printk("Bus%d MEM_BASE_ADDR 0x%x \n", ucBusNumber,pcidata); + + /* We always enable internal IO. */ + pci_read_config_dword(ucBusNumber, + ucSlotNumber, + 0, + PCI_IO_BASE_ADDR, + &pcidata); + printk("Bus%d IO_BASE_ADDR 0x%x \n", ucBusNumber,pcidata); +#endif + + pci_read_config_dword(ucBusNumber, + ucSlotNumber, + 0, + PCI_CACHE_LINE_SIZE, + &ulHeader); + if ((ulHeader>>16)&PCI_HEADER_TYPE_MULTI_FUNCTION) + ucNumFuncs=PCI_MAX_FUNCTIONS; + else + ucNumFuncs=1; + +#if PCI_DEBUG + printk("Bus%d Slot 0x%x HEADER/LAT/CACHE 0x%x \n", + ucBusNumber, ucSlotNumber, ulHeader); +#endif + + for (ucFnNumber=1;ucFnNumber<ucNumFuncs;ucFnNumber++) { + pci_read_config_dword(ucBusNumber, + ucSlotNumber, + ucFnNumber, + PCI_VENDOR_ID, + &ulDeviceID); + if (ulDeviceID==PCI_INVALID_VENDORDEVICEID) { + /* This slot/function is empty */ + continue; + } + + /* This slot/function has a device fitted.*/ + pci_read_config_dword(ucBusNumber, + ucSlotNumber, + ucFnNumber, + PCI_CLASS_REVISION, + &ulClass); +#if PCI_DEBUG + printk("Bus%d Slot 0x%x Func %d classID 0x%x \n",ucBusNumber,ucSlotNumber, + ucFnNumber, ulClass); +#endif + + } + } + if (deviceFound) ucMaxPCIBus++; + } /* for (ucBusNumber=0; ucBusNumber<BSP_MAX_PCI_BUS; ... */ +#if PCI_DEBUG + printk("number of PCI buses: %d, numPCIDevs %d\n", + pci_bus_count(), numPCIDevs); +#endif + pci_interface(); + return(0); +} + +void FixupPCI( const struct _int_map *bspmap, int (*swizzler)(int,int) ) +{ +} + +/* + * Return the number of PCI buses in the system + */ +unsigned char pci_bus_count() +{ + return(ucMaxPCIBus); +} + diff --git a/bsps/powerpc/mvme5500/pci/pci_interface.c b/bsps/powerpc/mvme5500/pci/pci_interface.c new file mode 100644 index 0000000000..9f7be59151 --- /dev/null +++ b/bsps/powerpc/mvme5500/pci/pci_interface.c @@ -0,0 +1,100 @@ +/* pci_interface.c + * + * Copyright 2004, 2006, 2007 All rights reserved. (NDA items) + * Brookhaven National Laboratory and Shuchen Kate Feng <feng1@bnl.gov> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution. + * + * 8/17/2006 : S. Kate Feng + * uses in_le32()/out_le32(), instead of inl()/outl() for compatibility. + * + * 11/2008 : Enable "PCI Read Agressive Prefetch", + * "PCI Read Line Agressive Prefetch", and + * "PCI Read Multiple Agressive Prefetch" to improve the + * performance of the PCI based applications (e.g. 1GHz NIC). + */ + +#include <libcpu/io.h> +#include <rtems/bspIo.h> /* printk */ + +#include <bsp.h> +#include <bsp/pci.h> +#include <bsp/gtreg.h> +#include <bsp/gtpcireg.h> + +#include <inttypes.h> + +#define PCI_DEBUG 0 + +#if 0 +#define CPU2PCI_ORDER +#define PCI2CPU_ORDER +#endif + +/* PCI Read Agressive Prefetch Enable (1<<16 ), + * PCI Read Line Agressive Prefetch Enable( 1<<17), + * PCI Read Multiple Agressive Prefetch Enable (1<<18). + */ +#ifdef PCI2CPU_ORDER +#define PCI_ACCCTLBASEL_VALUE 0x01079000 +#else +#define PCI_ACCCTLBASEL_VALUE 0x01071000 +#endif + + +#define ConfSBDis 0x10000000 /* 1: disable, 0: enable */ +#define IOSBDis 0x20000000 /* 1: disable, 0: enable */ +#define ConfIOSBDis 0x30000000 +#define CpuPipeline 0x00002000 /* optional, 1:enable, 0:disable */ + +/* CPU to PCI ordering register */ +#define DLOCK_ORDER_REG 0x2D0 /* Deadlock and Ordering register */ +#define PCI0OrEn 0x00000001 +#define PCI1OrEn 0x00000020 +#define PCIOrEn 0x40000000 +#define PCIOrEnMASK 0x40000021 + +#define CNT_SYNC_REG 0x2E0 /* Counters and Sync Barrier register */ +#define L0SyncBar 0x00001000 +#define L1SyncBar 0x00002000 +#define DSyncBar 0x00004000 +#define SyncBarMode 0x00008000 +#define SyncBarMASK 0x0000f000 + +#define WRTBK_PRIO_BUFFER 0x2d8 /* writback priority and buffer depth */ + +#define ADDR_PIPELINE 0x00020000 + +void pciAccessInit(void); + +void pci_interface(void) +{ + +#ifdef CPU2PCI_ORDER + /* MOTLOad deafult : 0x07ff8600 */ + out_le32((volatile uint32_t *)(GT64x60_REG_BASE+CNT_SYNC_REG), 0x07fff600); +#endif + /* asserts SERR upon various detection */ + out_le32((volatile uint32_t *)(GT64x60_REG_BASE+0xc28), 0x3fffff); + pciAccessInit(); +} + +void pciAccessInit(void) +{ + unsigned int PciLocal, data; + + for (PciLocal=0; PciLocal < 2; PciLocal++) { + data = in_le32((volatile uint32_t *)(GT64x60_REG_BASE+PCI0_ACCESS_CNTL_BASE0_LOW+(PciLocal * 0x80))); +#if 0 + printk("PCI%d_ACCESS_CNTL_BASE0_LOW was 0x%x\n",PciLocal,data); +#endif + data |= PCI_ACCCTLBASEL_VALUE; + data &= ~0x300000; + out_le32((volatile uint32_t *)(GT64x60_REG_BASE+PCI0_ACCESS_CNTL_BASE0_LOW+(PciLocal * 0x80)), data); +#if 0 + printf("PCI%d_ACCESS_CNTL_BASE0_LOW now 0x%" PRIx32 "\n",PciLocal,in_le32((volatile uint32_t *)(GT64x60_REG_BASE+PCI0_ACCESS_CNTL_BASE0_LOW+(PciLocal * 0x80)))); +#endif + } +} + diff --git a/bsps/powerpc/shared/pci/detect_raven_bridge.c b/bsps/powerpc/shared/pci/detect_raven_bridge.c new file mode 100644 index 0000000000..0a1c04a2e2 --- /dev/null +++ b/bsps/powerpc/shared/pci/detect_raven_bridge.c @@ -0,0 +1,197 @@ +#include <libcpu/io.h> +#include <libcpu/spr.h> +#include <inttypes.h> + +#include <bsp.h> +#include <bsp/pci.h> +#include <bsp/consoleIo.h> +#include <bsp/residual.h> +#include <bsp/openpic.h> +#include <bsp/irq.h> + +#include <rtems/bspIo.h> +#include <libcpu/cpuIdent.h> + +#define SHOW_RAVEN_SETTINGS + +#define RAVEN_MPIC_IOSPACE_ENABLE 0x0001 +#define RAVEN_MPIC_MEMSPACE_ENABLE 0x0002 +#define RAVEN_MASTER_ENABLE 0x0004 +#define RAVEN_PARITY_CHECK_ENABLE 0x0040 +#define RAVEN_SYSTEM_ERROR_ENABLE 0x0100 +#define RAVEN_CLEAR_EVENTS_MASK 0xf9000000 + +#define RAVEN_MPIC_MEREN ((volatile unsigned *)0xfeff0020) +#define RAVEN_MPIC_MERST ((volatile unsigned *)0xfeff0024) +#define MEREN_VAL 0x2f00 + +#define pci BSP_pci_configuration + +extern const pci_config_access_functions pci_direct_functions; +extern const pci_config_access_functions pci_indirect_functions; + +#if defined(mvme2100) +/* FIXME - this should really be in a separate file - the 2100 doesn't + * have a raven chip so there is no point having 2100 code here + */ + +extern unsigned int EUMBBAR; + +void detect_host_bridge(void) +{ + /* + * If the processor is an 8240 or an 8245 then the PIC is built + * in instead of being on the PCI bus. The MVME2100 is using Processor + * Address Map B (CHRP) although the Programmer's Reference Guide says + * it defaults to Map A. + */ + /* We have an EPIC Interrupt Controller */ + OpenPIC = (volatile struct OpenPIC *) (EUMBBAR + BSP_OPEN_PIC_BASE_OFFSET); + pci.pci_functions = &pci_indirect_functions; + pci.pci_config_addr = (volatile unsigned char *) 0xfec00000; + pci.pci_config_data = (volatile unsigned char *) 0xfee00000; +} + +#else + +#if 0 +/* Unfortunately, PCI config space access to empty slots generates + * a 'signalled master abort' condition --> we can't really use + * the machine check interrupt for memory probing unless + * we use probing for PCI scanning also (which would make + * all that code either BSP dependent or requiring yet another + * API, sigh...). + * So for the moment, we just don't use MCP on all mvme2xxx + * boards (using the generic, hostbridge-independent 'clear' + * implementation [generic_clear_hberrs.c]). + */ +/* + * enableMCP: whether to enable MCP checkstop / machine check interrupts + * on the hostbridge and in HID0. + * + * NOTE: HID0 and MEREN are left alone if this flag is 0 + * + * quiet : be silent + * + * RETURNS : raven MERST register contents (lowermost 16 bits), 0 if + * there were no errors + */ +unsigned long +_BSP_clear_hostbridge_errors(int enableMCP, int quiet) +{ +unsigned merst; + + merst = in_be32(RAVEN_MPIC_MERST); + /* write back value to clear status */ + out_be32(RAVEN_MPIC_MERST, merst); + + if (enableMCP) { + if (!quiet) + printk("Enabling MCP generation on hostbridge errors\n"); + out_be32(RAVEN_MPIC_MEREN, MEREN_VAL); + } else { + out_be32(RAVEN_MPIC_MEREN, 0); + if ( !quiet && enableMCP ) { + printk("leaving MCP interrupt disabled\n"); + } + } + return (merst & 0xffff); +} +#endif + +void detect_host_bridge(void) +{ + PPC_DEVICE *hostbridge; + uint32_t id0; + uint32_t tmp; + + /* + * This code assumes that the host bridge is located at + * bus 0, dev 0, func 0 AND that the old pre PCI 2.1 + * standard devices detection mechanism that was used on PC + * (still used in BSD source code) works. + */ + hostbridge=residual_find_device(&residualCopy, PROCESSORDEVICE, NULL, + BridgeController, + PCIBridge, -1, 0); + if (hostbridge) { + if (hostbridge->DeviceId.Interface==PCIBridgeIndirect) { + pci.pci_functions=&pci_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.pci_config_addr = ((volatile unsigned char *) + (ptr_mem_map->io_base+0xcf8)); + pci.pci_config_data = ptr_mem_map->io_base+0xcfc; + } else if(hostbridge->DeviceId.Interface==PCIBridgeDirect) { + pci.pci_functions=&pci_direct_functions; + pci.pci_config_data=(unsigned char *) 0x80800000; + } else { + } + } else { + /* Let us try by experimentation at our own risk! */ + pci.pci_functions = &pci_direct_functions; + /* On all direct bridges I know the host bridge itself + * appears as device 0 function 0. + */ + pci_read_config_dword(0, 0, 0, PCI_VENDOR_ID, &id0); + if (id0==~0U) { + pci.pci_functions = &pci_indirect_functions; + pci.pci_config_addr = ((volatile unsigned char*) + (ptr_mem_map->io_base+0xcf8)); + pci.pci_config_data = ((volatile unsigned char*)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. + */ + } + pci_read_config_dword(0, 0, 0, 0, &id0); +#ifdef SHOW_RAVEN_SETTINGS + printk("idreg 0 = 0x%" PRIu32 "\n",id0); +#endif + if((id0 == PCI_VENDOR_ID_MOTOROLA + + (PCI_DEVICE_ID_MOTOROLA_RAVEN<<16)) || + (id0 == PCI_VENDOR_ID_MOTOROLA + + (PCI_DEVICE_ID_MOTOROLA_HAWK<<16))) { + /* + * We have a Raven bridge. We will get information about its settings + */ + pci_read_config_dword(0, 0, 0, PCI_COMMAND, &id0); +#ifdef SHOW_RAVEN_SETTING + printk("RAVEN PCI command register = %x\n",id0); +#endif + id0 |= RAVEN_CLEAR_EVENTS_MASK; + pci_write_config_dword(0, 0, 0, PCI_COMMAND, id0); + pci_read_config_dword(0, 0, 0, PCI_COMMAND, &id0); +#ifdef SHOW_RAVEN_SETTING + printk("After error clearing RAVEN PCI command register = %x\n",id0); +#endif + + if (id0 & RAVEN_MPIC_IOSPACE_ENABLE) { + pci_read_config_dword(0, 0, 0,PCI_BASE_ADDRESS_0, &tmp); +#ifdef SHOW_RAVEN_SETTING + printk("Raven MPIC is accessed via IO Space Access at address : %x\n",(tmp & ~0x1)); +#endif + } + if (id0 & RAVEN_MPIC_MEMSPACE_ENABLE) { + pci_read_config_dword(0, 0, 0,PCI_BASE_ADDRESS_1, &tmp); +#ifdef SHOW_RAVEN_SETTING + printk("Raven MPIC is accessed via memory Space Access at address : %x\n", tmp); +#endif + OpenPIC=(volatile struct OpenPIC *) (tmp + PREP_ISA_MEM_BASE); + printk("OpenPIC found at %p.\n", OpenPIC); + } + } + +#if BSP_PCI_IRQ_NUMBER > 0 + if (OpenPIC == (volatile struct OpenPIC *)0) { + rtems_panic("OpenPic Not found\n"); + } +#endif + +} + +#endif diff --git a/bsps/powerpc/shared/pci/generic_clear_hberrs.c b/bsps/powerpc/shared/pci/generic_clear_hberrs.c new file mode 100644 index 0000000000..1bc4009b15 --- /dev/null +++ b/bsps/powerpc/shared/pci/generic_clear_hberrs.c @@ -0,0 +1,63 @@ +#include <libcpu/io.h> +#include <libcpu/spr.h> + +#include <bsp.h> +#include <bsp/pci.h> + +#include <rtems/bspIo.h> + +#define PCI_ERR_BITS 0xf900 +#define PCI_STATUS_OK(x) (!((x)&PCI_ERR_BITS)) + +/* For now, just clear errors in the PCI status reg. + * + * Returns: (for diagnostic purposes) + * original settings (i.e. before applying the clearing + * sequence) or the error bits or 0 if there were no errors. + * + */ + +unsigned short +(*_BSP_clear_vmebridge_errors)(int) = 0; + +unsigned long +_BSP_clear_hostbridge_errors(int enableMCP, int quiet) +{ +unsigned long rval; +unsigned short pcistat; +int count; + + if (enableMCP) + return -1; /* exceptions not supported / MCP not wired */ + + /* read error status for info return */ + pci_read_config_word(0,0,0,PCI_STATUS,&pcistat); + rval = pcistat; + + count=10; + do { + /* clear error reporting registers */ + + /* clear PCI status register */ + pci_write_config_word(0,0,0,PCI_STATUS, PCI_ERR_BITS); + + /* read new status */ + pci_read_config_word(0,0,0,PCI_STATUS, &pcistat); + + } while ( ! PCI_STATUS_OK(pcistat) && count-- ); + + if ( !PCI_STATUS_OK(rval) && !quiet) { + printk("Cleared PCI errors: pci_stat was 0x%04lx\n", rval); + } + if ( !PCI_STATUS_OK(pcistat) ) { + printk("Unable to clear PCI errors: still 0x%04x after 10 attempts\n", pcistat); + } + + rval &= PCI_ERR_BITS; + + /* Some VME bridges (Tsi148) don't propagate VME bus errors to PCI status reg. */ + if ( _BSP_clear_vmebridge_errors ) + rval |= _BSP_clear_vmebridge_errors(quiet)<<16; + + return rval; +} diff --git a/bsps/powerpc/shared/pci/pci.c b/bsps/powerpc/shared/pci/pci.c new file mode 100644 index 0000000000..f75151ad46 --- /dev/null +++ b/bsps/powerpc/shared/pci/pci.c @@ -0,0 +1,640 @@ +/* + * 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 the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + * Till Straumann, <strauman@slac.stanford.edu>, 1/2002 + * - separated bridge detection code out of this file + */ + +#include <rtems.h> +#include <bsp.h> + +#include <libcpu/io.h> +#include <bsp/pci.h> +#include <rtems/bspIo.h> + +#undef SHOW_PCI_SETTING + +/* 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 a shortcut */ +#define pci BSP_pci_configuration + +#ifndef PCI_CONFIG_ADDR_VAL +#define PCI_CONFIG_ADDR_VAL(bus, slot, funcion, offset) \ + (0x80<<24|((bus)<<16)|(PCI_DEVFN((slot),(function))<<8)|(((offset)&~3))) +#endif + +#ifndef PCI_CONFIG_WR_ADDR +#define PCI_CONFIG_WR_ADDR( addr, val ) out_le32((volatile uint32_t*)(addr), (val)) +#endif + +#define PCI_CONFIG_SET_ADDR(addr, bus, slot,function,offset) \ + PCI_CONFIG_WR_ADDR((addr), PCI_CONFIG_ADDR_VAL((bus), (slot), (function), (offset))) + + +extern void detect_host_bridge(void); + +/* + * Bit encode for PCI_CONFIG_HEADER_TYPE register + */ +unsigned char ucMaxPCIBus; + +static int +indirect_pci_read_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint8_t *val +) { + PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); + *val = in_8(pci.pci_config_data + (offset&3)); + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_pci_read_config_word( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint16_t *val +) { + *val = 0xffff; + if (offset&1) + return PCIBIOS_BAD_REGISTER_NUMBER; + + PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); + *val = in_le16((volatile uint16_t *)(pci.pci_config_data + (offset&3))); + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_pci_read_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint32_t *val +) { + *val = 0xffffffff; + if (offset&3) + return PCIBIOS_BAD_REGISTER_NUMBER; + + PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); + *val = in_le32((volatile uint32_t *)pci.pci_config_data); + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_pci_write_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint8_t val +) { + PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); + out_8(pci.pci_config_data + (offset&3), val); + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_pci_write_config_word( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint16_t val +) { + if (offset&1) + return PCIBIOS_BAD_REGISTER_NUMBER; + + PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); + out_le16((volatile uint16_t *)(pci.pci_config_data + (offset&3)), val); + return PCIBIOS_SUCCESSFUL; +} + +static int +indirect_pci_write_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint32_t val +) { + if (offset&3) + return PCIBIOS_BAD_REGISTER_NUMBER; + PCI_CONFIG_SET_ADDR(pci.pci_config_addr, bus, slot, function, offset); + out_le32((volatile uint32_t *)pci.pci_config_data, val); + return PCIBIOS_SUCCESSFUL; +} + +const pci_config_access_functions pci_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 +}; + +rtems_pci_config_t BSP_pci_configuration = { + (volatile unsigned char*)PCI_CONFIG_ADDR, + (volatile unsigned char*)PCI_CONFIG_DATA, + &pci_indirect_functions +}; + +static int +direct_pci_read_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint8_t *val +) { + if (bus != 0 || (1<<slot & 0xff8007fe)) { + *val=0xff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + *val=in_8(pci.pci_config_data + ((1<<slot)&~1) + + (function<<8) + offset); + return PCIBIOS_SUCCESSFUL; +} + +static int +direct_pci_read_config_word( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint16_t *val +) { + *val = 0xffff; + if (offset&1) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (bus != 0 || (1<<slot & 0xff8007fe)) + return PCIBIOS_DEVICE_NOT_FOUND; + + *val=in_le16((volatile uint16_t *) + (pci.pci_config_data + ((1<<slot)&~1) + + (function<<8) + offset)); + return PCIBIOS_SUCCESSFUL; +} + +static int +direct_pci_read_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint32_t *val +) { + *val = 0xffffffff; + if (offset&3) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (bus != 0 || (1<<slot & 0xff8007fe)) + return PCIBIOS_DEVICE_NOT_FOUND; + + *val=in_le32((volatile uint32_t *) + (pci.pci_config_data + ((1<<slot)&~1) + + (function<<8) + offset)); + return PCIBIOS_SUCCESSFUL; +} + +static int +direct_pci_write_config_byte( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint8_t val +) { + if (bus != 0 || (1<<slot & 0xff8007fe)) + return PCIBIOS_DEVICE_NOT_FOUND; + + out_8(pci.pci_config_data + ((1<<slot)&~1) + + (function<<8) + offset, + val); + return PCIBIOS_SUCCESSFUL; +} + +static int +direct_pci_write_config_word( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint16_t val +) { + if (offset&1) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (bus != 0 || (1<<slot & 0xff8007fe)) + return PCIBIOS_DEVICE_NOT_FOUND; + + out_le16((volatile uint16_t *) + (pci.pci_config_data + ((1<<slot)&~1) + + (function<<8) + offset), + val); + return PCIBIOS_SUCCESSFUL; +} + +static int +direct_pci_write_config_dword( + unsigned char bus, + unsigned char slot, + unsigned char function, + unsigned char offset, + uint32_t val +) { + if (offset&3) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (bus != 0 || (1<<slot & 0xff8007fe)) + return PCIBIOS_DEVICE_NOT_FOUND; + + out_le32((volatile uint32_t *) + (pci.pci_config_data + ((1<<slot)&~1) + + (function<<8) + offset), + val); + return PCIBIOS_SUCCESSFUL; +} + +const pci_config_access_functions pci_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 +}; + +#define PRINT_MSG() \ + printk("pci : Device %d:0x%02x:%d routed to interrupt_line %d\n", \ + pbus, pslot, pfun, int_name ) + +/* +** Validate a test interrupt name and print a warning if its not one of +** the names defined in the routing record. +*/ +static int test_intname( + const struct _int_map *row, + int pbus, + int pslot, + int pfun, + int int_pin, + int int_name +) { + int j, k; + int _nopin= -1, _noname= -1; + + for (j=0; row->pin_route[j].pin > -1; j++) { + if ( row->pin_route[j].pin == int_pin ) { + _nopin = 0; + + for (k=0; k<4 && row->pin_route[j].int_name[k] > -1; k++ ) { + if ( row->pin_route[j].int_name[k] == int_name ) { + _noname=0; break; + } + } + break; + } + } + + if( _nopin ) + { + printk("pci : Device %d:0x%02x:%d supplied a bogus interrupt_pin %d\n", pbus, pslot, pfun, int_pin ); + return -1; + } + else + { + if( _noname ) { + unsigned char v = row->pin_route[j].int_name[0]; + printk("pci : Device %d:0x%02x:%d supplied a suspicious interrupt_line %d, ", pbus, pslot, pfun, int_name ); + if ( (row->opts & PCI_FIXUP_OPT_OVERRIDE_NAME) && 255 != (v = row->pin_route[j].int_name[0]) ) { + printk("OVERRIDING with %d from fixup table\n", v); + pci_write_config_byte(pbus,pslot,pfun,PCI_INTERRUPT_LINE,v); + } else { + printk("using it anyway\n"); + } + } + } + return 0; +} + +struct pcibridge +{ + int bus; + int slot; +}; + +static int FindPCIbridge( int mybus, struct pcibridge *pb ) +{ + int pbus, pslot; + uint8_t bussec, buspri; + uint16_t devid, vendorid, dclass; + + for(pbus=0; pbus< pci_bus_count(); pbus++) { + for(pslot=0; pslot< PCI_MAX_DEVICES; pslot++) { + pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &devid); + if ( devid == 0xffff ) continue; + + pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &vendorid); + if ( vendorid == 0xffff ) continue; + + pci_read_config_word(pbus, pslot, 0, PCI_CLASS_DEVICE, &dclass); + + if ( dclass == PCI_CLASS_BRIDGE_PCI ) { + pci_read_config_byte(pbus, pslot, 0, PCI_PRIMARY_BUS, &buspri); + pci_read_config_byte(pbus, pslot, 0, PCI_SECONDARY_BUS, &bussec); + +#ifdef SHOW_PCI_SETTING + printk("pci : Found bridge at %d:0x%02x, mybus %d, pribus %d, secbus %d ", + pbus, pslot, mybus, buspri, bussec ); +#endif + if ( bussec == mybus ) { +#ifdef SHOW_PCI_SETTING + printk("match\n"); +#endif + /* found our nearest bridge going towards the root */ + pb->bus = pbus; + pb->slot = pslot; + + return 0; + } +#ifdef SHOW_PCI_SETTING + printk("no match\n"); +#endif + } + + } + } + return -1; +} + +void FixupPCI( const struct _int_map *bspmap, int (*swizzler)(int,int) ) +{ + unsigned char cvalue; + uint16_t devid; + int ismatch, i, j, pbus, pslot, pfun, int_pin, int_name, nfuns; + + /* + * If the device has a non-zero INTERRUPT_PIN, assign a bsp-specific + * INTERRUPT_NAME if one isn't already in place. Then, drivers can + * trivially use INTERRUPT_NAME to hook up with devices. + */ + + for (pbus=0; pbus< pci_bus_count(); pbus++) { + for (pslot=0; pslot< PCI_MAX_DEVICES; pslot++) { + pci_read_config_word(pbus, pslot, 0, PCI_DEVICE_ID, &devid); + if ( devid == 0xffff ) continue; + + /* got a device */ + pci_read_config_byte(pbus, pslot, 0, PCI_HEADER_TYPE, &cvalue); + nfuns = cvalue & PCI_HEADER_TYPE_MULTI_FUNCTION ? PCI_MAX_FUNCTIONS : 1; + + for (pfun=0; pfun< nfuns; pfun++) { + pci_read_config_word(pbus, pslot, pfun, PCI_DEVICE_ID, &devid); + if( devid == 0xffff ) continue; + + pci_read_config_byte( pbus, pslot, pfun, PCI_INTERRUPT_PIN, &cvalue); + int_pin = cvalue; + + pci_read_config_byte( pbus, pslot, pfun, PCI_INTERRUPT_LINE, &cvalue); + int_name = cvalue; + + /* printk("pci : device %d:0x%02x:%i devid %04x, intpin %d, intline %d\n", + pbus, pslot, pfun, devid, int_pin, int_name ); */ + +#ifdef SHOW_PCI_SETTING + { + unsigned short cmd,stat; + unsigned char lat, seclat, csize; + + pci_read_config_word(pbus,pslot,pfun,PCI_COMMAND, &cmd ); + pci_read_config_word(pbus,pslot,pfun,PCI_STATUS, &stat ); + pci_read_config_byte(pbus,pslot,pfun,PCI_LATENCY_TIMER, &lat ); + pci_read_config_byte(pbus,pslot,pfun,PCI_SEC_LATENCY_TIMER, &seclat ); + pci_read_config_byte(pbus,pslot,pfun,PCI_CACHE_LINE_SIZE, &csize ); + + + printk("pci : device %d:0x%02x:%d cmd %04X, stat %04X, latency %d, " + " sec_latency %d, clsize %d\n", pbus, pslot, pfun, cmd, stat, + lat, seclat, csize); + } +#endif + + if ( int_pin > 0 ) { + ismatch = 0; + + /* + * first run thru the bspmap table and see if we have an + * explicit configuration + */ + for (i=0; bspmap[i].bus > -1; i++) { + if ( bspmap[i].bus == pbus && bspmap[i].slot == pslot ) { + ismatch = -1; + /* we have a record in the table that gives specific + * pins and interrupts for devices in this slot */ + if ( int_name == 255 ) { + /* find the vector associated with whatever pin the + * device gives us + */ + for ( int_name=-1, j=0; bspmap[i].pin_route[j].pin > -1; j++ ) { + if ( bspmap[i].pin_route[j].pin == int_pin ) { + int_name = bspmap[i].pin_route[j].int_name[0]; + break; + } + } + if ( int_name == -1 ) { + printk("pci : Unable to resolve device %d:0x%02x:%d w/ swizzled int " + "pin %i to an interrupt_line.\n", pbus, pslot, pfun, int_pin ); + } else { + PRINT_MSG(); + pci_write_config_byte( pbus,pslot,pfun, + PCI_INTERRUPT_LINE,(cvalue= int_name, cvalue)); + } + } else { + test_intname( &bspmap[i],pbus,pslot,pfun,int_pin,int_name); + } + break; + } + } + + if ( !ismatch ) { + /* + * no match, which means we're on a bus someplace. Work + * backwards from it to one of our defined busses, + * swizzling thru each bridge on the way. + */ + + /* keep pbus, pslot pointed to the device being + * configured while we track down the bridges using + * tbus,tslot. We keep searching the routing table because + * we may end up finding our bridge in it + */ + + int tbus= pbus, tslot= pslot; + + for (;;) { + for (i=0; bspmap[i].bus > -1; i++) { + if ( bspmap[i].bus == tbus && + (bspmap[i].slot == tslot || bspmap[i].slot == -1) ) { + ismatch = -1; + /* found a record for this bus, so swizzle the + * int_pin which we then use to find the + * interrupt_name. + */ + + if ( int_name == 255 ) { + /* + * FIXME. I can't believe this little hack + * is right. It does not yield an error in + * convienently simple situations. + */ + if ( tbus ) int_pin = (*swizzler)(tslot,int_pin); + + /* + * int_pin points to the interrupt channel + * this card ends up delivering interrupts + * on. Find the int_name servicing it. + */ + for (int_name=-1, j=0; bspmap[i].pin_route[j].pin > -1; j++){ + if ( bspmap[i].pin_route[j].pin == int_pin ) { + int_name = bspmap[i].pin_route[j].int_name[0]; + break; + } + } + + if ( int_name == -1 ) { + printk("pci : Unable to resolve device %d:0x%02x:%d w/ swizzled " + "int pin %i to an interrupt_line.\n", + pbus, pslot, pfun, int_pin ); + } else { + PRINT_MSG(); + pci_write_config_byte(pbus,pslot,pfun, + PCI_INTERRUPT_LINE,(cvalue=int_name, cvalue)); + } + } else { + test_intname(&bspmap[i],pbus,pslot,pfun,int_pin,int_name); + } + goto donesearch; + } + } + + if ( !ismatch ) { + struct pcibridge pb; + + /* + * Haven't found our bus in the int map, so work + * upwards thru the bridges till we find it. + */ + + if ( FindPCIbridge( tbus, &pb )== 0 ) { + int_pin = (*swizzler)(tslot,int_pin); + + /* our next bridge up is on pb.bus, pb.slot- now + * instead of pointing to the device we're + * trying to configure, we move from bridge to + * bridge. + */ + + tbus = pb.bus; + tslot = pb.slot; + } else { + printk("pci : No bridge from bus %i towards root found\n", + tbus ); + goto donesearch; + } + } + } + } +donesearch: + + if ( !ismatch && int_pin != 0 && int_name == 255 ) { + printk("pci : Unable to match device %d:0x%02x:%d with an int " + "routing table entry\n", pbus, pslot, pfun ); + } + } + } + } + } +} + +/* + * This routine determines the maximum bus number in the system + */ +int pci_initialize(void) +{ + unsigned char ucSlotNumber, ucFnNumber, ucNumFuncs; + unsigned char ucHeader; + unsigned char ucMaxSubordinate; + uint32_t ulClass; + uint32_t ulDeviceID; + + detect_host_bridge(); + + /* + * Scan PCI bus 0 looking for PCI-PCI bridges + */ + for (ucSlotNumber=0;ucSlotNumber<PCI_MAX_DEVICES;ucSlotNumber++) { + pci_read_config_dword(0, ucSlotNumber, 0, PCI_VENDOR_ID, &ulDeviceID); + if (ulDeviceID==PCI_INVALID_VENDORDEVICEID) { + /* This slot is empty */ + continue; + } + pci_read_config_byte(0, ucSlotNumber, 0, PCI_HEADER_TYPE, &ucHeader); + if (ucHeader&PCI_HEADER_TYPE_MULTI_FUNCTION) { + ucNumFuncs=PCI_MAX_FUNCTIONS; + } else { + ucNumFuncs=1; + } + for (ucFnNumber=0;ucFnNumber<ucNumFuncs;ucFnNumber++) { + 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. */ + 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 */ + pci_read_config_byte(0, ucSlotNumber, ucFnNumber, + PCI_SUBORDINATE_BUS, &ucMaxSubordinate); + if (ucMaxSubordinate>ucMaxPCIBus) { + ucMaxPCIBus=ucMaxSubordinate; + } + } + } + } + return PCIB_ERR_SUCCESS; +} + +/* + * Return the number of PCI busses in the system + */ +unsigned char pci_bus_count(void) +{ + return (ucMaxPCIBus+1); +} diff --git a/bsps/powerpc/shared/pci/pcifinddevice.c b/bsps/powerpc/shared/pci/pcifinddevice.c new file mode 100644 index 0000000000..8aad50547a --- /dev/null +++ b/bsps/powerpc/shared/pci/pcifinddevice.c @@ -0,0 +1,212 @@ +/* find a particular PCI device + * (we assume, the firmware configured the PCI bus[es] for us) + */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann <strauman@slac.stanford.edu>, 2001, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include <inttypes.h> +#include <bsp/pci.h> +#include <rtems/bspIo.h> +#include <stdio.h> + +/* Stolen from i386... */ + +/* + * Make device signature from bus number, device number and function + * number + */ +#define PCIB_DEVSIG_MAKE(b,d,f) ((b<<8)|(d<<3)|(f)) + +/* + * Extract various parts from device signature + */ +#define PCIB_DEVSIG_BUS(x) (((x)>>8) &0xff) +#define PCIB_DEVSIG_DEV(x) (((x)>>3) & 0x1f) +#define PCIB_DEVSIG_FUNC(x) ((x) & 0x7) + +typedef struct { + unsigned short vid,did; + int inst; +} fd_arg; + +static int +find_dev_cb( + int bus, + int dev, + int fun, + void *uarg +) { +fd_arg *a = uarg; +unsigned short s; + + pci_read_config_word(bus,dev,fun,PCI_VENDOR_ID,&s); + if (a->vid == s) { + pci_read_config_word(bus,dev,fun,PCI_DEVICE_ID,&s); + if (a->did == s && 0 == a->inst-- ) { + a->inst = PCIB_DEVSIG_MAKE( bus, dev, fun ); + return 1; + } + } + return 0; +} + +int +pci_find_device( + unsigned short vendorid, + unsigned short deviceid, + int instance, + int *pbus, + int *pdev, + int *pfun +) { +fd_arg a; +void *h; + a.vid = vendorid; + a.did = deviceid; + a.inst = instance; + + if ( (h = BSP_pciScan(0, find_dev_cb, (void*)&a)) ) { + *pbus = PCIB_DEVSIG_BUS( a.inst ); + *pdev = PCIB_DEVSIG_DEV( a.inst ); + *pfun = PCIB_DEVSIG_FUNC( a.inst ); + return 0; + } + return -1; +} + +static int +dump_dev_cb( + int bus, + int dev, + int fun, + void *uarg +) +{ + uint16_t vi,di; + uint16_t cd,st; + uint32_t b1,b2; + uint8_t il,ip; + FILE *f = uarg; + + pci_read_config_word (bus, dev, fun, PCI_VENDOR_ID, &vi); + pci_read_config_word (bus, dev, fun, PCI_DEVICE_ID, &di); + pci_read_config_word (bus, dev, fun, PCI_COMMAND, &cd); + pci_read_config_word (bus, dev, fun, PCI_STATUS, &st); + pci_read_config_dword(bus, dev, fun, PCI_BASE_ADDRESS_0, &b1); + pci_read_config_dword(bus, dev, fun, PCI_BASE_ADDRESS_1, &b2); + pci_read_config_byte (bus, dev, fun, PCI_INTERRUPT_LINE, &il); + pci_read_config_byte (bus, dev, fun, PCI_INTERRUPT_PIN, &ip); + + /* fprintf(f,"%3d:0x%02x:%d 0x%04x-0x%04x: 0x%04x 0x%04x 0x%08x 0x%08x %d -> %3d (=0x%02x)\n", */ + fprintf(f,"%3d:0x%02x:%d 0x%04x-0x%04x: 0x%04x 0x%04x 0x%08" PRIx32 " 0x%08" PRIx32 " %d -> %3d (=0x%02x)\n", + bus, dev, fun, vi, di, cd, st, b1, b2, ip, il, il); + return 0; +} + +void +BSP_pciConfigDump(FILE *f) +{ + if ( !f ) + f = stdout; + fprintf(f,"BUS:SLOT:FUN VENDOR-DEV_ID: COMMAND STATUS BASE_ADDR0 BASE_ADDR1 IRQ_PIN -> IRQ_LINE\n"); + BSP_pciScan(0, dump_dev_cb, f); +} + +BSP_PciScanHandle +BSP_pciScan( + BSP_PciScanHandle handle, + BSP_PciScannerCb cb, + void *uarg +) { + + uint32_t d; + unsigned char bus,dev,fun,hd; + + bus = PCIB_DEVSIG_BUS( (unsigned long)handle ); + dev = PCIB_DEVSIG_DEV( (unsigned long)handle ); + fun = PCIB_DEVSIG_FUNC( (unsigned long)handle ); + + hd = fun > 0 ? PCI_MAX_FUNCTIONS : 1; + + for (; bus<pci_bus_count(); bus++, dev=0) { + for (; dev<PCI_MAX_DEVICES; dev++, fun=0) { + for (; fun<hd; fun++) { + /* + * The last devfn id/slot is special; must skip it + */ + if (PCI_MAX_DEVICES-1==dev && PCI_MAX_FUNCTIONS-1 == fun) + break; + + (void)pci_read_config_dword(bus,dev,0,PCI_VENDOR_ID,&d); + if (PCI_INVALID_VENDORDEVICEID == d) + continue; + + if ( 0 == fun ) { + pci_read_config_byte(bus,dev,0, PCI_HEADER_TYPE, &hd); + hd = (hd & PCI_HEADER_TYPE_MULTI_FUNCTION ? PCI_MAX_FUNCTIONS : 1); + } + + (void)pci_read_config_dword(bus,dev,fun,PCI_VENDOR_ID,&d); + if (PCI_INVALID_VENDORDEVICEID == d) + continue; +#ifdef PCI_DEBUG + printk("BSP_pciScan: found 0x%08x at %d/x%02x/%d\n",d,bus,dev,fun); +#endif + if ( cb(bus,dev,fun,uarg) > 0 ) { + if ( ++fun >= hd ) { + fun = 0; + if ( ++dev >= PCI_MAX_DEVICES ) { + dev = 0; + bus++; + } + } + return (void*) PCIB_DEVSIG_MAKE(bus,dev,fun); + } + } + } + } + return 0; +} |