/* * The V3 PCI interface chip in Integrator provides several windows from * local bus memory into the PCI memory areas. Unfortunately, there * are not really enough windows for our usage, therefore we reuse * one of the windows for access to PCI configuration space. On the * Integrator/AP, the memory map is as follows: * * Local Bus Memory Usage * * 40000000 - 4FFFFFFF PCI memory. 256M non-prefetchable * 50000000 - 5FFFFFFF PCI memory. 256M prefetchable * 60000000 - 60FFFFFF PCI IO. 16M * 61000000 - 61FFFFFF PCI Configuration. 16M * * There are three V3 windows, each described by a pair of V3 registers. * These are LB_BASE0/LB_MAP0, LB_BASE1/LB_MAP1 and LB_BASE2/LB_MAP2. * Base0 and Base1 can be used for any type of PCI memory access. Base2 * can be used either for PCI I/O or for I20 accesses. By default, uHAL * uses this only for PCI IO space. * * Normally these spaces are mapped using the following base registers: * * Usage Local Bus Memory Base/Map registers used * * Mem 40000000 - 4FFFFFFF LB_BASE0/LB_MAP0 * Mem 50000000 - 5FFFFFFF LB_BASE1/LB_MAP1 * IO 60000000 - 60FFFFFF LB_BASE2/LB_MAP2 * Cfg 61000000 - 61FFFFFF * * This means that I20 and PCI configuration space accesses will fail. * When PCI configuration accesses are needed (via the uHAL PCI * configuration space primitives) we must remap the spaces as follows: * * Usage Local Bus Memory Base/Map registers used * * Mem 40000000 - 4FFFFFFF LB_BASE0/LB_MAP0 * Mem 50000000 - 5FFFFFFF LB_BASE0/LB_MAP0 * IO 60000000 - 60FFFFFF LB_BASE2/LB_MAP2 * Cfg 61000000 - 61FFFFFF LB_BASE1/LB_MAP1 * * To make this work, the code depends on overlapping windows working. * The V3 chip translates an address by checking its range within * each of the BASE/MAP pairs in turn (in ascending register number * order). It will use the first matching pair. So, for example, * if the same address is mapped by both LB_BASE0/LB_MAP0 and * LB_BASE1/LB_MAP1, the V3 will use the translation from * LB_BASE0/LB_MAP0. * * To allow PCI Configuration space access, the code enlarges the * window mapped by LB_BASE0/LB_MAP0 from 256M to 512M. This occludes * the windows currently mapped by LB_BASE1/LB_MAP1 so that it can * be remapped for use by configuration cycles. * * At the end of the PCI Configuration space accesses, * LB_BASE1/LB_MAP1 is reset to map PCI Memory. Finally the window * mapped by LB_BASE0/LB_MAP0 is reduced in size from 512M to 256M to * reveal the now restored LB_BASE1/LB_MAP1 window. * * NOTE: We do not set up I2O mapping. I suspect that this is only * for an intelligent (target) device. Using I2O disables most of * the mappings into PCI memory.
*/ staticvoid __iomem *v3_map_bus(struct pci_bus *bus, unsignedint devfn, int offset)
{ struct v3_pci *v3 = bus->sysdata; unsignedint address, mapaddress, busnr;
busnr = bus->number; if (busnr == 0) { int slot = PCI_SLOT(devfn);
/* * local bus segment so need a type 0 config cycle * * build the PCI configuration "address" with one-hot in * A31-A11 * * mapaddress: * 3:1 = config cycle (101) * 0 = PCI A1 & A0 are 0 (0)
*/
address = PCI_FUNC(devfn) << 8;
mapaddress = V3_LB_MAP_TYPE_CONFIG;
if (slot > 12) /* * high order bits are handled by the MAP register
*/
mapaddress |= BIT(slot - 5); else /* * low order bits handled directly in the address
*/
address |= BIT(slot + 11);
} else { /* * not the local bus segment so need a type 1 config cycle * * address: * 23:16 = bus number * 15:11 = slot number (7:3 of devfn) * 10:8 = func number (2:0 of devfn) * * mapaddress: * 3:1 = config cycle (101) * 0 = PCI A1 & A0 from host bus (1)
*/
mapaddress = V3_LB_MAP_TYPE_CONFIG | V3_LB_MAP_AD_LOW_EN;
address = (busnr << 16) | (devfn << 8);
}
/* * Set up base0 to see all 512Mbytes of memory space (not * prefetchable), this frees up base1 for re-use by * configuration memory
*/
writel(v3_addr_to_lb_base(v3->non_pre_mem) |
V3_LB_BASE_ADR_SIZE_512MB | V3_LB_BASE_ENABLE,
v3->base + V3_LB_BASE0);
/* * Set up base1/map1 to point into configuration space. * The config mem is always 16MB.
*/
writel(v3_addr_to_lb_base(v3->config_mem) |
V3_LB_BASE_ADR_SIZE_16MB | V3_LB_BASE_ENABLE,
v3->base + V3_LB_BASE1);
writew(mapaddress, v3->base + V3_LB_MAP1);
return v3->config_base + address + offset;
}
staticvoid v3_unmap_bus(struct v3_pci *v3)
{ /* * Reassign base1 for use by prefetchable PCI memory
*/
writel(v3_addr_to_lb_base(v3->pre_mem) |
V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_PREFETCH |
V3_LB_BASE_ENABLE,
v3->base + V3_LB_BASE1);
writew(v3_addr_to_lb_map(v3->pre_bus_addr) |
V3_LB_MAP_TYPE_MEM, /* was V3_LB_MAP_TYPE_MEM_MULTIPLE */
v3->base + V3_LB_MAP1);
/* * And shrink base0 back to a 256M window (NOTE: MAP0 already correct)
*/
writel(v3_addr_to_lb_base(v3->non_pre_mem) |
V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_ENABLE,
v3->base + V3_LB_BASE0);
}
staticint v3_pci_read_config(struct pci_bus *bus, unsignedint fn, int config, int size, u32 *value)
{ struct v3_pci *v3 = bus->sysdata; int ret;
v3->map =
syscon_regmap_lookup_by_compatible("arm,integrator-ap-syscon"); if (IS_ERR(v3->map)) {
dev_err(v3->dev, "no syscon\n"); return -ENODEV;
}
regmap_read(v3->map, INTEGRATOR_SC_PCI_OFFSET, &val); /* Take the PCI bridge out of reset, clear IRQs */
regmap_write(v3->map, INTEGRATOR_SC_PCI_OFFSET,
INTEGRATOR_SC_PCI_ENABLE |
INTEGRATOR_SC_PCI_INTCLR);
if (!(val & INTEGRATOR_SC_PCI_ENABLE)) { /* If we were in reset we need to sleep a bit */
msleep(230);
/* Set the physical base for the controller itself */
writel(0x6200, v3->base + V3_LB_IO_BASE);
/* Wait for the mailbox to settle after reset */ do {
writeb(0xaa, v3->base + V3_MAIL_DATA);
writeb(0x55, v3->base + V3_MAIL_DATA + 4);
} while (readb(v3->base + V3_MAIL_DATA) != 0xaa &&
readb(v3->base + V3_MAIL_DATA) != 0x55);
}
if (pci_addr & ~V3_PCI_BASE_M_ADR_BASE) {
dev_err(dev, "illegal range, only PCI bits 31..20 allowed\n"); return -EINVAL;
}
val = ((u32)pci_addr) & V3_PCI_BASE_M_ADR_BASE;
*pci_base = val;
if (cpu_addr & ~V3_PCI_MAP_M_MAP_ADR) {
dev_err(dev, "illegal range, only CPU bits 31..20 allowed\n"); return -EINVAL;
}
val = ((u32)cpu_addr) & V3_PCI_MAP_M_MAP_ADR;
switch (resource_size(entry->res)) { case SZ_1M:
val |= V3_LB_BASE_ADR_SIZE_1MB; break; case SZ_2M:
val |= V3_LB_BASE_ADR_SIZE_2MB; break; case SZ_4M:
val |= V3_LB_BASE_ADR_SIZE_4MB; break; case SZ_8M:
val |= V3_LB_BASE_ADR_SIZE_8MB; break; case SZ_16M:
val |= V3_LB_BASE_ADR_SIZE_16MB; break; case SZ_32M:
val |= V3_LB_BASE_ADR_SIZE_32MB; break; case SZ_64M:
val |= V3_LB_BASE_ADR_SIZE_64MB; break; case SZ_128M:
val |= V3_LB_BASE_ADR_SIZE_128MB; break; case SZ_256M:
val |= V3_LB_BASE_ADR_SIZE_256MB; break; case SZ_512M:
val |= V3_LB_BASE_ADR_SIZE_512MB; break; case SZ_1G:
val |= V3_LB_BASE_ADR_SIZE_1GB; break; case SZ_2G:
val |= V3_LB_BASE_ADR_SIZE_2GB; break; default:
dev_err(v3->dev, "illegal dma memory chunk size\n"); return -EINVAL;
}
val |= V3_PCI_MAP_M_REG_EN | V3_PCI_MAP_M_ENABLE;
*pci_map = val;
/* Get and enable host clock */
clk = devm_clk_get(dev, NULL); if (IS_ERR(clk)) {
dev_err(dev, "clock not found\n"); return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk); if (ret) {
dev_err(dev, "unable to enable clock\n"); return ret;
}
v3->base = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); if (IS_ERR(v3->base)) return PTR_ERR(v3->base); /* * The hardware has a register with the physical base address * of the V3 controller itself, verify that this is the same * as the physical memory we've remapped it from.
*/ if (readl(v3->base + V3_LB_IO_BASE) != (regs->start >> 16))
dev_err(dev, "V3_LB_IO_BASE = %08x but device is @%pR\n",
readl(v3->base + V3_LB_IO_BASE), regs);
/* Configuration space is 16MB directly mapped */
regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (resource_size(regs) != SZ_16M) {
dev_err(dev, "config mem is not 16MB!\n"); return -EINVAL;
}
v3->config_mem = regs->start;
v3->config_base = devm_ioremap_resource(dev, regs); if (IS_ERR(v3->config_base)) return PTR_ERR(v3->config_base);
/* Get and request error IRQ resource */
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
ret = devm_request_irq(dev, irq, v3_irq, 0, "PCIv3 error", v3); if (ret < 0) {
dev_err(dev, "unable to request PCIv3 error IRQ %d (%d)\n",
irq, ret); return ret;
}
/* * Unlock V3 registers, but only if they were previously locked.
*/ if (readw(v3->base + V3_SYSTEM) & V3_SYSTEM_M_LOCK)
writew(V3_SYSTEM_UNLOCK, v3->base + V3_SYSTEM);
/* Disable all slave access while we set up the windows */
val = readw(v3->base + V3_PCI_CMD);
val &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
writew(val, v3->base + V3_PCI_CMD);
/* Put the PCI bus into reset */
val = readw(v3->base + V3_SYSTEM);
val &= ~V3_SYSTEM_M_RST_OUT;
writew(val, v3->base + V3_SYSTEM);
/* Retry until we're ready */
val = readw(v3->base + V3_PCI_CFG);
val |= V3_PCI_CFG_M_RETRY_EN;
writew(val, v3->base + V3_PCI_CFG);
/* Set up the local bus protocol */
val = readw(v3->base + V3_LB_CFG);
val |= V3_LB_CFG_LB_BE_IMODE; /* Byte enable input */
val |= V3_LB_CFG_LB_BE_OMODE; /* Byte enable output */
val &= ~V3_LB_CFG_LB_ENDIAN; /* Little endian */
val &= ~V3_LB_CFG_LB_PPC_RDY; /* TODO: when using on PPC403Gx, set to 1 */
writew(val, v3->base + V3_LB_CFG);
/* Enable the PCI bus master */
val = readw(v3->base + V3_PCI_CMD);
val |= PCI_COMMAND_MASTER;
writew(val, v3->base + V3_PCI_CMD);
/* Get the I/O and memory ranges from DT */
resource_list_for_each_entry(win, &host->windows) {
ret = v3_pci_setup_resource(v3, host, win); if (ret) {
dev_err(dev, "error setting up resources\n"); return ret;
}
}
ret = v3_pci_parse_map_dma_ranges(v3, np); if (ret) return ret;
/* * Disable PCI to host IO cycles, enable I/O buffers @3.3V, * set AD_LOW0 to 1 if one of the LB_MAP registers choose * to use this (should be unused).
*/
writel(0x00000000, v3->base + V3_PCI_IO_BASE);
val = V3_PCI_CFG_M_IO_REG_DIS | V3_PCI_CFG_M_IO_DIS |
V3_PCI_CFG_M_EN3V | V3_PCI_CFG_M_AD_LOW0; /* * DMA read and write from PCI bus commands types
*/
val |= V3_PCI_CFG_TYPE_DEFAULT << V3_PCI_CFG_M_RTYPE_SHIFT;
val |= V3_PCI_CFG_TYPE_DEFAULT << V3_PCI_CFG_M_WTYPE_SHIFT;
writew(val, v3->base + V3_PCI_CFG);
/* * Set the V3 FIFO such that writes have higher priority than * reads, and local bus write causes local bus read fifo flush * on aperture 1. Same for PCI.
*/
writew(V3_FIFO_PRIO_LB_RD1_FLUSH_AP1 |
V3_FIFO_PRIO_LB_RD0_FLUSH_AP1 |
V3_FIFO_PRIO_PCI_RD1_FLUSH_AP1 |
V3_FIFO_PRIO_PCI_RD0_FLUSH_AP1,
v3->base + V3_FIFO_PRIORITY);
/* * Clear any error interrupts, and enable parity and write error * interrupts
*/
writeb(0, v3->base + V3_LB_ISTAT);
val = readw(v3->base + V3_LB_CFG);
val |= V3_LB_CFG_LB_LB_INT;
writew(val, v3->base + V3_LB_CFG);
writeb(V3_LB_ISTAT_PCI_WR | V3_LB_ISTAT_PCI_PERR,
v3->base + V3_LB_IMASK);
/* Special Integrator initialization */ if (of_device_is_compatible(np, "arm,integrator-ap-pci")) {
ret = v3_integrator_init(v3); if (ret) return ret;
}
/* Post-init: enable PCI memory and invalidate (master already on) */
val = readw(v3->base + V3_PCI_CMD);
val |= PCI_COMMAND_MEMORY | PCI_COMMAND_INVALIDATE;
writew(val, v3->base + V3_PCI_CMD);
/* Clear pending interrupts */
writeb(0, v3->base + V3_LB_ISTAT); /* Read or write errors and parity errors cause interrupts */
writeb(V3_LB_ISTAT_PCI_RD | V3_LB_ISTAT_PCI_WR | V3_LB_ISTAT_PCI_PERR,
v3->base + V3_LB_IMASK);
/* Take the PCI bus out of reset so devices can initialize */
val = readw(v3->base + V3_SYSTEM);
val |= V3_SYSTEM_M_RST_OUT;
writew(val, v3->base + V3_SYSTEM);
/* * Re-lock the system register.
*/
val = readw(v3->base + V3_SYSTEM);
val |= V3_SYSTEM_M_LOCK;
writew(val, v3->base + V3_SYSTEM);
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.