forked from Imagelibrary/rtems
This patch changes the license to BSD-2 for all source files where the copyright is held by Aeroflex Gaisler, Cobham Gaisler, or Gaisler Research. Some files also includes copyright right statements from OAR and/or embedded Brains in addition to Gaisler. Updates #3053.
812 lines
23 KiB
C
812 lines
23 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/*
|
|
* COPYRIGHT (c) 2013.
|
|
* Cobham Gaisler AB.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/* @brief GR-CPCI-LEON4-N2X (NGFP) PCI Peripheral driver
|
|
*
|
|
* Configures the GR-CPIC-LEON4-N2X interface PCI board in peripheral
|
|
* mode. This driver provides a AMBA PnP bus by using the general part
|
|
* of the AMBA PnP bus driver (ambapp_bus.c).
|
|
*
|
|
*
|
|
* Driver resource options:
|
|
* NAME DEFAULT VALUE
|
|
* ahbmst2pci _RAM_START AMBA->PCI translation PCI base address
|
|
* ambaFreq 200000000 (200MHz) AMBA system frequency of LEON4-N2X
|
|
* cgEnMask 0x1f (all) Clock gating enable mask
|
|
*
|
|
* TODO/UNTESTED
|
|
* Interrupt testing
|
|
* bar0 RESOURCE 0x00000000 L2-Cache SDRAM memory
|
|
* bar1 RESOURCE 0xf0000000 L2-Cache registers
|
|
*/
|
|
|
|
#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 <bsp/gr_leon4_n2x.h>
|
|
|
|
#include <grlib/grlib_impl.h>
|
|
|
|
/* Determines which PCI address the AHB masters on the LEON-N2X board will
|
|
* access when accessing the AHB to PCI window, it should be set so that the
|
|
* masters can access the HOST RAM.
|
|
* Default is base of HOST RAM, HOST RAM is mapped 1:1 to PCI memory space.
|
|
*/
|
|
extern unsigned int _RAM_START;
|
|
#define AHBMST2PCIADR (((unsigned int)&_RAM_START) & 0xc0000000)
|
|
|
|
#define GRPCI2_BAR0_TO_AHB_MAP 0x04
|
|
#define GRPCI2_BAR1_TO_AHB_MAP 0x08
|
|
#define GRPCI2_BAR2_TO_AHB_MAP 0x0c
|
|
#define GRPCI2_PCI_CONFIG 0x20
|
|
#define CAP9_AHBPREF_OFS 0x3c
|
|
|
|
/* #define DEBUG 1 */
|
|
|
|
#ifdef DEBUG
|
|
#define DBG(x...) printk(x)
|
|
#else
|
|
#define DBG(x...)
|
|
#endif
|
|
|
|
int gr_cpci_leon4_n2x_init1(struct drvmgr_dev *dev);
|
|
int gr_cpci_leon4_n2x_init2(struct drvmgr_dev *dev);
|
|
void gr_cpci_leon4_n2x_isr(void *arg);
|
|
|
|
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 */
|
|
};
|
|
|
|
/* Clock gating unit register layout */
|
|
struct l4n2x_grcg_regs {
|
|
volatile unsigned int unlock;
|
|
volatile unsigned int enable;
|
|
volatile unsigned int reset;
|
|
volatile unsigned int cpu_fpu;
|
|
};
|
|
#define CG_MASK 0x1f
|
|
|
|
/* Private data structure for driver */
|
|
struct gr_cpci_leon4_n2x_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 */
|
|
int eirq;
|
|
genirq_t genirq;
|
|
|
|
/* GR-CPCI-LEON4-N2X */
|
|
unsigned int amba_freq_hz;
|
|
unsigned int cg_en_mask; /* Enabled cores */
|
|
struct irqmp_regs *irq;
|
|
struct l4n2x_grcg_regs *cg; /* Clock-gating unit */
|
|
struct grpci2_regs *grpci2;
|
|
struct drvmgr_map_entry bus_maps_up[2];
|
|
struct drvmgr_map_entry bus_maps_down[4];
|
|
|
|
/* AMBA Plug&Play information on GR-CPCI-LEON4-N2X */
|
|
struct ambapp_bus abus;
|
|
struct ambapp_mmap amba_maps[5];
|
|
struct ambapp_config config;
|
|
};
|
|
|
|
int ambapp_leon4_n2x_int_register(
|
|
struct drvmgr_dev *dev,
|
|
int irq,
|
|
const char *info,
|
|
drvmgr_isr handler,
|
|
void *arg);
|
|
int ambapp_leon4_n2x_int_unregister(
|
|
struct drvmgr_dev *dev,
|
|
int irq,
|
|
drvmgr_isr handler,
|
|
void *arg);
|
|
int ambapp_leon4_n2x_int_unmask(
|
|
struct drvmgr_dev *dev,
|
|
int irq);
|
|
int ambapp_leon4_n2x_int_mask(
|
|
struct drvmgr_dev *dev,
|
|
int irq);
|
|
int ambapp_leon4_n2x_int_clear(
|
|
struct drvmgr_dev *dev,
|
|
int irq);
|
|
int ambapp_leon4_n2x_get_params(
|
|
struct drvmgr_dev *dev,
|
|
struct drvmgr_bus_params *params);
|
|
|
|
static struct ambapp_ops ambapp_leon4_n2x_ops = {
|
|
.int_register = ambapp_leon4_n2x_int_register,
|
|
.int_unregister = ambapp_leon4_n2x_int_unregister,
|
|
.int_unmask = ambapp_leon4_n2x_int_unmask,
|
|
.int_mask = ambapp_leon4_n2x_int_mask,
|
|
.int_clear = ambapp_leon4_n2x_int_clear,
|
|
.get_params = ambapp_leon4_n2x_get_params
|
|
};
|
|
|
|
struct drvmgr_drv_ops gr_cpci_leon4_n2x_ops =
|
|
{
|
|
.init = {gr_cpci_leon4_n2x_init1, gr_cpci_leon4_n2x_init2, NULL, NULL},
|
|
.remove = NULL,
|
|
.info = NULL
|
|
};
|
|
|
|
struct pci_dev_id_match gr_cpci_leon4_n2x_ids[] =
|
|
{
|
|
PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_LEON4_N2X),
|
|
PCIID_DEVVEND(PCIID_VENDOR_GAISLER, PCIID_DEVICE_GR_NGMP_PROTO),
|
|
PCIID_END_TABLE /* Mark end of table */
|
|
};
|
|
|
|
struct pci_drv_info gr_cpci_leon4_n2x_info =
|
|
{
|
|
{
|
|
DRVMGR_OBJ_DRV, /* Driver */
|
|
NULL, /* Next driver */
|
|
NULL, /* Device list */
|
|
DRIVER_PCI_GAISLER_LEON4_N2X_ID,/* Driver ID */
|
|
"GR-CPCI-LEON4-N2X", /* Driver Name */
|
|
DRVMGR_BUS_TYPE_PCI, /* Bus Type */
|
|
&gr_cpci_leon4_n2x_ops,
|
|
NULL, /* Funcs */
|
|
0, /* No devices yet */
|
|
sizeof(struct gr_cpci_leon4_n2x_priv),
|
|
},
|
|
&gr_cpci_leon4_n2x_ids[0]
|
|
};
|
|
|
|
/* Driver resources configuration for the AMBA bus on the GR-CPCI-LEON4-N2X 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-CPCI-LEON4-N2X 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_leon4_n2x_resources[] __attribute__((weak)) =
|
|
{
|
|
NULL
|
|
};
|
|
|
|
void gr_cpci_leon4_n2x_register_drv(void)
|
|
{
|
|
DBG("Registering GR-CPCI-LEON4-N2X PCI driver\n");
|
|
drvmgr_drv_register(&gr_cpci_leon4_n2x_info.general);
|
|
}
|
|
|
|
void gr_cpci_leon4_n2x_isr(void *arg)
|
|
{
|
|
struct gr_cpci_leon4_n2x_priv *priv = arg;
|
|
unsigned int status, tmp;
|
|
int irq, eirq;
|
|
SPIN_ISR_IRQFLAGS(irqflags);
|
|
|
|
tmp = status = priv->irq->ipend;
|
|
|
|
/* DBG("GR-CPCI-LEON4-N2X: IRQ 0x%x\n",status); */
|
|
|
|
SPIN_LOCK(&priv->devlock, irqflags);
|
|
for(irq = 0; irq < 32; irq++) {
|
|
if (status & (1 << irq)) {
|
|
if (irq == priv->eirq) {
|
|
while ((eirq = priv->irq->intid[0] & 0x1f)) {
|
|
if ((eirq & 0x10) == 0)
|
|
continue;
|
|
genirq_doirq(priv->genirq, eirq);
|
|
priv->irq->iclear = (1 << eirq);
|
|
}
|
|
} else {
|
|
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-CPCI-LEON4-N2X-IRQ: 0x%x\n", tmp);
|
|
}
|
|
|
|
static int gr_cpci_leon4_n2x_hw_init1(struct gr_cpci_leon4_n2x_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;
|
|
unsigned int cgmask, enabled;
|
|
|
|
/* Check capabilities list bit and read its pointer */
|
|
pci_cfg_r8(pcidev, PCIR_STATUS, &tmp2);
|
|
if (!((tmp2 >> 4) & 1)) {
|
|
/* Capabilities list not available which it should be in the GRPCI2 */
|
|
return -2;
|
|
}
|
|
pci_cfg_r8(pcidev, PCIR_CAP_PTR, &cap_ptr);
|
|
|
|
/* Workarounds depends on PCI revision of GR-CPCI-LEON4-N2X board */
|
|
switch (devinfo->rev) {
|
|
case 0:
|
|
/* Limit the AMBA prefetch for GRPCI2 version 0. */
|
|
pci_cfg_w32(pcidev, cap_ptr+CAP9_AHBPREF_OFS, 0);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Set AHB address mappings for target PCI bars
|
|
* BAR0 maps to 0x00000000-0x07ffffff 128MB (SDRAM/DDR2 memory)
|
|
* BAR1 maps to 0xf0000000-0xf7ffffff 128MB (L2-Cache regs/diagnostics)
|
|
* BAR2 maps to 0xff800000-0xffffffff 8MB (PnP, I/O regs)
|
|
*/
|
|
pci_cfg_w32(pcidev, cap_ptr+GRPCI2_BAR0_TO_AHB_MAP, 0x00000000);
|
|
pci_cfg_w32(pcidev, cap_ptr+GRPCI2_BAR1_TO_AHB_MAP, 0xf0000000);
|
|
pci_cfg_w32(pcidev, cap_ptr+GRPCI2_BAR2_TO_AHB_MAP, 0xff800000);
|
|
|
|
/* 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 leon4-n2x) ==> 0x00000000 (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 = 0x00000000;
|
|
|
|
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 = 0xf0000000;
|
|
|
|
priv->amba_maps[2].size = devinfo->resources[2].size;
|
|
priv->amba_maps[2].local_adr = devinfo->resources[2].address;
|
|
priv->amba_maps[2].remote_adr = 0xff800000;
|
|
|
|
/* Addresses not matching with map be untouched */
|
|
priv->amba_maps[3].size = 0xfffffff0;
|
|
priv->amba_maps[3].local_adr = 0;
|
|
priv->amba_maps[3].remote_adr = 0;
|
|
|
|
/* Mark end of table */
|
|
priv->amba_maps[4].size=0;
|
|
|
|
/* Start AMBA PnP scan at first AHB bus */
|
|
ambapp_scan(
|
|
&priv->abus,
|
|
devinfo->resources[2].address + 0x00700000,
|
|
NULL,
|
|
&priv->amba_maps[0]);
|
|
|
|
/* Initialize Frequency of AMBA bus */
|
|
ambapp_freq_init(&priv->abus, NULL, priv->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-CPCI-LEON4-N2X irq controller
|
|
* Interrupts are routed from IRQCtrl0, we leave the configuration
|
|
* for the other CPUs, as the board's CPUs may be running something.
|
|
* We assume IRQCtrl has been set up properly, or at least the reset
|
|
* values shuold work with this code..
|
|
*/
|
|
priv->irq->mask[0] = 0;
|
|
priv->irq->iforce = 0;
|
|
priv->irq->force[0] = 0;
|
|
priv->irq->ilevel = 0;
|
|
priv->irq->ipend = 0;
|
|
priv->irq->iclear = 0xffffffff;
|
|
priv->irq->ilevel = 0;
|
|
/* Get extended Interrupt controller IRQ number */
|
|
priv->eirq = (priv->irq->mpstat >> 16) & 0xf;
|
|
|
|
/* Find first Clock-Gating unit, enable/disable the requested cores.
|
|
* It is optional in order to support FPGA prototypes.
|
|
*/
|
|
priv->cg = NULL;
|
|
tmp = (struct ambapp_dev *)ambapp_for_each(&priv->abus,
|
|
(OPTIONS_ALL|OPTIONS_APB_SLVS),
|
|
VENDOR_GAISLER, GAISLER_CLKGATE,
|
|
ambapp_find_by_idx, NULL);
|
|
if (tmp)
|
|
priv->cg = (struct l4n2x_grcg_regs *)DEV_TO_APB(tmp)->start;
|
|
|
|
/* Do reset and enable sequence only if not already enabled */
|
|
if (priv->cg && ((enabled = priv->cg->enable) != priv->cg_en_mask)) {
|
|
/* First disable already enabled cores */
|
|
cgmask = ~priv->cg_en_mask & enabled;
|
|
if (cgmask) {
|
|
priv->cg->unlock = cgmask;
|
|
priv->cg->enable = enabled = ~cgmask & enabled;
|
|
priv->cg->unlock = 0;
|
|
}
|
|
/* Enable disabled cores */
|
|
cgmask = priv->cg_en_mask & ~enabled;
|
|
if (cgmask) {
|
|
priv->cg->unlock = cgmask;
|
|
priv->cg->reset |= cgmask;
|
|
priv->cg->enable = cgmask | enabled;
|
|
priv->cg->reset &= ~cgmask;
|
|
priv->cg->unlock = 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].name = "PCI BAR2 -> AMBA";
|
|
priv->bus_maps_down[2].size = priv->amba_maps[2].size;
|
|
priv->bus_maps_down[2].from_adr = (void *)priv->amba_maps[2].local_adr;
|
|
priv->bus_maps_down[2].to_adr = (void *)priv->amba_maps[2].remote_adr;
|
|
priv->bus_maps_down[3].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 -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-CPCI-LEON4-N2X 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;
|
|
|
|
/* Workarounds depends on PCI revision of GR-CPCI-LEON4-N2X board */
|
|
switch (devinfo->rev) {
|
|
case 0:
|
|
/* Limit the PCI master prefetch for GRPCI2 version 0.
|
|
* This fix is required only when PCI Host bridge is
|
|
* GRPCI2 rev 0.
|
|
*/
|
|
priv->grpci2->ppref = 0xffff0000;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Set AHB to PCI mapping for all AMBA AHB masters */
|
|
for(i = 0; i < 16; i++) {
|
|
priv->grpci2->ahbmst_map[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 LEON4-N2X board */
|
|
return 0;
|
|
}
|
|
|
|
static int gr_cpci_leon4_n2x_hw_init2(struct gr_cpci_leon4_n2x_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_cpci_leon4_n2x_ids[].
|
|
*/
|
|
int gr_cpci_leon4_n2x_init1(struct drvmgr_dev *dev)
|
|
{
|
|
struct gr_cpci_leon4_n2x_priv *priv;
|
|
struct pci_dev_info *devinfo;
|
|
int status, i;
|
|
union drvmgr_key_value *value;
|
|
int resources_cnt;
|
|
int sc;
|
|
|
|
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_leon4_n2x_resources);
|
|
|
|
/* Generate Device prefix */
|
|
|
|
strcpy(priv->prefix, "/dev/leon4n2x0");
|
|
priv->prefix[13] += dev->minor_drv;
|
|
sc = mkdir(priv->prefix, S_IRWXU | S_IRWXG | S_IRWXO);
|
|
_Assert_Unused_variable_equals(sc, 0);
|
|
priv->prefix[14] = '/';
|
|
priv->prefix[15] = '\0';
|
|
|
|
priv->devinfo = devinfo = (struct pci_dev_info *)dev->businfo;
|
|
priv->pcidev = devinfo->pcidev;
|
|
printk("\n\n--- GR-CPCI-LEON4-N2X[%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);
|
|
for (i = 0; i < 3; i++) {
|
|
printk(" PCI BAR[%d]: 0x%08" PRIx32 " - 0x%08" PRIx32 "\n",
|
|
i, devinfo->resources[i].address,
|
|
devinfo->resources[i].address +
|
|
(devinfo->resources[i].size - 1));
|
|
/* all neccessary space assigned to GR-CPCI-LEON4-N2X target? */
|
|
if (devinfo->resources[i].size == 0)
|
|
return DRVMGR_ENORES;
|
|
}
|
|
printk(" IRQ: %d\n\n\n", devinfo->irq);
|
|
|
|
/* 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
|
|
* LEON4-N2X board access when doing DMA to HOST RAM. The AHB masters
|
|
* access the PCI Window of the AMBA bus, the MSB 2-bits of that address
|
|
* is translated according this config option before the address goes
|
|
* out on the PCI bus.
|
|
*
|
|
* Only the 2 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 */
|
|
|
|
/* Let user override the default AMBA system frequency of the
|
|
* CPU-bus of the remote GR-CPCI-LEON4-N2X. Default is 200MHz.
|
|
*/
|
|
value = drvmgr_dev_key_get(priv->dev, "ambaFreq", DRVMGR_KT_INT);
|
|
if (value)
|
|
priv->amba_freq_hz = value->i;
|
|
else
|
|
priv->amba_freq_hz = 200000000; /* default */
|
|
|
|
/* Let user determine clock-gating unit configuration. The default
|
|
* is to turn all cores on (disable gating). PCI is always turned ON.
|
|
*/
|
|
value = drvmgr_dev_key_get(priv->dev, "cgEnMask", DRVMGR_KT_INT);
|
|
if (value)
|
|
priv->cg_en_mask = (value->i & CG_MASK) | 0x08;
|
|
else
|
|
priv->cg_en_mask = CG_MASK; /* default all ON */
|
|
|
|
priv->genirq = genirq_init(32);
|
|
if (priv->genirq == NULL)
|
|
return DRVMGR_FAIL;
|
|
|
|
if ((status = gr_cpci_leon4_n2x_hw_init1(priv)) != 0) {
|
|
genirq_destroy(priv->genirq);
|
|
printk(" Failed to initialize GR-CPCI-LEON4-N2X HW: %d\n", status);
|
|
return DRVMGR_FAIL;
|
|
}
|
|
|
|
/* Init amba bus */
|
|
priv->config.abus = &priv->abus;
|
|
priv->config.ops = &ambapp_leon4_n2x_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_leon4_n2x_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_cpci_leon4_n2x_init2(struct drvmgr_dev *dev)
|
|
{
|
|
struct gr_cpci_leon4_n2x_priv *priv = dev->priv;
|
|
|
|
/* Clear any old interrupt requests */
|
|
drvmgr_interrupt_clear(dev, 0);
|
|
|
|
/* Enable System IRQ so that GR-CPCI-LEON4-N2X 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_cpci_leon4_n2x",
|
|
gr_cpci_leon4_n2x_isr,
|
|
(void *)priv);
|
|
|
|
return gr_cpci_leon4_n2x_hw_init2(priv);
|
|
}
|
|
|
|
int ambapp_leon4_n2x_int_register(
|
|
struct drvmgr_dev *dev,
|
|
int irq,
|
|
const char *info,
|
|
drvmgr_isr handler,
|
|
void *arg)
|
|
{
|
|
struct gr_cpci_leon4_n2x_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_leon4_n2x_int_unregister(
|
|
struct drvmgr_dev *dev,
|
|
int irq,
|
|
drvmgr_isr isr,
|
|
void *arg)
|
|
{
|
|
struct gr_cpci_leon4_n2x_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_leon4_n2x_int_unmask(
|
|
struct drvmgr_dev *dev,
|
|
int irq)
|
|
{
|
|
struct gr_cpci_leon4_n2x_priv *priv = dev->parent->dev->priv;
|
|
SPIN_IRQFLAGS(irqflags);
|
|
|
|
DBG("LEON4-N2X 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_leon4_n2x_int_mask(
|
|
struct drvmgr_dev *dev,
|
|
int irq)
|
|
{
|
|
struct gr_cpci_leon4_n2x_priv *priv = dev->parent->dev->priv;
|
|
SPIN_IRQFLAGS(irqflags);
|
|
|
|
DBG("LEON4-N2X 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_leon4_n2x_int_clear(
|
|
struct drvmgr_dev *dev,
|
|
int irq)
|
|
{
|
|
struct gr_cpci_leon4_n2x_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_leon4_n2x_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params)
|
|
{
|
|
struct gr_cpci_leon4_n2x_priv *priv = dev->parent->dev->priv;
|
|
|
|
/* Device name prefix pointer, skip /dev */
|
|
params->dev_prefix = &priv->prefix[5];
|
|
|
|
return 0;
|
|
}
|
|
|
|
void gr_cpci_leon4_n2x_print_dev(struct drvmgr_dev *dev, int options)
|
|
{
|
|
struct gr_cpci_leon4_n2x_priv *priv = dev->priv;
|
|
struct pci_dev_info *devinfo = priv->devinfo;
|
|
uint32_t bar0, bar0_size;
|
|
|
|
/* Print */
|
|
printf("--- GR-CPCI-LEON4-N2X [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->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 & GR_LEON4_N2X_OPTIONS_AMBA)
|
|
ambapp_print(&priv->abus, 10);
|
|
}
|
|
|
|
void gr_leon4_n2x_print(int options)
|
|
{
|
|
struct pci_drv_info *drv = &gr_cpci_leon4_n2x_info;
|
|
struct drvmgr_dev *dev;
|
|
|
|
dev = drv->general.dev;
|
|
while(dev) {
|
|
gr_cpci_leon4_n2x_print_dev(dev, options);
|
|
dev = dev->next_in_drv;
|
|
}
|
|
}
|