in kernel/leon_pci_grpci1.c [511:697]
static int grpci1_of_probe(struct platform_device *ofdev)
{
struct grpci1_regs __iomem *regs;
struct grpci1_priv *priv;
int err, len;
const int *tmp;
u32 cfg, size, err_mask;
struct resource *res;
if (grpci1priv) {
dev_err(&ofdev->dev, "only one GRPCI1 supported\n");
return -ENODEV;
}
if (ofdev->num_resources < 3) {
dev_err(&ofdev->dev, "not enough APB/AHB resources\n");
return -EIO;
}
priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(&ofdev->dev, "memory allocation failed\n");
return -ENOMEM;
}
platform_set_drvdata(ofdev, priv);
priv->dev = &ofdev->dev;
/* 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 = (unsigned long)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;
}
/* setup maximum supported PCI buses */
priv->info.busn.name = "GRPCI1 busn";
priv->info.busn.start = 0;
priv->info.busn.end = 15;
grpci1priv = priv;
/* Initialize hardware */
grpci1_hw_init(priv);
/*
* 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);
priv->irq_map[0] = grpci1_build_device_irq(1);
priv->irq_map[1] = grpci1_build_device_irq(2);
priv->irq_map[2] = grpci1_build_device_irq(3);
priv->irq_map[3] = grpci1_build_device_irq(4);
priv->irq_err = grpci1_build_device_irq(5);
printk(KERN_INFO " PCI INTA..D#: IRQ%d, IRQ%d, IRQ%d, IRQ%d\n",
priv->irq_map[0], priv->irq_map[1], priv->irq_map[2],
priv->irq_map[3]);
/* Enable IRQs on LEON IRQ controller */
err = devm_request_irq(&ofdev->dev, priv->irq, grpci1_jump_interrupt, 0,
"GRPCI1_JUMP", priv);
if (err) {
dev_err(&ofdev->dev, "ERR IRQ request failed: %d\n", err);
goto err3;
}
/* Setup IRQ handler for access errors */
err = devm_request_irq(&ofdev->dev, priv->irq_err,
grpci1_err_interrupt, IRQF_SHARED, "GRPCI1_ERR",
priv);
if (err) {
dev_err(&ofdev->dev, "ERR VIRQ request failed: %d\n", err);
goto err3;
}
tmp = of_get_property(ofdev->dev.of_node, "all_pci_errors", &len);
if (tmp && (len == 4)) {
priv->pci_err_mask = ALL_PCI_ERRORS;
err_mask = IRQ_ALL_ERRORS << IRQ_MASK_BIT;
} else {
priv->pci_err_mask = DEF_PCI_ERRORS;
err_mask = IRQ_DEF_ERRORS << IRQ_MASK_BIT;
}
/*
* Enable Error Interrupts. PCI interrupts are unmasked once request_irq
* is called by the PCI Device drivers
*/
REGSTORE(regs->irq, err_mask);
/* Init common layer and scan buses */
priv->info.ops = &grpci1_ops;
priv->info.map_irq = grpci1_map_irq;
leon_pci_init(ofdev, &priv->info);
return 0;
err3:
release_resource(&priv->info.io_space);
err2:
release_resource(&priv->info.mem_space);
err1:
iounmap((void __iomem *)priv->pci_io_va);
grpci1priv = NULL;
return err;
}