[PATCH 2/4] PCI: add driver for Cortina Gemini Host Bridge

Bjorn Helgaas helgaas at kernel.org
Mon Jan 30 16:37:51 PST 2017


Hi Linus,

Looks nice; a couple unused definitions below.  It looks like /proc/iomem
should make sense, too, since you used devm_request_pci_bus_resources().

On Sat, Jan 28, 2017 at 09:48:37PM +0100, Linus Walleij wrote:
> This adds a host bridge driver for the Cortina Systems Gemini
> SoC (SL3516) PCI Host Bridge.
> 
> This code is inspired by the out-of-tree OpenWRT patch and
> then extensively rewritten for device tree and using the modern
> helpers to cut down and modernize the code to all new PCI
> frameworks.
> 
> Tested on the ITian Square One SQ201 NAS with the following
> result in the boot log (trimmed to relevant parts):
> 
> OF: PCI: host bridge /pci at 50000000 ranges:
> OF: PCI:    IO 0x50000000..0x500fffff -> 0x00000000
> OF: PCI:   MEM 0x58000000..0x5fffffff -> 0x58000000
> gemini-pci 50000000.pci: PCI host bridge to bus 0000:00
> pci_bus 0000:00: root bus resource [bus 00]
> pci_bus 0000:00: root bus resource [io  0x0000-0xfffff]
> pci_bus 0000:00: root bus resource [mem 0x58000000-0x5fffffff]
> pci 0000:00:00.0: [159b:4321] type 00 class 0x060000
> pci 0000:00:09.0: [1106:3038] type 00 class 0x0c0300
> pci 0000:00:09.0: reg 0x20: [io  0xfce0-0xfcff]
> pci 0000:00:09.0: supports D1 D2
> pci 0000:00:09.0: PME# supported from D0 D1 D2 D3hot D3cold
> pci 0000:00:09.1: [1106:3038] type 00 class 0x0c0300
> pci 0000:00:09.1: reg 0x20: [io  0xfce0-0xfcff]
> pci 0000:00:09.1: supports D1 D2
> pci 0000:00:09.1: PME# supported from D0 D1 D2 D3hot D3cold
> pci 0000:00:09.2: [1106:3104] type 00 class 0x0c0320
> pci 0000:00:09.2: reg 0x10: [mem 0x00000000-0x000000ff]
> pci 0000:00:09.2: supports D1 D2
> pci 0000:00:09.2: PME# supported from D0 D1 D2 D3hot D3cold
> pci 0000:00:0c.0: [1814:0301] type 00 class 0x028000
> pci 0000:00:0c.0: reg 0x10: [mem 0x58000000-0x58007fff]
> PCI: bus0: Fast back to back transfers disabled
> gemini-pci 50000000.pci: clear all IRQs
> gemini-pci 50000000.pci: setting up PCI DMA
> pci 0000:00:00.0: of_irq_parse_pci() failed with rc=-22
> pci 0000:00:0c.0: BAR 0: assigned [mem 0x58000000-0x58007fff]
> pci 0000:00:09.2: BAR 0: assigned [mem 0x58008000-0x580080ff]
> pci 0000:00:09.0: BAR 4: assigned [io  0x0400-0x041f]
> pci 0000:00:09.1: BAR 4: assigned [io  0x0420-0x043f]
> pci 0000:00:09.0: enabling device (0140 -> 0141)
> pci 0000:00:09.0: HCRESET not completed yet!
> pci 0000:00:09.1: enabling device (0140 -> 0141)
> pci 0000:00:09.1: HCRESET not completed yet!
> pci 0000:00:09.2: enabling device (0140 -> 0142)
> ieee80211 phy0: rt2x00_set_chip: Info - Chipset detected - rt: 2561, rf: 0003, rev: 000c
> ieee80211 phy0: Selected rate control algorithm 'minstrel_ht'
> ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
> ehci-pci: EHCI PCI platform driver
> ehci-pci 0000:00:09.2: EHCI Host Controller
> ehci-pci 0000:00:09.2: new USB bus registered, assigned bus number 1
> ehci-pci 0000:00:09.2: irq 146, io mem 0x58008000
> ehci-pci 0000:00:09.2: USB 2.0 started, EHCI 1.00
> hub 1-0:1.0: USB hub found
> hub 1-0:1.0: 4 ports detected
> uhci_hcd: USB Universal Host Controller Interface driver
> uhci_hcd 0000:00:09.0: UHCI Host Controller
> uhci_hcd 0000:00:09.0: new USB bus registered, assigned bus number 2
> uhci_hcd 0000:00:09.0: HCRESET not completed yet!
> uhci_hcd 0000:00:09.0: irq 144, io base 0x00000400
> hub 2-0:1.0: USB hub found
> hub 2-0:1.0: config failed, hub doesn't have any ports! (err -19)
> uhci_hcd 0000:00:09.1: UHCI Host Controller
> uhci_hcd 0000:00:09.1: new USB bus registered, assigned bus number 3
> uhci_hcd 0000:00:09.1: HCRESET not completed yet!
> uhci_hcd 0000:00:09.1: irq 145, io base 0x00000420
> hub 3-0:1.0: USB hub found
> hub 3-0:1.0: config failed, hub doesn't have any ports! (err -19)
> usb 1-1: new high-speed USB device number 2 using ehci-pci
> usb-storage 1-1:1.0: USB Mass Storage device detected
> scsi host0: usb-storage 1-1:1.0
> scsi 0:0:0:0: Direct-Access     USB      Flash Disk       1.00 PQ: 0 ANSI: 2
> sd 0:0:0:0: [sda] 7900336 512-byte logical blocks: (4.04 GB/3.77 GiB)
> sd 0:0:0:0: [sda] Write Protect is off
> sd 0:0:0:0: [sda] Mode Sense: 0b 00 00 08
> sd 0:0:0:0: [sda] No Caching mode page found
> sd 0:0:0:0: [sda] Assuming drive cache: write through
>  sda: sda1 sda2 sda3
> sd 0:0:0:0: [sda] Attached SCSI removable disk
> 
> $ lspci
> 00:00.0 Class 0600: 159b:4321
> 00:09.2 Class 0c03: 1106:3104
> 00:09.0 Class 0c03: 1106:3038
> 00:09.1 Class 0c03: 1106:3038
> 00:0c.0 Class 0280: 1814:0301
> 
> cat /proc/interrupts
>            CPU0
>  19:          0    GEMINI   3 Level     watchdog bark
>  30:       4943    GEMINI  14 Edge      Gemini Timer Tick
>  33:          0    GEMINI  17 Level     45000000.rtc
>  34:        651    GEMINI  18 Level     serial
>  66:          0      GPIO  18 Edge      factory reset
> 144:          0       PCI   0 Edge      uhci_hcd:usb2
> 145:          0       PCI   1 Edge      uhci_hcd:usb3
> 146:        160       PCI   2 Edge      ehci_hcd:usb1
> 
> Well the EHCI USB hub works fine, I can mount and manage
> files and the IRQs just keep ticking up.
> 
> Cc: Janos Laube <janos.dev at gmail.com>
> Cc: Paulius Zaleckas <paulius.zaleckas at gmail.com>
> Cc: Hans Ulli Kroll <ulli.kroll at googlemail.com>
> Cc: Florian Fainelli <f.fainelli at gmail.com>
> Signed-off-by: Linus Walleij <linus.walleij at linaro.org>
> ---
> This can be merged to the PCI tree whenever it is considered
> fine for inclusion.
> ---
>  drivers/pci/host/Kconfig      |   7 +
>  drivers/pci/host/Makefile     |   1 +
>  drivers/pci/host/pci-gemini.c | 375 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 383 insertions(+)
>  create mode 100644 drivers/pci/host/pci-gemini.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 898d2c48239c..e29c2caf3492 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -60,6 +60,13 @@ config PCI_EXYNOS
>  	select PCIEPORTBUS
>  	select PCIE_DW
>  
> +config PCI_GEMINI
> +	bool "Cortina Gemini SL351x PCI roller"
> +	depends on ARCH_GEMINI
> +	depends on ARM
> +	depends on OF
> +	default ARCH_GEMINI
> +
>  config PCI_IMX6
>  	bool "Freescale i.MX6 PCIe controller"
>  	depends on SOC_IMX6Q
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index bfe3179ae74c..8f007fe7a19d 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -2,6 +2,7 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o
>  obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
>  obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
>  obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
> +obj-$(CONFIG_PCI_GEMINI) += pci-gemini.o
>  obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
>  obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o
>  obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
> diff --git a/drivers/pci/host/pci-gemini.c b/drivers/pci/host/pci-gemini.c
> new file mode 100644
> index 000000000000..7051dd992114
> --- /dev/null
> +++ b/drivers/pci/host/pci-gemini.c
> @@ -0,0 +1,375 @@
> +/*
> + * Support for Gemini PCI Controller
> + *
> + * Copyright (C) 2017 Linus Walleij <linus.walleij at linaro.org>
> + *
> + * Based on the out-of-tree OpenWRT patch:
> + * Copyright (C) 2009 Janos Laube <janos.dev at gmail.com>
> + * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas at teltonika.lt>
> + * Based on SL2312 PCI controller code
> + * Storlink (C) 2003
> + */
> +
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/of_address.h>
> +#include <linux/of_pci.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/irqdomain.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/bitops.h>
> +#include <linux/irq.h>
> +#include <linux/spinlock.h>
> +
> +#define GEMINI_PCI_IOSIZE_1M		0x0000
> +
> +#define GEMINI_PCI_PMC			0x40
> +#define GEMINI_PCI_PMCSR		0x44
> +#define GEMINI_PCI_CTRL1		0x48

Above three definitions unused.

> +#define GEMINI_PCI_CTRL2		0x4C
> +#define GEMINI_PCI_MEM1_BASE_SIZE	0x50
> +#define GEMINI_PCI_MEM2_BASE_SIZE	0x54
> +#define GEMINI_PCI_MEM3_BASE_SIZE	0x58
> +
> +#define PCI_CTRL2_INTSTS_SHIFT		28
> +#define PCI_CTRL2_INTMASK_SHIFT		22
> +
> +#define GEMINI_PCI_DMA_MASK		0xFFF00000
> +#define GEMINI_PCI_DMA_MEM1_BASE	0x00000000
> +#define GEMINI_PCI_DMA_MEM2_BASE	0x00000000
> +#define GEMINI_PCI_DMA_MEM3_BASE	0x00000000
> +#define GEMINI_PCI_DMA_MEM1_SIZE	7
> +#define GEMINI_PCI_DMA_MEM2_SIZE	6
> +#define GEMINI_PCI_DMA_MEM3_SIZE	6
> +
> +#define PCI_CONF_ENABLE		BIT(31)
> +#define PCI_CONF_WHERE(r)	((r) & 0xFC)
> +#define PCI_CONF_BUS(b)		(((b) & 0xFF) << 16)
> +#define PCI_CONF_DEVICE(d)	(((d) & 0x1F) << 11)
> +#define PCI_CONF_FUNCTION(f)	(((f) & 0x07) << 8)
> +
> +#define PCI_IOSIZE	0x00
> +#define PCI_PROT	0x04

Unused.

> +#define PCI_CTRL	0x08
> +#define PCI_SOFTRST	0x10

Unused.

> +#define PCI_CONFIG	0x28
> +#define PCI_DATA	0x2C
> +
> +struct gemini_pci {
> +	struct device *dev;
> +	void __iomem *base;
> +	struct irq_domain *irqdomain;
> +	spinlock_t lock;
> +	struct pci_bus *bus;
> +};
> +
> +static int gemini_pci_read_config(struct pci_bus *bus, unsigned int fn,
> +				  int config, int size, u32 *value)
> +{
> +	struct gemini_pci *p = bus->sysdata;
> +	unsigned long irq_flags;
> +
> +	spin_lock_irqsave(&p->lock, irq_flags);
> +
> +	writel(PCI_CONF_BUS(bus->number) |
> +			PCI_CONF_DEVICE(PCI_SLOT(fn)) |
> +			PCI_CONF_FUNCTION(PCI_FUNC(fn)) |
> +			PCI_CONF_WHERE(config) |
> +			PCI_CONF_ENABLE,
> +			p->base + PCI_CONFIG);
> +
> +	*value = readl(p->base + PCI_DATA);
> +
> +	if (size == 1)
> +		*value = (*value >> (8 * (config & 3))) & 0xFF;
> +	else if (size == 2)
> +		*value = (*value >> (8 * (config & 3))) & 0xFFFF;
> +
> +	spin_unlock_irqrestore(&p->lock, irq_flags);
> +
> +	dev_dbg(&bus->dev,
> +		"[read]  slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n",
> +		PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value);
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int gemini_pci_write_config(struct pci_bus *bus, unsigned int fn,
> +				   int config, int size, u32 value)
> +{
> +	struct gemini_pci *p = bus->sysdata;
> +	unsigned long irq_flags = 0;
> +	int ret = PCIBIOS_SUCCESSFUL;
> +
> +	dev_dbg(&bus->dev,
> +		"[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n",
> +		PCI_SLOT(fn), PCI_FUNC(fn), config, size, value);
> +
> +	spin_lock_irqsave(&p->lock, irq_flags);
> +
> +	writel(PCI_CONF_BUS(bus->number) |
> +			PCI_CONF_DEVICE(PCI_SLOT(fn)) |
> +			PCI_CONF_FUNCTION(PCI_FUNC(fn)) |
> +			PCI_CONF_WHERE(config) |
> +			PCI_CONF_ENABLE,
> +			p->base + PCI_CONFIG);
> +
> +	switch (size) {
> +	case 4:
> +		writel(value, p->base + PCI_DATA);
> +		break;
> +	case 2:
> +		writew(value, p->base + PCI_DATA + (config & 3));
> +		break;
> +	case 1:
> +		writeb(value, p->base + PCI_DATA + (config & 3));
> +		break;
> +	default:
> +		ret = PCIBIOS_BAD_REGISTER_NUMBER;
> +	}
> +
> +	spin_unlock_irqrestore(&p->lock, irq_flags);
> +
> +	return ret;
> +}
> +
> +static struct pci_ops gemini_pci_ops = {
> +	.read	= gemini_pci_read_config,
> +	.write	= gemini_pci_write_config,
> +};
> +
> +static void gemini_pci_ack_irq(struct irq_data *d)
> +{
> +	struct gemini_pci *p = irq_data_get_irq_chip_data(d);
> +	unsigned int reg;
> +
> +	gemini_pci_read_config(p->bus, 0, GEMINI_PCI_CTRL2, 4, &reg);
> +	reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT);
> +	reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTSTS_SHIFT);
> +	gemini_pci_write_config(p->bus, 0, GEMINI_PCI_CTRL2, 4, reg);
> +}
> +
> +static void gemini_pci_mask_irq(struct irq_data *d)
> +{
> +	struct gemini_pci *p = irq_data_get_irq_chip_data(d);
> +	unsigned int reg;
> +
> +	gemini_pci_read_config(p->bus, 0, GEMINI_PCI_CTRL2, 4, &reg);
> +	reg &= ~((0xF << PCI_CTRL2_INTSTS_SHIFT)
> +		 | BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT));
> +	gemini_pci_write_config(p->bus, 0, GEMINI_PCI_CTRL2, 4, reg);
> +}
> +
> +static void gemini_pci_unmask_irq(struct irq_data *d)
> +{
> +	struct gemini_pci *p = irq_data_get_irq_chip_data(d);
> +	unsigned int reg;
> +
> +	gemini_pci_read_config(p->bus, 0, GEMINI_PCI_CTRL2, 4, &reg);
> +	reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT);
> +	reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT);
> +	gemini_pci_write_config(p->bus, 0, GEMINI_PCI_CTRL2, 4, reg);
> +}
> +
> +static void gemini_pci_irq_handler(struct irq_desc *desc)
> +{
> +	struct gemini_pci *p = irq_desc_get_handler_data(desc);
> +	struct irq_chip *irqchip = irq_desc_get_chip(desc);
> +	unsigned int irq_stat, reg, i;
> +
> +	gemini_pci_read_config(p->bus, 0, GEMINI_PCI_CTRL2, 4, &reg);
> +	irq_stat = reg >> PCI_CTRL2_INTSTS_SHIFT;
> +
> +	chained_irq_enter(irqchip, desc);
> +
> +	for (i = 0; i < 4; i++) {
> +		if ((irq_stat & BIT(i)) == 0)
> +			continue;
> +		generic_handle_irq(irq_find_mapping(p->irqdomain, i));
> +	}
> +
> +	chained_irq_exit(irqchip, desc);
> +}
> +
> +static struct irq_chip gemini_pci_irq_chip = {
> +	.name = "PCI",
> +	.irq_ack = gemini_pci_ack_irq,
> +	.irq_mask = gemini_pci_mask_irq,
> +	.irq_unmask = gemini_pci_unmask_irq,
> +};
> +
> +static int gemini_pci_irq_map(struct irq_domain *domain, unsigned int irq,
> +			      irq_hw_number_t hwirq)
> +{
> +	irq_set_chip_and_handler(irq, &gemini_pci_irq_chip, handle_level_irq);
> +	irq_set_chip_data(irq, domain->host_data);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops gemini_pci_irqdomain_ops = {
> +	.map = gemini_pci_irq_map,
> +};
> +
> +static int gemini_pci_setup_irq(struct gemini_pci *p, int irq)
> +{
> +	struct device_node *intc = of_get_next_child(p->dev->of_node, NULL);
> +	int i;
> +
> +	if (!intc) {
> +		dev_err(p->dev, "missing child interrupt-controller node\n");
> +		return -EINVAL;
> +	}
> +
> +	p->irqdomain = irq_domain_add_linear(intc, 4,
> +					     &gemini_pci_irqdomain_ops,
> +					     p);
> +	if (!p->irqdomain) {
> +		dev_err(p->dev, "failed to create Gemini PCI IRQ domain\n");
> +		return -EINVAL;
> +	}
> +
> +	irq_set_chained_handler_and_data(irq, gemini_pci_irq_handler, p);
> +
> +	for (i = 0; i < 4; i++)
> +		irq_create_mapping(p->irqdomain, i);
> +
> +	return 0;
> +}
> +
> +static int gemini_pci_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource *regs;
> +	resource_size_t io_base;
> +	struct resource_entry *win;
> +	struct gemini_pci *p;
> +	struct resource *mem;
> +	struct resource *io;
> +	struct pci_bus *bus;
> +	int irq;
> +	int ret;
> +	u32 val;
> +	LIST_HEAD(res);
> +
> +	p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
> +	if (!p)
> +		return -ENOMEM;
> +
> +	p->dev = dev;
> +	spin_lock_init(&p->lock);
> +
> +	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	p->base = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(p->base))
> +		return PTR_ERR(p->base);
> +
> +	ret = of_pci_get_host_bridge_resources(dev->of_node, 0, 0xff,
> +					       &res, &io_base);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_request_pci_bus_resources(dev, &res);
> +	if (ret)
> +		return ret;
> +
> +	/* No clue what these do */
> +	pcibios_min_io = 0x100;
> +	pcibios_min_mem = 0;
> +
> +	/* setup I/O space to 1MB size */
> +	writel(GEMINI_PCI_IOSIZE_1M, p->base + PCI_IOSIZE);
> +
> +	/* setup hostbridge */
> +	val = readl(p->base + PCI_CTRL);
> +	val |= PCI_COMMAND_IO;
> +	val |= PCI_COMMAND_MEMORY;
> +	val |= PCI_COMMAND_MASTER;
> +	writel(val, p->base + PCI_CTRL);
> +
> +	/* Get the I/O and memory ranges from DT */
> +	resource_list_for_each_entry(win, &res) {
> +		switch (resource_type(win->res)) {
> +		case IORESOURCE_IO:
> +			io = win->res;
> +			io->name = "Gemini PCI I/O";
> +			ret = pci_remap_iospace(io, io_base);
> +			if (ret) {
> +				dev_warn(dev, "error %d: failed to map resource %pR\n",
> +					 ret, io);
> +				continue;
> +			}
> +			break;
> +		case IORESOURCE_MEM:
> +			mem = win->res;
> +			mem->name = "Gemini PCI MEM";
> +			break;
> +		case IORESOURCE_BUS:
> +			break;
> +		default:
> +			break;
> +		}
> +	}
> +
> +	bus = pci_scan_root_bus(&pdev->dev, 0, &gemini_pci_ops, p, &res);
> +	if (!bus)
> +		return -ENOMEM;
> +	p->bus = bus;
> +
> +	dev_info(dev, "clear all IRQs\n");
> +
> +	/* Mask and clear all interrupts */
> +	gemini_pci_write_config(bus, 0, GEMINI_PCI_CTRL2 + 2, 2, 0xF000);
> +
> +	/* IRQ - all PCI IRQs cascade off this one */
> +	irq = platform_get_irq(pdev, 0);
> +	if (!irq) {
> +		dev_err(dev, "failed to get IRQ\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = gemini_pci_setup_irq(p, irq);
> +	if (ret) {
> +		dev_err(dev, "failed to setup IRQ\n");
> +		return ret;
> +	}
> +
> +	dev_info(dev, "setting up PCI DMA\n");
> +	val = (GEMINI_PCI_DMA_MEM1_BASE & GEMINI_PCI_DMA_MASK)
> +		| (GEMINI_PCI_DMA_MEM1_SIZE << 16);
> +	gemini_pci_write_config(bus, 0, GEMINI_PCI_MEM1_BASE_SIZE, 4, val);
> +	val = (GEMINI_PCI_DMA_MEM2_BASE & GEMINI_PCI_DMA_MASK)
> +		| (GEMINI_PCI_DMA_MEM2_SIZE << 16);
> +	gemini_pci_write_config(bus, 0, GEMINI_PCI_MEM2_BASE_SIZE, 4, val);
> +	val = (GEMINI_PCI_DMA_MEM3_BASE & GEMINI_PCI_DMA_MASK)
> +		| (GEMINI_PCI_DMA_MEM3_SIZE << 16);
> +	gemini_pci_write_config(bus, 0, GEMINI_PCI_MEM3_BASE_SIZE, 4, val);
> +
> +	pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
> +	pci_bus_assign_resources(bus);
> +	pci_assign_unassigned_bus_resources(bus);
> +	pci_bus_add_devices(bus);
> +	pci_free_resource_list(&res);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id gemini_pci_of_match[] = {
> +	{
> +		.compatible = "cortina,gemini-pci",
> +	},
> +	{},
> +};
> +
> +static struct platform_driver gemini_pci_driver = {
> +	.driver = {
> +		.name = "gemini-pci",
> +		.of_match_table = of_match_ptr(gemini_pci_of_match),
> +	},
> +	.probe  = gemini_pci_probe,
> +};
> +builtin_platform_driver(gemini_pci_driver);
> -- 
> 2.9.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the linux-arm-kernel mailing list