/* 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.org/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 #include #include #include #include #include #include #include #include #include #include #include #include /* 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 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; }