diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-12-22 18:31:04 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2019-01-22 12:46:33 +0100 |
commit | 7eb606d393306da25fd6e6aa7f8595ffb2e924fc (patch) | |
tree | 085befd6fe5e29d229fec9683735516d48e9d41e /bsps/shared/grlib/pci/grpci2.c | |
parent | grlib: Move header files (diff) | |
download | rtems-7eb606d393306da25fd6e6aa7f8595ffb2e924fc.tar.bz2 |
grlib: Move source files
Update #3678.
Diffstat (limited to 'bsps/shared/grlib/pci/grpci2.c')
-rw-r--r-- | bsps/shared/grlib/pci/grpci2.c | 970 |
1 files changed, 970 insertions, 0 deletions
diff --git a/bsps/shared/grlib/pci/grpci2.c b/bsps/shared/grlib/pci/grpci2.c new file mode 100644 index 0000000000..21c09f47a5 --- /dev/null +++ b/bsps/shared/grlib/pci/grpci2.c @@ -0,0 +1,970 @@ +/* GRLIB GRPCI2 PCI HOST driver. + * + * COPYRIGHT (c) 2011 + * Cobham Gaisler AB. + * + * The license and distribution terms for this file may be + * found in found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +/* Configures the GRPCI2 core and initialize, + * - the PCI Library (pci.c) + * - the general part of the PCI Bus driver (pci_bus.c) + * + * System interrupt assigned to PCI interrupt (INTA#..INTD#) is by + * default taken from Plug and Play, but may be overridden by the + * driver resources INTA#..INTD#. GRPCI2 handles differently depending + * on the design (4 different ways). + * + * GRPCI2 IRQ implementation notes + * ------------------------------- + * Since the Driver Manager pci_bus layer implements IRQ by calling + * pci_interrupt_* which translates into BSP_shared_interrupt_*, and the + * root-bus also relies on BSP_shared_interrupt_*, it is safe for the GRPCI2 + * driver to use the drvmgr_interrupt_* routines since they will be + * accessing the same routines in the end. Otherwise the GRPCI2 driver must + * have used the pci_interrupt_* routines. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <rtems.h> +#include <rtems/bspIo.h> +#include <libcpu/byteorder.h> +#include <libcpu/access.h> +#include <pci.h> +#include <pci/cfg.h> + +#include <drvmgr/drvmgr.h> +#include <grlib/ambapp_bus.h> +#include <grlib/ambapp.h> +#include <drvmgr/pci_bus.h> +#include <grlib/grpci2.h> + +#include <grlib/grlib_impl.h> + +/* If defined to 1 - byte twisting is enabled by default */ +#define DEFAULT_BT_ENABLED 0 + +/* If defined to 64 - Latency timer is 64 by default */ +#define DEFAULT_LATENCY_TIMER 64 + +/* Interrupt assignment. Set to other value than 0xff in order to + * override defaults and plug&play information + */ +#ifndef GRPCI2_INTA_SYSIRQ + #define GRPCI2_INTA_SYSIRQ 0xff +#endif +#ifndef GRPCI2_INTB_SYSIRQ + #define GRPCI2_INTB_SYSIRQ 0xff +#endif +#ifndef GRPCI2_INTC_SYSIRQ + #define GRPCI2_INTC_SYSIRQ 0xff +#endif +#ifndef GRPCI2_INTD_SYSIRQ + #define GRPCI2_INTD_SYSIRQ 0xff +#endif + +/*#define DEBUG 1*/ + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* + * GRPCI2 APB Register MAP + */ +struct grpci2_regs { + volatile unsigned int ctrl; /* 0x00 */ + volatile unsigned int sts_cap; /* 0x04 */ + volatile unsigned int ppref; /* 0x08 */ + volatile unsigned int io_map; /* 0x0C */ + volatile unsigned int dma_ctrl; /* 0x10 */ + volatile unsigned int dma_bdbase; /* 0x14 */ + volatile unsigned int dma_chact; /* 0x18 */ + int res1; /* 0x1C */ + volatile unsigned int bars[6]; /* 0x20 */ + int res2[2]; /* 0x38 */ + volatile unsigned int ahbmst_map[16]; /* 0x40 */ +}; + +#define CTRL_BUS_BIT 16 + +#define CTRL_SI (1<<27) +#define CTRL_PE (1<<26) +#define CTRL_ER (1<<25) +#define CTRL_EI (1<<24) +#define CTRL_BUS (0xff<<CTRL_BUS_BIT) +#define CTRL_HOSTINT 0xf + +#define STS_HOST_BIT 31 +#define STS_MST_BIT 30 +#define STS_TAR_BIT 29 +#define STS_DMA_BIT 28 +#define STS_DI_BIT 27 +#define STS_HI_BIT 26 +#define STS_IRQMODE_BIT 24 +#define STS_TRACE_BIT 23 +#define STS_CFGERRVALID_BIT 20 +#define STS_CFGERR_BIT 19 +#define STS_INTTYPE_BIT 12 +#define STS_INTSTS_BIT 8 +#define STS_FDEPTH_BIT 2 +#define STS_FNUM_BIT 0 + +#define STS_HOST (1<<STS_HOST_BIT) +#define STS_MST (1<<STS_MST_BIT) +#define STS_TAR (1<<STS_TAR_BIT) +#define STS_DMA (1<<STS_DMA_BIT) +#define STS_DI (1<<STS_DI_BIT) +#define STS_HI (1<<STS_HI_BIT) +#define STS_IRQMODE (0x3<<STS_IRQMODE_BIT) +#define STS_TRACE (1<<STS_TRACE_BIT) +#define STS_CFGERRVALID (1<<STS_CFGERRVALID_BIT) +#define STS_CFGERR (1<<STS_CFGERR_BIT) +#define STS_INTTYPE (0x7f<<STS_INTTYPE_BIT) +#define STS_INTSTS (0xf<<STS_INTSTS_BIT) +#define STS_FDEPTH (0x7<<STS_FDEPTH_BIT) +#define STS_FNUM (0x3<<STS_FNUM_BIT) + +#define STS_ITIMEOUT (1<<18) +#define STS_ISYSERR (1<<17) +#define STS_IDMA (1<<16) +#define STS_IDMAERR (1<<15) +#define STS_IMSTABRT (1<<14) +#define STS_ITGTABRT (1<<13) +#define STS_IPARERR (1<<12) + +/* GRPCI2 Capability */ +struct grpci2_cap_first { + unsigned int ctrl; + unsigned int pci2ahb_map[6]; + unsigned int ext2ahb_map; + unsigned int io_map; + unsigned int pcibar_size[6]; + unsigned int ahb_pref; +}; +#define CAP9_CTRL_OFS 0 +#define CAP9_BAR_OFS 0x4 +#define CAP9_IOMAP_OFS 0x20 +#define CAP9_BARSIZE_OFS 0x24 +#define CAP9_AHBPREF_OFS 0x3C + +/* Used internally for accessing the PCI bridge's configuration space itself */ +#define HOST_TGT PCI_DEV(0xff, 0, 0) + +struct grpci2_priv *grpci2priv = NULL; + +/* PCI Interrupt assignment. Connects an PCI interrupt pin (INTA#..INTD#) + * to a system interrupt number. + */ +unsigned char grpci2_pci_irq_table[4] = +{ + /* INTA# */ GRPCI2_INTA_SYSIRQ, + /* INTB# */ GRPCI2_INTB_SYSIRQ, + /* INTC# */ GRPCI2_INTC_SYSIRQ, + /* INTD# */ GRPCI2_INTD_SYSIRQ +}; + +/* Start of workspace/dynamical area */ +extern unsigned int _end; +#define DMA_START ((unsigned int) &_end) + +/* Default BAR mapping, set BAR0 256MB 1:1 mapped base of CPU RAM */ +struct grpci2_pcibar_cfg grpci2_default_bar_mapping[6] = { + /* BAR0 */ {DMA_START, DMA_START, 0x10000000}, + /* BAR1 */ {0, 0, 0}, + /* BAR2 */ {0, 0, 0}, + /* BAR3 */ {0, 0, 0}, + /* BAR4 */ {0, 0, 0}, + /* BAR5 */ {0, 0, 0}, +}; + +/* Driver private data struture */ +struct grpci2_priv { + struct drvmgr_dev *dev; + struct grpci2_regs *regs; + unsigned char ver; + char irq; + char irq_mode; /* IRQ Mode from CAPSTS REG */ + char irq_dma; /* IRQ Index for DMA */ + char bt_enabled; + unsigned int irq_mask; + unsigned int latency_timer; + + struct grpci2_pcibar_cfg *barcfg; + + unsigned int pci_area; + unsigned int pci_area_end; + unsigned int pci_io; + unsigned int pci_conf; + unsigned int pci_conf_end; + + uint32_t devVend; /* Host PCI Device/Vendor ID */ + + struct drvmgr_map_entry maps_up[7]; + struct drvmgr_map_entry maps_down[2]; + struct pcibus_config config; + + /* DMA interrupts */ + void (*dma_isr)(void *data); + void *dma_isr_arg; + + SPIN_DECLARE(devlock) +}; + +int grpci2_init1(struct drvmgr_dev *dev); +int grpci2_init3(struct drvmgr_dev *dev); +void grpci2_err_isr(void *arg); +void grpci2_dma_isr(void *arg); + +/* GRPCI2 DRIVER */ + +struct drvmgr_drv_ops grpci2_ops = +{ + .init = {grpci2_init1, NULL, grpci2_init3, NULL}, + .remove = NULL, + .info = NULL +}; + +struct amba_dev_id grpci2_ids[] = +{ + {VENDOR_GAISLER, GAISLER_GRPCI2}, + {0, 0} /* Mark end of table */ +}; + +struct amba_drv_info grpci2_info = +{ + { + DRVMGR_OBJ_DRV, /* Driver */ + NULL, /* Next driver */ + NULL, /* Device list */ + DRIVER_AMBAPP_GAISLER_GRPCI2_ID,/* Driver ID */ + "GRPCI2_DRV", /* Driver Name */ + DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */ + &grpci2_ops, + NULL, /* Funcs */ + 0, /* No devices yet */ + sizeof(struct grpci2_priv), /* Make drvmgr alloc private */ + }, + &grpci2_ids[0] +}; + +/* Defaults to do nothing - user can override this function + * by including the DMA DRIVER. + */ +int __attribute__((weak)) grpci2dma_init(void * regs, void isr_register( void (*isr)(void *), void * arg)); + +int grpci2dma_init(void * regs, void isr_register( void (*isr)(void *), void * arg)) +{ + return 0; +} + +/* Prototype of grpci2_dma_isr_register function */ +static void grpci2_dma_isr_register( void (*isr)(void *), void * arg); + +void grpci2_register_drv(void) +{ + DBG("Registering GRPCI2 driver\n"); + drvmgr_drv_register(&grpci2_info.general); +} + +static int grpci2_cfg_r32(pci_dev_t dev, int ofs, uint32_t *val) +{ + struct grpci2_priv *priv = grpci2priv; + volatile uint32_t *pci_conf; + unsigned int tmp, devfn; + int retval, bus = PCI_DEV_BUS(dev); + SPIN_IRQFLAGS(irqflags); + + if ((unsigned int)ofs & 0xffffff03) { + retval = PCISTS_EINVAL; + goto out2; + } + + if (PCI_DEV_SLOT(dev) > 15) { + retval = PCISTS_MSTABRT; + goto out; + } + + /* GRPCI2 can access "non-standard" devices on bus0 (on AD11.AD16), + * we skip them. + */ + if (dev == HOST_TGT) + bus = devfn = 0; + else if (bus == 0) + devfn = PCI_DEV_DEVFUNC(dev) + PCI_DEV(0, 6, 0); + else + devfn = PCI_DEV_DEVFUNC(dev); + + pci_conf = (volatile uint32_t *) (priv->pci_conf | (devfn << 8) | ofs); + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + + /* Select bus */ + priv->regs->ctrl = (priv->regs->ctrl & ~(0xff<<16)) | (bus<<16); + /* clear old status */ + priv->regs->sts_cap = (STS_CFGERR | STS_CFGERRVALID); + + tmp = *pci_conf; + + /* Wait until GRPCI2 signals that CFG access is done, it should be + * done instantaneously unless a DMA operation is ongoing... + */ + while ((priv->regs->sts_cap & STS_CFGERRVALID) == 0) + ; + + if (priv->regs->sts_cap & STS_CFGERR) { + retval = PCISTS_MSTABRT; + } else { + /* Bus always little endian (unaffected by byte-swapping) */ + *val = CPU_swap_u32(tmp); + retval = PCISTS_OK; + } + + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + +out: + if (retval != PCISTS_OK) + *val = 0xffffffff; + + DBG("pci_read: [%x:%x:%x] reg: 0x%x => addr: 0x%x, val: 0x%x (%d)\n", + PCI_DEV_EXPAND(dev), ofs, pci_conf, *val, retval); + +out2: + return retval; +} + +static int grpci2_cfg_r16(pci_dev_t dev, int ofs, uint16_t *val) +{ + uint32_t v; + int retval; + + if (ofs & 1) + return PCISTS_EINVAL; + + retval = grpci2_cfg_r32(dev, ofs & ~0x3, &v); + *val = 0xffff & (v >> (8*(ofs & 0x3))); + + return retval; +} + +static int grpci2_cfg_r8(pci_dev_t dev, int ofs, uint8_t *val) +{ + uint32_t v; + int retval; + + retval = grpci2_cfg_r32(dev, ofs & ~0x3, &v); + + *val = 0xff & (v >> (8*(ofs & 3))); + + return retval; +} + +static int grpci2_cfg_w32(pci_dev_t dev, int ofs, uint32_t val) +{ + struct grpci2_priv *priv = grpci2priv; + volatile uint32_t *pci_conf; + uint32_t value, devfn; + int retval, bus = PCI_DEV_BUS(dev); + SPIN_IRQFLAGS(irqflags); + + if ((unsigned int)ofs & 0xffffff03) + return PCISTS_EINVAL; + + if (PCI_DEV_SLOT(dev) > 15) + return PCISTS_MSTABRT; + + value = CPU_swap_u32(val); + + /* GRPCI2 can access "non-standard" devices on bus0 (on AD11.AD16), + * we skip them. + */ + if (dev == HOST_TGT) + bus = devfn = 0; + else if (bus == 0) + devfn = PCI_DEV_DEVFUNC(dev) + PCI_DEV(0, 6, 0); + else + devfn = PCI_DEV_DEVFUNC(dev); + + pci_conf = (volatile uint32_t *) (priv->pci_conf | (devfn << 8) | ofs); + + SPIN_LOCK_IRQ(&priv->devlock, irqflags); + + /* Select bus */ + priv->regs->ctrl = (priv->regs->ctrl & ~(0xff<<16)) | (bus<<16); + /* clear old status */ + priv->regs->sts_cap = (STS_CFGERR | STS_CFGERRVALID); + + *pci_conf = value; + + /* Wait until GRPCI2 signals that CFG access is done, it should be + * done instantaneously unless a DMA operation is ongoing... + */ + while ((priv->regs->sts_cap & STS_CFGERRVALID) == 0) + ; + + if (priv->regs->sts_cap & STS_CFGERR) + retval = PCISTS_MSTABRT; + else + retval = PCISTS_OK; + + SPIN_UNLOCK_IRQ(&priv->devlock, irqflags); + + DBG("pci_write - [%x:%x:%x] reg: 0x%x => addr: 0x%x, val: 0x%x (%d)\n", + PCI_DEV_EXPAND(dev), ofs, pci_conf, value, retval); + + return retval; +} + +static int grpci2_cfg_w16(pci_dev_t dev, int ofs, uint16_t val) +{ + uint32_t v; + int retval; + + if (ofs & 1) + return PCISTS_EINVAL; + + retval = grpci2_cfg_r32(dev, ofs & ~0x3, &v); + if (retval != PCISTS_OK) + return retval; + + v = (v & ~(0xffff << (8*(ofs&3)))) | ((0xffff&val) << (8*(ofs&3))); + + return grpci2_cfg_w32(dev, ofs & ~0x3, v); +} + +static int grpci2_cfg_w8(pci_dev_t dev, int ofs, uint8_t val) +{ + uint32_t v; + int retval; + + retval = grpci2_cfg_r32(dev, ofs & ~0x3, &v); + if (retval != PCISTS_OK) + return retval; + + v = (v & ~(0xff << (8*(ofs&3)))) | ((0xff&val) << (8*(ofs&3))); + + return grpci2_cfg_w32(dev, ofs & ~0x3, v); +} + +/* Return the assigned system IRQ number that corresponds to the PCI + * "Interrupt Pin" information from configuration space. + * + * The IRQ information is stored in the grpci2_pci_irq_table configurable + * by the user. + * + * Returns the "system IRQ" for the PCI INTA#..INTD# pin in irq_pin. Returns + * 0xff if not assigned. + */ +static uint8_t grpci2_bus0_irq_map(pci_dev_t dev, int irq_pin) +{ + uint8_t sysIrqNr = 0; /* not assigned */ + int irq_group; + + if ( (irq_pin >= 1) && (irq_pin <= 4) ) { + /* Use default IRQ decoding on PCI BUS0 according slot numbering */ + irq_group = PCI_DEV_SLOT(dev) & 0x3; + irq_pin = ((irq_pin - 1) + irq_group) & 0x3; + /* Valid PCI "Interrupt Pin" number */ + sysIrqNr = grpci2_pci_irq_table[irq_pin]; + } + return sysIrqNr; +} + +static int grpci2_translate(uint32_t *address, int type, int dir) +{ + uint32_t adr, start, end; + struct grpci2_priv *priv = grpci2priv; + int i; + + if (type == 1) { + /* I/O */ + if (dir != 0) { + /* The PCI bus can not access the CPU bus from I/O + * because GRPCI2 core does not support I/O BARs + */ + return -1; + } + + /* We have got a PCI IO BAR address that the CPU want to access. + * Check that it is within the PCI I/O window, I/O adresses + * are NOT mapped 1:1 with GRPCI2 driver... translation needed. + */ + adr = *(uint32_t *)address; + if (adr < 0x100 || adr > 0x10000) + return -1; + *address = adr + priv->pci_io; + } else { + /* MEMIO and MEM. + * Memory space is mapped 1:1 so no translation is needed. + * Check that address is within accessible windows. + */ + adr = *(uint32_t *)address; + if (dir == 0) { + /* PCI BAR to AMBA-CPU address.. check that it is + * located within GRPCI2 PCI Memory Window + * adr = PCI address. + */ + if (adr < priv->pci_area || adr >= priv->pci_area_end) + return -1; + } else { + /* We have a CPU address and want to get access to it + * from PCI space, typically when doing DMA into CPU + * RAM. The GRPCI2 core may have multiple target BARs + * that PCI masters can access, the BARs are user + * configurable in the following ways: + * BAR_SIZE, PCI_BAR Address and MAPPING (AMBA ADR) + * + * The below code tries to find a BAR for which the + * AMBA bar may have been mapped onto, and translate + * the AMBA-CPU address into a PCI address using the + * given mapping. + * + * adr = AMBA address. + */ + for(i=0; i<6; i++) { + start = priv->barcfg[i].ahbadr; + end = priv->barcfg[i].ahbadr + + priv->barcfg[i].barsize; + if (adr >= start && adr < end) { + /* BAR match: Translate address */ + *address = (adr - start) + + priv->barcfg[i].pciadr; + return 0; + } + } + return -1; + } + } + + return 0; +} + +extern struct pci_memreg_ops pci_memreg_sparc_le_ops; +extern struct pci_memreg_ops pci_memreg_sparc_be_ops; + +/* GRPCI2 PCI access routines, default to Little-endian PCI Bus */ +struct pci_access_drv grpci2_access_drv = { + .cfg = + { + grpci2_cfg_r8, + grpci2_cfg_r16, + grpci2_cfg_r32, + grpci2_cfg_w8, + grpci2_cfg_w16, + grpci2_cfg_w32, + }, + .io = + { + _ld8, + _ld_le16, + _ld_le32, + _st8, + _st_le16, + _st_le32, + }, + .memreg = &pci_memreg_sparc_le_ops, + .translate = grpci2_translate, +}; + +struct pci_io_ops grpci2_io_ops_be = +{ + _ld8, + _ld_be16, + _ld_be32, + _st8, + _st_be16, + _st_be32, +}; + +/* PCI Error Interrupt handler, called when there may be a PCI Target/Master + * Abort. + */ +void grpci2_err_isr(void *arg) +{ + struct grpci2_priv *priv = arg; + unsigned int sts = priv->regs->sts_cap; + + if (sts & (STS_IMSTABRT | STS_ITGTABRT | STS_IPARERR | STS_ISYSERR | STS_ITIMEOUT)) { + /* A PCI error IRQ ... Error handler unimplemented + * add your code here... + */ + if (sts & STS_IMSTABRT) { + printk("GRPCI2: unhandled Master Abort IRQ\n"); + } + if (sts & STS_ITGTABRT) { + printk("GRPCI2: unhandled Target Abort IRQ\n"); + } + if (sts & STS_IPARERR) { + printk("GRPCI2: unhandled Parity Error IRQ\n"); + } + if (sts & STS_ISYSERR) { + printk("GRPCI2: unhandled System Error IRQ\n"); + } + if (sts & STS_ITIMEOUT) { + printk("GRPCI2: unhandled PCI target access timeout IRQ\n"); + } + } +} + +/* PCI DMA Interrupt handler, called when there may be a PCI DMA interrupt. + */ +void grpci2_dma_isr(void *arg) +{ + struct grpci2_priv *priv = arg; + unsigned int sts = (priv->regs->sts_cap & (STS_IDMAERR | STS_IDMA)); + + /* Clear Interrupt if taken*/ + if (sts != 0){ + /* Clear IDMAERR and IDMA bits */ + priv->regs->sts_cap = (STS_IDMAERR | STS_IDMA); + /* Clear DRVMGR interrupt */ + drvmgr_interrupt_clear(priv->dev, priv->irq_dma); + /* Call DMA driver ISR */ + (priv->dma_isr)(priv->dma_isr_arg); + } +} + +static int grpci2_hw_init(struct grpci2_priv *priv) +{ + struct grpci2_regs *regs = priv->regs; + int i; + uint8_t capptr; + uint32_t data, io_map, ahbadr, pciadr, size; + pci_dev_t host = HOST_TGT; + struct grpci2_pcibar_cfg *barcfg = priv->barcfg; + + /* Reset any earlier setup */ + regs->ctrl = 0; + regs->sts_cap = ~0; /* Clear Status */ + regs->dma_ctrl = 0; + regs->dma_bdbase = 0; + + /* Translate I/O accesses 1:1, (will not work for PCI 2.3) */ + regs->io_map = priv->pci_io & 0xffff0000; + + /* set 1:1 mapping between AHB -> PCI memory space, for all Masters + * Each AHB master has it's own mapping registers. Max 16 AHB masters. + */ + for (i=0; i<16; i++) + regs->ahbmst_map[i] = priv->pci_area; + + /* Get the GRPCI2 Host PCI ID */ + grpci2_cfg_r32(host, PCIR_VENDOR, &priv->devVend); + + /* Get address to first (always defined) capability structure */ + grpci2_cfg_r8(host, PCIR_CAP_PTR, &capptr); + if (capptr == 0) + return -1; + + /* Limit the prefetch for GRPCI2 version 0. */ + if (priv->ver == 0) + grpci2_cfg_w32(host, capptr+CAP9_AHBPREF_OFS, 0); + + /* Enable/Disable Byte twisting */ + grpci2_cfg_r32(host, capptr+CAP9_IOMAP_OFS, &io_map); + io_map = (io_map & ~0x1) | (priv->bt_enabled ? 1 : 0); + grpci2_cfg_w32(host, capptr+CAP9_IOMAP_OFS, io_map); + + /* Setup the Host's PCI Target BARs for others to access (DMA) */ + for (i=0; i<6; i++) { + /* Make sure address is properly aligned */ + size = ~(barcfg[i].barsize-1); + barcfg[i].pciadr &= size; + barcfg[i].ahbadr &= size; + + pciadr = barcfg[i].pciadr; + ahbadr = barcfg[i].ahbadr; + size |= PCIM_BAR_MEM_PREFETCH; + + grpci2_cfg_w32(host, capptr+CAP9_BARSIZE_OFS+i*4, size); + grpci2_cfg_w32(host, capptr+CAP9_BAR_OFS+i*4, ahbadr); + grpci2_cfg_w32(host, PCIR_BAR(0)+i*4, pciadr); + } + + /* set as bus master and enable pci memory responses */ + grpci2_cfg_r32(host, PCIR_COMMAND, &data); + data |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); + grpci2_cfg_w32(host, PCIR_COMMAND, data); + + /* set latency timer */ + grpci2_cfg_r32(host, PCIR_CACHELNSZ, &data); + data &= ~0xff00; + data |= ((priv->latency_timer & 0xff) << 8); + grpci2_cfg_w32(host, PCIR_CACHELNSZ, data); + + /* Enable Error respone (CPU-TRAP) on illegal memory access */ + regs->ctrl = CTRL_ER | CTRL_PE; + + /* Successful */ + return 0; +} + +/* Initializes the GRPCI2 core and driver, must be called before calling + * init_pci() + * + * Return values + * 0 Successful initalization + * -1 Error during initialization, for example "PCI core not found". + * -2 Error PCI controller not HOST (targets not supported) + * -3 Error due to GRPCI2 hardware initialization + */ +static int grpci2_init(struct grpci2_priv *priv) +{ + struct ambapp_apb_info *apb; + struct ambapp_ahb_info *ahb; + int pin, i, j; + union drvmgr_key_value *value; + char keyname[6]; + struct amba_dev_info *ainfo = priv->dev->businfo; + struct grpci2_pcibar_cfg *barcfg; + unsigned int size; + + /* Find PCI core from Plug&Play information */ + apb = ainfo->info.apb_slv; + ahb = ainfo->info.ahb_slv; + + /* Found PCI core, init private structure */ + priv->irq = apb->irq; + priv->ver = apb->ver; + priv->regs = (struct grpci2_regs *)apb->start; + priv->bt_enabled = DEFAULT_BT_ENABLED; + priv->irq_mode = (priv->regs->sts_cap & STS_IRQMODE) >> STS_IRQMODE_BIT; + priv->latency_timer = DEFAULT_LATENCY_TIMER; + + /* Initialize Spin-lock for GRPCI2 Device. */ + SPIN_INIT(&priv->devlock, "grpci2"); + + /* Calculate the PCI windows + * AMBA->PCI Window: AHB SLAVE AREA0 + * AMBA->PCI I/O cycles Window: AHB SLAVE AREA1 Lower half + * AMBA->PCI Configuration cycles Window: AHB SLAVE AREA1 Upper half + */ + priv->pci_area = ahb->start[0]; + priv->pci_area_end = ahb->start[0] + ahb->mask[0]; + priv->pci_io = ahb->start[1]; + priv->pci_conf = ahb->start[1] + 0x10000; + priv->pci_conf_end = priv->pci_conf + 0x10000; + + /* On systems where PCI I/O area and configuration area is apart of the + * "PCI Window" the PCI Window stops at the start of the PCI I/O area + */ + if ((priv->pci_io > priv->pci_area) && + (priv->pci_io < (priv->pci_area_end-1))) { + priv->pci_area_end = priv->pci_io; + } + + /* Init PCI interrupt assignment table to all use the interrupt routed + * through the GRPCI2 core. + */ + strcpy(keyname, "INTX#"); + for (pin=1; pin<5; pin++) { + if (grpci2_pci_irq_table[pin-1] == 0xff) { + if (priv->irq_mode < 2) { + /* PCI Interrupts are shared */ + grpci2_pci_irq_table[pin-1] = priv->irq; + } else { + /* Unique IRQ per PCI INT Pin */ + grpci2_pci_irq_table[pin-1] = priv->irq + pin-1; + } + + /* User may override Both hardcoded IRQ setup and Plug & Play IRQ */ + keyname[3] = 'A' + (pin-1); + value = drvmgr_dev_key_get(priv->dev, keyname, DRVMGR_KT_INT); + if (value) + grpci2_pci_irq_table[pin-1] = value->i; + } + + /* Remember which IRQs are enabled */ + if (grpci2_pci_irq_table[pin-1] != 0) + priv->irq_mask |= 1 << (pin-1); + } + + /* User may override DEFAULT_BT_ENABLED to enable/disable byte twisting */ + value = drvmgr_dev_key_get(priv->dev, "byteTwisting", DRVMGR_KT_INT); + if (value) + priv->bt_enabled = value->i; + + /* Let user Configure the 6 target BARs */ + value = drvmgr_dev_key_get(priv->dev, "tgtBarCfg", DRVMGR_KT_POINTER); + if (value) + priv->barcfg = value->ptr; + else + priv->barcfg = grpci2_default_bar_mapping; + + /* User may override DEFAULT_LATENCY_TIMER */ + value = drvmgr_dev_key_get(priv->dev, "latencyTimer", DRVMGR_KT_INT); + if (value) + priv->latency_timer = value->i; + + /* This driver only support HOST systems, we check that it can act as a + * PCI Master and that it is in the Host slot. */ + if ((priv->regs->sts_cap&STS_HOST) || !(priv->regs->sts_cap&STS_MST)) + return -2; /* Target not supported */ + + /* Init the PCI Core */ + if (grpci2_hw_init(priv)) + return -3; + + /* Down streams translation table */ + priv->maps_down[0].name = "AMBA -> PCI MEM Window"; + priv->maps_down[0].size = priv->pci_area_end - priv->pci_area; + priv->maps_down[0].from_adr = (void *)priv->pci_area; + priv->maps_down[0].to_adr = (void *)priv->pci_area; + /* End table */ + priv->maps_down[1].size = 0; + + /* Up streams translation table */ + /* Setup the Host's PCI Target BARs for others to access (DMA) */ + barcfg = priv->barcfg; + for (i=0,j=0; i<6; i++) { + size = barcfg[i].barsize; + if (size == 0) + continue; + + /* Make sure address is properly aligned */ + priv->maps_up[j].name = "Target BAR[I] -> AMBA"; + priv->maps_up[j].size = size; + priv->maps_up[j].from_adr = (void *) + (barcfg[i].pciadr & ~(size - 1)); + priv->maps_up[j].to_adr = (void *) + (barcfg[i].ahbadr & ~(size - 1)); + j++; + } + + /* End table */ + priv->maps_up[j].size = 0; + + return 0; +} + +/* Called when a core is found with the AMBA device and vendor ID + * given in grpci2_ids[]. IRQ, Console does not work here + */ +int grpci2_init1(struct drvmgr_dev *dev) +{ + int status; + struct grpci2_priv *priv; + struct pci_auto_setup grpci2_auto_cfg; + + DBG("GRPCI2[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name); + + if (grpci2priv) { + DBG("Driver only supports one PCI core\n"); + return DRVMGR_FAIL; + } + + if ((strcmp(dev->parent->dev->drv->name, "AMBAPP_GRLIB_DRV") != 0) && + (strcmp(dev->parent->dev->drv->name, "AMBAPP_LEON2_DRV") != 0)) { + /* We only support GRPCI2 driver on local bus */ + return DRVMGR_FAIL; + } + + priv = dev->priv; + if (!priv) + return DRVMGR_NOMEM; + + priv->dev = dev; + grpci2priv = priv; + + /* Initialize GRPCI2 Hardware */ + status = grpci2_init(priv); + if (status) { + printk("Failed to initialize grpci2 driver %d\n", status); + return -1; + } + + /* Register the PCI core at the PCI layers */ + + if (priv->bt_enabled == 0) { + /* Host is Big-Endian */ + pci_endian = PCI_BIG_ENDIAN; + + memcpy(&grpci2_access_drv.io, &grpci2_io_ops_be, + sizeof(grpci2_io_ops_be)); + grpci2_access_drv.memreg = &pci_memreg_sparc_be_ops; + } + + if (pci_access_drv_register(&grpci2_access_drv)) { + /* Access routines registration failed */ + return DRVMGR_FAIL; + } + + /* Prepare memory MAP */ + grpci2_auto_cfg.options = 0; + grpci2_auto_cfg.mem_start = 0; + grpci2_auto_cfg.mem_size = 0; + grpci2_auto_cfg.memio_start = priv->pci_area; + grpci2_auto_cfg.memio_size = priv->pci_area_end - priv->pci_area; + grpci2_auto_cfg.io_start = 0x100; /* avoid PCI address 0 */ + grpci2_auto_cfg.io_size = 0x10000 - 0x100; /* lower 64kB I/O 16 */ + grpci2_auto_cfg.irq_map = grpci2_bus0_irq_map; + grpci2_auto_cfg.irq_route = NULL; /* use standard routing */ + pci_config_register(&grpci2_auto_cfg); + + if (pci_config_init()) { + /* PCI configuration failed */ + return DRVMGR_FAIL; + } + + /* Initialize/Register Driver Manager PCI Bus */ + priv->config.maps_down = &priv->maps_down[0]; + priv->config.maps_up = &priv->maps_up[0]; + return pcibus_register(dev, &priv->config); +} + +int grpci2_init3(struct drvmgr_dev *dev) +{ + struct grpci2_priv *priv = dev->priv; + + /* Install and Enable PCI Error interrupt handler */ + drvmgr_interrupt_register(dev, 0, "grpci2", grpci2_err_isr, priv); + + /* Initialize DMA driver (if supported) */ + if (priv->regs->sts_cap & STS_DMA){ + grpci2dma_init((void *) &(priv->regs->dma_ctrl), grpci2_dma_isr_register); + } + + /* Unmask Error IRQ and all PCI interrupts at PCI Core. For this to be + * safe every PCI board have to be resetted (no IRQ generation) before + * Global IRQs are enabled (Init is reached or similar) + */ + priv->regs->ctrl |= (CTRL_EI | priv->irq_mask); + + return DRVMGR_OK; +} + +static void grpci2_dma_isr_register( void (*isr)(void *), void * arg) +{ + struct grpci2_priv *priv = grpci2priv; + + /* Handle unregistration */ + if (priv->dma_isr != NULL) { + drvmgr_interrupt_unregister(priv->dev, priv->irq_dma, grpci2_dma_isr, priv); + /* Uninstall user ISR */ + priv->dma_isr = NULL; + priv->dma_isr_arg = NULL; + } + + if (isr == NULL) + return; + + /* Install user ISR */ + priv->dma_isr_arg = arg; + priv->dma_isr = isr; + + /* Install and Enable PCI DMA interrupt handler */ + if (priv->irq_mode == 1) { + priv->irq_dma = 1; + } else if (priv->irq_mode == 3) { + priv->irq_dma = 4; + } else { + priv->irq_dma = 0; + } + drvmgr_interrupt_register(priv->dev, priv->irq_dma, "grpci2dma", grpci2_dma_isr, priv); +} |