From: Anders Berg <anders.b...@lsi.com> Also fixed the PCIe driver to respect the outbound window configuration via the ranges property in the device-tree.
Signed-off-by: Anders Berg <anders.b...@lsi.com> --- arch/arm/boot/dts/axm55xx.dts | 4 +- arch/arm/boot/dts/axm55xxsim.dts | 4 +- arch/arm/mach-axxia/include/mach/irqs.h | 4 +- arch/arm/mach-axxia/include/mach/pci.h | 6 + arch/arm/mach-axxia/pci.c | 1145 +++++++++++++++++-------------- 5 files changed, 642 insertions(+), 521 deletions(-) create mode 100644 arch/arm/mach-axxia/include/mach/pci.h diff --git a/arch/arm/boot/dts/axm55xx.dts b/arch/arm/boot/dts/axm55xx.dts index 305e79c..f397394 100644 --- a/arch/arm/boot/dts/axm55xx.dts +++ b/arch/arm/boot/dts/axm55xx.dts @@ -194,7 +194,7 @@ / ranges = <0x03000000 0x00000000 0xa0000000 0x30 0x00000000 - 0x00 0x10000000>; + 0x00 0x20000000>; /* Inbound ranges */ /* < <3-cell PCI addr> <2-cell CPU addr> <2-cell size> > */ dma-ranges = <0x03000000 0x00000000 0x00000000 @@ -236,7 +236,7 @@ /* < <3-cell PCI addr> <2-cell CPU (PLB) addr> <2-cell size> > */ ranges = <0x03000000 0x00000000 0xa0000000 0x30 0x80000000 - 0x00 0x10000000>; + 0x00 0x20000000>; /* Inbound ranges */ /* < <3-cell PCI addr> <2-cell CPU addr> <2-cell size> > */ dma-ranges = <0x03000000 0x00000000 0x00000000 diff --git a/arch/arm/boot/dts/axm55xxsim.dts b/arch/arm/boot/dts/axm55xxsim.dts index ee6f852..4972da9 100644 --- a/arch/arm/boot/dts/axm55xxsim.dts +++ b/arch/arm/boot/dts/axm55xxsim.dts @@ -306,7 +306,7 @@ /* < <3-cell PCI addr> <2-cell CPU (PLB) addr> <2-cell size> >*/ ranges = <0x03000000 0x00000000 0xa0000000 0x30 0x00000000 - 0x00 0x10000000>; + 0x00 0x20000000>; /* Inbound ranges */ /* < <3-cell PCI addr> <2-cell CPU addr> <2-cell size> > */ dma-ranges = <0x03000000 0x00000000 0x00000000 @@ -346,7 +346,7 @@ /* < <3-cell PCI addr> <2-cell CPU (PLB) addr> <2-cell size> > */ ranges = <0x03000000 0x00000000 0xa0000000 0x30 0x80000000 - 0x00 0x10000000>; + 0x00 0x20000000>; /* Inbound ranges */ /* < <3-cell PCI addr> <2-cell CPU addr> <2-cell size> > */ dma-ranges = <0x03000000 0x00000000 0x00000000 diff --git a/arch/arm/mach-axxia/include/mach/irqs.h b/arch/arm/mach-axxia/include/mach/irqs.h index 4b10ee7..5f25c95 100644 --- a/arch/arm/mach-axxia/include/mach/irqs.h +++ b/arch/arm/mach-axxia/include/mach/irqs.h @@ -1,4 +1,4 @@ #define IRQ_LOCALTIMER 29 #define IRQ_LOCALWDOG 30 - -#define NR_IRQS 256 +#define AXXIA_MSI_FIRST 224 +#define NR_IRQS 256 diff --git a/arch/arm/mach-axxia/include/mach/pci.h b/arch/arm/mach-axxia/include/mach/pci.h new file mode 100644 index 0000000..3260654 --- /dev/null +++ b/arch/arm/mach-axxia/include/mach/pci.h @@ -0,0 +1,6 @@ +#ifndef _AXXIA_PCI_H +#define _AXXIA_PCI_H + +void __init axxia_pcie_init(void); + +#endif diff --git a/arch/arm/mach-axxia/pci.c b/arch/arm/mach-axxia/pci.c index 2b39e0d..a405b79 100644 --- a/arch/arm/mach-axxia/pci.c +++ b/arch/arm/mach-axxia/pci.c @@ -1,84 +1,163 @@ /* - * PCI / PCI-X / PCI-Express support for ARM A15 Cortex parts + * arch/arm/mach-axxia/pci.c + * + * PCIe support for AXM55xx. + * + * Copyright (C) 2013 LSI + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#undef DEBUG - #include <linux/kernel.h> #include <linux/pci.h> #include <linux/init.h> #include <linux/of.h> #include <linux/of_address.h> -#include <linux/bootmem.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/of_irq.h> #include <linux/interrupt.h> - #include <linux/io.h> - +#include <linux/msi.h> +#include <linux/kernel_stat.h> #include <asm/sizes.h> #include <asm/mach/pci.h> #include <asm/irq.h> #include <asm/mach/irq.h> #include <asm-generic/errno-base.h> +#include <mach/pci.h> + +#define PCIE_CONFIG (0x1000) +#define PCIE_STATUS (0x1004) +#define PCIE_CORE_DEBUG (0x1008) +#define PCIE_LOOPBACK_FAIL (0x100C) +#define PCIE_MPAGE_U(n) (0x1010 + (n * 8)) /* n = 0..7 */ +#define PCIE_MPAGE_L(n) (0x1014 + (n * 8)) /* n = 0..7 */ +#define PCIE_TPAGE_BAR0(n) (0x1050 + (n * 4)) /* n = 0..7 */ +#define PCIE_TPAGE_32 (0<<31) /* AXI 32-bit access */ +#define PCIE_TPAGE_128 (1<<31) /* AXI 128-bit access */ +#define PCIE_TPAGE_BAR1(n) (0x1070 + (n * 4)) /* n = 0..7 */ +#define PCIE_TPAGE_BAR2(n) (0x1090 + (n * 4)) /* n = 0..7 */ +#define PCIE_MSG_IN_FIFO (0x10B0) +#define PCIE_MSG_IN_FIFO_STATUS (0x10B4) +#define PCIE_MSG_OUT (0x10B8) +#define PCIE_TRN_ORDER_STATUS (0x10BC) +#define PCIE_INT0_STATUS (0x10C0) +#define PCIE_INT0_ENABLE (0x10C4) +#define PCIE_INT0_FORCE (0x10C8) +#define INT0_MSI 0x80000000U +#define INT0_INT_ASSERTED 0x08000000U +#define INT0_INT_DEASSERTED 0x04000000U +#define INT0_ERROR 0x73FFFFABU +#define PCIE_PHY_STATUS0 (0x10CC) +#define PCIE_PHY_STATUS1 (0x10D0) +#define PCIE_PHY_CONTROL0 (0x10D4) +#define PCIE_PHY_CONTROL1 (0x10D8) +#define PCIE_PHY_CONTROL2 (0x10DC) +#define PCIE_RESERVED_E0 (0x10E0) +#define PCIE_RESERVED_E4 (0x10E4) +#define PCIE_RESERVED_E8 (0x10E8) +#define PCIE_AXI_MASTER_WR (0x10EC) +#define PCIE_LINK_STATUS (0x117C) +#define PCIE_EP_BAR2_CFG (0x1184) +#define PCIE_AXI_MSI_ADDR (0x1190) +#define PCIE_INT1_STATUS (0x11C4) +#define PCIE_INT1_ENABLE (0x11C8) +#define PCIE_INT1_FORCE (0x11CC) +#define PCIE_RC_BAR0_SIZE (0x11F4) +#define PCIE_MSI0_STATUS (0x1230) +#define PCIE_MSI0_ENABLE (0x1234) +#define PCIE_MSI0_FORCE (0x1238) +#define PCIE_MSI1_STATUS(_grp) (0x123C+(_grp)*12) +#define PCIE_MSI1_ENABLE(_grp) (0x1240+(_grp)*12) +#define PCIE_MSI1_FORCE(_grp) (0x1244+(_grp)*12) + +/* Every MPAGE register maps 128MB in the AXI memory range */ +#define MPAGE_SIZE (128U<<20) + +/* We have 7 MPAGE registers available for outbound window (one reserved for + * mapping PCI configuration space). + */ +#define MAX_OUTBOUND_SIZE (7 * MPAGE_SIZE) -#include <linux/msi.h> - -#undef PRINT_CONFIG_ACCESSES -/*#define PRINT_CONFIG_ACCESSES*/ - -static u32 last_mpage; -static u32 last_port; +/* Number of IRQs allocated to MSI */ +#define NUM_MSI_IRQ (NR_IRQS - AXXIA_MSI_FIRST) -#define U64_TO_U32_LOW(val) ((u32)((val) & 0x00000000ffffffffULL)) -#define U64_TO_U32_HIGH(val) ((u32)((val) >> 32)) +/* Bitmap for allocated MSIs */ +static DECLARE_BITMAP(msi_irq_in_use, NUM_MSI_IRQ); -#define ACPX1_PCIE_MPAGE_UPPER(n) (0x1010 + (n * 8)) -#define ACPX1_PCIE_MPAGE_LOWER(n) (0x1014 + (n * 8)) +static const struct resource pcie_outbound_default[] = { + [0] = { + .start = 0, + .end = 0, + .flags = IORESOURCE_MEM + }, + [1] = { + .start = 0, + .end = 0, + .flags = IORESOURCE_MEM + } +}; struct axxia_pciex_port { - unsigned int index; - u8 root_bus_nr; - bool link_up; - void __iomem *cfg_addr; - void __iomem *cfg_data; - u64 pci_addr; - int endpoint; - struct device_node *node; - struct resource utl_regs; - struct resource cfg_space; - struct resource outbound, inbound; - dma_addr_t msi_phys; + char name[16]; + unsigned int index; + u8 root_bus_nr; + bool link_up; + int irq[17]; /* 1 legacy, 16 MSI */ + void __iomem *regs; + void __iomem *cfg_data; + u32 last_mpage; + int endpoint; + struct device_node *node; + struct resource utl_regs; + struct resource cfg_space; + /* Outbound PCI base address */ + u64 pci_addr; + /* Outbound range in (physical) CPU addresses */ + struct resource outbound; + /* Inbound PCI base address */ + u64 pci_bar; + /* Inbound range in (physical) CPU addresses */ + struct resource inbound; + /* Virtual and physical (CPU space) address for MSI table */ + void *msi_virt; + dma_addr_t msi_phys; + /* PCI memory space address for MSI table */ + u32 msi_pci_addr; }; +#define PCIE_MAX_PORTS 2 static struct axxia_pciex_port *axxia_pciex_ports; -static unsigned int axxia_pciex_port_count = 2; -static void axxia_probe_pciex_bridge(struct device_node *np); static void __init fixup_axxia_pci_bridge(struct pci_dev *dev) { /* if we aren't a PCIe don't bother */ if (!pci_find_capability(dev, PCI_CAP_ID_EXP)) - return ; + return; - /* - * Set the class appropriately for a bridge device - */ - printk(KERN_INFO - "PCI: Setting PCI Class to PCI_CLASS_BRIDGE_HOST for " - "%04x:%04x\n", dev->vendor, dev->device); + /* Set the class appropriately for a bridge device */ + dev_info(&dev->dev, + "Fixup PCI Class to PCI_CLASS_BRIDGE_HOST for %04x:%04x\n", + dev->vendor, dev->device); dev->class = PCI_CLASS_BRIDGE_HOST << 8; - /* - * Make the bridge transparent - */ + /* Make the bridge transparent */ dev->transparent = 1; - - return ; } DECLARE_PCI_FIXUP_HEADER(0x1000, 0x5101, fixup_axxia_pci_bridge); @@ -91,8 +170,12 @@ static struct axxia_pciex_port *bus_to_port(struct pci_bus *bus) return axxia_pciex_ports + pci_domain_nr(bus); } -/* Validate the Bus#/Device#/Function# */ -static int axxia_pciex_validate_bdf(struct pci_bus *bus, unsigned int devfn) + +/* + * Validate the Bus#/Device#/Function# + */ +static int +axxia_pciex_validate_bdf(struct pci_bus *bus, unsigned int devfn) { struct axxia_pciex_port *port; @@ -111,58 +194,52 @@ static int axxia_pciex_validate_bdf(struct pci_bus *bus, unsigned int devfn) return 0; } -/* Get the configuration access base address */ -static void __iomem *axxia_pciex_get_config_base(struct axxia_pciex_port *port, - struct pci_bus *bus, unsigned int devfn) +/* + * Return the configuration access base address + */ +static void __iomem * +axxia_pciex_get_config_base(struct axxia_pciex_port *port, + struct pci_bus *bus, + unsigned int devfn) { + int relbus, dev, fn; unsigned mpage; - u32 addr; - int dev, fn; - int cfg_type; - int relbus; - if (bus->number == port->root_bus_nr) { - return port->cfg_addr; - } else { - relbus = bus->number - (port->root_bus_nr + 1); - dev = ((PCI_SLOT(devfn) & 0xf8) >> 3); - fn = PCI_FUNC(devfn) & 0x7; - - if (dev > 31) - return NULL; - - /* Primary bus */ - if (relbus && (bus->number != port->root_bus_nr)) - cfg_type = 1; - else - cfg_type = 0; - - /* build the mpage register */ - mpage = (bus->number << 11) | (dev << 6) | (cfg_type << 5); - mpage |= 0x10; /* enable MPAGE for configuration access */ - mpage |= (fn << 19); - - if ((mpage != last_mpage) || (port->index != last_port)) { - addr = ((u32)port->cfg_addr) - + ACPX1_PCIE_MPAGE_UPPER(7); - writel(0x0, (u32 *) addr); - addr = ((u32)port->cfg_addr) - + ACPX1_PCIE_MPAGE_LOWER(7); - writel(mpage, (u32 *) addr); - /* printk("pcie_get_base: %02x:%02x:%02x setting - mpage = 0x%08x in addr = 0x%08x\n", bus->number, - dev, fn, mpage, addr);*/ - last_mpage = mpage; - last_port = port->index; - } - return port->cfg_data; + if (bus->number == port->root_bus_nr) + return port->regs; + + relbus = bus->number - (port->root_bus_nr + 1); + dev = (PCI_SLOT(devfn) & 0xf8) >> 3; + fn = (PCI_FUNC(devfn) & 0x7); + + if (dev > 31) + return NULL; + + /* Build the mpage register (MPAGE[4]=1 for cfg access) */ + mpage = (fn << 19) | (bus->number << 11) | (dev << 6) | (1<<4); + + /* Primary bus */ + if (relbus && (bus->number != port->root_bus_nr)) + mpage |= 1<<5; + + if (mpage != port->last_mpage) { + writel(0, port->regs + PCIE_MPAGE_U(7)); + writel(mpage, port->regs + PCIE_MPAGE_L(7)); + port->last_mpage = mpage; } + + return port->cfg_data; } -/* Read the config space */ +/* + * Read PCI config space + */ static int -arm_pciex_axxia_read_config(struct pci_bus *bus, unsigned int devfn, - int offset, int len, u32 *val) +arm_pciex_axxia_read_config(struct pci_bus *bus, + unsigned int devfn, + int offset, + int len, + u32 *val) { struct axxia_pciex_port *port = bus_to_port(bus); void __iomem *addr; @@ -188,7 +265,7 @@ arm_pciex_axxia_read_config(struct pci_bus *bus, unsigned int devfn, */ if (bus->number == 0) { int wo = offset & 0xfffffffc; - bus_addr = (u32) addr + wo; + bus_addr = (u32)addr + wo; bus_addr1 = bus_addr; } else { /* @@ -220,7 +297,7 @@ arm_pciex_axxia_read_config(struct pci_bus *bus, unsigned int devfn, /* * do the read */ - val32 = readl((u32 *)bus_addr); + val32 = readl((u32 __iomem *)bus_addr); switch (len) { case 1: @@ -236,7 +313,7 @@ arm_pciex_axxia_read_config(struct pci_bus *bus, unsigned int devfn, #ifdef PRINT_CONFIG_ACCESSES printk(KERN_INFO - "acp_read_config for PEI%d: %3d fn=0x%04x o=0x%04x l=%d " + "acp_read_config for PCIE%d: %3d fn=0x%04x o=0x%04x l=%d " "a=0x%08x v=0x%08x, dev=%d\n", port->index, bus->number, devfn, offset, len, bus_addr, *val, PCI_SLOT(devfn)); @@ -244,9 +321,15 @@ arm_pciex_axxia_read_config(struct pci_bus *bus, unsigned int devfn, return rc; } -/* Write the config space */ -static int arm_pciex_axxia_write_config(struct pci_bus *bus, unsigned int devfn, - int offset, int len, u32 val) +/* + * Write PCI config space. + */ +static int +arm_pciex_axxia_write_config(struct pci_bus *bus, + unsigned int devfn, + int offset, + int len, + u32 val) { struct axxia_pciex_port *port = bus_to_port(bus); void __iomem *addr; @@ -277,7 +360,7 @@ static int arm_pciex_axxia_write_config(struct pci_bus *bus, unsigned int devfn, int bs = ((offset & 0x3) * 8); bus_addr = (u32) addr + (offset & 0xfffffffc); - val32 = readl((u32 *)bus_addr); + val32 = readl((u32 __iomem *)bus_addr); if (len == 2) { val32 = (val32 & ~(0xffff << bs)) @@ -303,13 +386,13 @@ static int arm_pciex_axxia_write_config(struct pci_bus *bus, unsigned int devfn, switch (len) { case 1: - writeb(val, (u8 *)(bus_addr)); + writeb(val, (u8 __iomem *)(bus_addr)); break; case 2: - writew(val, (u16 *)(bus_addr)); + writew(val, (u16 __iomem *)(bus_addr)); break; default: - writel(val, (u32 *)(bus_addr)); + writel(val, (u32 __iomem *)(bus_addr)); break; } return PCIBIOS_SUCCESSFUL; @@ -320,502 +403,428 @@ static struct pci_ops axxia_pciex_pci_ops = { .write = arm_pciex_axxia_write_config, }; -/* ACP PCIe ISR to handle Legacy Interrupts */ +/* + * pcie_legacy_isr + * + * The interrupt line for this handler is shared between the PCIE controller + * itself (for status and error interrupts) and devices using legacy PCI + * interupt signalling. Statis and error interrupts are serviced here and this + * handler will return IRQ_HANDLED. If the reasont is the assertion of a device + * legacy interrupt, this handler returns IRQ_NONE the next action on this line + * will be called (the PCI EP interrupt service routine). + */ static irqreturn_t -acp_pcie_isr(int irq, void *arg) +pcie_legacy_isr(int irq, void *arg) { - u32 intr_status; - u8 externalPciIntr = 0; - struct axxia_pciex_port *port = (struct axxia_pciex_port *)arg; - void __iomem *mbase = (void __iomem *)port->cfg_addr; + struct axxia_pciex_port *port = arg; + void __iomem *mbase = port->regs; + u32 intr_status; + irqreturn_t retval = IRQ_HANDLED; /* read the PEI interrupt status register */ - intr_status = readl(mbase+0x10c0); + intr_status = readl(mbase + PCIE_INT0_STATUS); /* check if this is a PCIe message not from an external device */ - if (intr_status & 0xf3ffffab) { - u32 t2a_err_stat; - u32 t2a_other_err_stat; + if (intr_status & INT0_ERROR) { u32 int_enb; - u32 linkStatus; u32 offset; - printk(KERN_ERR - "ACP_PCIE_ISR: got PEI%d error interrupt " - "0x%08x\n", intr_status, port->index); + pr_info("PCIE%d: Error interrupt %#x\n", + port->index, intr_status); - linkStatus = readl(mbase+0x117c); - printk(KERN_ERR "link_status (0x117c) = 0x%08x\n", - linkStatus); + pr_info("PCIE%d: link status = %#x\n", + port->index, readl(mbase + PCIE_LINK_STATUS)); if (intr_status & 0x00020000) { - t2a_err_stat = readl(mbase+0x1170); - printk(KERN_ERR "t2a_fn_indp_err_stat = 0x%08x\n", - t2a_err_stat); - - int_enb = readl(mbase+0x10c4); + pr_info("PCIE%d: t2a_fn_indp_err_stat = %#x\n", + port->index, readl(mbase+0x1170)); + int_enb = readl(mbase + PCIE_INT0_ENABLE); int_enb &= 0xfffdffff; - writel(int_enb, mbase + 0x10c4); + writel(int_enb, mbase + PCIE_INT0_ENABLE); } if (intr_status & 0x00040000) { - t2a_other_err_stat = readl(mbase+0x1174); - printk(KERN_ERR "t2a_fn_indp_other_err_stat = 0x%08x\n", - t2a_other_err_stat); - int_enb = readl(mbase+0x10c4); + pr_info("PCIE%d: t2a_fn_indp_other_err_stat = %#x\n", + port->index, readl(mbase+0x1174)); + int_enb = readl(mbase + PCIE_INT0_ENABLE); int_enb &= 0xfffbffff; - writel(int_enb, mbase + 0x10c4); + writel(int_enb, mbase + PCIE_INT0_ENABLE); } if (intr_status & 0x00000800) { - printk(KERN_INFO "pci_config = 0x%08x\n", - readl(mbase + 0x1000)); - printk(KERN_INFO "pci_status = 0x%08x\n", - readl(mbase + 0x1004)); - int_enb = readl(mbase + 0x10c4); + pr_info("PCIE%d: config=%#x status=%#x\n", + port->index, + readl(mbase + PCIE_CONFIG), + readl(mbase + PCIE_STATUS)); + int_enb = readl(mbase + PCIE_INT0_ENABLE); int_enb &= 0xfffff7ff; - writel(int_enb, mbase + 0x10c4); + writel(int_enb, mbase + PCIE_INT0_ENABLE); } + /* - * dump all the potentially interesting PEI registers + * Dump all the potentially interesting PEI registers */ for (offset = 0x114c; offset <= 0x1180; offset += 4) { - printk(KERN_INFO " 0x%04x : 0x%08x\n", offset, - readl(mbase + offset)); + pr_err(" [0x%04x] = 0x%08x\n", + offset, readl(mbase + offset)); } + } else if (intr_status & INT0_INT_ASSERTED) { + /* Empty the message FIFO */ + while ((readl(port->regs + PCIE_MSG_IN_FIFO_STATUS) & 1) == 0) + (void) readl(port->regs + PCIE_MSG_IN_FIFO); + /* Next handler in chain will service this interrupt */ + retval = IRQ_NONE; } /* * We clear all the interrupts in the PEI status, even though * interrupts from external devices have not yet been handled. - * That should be okay, since the PCI IRQ in the MPIC won't be + * That should be okay, since the PCI IRQ in the GIC won't be * re-enabled until all external handlers have been called. */ - writel(intr_status, mbase + 0x10c0); - return externalPciIntr ? IRQ_NONE : IRQ_HANDLED; + writel(intr_status, mbase + PCIE_INT0_STATUS); + + return retval; } -/* ACP PCIe ISR to handle MSI Interrupts */ -static irqreturn_t -acp_pcie_MSI_isr(int irq, void *arg) { - u32 intr_status; - u8 msiIntr = 0, bit = 0; - struct axxia_pciex_port *port = (struct axxia_pciex_port *)arg; - void __iomem *mbase = (void __iomem *)port->cfg_addr; - u32 statusReg, statusVal; - - /* read the PEI MSI Level2 interrupt status register */ - intr_status = readl(mbase+0x1230); - - /* check if this is a PCIe MSI interrupt */ - if (intr_status & 0x0000ffff) { - msiIntr = 1; - for (bit = 0; bit < 16; bit++) { - if (intr_status & (0x1 << bit)) { - printk(KERN_INFO "PEI%d --> MSI%d-%d interrupt asserted\n", - port->index, bit*16, ((bit+1)*16)-1); - /* MSI Level 1 interrupt status */ - statusReg = (0x123c + (0xc * bit)); - statusVal = readl(mbase+statusReg); - printk(KERN_INFO "MSI status Reg 0x%x val = 0x%x\n", - statusReg, statusVal); - /* clear statusReg */ - writel(statusVal, mbase+statusReg); - } - } +/* + * pcie_msi_irq_handler + * + * This is the handler for PCIE MSI service. It will decode the signalled MSI + * using the following hierarchy of status bits. This handler is installed as a + * chained handler for each of the 16 interrupt lines on the top-level + * interrupt controller. When a pending MSI is found, this handler forwards the + * interrupt service to the corresponding MSI IRQs (numbered from + * AXXIA_MSI_FIRST..NR_IRQS). + * + * PCIE_MSI1_STATUS(group) + * + * PCIE_MSI0_STATUS +----------+ + * | MSI | + * +----------+ +----------+ | 0..15 | + * | ARM GIC | | GROUP | /-----------+ | + * | +----+ 0..15 +-------/ | | + * | | | | | | + * | +----+ +-------\ +----------+ + * | | | | \ + * | +----+ | \ +----------+ + * | | | | \ | MSI | + * | +----+ | \ | 16..31 | + * | | | | \-------+ | + * | +----+ | | | + * | | | | | | + * | | | | +----------+ + * | | . | | + * | | . | | ... + * | | . | | + * | | | | +----------+ + * | | | | | MSI | + * | +----+ +--------------------+ 240..255 | + * | | | | | | + * +----------+ +----------+ | | + * | | + * +----------+ + */ +static void +pcie_msi_irq_handler(int irq, struct axxia_pciex_port *port) +{ + void __iomem *mbase = port->regs; + u32 group = irq - port->irq[1]; + u32 status; + + /* Check if interrupt is pending */ + status = readl(mbase + PCIE_MSI0_STATUS); + if (!(status & (1<<group))) + return; + + /* Check next level interrupt status */ + status = readl(mbase + PCIE_MSI1_STATUS(group)) & 0xffff; + while (status) { + u32 line = ffs(status) - 1; + status &= ~(1<<line); + /* Clear interrupt on sub-level */ + writel((1<<line), mbase + PCIE_MSI1_STATUS(group)); + generic_handle_irq(AXXIA_MSI_FIRST + (group * 16) + line); } - /* - * We clear all the interrupts in the PEI status, even though - * interrupts from MSI devices have not yet been handled. - */ - writel(intr_status, mbase + 0x1230); - return msiIntr ? IRQ_NONE : IRQ_HANDLED; + + /* Clear interrupt on top-level*/ + writel(1<<group, mbase + PCIE_MSI0_STATUS); + +} + +static void +pcie_pei0_msi_handler(unsigned int irq, struct irq_desc *desc) +{ + kstat_incr_irqs_this_cpu(irq, desc); + /* Handle the PCIe interrupt */ + pcie_msi_irq_handler(irq, &axxia_pciex_ports[0]); + /* Signal end-of-interrupt */ + irq_desc_get_chip(desc)->irq_eoi(&desc->irq_data); } /* PCIe setup function */ -int axxia_pcie_setup(int portno, struct pci_sys_data *sys) +static int axxia_pcie_setup(int portno, struct pci_sys_data *sys) { - struct axxia_pciex_port *port; + struct axxia_pciex_port *port = &axxia_pciex_ports[sys->domain]; u32 pci_config, pci_status, link_state; int i, num_pages, err; - u32 mpage_lower, pciah, pcial; - u64 size, bar0_size; - void __iomem *cfg_addr = NULL, *cfg_data = NULL, *tpage_base = NULL; - int mappedIrq, irq_entry; + u32 outbound_size; u32 inbound_size; + u64 dest; - port = &axxia_pciex_ports[sys->domain]; - printk(KERN_INFO "cfg_space start = 0x%012llx, end = 0x%012llx\n", - port->cfg_space.start, port->cfg_space.end); - printk(KERN_INFO "utl_regs start = 0x%012llx, end = 0x%012llx\n", - port->utl_regs.start, port->utl_regs.end); port->root_bus_nr = sys->busnr; - /* 1M external config */ - cfg_data = ioremap(port->cfg_space.start, 0x100000); - if (cfg_data == NULL) { - printk(KERN_ERR "%s: Can't map external config space !", - port->node->full_name); + /* Map PCIe bridge control registers */ + port->regs = ioremap(port->utl_regs.start, + resource_size(&port->utl_regs)); + if (!port->regs) { + pr_err("PCIE%d: Failed to map control registers\n", portno); goto fail; } - port->cfg_data = cfg_data; - - /* IORESOURCE_MEM */ - port->outbound.name = "PCIe MEM"; - port->outbound.start = port->cfg_space.start - 0x38000000; - /* allocate 256 M -- 2 MPAGEs worth */ - port->outbound.end = port->outbound.start + 0x10000000 - 1; - port->outbound.flags = IORESOURCE_MEM; - - if (request_resource(&iomem_resource, &port->outbound)) - panic("Request PCIe Memory resource failed for port %d\n", - portno); - pci_add_resource_offset(&sys->resources, &port->outbound, - sys->mem_offset); - printk(KERN_INFO "port res start = 0x%012llx, end = 0x%012llx\n", - port->outbound.start, port->outbound.end); - printk(KERN_INFO "port system mem_offset start = 0x%012llx\n", - sys->mem_offset); - - /* 4K internal config */ - cfg_addr = ioremap(port->utl_regs.start, 0x10000); - if (cfg_addr == NULL) { - printk(KERN_ERR "%s: Can't map external config space !", - port->node->full_name); + /* Map range for access to PCI configuration space */ + port->cfg_data = ioremap(port->cfg_space.start, + resource_size(&port->cfg_space)); + if (!port->cfg_data) { + pr_err("PCIE%d: Failed to map config space\n", portno); goto fail; } - port->cfg_addr = cfg_addr; - printk("cfg_addr for port %d = 0x%8x\n", port->index, - (unsigned int)port->cfg_addr); - pci_config = readl(cfg_addr); -#ifdef PRINT_CONFIG_ACCESSES - printk(KERN_INFO "pci_vendor = 0x%08x\n", pci_config); -#endif - /* hookup an interrupt handler */ - printk(KERN_INFO "PCIE%d mapping interrupt\n", port->index); - mappedIrq = irq_of_parse_and_map(port->node, 0); - printk(KERN_INFO "Requesting irq#%d for PEI%d Legacy INTs\n", - mappedIrq, port->index); + pci_add_resource_offset(&sys->resources, &port->outbound, + port->outbound.start - port->pci_addr); - err = request_irq(mappedIrq, acp_pcie_isr, - IRQF_SHARED, "acp_pcie", port); + /* Status/error interrupt */ + port->irq[0] = irq_of_parse_and_map(port->node, 0); + err = request_irq(port->irq[0], pcie_legacy_isr, IRQF_SHARED, + "pcie", port); if (err) { - printk(KERN_ERR "request_irq failed!!!! for IRQ# %d err = %d\n", - mappedIrq, err); + pr_err("PCIE%d: Failed to request IRQ#%d (%d)\n", + portno, port->irq[0], err); goto fail; } - /* MSI INTS for PEI0 */ + /* MSI interrupts for PEI0 */ if (sys->domain == 0) { - /* IRQ# 73-88 for PEI0 MSI INTs */ - for (irq_entry = 1; irq_entry <= 16; irq_entry++) { - mappedIrq = irq_of_parse_and_map(port->node, irq_entry); - printk(KERN_INFO - "Requesting irq#%d for PEI0 MSI INTs\n", - mappedIrq); - err = request_irq(mappedIrq, acp_pcie_MSI_isr, - IRQF_SHARED, "acp_pcie_MSI", port); - if (err) { - printk(KERN_ERR - "request_irq failed!!!! for IRQ# %d err = %d\n", - mappedIrq, err); - goto fail; - } + for (i = 1; i <= 16; i++) { + port->irq[i] = irq_of_parse_and_map(port->node, i); + irq_set_chained_handler(port->irq[i], + pcie_pei0_msi_handler); } } /* Setup as root complex */ - pci_config = readl(cfg_addr + 0x1000); -#ifdef PRINT_CONFIG_ACCESSES - printk("pci_config = 0x%08x\n", pci_config); -#endif - - pci_status = readl(cfg_addr + 0x1004); - link_state = (pci_status & 0x3f00) >> 8; - printk(KERN_INFO - "PCIE%d status = 0x%08x : PCI link state = 0x%x\n", + pci_config = readl(port->regs + PCIE_CONFIG); + pci_status = readl(port->regs + PCIE_STATUS); + link_state = (pci_status >> 8) & 0x3f; + pr_info("PCIE%d: status=0x%08x, link state=%#x\n", port->index, pci_status, link_state); /* make sure the ACP device is configured as PCI Root Complex */ if ((pci_status & 0x18) != 0x18) { - printk(KERN_INFO - "ACP device is not PCI Root Complex! status = 0x%08x\n", - pci_status); + pr_err("PCIE%d: Device is not Root Complex\n", portno); goto fail; } - /* make sure the link is up */ + /* Make sure the link is up */ if (link_state != 0xb) { - /* reset */ - printk("PCI link in bad state - resetting\n"); + /* Reset */ + pr_warn("PCIE%d: Link in bad state - resetting\n", port->index); pci_config |= 1; - writel(pci_config, cfg_addr + 0x1000); + writel(pci_config, port->regs + PCIE_CONFIG); msleep(1000); - pci_status = readl(cfg_addr + 0x1004); + pci_status = readl(port->regs + PCIE_STATUS); link_state = (pci_status & 0x3f00) >> 8; - printk(KERN_INFO "PCI link state now = 0x%x\n", link_state); + pr_warn("PCIE%d: (after reset) link state=%#x\n", + port->index, link_state); if (link_state != 0xb) { - printk(KERN_INFO "PCI link still in bad state - giving up!\n"); + pr_warn("PCIE%d: Link in bad state - giving up!\n", + port->index); goto fail; } } - /* ACP X1 setup MPAGE registers */ /* - * MPAGE7 is dedicated to config access, so we only - * have 7 128MB pages available for memory i/o. - * Calculate how many pages we need + * Setup outbound PCI Memory Window */ - size = 7 * 1024*128*1024; - num_pages = ((size - 1) >> 27) + 1; - pciah = U64_TO_U32_HIGH(port->pci_addr); - pcial = U64_TO_U32_LOW(port->pci_addr); + + outbound_size = resource_size(&port->outbound); + num_pages = (outbound_size + MPAGE_SIZE - 1) / MPAGE_SIZE; + dest = port->pci_addr; for (i = 0; i < num_pages; i++) { - mpage_lower = (pcial & 0xf8000000); - mpage_lower |= 0x0; - writel(pciah, cfg_addr + ACPX1_PCIE_MPAGE_UPPER(i)); - writel(mpage_lower, cfg_addr + ACPX1_PCIE_MPAGE_LOWER(i)); - pcial += 0x08000000; + u32 mpage_u = dest >> 32; + u32 mpage_l = (u32)dest & ~(MPAGE_SIZE-1); + writel(mpage_u, port->regs + PCIE_MPAGE_U(i)); + writel(mpage_l, port->regs + PCIE_MPAGE_L(i)); + pr_debug("PCIE%d: MPAGE(%d) = %08x %08x\n", + port->index, i, mpage_u, mpage_l); + dest += MPAGE_SIZE; } - inbound_size = (u32) (port->inbound.end - port->inbound.start + 1); + /* + * Setup inbound PCI window + */ - /* configures the RC Memory Space Configuration Register */ - writel(inbound_size, cfg_addr + 0x11f4); + /* Configure the inbound window size */ + inbound_size = (u32) resource_size(&port->inbound); + writel(inbound_size, port->regs + PCIE_RC_BAR0_SIZE); + + /* Verify BAR0 size */ + { + u32 bar0_size; + writel(~0, port->regs + PCI_BASE_ADDRESS_0); + bar0_size = readl(port->regs + PCI_BASE_ADDRESS_0); + if ((bar0_size & ~0xf) != inbound_size) + pr_err("PCIE%d: Config BAR0 failed\n", port->index); + } - /* write all 1s to BAR0 register */ - writel(0xffffffff, cfg_addr + 0x10); + /* Set the BASE0 address to start of PCIe base */ + writel(port->pci_bar, port->regs + PCI_BASE_ADDRESS_0); - /* read back BAR0 */ - bar0_size = readl(cfg_addr + 0x10); - if ((bar0_size & inbound_size) != inbound_size) - printk(KERN_INFO "Writing/Reading BAR0 reg failed\n"); + /* Setup TPAGE registers for inbound mapping + * + * We set the MSB of each TPAGE to select 128-bit AXI access. For the + * address field we simply program an incrementing value to map + * consecutive pages + */ + for (i = 0; i < 8; i++) + writel(PCIE_TPAGE_128 | i, port->regs + PCIE_TPAGE_BAR0(i)); - /* set the BASE0 address to start of PCIe base */ - writel(port->inbound.start, cfg_addr + 0x10); - /* setup TPAGE registers for inbound mapping */ - /* We set the MSB of each TPAGE to select 128-bit AXI access. - * For the address field we simply program an incrementing value - * to map consecutive pages - */ - tpage_base = cfg_addr + 0x1050; - for (i = 0; i < 8; i++) { - writel((0x80000000 + i), tpage_base); - tpage_base += 4; - } + /* Enable all legacy/status/error interrupts */ + writel(INT0_MSI | INT0_INT_ASSERTED | INT0_ERROR, + port->regs + PCIE_INT0_ENABLE); + /* Enable all MSI interrupt groups */ + writel(0xFFFF, port->regs + PCIE_MSI0_ENABLE); + /* Enable all lines in all subgroups */ + for (i = 0; i < 16; i++) + writel(0xFFFF, port->regs + PCIE_MSI1_ENABLE(i)); return 1; fail: - if (cfg_data) - iounmap(cfg_data); - if (cfg_addr) - iounmap(cfg_addr); + if (port->cfg_data) + iounmap(port->cfg_data); + if (port->regs) + iounmap(port->regs); return 0; } -/* Just a dummy arch_setup_msi_irq() function */ -int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) +/* + * Allocate MSI page. A MSI is generated when EP writes to this PCI address. + * The region must be 1Kb to manage 256 MSIs. + */ +static void * +pcie_alloc_msi_table(struct pci_dev *pdev, struct axxia_pciex_port *port) { - return 0; -} + u32 msi_lower; + void *msi_virt; + if (dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)) != 0 && + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)) != 0) { + dev_err(&pdev->dev, "No memory for MSI table\n"); + return NULL; + } -void arch_teardown_msi_irq(unsigned int irq) -{ -} + msi_virt = dma_alloc_coherent(&pdev->dev, 1024, + &port->msi_phys, + GFP_KERNEL); + if (msi_virt) { + msi_lower = (u32)port->msi_phys; + /* Please note we have 1:1 mapping for inbound */ + port->msi_pci_addr = port->inbound.start + msi_lower; + writel(msi_lower>>10, port->regs + PCIE_AXI_MSI_ADDR); + } + return msi_virt; +} -/* Scan PCIe bus */ +/* + * Scan PCIe bus + */ static struct pci_bus __init * axxia_pcie_scan_bus(int nr, struct pci_sys_data *sys) { - struct pci_bus *bus; - struct axxia_pciex_port *port; - - /* get the pointer to port struct from domain# */ - port = &axxia_pciex_ports[sys->domain]; - - if (nr < axxia_pciex_port_count) { - bus = pci_scan_root_bus(NULL, sys->busnr, - &axxia_pciex_pci_ops, sys, &sys->resources); - } else { - bus = NULL; - printk(KERN_WARNING "PCIE: exceeded number of supported PEI ports\n"); - } + if (WARN_ON(nr >= PCIE_MAX_PORTS)) + return NULL; - return bus; + return pci_scan_root_bus(NULL, sys->busnr, &axxia_pciex_pci_ops, + sys, &sys->resources); } -/* MSI setup */ -static void __devinit axxia_pcie_msi_enable(struct pci_dev *dev) -{ - u32 pci_higher = 0, msi_lower = 0; - u16 flag_val; - int pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - struct axxia_pciex_port *port; - void *msi_virt; - int device, fn, bus_num; - static u32 pci_lower, msi_count; - port = bus_to_port(dev->bus); - if (pos <= 0) { - dev_err(&dev->dev, "skipping MSI enable\n"); - printk(KERN_INFO "skipping MSI enable\n"); - return; - } - - /* MSI applicable only to PEI0 */ - if (port->index == 0) { - /* MSI is generated when EP writes to address mapped */ - if (msi_lower == 0) { - /* MSI support only in PEI0 */ - /* allocate 1K to manage 256 MSIs - * one for each endpoint */ - - if ((!dma_set_coherent_mask(&dev->dev, - DMA_BIT_MASK(64))) || - (!dma_set_coherent_mask(&dev->dev, - DMA_BIT_MASK(32)))) { - msi_virt = dma_alloc_coherent(&dev->dev, - 1024, &(port->msi_phys), GFP_KERNEL); - } else { - printk(KERN_INFO - "No suitable DMA available. MSI cannot" - "be supported\n"); - return; - } - msi_lower = (u32)port->msi_phys; - /* Please note we have 1:1 mapping for inbound */ - pci_lower = port->inbound.start + msi_lower; - printk("PEI%d dma_alloc_coherent msiAddr = 0x%x\n", - port->index, msi_lower); - writel(msi_lower>>10, port->cfg_addr + 0x1190); - } - device = ((PCI_SLOT(dev->devfn) & 0xf8) >> 3); - fn = PCI_FUNC(dev->devfn) & 0x7; - bus_num = dev->bus->number; - - printk(KERN_INFO - "PEI%d axxia_pcie_msi_enable Found MSI%d" - ", msi_lower = 0x%x" - "for bus_num = %d, dev = %d, fn = %d\n", - port->index, msi_count, msi_lower, - bus_num, device, fn); - pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, - pci_lower); - pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, - pci_higher); - - /* Enable MSI */ - dev->msi_enabled = 1; - - pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &flag_val); - flag_val = flag_val | 0x1; - pci_write_config_word(dev, pos + PCI_MSI_FLAGS, - (flag_val | 0x1)); - - /* for next EP MSI */ - pci_lower = pci_lower + 0x4; - msi_count++; - } - return; +static int __init +axxia_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + struct pci_sys_data *sys = dev->sysdata; + struct axxia_pciex_port *port = &axxia_pciex_ports[sys->domain]; + return port->irq[0]; } -DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, axxia_pcie_msi_enable); + +/* IRQ chip ops for MSI IRQs */ +static struct irq_chip axxia_msi_chip = { + .name = "PCI-MSI", + .irq_enable = unmask_msi_irq, + .irq_disable = mask_msi_irq, + .irq_mask = mask_msi_irq, + .irq_unmask = unmask_msi_irq, +}; -/* Port definition struct - * Please note: PEI core#1 is not used in AXM5500 */ +/* Port definition struct */ static struct hw_pci axxia_pcie_hw[] = { [0] = { .nr_controllers = 1, .domain = 0, .swizzle = pci_std_swizzle, .setup = axxia_pcie_setup, - .scan = axxia_pcie_scan_bus + .scan = axxia_pcie_scan_bus, + .map_irq = axxia_pcie_map_irq }, [1] = { .nr_controllers = 1, .domain = 1, .swizzle = pci_std_swizzle, .setup = axxia_pcie_setup, - .scan = axxia_pcie_scan_bus + .scan = axxia_pcie_scan_bus, + .map_irq = axxia_pcie_map_irq } }; -/* Initialize PCIe */ -void __init axxia_pcie_init(void) -{ - struct device_node *np; - int found_a_port = 0; - - /* allocate memory */ - axxia_pciex_ports = kzalloc(axxia_pciex_port_count - * sizeof(struct axxia_pciex_port), GFP_KERNEL); - - if (!axxia_pciex_ports) { - printk(KERN_WARNING "PCIE: failed to allocate ports array\n"); - return; - } - - for_each_compatible_node(np, NULL, "lsi,plb-pciex") { - ++found_a_port; - axxia_probe_pciex_bridge(np); - } - - if (0 != found_a_port) { - pci_common_init(&axxia_pcie_hw[0]); - pci_common_init(&axxia_pcie_hw[1]); - } - - return; -} - -static void axxia_probe_pciex_bridge(struct device_node *np) +static void +axxia_probe_pciex_bridge(struct device_node *np) { struct axxia_pciex_port *port; - u32 pval; - int portno; + u32 portno; const char *val; const u32 *field; int rlen; - int pna = of_n_addr_cells(np); - int num = pna + 5; - u64 size, pci_addr; + int pna = of_n_addr_cells(np); /* address-size of parent */ + u32 pci_space; + u64 pci_addr; + u64 cpu_addr; + u64 size; /* Check if device is enabled */ - if (!of_device_is_available(np)) { - printk(KERN_INFO "%s: Port disabled via device-tree\n", - np->full_name); + if (!of_device_is_available(np)) return; - } /* Get the port number from the device-tree */ - if (!of_property_read_u32(np, "port", &pval)) { - portno = pval; - - printk(KERN_INFO "PCIE Port %d found\n", portno); - } else { - printk(KERN_ERR "PCIE: Can't find port number for %s\n", - np->full_name); + if (of_property_read_u32(np, "port", &portno) != 0) { + pr_err("%s: No 'port' property\n", np->full_name); return; } - if (portno > axxia_pciex_port_count) { - printk(KERN_ERR "PCIE: port number out of range for %s\n", - np->full_name); + if (portno >= PCIE_MAX_PORTS) { + pr_err("%s: Port %d out of range\n", np->full_name, portno); return; } port = &axxia_pciex_ports[portno]; port->index = portno; - + snprintf(port->name, sizeof port->name - 1, "PCIE%d", portno); port->node = of_node_get(np); /* Check if device_type property is set to "pci" or "pci-endpoint". @@ -823,90 +832,196 @@ static void axxia_probe_pciex_bridge(struct device_node *np) * as root-complex or as endpoint. */ val = of_get_property(port->node, "device_type", NULL); - if (!strcmp(val, "pci-endpoint")) { - port->endpoint = 1; - } else if (!strcmp(val, "pci")) { - port->endpoint = 0; - } else { - printk(KERN_ERR "PCIE%d: missing or incorrect device_type for %s\n", - portno, np->full_name); - return; - } - printk(KERN_ERR "PCIE%d: endpoint = %d\n", portno, port->endpoint); + port->endpoint = val && strcmp(val, "pci-endpoint") == 0; - /* Fetch config space registers address */ + /* Fetch address range for PCIE config space */ if (of_address_to_resource(np, 0, &port->cfg_space)) { - printk(KERN_ERR "%s: Can't get PCI-E config space !", - np->full_name); + pr_err("PCIE%d: No resource for PCIe config space\n", portno); return; } - printk(KERN_INFO - "cfg_space start = 0x%012llx, end = 0x%012llx\n", - port->cfg_space.start, port->cfg_space.end); - /* Fetch host bridge internal registers address */ + /* Fetch address range for host bridge internal registers */ if (of_address_to_resource(np, 1, &port->utl_regs)) { - printk(KERN_ERR "%s: Can't get UTL register base !", - np->full_name); + pr_err("PCIE%d: No resource for PCIe registers\n", portno); return; } - printk(KERN_INFO - "utl_regs start = 0x%012llx, end = 0x%012llx\n", - port->utl_regs.start, port->utl_regs.end); + + if (request_resource(&iomem_resource, &port->utl_regs)) + return; + + /* + * Outbound PCI memory window + */ + + /* Defaults */ + port->outbound = pcie_outbound_default[portno]; + port->outbound.name = port->name; field = of_get_property(np, "ranges", &rlen); - if (field == NULL) - printk("not able to get ranges\n"); + if (field) { + pci_space = of_read_number(field + 0, 1); + switch ((pci_space >> 24) & 3) { + case 0: + pr_err("PCIE%d: Invalid 'ranges'\n", portno); + break; + case 1: /* PCI IO Space */ + pr_err("PCIE%d: PCI IO not supported\n", portno); + break; + case 2: /* PCI MEM 32-bit */ + case 3: /* PCI MEM 64-bit */ + cpu_addr = of_read_number(field + 3, 2); + size = of_read_number(field + 5, 2); + port->outbound.start = cpu_addr; + port->outbound.end = cpu_addr + size - 1; + port->pci_addr = of_read_number(field + 1, 2); + break; + } + } - pci_addr = of_read_number(field + 1, 2); - printk(KERN_INFO "pci_addr = 0x%012llx\n", pci_addr); - port->pci_addr = pci_addr; + if (resource_size(&port->outbound) > MAX_OUTBOUND_SIZE) { + pr_err("PCIE%d: Outbound window too large (using max %#x)\n", + portno, MAX_OUTBOUND_SIZE); + port->outbound.end = (port->outbound.start + + MAX_OUTBOUND_SIZE - 1); + } + + if (request_resource(&iomem_resource, &port->outbound)) { + pr_err("PCIE%d: Memory resource request failed\n", portno); + return; + } + + if (request_resource(&iomem_resource, &port->cfg_space)) { + pr_err("PCIE%d: Config space request failed\n", portno); + return; + } - printk(KERN_INFO "%s PCIE%d config base = 0x%012llx\n", - np->full_name, port->index, port->utl_regs.start); + pr_info("PCIE%d: Outbound %#llx..%#llx (CPU) -> %#llx (PCI)\n", + portno, + port->outbound.start, port->outbound.end, + port->pci_addr); - /* Default 256 MB */ - port->inbound.start = 0; - size = 0x10000000; - port->inbound.end = size - 1; + /* + * Inbound PCI memory window + */ + + /* Default 4GB */ + port->inbound.name = "PCIE DMA"; + port->inbound.start = 0x00000000; + port->inbound.end = 0xffffffff; port->inbound.flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; /* Get dma-ranges property */ field = of_get_property(np, "dma-ranges", &rlen); - if (field == NULL) { - printk(KERN_INFO "not able to get dma-ranges\n"); - /* use default */ - return; + if (!field) { + pr_info("PCIE%d: No 'dma-ranges' property, using defaults\n", + portno); + } else { + BUILD_BUG_ON(sizeof(resource_size_t) != sizeof(u64)); + + /* Limited to one inbound window for now... */ + pci_space = of_read_number(field + 0, 1); + pci_addr = of_read_number(field + 1, 2); + cpu_addr = of_read_number(field + 3, pna); + size = of_read_number(field + pna + 3, 2); + + port->inbound.start = cpu_addr; + port->inbound.end = cpu_addr + size - 1; + port->pci_bar = pci_addr; } - /* Walk it */ - while ((rlen -= num * 4) >= 0) { - u64 pci_addr = of_read_number(field + 1, 2); - u64 cpu_addr = of_read_number(field + 3, 1); - size = of_read_number(field + pna + 3, 2); - field += num; + pr_info("PCIE%d: Inbound %#llx (PCI) -> %#llx..%#llx (CPU)\n", + portno, + port->pci_bar, + port->inbound.start, port->inbound.end); - /* We currently only support memory at 0, and pci_addr - * within 32 bits space and 1:1 mapping - */ - if (cpu_addr != 0 || pci_addr > 0xffffffff) { - printk(KERN_WARNING - "%s: Ignored unsupported dma range" - "0x%016llx...0x%016llx -> 0x%016llx\n", - np->full_name, pci_addr, - pci_addr + size - 1, cpu_addr); +} + +/* + * Allocate a MSI interrupt number and IRQ (the IRQ is a constant offset from + * the MSI index). The kernel IRQs for MSI are in a range above hardware IRQs. + * + * First call also allocates the 1Kb MSI table and configures the controller. + */ +int +arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) +{ + struct axxia_pciex_port *port = bus_to_port(pdev->bus); + struct msi_msg msg; + int pos, irq; + + /* Allocate MSI table on demand when first device needs it */ + if (!port->msi_virt) + port->msi_virt = pcie_alloc_msi_table(pdev, port); + + if (!port->msi_virt) + return -ENOMEM; + + /* Find available MSI */ +again: + pos = find_first_zero_bit(msi_irq_in_use, NUM_MSI_IRQ); + /* Offset to get the IRQ */ + irq = AXXIA_MSI_FIRST + pos; + if (irq >= NR_IRQS) + return -ENOSYS; + if (test_and_set_bit(pos, msi_irq_in_use)) + goto again; + + /* Initialize IRQ descriptor */ + dynamic_irq_init(irq); + irq_set_msi_desc(irq, desc); + /* Use a simple handle for our "SW" MSI IRQs */ + irq_set_chip_and_handler(irq, &axxia_msi_chip, handle_simple_irq); + set_irq_flags(irq, IRQF_VALID); + + /* Configure PCI device with its MSI address */ + msg.address_hi = 0x0; + msg.address_lo = port->msi_pci_addr + 4*pos; + msg.data = irq; + write_msi_msg(irq, &msg); + + return 0; +} + +/* + * Called by the generic MSI layer to free MSI IRQ. + */ +void +arch_teardown_msi_irq(unsigned int irq) +{ + int pos = irq - AXXIA_MSI_FIRST; + + if (0 <= pos && pos < NR_IRQS) { + clear_bit(pos, msi_irq_in_use); + dynamic_irq_cleanup(irq); + } +} + +/** + * Initialize PCIe controller(s) found in the device tree. + */ +void __init +axxia_pcie_init(void) +{ + struct device_node *np; + int num_ports = 0; + + /* allocate memory */ + axxia_pciex_ports = kzalloc(PCIE_MAX_PORTS * + sizeof(struct axxia_pciex_port), + GFP_KERNEL); + if (!axxia_pciex_ports) { + pr_err("PCIE: No memory\n"); + return; + } + + for_each_compatible_node(np, NULL, "lsi,plb-pciex") { + if (!of_device_is_available(np)) continue; - } - /* Use that */ - port->inbound.start = pci_addr; - /* Beware of 32 bits resources */ - if (sizeof(resource_size_t) == sizeof(u32) && - (pci_addr + size) > 0x100000000ull) { - port->inbound.end = 0xffffffff; - } else - port->inbound.end = port->inbound.start + size - 1; - break; + + axxia_probe_pciex_bridge(np); + pci_common_init(&axxia_pcie_hw[num_ports]); + if (++num_ports == PCIE_MAX_PORTS) + break; } - printk(KERN_INFO "inbound start = 0x%016llx, end = 0x%016llx\n",\ - port->inbound.start, port->inbound.end); } + -- 1.8.3 _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto