forked from Imagelibrary/rtems
723 lines
18 KiB
C
723 lines
18 KiB
C
/* GRLIB GRPCI PCI HOST driver.
|
|
*
|
|
* COPYRIGHT (c) 2008.
|
|
* Cobham Gaisler AB.
|
|
*
|
|
* Configures the GRPCI 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#.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.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/grpci.h>
|
|
|
|
#define DMAPCI_ADDR 0x80000500
|
|
|
|
/* Configuration options */
|
|
#define SYSTEM_MAINMEM_START 0x40000000
|
|
|
|
/* If defined to 1 - byte twisting is enabled by default */
|
|
#define DEFAULT_BT_ENABLED 0
|
|
|
|
/* Interrupt assignment. Set to other value than 0xff in order to
|
|
* override defaults and plug&play information
|
|
*/
|
|
#ifndef GRPCI_INTA_SYSIRQ
|
|
#define GRPCI_INTA_SYSIRQ 0xff
|
|
#endif
|
|
#ifndef GRPCI_INTB_SYSIRQ
|
|
#define GRPCI_INTB_SYSIRQ 0xff
|
|
#endif
|
|
#ifndef GRPCI_INTC_SYSIRQ
|
|
#define GRPCI_INTC_SYSIRQ 0xff
|
|
#endif
|
|
#ifndef GRPCI_INTD_SYSIRQ
|
|
#define GRPCI_INTD_SYSIRQ 0xff
|
|
#endif
|
|
|
|
#define PAGE0_BTEN_BIT 0
|
|
#define PAGE0_BTEN (1<<PAGE0_BTEN_BIT)
|
|
|
|
#define CFGSTAT_HOST_BIT 13
|
|
#define CFGSTAT_HOST (1<<CFGSTAT_HOST_BIT)
|
|
|
|
/*#define DEBUG 1*/
|
|
|
|
#ifdef DEBUG
|
|
#define DBG(x...) printk(x)
|
|
#else
|
|
#define DBG(x...)
|
|
#endif
|
|
|
|
/*
|
|
* Bit encode for PCI_CONFIG_HEADER_TYPE register
|
|
*/
|
|
struct grpci_regs {
|
|
volatile unsigned int cfg_stat;
|
|
volatile unsigned int bar0;
|
|
volatile unsigned int page0;
|
|
volatile unsigned int bar1;
|
|
volatile unsigned int page1;
|
|
volatile unsigned int iomap;
|
|
volatile unsigned int stat_cmd;
|
|
volatile unsigned int irq;
|
|
};
|
|
|
|
#define HOST_TGT PCI_DEV(0xff, 0, 0)
|
|
|
|
struct grpci_priv *grpcipriv = NULL;
|
|
static int grpci_minor = 0;
|
|
static unsigned int *pcidma = (unsigned int *)DMAPCI_ADDR;
|
|
|
|
/* PCI Interrupt assignment. Connects an PCI interrupt pin (INTA#..INTD#)
|
|
* to a system interrupt number.
|
|
*/
|
|
unsigned char grpci_pci_irq_table[4] =
|
|
{
|
|
/* INTA# */ GRPCI_INTA_SYSIRQ,
|
|
/* INTB# */ GRPCI_INTB_SYSIRQ,
|
|
/* INTC# */ GRPCI_INTC_SYSIRQ,
|
|
/* INTD# */ GRPCI_INTD_SYSIRQ
|
|
};
|
|
|
|
/* Driver private data struture */
|
|
struct grpci_priv {
|
|
struct drvmgr_dev *dev;
|
|
struct grpci_regs *regs;
|
|
int irq;
|
|
int minor;
|
|
|
|
uint32_t bar1_pci_adr;
|
|
uint32_t bar1_size;
|
|
|
|
int bt_enabled;
|
|
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 Vendor/Device ID */
|
|
|
|
struct drvmgr_map_entry maps_up[2];
|
|
struct drvmgr_map_entry maps_down[2];
|
|
struct pcibus_config config;
|
|
};
|
|
|
|
int grpci_init1(struct drvmgr_dev *dev);
|
|
|
|
/* GRPCI DRIVER */
|
|
|
|
struct drvmgr_drv_ops grpci_ops =
|
|
{
|
|
.init = {grpci_init1, NULL, NULL, NULL},
|
|
.remove = NULL,
|
|
.info = NULL
|
|
};
|
|
|
|
struct amba_dev_id grpci_ids[] =
|
|
{
|
|
{VENDOR_GAISLER, GAISLER_PCIFBRG},
|
|
{0, 0} /* Mark end of table */
|
|
};
|
|
|
|
struct amba_drv_info grpci_info =
|
|
{
|
|
{
|
|
DRVMGR_OBJ_DRV, /* Driver */
|
|
NULL, /* Next driver */
|
|
NULL, /* Device list */
|
|
DRIVER_AMBAPP_GAISLER_GRPCI_ID, /* Driver ID */
|
|
"GRPCI_DRV", /* Driver Name */
|
|
DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */
|
|
&grpci_ops,
|
|
NULL, /* Funcs */
|
|
0, /* No devices yet */
|
|
sizeof(struct grpci_priv), /* Make drvmgr alloc private */
|
|
},
|
|
&grpci_ids[0]
|
|
};
|
|
|
|
void grpci_register_drv(void)
|
|
{
|
|
DBG("Registering GRPCI driver\n");
|
|
drvmgr_drv_register(&grpci_info.general);
|
|
}
|
|
|
|
static int grpci_cfg_r32(pci_dev_t dev, int ofs, uint32_t *val)
|
|
{
|
|
struct grpci_priv *priv = grpcipriv;
|
|
volatile uint32_t *pci_conf;
|
|
uint32_t devfn;
|
|
int retval;
|
|
int bus = PCI_DEV_BUS(dev);
|
|
|
|
if (ofs & 3)
|
|
return PCISTS_EINVAL;
|
|
|
|
if (PCI_DEV_SLOT(dev) > 15) {
|
|
*val = 0xffffffff;
|
|
return PCISTS_OK;
|
|
}
|
|
|
|
/* GRPCI can access "non-standard" devices on bus0 (on AD11.AD16),
|
|
* but 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);
|
|
|
|
/* Select bus */
|
|
priv->regs->cfg_stat = (priv->regs->cfg_stat & ~(0xf<<23)) | (bus<<23);
|
|
|
|
pci_conf = (volatile uint32_t *)(priv->pci_conf | (devfn << 8) | ofs);
|
|
|
|
if (priv->bt_enabled) {
|
|
*val = CPU_swap_u32(*pci_conf);
|
|
} else {
|
|
*val = *pci_conf;
|
|
}
|
|
|
|
if (priv->regs->cfg_stat & 0x100) {
|
|
*val = 0xffffffff;
|
|
retval = PCISTS_MSTABRT;
|
|
} else
|
|
retval = PCISTS_OK;
|
|
|
|
DBG("pci_read: [%x:%x:%x] reg: 0x%x => addr: 0x%x, val: 0x%x\n",
|
|
PCI_DEV_EXPAND(dev), ofs, pci_conf, *val);
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
static int grpci_cfg_r16(pci_dev_t dev, int ofs, uint16_t *val)
|
|
{
|
|
uint32_t v;
|
|
int retval;
|
|
|
|
if (ofs & 1)
|
|
return PCISTS_EINVAL;
|
|
|
|
retval = grpci_cfg_r32(dev, ofs & ~0x3, &v);
|
|
*val = 0xffff & (v >> (8*(ofs & 0x3)));
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int grpci_cfg_r8(pci_dev_t dev, int ofs, uint8_t *val)
|
|
{
|
|
uint32_t v;
|
|
int retval;
|
|
|
|
retval = grpci_cfg_r32(dev, ofs & ~0x3, &v);
|
|
|
|
*val = 0xff & (v >> (8*(ofs & 3)));
|
|
|
|
return retval;
|
|
}
|
|
|
|
static int grpci_cfg_w32(pci_dev_t dev, int ofs, uint32_t val)
|
|
{
|
|
struct grpci_priv *priv = grpcipriv;
|
|
volatile uint32_t *pci_conf;
|
|
uint32_t value, devfn = PCI_DEV_DEVFUNC(dev);
|
|
int bus = PCI_DEV_BUS(dev);
|
|
|
|
if (ofs & 0x3)
|
|
return PCISTS_EINVAL;
|
|
|
|
if (PCI_DEV_SLOT(dev) > 15)
|
|
return PCISTS_MSTABRT;
|
|
|
|
/* GRPCI can access "non-standard" devices on bus0 (on AD11.AD16),
|
|
* but 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);
|
|
|
|
/* Select bus */
|
|
priv->regs->cfg_stat = (priv->regs->cfg_stat & ~(0xf<<23)) | (bus<<23);
|
|
|
|
pci_conf = (volatile uint32_t *)(priv->pci_conf | (devfn << 8) | ofs);
|
|
|
|
if ( priv->bt_enabled ) {
|
|
value = CPU_swap_u32(val);
|
|
} else {
|
|
value = val;
|
|
}
|
|
|
|
*pci_conf = value;
|
|
|
|
DBG("pci_write - [%x:%x:%x] reg: 0x%x => addr: 0x%x, val: 0x%x\n",
|
|
PCI_DEV_EXPAND(dev), ofs, pci_conf, value);
|
|
|
|
return PCISTS_OK;
|
|
}
|
|
|
|
static int grpci_cfg_w16(pci_dev_t dev, int ofs, uint16_t val)
|
|
{
|
|
uint32_t v;
|
|
int retval;
|
|
|
|
if (ofs & 1)
|
|
return PCISTS_EINVAL;
|
|
|
|
retval = grpci_cfg_r32(dev, ofs & ~0x3, &v);
|
|
if (retval != PCISTS_OK)
|
|
return retval;
|
|
|
|
v = (v & ~(0xffff << (8*(ofs&3)))) | ((0xffff&val) << (8*(ofs&3)));
|
|
|
|
return grpci_cfg_w32(dev, ofs & ~0x3, v);
|
|
}
|
|
|
|
static int grpci_cfg_w8(pci_dev_t dev, int ofs, uint8_t val)
|
|
{
|
|
uint32_t v;
|
|
int retval;
|
|
|
|
retval = grpci_cfg_r32(dev, ofs & ~0x3, &v);
|
|
if (retval != PCISTS_OK)
|
|
return retval;
|
|
|
|
v = (v & ~(0xff << (8*(ofs&3)))) | ((0xff&val) << (8*(ofs&3)));
|
|
|
|
return grpci_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 grpci_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 grpci_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 = grpci_pci_irq_table[irq_pin];
|
|
}
|
|
return sysIrqNr;
|
|
}
|
|
|
|
static int grpci_translate(uint32_t *address, int type, int dir)
|
|
{
|
|
uint32_t adr;
|
|
struct grpci_priv *priv = grpcipriv;
|
|
|
|
if (type == 1) {
|
|
/* I/O */
|
|
if (dir != 0) {
|
|
/* The PCI bus can not access the CPU bus from I/O
|
|
* because GRPCI core does not support I/O BARs
|
|
*/
|
|
return -1;
|
|
}
|
|
|
|
/* We have got a PCI BAR address that the CPU want to access...
|
|
* Check that it is within the PCI I/O window, I/O adresses
|
|
* are mapped 1:1 with GRPCI driver... no translation needed.
|
|
*/
|
|
adr = *(uint32_t *)address;
|
|
if (adr < priv->pci_io || adr >= priv->pci_conf)
|
|
return -1;
|
|
} 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 GRPCI 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 GRPCI core has two target BARs that PCI
|
|
* masters can access, we check here that the address
|
|
* is accessible from PCI.
|
|
* adr = AMBA address.
|
|
*/
|
|
if (adr < priv->bar1_pci_adr ||
|
|
adr >= (priv->bar1_pci_adr + priv->bar1_size))
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern struct pci_memreg_ops pci_memreg_sparc_le_ops;
|
|
extern struct pci_memreg_ops pci_memreg_sparc_be_ops;
|
|
|
|
/* GRPCI PCI access routines, default to Little-endian PCI Bus */
|
|
struct pci_access_drv grpci_access_drv = {
|
|
.cfg =
|
|
{
|
|
grpci_cfg_r8,
|
|
grpci_cfg_r16,
|
|
grpci_cfg_r32,
|
|
grpci_cfg_w8,
|
|
grpci_cfg_w16,
|
|
grpci_cfg_w32,
|
|
},
|
|
.io =
|
|
{
|
|
_ld8,
|
|
_ld_le16,
|
|
_ld_le32,
|
|
_st8,
|
|
_st_le16,
|
|
_st_le32,
|
|
},
|
|
.memreg = &pci_memreg_sparc_le_ops,
|
|
.translate = grpci_translate,
|
|
};
|
|
|
|
struct pci_io_ops grpci_io_ops_be =
|
|
{
|
|
_ld8,
|
|
_ld_be16,
|
|
_ld_be32,
|
|
_st8,
|
|
_st_be16,
|
|
_st_be32,
|
|
};
|
|
|
|
static int grpci_hw_init(struct grpci_priv *priv)
|
|
{
|
|
volatile unsigned int *mbar0, *page0;
|
|
uint32_t data, addr, mbar0size;
|
|
pci_dev_t host = HOST_TGT;
|
|
|
|
mbar0 = (volatile unsigned int *)priv->pci_area;
|
|
|
|
if ( !priv->bt_enabled && ((priv->regs->page0 & PAGE0_BTEN) == PAGE0_BTEN) ) {
|
|
/* Byte twisting is on, turn it off */
|
|
grpci_cfg_w32(host, PCIR_BAR(0), 0xffffffff);
|
|
grpci_cfg_r32(host, PCIR_BAR(0), &addr);
|
|
/* Setup bar0 to nonzero value */
|
|
grpci_cfg_w32(host, PCIR_BAR(0),
|
|
CPU_swap_u32(0x80000000));
|
|
/* page0 is accessed through upper half of bar0 */
|
|
addr = (~CPU_swap_u32(addr)+1)>>1;
|
|
mbar0size = addr*2;
|
|
DBG("GRPCI: Size of MBAR0: 0x%x, MBAR0: 0x%x(lower) 0x%x(upper)\n",mbar0size,((unsigned int)mbar0),((unsigned int)mbar0)+mbar0size/2);
|
|
page0 = &mbar0[mbar0size/8];
|
|
DBG("GRPCI: PAGE0 reg address: 0x%x (0x%x)\n",((unsigned int)mbar0)+mbar0size/2,page0);
|
|
priv->regs->cfg_stat = (priv->regs->cfg_stat & (~0xf0000000)) | 0x80000000; /* Setup mmap reg so we can reach bar0 */
|
|
*page0 = 0<<PAGE0_BTEN_BIT; /* Disable bytetwisting ... */
|
|
}
|
|
|
|
/* Get the GRPCI Host PCI ID */
|
|
grpci_cfg_r32(host, PCIR_VENDOR, &priv->devVend);
|
|
|
|
/* set 1:1 mapping between AHB -> PCI memory */
|
|
priv->regs->cfg_stat = (priv->regs->cfg_stat & 0x0fffffff) | priv->pci_area;
|
|
|
|
/* determine size of target BAR1 */
|
|
grpci_cfg_w32(host, PCIR_BAR(1), 0xffffffff);
|
|
grpci_cfg_r32(host, PCIR_BAR(1), &addr);
|
|
priv->bar1_size = (~(addr & ~0xf)) + 1;
|
|
|
|
/* and map system RAM at pci address 0x40000000 */
|
|
priv->bar1_pci_adr &= ~(priv->bar1_size - 1); /* Fix alignment of BAR1 */
|
|
grpci_cfg_w32(host, PCIR_BAR(1), priv->bar1_pci_adr);
|
|
priv->regs->page1 = priv->bar1_pci_adr;
|
|
|
|
/* Translate I/O accesses 1:1 */
|
|
priv->regs->iomap = priv->pci_io & 0xffff0000;
|
|
|
|
/* Setup Latency Timer and cache line size. Default cache line
|
|
* size will result in poor performance (256 word fetches), 0xff
|
|
* will set it according to the max size of the PCI FIFO.
|
|
*/
|
|
grpci_cfg_w8(host, PCIR_CACHELNSZ, 0xff);
|
|
grpci_cfg_w8(host, PCIR_LATTIMER, 0x40);
|
|
|
|
/* set as bus master and enable pci memory responses */
|
|
grpci_cfg_r32(host, PCIR_COMMAND, &data);
|
|
data |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
|
|
grpci_cfg_w32(host, PCIR_COMMAND, data);
|
|
|
|
/* unmask all PCI interrupts at PCI Core, not all GRPCI cores support
|
|
* this
|
|
*/
|
|
priv->regs->irq = 0xf0000;
|
|
|
|
/* Successful */
|
|
return 0;
|
|
}
|
|
|
|
/* Initializes the GRPCI 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 GRPCI hardware initialization
|
|
* -4 Error registering driver to PCI layer
|
|
*/
|
|
static int grpci_init(struct grpci_priv *priv)
|
|
{
|
|
struct ambapp_apb_info *apb;
|
|
struct ambapp_ahb_info *ahb;
|
|
int pin;
|
|
union drvmgr_key_value *value;
|
|
char keyname[6];
|
|
struct amba_dev_info *ainfo = priv->dev->businfo;
|
|
|
|
/* 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->common.irq;
|
|
priv->regs = (struct grpci_regs *)apb->start;
|
|
priv->bt_enabled = DEFAULT_BT_ENABLED;
|
|
|
|
/* 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] + (ahb->mask[1] >> 1);
|
|
priv->pci_conf_end = ahb->start[1] + ahb->mask[1];
|
|
|
|
/* 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 GRPCI core.
|
|
*/
|
|
strcpy(keyname, "INTX#");
|
|
for (pin=1; pin<5; pin++) {
|
|
if ( grpci_pci_irq_table[pin-1] == 0xff ) {
|
|
grpci_pci_irq_table[pin-1] = priv->irq;
|
|
|
|
/* 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 )
|
|
grpci_pci_irq_table[pin-1] = value->i;
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
|
|
/* Use GRPCI target BAR1 to map CPU RAM to PCI, this is to make it
|
|
* possible for PCI peripherals to do DMA directly to CPU memory.
|
|
*/
|
|
value = drvmgr_dev_key_get(priv->dev, "tgtbar1", DRVMGR_KT_INT);
|
|
if (value)
|
|
priv->bar1_pci_adr = value->i;
|
|
else
|
|
priv->bar1_pci_adr = SYSTEM_MAINMEM_START; /* default */
|
|
|
|
/* This driver only support HOST systems, we check for HOST */
|
|
if ( !(priv->regs->cfg_stat & CFGSTAT_HOST) ) {
|
|
/* Target not supported */
|
|
return -2;
|
|
}
|
|
|
|
/* Init the PCI Core */
|
|
if ( grpci_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 */
|
|
priv->maps_up[0].name = "Target BAR1 -> AMBA";
|
|
priv->maps_up[0].size = priv->bar1_size;
|
|
priv->maps_up[0].from_adr = (void *)priv->bar1_pci_adr;
|
|
priv->maps_up[0].to_adr = (void *)priv->bar1_pci_adr;
|
|
/* End table */
|
|
priv->maps_up[1].size = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Called when a core is found with the AMBA device and vendor ID
|
|
* given in grpci_ids[]. IRQ, Console does not work here
|
|
*/
|
|
int grpci_init1(struct drvmgr_dev *dev)
|
|
{
|
|
int status;
|
|
struct grpci_priv *priv;
|
|
struct pci_auto_setup grpci_auto_cfg;
|
|
|
|
DBG("GRPCI[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name);
|
|
|
|
if ( grpci_minor != 0 ) {
|
|
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 GRPCI driver on local bus */
|
|
return DRVMGR_FAIL;
|
|
}
|
|
|
|
priv = dev->priv;
|
|
if ( !priv )
|
|
return DRVMGR_NOMEM;
|
|
|
|
priv->dev = dev;
|
|
priv->minor = grpci_minor++;
|
|
|
|
grpcipriv = priv;
|
|
status = grpci_init(priv);
|
|
if (status) {
|
|
printk("Failed to initialize grpci driver %d\n", status);
|
|
return DRVMGR_FAIL;
|
|
}
|
|
|
|
|
|
/* Register the PCI core at the PCI layers */
|
|
|
|
if (priv->bt_enabled == 0) {
|
|
/* Host is Big-Endian */
|
|
pci_endian = PCI_BIG_ENDIAN;
|
|
|
|
memcpy(&grpci_access_drv.io, &grpci_io_ops_be,
|
|
sizeof(grpci_io_ops_be));
|
|
grpci_access_drv.memreg = &pci_memreg_sparc_be_ops;
|
|
}
|
|
|
|
if (pci_access_drv_register(&grpci_access_drv)) {
|
|
/* Access routines registration failed */
|
|
return DRVMGR_FAIL;
|
|
}
|
|
|
|
/* Prepare memory MAP */
|
|
grpci_auto_cfg.options = 0;
|
|
grpci_auto_cfg.mem_start = 0;
|
|
grpci_auto_cfg.mem_size = 0;
|
|
grpci_auto_cfg.memio_start = priv->pci_area;
|
|
grpci_auto_cfg.memio_size = priv->pci_area_end - priv->pci_area;
|
|
grpci_auto_cfg.io_start = priv->pci_io;
|
|
grpci_auto_cfg.io_size = priv->pci_conf - priv->pci_io;
|
|
grpci_auto_cfg.irq_map = grpci_bus0_irq_map;
|
|
grpci_auto_cfg.irq_route = NULL; /* use standard routing */
|
|
pci_config_register(&grpci_auto_cfg);
|
|
|
|
if (pci_config_init()) {
|
|
/* PCI configuration failed */
|
|
return DRVMGR_FAIL;
|
|
}
|
|
|
|
priv->config.maps_down = &priv->maps_down[0];
|
|
priv->config.maps_up = &priv->maps_up[0];
|
|
return pcibus_register(dev, &priv->config);
|
|
}
|
|
|
|
/* DMA functions which uses GRPCIs optional DMA controller (len in words) */
|
|
int grpci_dma_to_pci(
|
|
unsigned int ahb_addr,
|
|
unsigned int pci_addr,
|
|
unsigned int len)
|
|
{
|
|
int ret = 0;
|
|
|
|
pcidma[0] = 0x82;
|
|
pcidma[1] = ahb_addr;
|
|
pcidma[2] = pci_addr;
|
|
pcidma[3] = len;
|
|
pcidma[0] = 0x83;
|
|
|
|
while ( (pcidma[0] & 0x4) == 0)
|
|
;
|
|
|
|
if (pcidma[0] & 0x8) { /* error */
|
|
ret = -1;
|
|
}
|
|
|
|
pcidma[0] |= 0xC;
|
|
return ret;
|
|
|
|
}
|
|
|
|
int grpci_dma_from_pci(
|
|
unsigned int ahb_addr,
|
|
unsigned int pci_addr,
|
|
unsigned int len)
|
|
{
|
|
int ret = 0;
|
|
|
|
pcidma[0] = 0x80;
|
|
pcidma[1] = ahb_addr;
|
|
pcidma[2] = pci_addr;
|
|
pcidma[3] = len;
|
|
pcidma[0] = 0x81;
|
|
|
|
while ( (pcidma[0] & 0x4) == 0)
|
|
;
|
|
|
|
if (pcidma[0] & 0x8) { /* error */
|
|
ret = -1;
|
|
}
|
|
|
|
pcidma[0] |= 0xC;
|
|
return ret;
|
|
|
|
}
|