forked from Imagelibrary/rtems
@@ -1,618 +0,0 @@
|
||||
/* GR-701 PCI Target driver.
|
||||
*
|
||||
* COPYRIGHT (c) 2008.
|
||||
* Cobham Gaisler AB.
|
||||
*
|
||||
* Configures the GR-701 interface PCI board.
|
||||
* This driver provides a AMBA PnP bus by using the general part
|
||||
* of the AMBA PnP bus driver (ambapp_bus.c).
|
||||
*
|
||||
* Driver resources for the AMBA PnP bus provided can be set using
|
||||
* gr701_set_resources().
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <bsp.h>
|
||||
#include <rtems/bspIo.h>
|
||||
#include <pci.h>
|
||||
#include <pci/access.h>
|
||||
|
||||
#include <grlib/ambapp.h>
|
||||
|
||||
#include <grlib/ambapp.h>
|
||||
#include <drvmgr/drvmgr.h>
|
||||
#include <grlib/ambapp_bus.h>
|
||||
#include <drvmgr/pci_bus.h>
|
||||
#include <grlib/bspcommon.h>
|
||||
#include <grlib/genirq.h>
|
||||
|
||||
#include <grlib/gr_701.h>
|
||||
|
||||
#include <grlib/grlib_impl.h>
|
||||
|
||||
/* Offset from 0x80000000 (dual bus version) */
|
||||
#define AHB1_BASE_ADDR 0x80000000
|
||||
#define AHB1_IOAREA_BASE_ADDR 0x80100000
|
||||
|
||||
/* #define DEBUG 1 */
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(x...) printk(x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
int gr701_init1(struct drvmgr_dev *dev);
|
||||
int gr701_init2(struct drvmgr_dev *dev);
|
||||
void gr701_interrupt(void *arg);
|
||||
|
||||
#define READ_REG(address) (*(volatile unsigned int *)address)
|
||||
|
||||
/* PCI bride reg layout on AMBA side */
|
||||
struct amba_bridge_regs {
|
||||
volatile unsigned int bar0;
|
||||
volatile unsigned int bar1;
|
||||
volatile unsigned int bar2;
|
||||
volatile unsigned int bar3;
|
||||
volatile unsigned int bar4;/* 0x10 */
|
||||
|
||||
volatile unsigned int unused[4*3-1];
|
||||
|
||||
volatile unsigned int ambabars[1]; /* 0x40 */
|
||||
};
|
||||
|
||||
/* PCI bride reg layout on PCI side */
|
||||
struct pci_bridge_regs {
|
||||
volatile unsigned int bar0;
|
||||
volatile unsigned int bar1;
|
||||
volatile unsigned int bar2;
|
||||
volatile unsigned int bar3;
|
||||
volatile unsigned int bar4; /* 0x10 */
|
||||
|
||||
volatile unsigned int ilevel;
|
||||
volatile unsigned int ipend;
|
||||
volatile unsigned int iforce;
|
||||
volatile unsigned int istatus;
|
||||
volatile unsigned int iclear;
|
||||
volatile unsigned int imask;
|
||||
};
|
||||
|
||||
/* Private data structure for driver */
|
||||
struct gr701_priv {
|
||||
/* Driver management */
|
||||
struct drvmgr_dev *dev;
|
||||
char prefix[16];
|
||||
SPIN_DECLARE(devlock);
|
||||
|
||||
struct pci_bridge_regs *pcib;
|
||||
struct amba_bridge_regs *ambab;
|
||||
|
||||
/* PCI */
|
||||
pci_dev_t pcidev;
|
||||
struct pci_dev_info *devinfo;
|
||||
|
||||
/* IRQ */
|
||||
genirq_t genirq;
|
||||
int interrupt_cnt;
|
||||
|
||||
/* GR-701 Address translation */
|
||||
struct drvmgr_map_entry bus_maps_up[2];
|
||||
struct drvmgr_map_entry bus_maps_down[2];
|
||||
|
||||
/* AMBA Plug&Play information on GR-701 */
|
||||
struct ambapp_bus abus;
|
||||
struct ambapp_mmap amba_maps[3];
|
||||
struct ambapp_config config;
|
||||
};
|
||||
|
||||
int ambapp_gr701_int_register(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
const char *info,
|
||||
drvmgr_isr handler,
|
||||
void *arg);
|
||||
int ambapp_gr701_int_unregister(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
drvmgr_isr isr,
|
||||
void *arg);
|
||||
int ambapp_gr701_int_unmask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_gr701_int_mask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_gr701_int_clear(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_gr701_get_params(
|
||||
struct drvmgr_dev *dev,
|
||||
struct drvmgr_bus_params *params);
|
||||
|
||||
struct ambapp_ops ambapp_gr701_ops = {
|
||||
.int_register = ambapp_gr701_int_register,
|
||||
.int_unregister = ambapp_gr701_int_unregister,
|
||||
.int_unmask = ambapp_gr701_int_unmask,
|
||||
.int_mask = ambapp_gr701_int_mask,
|
||||
.int_clear = ambapp_gr701_int_clear,
|
||||
.get_params = ambapp_gr701_get_params
|
||||
};
|
||||
|
||||
struct drvmgr_drv_ops gr701_ops =
|
||||
{
|
||||
.init = {gr701_init1, gr701_init2, NULL, NULL},
|
||||
.remove = NULL,
|
||||
.info = NULL
|
||||
};
|
||||
|
||||
struct pci_dev_id_match gr701_ids[] =
|
||||
{
|
||||
PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_701),
|
||||
PCIID_END_TABLE /* Mark end of table */
|
||||
};
|
||||
|
||||
struct pci_drv_info gr701_info =
|
||||
{
|
||||
{
|
||||
DRVMGR_OBJ_DRV, /* Driver */
|
||||
NULL, /* Next driver */
|
||||
NULL, /* Device list */
|
||||
DRIVER_PCI_GAISLER_GR701_ID, /* Driver ID */
|
||||
"GR-701_DRV", /* Driver Name */
|
||||
DRVMGR_BUS_TYPE_PCI, /* Bus Type */
|
||||
&gr701_ops,
|
||||
NULL, /* Funcs */
|
||||
0, /* No devices yet */
|
||||
0,
|
||||
},
|
||||
&gr701_ids[0]
|
||||
};
|
||||
|
||||
/* Driver resources configuration for the AMBA bus on the GR-701 board.
|
||||
* It is declared weak so that the user may override it from the project file,
|
||||
* if the default settings are not enough.
|
||||
*
|
||||
* The configuration consists of an array of configuration pointers, each
|
||||
* pointer determine the configuration of one GR-701 board. Pointer
|
||||
* zero is for board0, pointer 1 for board1 and so on.
|
||||
*
|
||||
* The array must end with a NULL pointer.
|
||||
*/
|
||||
struct drvmgr_bus_res *gr701_resources[] __attribute__((weak)) =
|
||||
{
|
||||
NULL
|
||||
};
|
||||
|
||||
void gr701_register_drv(void)
|
||||
{
|
||||
DBG("Registering GR-701 PCI driver\n");
|
||||
drvmgr_drv_register(&gr701_info.general);
|
||||
}
|
||||
|
||||
void gr701_interrupt(void *arg)
|
||||
{
|
||||
struct gr701_priv *priv = arg;
|
||||
unsigned int status;
|
||||
int irq = 0;
|
||||
SPIN_ISR_IRQFLAGS(irqflags);
|
||||
|
||||
SPIN_LOCK(&priv->devlock, irqflags);
|
||||
while ( (status=priv->pcib->istatus) != 0 ) {
|
||||
priv->interrupt_cnt++; /* An interrupt was generated */
|
||||
irq = status;
|
||||
genirq_doirq(priv->genirq, irq);
|
||||
/* ACK interrupt */
|
||||
priv->pcib->istatus = 0;
|
||||
}
|
||||
SPIN_UNLOCK(&priv->devlock, irqflags);
|
||||
|
||||
/* ACK interrupt, this is because PCI is Level, so the IRQ Controller still drives the IRQ. */
|
||||
if ( irq )
|
||||
drvmgr_interrupt_clear(priv->dev, 0);
|
||||
}
|
||||
|
||||
static int gr701_hw_init(struct gr701_priv *priv)
|
||||
{
|
||||
uint32_t com1;
|
||||
struct pci_bridge_regs *pcib;
|
||||
struct amba_bridge_regs *ambab;
|
||||
int mst;
|
||||
unsigned int pci_freq_hz;
|
||||
pci_dev_t pcidev = priv->pcidev;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
|
||||
/* Set up PCI ==> AMBA */
|
||||
priv->pcib = pcib = (void *)devinfo->resources[0].address;
|
||||
pcib->bar0 = 0xfc000000;
|
||||
|
||||
/* Set up GR701 AMBA Masters connection to PCI */
|
||||
priv->ambab = ambab = (struct amba_bridge_regs *)(
|
||||
devinfo->resources[1].address + 0x400);
|
||||
|
||||
/* Init all msters, max 16 */
|
||||
for (mst=0; mst<16; mst++) {
|
||||
ambab->ambabars[mst] = 0x40000000;
|
||||
if (READ_REG(&ambab->ambabars[mst]) != 0x40000000)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Setup Address translation for AMBA bus, assume that PCI BAR
|
||||
* are mapped 1:1 to CPU.
|
||||
*/
|
||||
|
||||
priv->amba_maps[0].size = 0x04000000;
|
||||
priv->amba_maps[0].local_adr = devinfo->resources[1].address;
|
||||
priv->amba_maps[0].remote_adr = 0xfc000000;
|
||||
|
||||
/* Mark end of table */
|
||||
priv->amba_maps[1].size=0;
|
||||
priv->amba_maps[1].local_adr = 0;
|
||||
priv->amba_maps[1].remote_adr = 0;
|
||||
|
||||
/* Setup DOWN-streams address translation */
|
||||
priv->bus_maps_down[0].name = "PCI BAR1 -> AMBA";
|
||||
priv->bus_maps_down[0].size = priv->amba_maps[0].size;
|
||||
priv->bus_maps_down[0].from_adr = (void *)devinfo->resources[1].address;
|
||||
priv->bus_maps_down[0].to_adr = (void *)0xfc000000;
|
||||
|
||||
/* Setup UP-streams address translation */
|
||||
priv->bus_maps_up[0].name = "AMBA PCIF Window";
|
||||
priv->bus_maps_up[0].size = 0x10000000;
|
||||
priv->bus_maps_up[0].from_adr = (void *)0xe0000000;
|
||||
priv->bus_maps_up[0].to_adr = (void *)0x40000000;
|
||||
|
||||
/* Mark end of translation tables */
|
||||
priv->bus_maps_down[1].size = 0;
|
||||
priv->bus_maps_up[1].size = 0;
|
||||
|
||||
/* Enable I/O and Mem accesses */
|
||||
pci_cfg_r32(pcidev, PCIR_COMMAND, &com1);
|
||||
com1 |= PCIM_CMD_PORTEN | PCIM_CMD_MEMEN;
|
||||
pci_cfg_w32(pcidev, PCIR_COMMAND, com1);
|
||||
|
||||
/* Start AMBA PnP scan at first AHB bus */
|
||||
ambapp_scan(&priv->abus, devinfo->resources[1].address + 0x3f00000,
|
||||
NULL, &priv->amba_maps[0]);
|
||||
|
||||
/* Frequency is the same as the PCI bus frequency */
|
||||
drvmgr_freq_get(priv->dev, 0, &pci_freq_hz);
|
||||
|
||||
/* Initialize Frequency of AMBA bus */
|
||||
ambapp_freq_init(&priv->abus, NULL, pci_freq_hz);
|
||||
|
||||
/* Init IRQ controller (avoid IRQ generation) */
|
||||
pcib->imask = 0x0000;
|
||||
pcib->ipend = 0;
|
||||
pcib->iclear = 0xffff;
|
||||
pcib->iforce = 0;
|
||||
pcib->ilevel = 0x0;
|
||||
|
||||
/* Successfully registered the GR-701 board */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gr701_hw_init2(struct gr701_priv *priv)
|
||||
{
|
||||
/* Enable PCI Master (for DMA) */
|
||||
pci_master_enable(priv->pcidev);
|
||||
}
|
||||
|
||||
/* Called when a PCI target is found with the PCI device and vendor ID
|
||||
* given in gr701_ids[].
|
||||
*/
|
||||
int gr701_init1(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct gr701_priv *priv;
|
||||
struct pci_dev_info *devinfo;
|
||||
uint32_t bar0, bar1, bar0_size, bar1_size;
|
||||
int resources_cnt;
|
||||
|
||||
priv = grlib_calloc(1, sizeof(*priv));
|
||||
if ( !priv )
|
||||
return DRVMGR_NOMEM;
|
||||
|
||||
dev->priv = priv;
|
||||
priv->dev = dev;
|
||||
|
||||
/* Determine number of configurations */
|
||||
resources_cnt = get_resarray_count(gr701_resources);
|
||||
|
||||
/* Generate Device prefix */
|
||||
strcpy(priv->prefix, "/dev/gr701_0");
|
||||
priv->prefix[11] += dev->minor_drv;
|
||||
mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
priv->prefix[12] = '/';
|
||||
priv->prefix[13] = '\0';
|
||||
|
||||
priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo;
|
||||
priv->pcidev = devinfo->pcidev;
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
bar1 = devinfo->resources[1].address;
|
||||
bar1_size = devinfo->resources[1].size;
|
||||
printk("\n\n--- GR-701[%d] ---\n", dev->minor_drv);
|
||||
printk(" PCI BUS: 0x%x, SLOT: 0x%x, FUNCTION: 0x%x\n",
|
||||
PCI_DEV_EXPAND(priv->pcidev));
|
||||
printk(" PCI VENDOR: 0x%04x, DEVICE: 0x%04x\n\n\n",
|
||||
devinfo->id.vendor, devinfo->id.device);
|
||||
printk(" PCI BAR[0]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar0, bar0 + bar0_size - 1);
|
||||
printk(" PCI BAR[1]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar1, bar1 + bar1_size - 1);
|
||||
printk(" IRQ: %d\n\n\n", devinfo->irq);
|
||||
|
||||
/* all neccessary space assigned to GR-701 target? */
|
||||
if ((bar0_size == 0) || (bar1_size == 0))
|
||||
return DRVMGR_ENORES;
|
||||
|
||||
/* Initialize spin-lock for this PCI perihperal device. This is to
|
||||
* protect the Interrupt Controller Registers. The genirq layer is
|
||||
* protecting its own internals and ISR dispatching.
|
||||
*/
|
||||
SPIN_INIT(&priv->devlock, priv->prefix);
|
||||
|
||||
priv->genirq = genirq_init(16);
|
||||
if ( priv->genirq == NULL ) {
|
||||
free(priv);
|
||||
dev->priv = NULL;
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
if ( gr701_hw_init(priv) ) {
|
||||
genirq_destroy(priv->genirq);
|
||||
free(priv);
|
||||
dev->priv = NULL;
|
||||
printk(" Failed to initialize GR-701 HW\n");
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
/* Init amba bus */
|
||||
priv->config.abus = &priv->abus;
|
||||
priv->config.ops = &ambapp_gr701_ops;
|
||||
priv->config.maps_up = &priv->bus_maps_up[0];
|
||||
priv->config.maps_down = &priv->bus_maps_down[0];
|
||||
if ( priv->dev->minor_drv < resources_cnt ) {
|
||||
priv->config.resources = gr701_resources[priv->dev->minor_drv];
|
||||
} else {
|
||||
priv->config.resources = NULL;
|
||||
}
|
||||
|
||||
/* Create and register AMBA PnP bus. */
|
||||
return ambapp_bus_register(dev, &priv->config);
|
||||
}
|
||||
|
||||
/* Called when a PCI target is found with the PCI device and vendor ID
|
||||
* given in gr701_ids[].
|
||||
*/
|
||||
int gr701_init2(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct gr701_priv *priv = dev->priv;
|
||||
|
||||
/* Clear any old interrupt requests */
|
||||
drvmgr_interrupt_clear(dev, 0);
|
||||
|
||||
/* Enable System IRQ so that GR-701 PCI target interrupt goes through.
|
||||
*
|
||||
* It is important to enable it in stage init2. If interrupts were
|
||||
* enabled in init1 this might hang the system when more than one PCI
|
||||
* board is connected, this is because PCI interrupts might be shared
|
||||
* and PCI target 2 have not initialized and might therefore drive
|
||||
* interrupt already when entering init1().
|
||||
*/
|
||||
drvmgr_interrupt_register(dev, 0, "gr701", gr701_interrupt, priv);
|
||||
|
||||
gr701_hw_init2(priv);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_gr701_int_register(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
const char *info,
|
||||
drvmgr_isr handler,
|
||||
void *arg)
|
||||
{
|
||||
struct gr701_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
int status;
|
||||
void *h;
|
||||
|
||||
h = genirq_alloc_handler(handler, arg);
|
||||
if ( h == NULL )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
status = genirq_register(priv->genirq, irq, h);
|
||||
if ( status == 0 ) {
|
||||
/* Clear IRQ for first registered handler */
|
||||
priv->pcib->iclear = (1<<irq);
|
||||
} else if ( status == 1 )
|
||||
status = 0;
|
||||
|
||||
if (status != 0) {
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
genirq_free_handler(h);
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
status = genirq_enable(priv->genirq, irq, handler, arg);
|
||||
if ( status == 0 ) {
|
||||
/* Enable IRQ for first enabled handler only */
|
||||
priv->pcib->imask |= (1<<irq); /* unmask interrupt source */
|
||||
} else if ( status == 1 )
|
||||
status = DRVMGR_OK;
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int ambapp_gr701_int_unregister(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
drvmgr_isr isr,
|
||||
void *arg)
|
||||
{
|
||||
struct gr701_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
int status;
|
||||
void *handler;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
status = genirq_disable(priv->genirq, irq, isr, arg);
|
||||
if ( status == 0 ) {
|
||||
/* Disable IRQ only when no enabled handler exists */
|
||||
priv->pcib->imask &= ~(1<<irq); /* mask interrupt source */
|
||||
}
|
||||
|
||||
handler = genirq_unregister(priv->genirq, irq, isr, arg);
|
||||
if ( handler == NULL )
|
||||
status = DRVMGR_FAIL;
|
||||
else
|
||||
status = DRVMGR_OK;
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
if (handler)
|
||||
genirq_free_handler(handler);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int ambapp_gr701_int_unmask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr701_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
|
||||
DBG("GR-701 IRQ %d: enable\n", irq);
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
/* Enable IRQ */
|
||||
priv->pcib->imask |= (1<<irq); /* unmask interrupt source */
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_gr701_int_mask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr701_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
|
||||
DBG("GR-701 IRQ %d: disable\n", irq);
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
/* Disable IRQ */
|
||||
priv->pcib->imask &= ~(1<<irq); /* mask interrupt source */
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_gr701_int_clear(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr701_priv *priv = dev->parent->dev->priv;
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
priv->pcib->iclear = (1<<irq);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_gr701_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params)
|
||||
{
|
||||
struct gr701_priv *priv = dev->parent->dev->priv;
|
||||
|
||||
/* Device name prefix pointer, skip /dev */
|
||||
params->dev_prefix = &priv->prefix[5];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gr701_print_dev(struct drvmgr_dev *dev, int options)
|
||||
{
|
||||
struct gr701_priv *priv = dev->priv;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
unsigned int freq_hz;
|
||||
uint32_t bar0, bar1, bar0_size, bar1_size;
|
||||
|
||||
/* Print */
|
||||
printf("--- GR-701 [bus 0x%x, dev 0x%x, fun 0x%x] ---\n",
|
||||
PCI_DEV_EXPAND(priv->pcidev));
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
bar1 = devinfo->resources[1].address;
|
||||
bar1_size = devinfo->resources[1].size;
|
||||
|
||||
printf(" PCI BAR[0]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar0, bar0 + bar0_size - 1);
|
||||
printf(" PCI BAR[1]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar1, bar1 + bar1_size - 1);
|
||||
printf(" IRQ: %d\n", devinfo->irq);
|
||||
|
||||
/* Frequency is the same as the PCI bus frequency */
|
||||
drvmgr_freq_get(dev, 0, &freq_hz);
|
||||
|
||||
printf(" FREQ: %u Hz\n", freq_hz);
|
||||
printf(" IMASK: 0x%08x\n", priv->pcib->imask);
|
||||
printf(" IPEND: 0x%08x\n", priv->pcib->ipend);
|
||||
|
||||
/* Print amba config */
|
||||
if ( options & GR701_OPTIONS_AMBA ) {
|
||||
ambapp_print(&priv->abus, 10);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Print IRQ handlers and their arguments */
|
||||
if ( options & GR701_OPTIONS_IRQ ) {
|
||||
int i;
|
||||
for(i=0; i<16; i++) {
|
||||
printf(" IRQ[%02d]: 0x%x, arg: 0x%x\n",
|
||||
i, (unsigned int)priv->isrs[i].handler, (unsigned int)priv->isrs[i].arg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gr701_print(int options)
|
||||
{
|
||||
struct pci_drv_info *drv = &gr701_info;
|
||||
struct drvmgr_dev *dev;
|
||||
|
||||
dev = drv->general.dev;
|
||||
while(dev) {
|
||||
gr701_print_dev(dev, options);
|
||||
dev = dev->next_in_drv;
|
||||
}
|
||||
}
|
||||
@@ -1,694 +0,0 @@
|
||||
/* GR-RASTA-ADCDAC PCI Target driver.
|
||||
*
|
||||
* COPYRIGHT (c) 2008.
|
||||
* Cobham Gaisler AB.
|
||||
*
|
||||
* Configures the GR-RASTA-ADCDAC interface PCI board.
|
||||
* This driver provides a AMBA PnP bus by using the general part
|
||||
* of the AMBA PnP bus driver (ambapp_bus.c).
|
||||
*
|
||||
* Driver resources for the AMBA PnP bus provided can be set using
|
||||
* gr_rasta_adcdac_set_resources().
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <bsp.h>
|
||||
#include <rtems/bspIo.h>
|
||||
#include <pci.h>
|
||||
|
||||
#include <grlib/ambapp.h>
|
||||
#include <grlib/grlib.h>
|
||||
#include <drvmgr/drvmgr.h>
|
||||
#include <grlib/ambapp_bus.h>
|
||||
#include <drvmgr/pci_bus.h>
|
||||
#include <grlib/bspcommon.h>
|
||||
#include <grlib/genirq.h>
|
||||
|
||||
#include <grlib/gr_rasta_adcdac.h>
|
||||
|
||||
#include <grlib/grlib_impl.h>
|
||||
|
||||
/*#define DEBUG 1*/
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(x...) printk(x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
/* Determines which PCI address the AHB masters will access, it should be
|
||||
* set so that the masters can access the CPU RAM. Default is base of CPU RAM,
|
||||
* CPU RAM is mapped 1:1 to PCI space.
|
||||
*/
|
||||
extern unsigned int _RAM_START;
|
||||
#define AHBMST2PCIADR (((unsigned int)&_RAM_START) & 0xf0000000)
|
||||
|
||||
/* PCI ID */
|
||||
#define PCIID_VENDOR_GAISLER 0x1AC8
|
||||
#define PCIID_DEVICE_GR_RASTA_ADCDAC 0x0014
|
||||
|
||||
int gr_rasta_adcdac_init1(struct drvmgr_dev *dev);
|
||||
int gr_rasta_adcdac_init2(struct drvmgr_dev *dev);
|
||||
void gr_rasta_adcdac_isr (void *arg);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
struct gr_rasta_adcdac_ver {
|
||||
const unsigned int amba_freq_hz; /* The frequency */
|
||||
const unsigned int amba_ioarea; /* The address where the PnP IOAREA starts at */
|
||||
};
|
||||
|
||||
/* Private data structure for driver */
|
||||
struct gr_rasta_adcdac_priv {
|
||||
/* Driver management */
|
||||
struct drvmgr_dev *dev;
|
||||
char prefix[20];
|
||||
SPIN_DECLARE(devlock);
|
||||
|
||||
/* PCI */
|
||||
pci_dev_t pcidev;
|
||||
struct pci_dev_info *devinfo;
|
||||
uint32_t ahbmst2pci_map;
|
||||
|
||||
/* IRQ */
|
||||
genirq_t genirq;
|
||||
|
||||
/* GR-RASTA-ADCDAC */
|
||||
struct gr_rasta_adcdac_ver *version;
|
||||
struct irqmp_regs *irq;
|
||||
struct grpci_regs *grpci;
|
||||
struct drvmgr_map_entry bus_maps_down[3];
|
||||
struct drvmgr_map_entry bus_maps_up[2];
|
||||
|
||||
/* AMBA Plug&Play information on GR-RASTA-ADCDAC */
|
||||
struct ambapp_bus abus;
|
||||
struct ambapp_mmap amba_maps[4];
|
||||
struct ambapp_config config;
|
||||
};
|
||||
|
||||
struct gr_rasta_adcdac_ver gr_rasta_adcdac_ver0 = {
|
||||
.amba_freq_hz = 50000000,
|
||||
.amba_ioarea = 0x80100000,
|
||||
};
|
||||
|
||||
int ambapp_rasta_adcdac_int_register(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
const char *info,
|
||||
drvmgr_isr handler,
|
||||
void *arg);
|
||||
int ambapp_rasta_adcdac_int_unregister(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
drvmgr_isr isr,
|
||||
void *arg);
|
||||
int ambapp_rasta_adcdac_int_unmask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_rasta_adcdac_int_mask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_rasta_adcdac_int_clear(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_rasta_adcdac_get_params(
|
||||
struct drvmgr_dev *dev,
|
||||
struct drvmgr_bus_params *params);
|
||||
|
||||
struct ambapp_ops ambapp_rasta_adcdac_ops = {
|
||||
.int_register = ambapp_rasta_adcdac_int_register,
|
||||
.int_unregister = ambapp_rasta_adcdac_int_unregister,
|
||||
.int_unmask = ambapp_rasta_adcdac_int_unmask,
|
||||
.int_mask = ambapp_rasta_adcdac_int_mask,
|
||||
.int_clear = ambapp_rasta_adcdac_int_clear,
|
||||
.get_params = ambapp_rasta_adcdac_get_params
|
||||
};
|
||||
|
||||
struct drvmgr_drv_ops gr_rasta_adcdac_ops =
|
||||
{ .init = {gr_rasta_adcdac_init1, gr_rasta_adcdac_init2, NULL, NULL},
|
||||
.remove = NULL,
|
||||
.info = NULL
|
||||
};
|
||||
|
||||
struct pci_dev_id_match gr_rasta_adcdac_ids[] =
|
||||
{
|
||||
PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_RASTA_ADCDAC),
|
||||
PCIID_END_TABLE /* Mark end of table */
|
||||
};
|
||||
|
||||
struct pci_drv_info gr_rasta_adcdac_info =
|
||||
{
|
||||
{
|
||||
DRVMGR_OBJ_DRV, /* Driver */
|
||||
NULL, /* Next driver */
|
||||
NULL, /* Device list */
|
||||
DRIVER_PCI_GAISLER_RASTAADCDAC_ID,/* Driver ID */
|
||||
"GR-RASTA-ADCDAC_DRV", /* Driver Name */
|
||||
DRVMGR_BUS_TYPE_PCI, /* Bus Type */
|
||||
&gr_rasta_adcdac_ops,
|
||||
NULL, /* Funcs */
|
||||
0, /* No devices yet */
|
||||
0,
|
||||
},
|
||||
&gr_rasta_adcdac_ids[0]
|
||||
};
|
||||
|
||||
/* Driver resources configuration for the AMBA bus on the GR-RASTA-ADCDAC board.
|
||||
* It is declared weak so that the user may override it from the project file,
|
||||
* if the default settings are not enough.
|
||||
*
|
||||
* The configuration consists of an array of configuration pointers, each
|
||||
* pointer determine the configuration of one GR-RASTA-ADCDAC board. Pointer
|
||||
* zero is for board0, pointer 1 for board1 and so on.
|
||||
*
|
||||
* The array must end with a NULL pointer.
|
||||
*/
|
||||
struct drvmgr_bus_res *gr_rasta_adcdac_resources[] __attribute__((weak)) =
|
||||
{
|
||||
NULL
|
||||
};
|
||||
|
||||
void gr_rasta_adcdac_register_drv(void)
|
||||
{
|
||||
DBG("Registering GR-RASTA-ADCDAC PCI driver\n");
|
||||
drvmgr_drv_register(&gr_rasta_adcdac_info.general);
|
||||
}
|
||||
|
||||
void gr_rasta_adcdac_isr (void *arg)
|
||||
{
|
||||
struct gr_rasta_adcdac_priv *priv = arg;
|
||||
unsigned int status, tmp;
|
||||
int irq;
|
||||
SPIN_ISR_IRQFLAGS(irqflags);
|
||||
|
||||
tmp = status = priv->irq->ipend;
|
||||
|
||||
/* DBG("GR-RASTA-ADCDAC: IRQ 0x%x\n",status); */
|
||||
|
||||
SPIN_LOCK(&priv->devlock, irqflags);
|
||||
for(irq=0; irq<16; irq++) {
|
||||
if ( status & (1<<irq) ) {
|
||||
genirq_doirq(priv->genirq, irq);
|
||||
priv->irq->iclear = (1<<irq);
|
||||
status &= ~(1<<irq);
|
||||
if ( status == 0 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
SPIN_UNLOCK(&priv->devlock, irqflags);
|
||||
|
||||
/* ACK interrupt, this is because PCI is Level, so the IRQ Controller still drives the IRQ. */
|
||||
if ( tmp )
|
||||
drvmgr_interrupt_clear(priv->dev, 0);
|
||||
|
||||
DBG("RASTA-ADCDAC-IRQ: 0x%x\n", tmp);
|
||||
}
|
||||
|
||||
static int gr_rasta_adcdac_hw_init1(struct gr_rasta_adcdac_priv *priv)
|
||||
{
|
||||
uint32_t data;
|
||||
unsigned int *page0 = NULL;
|
||||
struct ambapp_dev *tmp;
|
||||
struct ambapp_ahb_info *ahb;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
uint32_t bar0, bar0_size;
|
||||
|
||||
/* Select version of GR-RASTA-ADCDAC board */
|
||||
switch (devinfo->rev) {
|
||||
case 0:
|
||||
priv->version = &gr_rasta_adcdac_ver0;
|
||||
break;
|
||||
default:
|
||||
return -2;
|
||||
}
|
||||
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
page0 = (unsigned int *)(bar0 + bar0_size/2);
|
||||
|
||||
/* Point PAGE0 to start of Plug and Play information */
|
||||
*page0 = priv->version->amba_ioarea & 0xf0000000;
|
||||
|
||||
/* set parity error response */
|
||||
pci_cfg_r32(priv->pcidev, PCIR_COMMAND, &data);
|
||||
pci_cfg_w32(priv->pcidev, PCIR_COMMAND, (data|PCIM_CMD_PERRESPEN));
|
||||
|
||||
/* Setup 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.
|
||||
*/
|
||||
pci_cfg_w8(priv->pcidev, PCIR_CACHELNSZ, 0xff);
|
||||
|
||||
/* Scan AMBA Plug&Play */
|
||||
|
||||
/* AMBA MAP bar0 (in CPU) ==> 0x80000000(remote amba address) */
|
||||
priv->amba_maps[0].size = bar0_size/2;
|
||||
priv->amba_maps[0].local_adr = bar0;
|
||||
priv->amba_maps[0].remote_adr = 0x80000000;
|
||||
|
||||
/* AMBA MAP bar1 (in CPU) ==> 0x40000000(remote amba address) */
|
||||
priv->amba_maps[1].size = devinfo->resources[1].size;
|
||||
priv->amba_maps[1].local_adr = devinfo->resources[1].address;
|
||||
priv->amba_maps[1].remote_adr = 0x40000000;
|
||||
|
||||
/* Addresses not matching with map be untouched */
|
||||
priv->amba_maps[2].size = 0xfffffff0;
|
||||
priv->amba_maps[2].local_adr = 0;
|
||||
priv->amba_maps[2].remote_adr = 0;
|
||||
|
||||
/* Mark end of table */
|
||||
priv->amba_maps[3].size=0;
|
||||
priv->amba_maps[3].local_adr = 0;
|
||||
priv->amba_maps[3].remote_adr = 0;
|
||||
|
||||
/* Start AMBA PnP scan at first AHB bus */
|
||||
/*ambapp_scan(priv->bar0 + (priv->version->amba_ioarea & ~0xf0000000),
|
||||
NULL, &priv->amba_maps[0], NULL, &priv->abus.root, NULL);*/
|
||||
ambapp_scan(&priv->abus,
|
||||
bar0 + (priv->version->amba_ioarea & ~0xf0000000),
|
||||
NULL, &priv->amba_maps[0]);
|
||||
|
||||
/* Initialize Frequency of AMBA bus */
|
||||
ambapp_freq_init(&priv->abus, NULL, priv->version->amba_freq_hz);
|
||||
|
||||
/* Point PAGE0 to start of APB area */
|
||||
*page0 = 0x80000000;
|
||||
|
||||
/* Find GRPCI controller */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_PCIFBRG,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -3;
|
||||
}
|
||||
priv->grpci = (struct grpci_regs *)((struct ambapp_apb_info *)tmp->devinfo)->start;
|
||||
|
||||
/* Set GRPCI mmap so that AMBA masters can access CPU-RAM over
|
||||
* the PCI window.
|
||||
*/
|
||||
priv->grpci->cfg_stat = (priv->grpci->cfg_stat & 0x0fffffff) |
|
||||
(priv->ahbmst2pci_map & 0xf0000000);
|
||||
priv->grpci->page1 = 0x40000000;
|
||||
|
||||
/* Find IRQ controller */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_IRQMP,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -4;
|
||||
}
|
||||
priv->irq = (struct irqmp_regs *)DEV_TO_APB(tmp)->start;
|
||||
/* Set up GR-RASTA-ADCDAC irq controller */
|
||||
priv->irq->iclear = 0xffff;
|
||||
priv->irq->ilevel = 0;
|
||||
priv->irq->mask[0] = 0;
|
||||
|
||||
/* DOWN streams translation table */
|
||||
priv->bus_maps_down[0].name = "PCI BAR0 -> AMBA";
|
||||
priv->bus_maps_down[0].size = priv->amba_maps[0].size;
|
||||
priv->bus_maps_down[0].from_adr = (void *)priv->amba_maps[0].local_adr;
|
||||
priv->bus_maps_down[0].to_adr = (void *)priv->amba_maps[0].remote_adr;
|
||||
|
||||
priv->bus_maps_down[1].name = "PCI BAR1 -> AMBA";
|
||||
priv->bus_maps_down[1].size = priv->amba_maps[1].size;
|
||||
priv->bus_maps_down[1].from_adr = (void *)priv->amba_maps[1].local_adr;
|
||||
priv->bus_maps_down[1].to_adr = (void *)priv->amba_maps[1].remote_adr;
|
||||
|
||||
/* Mark end of translation table */
|
||||
priv->bus_maps_down[2].size = 0;
|
||||
|
||||
/* Find GRPCI controller AHB Slave interface */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_AHB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_PCIFBRG,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -5;
|
||||
}
|
||||
ahb = (struct ambapp_ahb_info *)tmp->devinfo;
|
||||
|
||||
/* UP streams translation table */
|
||||
priv->bus_maps_up[0].name = "AMBA GRPCI Window";
|
||||
priv->bus_maps_up[0].size = ahb->mask[0]; /* AMBA->PCI Window on GR-RASTA-ADCDAC board */
|
||||
priv->bus_maps_up[0].from_adr = (void *)ahb->start[0];
|
||||
priv->bus_maps_up[0].to_adr = (void *)
|
||||
(priv->ahbmst2pci_map & 0xf0000000);
|
||||
|
||||
/* Mark end of translation table */
|
||||
priv->bus_maps_up[1].size = 0;
|
||||
|
||||
/* Successfully registered the RASTA board */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gr_rasta_adcdac_hw_init2(struct gr_rasta_adcdac_priv *priv)
|
||||
{
|
||||
/* Enable DMA by enabling PCI target as master */
|
||||
pci_master_enable(priv->pcidev);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
/* Called when a PCI target is found with the PCI device and vendor ID
|
||||
* given in gr_rasta_adcdac_ids[].
|
||||
*/
|
||||
int gr_rasta_adcdac_init1(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct gr_rasta_adcdac_priv *priv;
|
||||
struct pci_dev_info *devinfo;
|
||||
int status;
|
||||
uint32_t bar0, bar1, bar0_size, bar1_size;
|
||||
union drvmgr_key_value *value;
|
||||
int resources_cnt;
|
||||
|
||||
priv = grlib_calloc(1, sizeof(*priv));
|
||||
if ( !priv )
|
||||
return DRVMGR_NOMEM;
|
||||
|
||||
dev->priv = priv;
|
||||
priv->dev = dev;
|
||||
|
||||
/* Determine number of configurations */
|
||||
resources_cnt = get_resarray_count(gr_rasta_adcdac_resources);
|
||||
|
||||
/* Generate Device prefix */
|
||||
|
||||
strcpy(priv->prefix, "/dev/rastaadcdac0");
|
||||
priv->prefix[16] += dev->minor_drv;
|
||||
mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
priv->prefix[17] = '/';
|
||||
priv->prefix[18] = '\0';
|
||||
|
||||
priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo;
|
||||
priv->pcidev = devinfo->pcidev;
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
bar1 = devinfo->resources[1].address;
|
||||
bar1_size = devinfo->resources[1].size;
|
||||
printk("\n\n--- GR-RASTA-ADCDAC[%d] ---\n", dev->minor_drv);
|
||||
printk(" PCI BUS: 0x%x, SLOT: 0x%x, FUNCTION: 0x%x\n",
|
||||
PCI_DEV_EXPAND(priv->pcidev));
|
||||
printk(" PCI VENDOR: 0x%04x, DEVICE: 0x%04x\n",
|
||||
devinfo->id.vendor, devinfo->id.device);
|
||||
printk(" PCI BAR[0]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar0, bar0 + bar0_size - 1);
|
||||
printk(" PCI BAR[1]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar1, bar1 + bar1_size - 1);
|
||||
printk(" IRQ: %d\n\n\n", devinfo->irq);
|
||||
|
||||
/* all neccessary space assigned to GR-RASTA-ADCDAC target? */
|
||||
if ((bar0_size == 0) || (bar1_size == 0))
|
||||
return DRVMGR_ENORES;
|
||||
|
||||
/* Initialize spin-lock for this PCI perihperal device. This is to
|
||||
* protect the Interrupt Controller Registers. The genirq layer is
|
||||
* protecting its own internals and ISR dispatching.
|
||||
*/
|
||||
SPIN_INIT(&priv->devlock, priv->prefix);
|
||||
|
||||
/* Let user override which PCI address the AHB masters of the
|
||||
* RASTA-ADCDAC board access when doing DMA to CPU RAM. The AHB masters
|
||||
* access the PCI Window of the AMBA bus, the MSB 4-bits of that address
|
||||
* is translated according this config option before the address
|
||||
* goes out on the PCI bus.
|
||||
* Only the 4 MSB bits have an effect;
|
||||
*/
|
||||
value = drvmgr_dev_key_get(priv->dev, "ahbmst2pci", DRVMGR_KT_INT);
|
||||
if (value)
|
||||
priv->ahbmst2pci_map = value->i;
|
||||
else
|
||||
priv->ahbmst2pci_map = AHBMST2PCIADR; /* default */
|
||||
|
||||
priv->genirq = genirq_init(16);
|
||||
if ( priv->genirq == NULL ) {
|
||||
free(priv);
|
||||
dev->priv = NULL;
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
if ( (status = gr_rasta_adcdac_hw_init1(priv)) != 0 ) {
|
||||
genirq_destroy(priv->genirq);
|
||||
free(priv);
|
||||
dev->priv = NULL;
|
||||
printk(" Failed to initialize GR-RASTA-ADCDAC HW: %d\n", status);
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
/* Init amba bus */
|
||||
priv->config.abus = &priv->abus;
|
||||
priv->config.ops = &ambapp_rasta_adcdac_ops;
|
||||
priv->config.maps_up = &priv->bus_maps_up[0];
|
||||
priv->config.maps_down = &priv->bus_maps_down[0];
|
||||
if ( priv->dev->minor_drv < resources_cnt ) {
|
||||
priv->config.resources = gr_rasta_adcdac_resources[priv->dev->minor_drv];
|
||||
} else {
|
||||
priv->config.resources = NULL;
|
||||
}
|
||||
|
||||
/* Create and register AMBA PnP bus. */
|
||||
return ambapp_bus_register(dev, &priv->config);
|
||||
}
|
||||
|
||||
int gr_rasta_adcdac_init2(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct gr_rasta_adcdac_priv *priv = dev->priv;
|
||||
|
||||
/* Clear any old interrupt requests */
|
||||
drvmgr_interrupt_clear(dev, 0);
|
||||
|
||||
/* Enable System IRQ so that GR-RASTA-ADCDAC PCI target interrupt
|
||||
* goes through.
|
||||
*
|
||||
* It is important to enable it in stage init2. If interrupts were
|
||||
* enabled in init1 this might hang the system when more than one
|
||||
* PCI board is connected, this is because PCI interrupts might
|
||||
* be shared and PCI board 2 have not initialized and might
|
||||
* therefore drive interrupt already when entering init1().
|
||||
*/
|
||||
drvmgr_interrupt_register(
|
||||
dev,
|
||||
0,
|
||||
"gr_rasta_adcdac",
|
||||
gr_rasta_adcdac_isr,
|
||||
(void *)priv);
|
||||
|
||||
return gr_rasta_adcdac_hw_init2(priv);
|
||||
}
|
||||
|
||||
int ambapp_rasta_adcdac_int_register(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
const char *info,
|
||||
drvmgr_isr handler,
|
||||
void *arg)
|
||||
{
|
||||
struct gr_rasta_adcdac_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
int status;
|
||||
void *h;
|
||||
|
||||
h = genirq_alloc_handler(handler, arg);
|
||||
if ( h == NULL )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
status = genirq_register(priv->genirq, irq, h);
|
||||
if ( status == 0 ) {
|
||||
/* Clear IRQ for first registered handler */
|
||||
priv->irq->iclear = (1<<irq);
|
||||
} else if ( status == 1 )
|
||||
status = 0;
|
||||
|
||||
if (status != 0) {
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
genirq_free_handler(h);
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
status = genirq_enable(priv->genirq, irq, handler, arg);
|
||||
if ( status == 0 ) {
|
||||
/* Enable IRQ for first enabled handler only */
|
||||
priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */
|
||||
} else if ( status == 1 )
|
||||
status = 0;
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int ambapp_rasta_adcdac_int_unregister(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
drvmgr_isr isr,
|
||||
void *arg)
|
||||
{
|
||||
struct gr_rasta_adcdac_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
int status;
|
||||
void *handler;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
status = genirq_disable(priv->genirq, irq, isr, arg);
|
||||
if ( status == 0 ) {
|
||||
/* Disable IRQ only when no enabled handler exists */
|
||||
priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */
|
||||
}
|
||||
|
||||
handler = genirq_unregister(priv->genirq, irq, isr, arg);
|
||||
if ( handler == NULL )
|
||||
status = DRVMGR_FAIL;
|
||||
else
|
||||
status = DRVMGR_OK;
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
if (handler)
|
||||
genirq_free_handler(handler);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int ambapp_rasta_adcdac_int_unmask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_rasta_adcdac_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
|
||||
DBG("RASTA-ADCDAC IRQ %d: unmask\n", irq);
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_EINVAL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
/* Enable IRQ for first enabled handler only */
|
||||
priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_adcdac_int_mask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_rasta_adcdac_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
|
||||
DBG("RASTA-ADCDAC IRQ %d: mask\n", irq);
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_EINVAL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
/* Disable/mask IRQ */
|
||||
priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_adcdac_int_clear(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_rasta_adcdac_priv *priv = dev->parent->dev->priv;
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
priv->irq->iclear = (1<<irq);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_adcdac_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params)
|
||||
{
|
||||
struct gr_rasta_adcdac_priv *priv = dev->parent->dev->priv;
|
||||
|
||||
/* Device name prefix pointer, skip /dev */
|
||||
params->dev_prefix = &priv->prefix[5];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gr_rasta_adcdac_print_dev(struct drvmgr_dev *dev, int options)
|
||||
{
|
||||
struct gr_rasta_adcdac_priv *priv = dev->priv;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
uint32_t bar0, bar1, bar0_size, bar1_size;
|
||||
|
||||
/* Print */
|
||||
printf("--- GR-RASTA-ADCDAC [bus 0x%x, dev 0x%x, fun 0x%x] ---\n",
|
||||
PCI_DEV_EXPAND(priv->pcidev));
|
||||
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
bar1 = devinfo->resources[1].address;
|
||||
bar1_size = devinfo->resources[1].size;
|
||||
|
||||
printf(" PCI BAR[0]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar0, bar0 + bar0_size - 1);
|
||||
printf(" PCI BAR[1]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar1, bar1 + bar1_size - 1);
|
||||
printf(" IRQ REGS: 0x%" PRIxPTR "\n", (uintptr_t)priv->irq);
|
||||
printf(" IRQ: %d\n", devinfo->irq);
|
||||
printf(" PCI REVISION: %d\n", devinfo->rev);
|
||||
printf(" FREQ: %d Hz\n", priv->version->amba_freq_hz);
|
||||
printf(" IMASK: 0x%08x\n", priv->irq->mask[0]);
|
||||
printf(" IPEND: 0x%08x\n", priv->irq->ipend);
|
||||
|
||||
/* Print amba config */
|
||||
if ( options & RASTA_ADCDAC_OPTIONS_AMBA ) {
|
||||
ambapp_print(&priv->abus, 10);
|
||||
}
|
||||
#if 0
|
||||
/* Print IRQ handlers and their arguments */
|
||||
if ( options & RASTA_ADCDAC_OPTIONS_IRQ ) {
|
||||
int i;
|
||||
for(i=0; i<16; i++) {
|
||||
printf(" IRQ[%02d]: 0x%x, arg: 0x%x\n",
|
||||
i, (unsigned int)priv->isrs[i].handler, (unsigned int)priv->isrs[i].arg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gr_rasta_adcdac_print(int options)
|
||||
{
|
||||
struct pci_drv_info *drv = &gr_rasta_adcdac_info;
|
||||
struct drvmgr_dev *dev;
|
||||
|
||||
dev = drv->general.dev;
|
||||
while(dev) {
|
||||
gr_rasta_adcdac_print_dev(dev, options);
|
||||
dev = dev->next_in_drv;
|
||||
}
|
||||
}
|
||||
@@ -1,892 +0,0 @@
|
||||
/* GR-RASTA-IO PCI Target driver.
|
||||
*
|
||||
* COPYRIGHT (c) 2008.
|
||||
* Cobham Gaisler AB.
|
||||
*
|
||||
* Configures the GR-RASTA-IO interface PCI board.
|
||||
* This driver provides a AMBA PnP bus by using the general part
|
||||
* of the AMBA PnP bus driver (ambapp_bus.c).
|
||||
*
|
||||
* Driver resources for the AMBA PnP bus provided can be set using
|
||||
* gr_rasta_io_set_resources().
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <bsp.h>
|
||||
#include <rtems/bspIo.h>
|
||||
#include <pci.h>
|
||||
|
||||
#include <grlib/ambapp.h>
|
||||
#include <grlib/grlib.h>
|
||||
#include <drvmgr/drvmgr.h>
|
||||
#include <grlib/ambapp_bus.h>
|
||||
#include <drvmgr/pci_bus.h>
|
||||
#include <grlib/bspcommon.h>
|
||||
#include <grlib/genirq.h>
|
||||
|
||||
#include <grlib/gr_rasta_io.h>
|
||||
|
||||
#include <grlib/grlib_impl.h>
|
||||
|
||||
/* Determines which PCI address the AHB masters will access, it should be
|
||||
* set so that the masters can access the CPU RAM. Default is base of CPU RAM,
|
||||
* CPU RAM is mapped 1:1 to PCI space.
|
||||
*/
|
||||
extern unsigned int _RAM_START;
|
||||
#define AHBMST2PCIADR (((unsigned int)&_RAM_START) & 0xf0000000)
|
||||
|
||||
/* Offset from 0x80000000 (dual bus version) */
|
||||
#define AHB1_BASE_ADDR 0x80000000
|
||||
#define AHB1_IOAREA_BASE_ADDR 0x80100000
|
||||
#define AHB1_IOAREA_OFS (AHB1_IOAREA_BASE_ADDR - AHB1_BASE_ADDR)
|
||||
|
||||
/* Second revision constants (GRPCI2) */
|
||||
#define GRPCI2_BAR0_TO_AHB_MAP 0x04 /* Fixme */
|
||||
#define GRPCI2_BAR1_TO_AHB_MAP 0x08 /* Fixme */
|
||||
#define GRPCI2_PCI_CONFIG 0x20 /* Fixme */
|
||||
|
||||
|
||||
/* #define DEBUG 1 */
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(x...) printk(x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
/* PCI ID */
|
||||
#define PCIID_VENDOR_GAISLER 0x1AC8
|
||||
|
||||
int gr_rasta_io_init1(struct drvmgr_dev *dev);
|
||||
int gr_rasta_io_init2(struct drvmgr_dev *dev);
|
||||
void gr_rasta_io_isr (void *arg);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
struct grpci2_regs {
|
||||
volatile unsigned int ctrl;
|
||||
volatile unsigned int statcap;
|
||||
volatile unsigned int pcimstprefetch;
|
||||
volatile unsigned int ahbtopciiomap;
|
||||
volatile unsigned int dmactrl;
|
||||
volatile unsigned int dmadesc;
|
||||
volatile unsigned int dmachanact;
|
||||
volatile unsigned int reserved;
|
||||
volatile unsigned int pcibartoahb[6];
|
||||
volatile unsigned int reserved2[2];
|
||||
volatile unsigned int ahbtopcimemmap[16];
|
||||
volatile unsigned int trcctrl;
|
||||
volatile unsigned int trccntmode;
|
||||
volatile unsigned int trcadpat;
|
||||
volatile unsigned int trcadmask;
|
||||
volatile unsigned int trcctrlsigpat;
|
||||
volatile unsigned int trcctrlsigmask;
|
||||
volatile unsigned int trcadstate;
|
||||
volatile unsigned int trcctrlsigstate;
|
||||
};
|
||||
|
||||
struct gr_rasta_io_ver {
|
||||
const unsigned int amba_freq_hz; /* The frequency */
|
||||
const unsigned int amba_ioarea; /* The address where the PnP IOAREA starts at */
|
||||
};
|
||||
|
||||
/* Private data structure for driver */
|
||||
struct gr_rasta_io_priv {
|
||||
/* Driver management */
|
||||
struct drvmgr_dev *dev;
|
||||
char prefix[16];
|
||||
SPIN_DECLARE(devlock);
|
||||
|
||||
/* PCI */
|
||||
pci_dev_t pcidev;
|
||||
struct pci_dev_info *devinfo;
|
||||
uint32_t ahbmst2pci_map;
|
||||
|
||||
/* IRQ */
|
||||
genirq_t genirq;
|
||||
|
||||
/* GR-RASTA-IO */
|
||||
struct gr_rasta_io_ver *version;
|
||||
struct irqmp_regs *irq;
|
||||
struct grpci_regs *grpci;
|
||||
struct grpci2_regs *grpci2;
|
||||
struct drvmgr_map_entry bus_maps_down[3];
|
||||
struct drvmgr_map_entry bus_maps_up[2];
|
||||
|
||||
/* AMBA Plug&Play information on GR-RASTA-IO */
|
||||
struct ambapp_bus abus;
|
||||
struct ambapp_mmap amba_maps[4];
|
||||
struct ambapp_config config;
|
||||
};
|
||||
|
||||
struct gr_rasta_io_ver gr_rasta_io_ver0 = {
|
||||
.amba_freq_hz = 30000000,
|
||||
.amba_ioarea = 0x80100000,
|
||||
};
|
||||
|
||||
struct gr_rasta_io_ver gr_rasta_io_ver1 = {
|
||||
.amba_freq_hz = 50000000,
|
||||
.amba_ioarea = 0x80100000,
|
||||
};
|
||||
|
||||
int ambapp_rasta_io_int_register(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
const char *info,
|
||||
drvmgr_isr handler,
|
||||
void *arg);
|
||||
int ambapp_rasta_io_int_unregister(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
drvmgr_isr handler,
|
||||
void *arg);
|
||||
int ambapp_rasta_io_int_unmask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_rasta_io_int_mask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_rasta_io_int_clear(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_rasta_io_get_params(
|
||||
struct drvmgr_dev *dev,
|
||||
struct drvmgr_bus_params *params);
|
||||
|
||||
struct ambapp_ops ambapp_rasta_io_ops = {
|
||||
.int_register = ambapp_rasta_io_int_register,
|
||||
.int_unregister = ambapp_rasta_io_int_unregister,
|
||||
.int_unmask = ambapp_rasta_io_int_unmask,
|
||||
.int_mask = ambapp_rasta_io_int_mask,
|
||||
.int_clear = ambapp_rasta_io_int_clear,
|
||||
.get_params = ambapp_rasta_io_get_params
|
||||
};
|
||||
|
||||
struct drvmgr_drv_ops gr_rasta_io_ops =
|
||||
{
|
||||
.init = {gr_rasta_io_init1, gr_rasta_io_init2, NULL, NULL},
|
||||
.remove = NULL,
|
||||
.info = NULL
|
||||
};
|
||||
|
||||
struct pci_dev_id_match gr_rasta_io_ids[] =
|
||||
{
|
||||
PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_RASTA_IO),
|
||||
PCIID_DEVVEND(PCIID_VENDOR_GAISLER_OLD, PCIID_DEVICE_GR_RASTA_IO_OLD),
|
||||
PCIID_END_TABLE /* Mark end of table */
|
||||
};
|
||||
|
||||
struct pci_drv_info gr_rasta_io_info =
|
||||
{
|
||||
{
|
||||
DRVMGR_OBJ_DRV, /* Driver */
|
||||
NULL, /* Next driver */
|
||||
NULL, /* Device list */
|
||||
DRIVER_PCI_GAISLER_RASTAIO_ID, /* Driver ID */
|
||||
"GR-RASTA-IO_DRV", /* Driver Name */
|
||||
DRVMGR_BUS_TYPE_PCI, /* Bus Type */
|
||||
&gr_rasta_io_ops,
|
||||
NULL, /* Funcs */
|
||||
0, /* No devices yet */
|
||||
0,
|
||||
},
|
||||
&gr_rasta_io_ids[0]
|
||||
};
|
||||
|
||||
/* Driver resources configuration for the AMBA bus on the GR-RASTA-IO board.
|
||||
* It is declared weak so that the user may override it from the project file,
|
||||
* if the default settings are not enough.
|
||||
*
|
||||
* The configuration consists of an array of configuration pointers, each
|
||||
* pointer determine the configuration of one GR-RASTA-IO board. Pointer
|
||||
* zero is for board0, pointer 1 for board1 and so on.
|
||||
*
|
||||
* The array must end with a NULL pointer.
|
||||
*/
|
||||
struct drvmgr_bus_res *gr_rasta_io_resources[] __attribute__((weak)) =
|
||||
{
|
||||
NULL
|
||||
};
|
||||
|
||||
void gr_rasta_io_register_drv(void)
|
||||
{
|
||||
DBG("Registering GR-RASTA-IO PCI driver\n");
|
||||
drvmgr_drv_register(&gr_rasta_io_info.general);
|
||||
}
|
||||
|
||||
void gr_rasta_io_isr (void *arg)
|
||||
{
|
||||
struct gr_rasta_io_priv *priv = arg;
|
||||
unsigned int status, tmp;
|
||||
int irq;
|
||||
SPIN_ISR_IRQFLAGS(irqflags);
|
||||
|
||||
tmp = status = priv->irq->ipend;
|
||||
|
||||
/* DBG("GR-RASTA-IO: IRQ 0x%x\n",status); */
|
||||
|
||||
SPIN_LOCK(&priv->devlock, irqflags);
|
||||
for(irq=0; irq<16; irq++) {
|
||||
if ( status & (1<<irq) ) {
|
||||
genirq_doirq(priv->genirq, irq);
|
||||
priv->irq->iclear = (1<<irq);
|
||||
status &= ~(1<<irq);
|
||||
if ( status == 0 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
SPIN_UNLOCK(&priv->devlock, irqflags);
|
||||
|
||||
/* ACK interrupt, this is because PCI is Level, so the IRQ Controller still drives the IRQ. */
|
||||
if ( tmp )
|
||||
drvmgr_interrupt_clear(priv->dev, 0);
|
||||
|
||||
DBG("RASTA-IO-IRQ: 0x%x\n", tmp);
|
||||
}
|
||||
|
||||
/* PCI Hardware (Revision 0 and 1) initialization */
|
||||
static int gr_rasta_io_hw_init(struct gr_rasta_io_priv *priv)
|
||||
{
|
||||
unsigned int *page0 = NULL;
|
||||
struct ambapp_dev *tmp;
|
||||
struct ambapp_ahb_info *ahb;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
uint32_t bar0, bar0_size;
|
||||
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
page0 = (unsigned int *)(bar0 + bar0_size/2);
|
||||
|
||||
/* Point PAGE0 to start of Plug and Play information */
|
||||
*page0 = priv->version->amba_ioarea & 0xff000000;
|
||||
|
||||
#if 0
|
||||
{
|
||||
uint32_t data;
|
||||
/* set parity error response */
|
||||
pci_cfg_r32(priv->pcidev, PCIR_COMMAND, &data);
|
||||
pci_cfg_w32(priv->pcidev, PCIR_COMMAND, (data|PCIM_CMD_PERRESPEN));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Setup 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.
|
||||
*/
|
||||
pci_cfg_w8(priv->pcidev, PCIR_CACHELNSZ, 0xff);
|
||||
|
||||
/* Scan AMBA Plug&Play */
|
||||
|
||||
/* AMBA MAP bar0 (in CPU) ==> 0x80000000(remote amba address) */
|
||||
priv->amba_maps[0].size = bar0_size/2;
|
||||
priv->amba_maps[0].local_adr = bar0;
|
||||
priv->amba_maps[0].remote_adr = AHB1_BASE_ADDR;
|
||||
|
||||
/* AMBA MAP bar1 (in CPU) ==> 0x40000000(remote amba address) */
|
||||
priv->amba_maps[1].size = devinfo->resources[1].size;
|
||||
priv->amba_maps[1].local_adr = devinfo->resources[1].address;
|
||||
priv->amba_maps[1].remote_adr = 0x40000000;
|
||||
|
||||
/* Addresses not matching with map be untouched */
|
||||
priv->amba_maps[2].size = 0xfffffff0;
|
||||
priv->amba_maps[2].local_adr = 0;
|
||||
priv->amba_maps[2].remote_adr = 0;
|
||||
|
||||
/* Mark end of table */
|
||||
priv->amba_maps[3].size=0;
|
||||
priv->amba_maps[3].local_adr = 0;
|
||||
priv->amba_maps[3].remote_adr = 0;
|
||||
|
||||
/* Start AMBA PnP scan at first AHB bus */
|
||||
ambapp_scan(&priv->abus,
|
||||
bar0 + (priv->version->amba_ioarea & ~0xff000000),
|
||||
NULL, &priv->amba_maps[0]);
|
||||
|
||||
/* Initialize Frequency of AMBA bus */
|
||||
ambapp_freq_init(&priv->abus, NULL, priv->version->amba_freq_hz);
|
||||
|
||||
/* Point PAGE0 to start of APB area */
|
||||
*page0 = AHB1_BASE_ADDR;
|
||||
|
||||
/* Find GRPCI controller */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_PCIFBRG,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -3;
|
||||
}
|
||||
priv->grpci = (struct grpci_regs *)((struct ambapp_apb_info *)tmp->devinfo)->start;
|
||||
|
||||
/* Set GRPCI mmap so that AMBA masters can access CPU-RAM over
|
||||
* the PCI window.
|
||||
*/
|
||||
priv->grpci->cfg_stat = (priv->grpci->cfg_stat & 0x0fffffff) |
|
||||
(priv->ahbmst2pci_map & 0xf0000000);
|
||||
priv->grpci->page1 = 0x40000000;
|
||||
|
||||
/* Find IRQ controller, Clear all current IRQs */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_IRQMP,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -4;
|
||||
}
|
||||
priv->irq = (struct irqmp_regs *)DEV_TO_APB(tmp)->start;
|
||||
/* Set up GR-RASTA-IO irq controller */
|
||||
priv->irq->mask[0] = 0;
|
||||
priv->irq->iclear = 0xffff;
|
||||
priv->irq->ilevel = 0;
|
||||
|
||||
/* DOWN streams translation table */
|
||||
priv->bus_maps_down[0].name = "PCI BAR0 -> AMBA";
|
||||
priv->bus_maps_down[0].size = priv->amba_maps[0].size;
|
||||
priv->bus_maps_down[0].from_adr = (void *)priv->amba_maps[0].local_adr;
|
||||
priv->bus_maps_down[0].to_adr = (void *)priv->amba_maps[0].remote_adr;
|
||||
|
||||
priv->bus_maps_down[1].name = "PCI BAR1 -> AMBA";
|
||||
priv->bus_maps_down[1].size = priv->amba_maps[1].size;
|
||||
priv->bus_maps_down[1].from_adr = (void *)priv->amba_maps[1].local_adr;
|
||||
priv->bus_maps_down[1].to_adr = (void *)priv->amba_maps[1].remote_adr;
|
||||
|
||||
/* Mark end of translation table */
|
||||
priv->bus_maps_down[2].size = 0;
|
||||
|
||||
/* Find GRPCI controller AHB Slave interface */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_AHB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_PCIFBRG,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -5;
|
||||
}
|
||||
ahb = (struct ambapp_ahb_info *)tmp->devinfo;
|
||||
|
||||
/* UP streams translation table */
|
||||
priv->bus_maps_up[0].name = "AMBA GRPCI Window";
|
||||
priv->bus_maps_up[0].size = ahb->mask[0]; /* AMBA->PCI Window on GR-RASTA-IO board */
|
||||
priv->bus_maps_up[0].from_adr = (void *)ahb->start[0];
|
||||
priv->bus_maps_up[0].to_adr = (void *)
|
||||
(priv->ahbmst2pci_map & 0xf0000000);
|
||||
|
||||
/* Mark end of translation table */
|
||||
priv->bus_maps_up[1].size = 0;
|
||||
|
||||
/* Successfully registered the RASTA board */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PCI Hardware (Revision 1) initialization */
|
||||
static int gr_rasta_io2_hw_init(struct gr_rasta_io_priv *priv)
|
||||
{
|
||||
int i;
|
||||
uint32_t data;
|
||||
unsigned int ctrl;
|
||||
uint8_t tmp2;
|
||||
struct ambapp_dev *tmp;
|
||||
struct ambapp_ahb_info *ahb;
|
||||
uint8_t cap_ptr;
|
||||
pci_dev_t pcidev = priv->pcidev;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
|
||||
/* Check capabilities list bit */
|
||||
pci_cfg_r8(pcidev, PCIR_STATUS, &tmp2);
|
||||
|
||||
if (!((tmp2 >> 4) & 1)) {
|
||||
/* Capabilities list not available which it should be in the
|
||||
* GRPCI2
|
||||
*/
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Read capabilities pointer */
|
||||
pci_cfg_r8(pcidev, PCIR_CAP_PTR, &cap_ptr);
|
||||
|
||||
/* Set AHB address mappings for target PCI bars
|
||||
* BAR0: 16MB : Mapped to I/O at 0x80000000
|
||||
* BAR1: 256MB : Mapped to MEM at 0x40000000
|
||||
*/
|
||||
pci_cfg_w32(pcidev, cap_ptr+GRPCI2_BAR0_TO_AHB_MAP, AHB1_BASE_ADDR);
|
||||
pci_cfg_w32(pcidev, cap_ptr+GRPCI2_BAR1_TO_AHB_MAP, 0x40000000);
|
||||
|
||||
/* Set PCI bus to be same endianess as PCI system */
|
||||
pci_cfg_r32(pcidev, cap_ptr+GRPCI2_PCI_CONFIG, &data);
|
||||
if (pci_endian == PCI_BIG_ENDIAN)
|
||||
data = data & 0xFFFFFFFE;
|
||||
else
|
||||
data = data | 0x00000001;
|
||||
pci_cfg_w32(pcidev, cap_ptr+GRPCI2_PCI_CONFIG, data);
|
||||
|
||||
#if 0
|
||||
/* set parity error response */
|
||||
pci_cfg_r32(pcidev, PCIR_COMMAND, &data);
|
||||
pci_cfg_w32(pcidev, PCIR_COMMAND, (data|PCIM_CMD_PERRESPEN));
|
||||
#endif
|
||||
|
||||
/* Scan AMBA Plug&Play */
|
||||
|
||||
/* AMBA MAP bar0 (in PCI) ==> 0x40000000 (remote amba address) */
|
||||
priv->amba_maps[0].size = devinfo->resources[0].size;
|
||||
priv->amba_maps[0].local_adr = devinfo->resources[0].address;
|
||||
priv->amba_maps[0].remote_adr = AHB1_BASE_ADDR;
|
||||
|
||||
/* AMBA MAP bar0 (in PCI) ==> 0x80000000 (remote amba address) */
|
||||
priv->amba_maps[1].size = devinfo->resources[1].size;
|
||||
priv->amba_maps[1].local_adr = devinfo->resources[1].address;
|
||||
priv->amba_maps[1].remote_adr = 0x40000000;
|
||||
|
||||
/* Addresses not matching with map be untouched */
|
||||
priv->amba_maps[2].size = 0xfffffff0;
|
||||
priv->amba_maps[2].local_adr = 0;
|
||||
priv->amba_maps[2].remote_adr = 0;
|
||||
|
||||
/* Mark end of table */
|
||||
priv->amba_maps[3].size=0;
|
||||
|
||||
/* Start AMBA PnP scan at first AHB bus */
|
||||
ambapp_scan(
|
||||
&priv->abus,
|
||||
devinfo->resources[0].address + AHB1_IOAREA_OFS,
|
||||
NULL,
|
||||
&priv->amba_maps[0]);
|
||||
|
||||
/* Initialize Frequency of AMBA bus. The AMBA bus runs at same
|
||||
* frequency as PCI bus
|
||||
*/
|
||||
ambapp_freq_init(&priv->abus, NULL, priv->version->amba_freq_hz);
|
||||
|
||||
/* Find IRQ controller, Clear all current IRQs */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_IRQMP,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -4;
|
||||
}
|
||||
priv->irq = (struct irqmp_regs *)DEV_TO_APB(tmp)->start;
|
||||
/* Set up GR-RASTA-SPW-ROUTER irq controller */
|
||||
priv->irq->mask[0] = 0;
|
||||
priv->irq->iclear = 0xffff;
|
||||
priv->irq->ilevel = 0;
|
||||
|
||||
priv->bus_maps_down[0].name = "PCI BAR0 -> AMBA";
|
||||
priv->bus_maps_down[0].size = priv->amba_maps[0].size;
|
||||
priv->bus_maps_down[0].from_adr = (void *)priv->amba_maps[0].local_adr;
|
||||
priv->bus_maps_down[0].to_adr = (void *)priv->amba_maps[0].remote_adr;
|
||||
priv->bus_maps_down[1].name = "PCI BAR1 -> AMBA";
|
||||
priv->bus_maps_down[1].size = priv->amba_maps[1].size;
|
||||
priv->bus_maps_down[1].from_adr = (void *)priv->amba_maps[1].local_adr;
|
||||
priv->bus_maps_down[1].to_adr = (void *)priv->amba_maps[1].remote_adr;
|
||||
priv->bus_maps_down[2].size = 0;
|
||||
|
||||
/* Find GRPCI2 controller AHB Slave interface */
|
||||
tmp = (void *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_AHB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_GRPCI2,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -5;
|
||||
}
|
||||
ahb = (struct ambapp_ahb_info *)tmp->devinfo;
|
||||
priv->bus_maps_up[0].name = "AMBA GRPCI2 Window";
|
||||
priv->bus_maps_up[0].size = ahb->mask[0]; /* AMBA->PCI Window on GR-RASTA-SPW-ROUTER board */
|
||||
priv->bus_maps_up[0].from_adr = (void *)ahb->start[0];
|
||||
priv->bus_maps_up[0].to_adr = (void *)
|
||||
(priv->ahbmst2pci_map & ~(ahb->mask[0]-1));
|
||||
priv->bus_maps_up[1].size = 0;
|
||||
|
||||
/* Find GRPCI2 controller APB Slave interface */
|
||||
tmp = (void *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_GRPCI2,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -6;
|
||||
}
|
||||
priv->grpci2 = (struct grpci2_regs *)
|
||||
((struct ambapp_apb_info *)tmp->devinfo)->start;
|
||||
|
||||
/* Set AHB to PCI mapping for all AMBA AHB masters */
|
||||
for(i = 0; i < 16; i++) {
|
||||
priv->grpci2->ahbtopcimemmap[i] = priv->ahbmst2pci_map &
|
||||
~(ahb->mask[0]-1);
|
||||
}
|
||||
|
||||
/* Make sure dirq(0) sampling is enabled */
|
||||
ctrl = priv->grpci2->ctrl;
|
||||
ctrl = (ctrl & 0xFFFFFF0F) | (1 << 4);
|
||||
priv->grpci2->ctrl = ctrl;
|
||||
|
||||
/* Successfully registered the RASTA-SPW-ROUTER board */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gr_rasta_io_hw_init2(struct gr_rasta_io_priv *priv)
|
||||
{
|
||||
/* Enable DMA by enabling PCI target as master */
|
||||
pci_master_enable(priv->pcidev);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
/* Called when a PCI target is found with the PCI device and vendor ID
|
||||
* given in gr_rasta_io_ids[].
|
||||
*/
|
||||
int gr_rasta_io_init1(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct gr_rasta_io_priv *priv;
|
||||
struct pci_dev_info *devinfo;
|
||||
int status;
|
||||
uint32_t bar0, bar1, bar0_size, bar1_size;
|
||||
union drvmgr_key_value *value;
|
||||
int resources_cnt;
|
||||
|
||||
priv = grlib_calloc(1, sizeof(*priv));
|
||||
if ( !priv )
|
||||
return DRVMGR_NOMEM;
|
||||
|
||||
dev->priv = priv;
|
||||
priv->dev = dev;
|
||||
|
||||
/* Determine number of configurations */
|
||||
resources_cnt = get_resarray_count(gr_rasta_io_resources);
|
||||
|
||||
/* Generate Device prefix */
|
||||
|
||||
strcpy(priv->prefix, "/dev/rastaio0");
|
||||
priv->prefix[12] += dev->minor_drv;
|
||||
mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
priv->prefix[13] = '/';
|
||||
priv->prefix[14] = '\0';
|
||||
|
||||
priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo;
|
||||
priv->pcidev = devinfo->pcidev;
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
bar1 = devinfo->resources[1].address;
|
||||
bar1_size = devinfo->resources[1].size;
|
||||
printk("\n\n--- GR-RASTA-IO[%d] ---\n", dev->minor_drv);
|
||||
printk(" PCI BUS: 0x%x, SLOT: 0x%x, FUNCTION: 0x%x\n",
|
||||
PCI_DEV_EXPAND(priv->pcidev));
|
||||
printk(" PCI VENDOR: 0x%04x, DEVICE: 0x%04x\n",
|
||||
devinfo->id.vendor, devinfo->id.device);
|
||||
printk(" PCI BAR[0]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar0, bar0 + bar0_size - 1);
|
||||
printk(" PCI BAR[1]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar1, bar1 + bar1_size - 1);
|
||||
printk(" IRQ: %d\n\n\n", devinfo->irq);
|
||||
|
||||
/* all neccessary space assigned to GR-RASTA-IO target? */
|
||||
if ((bar0_size == 0) || (bar1_size == 0))
|
||||
return DRVMGR_ENORES;
|
||||
|
||||
/* Initialize spin-lock for this PCI peripheral device. This is to
|
||||
* protect the Interrupt Controller Registers. The genirq layer is
|
||||
* protecting its own internals and ISR dispatching.
|
||||
*/
|
||||
SPIN_INIT(&priv->devlock, priv->prefix);
|
||||
|
||||
/* Let user override which PCI address the AHB masters of the
|
||||
* GR-RASTA-IO board access when doing DMA to CPU RAM. The AHB masters
|
||||
* access the PCI Window of the AMBA bus, the MSB 4-bits of that address
|
||||
* is translated according this config option before the address
|
||||
* goes out on the PCI bus.
|
||||
* Only the 4 MSB bits have an effect;
|
||||
*/
|
||||
value = drvmgr_dev_key_get(priv->dev, "ahbmst2pci", DRVMGR_KT_INT);
|
||||
if (value)
|
||||
priv->ahbmst2pci_map = value->i;
|
||||
else
|
||||
priv->ahbmst2pci_map = AHBMST2PCIADR; /* default */
|
||||
|
||||
priv->genirq = genirq_init(16);
|
||||
if ( priv->genirq == NULL ) {
|
||||
free(priv);
|
||||
dev->priv = NULL;
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
/* Select version of GR-RASTA-IO board */
|
||||
switch (devinfo->rev) {
|
||||
case 0:
|
||||
priv->version = &gr_rasta_io_ver0;
|
||||
status = gr_rasta_io_hw_init(priv);
|
||||
break;
|
||||
case 1:
|
||||
priv->version = &gr_rasta_io_ver1;
|
||||
status = gr_rasta_io_hw_init(priv);
|
||||
break;
|
||||
case 2:
|
||||
priv->version = &gr_rasta_io_ver1; /* same cfg as 1 */
|
||||
status = gr_rasta_io2_hw_init(priv);
|
||||
break;
|
||||
default:
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ( status != 0 ) {
|
||||
genirq_destroy(priv->genirq);
|
||||
free(priv);
|
||||
dev->priv = NULL;
|
||||
printk(" Failed to initialize GR-RASTA-IO HW: %d\n", status);
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
/* Init amba bus */
|
||||
priv->config.abus = &priv->abus;
|
||||
priv->config.ops = &ambapp_rasta_io_ops;
|
||||
priv->config.maps_up = &priv->bus_maps_up[0];
|
||||
priv->config.maps_down = &priv->bus_maps_down[0];
|
||||
if ( priv->dev->minor_drv < resources_cnt ) {
|
||||
priv->config.resources = gr_rasta_io_resources[priv->dev->minor_drv];
|
||||
} else {
|
||||
priv->config.resources = NULL;
|
||||
}
|
||||
|
||||
/* Create and register AMBA PnP bus. */
|
||||
return ambapp_bus_register(dev, &priv->config);
|
||||
}
|
||||
|
||||
int gr_rasta_io_init2(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct gr_rasta_io_priv *priv = dev->priv;
|
||||
|
||||
/* Clear any old interrupt requests */
|
||||
drvmgr_interrupt_clear(dev, 0);
|
||||
|
||||
/* Enable System IRQ so that GR-RASTA-IO PCI target interrupt goes
|
||||
* through.
|
||||
*
|
||||
* It is important to enable it in stage init2. If interrupts were
|
||||
* enabled in init1 this might hang the system when more than one
|
||||
* PCI board is connected, this is because PCI interrupts might
|
||||
* be shared and PCI board 2 have not initialized and
|
||||
* might therefore drive interrupt already when entering init1().
|
||||
*/
|
||||
drvmgr_interrupt_register(
|
||||
dev,
|
||||
0,
|
||||
"gr_rasta_io",
|
||||
gr_rasta_io_isr,
|
||||
(void *)priv);
|
||||
|
||||
return gr_rasta_io_hw_init2(priv);
|
||||
}
|
||||
|
||||
int ambapp_rasta_io_int_register(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
const char *info,
|
||||
drvmgr_isr handler,
|
||||
void *arg)
|
||||
{
|
||||
struct gr_rasta_io_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
int status;
|
||||
void *h;
|
||||
|
||||
h = genirq_alloc_handler(handler, arg);
|
||||
if ( h == NULL )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
status = genirq_register(priv->genirq, irq, h);
|
||||
if ( status == 0 ) {
|
||||
/* Clear IRQ for first registered handler */
|
||||
priv->irq->iclear = (1<<irq);
|
||||
} else if ( status == 1 )
|
||||
status = 0;
|
||||
|
||||
if (status != 0) {
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
genirq_free_handler(h);
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
status = genirq_enable(priv->genirq, irq, handler, arg);
|
||||
if ( status == 0 ) {
|
||||
/* Enable IRQ for first enabled handler only */
|
||||
priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */
|
||||
} else if ( status == 1 )
|
||||
status = 0;
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int ambapp_rasta_io_int_unregister(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
drvmgr_isr isr,
|
||||
void *arg)
|
||||
{
|
||||
struct gr_rasta_io_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
int status;
|
||||
void *handler;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
status = genirq_disable(priv->genirq, irq, isr, arg);
|
||||
if ( status == 0 ) {
|
||||
/* Disable IRQ only when no enabled handler exists */
|
||||
priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */
|
||||
}
|
||||
|
||||
handler = genirq_unregister(priv->genirq, irq, isr, arg);
|
||||
if ( handler == NULL )
|
||||
status = DRVMGR_FAIL;
|
||||
else
|
||||
status = DRVMGR_OK;
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
if (handler)
|
||||
genirq_free_handler(handler);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int ambapp_rasta_io_int_unmask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_rasta_io_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
|
||||
DBG("RASTA-IO IRQ %d: unmask\n", irq);
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_EINVAL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
/* Enable IRQ for first enabled handler only */
|
||||
priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_io_int_mask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_rasta_io_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
|
||||
DBG("RASTA-IO IRQ %d: mask\n", irq);
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_EINVAL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
/* Disable/mask IRQ */
|
||||
priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_io_int_clear(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_rasta_io_priv *priv = dev->parent->dev->priv;
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_EINVAL;
|
||||
|
||||
priv->irq->iclear = (1<<irq);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_io_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params)
|
||||
{
|
||||
struct gr_rasta_io_priv *priv = dev->parent->dev->priv;
|
||||
|
||||
/* Device name prefix pointer, skip /dev */
|
||||
params->dev_prefix = &priv->prefix[5];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gr_rasta_io_print_dev(struct drvmgr_dev *dev, int options)
|
||||
{
|
||||
struct gr_rasta_io_priv *priv = dev->priv;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
uint32_t bar0, bar1, bar0_size, bar1_size;
|
||||
|
||||
/* Print */
|
||||
printf("--- GR-RASTA-IO [bus 0x%x, dev 0x%x, fun 0x%x] ---\n",
|
||||
PCI_DEV_EXPAND(priv->pcidev));
|
||||
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
bar1 = devinfo->resources[1].address;
|
||||
bar1_size = devinfo->resources[1].size;
|
||||
|
||||
printf(" PCI BAR[0]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar0, bar0 + bar0_size - 1);
|
||||
printf(" PCI BAR[1]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar1, bar1 + bar1_size - 1);
|
||||
printf(" IRQ REGS: 0x%" PRIxPTR "\n", (uintptr_t)priv->irq);
|
||||
printf(" IRQ: %d\n", devinfo->irq);
|
||||
printf(" PCI REVISION: %d\n", devinfo->rev);
|
||||
printf(" FREQ: %d Hz\n", priv->version->amba_freq_hz);
|
||||
printf(" IMASK: 0x%08x\n", priv->irq->mask[0]);
|
||||
printf(" IPEND: 0x%08x\n", priv->irq->ipend);
|
||||
|
||||
/* Print amba config */
|
||||
if ( options & RASTA_IO_OPTIONS_AMBA ) {
|
||||
ambapp_print(&priv->abus, 10);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Print IRQ handlers and their arguments */
|
||||
if ( options & RASTA_IO_OPTIONS_IRQ ) {
|
||||
int i;
|
||||
for(i=0; i<16; i++) {
|
||||
printf(" IRQ[%02d]: 0x%x, arg: 0x%x\n",
|
||||
i, (unsigned int)priv->isrs[i].handler, (unsigned int)priv->isrs[i].arg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gr_rasta_io_print(int options)
|
||||
{
|
||||
struct pci_drv_info *drv = &gr_rasta_io_info;
|
||||
struct drvmgr_dev *dev;
|
||||
|
||||
dev = drv->general.dev;
|
||||
while(dev) {
|
||||
gr_rasta_io_print_dev(dev, options);
|
||||
dev = dev->next_in_drv;
|
||||
}
|
||||
}
|
||||
@@ -1,696 +0,0 @@
|
||||
/* GR-RASTA-SPW-ROUTER PCI Target 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 GR-RASTA-SPW-ROUTER interface PCI board.
|
||||
* This driver provides a AMBA PnP bus by using the general part
|
||||
* of the AMBA PnP bus driver (ambapp_bus.c). Based on the
|
||||
* GR-RASTA-IO driver.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <bsp.h>
|
||||
#include <rtems/bspIo.h>
|
||||
#include <pci.h>
|
||||
|
||||
#include <grlib/ambapp.h>
|
||||
#include <grlib/grlib.h>
|
||||
#include <drvmgr/drvmgr.h>
|
||||
#include <grlib/ambapp_bus.h>
|
||||
#include <drvmgr/pci_bus.h>
|
||||
#include <grlib/bspcommon.h>
|
||||
#include <grlib/genirq.h>
|
||||
#include <grlib/gr_rasta_spw_router.h>
|
||||
|
||||
#include <grlib/grlib_impl.h>
|
||||
|
||||
/* Determines which PCI address the AHB masters will access, it should be
|
||||
* set so that the masters can access the CPU RAM. Default is base of CPU RAM,
|
||||
* CPU RAM is mapped 1:1 to PCI space.
|
||||
*/
|
||||
extern unsigned int _RAM_START;
|
||||
#define AHBMST2PCIADR (((unsigned int)&_RAM_START) & 0xf0000000)
|
||||
|
||||
/* Offset from 0x80000000 (dual bus version) */
|
||||
#define AHB1_BASE_ADDR 0x80000000
|
||||
#define AHB1_IOAREA_BASE_ADDR 0x80100000
|
||||
|
||||
#define GRPCI2_BAR0_TO_AHB_MAP 0x04 /* Fixme */
|
||||
#define GRPCI2_PCI_CONFIG 0x20 /* Fixme */
|
||||
|
||||
/* #define DEBUG 1 */
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(x...) printk(x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
/* PCI ID */
|
||||
#define PCIID_VENDOR_GAISLER 0x1AC8
|
||||
|
||||
int gr_rasta_spw_router_init1(struct drvmgr_dev *dev);
|
||||
int gr_rasta_spw_router_init2(struct drvmgr_dev *dev);
|
||||
void gr_rasta_spw_router_isr(void *arg);
|
||||
|
||||
struct grpci2_regs {
|
||||
volatile unsigned int ctrl;
|
||||
volatile unsigned int statcap;
|
||||
volatile unsigned int pcimstprefetch;
|
||||
volatile unsigned int ahbtopciiomap;
|
||||
volatile unsigned int dmactrl;
|
||||
volatile unsigned int dmadesc;
|
||||
volatile unsigned int dmachanact;
|
||||
volatile unsigned int reserved;
|
||||
volatile unsigned int pcibartoahb[6];
|
||||
volatile unsigned int reserved2[2];
|
||||
volatile unsigned int ahbtopcimemmap[16];
|
||||
volatile unsigned int trcctrl;
|
||||
volatile unsigned int trccntmode;
|
||||
volatile unsigned int trcadpat;
|
||||
volatile unsigned int trcadmask;
|
||||
volatile unsigned int trcctrlsigpat;
|
||||
volatile unsigned int trcctrlsigmask;
|
||||
volatile unsigned int trcadstate;
|
||||
volatile unsigned int trcctrlsigstate;
|
||||
};
|
||||
|
||||
struct gr_rasta_spw_router_ver {
|
||||
const unsigned int amba_freq_hz; /* The frequency */
|
||||
const unsigned int amba_ioarea; /* The address where the PnP IOAREA starts at */
|
||||
};
|
||||
|
||||
/* Private data structure for driver */
|
||||
struct gr_rasta_spw_router_priv {
|
||||
/* Driver management */
|
||||
struct drvmgr_dev *dev;
|
||||
char prefix[20];
|
||||
SPIN_DECLARE(devlock);
|
||||
|
||||
/* PCI */
|
||||
pci_dev_t pcidev;
|
||||
struct pci_dev_info *devinfo;
|
||||
uint32_t ahbmst2pci_map;
|
||||
|
||||
/* IRQ */
|
||||
genirq_t genirq;
|
||||
|
||||
/* GR-RASTA-SPW-ROUTER */
|
||||
struct gr_rasta_spw_router_ver *version;
|
||||
struct irqmp_regs *irq;
|
||||
struct grpci2_regs *grpci2;
|
||||
struct drvmgr_map_entry bus_maps_up[2];
|
||||
struct drvmgr_map_entry bus_maps_down[2];
|
||||
|
||||
/* AMBA Plug&Play information on GR-RASTA-SPW-ROUTER */
|
||||
struct ambapp_bus abus;
|
||||
struct ambapp_mmap amba_maps[3];
|
||||
struct ambapp_config config;
|
||||
};
|
||||
|
||||
struct gr_rasta_spw_router_ver gr_rasta_spw_router_ver0 = {
|
||||
.amba_freq_hz = 50000000,
|
||||
.amba_ioarea = 0xfff00000,
|
||||
};
|
||||
|
||||
int ambapp_rasta_spw_router_int_register(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
const char *info,
|
||||
drvmgr_isr handler,
|
||||
void *arg);
|
||||
int ambapp_rasta_spw_router_int_unregister(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
drvmgr_isr handler,
|
||||
void *arg);
|
||||
int ambapp_rasta_spw_router_int_unmask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_rasta_spw_router_int_mask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_rasta_spw_router_int_clear(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_rasta_spw_router_get_params(
|
||||
struct drvmgr_dev *dev,
|
||||
struct drvmgr_bus_params *params);
|
||||
|
||||
struct ambapp_ops ambapp_rasta_spw_router_ops = {
|
||||
.int_register = ambapp_rasta_spw_router_int_register,
|
||||
.int_unregister = ambapp_rasta_spw_router_int_unregister,
|
||||
.int_unmask = ambapp_rasta_spw_router_int_unmask,
|
||||
.int_mask = ambapp_rasta_spw_router_int_mask,
|
||||
.int_clear = ambapp_rasta_spw_router_int_clear,
|
||||
.get_params = ambapp_rasta_spw_router_get_params
|
||||
};
|
||||
|
||||
struct drvmgr_drv_ops gr_rasta_spw_router_ops =
|
||||
{
|
||||
.init = {gr_rasta_spw_router_init1, gr_rasta_spw_router_init2, NULL, NULL},
|
||||
.remove = NULL,
|
||||
.info = NULL
|
||||
};
|
||||
|
||||
struct pci_dev_id_match gr_rasta_spw_router_ids[] =
|
||||
{
|
||||
PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_RASTA_SPW_RTR),
|
||||
PCIID_END_TABLE /* Mark end of table */
|
||||
};
|
||||
|
||||
struct pci_drv_info gr_rasta_spw_router_info =
|
||||
{
|
||||
{
|
||||
DRVMGR_OBJ_DRV, /* Driver */
|
||||
NULL, /* Next driver */
|
||||
NULL, /* Device list */
|
||||
DRIVER_PCI_GAISLER_RASTA_SPW_ROUTER_ID, /* Driver ID */
|
||||
"GR-RASTA-SPW_ROUTER_DRV", /* Driver Name */
|
||||
DRVMGR_BUS_TYPE_PCI, /* Bus Type */
|
||||
&gr_rasta_spw_router_ops,
|
||||
NULL, /* Funcs */
|
||||
0, /* No devices yet */
|
||||
sizeof(struct gr_rasta_spw_router_priv),
|
||||
},
|
||||
&gr_rasta_spw_router_ids[0]
|
||||
};
|
||||
|
||||
/* Driver resources configuration for the AMBA bus on the GR-RASTA-SPW-ROUTER board.
|
||||
* It is declared weak so that the user may override it from the project file,
|
||||
* if the default settings are not enough.
|
||||
*
|
||||
* The configuration consists of an array of configuration pointers, each
|
||||
* pointer determine the configuration of one GR-RASTA-SPW-ROUTER board. Pointer
|
||||
* zero is for board0, pointer 1 for board1 and so on.
|
||||
*
|
||||
* The array must end with a NULL pointer.
|
||||
*/
|
||||
struct drvmgr_bus_res *gr_rasta_spw_router_resources[] __attribute__((weak)) =
|
||||
{
|
||||
NULL
|
||||
};
|
||||
|
||||
void gr_rasta_spw_router_register_drv(void)
|
||||
{
|
||||
DBG("Registering GR-RASTA-SPW-ROUTER PCI driver\n");
|
||||
drvmgr_drv_register(&gr_rasta_spw_router_info.general);
|
||||
}
|
||||
|
||||
void gr_rasta_spw_router_isr(void *arg)
|
||||
{
|
||||
struct gr_rasta_spw_router_priv *priv = arg;
|
||||
unsigned int status, tmp;
|
||||
int irq;
|
||||
SPIN_ISR_IRQFLAGS(irqflags);
|
||||
|
||||
tmp = status = priv->irq->ipend;
|
||||
|
||||
/* DBG("GR-RASTA-SPW-ROUTER: IRQ 0x%x\n",status); */
|
||||
|
||||
SPIN_LOCK(&priv->devlock, irqflags);
|
||||
for(irq=0; irq<16; irq++) {
|
||||
if ( status & (1<<irq) ) {
|
||||
genirq_doirq(priv->genirq, irq);
|
||||
priv->irq->iclear = (1<<irq);
|
||||
status &= ~(1<<irq);
|
||||
if ( status == 0 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
SPIN_UNLOCK(&priv->devlock, irqflags);
|
||||
|
||||
/* ACK interrupt, this is because PCI is Level, so the IRQ Controller
|
||||
* still drives the IRQ
|
||||
*/
|
||||
if ( tmp )
|
||||
drvmgr_interrupt_clear(priv->dev, 0);
|
||||
|
||||
DBG("RASTA-SPW_ROUTER-IRQ: 0x%x\n", tmp);
|
||||
}
|
||||
|
||||
static int gr_rasta_spw_router_hw_init(struct gr_rasta_spw_router_priv *priv)
|
||||
{
|
||||
int i;
|
||||
uint32_t data;
|
||||
unsigned int ctrl;
|
||||
uint8_t tmp2;
|
||||
struct ambapp_dev *tmp;
|
||||
struct ambapp_ahb_info *ahb;
|
||||
uint8_t cap_ptr;
|
||||
pci_dev_t pcidev = priv->pcidev;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
|
||||
/* Select version of GR-RASTA-SPW-ROUTER board. Currently only one
|
||||
* version
|
||||
*/
|
||||
switch (devinfo->rev) {
|
||||
case 0:
|
||||
priv->version = &gr_rasta_spw_router_ver0;
|
||||
break;
|
||||
default:
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Check capabilities list bit */
|
||||
pci_cfg_r8(pcidev, PCIR_STATUS, &tmp2);
|
||||
|
||||
if (!((tmp2 >> 4) & 1)) {
|
||||
/* Capabilities list not available which it should be in the GRPCI2 */
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Read capabilities pointer */
|
||||
pci_cfg_r8(pcidev, PCIR_CAP_PTR, &cap_ptr);
|
||||
|
||||
/* Set AHB address mappings for target PCI bars */
|
||||
pci_cfg_w32(pcidev, cap_ptr+GRPCI2_BAR0_TO_AHB_MAP, 0xffe00000); /* APB bus, AHB I/O bus 2 MB */
|
||||
|
||||
/* Set PCI bus to be big endian */
|
||||
pci_cfg_r32(pcidev, cap_ptr+GRPCI2_PCI_CONFIG, &data);
|
||||
data = data & 0xFFFFFFFE;
|
||||
pci_cfg_w32(pcidev, cap_ptr+GRPCI2_PCI_CONFIG, data);
|
||||
|
||||
#if 0
|
||||
/* set parity error response */
|
||||
pci_cfg_r32(pcidev, PCIR_COMMAND, &data);
|
||||
pci_cfg_w32(pcidev, PCIR_COMMAND, (data|PCIM_CMD_PERRESPEN));
|
||||
#endif
|
||||
|
||||
/* Scan AMBA Plug&Play */
|
||||
|
||||
/* AMBA MAP bar0 (in router) ==> 0xffe00000(remote amba address) */
|
||||
priv->amba_maps[0].size = devinfo->resources[0].size;
|
||||
priv->amba_maps[0].local_adr = devinfo->resources[0].address;
|
||||
priv->amba_maps[0].remote_adr = 0xffe00000;
|
||||
|
||||
/* Addresses not matching with map be untouched */
|
||||
priv->amba_maps[1].size = 0xfffffff0;
|
||||
priv->amba_maps[1].local_adr = 0;
|
||||
priv->amba_maps[1].remote_adr = 0;
|
||||
|
||||
/* Mark end of table */
|
||||
priv->amba_maps[2].size=0;
|
||||
|
||||
/* Start AMBA PnP scan at first AHB bus */
|
||||
ambapp_scan(
|
||||
&priv->abus,
|
||||
devinfo->resources[0].address + 0x100000,
|
||||
NULL,
|
||||
&priv->amba_maps[0]);
|
||||
|
||||
/* Initialize Frequency of AMBA bus */
|
||||
ambapp_freq_init(&priv->abus, NULL, priv->version->amba_freq_hz);
|
||||
|
||||
/* Find IRQ controller, Clear all current IRQs */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_IRQMP,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -4;
|
||||
}
|
||||
priv->irq = (struct irqmp_regs *)DEV_TO_APB(tmp)->start;
|
||||
/* Set up GR-RASTA-SPW-ROUTER irq controller */
|
||||
priv->irq->mask[0] = 0;
|
||||
priv->irq->iclear = 0xffff;
|
||||
priv->irq->ilevel = 0;
|
||||
|
||||
priv->bus_maps_down[0].name = "PCI BAR0 -> AMBA";
|
||||
priv->bus_maps_down[0].size = priv->amba_maps[0].size;
|
||||
priv->bus_maps_down[0].from_adr = (void *)priv->amba_maps[0].local_adr;
|
||||
priv->bus_maps_down[0].to_adr = (void *)priv->amba_maps[0].remote_adr;
|
||||
priv->bus_maps_down[1].size = 0;
|
||||
|
||||
/* Find GRPCI2 controller AHB Slave interface */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_AHB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_GRPCI2,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -5;
|
||||
}
|
||||
ahb = (struct ambapp_ahb_info *)tmp->devinfo;
|
||||
priv->bus_maps_up[0].name = "AMBA GRPCI2 Window";
|
||||
priv->bus_maps_up[0].size = ahb->mask[0]; /* AMBA->PCI Window on GR-RASTA-SPW-ROUTER board */
|
||||
priv->bus_maps_up[0].from_adr = (void *)ahb->start[0];
|
||||
priv->bus_maps_up[0].to_adr = (void *)
|
||||
(priv->ahbmst2pci_map & ~(ahb->mask[0]-1));
|
||||
priv->bus_maps_up[1].size = 0;
|
||||
|
||||
/* Find GRPCI2 controller APB Slave interface */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_GRPCI2,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -6;
|
||||
}
|
||||
priv->grpci2 = (struct grpci2_regs *)
|
||||
((struct ambapp_apb_info *)tmp->devinfo)->start;
|
||||
|
||||
/* Set AHB to PCI mapping for all AMBA AHB masters */
|
||||
for(i = 0; i < 16; i++) {
|
||||
priv->grpci2->ahbtopcimemmap[i] = priv->ahbmst2pci_map &
|
||||
~(ahb->mask[0]-1);
|
||||
}
|
||||
|
||||
/* Make sure dirq(0) sampling is enabled */
|
||||
ctrl = priv->grpci2->ctrl;
|
||||
ctrl = (ctrl & 0xFFFFFF0F) | (1 << 4);
|
||||
priv->grpci2->ctrl = ctrl;
|
||||
|
||||
/* Successfully registered the RASTA-SPW-ROUTER board */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gr_rasta_spw_router_hw_init2(struct gr_rasta_spw_router_priv *priv)
|
||||
{
|
||||
/* Enable DMA by enabling PCI target as master */
|
||||
pci_master_enable(priv->pcidev);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
/* Called when a PCI target is found with the PCI device and vendor ID
|
||||
* given in gr_rasta_spw_router_ids[].
|
||||
*/
|
||||
int gr_rasta_spw_router_init1(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct gr_rasta_spw_router_priv *priv;
|
||||
struct pci_dev_info *devinfo;
|
||||
int status;
|
||||
uint32_t bar0, bar0_size;
|
||||
union drvmgr_key_value *value;
|
||||
int resources_cnt;
|
||||
|
||||
priv = dev->priv;
|
||||
if (!priv)
|
||||
return DRVMGR_NOMEM;
|
||||
|
||||
memset(priv, 0, sizeof(*priv));
|
||||
dev->priv = priv;
|
||||
priv->dev = dev;
|
||||
|
||||
/* Determine number of configurations */
|
||||
resources_cnt = get_resarray_count(gr_rasta_spw_router_resources);
|
||||
|
||||
/* Generate Device prefix */
|
||||
|
||||
strcpy(priv->prefix, "/dev/spwrouter0");
|
||||
priv->prefix[14] += dev->minor_drv;
|
||||
mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
priv->prefix[15] = '/';
|
||||
priv->prefix[16] = '\0';
|
||||
|
||||
priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo;
|
||||
priv->pcidev = devinfo->pcidev;
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
printk("\n\n--- GR-RASTA-SPW-ROUTER[%d] ---\n", dev->minor_drv);
|
||||
printk(" PCI BUS: 0x%x, SLOT: 0x%x, FUNCTION: 0x%x\n",
|
||||
PCI_DEV_EXPAND(priv->pcidev));
|
||||
printk(" PCI VENDOR: 0x%04x, DEVICE: 0x%04x\n",
|
||||
devinfo->id.vendor, devinfo->id.device);
|
||||
printk(" PCI BAR[0]: 0x%08" PRIx32 " - 0x%08" PRIx32 "\n",
|
||||
bar0, bar0 + bar0_size - 1);
|
||||
printk(" IRQ: %d\n\n\n", devinfo->irq);
|
||||
|
||||
/* all neccessary space assigned to GR-RASTA-SPW-ROUTER target? */
|
||||
if (bar0_size == 0)
|
||||
return DRVMGR_ENORES;
|
||||
|
||||
/* Initialize spin-lock for this PCI peripheral device. This is to
|
||||
* protect the Interrupt Controller Registers. The genirq layer is
|
||||
* protecting its own internals and ISR dispatching.
|
||||
*/
|
||||
SPIN_INIT(&priv->devlock, priv->prefix);
|
||||
|
||||
/* Let user override which PCI address the AHB masters of the
|
||||
* GR-RASTA-SPW board access when doing DMA to CPU RAM. The AHB masters
|
||||
* access the PCI Window of the AMBA bus, the MSB 4-bits of that address
|
||||
* is translated according this config option before the address
|
||||
* goes out on the PCI bus.
|
||||
* Only the 4 MSB bits have an effect;
|
||||
*/
|
||||
value = drvmgr_dev_key_get(priv->dev, "ahbmst2pci", DRVMGR_KT_INT);
|
||||
if (value)
|
||||
priv->ahbmst2pci_map = value->i;
|
||||
else
|
||||
priv->ahbmst2pci_map = AHBMST2PCIADR; /* default */
|
||||
|
||||
priv->genirq = genirq_init(16);
|
||||
if ( priv->genirq == NULL )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
if ((status = gr_rasta_spw_router_hw_init(priv)) != 0) {
|
||||
genirq_destroy(priv->genirq);
|
||||
printk(" Failed to initialize GR-RASTA-SPW-ROUTER HW: %d\n", status);
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
/* Init amba bus */
|
||||
priv->config.abus = &priv->abus;
|
||||
priv->config.ops = &ambapp_rasta_spw_router_ops;
|
||||
priv->config.maps_up = &priv->bus_maps_up[0];
|
||||
priv->config.maps_down = &priv->bus_maps_down[0];
|
||||
if ( priv->dev->minor_drv < resources_cnt ) {
|
||||
priv->config.resources = gr_rasta_spw_router_resources[priv->dev->minor_drv];
|
||||
} else {
|
||||
priv->config.resources = NULL;
|
||||
}
|
||||
|
||||
/* Create and register AMBA PnP bus. */
|
||||
return ambapp_bus_register(dev, &priv->config);
|
||||
}
|
||||
|
||||
int gr_rasta_spw_router_init2(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct gr_rasta_spw_router_priv *priv = dev->priv;
|
||||
|
||||
/* Clear any old interrupt requests */
|
||||
drvmgr_interrupt_clear(dev, 0);
|
||||
|
||||
/* Enable System IRQ so that GR-RASTA-SPW-ROUTER PCI target interrupt
|
||||
* goes through.
|
||||
*
|
||||
* It is important to enable it in stage init2. If interrupts were
|
||||
* enabled in init1 this might hang the system when more than one
|
||||
* PCI board is connected, this is because PCI interrupts might
|
||||
* be shared and PCI board 2 have not initialized and
|
||||
* might therefore drive interrupt already when entering init1().
|
||||
*/
|
||||
drvmgr_interrupt_register(
|
||||
dev,
|
||||
0,
|
||||
"gr_rasta_spw_router",
|
||||
gr_rasta_spw_router_isr,
|
||||
(void *)priv);
|
||||
|
||||
return gr_rasta_spw_router_hw_init2(priv);
|
||||
}
|
||||
|
||||
int ambapp_rasta_spw_router_int_register(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
const char *info,
|
||||
drvmgr_isr handler,
|
||||
void *arg)
|
||||
{
|
||||
struct gr_rasta_spw_router_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
int status;
|
||||
void *h;
|
||||
|
||||
h = genirq_alloc_handler(handler, arg);
|
||||
if ( h == NULL )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
status = genirq_register(priv->genirq, irq, h);
|
||||
if (status == 0) {
|
||||
/* Clear IRQ for first registered handler */
|
||||
priv->irq->iclear = (1<<irq);
|
||||
} else if (status == 1)
|
||||
status = 0;
|
||||
|
||||
if (status != 0) {
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
genirq_free_handler(h);
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
status = genirq_enable(priv->genirq, irq, handler, arg);
|
||||
if ( status == 0 ) {
|
||||
/* Enable IRQ for first enabled handler only */
|
||||
priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */
|
||||
} else if ( status == 1 )
|
||||
status = 0;
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int ambapp_rasta_spw_router_int_unregister(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
drvmgr_isr isr,
|
||||
void *arg)
|
||||
{
|
||||
struct gr_rasta_spw_router_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
int status;
|
||||
void *handler;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
status = genirq_disable(priv->genirq, irq, isr, arg);
|
||||
if ( status == 0 ) {
|
||||
/* Disable IRQ only when no enabled handler exists */
|
||||
priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */
|
||||
}
|
||||
|
||||
handler = genirq_unregister(priv->genirq, irq, isr, arg);
|
||||
if ( handler == NULL )
|
||||
status = DRVMGR_FAIL;
|
||||
else
|
||||
status = DRVMGR_OK;
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
if (handler)
|
||||
genirq_free_handler(handler);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int ambapp_rasta_spw_router_int_unmask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_rasta_spw_router_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
|
||||
DBG("RASTA-SPW-ROUTER IRQ %d: unmask\n", irq);
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_EINVAL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
/* Enable IRQ for first enabled handler only */
|
||||
priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_spw_router_int_mask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_rasta_spw_router_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
|
||||
DBG("RASTA-SPW-ROUTER IRQ %d: mask\n", irq);
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_EINVAL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
/* Disable/mask IRQ */
|
||||
priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_spw_router_int_clear(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_rasta_spw_router_priv *priv = dev->parent->dev->priv;
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_EINVAL;
|
||||
|
||||
priv->irq->iclear = (1<<irq);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_spw_router_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params)
|
||||
{
|
||||
struct gr_rasta_spw_router_priv *priv = dev->parent->dev->priv;
|
||||
|
||||
/* Device name prefix pointer, skip /dev */
|
||||
params->dev_prefix = &priv->prefix[5];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gr_rasta_spw_router_print_dev(struct drvmgr_dev *dev, int options)
|
||||
{
|
||||
struct gr_rasta_spw_router_priv *priv = dev->priv;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
uint32_t bar0, bar0_size;
|
||||
|
||||
/* Print */
|
||||
printf("--- GR-RASTA-SPW-ROUTER [bus 0x%x, dev 0x%x, fun 0x%x] ---\n",
|
||||
PCI_DEV_EXPAND(priv->pcidev));
|
||||
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
printf(" PCI BAR[0]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar0, bar0 + bar0_size - 1);
|
||||
printf(" IRQ REGS: 0x%" PRIxPTR "\n", (uintptr_t)priv->irq);
|
||||
printf(" IRQ: %d\n", devinfo->irq);
|
||||
printf(" PCI REVISION: %d\n", devinfo->rev);
|
||||
printf(" FREQ: %d Hz\n", priv->version->amba_freq_hz);
|
||||
printf(" IMASK: 0x%08x\n", priv->irq->mask[0]);
|
||||
printf(" IPEND: 0x%08x\n", priv->irq->ipend);
|
||||
|
||||
/* Print amba config */
|
||||
if (options & RASTA_SPW_ROUTER_OPTIONS_AMBA)
|
||||
ambapp_print(&priv->abus, 10);
|
||||
|
||||
#if 0
|
||||
/* Print IRQ handlers and their arguments */
|
||||
if (options & RASTA_SPW_ROUTER_OPTIONS_IRQ) {
|
||||
int i;
|
||||
for(i = 0; i < 16; i++) {
|
||||
printf(" IRQ[%02d]: 0x%x, arg: 0x%x\n",
|
||||
i, (unsigned int)priv->isrs[i].handler,
|
||||
(unsigned int)priv->isrs[i].arg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gr_rasta_spw_router_print(int options)
|
||||
{
|
||||
struct pci_drv_info *drv = &gr_rasta_spw_router_info;
|
||||
struct drvmgr_dev *dev;
|
||||
|
||||
dev = drv->general.dev;
|
||||
while(dev) {
|
||||
gr_rasta_spw_router_print_dev(dev, options);
|
||||
dev = dev->next_in_drv;
|
||||
}
|
||||
}
|
||||
@@ -1,897 +0,0 @@
|
||||
/* GR-RASTA-TMTC PCI Target driver.
|
||||
*
|
||||
* COPYRIGHT (c) 2008.
|
||||
* Cobham Gaisler AB.
|
||||
*
|
||||
* Configures the GR-RASTA-TMTC interface PCI board.
|
||||
* This driver provides a AMBA PnP bus by using the general part
|
||||
* of the AMBA PnP bus driver (ambapp_bus.c).
|
||||
*
|
||||
* Driver resources for the AMBA PnP bus provided can be set by overriding
|
||||
* the defaults by declaring gr_rasta_tmtc_resources[].
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <bsp.h>
|
||||
#include <rtems/bspIo.h>
|
||||
#include <pci.h>
|
||||
|
||||
#include <grlib/ambapp.h>
|
||||
#include <grlib/grlib.h>
|
||||
#include <drvmgr/drvmgr.h>
|
||||
#include <grlib/ambapp_bus.h>
|
||||
#include <drvmgr/pci_bus.h>
|
||||
#include <grlib/bspcommon.h>
|
||||
#include <grlib/genirq.h>
|
||||
|
||||
#include <grlib/gr_rasta_tmtc.h>
|
||||
|
||||
#include <grlib/grlib_impl.h>
|
||||
|
||||
/* Determines which PCI address the AHB masters will access, it should be
|
||||
* set so that the masters can access the CPU RAM. Default is base of CPU RAM,
|
||||
* CPU RAM is mapped 1:1 to PCI space.
|
||||
*/
|
||||
extern unsigned int _RAM_START;
|
||||
#define AHBMST2PCIADR (((unsigned int)&_RAM_START) & 0xf0000000)
|
||||
|
||||
#define GAISLER_GPIO 0x01a
|
||||
#define AHB1_BASE_ADDR 0x80000000
|
||||
#define AHB1_IOAREA_BASE_ADDR 0x80200000
|
||||
#define AHB1_IOAREA_OFS (AHB1_IOAREA_BASE_ADDR - AHB1_BASE_ADDR)
|
||||
|
||||
/* Second revision constants (GRPCI2) */
|
||||
#define GRPCI2_BAR0_TO_AHB_MAP 0x04 /* Fixme */
|
||||
#define GRPCI2_BAR1_TO_AHB_MAP 0x08 /* Fixme */
|
||||
#define GRPCI2_PCI_CONFIG 0x20 /* Fixme */
|
||||
|
||||
/* #define DEBUG 1 */
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(x...) printk(x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
int gr_rasta_tmtc_init1(struct drvmgr_dev *dev);
|
||||
int gr_rasta_tmtc_init2(struct drvmgr_dev *dev);
|
||||
void gr_rasta_tmtc_isr (void *arg);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
struct grpci2_regs {
|
||||
volatile unsigned int ctrl;
|
||||
volatile unsigned int statcap;
|
||||
volatile unsigned int pcimstprefetch;
|
||||
volatile unsigned int ahbtopciiomap;
|
||||
volatile unsigned int dmactrl;
|
||||
volatile unsigned int dmadesc;
|
||||
volatile unsigned int dmachanact;
|
||||
volatile unsigned int reserved;
|
||||
volatile unsigned int pcibartoahb[6];
|
||||
volatile unsigned int reserved2[2];
|
||||
volatile unsigned int ahbtopcimemmap[16];
|
||||
volatile unsigned int trcctrl;
|
||||
volatile unsigned int trccntmode;
|
||||
volatile unsigned int trcadpat;
|
||||
volatile unsigned int trcadmask;
|
||||
volatile unsigned int trcctrlsigpat;
|
||||
volatile unsigned int trcctrlsigmask;
|
||||
volatile unsigned int trcadstate;
|
||||
volatile unsigned int trcctrlsigstate;
|
||||
};
|
||||
|
||||
struct gr_rasta_tmtc_ver {
|
||||
const unsigned int amba_freq_hz; /* The frequency */
|
||||
const unsigned int amba_ioarea; /* The address where the PnP IOAREA starts at */
|
||||
};
|
||||
|
||||
/* Private data structure for driver */
|
||||
struct gr_rasta_tmtc_priv {
|
||||
/* Driver management */
|
||||
struct drvmgr_dev *dev;
|
||||
char prefix[20];
|
||||
SPIN_DECLARE(devlock);
|
||||
|
||||
/* PCI */
|
||||
pci_dev_t pcidev;
|
||||
struct pci_dev_info *devinfo;
|
||||
uint32_t ahbmst2pci_map;
|
||||
|
||||
/* IRQ */
|
||||
genirq_t genirq;
|
||||
|
||||
/* GR-RASTA-TMTC */
|
||||
struct gr_rasta_tmtc_ver *version;
|
||||
struct irqmp_regs *irq;
|
||||
struct grpci_regs *grpci;
|
||||
struct grpci2_regs *grpci2;
|
||||
struct grgpio_regs *gpio;
|
||||
struct drvmgr_map_entry bus_maps_down[3];
|
||||
struct drvmgr_map_entry bus_maps_up[2];
|
||||
|
||||
/* AMBA Plug&Play information on GR-RASTA-TMTC */
|
||||
struct ambapp_bus abus;
|
||||
struct ambapp_mmap amba_maps[4];
|
||||
struct ambapp_config config;
|
||||
};
|
||||
|
||||
struct gr_rasta_tmtc_ver gr_rasta_tmtc_ver0 = {
|
||||
.amba_freq_hz = 30000000,
|
||||
.amba_ioarea = AHB1_IOAREA_BASE_ADDR,
|
||||
};
|
||||
|
||||
int ambapp_rasta_tmtc_int_register(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
const char *info,
|
||||
drvmgr_isr handler,
|
||||
void *arg);
|
||||
int ambapp_rasta_tmtc_int_unregister(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
drvmgr_isr handler,
|
||||
void *arg);
|
||||
int ambapp_rasta_tmtc_int_unmask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_rasta_tmtc_int_mask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_rasta_tmtc_int_clear(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_rasta_tmtc_get_params(
|
||||
struct drvmgr_dev *dev,
|
||||
struct drvmgr_bus_params *params);
|
||||
|
||||
struct ambapp_ops ambapp_rasta_tmtc_ops = {
|
||||
.int_register = ambapp_rasta_tmtc_int_register,
|
||||
.int_unregister = ambapp_rasta_tmtc_int_unregister,
|
||||
.int_unmask = ambapp_rasta_tmtc_int_unmask,
|
||||
.int_mask = ambapp_rasta_tmtc_int_mask,
|
||||
.int_clear = ambapp_rasta_tmtc_int_clear,
|
||||
.get_params = ambapp_rasta_tmtc_get_params
|
||||
};
|
||||
|
||||
struct drvmgr_drv_ops gr_rasta_tmtc_ops =
|
||||
{
|
||||
.init = {gr_rasta_tmtc_init1, gr_rasta_tmtc_init2, NULL, NULL},
|
||||
.remove = NULL,
|
||||
.info = NULL,
|
||||
};
|
||||
|
||||
struct pci_dev_id_match gr_rasta_tmtc_ids[] =
|
||||
{
|
||||
PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_RASTA_TMTC),
|
||||
PCIID_END_TABLE /* Mark end of table */
|
||||
};
|
||||
|
||||
struct pci_drv_info gr_rasta_tmtc_info =
|
||||
{
|
||||
{
|
||||
DRVMGR_OBJ_DRV, /* Driver */
|
||||
NULL, /* Next driver */
|
||||
NULL, /* Device list */
|
||||
DRIVER_PCI_GAISLER_RASTATMTC_ID,/* Driver ID */
|
||||
"GR-RASTA-TMTC_DRV", /* Driver Name */
|
||||
DRVMGR_BUS_TYPE_PCI, /* Bus Type */
|
||||
&gr_rasta_tmtc_ops,
|
||||
NULL, /* Funcs */
|
||||
0, /* No devices yet */
|
||||
sizeof(struct gr_rasta_tmtc_priv) /* Let drvmgr alloc private */
|
||||
},
|
||||
&gr_rasta_tmtc_ids[0]
|
||||
};
|
||||
|
||||
/* Driver resources configuration for the AMBA bus on the GR-RASTA-TMTC board.
|
||||
* It is declared weak so that the user may override it from the project file,
|
||||
* if the default settings are not enough.
|
||||
*
|
||||
* The configuration consists of an array of configuration pointers, each
|
||||
* pointer determine the configuration of one GR-RASTA-TMTC board. Pointer
|
||||
* zero is for board0, pointer 1 for board1 and so on.
|
||||
*
|
||||
* The array must end with a NULL pointer.
|
||||
*/
|
||||
struct drvmgr_bus_res *gr_rasta_tmtc_resources[] __attribute__((weak)) =
|
||||
{
|
||||
NULL,
|
||||
};
|
||||
|
||||
void gr_rasta_tmtc_register_drv(void)
|
||||
{
|
||||
DBG("Registering GR-RASTA-TMTC PCI driver\n");
|
||||
drvmgr_drv_register(&gr_rasta_tmtc_info.general);
|
||||
}
|
||||
|
||||
void gr_rasta_tmtc_isr (void *arg)
|
||||
{
|
||||
struct gr_rasta_tmtc_priv *priv = arg;
|
||||
unsigned int status, tmp;
|
||||
int irq;
|
||||
SPIN_ISR_IRQFLAGS(irqflags);
|
||||
|
||||
tmp = status = priv->irq->ipend;
|
||||
|
||||
/* printk("GR-RASTA-TMTC: IRQ 0x%x\n",status); */
|
||||
|
||||
SPIN_LOCK(&priv->devlock, irqflags);
|
||||
for(irq=0; irq<32; irq++) {
|
||||
if ( status & (1<<irq) ) {
|
||||
genirq_doirq(priv->genirq, irq);
|
||||
priv->irq->iclear = (1<<irq);
|
||||
status &= ~(1<<irq);
|
||||
if ( status == 0 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
SPIN_UNLOCK(&priv->devlock, irqflags);
|
||||
|
||||
/* ACK interrupt, this is because PCI is Level, so the IRQ Controller still drives the IRQ. */
|
||||
if ( tmp )
|
||||
drvmgr_interrupt_clear(priv->dev, 0);
|
||||
|
||||
DBG("RASTA-TMTC-IRQ: 0x%x\n", tmp);
|
||||
}
|
||||
|
||||
/* Init AMBA bus frequency, IRQ controller, GPIO register, bus maps and other
|
||||
* common stuff between rev0 and rev1.
|
||||
*/
|
||||
static int gr_rasta_tmtc_hw_init_common(struct gr_rasta_tmtc_priv *priv)
|
||||
{
|
||||
struct ambapp_dev *tmp;
|
||||
unsigned int pci_freq_hz;
|
||||
|
||||
/* Initialize Frequency of AMBA bus. The AMBA bus runs at same
|
||||
* frequency as PCI bus
|
||||
*/
|
||||
drvmgr_freq_get(priv->dev, 0, &pci_freq_hz);
|
||||
ambapp_freq_init(&priv->abus, NULL, pci_freq_hz);
|
||||
|
||||
/* Find IRQ controller, Clear all current IRQs */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_IRQMP,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -4;
|
||||
}
|
||||
priv->irq = (struct irqmp_regs *)DEV_TO_APB(tmp)->start;
|
||||
/* Set up GR-RASTA-TMTC irq controller */
|
||||
priv->irq->mask[0] = 0;
|
||||
priv->irq->iclear = 0xffffffff;
|
||||
priv->irq->ilevel = 0;
|
||||
|
||||
/* Find First GPIO controller */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_GPIO,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -5;
|
||||
}
|
||||
priv->gpio = (struct grgpio_regs *) (((struct ambapp_apb_info *)tmp->devinfo)->start);
|
||||
/* Clear GR-RASTA-TMTC GPIO controller */
|
||||
priv->gpio->imask = 0;
|
||||
priv->gpio->ipol = 0;
|
||||
priv->gpio->iedge = 0;
|
||||
priv->gpio->bypass = 0;
|
||||
/* Set up GR-RASTA-TMTC GPIO controller to select GRTM and GRTC */
|
||||
priv->gpio->output = (GR_TMTC_GPIO_GRTM_SEL|GR_TMTC_GPIO_TRANSP_CLK) | (GR_TMTC_GPIO_TC_BIT_LOCK|GR_TMTC_GPIO_TC_RF_AVAIL|GR_TMTC_GPIO_TC_ACTIVE_HIGH|GR_TMTC_GPIO_TC_RISING_CLK);
|
||||
priv->gpio->dir = 0xffffffff;
|
||||
DBG("GR-TMTC GPIO: 0x%x\n", (unsigned int)priv->gpio);
|
||||
|
||||
/* DOWN streams translation table */
|
||||
priv->bus_maps_down[0].name = "PCI BAR0 -> AMBA";
|
||||
priv->bus_maps_down[0].size = priv->amba_maps[0].size;
|
||||
priv->bus_maps_down[0].from_adr = (void *)priv->amba_maps[0].local_adr;
|
||||
priv->bus_maps_down[0].to_adr = (void *)priv->amba_maps[0].remote_adr;
|
||||
|
||||
priv->bus_maps_down[1].name = "PCI BAR1 -> AMBA";
|
||||
priv->bus_maps_down[1].size = priv->amba_maps[1].size;
|
||||
priv->bus_maps_down[1].from_adr = (void *)priv->amba_maps[1].local_adr;
|
||||
priv->bus_maps_down[1].to_adr = (void *)priv->amba_maps[1].remote_adr;
|
||||
|
||||
/* Mark end of translation table */
|
||||
priv->bus_maps_down[2].size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PCI Hardware (Revision 0) initialization */
|
||||
static int gr_rasta_tmtc0_hw_init(struct gr_rasta_tmtc_priv *priv)
|
||||
{
|
||||
unsigned int *page0 = NULL;
|
||||
struct ambapp_dev *tmp;
|
||||
struct ambapp_ahb_info *ahb;
|
||||
int status;
|
||||
pci_dev_t pcidev = priv->pcidev;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
uint32_t bar0, bar0_size;
|
||||
|
||||
/* Select version of GR-RASTA-TMTC board */
|
||||
switch (devinfo->rev) {
|
||||
case 0:
|
||||
priv->version = &gr_rasta_tmtc_ver0;
|
||||
break;
|
||||
default:
|
||||
return -2;
|
||||
}
|
||||
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
page0 = (unsigned int *)(bar0 + bar0_size/2);
|
||||
|
||||
/* Point PAGE0 to start of Plug and Play information */
|
||||
*page0 = priv->version->amba_ioarea & 0xf0000000;
|
||||
|
||||
#if 0
|
||||
{
|
||||
uint32_t data;
|
||||
/* set parity error response */
|
||||
pci_cfg_r32(pcidev, PCIR_COMMAND, &data);
|
||||
pci_cfg_w32(pcidev, PCIR_COMMAND, (data|PCIM_CMD_PERRESPEN));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Setup 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.
|
||||
*/
|
||||
pci_cfg_w8(pcidev, PCIR_CACHELNSZ, 0xff);
|
||||
|
||||
/* Scan AMBA Plug&Play */
|
||||
|
||||
/* AMBA MAP bar0 (in CPU) ==> 0x80000000(remote amba address) */
|
||||
priv->amba_maps[0].size = 0x10000000;
|
||||
priv->amba_maps[0].local_adr = bar0;
|
||||
priv->amba_maps[0].remote_adr = AHB1_BASE_ADDR;
|
||||
|
||||
/* AMBA MAP bar1 (in CPU) ==> 0x40000000(remote amba address) */
|
||||
priv->amba_maps[1].size = devinfo->resources[1].size;
|
||||
priv->amba_maps[1].local_adr = devinfo->resources[1].address;
|
||||
priv->amba_maps[1].remote_adr = 0x40000000;
|
||||
|
||||
/* Addresses not matching with map be untouched */
|
||||
priv->amba_maps[2].size = 0xfffffff0;
|
||||
priv->amba_maps[2].local_adr = 0;
|
||||
priv->amba_maps[2].remote_adr = 0;
|
||||
|
||||
/* Mark end of table */
|
||||
priv->amba_maps[3].size=0;
|
||||
priv->amba_maps[3].local_adr = 0;
|
||||
priv->amba_maps[3].remote_adr = 0;
|
||||
|
||||
/* Start AMBA PnP scan at first AHB bus */
|
||||
ambapp_scan(&priv->abus,
|
||||
bar0 + (priv->version->amba_ioarea & ~0xf0000000),
|
||||
NULL, &priv->amba_maps[0]);
|
||||
|
||||
/* Point PAGE0 to start of APB area */
|
||||
*page0 = AHB1_BASE_ADDR;
|
||||
|
||||
/* Find GRPCI controller */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_PCIFBRG,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -3;
|
||||
}
|
||||
priv->grpci = (struct grpci_regs *)((struct ambapp_apb_info *)tmp->devinfo)->start;
|
||||
|
||||
/* Set GRPCI mmap so that AMBA masters can access CPU-RAM over
|
||||
* the PCI window.
|
||||
*/
|
||||
priv->grpci->cfg_stat = (priv->grpci->cfg_stat & 0x0fffffff) |
|
||||
(priv->ahbmst2pci_map & 0xf0000000);
|
||||
priv->grpci->page1 = 0x40000000;
|
||||
|
||||
/* init AMBA bus, IRQCtrl, GPIO, bus down-maps */
|
||||
status = gr_rasta_tmtc_hw_init_common(priv);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* Find GRPCI controller AHB Slave interface */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_AHB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_PCIFBRG,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -6;
|
||||
}
|
||||
ahb = (struct ambapp_ahb_info *)tmp->devinfo;
|
||||
|
||||
/* UP streams translation table */
|
||||
priv->bus_maps_up[0].name = "AMBA GRPCI Window";
|
||||
priv->bus_maps_up[0].size = ahb->mask[0]; /* AMBA->PCI Window on GR-RASTA-TMTC board */
|
||||
priv->bus_maps_up[0].from_adr = (void *)ahb->start[0];
|
||||
priv->bus_maps_up[0].to_adr = (void *)
|
||||
(priv->ahbmst2pci_map & 0xf0000000);
|
||||
|
||||
/* Mark end of translation table */
|
||||
priv->bus_maps_up[1].size = 0;
|
||||
|
||||
/* Successfully registered the RASTA board */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PCI Hardware (Revision 1) initialization */
|
||||
static int gr_rasta_tmtc1_hw_init(struct gr_rasta_tmtc_priv *priv)
|
||||
{
|
||||
int i;
|
||||
uint32_t data;
|
||||
unsigned int ctrl;
|
||||
uint8_t tmp2;
|
||||
struct ambapp_dev *tmp;
|
||||
int status;
|
||||
struct ambapp_ahb_info *ahb;
|
||||
uint8_t cap_ptr;
|
||||
pci_dev_t pcidev = priv->pcidev;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
|
||||
/* Check capabilities list bit */
|
||||
pci_cfg_r8(pcidev, PCIR_STATUS, &tmp2);
|
||||
|
||||
if (!((tmp2 >> 4) & 1)) {
|
||||
/* Capabilities list not available which it should be in the
|
||||
* GRPCI2
|
||||
*/
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Read capabilities pointer */
|
||||
pci_cfg_r8(pcidev, PCIR_CAP_PTR, &cap_ptr);
|
||||
|
||||
/* Set AHB address mappings for target PCI bars
|
||||
* BAR0: 16MB : Mapped to I/O at 0x80000000
|
||||
* BAR1: 256MB : Mapped to MEM at 0x40000000
|
||||
*/
|
||||
pci_cfg_w32(pcidev, cap_ptr+GRPCI2_BAR0_TO_AHB_MAP, AHB1_BASE_ADDR);
|
||||
pci_cfg_w32(pcidev, cap_ptr+GRPCI2_BAR1_TO_AHB_MAP, 0x40000000);
|
||||
|
||||
/* Set PCI bus to be same endianess as PCI system */
|
||||
pci_cfg_r32(pcidev, cap_ptr+GRPCI2_PCI_CONFIG, &data);
|
||||
if (pci_endian == PCI_BIG_ENDIAN)
|
||||
data = data & 0xFFFFFFFE;
|
||||
else
|
||||
data = data | 0x00000001;
|
||||
pci_cfg_w32(pcidev, cap_ptr+GRPCI2_PCI_CONFIG, data);
|
||||
|
||||
#if 0
|
||||
/* set parity error response */
|
||||
pci_cfg_r32(pcidev, PCIR_COMMAND, &data);
|
||||
pci_cfg_w32(pcidev, PCIR_COMMAND, (data|PCIM_CMD_PERRESPEN));
|
||||
#endif
|
||||
|
||||
/* Scan AMBA Plug&Play */
|
||||
|
||||
/* AMBA MAP bar0 (in PCI) ==> 0x40000000 (remote amba address) */
|
||||
priv->amba_maps[0].size = devinfo->resources[0].size;
|
||||
priv->amba_maps[0].local_adr = devinfo->resources[0].address;
|
||||
priv->amba_maps[0].remote_adr = AHB1_BASE_ADDR;
|
||||
|
||||
/* AMBA MAP bar0 (in PCI) ==> 0x80000000 (remote amba address) */
|
||||
priv->amba_maps[1].size = devinfo->resources[1].size;
|
||||
priv->amba_maps[1].local_adr = devinfo->resources[1].address;
|
||||
priv->amba_maps[1].remote_adr = 0x40000000;
|
||||
|
||||
/* Addresses not matching with map be untouched */
|
||||
priv->amba_maps[2].size = 0xfffffff0;
|
||||
priv->amba_maps[2].local_adr = 0;
|
||||
priv->amba_maps[2].remote_adr = 0;
|
||||
|
||||
/* Mark end of table */
|
||||
priv->amba_maps[3].size=0;
|
||||
|
||||
/* Start AMBA PnP scan at first AHB bus */
|
||||
ambapp_scan(
|
||||
&priv->abus,
|
||||
devinfo->resources[0].address + AHB1_IOAREA_OFS,
|
||||
NULL,
|
||||
&priv->amba_maps[0]);
|
||||
|
||||
/* init AMBA bus, IRQCtrl, GPIO, bus down-maps */
|
||||
status = gr_rasta_tmtc_hw_init_common(priv);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
/* Find GRPCI2 controller AHB Slave interface */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_AHB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_GRPCI2,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -6;
|
||||
}
|
||||
ahb = (struct ambapp_ahb_info *)tmp->devinfo;
|
||||
priv->bus_maps_up[0].name = "AMBA GRPCI2 Window";
|
||||
priv->bus_maps_up[0].size = ahb->mask[0]; /* AMBA->PCI Window on GR-RASTA-SPW-ROUTER board */
|
||||
priv->bus_maps_up[0].from_adr = (void *)ahb->start[0];
|
||||
priv->bus_maps_up[0].to_adr = (void *)
|
||||
(priv->ahbmst2pci_map & ~(ahb->mask[0]-1));
|
||||
priv->bus_maps_up[1].size = 0;
|
||||
|
||||
/* Find GRPCI2 controller APB Slave interface */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_GRPCI2,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -7;
|
||||
}
|
||||
priv->grpci2 = (struct grpci2_regs *)
|
||||
((struct ambapp_apb_info *)tmp->devinfo)->start;
|
||||
|
||||
/* Set AHB to PCI mapping for all AMBA AHB masters */
|
||||
for(i = 0; i < 16; i++) {
|
||||
priv->grpci2->ahbtopcimemmap[i] = priv->ahbmst2pci_map &
|
||||
~(ahb->mask[0]-1);
|
||||
}
|
||||
|
||||
/* Make sure dirq(0) sampling is enabled */
|
||||
ctrl = priv->grpci2->ctrl;
|
||||
ctrl = (ctrl & 0xFFFFFF0F) | (1 << 4);
|
||||
priv->grpci2->ctrl = ctrl;
|
||||
|
||||
/* Successfully registered the RASTA-SPW-ROUTER board */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gr_rasta_tmtc_hw_init2(struct gr_rasta_tmtc_priv *priv)
|
||||
{
|
||||
/* Enable DMA by enabling PCI target as master */
|
||||
pci_master_enable(priv->pcidev);
|
||||
}
|
||||
|
||||
/* Called when a PCI target is found with the PCI device and vendor ID
|
||||
* given in gr_rasta_tmtc_ids[].
|
||||
*/
|
||||
int gr_rasta_tmtc_init1(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct gr_rasta_tmtc_priv *priv;
|
||||
struct pci_dev_info *devinfo;
|
||||
int status;
|
||||
uint32_t bar0, bar1, bar0_size, bar1_size;
|
||||
union drvmgr_key_value *value;
|
||||
int resources_cnt;
|
||||
|
||||
priv = dev->priv;
|
||||
if (!priv)
|
||||
return DRVMGR_NOMEM;
|
||||
priv->dev = dev;
|
||||
|
||||
/* Determine number of configurations */
|
||||
resources_cnt = get_resarray_count(gr_rasta_tmtc_resources);
|
||||
|
||||
/* Generate Device prefix */
|
||||
|
||||
strcpy(priv->prefix, "/dev/rastatmtc0");
|
||||
priv->prefix[14] += dev->minor_drv;
|
||||
mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
priv->prefix[15] = '/';
|
||||
priv->prefix[16] = '\0';
|
||||
|
||||
priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo;
|
||||
priv->pcidev = devinfo->pcidev;
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
bar1 = devinfo->resources[1].address;
|
||||
bar1_size = devinfo->resources[1].size;
|
||||
printk("\n\n--- GR-RASTA-TMTC[%d] ---\n", dev->minor_drv);
|
||||
printk(" PCI BUS: 0x%x, SLOT: 0x%x, FUNCTION: 0x%x\n",
|
||||
PCI_DEV_EXPAND(priv->pcidev));
|
||||
printk(" PCI VENDOR: 0x%04x, DEVICE: 0x%04x\n",
|
||||
devinfo->id.vendor, devinfo->id.device);
|
||||
printk(" PCI BAR[0]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar0, bar0 + bar0_size - 1);
|
||||
printk(" PCI BAR[1]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar1, bar1 + bar1_size - 1);
|
||||
printk(" IRQ: %d\n\n\n", devinfo->irq);
|
||||
|
||||
/* all neccessary space assigned to GR-RASTA-IO target? */
|
||||
if ((bar0_size == 0) || (bar1_size == 0))
|
||||
return DRVMGR_ENORES;
|
||||
|
||||
/* Initialize spin-lock for this PCI peripheral device. This is to
|
||||
* protect the Interrupt Controller Registers. The genirq layer is
|
||||
* protecting its own internals and ISR dispatching.
|
||||
*/
|
||||
SPIN_INIT(&priv->devlock, priv->prefix);
|
||||
|
||||
/* Let user override which PCI address the AHB masters of the
|
||||
* GR-RASTA-TMTC board access when doing DMA to CPU RAM. The AHB masters
|
||||
* access the PCI Window of the AMBA bus, the MSB 4-bits of that address
|
||||
* is translated according this config option before the address
|
||||
* goes out on the PCI bus.
|
||||
* Only the 4 MSB bits have an effect;
|
||||
*/
|
||||
value = drvmgr_dev_key_get(priv->dev, "ahbmst2pci", DRVMGR_KT_INT);
|
||||
if (value)
|
||||
priv->ahbmst2pci_map = value->i;
|
||||
else
|
||||
priv->ahbmst2pci_map = AHBMST2PCIADR; /* default */
|
||||
|
||||
priv->genirq = genirq_init(32);
|
||||
if ( priv->genirq == NULL )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
/* Select version of GR-RASTA-IO board */
|
||||
switch (devinfo->rev) {
|
||||
case 0:
|
||||
puts("GR-RASTA-TMTC: REVISION 0");
|
||||
status = gr_rasta_tmtc0_hw_init(priv);
|
||||
break;
|
||||
case 1:
|
||||
puts("GR-RASTA-TMTC: REVISION 1");
|
||||
status = gr_rasta_tmtc1_hw_init(priv);
|
||||
break;
|
||||
default:
|
||||
return DRVMGR_ENOSYS; /* HW not supported */
|
||||
}
|
||||
|
||||
if ( status != 0 ) {
|
||||
genirq_destroy(priv->genirq);
|
||||
printk(" Failed to initialize GR-RASTA-TMTC HW: %d\n", status);
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
/* Init amba bus */
|
||||
priv->config.abus = &priv->abus;
|
||||
priv->config.ops = &ambapp_rasta_tmtc_ops;
|
||||
priv->config.maps_up = &priv->bus_maps_up[0];
|
||||
priv->config.maps_down = &priv->bus_maps_down[0];
|
||||
if ( priv->dev->minor_drv < resources_cnt ) {
|
||||
priv->config.resources = gr_rasta_tmtc_resources[priv->dev->minor_drv];
|
||||
} else {
|
||||
priv->config.resources = NULL;
|
||||
}
|
||||
|
||||
return ambapp_bus_register(dev, &priv->config);
|
||||
}
|
||||
|
||||
int gr_rasta_tmtc_init2(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct gr_rasta_tmtc_priv *priv = dev->priv;
|
||||
|
||||
/* Clear any old interrupt requests */
|
||||
drvmgr_interrupt_clear(priv->dev, 0);
|
||||
|
||||
/* Enable System IRQ so that GR-RASTA-TMTC PCI target interrupt goes
|
||||
* through.
|
||||
*
|
||||
* It is important to enable it in stage init2. If interrupts were
|
||||
* enabled in init1 this might hang the system when more than one
|
||||
* PCI target is connected, this is because PCI interrupts might
|
||||
* be shared and PCI board 2 have not initialized and
|
||||
* might therefore drive interrupt already when entering init1().
|
||||
*/
|
||||
drvmgr_interrupt_register(
|
||||
priv->dev,
|
||||
0,
|
||||
"gr_rasta_tmtc",
|
||||
gr_rasta_tmtc_isr,
|
||||
(void *)priv);
|
||||
|
||||
gr_rasta_tmtc_hw_init2(priv);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_tmtc_int_register(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
const char *info,
|
||||
drvmgr_isr handler,
|
||||
void *arg)
|
||||
{
|
||||
struct gr_rasta_tmtc_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
int status;
|
||||
void *h;
|
||||
|
||||
h = genirq_alloc_handler(handler, arg);
|
||||
if ( h == NULL )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
status = genirq_register(priv->genirq, irq, h);
|
||||
if ( status == 0 ) {
|
||||
/* Disable and clear IRQ for first registered handler */
|
||||
priv->irq->iclear = (1<<irq);
|
||||
} else if ( status == 1 )
|
||||
status = 0;
|
||||
|
||||
if (status != 0) {
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
genirq_free_handler(h);
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
status = genirq_enable(priv->genirq, irq, handler, arg);
|
||||
if ( status == 0 ) {
|
||||
/* Enable IRQ for first enabled handler only */
|
||||
priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */
|
||||
} else if ( status == 1 )
|
||||
status = 0;
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int ambapp_rasta_tmtc_int_unregister(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
drvmgr_isr isr,
|
||||
void *arg)
|
||||
{
|
||||
struct gr_rasta_tmtc_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
int status;
|
||||
void *handler;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
status = genirq_disable(priv->genirq, irq, isr, arg);
|
||||
if ( status == 0 ) {
|
||||
/* Disable IRQ only when no enabled handler exists */
|
||||
priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */
|
||||
} else if ( status == 1 )
|
||||
status = 0;
|
||||
|
||||
handler = genirq_unregister(priv->genirq, irq, isr, arg);
|
||||
if ( handler == NULL )
|
||||
status = DRVMGR_FAIL;
|
||||
else
|
||||
status = DRVMGR_OK;
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
if (handler)
|
||||
genirq_free_handler(handler);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int ambapp_rasta_tmtc_int_unmask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_rasta_tmtc_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
|
||||
DBG("RASTA-TMTC IRQ %d: unmask\n", irq);
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_EINVAL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
/* Enable IRQ */
|
||||
priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_tmtc_int_mask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_rasta_tmtc_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
|
||||
DBG("RASTA-TMTC IRQ %d: mask\n", irq);
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_EINVAL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
/* Disable IRQ */
|
||||
priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_tmtc_int_clear(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_rasta_tmtc_priv *priv = dev->parent->dev->priv;
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
priv->irq->iclear = (1<<irq);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_rasta_tmtc_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params)
|
||||
{
|
||||
struct gr_rasta_tmtc_priv *priv = dev->parent->dev->priv;
|
||||
|
||||
/* Device name prefix pointer, skip /dev */
|
||||
params->dev_prefix = &priv->prefix[5];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gr_rasta_tmtc_print_dev(struct drvmgr_dev *dev, int options)
|
||||
{
|
||||
struct gr_rasta_tmtc_priv *priv = dev->priv;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
uint32_t bar0, bar1, bar0_size, bar1_size;
|
||||
|
||||
/* Print */
|
||||
printf("--- GR-RASTA-TMTC [bus 0x%x, dev 0x%x, fun 0x%x] ---\n",
|
||||
PCI_DEV_EXPAND(priv->pcidev));
|
||||
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
bar1 = devinfo->resources[1].address;
|
||||
bar1_size = devinfo->resources[1].size;
|
||||
|
||||
printf(" PCI BAR[0]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar0, bar0 + bar0_size - 1);
|
||||
printf(" PCI BAR[1]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar1, bar1 + bar1_size - 1);
|
||||
printf(" IRQ: %d\n", devinfo->irq);
|
||||
printf(" PCI REVISION: %d\n", devinfo->rev);
|
||||
printf(" FREQ: %d Hz\n", priv->version->amba_freq_hz);
|
||||
printf(" IMASK: 0x%08x\n", priv->irq->mask[0]);
|
||||
printf(" IPEND: 0x%08x\n", priv->irq->ipend);
|
||||
|
||||
/* Print amba config */
|
||||
if ( options & RASTA_TMTC_OPTIONS_AMBA ) {
|
||||
ambapp_print(&priv->abus, 10);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Print IRQ handlers and their arguments */
|
||||
if ( options & RASTA_TMTC_OPTIONS_IRQ ) {
|
||||
int i;
|
||||
for(i=0; i<16; i++) {
|
||||
printf(" IRQ[%02d]: 0x%x, arg: 0x%x\n",
|
||||
i, (unsigned int)priv->isrs[i].handler, (unsigned int)priv->isrs[i].arg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gr_rasta_tmtc_print(int options)
|
||||
{
|
||||
struct pci_drv_info *drv = &gr_rasta_tmtc_info;
|
||||
struct drvmgr_dev *dev;
|
||||
|
||||
dev = drv->general.dev;
|
||||
while(dev) {
|
||||
gr_rasta_tmtc_print_dev(dev, options);
|
||||
dev = dev->next_in_drv;
|
||||
}
|
||||
}
|
||||
@@ -1,595 +0,0 @@
|
||||
/* GR-TMTC-1553 PCI Target driver.
|
||||
*
|
||||
* COPYRIGHT (c) 2008.
|
||||
* Cobham Gaisler AB.
|
||||
*
|
||||
* Configures the GR-TMTC-1553 interface PCI board.
|
||||
* This driver provides a AMBA PnP bus by using the general part
|
||||
* of the AMBA PnP bus driver (ambapp_bus.c).
|
||||
*
|
||||
* Driver resources for the AMBA PnP bus provided can be set using
|
||||
* gr_tmtc_1553_set_resources().
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <bsp.h>
|
||||
#include <rtems/bspIo.h>
|
||||
#include <pci.h>
|
||||
#include <pci/access.h>
|
||||
|
||||
#include <grlib/ambapp.h>
|
||||
#include <grlib/grlib.h>
|
||||
#include <drvmgr/drvmgr.h>
|
||||
#include <grlib/ambapp_bus.h>
|
||||
#include <drvmgr/pci_bus.h>
|
||||
#include <grlib/bspcommon.h>
|
||||
#include <grlib/genirq.h>
|
||||
|
||||
#include <grlib/gr_tmtc_1553.h>
|
||||
|
||||
#include <grlib/grlib_impl.h>
|
||||
|
||||
/*#define DEBUG 1 */
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(x...) printk(x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
/* PCI ID */
|
||||
#define PCIID_VENDOR_GAISLER 0x1AC8
|
||||
|
||||
int gr_tmtc_1553_init1(struct drvmgr_dev *dev);
|
||||
int gr_tmtc_1553_init2(struct drvmgr_dev *dev);
|
||||
void gr_tmtc_1553_isr (void *arg);
|
||||
|
||||
struct gr_tmtc_1553_ver {
|
||||
const unsigned int amba_freq_hz; /* The frequency */
|
||||
const unsigned int amba_ioarea; /* The address where the PnP IOAREA starts at */
|
||||
};
|
||||
|
||||
/* Private data structure for driver */
|
||||
struct gr_tmtc_1553_priv {
|
||||
/* Driver management */
|
||||
struct drvmgr_dev *dev;
|
||||
char prefix[32];
|
||||
SPIN_DECLARE(devlock);
|
||||
|
||||
/* PCI */
|
||||
pci_dev_t pcidev;
|
||||
struct pci_dev_info *devinfo;
|
||||
|
||||
/* IRQ */
|
||||
genirq_t genirq;
|
||||
|
||||
struct gr_tmtc_1553_ver *version;
|
||||
struct irqmp_regs *irq;
|
||||
struct drvmgr_map_entry bus_maps_down[2];
|
||||
|
||||
struct ambapp_bus abus;
|
||||
struct ambapp_mmap amba_maps[4];
|
||||
struct ambapp_config config;
|
||||
};
|
||||
|
||||
struct gr_tmtc_1553_ver gr_tmtc_1553_ver0 = {
|
||||
.amba_freq_hz = 33333333,
|
||||
.amba_ioarea = 0xfff00000,
|
||||
};
|
||||
|
||||
|
||||
int ambapp_tmtc_1553_int_register(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
const char *info,
|
||||
drvmgr_isr handler,
|
||||
void *arg);
|
||||
int ambapp_tmtc_1553_int_unregister(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
drvmgr_isr handler,
|
||||
void *arg);
|
||||
int ambapp_tmtc_1553_int_unmask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_tmtc_1553_int_mask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_tmtc_1553_int_clear(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq);
|
||||
int ambapp_tmtc_1553_get_params(
|
||||
struct drvmgr_dev *dev,
|
||||
struct drvmgr_bus_params *params);
|
||||
|
||||
struct ambapp_ops ambapp_tmtc_1553_ops = {
|
||||
.int_register = ambapp_tmtc_1553_int_register,
|
||||
.int_unregister = ambapp_tmtc_1553_int_unregister,
|
||||
.int_unmask = ambapp_tmtc_1553_int_unmask,
|
||||
.int_mask = ambapp_tmtc_1553_int_mask,
|
||||
.int_clear = ambapp_tmtc_1553_int_clear,
|
||||
.get_params = ambapp_tmtc_1553_get_params
|
||||
};
|
||||
|
||||
struct drvmgr_drv_ops gr_tmtc_1553_ops =
|
||||
{
|
||||
{gr_tmtc_1553_init1, gr_tmtc_1553_init2, NULL, NULL},
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
struct pci_dev_id_match gr_tmtc_1553_ids[] =
|
||||
{
|
||||
PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_TMTC_1553),
|
||||
PCIID_END_TABLE /* Mark end of table */
|
||||
};
|
||||
|
||||
struct pci_drv_info gr_tmtc_1553_info =
|
||||
{
|
||||
{
|
||||
DRVMGR_OBJ_DRV, /* Driver */
|
||||
NULL, /* Next driver */
|
||||
NULL, /* Device list */
|
||||
DRIVER_PCI_GAISLER_TMTC_1553_ID, /* Driver ID */
|
||||
"GR-TMTC-1553_DRV", /* Driver Name */
|
||||
DRVMGR_BUS_TYPE_PCI, /* Bus Type */
|
||||
&gr_tmtc_1553_ops,
|
||||
NULL, /* Funcs */
|
||||
0, /* No devices yet */
|
||||
0,
|
||||
},
|
||||
&gr_tmtc_1553_ids[0]
|
||||
};
|
||||
|
||||
/* Driver resources configuration for the AMBA bus on the GR-RASTA-IO board.
|
||||
* It is declared weak so that the user may override it from the project file,
|
||||
* if the default settings are not enough.
|
||||
*
|
||||
* The configuration consists of an array of configuration pointers, each
|
||||
* pointer determine the configuration of one GR-RASTA-IO board. Pointer
|
||||
* zero is for board0, pointer 1 for board1 and so on.
|
||||
*
|
||||
* The array must end with a NULL pointer.
|
||||
*/
|
||||
struct drvmgr_bus_res *gr_tmtc_1553_resources[] __attribute__((weak)) =
|
||||
{
|
||||
NULL
|
||||
};
|
||||
|
||||
void gr_tmtc_1553_register_drv(void)
|
||||
{
|
||||
DBG("Registering GR-TMTC-1553 PCI driver\n");
|
||||
drvmgr_drv_register(&gr_tmtc_1553_info.general);
|
||||
}
|
||||
|
||||
void gr_tmtc_1553_isr (void *arg)
|
||||
{
|
||||
struct gr_tmtc_1553_priv *priv = arg;
|
||||
unsigned int status, tmp;
|
||||
int irq;
|
||||
SPIN_ISR_IRQFLAGS(irqflags);
|
||||
|
||||
tmp = status = priv->irq->ipend;
|
||||
|
||||
/* DBG("GR-RASTA-IO: IRQ 0x%x\n",status); */
|
||||
|
||||
SPIN_LOCK(&priv->devlock, irqflags);
|
||||
for(irq=0; irq<16; irq++) {
|
||||
if ( status & (1<<irq) ) {
|
||||
genirq_doirq(priv->genirq, irq);
|
||||
priv->irq->iclear = (1<<irq);
|
||||
status &= ~(1<<irq);
|
||||
if ( status == 0 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
SPIN_UNLOCK(&priv->devlock, irqflags);
|
||||
|
||||
/* ACK interrupt, this is because PCI is Level, so the IRQ Controller still drives the IRQ. */
|
||||
if ( tmp )
|
||||
drvmgr_interrupt_clear(priv->dev, 0);
|
||||
|
||||
DBG("GR-TMTC-1553-IRQ: 0x%x\n", tmp);
|
||||
}
|
||||
|
||||
static int gr_tmtc_1553_hw_init(struct gr_tmtc_1553_priv *priv)
|
||||
{
|
||||
unsigned int *page0 = NULL;
|
||||
struct ambapp_dev *tmp;
|
||||
unsigned int pci_freq_hz;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
uint32_t bar0, bar0_size;
|
||||
|
||||
/* Select version of GR-TMTC-1553 board */
|
||||
switch (devinfo->rev) {
|
||||
case 0:
|
||||
priv->version = &gr_tmtc_1553_ver0;
|
||||
break;
|
||||
default:
|
||||
return -2;
|
||||
}
|
||||
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
page0 = (unsigned int *)(bar0 + bar0_size/2);
|
||||
|
||||
/* Point PAGE0 to start of board address map. RAM at 0xff000000, APB at 0xffc00000, IOAREA at 0xfff000000 */
|
||||
/* XXX We assume little endian host with byte twisting enabled here */
|
||||
*page0 = 0x010000ff; /* Set little endian mode on peripheral. */
|
||||
|
||||
/* Scan AMBA Plug&Play */
|
||||
|
||||
/* AMBA MAP bar0 (in CPU) ==> 0x80000000(remote amba address) */
|
||||
priv->amba_maps[0].size = 0x1000000;
|
||||
priv->amba_maps[0].local_adr = bar0;
|
||||
priv->amba_maps[0].remote_adr = 0xff000000;
|
||||
|
||||
/* Addresses not matching with map be untouched */
|
||||
priv->amba_maps[2].size = 0xfffffff0;
|
||||
priv->amba_maps[2].local_adr = 0;
|
||||
priv->amba_maps[2].remote_adr = 0;
|
||||
|
||||
/* Mark end of table */
|
||||
priv->amba_maps[3].size=0;
|
||||
priv->amba_maps[3].local_adr = 0;
|
||||
priv->amba_maps[3].remote_adr = 0;
|
||||
|
||||
/* Start AMBA PnP scan at first AHB bus */
|
||||
ambapp_scan(&priv->abus,
|
||||
bar0 + (priv->version->amba_ioarea & ~0xff000000),
|
||||
NULL, &priv->amba_maps[0]);
|
||||
|
||||
/* Frequency is the hsame as the PCI bus frequency */
|
||||
drvmgr_freq_get(priv->dev, 0, &pci_freq_hz);
|
||||
|
||||
ambapp_freq_init(&priv->abus, NULL, pci_freq_hz);
|
||||
|
||||
/* Find IRQ controller */
|
||||
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
||||
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
||||
VENDOR_GAISLER, GAISLER_IRQMP,
|
||||
ambapp_find_by_idx, NULL);
|
||||
if ( !tmp ) {
|
||||
return -4;
|
||||
}
|
||||
priv->irq = (struct irqmp_regs *)DEV_TO_APB(tmp)->start;
|
||||
/* Set up irq controller */
|
||||
priv->irq->mask[0] = 0;
|
||||
priv->irq->iclear = 0xffff;
|
||||
priv->irq->ilevel = 0;
|
||||
|
||||
/* DOWN streams translation table */
|
||||
priv->bus_maps_down[0].name = "PCI BAR0 -> AMBA";
|
||||
priv->bus_maps_down[0].size = priv->amba_maps[0].size;
|
||||
priv->bus_maps_down[0].from_adr = (void *)priv->amba_maps[0].local_adr;
|
||||
priv->bus_maps_down[0].to_adr = (void *)priv->amba_maps[0].remote_adr;
|
||||
/* Mark end of translation table */
|
||||
priv->bus_maps_down[1].size = 0;
|
||||
|
||||
/* Successfully registered the board */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Called when a PCI target is found with the PCI device and vendor ID
|
||||
* given in gr_tmtc_1553_ids[].
|
||||
*/
|
||||
int gr_tmtc_1553_init1(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct gr_tmtc_1553_priv *priv;
|
||||
struct pci_dev_info *devinfo;
|
||||
int status;
|
||||
uint32_t bar0, bar0_size;
|
||||
int resources_cnt;
|
||||
|
||||
/* PCI device does not have the IRQ line register, when PCI autoconf configures it the configuration
|
||||
* is forgotten. We take the IRQ number from the PCI Host device (AMBA device), this works as long
|
||||
* as PCI-IRQs are ored together on the bus.
|
||||
*
|
||||
* Note that this only works on LEON.
|
||||
*/
|
||||
((struct pci_dev_info *)dev->businfo)->irq = ((struct amba_dev_info *)dev->parent->dev->businfo)->info.irq;
|
||||
|
||||
priv = grlib_calloc(1, sizeof(*priv));
|
||||
if ( !priv )
|
||||
return DRVMGR_NOMEM;
|
||||
|
||||
dev->priv = priv;
|
||||
priv->dev = dev;
|
||||
|
||||
/* Determine number of configurations */
|
||||
resources_cnt = get_resarray_count(gr_tmtc_1553_resources);
|
||||
|
||||
/* Generate Device prefix */
|
||||
|
||||
strcpy(priv->prefix, "/dev/tmtc1553_0");
|
||||
priv->prefix[14] += dev->minor_drv;
|
||||
mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
priv->prefix[15] = '/';
|
||||
priv->prefix[16] = '\0';
|
||||
|
||||
priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo;
|
||||
priv->pcidev = devinfo->pcidev;
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
printk("\n\n--- GR-TMTC-1553[%d] ---\n", dev->minor_drv);
|
||||
printk(" PCI BUS: 0x%x, SLOT: 0x%x, FUNCTION: 0x%x\n",
|
||||
PCI_DEV_EXPAND(priv->pcidev));
|
||||
printk(" PCI VENDOR: 0x%04x, DEVICE: 0x%04x\n",
|
||||
devinfo->id.vendor, devinfo->id.device);
|
||||
printk(" PCI BAR[0]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar0, bar0 + bar0_size - 1);
|
||||
printk(" IRQ: %d\n\n\n", devinfo->irq);
|
||||
|
||||
/* all neccessary space assigned to GR-TMTC-1553 target? */
|
||||
if (bar0_size == 0)
|
||||
return DRVMGR_ENORES;
|
||||
|
||||
/* Initialize spin-lock for this PCI peripheral device. This is to
|
||||
* protect the Interrupt Controller Registers. The genirq layer is
|
||||
* protecting its own internals and ISR dispatching.
|
||||
*/
|
||||
SPIN_INIT(&priv->devlock, priv->prefix);
|
||||
|
||||
priv->genirq = genirq_init(16);
|
||||
if ( priv->genirq == NULL ) {
|
||||
free(priv);
|
||||
dev->priv = NULL;
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
status = gr_tmtc_1553_hw_init(priv);
|
||||
if ( status != 0 ) {
|
||||
genirq_destroy(priv->genirq);
|
||||
free(priv);
|
||||
dev->priv = NULL;
|
||||
printk(" Failed to initialize GR-TMTC-1553 HW: %d\n", status);
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
/* Init amba bus */
|
||||
priv->config.abus = &priv->abus;
|
||||
priv->config.ops = &ambapp_tmtc_1553_ops;
|
||||
priv->config.maps_down = &priv->bus_maps_down[0];
|
||||
/* This PCI device has only target interface so DMA is not supported,
|
||||
* which means that translation from AMBA->PCI should fail if attempted.
|
||||
*/
|
||||
priv->config.maps_up = DRVMGR_TRANSLATE_NO_BRIDGE;
|
||||
if ( priv->dev->minor_drv < resources_cnt ) {
|
||||
priv->config.resources = gr_tmtc_1553_resources[priv->dev->minor_drv];
|
||||
} else {
|
||||
priv->config.resources = NULL;
|
||||
}
|
||||
|
||||
/* Create And Register AMBA PnP Bus */
|
||||
return ambapp_bus_register(dev, &priv->config);
|
||||
}
|
||||
|
||||
int gr_tmtc_1553_init2(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct gr_tmtc_1553_priv *priv = dev->priv;
|
||||
|
||||
/* Clear any old interrupt requests */
|
||||
drvmgr_interrupt_clear(dev, 0);
|
||||
|
||||
/* Enable System IRQ so that GR-TMTC-1553 PCI target interrupt goes through.
|
||||
*
|
||||
* It is important to enable it in stage init2. If interrupts were enabled in init1
|
||||
* this might hang the system when more than one PCI target is connected, this is
|
||||
* because PCI interrupts might be shared and PCI target 2 have not initialized and
|
||||
* might therefore drive interrupt already when entering init1().
|
||||
*/
|
||||
drvmgr_interrupt_register(
|
||||
dev,
|
||||
0,
|
||||
"gr_tmtc_1553",
|
||||
gr_tmtc_1553_isr,
|
||||
(void *)priv);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_tmtc_1553_int_register(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
const char *info,
|
||||
drvmgr_isr handler,
|
||||
void *arg)
|
||||
{
|
||||
struct gr_tmtc_1553_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
int status;
|
||||
void *h;
|
||||
|
||||
h = genirq_alloc_handler(handler, arg);
|
||||
if ( h == NULL )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
status = genirq_register(priv->genirq, irq, h);
|
||||
if ( status == 0 ) {
|
||||
/* Disable and clear IRQ for first registered handler */
|
||||
priv->irq->iclear = (1<<irq);
|
||||
priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */
|
||||
} else if ( status == 1 )
|
||||
status = 0;
|
||||
|
||||
if (status != 0) {
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
genirq_free_handler(h);
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
status = genirq_enable(priv->genirq, irq, handler, arg);
|
||||
if ( status == 0 ) {
|
||||
/* Enable IRQ for first enabled handler only */
|
||||
priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */
|
||||
} else if ( status == 1 )
|
||||
status = 0;
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int ambapp_tmtc_1553_int_unregister(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq,
|
||||
drvmgr_isr isr,
|
||||
void *arg)
|
||||
{
|
||||
struct gr_tmtc_1553_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
int status;
|
||||
void *handler;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
status = genirq_disable(priv->genirq, irq, isr, arg);
|
||||
if ( status == 0 ) {
|
||||
/* Disable IRQ only when no enabled handler exists */
|
||||
priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */
|
||||
} else if ( status == 1 )
|
||||
status = 0;
|
||||
|
||||
handler = genirq_unregister(priv->genirq, irq, isr, arg);
|
||||
if ( handler == NULL )
|
||||
status = DRVMGR_FAIL;
|
||||
else
|
||||
status = DRVMGR_OK;
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
if (handler)
|
||||
genirq_free_handler(handler);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int ambapp_tmtc_1553_int_unmask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_tmtc_1553_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
|
||||
DBG("TMTC-1553 IRQ %d: enable\n", irq);
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
/* Enable IRQ */
|
||||
priv->irq->mask[0] |= (1<<irq); /* unmask interrupt source */
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_tmtc_1553_int_mask(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_tmtc_1553_priv *priv = dev->parent->dev->priv;
|
||||
SPIN_IRQFLAGS(irqflags);
|
||||
|
||||
DBG("TMTC-1553 IRQ %d: disable\n", irq);
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
/* Disable IRQ */
|
||||
priv->irq->mask[0] &= ~(1<<irq); /* mask interrupt source */
|
||||
|
||||
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_tmtc_1553_int_clear(
|
||||
struct drvmgr_dev *dev,
|
||||
int irq)
|
||||
{
|
||||
struct gr_tmtc_1553_priv *priv = dev->parent->dev->priv;
|
||||
|
||||
if ( genirq_check(priv->genirq, irq) )
|
||||
return DRVMGR_FAIL;
|
||||
|
||||
priv->irq->iclear = (1<<irq);
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
|
||||
int ambapp_tmtc_1553_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params)
|
||||
{
|
||||
struct gr_tmtc_1553_priv *priv = dev->parent->dev->priv;
|
||||
|
||||
/* Device name prefix pointer, skip /dev */
|
||||
params->dev_prefix = &priv->prefix[5];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gr_tmtc_1553_print_dev(struct drvmgr_dev *dev, int options)
|
||||
{
|
||||
struct gr_tmtc_1553_priv *priv = dev->priv;
|
||||
struct pci_dev_info *devinfo = priv->devinfo;
|
||||
uint32_t bar0, bar0_size;
|
||||
|
||||
/* Print */
|
||||
printf("--- GR-TMTC-1553 [bus 0x%x, dev 0x%x, fun 0x%x] ---\n",
|
||||
PCI_DEV_EXPAND(priv->pcidev));
|
||||
|
||||
bar0 = devinfo->resources[0].address;
|
||||
bar0_size = devinfo->resources[0].size;
|
||||
|
||||
printf(" PCI BAR[0]: 0x%" PRIx32 " - 0x%" PRIx32 "\n",
|
||||
bar0, bar0 + bar0_size - 1);
|
||||
printf(" IRQ REGS: 0x%" PRIxPTR "\n", (uintptr_t)priv->irq);
|
||||
printf(" IRQ: %d\n", devinfo->irq);
|
||||
printf(" FREQ: %d Hz\n", priv->version->amba_freq_hz);
|
||||
printf(" IMASK: 0x%08x\n", priv->irq->mask[0]);
|
||||
printf(" IPEND: 0x%08x\n", priv->irq->ipend);
|
||||
|
||||
/* Print amba config */
|
||||
if ( options & TMTC_1553_OPTIONS_AMBA ) {
|
||||
ambapp_print(&priv->abus, 10);
|
||||
}
|
||||
#if 0
|
||||
/* Print IRQ handlers and their arguments */
|
||||
if ( options & TMTC_1553_OPTIONS_IRQ ) {
|
||||
int i;
|
||||
for(i=0; i<16; i++) {
|
||||
printf(" IRQ[%02d]: 0x%x, arg: 0x%x\n",
|
||||
i, (unsigned int)priv->isrs[i].handler, (unsigned int)priv->isrs[i].arg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gr_tmtc_1553_print(int options)
|
||||
{
|
||||
struct pci_drv_info *drv = &gr_tmtc_1553_info;
|
||||
struct drvmgr_dev *dev;
|
||||
|
||||
dev = drv->general.dev;
|
||||
while(dev) {
|
||||
gr_tmtc_1553_print_dev(dev, options);
|
||||
dev = dev->next_in_drv;
|
||||
}
|
||||
}
|
||||
@@ -1,722 +0,0 @@
|
||||
/* 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->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;
|
||||
|
||||
}
|
||||
@@ -1,970 +0,0 @@
|
||||
/* 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);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,586 +0,0 @@
|
||||
/* GRLIB PCIF PCI HOST driver.
|
||||
*
|
||||
* COPYRIGHT (c) 2008.
|
||||
* Cobham Gaisler AB.
|
||||
*
|
||||
* Configures the PCIF 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libcpu/byteorder.h>
|
||||
#include <libcpu/access.h>
|
||||
#include <rtems/bspIo.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/pcif.h>
|
||||
|
||||
|
||||
/* Configuration options */
|
||||
#define SYSTEM_MAINMEM_START 0x40000000
|
||||
|
||||
/* Interrupt assignment. Set to other value than 0xff in order to
|
||||
* override defaults and plug&play information
|
||||
*/
|
||||
#ifndef PCIF_INTA_SYSIRQ
|
||||
#define PCIF_INTA_SYSIRQ 0xff
|
||||
#endif
|
||||
#ifndef PCIF_INTB_SYSIRQ
|
||||
#define PCIF_INTB_SYSIRQ 0xff
|
||||
#endif
|
||||
#ifndef PCIF_INTC_SYSIRQ
|
||||
#define PCIF_INTC_SYSIRQ 0xff
|
||||
#endif
|
||||
#ifndef PCIF_INTD_SYSIRQ
|
||||
#define PCIF_INTD_SYSIRQ 0xff
|
||||
#endif
|
||||
|
||||
/*#define DEBUG 1 */
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(x...) printk(x)
|
||||
#else
|
||||
#define DBG(x...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Bit encode for PCI_CONFIG_HEADER_TYPE register
|
||||
*/
|
||||
struct pcif_regs {
|
||||
volatile unsigned int bars[4]; /* 0x00-0x10 */
|
||||
volatile unsigned int bus; /* 0x10 */
|
||||
volatile unsigned int map_io; /* 0x14 */
|
||||
volatile unsigned int status; /* 0x18 */
|
||||
volatile unsigned int intr; /* 0x1c */
|
||||
int unused[(0x40-0x20)/4]; /* 0x20-0x40 */
|
||||
volatile unsigned int maps[(0x80-0x40)/4]; /* 0x40-0x80*/
|
||||
};
|
||||
|
||||
/* Used internally for accessing the PCI bridge's configuration space itself */
|
||||
#define HOST_TGT PCI_DEV(0xff, 0, 0)
|
||||
|
||||
struct pcif_priv *pcifpriv = NULL;
|
||||
static int pcif_minor = 0;
|
||||
|
||||
/* PCI Interrupt assignment. Connects an PCI interrupt pin (INTA#..INTD#)
|
||||
* to a system interrupt number.
|
||||
*/
|
||||
unsigned char pcif_pci_irq_table[4] =
|
||||
{
|
||||
/* INTA# */ PCIF_INTA_SYSIRQ,
|
||||
/* INTB# */ PCIF_INTB_SYSIRQ,
|
||||
/* INTC# */ PCIF_INTC_SYSIRQ,
|
||||
/* INTD# */ PCIF_INTD_SYSIRQ
|
||||
};
|
||||
|
||||
/* Driver private data struture */
|
||||
struct pcif_priv {
|
||||
struct drvmgr_dev *dev;
|
||||
struct pcif_regs *regs;
|
||||
int irq;
|
||||
int minor;
|
||||
int irq_mask;
|
||||
|
||||
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 */
|
||||
uint32_t bar1_size;
|
||||
|
||||
struct drvmgr_map_entry maps_up[2];
|
||||
struct drvmgr_map_entry maps_down[2];
|
||||
struct pcibus_config config;
|
||||
};
|
||||
|
||||
int pcif_init1(struct drvmgr_dev *dev);
|
||||
int pcif_init3(struct drvmgr_dev *dev);
|
||||
|
||||
/* PCIF DRIVER */
|
||||
|
||||
struct drvmgr_drv_ops pcif_ops =
|
||||
{
|
||||
.init = {pcif_init1, NULL, pcif_init3, NULL},
|
||||
.remove = NULL,
|
||||
.info = NULL
|
||||
};
|
||||
|
||||
struct amba_dev_id pcif_ids[] =
|
||||
{
|
||||
{VENDOR_GAISLER, GAISLER_PCIF},
|
||||
{0, 0} /* Mark end of table */
|
||||
};
|
||||
|
||||
struct amba_drv_info pcif_info =
|
||||
{
|
||||
{
|
||||
DRVMGR_OBJ_DRV, /* Driver */
|
||||
NULL, /* Next driver */
|
||||
NULL, /* Device list */
|
||||
DRIVER_AMBAPP_GAISLER_PCIF_ID, /* Driver ID */
|
||||
"PCIF_DRV", /* Driver Name */
|
||||
DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */
|
||||
&pcif_ops,
|
||||
NULL, /* Funcs */
|
||||
0, /* No devices yet */
|
||||
sizeof(struct pcif_priv), /* Let drvmgr alloc private */
|
||||
},
|
||||
&pcif_ids[0]
|
||||
};
|
||||
|
||||
void pcif_register_drv(void)
|
||||
{
|
||||
DBG("Registering PCIF driver\n");
|
||||
drvmgr_drv_register(&pcif_info.general);
|
||||
}
|
||||
|
||||
static int pcif_cfg_r32(pci_dev_t dev, int ofs, uint32_t *val)
|
||||
{
|
||||
struct pcif_priv *priv = pcifpriv;
|
||||
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;
|
||||
}
|
||||
|
||||
/* PCIF 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->bus = bus << 16;
|
||||
|
||||
pci_conf = (volatile uint32_t *)(priv->pci_conf | (devfn << 8) | ofs);
|
||||
|
||||
*val = *pci_conf;
|
||||
|
||||
if (priv->regs->status & 0x30000000) {
|
||||
*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 pcif_cfg_r16(pci_dev_t dev, int ofs, uint16_t *val)
|
||||
{
|
||||
uint32_t v;
|
||||
int retval;
|
||||
|
||||
if (ofs & 1)
|
||||
return PCISTS_EINVAL;
|
||||
|
||||
retval = pcif_cfg_r32(dev, ofs & ~0x3, &v);
|
||||
*val = 0xffff & (v >> (8*(ofs & 0x3)));
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int pcif_cfg_r8(pci_dev_t dev, int ofs, uint8_t *val)
|
||||
{
|
||||
uint32_t v;
|
||||
int retval;
|
||||
|
||||
retval = pcif_cfg_r32(dev, ofs & ~0x3, &v);
|
||||
|
||||
*val = 0xff & (v >> (8*(ofs & 3)));
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int pcif_cfg_w32(pci_dev_t dev, int ofs, uint32_t val)
|
||||
{
|
||||
struct pcif_priv *priv = pcifpriv;
|
||||
volatile uint32_t *pci_conf;
|
||||
uint32_t devfn;
|
||||
int bus = PCI_DEV_BUS(dev);
|
||||
|
||||
if (ofs & ~0xfc)
|
||||
return PCISTS_EINVAL;
|
||||
|
||||
if (PCI_DEV_SLOT(dev) > 15)
|
||||
return PCISTS_MSTABRT;
|
||||
|
||||
/* PCIF 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->bus = bus << 16;
|
||||
|
||||
pci_conf = (volatile uint32_t *)(priv->pci_conf | (devfn << 8) | ofs);
|
||||
|
||||
*pci_conf = val;
|
||||
|
||||
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 pcif_cfg_w16(pci_dev_t dev, int ofs, uint16_t val)
|
||||
{
|
||||
uint32_t v;
|
||||
int retval;
|
||||
|
||||
if (ofs & 1)
|
||||
return PCISTS_EINVAL;
|
||||
|
||||
retval = pcif_cfg_r32(dev, ofs & ~0x3, &v);
|
||||
if (retval != PCISTS_OK)
|
||||
return retval;
|
||||
|
||||
v = (v & ~(0xffff << (8*(ofs&3)))) | ((0xffff&val) << (8*(ofs&3)));
|
||||
|
||||
return pcif_cfg_w32(dev, ofs & ~0x3, v);
|
||||
}
|
||||
|
||||
static int pcif_cfg_w8(pci_dev_t dev, int ofs, uint8_t val)
|
||||
{
|
||||
uint32_t v;
|
||||
int retval;
|
||||
|
||||
retval = pcif_cfg_r32(dev, ofs & ~0x3, &v);
|
||||
if (retval != PCISTS_OK)
|
||||
return retval;
|
||||
|
||||
v = (v & ~(0xff << (8*(ofs&3)))) | ((0xff&val) << (8*(ofs&3)));
|
||||
|
||||
return pcif_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 pcif_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 pcif_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 = pcif_pci_irq_table[irq_pin];
|
||||
}
|
||||
return sysIrqNr;
|
||||
}
|
||||
|
||||
static int pcif_translate(uint32_t *address, int type, int dir)
|
||||
{
|
||||
/* No address translation implmented at this point */
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern struct pci_memreg_ops pci_memreg_sparc_be_ops;
|
||||
|
||||
/* PCIF Big-Endian PCI access routines */
|
||||
struct pci_access_drv pcif_access_drv = {
|
||||
.cfg =
|
||||
{
|
||||
pcif_cfg_r8,
|
||||
pcif_cfg_r16,
|
||||
pcif_cfg_r32,
|
||||
pcif_cfg_w8,
|
||||
pcif_cfg_w16,
|
||||
pcif_cfg_w32,
|
||||
},
|
||||
.io = /* PCIF only supports Big-endian */
|
||||
{
|
||||
_ld8,
|
||||
_ld_be16,
|
||||
_ld_be32,
|
||||
_st8,
|
||||
_st_be16,
|
||||
_st_be32,
|
||||
},
|
||||
.memreg = &pci_memreg_sparc_be_ops,
|
||||
.translate = pcif_translate,
|
||||
};
|
||||
|
||||
/* Initializes the PCIF core hardware
|
||||
*
|
||||
*/
|
||||
static int pcif_hw_init(struct pcif_priv *priv)
|
||||
{
|
||||
struct pcif_regs *regs;
|
||||
uint32_t data, size;
|
||||
int mst;
|
||||
pci_dev_t host = HOST_TGT;
|
||||
|
||||
regs = priv->regs;
|
||||
|
||||
/* Mask PCI interrupts */
|
||||
regs->intr = 0;
|
||||
|
||||
/* Get the PCIF Host PCI ID */
|
||||
pcif_cfg_r32(host, PCIR_VENDOR, &priv->devVend);
|
||||
|
||||
/* set 1:1 mapping between AHB -> PCI memory space, for all Master cores */
|
||||
for ( mst=0; mst<16; mst++) {
|
||||
regs->maps[mst] = priv->pci_area;
|
||||
|
||||
/* Check if this register is implemented */
|
||||
if ( regs->maps[mst] != priv->pci_area )
|
||||
break;
|
||||
}
|
||||
|
||||
/* and map system RAM at pci address SYSTEM_MAINMEM_START. This way
|
||||
* PCI targets can do DMA directly into CPU main memory.
|
||||
*/
|
||||
regs->bars[0] = SYSTEM_MAINMEM_START;
|
||||
regs->bars[1] = 0;
|
||||
regs->bars[2] = 0;
|
||||
regs->bars[3] = 0;
|
||||
|
||||
/* determine size of target BAR1 */
|
||||
pcif_cfg_w32(host, PCIR_BAR(1), 0xffffffff);
|
||||
pcif_cfg_r32(host, PCIR_BAR(1), &size);
|
||||
priv->bar1_size = (~(size & ~0xf)) + 1;
|
||||
|
||||
pcif_cfg_w32(host, PCIR_BAR(0), 0);
|
||||
pcif_cfg_w32(host, PCIR_BAR(1), SYSTEM_MAINMEM_START);
|
||||
pcif_cfg_w32(host, PCIR_BAR(2), 0);
|
||||
pcif_cfg_w32(host, PCIR_BAR(3), 0);
|
||||
pcif_cfg_w32(host, PCIR_BAR(4), 0);
|
||||
pcif_cfg_w32(host, PCIR_BAR(5), 0);
|
||||
|
||||
/* set as bus master and enable pci memory responses */
|
||||
pcif_cfg_r32(host, PCIR_COMMAND, &data);
|
||||
data |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
|
||||
pcif_cfg_w32(host, PCIR_COMMAND, data);
|
||||
|
||||
/* Successful */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initializes the PCIF 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 PCIF hardware initialization
|
||||
* -4 Error registering driver to PCI layer
|
||||
*/
|
||||
static int pcif_init(struct pcif_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->irq;
|
||||
priv->regs = (struct pcif_regs *)apb->start;
|
||||
|
||||
/* 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 PCIF core.
|
||||
*/
|
||||
strcpy(keyname, "INTX#");
|
||||
for (pin=1; pin<5; pin++) {
|
||||
if ( pcif_pci_irq_table[pin-1] == 0xff ) {
|
||||
pcif_pci_irq_table[pin-1] = priv->irq;
|
||||
|
||||
/* User may override Plug & Play IRQ */
|
||||
keyname[3] = 'A' + (pin-1);
|
||||
value = drvmgr_dev_key_get(priv->dev, keyname, DRVMGR_KT_INT);
|
||||
if ( value )
|
||||
pcif_pci_irq_table[pin-1] = value->i;
|
||||
}
|
||||
}
|
||||
|
||||
priv->irq_mask = 0xf;
|
||||
value = drvmgr_dev_key_get(priv->dev, "", DRVMGR_KT_INT);
|
||||
if ( value )
|
||||
priv->irq_mask = value->i & 0xf;
|
||||
|
||||
/* This driver only support HOST systems, we check for HOST */
|
||||
if ( priv->regs->status & 0x00000001 ) {
|
||||
/* Target not supported */
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Init the PCI Core */
|
||||
if ( pcif_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 *)SYSTEM_MAINMEM_START;
|
||||
priv->maps_up[0].to_adr = (void *)SYSTEM_MAINMEM_START;
|
||||
/* 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 pcif_ids[].
|
||||
*/
|
||||
int pcif_init1(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct pcif_priv *priv;
|
||||
struct pci_auto_setup pcif_auto_cfg;
|
||||
|
||||
DBG("PCIF[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name);
|
||||
|
||||
if ( pcif_minor != 0 ) {
|
||||
printk("Driver only supports one PCI core\n");
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
priv = dev->priv;
|
||||
if ( !priv )
|
||||
return DRVMGR_NOMEM;
|
||||
|
||||
dev->priv = priv;
|
||||
priv->dev = dev;
|
||||
priv->minor = pcif_minor++;
|
||||
|
||||
pcifpriv = priv;
|
||||
if ( pcif_init(priv) ) {
|
||||
printk("Failed to initialize PCIF driver\n");
|
||||
free(priv);
|
||||
dev->priv = NULL;
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
/* Host is always Big-Endian */
|
||||
pci_endian = PCI_BIG_ENDIAN;
|
||||
|
||||
/* Register the PCI core at the PCI layer */
|
||||
|
||||
if (pci_access_drv_register(&pcif_access_drv)) {
|
||||
/* Access routines registration failed */
|
||||
return DRVMGR_FAIL;
|
||||
}
|
||||
|
||||
/* Prepare memory MAP */
|
||||
pcif_auto_cfg.options = 0;
|
||||
pcif_auto_cfg.mem_start = 0;
|
||||
pcif_auto_cfg.mem_size = 0;
|
||||
pcif_auto_cfg.memio_start = priv->pci_area;
|
||||
pcif_auto_cfg.memio_size = priv->pci_area_end - priv->pci_area;
|
||||
pcif_auto_cfg.io_start = priv->pci_io;
|
||||
pcif_auto_cfg.io_size = priv->pci_conf - priv->pci_io;
|
||||
pcif_auto_cfg.irq_map = pcif_bus0_irq_map;
|
||||
pcif_auto_cfg.irq_route = NULL; /* use standard routing */
|
||||
pci_config_register(&pcif_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);
|
||||
}
|
||||
|
||||
int pcif_init3(struct drvmgr_dev *dev)
|
||||
{
|
||||
struct pcif_priv *priv = dev->priv;
|
||||
|
||||
/* Unmask all interrupts, on some sytems this
|
||||
* might be problematic because all PCI IRQs are
|
||||
* not connected on the PCB or used for something
|
||||
* else. The irqMask driver resource can be used to
|
||||
* control which PCI IRQs are used to generate the
|
||||
* PCI system IRQ, example:
|
||||
*
|
||||
* 0xf - enable all (DEFAULT)
|
||||
* 0x8 - enable one PCI irq
|
||||
*
|
||||
* Before unmasking PCI IRQ, all PCI boards must
|
||||
* have been initialized and IRQ turned off to avoid
|
||||
* system hang.
|
||||
*/
|
||||
|
||||
priv->regs->intr = priv->irq_mask;
|
||||
|
||||
return DRVMGR_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user