Merge branch 'pci/yinghai-assign-unassigned-v6' into next
authorBjorn Helgaas <bhelgaas@google.com>
Mon, 26 Aug 2013 21:40:03 +0000 (15:40 -0600)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 26 Aug 2013 21:40:03 +0000 (15:40 -0600)
* pci/yinghai-assign-unassigned-v6:
  PCI: Assign resources for hot-added host bridge more aggressively
  PCI: Move resource reallocation code to non-__init
  PCI: Delay enabling bridges until they're needed
  PCI: Assign resources on a per-bus basis
  PCI: Enable unassigned resource reallocation on per-bus basis
  PCI: Turn on reallocation for unassigned resources with host bridge offset
  PCI: Look for unassigned resources on per-bus basis
  PCI: Drop temporary variable in pci_assign_unassigned_resources()

28 files changed:
Documentation/devicetree/bindings/pci/designware-pcie.txt
arch/arm/boot/dts/exynos5440.dtsi
arch/frv/mb93090-mb00/pci-vdk.c
arch/x86/pci/mmconfig-shared.c
arch/x86/pci/mrst.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/pci/host/Kconfig
drivers/pci/host/Makefile
drivers/pci/host/pci-exynos.c [new file with mode: 0644]
drivers/pci/host/pci-mvebu.c
drivers/pci/host/pcie-designware.c
drivers/pci/host/pcie-designware.h [new file with mode: 0644]
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_core.c
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/iov.c
drivers/pci/pci-sysfs.c
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/pcie/Kconfig
drivers/pci/pcie/aer/aerdrv.c
drivers/pci/pcie/aer/aerdrv.h
drivers/pci/pcie/aer/aerdrv_core.c
drivers/pci/probe.c
drivers/pci/quirks.c
drivers/pci/setup-bus.c
include/linux/pci.h
include/linux/pci_hotplug.h

index e2371f5..eabcb4b 100644 (file)
@@ -18,6 +18,7 @@ Required properties:
 - interrupt-map-mask and interrupt-map: standard PCI properties
        to define the mapping of the PCIe interface to interrupt
        numbers.
+- num-lanes: number of lanes to use
 - reset-gpio: gpio pin number of power good signal
 
 Example:
@@ -41,6 +42,7 @@ SoC specific DT Entry:
                #interrupt-cells = <1>;
                interrupt-map-mask = <0 0 0 0>;
                interrupt-map = <0x0 0 &gic 53>;
+               num-lanes = <4>;
        };
 
        pcie@2a0000 {
@@ -60,6 +62,7 @@ SoC specific DT Entry:
                #interrupt-cells = <1>;
                interrupt-map-mask = <0 0 0 0>;
                interrupt-map = <0x0 0 &gic 56>;
+               num-lanes = <4>;
        };
 
 Board specific DT Entry:
index ff7f5d8..586134e 100644 (file)
                #interrupt-cells = <1>;
                interrupt-map-mask = <0 0 0 0>;
                interrupt-map = <0x0 0 &gic 53>;
+               num-lanes = <4>;
        };
 
        pcie@2a0000 {
                #interrupt-cells = <1>;
                interrupt-map-mask = <0 0 0 0>;
                interrupt-map = <0x0 0 &gic 56>;
+               num-lanes = <4>;
        };
 };
index 0aa35f0..deb6784 100644 (file)
@@ -320,7 +320,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases);
  *  are examined.
  */
 
-void __init pcibios_fixup_bus(struct pci_bus *bus)
+void pcibios_fixup_bus(struct pci_bus *bus)
 {
 #if 0
        printk("### PCIBIOS_FIXUP_BUS(%d)\n",bus->number);
index 082e881..5596c7b 100644 (file)
@@ -700,7 +700,7 @@ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
        if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
                return -ENODEV;
 
-       if (start > end)
+       if (start > end || !addr)
                return -EINVAL;
 
        mutex_lock(&pci_mmcfg_lock);
@@ -716,11 +716,6 @@ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
                return -EEXIST;
        }
 
-       if (!addr) {
-               mutex_unlock(&pci_mmcfg_lock);
-               return -EINVAL;
-       }
-
        rc = -EBUSY;
        cfg = pci_mmconfig_alloc(seg, start, end, addr);
        if (cfg == NULL) {
index 6eb18c4..903fded 100644 (file)
 #include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/smp.h>
 
-#include <asm/acpi.h>
 #include <asm/segment.h>
-#include <asm/io.h>
-#include <asm/smp.h>
 #include <asm/pci_x86.h>
 #include <asm/hw_irq.h>
 #include <asm/io_apic.h>
@@ -43,7 +43,7 @@
 #define PCI_FIXED_BAR_4_SIZE   0x14
 #define PCI_FIXED_BAR_5_SIZE   0x1c
 
-static int pci_soc_mode = 0;
+static int pci_soc_mode;
 
 /**
  * fixed_bar_cap - return the offset of the fixed BAR cap if found
@@ -141,7 +141,8 @@ static int pci_device_update_fixed(struct pci_bus *bus, unsigned int devfn,
  */
 static bool type1_access_ok(unsigned int bus, unsigned int devfn, int reg)
 {
-       /* This is a workaround for A0 LNC bug where PCI status register does
+       /*
+        * This is a workaround for A0 LNC bug where PCI status register does
         * not have new CAP bit set. can not be written by SW either.
         *
         * PCI header type in real LNC indicates a single function device, this
@@ -154,7 +155,7 @@ static bool type1_access_ok(unsigned int bus, unsigned int devfn, int reg)
                                || devfn == PCI_DEVFN(0, 0)
                                || devfn == PCI_DEVFN(3, 0)))
                return 1;
-       return 0; /* langwell on others */
+       return 0; /* Langwell on others */
 }
 
 static int pci_read(struct pci_bus *bus, unsigned int devfn, int where,
@@ -172,7 +173,8 @@ static int pci_write(struct pci_bus *bus, unsigned int devfn, int where,
 {
        int offset;
 
-       /* On MRST, there is no PCI ROM BAR, this will cause a subsequent read
+       /*
+        * On MRST, there is no PCI ROM BAR, this will cause a subsequent read
         * to ROM BAR return 0 then being ignored.
         */
        if (where == PCI_ROM_ADDRESS)
@@ -210,7 +212,8 @@ static int mrst_pci_irq_enable(struct pci_dev *dev)
 
        pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
 
-       /* MRST only have IOAPIC, the PCI irq lines are 1:1 mapped to
+       /*
+        * MRST only have IOAPIC, the PCI irq lines are 1:1 mapped to
         * IOAPIC RTE entries, so we just enable RTE for the device.
         */
        irq_attr.ioapic = mp_find_ioapic(dev->irq);
@@ -235,7 +238,7 @@ struct pci_ops pci_mrst_ops = {
  */
 int __init pci_mrst_init(void)
 {
-       printk(KERN_INFO "Intel MID platform detected, using MID PCI ops\n");
+       pr_info("Intel MID platform detected, using MID PCI ops\n");
        pci_mmcfg_late_init();
        pcibios_enable_irq = mrst_pci_irq_enable;
        pci_root_ops = pci_mrst_ops;
@@ -244,17 +247,21 @@ int __init pci_mrst_init(void)
        return 1;
 }
 
-/* Langwell devices are not true pci devices, they are not subject to 10 ms
- * d3 to d0 delay required by pci spec.
+/*
+ * Langwell devices are not true PCI devices; they are not subject to 10 ms
+ * d3 to d0 delay required by PCI spec.
  */
 static void pci_d3delay_fixup(struct pci_dev *dev)
 {
-       /* PCI fixups are effectively decided compile time. If we have a dual
-          SoC/non-SoC kernel we don't want to mangle d3 on non SoC devices */
-        if (!pci_soc_mode)
-            return;
-       /* true pci devices in lincroft should allow type 1 access, the rest
-        * are langwell fake pci devices.
+       /*
+        * PCI fixups are effectively decided compile time. If we have a dual
+        * SoC/non-SoC kernel we don't want to mangle d3 on non-SoC devices.
+        */
+       if (!pci_soc_mode)
+               return;
+       /*
+        * True PCI devices in Lincroft should allow type 1 access, the rest
+        * are Langwell fake PCI devices.
         */
        if (type1_access_ok(dev->bus->number, dev->devfn, PCI_DEVICE_ID))
                return;
index e5da078..c51d2f8 100644 (file)
@@ -9935,8 +9935,6 @@ static int bnx2x_prev_mark_path(struct bnx2x *bp, bool after_undi)
 
 static int bnx2x_do_flr(struct bnx2x *bp)
 {
-       int i;
-       u16 status;
        struct pci_dev *dev = bp->pdev;
 
        if (CHIP_IS_E1x(bp)) {
@@ -9951,20 +9949,8 @@ static int bnx2x_do_flr(struct bnx2x *bp)
                return -EINVAL;
        }
 
-       /* Wait for Transaction Pending bit clean */
-       for (i = 0; i < 4; i++) {
-               if (i)
-                       msleep((1 << (i - 1)) * 100);
-
-               pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
-               if (!(status & PCI_EXP_DEVSTA_TRPND))
-                       goto clear;
-       }
-
-       dev_err(&dev->dev,
-               "transaction is not cleared; proceeding with reset anyway\n");
-
-clear:
+       if (!pci_wait_for_pending_transaction(dev))
+               dev_err(&dev->dev, "transaction is not cleared; proceeding with reset anyway\n");
 
        BNX2X_DEV_INFO("Initiating FLR\n");
        bnx2x_fw_command(bp, DRV_MSG_CODE_INITIATE_FLR, 0);
index 1184ff6..e5ba4eb 100644 (file)
@@ -4,6 +4,7 @@ menu "PCI host controller drivers"
 config PCI_MVEBU
        bool "Marvell EBU PCIe controller"
        depends on ARCH_MVEBU || ARCH_KIRKWOOD
+       depends on OF
 
 config PCIE_DW
        bool
index 086d850..ab79ccb 100644 (file)
@@ -1,2 +1,3 @@
-obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
 obj-$(CONFIG_PCIE_DW) += pcie-designware.o
+obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
+obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
new file mode 100644 (file)
index 0000000..012ca8a
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ * PCIe host controller driver for Samsung EXYNOS SoCs
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+#define to_exynos_pcie(x)      container_of(x, struct exynos_pcie, pp)
+
+struct exynos_pcie {
+       void __iomem            *elbi_base;
+       void __iomem            *phy_base;
+       void __iomem            *block_base;
+       int                     reset_gpio;
+       struct clk              *clk;
+       struct clk              *bus_clk;
+       struct pcie_port        pp;
+};
+
+/* PCIe ELBI registers */
+#define PCIE_IRQ_PULSE                 0x000
+#define IRQ_INTA_ASSERT                        (0x1 << 0)
+#define IRQ_INTB_ASSERT                        (0x1 << 2)
+#define IRQ_INTC_ASSERT                        (0x1 << 4)
+#define IRQ_INTD_ASSERT                        (0x1 << 6)
+#define PCIE_IRQ_LEVEL                 0x004
+#define PCIE_IRQ_SPECIAL               0x008
+#define PCIE_IRQ_EN_PULSE              0x00c
+#define PCIE_IRQ_EN_LEVEL              0x010
+#define PCIE_IRQ_EN_SPECIAL            0x014
+#define PCIE_PWR_RESET                 0x018
+#define PCIE_CORE_RESET                        0x01c
+#define PCIE_CORE_RESET_ENABLE         (0x1 << 0)
+#define PCIE_STICKY_RESET              0x020
+#define PCIE_NONSTICKY_RESET           0x024
+#define PCIE_APP_INIT_RESET            0x028
+#define PCIE_APP_LTSSM_ENABLE          0x02c
+#define PCIE_ELBI_RDLH_LINKUP          0x064
+#define PCIE_ELBI_LTSSM_ENABLE         0x1
+#define PCIE_ELBI_SLV_AWMISC           0x11c
+#define PCIE_ELBI_SLV_ARMISC           0x120
+#define PCIE_ELBI_SLV_DBI_ENABLE       (0x1 << 21)
+
+/* PCIe Purple registers */
+#define PCIE_PHY_GLOBAL_RESET          0x000
+#define PCIE_PHY_COMMON_RESET          0x004
+#define PCIE_PHY_CMN_REG               0x008
+#define PCIE_PHY_MAC_RESET             0x00c
+#define PCIE_PHY_PLL_LOCKED            0x010
+#define PCIE_PHY_TRSVREG_RESET         0x020
+#define PCIE_PHY_TRSV_RESET            0x024
+
+/* PCIe PHY registers */
+#define PCIE_PHY_IMPEDANCE             0x004
+#define PCIE_PHY_PLL_DIV_0             0x008
+#define PCIE_PHY_PLL_BIAS              0x00c
+#define PCIE_PHY_DCC_FEEDBACK          0x014
+#define PCIE_PHY_PLL_DIV_1             0x05c
+#define PCIE_PHY_TRSV0_EMP_LVL         0x084
+#define PCIE_PHY_TRSV0_DRV_LVL         0x088
+#define PCIE_PHY_TRSV0_RXCDR           0x0ac
+#define PCIE_PHY_TRSV0_LVCC            0x0dc
+#define PCIE_PHY_TRSV1_EMP_LVL         0x144
+#define PCIE_PHY_TRSV1_RXCDR           0x16c
+#define PCIE_PHY_TRSV1_LVCC            0x19c
+#define PCIE_PHY_TRSV2_EMP_LVL         0x204
+#define PCIE_PHY_TRSV2_RXCDR           0x22c
+#define PCIE_PHY_TRSV2_LVCC            0x25c
+#define PCIE_PHY_TRSV3_EMP_LVL         0x2c4
+#define PCIE_PHY_TRSV3_RXCDR           0x2ec
+#define PCIE_PHY_TRSV3_LVCC            0x31c
+
+static void exynos_pcie_sideband_dbi_w_mode(struct pcie_port *pp, bool on)
+{
+       u32 val;
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+       if (on) {
+               val = readl(exynos_pcie->elbi_base + PCIE_ELBI_SLV_AWMISC);
+               val |= PCIE_ELBI_SLV_DBI_ENABLE;
+               writel(val, exynos_pcie->elbi_base + PCIE_ELBI_SLV_AWMISC);
+       } else {
+               val = readl(exynos_pcie->elbi_base + PCIE_ELBI_SLV_AWMISC);
+               val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
+               writel(val, exynos_pcie->elbi_base + PCIE_ELBI_SLV_AWMISC);
+       }
+}
+
+static void exynos_pcie_sideband_dbi_r_mode(struct pcie_port *pp, bool on)
+{
+       u32 val;
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+       if (on) {
+               val = readl(exynos_pcie->elbi_base + PCIE_ELBI_SLV_ARMISC);
+               val |= PCIE_ELBI_SLV_DBI_ENABLE;
+               writel(val, exynos_pcie->elbi_base + PCIE_ELBI_SLV_ARMISC);
+       } else {
+               val = readl(exynos_pcie->elbi_base + PCIE_ELBI_SLV_ARMISC);
+               val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
+               writel(val, exynos_pcie->elbi_base + PCIE_ELBI_SLV_ARMISC);
+       }
+}
+
+static void exynos_pcie_assert_core_reset(struct pcie_port *pp)
+{
+       u32 val;
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       void __iomem *elbi_base = exynos_pcie->elbi_base;
+
+       val = readl(elbi_base + PCIE_CORE_RESET);
+       val &= ~PCIE_CORE_RESET_ENABLE;
+       writel(val, elbi_base + PCIE_CORE_RESET);
+       writel(0, elbi_base + PCIE_PWR_RESET);
+       writel(0, elbi_base + PCIE_STICKY_RESET);
+       writel(0, elbi_base + PCIE_NONSTICKY_RESET);
+}
+
+static void exynos_pcie_deassert_core_reset(struct pcie_port *pp)
+{
+       u32 val;
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       void __iomem *elbi_base = exynos_pcie->elbi_base;
+       void __iomem *block_base = exynos_pcie->block_base;
+
+       val = readl(elbi_base + PCIE_CORE_RESET);
+       val |= PCIE_CORE_RESET_ENABLE;
+       writel(val, elbi_base + PCIE_CORE_RESET);
+       writel(1, elbi_base + PCIE_STICKY_RESET);
+       writel(1, elbi_base + PCIE_NONSTICKY_RESET);
+       writel(1, elbi_base + PCIE_APP_INIT_RESET);
+       writel(0, elbi_base + PCIE_APP_INIT_RESET);
+       writel(1, block_base + PCIE_PHY_MAC_RESET);
+}
+
+static void exynos_pcie_assert_phy_reset(struct pcie_port *pp)
+{
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       void __iomem *block_base = exynos_pcie->block_base;
+
+       writel(0, block_base + PCIE_PHY_MAC_RESET);
+       writel(1, block_base + PCIE_PHY_GLOBAL_RESET);
+}
+
+static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp)
+{
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       void __iomem *elbi_base = exynos_pcie->elbi_base;
+       void __iomem *block_base = exynos_pcie->block_base;
+
+       writel(0, block_base + PCIE_PHY_GLOBAL_RESET);
+       writel(1, elbi_base + PCIE_PWR_RESET);
+       writel(0, block_base + PCIE_PHY_COMMON_RESET);
+       writel(0, block_base + PCIE_PHY_CMN_REG);
+       writel(0, block_base + PCIE_PHY_TRSVREG_RESET);
+       writel(0, block_base + PCIE_PHY_TRSV_RESET);
+}
+
+static void exynos_pcie_init_phy(struct pcie_port *pp)
+{
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       void __iomem *phy_base = exynos_pcie->phy_base;
+
+       /* DCC feedback control off */
+       writel(0x29, phy_base + PCIE_PHY_DCC_FEEDBACK);
+
+       /* set TX/RX impedance */
+       writel(0xd5, phy_base + PCIE_PHY_IMPEDANCE);
+
+       /* set 50Mhz PHY clock */
+       writel(0x14, phy_base + PCIE_PHY_PLL_DIV_0);
+       writel(0x12, phy_base + PCIE_PHY_PLL_DIV_1);
+
+       /* set TX Differential output for lane 0 */
+       writel(0x7f, phy_base + PCIE_PHY_TRSV0_DRV_LVL);
+
+       /* set TX Pre-emphasis Level Control for lane 0 to minimum */
+       writel(0x0, phy_base + PCIE_PHY_TRSV0_EMP_LVL);
+
+       /* set RX clock and data recovery bandwidth */
+       writel(0xe7, phy_base + PCIE_PHY_PLL_BIAS);
+       writel(0x82, phy_base + PCIE_PHY_TRSV0_RXCDR);
+       writel(0x82, phy_base + PCIE_PHY_TRSV1_RXCDR);
+       writel(0x82, phy_base + PCIE_PHY_TRSV2_RXCDR);
+       writel(0x82, phy_base + PCIE_PHY_TRSV3_RXCDR);
+
+       /* change TX Pre-emphasis Level Control for lanes */
+       writel(0x39, phy_base + PCIE_PHY_TRSV0_EMP_LVL);
+       writel(0x39, phy_base + PCIE_PHY_TRSV1_EMP_LVL);
+       writel(0x39, phy_base + PCIE_PHY_TRSV2_EMP_LVL);
+       writel(0x39, phy_base + PCIE_PHY_TRSV3_EMP_LVL);
+
+       /* set LVCC */
+       writel(0x20, phy_base + PCIE_PHY_TRSV0_LVCC);
+       writel(0xa0, phy_base + PCIE_PHY_TRSV1_LVCC);
+       writel(0xa0, phy_base + PCIE_PHY_TRSV2_LVCC);
+       writel(0xa0, phy_base + PCIE_PHY_TRSV3_LVCC);
+}
+
+static void exynos_pcie_assert_reset(struct pcie_port *pp)
+{
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+       if (exynos_pcie->reset_gpio >= 0)
+               devm_gpio_request_one(pp->dev, exynos_pcie->reset_gpio,
+                               GPIOF_OUT_INIT_HIGH, "RESET");
+       return;
+}
+
+static int exynos_pcie_establish_link(struct pcie_port *pp)
+{
+       u32 val;
+       int count = 0;
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       void __iomem *elbi_base = exynos_pcie->elbi_base;
+       void __iomem *block_base = exynos_pcie->block_base;
+       void __iomem *phy_base = exynos_pcie->phy_base;
+
+       if (dw_pcie_link_up(pp)) {
+               dev_err(pp->dev, "Link already up\n");
+               return 0;
+       }
+
+       /* assert reset signals */
+       exynos_pcie_assert_core_reset(pp);
+       exynos_pcie_assert_phy_reset(pp);
+
+       /* de-assert phy reset */
+       exynos_pcie_deassert_phy_reset(pp);
+
+       /* initialize phy */
+       exynos_pcie_init_phy(pp);
+
+       /* pulse for common reset */
+       writel(1, block_base + PCIE_PHY_COMMON_RESET);
+       udelay(500);
+       writel(0, block_base + PCIE_PHY_COMMON_RESET);
+
+       /* de-assert core reset */
+       exynos_pcie_deassert_core_reset(pp);
+
+       /* setup root complex */
+       dw_pcie_setup_rc(pp);
+
+       /* assert reset signal */
+       exynos_pcie_assert_reset(pp);
+
+       /* assert LTSSM enable */
+       writel(PCIE_ELBI_LTSSM_ENABLE, elbi_base + PCIE_APP_LTSSM_ENABLE);
+
+       /* check if the link is up or not */
+       while (!dw_pcie_link_up(pp)) {
+               mdelay(100);
+               count++;
+               if (count == 10) {
+                       while (readl(phy_base + PCIE_PHY_PLL_LOCKED) == 0) {
+                               val = readl(block_base + PCIE_PHY_PLL_LOCKED);
+                               dev_info(pp->dev, "PLL Locked: 0x%x\n", val);
+                       }
+                       dev_err(pp->dev, "PCIe Link Fail\n");
+                       return -EINVAL;
+               }
+       }
+
+       dev_info(pp->dev, "Link up\n");
+
+       return 0;
+}
+
+static void exynos_pcie_clear_irq_pulse(struct pcie_port *pp)
+{
+       u32 val;
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       void __iomem *elbi_base = exynos_pcie->elbi_base;
+
+       val = readl(elbi_base + PCIE_IRQ_PULSE);
+       writel(val, elbi_base + PCIE_IRQ_PULSE);
+       return;
+}
+
+static void exynos_pcie_enable_irq_pulse(struct pcie_port *pp)
+{
+       u32 val;
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       void __iomem *elbi_base = exynos_pcie->elbi_base;
+
+       /* enable INTX interrupt */
+       val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
+               IRQ_INTC_ASSERT | IRQ_INTD_ASSERT,
+       writel(val, elbi_base + PCIE_IRQ_EN_PULSE);
+       return;
+}
+
+static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
+{
+       struct pcie_port *pp = arg;
+
+       exynos_pcie_clear_irq_pulse(pp);
+       return IRQ_HANDLED;
+}
+
+static void exynos_pcie_enable_interrupts(struct pcie_port *pp)
+{
+       exynos_pcie_enable_irq_pulse(pp);
+       return;
+}
+
+static inline void exynos_pcie_readl_rc(struct pcie_port *pp,
+                                       void __iomem *dbi_base, u32 *val)
+{
+       exynos_pcie_sideband_dbi_r_mode(pp, true);
+       *val = readl(dbi_base);
+       exynos_pcie_sideband_dbi_r_mode(pp, false);
+       return;
+}
+
+static inline void exynos_pcie_writel_rc(struct pcie_port *pp,
+                                       u32 val, void __iomem *dbi_base)
+{
+       exynos_pcie_sideband_dbi_w_mode(pp, true);
+       writel(val, dbi_base);
+       exynos_pcie_sideband_dbi_w_mode(pp, false);
+       return;
+}
+
+static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
+                               u32 *val)
+{
+       int ret;
+
+       exynos_pcie_sideband_dbi_r_mode(pp, true);
+       ret = cfg_read(pp->dbi_base + (where & ~0x3), where, size, val);
+       exynos_pcie_sideband_dbi_r_mode(pp, false);
+       return ret;
+}
+
+static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
+                               u32 val)
+{
+       int ret;
+
+       exynos_pcie_sideband_dbi_w_mode(pp, true);
+       ret = cfg_write(pp->dbi_base + (where & ~0x3), where, size, val);
+       exynos_pcie_sideband_dbi_w_mode(pp, false);
+       return ret;
+}
+
+static int exynos_pcie_link_up(struct pcie_port *pp)
+{
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       u32 val = readl(exynos_pcie->elbi_base + PCIE_ELBI_RDLH_LINKUP);
+
+       if (val == PCIE_ELBI_LTSSM_ENABLE)
+               return 1;
+
+       return 0;
+}
+
+static void exynos_pcie_host_init(struct pcie_port *pp)
+{
+       exynos_pcie_establish_link(pp);
+       exynos_pcie_enable_interrupts(pp);
+}
+
+static struct pcie_host_ops exynos_pcie_host_ops = {
+       .readl_rc = exynos_pcie_readl_rc,
+       .writel_rc = exynos_pcie_writel_rc,
+       .rd_own_conf = exynos_pcie_rd_own_conf,
+       .wr_own_conf = exynos_pcie_wr_own_conf,
+       .link_up = exynos_pcie_link_up,
+       .host_init = exynos_pcie_host_init,
+};
+
+static int add_pcie_port(struct pcie_port *pp, struct platform_device *pdev)
+{
+       int ret;
+
+       pp->irq = platform_get_irq(pdev, 1);
+       if (!pp->irq) {
+               dev_err(&pdev->dev, "failed to get irq\n");
+               return -ENODEV;
+       }
+       ret = devm_request_irq(&pdev->dev, pp->irq, exynos_pcie_irq_handler,
+                               IRQF_SHARED, "exynos-pcie", pp);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to request irq\n");
+               return ret;
+       }
+
+       pp->root_bus_nr = -1;
+       pp->ops = &exynos_pcie_host_ops;
+
+       spin_lock_init(&pp->conf_lock);
+       ret = dw_pcie_host_init(pp);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to initialize host\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int __init exynos_pcie_probe(struct platform_device *pdev)
+{
+       struct exynos_pcie *exynos_pcie;
+       struct pcie_port *pp;
+       struct device_node *np = pdev->dev.of_node;
+       struct resource *elbi_base;
+       struct resource *phy_base;
+       struct resource *block_base;
+       int ret;
+
+       exynos_pcie = devm_kzalloc(&pdev->dev, sizeof(*exynos_pcie),
+                               GFP_KERNEL);
+       if (!exynos_pcie) {
+               dev_err(&pdev->dev, "no memory for exynos pcie\n");
+               return -ENOMEM;
+       }
+
+       pp = &exynos_pcie->pp;
+
+       pp->dev = &pdev->dev;
+
+       exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
+
+       exynos_pcie->clk = devm_clk_get(&pdev->dev, "pcie");
+       if (IS_ERR(exynos_pcie->clk)) {
+               dev_err(&pdev->dev, "Failed to get pcie rc clock\n");
+               return PTR_ERR(exynos_pcie->clk);
+       }
+       ret = clk_prepare_enable(exynos_pcie->clk);
+       if (ret)
+               return ret;
+
+       exynos_pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus");
+       if (IS_ERR(exynos_pcie->bus_clk)) {
+               dev_err(&pdev->dev, "Failed to get pcie bus clock\n");
+               ret = PTR_ERR(exynos_pcie->bus_clk);
+               goto fail_clk;
+       }
+       ret = clk_prepare_enable(exynos_pcie->bus_clk);
+       if (ret)
+               goto fail_clk;
+
+       elbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       exynos_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base);
+       if (IS_ERR(exynos_pcie->elbi_base))
+               return PTR_ERR(exynos_pcie->elbi_base);
+
+       phy_base = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       exynos_pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy_base);
+       if (IS_ERR(exynos_pcie->phy_base))
+               return PTR_ERR(exynos_pcie->phy_base);
+
+       block_base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+       exynos_pcie->block_base = devm_ioremap_resource(&pdev->dev, block_base);
+       if (IS_ERR(exynos_pcie->block_base))
+               return PTR_ERR(exynos_pcie->block_base);
+
+       ret = add_pcie_port(pp, pdev);
+       if (ret < 0)
+               goto fail_bus_clk;
+
+       platform_set_drvdata(pdev, exynos_pcie);
+       return 0;
+
+fail_bus_clk:
+       clk_disable_unprepare(exynos_pcie->bus_clk);
+fail_clk:
+       clk_disable_unprepare(exynos_pcie->clk);
+       return ret;
+}
+
+static int __exit exynos_pcie_remove(struct platform_device *pdev)
+{
+       struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev);
+
+       clk_disable_unprepare(exynos_pcie->bus_clk);
+       clk_disable_unprepare(exynos_pcie->clk);
+
+       return 0;
+}
+
+static const struct of_device_id exynos_pcie_of_match[] = {
+       { .compatible = "samsung,exynos5440-pcie", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos_pcie_of_match);
+
+static struct platform_driver exynos_pcie_driver = {
+       .remove         = __exit_p(exynos_pcie_remove),
+       .driver = {
+               .name   = "exynos-pcie",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(exynos_pcie_of_match),
+       },
+};
+
+/* Exynos PCIe driver does not allow module unload */
+
+static int __init pcie_init(void)
+{
+       return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe);
+}
+subsys_initcall(pcie_init);
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("Samsung PCIe host controller driver");
+MODULE_LICENSE("GPL v2");
index 13a633b..8fc2a82 100644 (file)
@@ -750,9 +750,9 @@ mvebu_pcie_map_registers(struct platform_device *pdev,
 
        ret = of_address_to_resource(np, 0, &regs);
        if (ret)
-               return NULL;
+               return ERR_PTR(ret);
 
-       return devm_request_and_ioremap(&pdev->dev, &regs);
+       return devm_ioremap_resource(&pdev->dev, &regs);
 }
 
 static int __init mvebu_pcie_probe(struct platform_device *pdev)
@@ -842,9 +842,10 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
                        continue;
 
                port->base = mvebu_pcie_map_registers(pdev, child, port);
-               if (!port->base) {
+               if (IS_ERR(port->base)) {
                        dev_err(&pdev->dev, "PCIe%d.%d: cannot map registers\n",
                                port->port, port->lane);
+                       port->base = NULL;
                        continue;
                }
 
index 26bdbda..77b0c25 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * PCIe host controller driver for Samsung EXYNOS SoCs
+ * Synopsys Designware PCIe host controller driver
  *
  * Copyright (C) 2013 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com
  * published by the Free Software Foundation.
  */
 
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/interrupt.h>
 #include <linux/kernel.h>
-#include <linux/list.h>
 #include <linux/module.h>
-#include <linux/of.h>
 #include <linux/of_address.h>
-#include <linux/of_gpio.h>
-#include <linux/of_pci.h>
 #include <linux/pci.h>
 #include <linux/pci_regs.h>
-#include <linux/platform_device.h>
-#include <linux/resource.h>
-#include <linux/signal.h>
-#include <linux/slab.h>
 #include <linux/types.h>
 
-struct pcie_port_info {
-       u32             cfg0_size;
-       u32             cfg1_size;
-       u32             io_size;
-       u32             mem_size;
-       phys_addr_t     io_bus_addr;
-       phys_addr_t     mem_bus_addr;
-};
-
-struct pcie_port {
-       struct device           *dev;
-       u8                      controller;
-       u8                      root_bus_nr;
-       void __iomem            *dbi_base;
-       void __iomem            *elbi_base;
-       void __iomem            *phy_base;
-       void __iomem            *purple_base;
-       u64                     cfg0_base;
-       void __iomem            *va_cfg0_base;
-       u64                     cfg1_base;
-       void __iomem            *va_cfg1_base;
-       u64                     io_base;
-       u64                     mem_base;
-       spinlock_t              conf_lock;
-       struct resource         cfg;
-       struct resource         io;
-       struct resource         mem;
-       struct pcie_port_info   config;
-       struct clk              *clk;
-       struct clk              *bus_clk;
-       int                     irq;
-       int                     reset_gpio;
-};
-
-/*
- * Exynos PCIe IP consists of Synopsys specific part and Exynos
- * specific part. Only core block is a Synopsys designware part;
- * other parts are Exynos specific.
- */
+#include "pcie-designware.h"
 
 /* Synopsis specific PCIE configuration registers */
 #define PCIE_PORT_LINK_CONTROL         0x710
 #define PORT_LINK_MODE_MASK            (0x3f << 16)
+#define PORT_LINK_MODE_1_LANES         (0x1 << 16)
+#define PORT_LINK_MODE_2_LANES         (0x3 << 16)
 #define PORT_LINK_MODE_4_LANES         (0x7 << 16)
 
 #define PCIE_LINK_WIDTH_SPEED_CONTROL  0x80C
 #define PORT_LOGIC_SPEED_CHANGE                (0x1 << 17)
 #define PORT_LOGIC_LINK_WIDTH_MASK     (0x1ff << 8)
-#define PORT_LOGIC_LINK_WIDTH_4_LANES  (0x7 << 8)
+#define PORT_LOGIC_LINK_WIDTH_1_LANES  (0x1 << 8)
+#define PORT_LOGIC_LINK_WIDTH_2_LANES  (0x2 << 8)
+#define PORT_LOGIC_LINK_WIDTH_4_LANES  (0x4 << 8)
 
 #define PCIE_MSI_ADDR_LO               0x820
 #define PCIE_MSI_ADDR_HI               0x824
@@ -108,69 +62,16 @@ struct pcie_port {
 #define PCIE_ATU_FUNC(x)               (((x) & 0x7) << 16)
 #define PCIE_ATU_UPPER_TARGET          0x91C
 
-/* Exynos specific PCIE configuration registers */
-
-/* PCIe ELBI registers */
-#define PCIE_IRQ_PULSE                 0x000
-#define IRQ_INTA_ASSERT                        (0x1 << 0)
-#define IRQ_INTB_ASSERT                        (0x1 << 2)
-#define IRQ_INTC_ASSERT                        (0x1 << 4)
-#define IRQ_INTD_ASSERT                        (0x1 << 6)
-#define PCIE_IRQ_LEVEL                 0x004
-#define PCIE_IRQ_SPECIAL               0x008
-#define PCIE_IRQ_EN_PULSE              0x00c
-#define PCIE_IRQ_EN_LEVEL              0x010
-#define PCIE_IRQ_EN_SPECIAL            0x014
-#define PCIE_PWR_RESET                 0x018
-#define PCIE_CORE_RESET                        0x01c
-#define PCIE_CORE_RESET_ENABLE         (0x1 << 0)
-#define PCIE_STICKY_RESET              0x020
-#define PCIE_NONSTICKY_RESET           0x024
-#define PCIE_APP_INIT_RESET            0x028
-#define PCIE_APP_LTSSM_ENABLE          0x02c
-#define PCIE_ELBI_RDLH_LINKUP          0x064
-#define PCIE_ELBI_LTSSM_ENABLE         0x1
-#define PCIE_ELBI_SLV_AWMISC           0x11c
-#define PCIE_ELBI_SLV_ARMISC           0x120
-#define PCIE_ELBI_SLV_DBI_ENABLE       (0x1 << 21)
-
-/* PCIe Purple registers */
-#define PCIE_PHY_GLOBAL_RESET          0x000
-#define PCIE_PHY_COMMON_RESET          0x004
-#define PCIE_PHY_CMN_REG               0x008
-#define PCIE_PHY_MAC_RESET             0x00c
-#define PCIE_PHY_PLL_LOCKED            0x010
-#define PCIE_PHY_TRSVREG_RESET         0x020
-#define PCIE_PHY_TRSV_RESET            0x024
-
-/* PCIe PHY registers */
-#define PCIE_PHY_IMPEDANCE             0x004
-#define PCIE_PHY_PLL_DIV_0             0x008
-#define PCIE_PHY_PLL_BIAS              0x00c
-#define PCIE_PHY_DCC_FEEDBACK          0x014
-#define PCIE_PHY_PLL_DIV_1             0x05c
-#define PCIE_PHY_TRSV0_EMP_LVL         0x084
-#define PCIE_PHY_TRSV0_DRV_LVL         0x088
-#define PCIE_PHY_TRSV0_RXCDR           0x0ac
-#define PCIE_PHY_TRSV0_LVCC            0x0dc
-#define PCIE_PHY_TRSV1_EMP_LVL         0x144
-#define PCIE_PHY_TRSV1_RXCDR           0x16c
-#define PCIE_PHY_TRSV1_LVCC            0x19c
-#define PCIE_PHY_TRSV2_EMP_LVL         0x204
-#define PCIE_PHY_TRSV2_RXCDR           0x22c
-#define PCIE_PHY_TRSV2_LVCC            0x25c
-#define PCIE_PHY_TRSV3_EMP_LVL         0x2c4
-#define PCIE_PHY_TRSV3_RXCDR           0x2ec
-#define PCIE_PHY_TRSV3_LVCC            0x31c
-
-static struct hw_pci exynos_pci;
+static struct hw_pci dw_pci;
+
+unsigned long global_io_offset;
 
 static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys)
 {
        return sys->private_data;
 }
 
-static inline int cfg_read(void *addr, int where, int size, u32 *val)
+int cfg_read(void __iomem *addr, int where, int size, u32 *val)
 {
        *val = readl(addr);
 
@@ -184,7 +85,7 @@ static inline int cfg_read(void *addr, int where, int size, u32 *val)
        return PCIBIOS_SUCCESSFUL;
 }
 
-static inline int cfg_write(void *addr, int where, int size, u32 val)
+int cfg_write(void __iomem *addr, int where, int size, u32 val)
 {
        if (size == 4)
                writel(val, addr);
@@ -198,155 +99,241 @@ static inline int cfg_write(void *addr, int where, int size, u32 val)
        return PCIBIOS_SUCCESSFUL;
 }
 
-static void exynos_pcie_sideband_dbi_w_mode(struct pcie_port *pp, bool on)
+static inline void dw_pcie_readl_rc(struct pcie_port *pp,
+                               void __iomem *dbi_addr, u32 *val)
 {
-       u32 val;
-
-       if (on) {
-               val = readl(pp->elbi_base + PCIE_ELBI_SLV_AWMISC);
-               val |= PCIE_ELBI_SLV_DBI_ENABLE;
-               writel(val, pp->elbi_base + PCIE_ELBI_SLV_AWMISC);
-       } else {
-               val = readl(pp->elbi_base + PCIE_ELBI_SLV_AWMISC);
-               val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
-               writel(val, pp->elbi_base + PCIE_ELBI_SLV_AWMISC);
-       }
-}
-
-static void exynos_pcie_sideband_dbi_r_mode(struct pcie_port *pp, bool on)
-{
-       u32 val;
-
-       if (on) {
-               val = readl(pp->elbi_base + PCIE_ELBI_SLV_ARMISC);
-               val |= PCIE_ELBI_SLV_DBI_ENABLE;
-               writel(val, pp->elbi_base + PCIE_ELBI_SLV_ARMISC);
-       } else {
-               val = readl(pp->elbi_base + PCIE_ELBI_SLV_ARMISC);
-               val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
-               writel(val, pp->elbi_base + PCIE_ELBI_SLV_ARMISC);
-       }
-}
-
-static inline void readl_rc(struct pcie_port *pp, void *dbi_base, u32 *val)
-{
-       exynos_pcie_sideband_dbi_r_mode(pp, true);
-       *val = readl(dbi_base);
-       exynos_pcie_sideband_dbi_r_mode(pp, false);
-       return;
+       if (pp->ops->readl_rc)
+               pp->ops->readl_rc(pp, dbi_addr, val);
+       else
+               *val = readl(dbi_addr);
 }
 
-static inline void writel_rc(struct pcie_port *pp, u32 val, void *dbi_base)
+static inline void dw_pcie_writel_rc(struct pcie_port *pp,
+                               u32 val, void __iomem *dbi_addr)
 {
-       exynos_pcie_sideband_dbi_w_mode(pp, true);
-       writel(val, dbi_base);
-       exynos_pcie_sideband_dbi_w_mode(pp, false);
-       return;
+       if (pp->ops->writel_rc)
+               pp->ops->writel_rc(pp, val, dbi_addr);
+       else
+               writel(val, dbi_addr);
 }
 
-static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
+int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
                                u32 *val)
 {
        int ret;
 
-       exynos_pcie_sideband_dbi_r_mode(pp, true);
-       ret = cfg_read(pp->dbi_base + (where & ~0x3), where, size, val);
-       exynos_pcie_sideband_dbi_r_mode(pp, false);
+       if (pp->ops->rd_own_conf)
+               ret = pp->ops->rd_own_conf(pp, where, size, val);
+       else
+               ret = cfg_read(pp->dbi_base + (where & ~0x3), where, size, val);
+
        return ret;
 }
 
-static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
+int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
                                u32 val)
 {
        int ret;
 
-       exynos_pcie_sideband_dbi_w_mode(pp, true);
-       ret = cfg_write(pp->dbi_base + (where & ~0x3), where, size, val);
-       exynos_pcie_sideband_dbi_w_mode(pp, false);
+       if (pp->ops->wr_own_conf)
+               ret = pp->ops->wr_own_conf(pp, where, size, val);
+       else
+               ret = cfg_write(pp->dbi_base + (where & ~0x3), where, size,
+                               val);
+
        return ret;
 }
 
-static void exynos_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev)
+int dw_pcie_link_up(struct pcie_port *pp)
+{
+       if (pp->ops->link_up)
+               return pp->ops->link_up(pp);
+       else
+               return 0;
+}
+
+int __init dw_pcie_host_init(struct pcie_port *pp)
+{
+       struct device_node *np = pp->dev->of_node;
+       struct of_pci_range range;
+       struct of_pci_range_parser parser;
+       u32 val;
+
+       if (of_pci_range_parser_init(&parser, np)) {
+               dev_err(pp->dev, "missing ranges property\n");
+               return -EINVAL;
+       }
+
+       /* Get the I/O and memory ranges from DT */
+       for_each_of_pci_range(&parser, &range) {
+               unsigned long restype = range.flags & IORESOURCE_TYPE_BITS;
+               if (restype == IORESOURCE_IO) {
+                       of_pci_range_to_resource(&range, np, &pp->io);
+                       pp->io.name = "I/O";
+                       pp->io.start = max_t(resource_size_t,
+                                            PCIBIOS_MIN_IO,
+                                            range.pci_addr + global_io_offset);
+                       pp->io.end = min_t(resource_size_t,
+                                          IO_SPACE_LIMIT,
+                                          range.pci_addr + range.size
+                                          + global_io_offset);
+                       pp->config.io_size = resource_size(&pp->io);
+                       pp->config.io_bus_addr = range.pci_addr;
+               }
+               if (restype == IORESOURCE_MEM) {
+                       of_pci_range_to_resource(&range, np, &pp->mem);
+                       pp->mem.name = "MEM";
+                       pp->config.mem_size = resource_size(&pp->mem);
+                       pp->config.mem_bus_addr = range.pci_addr;
+               }
+               if (restype == 0) {
+                       of_pci_range_to_resource(&range, np, &pp->cfg);
+                       pp->config.cfg0_size = resource_size(&pp->cfg)/2;
+                       pp->config.cfg1_size = resource_size(&pp->cfg)/2;
+               }
+       }
+
+       if (!pp->dbi_base) {
+               pp->dbi_base = devm_ioremap(pp->dev, pp->cfg.start,
+                                       resource_size(&pp->cfg));
+               if (!pp->dbi_base) {
+                       dev_err(pp->dev, "error with ioremap\n");
+                       return -ENOMEM;
+               }
+       }
+
+       pp->cfg0_base = pp->cfg.start;
+       pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size;
+       pp->io_base = pp->io.start;
+       pp->mem_base = pp->mem.start;
+
+       pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base,
+                                       pp->config.cfg0_size);
+       if (!pp->va_cfg0_base) {
+               dev_err(pp->dev, "error with ioremap in function\n");
+               return -ENOMEM;
+       }
+       pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base,
+                                       pp->config.cfg1_size);
+       if (!pp->va_cfg1_base) {
+               dev_err(pp->dev, "error with ioremap\n");
+               return -ENOMEM;
+       }
+
+       if (of_property_read_u32(np, "num-lanes", &pp->lanes)) {
+               dev_err(pp->dev, "Failed to parse the number of lanes\n");
+               return -EINVAL;
+       }
+
+       if (pp->ops->host_init)
+               pp->ops->host_init(pp);
+
+       dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
+
+       /* program correct class for RC */
+       dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI);
+
+       dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
+       val |= PORT_LOGIC_SPEED_CHANGE;
+       dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
+
+       dw_pci.nr_controllers = 1;
+       dw_pci.private_data = (void **)&pp;
+
+       pci_common_init(&dw_pci);
+       pci_assign_unassigned_resources();
+#ifdef CONFIG_PCI_DOMAINS
+       dw_pci.domain++;
+#endif
+
+       return 0;
+}
+
+static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev)
 {
        u32 val;
        void __iomem *dbi_base = pp->dbi_base;
 
        /* Program viewport 0 : OUTBOUND : CFG0 */
        val = PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0;
-       writel_rc(pp, val, dbi_base + PCIE_ATU_VIEWPORT);
-       writel_rc(pp, pp->cfg0_base, dbi_base + PCIE_ATU_LOWER_BASE);
-       writel_rc(pp, (pp->cfg0_base >> 32), dbi_base + PCIE_ATU_UPPER_BASE);
-       writel_rc(pp, pp->cfg0_base + pp->config.cfg0_size - 1,
+       dw_pcie_writel_rc(pp, val, dbi_base + PCIE_ATU_VIEWPORT);
+       dw_pcie_writel_rc(pp, pp->cfg0_base, dbi_base + PCIE_ATU_LOWER_BASE);
+       dw_pcie_writel_rc(pp, (pp->cfg0_base >> 32),
+                       dbi_base + PCIE_ATU_UPPER_BASE);
+       dw_pcie_writel_rc(pp, pp->cfg0_base + pp->config.cfg0_size - 1,
                        dbi_base + PCIE_ATU_LIMIT);
-       writel_rc(pp, busdev, dbi_base + PCIE_ATU_LOWER_TARGET);
-       writel_rc(pp, 0, dbi_base + PCIE_ATU_UPPER_TARGET);
-       writel_rc(pp, PCIE_ATU_TYPE_CFG0, dbi_base + PCIE_ATU_CR1);
+       dw_pcie_writel_rc(pp, busdev, dbi_base + PCIE_ATU_LOWER_TARGET);
+       dw_pcie_writel_rc(pp, 0, dbi_base + PCIE_ATU_UPPER_TARGET);
+       dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG0, dbi_base + PCIE_ATU_CR1);
        val = PCIE_ATU_ENABLE;
-       writel_rc(pp, val, dbi_base + PCIE_ATU_CR2);
+       dw_pcie_writel_rc(pp, val, dbi_base + PCIE_ATU_CR2);
 }
 
-static void exynos_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev)
+static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev)
 {
        u32 val;
        void __iomem *dbi_base = pp->dbi_base;
 
        /* Program viewport 1 : OUTBOUND : CFG1 */
        val = PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1;
-       writel_rc(pp, val, dbi_base + PCIE_ATU_VIEWPORT);
-       writel_rc(pp, PCIE_ATU_TYPE_CFG1, dbi_base + PCIE_ATU_CR1);
+       dw_pcie_writel_rc(pp, val, dbi_base + PCIE_ATU_VIEWPORT);
+       dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, dbi_base + PCIE_ATU_CR1);
        val = PCIE_ATU_ENABLE;
-       writel_rc(pp, val, dbi_base + PCIE_ATU_CR2);
-       writel_rc(pp, pp->cfg1_base, dbi_base + PCIE_ATU_LOWER_BASE);
-       writel_rc(pp, (pp->cfg1_base >> 32), dbi_base + PCIE_ATU_UPPER_BASE);
-       writel_rc(pp, pp->cfg1_base + pp->config.cfg1_size - 1,
+       dw_pcie_writel_rc(pp, val, dbi_base + PCIE_ATU_CR2);
+       dw_pcie_writel_rc(pp, pp->cfg1_base, dbi_base + PCIE_ATU_LOWER_BASE);
+       dw_pcie_writel_rc(pp, (pp->cfg1_base >> 32),
+                       dbi_base + PCIE_ATU_UPPER_BASE);
+       dw_pcie_writel_rc(pp, pp->cfg1_base + pp->config.cfg1_size - 1,
                        dbi_base + PCIE_ATU_LIMIT);
-       writel_rc(pp, busdev, dbi_base + PCIE_ATU_LOWER_TARGET);
-       writel_rc(pp, 0, dbi_base + PCIE_ATU_UPPER_TARGET);
+       dw_pcie_writel_rc(pp, busdev, dbi_base + PCIE_ATU_LOWER_TARGET);
+       dw_pcie_writel_rc(pp, 0, dbi_base + PCIE_ATU_UPPER_TARGET);
 }
 
-static void exynos_pcie_prog_viewport_mem_outbound(struct pcie_port *pp)
+static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp)
 {
        u32 val;
        void __iomem *dbi_base = pp->dbi_base;
 
        /* Program viewport 0 : OUTBOUND : MEM */
        val = PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0;
-       writel_rc(pp, val, dbi_base + PCIE_ATU_VIEWPORT);
-       writel_rc(pp, PCIE_ATU_TYPE_MEM, dbi_base + PCIE_ATU_CR1);
+       dw_pcie_writel_rc(pp, val, dbi_base + PCIE_ATU_VIEWPORT);
+       dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, dbi_base + PCIE_ATU_CR1);
        val = PCIE_ATU_ENABLE;
-       writel_rc(pp, val, dbi_base + PCIE_ATU_CR2);
-       writel_rc(pp, pp->mem_base, dbi_base + PCIE_ATU_LOWER_BASE);
-       writel_rc(pp, (pp->mem_base >> 32), dbi_base + PCIE_ATU_UPPER_BASE);
-       writel_rc(pp, pp->mem_base + pp->config.mem_size - 1,
+       dw_pcie_writel_rc(pp, val, dbi_base + PCIE_ATU_CR2);
+       dw_pcie_writel_rc(pp, pp->mem_base, dbi_base + PCIE_ATU_LOWER_BASE);
+       dw_pcie_writel_rc(pp, (pp->mem_base >> 32),
+                       dbi_base + PCIE_ATU_UPPER_BASE);
+       dw_pcie_writel_rc(pp, pp->mem_base + pp->config.mem_size - 1,
                        dbi_base + PCIE_ATU_LIMIT);
-       writel_rc(pp, pp->config.mem_bus_addr,
+       dw_pcie_writel_rc(pp, pp->config.mem_bus_addr,
                        dbi_base + PCIE_ATU_LOWER_TARGET);
-       writel_rc(pp, upper_32_bits(pp->config.mem_bus_addr),
+       dw_pcie_writel_rc(pp, upper_32_bits(pp->config.mem_bus_addr),
                        dbi_base + PCIE_ATU_UPPER_TARGET);
 }
 
-static void exynos_pcie_prog_viewport_io_outbound(struct pcie_port *pp)
+static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp)
 {
        u32 val;
        void __iomem *dbi_base = pp->dbi_base;
 
        /* Program viewport 1 : OUTBOUND : IO */
        val = PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1;
-       writel_rc(pp, val, dbi_base + PCIE_ATU_VIEWPORT);
-       writel_rc(pp, PCIE_ATU_TYPE_IO, dbi_base + PCIE_ATU_CR1);
+       dw_pcie_writel_rc(pp, val, dbi_base + PCIE_ATU_VIEWPORT);
+       dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, dbi_base + PCIE_ATU_CR1);
        val = PCIE_ATU_ENABLE;
-       writel_rc(pp, val, dbi_base + PCIE_ATU_CR2);
-       writel_rc(pp, pp->io_base, dbi_base + PCIE_ATU_LOWER_BASE);
-       writel_rc(pp, (pp->io_base >> 32), dbi_base + PCIE_ATU_UPPER_BASE);
-       writel_rc(pp, pp->io_base + pp->config.io_size - 1,
+       dw_pcie_writel_rc(pp, val, dbi_base + PCIE_ATU_CR2);
+       dw_pcie_writel_rc(pp, pp->io_base, dbi_base + PCIE_ATU_LOWER_BASE);
+       dw_pcie_writel_rc(pp, (pp->io_base >> 32),
+                       dbi_base + PCIE_ATU_UPPER_BASE);
+       dw_pcie_writel_rc(pp, pp->io_base + pp->config.io_size - 1,
                        dbi_base + PCIE_ATU_LIMIT);
-       writel_rc(pp, pp->config.io_bus_addr,
+       dw_pcie_writel_rc(pp, pp->config.io_bus_addr,
                        dbi_base + PCIE_ATU_LOWER_TARGET);
-       writel_rc(pp, upper_32_bits(pp->config.io_bus_addr),
+       dw_pcie_writel_rc(pp, upper_32_bits(pp->config.io_bus_addr),
                        dbi_base + PCIE_ATU_UPPER_TARGET);
 }
 
-static int exynos_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
+static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
                u32 devfn, int where, int size, u32 *val)
 {
        int ret = PCIBIOS_SUCCESSFUL;
@@ -357,19 +344,19 @@ static int exynos_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
        address = where & ~0x3;
 
        if (bus->parent->number == pp->root_bus_nr) {
-               exynos_pcie_prog_viewport_cfg0(pp, busdev);
+               dw_pcie_prog_viewport_cfg0(pp, busdev);
                ret = cfg_read(pp->va_cfg0_base + address, where, size, val);
-               exynos_pcie_prog_viewport_mem_outbound(pp);
+               dw_pcie_prog_viewport_mem_outbound(pp);
        } else {
-               exynos_pcie_prog_viewport_cfg1(pp, busdev);
+               dw_pcie_prog_viewport_cfg1(pp, busdev);
                ret = cfg_read(pp->va_cfg1_base + address, where, size, val);
-               exynos_pcie_prog_viewport_io_outbound(pp);
+               dw_pcie_prog_viewport_io_outbound(pp);
        }
 
        return ret;
 }
 
-static int exynos_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
+static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
                u32 devfn, int where, int size, u32 val)
 {
        int ret = PCIBIOS_SUCCESSFUL;
@@ -380,59 +367,25 @@ static int exynos_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
        address = where & ~0x3;
 
        if (bus->parent->number == pp->root_bus_nr) {
-               exynos_pcie_prog_viewport_cfg0(pp, busdev);
+               dw_pcie_prog_viewport_cfg0(pp, busdev);
                ret = cfg_write(pp->va_cfg0_base + address, where, size, val);
-               exynos_pcie_prog_viewport_mem_outbound(pp);
+               dw_pcie_prog_viewport_mem_outbound(pp);
        } else {
-               exynos_pcie_prog_viewport_cfg1(pp, busdev);
+               dw_pcie_prog_viewport_cfg1(pp, busdev);
                ret = cfg_write(pp->va_cfg1_base + address, where, size, val);
-               exynos_pcie_prog_viewport_io_outbound(pp);
+               dw_pcie_prog_viewport_io_outbound(pp);
        }
 
        return ret;
 }
 
-static unsigned long global_io_offset;
 
-static int exynos_pcie_setup(int nr, struct pci_sys_data *sys)
-{
-       struct pcie_port *pp;
-
-       pp = sys_to_pcie(sys);
-
-       if (!pp)
-               return 0;
-
-       if (global_io_offset < SZ_1M && pp->config.io_size > 0) {
-               sys->io_offset = global_io_offset - pp->config.io_bus_addr;
-               pci_ioremap_io(sys->io_offset, pp->io.start);
-               global_io_offset += SZ_64K;
-               pci_add_resource_offset(&sys->resources, &pp->io,
-                                       sys->io_offset);
-       }
-
-       sys->mem_offset = pp->mem.start - pp->config.mem_bus_addr;
-       pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset);
-
-       return 1;
-}
-
-static int exynos_pcie_link_up(struct pcie_port *pp)
-{
-       u32 val = readl(pp->elbi_base + PCIE_ELBI_RDLH_LINKUP);
-
-       if (val == PCIE_ELBI_LTSSM_ENABLE)
-               return 1;
-
-       return 0;
-}
-
-static int exynos_pcie_valid_config(struct pcie_port *pp,
+static int dw_pcie_valid_config(struct pcie_port *pp,
                                struct pci_bus *bus, int dev)
 {
        /* If there is no link, then there is no device */
        if (bus->number != pp->root_bus_nr) {
-               if (!exynos_pcie_link_up(pp))
+               if (!dw_pcie_link_up(pp))
                        return 0;
        }
 
@@ -450,7 +403,7 @@ static int exynos_pcie_valid_config(struct pcie_port *pp,
        return 1;
 }
 
-static int exynos_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
                        int size, u32 *val)
 {
        struct pcie_port *pp = sys_to_pcie(bus->sysdata);
@@ -462,23 +415,23 @@ static int exynos_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
                return -EINVAL;
        }
 
-       if (exynos_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
+       if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
                *val = 0xffffffff;
                return PCIBIOS_DEVICE_NOT_FOUND;
        }
 
        spin_lock_irqsave(&pp->conf_lock, flags);
        if (bus->number != pp->root_bus_nr)
-               ret = exynos_pcie_rd_other_conf(pp, bus, devfn,
+               ret = dw_pcie_rd_other_conf(pp, bus, devfn,
                                                where, size, val);
        else
-               ret = exynos_pcie_rd_own_conf(pp, where, size, val);
+               ret = dw_pcie_rd_own_conf(pp, where, size, val);
        spin_unlock_irqrestore(&pp->conf_lock, flags);
 
        return ret;
 }
 
-static int exynos_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
                        int where, int size, u32 val)
 {
        struct pcie_port *pp = sys_to_pcie(bus->sysdata);
@@ -490,34 +443,56 @@ static int exynos_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
                return -EINVAL;
        }
 
-       if (exynos_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
+       if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
                return PCIBIOS_DEVICE_NOT_FOUND;
 
        spin_lock_irqsave(&pp->conf_lock, flags);
        if (bus->number != pp->root_bus_nr)
-               ret = exynos_pcie_wr_other_conf(pp, bus, devfn,
+               ret = dw_pcie_wr_other_conf(pp, bus, devfn,
                                                where, size, val);
        else
-               ret = exynos_pcie_wr_own_conf(pp, where, size, val);
+               ret = dw_pcie_wr_own_conf(pp, where, size, val);
        spin_unlock_irqrestore(&pp->conf_lock, flags);
 
        return ret;
 }
 
-static struct pci_ops exynos_pcie_ops = {
-       .read = exynos_pcie_rd_conf,
-       .write = exynos_pcie_wr_conf,
+static struct pci_ops dw_pcie_ops = {
+       .read = dw_pcie_rd_conf,
+       .write = dw_pcie_wr_conf,
 };
 
-static struct pci_bus *exynos_pcie_scan_bus(int nr,
-                                       struct pci_sys_data *sys)
+int dw_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+       struct pcie_port *pp;
+
+       pp = sys_to_pcie(sys);
+
+       if (!pp)
+               return 0;
+
+       if (global_io_offset < SZ_1M && pp->config.io_size > 0) {
+               sys->io_offset = global_io_offset - pp->config.io_bus_addr;
+               pci_ioremap_io(sys->io_offset, pp->io.start);
+               global_io_offset += SZ_64K;
+               pci_add_resource_offset(&sys->resources, &pp->io,
+                                       sys->io_offset);
+       }
+
+       sys->mem_offset = pp->mem.start - pp->config.mem_bus_addr;
+       pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset);
+
+       return 1;
+}
+
+struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys)
 {
        struct pci_bus *bus;
        struct pcie_port *pp = sys_to_pcie(sys);
 
        if (pp) {
                pp->root_bus_nr = sys->busnr;
-               bus = pci_scan_root_bus(NULL, sys->busnr, &exynos_pcie_ops,
+               bus = pci_scan_root_bus(NULL, sys->busnr, &dw_pcie_ops,
                                        sys, &sys->resources);
        } else {
                bus = NULL;
@@ -527,20 +502,20 @@ static struct pci_bus *exynos_pcie_scan_bus(int nr,
        return bus;
 }
 
-static int exynos_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
        struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata);
 
        return pp->irq;
 }
 
-static struct hw_pci exynos_pci = {
-       .setup          = exynos_pcie_setup,
-       .scan           = exynos_pcie_scan_bus,
-       .map_irq        = exynos_pcie_map_irq,
+static struct hw_pci dw_pci = {
+       .setup          = dw_pcie_setup,
+       .scan           = dw_pcie_scan_bus,
+       .map_irq        = dw_pcie_map_irq,
 };
 
-static void exynos_pcie_setup_rc(struct pcie_port *pp)
+void dw_pcie_setup_rc(struct pcie_port *pp)
 {
        struct pcie_port_info *config = &pp->config;
        void __iomem *dbi_base = pp->dbi_base;
@@ -549,509 +524,67 @@ static void exynos_pcie_setup_rc(struct pcie_port *pp)
        u32 memlimit;
 
        /* set the number of lines as 4 */
-       readl_rc(pp, dbi_base + PCIE_PORT_LINK_CONTROL, &val);
+       dw_pcie_readl_rc(pp, dbi_base + PCIE_PORT_LINK_CONTROL, &val);
        val &= ~PORT_LINK_MODE_MASK;
-       val |= PORT_LINK_MODE_4_LANES;
-       writel_rc(pp, val, dbi_base + PCIE_PORT_LINK_CONTROL);
+       switch (pp->lanes) {
+       case 1:
+               val |= PORT_LINK_MODE_1_LANES;
+               break;
+       case 2:
+               val |= PORT_LINK_MODE_2_LANES;
+               break;
+       case 4:
+               val |= PORT_LINK_MODE_4_LANES;
+               break;
+       }
+       dw_pcie_writel_rc(pp, val, dbi_base + PCIE_PORT_LINK_CONTROL);
 
        /* set link width speed control register */
-       readl_rc(pp, dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL, &val);
+       dw_pcie_readl_rc(pp, dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL, &val);
        val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
-       val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
-       writel_rc(pp, val, dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+       switch (pp->lanes) {
+       case 1:
+               val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
+               break;
+       case 2:
+               val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
+               break;
+       case 4:
+               val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
+               break;
+       }
+       dw_pcie_writel_rc(pp, val, dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
 
        /* setup RC BARs */
-       writel_rc(pp, 0x00000004, dbi_base + PCI_BASE_ADDRESS_0);
-       writel_rc(pp, 0x00000004, dbi_base + PCI_BASE_ADDRESS_1);
+       dw_pcie_writel_rc(pp, 0x00000004, dbi_base + PCI_BASE_ADDRESS_0);
+       dw_pcie_writel_rc(pp, 0x00000004, dbi_base + PCI_BASE_ADDRESS_1);
 
        /* setup interrupt pins */
-       readl_rc(pp, dbi_base + PCI_INTERRUPT_LINE, &val);
+       dw_pcie_readl_rc(pp, dbi_base + PCI_INTERRUPT_LINE, &val);
        val &= 0xffff00ff;
        val |= 0x00000100;
-       writel_rc(pp, val, dbi_base + PCI_INTERRUPT_LINE);
+       dw_pcie_writel_rc(pp, val, dbi_base + PCI_INTERRUPT_LINE);
 
        /* setup bus numbers */
-       readl_rc(pp, dbi_base + PCI_PRIMARY_BUS, &val);
+       dw_pcie_readl_rc(pp, dbi_base + PCI_PRIMARY_BUS, &val);
        val &= 0xff000000;
        val |= 0x00010100;
-       writel_rc(pp, val, dbi_base + PCI_PRIMARY_BUS);
+       dw_pcie_writel_rc(pp, val, dbi_base + PCI_PRIMARY_BUS);
 
        /* setup memory base, memory limit */
        membase = ((u32)pp->mem_base & 0xfff00000) >> 16;
        memlimit = (config->mem_size + (u32)pp->mem_base) & 0xfff00000;
        val = memlimit | membase;
-       writel_rc(pp, val, dbi_base + PCI_MEMORY_BASE);
+       dw_pcie_writel_rc(pp, val, dbi_base + PCI_MEMORY_BASE);
 
        /* setup command register */
-       readl_rc(pp, dbi_base + PCI_COMMAND, &val);
+       dw_pcie_readl_rc(pp, dbi_base + PCI_COMMAND, &val);
        val &= 0xffff0000;
        val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
                PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
-       writel_rc(pp, val, dbi_base + PCI_COMMAND);
-}
-
-static void exynos_pcie_assert_core_reset(struct pcie_port *pp)
-{
-       u32 val;
-       void __iomem *elbi_base = pp->elbi_base;
-
-       val = readl(elbi_base + PCIE_CORE_RESET);
-       val &= ~PCIE_CORE_RESET_ENABLE;
-       writel(val, elbi_base + PCIE_CORE_RESET);
-       writel(0, elbi_base + PCIE_PWR_RESET);
-       writel(0, elbi_base + PCIE_STICKY_RESET);
-       writel(0, elbi_base + PCIE_NONSTICKY_RESET);
-}
-
-static void exynos_pcie_deassert_core_reset(struct pcie_port *pp)
-{
-       u32 val;
-       void __iomem *elbi_base = pp->elbi_base;
-       void __iomem *purple_base = pp->purple_base;
-
-       val = readl(elbi_base + PCIE_CORE_RESET);
-       val |= PCIE_CORE_RESET_ENABLE;
-       writel(val, elbi_base + PCIE_CORE_RESET);
-       writel(1, elbi_base + PCIE_STICKY_RESET);
-       writel(1, elbi_base + PCIE_NONSTICKY_RESET);
-       writel(1, elbi_base + PCIE_APP_INIT_RESET);
-       writel(0, elbi_base + PCIE_APP_INIT_RESET);
-       writel(1, purple_base + PCIE_PHY_MAC_RESET);
-}
-
-static void exynos_pcie_assert_phy_reset(struct pcie_port *pp)
-{
-       void __iomem *purple_base = pp->purple_base;
-
-       writel(0, purple_base + PCIE_PHY_MAC_RESET);
-       writel(1, purple_base + PCIE_PHY_GLOBAL_RESET);
-}
-
-static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp)
-{
-       void __iomem *elbi_base = pp->elbi_base;
-       void __iomem *purple_base = pp->purple_base;
-
-       writel(0, purple_base + PCIE_PHY_GLOBAL_RESET);
-       writel(1, elbi_base + PCIE_PWR_RESET);
-       writel(0, purple_base + PCIE_PHY_COMMON_RESET);
-       writel(0, purple_base + PCIE_PHY_CMN_REG);
-       writel(0, purple_base + PCIE_PHY_TRSVREG_RESET);
-       writel(0, purple_base + PCIE_PHY_TRSV_RESET);
-}
-
-static void exynos_pcie_init_phy(struct pcie_port *pp)
-{
-       void __iomem *phy_base = pp->phy_base;
-
-       /* DCC feedback control off */
-       writel(0x29, phy_base + PCIE_PHY_DCC_FEEDBACK);
-
-       /* set TX/RX impedance */
-       writel(0xd5, phy_base + PCIE_PHY_IMPEDANCE);
-
-       /* set 50Mhz PHY clock */
-       writel(0x14, phy_base + PCIE_PHY_PLL_DIV_0);
-       writel(0x12, phy_base + PCIE_PHY_PLL_DIV_1);
-
-       /* set TX Differential output for lane 0 */
-       writel(0x7f, phy_base + PCIE_PHY_TRSV0_DRV_LVL);
-
-       /* set TX Pre-emphasis Level Control for lane 0 to minimum */
-       writel(0x0, phy_base + PCIE_PHY_TRSV0_EMP_LVL);
-
-       /* set RX clock and data recovery bandwidth */
-       writel(0xe7, phy_base + PCIE_PHY_PLL_BIAS);
-       writel(0x82, phy_base + PCIE_PHY_TRSV0_RXCDR);
-       writel(0x82, phy_base + PCIE_PHY_TRSV1_RXCDR);
-       writel(0x82, phy_base + PCIE_PHY_TRSV2_RXCDR);
-       writel(0x82, phy_base + PCIE_PHY_TRSV3_RXCDR);
-
-       /* change TX Pre-emphasis Level Control for lanes */
-       writel(0x39, phy_base + PCIE_PHY_TRSV0_EMP_LVL);
-       writel(0x39, phy_base + PCIE_PHY_TRSV1_EMP_LVL);
-       writel(0x39, phy_base + PCIE_PHY_TRSV2_EMP_LVL);
-       writel(0x39, phy_base + PCIE_PHY_TRSV3_EMP_LVL);
-
-       /* set LVCC */
-       writel(0x20, phy_base + PCIE_PHY_TRSV0_LVCC);
-       writel(0xa0, phy_base + PCIE_PHY_TRSV1_LVCC);
-       writel(0xa0, phy_base + PCIE_PHY_TRSV2_LVCC);
-       writel(0xa0, phy_base + PCIE_PHY_TRSV3_LVCC);
-}
-
-static void exynos_pcie_assert_reset(struct pcie_port *pp)
-{
-       if (pp->reset_gpio >= 0)
-               devm_gpio_request_one(pp->dev, pp->reset_gpio,
-                               GPIOF_OUT_INIT_HIGH, "RESET");
-       return;
-}
-
-static int exynos_pcie_establish_link(struct pcie_port *pp)
-{
-       u32 val;
-       int count = 0;
-       void __iomem *elbi_base = pp->elbi_base;
-       void __iomem *purple_base = pp->purple_base;
-       void __iomem *phy_base = pp->phy_base;
-
-       if (exynos_pcie_link_up(pp)) {
-               dev_err(pp->dev, "Link already up\n");
-               return 0;
-       }
-
-       /* assert reset signals */
-       exynos_pcie_assert_core_reset(pp);
-       exynos_pcie_assert_phy_reset(pp);
-
-       /* de-assert phy reset */
-       exynos_pcie_deassert_phy_reset(pp);
-
-       /* initialize phy */
-       exynos_pcie_init_phy(pp);
-
-       /* pulse for common reset */
-       writel(1, purple_base + PCIE_PHY_COMMON_RESET);
-       udelay(500);
-       writel(0, purple_base + PCIE_PHY_COMMON_RESET);
-
-       /* de-assert core reset */
-       exynos_pcie_deassert_core_reset(pp);
-
-       /* setup root complex */
-       exynos_pcie_setup_rc(pp);
-
-       /* assert reset signal */
-       exynos_pcie_assert_reset(pp);
-
-       /* assert LTSSM enable */
-       writel(PCIE_ELBI_LTSSM_ENABLE, elbi_base + PCIE_APP_LTSSM_ENABLE);
-
-       /* check if the link is up or not */
-       while (!exynos_pcie_link_up(pp)) {
-               mdelay(100);
-               count++;
-               if (count == 10) {
-                       while (readl(phy_base + PCIE_PHY_PLL_LOCKED) == 0) {
-                               val = readl(purple_base + PCIE_PHY_PLL_LOCKED);
-                               dev_info(pp->dev, "PLL Locked: 0x%x\n", val);
-                       }
-                       dev_err(pp->dev, "PCIe Link Fail\n");
-                       return -EINVAL;
-               }
-       }
-
-       dev_info(pp->dev, "Link up\n");
-
-       return 0;
-}
-
-static void exynos_pcie_clear_irq_pulse(struct pcie_port *pp)
-{
-       u32 val;
-       void __iomem *elbi_base = pp->elbi_base;
-
-       val = readl(elbi_base + PCIE_IRQ_PULSE);
-       writel(val, elbi_base + PCIE_IRQ_PULSE);
-       return;
-}
-
-static void exynos_pcie_enable_irq_pulse(struct pcie_port *pp)
-{
-       u32 val;
-       void __iomem *elbi_base = pp->elbi_base;
-
-       /* enable INTX interrupt */
-       val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
-               IRQ_INTC_ASSERT | IRQ_INTD_ASSERT,
-       writel(val, elbi_base + PCIE_IRQ_EN_PULSE);
-       return;
-}
-
-static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
-{
-       struct pcie_port *pp = arg;
-
-       exynos_pcie_clear_irq_pulse(pp);
-       return IRQ_HANDLED;
-}
-
-static void exynos_pcie_enable_interrupts(struct pcie_port *pp)
-{
-       exynos_pcie_enable_irq_pulse(pp);
-       return;
-}
-
-static void exynos_pcie_host_init(struct pcie_port *pp)
-{
-       struct pcie_port_info *config = &pp->config;
-       u32 val;
-
-       /* Keep first 64K for IO */
-       pp->cfg0_base = pp->cfg.start;
-       pp->cfg1_base = pp->cfg.start + config->cfg0_size;
-       pp->io_base = pp->io.start;
-       pp->mem_base = pp->mem.start;
-
-       /* enable link */
-       exynos_pcie_establish_link(pp);
-
-       exynos_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
-
-       /* program correct class for RC */
-       exynos_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI);
-
-       exynos_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
-       val |= PORT_LOGIC_SPEED_CHANGE;
-       exynos_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
-
-       exynos_pcie_enable_interrupts(pp);
-}
-
-static int add_pcie_port(struct pcie_port *pp, struct platform_device *pdev)
-{
-       struct resource *elbi_base;
-       struct resource *phy_base;
-       struct resource *purple_base;
-       int ret;
-
-       elbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!elbi_base) {
-               dev_err(&pdev->dev, "couldn't get elbi base resource\n");
-               return -EINVAL;
-       }
-       pp->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base);
-       if (IS_ERR(pp->elbi_base))
-               return PTR_ERR(pp->elbi_base);
-
-       phy_base = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       if (!phy_base) {
-               dev_err(&pdev->dev, "couldn't get phy base resource\n");
-               return -EINVAL;
-       }
-       pp->phy_base = devm_ioremap_resource(&pdev->dev, phy_base);
-       if (IS_ERR(pp->phy_base))
-               return PTR_ERR(pp->phy_base);
-
-       purple_base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
-       if (!purple_base) {
-               dev_err(&pdev->dev, "couldn't get purple base resource\n");
-               return -EINVAL;
-       }
-       pp->purple_base = devm_ioremap_resource(&pdev->dev, purple_base);
-       if (IS_ERR(pp->purple_base))
-               return PTR_ERR(pp->purple_base);
-
-       pp->irq = platform_get_irq(pdev, 1);
-       if (!pp->irq) {
-               dev_err(&pdev->dev, "failed to get irq\n");
-               return -ENODEV;
-       }
-       ret = devm_request_irq(&pdev->dev, pp->irq, exynos_pcie_irq_handler,
-                               IRQF_SHARED, "exynos-pcie", pp);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to request irq\n");
-               return ret;
-       }
-
-       pp->dbi_base = devm_ioremap(&pdev->dev, pp->cfg.start,
-                               resource_size(&pp->cfg));
-       if (!pp->dbi_base) {
-               dev_err(&pdev->dev, "error with ioremap\n");
-               return -ENOMEM;
-       }
-
-       pp->root_bus_nr = -1;
-
-       spin_lock_init(&pp->conf_lock);
-       exynos_pcie_host_init(pp);
-       pp->va_cfg0_base = devm_ioremap(&pdev->dev, pp->cfg0_base,
-                                       pp->config.cfg0_size);
-       if (!pp->va_cfg0_base) {
-               dev_err(pp->dev, "error with ioremap in function\n");
-               return -ENOMEM;
-       }
-       pp->va_cfg1_base = devm_ioremap(&pdev->dev, pp->cfg1_base,
-                                       pp->config.cfg1_size);
-       if (!pp->va_cfg1_base) {
-               dev_err(pp->dev, "error with ioremap\n");
-               return -ENOMEM;
-       }
-
-       return 0;
-}
-
-static int __init exynos_pcie_probe(struct platform_device *pdev)
-{
-       struct pcie_port *pp;
-       struct device_node *np = pdev->dev.of_node;
-       struct of_pci_range range;
-       struct of_pci_range_parser parser;
-       int ret;
-
-       pp = devm_kzalloc(&pdev->dev, sizeof(*pp), GFP_KERNEL);
-       if (!pp) {
-               dev_err(&pdev->dev, "no memory for pcie port\n");
-               return -ENOMEM;
-       }
-
-       pp->dev = &pdev->dev;
-
-       if (of_pci_range_parser_init(&parser, np)) {
-               dev_err(&pdev->dev, "missing ranges property\n");
-               return -EINVAL;
-       }
-
-       /* Get the I/O and memory ranges from DT */
-       for_each_of_pci_range(&parser, &range) {
-               unsigned long restype = range.flags & IORESOURCE_TYPE_BITS;
-               if (restype == IORESOURCE_IO) {
-                       of_pci_range_to_resource(&range, np, &pp->io);
-                       pp->io.name = "I/O";
-                       pp->io.start = max_t(resource_size_t,
-                                            PCIBIOS_MIN_IO,
-                                            range.pci_addr + global_io_offset);
-                       pp->io.end = min_t(resource_size_t,
-                                          IO_SPACE_LIMIT,
-                                          range.pci_addr + range.size
-                                          + global_io_offset);
-                       pp->config.io_size = resource_size(&pp->io);
-                       pp->config.io_bus_addr = range.pci_addr;
-               }
-               if (restype == IORESOURCE_MEM) {
-                       of_pci_range_to_resource(&range, np, &pp->mem);
-                       pp->mem.name = "MEM";
-                       pp->config.mem_size = resource_size(&pp->mem);
-                       pp->config.mem_bus_addr = range.pci_addr;
-               }
-               if (restype == 0) {
-                       of_pci_range_to_resource(&range, np, &pp->cfg);
-                       pp->config.cfg0_size = resource_size(&pp->cfg)/2;
-                       pp->config.cfg1_size = resource_size(&pp->cfg)/2;
-               }
-       }
-
-       pp->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
-
-       pp->clk = devm_clk_get(&pdev->dev, "pcie");
-       if (IS_ERR(pp->clk)) {
-               dev_err(&pdev->dev, "Failed to get pcie rc clock\n");
-               return PTR_ERR(pp->clk);
-       }
-       ret = clk_prepare_enable(pp->clk);
-       if (ret)
-               return ret;
-
-       pp->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus");
-       if (IS_ERR(pp->bus_clk)) {
-               dev_err(&pdev->dev, "Failed to get pcie bus clock\n");
-               ret = PTR_ERR(pp->bus_clk);
-               goto fail_clk;
-       }
-       ret = clk_prepare_enable(pp->bus_clk);
-       if (ret)
-               goto fail_clk;
-
-       ret = add_pcie_port(pp, pdev);
-       if (ret < 0)
-               goto fail_bus_clk;
-
-       pp->controller = exynos_pci.nr_controllers;
-       exynos_pci.nr_controllers = 1;
-       exynos_pci.private_data = (void **)&pp;
-
-       pci_common_init(&exynos_pci);
-       pci_assign_unassigned_resources();
-#ifdef CONFIG_PCI_DOMAINS
-       exynos_pci.domain++;
-#endif
-
-       platform_set_drvdata(pdev, pp);
-       return 0;
-
-fail_bus_clk:
-       clk_disable_unprepare(pp->bus_clk);
-fail_clk:
-       clk_disable_unprepare(pp->clk);
-       return ret;
-}
-
-static int __exit exynos_pcie_remove(struct platform_device *pdev)
-{
-       struct pcie_port *pp = platform_get_drvdata(pdev);
-
-       clk_disable_unprepare(pp->bus_clk);
-       clk_disable_unprepare(pp->clk);
-
-       return 0;
-}
-
-static const struct of_device_id exynos_pcie_of_match[] = {
-       { .compatible = "samsung,exynos5440-pcie", },
-       {},
-};
-MODULE_DEVICE_TABLE(of, exynos_pcie_of_match);
-
-static struct platform_driver exynos_pcie_driver = {
-       .remove         = __exit_p(exynos_pcie_remove),
-       .driver = {
-               .name   = "exynos-pcie",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(exynos_pcie_of_match),
-       },
-};
-
-static int exynos_pcie_abort(unsigned long addr, unsigned int fsr,
-                       struct pt_regs *regs)
-{
-       unsigned long pc = instruction_pointer(regs);
-       unsigned long instr = *(unsigned long *)pc;
-
-       WARN_ONCE(1, "pcie abort\n");
-
-       /*
-        * If the instruction being executed was a read,
-        * make it look like it read all-ones.
-        */
-       if ((instr & 0x0c100000) == 0x04100000) {
-               int reg = (instr >> 12) & 15;
-               unsigned long val;
-
-               if (instr & 0x00400000)
-                       val = 255;
-               else
-                       val = -1;
-
-               regs->uregs[reg] = val;
-               regs->ARM_pc += 4;
-               return 0;
-       }
-
-       if ((instr & 0x0e100090) == 0x00100090) {
-               int reg = (instr >> 12) & 15;
-
-               regs->uregs[reg] = -1;
-               regs->ARM_pc += 4;
-               return 0;
-       }
-
-       return 1;
-}
-
-/* Exynos PCIe driver does not allow module unload */
-
-static int __init pcie_init(void)
-{
-       hook_fault_code(16 + 6, exynos_pcie_abort, SIGBUS, 0,
-                       "imprecise external abort");
-
-       platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe);
-
-       return 0;
+       dw_pcie_writel_rc(pp, val, dbi_base + PCI_COMMAND);
 }
-subsys_initcall(pcie_init);
 
 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
-MODULE_DESCRIPTION("Samsung PCIe host controller driver");
+MODULE_DESCRIPTION("Designware PCIe host controller driver");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
new file mode 100644 (file)
index 0000000..133820f
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Synopsys Designware PCIe host controller driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+struct pcie_port_info {
+       u32             cfg0_size;
+       u32             cfg1_size;
+       u32             io_size;
+       u32             mem_size;
+       phys_addr_t     io_bus_addr;
+       phys_addr_t     mem_bus_addr;
+};
+
+struct pcie_port {
+       struct device           *dev;
+       u8                      root_bus_nr;
+       void __iomem            *dbi_base;
+       u64                     cfg0_base;
+       void __iomem            *va_cfg0_base;
+       u64                     cfg1_base;
+       void __iomem            *va_cfg1_base;
+       u64                     io_base;
+       u64                     mem_base;
+       spinlock_t              conf_lock;
+       struct resource         cfg;
+       struct resource         io;
+       struct resource         mem;
+       struct pcie_port_info   config;
+       int                     irq;
+       u32                     lanes;
+       struct pcie_host_ops    *ops;
+};
+
+struct pcie_host_ops {
+       void (*readl_rc)(struct pcie_port *pp,
+                       void __iomem *dbi_base, u32 *val);
+       void (*writel_rc)(struct pcie_port *pp,
+                       u32 val, void __iomem *dbi_base);
+       int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val);
+       int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val);
+       int (*link_up)(struct pcie_port *pp);
+       void (*host_init)(struct pcie_port *pp);
+};
+
+extern unsigned long global_io_offset;
+
+int cfg_read(void __iomem *addr, int where, int size, u32 *val);
+int cfg_write(void __iomem *addr, int where, int size, u32 val);
+int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val);
+int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val);
+int dw_pcie_link_up(struct pcie_port *pp);
+void dw_pcie_setup_rc(struct pcie_port *pp);
+int dw_pcie_host_init(struct pcie_port *pp);
+int dw_pcie_setup(int nr, struct pci_sys_data *sys);
+struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys);
+int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
index 7fb3269..541bbe6 100644 (file)
@@ -155,6 +155,7 @@ void pciehp_green_led_off(struct slot *slot);
 void pciehp_green_led_blink(struct slot *slot);
 int pciehp_check_link_status(struct controller *ctrl);
 void pciehp_release_ctrl(struct controller *ctrl);
+int pciehp_reset_slot(struct slot *slot, int probe);
 
 static inline const char *slot_name(struct slot *slot)
 {
index 7d72c5e..f4a18f5 100644 (file)
@@ -69,6 +69,7 @@ static int get_power_status   (struct hotplug_slot *slot, u8 *value);
 static int get_attention_status        (struct hotplug_slot *slot, u8 *value);
 static int get_latch_status    (struct hotplug_slot *slot, u8 *value);
 static int get_adapter_status  (struct hotplug_slot *slot, u8 *value);
+static int reset_slot          (struct hotplug_slot *slot, int probe);
 
 /**
  * release_slot - free up the memory used by a slot
@@ -111,6 +112,7 @@ static int init_slot(struct controller *ctrl)
        ops->disable_slot = disable_slot;
        ops->get_power_status = get_power_status;
        ops->get_adapter_status = get_adapter_status;
+       ops->reset_slot = reset_slot;
        if (MRL_SENS(ctrl))
                ops->get_latch_status = get_latch_status;
        if (ATTN_LED(ctrl)) {
@@ -223,6 +225,16 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
        return pciehp_get_adapter_status(slot, value);
 }
 
+static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
+{
+       struct slot *slot = hotplug_slot->private;
+
+       ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+                __func__, slot_name(slot));
+
+       return pciehp_reset_slot(slot, probe);
+}
+
 static int pciehp_probe(struct pcie_device *dev)
 {
        int rc;
index b225573..51f56ef 100644 (file)
@@ -749,6 +749,37 @@ static void pcie_disable_notification(struct controller *ctrl)
                ctrl_warn(ctrl, "Cannot disable software notification\n");
 }
 
+/*
+ * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
+ * bus reset of the bridge, but if the slot supports surprise removal we need
+ * to disable presence detection around the bus reset and clear any spurious
+ * events after.
+ */
+int pciehp_reset_slot(struct slot *slot, int probe)
+{
+       struct controller *ctrl = slot->ctrl;
+
+       if (probe)
+               return 0;
+
+       if (HP_SUPR_RM(ctrl)) {
+               pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_PDCE);
+               if (pciehp_poll_mode)
+                       del_timer_sync(&ctrl->poll_timer);
+       }
+
+       pci_reset_bridge_secondary_bus(ctrl->pcie->port);
+
+       if (HP_SUPR_RM(ctrl)) {
+               pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC);
+               pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PDCE, PCI_EXP_SLTCTL_PDCE);
+               if (pciehp_poll_mode)
+                       int_poll_timeout(ctrl->poll_timer.data);
+       }
+
+       return 0;
+}
+
 int pcie_init_notification(struct controller *ctrl)
 {
        if (pciehp_request_irq(ctrl))
index de8ffac..21a7182 100644 (file)
@@ -286,7 +286,6 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
            (!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
                return -EINVAL;
 
-       pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
        pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &offset);
        pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &stride);
        if (!offset || (nr_virtfn > 1 && !stride))
@@ -324,7 +323,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
 
                if (!pdev->is_physfn) {
                        pci_dev_put(pdev);
-                       return -ENODEV;
+                       return -ENOSYS;
                }
 
                rc = sysfs_create_link(&dev->dev.kobj,
@@ -334,6 +333,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
                        return rc;
        }
 
+       pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
        iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
        pci_cfg_access_lock(dev);
        pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
@@ -368,6 +368,7 @@ failed:
        iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE);
        pci_cfg_access_lock(dev);
        pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
+       pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0);
        ssleep(1);
        pci_cfg_access_unlock(dev);
 
@@ -401,6 +402,7 @@ static void sriov_disable(struct pci_dev *dev)
                sysfs_remove_link(&dev->dev.kobj, "dep_link");
 
        iov->num_VFs = 0;
+       pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, 0);
 }
 
 static int sriov_init(struct pci_dev *dev, int pos)
@@ -662,7 +664,7 @@ int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn)
        might_sleep();
 
        if (!dev->is_physfn)
-               return -ENODEV;
+               return -ENOSYS;
 
        return sriov_enable(dev, nr_virtfn);
 }
@@ -722,7 +724,7 @@ EXPORT_SYMBOL_GPL(pci_num_vf);
  * @dev: the PCI device
  *
  * Returns number of VFs belonging to this device that are assigned to a guest.
- * If device is not a physical function returns -ENODEV.
+ * If device is not a physical function returns 0.
  */
 int pci_vfs_assigned(struct pci_dev *dev)
 {
@@ -767,12 +769,15 @@ EXPORT_SYMBOL_GPL(pci_vfs_assigned);
  * device's mutex held.
  *
  * Returns 0 if PF is an SRIOV-capable device and
- * value of numvfs valid. If not a PF with VFS, return -EINVAL;
+ * value of numvfs valid. If not a PF return -ENOSYS;
+ * if numvfs is invalid return -EINVAL;
  * if VFs already enabled, return -EBUSY.
  */
 int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
 {
-       if (!dev->is_physfn || (numvfs > dev->sriov->total_VFs))
+       if (!dev->is_physfn)
+               return -ENOSYS;
+       if (numvfs > dev->sriov->total_VFs)
                return -EINVAL;
 
        /* Shouldn't change if VFs already enabled */
@@ -786,17 +791,17 @@ int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)
 EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);
 
 /**
- * pci_sriov_get_totalvfs -- get total VFs supported on this devic3
+ * pci_sriov_get_totalvfs -- get total VFs supported on this device
  * @dev: the PCI PF device
  *
  * For a PCIe device with SRIOV support, return the PCIe
  * SRIOV capability value of TotalVFs or the value of driver_max_VFs
- * if the driver reduced it.  Otherwise, -EINVAL.
+ * if the driver reduced it.  Otherwise 0.
  */
 int pci_sriov_get_totalvfs(struct pci_dev *dev)
 {
        if (!dev->is_physfn)
-               return -EINVAL;
+               return 0;
 
        if (dev->sriov->driver_max_VFs)
                return dev->sriov->driver_max_VFs;
index c0dbe1f..7128cfd 100644 (file)
@@ -131,19 +131,19 @@ static ssize_t pci_bus_show_cpuaffinity(struct device *dev,
        return ret;
 }
 
-static inline ssize_t pci_bus_show_cpumaskaffinity(struct device *dev,
-                                       struct device_attribute *attr,
-                                       char *buf)
+static ssize_t cpuaffinity_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
 {
        return pci_bus_show_cpuaffinity(dev, 0, attr, buf);
 }
+static DEVICE_ATTR_RO(cpuaffinity);
 
-static inline ssize_t pci_bus_show_cpulistaffinity(struct device *dev,
-                                       struct device_attribute *attr,
-                                       char *buf)
+static ssize_t cpulistaffinity_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
 {
        return pci_bus_show_cpuaffinity(dev, 1, attr, buf);
 }
+static DEVICE_ATTR_RO(cpulistaffinity);
 
 /* show resources */
 static ssize_t
@@ -379,6 +379,7 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr,
        }
        return count;
 }
+static DEVICE_ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_bus_rescan_store);
 
 #if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI)
 static ssize_t d3cold_allowed_store(struct device *dev,
@@ -514,11 +515,20 @@ struct device_attribute pci_dev_attrs[] = {
        __ATTR_NULL,
 };
 
-struct device_attribute pcibus_dev_attrs[] = {
-       __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_bus_rescan_store),
-       __ATTR(cpuaffinity, S_IRUGO, pci_bus_show_cpumaskaffinity, NULL),
-       __ATTR(cpulistaffinity, S_IRUGO, pci_bus_show_cpulistaffinity, NULL),
-       __ATTR_NULL,
+static struct attribute *pcibus_attrs[] = {
+       &dev_attr_rescan.attr,
+       &dev_attr_cpuaffinity.attr,
+       &dev_attr_cpulistaffinity.attr,
+       NULL,
+};
+
+static const struct attribute_group pcibus_group = {
+       .attrs = pcibus_attrs,
+};
+
+const struct attribute_group *pcibus_groups[] = {
+       &pcibus_group,
+       NULL,
 };
 
 static ssize_t
index 44a1a8a..d3fdce8 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/device.h>
 #include <linux/pm_runtime.h>
+#include <linux/pci_hotplug.h>
 #include <asm-generic/pci-bridge.h>
 #include <asm/setup.h>
 #include "pci.h"
@@ -2012,7 +2013,7 @@ static void pci_add_saved_cap(struct pci_dev *pci_dev,
 }
 
 /**
- * pci_add_save_buffer - allocate buffer for saving given capability registers
+ * pci_add_cap_save_buffer - allocate buffer for saving given capability registers
  * @dev: the PCI device
  * @cap: the capability to allocate the buffer for
  * @size: requested size of the buffer
@@ -2379,6 +2380,27 @@ void pci_enable_acs(struct pci_dev *dev)
        pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
 }
 
+static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
+{
+       int pos;
+       u16 cap, ctrl;
+
+       pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS);
+       if (!pos)
+               return false;
+
+       /*
+        * Except for egress control, capabilities are either required
+        * or only required if controllable.  Features missing from the
+        * capability field can therefore be assumed as hard-wired enabled.
+        */
+       pci_read_config_word(pdev, pos + PCI_ACS_CAP, &cap);
+       acs_flags &= (cap | PCI_ACS_EC);
+
+       pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl);
+       return (ctrl & acs_flags) == acs_flags;
+}
+
 /**
  * pci_acs_enabled - test ACS against required flags for a given device
  * @pdev: device to test
@@ -2386,36 +2408,76 @@ void pci_enable_acs(struct pci_dev *dev)
  *
  * Return true if the device supports the provided flags.  Automatically
  * filters out flags that are not implemented on multifunction devices.
+ *
+ * Note that this interface checks the effective ACS capabilities of the
+ * device rather than the actual capabilities.  For instance, most single
+ * function endpoints are not required to support ACS because they have no
+ * opportunity for peer-to-peer access.  We therefore return 'true'
+ * regardless of whether the device exposes an ACS capability.  This makes
+ * it much easier for callers of this function to ignore the actual type
+ * or topology of the device when testing ACS support.
  */
 bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags)
 {
-       int pos, ret;
-       u16 ctrl;
+       int ret;
 
        ret = pci_dev_specific_acs_enabled(pdev, acs_flags);
        if (ret >= 0)
                return ret > 0;
 
+       /*
+        * Conventional PCI and PCI-X devices never support ACS, either
+        * effectively or actually.  The shared bus topology implies that
+        * any device on the bus can receive or snoop DMA.
+        */
        if (!pci_is_pcie(pdev))
                return false;
 
-       /* Filter out flags not applicable to multifunction */
-       if (pdev->multifunction)
-               acs_flags &= (PCI_ACS_RR | PCI_ACS_CR |
-                             PCI_ACS_EC | PCI_ACS_DT);
-
-       if (pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM ||
-           pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
-           pdev->multifunction) {
-               pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS);
-               if (!pos)
-                       return false;
+       switch (pci_pcie_type(pdev)) {
+       /*
+        * PCI/X-to-PCIe bridges are not specifically mentioned by the spec,
+        * but since their primary inteface is PCI/X, we conservatively
+        * handle them as we would a non-PCIe device.
+        */
+       case PCI_EXP_TYPE_PCIE_BRIDGE:
+       /*
+        * PCIe 3.0, 6.12.1 excludes ACS on these devices.  "ACS is never
+        * applicable... must never implement an ACS Extended Capability...".
+        * This seems arbitrary, but we take a conservative interpretation
+        * of this statement.
+        */
+       case PCI_EXP_TYPE_PCI_BRIDGE:
+       case PCI_EXP_TYPE_RC_EC:
+               return false;
+       /*
+        * PCIe 3.0, 6.12.1.1 specifies that downstream and root ports should
+        * implement ACS in order to indicate their peer-to-peer capabilities,
+        * regardless of whether they are single- or multi-function devices.
+        */
+       case PCI_EXP_TYPE_DOWNSTREAM:
+       case PCI_EXP_TYPE_ROOT_PORT:
+               return pci_acs_flags_enabled(pdev, acs_flags);
+       /*
+        * PCIe 3.0, 6.12.1.2 specifies ACS capabilities that should be
+        * implemented by the remaining PCIe types to indicate peer-to-peer
+        * capabilities, but only when they are part of a multifunciton
+        * device.  The footnote for section 6.12 indicates the specific
+        * PCIe types included here.
+        */
+       case PCI_EXP_TYPE_ENDPOINT:
+       case PCI_EXP_TYPE_UPSTREAM:
+       case PCI_EXP_TYPE_LEG_END:
+       case PCI_EXP_TYPE_RC_END:
+               if (!pdev->multifunction)
+                       break;
 
-               pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl);
-               if ((ctrl & acs_flags) != acs_flags)
-                       return false;
+               return pci_acs_flags_enabled(pdev, acs_flags);
        }
 
+       /*
+        * PCIe 3.0, 6.12.1.3 specifies no ACS capabilties are applicable
+        * to single function devices with the exception of downstream ports.
+        */
        return true;
 }
 
@@ -3118,19 +3180,17 @@ int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask)
 }
 EXPORT_SYMBOL(pci_set_dma_seg_boundary);
 
-static int pcie_flr(struct pci_dev *dev, int probe)
+/**
+ * pci_wait_for_pending_transaction - waits for pending transaction
+ * @dev: the PCI device to operate on
+ *
+ * Return 0 if transaction is pending 1 otherwise.
+ */
+int pci_wait_for_pending_transaction(struct pci_dev *dev)
 {
        int i;
-       u32 cap;
        u16 status;
 
-       pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap);
-       if (!(cap & PCI_EXP_DEVCAP_FLR))
-               return -ENOTTY;
-
-       if (probe)
-               return 0;
-
        /* Wait for Transaction Pending bit clean */
        for (i = 0; i < 4; i++) {
                if (i)
@@ -3138,13 +3198,27 @@ static int pcie_flr(struct pci_dev *dev, int probe)
 
                pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
                if (!(status & PCI_EXP_DEVSTA_TRPND))
-                       goto clear;
+                       return 1;
        }
 
-       dev_err(&dev->dev, "transaction is not cleared; "
-                       "proceeding with reset anyway\n");
+       return 0;
+}
+EXPORT_SYMBOL(pci_wait_for_pending_transaction);
+
+static int pcie_flr(struct pci_dev *dev, int probe)
+{
+       u32 cap;
+
+       pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap);
+       if (!(cap & PCI_EXP_DEVCAP_FLR))
+               return -ENOTTY;
+
+       if (probe)
+               return 0;
+
+       if (!pci_wait_for_pending_transaction(dev))
+               dev_err(&dev->dev, "transaction is not cleared; proceeding with reset anyway\n");
 
-clear:
        pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
 
        msleep(100);
@@ -3235,9 +3309,42 @@ static int pci_pm_reset(struct pci_dev *dev, int probe)
        return 0;
 }
 
-static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
+/**
+ * pci_reset_bridge_secondary_bus - Reset the secondary bus on a PCI bridge.
+ * @dev: Bridge device
+ *
+ * Use the bridge control register to assert reset on the secondary bus.
+ * Devices on the secondary bus are left in power-on state.
+ */
+void pci_reset_bridge_secondary_bus(struct pci_dev *dev)
 {
        u16 ctrl;
+
+       pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl);
+       ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
+       pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
+       /*
+        * PCI spec v3.0 7.6.4.2 requires minimum Trst of 1ms.  Double
+        * this to 2ms to ensure that we meet the minium requirement.
+        */
+       msleep(2);
+
+       ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
+       pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
+
+       /*
+        * Trhfa for conventional PCI is 2^25 clock cycles.
+        * Assuming a minimum 33MHz clock this results in a 1s
+        * delay before we can consider subordinate devices to
+        * be re-initialized.  PCIe has some ways to shorten this,
+        * but we don't make use of them yet.
+        */
+       ssleep(1);
+}
+EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus);
+
+static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
+{
        struct pci_dev *pdev;
 
        if (pci_is_root_bus(dev->bus) || dev->subordinate || !dev->bus->self)
@@ -3250,18 +3357,40 @@ static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
        if (probe)
                return 0;
 
-       pci_read_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, &ctrl);
-       ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
-       pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl);
-       msleep(100);
-
-       ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
-       pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl);
-       msleep(100);
+       pci_reset_bridge_secondary_bus(dev->bus->self);
 
        return 0;
 }
 
+static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, int probe)
+{
+       int rc = -ENOTTY;
+
+       if (!hotplug || !try_module_get(hotplug->ops->owner))
+               return rc;
+
+       if (hotplug->ops->reset_slot)
+               rc = hotplug->ops->reset_slot(hotplug, probe);
+
+       module_put(hotplug->ops->owner);
+
+       return rc;
+}
+
+static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe)
+{
+       struct pci_dev *pdev;
+
+       if (dev->subordinate || !dev->slot)
+               return -ENOTTY;
+
+       list_for_each_entry(pdev, &dev->bus->devices, bus_list)
+               if (pdev != dev && pdev->slot == dev->slot)
+                       return -ENOTTY;
+
+       return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
+}
+
 static int __pci_dev_reset(struct pci_dev *dev, int probe)
 {
        int rc;
@@ -3284,27 +3413,65 @@ static int __pci_dev_reset(struct pci_dev *dev, int probe)
        if (rc != -ENOTTY)
                goto done;
 
+       rc = pci_dev_reset_slot_function(dev, probe);
+       if (rc != -ENOTTY)
+               goto done;
+
        rc = pci_parent_bus_reset(dev, probe);
 done:
        return rc;
 }
 
+static void pci_dev_lock(struct pci_dev *dev)
+{
+       pci_cfg_access_lock(dev);
+       /* block PM suspend, driver probe, etc. */
+       device_lock(&dev->dev);
+}
+
+static void pci_dev_unlock(struct pci_dev *dev)
+{
+       device_unlock(&dev->dev);
+       pci_cfg_access_unlock(dev);
+}
+
+static void pci_dev_save_and_disable(struct pci_dev *dev)
+{
+       /*
+        * Wake-up device prior to save.  PM registers default to D0 after
+        * reset and a simple register restore doesn't reliably return
+        * to a non-D0 state anyway.
+        */
+       pci_set_power_state(dev, PCI_D0);
+
+       pci_save_state(dev);
+       /*
+        * Disable the device by clearing the Command register, except for
+        * INTx-disable which is set.  This not only disables MMIO and I/O port
+        * BARs, but also prevents the device from being Bus Master, preventing
+        * DMA from the device including MSI/MSI-X interrupts.  For PCI 2.3
+        * compliant devices, INTx-disable prevents legacy interrupts.
+        */
+       pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
+}
+
+static void pci_dev_restore(struct pci_dev *dev)
+{
+       pci_restore_state(dev);
+}
+
 static int pci_dev_reset(struct pci_dev *dev, int probe)
 {
        int rc;
 
-       if (!probe) {
-               pci_cfg_access_lock(dev);
-               /* block PM suspend, driver probe, etc. */
-               device_lock(&dev->dev);
-       }
+       if (!probe)
+               pci_dev_lock(dev);
 
        rc = __pci_dev_reset(dev, probe);
 
-       if (!probe) {
-               device_unlock(&dev->dev);
-               pci_cfg_access_unlock(dev);
-       }
+       if (!probe)
+               pci_dev_unlock(dev);
+
        return rc;
 }
 /**
@@ -3395,22 +3562,249 @@ int pci_reset_function(struct pci_dev *dev)
        if (rc)
                return rc;
 
-       pci_save_state(dev);
-
-       /*
-        * both INTx and MSI are disabled after the Interrupt Disable bit
-        * is set and the Bus Master bit is cleared.
-        */
-       pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
+       pci_dev_save_and_disable(dev);
 
        rc = pci_dev_reset(dev, 0);
 
-       pci_restore_state(dev);
+       pci_dev_restore(dev);
 
        return rc;
 }
 EXPORT_SYMBOL_GPL(pci_reset_function);
 
+/* Lock devices from the top of the tree down */
+static void pci_bus_lock(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               pci_dev_lock(dev);
+               if (dev->subordinate)
+                       pci_bus_lock(dev->subordinate);
+       }
+}
+
+/* Unlock devices from the bottom of the tree up */
+static void pci_bus_unlock(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               if (dev->subordinate)
+                       pci_bus_unlock(dev->subordinate);
+               pci_dev_unlock(dev);
+       }
+}
+
+/* Lock devices from the top of the tree down */
+static void pci_slot_lock(struct pci_slot *slot)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+               if (!dev->slot || dev->slot != slot)
+                       continue;
+               pci_dev_lock(dev);
+               if (dev->subordinate)
+                       pci_bus_lock(dev->subordinate);
+       }
+}
+
+/* Unlock devices from the bottom of the tree up */
+static void pci_slot_unlock(struct pci_slot *slot)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+               if (!dev->slot || dev->slot != slot)
+                       continue;
+               if (dev->subordinate)
+                       pci_bus_unlock(dev->subordinate);
+               pci_dev_unlock(dev);
+       }
+}
+
+/* Save and disable devices from the top of the tree down */
+static void pci_bus_save_and_disable(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               pci_dev_save_and_disable(dev);
+               if (dev->subordinate)
+                       pci_bus_save_and_disable(dev->subordinate);
+       }
+}
+
+/*
+ * Restore devices from top of the tree down - parent bridges need to be
+ * restored before we can get to subordinate devices.
+ */
+static void pci_bus_restore(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               pci_dev_restore(dev);
+               if (dev->subordinate)
+                       pci_bus_restore(dev->subordinate);
+       }
+}
+
+/* Save and disable devices from the top of the tree down */
+static void pci_slot_save_and_disable(struct pci_slot *slot)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+               if (!dev->slot || dev->slot != slot)
+                       continue;
+               pci_dev_save_and_disable(dev);
+               if (dev->subordinate)
+                       pci_bus_save_and_disable(dev->subordinate);
+       }
+}
+
+/*
+ * Restore devices from top of the tree down - parent bridges need to be
+ * restored before we can get to subordinate devices.
+ */
+static void pci_slot_restore(struct pci_slot *slot)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+               if (!dev->slot || dev->slot != slot)
+                       continue;
+               pci_dev_restore(dev);
+               if (dev->subordinate)
+                       pci_bus_restore(dev->subordinate);
+       }
+}
+
+static int pci_slot_reset(struct pci_slot *slot, int probe)
+{
+       int rc;
+
+       if (!slot)
+               return -ENOTTY;
+
+       if (!probe)
+               pci_slot_lock(slot);
+
+       might_sleep();
+
+       rc = pci_reset_hotplug_slot(slot->hotplug, probe);
+
+       if (!probe)
+               pci_slot_unlock(slot);
+
+       return rc;
+}
+
+/**
+ * pci_probe_reset_slot - probe whether a PCI slot can be reset
+ * @slot: PCI slot to probe
+ *
+ * Return 0 if slot can be reset, negative if a slot reset is not supported.
+ */
+int pci_probe_reset_slot(struct pci_slot *slot)
+{
+       return pci_slot_reset(slot, 1);
+}
+EXPORT_SYMBOL_GPL(pci_probe_reset_slot);
+
+/**
+ * pci_reset_slot - reset a PCI slot
+ * @slot: PCI slot to reset
+ *
+ * A PCI bus may host multiple slots, each slot may support a reset mechanism
+ * independent of other slots.  For instance, some slots may support slot power
+ * control.  In the case of a 1:1 bus to slot architecture, this function may
+ * wrap the bus reset to avoid spurious slot related events such as hotplug.
+ * Generally a slot reset should be attempted before a bus reset.  All of the
+ * function of the slot and any subordinate buses behind the slot are reset
+ * through this function.  PCI config space of all devices in the slot and
+ * behind the slot is saved before and restored after reset.
+ *
+ * Return 0 on success, non-zero on error.
+ */
+int pci_reset_slot(struct pci_slot *slot)
+{
+       int rc;
+
+       rc = pci_slot_reset(slot, 1);
+       if (rc)
+               return rc;
+
+       pci_slot_save_and_disable(slot);
+
+       rc = pci_slot_reset(slot, 0);
+
+       pci_slot_restore(slot);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(pci_reset_slot);
+
+static int pci_bus_reset(struct pci_bus *bus, int probe)
+{
+       if (!bus->self)
+               return -ENOTTY;
+
+       if (probe)
+               return 0;
+
+       pci_bus_lock(bus);
+
+       might_sleep();
+
+       pci_reset_bridge_secondary_bus(bus->self);
+
+       pci_bus_unlock(bus);
+
+       return 0;
+}
+
+/**
+ * pci_probe_reset_bus - probe whether a PCI bus can be reset
+ * @bus: PCI bus to probe
+ *
+ * Return 0 if bus can be reset, negative if a bus reset is not supported.
+ */
+int pci_probe_reset_bus(struct pci_bus *bus)
+{
+       return pci_bus_reset(bus, 1);
+}
+EXPORT_SYMBOL_GPL(pci_probe_reset_bus);
+
+/**
+ * pci_reset_bus - reset a PCI bus
+ * @bus: top level PCI bus to reset
+ *
+ * Do a bus reset on the given bus and any subordinate buses, saving
+ * and restoring state of all devices.
+ *
+ * Return 0 on success, non-zero on error.
+ */
+int pci_reset_bus(struct pci_bus *bus)
+{
+       int rc;
+
+       rc = pci_bus_reset(bus, 1);
+       if (rc)
+               return rc;
+
+       pci_bus_save_and_disable(bus);
+
+       rc = pci_bus_reset(bus, 0);
+
+       pci_bus_restore(bus);
+
+       return rc;
+}
+EXPORT_SYMBOL_GPL(pci_reset_bus);
+
 /**
  * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
  * @dev: PCI device to query
index d1182c4..816c297 100644 (file)
@@ -151,7 +151,7 @@ static inline int pci_no_d1d2(struct pci_dev *dev)
 
 }
 extern struct device_attribute pci_dev_attrs[];
-extern struct device_attribute pcibus_dev_attrs[];
+extern const struct attribute_group *pcibus_groups[];
 extern struct device_type pci_dev_type;
 extern struct bus_attribute pci_bus_attrs[];
 
index 569f82f..a82e70a 100644 (file)
@@ -2,7 +2,7 @@
 # PCI Express Port Bus Configuration
 #
 config PCIEPORTBUS
-       bool "PCI Express support"
+       bool "PCI Express Port Bus support"
        depends on PCI
        help
          This automatically enables PCI Express Port Bus support. Users can
index 76ef634..0bf82a2 100644 (file)
@@ -352,7 +352,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
        reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
        pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
 
-       aer_do_secondary_bus_reset(dev);
+       pci_reset_bridge_secondary_bus(dev);
        dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
 
        /* Clear Root Error Status */
index 90ea3e8..84420b7 100644 (file)
@@ -106,7 +106,6 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
 }
 
 extern struct bus_type pcie_port_bus_type;
-void aer_do_secondary_bus_reset(struct pci_dev *dev);
 int aer_init(struct pcie_device *dev);
 void aer_isr(struct work_struct *work);
 void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
index 8b68ae5..85ca36f 100644 (file)
@@ -366,39 +366,6 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
        return result_data.result;
 }
 
-/**
- * aer_do_secondary_bus_reset - perform secondary bus reset
- * @dev: pointer to bridge's pci_dev data structure
- *
- * Invoked when performing link reset at Root Port or Downstream Port.
- */
-void aer_do_secondary_bus_reset(struct pci_dev *dev)
-{
-       u16 p2p_ctrl;
-
-       /* Assert Secondary Bus Reset */
-       pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
-       p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
-       pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
-
-       /*
-        * we should send hot reset message for 2ms to allow it time to
-        * propagate to all downstream ports
-        */
-       msleep(2);
-
-       /* De-assert Secondary Bus Reset */
-       p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
-       pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
-
-       /*
-        * System software must wait for at least 100ms from the end
-        * of a reset of one or more device before it is permitted
-        * to issue Configuration Requests to those devices.
-        */
-       msleep(200);
-}
-
 /**
  * default_reset_link - default reset function
  * @dev: pointer to pci_dev data structure
@@ -408,7 +375,7 @@ void aer_do_secondary_bus_reset(struct pci_dev *dev)
  */
 static pci_ers_result_t default_reset_link(struct pci_dev *dev)
 {
-       aer_do_secondary_bus_reset(dev);
+       pci_reset_bridge_secondary_bus(dev);
        dev_printk(KERN_DEBUG, &dev->dev, "downstream link has been reset\n");
        return PCI_ERS_RESULT_RECOVERED;
 }
index 85c114c..4ab388a 100644 (file)
@@ -96,7 +96,7 @@ static void release_pcibus_dev(struct device *dev)
 static struct class pcibus_class = {
        .name           = "pci_bus",
        .dev_release    = &release_pcibus_dev,
-       .dev_attrs      = pcibus_dev_attrs,
+       .dev_groups     = pcibus_groups,
 };
 
 static int __init pcibus_class_init(void)
index e85d230..f6c31fa 100644 (file)
@@ -3126,9 +3126,6 @@ static int reset_intel_generic_dev(struct pci_dev *dev, int probe)
 
 static int reset_intel_82599_sfp_virtfn(struct pci_dev *dev, int probe)
 {
-       int i;
-       u16 status;
-
        /*
         * http://www.intel.com/content/dam/doc/datasheet/82599-10-gbe-controller-datasheet.pdf
         *
@@ -3140,20 +3137,9 @@ static int reset_intel_82599_sfp_virtfn(struct pci_dev *dev, int probe)
        if (probe)
                return 0;
 
-       /* Wait for Transaction Pending bit clean */
-       for (i = 0; i < 4; i++) {
-               if (i)
-                       msleep((1 << (i - 1)) * 100);
-
-               pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
-               if (!(status & PCI_EXP_DEVSTA_TRPND))
-                       goto clear;
-       }
-
-       dev_err(&dev->dev, "transaction is not cleared; "
-                       "proceeding with reset anyway\n");
+       if (!pci_wait_for_pending_transaction(dev))
+               dev_err(&dev->dev, "transaction is not cleared; proceeding with reset anyway\n");
 
-clear:
        pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
 
        msleep(100);
@@ -3208,6 +3194,83 @@ reset_complete:
        return 0;
 }
 
+/*
+ * Device-specific reset method for Chelsio T4-based adapters.
+ */
+static int reset_chelsio_generic_dev(struct pci_dev *dev, int probe)
+{
+       u16 old_command;
+       u16 msix_flags;
+
+       /*
+        * If this isn't a Chelsio T4-based device, return -ENOTTY indicating
+        * that we have no device-specific reset method.
+        */
+       if ((dev->device & 0xf000) != 0x4000)
+               return -ENOTTY;
+
+       /*
+        * If this is the "probe" phase, return 0 indicating that we can
+        * reset this device.
+        */
+       if (probe)
+               return 0;
+
+       /*
+        * T4 can wedge if there are DMAs in flight within the chip and Bus
+        * Master has been disabled.  We need to have it on till the Function
+        * Level Reset completes.  (BUS_MASTER is disabled in
+        * pci_reset_function()).
+        */
+       pci_read_config_word(dev, PCI_COMMAND, &old_command);
+       pci_write_config_word(dev, PCI_COMMAND,
+                             old_command | PCI_COMMAND_MASTER);
+
+       /*
+        * Perform the actual device function reset, saving and restoring
+        * configuration information around the reset.
+        */
+       pci_save_state(dev);
+
+       /*
+        * T4 also suffers a Head-Of-Line blocking problem if MSI-X interrupts
+        * are disabled when an MSI-X interrupt message needs to be delivered.
+        * So we briefly re-enable MSI-X interrupts for the duration of the
+        * FLR.  The pci_restore_state() below will restore the original
+        * MSI-X state.
+        */
+       pci_read_config_word(dev, dev->msix_cap+PCI_MSIX_FLAGS, &msix_flags);
+       if ((msix_flags & PCI_MSIX_FLAGS_ENABLE) == 0)
+               pci_write_config_word(dev, dev->msix_cap+PCI_MSIX_FLAGS,
+                                     msix_flags |
+                                     PCI_MSIX_FLAGS_ENABLE |
+                                     PCI_MSIX_FLAGS_MASKALL);
+
+       /*
+        * Start of pcie_flr() code sequence.  This reset code is a copy of
+        * the guts of pcie_flr() because that's not an exported function.
+        */
+
+       if (!pci_wait_for_pending_transaction(dev))
+               dev_err(&dev->dev, "transaction is not cleared; proceeding with reset anyway\n");
+
+       pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
+       msleep(100);
+
+       /*
+        * End of pcie_flr() code sequence.
+        */
+
+       /*
+        * Restore the configuration information (BAR values, etc.) including
+        * the original PCI Configuration Space Command word, and return
+        * success.
+        */
+       pci_restore_state(dev);
+       pci_write_config_word(dev, PCI_COMMAND, old_command);
+       return 0;
+}
+
 #define PCI_DEVICE_ID_INTEL_82599_SFP_VF   0x10ed
 #define PCI_DEVICE_ID_INTEL_IVB_M_VGA      0x0156
 #define PCI_DEVICE_ID_INTEL_IVB_M2_VGA     0x0166
@@ -3221,6 +3284,8 @@ static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
                reset_ivb_igd },
        { PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
                reset_intel_generic_dev },
+       { PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID,
+               reset_chelsio_generic_dev },
        { 0 }
 };
 
@@ -3295,11 +3360,61 @@ struct pci_dev *pci_get_dma_source(struct pci_dev *dev)
        return pci_dev_get(dev);
 }
 
+/*
+ * AMD has indicated that the devices below do not support peer-to-peer
+ * in any system where they are found in the southbridge with an AMD
+ * IOMMU in the system.  Multifunction devices that do not support
+ * peer-to-peer between functions can claim to support a subset of ACS.
+ * Such devices effectively enable request redirect (RR) and completion
+ * redirect (CR) since all transactions are redirected to the upstream
+ * root complex.
+ *
+ * http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/94086
+ * http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/94102
+ * http://permalink.gmane.org/gmane.comp.emulators.kvm.devel/99402
+ *
+ * 1002:4385 SBx00 SMBus Controller
+ * 1002:439c SB7x0/SB8x0/SB9x0 IDE Controller
+ * 1002:4383 SBx00 Azalia (Intel HDA)
+ * 1002:439d SB7x0/SB8x0/SB9x0 LPC host controller
+ * 1002:4384 SBx00 PCI to PCI Bridge
+ * 1002:4399 SB7x0/SB8x0/SB9x0 USB OHCI2 Controller
+ */
+static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags)
+{
+#ifdef CONFIG_ACPI
+       struct acpi_table_header *header = NULL;
+       acpi_status status;
+
+       /* Targeting multifunction devices on the SB (appears on root bus) */
+       if (!dev->multifunction || !pci_is_root_bus(dev->bus))
+               return -ENODEV;
+
+       /* The IVRS table describes the AMD IOMMU */
+       status = acpi_get_table("IVRS", 0, &header);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       /* Filter out flags not applicable to multifunction */
+       acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC | PCI_ACS_DT);
+
+       return acs_flags & ~(PCI_ACS_RR | PCI_ACS_CR) ? 0 : 1;
+#else
+       return -ENODEV;
+#endif
+}
+
 static const struct pci_dev_acs_enabled {
        u16 vendor;
        u16 device;
        int (*acs_enabled)(struct pci_dev *dev, u16 acs_flags);
 } pci_dev_acs_enabled[] = {
+       { PCI_VENDOR_ID_ATI, 0x4385, pci_quirk_amd_sb_acs },
+       { PCI_VENDOR_ID_ATI, 0x439c, pci_quirk_amd_sb_acs },
+       { PCI_VENDOR_ID_ATI, 0x4383, pci_quirk_amd_sb_acs },
+       { PCI_VENDOR_ID_ATI, 0x439d, pci_quirk_amd_sb_acs },
+       { PCI_VENDOR_ID_ATI, 0x4384, pci_quirk_amd_sb_acs },
+       { PCI_VENDOR_ID_ATI, 0x4399, pci_quirk_amd_sb_acs },
        { 0 }
 };
 
index 94b777d..9be3595 100644 (file)
@@ -747,14 +747,14 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
 {
        struct pci_dev *dev;
        struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);
-       unsigned long size = 0, size0 = 0, size1 = 0;
+       resource_size_t size = 0, size0 = 0, size1 = 0;
        resource_size_t children_add_size = 0;
-       resource_size_t min_align, io_align, align;
+       resource_size_t min_align, align;
 
        if (!b_res)
                return;
 
-       io_align = min_align = window_alignment(bus, IORESOURCE_IO);
+       min_align = window_alignment(bus, IORESOURCE_IO);
        list_for_each_entry(dev, &bus->devices, bus_list) {
                int i;
 
@@ -781,9 +781,6 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
                }
        }
 
-       if (min_align > io_align)
-               min_align = io_align;
-
        size0 = calculate_iosize(size, min_size, size1,
                        resource_size(b_res), min_align);
        if (children_add_size > add_size)
@@ -807,8 +804,9 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
                add_to_list(realloc_head, bus->self, b_res, size1-size0,
                            min_align);
                dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window "
-                                "%pR to %pR add_size %lx\n", b_res,
-                                &bus->busn_res, size1-size0);
+                                "%pR to %pR add_size %llx\n", b_res,
+                                &bus->busn_res,
+                                (unsigned long long)size1-size0);
        }
 }
 
@@ -838,6 +836,8 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns,
  * pbus_size_mem() - size the memory window of a given bus
  *
  * @bus : the bus
+ * @mask: mask the resource flag, then compare it with type
+ * @type: the type of free resource from bridge
  * @min_size : the minimum memory window that must to be allocated
  * @add_size : additional optional memory window
  * @realloc_head : track the additional memory window on this list
@@ -1300,15 +1300,12 @@ static void pci_bus_dump_resources(struct pci_bus *bus)
 static int pci_bus_get_depth(struct pci_bus *bus)
 {
        int depth = 0;
-       struct pci_dev *dev;
+       struct pci_bus *child_bus;
 
-       list_for_each_entry(dev, &bus->devices, bus_list) {
+       list_for_each_entry(child_bus, &bus->children, node){
                int ret;
-               struct pci_bus *b = dev->subordinate;
-               if (!b)
-                       continue;
 
-               ret = pci_bus_get_depth(b);
+               ret = pci_bus_get_depth(child_bus);
                if (ret + 1 > depth)
                        depth = ret + 1;
        }
index e494c90..82d1c78 100644 (file)
@@ -914,6 +914,7 @@ bool pci_check_and_unmask_intx(struct pci_dev *dev);
 void pci_msi_off(struct pci_dev *dev);
 int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size);
 int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask);
+int pci_wait_for_pending_transaction(struct pci_dev *dev);
 int pcix_get_max_mmrbc(struct pci_dev *dev);
 int pcix_get_mmrbc(struct pci_dev *dev);
 int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc);
@@ -924,6 +925,11 @@ int pcie_set_mps(struct pci_dev *dev, int mps);
 int __pci_reset_function(struct pci_dev *dev);
 int __pci_reset_function_locked(struct pci_dev *dev);
 int pci_reset_function(struct pci_dev *dev);
+int pci_probe_reset_slot(struct pci_slot *slot);
+int pci_reset_slot(struct pci_slot *slot);
+int pci_probe_reset_bus(struct pci_bus *bus);
+int pci_reset_bus(struct pci_bus *bus);
+void pci_reset_bridge_secondary_bus(struct pci_dev *dev);
 void pci_update_resource(struct pci_dev *dev, int resno);
 int __must_check pci_assign_resource(struct pci_dev *dev, int i);
 int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
index 8db71dc..bd32109 100644 (file)
@@ -63,6 +63,9 @@ enum pcie_link_width {
  * @get_adapter_status: Called to get see if an adapter is present in the slot or not.
  *     If this field is NULL, the value passed in the struct hotplug_slot_info
  *     will be used when this value is requested by a user.
+ * @reset_slot: Optional interface to allow override of a bus reset for the
+ *     slot for cases where a secondary bus reset can result in spurious
+ *     hotplug events or where a slot can be reset independent of the bus.
  *
  * The table of function pointers that is passed to the hotplug pci core by a
  * hotplug pci driver.  These functions are called by the hotplug pci core when
@@ -80,6 +83,7 @@ struct hotplug_slot_ops {
        int (*get_attention_status)     (struct hotplug_slot *slot, u8 *value);
        int (*get_latch_status)         (struct hotplug_slot *slot, u8 *value);
        int (*get_adapter_status)       (struct hotplug_slot *slot, u8 *value);
+       int (*reset_slot)               (struct hotplug_slot *slot, int probe);
 };
 
 /**