static int grpci2_of_probe()

in kernel/leon_pci_grpci2.c [684:888]


static int grpci2_of_probe(struct platform_device *ofdev)
{
	struct grpci2_regs __iomem *regs;
	struct grpci2_priv *priv;
	int err, i, len;
	const int *tmp;
	unsigned int capability;

	if (grpci2priv) {
		printk(KERN_ERR "GRPCI2: only one GRPCI2 core supported\n");
		return -ENODEV;
	}

	if (ofdev->num_resources < 3) {
		printk(KERN_ERR "GRPCI2: not enough APB/AHB resources\n");
		return -EIO;
	}

	/* Find Device Address */
	regs = of_ioremap(&ofdev->resource[0], 0,
			  resource_size(&ofdev->resource[0]),
			  "grlib-grpci2 regs");
	if (regs == NULL) {
		printk(KERN_ERR "GRPCI2: ioremap failed\n");
		return -EIO;
	}

	/*
	 * Check that we're in Host Slot and that we can act as a Host Bridge
	 * and not only as target.
	 */
	capability = REGLOAD(regs->sts_cap);
	if ((capability & STS_HOST) || !(capability & STS_MST)) {
		printk(KERN_INFO "GRPCI2: not in host system slot\n");
		err = -EIO;
		goto err1;
	}

	priv = grpci2priv = kzalloc(sizeof(struct grpci2_priv), GFP_KERNEL);
	if (grpci2priv == NULL) {
		err = -ENOMEM;
		goto err1;
	}
	priv->regs = regs;
	priv->irq = ofdev->archdata.irqs[0]; /* BASE IRQ */
	priv->irq_mode = (capability & STS_IRQMODE) >> STS_IRQMODE_BIT;

	printk(KERN_INFO "GRPCI2: host found at %p, irq%d\n", regs, priv->irq);

	/* Byte twisting should be made configurable from kernel command line */
	priv->bt_enabled = 1;

	/* Let user do custom Target BAR assignment */
	tmp = of_get_property(ofdev->dev.of_node, "barcfg", &len);
	if (tmp && (len == 2*4*6))
		memcpy(priv->tgtbars, tmp, 2*4*6);
	else
		memset(priv->tgtbars, -1, 2*4*6);

	/* Limit IRQ unmasking in irq_mode 2 and 3 */
	tmp = of_get_property(ofdev->dev.of_node, "irq_mask", &len);
	if (tmp && (len == 4))
		priv->do_reset = *tmp;
	else
		priv->irq_mask = 0xf;

	/* Optional PCI reset. Force PCI reset on startup */
	tmp = of_get_property(ofdev->dev.of_node, "reset", &len);
	if (tmp && (len == 4))
		priv->do_reset = *tmp;
	else
		priv->do_reset = 0;

	/* 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) {
		err = -EIO;
		goto err2;
	}

	printk(KERN_INFO
		"GRPCI2: 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.
	 */
	memset(&priv->info.io_space, 0, sizeof(struct resource));
	priv->info.io_space.name = "GRPCI2 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;

	/*
	 * GRPCI2 has no prefetchable memory, map everything as
	 * non-prefetchable memory
	 */
	memset(&priv->info.mem_space, 0, sizeof(struct resource));
	priv->info.mem_space.name = "GRPCI2 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)
		goto err3;
	if (request_resource(&ioport_resource, &priv->info.io_space) < 0)
		goto err4;

	/* setup maximum supported PCI buses */
	priv->info.busn.name = "GRPCI2 busn";
	priv->info.busn.start = 0;
	priv->info.busn.end = 255;

	grpci2_hw_init(priv);

	/*
	 * Get PCI Interrupt to System IRQ mapping and setup IRQ handling
	 * Error IRQ always on PCI INTA.
	 */
	if (priv->irq_mode < 2) {
		/* All PCI interrupts are shared using the same system IRQ */
		leon_update_virq_handling(priv->irq, grpci2_pci_flow_irq,
					 "pcilvl", 0);

		priv->irq_map[0] = grpci2_build_device_irq(1);
		priv->irq_map[1] = grpci2_build_device_irq(2);
		priv->irq_map[2] = grpci2_build_device_irq(3);
		priv->irq_map[3] = grpci2_build_device_irq(4);

		priv->virq_err = grpci2_build_device_irq(5);
		if (priv->irq_mode & 1)
			priv->virq_dma = ofdev->archdata.irqs[1];
		else
			priv->virq_dma = grpci2_build_device_irq(6);

		/* Enable IRQs on LEON IRQ controller */
		err = request_irq(priv->irq, grpci2_jump_interrupt, 0,
					"GRPCI2_JUMP", priv);
		if (err)
			printk(KERN_ERR "GRPCI2: ERR IRQ request failed\n");
	} else {
		/* All PCI interrupts have an unique IRQ interrupt */
		for (i = 0; i < 4; i++) {
			/* Make LEON IRQ layer handle level IRQ by acking */
			leon_update_virq_handling(ofdev->archdata.irqs[i],
						 handle_fasteoi_irq, "pcilvl",
						 1);
			priv->irq_map[i] = ofdev->archdata.irqs[i];
		}
		priv->virq_err = priv->irq_map[0];
		if (priv->irq_mode & 1)
			priv->virq_dma = ofdev->archdata.irqs[4];
		else
			priv->virq_dma = priv->irq_map[0];

		/* Unmask all PCI interrupts, request_irq will not do that */
		REGSTORE(regs->ctrl, REGLOAD(regs->ctrl)|(priv->irq_mask&0xf));
	}

	/* Setup IRQ handler for non-configuration space access errors */
	err = request_irq(priv->virq_err, grpci2_err_interrupt, IRQF_SHARED,
				"GRPCI2_ERR", priv);
	if (err) {
		printk(KERN_DEBUG "GRPCI2: ERR VIRQ request failed: %d\n", err);
		goto err5;
	}

	/*
	 * Enable Error Interrupts. PCI interrupts are unmasked once request_irq
	 * is called by the PCI Device drivers
	 */
	REGSTORE(regs->ctrl, REGLOAD(regs->ctrl) | CTRL_EI | CTRL_SI);

	/* Init common layer and scan buses */
	priv->info.ops = &grpci2_ops;
	priv->info.map_irq = grpci2_map_irq;
	leon_pci_init(ofdev, &priv->info);

	return 0;

err5:
	release_resource(&priv->info.io_space);
err4:
	release_resource(&priv->info.mem_space);
err3:
	err = -ENOMEM;
	iounmap((void __iomem *)priv->pci_io_va);
err2:
	kfree(priv);
err1:
	of_iounmap(&ofdev->resource[0], regs,
		resource_size(&ofdev->resource[0]));
	return err;
}