diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-12-10 13:18:59 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-12-10 13:18:59 -0500 |
commit | 44bf32b550bde3b0016a3892ef3dea619ee79dae (patch) | |
tree | b18387b30145396bf2adefbf7707ca775503a489 | |
parent | 4e094627eb584c717a6d9eabdb5e38d4d08b845b (diff) | |
download | ltsi-kernel-44bf32b550bde3b0016a3892ef3dea619ee79dae.tar.gz |
altera pcie driver added
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 0000000000000..6ae892ea75e24 --- /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 0000000000000..a6f9036e0873d --- /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 0000000000000..5aa5193892080 --- /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 0000000000000..5c9de4ddf4ca0 --- /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 0000000000000..eb5b73aec62f9 --- /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 0000000000000..1aa865af88d28 --- /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 0000000000000..31edf50434ef1 --- /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 + @@ -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 |