Message ID | 20170128204839.18330-2-linus.walleij@linaro.org (mailing list archive) |
---|---|
State | New, archived |
Delegated to: | Bjorn Helgaas |
Headers | show |
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@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@gmail.com> > Cc: Paulius Zaleckas <paulius.zaleckas@gmail.com> > Cc: Hans Ulli Kroll <ulli.kroll@googlemail.com> > Cc: Florian Fainelli <f.fainelli@gmail.com> > Signed-off-by: Linus Walleij <linus.walleij@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@linaro.org> > + * > + * Based on the out-of-tree OpenWRT patch: > + * Copyright (C) 2009 Janos Laube <janos.dev@gmail.com> > + * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@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 &= ~(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 &= ~((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 &= ~(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, ®); > + 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@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Saturday, January 28, 2017 9:48:37 PM CET Linus Walleij wrote: > +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); The read_config/write_config functions are called under a spinlock, no need for another one. > + 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; Don't touch these > + /* setup I/O space to 1MB size */ > + writel(GEMINI_PCI_IOSIZE_1M, p->base + PCI_IOSIZE); Maybe get the size from the resource instead? Note that pci_remap_iospace only registers 64K of I/O space, so you could also use that. > + /* 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); Can you try using the new pci_register_host_bridge() API? Arnd
On Wed, Feb 1, 2017 at 12:11 PM, Arnd Bergmann <arnd@arndb.de> wrote: > On Saturday, January 28, 2017 9:48:37 PM CET Linus Walleij wrote: >> + /* No clue what these do */ >> + pcibios_min_io = 0x100; >> + pcibios_min_mem = 0; > > Don't touch these OK I have a clue why this is there now, atleast the first one. The first 0x100 in the IOspace is actually configuration registers for the bridge. That is why we have this: reg = <0x50000000 0x100>; (...) /* PCI ranges mappings */ ranges = /* 1MiB I/O space 0x50000000-0x500fffff */ <0x01000000 0 0 0x50000000 0 0x00100000>, This is in all the vendor code I have located too. So the pcibios_min_io is manipulated to avoid touching that sensitive area. But I also see that arch/arm/mm/iomap.c sets it to 0x1000, according to the commit because you said it's the only valid value :D I tried setting the IO range to <0x50000100 0x000FFF00> instead of <0x50000000 0x00100000> but predictably that doesn't work. Maybe it should, I don't know really. Yours, Linus Walleij
On Saturday, February 4, 2017 7:43:15 PM CET Linus Walleij wrote: > On Wed, Feb 1, 2017 at 12:11 PM, Arnd Bergmann <arnd@arndb.de> wrote: > > On Saturday, January 28, 2017 9:48:37 PM CET Linus Walleij wrote: > > >> + /* No clue what these do */ > >> + pcibios_min_io = 0x100; > >> + pcibios_min_mem = 0; > > > > Don't touch these > > OK I have a clue why this is there now, atleast the first one. > > The first 0x100 in the IOspace is actually configuration registers > for the bridge. That is why we have this: I see. It's normal to have the PCI config space done through ports 0cf8-0cff, but apparently this one uses other ports in the same range. > reg = <0x50000000 0x100>; > (...) > /* PCI ranges mappings */ > ranges = /* 1MiB I/O space 0x50000000-0x500fffff */ > <0x01000000 0 0 0x50000000 0 0x00100000>, > > This is in all the vendor code I have located too. > > So the pcibios_min_io is manipulated to avoid touching that > sensitive area. But I also see that arch/arm/mm/iomap.c > sets it to 0x1000, according to the commit because you said > it's the only valid value :D > > I tried setting the IO range to <0x50000100 0x000FFF00> > instead of <0x50000000 0x00100000> > but predictably that doesn't work. Maybe it should, I don't > know really. It should work in theory, but then you'd have to update io_offset accordingly, and it would be a bit confusing. Did you notice my other comment below (quoting from my own message)? > > + > > + bus = pci_scan_root_bus(&pdev->dev, 0, &gemini_pci_ops, p, &res); > > Can you try using the new pci_register_host_bridge() API? Arnd
On Thu, Feb 16, 2017 at 3:08 PM, Arnd Bergmann <arnd@arndb.de> wrote: > On Saturday, February 4, 2017 7:43:15 PM CET Linus Walleij wrote: > Did you notice my other comment below (quoting from my own > message)? > >> > + >> > + bus = pci_scan_root_bus(&pdev->dev, 0, &gemini_pci_ops, p, &res); >> >> Can you try using the new pci_register_host_bridge() API? Yes. It crashes badly when I try it so I'm still trying to figure this out... If I do I will send an iteration. Yours, Linus Walleij
On Tue, Jan 31, 2017 at 1:37 AM, Bjorn Helgaas <helgaas@kernel.org> wrote:
> Looks nice; a couple unused definitions below.
Just so I do things right: what is your policy on unused defines?
I'm asking because the defines in this case is pretty much the
documentation... there is no datasheet for this IP core. I usually
like to keep the defines around so people can have them as a
hint if they want to tinker and hack the driver.
Would you be OK with keeping them if I comment them out?
I'll repost the driver soon, including all things asked to be
fixed up by you & Arnd.
Yours,
Linus Walleij
On Sun, Feb 26, 2017 at 08:42:51PM +0100, Linus Walleij wrote: > On Tue, Jan 31, 2017 at 1:37 AM, Bjorn Helgaas <helgaas@kernel.org> wrote: > > > Looks nice; a couple unused definitions below. > > Just so I do things right: what is your policy on unused defines? > I'm asking because the defines in this case is pretty much the > documentation... there is no datasheet for this IP core. I usually > like to keep the defines around so people can have them as a > hint if they want to tinker and hack the driver. > > Would you be OK with keeping them if I comment them out? It's fine if you keep them; don't even bother commenting them out. If there's a datasheet, especially a public one like the PCI specs (I know even those aren't completely free), I have been removing unused defines simply because they can't be tested and there may be transcription errors. But I'm starting to think that might be too aggressive. Bjorn
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@linaro.org> + * + * Based on the out-of-tree OpenWRT patch: + * Copyright (C) 2009 Janos Laube <janos.dev@gmail.com> + * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@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 +#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 +#define PCI_CTRL 0x08 +#define PCI_SOFTRST 0x10 +#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 &= ~(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 &= ~((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 &= ~(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, ®); + 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);
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@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@gmail.com> Cc: Paulius Zaleckas <paulius.zaleckas@gmail.com> Cc: Hans Ulli Kroll <ulli.kroll@googlemail.com> Cc: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: Linus Walleij <linus.walleij@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