summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/sparc/leon2/pci/at697_pci.c
blob: d7064234bec08534fdd6c99003a969fa4d0630b5 (plain) (tree)












































                                                                              
                          





















































































































                                                                                                           























































                                                                              
                                                                     








                                       
                                            









                                                    
                                                                




























                                                                                  
                                                                  












                                                       
                                                                










                                                       
                                                                    








                                            
                                          







                                                   
                                                                

























                                                                                   
                                                                 















                                                                           
                                                               


















                                                                            
                                                                













                                                                                   
                                                                   







                                                             
                                                    

























                                                                         
                                                       


                                                   






                                                            






                                                                   




















                                                                                                         








                                                                                      
                                                    










































































































                                                                                
                                  






































































                                                                              
/*  LEON2 AT697 PCI Host Driver.
 * 
 *  COPYRIGHT (c) 2008.
 *  Cobham Gaisler AB.
 *
 *  Configures the AT697 PCI 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.com/license/LICENSE.
 */

/* Configurable parameters
 * =======================
 *  INT[A..D]#         Select system IRQ (can be tranlated into I/O interrupt)
 *  INT[A..D]#_PIO     Select PIO used to generate I/O interrupt
 *
 * Notes
 * =====
 *  IRQ must not be enabled before all PCI boards have been enabled, the
 *  IRQ is therefore enabled first in init2. The init2() for this driver
 *  is assumed to be executed earlier that all boards and their devices
 *  driver's init2() function.
 *
 */

#include <rtems/bspIo.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <libcpu/byteorder.h>
#include <libcpu/access.h>
#include <pci.h>
#include <pci/cfg.h>

#include <drvmgr/drvmgr.h>
#include <drvmgr/pci_bus.h>
#include <drvmgr/leon2_amba_bus.h>

#include <bsp/at697_pci.h>
#include <leon.h>

/* Configuration options */

#define SYSTEM_MAINMEM_START	0x40000000
#define SYSTEM_MAINMEM_START2	0x60000000

/* Interrupt assignment. Set to other value than 0xff in order to 
 * override defaults and plug&play information
 */
#ifndef AT697_INTA_SYSIRQ
 #define AT697_INTA_SYSIRQ 0xff
#endif
#ifndef AT697_INTB_SYSIRQ
 #define AT697_INTB_SYSIRQ 0xff
#endif
#ifndef AT697_INTC_SYSIRQ
 #define AT697_INTC_SYSIRQ 0xff
#endif
#ifndef AT697_INTD_SYSIRQ
 #define AT697_INTD_SYSIRQ 0xff
#endif

#ifndef AT697_INTA_PIO
 #define AT697_INTA_PIO 0xff
#endif
#ifndef AT697_INTB_PIO
 #define AT697_INTB_PIO 0xff
#endif
#ifndef AT697_INTC_PIO
 #define AT697_INTC_PIO 0xff
#endif
#ifndef AT697_INTD_PIO
 #define AT697_INTD_PIO 0xff
#endif


/* AT697 PCI */
#define AT697_PCI_REG_ADR 0x80000100

/* PCI Window used */
#define PCI_MEM_START 0xa0000000
#define PCI_MEM_END   0xf0000000
#define PCI_MEM_SIZE  (PCI_MEM_END - PCI_MEM_START)

/* #define DEBUG 1 */

#ifdef DEBUG
#define DBG(x...) printf(x)
#else
#define DBG(x...) 
#endif

#define PCI_INVALID_VENDORDEVICEID	0xffffffff
#define PCI_MULTI_FUNCTION		0x80

struct at697pci_regs {
    volatile unsigned int pciid1;        /* 0x80000100 - PCI Device identification register 1         */
    volatile unsigned int pcisc;         /* 0x80000104 - PCI Status & Command                         */
    volatile unsigned int pciid2;        /* 0x80000108 - PCI Device identification register 2         */
    volatile unsigned int pcibhlc;       /* 0x8000010c - BIST, Header type, Cache line size register  */
    volatile unsigned int mbar1;         /* 0x80000110 - Memory Base Address Register 1               */
    volatile unsigned int mbar2;         /* 0x80000114 - Memory Base Address Register 2               */
    volatile unsigned int iobar3;        /* 0x80000118 - IO Base Address Register 3                   */
    volatile unsigned int dummy1[4];     /* 0x8000011c - 0x80000128                                   */ 
    volatile unsigned int pcisid;        /* 0x8000012c - Subsystem identification register            */
    volatile unsigned int dummy2;        /* 0x80000130                                                */
    volatile unsigned int pcicp;         /* 0x80000134 - PCI capabilities pointer register            */
    volatile unsigned int dummy3;        /* 0x80000138                                                */
    volatile unsigned int pcili;         /* 0x8000013c - PCI latency interrupt register               */
    volatile unsigned int pcirt;         /* 0x80000140 - PCI retry, trdy config                       */
    volatile unsigned int pcicw;         /* 0x80000144 - PCI configuration write register             */
    volatile unsigned int pcisa;         /* 0x80000148 - PCI Initiator Start Address                  */
    volatile unsigned int pciiw;         /* 0x8000014c - PCI Initiator Write Register                 */
    volatile unsigned int pcidma;        /* 0x80000150 - PCI DMA configuration register               */
    volatile unsigned int pciis;         /* 0x80000154 - PCI Initiator Status Register                */
    volatile unsigned int pciic;         /* 0x80000158 - PCI Initiator Configuration                  */
    volatile unsigned int pcitpa;        /* 0x8000015c - PCI Target Page Address Register             */   
    volatile unsigned int pcitsc;        /* 0x80000160 - PCI Target Status-Command Register           */
    volatile unsigned int pciite;        /* 0x80000164 - PCI Interrupt Enable Register                */
    volatile unsigned int pciitp;        /* 0x80000168 - PCI Interrupt Pending Register               */
    volatile unsigned int pciitf;        /* 0x8000016c - PCI Interrupt Force Register                 */
    volatile unsigned int pcid;          /* 0x80000170 - PCI Data Register                            */   
    volatile unsigned int pcibe;         /* 0x80000174 - PCI Burst End Register                       */
    volatile unsigned int pcidmaa;       /* 0x80000178 - PCI DMA Address Register                     */
};

/* PCI Interrupt assignment. Connects an PCI interrupt pin (INTA#..INTD#)
 * to a system interrupt number.
 */
unsigned char at697_pci_irq_table[4] =
{
	/* INTA# */	AT697_INTA_SYSIRQ,
	/* INTB# */	AT697_INTB_SYSIRQ,
	/* INTC# */	AT697_INTC_SYSIRQ,
	/* INTD# */	AT697_INTD_SYSIRQ
};

/* PCI Interrupt PIO assignment. Selects which GPIO pin will be used to
 * generate the system IRQ.
 *
 * PCI IRQ -> GPIO -> 4 x I/O select -> System IRQ
 *              ^- pio_table              ^- irq_select
 */
unsigned char at697_pci_irq_pio_table[4] =
{
	/* INTA# */	AT697_INTA_PIO,
	/* INTB# */	AT697_INTB_PIO,
	/* INTC# */	AT697_INTC_PIO,
	/* INTD# */	AT697_INTD_PIO
};

/* Driver private data struture */
struct at697pci_priv {
	struct drvmgr_dev	*dev;
	struct at697pci_regs	*regs;
	int			minor;

	uint32_t		bar1_pci_adr;
	uint32_t		bar2_pci_adr;

	struct drvmgr_map_entry	maps_up[3];
	struct drvmgr_map_entry	maps_down[2];
	struct pcibus_config	config;
};

struct at697pci_priv *at697pcipriv = NULL;
static int at697pci_minor = 0;

int at697pci_init1(struct drvmgr_dev *dev);
int at697pci_init2(struct drvmgr_dev *dev);

/* AT697 PCI DRIVER */

struct drvmgr_drv_ops at697pci_ops = 
{
	.init = {at697pci_init1, at697pci_init2, NULL, NULL},
	.remove = NULL,
	.info = NULL
};

struct leon2_amba_dev_id at697pci_ids[] = 
{
	{LEON2_AMBA_AT697PCI_ID},
	{0}		/* Mark end of table */
};

struct leon2_amba_drv_info at697pci_info =
{
	{
		DRVMGR_OBJ_DRV,			/* Driver */
		NULL,				/* Next driver */
		NULL,				/* Device list */
		DRIVER_LEON2_AMBA_AT697PCI,	/* Driver ID */
		"AT697PCI_DRV",			/* Driver Name */
		DRVMGR_BUS_TYPE_LEON2_AMBA,	/* Bus Type */
		&at697pci_ops,
		NULL,				/* Funcs */
		0,				/* No devices yet */
		sizeof(struct at697pci_priv),	/* let drvmgr alloc private */
	},
	&at697pci_ids[0]
};

void at697pci_register_drv(void)
{
	DBG("Registering AT697 PCI driver\n");
	drvmgr_drv_register(&at697pci_info.general);
}

/*  The configuration access functions uses the DMA functionality of the
 *  AT697 pci controller to be able access all slots
 */

static int at697pci_cfg_r32(pci_dev_t dev, int offset, uint32_t *val)
{
	struct at697pci_regs *regs;
	volatile unsigned int data = 0;
	unsigned int address;
	int bus = PCI_DEV_BUS(dev);
	int slot = PCI_DEV_SLOT(dev);
	int func = PCI_DEV_FUNC(dev);
	int retval;

	if (slot > 15 || (offset & ~0xfc)) {
		*val = 0xffffffff;
		return PCISTS_EINVAL;
	}

	regs = at697pcipriv->regs;

	regs->pciitp = 0xff; /* clear interrupts */ 

	if ( bus == 0 ) {
		/* PCI Access - TYPE 0 */
		address = (1<<(16+slot)) | (func << 8) | offset;
	} else {
		/* PCI access - TYPE 1 */
		address = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) |
				(func << 8) | offset | 1;
	}
	regs->pcisa = address;
	regs->pcidma = 0xa01;
	regs->pcidmaa = (unsigned int) &data;

	while (regs->pciitp == 0)
		;

	regs->pciitp = 0xff; /* clear interrupts */ 

	if (regs->pcisc & 0x20000000)  { /* Master Abort */
		regs->pcisc |= 0x20000000;
		*val = 0xffffffff;
		retval = PCISTS_MSTABRT;
	} else {
		*val = data;
		retval = PCISTS_OK;
	}

	DBG("pci_read - bus: %d, dev: %d, fn: %d, off: %d => addr: %x, val: %x\n",
		bus, slot, func, offset,  address, *val); 

	return retval;
}

static int at697pci_cfg_r16(pci_dev_t dev, int ofs, uint16_t *val)
{
	uint32_t v;
	int retval;

	if (ofs & 1)
		return PCISTS_EINVAL;

	retval = at697pci_cfg_r32(dev, ofs & ~0x3, &v);
	*val = 0xffff & (v >> (8*(ofs & 0x3)));

	return retval;
}

static int at697pci_cfg_r8(pci_dev_t dev, int ofs, uint8_t *val)
{
	uint32_t v;
	int retval;

	retval = at697pci_cfg_r32(dev, ofs & ~0x3, &v);

	*val = 0xff & (v >> (8*(ofs & 3)));

	return retval;
}

static int at697pci_cfg_w32(pci_dev_t dev, int offset, uint32_t val)
{
	struct at697pci_regs *regs;
	volatile unsigned int tmp_val = val;
	unsigned int address;
	int bus = PCI_DEV_BUS(dev);
	int slot = PCI_DEV_SLOT(dev);
	int func = PCI_DEV_FUNC(dev);
	int retval;

	if (slot > 15 || (offset & ~0xfc))
		return PCISTS_EINVAL;

	regs = at697pcipriv->regs;

	regs->pciitp = 0xff; /* clear interrupts */

	if ( bus == 0 ) {
		/* PCI Access - TYPE 0 */
		address = (1<<(16+slot)) | (func << 8) | offset;
	} else {
		/* PCI access - TYPE 1 */
		address = ((bus & 0xff) << 16) | ((slot & 0x1f) << 11) |
				(func << 8) | offset | 1;
	}
	regs->pcisa = address;
	regs->pcidma = 0xb01;
	regs->pcidmaa = (unsigned int) &tmp_val;

	while (regs->pciitp == 0)
		;

	if (regs->pcisc & 0x20000000)  { /* Master Abort */
		regs->pcisc |= 0x20000000;
		retval = PCISTS_MSTABRT;
	} else
		retval = PCISTS_OK;

	regs->pciitp = 0xff; /* clear interrupts */

	DBG("pci_write - bus: %d, dev: %d, fn: %d, off: %d => addr: %x, val: %x\n",
		bus, slot, func, offset, address, val);

	return retval;
}

static int at697pci_cfg_w16(pci_dev_t dev, int ofs, uint16_t val)
{
	uint32_t v;
	int retval;

	if (ofs & 1)
		return PCISTS_EINVAL;

	retval = at697pci_cfg_r32(dev, ofs & ~0x3, &v);
	if (retval != PCISTS_OK)
		return retval;

	v = (v & ~(0xffff << (8*(ofs&3)))) | ((0xffff&val) << (8*(ofs&3)));

	return at697pci_cfg_w32(dev, ofs & ~0x3, v);
}

static int at697pci_cfg_w8(pci_dev_t dev, int ofs, uint8_t val)
{
	uint32_t v;

	at697pci_cfg_r32(dev, ofs & ~0x3, &v);

	v = (v & ~(0xff << (8*(ofs&3)))) | ((0xff&val) << (8*(ofs&3)));

	return at697pci_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 at697_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 at697pci_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 = at697_pci_irq_table[irq_pin];
	}
	return sysIrqNr;
}

static int at697pci_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;

/* AT697 Big-Endian PCI access routines */
static struct pci_access_drv at697pci_access_drv = {
	.cfg =
	{
		at697pci_cfg_r8,
		at697pci_cfg_r16,
		at697pci_cfg_r32,
		at697pci_cfg_w8,
		at697pci_cfg_w16,
		at697pci_cfg_w32,
	},
	.io =
	{	/* AT697 only supports non-standard Big-Endian PCI Bus */
		_ld8,
		_ld_be16,
		_ld_be32,
		_st8,
		_st_be16,
		_st_be32,

	},
	.memreg = &pci_memreg_sparc_be_ops,
	.translate = at697pci_translate,
};

/* Initializes the AT697PCI core hardware
 *
 */
static int at697pci_hw_init(struct at697pci_priv *priv)
{
	struct at697pci_regs *regs = priv->regs;
	unsigned short vendor = regs->pciid1 >> 16;

	/* Must match ATMEL or ESA ID */
	if ( !((vendor == 0x1202) || (vendor == 0x1E0F)) ) {
		/* No AT697 PCI, quit */
		return -1;
	}

	/* If not in system slot we are not host and we must abort.
	 * This is a host only driver.
	 */
	if ((regs->pciis & 0x1000) != 0) {
		return -1;
	}

	/* Reset PCI Core */
	regs->pciic = 0xffffffff;

	/* Mask PCI interrupts */
	regs->pciite = 0;

	/* Map parts of AT697 main memory into PCI (for DMA) */
	regs->mbar1  = priv->bar1_pci_adr;
	regs->mbar2  = priv->bar2_pci_adr;
	regs->pcitpa = (priv->bar1_pci_adr & 0xff000000) |
	               ((priv->bar2_pci_adr>>16) & 0xff00);

	/* Enable PCI master and target memory command response  */
	regs->pcisc |= 0x40 | 0x6;

	/* Set latency timer to 64 */
	regs->pcibhlc = 0x00004000;

	/* Set Inititator configuration so that AHB slave accesses generate memory read/write commands */
	regs->pciic = 0x41;

	return 0;
}

/* Initializes the AT697PCI core and driver, must be called before calling init_pci() 
 *
 * Return values
 *  0             Successful initalization
 *  -1            Error during initialization.
 */
static int at697pci_init(struct at697pci_priv *priv)
{
	int pin;
	union drvmgr_key_value *value;
	char keyname_sysirq[6];
	char keyname_pio[10];

	/* PCI core, init private structure */
	priv->regs = (struct at697pci_regs *) AT697_PCI_REG_ADR;

	/* Init PCI interrupt assignment table to all use the interrupt routed
	 * through the GPIO core.
	 *
	 * INT[A..D]# selects system IRQ (and I/O interrupt)
	 * INT[A..D]#_PIO selects PIO used to generate I/O interrupt
	 */
	strcpy(keyname_sysirq, "INTX#");
	strcpy(keyname_pio, "INTX#_PIO");
	for (pin=1; pin<5; pin++) {
		if ( at697_pci_irq_table[pin-1] == 0xff ) {
			/* User may override hardcoded IRQ setup */
			keyname_sysirq[3] = 'A' + (pin-1);
			value = drvmgr_dev_key_get(priv->dev,
					keyname_sysirq, KEY_TYPE_INT);
			if ( value )
				at697_pci_irq_table[pin-1] = value->i;
		}
		if ( at697_pci_irq_pio_table[pin-1] == 0xff ) {
			/* User may override hardcoded IRQ setup */
			keyname_pio[3] = 'A' + (pin-1);
			value = drvmgr_dev_key_get(priv->dev,
						keyname_pio, KEY_TYPE_INT);
			if ( value )
				at697_pci_irq_pio_table[pin-1] = value->i;
		}
	}

	/* Use GRPCI target BAR1 and BAR2 to map CPU RAM to PCI, this is to
	 * make it possible for PCI peripherals to do DMA directly to CPU memory
	 *
	 * Defualt is to map system RAM at pci address 0x40000000 and system
	 * SDRAM to pci address 0x60000000
	 */
	value = drvmgr_dev_key_get(priv->dev, "tgtbar1", KEY_TYPE_INT);
	if (value)
		priv->bar1_pci_adr = value->i;
	else
		priv->bar1_pci_adr = SYSTEM_MAINMEM_START; /* default */

	value = drvmgr_dev_key_get(priv->dev, "tgtbar2", KEY_TYPE_INT);
	if (value)
		priv->bar2_pci_adr = value->i;
	else
		priv->bar2_pci_adr = SYSTEM_MAINMEM_START2; /* default */

	/* Init the PCI Core */
	if ( at697pci_hw_init(priv) ) {
		return -3;
	}

	/* Down streams translation table */
	priv->maps_down[0].name = "AMBA -> PCI MEM Window";
	priv->maps_down[0].size = 0xF0000000 - 0xA0000000;
	priv->maps_down[0].from_adr = (void *)0xA0000000;
	priv->maps_down[0].to_adr = (void *)0xA0000000;
	/* End table */
	priv->maps_down[1].size = 0;

	/* Up streams translation table, 2x16Mb mapped 1:1  */
	priv->maps_up[0].name = "Target BAR0 -> AMBA";
	priv->maps_up[0].size = 0x01000000; /* 16Mb BAR1 */
	priv->maps_up[0].from_adr = (void *)priv->bar1_pci_adr;
	priv->maps_up[0].to_adr = (void *)priv->bar1_pci_adr;
	priv->maps_up[1].name = "Target BAR1 -> AMBA";
	priv->maps_up[1].size = 0x01000000; /* 16Mb BAR2 */
	priv->maps_up[1].from_adr = (void *)priv->bar2_pci_adr;
	priv->maps_up[1].to_adr = (void *)priv->bar2_pci_adr;
	/* End table */
	priv->maps_up[2].size = 0;

	return 0;
}

/* Called when a core is found with the AMBA device and vendor ID 
 * given in at697pci_ids[].
 */
int at697pci_init1(struct drvmgr_dev *dev)
{
	struct at697pci_priv *priv;
	struct pci_auto_setup at697pci_auto_cfg;

	DBG("AT697PCI[%d] on bus %s\n", dev->minor_drv,
		dev->parent->dev->name);

	if ( at697pci_minor != 0 ) {
		DBG("Driver only supports one PCI core\n");
		return DRVMGR_FAIL;
	}

	at697pcipriv = priv = dev->priv;
	if ( !priv )
		return DRVMGR_NOMEM;

	priv->dev = dev;
	priv->minor = at697pci_minor++;

	if (at697pci_init(priv)) {
		DBG("Failed to initialize at697pci driver\n");
		return DRVMGR_EIO;
	}

	/* Host is always Big-Endian */
	pci_endian = PCI_BIG_ENDIAN;

	if (pci_access_drv_register(&at697pci_access_drv)) {
		/* Access routines registration failed */
		return DRVMGR_FAIL;
	}

	/* Prepare memory MAP */
	at697pci_auto_cfg.options = 0;
	at697pci_auto_cfg.mem_start = 0;
	at697pci_auto_cfg.mem_size = 0;
	at697pci_auto_cfg.memio_start = PCI_MEM_START;
	at697pci_auto_cfg.memio_size = PCI_MEM_SIZE;
	at697pci_auto_cfg.io_start = 0;
	at697pci_auto_cfg.io_size = 0;
	at697pci_auto_cfg.irq_map = at697pci_bus0_irq_map;
	at697pci_auto_cfg.irq_route = NULL; /* use standard routing */
	pci_config_register(&at697pci_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 at697pci_init2(struct drvmgr_dev *dev)
{
#if 0
	struct at697pci_priv *priv = dev->priv;
#endif
	int pin, irq, pio, ioport;
	LEON_Register_Map *regs = (LEON_Register_Map *)0x80000000;

	/* Enable interrupts now that init1 has been reached for all devices
	 * on the bus.
	 */

	for (pin=1; pin<5; pin++) {
		irq = at697_pci_irq_table[pin-1];
		pio = at697_pci_irq_pio_table[pin-1];
		if ( (pio < 16) && (irq >= 4) && (irq <= 7) ) {
			/* AT697 I/O IRQ, we know how to set up this 
			 *
			 * IRQ 4 -> I/O 0
			 * IRQ 5 -> I/O 1
			 * IRQ 6 -> I/O 2
			 * IRQ 7 -> I/O 3
			 */
			ioport = irq - 4;

			/* First disable interrupts */
			regs->PIO_Interrupt &= ~(0xff << (ioport * 8));
			/* Set PIO as input pin */
			regs->PIO_Direction &= ~(1 << pio);
			/* Set Low Level sensitivity */
			regs->PIO_Interrupt |= ((0x80 | pio) << (ioport * 8));
		}
	}

	/* Unmask Interrupt */
	/*priv->regs->pciite = 0xff;*/

	return DRVMGR_OK;
}