in controller/pci-v3-semi.c [706:893]
static int v3_pci_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct resource *regs;
struct resource_entry *win;
struct v3_pci *v3;
struct pci_host_bridge *host;
struct clk *clk;
u16 val;
int irq;
int ret;
host = devm_pci_alloc_host_bridge(dev, sizeof(*v3));
if (!host)
return -ENOMEM;
host->ops = &v3_pci_ops;
v3 = pci_host_bridge_priv(host);
host->sysdata = v3;
v3->dev = dev;
/* 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;
}
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
v3->base = devm_ioremap_resource(dev, regs);
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);
return pci_host_probe(host);
}