summaryrefslogtreecommitdiffstats
path: root/bsps/shared/grlib/pci/grpci2.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-12-22 18:31:04 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2019-01-22 12:46:33 +0100
commit7eb606d393306da25fd6e6aa7f8595ffb2e924fc (patch)
tree085befd6fe5e29d229fec9683735516d48e9d41e /bsps/shared/grlib/pci/grpci2.c
parentgrlib: Move header files (diff)
downloadrtems-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.c970
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);
+}