aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-12-10 13:18:59 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-12-10 13:18:59 -0500
commit44bf32b550bde3b0016a3892ef3dea619ee79dae (patch)
treeb18387b30145396bf2adefbf7707ca775503a489
parent4e094627eb584c717a6d9eabdb5e38d4d08b845b (diff)
downloadltsi-kernel-44bf32b550bde3b0016a3892ef3dea619ee79dae.tar.gz
altera pcie driver added
-rw-r--r--patches.altera/0001-ARM-Add-msi.h-to-Kbuild.patch31
-rw-r--r--patches.altera/0002-PCI-altera-Add-Altera-PCIe-host-controller-driver.patch746
-rw-r--r--patches.altera/0003-PCI-altera-Add-Altera-PCIe-MSI-driver.patch435
-rw-r--r--patches.altera/0004-PCI-altera-Fix-loop-in-tlp_read_packet.patch45
-rw-r--r--patches.altera/0005-PCI-altera-Fix-Requester-ID-for-config-accesses.patch54
-rw-r--r--patches.altera/0006-PCI-altera-Check-TLP-completion-status.patch70
-rw-r--r--patches.altera/0007-PCI-altera-Fix-error-when-INTx-is-4.patch38
-rw-r--r--series7
8 files changed, 1426 insertions, 0 deletions
diff --git a/patches.altera/0001-ARM-Add-msi.h-to-Kbuild.patch b/patches.altera/0001-ARM-Add-msi.h-to-Kbuild.patch
new file mode 100644
index 00000000000000..6ae892ea75e240
--- /dev/null
+++ b/patches.altera/0001-ARM-Add-msi.h-to-Kbuild.patch
@@ -0,0 +1,31 @@
+From 12648d2bb2f4fec718948e9758aafa251ade27d0 Mon Sep 17 00:00:00 2001
+From: Ley Foon Tan <lftan@altera.com>
+Date: Wed, 9 Dec 2015 21:07:22 +0800
+Subject: [PATCH 1/7] ARM: Add msi.h to Kbuild
+
+Include asm-generic/msi.h to support CONFIG_GENERIC_MSI_IRQ_DOMAIN.
+This fixes a compilation error:
+
+ include/linux/msi.h:123:21: fatal error: asm/msi.h: No such file or directory
+
+Signed-off-by: Ley Foon Tan <lftan@altera.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+---
+ arch/arm/include/asm/Kbuild | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild
+index 3c4596d0ce6c..01806f52768c 100644
+--- a/arch/arm/include/asm/Kbuild
++++ b/arch/arm/include/asm/Kbuild
+@@ -14,6 +14,7 @@ generic-y += local.h
+ generic-y += local64.h
+ generic-y += mcs_spinlock.h
+ generic-y += msgbuf.h
++generic-y += msi.h
+ generic-y += param.h
+ generic-y += parport.h
+ generic-y += poll.h
+--
+2.6.3
+
diff --git a/patches.altera/0002-PCI-altera-Add-Altera-PCIe-host-controller-driver.patch b/patches.altera/0002-PCI-altera-Add-Altera-PCIe-host-controller-driver.patch
new file mode 100644
index 00000000000000..a6f9036e0873d9
--- /dev/null
+++ b/patches.altera/0002-PCI-altera-Add-Altera-PCIe-host-controller-driver.patch
@@ -0,0 +1,746 @@
+From 2ad8e4c2e98b8c67c94d831e2f5a8056178c4fbb Mon Sep 17 00:00:00 2001
+From: Ley Foon Tan <lftan@altera.com>
+Date: Wed, 9 Dec 2015 21:07:23 +0800
+Subject: [PATCH 2/7] PCI: altera: Add Altera PCIe host controller driver
+
+Add the Altera PCIe host controller driver.
+
+[lftan: backport to 4.1-ltsi]
+[bhelgaas: whitespace, fold in DT and maintainer updates, OF_PCI
+dependency from Arnd]
+Signed-off-by: Ley Foon Tan <lftan@altera.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
+Acked-by: Rob Herring <robh@kernel.org> (DT binding)
+---
+ .../devicetree/bindings/pci/altera-pcie.txt | 49 ++
+ MAINTAINERS | 8 +
+ drivers/pci/host/Kconfig | 9 +
+ drivers/pci/host/Makefile | 1 +
+ drivers/pci/host/pcie-altera.c | 612 +++++++++++++++++++++
+ 5 files changed, 679 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pci/altera-pcie.txt
+ create mode 100644 drivers/pci/host/pcie-altera.c
+
+diff --git a/Documentation/devicetree/bindings/pci/altera-pcie.txt b/Documentation/devicetree/bindings/pci/altera-pcie.txt
+new file mode 100644
+index 000000000000..2951a6a50704
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/altera-pcie.txt
+@@ -0,0 +1,49 @@
++* Altera PCIe controller
++
++Required properties:
++- compatible : should contain "altr,pcie-root-port-1.0"
++- reg: a list of physical base address and length for TXS and CRA.
++- reg-names: must include the following entries:
++ "Txs": TX slave port region
++ "Cra": Control register access region
++- interrupt-parent: interrupt source phandle.
++- interrupts: specifies the interrupt source of the parent interrupt controller.
++ The format of the interrupt specifier depends on the parent interrupt
++ controller.
++- device_type: must be "pci"
++- #address-cells: set to <3>
++- #size-cells: set to <2>
++- #interrupt-cells: set to <1>
++- ranges: describes the translation of addresses for root ports and standard
++ PCI regions.
++- interrupt-map-mask and interrupt-map: standard PCI properties to define the
++ mapping of the PCIe interface to interrupt numbers.
++
++Optional properties:
++- msi-parent: Link to the hardware entity that serves as the MSI controller for this PCIe
++ controller.
++- bus-range: PCI bus numbers covered
++
++Example
++ pcie_0: pcie@0xc00000000 {
++ compatible = "altr,pcie-root-port-1.0";
++ reg = <0xc0000000 0x20000000>,
++ <0xff220000 0x00004000>;
++ reg-names = "Txs", "Cra";
++ interrupt-parent = <&hps_0_arm_gic_0>;
++ interrupts = <0 40 4>;
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ bus-range = <0x0 0xFF>;
++ device_type = "pci";
++ msi-parent = <&msi_to_gic_gen_0>;
++ #address-cells = <3>;
++ #size-cells = <2>;
++ interrupt-map-mask = <0 0 0 7>;
++ interrupt-map = <0 0 0 1 &pcie_0 1>,
++ <0 0 0 2 &pcie_0 2>,
++ <0 0 0 3 &pcie_0 3>,
++ <0 0 0 4 &pcie_0 4>;
++ ranges = <0x82000000 0x00000000 0x00000000 0xc0000000 0x00000000 0x10000000
++ 0x82000000 0x00000000 0x10000000 0xd0000000 0x00000000 0x10000000>;
++ };
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 31535b79d7ab..05cfb55c0dc5 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -7498,6 +7498,14 @@ F: include/linux/pci*
+ F: arch/x86/pci/
+ F: arch/x86/kernel/quirks.c
+
++PCI DRIVER FOR ALTERA PCIE IP
++M: Ley Foon Tan <lftan@altera.com>
++L: rfi@lists.rocketboards.org (moderated for non-subscribers)
++L: linux-pci@vger.kernel.org
++S: Supported
++F: Documentation/devicetree/bindings/pci/altera-pcie.txt
++F: drivers/pci/host/pcie-altera.c
++
+ PCI DRIVER FOR ARM VERSATILE PLATFORM
+ M: Rob Herring <robh@kernel.org>
+ L: linux-pci@vger.kernel.org
+diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
+index 1dfb567b3522..cfdbb72d4003 100644
+--- a/drivers/pci/host/Kconfig
++++ b/drivers/pci/host/Kconfig
+@@ -125,4 +125,13 @@ config PCIE_IPROC_PLATFORM
+ Say Y here if you want to use the Broadcom iProc PCIe controller
+ through the generic platform bus interface
+
++config PCIE_ALTERA
++ bool "Altera PCIe controller"
++ depends on ARM || NIOS2
++ depends on OF_PCI
++ select PCI_DOMAINS
++ help
++ Say Y here if you want to enable PCIe controller support on Altera
++ FPGA.
++
+ endmenu
+diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
+index f733b4e27642..f26e5a25ef3a 100644
+--- a/drivers/pci/host/Makefile
++++ b/drivers/pci/host/Makefile
+@@ -15,3 +15,4 @@ obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
+ obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
+ obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
+ obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
++obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
+diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c
+new file mode 100644
+index 000000000000..5d018092a484
+--- /dev/null
++++ b/drivers/pci/host/pcie-altera.c
+@@ -0,0 +1,612 @@
++/*
++ * Copyright Altera Corporation (C) 2013-2015. All rights reserved
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/module.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_pci.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++
++#define RP_TX_REG0 0x2000
++#define RP_TX_REG1 0x2004
++#define RP_TX_CNTRL 0x2008
++#define RP_TX_EOP 0x2
++#define RP_TX_SOP 0x1
++#define RP_RXCPL_STATUS 0x2010
++#define RP_RXCPL_EOP 0x2
++#define RP_RXCPL_SOP 0x1
++#define RP_RXCPL_REG0 0x2014
++#define RP_RXCPL_REG1 0x2018
++#define P2A_INT_STATUS 0x3060
++#define P2A_INT_STS_ALL 0xf
++#define P2A_INT_ENABLE 0x3070
++#define P2A_INT_ENA_ALL 0xf
++#define RP_LTSSM 0x3c64
++#define LTSSM_L0 0xf
++
++/* TLP configuration type 0 and 1 */
++#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
++#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
++#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */
++#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */
++#define TLP_PAYLOAD_SIZE 0x01
++#define TLP_READ_TAG 0x1d
++#define TLP_WRITE_TAG 0x10
++#define TLP_CFG_DW0(fmttype) (((fmttype) << 24) | TLP_PAYLOAD_SIZE)
++#define TLP_CFG_DW1(reqid, tag, be) (((reqid) << 16) | (tag << 8) | (be))
++#define TLP_CFG_DW2(bus, devfn, offset) \
++ (((bus) << 24) | ((devfn) << 16) | (offset))
++#define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn))
++#define TLP_HDR_SIZE 3
++#define TLP_LOOP 500
++
++#define INTX_NUM 4
++
++#define DWORD_MASK 3
++
++struct altera_pcie {
++ struct platform_device *pdev;
++ void __iomem *cra_base;
++ int irq;
++ u8 root_bus_nr;
++ struct irq_domain *irq_domain;
++ struct resource bus_range;
++ struct list_head resources;
++ struct msi_controller *msi;
++};
++
++struct tlp_rp_regpair_t {
++ u32 ctrl;
++ u32 reg0;
++ u32 reg1;
++};
++
++#ifdef CONFIG_PCI_MSI
++struct irq_domain *arch_get_pci_msi_domain(struct pci_dev *dev)
++{
++ struct altera_pcie *pcie = dev->bus->sysdata;
++
++ return pcie->msi->domain;
++}
++#endif /* CONFIG_PCI_MSI */
++
++static void altera_pcie_retrain(struct pci_dev *dev)
++{
++ u16 linkcap, linkstat;
++
++ /*
++ * Set the retrain bit if the PCIe rootport support > 2.5GB/s, but
++ * current speed is 2.5 GB/s.
++ */
++ pcie_capability_read_word(dev, PCI_EXP_LNKCAP, &linkcap);
++
++ if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
++ return;
++
++ pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &linkstat);
++ if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB)
++ pcie_capability_set_word(dev, PCI_EXP_LNKCTL,
++ PCI_EXP_LNKCTL_RL);
++}
++DECLARE_PCI_FIXUP_EARLY(0x1172, PCI_ANY_ID, altera_pcie_retrain);
++
++/*
++ * Altera PCIe port uses BAR0 of RC's configuration space as the translation
++ * from PCI bus to native BUS. Entire DDR region is mapped into PCIe space
++ * using these registers, so it can be reached by DMA from EP devices.
++ * This BAR0 will also access to MSI vector when receiving MSI/MSIX interrupt
++ * from EP devices, eventually trigger interrupt to GIC. The BAR0 of bridge
++ * should be hidden during enumeration to avoid the sizing and resource
++ * allocation by PCIe core.
++ */
++static bool altera_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn,
++ int offset)
++{
++ if (pci_is_root_bus(bus) && (devfn == 0) &&
++ (offset == PCI_BASE_ADDRESS_0))
++ return true;
++
++ return false;
++}
++
++static inline void cra_writel(struct altera_pcie *pcie, const u32 value,
++ const u32 reg)
++{
++ writel_relaxed(value, pcie->cra_base + reg);
++}
++
++static inline u32 cra_readl(struct altera_pcie *pcie, const u32 reg)
++{
++ return readl_relaxed(pcie->cra_base + reg);
++}
++
++static void tlp_write_tx(struct altera_pcie *pcie,
++ struct tlp_rp_regpair_t *tlp_rp_regdata)
++{
++ cra_writel(pcie, tlp_rp_regdata->reg0, RP_TX_REG0);
++ cra_writel(pcie, tlp_rp_regdata->reg1, RP_TX_REG1);
++ cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL);
++}
++
++static bool altera_pcie_link_is_up(struct altera_pcie *pcie)
++{
++ return !!(cra_readl(pcie, RP_LTSSM) & LTSSM_L0);
++}
++
++static bool altera_pcie_valid_config(struct altera_pcie *pcie,
++ struct pci_bus *bus, int dev)
++{
++ /* If there is no link, then there is no device */
++ if (bus->number != pcie->root_bus_nr) {
++ if (!altera_pcie_link_is_up(pcie))
++ return false;
++ }
++
++ /* access only one slot on each root port */
++ if (bus->number == pcie->root_bus_nr && dev > 0)
++ return false;
++
++ /*
++ * Do not read more than one device on the bus directly attached
++ * to root port, root port can only attach to one downstream port.
++ */
++ if (bus->primary == pcie->root_bus_nr && dev > 0)
++ return false;
++
++ return true;
++}
++
++static int tlp_read_packet(struct altera_pcie *pcie, u32 *value)
++{
++ u8 loop;
++ bool sop = 0;
++ u32 ctrl;
++ u32 reg0, reg1;
++
++ /*
++ * Minimum 2 loops to read TLP headers and 1 loop to read data
++ * payload.
++ */
++ for (loop = 0; loop < TLP_LOOP; loop++) {
++ ctrl = cra_readl(pcie, RP_RXCPL_STATUS);
++ if ((ctrl & RP_RXCPL_SOP) || (ctrl & RP_RXCPL_EOP) || sop) {
++ reg0 = cra_readl(pcie, RP_RXCPL_REG0);
++ reg1 = cra_readl(pcie, RP_RXCPL_REG1);
++
++ if (ctrl & RP_RXCPL_SOP)
++ sop = true;
++
++ if (ctrl & RP_RXCPL_EOP) {
++ if (value)
++ *value = reg0;
++ return PCIBIOS_SUCCESSFUL;
++ }
++ }
++ udelay(5);
++ }
++
++ return -ENOENT;
++}
++
++static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers,
++ u32 data, bool align)
++{
++ struct tlp_rp_regpair_t tlp_rp_regdata;
++
++ tlp_rp_regdata.reg0 = headers[0];
++ tlp_rp_regdata.reg1 = headers[1];
++ tlp_rp_regdata.ctrl = RP_TX_SOP;
++ tlp_write_tx(pcie, &tlp_rp_regdata);
++
++ if (align) {
++ tlp_rp_regdata.reg0 = headers[2];
++ tlp_rp_regdata.reg1 = 0;
++ tlp_rp_regdata.ctrl = 0;
++ tlp_write_tx(pcie, &tlp_rp_regdata);
++
++ tlp_rp_regdata.reg0 = data;
++ tlp_rp_regdata.reg1 = 0;
++ } else {
++ tlp_rp_regdata.reg0 = headers[2];
++ tlp_rp_regdata.reg1 = data;
++ }
++
++ tlp_rp_regdata.ctrl = RP_TX_EOP;
++ tlp_write_tx(pcie, &tlp_rp_regdata);
++}
++
++static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
++ int where, u8 byte_en, u32 *value)
++{
++ u32 headers[TLP_HDR_SIZE];
++
++ if (bus == pcie->root_bus_nr)
++ headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD0);
++ else
++ headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD1);
++
++ headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, devfn),
++ TLP_READ_TAG, byte_en);
++ headers[2] = TLP_CFG_DW2(bus, devfn, where);
++
++ tlp_write_packet(pcie, headers, 0, false);
++
++ return tlp_read_packet(pcie, value);
++}
++
++static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
++ int where, u8 byte_en, u32 value)
++{
++ u32 headers[TLP_HDR_SIZE];
++ int ret;
++
++ if (bus == pcie->root_bus_nr)
++ headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR0);
++ else
++ headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR1);
++
++ headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, devfn),
++ TLP_WRITE_TAG, byte_en);
++ headers[2] = TLP_CFG_DW2(bus, devfn, where);
++
++ /* check alignment to Qword */
++ if ((where & 0x7) == 0)
++ tlp_write_packet(pcie, headers, value, true);
++ else
++ tlp_write_packet(pcie, headers, value, false);
++
++ ret = tlp_read_packet(pcie, NULL);
++ if (ret != PCIBIOS_SUCCESSFUL)
++ return ret;
++
++ /*
++ * Monitor changes to PCI_PRIMARY_BUS register on root port
++ * and update local copy of root bus number accordingly.
++ */
++ if ((bus == pcie->root_bus_nr) && (where == PCI_PRIMARY_BUS))
++ pcie->root_bus_nr = (u8)(value);
++
++ return PCIBIOS_SUCCESSFUL;
++}
++
++static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
++ int where, int size, u32 *value)
++{
++ struct altera_pcie *pcie = bus->sysdata;
++ int ret;
++ u32 data;
++ u8 byte_en;
++
++ if (altera_pcie_hide_rc_bar(bus, devfn, where))
++ return PCIBIOS_BAD_REGISTER_NUMBER;
++
++ if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) {
++ *value = 0xffffffff;
++ return PCIBIOS_DEVICE_NOT_FOUND;
++ }
++
++ switch (size) {
++ case 1:
++ byte_en = 1 << (where & 3);
++ break;
++ case 2:
++ byte_en = 3 << (where & 3);
++ break;
++ default:
++ byte_en = 0xf;
++ break;
++ }
++
++ ret = tlp_cfg_dword_read(pcie, bus->number, devfn,
++ (where & ~DWORD_MASK), byte_en, &data);
++ if (ret != PCIBIOS_SUCCESSFUL)
++ return ret;
++
++ switch (size) {
++ case 1:
++ *value = (data >> (8 * (where & 0x3))) & 0xff;
++ break;
++ case 2:
++ *value = (data >> (8 * (where & 0x2))) & 0xffff;
++ break;
++ default:
++ *value = data;
++ break;
++ }
++
++ return PCIBIOS_SUCCESSFUL;
++}
++
++static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
++ int where, int size, u32 value)
++{
++ struct altera_pcie *pcie = bus->sysdata;
++ u32 data32;
++ u32 shift = 8 * (where & 3);
++ u8 byte_en;
++
++ if (altera_pcie_hide_rc_bar(bus, devfn, where))
++ return PCIBIOS_BAD_REGISTER_NUMBER;
++
++ if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn)))
++ return PCIBIOS_DEVICE_NOT_FOUND;
++
++ switch (size) {
++ case 1:
++ data32 = (value & 0xff) << shift;
++ byte_en = 1 << (where & 3);
++ break;
++ case 2:
++ data32 = (value & 0xffff) << shift;
++ byte_en = 3 << (where & 3);
++ break;
++ default:
++ data32 = value;
++ byte_en = 0xf;
++ break;
++ }
++
++ return tlp_cfg_dword_write(pcie, bus->number, devfn,
++ (where & ~DWORD_MASK), byte_en, data32);
++}
++
++static struct pci_ops altera_pcie_ops = {
++ .read = altera_pcie_cfg_read,
++ .write = altera_pcie_cfg_write,
++};
++
++static int altera_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
++ irq_hw_number_t hwirq)
++{
++ irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
++ irq_set_chip_data(irq, domain->host_data);
++
++ return 0;
++}
++
++static const struct irq_domain_ops intx_domain_ops = {
++ .map = altera_pcie_intx_map,
++};
++
++static void altera_pcie_isr(unsigned int irq, struct irq_desc *desc)
++{
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++ struct altera_pcie *pcie;
++ unsigned long status;
++ u32 bit;
++ u32 virq;
++
++ chained_irq_enter(chip, desc);
++ pcie = irq_desc_get_handler_data(desc);
++
++ while ((status = cra_readl(pcie, P2A_INT_STATUS)
++ & P2A_INT_STS_ALL) != 0) {
++ for_each_set_bit(bit, &status, INTX_NUM) {
++ /* clear interrupts */
++ cra_writel(pcie, 1 << bit, P2A_INT_STATUS);
++
++ virq = irq_find_mapping(pcie->irq_domain, bit + 1);
++ if (virq)
++ generic_handle_irq(virq);
++ else
++ dev_err(&pcie->pdev->dev,
++ "unexpected IRQ, INT%d\n", bit);
++ }
++ }
++
++ chained_irq_exit(chip, desc);
++}
++
++static void altera_pcie_release_of_pci_ranges(struct altera_pcie *pcie)
++{
++ pci_free_resource_list(&pcie->resources);
++}
++
++static int altera_pcie_parse_request_of_pci_ranges(struct altera_pcie *pcie)
++{
++ int err, res_valid = 0;
++ struct device *dev = &pcie->pdev->dev;
++ struct device_node *np = dev->of_node;
++ struct resource_entry *win;
++
++ err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pcie->resources,
++ NULL);
++ if (err)
++ return err;
++
++ resource_list_for_each_entry(win, &pcie->resources) {
++ struct resource *parent, *res = win->res;
++
++ switch (resource_type(res)) {
++ case IORESOURCE_MEM:
++ parent = &iomem_resource;
++ res_valid |= !(res->flags & IORESOURCE_PREFETCH);
++ break;
++ default:
++ continue;
++ }
++
++ err = devm_request_resource(dev, parent, res);
++ if (err)
++ goto out_release_res;
++ }
++
++ if (!res_valid) {
++ dev_err(dev, "non-prefetchable memory resource required\n");
++ err = -EINVAL;
++ goto out_release_res;
++ }
++
++ return 0;
++
++out_release_res:
++ altera_pcie_release_of_pci_ranges(pcie);
++ return err;
++}
++
++static int altera_pcie_init_irq_domain(struct altera_pcie *pcie)
++{
++ struct device *dev = &pcie->pdev->dev;
++ struct device_node *node = dev->of_node;
++
++ /* Setup INTx */
++ pcie->irq_domain = irq_domain_add_linear(node, INTX_NUM,
++ &intx_domain_ops, pcie);
++ if (!pcie->irq_domain) {
++ dev_err(dev, "Failed to get a INTx IRQ domain\n");
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++static int altera_pcie_parse_dt(struct altera_pcie *pcie)
++{
++ struct resource *cra;
++ struct platform_device *pdev = pcie->pdev;
++
++ cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra");
++ if (!cra) {
++ dev_err(&pdev->dev, "no Cra memory resource defined\n");
++ return -ENODEV;
++ }
++
++ pcie->cra_base = devm_ioremap_resource(&pdev->dev, cra);
++ if (IS_ERR(pcie->cra_base)) {
++ dev_err(&pdev->dev, "failed to map cra memory\n");
++ return PTR_ERR(pcie->cra_base);
++ }
++
++ /* setup IRQ */
++ pcie->irq = platform_get_irq(pdev, 0);
++ if (pcie->irq <= 0) {
++ dev_err(&pdev->dev, "failed to get IRQ: %d\n", pcie->irq);
++ return -EINVAL;
++ }
++
++ irq_set_handler_data(pcie->irq, pcie);
++ irq_set_chained_handler(pcie->irq, altera_pcie_isr);
++
++ return 0;
++}
++
++static int altera_pcie_msi_enable(struct altera_pcie *pcie)
++{
++ struct device_node *msi_node;
++
++ msi_node = of_parse_phandle(pcie->pdev->dev.of_node,
++ "msi-parent", 0);
++
++ if (!msi_node)
++ return -ENODEV;
++
++ pcie->msi = of_pci_find_msi_chip_by_node(msi_node);
++
++ if (!pcie->msi)
++ return -ENODEV;
++
++ return 0;
++}
++
++static int altera_pcie_probe(struct platform_device *pdev)
++{
++ struct altera_pcie *pcie;
++ struct pci_bus *bus;
++ struct pci_bus *child;
++ int ret;
++
++ pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
++ if (!pcie)
++ return -ENOMEM;
++
++ pcie->pdev = pdev;
++
++ ret = altera_pcie_parse_dt(pcie);
++ if (ret) {
++ dev_err(&pdev->dev, "Parsing DT failed\n");
++ return ret;
++ }
++
++ INIT_LIST_HEAD(&pcie->resources);
++
++ ret = altera_pcie_parse_request_of_pci_ranges(pcie);
++ if (ret) {
++ dev_err(&pdev->dev, "Failed add resources\n");
++ return ret;
++ }
++
++ ret = altera_pcie_init_irq_domain(pcie);
++ if (ret) {
++ dev_err(&pdev->dev, "Failed creating IRQ Domain\n");
++ return ret;
++ }
++
++ /* clear all interrupts */
++ cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS);
++ /* enable all interrupts */
++ cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE);
++
++ bus = pci_scan_root_bus(&pdev->dev, pcie->root_bus_nr, &altera_pcie_ops,
++ pcie, &pcie->resources);
++ if (!bus)
++ return -ENOMEM;
++
++ if (IS_ENABLED(CONFIG_PCI_MSI))
++ if (altera_pcie_msi_enable(pcie))
++ dev_info(&pdev->dev, "failed to enable MSI\n");
++
++ pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
++ pci_assign_unassigned_bus_resources(bus);
++
++ /* Configure PCI Express setting. */
++ list_for_each_entry(child, &bus->children, node)
++ pcie_bus_configure_settings(child);
++
++ pci_bus_add_devices(bus);
++
++ platform_set_drvdata(pdev, pcie);
++ return ret;
++}
++
++static const struct of_device_id altera_pcie_of_match[] = {
++ { .compatible = "altr,pcie-root-port-1.0", },
++ {},
++};
++MODULE_DEVICE_TABLE(of, altera_pcie_of_match);
++
++static struct platform_driver altera_pcie_driver = {
++ .probe = altera_pcie_probe,
++ .driver = {
++ .name = "altera-pcie",
++ .of_match_table = altera_pcie_of_match,
++ .suppress_bind_attrs = true,
++ },
++};
++
++static int altera_pcie_init(void)
++{
++ return platform_driver_register(&altera_pcie_driver);
++}
++module_init(altera_pcie_init);
++
++MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
++MODULE_DESCRIPTION("Altera PCIe host controller driver");
++MODULE_LICENSE("GPL v2");
+--
+2.6.3
+
diff --git a/patches.altera/0003-PCI-altera-Add-Altera-PCIe-MSI-driver.patch b/patches.altera/0003-PCI-altera-Add-Altera-PCIe-MSI-driver.patch
new file mode 100644
index 00000000000000..5aa5193892080d
--- /dev/null
+++ b/patches.altera/0003-PCI-altera-Add-Altera-PCIe-MSI-driver.patch
@@ -0,0 +1,435 @@
+From 43bc1a95a4e5662552b3c096e68902daeccfe180 Mon Sep 17 00:00:00 2001
+From: Ley Foon Tan <lftan@altera.com>
+Date: Wed, 9 Dec 2015 21:07:24 +0800
+Subject: [PATCH 3/7] PCI: altera: Add Altera PCIe MSI driver
+
+Add Altera PCIe MSI driver. This soft IP supports a configurable number of
+vectors, which is a DTS parameter.
+
+[lftan: backport to 4.1-ltsi]
+[bhelgaas: Kconfig depend on PCIE_ALTERA, typos, whitespace]
+Signed-off-by: Ley Foon Tan <lftan@altera.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
+Acked-by: Rob Herring <robh@kernel.org>
+---
+ .../devicetree/bindings/pci/altera-pcie-msi.txt | 28 ++
+ MAINTAINERS | 8 +
+ drivers/pci/host/Kconfig | 8 +
+ drivers/pci/host/Makefile | 1 +
+ drivers/pci/host/pcie-altera-msi.c | 323 +++++++++++++++++++++
+ 5 files changed, 368 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
+ create mode 100644 drivers/pci/host/pcie-altera-msi.c
+
+diff --git a/Documentation/devicetree/bindings/pci/altera-pcie-msi.txt b/Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
+new file mode 100644
+index 000000000000..09cd3bc4d038
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
+@@ -0,0 +1,28 @@
++* Altera PCIe MSI controller
++
++Required properties:
++- compatible: should contain "altr,msi-1.0"
++- reg: specifies the physical base address of the controller and
++ the length of the memory mapped region.
++- reg-names: must include the following entries:
++ "csr": CSR registers
++ "vector_slave": vectors slave port region
++- interrupt-parent: interrupt source phandle.
++- interrupts: specifies the interrupt source of the parent interrupt
++ controller. The format of the interrupt specifier depends on the
++ parent interrupt controller.
++- num-vectors: number of vectors, range 1 to 32.
++- msi-controller: indicates that this is MSI controller node
++
++
++Example
++msi0: msi@0xFF200000 {
++ compatible = "altr,msi-1.0";
++ reg = <0xFF200000 0x00000010
++ 0xFF200010 0x00000080>;
++ reg-names = "csr", "vector_slave";
++ interrupt-parent = <&hps_0_arm_gic_0>;
++ interrupts = <0 42 4>;
++ msi-controller;
++ num-vectors = <32>;
++};
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 05cfb55c0dc5..bff014445443 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -7607,6 +7607,14 @@ L: linux-pci@vger.kernel.org
+ S: Maintained
+ F: drivers/pci/host/*spear*
+
++PCI MSI DRIVER FOR ALTERA MSI IP
++M: Ley Foon Tan <lftan@altera.com>
++L: rfi@lists.rocketboards.org (moderated for non-subscribers)
++L: linux-pci@vger.kernel.org
++S: Supported
++F: Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
++F: drivers/pci/host/pcie-altera-msi.c
++
+ PCMCIA SUBSYSTEM
+ P: Linux PCMCIA Team
+ L: linux-pcmcia@lists.infradead.org
+diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
+index cfdbb72d4003..f7cf0926b6e7 100644
+--- a/drivers/pci/host/Kconfig
++++ b/drivers/pci/host/Kconfig
+@@ -134,4 +134,12 @@ config PCIE_ALTERA
+ Say Y here if you want to enable PCIe controller support on Altera
+ FPGA.
+
++config PCIE_ALTERA_MSI
++ bool "Altera PCIe MSI feature"
++ depends on PCIE_ALTERA && PCI_MSI
++ select PCI_MSI_IRQ_DOMAIN
++ help
++ Say Y here if you want PCIe MSI support for the Altera FPGA.
++ This MSI driver supports Altera MSI to GIC controller IP.
++
+ endmenu
+diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
+index f26e5a25ef3a..537d3eaa0dae 100644
+--- a/drivers/pci/host/Makefile
++++ b/drivers/pci/host/Makefile
+@@ -16,3 +16,4 @@ obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
+ obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
+ obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
+ obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
++obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
+diff --git a/drivers/pci/host/pcie-altera-msi.c b/drivers/pci/host/pcie-altera-msi.c
+new file mode 100644
+index 000000000000..6df00ac1f721
+--- /dev/null
++++ b/drivers/pci/host/pcie-altera-msi.c
+@@ -0,0 +1,323 @@
++/*
++ * Copyright Altera Corporation (C) 2013-2015. All rights reserved
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <linux/interrupt.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/module.h>
++#include <linux/msi.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_pci.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++
++#define MSI_STATUS 0x0
++#define MSI_ERROR 0x4
++#define MSI_INTMASK 0x8
++
++#define MAX_MSI_VECTORS 32
++
++struct altera_msi {
++ DECLARE_BITMAP(used, MAX_MSI_VECTORS);
++ struct mutex lock; /* protect "used" bitmap */
++ struct platform_device *pdev;
++ struct irq_domain *inner_domain;
++ void __iomem *csr_base;
++ void __iomem *vector_base;
++ phys_addr_t vector_phy;
++ u32 num_of_vectors;
++ int irq;
++ struct msi_controller mchip;
++};
++
++static inline void msi_writel(struct altera_msi *msi, const u32 value,
++ const u32 reg)
++{
++ writel_relaxed(value, msi->csr_base + reg);
++}
++
++static inline u32 msi_readl(struct altera_msi *msi, const u32 reg)
++{
++ return readl_relaxed(msi->csr_base + reg);
++}
++
++static void altera_msi_isr(unsigned int irq, struct irq_desc *desc)
++{
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++ struct altera_msi *msi;
++ unsigned long status;
++ u32 num_of_vectors;
++ u32 bit;
++ u32 virq;
++
++ chained_irq_enter(chip, desc);
++ msi = irq_desc_get_handler_data(desc);
++ num_of_vectors = msi->num_of_vectors;
++
++ while ((status = msi_readl(msi, MSI_STATUS)) != 0) {
++ for_each_set_bit(bit, &status, msi->num_of_vectors) {
++ /* Dummy read from vector to clear the interrupt */
++ readl_relaxed(msi->vector_base + (bit * sizeof(u32)));
++
++ virq = irq_find_mapping(msi->inner_domain, bit);
++ if (virq)
++ generic_handle_irq(virq);
++ else
++ dev_err(&msi->pdev->dev, "unexpected MSI\n");
++ }
++ }
++
++ chained_irq_exit(chip, desc);
++}
++
++static struct irq_chip altera_msi_irq_chip = {
++ .name = "Altera PCIe MSI",
++ .irq_mask = pci_msi_mask_irq,
++ .irq_unmask = pci_msi_unmask_irq,
++};
++
++static struct msi_domain_info altera_msi_domain_info = {
++ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
++ MSI_FLAG_PCI_MSIX),
++ .chip = &altera_msi_irq_chip,
++};
++
++static void altera_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
++{
++ struct altera_msi *msi = irq_data_get_irq_chip_data(data);
++ phys_addr_t addr = msi->vector_phy + (data->hwirq * sizeof(u32));
++
++ msg->address_lo = lower_32_bits(addr);
++ msg->address_hi = upper_32_bits(addr);
++ msg->data = data->hwirq;
++
++ dev_dbg(&msi->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n",
++ (int)data->hwirq, msg->address_hi, msg->address_lo);
++}
++
++static int altera_msi_set_affinity(struct irq_data *irq_data,
++ const struct cpumask *mask, bool force)
++{
++ return -EINVAL;
++}
++
++static struct irq_chip altera_msi_bottom_irq_chip = {
++ .name = "Altera MSI",
++ .irq_compose_msi_msg = altera_compose_msi_msg,
++ .irq_set_affinity = altera_msi_set_affinity,
++};
++
++static int altera_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
++ unsigned int nr_irqs, void *args)
++{
++ struct altera_msi *msi = domain->host_data;
++ unsigned long bit;
++ u32 mask;
++
++ WARN_ON(nr_irqs != 1);
++ mutex_lock(&msi->lock);
++
++ bit = find_first_zero_bit(msi->used, msi->num_of_vectors);
++ if (bit >= msi->num_of_vectors) {
++ mutex_unlock(&msi->lock);
++ return -ENOSPC;
++ }
++
++ set_bit(bit, msi->used);
++
++ mutex_unlock(&msi->lock);
++
++ irq_domain_set_info(domain, virq, bit, &altera_msi_bottom_irq_chip,
++ domain->host_data, handle_simple_irq,
++ NULL, NULL);
++
++ mask = msi_readl(msi, MSI_INTMASK);
++ mask |= 1 << bit;
++ msi_writel(msi, mask, MSI_INTMASK);
++
++ return 0;
++}
++
++static void altera_irq_domain_free(struct irq_domain *domain,
++ unsigned int virq, unsigned int nr_irqs)
++{
++ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
++ struct altera_msi *msi = irq_data_get_irq_chip_data(d);
++ u32 mask;
++
++ mutex_lock(&msi->lock);
++
++ if (!test_bit(d->hwirq, msi->used)) {
++ dev_err(&msi->pdev->dev, "trying to free unused MSI#%lu\n",
++ d->hwirq);
++ } else {
++ __clear_bit(d->hwirq, msi->used);
++ mask = msi_readl(msi, MSI_INTMASK);
++ mask &= ~(1 << d->hwirq);
++ msi_writel(msi, mask, MSI_INTMASK);
++ }
++
++ mutex_unlock(&msi->lock);
++}
++
++static const struct irq_domain_ops msi_domain_ops = {
++ .alloc = altera_irq_domain_alloc,
++ .free = altera_irq_domain_free,
++};
++
++static int altera_allocate_domains(struct altera_msi *msi)
++{
++ int ret;
++
++ msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors,
++ &msi_domain_ops, msi);
++ if (!msi->inner_domain) {
++ dev_err(&msi->pdev->dev, "failed to create IRQ domain\n");
++ return -ENOMEM;
++ }
++
++ msi->mchip.domain = pci_msi_create_irq_domain(msi->pdev->dev.of_node,
++ &altera_msi_domain_info, msi->inner_domain);
++ if (!msi->mchip.domain) {
++ dev_err(&msi->pdev->dev, "failed to create MSI domain\n");
++ irq_domain_remove(msi->inner_domain);
++ return -ENOMEM;
++ }
++
++ msi->mchip.of_node = msi->pdev->dev.of_node;
++ ret = of_pci_msi_chip_add(&msi->mchip);
++ if (ret) {
++ dev_err(&msi->pdev->dev, "failed to add MSI controller chip\n");
++ return ret;
++ }
++
++ return 0;
++}
++
++static void altera_free_domains(struct altera_msi *msi)
++{
++ irq_domain_remove(msi->mchip.domain);
++ irq_domain_remove(msi->inner_domain);
++}
++
++static int altera_msi_remove(struct platform_device *pdev)
++{
++ struct altera_msi *msi = platform_get_drvdata(pdev);
++
++ msi_writel(msi, 0, MSI_INTMASK);
++ irq_set_chained_handler(msi->irq, NULL);
++ irq_set_handler_data(msi->irq, NULL);
++
++ altera_free_domains(msi);
++
++ platform_set_drvdata(pdev, NULL);
++ return 0;
++}
++
++static int altera_msi_probe(struct platform_device *pdev)
++{
++ struct altera_msi *msi;
++ struct device_node *np = pdev->dev.of_node;
++ struct resource *res;
++ int ret;
++
++ msi = devm_kzalloc(&pdev->dev, sizeof(struct altera_msi),
++ GFP_KERNEL);
++ if (!msi)
++ return -ENOMEM;
++
++ mutex_init(&msi->lock);
++ msi->pdev = pdev;
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
++ if (!res) {
++ dev_err(&pdev->dev, "no csr memory resource defined\n");
++ return -ENODEV;
++ }
++
++ msi->csr_base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(msi->csr_base)) {
++ dev_err(&pdev->dev, "failed to map csr memory\n");
++ return PTR_ERR(msi->csr_base);
++ }
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++ "vector_slave");
++ if (!res) {
++ dev_err(&pdev->dev, "no vector_slave memory resource defined\n");
++ return -ENODEV;
++ }
++
++ msi->vector_base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(msi->vector_base)) {
++ dev_err(&pdev->dev, "failed to map vector_slave memory\n");
++ return PTR_ERR(msi->vector_base);
++ }
++
++ msi->vector_phy = res->start;
++
++ if (of_property_read_u32(np, "num-vectors", &msi->num_of_vectors)) {
++ dev_err(&pdev->dev, "failed to parse the number of vectors\n");
++ return -EINVAL;
++ }
++
++ ret = altera_allocate_domains(msi);
++ if (ret)
++ return ret;
++
++ msi->irq = platform_get_irq(pdev, 0);
++ if (msi->irq <= 0) {
++ dev_err(&pdev->dev, "failed to map IRQ: %d\n", msi->irq);
++ ret = -ENODEV;
++ goto err;
++ }
++
++ irq_set_handler_data(msi->irq, msi);
++ irq_set_chained_handler(msi->irq, altera_msi_isr);
++
++ platform_set_drvdata(pdev, msi);
++
++ return 0;
++
++err:
++ altera_msi_remove(pdev);
++ return ret;
++}
++
++static const struct of_device_id altera_msi_of_match[] = {
++ { .compatible = "altr,msi-1.0", NULL },
++ { },
++};
++
++static struct platform_driver altera_msi_driver = {
++ .driver = {
++ .name = "altera-msi",
++ .of_match_table = altera_msi_of_match,
++ },
++ .probe = altera_msi_probe,
++ .remove = altera_msi_remove,
++};
++
++static int __init altera_msi_init(void)
++{
++ return platform_driver_register(&altera_msi_driver);
++}
++subsys_initcall(altera_msi_init);
++
++MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
++MODULE_DESCRIPTION("Altera PCIe MSI support");
++MODULE_LICENSE("GPL v2");
+--
+2.6.3
+
diff --git a/patches.altera/0004-PCI-altera-Fix-loop-in-tlp_read_packet.patch b/patches.altera/0004-PCI-altera-Fix-loop-in-tlp_read_packet.patch
new file mode 100644
index 00000000000000..5c9de4ddf4ca0c
--- /dev/null
+++ b/patches.altera/0004-PCI-altera-Fix-loop-in-tlp_read_packet.patch
@@ -0,0 +1,45 @@
+From 22b72b6683c75f8c7a349f3dac5f1d107e81f8ae Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Wed, 9 Dec 2015 21:07:25 +0800
+Subject: [PATCH 4/7] PCI: altera: Fix loop in tlp_read_packet()
+
+TLP_LOOP is 500 and the "loop" variable was a u8 so "loop < TLP_LOOP" is
+always true. We only need this condition to work if there is a problem so
+it would have been easy to miss this in testing.
+
+Make it a normal for loop with "int i" instead of over thinking things and
+making it complicated.
+
+Fixes: 6bb4dd154ae8 ("PCI: altera: Add Altera PCIe host controller driver")
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Acked-by: Ley Foon Tan <lftan@altera.com>
+---
+ drivers/pci/host/pcie-altera.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c
+index 5d018092a484..0ff6b0053e82 100644
+--- a/drivers/pci/host/pcie-altera.c
++++ b/drivers/pci/host/pcie-altera.c
+@@ -176,7 +176,7 @@ static bool altera_pcie_valid_config(struct altera_pcie *pcie,
+
+ static int tlp_read_packet(struct altera_pcie *pcie, u32 *value)
+ {
+- u8 loop;
++ int i;
+ bool sop = 0;
+ u32 ctrl;
+ u32 reg0, reg1;
+@@ -185,7 +185,7 @@ static int tlp_read_packet(struct altera_pcie *pcie, u32 *value)
+ * Minimum 2 loops to read TLP headers and 1 loop to read data
+ * payload.
+ */
+- for (loop = 0; loop < TLP_LOOP; loop++) {
++ for (i = 0; i < TLP_LOOP; i++) {
+ ctrl = cra_readl(pcie, RP_RXCPL_STATUS);
+ if ((ctrl & RP_RXCPL_SOP) || (ctrl & RP_RXCPL_EOP) || sop) {
+ reg0 = cra_readl(pcie, RP_RXCPL_REG0);
+--
+2.6.3
+
diff --git a/patches.altera/0005-PCI-altera-Fix-Requester-ID-for-config-accesses.patch b/patches.altera/0005-PCI-altera-Fix-Requester-ID-for-config-accesses.patch
new file mode 100644
index 00000000000000..eb5b73aec62f92
--- /dev/null
+++ b/patches.altera/0005-PCI-altera-Fix-Requester-ID-for-config-accesses.patch
@@ -0,0 +1,54 @@
+From 3b8086077159a6c7f0cd9a5680f64e46db30e721 Mon Sep 17 00:00:00 2001
+From: Ley Foon Tan <lftan@altera.com>
+Date: Wed, 9 Dec 2015 21:07:26 +0800
+Subject: [PATCH 5/7] PCI: altera: Fix Requester ID for config accesses
+
+The Requester ID should use the Root Port devfn and it should be always 0.
+Previously we constructed the Requester ID using the *Completer* devfn,
+i.e., the devfn of the Function we expect to respond to the config access.
+This causes issues when accessing configuration space for devices other
+than the Root Port.
+
+Build the Requester ID using the Root Port devfn.
+
+Tested on Ethernet adapter card with multi-functions.
+
+Signed-off-by: Ley Foon Tan <lftan@altera.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+---
+ drivers/pci/host/pcie-altera.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c
+index 0ff6b0053e82..f23d3d1846ba 100644
+--- a/drivers/pci/host/pcie-altera.c
++++ b/drivers/pci/host/pcie-altera.c
+@@ -57,6 +57,7 @@
+ #define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn))
+ #define TLP_HDR_SIZE 3
+ #define TLP_LOOP 500
++#define RP_DEVFN 0
+
+ #define INTX_NUM 4
+
+@@ -243,7 +244,7 @@ static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
+ else
+ headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD1);
+
+- headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, devfn),
++ headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN),
+ TLP_READ_TAG, byte_en);
+ headers[2] = TLP_CFG_DW2(bus, devfn, where);
+
+@@ -263,7 +264,7 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
+ else
+ headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR1);
+
+- headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, devfn),
++ headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN),
+ TLP_WRITE_TAG, byte_en);
+ headers[2] = TLP_CFG_DW2(bus, devfn, where);
+
+--
+2.6.3
+
diff --git a/patches.altera/0006-PCI-altera-Check-TLP-completion-status.patch b/patches.altera/0006-PCI-altera-Check-TLP-completion-status.patch
new file mode 100644
index 00000000000000..1aa865af88d28c
--- /dev/null
+++ b/patches.altera/0006-PCI-altera-Check-TLP-completion-status.patch
@@ -0,0 +1,70 @@
+From e36f748db5b84273db9aaa6b8d366a7db1711e3b Mon Sep 17 00:00:00 2001
+From: Ley Foon Tan <lftan@altera.com>
+Date: Wed, 9 Dec 2015 21:07:27 +0800
+Subject: [PATCH 6/7] PCI: altera: Check TLP completion status
+
+Check TLP packet successful completion status. This fix the issue when
+accessing multi-function devices in enumeration process, TLP will return
+error when accessing non-exist function number. Returns PCI error code
+instead of generic errno.
+
+Tested on Ethernet adapter card with multi-functions.
+
+[bhelgaas: simplify completion status checking code]
+Signed-off-by: Ley Foon Tan <lftan@altera.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+---
+ drivers/pci/host/pcie-altera.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c
+index f23d3d1846ba..929a80878bb7 100644
+--- a/drivers/pci/host/pcie-altera.c
++++ b/drivers/pci/host/pcie-altera.c
+@@ -55,6 +55,7 @@
+ #define TLP_CFG_DW2(bus, devfn, offset) \
+ (((bus) << 24) | ((devfn) << 16) | (offset))
+ #define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn))
++#define TLP_COMP_STATUS(s) (((s) >> 12) & 7)
+ #define TLP_HDR_SIZE 3
+ #define TLP_LOOP 500
+ #define RP_DEVFN 0
+@@ -181,6 +182,7 @@ static int tlp_read_packet(struct altera_pcie *pcie, u32 *value)
+ bool sop = 0;
+ u32 ctrl;
+ u32 reg0, reg1;
++ u32 comp_status = 1;
+
+ /*
+ * Minimum 2 loops to read TLP headers and 1 loop to read data
+@@ -192,19 +194,25 @@ static int tlp_read_packet(struct altera_pcie *pcie, u32 *value)
+ reg0 = cra_readl(pcie, RP_RXCPL_REG0);
+ reg1 = cra_readl(pcie, RP_RXCPL_REG1);
+
+- if (ctrl & RP_RXCPL_SOP)
++ if (ctrl & RP_RXCPL_SOP) {
+ sop = true;
++ comp_status = TLP_COMP_STATUS(reg1);
++ }
+
+ if (ctrl & RP_RXCPL_EOP) {
++ if (comp_status)
++ return PCIBIOS_DEVICE_NOT_FOUND;
++
+ if (value)
+ *value = reg0;
++
+ return PCIBIOS_SUCCESSFUL;
+ }
+ }
+ udelay(5);
+ }
+
+- return -ENOENT;
++ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers,
+--
+2.6.3
+
diff --git a/patches.altera/0007-PCI-altera-Fix-error-when-INTx-is-4.patch b/patches.altera/0007-PCI-altera-Fix-error-when-INTx-is-4.patch
new file mode 100644
index 00000000000000..31edf50434ef1b
--- /dev/null
+++ b/patches.altera/0007-PCI-altera-Fix-error-when-INTx-is-4.patch
@@ -0,0 +1,38 @@
+From 9a036240ecf3cea9f5bd742e1d2f34a9ab9a0e42 Mon Sep 17 00:00:00 2001
+From: Ley Foon Tan <lftan@altera.com>
+Date: Wed, 9 Dec 2015 21:07:28 +0800
+Subject: [PATCH 7/7] PCI: altera: Fix error when INTx is 4
+
+PCI interrupt lines start at 1, not at 0. So, creates additional one
+interrupt when register for irq domain.
+
+Error when PCIe devices have 4 INTx:
+
+ WARNING: CPU: 1 PID: 1 at kernel/irq/irqdomain.c:280
+ irq_domain_associate+0x17c/0x1cc()
+ error: hwirq 0x4 is too large for dummy
+
+Tested on Ethernet adapter card with multi-functions.
+
+Signed-off-by: Ley Foon Tan <lftan@altera.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+---
+ drivers/pci/host/pcie-altera.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c
+index 929a80878bb7..749954c3f9ec 100644
+--- a/drivers/pci/host/pcie-altera.c
++++ b/drivers/pci/host/pcie-altera.c
+@@ -477,7 +477,7 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie)
+ struct device_node *node = dev->of_node;
+
+ /* Setup INTx */
+- pcie->irq_domain = irq_domain_add_linear(node, INTX_NUM,
++ pcie->irq_domain = irq_domain_add_linear(node, INTX_NUM + 1,
+ &intx_domain_ops, pcie);
+ if (!pcie->irq_domain) {
+ dev_err(dev, "Failed to get a INTx IRQ domain\n");
+--
+2.6.3
+
diff --git a/series b/series
index 1bc5293f86fb13..981d220486d2b8 100644
--- a/series
+++ b/series
@@ -413,6 +413,13 @@ patches.altera/0006-nios2-Add-Max10-device-tree.patch
patches.altera/0007-nios2-add-Max10-defconfig.patch
patches.altera/0008-nios2-Fix-unused-variable-warning.patch
patches.altera/0009-nios2-Switch-to-generic-__xchg.patch
+patches.altera/0001-ARM-Add-msi.h-to-Kbuild.patch
+patches.altera/0002-PCI-altera-Add-Altera-PCIe-host-controller-driver.patch
+patches.altera/0003-PCI-altera-Add-Altera-PCIe-MSI-driver.patch
+patches.altera/0004-PCI-altera-Fix-loop-in-tlp_read_packet.patch
+patches.altera/0005-PCI-altera-Fix-Requester-ID-for-config-accesses.patch
+patches.altera/0006-PCI-altera-Check-TLP-completion-status.patch
+patches.altera/0007-PCI-altera-Fix-error-when-INTx-is-4.patch
#############################################################################
# Misc patches