// SPDX-License-Identifier: GPL-2.0 /* * leon_pci_grpci1.c: GRPCI1 Host PCI driver * * Copyright (C) 2013 Aeroflex Gaisler AB * * This GRPCI1 driver does not support PCI interrupts taken from * GPIO pins. Interrupt generation at PCI parity and system error * detection is by default turned off since some GRPCI1 cores does * not support detection. It can be turned on from the bootloader * using the all_pci_errors property. * * Contributors: Daniel Hellstrom <daniel@gaisler.com>
*/
staticint grpci1_cfg_w16(struct grpci1_priv *priv, unsignedint bus, unsignedint devfn, int where, u32 val)
{ int ret;
u32 v;
if (where & 0x1) return -EINVAL;
ret = grpci1_cfg_r32(priv, bus, devfn, where&~3, &v); if (ret) return ret;
v = (v & ~(0xffff << (8 * (where & 0x3)))) |
((0xffff & val) << (8 * (where & 0x3))); return grpci1_cfg_w32(priv, bus, devfn, where & ~0x3, v);
}
staticint grpci1_cfg_w8(struct grpci1_priv *priv, unsignedint bus, unsignedint devfn, int where, u32 val)
{ int ret;
u32 v;
ret = grpci1_cfg_r32(priv, bus, devfn, where & ~0x3, &v); if (ret != 0) return ret;
v = (v & ~(0xff << (8 * (where & 0x3)))) |
((0xff & val) << (8 * (where & 0x3))); return grpci1_cfg_w32(priv, bus, devfn, where & ~0x3, v);
}
/* Read from Configuration Space. When entering here the PCI layer has taken * the pci_lock spinlock and IRQ is off.
*/ staticint grpci1_read_config(struct pci_bus *bus, unsignedint devfn, int where, int size, u32 *val)
{ struct grpci1_priv *priv = grpci1priv; unsignedint busno = bus->number; int ret;
/* Write to Configuration Space. When entering here the PCI layer has taken * the pci_lock spinlock and IRQ is off.
*/ staticint grpci1_write_config(struct pci_bus *bus, unsignedint devfn, int where, int size, u32 val)
{ struct grpci1_priv *priv = grpci1priv; unsignedint busno = bus->number;
/* GENIRQ IRQ chip implementation for grpci1 irqmode=0..2. In configuration * 3 where all PCI Interrupts has a separate IRQ on the system IRQ controller * this is not needed and the standard IRQ controller can be used.
*/
/* Handle one or multiple IRQs from the PCI core */ staticvoid grpci1_pci_flow_irq(struct irq_desc *desc)
{ struct grpci1_priv *priv = grpci1priv; int i, ack = 0; unsignedint irqreg;
/* PCI Interrupt? */ if (irqreg & IRQ_INTX) { /* Call respective PCI Interrupt handler */ for (i = 0; i < 4; i++) { if (irqreg & (1 << i))
generic_handle_irq(priv->irq_map[i]);
}
ack = 1;
}
/* * Call "first level" IRQ chip end-of-irq handler. It will ACK LEON IRQ * Controller, this must be done after IRQ sources have been handled to * avoid double IRQ generation
*/ if (ack)
desc->irq_data.chip->irq_eoi(&desc->irq_data);
}
/* * Initialize mappings AMBA<->PCI, clear IRQ state, setup PCI interface * * Target BARs: * BAR0: unused in this implementation * BAR1: peripheral DMA to host's memory (size at least 256MByte) * BAR2..BAR5: not implemented in hardware
*/ staticvoid grpci1_hw_init(struct grpci1_priv *priv)
{
u32 ahbadr, bar_sz, data, pciadr; struct grpci1_regs __iomem *regs = priv->regs;
/* set 1:1 mapping between AHB -> PCI memory space */
REGSTORE(regs->cfg_stat, priv->pci_area & 0xf0000000);
/* map PCI accesses to target BAR1 to Linux kernel memory 1:1 */
ahbadr = 0xf0000000 & (u32)__pa(PAGE_ALIGN((unsignedlong) &_end));
REGSTORE(regs->page1, ahbadr);
/* translate I/O accesses to 0, I/O Space always @ PCI low 64Kbytes */
REGSTORE(regs->iomap, REGLOAD(regs->iomap) & 0x0000ffff);
/* disable and clear pending interrupts */
REGSTORE(regs->irq, 0);
/* Setup BAR0 outside access range so that it does not conflict with * peripheral DMA. There is no need to set up the PAGE0 register.
*/
grpci1_cfg_w32(priv, TGT, 0, PCI_BASE_ADDRESS_0, 0xffffffff);
grpci1_cfg_r32(priv, TGT, 0, PCI_BASE_ADDRESS_0, &bar_sz);
bar_sz = ~bar_sz + 1;
pciadr = priv->pci_area - bar_sz;
grpci1_cfg_w32(priv, TGT, 0, PCI_BASE_ADDRESS_0, pciadr);
/* * Setup the Host's PCI Target BAR1 for other peripherals to access, * and do DMA to the host's memory.
*/
grpci1_cfg_w32(priv, TGT, 0, PCI_BASE_ADDRESS_1, ahbadr);
/* * 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.
*/
grpci1_cfg_w8(priv, TGT, 0, PCI_CACHE_LINE_SIZE, 0xff);
grpci1_cfg_w8(priv, TGT, 0, PCI_LATENCY_TIMER, 0x40);
/* set as bus master, enable pci memory responses, clear status bits */
grpci1_cfg_r32(priv, TGT, 0, PCI_COMMAND, &data);
data |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
grpci1_cfg_w32(priv, TGT, 0, PCI_COMMAND, data);
}
/* find device register base address */
res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(&ofdev->dev, res); if (IS_ERR(regs)) return PTR_ERR(regs);
/* * check that we're in Host Slot and that we can act as a Host Bridge * and not only as target/peripheral.
*/
cfg = REGLOAD(regs->cfg_stat); if ((cfg & CFGSTAT_HOST) == 0) {
dev_err(&ofdev->dev, "not in host system slot\n"); return -EIO;
}
/* check that BAR1 support 256 MByte so that we can map kernel space */
REGSTORE(regs->page1, 0xffffffff);
size = ~REGLOAD(regs->page1) + 1; if (size < 0x10000000) {
dev_err(&ofdev->dev, "BAR1 must be at least 256MByte\n"); return -EIO;
}
/* hardware must support little-endian PCI (byte-twisting) */ if ((REGLOAD(regs->page0) & PAGE0_BTEN) == 0) {
dev_err(&ofdev->dev, "byte-twisting is required\n"); return -EIO;
}
priv->regs = regs;
priv->irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
dev_info(&ofdev->dev, "host found at 0x%p, irq%d\n", regs, priv->irq);
/* Find PCI Memory, I/O and Configuration Space Windows */
priv->pci_area = ofdev->resource[1].start;
priv->pci_area_end = ofdev->resource[1].end+1;
priv->pci_io = ofdev->resource[2].start;
priv->pci_conf = ofdev->resource[2].start + 0x10000;
priv->pci_conf_end = priv->pci_conf + 0x10000;
priv->pci_io_va = (unsignedlong)ioremap(priv->pci_io, 0x10000); if (!priv->pci_io_va) {
dev_err(&ofdev->dev, "unable to map PCI I/O area\n"); return -EIO;
}
printk(KERN_INFO "GRPCI1: MEMORY SPACE [0x%08lx - 0x%08lx]\n" " I/O SPACE [0x%08lx - 0x%08lx]\n" " CONFIG SPACE [0x%08lx - 0x%08lx]\n",
priv->pci_area, priv->pci_area_end-1,
priv->pci_io, priv->pci_conf-1,
priv->pci_conf, priv->pci_conf_end-1);
/* * I/O Space resources in I/O Window mapped into Virtual Adr Space * We never use low 4KB because some devices seem have problems using * address 0.
*/
priv->info.io_space.name = "GRPCI1 PCI I/O Space";
priv->info.io_space.start = priv->pci_io_va + 0x1000;
priv->info.io_space.end = priv->pci_io_va + 0x10000 - 1;
priv->info.io_space.flags = IORESOURCE_IO;
/* * grpci1 has no prefetchable memory, map everything as * non-prefetchable memory
*/
priv->info.mem_space.name = "GRPCI1 PCI MEM Space";
priv->info.mem_space.start = priv->pci_area;
priv->info.mem_space.end = priv->pci_area_end - 1;
priv->info.mem_space.flags = IORESOURCE_MEM;
if (request_resource(&iomem_resource, &priv->info.mem_space) < 0) {
dev_err(&ofdev->dev, "unable to request PCI memory area\n");
err = -ENOMEM; goto err1;
}
if (request_resource(&ioport_resource, &priv->info.io_space) < 0) {
dev_err(&ofdev->dev, "unable to request PCI I/O area\n");
err = -ENOMEM; goto err2;
}
/* * Get PCI Interrupt to System IRQ mapping and setup IRQ handling * Error IRQ. All PCI and PCI-Error interrupts are shared using the * same system IRQ.
*/
leon_update_virq_handling(priv->irq, grpci1_pci_flow_irq, "pcilvl", 0);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.