aboutsummaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
authorMark Brown2017-04-25 16:35:35 +0100
committerMark Brown2017-04-25 16:35:35 +0100
commit9095bf25ea08135a5b74875dd0e3eeaddc4218a0 (patch)
treea4e6976eab0e7a5b6258281077cd5853c7e69514 /drivers/pci
parentcdf4275e957c6bad3756e98942341667f1d7de7d (diff)
parentc1ae3cfa0e89fa1a7ecc4c99031f5e9ae99d9201 (diff)
Merge tag 'v4.11-rc1' into regulator-arizona
Linux 4.11-rc1
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/Kconfig1
-rw-r--r--drivers/pci/access.c23
-rw-r--r--drivers/pci/bus.c2
-rw-r--r--drivers/pci/dwc/Kconfig132
-rw-r--r--drivers/pci/dwc/Makefile24
-rw-r--r--drivers/pci/dwc/pci-dra7xx.c (renamed from drivers/pci/host/pci-dra7xx.c)247
-rw-r--r--drivers/pci/dwc/pci-exynos.c752
-rw-r--r--drivers/pci/dwc/pci-imx6.c (renamed from drivers/pci/host/pci-imx6.c)163
-rw-r--r--drivers/pci/dwc/pci-keystone-dw.c (renamed from drivers/pci/host/pci-keystone-dw.c)87
-rw-r--r--drivers/pci/dwc/pci-keystone.c (renamed from drivers/pci/host/pci-keystone.c)58
-rw-r--r--drivers/pci/dwc/pci-keystone.h (renamed from drivers/pci/host/pci-keystone.h)4
-rw-r--r--drivers/pci/dwc/pci-layerscape.c (renamed from drivers/pci/host/pci-layerscape.c)120
-rw-r--r--drivers/pci/dwc/pcie-armada8k.c (renamed from drivers/pci/host/pcie-armada8k.c)89
-rw-r--r--drivers/pci/dwc/pcie-artpec6.c (renamed from drivers/pci/host/pcie-artpec6.c)52
-rw-r--r--drivers/pci/dwc/pcie-designware-host.c (renamed from drivers/pci/host/pcie-designware.c)445
-rw-r--r--drivers/pci/dwc/pcie-designware-plat.c (renamed from drivers/pci/host/pcie-designware-plat.c)31
-rw-r--r--drivers/pci/dwc/pcie-designware.c233
-rw-r--r--drivers/pci/dwc/pcie-designware.h198
-rw-r--r--drivers/pci/dwc/pcie-hisi.c399
-rw-r--r--drivers/pci/dwc/pcie-qcom.c (renamed from drivers/pci/host/pcie-qcom.c)250
-rw-r--r--drivers/pci/dwc/pcie-spear13xx.c (renamed from drivers/pci/host/pcie-spear13xx.c)93
-rw-r--r--drivers/pci/ecam.c12
-rw-r--r--drivers/pci/host/Kconfig129
-rw-r--r--drivers/pci/host/Makefile29
-rw-r--r--drivers/pci/host/pci-exynos.c629
-rw-r--r--drivers/pci/host/pci-host-common.c2
-rw-r--r--drivers/pci/host/pci-hyperv.c120
-rw-r--r--drivers/pci/host/pci-mvebu.c103
-rw-r--r--drivers/pci/host/pci-rcar-gen2.c2
-rw-r--r--drivers/pci/host/pci-tegra.c160
-rw-r--r--drivers/pci/host/pci-thunder-ecam.c9
-rw-r--r--drivers/pci/host/pci-thunder-pem.c119
-rw-r--r--drivers/pci/host/pci-versatile.c4
-rw-r--r--drivers/pci/host/pci-xgene-msi.c69
-rw-r--r--drivers/pci/host/pci-xgene.c127
-rw-r--r--drivers/pci/host/pcie-altera.c22
-rw-r--r--drivers/pci/host/pcie-designware.h86
-rw-r--r--drivers/pci/host/pcie-hisi.c229
-rw-r--r--drivers/pci/host/pcie-iproc-bcma.c1
-rw-r--r--drivers/pci/host/pcie-iproc-msi.c1
-rw-r--r--drivers/pci/host/pcie-iproc-platform.c35
-rw-r--r--drivers/pci/host/pcie-iproc.c954
-rw-r--r--drivers/pci/host/pcie-iproc.h45
-rw-r--r--drivers/pci/host/pcie-rcar.c14
-rw-r--r--drivers/pci/host/pcie-rockchip.c429
-rw-r--r--drivers/pci/host/pcie-xilinx-nwl.c14
-rw-r--r--drivers/pci/host/pcie-xilinx.c4
-rw-r--r--drivers/pci/host/vmd.c32
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c31
-rw-r--r--drivers/pci/hotplug/acpiphp_ibm.c4
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_core.c1
-rw-r--r--drivers/pci/hotplug/cpqphp.h2
-rw-r--r--drivers/pci/hotplug/cpqphp_core.c5
-rw-r--r--drivers/pci/hotplug/cpqphp_nvram.c2
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c12
-rw-r--r--drivers/pci/hotplug/pciehp.h2
-rw-r--r--drivers/pci/hotplug/pciehp_core.c9
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c2
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c21
-rw-r--r--drivers/pci/hotplug/pnv_php.c83
-rw-r--r--drivers/pci/hotplug/rpadlpar_core.c14
-rw-r--r--drivers/pci/hotplug/s390_pci_hpc.c7
-rw-r--r--drivers/pci/hotplug/shpchp.h2
-rw-r--r--drivers/pci/iov.c77
-rw-r--r--drivers/pci/msi.c227
-rw-r--r--drivers/pci/pci-acpi.c100
-rw-r--r--drivers/pci/pci-driver.c8
-rw-r--r--drivers/pci/pci-mid.c2
-rw-r--r--drivers/pci/pci-sysfs.c25
-rw-r--r--drivers/pci/pci.c138
-rw-r--r--drivers/pci/pci.h21
-rw-r--r--drivers/pci/pcie/Kconfig8
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c18
-rw-r--r--drivers/pci/pcie/aspm.c330
-rw-r--r--drivers/pci/pcie/pcie-dpc.c34
-rw-r--r--drivers/pci/pcie/pme.c41
-rw-r--r--drivers/pci/pcie/portdrv_core.c164
-rw-r--r--drivers/pci/pcie/portdrv_pci.c13
-rw-r--r--drivers/pci/probe.c293
-rw-r--r--drivers/pci/proc.c2
-rw-r--r--drivers/pci/quirks.c285
-rw-r--r--drivers/pci/remove.c2
-rw-r--r--drivers/pci/rom.c5
-rw-r--r--drivers/pci/setup-bus.c11
-rw-r--r--drivers/pci/setup-res.c48
-rw-r--r--drivers/pci/slot.c2
-rw-r--r--drivers/pci/syscall.c2
-rw-r--r--drivers/pci/xen-pcifront.c6
88 files changed, 5681 insertions, 3161 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 6555eb78d91c..df141420c902 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -132,4 +132,5 @@ config PCI_HYPERV
PCI devices from a PCI backend to support PCI driver domains.
source "drivers/pci/hotplug/Kconfig"
+source "drivers/pci/dwc/Kconfig"
source "drivers/pci/host/Kconfig"
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index d11cdbb8fba3..8b7382705bf2 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -1,7 +1,7 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/module.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/wait.h>
@@ -142,10 +142,22 @@ int pci_generic_config_write32(struct pci_bus *bus, unsigned int devfn,
if (size == 4) {
writel(val, addr);
return PCIBIOS_SUCCESSFUL;
- } else {
- mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
}
+ /*
+ * In general, hardware that supports only 32-bit writes on PCI is
+ * not spec-compliant. For example, software may perform a 16-bit
+ * write. If the hardware only supports 32-bit accesses, we must
+ * do a 32-bit read, merge in the 16 bits we intend to write,
+ * followed by a 32-bit write. If the 16 bits we *don't* intend to
+ * write happen to have any RW1C (write-one-to-clear) bits set, we
+ * just inadvertently cleared something we shouldn't have.
+ */
+ dev_warn_ratelimited(&bus->dev, "%d-byte config write to %04x:%02x:%02x.%d offset %#x may corrupt adjacent RW1C bits\n",
+ size, pci_domain_nr(bus), bus->number,
+ PCI_SLOT(devfn), PCI_FUNC(devfn), where);
+
+ mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
tmp = readl(addr) & mask;
tmp |= val << ((where & 0x3) * 8);
writel(tmp, addr);
@@ -355,7 +367,7 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size)
static int pci_vpd_wait(struct pci_dev *dev)
{
struct pci_vpd *vpd = dev->vpd;
- unsigned long timeout = jiffies + msecs_to_jiffies(50);
+ unsigned long timeout = jiffies + msecs_to_jiffies(125);
unsigned long max_sleep = 16;
u16 status;
int ret;
@@ -672,8 +684,9 @@ void pci_cfg_access_unlock(struct pci_dev *dev)
WARN_ON(!dev->block_cfg_access);
dev->block_cfg_access = 0;
- wake_up_all(&pci_cfg_wait);
raw_spin_unlock_irqrestore(&pci_lock, flags);
+
+ wake_up_all(&pci_cfg_wait);
}
EXPORT_SYMBOL_GPL(pci_cfg_access_unlock);
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index c288e5a52575..bc56cf19afd3 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -320,7 +320,7 @@ void pci_bus_add_device(struct pci_dev *dev)
pci_fixup_device(pci_fixup_final, dev);
pci_create_sysfs_dev_files(dev);
pci_proc_attach_device(dev);
- pci_bridge_d3_device_changed(dev);
+ pci_bridge_d3_update(dev);
dev->match_driver = true;
retval = device_attach(&dev->dev);
diff --git a/drivers/pci/dwc/Kconfig b/drivers/pci/dwc/Kconfig
new file mode 100644
index 000000000000..dfb8a69afc28
--- /dev/null
+++ b/drivers/pci/dwc/Kconfig
@@ -0,0 +1,132 @@
+menu "DesignWare PCI Core Support"
+
+config PCIE_DW
+ bool
+
+config PCIE_DW_HOST
+ bool
+ depends on PCI
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIE_DW
+
+config PCI_DRA7XX
+ bool "TI DRA7xx PCIe controller"
+ depends on PCI
+ depends on OF && HAS_IOMEM && TI_PIPE3
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIE_DW_HOST
+ help
+ Enables support for the PCIe controller in the DRA7xx SoC. There
+ are two instances of PCIe controller in DRA7xx. This controller can
+ act both as EP and RC. This reuses the Designware core.
+
+config PCIE_DW_PLAT
+ bool "Platform bus based DesignWare PCIe Controller"
+ depends on PCI
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIE_DW_HOST
+ ---help---
+ This selects the DesignWare PCIe controller support. Select this if
+ you have a PCIe controller on Platform bus.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
+config PCI_EXYNOS
+ bool "Samsung Exynos PCIe controller"
+ depends on PCI
+ depends on SOC_EXYNOS5440
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIEPORTBUS
+ select PCIE_DW_HOST
+
+config PCI_IMX6
+ bool "Freescale i.MX6 PCIe controller"
+ depends on PCI
+ depends on SOC_IMX6Q
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIEPORTBUS
+ select PCIE_DW_HOST
+
+config PCIE_SPEAR13XX
+ bool "STMicroelectronics SPEAr PCIe controller"
+ depends on PCI
+ depends on ARCH_SPEAR13XX
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIEPORTBUS
+ select PCIE_DW_HOST
+ help
+ Say Y here if you want PCIe support on SPEAr13XX SoCs.
+
+config PCI_KEYSTONE
+ bool "TI Keystone PCIe controller"
+ depends on PCI
+ depends on ARCH_KEYSTONE
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIEPORTBUS
+ select PCIE_DW_HOST
+ help
+ Say Y here if you want to enable PCI controller support on Keystone
+ SoCs. The PCI controller on Keystone is based on Designware hardware
+ and therefore the driver re-uses the Designware core functions to
+ implement the driver.
+
+config PCI_LAYERSCAPE
+ bool "Freescale Layerscape PCIe controller"
+ depends on PCI
+ depends on OF && (ARM || ARCH_LAYERSCAPE)
+ depends on PCI_MSI_IRQ_DOMAIN
+ select MFD_SYSCON
+ select PCIE_DW_HOST
+ help
+ Say Y here if you want PCIe controller support on Layerscape SoCs.
+
+config PCI_HISI
+ depends on OF && ARM64
+ bool "HiSilicon Hip05 and Hip06 SoCs PCIe controllers"
+ depends on PCI
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIEPORTBUS
+ select PCIE_DW_HOST
+ help
+ Say Y here if you want PCIe controller support on HiSilicon
+ Hip05 and Hip06 SoCs
+
+config PCIE_QCOM
+ bool "Qualcomm PCIe controller"
+ depends on PCI
+ depends on ARCH_QCOM && OF
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIEPORTBUS
+ select PCIE_DW_HOST
+ help
+ Say Y here to enable PCIe controller support on Qualcomm SoCs. The
+ PCIe controller uses the Designware core plus Qualcomm-specific
+ hardware wrappers.
+
+config PCIE_ARMADA_8K
+ bool "Marvell Armada-8K PCIe controller"
+ depends on PCI
+ depends on ARCH_MVEBU
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIEPORTBUS
+ select PCIE_DW_HOST
+ help
+ Say Y here if you want to enable PCIe controller support on
+ Armada-8K SoCs. The PCIe controller on Armada-8K is based on
+ Designware hardware and therefore the driver re-uses the
+ Designware core functions to implement the driver.
+
+config PCIE_ARTPEC6
+ bool "Axis ARTPEC-6 PCIe controller"
+ depends on PCI
+ depends on MACH_ARTPEC6
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIEPORTBUS
+ select PCIE_DW_HOST
+ help
+ Say Y here to enable PCIe controller support on Axis ARTPEC-6
+ SoCs. This PCIe controller uses the DesignWare core.
+
+endmenu
diff --git a/drivers/pci/dwc/Makefile b/drivers/pci/dwc/Makefile
new file mode 100644
index 000000000000..a2df13c28798
--- /dev/null
+++ b/drivers/pci/dwc/Makefile
@@ -0,0 +1,24 @@
+obj-$(CONFIG_PCIE_DW) += pcie-designware.o
+obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o
+obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
+obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
+obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
+obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
+obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
+obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
+obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
+obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
+obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
+obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
+
+# The following drivers are for devices that use the generic ACPI
+# pci_root.c driver but don't support standard ECAM config access.
+# They contain MCFG quirks to replace the generic ECAM accessors with
+# device-specific ones that are shared with the DT driver.
+
+# The ACPI driver is generic and should not require driver-specific
+# config options to be enabled, so we always build these drivers on
+# ARM64 and use internal ifdefs to only build the pieces we need
+# depending on whether ACPI, the DT driver, or both are enabled.
+
+obj-$(CONFIG_ARM64) += pcie-hisi.o
diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c
index 9595fad63f6f..0984baff07e3 100644
--- a/drivers/pci/host/pci-dra7xx.c
+++ b/drivers/pci/dwc/pci-dra7xx.c
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_gpio.h>
+#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
@@ -63,14 +64,18 @@
#define LINK_UP BIT(16)
#define DRA7XX_CPU_TO_BUS_ADDR 0x0FFFFFFF
+#define EXP_CAP_ID_OFFSET 0x70
+
struct dra7xx_pcie {
- struct pcie_port pp;
+ struct dw_pcie *pci;
void __iomem *base; /* DT ti_conf */
int phy_count; /* DT phy-names count */
struct phy **phy;
+ int link_gen;
+ struct irq_domain *irq_domain;
};
-#define to_dra7xx_pcie(x) container_of((x), struct dra7xx_pcie, pp)
+#define to_dra7xx_pcie(x) dev_get_drvdata((x)->dev)
static inline u32 dra7xx_pcie_readl(struct dra7xx_pcie *pcie, u32 offset)
{
@@ -83,9 +88,9 @@ static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset,
writel(value, pcie->base + offset);
}
-static int dra7xx_pcie_link_up(struct pcie_port *pp)
+static int dra7xx_pcie_link_up(struct dw_pcie *pci)
{
- struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
+ struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
u32 reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS);
return !!(reg & LINK_UP);
@@ -93,20 +98,41 @@ static int dra7xx_pcie_link_up(struct pcie_port *pp)
static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx)
{
- struct pcie_port *pp = &dra7xx->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = dra7xx->pci;
+ struct device *dev = pci->dev;
u32 reg;
+ u32 exp_cap_off = EXP_CAP_ID_OFFSET;
- if (dw_pcie_link_up(pp)) {
+ if (dw_pcie_link_up(pci)) {
dev_err(dev, "link is already up\n");
return 0;
}
+ if (dra7xx->link_gen == 1) {
+ dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCAP,
+ 4, &reg);
+ if ((reg & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
+ reg &= ~((u32)PCI_EXP_LNKCAP_SLS);
+ reg |= PCI_EXP_LNKCAP_SLS_2_5GB;
+ dw_pcie_write(pci->dbi_base + exp_cap_off +
+ PCI_EXP_LNKCAP, 4, reg);
+ }
+
+ dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2,
+ 2, &reg);
+ if ((reg & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
+ reg &= ~((u32)PCI_EXP_LNKCAP_SLS);
+ reg |= PCI_EXP_LNKCAP_SLS_2_5GB;
+ dw_pcie_write(pci->dbi_base + exp_cap_off +
+ PCI_EXP_LNKCTL2, 2, reg);
+ }
+ }
+
reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD);
reg |= LTSSM_EN;
dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
- return dw_pcie_wait_for_link(pp);
+ return dw_pcie_wait_for_link(pci);
}
static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx)
@@ -117,19 +143,14 @@ static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx)
PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, INTERRUPTS);
dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI,
~LEG_EP_INTERRUPTS & ~MSI);
-
- if (IS_ENABLED(CONFIG_PCI_MSI))
- dra7xx_pcie_writel(dra7xx,
- PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, MSI);
- else
- dra7xx_pcie_writel(dra7xx,
- PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI,
- LEG_EP_INTERRUPTS);
+ dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI,
+ MSI | LEG_EP_INTERRUPTS);
}
static void dra7xx_pcie_host_init(struct pcie_port *pp)
{
- struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR;
pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR;
@@ -139,13 +160,11 @@ static void dra7xx_pcie_host_init(struct pcie_port *pp)
dw_pcie_setup_rc(pp);
dra7xx_pcie_establish_link(dra7xx);
- if (IS_ENABLED(CONFIG_PCI_MSI))
- dw_pcie_msi_init(pp);
+ dw_pcie_msi_init(pp);
dra7xx_pcie_enable_interrupts(dra7xx);
}
-static struct pcie_host_ops dra7xx_pcie_host_ops = {
- .link_up = dra7xx_pcie_link_up,
+static struct dw_pcie_host_ops dra7xx_pcie_host_ops = {
.host_init = dra7xx_pcie_host_init,
};
@@ -164,7 +183,9 @@ static const struct irq_domain_ops intx_domain_ops = {
static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp)
{
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct device *dev = pci->dev;
+ struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
struct device_node *node = dev->of_node;
struct device_node *pcie_intc_node = of_get_next_child(node, NULL);
@@ -173,9 +194,9 @@ static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp)
return -ENODEV;
}
- pp->irq_domain = irq_domain_add_linear(pcie_intc_node, 4,
- &intx_domain_ops, pp);
- if (!pp->irq_domain) {
+ dra7xx->irq_domain = irq_domain_add_linear(pcie_intc_node, 4,
+ &intx_domain_ops, pp);
+ if (!dra7xx->irq_domain) {
dev_err(dev, "Failed to get a INTx IRQ domain\n");
return -ENODEV;
}
@@ -186,7 +207,8 @@ static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp)
static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg)
{
struct dra7xx_pcie *dra7xx = arg;
- struct pcie_port *pp = &dra7xx->pp;
+ struct dw_pcie *pci = dra7xx->pci;
+ struct pcie_port *pp = &pci->pp;
u32 reg;
reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI);
@@ -199,7 +221,8 @@ static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg)
case INTB:
case INTC:
case INTD:
- generic_handle_irq(irq_find_mapping(pp->irq_domain, ffs(reg)));
+ generic_handle_irq(irq_find_mapping(dra7xx->irq_domain,
+ ffs(reg)));
break;
}
@@ -212,7 +235,8 @@ static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg)
static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
{
struct dra7xx_pcie *dra7xx = arg;
- struct device *dev = dra7xx->pp.dev;
+ struct dw_pcie *pci = dra7xx->pci;
+ struct device *dev = pci->dev;
u32 reg;
reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN);
@@ -267,8 +291,9 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
struct platform_device *pdev)
{
int ret;
- struct pcie_port *pp = &dra7xx->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = dra7xx->pci;
+ struct pcie_port *pp = &pci->pp;
+ struct device *dev = pci->dev;
struct resource *res;
pp->irq = platform_get_irq(pdev, 1);
@@ -285,15 +310,13 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
return ret;
}
- if (!IS_ENABLED(CONFIG_PCI_MSI)) {
- ret = dra7xx_pcie_init_irq_domain(pp);
- if (ret < 0)
- return ret;
- }
+ ret = dra7xx_pcie_init_irq_domain(pp);
+ if (ret < 0)
+ return ret;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbics");
- pp->dbi_base = devm_ioremap(dev, res->start, resource_size(res));
- if (!pp->dbi_base)
+ pci->dbi_base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!pci->dbi_base)
return -ENOMEM;
ret = dw_pcie_host_init(pp);
@@ -305,6 +328,49 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
return 0;
}
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .link_up = dra7xx_pcie_link_up,
+};
+
+static void dra7xx_pcie_disable_phy(struct dra7xx_pcie *dra7xx)
+{
+ int phy_count = dra7xx->phy_count;
+
+ while (phy_count--) {
+ phy_power_off(dra7xx->phy[phy_count]);
+ phy_exit(dra7xx->phy[phy_count]);
+ }
+}
+
+static int dra7xx_pcie_enable_phy(struct dra7xx_pcie *dra7xx)
+{
+ int phy_count = dra7xx->phy_count;
+ int ret;
+ int i;
+
+ for (i = 0; i < phy_count; i++) {
+ ret = phy_init(dra7xx->phy[i]);
+ if (ret < 0)
+ goto err_phy;
+
+ ret = phy_power_on(dra7xx->phy[i]);
+ if (ret < 0) {
+ phy_exit(dra7xx->phy[i]);
+ goto err_phy;
+ }
+ }
+
+ return 0;
+
+err_phy:
+ while (--i >= 0) {
+ phy_power_off(dra7xx->phy[i]);
+ phy_exit(dra7xx->phy[i]);
+ }
+
+ return ret;
+}
+
static int __init dra7xx_pcie_probe(struct platform_device *pdev)
{
u32 reg;
@@ -315,21 +381,26 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
struct phy **phy;
void __iomem *base;
struct resource *res;
- struct dra7xx_pcie *dra7xx;
+ struct dw_pcie *pci;
struct pcie_port *pp;
+ struct dra7xx_pcie *dra7xx;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
char name[10];
- int gpio_sel;
- enum of_gpio_flags flags;
- unsigned long gpio_flags;
+ struct gpio_desc *reset;
dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL);
if (!dra7xx)
return -ENOMEM;
- pp = &dra7xx->pp;
- pp->dev = dev;
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pci->dev = dev;
+ pci->ops = &dw_pcie_ops;
+
+ pp = &pci->pp;
pp->ops = &dra7xx_pcie_host_ops;
irq = platform_get_irq(pdev, 0);
@@ -365,22 +436,21 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
phy[i] = devm_phy_get(dev, name);
if (IS_ERR(phy[i]))
return PTR_ERR(phy[i]);
-
- ret = phy_init(phy[i]);
- if (ret < 0)
- goto err_phy;
-
- ret = phy_power_on(phy[i]);
- if (ret < 0) {
- phy_exit(phy[i]);
- goto err_phy;
- }
}
dra7xx->base = base;
dra7xx->phy = phy;
+ dra7xx->pci = pci;
dra7xx->phy_count = phy_count;
+ ret = dra7xx_pcie_enable_phy(dra7xx);
+ if (ret) {
+ dev_err(dev, "failed to enable phy\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, dra7xx);
+
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
@@ -388,19 +458,10 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
goto err_get_sync;
}
- gpio_sel = of_get_gpio_flags(dev->of_node, 0, &flags);
- if (gpio_is_valid(gpio_sel)) {
- gpio_flags = (flags & OF_GPIO_ACTIVE_LOW) ?
- GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH;
- ret = devm_gpio_request_one(dev, gpio_sel, gpio_flags,
- "pcie_reset");
- if (ret) {
- dev_err(dev, "gpio%d request failed, ret %d\n",
- gpio_sel, ret);
- goto err_gpio;
- }
- } else if (gpio_sel == -EPROBE_DEFER) {
- ret = -EPROBE_DEFER;
+ reset = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH);
+ if (IS_ERR(reset)) {
+ ret = PTR_ERR(reset);
+ dev_err(&pdev->dev, "gpio request failed, ret %d\n", ret);
goto err_gpio;
}
@@ -408,11 +469,14 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
reg &= ~LTSSM_EN;
dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
+ dra7xx->link_gen = of_pci_get_max_link_speed(np);
+ if (dra7xx->link_gen < 0 || dra7xx->link_gen > 2)
+ dra7xx->link_gen = 2;
+
ret = dra7xx_add_pcie_port(dra7xx, pdev);
if (ret < 0)
goto err_gpio;
- platform_set_drvdata(pdev, dra7xx);
return 0;
err_gpio:
@@ -420,12 +484,7 @@ err_gpio:
err_get_sync:
pm_runtime_disable(dev);
-
-err_phy:
- while (--i >= 0) {
- phy_power_off(phy[i]);
- phy_exit(phy[i]);
- }
+ dra7xx_pcie_disable_phy(dra7xx);
return ret;
}
@@ -434,13 +493,13 @@ err_phy:
static int dra7xx_pcie_suspend(struct device *dev)
{
struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev);
- struct pcie_port *pp = &dra7xx->pp;
+ struct dw_pcie *pci = dra7xx->pci;
u32 val;
/* clear MSE */
- val = dw_pcie_readl_rc(pp, PCI_COMMAND);
+ val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
val &= ~PCI_COMMAND_MEMORY;
- dw_pcie_writel_rc(pp, PCI_COMMAND, val);
+ dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
return 0;
}
@@ -448,13 +507,13 @@ static int dra7xx_pcie_suspend(struct device *dev)
static int dra7xx_pcie_resume(struct device *dev)
{
struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev);
- struct pcie_port *pp = &dra7xx->pp;
+ struct dw_pcie *pci = dra7xx->pci;
u32 val;
/* set MSE */
- val = dw_pcie_readl_rc(pp, PCI_COMMAND);
+ val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
val |= PCI_COMMAND_MEMORY;
- dw_pcie_writel_rc(pp, PCI_COMMAND, val);
+ dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
return 0;
}
@@ -462,12 +521,8 @@ static int dra7xx_pcie_resume(struct device *dev)
static int dra7xx_pcie_suspend_noirq(struct device *dev)
{
struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev);
- int count = dra7xx->phy_count;
- while (count--) {
- phy_power_off(dra7xx->phy[count]);
- phy_exit(dra7xx->phy[count]);
- }
+ dra7xx_pcie_disable_phy(dra7xx);
return 0;
}
@@ -475,31 +530,15 @@ static int dra7xx_pcie_suspend_noirq(struct device *dev)
static int dra7xx_pcie_resume_noirq(struct device *dev)
{
struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev);
- int phy_count = dra7xx->phy_count;
int ret;
- int i;
-
- for (i = 0; i < phy_count; i++) {
- ret = phy_init(dra7xx->phy[i]);
- if (ret < 0)
- goto err_phy;
- ret = phy_power_on(dra7xx->phy[i]);
- if (ret < 0) {
- phy_exit(dra7xx->phy[i]);
- goto err_phy;
- }
+ ret = dra7xx_pcie_enable_phy(dra7xx);
+ if (ret) {
+ dev_err(dev, "failed to enable phy\n");
+ return ret;
}
return 0;
-
-err_phy:
- while (--i >= 0) {
- phy_power_off(dra7xx->phy[i]);
- phy_exit(dra7xx->phy[i]);
- }
-
- return ret;
}
#endif
diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c
new file mode 100644
index 000000000000..993b650ef275
--- /dev/null
+++ b/drivers/pci/dwc/pci-exynos.c
@@ -0,0 +1,752 @@
+/*
+ * 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/init.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+#define to_exynos_pcie(x) dev_get_drvdata((x)->dev)
+
+/* PCIe ELBI registers */
+#define PCIE_IRQ_PULSE 0x000
+#define IRQ_INTA_ASSERT BIT(0)
+#define IRQ_INTB_ASSERT BIT(2)
+#define IRQ_INTC_ASSERT BIT(4)
+#define IRQ_INTD_ASSERT BIT(6)
+#define PCIE_IRQ_LEVEL 0x004
+#define PCIE_IRQ_SPECIAL 0x008
+#define PCIE_IRQ_EN_PULSE 0x00c
+#define PCIE_IRQ_EN_LEVEL 0x010
+#define IRQ_MSI_ENABLE BIT(2)
+#define PCIE_IRQ_EN_SPECIAL 0x014
+#define PCIE_PWR_RESET 0x018
+#define PCIE_CORE_RESET 0x01c
+#define PCIE_CORE_RESET_ENABLE BIT(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 BIT(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_COMMON_POWER 0x064
+#define PCIE_PHY_COMMON_PD_CMN BIT(3)
+#define PCIE_PHY_TRSV0_EMP_LVL 0x084
+#define PCIE_PHY_TRSV0_DRV_LVL 0x088
+#define PCIE_PHY_TRSV0_RXCDR 0x0ac
+#define PCIE_PHY_TRSV0_POWER 0x0c4
+#define PCIE_PHY_TRSV0_PD_TSV BIT(7)
+#define PCIE_PHY_TRSV0_LVCC 0x0dc
+#define PCIE_PHY_TRSV1_EMP_LVL 0x144
+#define PCIE_PHY_TRSV1_RXCDR 0x16c
+#define PCIE_PHY_TRSV1_POWER 0x184
+#define PCIE_PHY_TRSV1_PD_TSV BIT(7)
+#define PCIE_PHY_TRSV1_LVCC 0x19c
+#define PCIE_PHY_TRSV2_EMP_LVL 0x204
+#define PCIE_PHY_TRSV2_RXCDR 0x22c
+#define PCIE_PHY_TRSV2_POWER 0x244
+#define PCIE_PHY_TRSV2_PD_TSV BIT(7)
+#define PCIE_PHY_TRSV2_LVCC 0x25c
+#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4
+#define PCIE_PHY_TRSV3_RXCDR 0x2ec
+#define PCIE_PHY_TRSV3_POWER 0x304
+#define PCIE_PHY_TRSV3_PD_TSV BIT(7)
+#define PCIE_PHY_TRSV3_LVCC 0x31c
+
+struct exynos_pcie_mem_res {
+ void __iomem *elbi_base; /* DT 0th resource: PCIe CTRL */
+ void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
+ void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL */
+};
+
+struct exynos_pcie_clk_res {
+ struct clk *clk;
+ struct clk *bus_clk;
+};
+
+struct exynos_pcie {
+ struct dw_pcie *pci;
+ struct exynos_pcie_mem_res *mem_res;
+ struct exynos_pcie_clk_res *clk_res;
+ const struct exynos_pcie_ops *ops;
+ int reset_gpio;
+
+ /* For Generic PHY Framework */
+ bool using_phy;
+ struct phy *phy;
+};
+
+struct exynos_pcie_ops {
+ int (*get_mem_resources)(struct platform_device *pdev,
+ struct exynos_pcie *ep);
+ int (*get_clk_resources)(struct exynos_pcie *ep);
+ int (*init_clk_resources)(struct exynos_pcie *ep);
+ void (*deinit_clk_resources)(struct exynos_pcie *ep);
+};
+
+static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
+ struct exynos_pcie *ep)
+{
+ struct dw_pcie *pci = ep->pci;
+ struct device *dev = pci->dev;
+ struct resource *res;
+
+ /* If using the PHY framework, doesn't need to get other resource */
+ if (ep->using_phy)
+ return 0;
+
+ ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
+ if (!ep->mem_res)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ep->mem_res->elbi_base))
+ return PTR_ERR(ep->mem_res->elbi_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ep->mem_res->phy_base))
+ return PTR_ERR(ep->mem_res->phy_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ ep->mem_res->block_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ep->mem_res->block_base))
+ return PTR_ERR(ep->mem_res->block_base);
+
+ return 0;
+}
+
+static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
+{
+ struct dw_pcie *pci = ep->pci;
+ struct device *dev = pci->dev;
+
+ ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL);
+ if (!ep->clk_res)
+ return -ENOMEM;
+
+ ep->clk_res->clk = devm_clk_get(dev, "pcie");
+ if (IS_ERR(ep->clk_res->clk)) {
+ dev_err(dev, "Failed to get pcie rc clock\n");
+ return PTR_ERR(ep->clk_res->clk);
+ }
+
+ ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus");
+ if (IS_ERR(ep->clk_res->bus_clk)) {
+ dev_err(dev, "Failed to get pcie bus clock\n");
+ return PTR_ERR(ep->clk_res->bus_clk);
+ }
+
+ return 0;
+}
+
+static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep)
+{
+ struct dw_pcie *pci = ep->pci;
+ struct device *dev = pci->dev;
+ int ret;
+
+ ret = clk_prepare_enable(ep->clk_res->clk);
+ if (ret) {
+ dev_err(dev, "cannot enable pcie rc clock");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ep->clk_res->bus_clk);
+ if (ret) {
+ dev_err(dev, "cannot enable pcie bus clock");
+ goto err_bus_clk;
+ }
+
+ return 0;
+
+err_bus_clk:
+ clk_disable_unprepare(ep->clk_res->clk);
+
+ return ret;
+}
+
+static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep)
+{
+ clk_disable_unprepare(ep->clk_res->bus_clk);
+ clk_disable_unprepare(ep->clk_res->clk);
+}
+
+static const struct exynos_pcie_ops exynos5440_pcie_ops = {
+ .get_mem_resources = exynos5440_pcie_get_mem_resources,
+ .get_clk_resources = exynos5440_pcie_get_clk_resources,
+ .init_clk_resources = exynos5440_pcie_init_clk_resources,
+ .deinit_clk_resources = exynos5440_pcie_deinit_clk_resources,
+};
+
+static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
+{
+ writel(val, base + reg);
+}
+
+static u32 exynos_pcie_readl(void __iomem *base, u32 reg)
+{
+ return readl(base + reg);
+}
+
+static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
+{
+ u32 val;
+
+ val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC);
+ if (on)
+ val |= PCIE_ELBI_SLV_DBI_ENABLE;
+ else
+ val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
+}
+
+static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
+{
+ u32 val;
+
+ val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC);
+ if (on)
+ val |= PCIE_ELBI_SLV_DBI_ENABLE;
+ else
+ val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
+}
+
+static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
+{
+ u32 val;
+
+ val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
+ val &= ~PCIE_CORE_RESET_ENABLE;
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET);
+}
+
+static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
+{
+ u32 val;
+
+ val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
+ val |= PCIE_CORE_RESET_ENABLE;
+
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
+ exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
+}
+
+static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
+{
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
+ exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_GLOBAL_RESET);
+}
+
+static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
+{
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_GLOBAL_RESET);
+ exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET);
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSVREG_RESET);
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
+}
+
+static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
+{
+ u32 val;
+
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
+ val &= ~PCIE_PHY_COMMON_PD_CMN;
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
+
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
+ val &= ~PCIE_PHY_TRSV0_PD_TSV;
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
+
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
+ val &= ~PCIE_PHY_TRSV1_PD_TSV;
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
+
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
+ val &= ~PCIE_PHY_TRSV2_PD_TSV;
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
+
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
+ val &= ~PCIE_PHY_TRSV3_PD_TSV;
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
+}
+
+static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
+{
+ u32 val;
+
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
+ val |= PCIE_PHY_COMMON_PD_CMN;
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
+
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
+ val |= PCIE_PHY_TRSV0_PD_TSV;
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
+
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
+ val |= PCIE_PHY_TRSV1_PD_TSV;
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
+
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
+ val |= PCIE_PHY_TRSV2_PD_TSV;
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
+
+ val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
+ val |= PCIE_PHY_TRSV3_PD_TSV;
+ exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
+}
+
+static void exynos_pcie_init_phy(struct exynos_pcie *ep)
+{
+ /* DCC feedback control off */
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
+
+ /* set TX/RX impedance */
+ exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
+
+ /* set 50Mhz PHY clock */
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
+
+ /* set TX Differential output for lane 0 */
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
+
+ /* set TX Pre-emphasis Level Control for lane 0 to minimum */
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
+
+ /* set RX clock and data recovery bandwidth */
+ exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
+
+ /* change TX Pre-emphasis Level Control for lanes */
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
+
+ /* set LVCC */
+ exynos_pcie_writel(ep->mem_res->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
+ exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
+}
+
+static void exynos_pcie_assert_reset(struct exynos_pcie *ep)
+{
+ struct dw_pcie *pci = ep->pci;
+ struct device *dev = pci->dev;
+
+ if (ep->reset_gpio >= 0)
+ devm_gpio_request_one(dev, ep->reset_gpio,
+ GPIOF_OUT_INIT_HIGH, "RESET");
+}
+
+static int exynos_pcie_establish_link(struct exynos_pcie *ep)
+{
+ struct dw_pcie *pci = ep->pci;
+ struct pcie_port *pp = &pci->pp;
+ struct device *dev = pci->dev;
+ u32 val;
+
+ if (dw_pcie_link_up(pci)) {
+ dev_err(dev, "Link already up\n");
+ return 0;
+ }
+
+ exynos_pcie_assert_core_reset(ep);
+
+ if (ep->using_phy) {
+ phy_reset(ep->phy);
+
+ exynos_pcie_writel(ep->mem_res->elbi_base, 1,
+ PCIE_PWR_RESET);
+
+ phy_power_on(ep->phy);
+ phy_init(ep->phy);
+ } else {
+ exynos_pcie_assert_phy_reset(ep);
+ exynos_pcie_deassert_phy_reset(ep);
+ exynos_pcie_power_on_phy(ep);
+ exynos_pcie_init_phy(ep);
+
+ /* pulse for common reset */
+ exynos_pcie_writel(ep->mem_res->block_base, 1,
+ PCIE_PHY_COMMON_RESET);
+ udelay(500);
+ exynos_pcie_writel(ep->mem_res->block_base, 0,
+ PCIE_PHY_COMMON_RESET);
+ }
+
+ /* pulse for common reset */
+ exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_COMMON_RESET);
+ udelay(500);
+ exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET);
+
+ exynos_pcie_deassert_core_reset(ep);
+ dw_pcie_setup_rc(pp);
+ exynos_pcie_assert_reset(ep);
+
+ /* assert LTSSM enable */
+ exynos_pcie_writel(ep->mem_res->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
+ PCIE_APP_LTSSM_ENABLE);
+
+ /* check if the link is up or not */
+ if (!dw_pcie_wait_for_link(pci))
+ return 0;
+
+ if (ep->using_phy) {
+ phy_power_off(ep->phy);
+ return -ETIMEDOUT;
+ }
+
+ while (exynos_pcie_readl(ep->mem_res->phy_base,
+ PCIE_PHY_PLL_LOCKED) == 0) {
+ val = exynos_pcie_readl(ep->mem_res->block_base,
+ PCIE_PHY_PLL_LOCKED);
+ dev_info(dev, "PLL Locked: 0x%x\n", val);
+ }
+ exynos_pcie_power_off_phy(ep);
+ return -ETIMEDOUT;
+}
+
+static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
+{
+ u32 val;
+
+ val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE);
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE);
+}
+
+static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
+{
+ u32 val;
+
+ /* enable INTX interrupt */
+ val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
+ IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
+}
+
+static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
+{
+ struct exynos_pcie *ep = arg;
+
+ exynos_pcie_clear_irq_pulse(ep);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg)
+{
+ struct exynos_pcie *ep = arg;
+ struct dw_pcie *pci = ep->pci;
+ struct pcie_port *pp = &pci->pp;
+
+ return dw_handle_msi_irq(pp);
+}
+
+static void exynos_pcie_msi_init(struct exynos_pcie *ep)
+{
+ struct dw_pcie *pci = ep->pci;
+ struct pcie_port *pp = &pci->pp;
+ u32 val;
+
+ dw_pcie_msi_init(pp);
+
+ /* enable MSI interrupt */
+ val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL);
+ val |= IRQ_MSI_ENABLE;
+ exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL);
+}
+
+static void exynos_pcie_enable_interrupts(struct exynos_pcie *ep)
+{
+ exynos_pcie_enable_irq_pulse(ep);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ exynos_pcie_msi_init(ep);
+}
+
+static u32 exynos_pcie_readl_dbi(struct dw_pcie *pci, u32 reg)
+{
+ struct exynos_pcie *ep = to_exynos_pcie(pci);
+ u32 val;
+
+ exynos_pcie_sideband_dbi_r_mode(ep, true);
+ val = readl(pci->dbi_base + reg);
+ exynos_pcie_sideband_dbi_r_mode(ep, false);
+ return val;
+}
+
+static void exynos_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
+{
+ struct exynos_pcie *ep = to_exynos_pcie(pci);
+
+ exynos_pcie_sideband_dbi_w_mode(ep, true);
+ writel(val, pci->dbi_base + reg);
+ exynos_pcie_sideband_dbi_w_mode(ep, false);
+}
+
+static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
+ u32 *val)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct exynos_pcie *ep = to_exynos_pcie(pci);
+ int ret;
+
+ exynos_pcie_sideband_dbi_r_mode(ep, true);
+ ret = dw_pcie_read(pci->dbi_base + where, size, val);
+ exynos_pcie_sideband_dbi_r_mode(ep, false);
+ return ret;
+}
+
+static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
+ u32 val)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct exynos_pcie *ep = to_exynos_pcie(pci);
+ int ret;
+
+ exynos_pcie_sideband_dbi_w_mode(ep, true);
+ ret = dw_pcie_write(pci->dbi_base + where, size, val);
+ exynos_pcie_sideband_dbi_w_mode(ep, false);
+ return ret;
+}
+
+static int exynos_pcie_link_up(struct dw_pcie *pci)
+{
+ struct exynos_pcie *ep = to_exynos_pcie(pci);
+ u32 val;
+
+ val = exynos_pcie_readl(ep->mem_res->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)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct exynos_pcie *ep = to_exynos_pcie(pci);
+
+ exynos_pcie_establish_link(ep);
+ exynos_pcie_enable_interrupts(ep);
+}
+
+static struct dw_pcie_host_ops exynos_pcie_host_ops = {
+ .rd_own_conf = exynos_pcie_rd_own_conf,
+ .wr_own_conf = exynos_pcie_wr_own_conf,
+ .host_init = exynos_pcie_host_init,
+};
+
+static int __init exynos_add_pcie_port(struct exynos_pcie *ep,
+ struct platform_device *pdev)
+{
+ struct dw_pcie *pci = ep->pci;
+ struct pcie_port *pp = &pci->pp;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ pp->irq = platform_get_irq(pdev, 1);
+ if (!pp->irq) {
+ dev_err(dev, "failed to get irq\n");
+ return -ENODEV;
+ }
+ ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler,
+ IRQF_SHARED, "exynos-pcie", ep);
+ if (ret) {
+ dev_err(dev, "failed to request irq\n");
+ return ret;
+ }
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ pp->msi_irq = platform_get_irq(pdev, 0);
+ if (!pp->msi_irq) {
+ dev_err(dev, "failed to get msi irq\n");
+ return -ENODEV;
+ }
+
+ ret = devm_request_irq(dev, pp->msi_irq,
+ exynos_pcie_msi_irq_handler,
+ IRQF_SHARED | IRQF_NO_THREAD,
+ "exynos-pcie", ep);
+ if (ret) {
+ dev_err(dev, "failed to request msi irq\n");
+ return ret;
+ }
+ }
+
+ pp->root_bus_nr = -1;
+ pp->ops = &exynos_pcie_host_ops;
+
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .readl_dbi = exynos_pcie_readl_dbi,
+ .writel_dbi = exynos_pcie_writel_dbi,
+ .link_up = exynos_pcie_link_up,
+};
+
+static int __init exynos_pcie_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dw_pcie *pci;
+ struct exynos_pcie *ep;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL);
+ if (!ep)
+ return -ENOMEM;
+
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pci->dev = dev;
+ pci->ops = &dw_pcie_ops;
+
+ ep->pci = pci;
+ ep->ops = (const struct exynos_pcie_ops *)
+ of_device_get_match_data(dev);
+
+ ep->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
+
+ /* Assume that controller doesn't use the PHY framework */
+ ep->using_phy = false;
+
+ ep->phy = devm_of_phy_get(dev, np, NULL);
+ if (IS_ERR(ep->phy)) {
+ if (PTR_ERR(ep->phy) == -EPROBE_DEFER)
+ return PTR_ERR(ep->phy);
+ dev_warn(dev, "Use the 'phy' property. Current DT of pci-exynos was deprecated!!\n");
+ } else
+ ep->using_phy = true;
+
+ if (ep->ops && ep->ops->get_mem_resources) {
+ ret = ep->ops->get_mem_resources(pdev, ep);
+ if (ret)
+ return ret;
+ }
+
+ if (ep->ops && ep->ops->get_clk_resources) {
+ ret = ep->ops->get_clk_resources(ep);
+ if (ret)
+ return ret;
+ ret = ep->ops->init_clk_resources(ep);
+ if (ret)
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, ep);
+
+ ret = exynos_add_pcie_port(ep, pdev);
+ if (ret < 0)
+ goto fail_probe;
+
+ return 0;
+
+fail_probe:
+ if (ep->using_phy)
+ phy_exit(ep->phy);
+
+ if (ep->ops && ep->ops->deinit_clk_resources)
+ ep->ops->deinit_clk_resources(ep);
+ return ret;
+}
+
+static int __exit exynos_pcie_remove(struct platform_device *pdev)
+{
+ struct exynos_pcie *ep = platform_get_drvdata(pdev);
+
+ if (ep->ops && ep->ops->deinit_clk_resources)
+ ep->ops->deinit_clk_resources(ep);
+
+ return 0;
+}
+
+static const struct of_device_id exynos_pcie_of_match[] = {
+ {
+ .compatible = "samsung,exynos5440-pcie",
+ .data = &exynos5440_pcie_ops
+ },
+ {},
+};
+
+static struct platform_driver exynos_pcie_driver = {
+ .remove = __exit_p(exynos_pcie_remove),
+ .driver = {
+ .name = "exynos-pcie",
+ .of_match_table = exynos_pcie_of_match,
+ },
+};
+
+/* Exynos PCIe driver does not allow module unload */
+
+static int __init exynos_pcie_init(void)
+{
+ return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe);
+}
+subsys_initcall(exynos_pcie_init);
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c
index c8cefb078218..801e46cd266d 100644
--- a/drivers/pci/host/pci-imx6.c
+++ b/drivers/pci/dwc/pci-imx6.c
@@ -30,7 +30,7 @@
#include "pcie-designware.h"
-#define to_imx6_pcie(x) container_of(x, struct imx6_pcie, pp)
+#define to_imx6_pcie(x) dev_get_drvdata((x)->dev)
enum imx6_pcie_variants {
IMX6Q,
@@ -39,7 +39,7 @@ enum imx6_pcie_variants {
};
struct imx6_pcie {
- struct pcie_port pp; /* pp.dbi_base is DT 0th resource */
+ struct dw_pcie *pci;
int reset_gpio;
bool gpio_active_high;
struct clk *pcie_bus;
@@ -97,13 +97,13 @@ struct imx6_pcie {
static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val)
{
- struct pcie_port *pp = &imx6_pcie->pp;
+ struct dw_pcie *pci = imx6_pcie->pci;
u32 val;
u32 max_iterations = 10;
u32 wait_counter = 0;
do {
- val = dw_pcie_readl_rc(pp, PCIE_PHY_STAT);
+ val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT);
val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1;
wait_counter++;
@@ -118,22 +118,22 @@ static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val)
static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr)
{
- struct pcie_port *pp = &imx6_pcie->pp;
+ struct dw_pcie *pci = imx6_pcie->pci;
u32 val;
int ret;
val = addr << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val);
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val);
val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC);
- dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val);
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val);
ret = pcie_phy_poll_ack(imx6_pcie, 1);
if (ret)
return ret;
val = addr << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val);
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val);
return pcie_phy_poll_ack(imx6_pcie, 0);
}
@@ -141,7 +141,7 @@ static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr)
/* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */
static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data)
{
- struct pcie_port *pp = &imx6_pcie->pp;
+ struct dw_pcie *pci = imx6_pcie->pci;
u32 val, phy_ctl;
int ret;
@@ -151,24 +151,24 @@ static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data)
/* assert Read signal */
phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC;
- dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, phy_ctl);
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, phy_ctl);
ret = pcie_phy_poll_ack(imx6_pcie, 1);
if (ret)
return ret;
- val = dw_pcie_readl_rc(pp, PCIE_PHY_STAT);
+ val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT);
*data = val & 0xffff;
/* deassert Read signal */
- dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, 0x00);
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x00);
return pcie_phy_poll_ack(imx6_pcie, 0);
}
static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
{
- struct pcie_port *pp = &imx6_pcie->pp;
+ struct dw_pcie *pci = imx6_pcie->pci;
u32 var;
int ret;
@@ -179,11 +179,11 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
return ret;
var = data << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
/* capture data */
var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC);
- dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
ret = pcie_phy_poll_ack(imx6_pcie, 1);
if (ret)
@@ -191,7 +191,7 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
/* deassert cap data */
var = data << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
/* wait for ack de-assertion */
ret = pcie_phy_poll_ack(imx6_pcie, 0);
@@ -200,7 +200,7 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
/* assert wr signal */
var = 0x1 << PCIE_PHY_CTRL_WR_LOC;
- dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
/* wait for ack */
ret = pcie_phy_poll_ack(imx6_pcie, 1);
@@ -209,14 +209,14 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
/* deassert wr signal */
var = data << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
/* wait for ack de-assertion */
ret = pcie_phy_poll_ack(imx6_pcie, 0);
if (ret)
return ret;
- dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, 0x0);
+ dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x0);
return 0;
}
@@ -247,9 +247,6 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
{
- struct pcie_port *pp = &imx6_pcie->pp;
- u32 val, gpr1, gpr12;
-
switch (imx6_pcie->variant) {
case IMX6SX:
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
@@ -266,33 +263,6 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
IMX6Q_GPR1_PCIE_SW_RST);
break;
case IMX6Q:
- /*
- * If the bootloader already enabled the link we need some
- * special handling to get the core back into a state where
- * it is safe to touch it for configuration. As there is
- * no dedicated reset signal wired up for MX6QDL, we need
- * to manually force LTSSM into "detect" state before
- * completely disabling LTSSM, which is a prerequisite for
- * core configuration.
- *
- * If both LTSSM_ENABLE and REF_SSP_ENABLE are active we
- * have a strong indication that the bootloader activated
- * the link.
- */
- regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, &gpr1);
- regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, &gpr12);
-
- if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) &&
- (gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) {
- val = dw_pcie_readl_rc(pp, PCIE_PL_PFLR);
- val &= ~PCIE_PL_PFLR_LINK_STATE_MASK;
- val |= PCIE_PL_PFLR_FORCE_LINK;
- dw_pcie_writel_rc(pp, PCIE_PL_PFLR, val);
-
- regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
- IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
- }
-
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
@@ -303,8 +273,8 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
{
- struct pcie_port *pp = &imx6_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = imx6_pcie->pci;
+ struct device *dev = pci->dev;
int ret = 0;
switch (imx6_pcie->variant) {
@@ -340,8 +310,8 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
{
- struct pcie_port *pp = &imx6_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = imx6_pcie->pci;
+ struct device *dev = pci->dev;
int ret;
ret = clk_prepare_enable(imx6_pcie->pcie_phy);
@@ -440,28 +410,28 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
{
- struct pcie_port *pp = &imx6_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = imx6_pcie->pci;
+ struct device *dev = pci->dev;
/* check if the link is up or not */
- if (!dw_pcie_wait_for_link(pp))
+ if (!dw_pcie_wait_for_link(pci))
return 0;
dev_dbg(dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
- dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0),
- dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1));
+ dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0),
+ dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1));
return -ETIMEDOUT;
}
static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie)
{
- struct pcie_port *pp = &imx6_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = imx6_pcie->pci;
+ struct device *dev = pci->dev;
u32 tmp;
unsigned int retries;
for (retries = 0; retries < 200; retries++) {
- tmp = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
/* Test if the speed change finished. */
if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
return 0;
@@ -475,15 +445,16 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie)
static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg)
{
struct imx6_pcie *imx6_pcie = arg;
- struct pcie_port *pp = &imx6_pcie->pp;
+ struct dw_pcie *pci = imx6_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
return dw_handle_msi_irq(pp);
}
static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
{
- struct pcie_port *pp = &imx6_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = imx6_pcie->pci;
+ struct device *dev = pci->dev;
u32 tmp;
int ret;
@@ -492,27 +463,25 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
* started in Gen2 mode, there is a possibility the devices on the
* bus will not be detected at all. This happens with PCIe switches.
*/
- tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCR);
+ tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR);
tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1;
- dw_pcie_writel_rc(pp, PCIE_RC_LCR, tmp);
+ dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
/* Start LTSSM. */
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
ret = imx6_pcie_wait_for_link(imx6_pcie);
- if (ret) {
- dev_info(dev, "Link never came up\n");
+ if (ret)
goto err_reset_phy;
- }
if (imx6_pcie->link_gen == 2) {
/* Allow Gen2 mode after the link is up. */
- tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCR);
+ tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR);
tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
- dw_pcie_writel_rc(pp, PCIE_RC_LCR, tmp);
+ dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
} else {
dev_info(dev, "Link: Gen2 disabled\n");
}
@@ -521,9 +490,9 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
* Start Directed Speed Change so the best possible speed both link
* partners support can be negotiated.
*/
- tmp = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
tmp |= PORT_LOGIC_SPEED_CHANGE;
- dw_pcie_writel_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
+ dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
ret = imx6_pcie_wait_for_speed_change(imx6_pcie);
if (ret) {
@@ -538,21 +507,22 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
goto err_reset_phy;
}
- tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCSR);
+ tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCSR);
dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf);
return 0;
err_reset_phy:
dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
- dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0),
- dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1));
+ dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0),
+ dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1));
imx6_pcie_reset_phy(imx6_pcie);
return ret;
}
static void imx6_pcie_host_init(struct pcie_port *pp)
{
- struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct imx6_pcie *imx6_pcie = to_imx6_pcie(pci);
imx6_pcie_assert_core_reset(imx6_pcie);
imx6_pcie_init_phy(imx6_pcie);
@@ -564,22 +534,22 @@ static void imx6_pcie_host_init(struct pcie_port *pp)
dw_pcie_msi_init(pp);
}
-static int imx6_pcie_link_up(struct pcie_port *pp)
+static int imx6_pcie_link_up(struct dw_pcie *pci)
{
- return dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1) &
+ return dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1) &
PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
}
-static struct pcie_host_ops imx6_pcie_host_ops = {
- .link_up = imx6_pcie_link_up,
+static struct dw_pcie_host_ops imx6_pcie_host_ops = {
.host_init = imx6_pcie_host_init,
};
static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
struct platform_device *pdev)
{
- struct pcie_port *pp = &imx6_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = imx6_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
+ struct device *dev = &pdev->dev;
int ret;
if (IS_ENABLED(CONFIG_PCI_MSI)) {
@@ -611,11 +581,15 @@ static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
return 0;
}
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .link_up = imx6_pcie_link_up,
+};
+
static int __init imx6_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct dw_pcie *pci;
struct imx6_pcie *imx6_pcie;
- struct pcie_port *pp;
struct resource *dbi_base;
struct device_node *node = dev->of_node;
int ret;
@@ -624,9 +598,14 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
if (!imx6_pcie)
return -ENOMEM;
- pp = &imx6_pcie->pp;
- pp->dev = dev;
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+ pci->dev = dev;
+ pci->ops = &dw_pcie_ops;
+
+ imx6_pcie->pci = pci;
imx6_pcie->variant =
(enum imx6_pcie_variants)of_device_get_match_data(dev);
@@ -635,9 +614,9 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
"imprecise external abort");
dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pp->dbi_base = devm_ioremap_resource(dev, dbi_base);
- if (IS_ERR(pp->dbi_base))
- return PTR_ERR(pp->dbi_base);
+ pci->dbi_base = devm_ioremap_resource(dev, dbi_base);
+ if (IS_ERR(pci->dbi_base))
+ return PTR_ERR(pci->dbi_base);
/* Fetch GPIOs */
imx6_pcie->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0);
@@ -678,8 +657,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
imx6_pcie->pcie_inbound_axi = devm_clk_get(dev,
"pcie_inbound_axi");
if (IS_ERR(imx6_pcie->pcie_inbound_axi)) {
- dev_err(dev,
- "pcie_incbound_axi clock missing or invalid\n");
+ dev_err(dev, "pcie_inbound_axi clock missing or invalid\n");
return PTR_ERR(imx6_pcie->pcie_inbound_axi);
}
}
@@ -719,11 +697,12 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
if (ret)
imx6_pcie->link_gen = 1;
+ platform_set_drvdata(pdev, imx6_pcie);
+
ret = imx6_add_pcie_port(imx6_pcie, pdev);
if (ret < 0)
return ret;
- platform_set_drvdata(pdev, imx6_pcie);
return 0;
}
diff --git a/drivers/pci/host/pci-keystone-dw.c b/drivers/pci/dwc/pci-keystone-dw.c
index 9397c4667106..6b396f6b4615 100644
--- a/drivers/pci/host/pci-keystone-dw.c
+++ b/drivers/pci/dwc/pci-keystone-dw.c
@@ -72,7 +72,7 @@
/* Config space registers */
#define DEBUG0 0x728
-#define to_keystone_pcie(x) container_of(x, struct keystone_pcie, pp)
+#define to_keystone_pcie(x) dev_get_drvdata((x)->dev)
static inline void update_reg_offset_bit_pos(u32 offset, u32 *reg_offset,
u32 *bit_pos)
@@ -83,7 +83,8 @@ static inline void update_reg_offset_bit_pos(u32 offset, u32 *reg_offset,
phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp)
{
- struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
return ks_pcie->app.start + MSI_IRQ;
}
@@ -100,8 +101,9 @@ static void ks_dw_app_writel(struct keystone_pcie *ks_pcie, u32 offset, u32 val)
void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset)
{
- struct pcie_port *pp = &ks_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = ks_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
+ struct device *dev = pci->dev;
u32 pending, vector;
int src, virq;
@@ -128,10 +130,12 @@ static void ks_dw_pcie_msi_irq_ack(struct irq_data *d)
struct keystone_pcie *ks_pcie;
struct msi_desc *msi;
struct pcie_port *pp;
+ struct dw_pcie *pci;
msi = irq_data_get_msi_desc(d);
pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi);
- ks_pcie = to_keystone_pcie(pp);
+ pci = to_dw_pcie_from_pp(pp);
+ ks_pcie = to_keystone_pcie(pci);
offset = d->irq - irq_linear_revmap(pp->irq_domain, 0);
update_reg_offset_bit_pos(offset, &reg_offset, &bit_pos);
@@ -143,7 +147,8 @@ static void ks_dw_pcie_msi_irq_ack(struct irq_data *d)
void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
{
u32 reg_offset, bit_pos;
- struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
update_reg_offset_bit_pos(irq, &reg_offset, &bit_pos);
ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_SET + (reg_offset << 4),
@@ -153,7 +158,8 @@ void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
{
u32 reg_offset, bit_pos;
- struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
update_reg_offset_bit_pos(irq, &reg_offset, &bit_pos);
ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_CLR + (reg_offset << 4),
@@ -165,11 +171,13 @@ static void ks_dw_pcie_msi_irq_mask(struct irq_data *d)
struct keystone_pcie *ks_pcie;
struct msi_desc *msi;
struct pcie_port *pp;
+ struct dw_pcie *pci;
u32 offset;
msi = irq_data_get_msi_desc(d);
pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi);
- ks_pcie = to_keystone_pcie(pp);
+ pci = to_dw_pcie_from_pp(pp);
+ ks_pcie = to_keystone_pcie(pci);
offset = d->irq - irq_linear_revmap(pp->irq_domain, 0);
/* Mask the end point if PVM implemented */
@@ -186,11 +194,13 @@ static void ks_dw_pcie_msi_irq_unmask(struct irq_data *d)
struct keystone_pcie *ks_pcie;
struct msi_desc *msi;
struct pcie_port *pp;
+ struct dw_pcie *pci;
u32 offset;
msi = irq_data_get_msi_desc(d);
pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi);
- ks_pcie = to_keystone_pcie(pp);
+ pci = to_dw_pcie_from_pp(pp);
+ ks_pcie = to_keystone_pcie(pci);
offset = d->irq - irq_linear_revmap(pp->irq_domain, 0);
/* Mask the end point if PVM implemented */
@@ -225,8 +235,9 @@ static const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = {
int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip)
{
- struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
+ struct device *dev = pci->dev;
int i;
pp->irq_domain = irq_domain_add_linear(ks_pcie->msi_intc_np,
@@ -254,8 +265,8 @@ void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie)
void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset)
{
- struct pcie_port *pp = &ks_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = ks_pcie->pci;
+ struct device *dev = pci->dev;
u32 pending;
int virq;
@@ -285,7 +296,7 @@ irqreturn_t ks_dw_pcie_handle_error_irq(struct keystone_pcie *ks_pcie)
return IRQ_NONE;
if (status & ERR_FATAL_IRQ)
- dev_err(ks_pcie->pp.dev, "fatal error (status %#010x)\n",
+ dev_err(ks_pcie->pci->dev, "fatal error (status %#010x)\n",
status);
/* Ack the IRQ; status bits are RW1C */
@@ -366,15 +377,16 @@ static void ks_dw_pcie_clear_dbi_mode(struct keystone_pcie *ks_pcie)
void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie)
{
- struct pcie_port *pp = &ks_pcie->pp;
+ struct dw_pcie *pci = ks_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
u32 start = pp->mem->start, end = pp->mem->end;
int i, tr_size;
u32 val;
/* Disable BARs for inbound access */
ks_dw_pcie_set_dbi_mode(ks_pcie);
- dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 0);
- dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_1, 0);
+ dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0);
+ dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0);
ks_dw_pcie_clear_dbi_mode(ks_pcie);
/* Set outbound translation size per window division */
@@ -415,11 +427,12 @@ static void __iomem *ks_pcie_cfg_setup(struct keystone_pcie *ks_pcie, u8 bus,
unsigned int devfn)
{
u8 device = PCI_SLOT(devfn), function = PCI_FUNC(devfn);
- struct pcie_port *pp = &ks_pcie->pp;
+ struct dw_pcie *pci = ks_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
u32 regval;
if (bus == 0)
- return pp->dbi_base;
+ return pci->dbi_base;
regval = (bus << 16) | (device << 8) | function;
@@ -438,25 +451,27 @@ static void __iomem *ks_pcie_cfg_setup(struct keystone_pcie *ks_pcie, u8 bus,
int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
unsigned int devfn, int where, int size, u32 *val)
{
- struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
u8 bus_num = bus->number;
void __iomem *addr;
addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
- return dw_pcie_cfg_read(addr + where, size, val);
+ return dw_pcie_read(addr + where, size, val);
}
int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
unsigned int devfn, int where, int size, u32 val)
{
- struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
u8 bus_num = bus->number;
void __iomem *addr;
addr = ks_pcie_cfg_setup(ks_pcie, bus_num, devfn);
- return dw_pcie_cfg_write(addr + where, size, val);
+ return dw_pcie_write(addr + where, size, val);
}
/**
@@ -466,14 +481,15 @@ int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
*/
void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
{
- struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
/* Configure and set up BAR0 */
ks_dw_pcie_set_dbi_mode(ks_pcie);
/* Enable BAR0 */
- dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 1);
- dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, SZ_4K - 1);
+ dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 1);
+ dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, SZ_4K - 1);
ks_dw_pcie_clear_dbi_mode(ks_pcie);
@@ -481,17 +497,17 @@ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
* For BAR0, just setting bus address for inbound writes (MSI) should
* be sufficient. Use physical address to avoid any conflicts.
*/
- dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, ks_pcie->app.start);
+ dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, ks_pcie->app.start);
}
/**
* ks_dw_pcie_link_up() - Check if link up
*/
-int ks_dw_pcie_link_up(struct pcie_port *pp)
+int ks_dw_pcie_link_up(struct dw_pcie *pci)
{
u32 val;
- val = dw_pcie_readl_rc(pp, DEBUG0);
+ val = dw_pcie_readl_dbi(pci, DEBUG0);
return (val & LTSSM_STATE_MASK) == LTSSM_STATE_L0;
}
@@ -519,22 +535,23 @@ void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie)
int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
struct device_node *msi_intc_np)
{
- struct pcie_port *pp = &ks_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = ks_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
+ struct device *dev = pci->dev;
struct platform_device *pdev = to_platform_device(dev);
struct resource *res;
/* Index 0 is the config reg. space address */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pp->dbi_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(pp->dbi_base))
- return PTR_ERR(pp->dbi_base);
+ pci->dbi_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pci->dbi_base))
+ return PTR_ERR(pci->dbi_base);
/*
* We set these same and is used in pcie rd/wr_other_conf
* functions
*/
- pp->va_cfg0_base = pp->dbi_base + SPACE0_REMOTE_CFG_OFFSET;
+ pp->va_cfg0_base = pci->dbi_base + SPACE0_REMOTE_CFG_OFFSET;
pp->va_cfg1_base = pp->va_cfg0_base;
/* Index 1 is the application reg. space address */
diff --git a/drivers/pci/host/pci-keystone.c b/drivers/pci/dwc/pci-keystone.c
index 043c19a05da1..fcc9723bad6e 100644
--- a/drivers/pci/host/pci-keystone.c
+++ b/drivers/pci/dwc/pci-keystone.c
@@ -44,7 +44,7 @@
#define PCIE_RC_K2E 0xb009
#define PCIE_RC_K2L 0xb00a
-#define to_keystone_pcie(x) container_of(x, struct keystone_pcie, pp)
+#define to_keystone_pcie(x) dev_get_drvdata((x)->dev)
static void quirk_limit_mrrs(struct pci_dev *dev)
{
@@ -88,13 +88,14 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, quirk_limit_mrrs);
static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie)
{
- struct pcie_port *pp = &ks_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = ks_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
+ struct device *dev = pci->dev;
unsigned int retries;
dw_pcie_setup_rc(pp);
- if (dw_pcie_link_up(pp)) {
+ if (dw_pcie_link_up(pci)) {
dev_err(dev, "Link already up\n");
return 0;
}
@@ -102,7 +103,7 @@ static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie)
/* check if the link is up or not */
for (retries = 0; retries < 5; retries++) {
ks_dw_pcie_initiate_link_train(ks_pcie);
- if (!dw_pcie_wait_for_link(pp))
+ if (!dw_pcie_wait_for_link(pci))
return 0;
}
@@ -115,8 +116,8 @@ static void ks_pcie_msi_irq_handler(struct irq_desc *desc)
unsigned int irq = irq_desc_get_irq(desc);
struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc);
u32 offset = irq - ks_pcie->msi_host_irqs[0];
- struct pcie_port *pp = &ks_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = ks_pcie->pci;
+ struct device *dev = pci->dev;
struct irq_chip *chip = irq_desc_get_chip(desc);
dev_dbg(dev, "%s, irq %d\n", __func__, irq);
@@ -143,8 +144,8 @@ static void ks_pcie_legacy_irq_handler(struct irq_desc *desc)
{
unsigned int irq = irq_desc_get_irq(desc);
struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc);
- struct pcie_port *pp = &ks_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = ks_pcie->pci;
+ struct device *dev = pci->dev;
u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0];
struct irq_chip *chip = irq_desc_get_chip(desc);
@@ -164,7 +165,7 @@ static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie,
char *controller, int *num_irqs)
{
int temp, max_host_irqs, legacy = 1, *host_irqs;
- struct device *dev = ks_pcie->pp.dev;
+ struct device *dev = ks_pcie->pci->dev;
struct device_node *np_pcie = dev->of_node, **np_temp;
if (!strcmp(controller, "msi-interrupt-controller"))
@@ -262,24 +263,25 @@ static int keystone_pcie_fault(unsigned long addr, unsigned int fsr,
static void __init ks_pcie_host_init(struct pcie_port *pp)
{
- struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
u32 val;
ks_pcie_establish_link(ks_pcie);
ks_dw_pcie_setup_rc_app_regs(ks_pcie);
ks_pcie_setup_interrupts(ks_pcie);
writew(PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8),
- pp->dbi_base + PCI_IO_BASE);
+ pci->dbi_base + PCI_IO_BASE);
/* update the Vendor ID */
- writew(ks_pcie->device_id, pp->dbi_base + PCI_DEVICE_ID);
+ writew(ks_pcie->device_id, pci->dbi_base + PCI_DEVICE_ID);
/* update the DEV_STAT_CTRL to publish right mrrs */
- val = readl(pp->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL);
+ val = readl(pci->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL);
val &= ~PCI_EXP_DEVCTL_READRQ;
/* set the mrrs to 256 bytes */
val |= BIT(12);
- writel(val, pp->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL);
+ writel(val, pci->dbi_base + PCIE_CAP_BASE + PCI_EXP_DEVCTL);
/*
* PCIe access errors that result into OCP errors are caught by ARM as
@@ -289,10 +291,9 @@ static void __init ks_pcie_host_init(struct pcie_port *pp)
"Asynchronous external abort");
}
-static struct pcie_host_ops keystone_pcie_host_ops = {
+static struct dw_pcie_host_ops keystone_pcie_host_ops = {
.rd_other_conf = ks_dw_pcie_rd_other_conf,
.wr_other_conf = ks_dw_pcie_wr_other_conf,
- .link_up = ks_dw_pcie_link_up,
.host_init = ks_pcie_host_init,
.msi_set_irq = ks_dw_pcie_msi_set_irq,
.msi_clear_irq = ks_dw_pcie_msi_clear_irq,
@@ -311,8 +312,9 @@ static irqreturn_t pcie_err_irq_handler(int irq, void *priv)
static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
struct platform_device *pdev)
{
- struct pcie_port *pp = &ks_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = ks_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
+ struct device *dev = &pdev->dev;
int ret;
ret = ks_pcie_get_irq_controller_info(ks_pcie,
@@ -365,6 +367,10 @@ static const struct of_device_id ks_pcie_of_match[] = {
{ },
};
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .link_up = ks_dw_pcie_link_up,
+};
+
static int __exit ks_pcie_remove(struct platform_device *pdev)
{
struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev);
@@ -377,8 +383,8 @@ static int __exit ks_pcie_remove(struct platform_device *pdev)
static int __init ks_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct dw_pcie *pci;
struct keystone_pcie *ks_pcie;
- struct pcie_port *pp;
struct resource *res;
void __iomem *reg_p;
struct phy *phy;
@@ -388,8 +394,14 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
if (!ks_pcie)
return -ENOMEM;
- pp = &ks_pcie->pp;
- pp->dev = dev;
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pci->dev = dev;
+ pci->ops = &dw_pcie_ops;
+
+ ks_pcie->pci = pci;
/* initialize SerDes Phy if present */
phy = devm_phy_get(dev, "pcie-phy");
@@ -422,6 +434,8 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
if (ret)
return ret;
+ platform_set_drvdata(pdev, ks_pcie);
+
ret = ks_add_pcie_port(ks_pcie, pdev);
if (ret < 0)
goto fail_clk;
diff --git a/drivers/pci/host/pci-keystone.h b/drivers/pci/dwc/pci-keystone.h
index bc54bafda068..74c5825882df 100644
--- a/drivers/pci/host/pci-keystone.h
+++ b/drivers/pci/dwc/pci-keystone.h
@@ -17,7 +17,7 @@
#define MAX_LEGACY_HOST_IRQS 4
struct keystone_pcie {
- struct pcie_port pp; /* pp.dbi_base is DT 0th res */
+ struct dw_pcie *pci;
struct clk *clk;
/* PCI Device ID */
u32 device_id;
@@ -54,10 +54,10 @@ int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
unsigned int devfn, int where, int size, u32 *val);
void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie);
-int ks_dw_pcie_link_up(struct pcie_port *pp);
void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie);
void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq);
void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq);
void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp);
int ks_dw_pcie_msi_host_init(struct pcie_port *pp,
struct msi_controller *chip);
+int ks_dw_pcie_link_up(struct dw_pcie *pci);
diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/dwc/pci-layerscape.c
index 653707996342..c32e392a0ae6 100644
--- a/drivers/pci/host/pci-layerscape.c
+++ b/drivers/pci/dwc/pci-layerscape.c
@@ -35,30 +35,30 @@
#define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */
#define PCIE_DBI_RO_WR_EN 0x8bc /* DBI Read-Only Write Enable Register */
-/* PEX LUT registers */
-#define PCIE_LUT_DBG 0x7FC /* PEX LUT Debug Register */
-
struct ls_pcie_drvdata {
u32 lut_offset;
u32 ltssm_shift;
- struct pcie_host_ops *ops;
+ u32 lut_dbg;
+ struct dw_pcie_host_ops *ops;
+ const struct dw_pcie_ops *dw_pcie_ops;
};
struct ls_pcie {
- struct pcie_port pp; /* pp.dbi_base is DT regs */
+ struct dw_pcie *pci;
void __iomem *lut;
struct regmap *scfg;
const struct ls_pcie_drvdata *drvdata;
int index;
};
-#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp)
+#define to_ls_pcie(x) dev_get_drvdata((x)->dev)
static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
{
+ struct dw_pcie *pci = pcie->pci;
u32 header_type;
- header_type = ioread8(pcie->pp.dbi_base + PCI_HEADER_TYPE);
+ header_type = ioread8(pci->dbi_base + PCI_HEADER_TYPE);
header_type &= 0x7f;
return header_type == PCI_HEADER_TYPE_BRIDGE;
@@ -67,29 +67,34 @@ static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
/* Clear multi-function bit */
static void ls_pcie_clear_multifunction(struct ls_pcie *pcie)
{
- iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->pp.dbi_base + PCI_HEADER_TYPE);
+ struct dw_pcie *pci = pcie->pci;
+
+ iowrite8(PCI_HEADER_TYPE_BRIDGE, pci->dbi_base + PCI_HEADER_TYPE);
}
/* Fix class value */
static void ls_pcie_fix_class(struct ls_pcie *pcie)
{
- iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->pp.dbi_base + PCI_CLASS_DEVICE);
+ struct dw_pcie *pci = pcie->pci;
+
+ iowrite16(PCI_CLASS_BRIDGE_PCI, pci->dbi_base + PCI_CLASS_DEVICE);
}
/* Drop MSG TLP except for Vendor MSG */
static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie)
{
u32 val;
+ struct dw_pcie *pci = pcie->pci;
- val = ioread32(pcie->pp.dbi_base + PCIE_STRFMR1);
+ val = ioread32(pci->dbi_base + PCIE_STRFMR1);
val &= 0xDFFFFFFF;
- iowrite32(val, pcie->pp.dbi_base + PCIE_STRFMR1);
+ iowrite32(val, pci->dbi_base + PCIE_STRFMR1);
}
-static int ls1021_pcie_link_up(struct pcie_port *pp)
+static int ls1021_pcie_link_up(struct dw_pcie *pci)
{
u32 state;
- struct ls_pcie *pcie = to_ls_pcie(pp);
+ struct ls_pcie *pcie = to_ls_pcie(pci);
if (!pcie->scfg)
return 0;
@@ -105,8 +110,9 @@ static int ls1021_pcie_link_up(struct pcie_port *pp)
static void ls1021_pcie_host_init(struct pcie_port *pp)
{
- struct device *dev = pp->dev;
- struct ls_pcie *pcie = to_ls_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct ls_pcie *pcie = to_ls_pcie(pci);
+ struct device *dev = pci->dev;
u32 index[2];
pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node,
@@ -129,12 +135,12 @@ static void ls1021_pcie_host_init(struct pcie_port *pp)
ls_pcie_drop_msg_tlp(pcie);
}
-static int ls_pcie_link_up(struct pcie_port *pp)
+static int ls_pcie_link_up(struct dw_pcie *pci)
{
- struct ls_pcie *pcie = to_ls_pcie(pp);
+ struct ls_pcie *pcie = to_ls_pcie(pci);
u32 state;
- state = (ioread32(pcie->lut + PCIE_LUT_DBG) >>
+ state = (ioread32(pcie->lut + pcie->drvdata->lut_dbg) >>
pcie->drvdata->ltssm_shift) &
LTSSM_STATE_MASK;
@@ -146,19 +152,21 @@ static int ls_pcie_link_up(struct pcie_port *pp)
static void ls_pcie_host_init(struct pcie_port *pp)
{
- struct ls_pcie *pcie = to_ls_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct ls_pcie *pcie = to_ls_pcie(pci);
- iowrite32(1, pcie->pp.dbi_base + PCIE_DBI_RO_WR_EN);
+ iowrite32(1, pci->dbi_base + PCIE_DBI_RO_WR_EN);
ls_pcie_fix_class(pcie);
ls_pcie_clear_multifunction(pcie);
ls_pcie_drop_msg_tlp(pcie);
- iowrite32(0, pcie->pp.dbi_base + PCIE_DBI_RO_WR_EN);
+ iowrite32(0, pci->dbi_base + PCIE_DBI_RO_WR_EN);
}
static int ls_pcie_msi_host_init(struct pcie_port *pp,
struct msi_controller *chip)
{
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct device *dev = pci->dev;
struct device_node *np = dev->of_node;
struct device_node *msi_node;
@@ -177,37 +185,57 @@ static int ls_pcie_msi_host_init(struct pcie_port *pp,
return 0;
}
-static struct pcie_host_ops ls1021_pcie_host_ops = {
- .link_up = ls1021_pcie_link_up,
+static struct dw_pcie_host_ops ls1021_pcie_host_ops = {
.host_init = ls1021_pcie_host_init,
.msi_host_init = ls_pcie_msi_host_init,
};
-static struct pcie_host_ops ls_pcie_host_ops = {
- .link_up = ls_pcie_link_up,
+static struct dw_pcie_host_ops ls_pcie_host_ops = {
.host_init = ls_pcie_host_init,
.msi_host_init = ls_pcie_msi_host_init,
};
+static const struct dw_pcie_ops dw_ls1021_pcie_ops = {
+ .link_up = ls1021_pcie_link_up,
+};
+
+static const struct dw_pcie_ops dw_ls_pcie_ops = {
+ .link_up = ls_pcie_link_up,
+};
+
static struct ls_pcie_drvdata ls1021_drvdata = {
.ops = &ls1021_pcie_host_ops,
+ .dw_pcie_ops = &dw_ls1021_pcie_ops,
};
static struct ls_pcie_drvdata ls1043_drvdata = {
.lut_offset = 0x10000,
.ltssm_shift = 24,
+ .lut_dbg = 0x7fc,
.ops = &ls_pcie_host_ops,
+ .dw_pcie_ops = &dw_ls_pcie_ops,
+};
+
+static struct ls_pcie_drvdata ls1046_drvdata = {
+ .lut_offset = 0x80000,
+ .ltssm_shift = 24,
+ .lut_dbg = 0x407fc,
+ .ops = &ls_pcie_host_ops,
+ .dw_pcie_ops = &dw_ls_pcie_ops,
};
static struct ls_pcie_drvdata ls2080_drvdata = {
.lut_offset = 0x80000,
.ltssm_shift = 0,
+ .lut_dbg = 0x7fc,
.ops = &ls_pcie_host_ops,
+ .dw_pcie_ops = &dw_ls_pcie_ops,
};
static const struct of_device_id ls_pcie_of_match[] = {
{ .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata },
{ .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata },
+ { .compatible = "fsl,ls1046a-pcie", .data = &ls1046_drvdata },
{ .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata },
{ .compatible = "fsl,ls2085a-pcie", .data = &ls2080_drvdata },
{ },
@@ -215,10 +243,13 @@ static const struct of_device_id ls_pcie_of_match[] = {
static int __init ls_add_pcie_port(struct ls_pcie *pcie)
{
- struct pcie_port *pp = &pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = pcie->pci;
+ struct pcie_port *pp = &pci->pp;
+ struct device *dev = pci->dev;
int ret;
+ pp->ops = pcie->drvdata->ops;
+
ret = dw_pcie_host_init(pp);
if (ret) {
dev_err(dev, "failed to initialize host\n");
@@ -231,37 +262,38 @@ static int __init ls_add_pcie_port(struct ls_pcie *pcie)
static int __init ls_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- const struct of_device_id *match;
+ struct dw_pcie *pci;
struct ls_pcie *pcie;
- struct pcie_port *pp;
struct resource *dbi_base;
int ret;
- match = of_match_device(ls_pcie_of_match, dev);
- if (!match)
- return -ENODEV;
-
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
if (!pcie)
return -ENOMEM;
- pp = &pcie->pp;
- pp->dev = dev;
- pcie->drvdata = match->data;
- pp->ops = pcie->drvdata->ops;
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pcie->drvdata = of_device_get_match_data(dev);
+
+ pci->dev = dev;
+ pci->ops = pcie->drvdata->dw_pcie_ops;
+
+ pcie->pci = pci;
dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
- pcie->pp.dbi_base = devm_ioremap_resource(dev, dbi_base);
- if (IS_ERR(pcie->pp.dbi_base)) {
- dev_err(dev, "missing *regs* space\n");
- return PTR_ERR(pcie->pp.dbi_base);
- }
+ pci->dbi_base = devm_ioremap_resource(dev, dbi_base);
+ if (IS_ERR(pci->dbi_base))
+ return PTR_ERR(pci->dbi_base);
- pcie->lut = pcie->pp.dbi_base + pcie->drvdata->lut_offset;
+ pcie->lut = pci->dbi_base + pcie->drvdata->lut_offset;
if (!ls_pcie_is_bridge(pcie))
return -ENODEV;
+ platform_set_drvdata(pdev, pcie);
+
ret = ls_add_pcie_port(pcie);
if (ret < 0)
return ret;
diff --git a/drivers/pci/host/pcie-armada8k.c b/drivers/pci/dwc/pcie-armada8k.c
index 0ac0f18690f2..f110e3b24a26 100644
--- a/drivers/pci/host/pcie-armada8k.c
+++ b/drivers/pci/dwc/pcie-armada8k.c
@@ -29,7 +29,7 @@
#include "pcie-designware.h"
struct armada8k_pcie {
- struct pcie_port pp; /* pp.dbi_base is DT ctrl */
+ struct dw_pcie *pci;
struct clk *clk;
};
@@ -67,76 +67,77 @@ struct armada8k_pcie {
#define AX_USER_DOMAIN_MASK 0x3
#define AX_USER_DOMAIN_SHIFT 4
-#define to_armada8k_pcie(x) container_of(x, struct armada8k_pcie, pp)
+#define to_armada8k_pcie(x) dev_get_drvdata((x)->dev)
-static int armada8k_pcie_link_up(struct pcie_port *pp)
+static int armada8k_pcie_link_up(struct dw_pcie *pci)
{
u32 reg;
u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP;
- reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_STATUS_REG);
+ reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_STATUS_REG);
if ((reg & mask) == mask)
return 1;
- dev_dbg(pp->dev, "No link detected (Global-Status: 0x%08x).\n", reg);
+ dev_dbg(pci->dev, "No link detected (Global-Status: 0x%08x).\n", reg);
return 0;
}
static void armada8k_pcie_establish_link(struct armada8k_pcie *pcie)
{
- struct pcie_port *pp = &pcie->pp;
+ struct dw_pcie *pci = pcie->pci;
u32 reg;
- if (!dw_pcie_link_up(pp)) {
+ if (!dw_pcie_link_up(pci)) {
/* Disable LTSSM state machine to enable configuration */
- reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG);
+ reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG);
reg &= ~(PCIE_APP_LTSSM_EN);
- dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg);
+ dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg);
}
/* Set the device to root complex mode */
- reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG);
+ reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG);
reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT);
reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT;
- dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg);
+ dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg);
/* Set the PCIe master AxCache attributes */
- dw_pcie_writel_rc(pp, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE);
- dw_pcie_writel_rc(pp, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE);
+ dw_pcie_writel_dbi(pci, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE);
+ dw_pcie_writel_dbi(pci, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE);
/* Set the PCIe master AxDomain attributes */
- reg = dw_pcie_readl_rc(pp, PCIE_ARUSER_REG);
+ reg = dw_pcie_readl_dbi(pci, PCIE_ARUSER_REG);
reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
- dw_pcie_writel_rc(pp, PCIE_ARUSER_REG, reg);
+ dw_pcie_writel_dbi(pci, PCIE_ARUSER_REG, reg);
- reg = dw_pcie_readl_rc(pp, PCIE_AWUSER_REG);
+ reg = dw_pcie_readl_dbi(pci, PCIE_AWUSER_REG);
reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
- dw_pcie_writel_rc(pp, PCIE_AWUSER_REG, reg);
+ dw_pcie_writel_dbi(pci, PCIE_AWUSER_REG, reg);
/* Enable INT A-D interrupts */
- reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_INT_MASK1_REG);
+ reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG);
reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK |
PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK;
- dw_pcie_writel_rc(pp, PCIE_GLOBAL_INT_MASK1_REG, reg);
+ dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG, reg);
- if (!dw_pcie_link_up(pp)) {
+ if (!dw_pcie_link_up(pci)) {
/* Configuration done. Start LTSSM */
- reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG);
+ reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG);
reg |= PCIE_APP_LTSSM_EN;
- dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg);
+ dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg);
}
/* Wait until the link becomes active again */
- if (dw_pcie_wait_for_link(pp))
- dev_err(pp->dev, "Link not up after reconfiguration\n");
+ if (dw_pcie_wait_for_link(pci))
+ dev_err(pci->dev, "Link not up after reconfiguration\n");
}
static void armada8k_pcie_host_init(struct pcie_port *pp)
{
- struct armada8k_pcie *pcie = to_armada8k_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct armada8k_pcie *pcie = to_armada8k_pcie(pci);
dw_pcie_setup_rc(pp);
armada8k_pcie_establish_link(pcie);
@@ -145,7 +146,7 @@ static void armada8k_pcie_host_init(struct pcie_port *pp)
static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg)
{
struct armada8k_pcie *pcie = arg;
- struct pcie_port *pp = &pcie->pp;
+ struct dw_pcie *pci = pcie->pci;
u32 val;
/*
@@ -153,21 +154,21 @@ static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg)
* PCI device. However, they are also latched into the PCIe
* controller, so we simply discard them.
*/
- val = dw_pcie_readl_rc(pp, PCIE_GLOBAL_INT_CAUSE1_REG);
- dw_pcie_writel_rc(pp, PCIE_GLOBAL_INT_CAUSE1_REG, val);
+ val = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG);
+ dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG, val);
return IRQ_HANDLED;
}
-static struct pcie_host_ops armada8k_pcie_host_ops = {
- .link_up = armada8k_pcie_link_up,
+static struct dw_pcie_host_ops armada8k_pcie_host_ops = {
.host_init = armada8k_pcie_host_init,
};
static int armada8k_add_pcie_port(struct armada8k_pcie *pcie,
struct platform_device *pdev)
{
- struct pcie_port *pp = &pcie->pp;
+ struct dw_pcie *pci = pcie->pci;
+ struct pcie_port *pp = &pci->pp;
struct device *dev = &pdev->dev;
int ret;
@@ -196,10 +197,14 @@ static int armada8k_add_pcie_port(struct armada8k_pcie *pcie,
return 0;
}
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .link_up = armada8k_pcie_link_up,
+};
+
static int armada8k_pcie_probe(struct platform_device *pdev)
{
+ struct dw_pcie *pci;
struct armada8k_pcie *pcie;
- struct pcie_port *pp;
struct device *dev = &pdev->dev;
struct resource *base;
int ret;
@@ -208,24 +213,32 @@ static int armada8k_pcie_probe(struct platform_device *pdev)
if (!pcie)
return -ENOMEM;
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pci->dev = dev;
+ pci->ops = &dw_pcie_ops;
+
+ pcie->pci = pci;
+
pcie->clk = devm_clk_get(dev, NULL);
if (IS_ERR(pcie->clk))
return PTR_ERR(pcie->clk);
clk_prepare_enable(pcie->clk);
- pp = &pcie->pp;
- pp->dev = dev;
-
/* Get the dw-pcie unit configuration/control registers base. */
base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
- pp->dbi_base = devm_ioremap_resource(dev, base);
- if (IS_ERR(pp->dbi_base)) {
+ pci->dbi_base = devm_ioremap_resource(dev, base);
+ if (IS_ERR(pci->dbi_base)) {
dev_err(dev, "couldn't remap regs base %p\n", base);
- ret = PTR_ERR(pp->dbi_base);
+ ret = PTR_ERR(pci->dbi_base);
goto fail;
}
+ platform_set_drvdata(pdev, pcie);
+
ret = armada8k_add_pcie_port(pcie, pdev);
if (ret)
goto fail;
diff --git a/drivers/pci/host/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c
index 212786b27f1a..fcd3ef845883 100644
--- a/drivers/pci/host/pcie-artpec6.c
+++ b/drivers/pci/dwc/pcie-artpec6.c
@@ -24,10 +24,10 @@
#include "pcie-designware.h"
-#define to_artpec6_pcie(x) container_of(x, struct artpec6_pcie, pp)
+#define to_artpec6_pcie(x) dev_get_drvdata((x)->dev)
struct artpec6_pcie {
- struct pcie_port pp; /* pp.dbi_base is DT dbi */
+ struct dw_pcie *pci;
struct regmap *regmap; /* DT axis,syscon-pcie */
void __iomem *phy_base; /* DT phy */
};
@@ -80,7 +80,8 @@ static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u
static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
{
- struct pcie_port *pp = &artpec6_pcie->pp;
+ struct dw_pcie *pci = artpec6_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
u32 val;
unsigned int retries;
@@ -139,7 +140,7 @@ static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
* Enable writing to config regs. This is required as the Synopsys
* driver changes the class code. That register needs DBI write enable.
*/
- dw_pcie_writel_rc(pp, MISC_CONTROL_1_OFF, DBI_RO_WR_EN);
+ dw_pcie_writel_dbi(pci, MISC_CONTROL_1_OFF, DBI_RO_WR_EN);
pp->io_base &= ARTPEC6_CPU_TO_BUS_ADDR;
pp->mem_base &= ARTPEC6_CPU_TO_BUS_ADDR;
@@ -155,19 +156,20 @@ static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
/* check if the link is up or not */
- if (!dw_pcie_wait_for_link(pp))
+ if (!dw_pcie_wait_for_link(pci))
return 0;
- dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
- dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0),
- dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1));
+ dev_dbg(pci->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
+ dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0),
+ dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1));
return -ETIMEDOUT;
}
static void artpec6_pcie_enable_interrupts(struct artpec6_pcie *artpec6_pcie)
{
- struct pcie_port *pp = &artpec6_pcie->pp;
+ struct dw_pcie *pci = artpec6_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
if (IS_ENABLED(CONFIG_PCI_MSI))
dw_pcie_msi_init(pp);
@@ -175,20 +177,22 @@ static void artpec6_pcie_enable_interrupts(struct artpec6_pcie *artpec6_pcie)
static void artpec6_pcie_host_init(struct pcie_port *pp)
{
- struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci);
artpec6_pcie_establish_link(artpec6_pcie);
artpec6_pcie_enable_interrupts(artpec6_pcie);
}
-static struct pcie_host_ops artpec6_pcie_host_ops = {
+static struct dw_pcie_host_ops artpec6_pcie_host_ops = {
.host_init = artpec6_pcie_host_init,
};
static irqreturn_t artpec6_pcie_msi_handler(int irq, void *arg)
{
struct artpec6_pcie *artpec6_pcie = arg;
- struct pcie_port *pp = &artpec6_pcie->pp;
+ struct dw_pcie *pci = artpec6_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
return dw_handle_msi_irq(pp);
}
@@ -196,8 +200,9 @@ static irqreturn_t artpec6_pcie_msi_handler(int irq, void *arg)
static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie,
struct platform_device *pdev)
{
- struct pcie_port *pp = &artpec6_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = artpec6_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
+ struct device *dev = pci->dev;
int ret;
if (IS_ENABLED(CONFIG_PCI_MSI)) {
@@ -232,8 +237,8 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie,
static int artpec6_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct dw_pcie *pci;
struct artpec6_pcie *artpec6_pcie;
- struct pcie_port *pp;
struct resource *dbi_base;
struct resource *phy_base;
int ret;
@@ -242,13 +247,18 @@ static int artpec6_pcie_probe(struct platform_device *pdev)
if (!artpec6_pcie)
return -ENOMEM;
- pp = &artpec6_pcie->pp;
- pp->dev = dev;
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pci->dev = dev;
+
+ artpec6_pcie->pci = pci;
dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
- pp->dbi_base = devm_ioremap_resource(dev, dbi_base);
- if (IS_ERR(pp->dbi_base))
- return PTR_ERR(pp->dbi_base);
+ pci->dbi_base = devm_ioremap_resource(dev, dbi_base);
+ if (IS_ERR(pci->dbi_base))
+ return PTR_ERR(pci->dbi_base);
phy_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
artpec6_pcie->phy_base = devm_ioremap_resource(dev, phy_base);
@@ -261,6 +271,8 @@ static int artpec6_pcie_probe(struct platform_device *pdev)
if (IS_ERR(artpec6_pcie->regmap))
return PTR_ERR(artpec6_pcie->regmap);
+ platform_set_drvdata(pdev, artpec6_pcie);
+
ret = artpec6_add_pcie_port(artpec6_pcie, pdev);
if (ret < 0)
return ret;
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/dwc/pcie-designware-host.c
index bed19994c1e9..5ba334938b52 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/dwc/pcie-designware-host.c
@@ -11,239 +11,38 @@
* published by the Free Software Foundation.
*/
-#include <linux/irq.h>
#include <linux/irqdomain.h>
-#include <linux/kernel.h>
-#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
-#include <linux/pci.h>
#include <linux/pci_regs.h>
#include <linux/platform_device.h>
-#include <linux/types.h>
-#include <linux/delay.h>
#include "pcie-designware.h"
-/* Parameters for the waiting for link up routine */
-#define LINK_WAIT_MAX_RETRIES 10
-#define LINK_WAIT_USLEEP_MIN 90000
-#define LINK_WAIT_USLEEP_MAX 100000
-
-/* Parameters for the waiting for iATU enabled routine */
-#define LINK_WAIT_MAX_IATU_RETRIES 5
-#define LINK_WAIT_IATU_MIN 9000
-#define LINK_WAIT_IATU_MAX 10000
-
-/* Synopsys-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 PORT_LINK_MODE_8_LANES (0xf << 16)
-
-#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
-#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
-#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 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 PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8)
-
-#define PCIE_MSI_ADDR_LO 0x820
-#define PCIE_MSI_ADDR_HI 0x824
-#define PCIE_MSI_INTR0_ENABLE 0x828
-#define PCIE_MSI_INTR0_MASK 0x82C
-#define PCIE_MSI_INTR0_STATUS 0x830
-
-#define PCIE_ATU_VIEWPORT 0x900
-#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
-#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
-#define PCIE_ATU_REGION_INDEX2 (0x2 << 0)
-#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
-#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
-#define PCIE_ATU_CR1 0x904
-#define PCIE_ATU_TYPE_MEM (0x0 << 0)
-#define PCIE_ATU_TYPE_IO (0x2 << 0)
-#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
-#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
-#define PCIE_ATU_CR2 0x908
-#define PCIE_ATU_ENABLE (0x1 << 31)
-#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
-#define PCIE_ATU_LOWER_BASE 0x90C
-#define PCIE_ATU_UPPER_BASE 0x910
-#define PCIE_ATU_LIMIT 0x914
-#define PCIE_ATU_LOWER_TARGET 0x918
-#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
-#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
-#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
-#define PCIE_ATU_UPPER_TARGET 0x91C
-
-/*
- * iATU Unroll-specific register definitions
- * From 4.80 core version the address translation will be made by unroll
- */
-#define PCIE_ATU_UNR_REGION_CTRL1 0x00
-#define PCIE_ATU_UNR_REGION_CTRL2 0x04
-#define PCIE_ATU_UNR_LOWER_BASE 0x08
-#define PCIE_ATU_UNR_UPPER_BASE 0x0C
-#define PCIE_ATU_UNR_LIMIT 0x10
-#define PCIE_ATU_UNR_LOWER_TARGET 0x14
-#define PCIE_ATU_UNR_UPPER_TARGET 0x18
-
-/* Register address builder */
-#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) ((0x3 << 20) | (region << 9))
-
-/* PCIe Port Logic registers */
-#define PLR_OFFSET 0x700
-#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c)
-#define PCIE_PHY_DEBUG_R1_LINK_UP (0x1 << 4)
-#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (0x1 << 29)
-
static struct pci_ops dw_pcie_ops;
-int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
-{
- if ((uintptr_t)addr & (size - 1)) {
- *val = 0;
- return PCIBIOS_BAD_REGISTER_NUMBER;
- }
-
- if (size == 4)
- *val = readl(addr);
- else if (size == 2)
- *val = readw(addr);
- else if (size == 1)
- *val = readb(addr);
- else {
- *val = 0;
- return PCIBIOS_BAD_REGISTER_NUMBER;
- }
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val)
-{
- if ((uintptr_t)addr & (size - 1))
- return PCIBIOS_BAD_REGISTER_NUMBER;
-
- if (size == 4)
- writel(val, addr);
- else if (size == 2)
- writew(val, addr);
- else if (size == 1)
- writeb(val, addr);
- else
- return PCIBIOS_BAD_REGISTER_NUMBER;
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg)
-{
- if (pp->ops->readl_rc)
- return pp->ops->readl_rc(pp, reg);
-
- return readl(pp->dbi_base + reg);
-}
-
-void dw_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val)
-{
- if (pp->ops->writel_rc)
- pp->ops->writel_rc(pp, reg, val);
- else
- writel(val, pp->dbi_base + reg);
-}
-
-static u32 dw_pcie_readl_unroll(struct pcie_port *pp, u32 index, u32 reg)
-{
- u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
-
- return dw_pcie_readl_rc(pp, offset + reg);
-}
-
-static void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index, u32 reg,
- u32 val)
-{
- u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
-
- dw_pcie_writel_rc(pp, offset + reg, val);
-}
-
static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
u32 *val)
{
+ struct dw_pcie *pci;
+
if (pp->ops->rd_own_conf)
return pp->ops->rd_own_conf(pp, where, size, val);
- return dw_pcie_cfg_read(pp->dbi_base + where, size, val);
+ pci = to_dw_pcie_from_pp(pp);
+ return dw_pcie_read(pci->dbi_base + where, size, val);
}
static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
u32 val)
{
+ struct dw_pcie *pci;
+
if (pp->ops->wr_own_conf)
return pp->ops->wr_own_conf(pp, where, size, val);
- return dw_pcie_cfg_write(pp->dbi_base + where, size, val);
-}
-
-static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
- int type, u64 cpu_addr, u64 pci_addr, u32 size)
-{
- u32 retries, val;
-
- if (pp->iatu_unroll_enabled) {
- dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LOWER_BASE,
- lower_32_bits(cpu_addr));
- dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_UPPER_BASE,
- upper_32_bits(cpu_addr));
- dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LIMIT,
- lower_32_bits(cpu_addr + size - 1));
- dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LOWER_TARGET,
- lower_32_bits(pci_addr));
- dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_UPPER_TARGET,
- upper_32_bits(pci_addr));
- dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_REGION_CTRL1,
- type);
- dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_REGION_CTRL2,
- PCIE_ATU_ENABLE);
- } else {
- dw_pcie_writel_rc(pp, PCIE_ATU_VIEWPORT,
- PCIE_ATU_REGION_OUTBOUND | index);
- dw_pcie_writel_rc(pp, PCIE_ATU_LOWER_BASE,
- lower_32_bits(cpu_addr));
- dw_pcie_writel_rc(pp, PCIE_ATU_UPPER_BASE,
- upper_32_bits(cpu_addr));
- dw_pcie_writel_rc(pp, PCIE_ATU_LIMIT,
- lower_32_bits(cpu_addr + size - 1));
- dw_pcie_writel_rc(pp, PCIE_ATU_LOWER_TARGET,
- lower_32_bits(pci_addr));
- dw_pcie_writel_rc(pp, PCIE_ATU_UPPER_TARGET,
- upper_32_bits(pci_addr));
- dw_pcie_writel_rc(pp, PCIE_ATU_CR1, type);
- dw_pcie_writel_rc(pp, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
- }
-
- /*
- * Make sure ATU enable takes effect before any subsequent config
- * and I/O accesses.
- */
- for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
- if (pp->iatu_unroll_enabled)
- val = dw_pcie_readl_unroll(pp, index,
- PCIE_ATU_UNR_REGION_CTRL2);
- else
- val = dw_pcie_readl_rc(pp, PCIE_ATU_CR2);
-
- if (val == PCIE_ATU_ENABLE)
- return;
-
- usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX);
- }
- dev_err(pp->dev, "iATU is not being enabled\n");
+ pci = to_dw_pcie_from_pp(pp);
+ return dw_pcie_write(pci->dbi_base + where, size, val);
}
static struct irq_chip dw_msi_irq_chip = {
@@ -263,16 +62,15 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
for (i = 0; i < MAX_MSI_CTRLS; i++) {
dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4,
- (u32 *)&val);
+ (u32 *)&val);
if (val) {
ret = IRQ_HANDLED;
pos = 0;
while ((pos = find_next_bit(&val, 32, pos)) != 32) {
irq = irq_find_mapping(pp->irq_domain,
- i * 32 + pos);
- dw_pcie_wr_own_conf(pp,
- PCIE_MSI_INTR0_STATUS + i * 12,
- 4, 1 << pos);
+ i * 32 + pos);
+ dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS +
+ i * 12, 4, 1 << pos);
generic_handle_irq(irq);
pos++;
}
@@ -338,8 +136,9 @@ static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
{
int irq, pos0, i;
- struct pcie_port *pp = (struct pcie_port *) msi_desc_to_pci_sysdata(desc);
+ struct pcie_port *pp;
+ pp = (struct pcie_port *)msi_desc_to_pci_sysdata(desc);
pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS,
order_base_2(no_irqs));
if (pos0 < 0)
@@ -401,7 +200,7 @@ static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos)
}
static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
- struct msi_desc *desc)
+ struct msi_desc *desc)
{
int irq, pos;
struct pcie_port *pp = pdev->bus->sysdata;
@@ -449,7 +248,7 @@ static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
{
struct irq_data *data = irq_get_irq_data(irq);
struct msi_desc *msi = irq_data_get_msi_desc(data);
- struct pcie_port *pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi);
+ struct pcie_port *pp = (struct pcie_port *)msi_desc_to_pci_sysdata(msi);
clear_irq_range(pp, irq, 1, data->hwirq);
}
@@ -460,38 +259,8 @@ static struct msi_controller dw_pcie_msi_chip = {
.teardown_irq = dw_msi_teardown_irq,
};
-int dw_pcie_wait_for_link(struct pcie_port *pp)
-{
- int retries;
-
- /* check if the link is up or not */
- for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
- if (dw_pcie_link_up(pp)) {
- dev_info(pp->dev, "link up\n");
- return 0;
- }
- usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
- }
-
- dev_err(pp->dev, "phy link never came up\n");
-
- return -ETIMEDOUT;
-}
-
-int dw_pcie_link_up(struct pcie_port *pp)
-{
- u32 val;
-
- if (pp->ops->link_up)
- return pp->ops->link_up(pp);
-
- val = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
- return ((val & PCIE_PHY_DEBUG_R1_LINK_UP) &&
- (!(val & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING)));
-}
-
static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
- irq_hw_number_t hwirq)
+ irq_hw_number_t hwirq)
{
irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq);
irq_set_chip_data(irq, domain->host_data);
@@ -503,21 +272,12 @@ static const struct irq_domain_ops msi_domain_ops = {
.map = dw_pcie_msi_map,
};
-static u8 dw_pcie_iatu_unroll_enabled(struct pcie_port *pp)
-{
- u32 val;
-
- val = dw_pcie_readl_rc(pp, PCIE_ATU_VIEWPORT);
- if (val == 0xffffffff)
- return 1;
-
- return 0;
-}
-
int dw_pcie_host_init(struct pcie_port *pp)
{
- struct device_node *np = pp->dev->of_node;
- struct platform_device *pdev = to_platform_device(pp->dev);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct device *dev = pci->dev;
+ struct device_node *np = dev->of_node;
+ struct platform_device *pdev = to_platform_device(dev);
struct pci_bus *bus, *child;
struct resource *cfg_res;
int i, ret;
@@ -526,19 +286,19 @@ int dw_pcie_host_init(struct pcie_port *pp)
cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
if (cfg_res) {
- pp->cfg0_size = resource_size(cfg_res)/2;
- pp->cfg1_size = resource_size(cfg_res)/2;
+ pp->cfg0_size = resource_size(cfg_res) / 2;
+ pp->cfg1_size = resource_size(cfg_res) / 2;
pp->cfg0_base = cfg_res->start;
pp->cfg1_base = cfg_res->start + pp->cfg0_size;
} else if (!pp->va_cfg0_base) {
- dev_err(pp->dev, "missing *config* reg space\n");
+ dev_err(dev, "missing *config* reg space\n");
}
ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &pp->io_base);
if (ret)
return ret;
- ret = devm_request_pci_bus_resources(&pdev->dev, &res);
+ ret = devm_request_pci_bus_resources(dev, &res);
if (ret)
goto error;
@@ -548,7 +308,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
case IORESOURCE_IO:
ret = pci_remap_iospace(win->res, pp->io_base);
if (ret) {
- dev_warn(pp->dev, "error %d: failed to map resource %pR\n",
+ dev_warn(dev, "error %d: failed to map resource %pR\n",
ret, win->res);
resource_list_destroy_entry(win);
} else {
@@ -566,8 +326,8 @@ int dw_pcie_host_init(struct pcie_port *pp)
break;
case 0:
pp->cfg = win->res;
- pp->cfg0_size = resource_size(pp->cfg)/2;
- pp->cfg1_size = resource_size(pp->cfg)/2;
+ pp->cfg0_size = resource_size(pp->cfg) / 2;
+ pp->cfg1_size = resource_size(pp->cfg) / 2;
pp->cfg0_base = pp->cfg->start;
pp->cfg1_base = pp->cfg->start + pp->cfg0_size;
break;
@@ -577,11 +337,11 @@ int dw_pcie_host_init(struct pcie_port *pp)
}
}
- if (!pp->dbi_base) {
- pp->dbi_base = devm_ioremap(pp->dev, pp->cfg->start,
+ if (!pci->dbi_base) {
+ pci->dbi_base = devm_ioremap(dev, pp->cfg->start,
resource_size(pp->cfg));
- if (!pp->dbi_base) {
- dev_err(pp->dev, "error with ioremap\n");
+ if (!pci->dbi_base) {
+ dev_err(dev, "error with ioremap\n");
ret = -ENOMEM;
goto error;
}
@@ -590,40 +350,36 @@ int dw_pcie_host_init(struct pcie_port *pp)
pp->mem_base = pp->mem->start;
if (!pp->va_cfg0_base) {
- pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base,
+ pp->va_cfg0_base = devm_ioremap(dev, pp->cfg0_base,
pp->cfg0_size);
if (!pp->va_cfg0_base) {
- dev_err(pp->dev, "error with ioremap in function\n");
+ dev_err(dev, "error with ioremap in function\n");
ret = -ENOMEM;
goto error;
}
}
if (!pp->va_cfg1_base) {
- pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base,
+ pp->va_cfg1_base = devm_ioremap(dev, pp->cfg1_base,
pp->cfg1_size);
if (!pp->va_cfg1_base) {
- dev_err(pp->dev, "error with ioremap\n");
+ dev_err(dev, "error with ioremap\n");
ret = -ENOMEM;
goto error;
}
}
- ret = of_property_read_u32(np, "num-lanes", &pp->lanes);
+ ret = of_property_read_u32(np, "num-viewport", &pci->num_viewport);
if (ret)
- pp->lanes = 0;
-
- ret = of_property_read_u32(np, "num-viewport", &pp->num_viewport);
- if (ret)
- pp->num_viewport = 2;
+ pci->num_viewport = 2;
if (IS_ENABLED(CONFIG_PCI_MSI)) {
if (!pp->ops->msi_host_init) {
- pp->irq_domain = irq_domain_add_linear(pp->dev->of_node,
+ pp->irq_domain = irq_domain_add_linear(dev->of_node,
MAX_MSI_IRQS, &msi_domain_ops,
&dw_pcie_msi_chip);
if (!pp->irq_domain) {
- dev_err(pp->dev, "irq domain init failed\n");
+ dev_err(dev, "irq domain init failed\n");
ret = -ENXIO;
goto error;
}
@@ -642,12 +398,12 @@ int dw_pcie_host_init(struct pcie_port *pp)
pp->root_bus_nr = pp->busn->start;
if (IS_ENABLED(CONFIG_PCI_MSI)) {
- bus = pci_scan_root_bus_msi(pp->dev, pp->root_bus_nr,
+ bus = pci_scan_root_bus_msi(dev, pp->root_bus_nr,
&dw_pcie_ops, pp, &res,
&dw_pcie_msi_chip);
- dw_pcie_msi_chip.dev = pp->dev;
+ dw_pcie_msi_chip.dev = dev;
} else
- bus = pci_scan_root_bus(pp->dev, pp->root_bus_nr, &dw_pcie_ops,
+ bus = pci_scan_root_bus(dev, pp->root_bus_nr, &dw_pcie_ops,
pp, &res);
if (!bus) {
ret = -ENOMEM;
@@ -677,12 +433,13 @@ error:
}
static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
- u32 devfn, int where, int size, u32 *val)
+ u32 devfn, int where, int size, u32 *val)
{
int ret, type;
u32 busdev, cfg_size;
u64 cpu_addr;
void __iomem *va_cfg_base;
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
if (pp->ops->rd_other_conf)
return pp->ops->rd_other_conf(pp, bus, devfn, where, size, val);
@@ -702,12 +459,12 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
va_cfg_base = pp->va_cfg1_base;
}
- dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
+ dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
type, cpu_addr,
busdev, cfg_size);
- ret = dw_pcie_cfg_read(va_cfg_base + where, size, val);
- if (pp->num_viewport <= 2)
- dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
+ ret = dw_pcie_read(va_cfg_base + where, size, val);
+ if (pci->num_viewport <= 2)
+ dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
PCIE_ATU_TYPE_IO, pp->io_base,
pp->io_bus_addr, pp->io_size);
@@ -715,12 +472,13 @@ static int dw_pcie_rd_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)
+ u32 devfn, int where, int size, u32 val)
{
int ret, type;
u32 busdev, cfg_size;
u64 cpu_addr;
void __iomem *va_cfg_base;
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
if (pp->ops->wr_other_conf)
return pp->ops->wr_other_conf(pp, bus, devfn, where, size, val);
@@ -740,12 +498,12 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
va_cfg_base = pp->va_cfg1_base;
}
- dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
+ dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
type, cpu_addr,
busdev, cfg_size);
- ret = dw_pcie_cfg_write(va_cfg_base + where, size, val);
- if (pp->num_viewport <= 2)
- dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
+ ret = dw_pcie_write(va_cfg_base + where, size, val);
+ if (pci->num_viewport <= 2)
+ dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
PCIE_ATU_TYPE_IO, pp->io_base,
pp->io_bus_addr, pp->io_size);
@@ -755,9 +513,11 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus,
int dev)
{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+
/* If there is no link, then there is no device */
if (bus->number != pp->root_bus_nr) {
- if (!dw_pcie_link_up(pp))
+ if (!dw_pcie_link_up(pci))
return 0;
}
@@ -769,7 +529,7 @@ static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus,
}
static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
- int size, u32 *val)
+ int size, u32 *val)
{
struct pcie_port *pp = bus->sysdata;
@@ -785,7 +545,7 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
}
static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
- int where, int size, u32 val)
+ int where, int size, u32 val)
{
struct pcie_port *pp = bus->sysdata;
@@ -803,78 +563,46 @@ static struct pci_ops dw_pcie_ops = {
.write = dw_pcie_wr_conf,
};
+static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci)
+{
+ u32 val;
+
+ val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT);
+ if (val == 0xffffffff)
+ return 1;
+
+ return 0;
+}
+
void dw_pcie_setup_rc(struct pcie_port *pp)
{
u32 val;
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
- /* get iATU unroll support */
- pp->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pp);
- dev_dbg(pp->dev, "iATU unroll: %s\n",
- pp->iatu_unroll_enabled ? "enabled" : "disabled");
-
- /* set the number of lanes */
- val = dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL);
- val &= ~PORT_LINK_MODE_MASK;
- 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;
- case 8:
- val |= PORT_LINK_MODE_8_LANES;
- break;
- default:
- dev_err(pp->dev, "num-lanes %u: invalid value\n", pp->lanes);
- return;
- }
- dw_pcie_writel_rc(pp, PCIE_PORT_LINK_CONTROL, val);
-
- /* set link width speed control register */
- val = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
- val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
- 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;
- case 8:
- val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
- break;
- }
- dw_pcie_writel_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+ dw_pcie_setup(pci);
/* setup RC BARs */
- dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 0x00000004);
- dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_1, 0x00000000);
+ dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004);
+ dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000);
/* setup interrupt pins */
- val = dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE);
+ val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE);
val &= 0xffff00ff;
val |= 0x00000100;
- dw_pcie_writel_rc(pp, PCI_INTERRUPT_LINE, val);
+ dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val);
/* setup bus numbers */
- val = dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS);
+ val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS);
val &= 0xff000000;
val |= 0x00010100;
- dw_pcie_writel_rc(pp, PCI_PRIMARY_BUS, val);
+ dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val);
/* setup command register */
- val = dw_pcie_readl_rc(pp, PCI_COMMAND);
+ val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
val &= 0xffff0000;
val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
- dw_pcie_writel_rc(pp, PCI_COMMAND, val);
+ dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
/*
* If the platform provides ->rd_other_conf, it means the platform
@@ -882,11 +610,16 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
* we should not program the ATU here.
*/
if (!pp->ops->rd_other_conf) {
- dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
+ /* get iATU unroll support */
+ pci->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pci);
+ dev_dbg(pci->dev, "iATU unroll: %s\n",
+ pci->iatu_unroll_enabled ? "enabled" : "disabled");
+
+ dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0,
PCIE_ATU_TYPE_MEM, pp->mem_base,
pp->mem_bus_addr, pp->mem_size);
- if (pp->num_viewport > 2)
- dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX2,
+ if (pci->num_viewport > 2)
+ dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX2,
PCIE_ATU_TYPE_IO, pp->io_base,
pp->io_bus_addr, pp->io_size);
}
diff --git a/drivers/pci/host/pcie-designware-plat.c b/drivers/pci/dwc/pcie-designware-plat.c
index 1a02038c4640..b6c832ba39dd 100644
--- a/drivers/pci/host/pcie-designware-plat.c
+++ b/drivers/pci/dwc/pcie-designware-plat.c
@@ -25,7 +25,7 @@
#include "pcie-designware.h"
struct dw_plat_pcie {
- struct pcie_port pp; /* pp.dbi_base is DT 0th resource */
+ struct dw_pcie *pci;
};
static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg)
@@ -37,21 +37,23 @@ static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg)
static void dw_plat_pcie_host_init(struct pcie_port *pp)
{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+
dw_pcie_setup_rc(pp);
- dw_pcie_wait_for_link(pp);
+ dw_pcie_wait_for_link(pci);
if (IS_ENABLED(CONFIG_PCI_MSI))
dw_pcie_msi_init(pp);
}
-static struct pcie_host_ops dw_plat_pcie_host_ops = {
+static struct dw_pcie_host_ops dw_plat_pcie_host_ops = {
.host_init = dw_plat_pcie_host_init,
};
static int dw_plat_add_pcie_port(struct pcie_port *pp,
struct platform_device *pdev)
{
- struct device *dev = pp->dev;
+ struct device *dev = &pdev->dev;
int ret;
pp->irq = platform_get_irq(pdev, 1);
@@ -88,7 +90,7 @@ static int dw_plat_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dw_plat_pcie *dw_plat_pcie;
- struct pcie_port *pp;
+ struct dw_pcie *pci;
struct resource *res; /* Resource from DT */
int ret;
@@ -96,15 +98,22 @@ static int dw_plat_pcie_probe(struct platform_device *pdev)
if (!dw_plat_pcie)
return -ENOMEM;
- pp = &dw_plat_pcie->pp;
- pp->dev = dev;
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pci->dev = dev;
+
+ dw_plat_pcie->pci = pci;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pp->dbi_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(pp->dbi_base))
- return PTR_ERR(pp->dbi_base);
+ pci->dbi_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pci->dbi_base))
+ return PTR_ERR(pci->dbi_base);
+
+ platform_set_drvdata(pdev, dw_plat_pcie);
- ret = dw_plat_add_pcie_port(pp, pdev);
+ ret = dw_plat_add_pcie_port(&pci->pp, pdev);
if (ret < 0)
return ret;
diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c
new file mode 100644
index 000000000000..7e1fb7d6643c
--- /dev/null
+++ b/drivers/pci/dwc/pcie-designware.c
@@ -0,0 +1,233 @@
+/*
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+/* PCIe Port Logic registers */
+#define PLR_OFFSET 0x700
+#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c)
+#define PCIE_PHY_DEBUG_R1_LINK_UP (0x1 << 4)
+#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (0x1 << 29)
+
+int dw_pcie_read(void __iomem *addr, int size, u32 *val)
+{
+ if ((uintptr_t)addr & (size - 1)) {
+ *val = 0;
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+
+ if (size == 4) {
+ *val = readl(addr);
+ } else if (size == 2) {
+ *val = readw(addr);
+ } else if (size == 1) {
+ *val = readb(addr);
+ } else {
+ *val = 0;
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int dw_pcie_write(void __iomem *addr, int size, u32 val)
+{
+ if ((uintptr_t)addr & (size - 1))
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ if (size == 4)
+ writel(val, addr);
+ else if (size == 2)
+ writew(val, addr);
+ else if (size == 1)
+ writeb(val, addr);
+ else
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg)
+{
+ if (pci->ops->readl_dbi)
+ return pci->ops->readl_dbi(pci, reg);
+
+ return readl(pci->dbi_base + reg);
+}
+
+void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
+{
+ if (pci->ops->writel_dbi)
+ pci->ops->writel_dbi(pci, reg, val);
+ else
+ writel(val, pci->dbi_base + reg);
+}
+
+static u32 dw_pcie_readl_unroll(struct dw_pcie *pci, u32 index, u32 reg)
+{
+ u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
+
+ return dw_pcie_readl_dbi(pci, offset + reg);
+}
+
+static void dw_pcie_writel_unroll(struct dw_pcie *pci, u32 index, u32 reg,
+ u32 val)
+{
+ u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
+
+ dw_pcie_writel_dbi(pci, offset + reg, val);
+}
+
+void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
+ u64 cpu_addr, u64 pci_addr, u32 size)
+{
+ u32 retries, val;
+
+ if (pci->iatu_unroll_enabled) {
+ dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE,
+ lower_32_bits(cpu_addr));
+ dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE,
+ upper_32_bits(cpu_addr));
+ dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LIMIT,
+ lower_32_bits(cpu_addr + size - 1));
+ dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET,
+ lower_32_bits(pci_addr));
+ dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
+ upper_32_bits(pci_addr));
+ dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1,
+ type);
+ dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
+ PCIE_ATU_ENABLE);
+ } else {
+ dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT,
+ PCIE_ATU_REGION_OUTBOUND | index);
+ dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE,
+ lower_32_bits(cpu_addr));
+ dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE,
+ upper_32_bits(cpu_addr));
+ dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
+ lower_32_bits(cpu_addr + size - 1));
+ dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
+ lower_32_bits(pci_addr));
+ dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET,
+ upper_32_bits(pci_addr));
+ dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type);
+ dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
+ }
+
+ /*
+ * Make sure ATU enable takes effect before any subsequent config
+ * and I/O accesses.
+ */
+ for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
+ if (pci->iatu_unroll_enabled)
+ val = dw_pcie_readl_unroll(pci, index,
+ PCIE_ATU_UNR_REGION_CTRL2);
+ else
+ val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2);
+
+ if (val == PCIE_ATU_ENABLE)
+ return;
+
+ usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX);
+ }
+ dev_err(pci->dev, "iATU is not being enabled\n");
+}
+
+int dw_pcie_wait_for_link(struct dw_pcie *pci)
+{
+ int retries;
+
+ /* check if the link is up or not */
+ for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
+ if (dw_pcie_link_up(pci)) {
+ dev_info(pci->dev, "link up\n");
+ return 0;
+ }
+ usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
+ }
+
+ dev_err(pci->dev, "phy link never came up\n");
+
+ return -ETIMEDOUT;
+}
+
+int dw_pcie_link_up(struct dw_pcie *pci)
+{
+ u32 val;
+
+ if (pci->ops->link_up)
+ return pci->ops->link_up(pci);
+
+ val = readl(pci->dbi_base + PCIE_PHY_DEBUG_R1);
+ return ((val & PCIE_PHY_DEBUG_R1_LINK_UP) &&
+ (!(val & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING)));
+}
+
+void dw_pcie_setup(struct dw_pcie *pci)
+{
+ int ret;
+ u32 val;
+ u32 lanes;
+ struct device *dev = pci->dev;
+ struct device_node *np = dev->of_node;
+
+ ret = of_property_read_u32(np, "num-lanes", &lanes);
+ if (ret)
+ lanes = 0;
+
+ /* set the number of lanes */
+ val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
+ val &= ~PORT_LINK_MODE_MASK;
+ switch (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;
+ case 8:
+ val |= PORT_LINK_MODE_8_LANES;
+ break;
+ default:
+ dev_err(pci->dev, "num-lanes %u: invalid value\n", lanes);
+ return;
+ }
+ dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
+
+ /* set link width speed control register */
+ val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
+ switch (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;
+ case 8:
+ val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
+ break;
+ }
+ dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+}
diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h
new file mode 100644
index 000000000000..cd3b8713fe50
--- /dev/null
+++ b/drivers/pci/dwc/pcie-designware.h
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ */
+
+#ifndef _PCIE_DESIGNWARE_H
+#define _PCIE_DESIGNWARE_H
+
+#include <linux/irq.h>
+#include <linux/msi.h>
+#include <linux/pci.h>
+
+/* Parameters for the waiting for link up routine */
+#define LINK_WAIT_MAX_RETRIES 10
+#define LINK_WAIT_USLEEP_MIN 90000
+#define LINK_WAIT_USLEEP_MAX 100000
+
+/* Parameters for the waiting for iATU enabled routine */
+#define LINK_WAIT_MAX_IATU_RETRIES 5
+#define LINK_WAIT_IATU_MIN 9000
+#define LINK_WAIT_IATU_MAX 10000
+
+/* Synopsys-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 PORT_LINK_MODE_8_LANES (0xf << 16)
+
+#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
+#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
+#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 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 PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8)
+
+#define PCIE_MSI_ADDR_LO 0x820
+#define PCIE_MSI_ADDR_HI 0x824
+#define PCIE_MSI_INTR0_ENABLE 0x828
+#define PCIE_MSI_INTR0_MASK 0x82C
+#define PCIE_MSI_INTR0_STATUS 0x830
+
+#define PCIE_ATU_VIEWPORT 0x900
+#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
+#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
+#define PCIE_ATU_REGION_INDEX2 (0x2 << 0)
+#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
+#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
+#define PCIE_ATU_CR1 0x904
+#define PCIE_ATU_TYPE_MEM (0x0 << 0)
+#define PCIE_ATU_TYPE_IO (0x2 << 0)
+#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
+#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
+#define PCIE_ATU_CR2 0x908
+#define PCIE_ATU_ENABLE (0x1 << 31)
+#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
+#define PCIE_ATU_LOWER_BASE 0x90C
+#define PCIE_ATU_UPPER_BASE 0x910
+#define PCIE_ATU_LIMIT 0x914
+#define PCIE_ATU_LOWER_TARGET 0x918
+#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
+#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
+#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
+#define PCIE_ATU_UPPER_TARGET 0x91C
+
+/*
+ * iATU Unroll-specific register definitions
+ * From 4.80 core version the address translation will be made by unroll
+ */
+#define PCIE_ATU_UNR_REGION_CTRL1 0x00
+#define PCIE_ATU_UNR_REGION_CTRL2 0x04
+#define PCIE_ATU_UNR_LOWER_BASE 0x08
+#define PCIE_ATU_UNR_UPPER_BASE 0x0C
+#define PCIE_ATU_UNR_LIMIT 0x10
+#define PCIE_ATU_UNR_LOWER_TARGET 0x14
+#define PCIE_ATU_UNR_UPPER_TARGET 0x18
+
+/* Register address builder */
+#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) \
+ ((0x3 << 20) | ((region) << 9))
+
+/*
+ * Maximum number of MSI IRQs can be 256 per controller. But keep
+ * it 32 as of now. Probably we will never need more than 32. If needed,
+ * then increment it in multiple of 32.
+ */
+#define MAX_MSI_IRQS 32
+#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32)
+
+struct pcie_port;
+struct dw_pcie;
+
+struct dw_pcie_host_ops {
+ 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 (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus,
+ unsigned int devfn, int where, int size, u32 *val);
+ int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus,
+ unsigned int devfn, int where, int size, u32 val);
+ void (*host_init)(struct pcie_port *pp);
+ void (*msi_set_irq)(struct pcie_port *pp, int irq);
+ void (*msi_clear_irq)(struct pcie_port *pp, int irq);
+ phys_addr_t (*get_msi_addr)(struct pcie_port *pp);
+ u32 (*get_msi_data)(struct pcie_port *pp, int pos);
+ void (*scan_bus)(struct pcie_port *pp);
+ int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip);
+};
+
+struct pcie_port {
+ u8 root_bus_nr;
+ u64 cfg0_base;
+ void __iomem *va_cfg0_base;
+ u32 cfg0_size;
+ u64 cfg1_base;
+ void __iomem *va_cfg1_base;
+ u32 cfg1_size;
+ resource_size_t io_base;
+ phys_addr_t io_bus_addr;
+ u32 io_size;
+ u64 mem_base;
+ phys_addr_t mem_bus_addr;
+ u32 mem_size;
+ struct resource *cfg;
+ struct resource *io;
+ struct resource *mem;
+ struct resource *busn;
+ int irq;
+ struct dw_pcie_host_ops *ops;
+ int msi_irq;
+ struct irq_domain *irq_domain;
+ unsigned long msi_data;
+ DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
+};
+
+struct dw_pcie_ops {
+ u32 (*readl_dbi)(struct dw_pcie *pcie, u32 reg);
+ void (*writel_dbi)(struct dw_pcie *pcie, u32 reg, u32 val);
+ int (*link_up)(struct dw_pcie *pcie);
+};
+
+struct dw_pcie {
+ struct device *dev;
+ void __iomem *dbi_base;
+ u32 num_viewport;
+ u8 iatu_unroll_enabled;
+ struct pcie_port pp;
+ const struct dw_pcie_ops *ops;
+};
+
+#define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)
+
+int dw_pcie_read(void __iomem *addr, int size, u32 *val);
+int dw_pcie_write(void __iomem *addr, int size, u32 val);
+
+u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg);
+void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val);
+int dw_pcie_link_up(struct dw_pcie *pci);
+int dw_pcie_wait_for_link(struct dw_pcie *pci);
+void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
+ int type, u64 cpu_addr, u64 pci_addr,
+ u32 size);
+void dw_pcie_setup(struct dw_pcie *pci);
+
+#ifdef CONFIG_PCIE_DW_HOST
+irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
+void dw_pcie_msi_init(struct pcie_port *pp);
+void dw_pcie_setup_rc(struct pcie_port *pp);
+int dw_pcie_host_init(struct pcie_port *pp);
+#else
+static inline irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
+{
+ return IRQ_NONE;
+}
+
+static inline void dw_pcie_msi_init(struct pcie_port *pp)
+{
+}
+
+static inline void dw_pcie_setup_rc(struct pcie_port *pp)
+{
+}
+
+static inline int dw_pcie_host_init(struct pcie_port *pp)
+{
+ return 0;
+}
+#endif
+#endif /* _PCIE_DESIGNWARE_H */
diff --git a/drivers/pci/dwc/pcie-hisi.c b/drivers/pci/dwc/pcie-hisi.c
new file mode 100644
index 000000000000..fd66a3199db7
--- /dev/null
+++ b/drivers/pci/dwc/pcie-hisi.c
@@ -0,0 +1,399 @@
+/*
+ * PCIe host controller driver for HiSilicon SoCs
+ *
+ * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com
+ *
+ * Authors: Zhou Wang <wangzhou1@hisilicon.com>
+ * Dacai Zhu <zhudacai@hisilicon.com>
+ * Gabriele Paoloni <gabriele.paoloni@huawei.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/interrupt.h>
+#include <linux/init.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/pci.h>
+#include <linux/pci-acpi.h>
+#include <linux/pci-ecam.h>
+#include <linux/regmap.h>
+#include "../pci.h"
+
+#if defined(CONFIG_PCI_HISI) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS))
+
+static int hisi_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+ int size, u32 *val)
+{
+ struct pci_config_window *cfg = bus->sysdata;
+ int dev = PCI_SLOT(devfn);
+
+ if (bus->number == cfg->busr.start) {
+ /* access only one slot on each root port */
+ if (dev > 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ else
+ return pci_generic_config_read32(bus, devfn, where,
+ size, val);
+ }
+
+ return pci_generic_config_read(bus, devfn, where, size, val);
+}
+
+static int hisi_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+ int where, int size, u32 val)
+{
+ struct pci_config_window *cfg = bus->sysdata;
+ int dev = PCI_SLOT(devfn);
+
+ if (bus->number == cfg->busr.start) {
+ /* access only one slot on each root port */
+ if (dev > 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ else
+ return pci_generic_config_write32(bus, devfn, where,
+ size, val);
+ }
+
+ return pci_generic_config_write(bus, devfn, where, size, val);
+}
+
+static void __iomem *hisi_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
+ int where)
+{
+ struct pci_config_window *cfg = bus->sysdata;
+ void __iomem *reg_base = cfg->priv;
+
+ if (bus->number == cfg->busr.start)
+ return reg_base + where;
+ else
+ return pci_ecam_map_bus(bus, devfn, where);
+}
+
+#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
+
+static int hisi_pcie_init(struct pci_config_window *cfg)
+{
+ struct device *dev = cfg->parent;
+ struct acpi_device *adev = to_acpi_device(dev);
+ struct acpi_pci_root *root = acpi_driver_data(adev);
+ struct resource *res;
+ void __iomem *reg_base;
+ int ret;
+
+ /*
+ * Retrieve RC base and size from a HISI0081 device with _UID
+ * matching our segment.
+ */
+ res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL);
+ if (!res)
+ return -ENOMEM;
+
+ ret = acpi_get_rc_resources(dev, "HISI0081", root->segment, res);
+ if (ret) {
+ dev_err(dev, "can't get rc base address\n");
+ return -ENOMEM;
+ }
+
+ reg_base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!reg_base)
+ return -ENOMEM;
+
+ cfg->priv = reg_base;
+ return 0;
+}
+
+struct pci_ecam_ops hisi_pcie_ops = {
+ .bus_shift = 20,
+ .init = hisi_pcie_init,
+ .pci_ops = {
+ .map_bus = hisi_pcie_map_bus,
+ .read = hisi_pcie_rd_conf,
+ .write = hisi_pcie_wr_conf,
+ }
+};
+
+#endif
+
+#ifdef CONFIG_PCI_HISI
+
+#include "pcie-designware.h"
+
+#define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818
+#define PCIE_HIP06_CTRL_OFF 0x1000
+#define PCIE_SYS_STATE4 (PCIE_HIP06_CTRL_OFF + 0x31c)
+#define PCIE_LTSSM_LINKUP_STATE 0x11
+#define PCIE_LTSSM_STATE_MASK 0x3F
+
+#define to_hisi_pcie(x) dev_get_drvdata((x)->dev)
+
+struct hisi_pcie;
+
+struct pcie_soc_ops {
+ int (*hisi_pcie_link_up)(struct hisi_pcie *hisi_pcie);
+};
+
+struct hisi_pcie {
+ struct dw_pcie *pci;
+ struct regmap *subctrl;
+ u32 port_id;
+ const struct pcie_soc_ops *soc_ops;
+};
+
+/* HipXX PCIe host only supports 32-bit config access */
+static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
+ u32 *val)
+{
+ u32 reg;
+ u32 reg_val;
+ void *walker = &reg_val;
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+
+ walker += (where & 0x3);
+ reg = where & ~0x3;
+ reg_val = dw_pcie_readl_dbi(pci, reg);
+
+ if (size == 1)
+ *val = *(u8 __force *) walker;
+ else if (size == 2)
+ *val = *(u16 __force *) walker;
+ else if (size == 4)
+ *val = reg_val;
+ else
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+/* HipXX PCIe host only supports 32-bit config access */
+static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size,
+ u32 val)
+{
+ u32 reg_val;
+ u32 reg;
+ void *walker = &reg_val;
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+
+ walker += (where & 0x3);
+ reg = where & ~0x3;
+ if (size == 4)
+ dw_pcie_writel_dbi(pci, reg, val);
+ else if (size == 2) {
+ reg_val = dw_pcie_readl_dbi(pci, reg);
+ *(u16 __force *) walker = val;
+ dw_pcie_writel_dbi(pci, reg, reg_val);
+ } else if (size == 1) {
+ reg_val = dw_pcie_readl_dbi(pci, reg);
+ *(u8 __force *) walker = val;
+ dw_pcie_writel_dbi(pci, reg, reg_val);
+ } else
+ return PCIBIOS_BAD_REGISTER_NUMBER;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie)
+{
+ u32 val;
+
+ regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG +
+ 0x100 * hisi_pcie->port_id, &val);
+
+ return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
+}
+
+static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie)
+{
+ struct dw_pcie *pci = hisi_pcie->pci;
+ u32 val;
+
+ val = dw_pcie_readl_dbi(pci, PCIE_SYS_STATE4);
+
+ return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
+}
+
+static int hisi_pcie_link_up(struct dw_pcie *pci)
+{
+ struct hisi_pcie *hisi_pcie = to_hisi_pcie(pci);
+
+ return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie);
+}
+
+static struct dw_pcie_host_ops hisi_pcie_host_ops = {
+ .rd_own_conf = hisi_pcie_cfg_read,
+ .wr_own_conf = hisi_pcie_cfg_write,
+};
+
+static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie,
+ struct platform_device *pdev)
+{
+ struct dw_pcie *pci = hisi_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
+ struct device *dev = &pdev->dev;
+ int ret;
+ u32 port_id;
+
+ if (of_property_read_u32(dev->of_node, "port-id", &port_id)) {
+ dev_err(dev, "failed to read port-id\n");
+ return -EINVAL;
+ }
+ if (port_id > 3) {
+ dev_err(dev, "Invalid port-id: %d\n", port_id);
+ return -EINVAL;
+ }
+ hisi_pcie->port_id = port_id;
+
+ pp->ops = &hisi_pcie_host_ops;
+
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .link_up = hisi_pcie_link_up,
+};
+
+static int hisi_pcie_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dw_pcie *pci;
+ struct hisi_pcie *hisi_pcie;
+ struct resource *reg;
+ struct device_driver *driver;
+ int ret;
+
+ hisi_pcie = devm_kzalloc(dev, sizeof(*hisi_pcie), GFP_KERNEL);
+ if (!hisi_pcie)
+ return -ENOMEM;
+
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pci->dev = dev;
+ pci->ops = &dw_pcie_ops;
+
+ driver = dev->driver;
+
+ hisi_pcie->pci = pci;
+
+ hisi_pcie->soc_ops = of_device_get_match_data(dev);
+
+ hisi_pcie->subctrl =
+ syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl");
+ if (IS_ERR(hisi_pcie->subctrl)) {
+ dev_err(dev, "cannot get subctrl base\n");
+ return PTR_ERR(hisi_pcie->subctrl);
+ }
+
+ reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi");
+ pci->dbi_base = devm_ioremap_resource(dev, reg);
+ if (IS_ERR(pci->dbi_base))
+ return PTR_ERR(pci->dbi_base);
+
+ platform_set_drvdata(pdev, hisi_pcie);
+
+ ret = hisi_add_pcie_port(hisi_pcie, pdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct pcie_soc_ops hip05_ops = {
+ &hisi_pcie_link_up_hip05
+};
+
+static struct pcie_soc_ops hip06_ops = {
+ &hisi_pcie_link_up_hip06
+};
+
+static const struct of_device_id hisi_pcie_of_match[] = {
+ {
+ .compatible = "hisilicon,hip05-pcie",
+ .data = (void *) &hip05_ops,
+ },
+ {
+ .compatible = "hisilicon,hip06-pcie",
+ .data = (void *) &hip06_ops,
+ },
+ {},
+};
+
+static struct platform_driver hisi_pcie_driver = {
+ .probe = hisi_pcie_probe,
+ .driver = {
+ .name = "hisi-pcie",
+ .of_match_table = hisi_pcie_of_match,
+ },
+};
+builtin_platform_driver(hisi_pcie_driver);
+
+static int hisi_pcie_almost_ecam_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pci_ecam_ops *ops;
+
+ ops = (struct pci_ecam_ops *)of_device_get_match_data(dev);
+ return pci_host_common_probe(pdev, ops);
+}
+
+static int hisi_pcie_platform_init(struct pci_config_window *cfg)
+{
+ struct device *dev = cfg->parent;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *res;
+ void __iomem *reg_base;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(dev, "missing \"reg[1]\"property\n");
+ return -EINVAL;
+ }
+
+ reg_base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!reg_base)
+ return -ENOMEM;
+
+ cfg->priv = reg_base;
+ return 0;
+}
+
+struct pci_ecam_ops hisi_pcie_platform_ops = {
+ .bus_shift = 20,
+ .init = hisi_pcie_platform_init,
+ .pci_ops = {
+ .map_bus = hisi_pcie_map_bus,
+ .read = hisi_pcie_rd_conf,
+ .write = hisi_pcie_wr_conf,
+ }
+};
+
+static const struct of_device_id hisi_pcie_almost_ecam_of_match[] = {
+ {
+ .compatible = "hisilicon,pcie-almost-ecam",
+ .data = (void *) &hisi_pcie_platform_ops,
+ },
+ {},
+};
+
+static struct platform_driver hisi_pcie_almost_ecam_driver = {
+ .probe = hisi_pcie_almost_ecam_probe,
+ .driver = {
+ .name = "hisi-pcie-almost-ecam",
+ .of_match_table = hisi_pcie_almost_ecam_of_match,
+ },
+};
+builtin_platform_driver(hisi_pcie_almost_ecam_driver);
+
+#endif
+#endif
diff --git a/drivers/pci/host/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c
index 35936409b2d4..67eb7f5926dd 100644
--- a/drivers/pci/host/pcie-qcom.c
+++ b/drivers/pci/dwc/pcie-qcom.c
@@ -36,11 +36,17 @@
#include "pcie-designware.h"
+#define PCIE20_PARF_SYS_CTRL 0x00
#define PCIE20_PARF_PHY_CTRL 0x40
#define PCIE20_PARF_PHY_REFCLK 0x4C
#define PCIE20_PARF_DBI_BASE_ADDR 0x168
-#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c
+#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16C
+#define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174
#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178
+#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1A8
+#define PCIE20_PARF_LTSSM 0x1B0
+#define PCIE20_PARF_SID_OFFSET 0x234
+#define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C
#define PCIE20_ELBI_SYS_CTRL 0x04
#define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0)
@@ -72,9 +78,18 @@ struct qcom_pcie_resources_v1 {
struct regulator *vdda;
};
+struct qcom_pcie_resources_v2 {
+ struct clk *aux_clk;
+ struct clk *master_clk;
+ struct clk *slave_clk;
+ struct clk *cfg_clk;
+ struct clk *pipe_clk;
+};
+
union qcom_pcie_resources {
struct qcom_pcie_resources_v0 v0;
struct qcom_pcie_resources_v1 v1;
+ struct qcom_pcie_resources_v2 v2;
};
struct qcom_pcie;
@@ -82,11 +97,13 @@ struct qcom_pcie;
struct qcom_pcie_ops {
int (*get_resources)(struct qcom_pcie *pcie);
int (*init)(struct qcom_pcie *pcie);
+ int (*post_init)(struct qcom_pcie *pcie);
void (*deinit)(struct qcom_pcie *pcie);
+ void (*ltssm_enable)(struct qcom_pcie *pcie);
};
struct qcom_pcie {
- struct pcie_port pp; /* pp.dbi_base is DT dbi */
+ struct dw_pcie *pci;
void __iomem *parf; /* DT parf */
void __iomem *elbi; /* DT elbi */
union qcom_pcie_resources res;
@@ -95,7 +112,7 @@ struct qcom_pcie {
struct qcom_pcie_ops *ops;
};
-#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp)
+#define to_qcom_pcie(x) dev_get_drvdata((x)->dev)
static void qcom_ep_reset_assert(struct qcom_pcie *pcie)
{
@@ -116,25 +133,45 @@ static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg)
return dw_handle_msi_irq(pp);
}
-static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
+static void qcom_pcie_v0_v1_ltssm_enable(struct qcom_pcie *pcie)
{
u32 val;
- if (dw_pcie_link_up(&pcie->pp))
- return 0;
-
/* enable link training */
val = readl(pcie->elbi + PCIE20_ELBI_SYS_CTRL);
val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE;
writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL);
+}
+
+static void qcom_pcie_v2_ltssm_enable(struct qcom_pcie *pcie)
+{
+ u32 val;
+
+ /* enable link training */
+ val = readl(pcie->parf + PCIE20_PARF_LTSSM);
+ val |= BIT(8);
+ writel(val, pcie->parf + PCIE20_PARF_LTSSM);
+}
- return dw_pcie_wait_for_link(&pcie->pp);
+static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
+{
+ struct dw_pcie *pci = pcie->pci;
+
+ if (dw_pcie_link_up(pci))
+ return 0;
+
+ /* Enable Link Training state machine */
+ if (pcie->ops->ltssm_enable)
+ pcie->ops->ltssm_enable(pcie);
+
+ return dw_pcie_wait_for_link(pci);
}
static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
- struct device *dev = pcie->pp.dev;
+ struct dw_pcie *pci = pcie->pci;
+ struct device *dev = pci->dev;
res->vdda = devm_regulator_get(dev, "vdda");
if (IS_ERR(res->vdda))
@@ -177,16 +214,14 @@ static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
return PTR_ERR(res->por_reset);
res->phy_reset = devm_reset_control_get(dev, "phy");
- if (IS_ERR(res->phy_reset))
- return PTR_ERR(res->phy_reset);
-
- return 0;
+ return PTR_ERR_OR_ZERO(res->phy_reset);
}
static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
- struct device *dev = pcie->pp.dev;
+ struct dw_pcie *pci = pcie->pci;
+ struct device *dev = pci->dev;
res->vdda = devm_regulator_get(dev, "vdda");
if (IS_ERR(res->vdda))
@@ -209,10 +244,7 @@ static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie)
return PTR_ERR(res->slave_bus);
res->core = devm_reset_control_get(dev, "core");
- if (IS_ERR(res->core))
- return PTR_ERR(res->core);
-
- return 0;
+ return PTR_ERR_OR_ZERO(res->core);
}
static void qcom_pcie_deinit_v0(struct qcom_pcie *pcie)
@@ -235,7 +267,8 @@ static void qcom_pcie_deinit_v0(struct qcom_pcie *pcie)
static int qcom_pcie_init_v0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
- struct device *dev = pcie->pp.dev;
+ struct dw_pcie *pci = pcie->pci;
+ struct device *dev = pci->dev;
u32 val;
int ret;
@@ -357,7 +390,8 @@ static void qcom_pcie_deinit_v1(struct qcom_pcie *pcie)
static int qcom_pcie_init_v1(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
- struct device *dev = pcie->pp.dev;
+ struct dw_pcie *pci = pcie->pci;
+ struct device *dev = pci->dev;
int ret;
ret = reset_control_deassert(res->core);
@@ -421,17 +455,135 @@ err_res:
return ret;
}
-static int qcom_pcie_link_up(struct pcie_port *pp)
+static int qcom_pcie_get_resources_v2(struct qcom_pcie *pcie)
+{
+ struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
+ struct dw_pcie *pci = pcie->pci;
+ struct device *dev = pci->dev;
+
+ res->aux_clk = devm_clk_get(dev, "aux");
+ if (IS_ERR(res->aux_clk))
+ return PTR_ERR(res->aux_clk);
+
+ res->cfg_clk = devm_clk_get(dev, "cfg");
+ if (IS_ERR(res->cfg_clk))
+ return PTR_ERR(res->cfg_clk);
+
+ res->master_clk = devm_clk_get(dev, "bus_master");
+ if (IS_ERR(res->master_clk))
+ return PTR_ERR(res->master_clk);
+
+ res->slave_clk = devm_clk_get(dev, "bus_slave");
+ if (IS_ERR(res->slave_clk))
+ return PTR_ERR(res->slave_clk);
+
+ res->pipe_clk = devm_clk_get(dev, "pipe");
+ return PTR_ERR_OR_ZERO(res->pipe_clk);
+}
+
+static int qcom_pcie_init_v2(struct qcom_pcie *pcie)
+{
+ struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
+ struct dw_pcie *pci = pcie->pci;
+ struct device *dev = pci->dev;
+ u32 val;
+ int ret;
+
+ ret = clk_prepare_enable(res->aux_clk);
+ if (ret) {
+ dev_err(dev, "cannot prepare/enable aux clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(res->cfg_clk);
+ if (ret) {
+ dev_err(dev, "cannot prepare/enable cfg clock\n");
+ goto err_cfg_clk;
+ }
+
+ ret = clk_prepare_enable(res->master_clk);
+ if (ret) {
+ dev_err(dev, "cannot prepare/enable master clock\n");
+ goto err_master_clk;
+ }
+
+ ret = clk_prepare_enable(res->slave_clk);
+ if (ret) {
+ dev_err(dev, "cannot prepare/enable slave clock\n");
+ goto err_slave_clk;
+ }
+
+ /* enable PCIe clocks and resets */
+ val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL);
+ val &= ~BIT(0);
+ writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL);
+
+ /* change DBI base address */
+ writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
+
+ /* MAC PHY_POWERDOWN MUX DISABLE */
+ val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL);
+ val &= ~BIT(29);
+ writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL);
+
+ val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
+ val |= BIT(4);
+ writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
+
+ val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2);
+ val |= BIT(31);
+ writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT_V2);
+
+ return 0;
+
+err_slave_clk:
+ clk_disable_unprepare(res->master_clk);
+err_master_clk:
+ clk_disable_unprepare(res->cfg_clk);
+err_cfg_clk:
+ clk_disable_unprepare(res->aux_clk);
+
+ return ret;
+}
+
+static int qcom_pcie_post_init_v2(struct qcom_pcie *pcie)
{
- struct qcom_pcie *pcie = to_qcom_pcie(pp);
- u16 val = readw(pcie->pp.dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA);
+ struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
+ struct dw_pcie *pci = pcie->pci;
+ struct device *dev = pci->dev;
+ int ret;
+
+ ret = clk_prepare_enable(res->pipe_clk);
+ if (ret) {
+ dev_err(dev, "cannot prepare/enable pipe clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qcom_pcie_link_up(struct dw_pcie *pci)
+{
+ u16 val = readw(pci->dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA);
return !!(val & PCI_EXP_LNKSTA_DLLLA);
}
+static void qcom_pcie_deinit_v2(struct qcom_pcie *pcie)
+{
+ struct qcom_pcie_resources_v2 *res = &pcie->res.v2;
+
+ clk_disable_unprepare(res->pipe_clk);
+ clk_disable_unprepare(res->slave_clk);
+ clk_disable_unprepare(res->master_clk);
+ clk_disable_unprepare(res->cfg_clk);
+ clk_disable_unprepare(res->aux_clk);
+}
+
static void qcom_pcie_host_init(struct pcie_port *pp)
{
- struct qcom_pcie *pcie = to_qcom_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct qcom_pcie *pcie = to_qcom_pcie(pci);
int ret;
qcom_ep_reset_assert(pcie);
@@ -444,6 +596,9 @@ static void qcom_pcie_host_init(struct pcie_port *pp)
if (ret)
goto err_deinit;
+ if (pcie->ops->post_init)
+ pcie->ops->post_init(pcie);
+
dw_pcie_setup_rc(pp);
if (IS_ENABLED(CONFIG_PCI_MSI))
@@ -466,19 +621,20 @@ err_deinit:
static int qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
u32 *val)
{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+
/* the device class is not reported correctly from the register */
if (where == PCI_CLASS_REVISION && size == 4) {
- *val = readl(pp->dbi_base + PCI_CLASS_REVISION);
+ *val = readl(pci->dbi_base + PCI_CLASS_REVISION);
*val &= 0xff; /* keep revision id */
*val |= PCI_CLASS_BRIDGE_PCI << 16;
return PCIBIOS_SUCCESSFUL;
}
- return dw_pcie_cfg_read(pp->dbi_base + where, size, val);
+ return dw_pcie_read(pci->dbi_base + where, size, val);
}
-static struct pcie_host_ops qcom_pcie_dw_ops = {
- .link_up = qcom_pcie_link_up,
+static struct dw_pcie_host_ops qcom_pcie_dw_ops = {
.host_init = qcom_pcie_host_init,
.rd_own_conf = qcom_pcie_rd_own_conf,
};
@@ -487,27 +643,51 @@ static const struct qcom_pcie_ops ops_v0 = {
.get_resources = qcom_pcie_get_resources_v0,
.init = qcom_pcie_init_v0,
.deinit = qcom_pcie_deinit_v0,
+ .ltssm_enable = qcom_pcie_v0_v1_ltssm_enable,
};
static const struct qcom_pcie_ops ops_v1 = {
.get_resources = qcom_pcie_get_resources_v1,
.init = qcom_pcie_init_v1,
.deinit = qcom_pcie_deinit_v1,
+ .ltssm_enable = qcom_pcie_v0_v1_ltssm_enable,
+};
+
+static const struct qcom_pcie_ops ops_v2 = {
+ .get_resources = qcom_pcie_get_resources_v2,
+ .init = qcom_pcie_init_v2,
+ .post_init = qcom_pcie_post_init_v2,
+ .deinit = qcom_pcie_deinit_v2,
+ .ltssm_enable = qcom_pcie_v2_ltssm_enable,
+};
+
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .link_up = qcom_pcie_link_up,
};
static int qcom_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
- struct qcom_pcie *pcie;
struct pcie_port *pp;
+ struct dw_pcie *pci;
+ struct qcom_pcie *pcie;
int ret;
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
if (!pcie)
return -ENOMEM;
- pp = &pcie->pp;
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pci->dev = dev;
+ pci->ops = &dw_pcie_ops;
+ pp = &pci->pp;
+
+ pcie->pci = pci;
+
pcie->ops = (struct qcom_pcie_ops *)of_device_get_match_data(dev);
pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW);
@@ -520,9 +700,9 @@ static int qcom_pcie_probe(struct platform_device *pdev)
return PTR_ERR(pcie->parf);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
- pp->dbi_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(pp->dbi_base))
- return PTR_ERR(pp->dbi_base);
+ pci->dbi_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pci->dbi_base))
+ return PTR_ERR(pci->dbi_base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
pcie->elbi = devm_ioremap_resource(dev, res);
@@ -533,7 +713,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)
if (IS_ERR(pcie->phy))
return PTR_ERR(pcie->phy);
- pp->dev = dev;
ret = pcie->ops->get_resources(pcie);
if (ret)
return ret;
@@ -559,6 +738,8 @@ static int qcom_pcie_probe(struct platform_device *pdev)
if (ret)
return ret;
+ platform_set_drvdata(pdev, pcie);
+
ret = dw_pcie_host_init(pp);
if (ret) {
dev_err(dev, "cannot initialize host\n");
@@ -572,6 +753,7 @@ static const struct of_device_id qcom_pcie_match[] = {
{ .compatible = "qcom,pcie-ipq8064", .data = &ops_v0 },
{ .compatible = "qcom,pcie-apq8064", .data = &ops_v0 },
{ .compatible = "qcom,pcie-apq8084", .data = &ops_v1 },
+ { .compatible = "qcom,pcie-msm8996", .data = &ops_v2 },
{ }
};
diff --git a/drivers/pci/host/pcie-spear13xx.c b/drivers/pci/dwc/pcie-spear13xx.c
index 3cf197ba7f37..eaa4ea8e2ea4 100644
--- a/drivers/pci/host/pcie-spear13xx.c
+++ b/drivers/pci/dwc/pcie-spear13xx.c
@@ -25,7 +25,7 @@
#include "pcie-designware.h"
struct spear13xx_pcie {
- struct pcie_port pp; /* DT dbi is pp.dbi_base */
+ struct dw_pcie *pci;
void __iomem *app_base;
struct phy *phy;
struct clk *clk;
@@ -70,17 +70,18 @@ struct pcie_app_reg {
#define EXP_CAP_ID_OFFSET 0x70
-#define to_spear13xx_pcie(x) container_of(x, struct spear13xx_pcie, pp)
+#define to_spear13xx_pcie(x) dev_get_drvdata((x)->dev)
static int spear13xx_pcie_establish_link(struct spear13xx_pcie *spear13xx_pcie)
{
- struct pcie_port *pp = &spear13xx_pcie->pp;
+ struct dw_pcie *pci = spear13xx_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
u32 val;
u32 exp_cap_off = EXP_CAP_ID_OFFSET;
- if (dw_pcie_link_up(pp)) {
- dev_err(pp->dev, "link already up\n");
+ if (dw_pcie_link_up(pci)) {
+ dev_err(pci->dev, "link already up\n");
return 0;
}
@@ -91,34 +92,34 @@ static int spear13xx_pcie_establish_link(struct spear13xx_pcie *spear13xx_pcie)
* default value in capability register is 512 bytes. So force
* it to 128 here.
*/
- dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, &val);
+ dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, &val);
val &= ~PCI_EXP_DEVCTL_READRQ;
- dw_pcie_cfg_write(pp->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, val);
+ dw_pcie_write(pci->dbi_base + exp_cap_off + PCI_EXP_DEVCTL, 2, val);
- dw_pcie_cfg_write(pp->dbi_base + PCI_VENDOR_ID, 2, 0x104A);
- dw_pcie_cfg_write(pp->dbi_base + PCI_DEVICE_ID, 2, 0xCD80);
+ dw_pcie_write(pci->dbi_base + PCI_VENDOR_ID, 2, 0x104A);
+ dw_pcie_write(pci->dbi_base + PCI_DEVICE_ID, 2, 0xCD80);
/*
* if is_gen1 is set then handle it, so that some buggy card
* also works
*/
if (spear13xx_pcie->is_gen1) {
- dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCAP,
- 4, &val);
+ dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCAP,
+ 4, &val);
if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
val &= ~((u32)PCI_EXP_LNKCAP_SLS);
val |= PCI_EXP_LNKCAP_SLS_2_5GB;
- dw_pcie_cfg_write(pp->dbi_base + exp_cap_off +
- PCI_EXP_LNKCAP, 4, val);
+ dw_pcie_write(pci->dbi_base + exp_cap_off +
+ PCI_EXP_LNKCAP, 4, val);
}
- dw_pcie_cfg_read(pp->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2,
- 2, &val);
+ dw_pcie_read(pci->dbi_base + exp_cap_off + PCI_EXP_LNKCTL2,
+ 2, &val);
if ((val & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_2_5GB) {
val &= ~((u32)PCI_EXP_LNKCAP_SLS);
val |= PCI_EXP_LNKCAP_SLS_2_5GB;
- dw_pcie_cfg_write(pp->dbi_base + exp_cap_off +
- PCI_EXP_LNKCTL2, 2, val);
+ dw_pcie_write(pci->dbi_base + exp_cap_off +
+ PCI_EXP_LNKCTL2, 2, val);
}
}
@@ -128,14 +129,15 @@ static int spear13xx_pcie_establish_link(struct spear13xx_pcie *spear13xx_pcie)
| ((u32)1 << REG_TRANSLATION_ENABLE),
&app_reg->app_ctrl_0);
- return dw_pcie_wait_for_link(pp);
+ return dw_pcie_wait_for_link(pci);
}
static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg)
{
struct spear13xx_pcie *spear13xx_pcie = arg;
struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
- struct pcie_port *pp = &spear13xx_pcie->pp;
+ struct dw_pcie *pci = spear13xx_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
unsigned int status;
status = readl(&app_reg->int_sts);
@@ -152,7 +154,8 @@ static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg)
static void spear13xx_pcie_enable_interrupts(struct spear13xx_pcie *spear13xx_pcie)
{
- struct pcie_port *pp = &spear13xx_pcie->pp;
+ struct dw_pcie *pci = spear13xx_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
/* Enable MSI interrupt */
@@ -163,9 +166,9 @@ static void spear13xx_pcie_enable_interrupts(struct spear13xx_pcie *spear13xx_pc
}
}
-static int spear13xx_pcie_link_up(struct pcie_port *pp)
+static int spear13xx_pcie_link_up(struct dw_pcie *pci)
{
- struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
+ struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pci);
struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
if (readl(&app_reg->app_status_1) & XMLH_LINK_UP)
@@ -176,22 +179,23 @@ static int spear13xx_pcie_link_up(struct pcie_port *pp)
static void spear13xx_pcie_host_init(struct pcie_port *pp)
{
- struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pci);
spear13xx_pcie_establish_link(spear13xx_pcie);
spear13xx_pcie_enable_interrupts(spear13xx_pcie);
}
-static struct pcie_host_ops spear13xx_pcie_host_ops = {
- .link_up = spear13xx_pcie_link_up,
+static struct dw_pcie_host_ops spear13xx_pcie_host_ops = {
.host_init = spear13xx_pcie_host_init,
};
static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie,
struct platform_device *pdev)
{
- struct pcie_port *pp = &spear13xx_pcie->pp;
- struct device *dev = pp->dev;
+ struct dw_pcie *pci = spear13xx_pcie->pci;
+ struct pcie_port *pp = &pci->pp;
+ struct device *dev = &pdev->dev;
int ret;
pp->irq = platform_get_irq(pdev, 0);
@@ -219,11 +223,15 @@ static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie,
return 0;
}
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .link_up = spear13xx_pcie_link_up,
+};
+
static int spear13xx_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct dw_pcie *pci;
struct spear13xx_pcie *spear13xx_pcie;
- struct pcie_port *pp;
struct device_node *np = dev->of_node;
struct resource *dbi_base;
int ret;
@@ -232,6 +240,15 @@ static int spear13xx_pcie_probe(struct platform_device *pdev)
if (!spear13xx_pcie)
return -ENOMEM;
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pci->dev = dev;
+ pci->ops = &dw_pcie_ops;
+
+ spear13xx_pcie->pci = pci;
+
spear13xx_pcie->phy = devm_phy_get(dev, "pcie-phy");
if (IS_ERR(spear13xx_pcie->phy)) {
ret = PTR_ERR(spear13xx_pcie->phy);
@@ -255,26 +272,24 @@ static int spear13xx_pcie_probe(struct platform_device *pdev)
return ret;
}
- pp = &spear13xx_pcie->pp;
- pp->dev = dev;
-
dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
- pp->dbi_base = devm_ioremap_resource(dev, dbi_base);
- if (IS_ERR(pp->dbi_base)) {
+ pci->dbi_base = devm_ioremap_resource(dev, dbi_base);
+ if (IS_ERR(pci->dbi_base)) {
dev_err(dev, "couldn't remap dbi base %p\n", dbi_base);
- ret = PTR_ERR(pp->dbi_base);
+ ret = PTR_ERR(pci->dbi_base);
goto fail_clk;
}
- spear13xx_pcie->app_base = pp->dbi_base + 0x2000;
+ spear13xx_pcie->app_base = pci->dbi_base + 0x2000;
if (of_property_read_bool(np, "st,pcie-is-gen1"))
spear13xx_pcie->is_gen1 = true;
+ platform_set_drvdata(pdev, spear13xx_pcie);
+
ret = spear13xx_add_pcie_port(spear13xx_pcie, pdev);
if (ret < 0)
goto fail_clk;
- platform_set_drvdata(pdev, spear13xx_pcie);
return 0;
fail_clk:
@@ -296,8 +311,4 @@ static struct platform_driver spear13xx_pcie_driver = {
},
};
-static int __init spear13xx_pcie_init(void)
-{
- return platform_driver_register(&spear13xx_pcie_driver);
-}
-device_initcall(spear13xx_pcie_init);
+builtin_platform_driver(spear13xx_pcie_driver);
diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c
index 43ed08dd8b01..2fee61bb6559 100644
--- a/drivers/pci/ecam.c
+++ b/drivers/pci/ecam.c
@@ -162,3 +162,15 @@ struct pci_ecam_ops pci_generic_ecam_ops = {
.write = pci_generic_config_write,
}
};
+
+#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
+/* ECAM ops for 32-bit access only (non-compliant) */
+struct pci_ecam_ops pci_32b_ops = {
+ .bus_shift = 20,
+ .pci_ops = {
+ .map_bus = pci_ecam_map_bus,
+ .read = pci_generic_config_read32,
+ .write = pci_generic_config_write32,
+ }
+};
+#endif
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index d7e7c0a827c3..f7c1d4d5c665 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -1,16 +1,6 @@
menu "PCI host controller drivers"
depends on PCI
-config PCI_DRA7XX
- bool "TI DRA7xx PCIe controller"
- depends on OF && HAS_IOMEM && TI_PIPE3
- depends on PCI_MSI_IRQ_DOMAIN
- select PCIE_DW
- help
- Enables support for the PCIe controller in the DRA7xx SoC. There
- are two instances of PCIe controller in DRA7xx. This controller can
- act both as EP and RC. This reuses the Designware core.
-
config PCI_MVEBU
bool "Marvell EBU PCIe controller"
depends on ARCH_MVEBU || ARCH_DOVE
@@ -37,39 +27,9 @@ config PCIE_XILINX_NWL
or End Point. The current option selection will only
support root port enabling.
-config PCIE_DW_PLAT
- bool "Platform bus based DesignWare PCIe Controller"
- depends on PCI_MSI_IRQ_DOMAIN
- select PCIE_DW
- ---help---
- This selects the DesignWare PCIe controller support. Select this if
- you have a PCIe controller on Platform bus.
-
- If you have a controller with this interface, say Y or M here.
-
- If unsure, say N.
-
-config PCIE_DW
- bool
- depends on PCI_MSI_IRQ_DOMAIN
-
-config PCI_EXYNOS
- bool "Samsung Exynos PCIe controller"
- depends on SOC_EXYNOS5440
- depends on PCI_MSI_IRQ_DOMAIN
- select PCIEPORTBUS
- select PCIE_DW
-
-config PCI_IMX6
- bool "Freescale i.MX6 PCIe controller"
- depends on SOC_IMX6Q
- depends on PCI_MSI_IRQ_DOMAIN
- select PCIEPORTBUS
- select PCIE_DW
-
config PCI_TEGRA
bool "NVIDIA Tegra PCIe controller"
- depends on ARCH_TEGRA && !ARM64
+ depends on ARCH_TEGRA
help
Say Y here if you want support for the PCIe host controller found
on NVIDIA Tegra SoCs.
@@ -103,27 +63,6 @@ config PCI_HOST_GENERIC
Say Y here if you want to support a simple generic PCI host
controller, such as the one emulated by kvmtool.
-config PCIE_SPEAR13XX
- bool "STMicroelectronics SPEAr PCIe controller"
- depends on ARCH_SPEAR13XX
- depends on PCI_MSI_IRQ_DOMAIN
- select PCIEPORTBUS
- select PCIE_DW
- help
- Say Y here if you want PCIe support on SPEAr13XX SoCs.
-
-config PCI_KEYSTONE
- bool "TI Keystone PCIe controller"
- depends on ARCH_KEYSTONE
- depends on PCI_MSI_IRQ_DOMAIN
- select PCIE_DW
- select PCIEPORTBUS
- help
- Say Y here if you want to enable PCI controller support on Keystone
- SoCs. The PCI controller on Keystone is based on Designware hardware
- and therefore the driver re-uses the Designware core functions to
- implement the driver.
-
config PCIE_XILINX
bool "Xilinx AXI PCIe host bridge support"
depends on ARCH_ZYNQ || MICROBLAZE
@@ -133,8 +72,8 @@ config PCIE_XILINX
config PCI_XGENE
bool "X-Gene PCIe controller"
- depends on ARCH_XGENE
- depends on OF
+ depends on ARM64
+ depends on OF || (ACPI && PCI_QUIRKS)
select PCIEPORTBUS
help
Say Y here if you want internal PCI support on APM X-Gene SoC.
@@ -150,15 +89,6 @@ config PCI_XGENE_MSI
Say Y here if you want PCIe MSI support for the APM X-Gene v1 SoC.
This MSI driver supports 5 PCIe ports on the APM X-Gene v1 SoC.
-config PCI_LAYERSCAPE
- bool "Freescale Layerscape PCIe controller"
- depends on OF && (ARM || ARCH_LAYERSCAPE)
- depends on PCI_MSI_IRQ_DOMAIN
- select PCIE_DW
- select MFD_SYSCON
- help
- Say Y here if you want PCIe controller support on Layerscape SoCs.
-
config PCI_VERSATILE
bool "ARM Versatile PB PCI controller"
depends on ARCH_VERSATILE
@@ -217,66 +147,25 @@ config PCIE_ALTERA_MSI
Say Y here if you want PCIe MSI support for the Altera FPGA.
This MSI driver supports Altera MSI to GIC controller IP.
-config PCI_HISI
- depends on OF && ARM64
- bool "HiSilicon Hip05 and Hip06 SoCs PCIe controllers"
- depends on PCI_MSI_IRQ_DOMAIN
- select PCIEPORTBUS
- select PCIE_DW
- help
- Say Y here if you want PCIe controller support on HiSilicon
- Hip05 and Hip06 SoCs
-
-config PCIE_QCOM
- bool "Qualcomm PCIe controller"
- depends on ARCH_QCOM && OF
- depends on PCI_MSI_IRQ_DOMAIN
- select PCIE_DW
- select PCIEPORTBUS
- help
- Say Y here to enable PCIe controller support on Qualcomm SoCs. The
- PCIe controller uses the Designware core plus Qualcomm-specific
- hardware wrappers.
-
config PCI_HOST_THUNDER_PEM
bool "Cavium Thunder PCIe controller to off-chip devices"
- depends on OF && ARM64
+ depends on ARM64
+ depends on OF || (ACPI && PCI_QUIRKS)
select PCI_HOST_COMMON
help
Say Y here if you want PCIe support for CN88XX Cavium Thunder SoCs.
config PCI_HOST_THUNDER_ECAM
bool "Cavium Thunder ECAM controller to on-chip devices on pass-1.x silicon"
- depends on OF && ARM64
+ depends on ARM64
+ depends on OF || (ACPI && PCI_QUIRKS)
select PCI_HOST_COMMON
help
Say Y here if you want ECAM support for CN88XX-Pass-1.x Cavium Thunder SoCs.
-config PCIE_ARMADA_8K
- bool "Marvell Armada-8K PCIe controller"
- depends on ARCH_MVEBU
- depends on PCI_MSI_IRQ_DOMAIN
- select PCIE_DW
- select PCIEPORTBUS
- help
- Say Y here if you want to enable PCIe controller support on
- Armada-8K SoCs. The PCIe controller on Armada-8K is based on
- Designware hardware and therefore the driver re-uses the
- Designware core functions to implement the driver.
-
-config PCIE_ARTPEC6
- bool "Axis ARTPEC-6 PCIe controller"
- depends on MACH_ARTPEC6
- depends on PCI_MSI_IRQ_DOMAIN
- select PCIE_DW
- select PCIEPORTBUS
- help
- Say Y here to enable PCIe controller support on Axis ARTPEC-6
- SoCs. This PCIe controller uses the DesignWare core.
-
config PCIE_ROCKCHIP
bool "Rockchip PCIe controller"
- depends on ARCH_ROCKCHIP
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
depends on OF
depends on PCI_MSI_IRQ_DOMAIN
select MFD_SYSCON
@@ -286,7 +175,7 @@ config PCIE_ROCKCHIP
4 slots.
config VMD
- depends on PCI_MSI && X86_64
+ depends on PCI_MSI && X86_64 && SRCU
tristate "Intel Volume Management Device Driver"
default N
---help---
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 084cb4983645..4d3686676cc3 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -1,8 +1,3 @@
-obj-$(CONFIG_PCIE_DW) += pcie-designware.o
-obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
-obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
-obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
-obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o
obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o
@@ -11,13 +6,9 @@ obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
obj-$(CONFIG_PCIE_RCAR) += pcie-rcar.o
obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o
obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
-obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
-obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o
-obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
-obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
obj-$(CONFIG_PCIE_IPROC_MSI) += pcie-iproc-msi.o
@@ -25,11 +16,19 @@ obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
-obj-$(CONFIG_PCI_HISI) += pcie-hisi.o
-obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
-obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
-obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
-obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
-obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
obj-$(CONFIG_VMD) += vmd.o
+
+# The following drivers are for devices that use the generic ACPI
+# pci_root.c driver but don't support standard ECAM config access.
+# They contain MCFG quirks to replace the generic ECAM accessors with
+# device-specific ones that are shared with the DT driver.
+
+# The ACPI driver is generic and should not require driver-specific
+# config options to be enabled, so we always build these drivers on
+# ARM64 and use internal ifdefs to only build the pieces we need
+# depending on whether ACPI, the DT driver, or both are enabled.
+
+obj-$(CONFIG_ARM64) += pci-thunder-ecam.o
+obj-$(CONFIG_ARM64) += pci-thunder-pem.o
+obj-$(CONFIG_ARM64) += pci-xgene.o
diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
deleted file mode 100644
index f1c544bb8b68..000000000000
--- a/drivers/pci/host/pci-exynos.c
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- * 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/init.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 {
- struct pcie_port pp;
- void __iomem *elbi_base; /* DT 0th resource */
- void __iomem *phy_base; /* DT 1st resource */
- void __iomem *block_base; /* DT 2nd resource */
- int reset_gpio;
- struct clk *clk;
- struct clk *bus_clk;
-};
-
-/* 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 IRQ_MSI_ENABLE (0x1 << 2)
-#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_COMMON_POWER 0x064
-#define PCIE_PHY_COMMON_PD_CMN (0x1 << 3)
-#define PCIE_PHY_TRSV0_EMP_LVL 0x084
-#define PCIE_PHY_TRSV0_DRV_LVL 0x088
-#define PCIE_PHY_TRSV0_RXCDR 0x0ac
-#define PCIE_PHY_TRSV0_POWER 0x0c4
-#define PCIE_PHY_TRSV0_PD_TSV (0x1 << 7)
-#define PCIE_PHY_TRSV0_LVCC 0x0dc
-#define PCIE_PHY_TRSV1_EMP_LVL 0x144
-#define PCIE_PHY_TRSV1_RXCDR 0x16c
-#define PCIE_PHY_TRSV1_POWER 0x184
-#define PCIE_PHY_TRSV1_PD_TSV (0x1 << 7)
-#define PCIE_PHY_TRSV1_LVCC 0x19c
-#define PCIE_PHY_TRSV2_EMP_LVL 0x204
-#define PCIE_PHY_TRSV2_RXCDR 0x22c
-#define PCIE_PHY_TRSV2_POWER 0x244
-#define PCIE_PHY_TRSV2_PD_TSV (0x1 << 7)
-#define PCIE_PHY_TRSV2_LVCC 0x25c
-#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4
-#define PCIE_PHY_TRSV3_RXCDR 0x2ec
-#define PCIE_PHY_TRSV3_POWER 0x304
-#define PCIE_PHY_TRSV3_PD_TSV (0x1 << 7)
-#define PCIE_PHY_TRSV3_LVCC 0x31c
-
-static void exynos_elb_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg)
-{
- writel(val, exynos_pcie->elbi_base + reg);
-}
-
-static u32 exynos_elb_readl(struct exynos_pcie *exynos_pcie, u32 reg)
-{
- return readl(exynos_pcie->elbi_base + reg);
-}
-
-static void exynos_phy_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg)
-{
- writel(val, exynos_pcie->phy_base + reg);
-}
-
-static u32 exynos_phy_readl(struct exynos_pcie *exynos_pcie, u32 reg)
-{
- return readl(exynos_pcie->phy_base + reg);
-}
-
-static void exynos_blk_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg)
-{
- writel(val, exynos_pcie->block_base + reg);
-}
-
-static u32 exynos_blk_readl(struct exynos_pcie *exynos_pcie, u32 reg)
-{
- return readl(exynos_pcie->block_base + reg);
-}
-
-static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *exynos_pcie,
- bool on)
-{
- u32 val;
-
- if (on) {
- val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_AWMISC);
- val |= PCIE_ELBI_SLV_DBI_ENABLE;
- exynos_elb_writel(exynos_pcie, val, PCIE_ELBI_SLV_AWMISC);
- } else {
- val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_AWMISC);
- val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
- exynos_elb_writel(exynos_pcie, val, PCIE_ELBI_SLV_AWMISC);
- }
-}
-
-static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *exynos_pcie,
- bool on)
-{
- u32 val;
-
- if (on) {
- val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_ARMISC);
- val |= PCIE_ELBI_SLV_DBI_ENABLE;
- exynos_elb_writel(exynos_pcie, val, PCIE_ELBI_SLV_ARMISC);
- } else {
- val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_ARMISC);
- val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
- exynos_elb_writel(exynos_pcie, val, PCIE_ELBI_SLV_ARMISC);
- }
-}
-
-static void exynos_pcie_assert_core_reset(struct exynos_pcie *exynos_pcie)
-{
- u32 val;
-
- val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET);
- val &= ~PCIE_CORE_RESET_ENABLE;
- exynos_elb_writel(exynos_pcie, val, PCIE_CORE_RESET);
- exynos_elb_writel(exynos_pcie, 0, PCIE_PWR_RESET);
- exynos_elb_writel(exynos_pcie, 0, PCIE_STICKY_RESET);
- exynos_elb_writel(exynos_pcie, 0, PCIE_NONSTICKY_RESET);
-}
-
-static void exynos_pcie_deassert_core_reset(struct exynos_pcie *exynos_pcie)
-{
- u32 val;
-
- val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET);
- val |= PCIE_CORE_RESET_ENABLE;
-
- exynos_elb_writel(exynos_pcie, val, PCIE_CORE_RESET);
- exynos_elb_writel(exynos_pcie, 1, PCIE_STICKY_RESET);
- exynos_elb_writel(exynos_pcie, 1, PCIE_NONSTICKY_RESET);
- exynos_elb_writel(exynos_pcie, 1, PCIE_APP_INIT_RESET);
- exynos_elb_writel(exynos_pcie, 0, PCIE_APP_INIT_RESET);
- exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_MAC_RESET);
-}
-
-static void exynos_pcie_assert_phy_reset(struct exynos_pcie *exynos_pcie)
-{
- exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_MAC_RESET);
- exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_GLOBAL_RESET);
-}
-
-static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *exynos_pcie)
-{
- exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_GLOBAL_RESET);
- exynos_elb_writel(exynos_pcie, 1, PCIE_PWR_RESET);
- exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET);
- exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_CMN_REG);
- exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSVREG_RESET);
- exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSV_RESET);
-}
-
-static void exynos_pcie_power_on_phy(struct exynos_pcie *exynos_pcie)
-{
- u32 val;
-
- val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER);
- val &= ~PCIE_PHY_COMMON_PD_CMN;
- exynos_phy_writel(exynos_pcie, val, PCIE_PHY_COMMON_POWER);
-
- val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV0_POWER);
- val &= ~PCIE_PHY_TRSV0_PD_TSV;
- exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV0_POWER);
-
- val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV1_POWER);
- val &= ~PCIE_PHY_TRSV1_PD_TSV;
- exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV1_POWER);
-
- val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV2_POWER);
- val &= ~PCIE_PHY_TRSV2_PD_TSV;
- exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV2_POWER);
-
- val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV3_POWER);
- val &= ~PCIE_PHY_TRSV3_PD_TSV;
- exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER);
-}
-
-static void exynos_pcie_power_off_phy(struct exynos_pcie *exynos_pcie)
-{
- u32 val;
-
- val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER);
- val |= PCIE_PHY_COMMON_PD_CMN;
- exynos_phy_writel(exynos_pcie, val, PCIE_PHY_COMMON_POWER);
-
- val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV0_POWER);
- val |= PCIE_PHY_TRSV0_PD_TSV;
- exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV0_POWER);
-
- val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV1_POWER);
- val |= PCIE_PHY_TRSV1_PD_TSV;
- exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV1_POWER);
-
- val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV2_POWER);
- val |= PCIE_PHY_TRSV2_PD_TSV;
- exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV2_POWER);
-
- val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV3_POWER);
- val |= PCIE_PHY_TRSV3_PD_TSV;
- exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER);
-}
-
-static void exynos_pcie_init_phy(struct exynos_pcie *exynos_pcie)
-{
- /* DCC feedback control off */
- exynos_phy_writel(exynos_pcie, 0x29, PCIE_PHY_DCC_FEEDBACK);
-
- /* set TX/RX impedance */
- exynos_phy_writel(exynos_pcie, 0xd5, PCIE_PHY_IMPEDANCE);
-
- /* set 50Mhz PHY clock */
- exynos_phy_writel(exynos_pcie, 0x14, PCIE_PHY_PLL_DIV_0);
- exynos_phy_writel(exynos_pcie, 0x12, PCIE_PHY_PLL_DIV_1);
-
- /* set TX Differential output for lane 0 */
- exynos_phy_writel(exynos_pcie, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
-
- /* set TX Pre-emphasis Level Control for lane 0 to minimum */
- exynos_phy_writel(exynos_pcie, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
-
- /* set RX clock and data recovery bandwidth */
- exynos_phy_writel(exynos_pcie, 0xe7, PCIE_PHY_PLL_BIAS);
- exynos_phy_writel(exynos_pcie, 0x82, PCIE_PHY_TRSV0_RXCDR);
- exynos_phy_writel(exynos_pcie, 0x82, PCIE_PHY_TRSV1_RXCDR);
- exynos_phy_writel(exynos_pcie, 0x82, PCIE_PHY_TRSV2_RXCDR);
- exynos_phy_writel(exynos_pcie, 0x82, PCIE_PHY_TRSV3_RXCDR);
-
- /* change TX Pre-emphasis Level Control for lanes */
- exynos_phy_writel(exynos_pcie, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
- exynos_phy_writel(exynos_pcie, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
- exynos_phy_writel(exynos_pcie, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
- exynos_phy_writel(exynos_pcie, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
-
- /* set LVCC */
- exynos_phy_writel(exynos_pcie, 0x20, PCIE_PHY_TRSV0_LVCC);
- exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV1_LVCC);
- exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV2_LVCC);
- exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV3_LVCC);
-}
-
-static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie)
-{
- struct pcie_port *pp = &exynos_pcie->pp;
- struct device *dev = pp->dev;
-
- if (exynos_pcie->reset_gpio >= 0)
- devm_gpio_request_one(dev, exynos_pcie->reset_gpio,
- GPIOF_OUT_INIT_HIGH, "RESET");
-}
-
-static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
-{
- struct pcie_port *pp = &exynos_pcie->pp;
- struct device *dev = pp->dev;
- u32 val;
-
- if (dw_pcie_link_up(pp)) {
- dev_err(dev, "Link already up\n");
- return 0;
- }
-
- exynos_pcie_assert_core_reset(exynos_pcie);
- exynos_pcie_assert_phy_reset(exynos_pcie);
- exynos_pcie_deassert_phy_reset(exynos_pcie);
- exynos_pcie_power_on_phy(exynos_pcie);
- exynos_pcie_init_phy(exynos_pcie);
-
- /* pulse for common reset */
- exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_COMMON_RESET);
- udelay(500);
- exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET);
-
- exynos_pcie_deassert_core_reset(exynos_pcie);
- dw_pcie_setup_rc(pp);
- exynos_pcie_assert_reset(exynos_pcie);
-
- /* assert LTSSM enable */
- exynos_elb_writel(exynos_pcie, PCIE_ELBI_LTSSM_ENABLE,
- PCIE_APP_LTSSM_ENABLE);
-
- /* check if the link is up or not */
- if (!dw_pcie_wait_for_link(pp))
- return 0;
-
- while (exynos_phy_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED) == 0) {
- val = exynos_blk_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED);
- dev_info(dev, "PLL Locked: 0x%x\n", val);
- }
- exynos_pcie_power_off_phy(exynos_pcie);
- return -ETIMEDOUT;
-}
-
-static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *exynos_pcie)
-{
- u32 val;
-
- val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_PULSE);
- exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_PULSE);
-}
-
-static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *exynos_pcie)
-{
- u32 val;
-
- /* enable INTX interrupt */
- val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
- IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
- exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_PULSE);
-}
-
-static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
-{
- struct exynos_pcie *exynos_pcie = arg;
-
- exynos_pcie_clear_irq_pulse(exynos_pcie);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg)
-{
- struct exynos_pcie *exynos_pcie = arg;
- struct pcie_port *pp = &exynos_pcie->pp;
-
- return dw_handle_msi_irq(pp);
-}
-
-static void exynos_pcie_msi_init(struct exynos_pcie *exynos_pcie)
-{
- struct pcie_port *pp = &exynos_pcie->pp;
- u32 val;
-
- dw_pcie_msi_init(pp);
-
- /* enable MSI interrupt */
- val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_EN_LEVEL);
- val |= IRQ_MSI_ENABLE;
- exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_LEVEL);
-}
-
-static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
-{
- exynos_pcie_enable_irq_pulse(exynos_pcie);
-
- if (IS_ENABLED(CONFIG_PCI_MSI))
- exynos_pcie_msi_init(exynos_pcie);
-}
-
-static u32 exynos_pcie_readl_rc(struct pcie_port *pp, u32 reg)
-{
- struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
- u32 val;
-
- exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true);
- val = readl(pp->dbi_base + reg);
- exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false);
- return val;
-}
-
-static void exynos_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val)
-{
- struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
-
- exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true);
- writel(val, pp->dbi_base + reg);
- exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false);
-}
-
-static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
- u32 *val)
-{
- struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
- int ret;
-
- exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true);
- ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
- exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false);
- return ret;
-}
-
-static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
- u32 val)
-{
- struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
- int ret;
-
- exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true);
- ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
- exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false);
- return ret;
-}
-
-static int exynos_pcie_link_up(struct pcie_port *pp)
-{
- struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
- u32 val;
-
- val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_RDLH_LINKUP);
- if (val == PCIE_ELBI_LTSSM_ENABLE)
- return 1;
-
- return 0;
-}
-
-static void exynos_pcie_host_init(struct pcie_port *pp)
-{
- struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
-
- exynos_pcie_establish_link(exynos_pcie);
- exynos_pcie_enable_interrupts(exynos_pcie);
-}
-
-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 __init exynos_add_pcie_port(struct exynos_pcie *exynos_pcie,
- struct platform_device *pdev)
-{
- struct pcie_port *pp = &exynos_pcie->pp;
- struct device *dev = pp->dev;
- int ret;
-
- pp->irq = platform_get_irq(pdev, 1);
- if (!pp->irq) {
- dev_err(dev, "failed to get irq\n");
- return -ENODEV;
- }
- ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler,
- IRQF_SHARED, "exynos-pcie", exynos_pcie);
- if (ret) {
- dev_err(dev, "failed to request irq\n");
- return ret;
- }
-
- if (IS_ENABLED(CONFIG_PCI_MSI)) {
- pp->msi_irq = platform_get_irq(pdev, 0);
- if (!pp->msi_irq) {
- dev_err(dev, "failed to get msi irq\n");
- return -ENODEV;
- }
-
- ret = devm_request_irq(dev, pp->msi_irq,
- exynos_pcie_msi_irq_handler,
- IRQF_SHARED | IRQF_NO_THREAD,
- "exynos-pcie", exynos_pcie);
- if (ret) {
- dev_err(dev, "failed to request msi irq\n");
- return ret;
- }
- }
-
- pp->root_bus_nr = -1;
- pp->ops = &exynos_pcie_host_ops;
-
- ret = dw_pcie_host_init(pp);
- if (ret) {
- dev_err(dev, "failed to initialize host\n");
- return ret;
- }
-
- return 0;
-}
-
-static int __init exynos_pcie_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct exynos_pcie *exynos_pcie;
- struct pcie_port *pp;
- struct device_node *np = dev->of_node;
- struct resource *elbi_base;
- struct resource *phy_base;
- struct resource *block_base;
- int ret;
-
- exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL);
- if (!exynos_pcie)
- return -ENOMEM;
-
- pp = &exynos_pcie->pp;
- pp->dev = dev;
-
- exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
-
- exynos_pcie->clk = devm_clk_get(dev, "pcie");
- if (IS_ERR(exynos_pcie->clk)) {
- dev_err(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(dev, "pcie_bus");
- if (IS_ERR(exynos_pcie->bus_clk)) {
- dev_err(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(dev, elbi_base);
- if (IS_ERR(exynos_pcie->elbi_base)) {
- ret = PTR_ERR(exynos_pcie->elbi_base);
- goto fail_bus_clk;
- }
-
- phy_base = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- exynos_pcie->phy_base = devm_ioremap_resource(dev, phy_base);
- if (IS_ERR(exynos_pcie->phy_base)) {
- ret = PTR_ERR(exynos_pcie->phy_base);
- goto fail_bus_clk;
- }
-
- block_base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- exynos_pcie->block_base = devm_ioremap_resource(dev, block_base);
- if (IS_ERR(exynos_pcie->block_base)) {
- ret = PTR_ERR(exynos_pcie->block_base);
- goto fail_bus_clk;
- }
-
- ret = exynos_add_pcie_port(exynos_pcie, 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", },
- {},
-};
-
-static struct platform_driver exynos_pcie_driver = {
- .remove = __exit_p(exynos_pcie_remove),
- .driver = {
- .name = "exynos-pcie",
- .of_match_table = exynos_pcie_of_match,
- },
-};
-
-/* Exynos PCIe driver does not allow module unload */
-
-static int __init exynos_pcie_init(void)
-{
- return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe);
-}
-subsys_initcall(exynos_pcie_init);
diff --git a/drivers/pci/host/pci-host-common.c b/drivers/pci/host/pci-host-common.c
index e3c48b5deb93..e9a53bae1c25 100644
--- a/drivers/pci/host/pci-host-common.c
+++ b/drivers/pci/host/pci-host-common.c
@@ -145,7 +145,9 @@ int pci_host_common_probe(struct platform_device *pdev,
return -ENODEV;
}
+#ifdef CONFIG_ARM
pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+#endif
/*
* We insert PCI resources into the iomem_resource and
diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c
index 763ff8745828..ada98569b78e 100644
--- a/drivers/pci/host/pci-hyperv.c
+++ b/drivers/pci/host/pci-hyperv.c
@@ -130,7 +130,8 @@ union pci_version {
*/
union win_slot_encoding {
struct {
- u32 func:8;
+ u32 dev:5;
+ u32 func:3;
u32 reserved:24;
} bits;
u32 slot;
@@ -378,6 +379,8 @@ struct hv_pcibus_device {
struct msi_domain_info msi_info;
struct msi_controller msi_chip;
struct irq_domain *irq_domain;
+ struct retarget_msi_interrupt retarget_msi_interrupt_params;
+ spinlock_t retarget_msi_interrupt_lock;
};
/*
@@ -483,7 +486,8 @@ static u32 devfn_to_wslot(int devfn)
union win_slot_encoding wslot;
wslot.slot = 0;
- wslot.bits.func = PCI_SLOT(devfn) | (PCI_FUNC(devfn) << 5);
+ wslot.bits.dev = PCI_SLOT(devfn);
+ wslot.bits.func = PCI_FUNC(devfn);
return wslot.slot;
}
@@ -501,7 +505,7 @@ static int wslot_to_devfn(u32 wslot)
union win_slot_encoding slot_no;
slot_no.slot = wslot;
- return PCI_DEVFN(0, slot_no.bits.func);
+ return PCI_DEVFN(slot_no.bits.dev, slot_no.bits.func);
}
/*
@@ -755,7 +759,7 @@ static int hv_set_affinity(struct irq_data *data, const struct cpumask *dest,
return parent->chip->irq_set_affinity(parent, dest, force);
}
-void hv_irq_mask(struct irq_data *data)
+static void hv_irq_mask(struct irq_data *data)
{
pci_msi_mask_irq(data);
}
@@ -770,38 +774,44 @@ void hv_irq_mask(struct irq_data *data)
* is built out of this PCI bus's instance GUID and the function
* number of the device.
*/
-void hv_irq_unmask(struct irq_data *data)
+static void hv_irq_unmask(struct irq_data *data)
{
struct msi_desc *msi_desc = irq_data_get_msi_desc(data);
struct irq_cfg *cfg = irqd_cfg(data);
- struct retarget_msi_interrupt params;
+ struct retarget_msi_interrupt *params;
struct hv_pcibus_device *hbus;
struct cpumask *dest;
struct pci_bus *pbus;
struct pci_dev *pdev;
int cpu;
+ unsigned long flags;
dest = irq_data_get_affinity_mask(data);
pdev = msi_desc_to_pci_dev(msi_desc);
pbus = pdev->bus;
hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata);
- memset(&params, 0, sizeof(params));
- params.partition_id = HV_PARTITION_ID_SELF;
- params.source = 1; /* MSI(-X) */
- params.address = msi_desc->msg.address_lo;
- params.data = msi_desc->msg.data;
- params.device_id = (hbus->hdev->dev_instance.b[5] << 24) |
+ spin_lock_irqsave(&hbus->retarget_msi_interrupt_lock, flags);
+
+ params = &hbus->retarget_msi_interrupt_params;
+ memset(params, 0, sizeof(*params));
+ params->partition_id = HV_PARTITION_ID_SELF;
+ params->source = 1; /* MSI(-X) */
+ params->address = msi_desc->msg.address_lo;
+ params->data = msi_desc->msg.data;
+ params->device_id = (hbus->hdev->dev_instance.b[5] << 24) |
(hbus->hdev->dev_instance.b[4] << 16) |
(hbus->hdev->dev_instance.b[7] << 8) |
(hbus->hdev->dev_instance.b[6] & 0xf8) |
PCI_FUNC(pdev->devfn);
- params.vector = cfg->vector;
+ params->vector = cfg->vector;
for_each_cpu_and(cpu, dest, cpu_online_mask)
- params.vp_mask |= (1ULL << vmbus_cpu_number_to_vp_number(cpu));
+ params->vp_mask |= (1ULL << vmbus_cpu_number_to_vp_number(cpu));
- hv_do_hypercall(HVCALL_RETARGET_INTERRUPT, &params, NULL);
+ hv_do_hypercall(HVCALL_RETARGET_INTERRUPT, params, NULL);
+
+ spin_unlock_irqrestore(&hbus->retarget_msi_interrupt_lock, flags);
pci_msi_unmask_irq(data);
}
@@ -1271,9 +1281,9 @@ static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus,
struct hv_pci_dev *hpdev;
struct pci_child_message *res_req;
struct q_res_req_compl comp_pkt;
- union {
- struct pci_packet init_packet;
- u8 buffer[0x100];
+ struct {
+ struct pci_packet init_packet;
+ u8 buffer[sizeof(struct pci_child_message)];
} pkt;
unsigned long flags;
int ret;
@@ -1307,6 +1317,18 @@ static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus,
get_pcichild(hpdev, hv_pcidev_ref_initial);
get_pcichild(hpdev, hv_pcidev_ref_childlist);
spin_lock_irqsave(&hbus->device_list_lock, flags);
+
+ /*
+ * When a device is being added to the bus, we set the PCI domain
+ * number to be the device serial number, which is non-zero and
+ * unique on the same VM. The serial numbers start with 1, and
+ * increase by 1 for each device. So device names including this
+ * can have shorter names than based on the bus instance UUID.
+ * Only the first device serial number is used for domain, so the
+ * domain number will not change after the first device is added.
+ */
+ if (list_empty(&hbus->children))
+ hbus->sysdata.domain = desc->ser;
list_add_tail(&hpdev->list_entry, &hbus->children);
spin_unlock_irqrestore(&hbus->device_list_lock, flags);
return hpdev;
@@ -1582,6 +1604,10 @@ static void hv_eject_device_work(struct work_struct *work)
pci_dev_put(pdev);
}
+ spin_lock_irqsave(&hpdev->hbus->device_list_lock, flags);
+ list_del(&hpdev->list_entry);
+ spin_unlock_irqrestore(&hpdev->hbus->device_list_lock, flags);
+
memset(&ctxt, 0, sizeof(ctxt));
ejct_pkt = (struct pci_eject_response *)&ctxt.pkt.message;
ejct_pkt->message_type.type = PCI_EJECTION_COMPLETE;
@@ -1590,10 +1616,6 @@ static void hv_eject_device_work(struct work_struct *work)
sizeof(*ejct_pkt), (unsigned long)&ctxt.pkt,
VM_PKT_DATA_INBAND, 0);
- spin_lock_irqsave(&hpdev->hbus->device_list_lock, flags);
- list_del(&hpdev->list_entry);
- spin_unlock_irqrestore(&hpdev->hbus->device_list_lock, flags);
-
put_pcichild(hpdev, hv_pcidev_ref_childlist);
put_pcichild(hpdev, hv_pcidev_ref_pnp);
put_hvpcibus(hpdev->hbus);
@@ -2186,6 +2208,7 @@ static int hv_pci_probe(struct hv_device *hdev,
INIT_LIST_HEAD(&hbus->resources_for_children);
spin_lock_init(&hbus->config_lock);
spin_lock_init(&hbus->device_list_lock);
+ spin_lock_init(&hbus->retarget_msi_interrupt_lock);
sema_init(&hbus->enum_sem, 1);
init_completion(&hbus->remove_event);
@@ -2266,24 +2289,32 @@ free_bus:
return ret;
}
-/**
- * hv_pci_remove() - Remove routine for this VMBus channel
- * @hdev: VMBus's tracking struct for this root PCI bus
- *
- * Return: 0 on success, -errno on failure
- */
-static int hv_pci_remove(struct hv_device *hdev)
+static void hv_pci_bus_exit(struct hv_device *hdev)
{
- int ret;
- struct hv_pcibus_device *hbus;
- union {
+ struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
+ struct {
struct pci_packet teardown_packet;
- u8 buffer[0x100];
+ u8 buffer[sizeof(struct pci_message)];
} pkt;
struct pci_bus_relations relations;
struct hv_pci_compl comp_pkt;
+ int ret;
- hbus = hv_get_drvdata(hdev);
+ /*
+ * After the host sends the RESCIND_CHANNEL message, it doesn't
+ * access the per-channel ringbuffer any longer.
+ */
+ if (hdev->channel->rescind)
+ return;
+
+ /* Delete any children which might still exist. */
+ memset(&relations, 0, sizeof(relations));
+ hv_pci_devices_present(hbus, &relations);
+
+ ret = hv_send_resources_released(hdev);
+ if (ret)
+ dev_err(&hdev->device,
+ "Couldn't send resources released packet(s)\n");
memset(&pkt.teardown_packet, 0, sizeof(pkt.teardown_packet));
init_completion(&comp_pkt.host_event);
@@ -2298,7 +2329,19 @@ static int hv_pci_remove(struct hv_device *hdev)
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
if (!ret)
wait_for_completion_timeout(&comp_pkt.host_event, 10 * HZ);
+}
+
+/**
+ * hv_pci_remove() - Remove routine for this VMBus channel
+ * @hdev: VMBus's tracking struct for this root PCI bus
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int hv_pci_remove(struct hv_device *hdev)
+{
+ struct hv_pcibus_device *hbus;
+ hbus = hv_get_drvdata(hdev);
if (hbus->state == hv_pcibus_installed) {
/* Remove the bus from PCI's point of view. */
pci_lock_rescan_remove();
@@ -2307,17 +2350,10 @@ static int hv_pci_remove(struct hv_device *hdev)
pci_unlock_rescan_remove();
}
- ret = hv_send_resources_released(hdev);
- if (ret)
- dev_err(&hdev->device,
- "Couldn't send resources released packet(s)\n");
+ hv_pci_bus_exit(hdev);
vmbus_close(hdev->channel);
- /* Delete any children which might still exist. */
- memset(&relations, 0, sizeof(relations));
- hv_pci_devices_present(hbus, &relations);
-
iounmap(hbus->cfg_addr);
hv_free_config_window(hbus);
pci_free_resource_list(&hbus->resources_for_children);
diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c
index 45a89d969700..cd7d51988738 100644
--- a/drivers/pci/host/pci-mvebu.c
+++ b/drivers/pci/host/pci-mvebu.c
@@ -133,6 +133,12 @@ struct mvebu_pcie {
int nports;
};
+struct mvebu_pcie_window {
+ phys_addr_t base;
+ phys_addr_t remap;
+ size_t size;
+};
+
/* Structure representing one PCIe interface */
struct mvebu_pcie_port {
char *name;
@@ -150,10 +156,8 @@ struct mvebu_pcie_port {
struct mvebu_sw_pci_bridge bridge;
struct device_node *dn;
struct mvebu_pcie *pcie;
- phys_addr_t memwin_base;
- size_t memwin_size;
- phys_addr_t iowin_base;
- size_t iowin_size;
+ struct mvebu_pcie_window memwin;
+ struct mvebu_pcie_window iowin;
u32 saved_pcie_stat;
};
@@ -379,23 +383,45 @@ static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port,
}
}
+static void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
+ unsigned int target, unsigned int attribute,
+ const struct mvebu_pcie_window *desired,
+ struct mvebu_pcie_window *cur)
+{
+ if (desired->base == cur->base && desired->remap == cur->remap &&
+ desired->size == cur->size)
+ return;
+
+ if (cur->size != 0) {
+ mvebu_pcie_del_windows(port, cur->base, cur->size);
+ cur->size = 0;
+ cur->base = 0;
+
+ /*
+ * If something tries to change the window while it is enabled
+ * the change will not be done atomically. That would be
+ * difficult to do in the general case.
+ */
+ }
+
+ if (desired->size == 0)
+ return;
+
+ mvebu_pcie_add_windows(port, target, attribute, desired->base,
+ desired->size, desired->remap);
+ *cur = *desired;
+}
+
static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
{
- phys_addr_t iobase;
+ struct mvebu_pcie_window desired = {};
/* Are the new iobase/iolimit values invalid? */
if (port->bridge.iolimit < port->bridge.iobase ||
port->bridge.iolimitupper < port->bridge.iobaseupper ||
!(port->bridge.command & PCI_COMMAND_IO)) {
-
- /* If a window was configured, remove it */
- if (port->iowin_base) {
- mvebu_pcie_del_windows(port, port->iowin_base,
- port->iowin_size);
- port->iowin_base = 0;
- port->iowin_size = 0;
- }
-
+ mvebu_pcie_set_window(port, port->io_target, port->io_attr,
+ &desired, &port->iowin);
return;
}
@@ -412,32 +438,27 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
* specifications. iobase is the bus address, port->iowin_base
* is the CPU address.
*/
- iobase = ((port->bridge.iobase & 0xF0) << 8) |
- (port->bridge.iobaseupper << 16);
- port->iowin_base = port->pcie->io.start + iobase;
- port->iowin_size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
- (port->bridge.iolimitupper << 16)) -
- iobase) + 1;
-
- mvebu_pcie_add_windows(port, port->io_target, port->io_attr,
- port->iowin_base, port->iowin_size,
- iobase);
+ desired.remap = ((port->bridge.iobase & 0xF0) << 8) |
+ (port->bridge.iobaseupper << 16);
+ desired.base = port->pcie->io.start + desired.remap;
+ desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
+ (port->bridge.iolimitupper << 16)) -
+ desired.remap) +
+ 1;
+
+ mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired,
+ &port->iowin);
}
static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
{
+ struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP};
+
/* Are the new membase/memlimit values invalid? */
if (port->bridge.memlimit < port->bridge.membase ||
!(port->bridge.command & PCI_COMMAND_MEMORY)) {
-
- /* If a window was configured, remove it */
- if (port->memwin_base) {
- mvebu_pcie_del_windows(port, port->memwin_base,
- port->memwin_size);
- port->memwin_base = 0;
- port->memwin_size = 0;
- }
-
+ mvebu_pcie_set_window(port, port->mem_target, port->mem_attr,
+ &desired, &port->memwin);
return;
}
@@ -447,14 +468,12 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
* window to setup, according to the PCI-to-PCI bridge
* specifications.
*/
- port->memwin_base = ((port->bridge.membase & 0xFFF0) << 16);
- port->memwin_size =
- (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) -
- port->memwin_base + 1;
-
- mvebu_pcie_add_windows(port, port->mem_target, port->mem_attr,
- port->memwin_base, port->memwin_size,
- MVEBU_MBUS_NO_REMAP);
+ desired.base = ((port->bridge.membase & 0xFFF0) << 16);
+ desired.size = (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) -
+ desired.base + 1;
+
+ mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired,
+ &port->memwin);
}
/*
@@ -1162,7 +1181,7 @@ static int mvebu_pcie_powerup(struct mvebu_pcie_port *port)
return ret;
if (port->reset_gpio) {
- u32 reset_udelay = 20000;
+ u32 reset_udelay = PCI_PM_D3COLD_WAIT * 1000;
of_property_read_u32(port->dn, "reset-delay-us",
&reset_udelay);
diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c
index 1eeefa4df64c..85348590848b 100644
--- a/drivers/pci/host/pci-rcar-gen2.c
+++ b/drivers/pci/host/pci-rcar-gen2.c
@@ -430,10 +430,10 @@ static int rcar_pci_probe(struct platform_device *pdev)
}
static struct of_device_id rcar_pci_of_match[] = {
- { .compatible = "renesas,pci-rcar-gen2", },
{ .compatible = "renesas,pci-r8a7790", },
{ .compatible = "renesas,pci-r8a7791", },
{ .compatible = "renesas,pci-r8a7794", },
+ { .compatible = "renesas,pci-rcar-gen2", },
{ },
};
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index 8dfccf733241..ed8a93f2bfb5 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -51,10 +51,6 @@
#include <soc/tegra/cpuidle.h>
#include <soc/tegra/pmc.h>
-#include <asm/mach/irq.h>
-#include <asm/mach/map.h>
-#include <asm/mach/pci.h>
-
#define INT_PCI_MSI_NR (8 * 32)
/* register definitions */
@@ -188,6 +184,9 @@
#define RP_VEND_XP 0x00000f00
#define RP_VEND_XP_DL_UP (1 << 30)
+#define RP_VEND_CTL2 0x00000fa8
+#define RP_VEND_CTL2_PCA_ENABLE (1 << 7)
+
#define RP_PRIV_MISC 0x00000fe0
#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xe << 0)
#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xf << 0)
@@ -252,6 +251,7 @@ struct tegra_pcie_soc {
bool has_intr_prsnt_sense;
bool has_cml_clk;
bool has_gen2;
+ bool force_pca_enable;
};
static inline struct tegra_msi *to_tegra_msi(struct msi_controller *chip)
@@ -322,11 +322,6 @@ struct tegra_pcie_bus {
unsigned int nr;
};
-static inline struct tegra_pcie *sys_to_pcie(struct pci_sys_data *sys)
-{
- return sys->private_data;
-}
-
static inline void afi_writel(struct tegra_pcie *pcie, u32 value,
unsigned long offset)
{
@@ -385,8 +380,7 @@ static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
unsigned int busnr)
{
struct device *dev = pcie->dev;
- pgprot_t prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
- L_PTE_XN | L_PTE_MT_DEV_SHARED | L_PTE_SHARED);
+ pgprot_t prot = pgprot_device(PAGE_KERNEL);
phys_addr_t cs = pcie->cs->start;
struct tegra_pcie_bus *bus;
unsigned int i;
@@ -430,7 +424,8 @@ free:
static int tegra_pcie_add_bus(struct pci_bus *bus)
{
- struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata);
+ struct pci_host_bridge *host = pci_find_host_bridge(bus);
+ struct tegra_pcie *pcie = pci_host_bridge_priv(host);
struct tegra_pcie_bus *b;
b = tegra_pcie_bus_alloc(pcie, bus->number);
@@ -444,7 +439,8 @@ static int tegra_pcie_add_bus(struct pci_bus *bus)
static void tegra_pcie_remove_bus(struct pci_bus *child)
{
- struct tegra_pcie *pcie = sys_to_pcie(child->sysdata);
+ struct pci_host_bridge *host = pci_find_host_bridge(child);
+ struct tegra_pcie *pcie = pci_host_bridge_priv(host);
struct tegra_pcie_bus *bus, *tmp;
list_for_each_entry_safe(bus, tmp, &pcie->buses, list) {
@@ -461,7 +457,8 @@ static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus,
unsigned int devfn,
int where)
{
- struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata);
+ struct pci_host_bridge *host = pci_find_host_bridge(bus);
+ struct tegra_pcie *pcie = pci_host_bridge_priv(host);
struct device *dev = pcie->dev;
void __iomem *addr = NULL;
@@ -558,6 +555,12 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
afi_writel(port->pcie, value, ctrl);
tegra_pcie_port_reset(port);
+
+ if (soc->force_pca_enable) {
+ value = readl(port->base + RP_VEND_CTL2);
+ value |= RP_VEND_CTL2_PCA_ENABLE;
+ writel(value, port->base + RP_VEND_CTL2);
+ }
}
static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
@@ -610,39 +613,31 @@ static void tegra_pcie_relax_enable(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
-static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
+static int tegra_pcie_request_resources(struct tegra_pcie *pcie)
{
- struct tegra_pcie *pcie = sys_to_pcie(sys);
+ struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
+ struct list_head *windows = &host->windows;
struct device *dev = pcie->dev;
int err;
- sys->mem_offset = pcie->offset.mem;
- sys->io_offset = pcie->offset.io;
+ pci_add_resource_offset(windows, &pcie->pio, pcie->offset.io);
+ pci_add_resource_offset(windows, &pcie->mem, pcie->offset.mem);
+ pci_add_resource_offset(windows, &pcie->prefetch, pcie->offset.mem);
+ pci_add_resource(windows, &pcie->busn);
- err = devm_request_resource(dev, &iomem_resource, &pcie->io);
+ err = devm_request_pci_bus_resources(dev, windows);
if (err < 0)
return err;
- err = pci_remap_iospace(&pcie->pio, pcie->io.start);
- if (!err)
- pci_add_resource_offset(&sys->resources, &pcie->pio,
- sys->io_offset);
+ pci_remap_iospace(&pcie->pio, pcie->io.start);
- pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
- pci_add_resource_offset(&sys->resources, &pcie->prefetch,
- sys->mem_offset);
- pci_add_resource(&sys->resources, &pcie->busn);
-
- err = devm_request_pci_bus_resources(dev, &sys->resources);
- if (err < 0)
- return err;
-
- return 1;
+ return 0;
}
static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
{
- struct tegra_pcie *pcie = sys_to_pcie(pdev->bus->sysdata);
+ struct pci_host_bridge *host = pci_find_host_bridge(pdev->bus);
+ struct tegra_pcie *pcie = pci_host_bridge_priv(host);
int irq;
tegra_cpuidle_pcie_irqs_in_use();
@@ -1499,10 +1494,11 @@ static const struct irq_domain_ops msi_domain_ops = {
static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
{
- struct device *dev = pcie->dev;
- struct platform_device *pdev = to_platform_device(dev);
+ struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
+ struct platform_device *pdev = to_platform_device(pcie->dev);
const struct tegra_pcie_soc *soc = pcie->soc;
struct tegra_msi *msi = &pcie->msi;
+ struct device *dev = pcie->dev;
unsigned long base;
int err;
u32 reg;
@@ -1559,6 +1555,8 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
reg |= AFI_INTR_MASK_MSI_MASK;
afi_writel(pcie, reg, AFI_INTR_MASK);
+ host->msi = &msi->chip;
+
return 0;
err:
@@ -1609,7 +1607,8 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
struct device *dev = pcie->dev;
struct device_node *np = dev->of_node;
- if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
+ if (of_device_is_compatible(np, "nvidia,tegra124-pcie") ||
+ of_device_is_compatible(np, "nvidia,tegra210-pcie")) {
switch (lanes) {
case 0x0000104:
dev_info(dev, "4x1, 1x1 configuration\n");
@@ -1730,7 +1729,22 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
struct device_node *np = dev->of_node;
unsigned int i = 0;
- if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
+ if (of_device_is_compatible(np, "nvidia,tegra210-pcie")) {
+ pcie->num_supplies = 6;
+
+ pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+ sizeof(*pcie->supplies),
+ GFP_KERNEL);
+ if (!pcie->supplies)
+ return -ENOMEM;
+
+ pcie->supplies[i++].supply = "avdd-pll-uerefe";
+ pcie->supplies[i++].supply = "hvddio-pex";
+ pcie->supplies[i++].supply = "dvddio-pex";
+ pcie->supplies[i++].supply = "dvdd-pex-pll";
+ pcie->supplies[i++].supply = "hvdd-pex-pll-e";
+ pcie->supplies[i++].supply = "vddio-pex-ctl";
+ } else if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
pcie->num_supplies = 7;
pcie->supplies = devm_kcalloc(dev, pcie->num_supplies,
@@ -2021,11 +2035,10 @@ retry:
return false;
}
-static int tegra_pcie_enable(struct tegra_pcie *pcie)
+static void tegra_pcie_enable_ports(struct tegra_pcie *pcie)
{
struct device *dev = pcie->dev;
struct tegra_pcie_port *port, *tmp;
- struct hw_pci hw;
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
dev_info(dev, "probing port %u, using %u lanes\n",
@@ -2041,21 +2054,6 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
tegra_pcie_port_disable(port);
tegra_pcie_port_free(port);
}
-
- memset(&hw, 0, sizeof(hw));
-
-#ifdef CONFIG_PCI_MSI
- hw.msi_ctrl = &pcie->msi.chip;
-#endif
-
- hw.nr_controllers = 1;
- hw.private_data = (void **)&pcie;
- hw.setup = tegra_pcie_setup;
- hw.map_irq = tegra_pcie_map_irq;
- hw.ops = &tegra_pcie_ops;
-
- pci_common_init_dev(dev, &hw);
- return 0;
}
static const struct tegra_pcie_soc tegra20_pcie = {
@@ -2069,6 +2067,7 @@ static const struct tegra_pcie_soc tegra20_pcie = {
.has_intr_prsnt_sense = false,
.has_cml_clk = false,
.has_gen2 = false,
+ .force_pca_enable = false,
};
static const struct tegra_pcie_soc tegra30_pcie = {
@@ -2083,6 +2082,7 @@ static const struct tegra_pcie_soc tegra30_pcie = {
.has_intr_prsnt_sense = true,
.has_cml_clk = true,
.has_gen2 = false,
+ .force_pca_enable = false,
};
static const struct tegra_pcie_soc tegra124_pcie = {
@@ -2096,9 +2096,25 @@ static const struct tegra_pcie_soc tegra124_pcie = {
.has_intr_prsnt_sense = true,
.has_cml_clk = true,
.has_gen2 = true,
+ .force_pca_enable = false,
+};
+
+static const struct tegra_pcie_soc tegra210_pcie = {
+ .num_ports = 2,
+ .msi_base_shift = 8,
+ .pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
+ .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
+ .pads_refclk_cfg0 = 0x90b890b8,
+ .has_pex_clkreq_en = true,
+ .has_pex_bias_ctrl = true,
+ .has_intr_prsnt_sense = true,
+ .has_cml_clk = true,
+ .has_gen2 = true,
+ .force_pca_enable = true,
};
static const struct of_device_id tegra_pcie_of_match[] = {
+ { .compatible = "nvidia,tegra210-pcie", .data = &tegra210_pcie },
{ .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie },
{ .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie },
{ .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie },
@@ -2217,13 +2233,17 @@ remove:
static int tegra_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct pci_host_bridge *host;
struct tegra_pcie *pcie;
+ struct pci_bus *child;
int err;
- pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
- if (!pcie)
+ host = pci_alloc_host_bridge(sizeof(*pcie));
+ if (!host)
return -ENOMEM;
+ pcie = pci_host_bridge_priv(host);
+
pcie->soc = of_device_get_match_data(dev);
INIT_LIST_HEAD(&pcie->buses);
INIT_LIST_HEAD(&pcie->ports);
@@ -2243,6 +2263,10 @@ static int tegra_pcie_probe(struct platform_device *pdev)
if (err)
goto put_resources;
+ err = tegra_pcie_request_resources(pcie);
+ if (err)
+ goto put_resources;
+
/* setup the AFI address translations */
tegra_pcie_setup_translations(pcie);
@@ -2254,12 +2278,30 @@ static int tegra_pcie_probe(struct platform_device *pdev)
}
}
- err = tegra_pcie_enable(pcie);
+ tegra_pcie_enable_ports(pcie);
+
+ pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
+ host->busnr = pcie->busn.start;
+ host->dev.parent = &pdev->dev;
+ host->ops = &tegra_pcie_ops;
+
+ err = pci_register_host_bridge(host);
if (err < 0) {
- dev_err(dev, "failed to enable PCIe ports: %d\n", err);
+ dev_err(dev, "failed to register host: %d\n", err);
goto disable_msi;
}
+ pci_scan_child_bus(host->bus);
+
+ pci_fixup_irqs(pci_common_swizzle, tegra_pcie_map_irq);
+ pci_bus_size_bridges(host->bus);
+ pci_bus_assign_resources(host->bus);
+
+ list_for_each_entry(child, &host->bus->children, node)
+ pcie_bus_configure_settings(child);
+
+ pci_bus_add_devices(host->bus);
+
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_pcie_debugfs_init(pcie);
if (err < 0)
diff --git a/drivers/pci/host/pci-thunder-ecam.c b/drivers/pci/host/pci-thunder-ecam.c
index d50a3dc2d8db..3f54a43bbbea 100644
--- a/drivers/pci/host/pci-thunder-ecam.c
+++ b/drivers/pci/host/pci-thunder-ecam.c
@@ -14,6 +14,8 @@
#include <linux/pci-ecam.h>
#include <linux/platform_device.h>
+#if defined(CONFIG_PCI_HOST_THUNDER_ECAM) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS))
+
static void set_val(u32 v, int where, int size, u32 *val)
{
int shift = (where & 3) * 8;
@@ -346,7 +348,7 @@ static int thunder_ecam_config_write(struct pci_bus *bus, unsigned int devfn,
return pci_generic_config_write(bus, devfn, where, size, val);
}
-static struct pci_ecam_ops pci_thunder_ecam_ops = {
+struct pci_ecam_ops pci_thunder_ecam_ops = {
.bus_shift = 20,
.pci_ops = {
.map_bus = pci_ecam_map_bus,
@@ -355,6 +357,8 @@ static struct pci_ecam_ops pci_thunder_ecam_ops = {
}
};
+#ifdef CONFIG_PCI_HOST_THUNDER_ECAM
+
static const struct of_device_id thunder_ecam_of_match[] = {
{ .compatible = "cavium,pci-host-thunder-ecam" },
{ },
@@ -373,3 +377,6 @@ static struct platform_driver thunder_ecam_driver = {
.probe = thunder_ecam_probe,
};
builtin_platform_driver(thunder_ecam_driver);
+
+#endif
+#endif
diff --git a/drivers/pci/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c
index 6abaf80ffb39..52b5bdccf5f0 100644
--- a/drivers/pci/host/pci-thunder-pem.c
+++ b/drivers/pci/host/pci-thunder-pem.c
@@ -18,8 +18,12 @@
#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
+#include <linux/pci-acpi.h>
#include <linux/pci-ecam.h>
#include <linux/platform_device.h>
+#include "../pci.h"
+
+#if defined(CONFIG_PCI_HOST_THUNDER_PEM) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS))
#define PEM_CFG_WR 0x28
#define PEM_CFG_RD 0x30
@@ -32,7 +36,7 @@ struct thunder_pem_pci {
static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
- u64 read_val;
+ u64 read_val, tmp_val;
struct pci_config_window *cfg = bus->sysdata;
struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv;
@@ -61,13 +65,28 @@ static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
read_val |= 0x00007000; /* Skip MSI CAP */
break;
case 0x70: /* Express Cap */
- /* PME interrupt on vector 2*/
- read_val |= (2u << 25);
+ /*
+ * Change PME interrupt to vector 2 on T88 where it
+ * reads as 0, else leave it alone.
+ */
+ if (!(read_val & (0x1f << 25)))
+ read_val |= (2u << 25);
break;
case 0xb0: /* MSI-X Cap */
- /* TableSize=4, Next Cap is EA */
+ /* TableSize=2 or 4, Next Cap is EA */
read_val &= 0xc00000ff;
- read_val |= 0x0003bc00;
+ /*
+ * If Express Cap(0x70) raw PME vector reads as 0 we are on
+ * T88 and TableSize is reported as 4, else TableSize
+ * is 2.
+ */
+ writeq(0x70, pem_pci->pem_reg_base + PEM_CFG_RD);
+ tmp_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
+ tmp_val >>= 32;
+ if (!(tmp_val & (0x1f << 25)))
+ read_val |= 0x0003bc00;
+ else
+ read_val |= 0x0001bc00;
break;
case 0xb4:
/* Table offset=0, BIR=0 */
@@ -284,35 +303,16 @@ static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn,
return pci_generic_config_write(bus, devfn, where, size, val);
}
-static int thunder_pem_init(struct pci_config_window *cfg)
+static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg,
+ struct resource *res_pem)
{
- struct device *dev = cfg->parent;
- resource_size_t bar4_start;
- struct resource *res_pem;
struct thunder_pem_pci *pem_pci;
- struct platform_device *pdev;
-
- /* Only OF support for now */
- if (!dev->of_node)
- return -EINVAL;
+ resource_size_t bar4_start;
pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL);
if (!pem_pci)
return -ENOMEM;
- pdev = to_platform_device(dev);
-
- /*
- * The second register range is the PEM bridge to the PCIe
- * bus. It has a different config access method than those
- * devices behind the bridge.
- */
- res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res_pem) {
- dev_err(dev, "missing \"reg[1]\"property\n");
- return -EINVAL;
- }
-
pem_pci->pem_reg_base = devm_ioremap(dev, res_pem->start, 0x10000);
if (!pem_pci->pem_reg_base)
return -ENOMEM;
@@ -332,9 +332,69 @@ static int thunder_pem_init(struct pci_config_window *cfg)
return 0;
}
+#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
+
+static int thunder_pem_acpi_init(struct pci_config_window *cfg)
+{
+ struct device *dev = cfg->parent;
+ struct acpi_device *adev = to_acpi_device(dev);
+ struct acpi_pci_root *root = acpi_driver_data(adev);
+ struct resource *res_pem;
+ int ret;
+
+ res_pem = devm_kzalloc(&adev->dev, sizeof(*res_pem), GFP_KERNEL);
+ if (!res_pem)
+ return -ENOMEM;
+
+ ret = acpi_get_rc_resources(dev, "THRX0002", root->segment, res_pem);
+ if (ret) {
+ dev_err(dev, "can't get rc base address\n");
+ return ret;
+ }
+
+ return thunder_pem_init(dev, cfg, res_pem);
+}
+
+struct pci_ecam_ops thunder_pem_ecam_ops = {
+ .bus_shift = 24,
+ .init = thunder_pem_acpi_init,
+ .pci_ops = {
+ .map_bus = pci_ecam_map_bus,
+ .read = thunder_pem_config_read,
+ .write = thunder_pem_config_write,
+ }
+};
+
+#endif
+
+#ifdef CONFIG_PCI_HOST_THUNDER_PEM
+
+static int thunder_pem_platform_init(struct pci_config_window *cfg)
+{
+ struct device *dev = cfg->parent;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *res_pem;
+
+ if (!dev->of_node)
+ return -EINVAL;
+
+ /*
+ * The second register range is the PEM bridge to the PCIe
+ * bus. It has a different config access method than those
+ * devices behind the bridge.
+ */
+ res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res_pem) {
+ dev_err(dev, "missing \"reg[1]\"property\n");
+ return -EINVAL;
+ }
+
+ return thunder_pem_init(dev, cfg, res_pem);
+}
+
static struct pci_ecam_ops pci_thunder_pem_ops = {
.bus_shift = 24,
- .init = thunder_pem_init,
+ .init = thunder_pem_platform_init,
.pci_ops = {
.map_bus = pci_ecam_map_bus,
.read = thunder_pem_config_read,
@@ -360,3 +420,6 @@ static struct platform_driver thunder_pem_driver = {
.probe = thunder_pem_probe,
};
builtin_platform_driver(thunder_pem_driver);
+
+#endif
+#endif
diff --git a/drivers/pci/host/pci-versatile.c b/drivers/pci/host/pci-versatile.c
index b7dc07002f13..5ebee7d37ff5 100644
--- a/drivers/pci/host/pci-versatile.c
+++ b/drivers/pci/host/pci-versatile.c
@@ -124,7 +124,7 @@ static int versatile_pci_probe(struct platform_device *pdev)
int ret, i, myslot = -1;
u32 val;
void __iomem *local_pci_cfg_base;
- struct pci_bus *bus;
+ struct pci_bus *bus, *child;
LIST_HEAD(pci_res);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -204,6 +204,8 @@ static int versatile_pci_probe(struct platform_device *pdev)
pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
pci_assign_unassigned_bus_resources(bus);
+ list_for_each_entry(child, &bus->children, node)
+ pcie_bus_configure_settings(child);
pci_bus_add_devices(bus);
return 0;
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
index a6456b578269..f1b633bce525 100644
--- a/drivers/pci/host/pci-xgene-msi.c
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -360,16 +360,16 @@ static void xgene_msi_isr(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
+static enum cpuhp_state pci_xgene_online;
+
static int xgene_msi_remove(struct platform_device *pdev)
{
- int virq, i;
struct xgene_msi *msi = platform_get_drvdata(pdev);
- for (i = 0; i < NR_HW_IRQS; i++) {
- virq = msi->msi_groups[i].gic_irq;
- if (virq != 0)
- irq_set_chained_handler_and_data(virq, NULL, NULL);
- }
+ if (pci_xgene_online)
+ cpuhp_remove_state(pci_xgene_online);
+ cpuhp_remove_state(CPUHP_PCI_XGENE_DEAD);
+
kfree(msi->msi_groups);
kfree(msi->bitmap);
@@ -427,7 +427,7 @@ static int xgene_msi_hwirq_alloc(unsigned int cpu)
return 0;
}
-static void xgene_msi_hwirq_free(unsigned int cpu)
+static int xgene_msi_hwirq_free(unsigned int cpu)
{
struct xgene_msi *msi = &xgene_msi_ctrl;
struct xgene_msi_group *msi_group;
@@ -441,33 +441,9 @@ static void xgene_msi_hwirq_free(unsigned int cpu)
irq_set_chained_handler_and_data(msi_group->gic_irq, NULL,
NULL);
}
+ return 0;
}
-static int xgene_msi_cpu_callback(struct notifier_block *nfb,
- unsigned long action, void *hcpu)
-{
- unsigned cpu = (unsigned long)hcpu;
-
- switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- xgene_msi_hwirq_alloc(cpu);
- break;
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- xgene_msi_hwirq_free(cpu);
- break;
- default:
- break;
- }
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block xgene_msi_cpu_notifier = {
- .notifier_call = xgene_msi_cpu_callback,
-};
-
static const struct of_device_id xgene_msi_match_table[] = {
{.compatible = "apm,xgene1-msi"},
{},
@@ -478,7 +454,6 @@ static int xgene_msi_probe(struct platform_device *pdev)
struct resource *res;
int rc, irq_index;
struct xgene_msi *xgene_msi;
- unsigned int cpu;
int virt_msir;
u32 msi_val, msi_idx;
@@ -540,28 +515,22 @@ static int xgene_msi_probe(struct platform_device *pdev)
}
}
- cpu_notifier_register_begin();
-
- for_each_online_cpu(cpu)
- if (xgene_msi_hwirq_alloc(cpu)) {
- dev_err(&pdev->dev, "failed to register MSI handlers\n");
- cpu_notifier_register_done();
- goto error;
- }
-
- rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
- if (rc) {
- dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
- cpu_notifier_register_done();
- goto error;
- }
-
- cpu_notifier_register_done();
+ rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "pci/xgene:online",
+ xgene_msi_hwirq_alloc, NULL);
+ if (rc < 0)
+ goto err_cpuhp;
+ pci_xgene_online = rc;
+ rc = cpuhp_setup_state(CPUHP_PCI_XGENE_DEAD, "pci/xgene:dead", NULL,
+ xgene_msi_hwirq_free);
+ if (rc)
+ goto err_cpuhp;
dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
return 0;
+err_cpuhp:
+ dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
error:
xgene_msi_remove(pdev);
return rc;
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 1de23d74783f..1a6108788f6f 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -27,6 +27,8 @@
#include <linux/of_irq.h>
#include <linux/of_pci.h>
#include <linux/pci.h>
+#include <linux/pci-acpi.h>
+#include <linux/pci-ecam.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -64,7 +66,9 @@
/* PCIe IP version */
#define XGENE_PCIE_IP_VER_UNKN 0
#define XGENE_PCIE_IP_VER_1 1
+#define XGENE_PCIE_IP_VER_2 2
+#if defined(CONFIG_PCI_XGENE) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS))
struct xgene_pcie_port {
struct device_node *node;
struct device *dev;
@@ -91,13 +95,24 @@ static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags;
}
+static inline struct xgene_pcie_port *pcie_bus_to_port(struct pci_bus *bus)
+{
+ struct pci_config_window *cfg;
+
+ if (acpi_disabled)
+ return (struct xgene_pcie_port *)(bus->sysdata);
+
+ cfg = bus->sysdata;
+ return (struct xgene_pcie_port *)(cfg->priv);
+}
+
/*
* When the address bit [17:16] is 2'b01, the Configuration access will be
* treated as Type 1 and it will be forwarded to external PCIe device.
*/
static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
{
- struct xgene_pcie_port *port = bus->sysdata;
+ struct xgene_pcie_port *port = pcie_bus_to_port(bus);
if (bus->number >= (bus->primary + 1))
return port->cfg_base + AXI_EP_CFG_ACCESS;
@@ -111,7 +126,7 @@ static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
*/
static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
{
- struct xgene_pcie_port *port = bus->sysdata;
+ struct xgene_pcie_port *port = pcie_bus_to_port(bus);
unsigned int b, d, f;
u32 rtdid_val = 0;
@@ -158,7 +173,7 @@ static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
- struct xgene_pcie_port *port = bus->sysdata;
+ struct xgene_pcie_port *port = pcie_bus_to_port(bus);
if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
PCIBIOS_SUCCESSFUL)
@@ -182,13 +197,100 @@ static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
return PCIBIOS_SUCCESSFUL;
}
+#endif
-static struct pci_ops xgene_pcie_ops = {
- .map_bus = xgene_pcie_map_bus,
- .read = xgene_pcie_config_read32,
- .write = pci_generic_config_write32,
+#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
+static int xgene_get_csr_resource(struct acpi_device *adev,
+ struct resource *res)
+{
+ struct device *dev = &adev->dev;
+ struct resource_entry *entry;
+ struct list_head list;
+ unsigned long flags;
+ int ret;
+
+ INIT_LIST_HEAD(&list);
+ flags = IORESOURCE_MEM;
+ ret = acpi_dev_get_resources(adev, &list,
+ acpi_dev_filter_resource_type_cb,
+ (void *) flags);
+ if (ret < 0) {
+ dev_err(dev, "failed to parse _CRS method, error code %d\n",
+ ret);
+ return ret;
+ }
+
+ if (ret == 0) {
+ dev_err(dev, "no IO and memory resources present in _CRS\n");
+ return -EINVAL;
+ }
+
+ entry = list_first_entry(&list, struct resource_entry, node);
+ *res = *entry->res;
+ acpi_dev_free_resource_list(&list);
+ return 0;
+}
+
+static int xgene_pcie_ecam_init(struct pci_config_window *cfg, u32 ipversion)
+{
+ struct device *dev = cfg->parent;
+ struct acpi_device *adev = to_acpi_device(dev);
+ struct xgene_pcie_port *port;
+ struct resource csr;
+ int ret;
+
+ port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ ret = xgene_get_csr_resource(adev, &csr);
+ if (ret) {
+ dev_err(dev, "can't get CSR resource\n");
+ return ret;
+ }
+ port->csr_base = devm_ioremap_resource(dev, &csr);
+ if (IS_ERR(port->csr_base))
+ return PTR_ERR(port->csr_base);
+
+ port->cfg_base = cfg->win;
+ port->version = ipversion;
+
+ cfg->priv = port;
+ return 0;
+}
+
+static int xgene_v1_pcie_ecam_init(struct pci_config_window *cfg)
+{
+ return xgene_pcie_ecam_init(cfg, XGENE_PCIE_IP_VER_1);
+}
+
+struct pci_ecam_ops xgene_v1_pcie_ecam_ops = {
+ .bus_shift = 16,
+ .init = xgene_v1_pcie_ecam_init,
+ .pci_ops = {
+ .map_bus = xgene_pcie_map_bus,
+ .read = xgene_pcie_config_read32,
+ .write = pci_generic_config_write,
+ }
+};
+
+static int xgene_v2_pcie_ecam_init(struct pci_config_window *cfg)
+{
+ return xgene_pcie_ecam_init(cfg, XGENE_PCIE_IP_VER_2);
+}
+
+struct pci_ecam_ops xgene_v2_pcie_ecam_ops = {
+ .bus_shift = 16,
+ .init = xgene_v2_pcie_ecam_init,
+ .pci_ops = {
+ .map_bus = xgene_pcie_map_bus,
+ .read = xgene_pcie_config_read32,
+ .write = pci_generic_config_write,
+ }
};
+#endif
+#if defined(CONFIG_PCI_XGENE)
static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr,
u32 flags, u64 size)
{
@@ -521,13 +623,19 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
return 0;
}
+static struct pci_ops xgene_pcie_ops = {
+ .map_bus = xgene_pcie_map_bus,
+ .read = xgene_pcie_config_read32,
+ .write = pci_generic_config_write32,
+};
+
static int xgene_pcie_probe_bridge(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *dn = dev->of_node;
struct xgene_pcie_port *port;
resource_size_t iobase = 0;
- struct pci_bus *bus;
+ struct pci_bus *bus, *child;
int ret;
LIST_HEAD(res);
@@ -570,6 +678,8 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
pci_scan_child_bus(bus);
pci_assign_unassigned_bus_resources(bus);
+ list_for_each_entry(child, &bus->children, node)
+ pcie_bus_configure_settings(child);
pci_bus_add_devices(bus);
return 0;
@@ -591,3 +701,4 @@ static struct platform_driver xgene_pcie_driver = {
.probe = xgene_pcie_probe_bridge,
};
builtin_platform_driver(xgene_pcie_driver);
+#endif
diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c
index b0ac4dfafa0b..75ec5cea26f6 100644
--- a/drivers/pci/host/pcie-altera.c
+++ b/drivers/pci/host/pcie-altera.c
@@ -57,15 +57,19 @@
#define TLP_WRITE_TAG 0x10
#define RP_DEVFN 0
#define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn))
-#define TLP_CFG_DW0(pcie, bus) \
+#define TLP_CFGRD_DW0(pcie, bus) \
((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGRD0 \
: TLP_FMTTYPE_CFGRD1) << 24) | \
TLP_PAYLOAD_SIZE)
+#define TLP_CFGWR_DW0(pcie, bus) \
+ ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGWR0 \
+ : TLP_FMTTYPE_CFGWR1) << 24) | \
+ TLP_PAYLOAD_SIZE)
#define TLP_CFG_DW1(pcie, tag, be) \
(((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be))
#define TLP_CFG_DW2(bus, devfn, offset) \
(((bus) << 24) | ((devfn) << 16) | (offset))
-#define TLP_COMP_STATUS(s) (((s) >> 12) & 7)
+#define TLP_COMP_STATUS(s) (((s) >> 13) & 7)
#define TLP_HDR_SIZE 3
#define TLP_LOOP 500
@@ -222,7 +226,7 @@ static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
{
u32 headers[TLP_HDR_SIZE];
- headers[0] = TLP_CFG_DW0(pcie, bus);
+ headers[0] = TLP_CFGRD_DW0(pcie, bus);
headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en);
headers[2] = TLP_CFG_DW2(bus, devfn, where);
@@ -237,7 +241,7 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
u32 headers[TLP_HDR_SIZE];
int ret;
- headers[0] = TLP_CFG_DW0(pcie, bus);
+ headers[0] = TLP_CFGWR_DW0(pcie, bus);
headers[1] = TLP_CFG_DW1(pcie, TLP_WRITE_TAG, byte_en);
headers[2] = TLP_CFG_DW2(bus, devfn, where);
@@ -550,10 +554,8 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie)
cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra");
pcie->cra_base = devm_ioremap_resource(dev, cra);
- if (IS_ERR(pcie->cra_base)) {
- dev_err(dev, "failed to map cra memory\n");
+ if (IS_ERR(pcie->cra_base))
return PTR_ERR(pcie->cra_base);
- }
/* setup IRQ */
pcie->irq = platform_get_irq(pdev, 0);
@@ -641,8 +643,4 @@ static struct platform_driver altera_pcie_driver = {
},
};
-static int altera_pcie_init(void)
-{
- return platform_driver_register(&altera_pcie_driver);
-}
-device_initcall(altera_pcie_init);
+builtin_platform_driver(altera_pcie_driver);
diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
deleted file mode 100644
index a567ea288ee2..000000000000
--- a/drivers/pci/host/pcie-designware.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _PCIE_DESIGNWARE_H
-#define _PCIE_DESIGNWARE_H
-
-/*
- * Maximum number of MSI IRQs can be 256 per controller. But keep
- * it 32 as of now. Probably we will never need more than 32. If needed,
- * then increment it in multiple of 32.
- */
-#define MAX_MSI_IRQS 32
-#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32)
-
-struct pcie_port {
- struct device *dev;
- u8 root_bus_nr;
- void __iomem *dbi_base;
- u64 cfg0_base;
- void __iomem *va_cfg0_base;
- u32 cfg0_size;
- u64 cfg1_base;
- void __iomem *va_cfg1_base;
- u32 cfg1_size;
- resource_size_t io_base;
- phys_addr_t io_bus_addr;
- u32 io_size;
- u64 mem_base;
- phys_addr_t mem_bus_addr;
- u32 mem_size;
- struct resource *cfg;
- struct resource *io;
- struct resource *mem;
- struct resource *busn;
- int irq;
- u32 lanes;
- u32 num_viewport;
- struct pcie_host_ops *ops;
- int msi_irq;
- struct irq_domain *irq_domain;
- unsigned long msi_data;
- u8 iatu_unroll_enabled;
- DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
-};
-
-struct pcie_host_ops {
- u32 (*readl_rc)(struct pcie_port *pp, u32 reg);
- void (*writel_rc)(struct pcie_port *pp, u32 reg, u32 val);
- 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 (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus,
- unsigned int devfn, int where, int size, u32 *val);
- int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus,
- unsigned int devfn, int where, int size, u32 val);
- int (*link_up)(struct pcie_port *pp);
- void (*host_init)(struct pcie_port *pp);
- void (*msi_set_irq)(struct pcie_port *pp, int irq);
- void (*msi_clear_irq)(struct pcie_port *pp, int irq);
- phys_addr_t (*get_msi_addr)(struct pcie_port *pp);
- u32 (*get_msi_data)(struct pcie_port *pp, int pos);
- void (*scan_bus)(struct pcie_port *pp);
- int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip);
-};
-
-u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg);
-void dw_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val);
-int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val);
-int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val);
-irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
-void dw_pcie_msi_init(struct pcie_port *pp);
-int dw_pcie_wait_for_link(struct pcie_port *pp);
-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);
-
-#endif /* _PCIE_DESIGNWARE_H */
diff --git a/drivers/pci/host/pcie-hisi.c b/drivers/pci/host/pcie-hisi.c
deleted file mode 100644
index 56154c25980c..000000000000
--- a/drivers/pci/host/pcie-hisi.c
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * PCIe host controller driver for HiSilicon SoCs
- *
- * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com
- *
- * Authors: Zhou Wang <wangzhou1@hisilicon.com>
- * Dacai Zhu <zhudacai@hisilicon.com>
- * Gabriele Paoloni <gabriele.paoloni@huawei.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/interrupt.h>
-#include <linux/init.h>
-#include <linux/mfd/syscon.h>
-#include <linux/of_address.h>
-#include <linux/of_pci.h>
-#include <linux/platform_device.h>
-#include <linux/of_device.h>
-#include <linux/regmap.h>
-
-#include "pcie-designware.h"
-
-#define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818
-#define PCIE_HIP06_CTRL_OFF 0x1000
-#define PCIE_SYS_STATE4 (PCIE_HIP06_CTRL_OFF + 0x31c)
-#define PCIE_LTSSM_LINKUP_STATE 0x11
-#define PCIE_LTSSM_STATE_MASK 0x3F
-
-#define to_hisi_pcie(x) container_of(x, struct hisi_pcie, pp)
-
-struct hisi_pcie;
-
-struct pcie_soc_ops {
- int (*hisi_pcie_link_up)(struct hisi_pcie *hisi_pcie);
-};
-
-struct hisi_pcie {
- struct pcie_port pp; /* pp.dbi_base is DT rc_dbi */
- struct regmap *subctrl;
- u32 port_id;
- struct pcie_soc_ops *soc_ops;
-};
-
-/* HipXX PCIe host only supports 32-bit config access */
-static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
- u32 *val)
-{
- u32 reg;
- u32 reg_val;
- void *walker = &reg_val;
-
- walker += (where & 0x3);
- reg = where & ~0x3;
- reg_val = dw_pcie_readl_rc(pp, reg);
-
- if (size == 1)
- *val = *(u8 __force *) walker;
- else if (size == 2)
- *val = *(u16 __force *) walker;
- else if (size == 4)
- *val = reg_val;
- else
- return PCIBIOS_BAD_REGISTER_NUMBER;
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-/* HipXX PCIe host only supports 32-bit config access */
-static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size,
- u32 val)
-{
- u32 reg_val;
- u32 reg;
- void *walker = &reg_val;
-
- walker += (where & 0x3);
- reg = where & ~0x3;
- if (size == 4)
- dw_pcie_writel_rc(pp, reg, val);
- else if (size == 2) {
- reg_val = dw_pcie_readl_rc(pp, reg);
- *(u16 __force *) walker = val;
- dw_pcie_writel_rc(pp, reg, reg_val);
- } else if (size == 1) {
- reg_val = dw_pcie_readl_rc(pp, reg);
- *(u8 __force *) walker = val;
- dw_pcie_writel_rc(pp, reg, reg_val);
- } else
- return PCIBIOS_BAD_REGISTER_NUMBER;
-
- return PCIBIOS_SUCCESSFUL;
-}
-
-static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie)
-{
- u32 val;
-
- regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG +
- 0x100 * hisi_pcie->port_id, &val);
-
- return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
-}
-
-static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie)
-{
- struct pcie_port *pp = &hisi_pcie->pp;
- u32 val;
-
- val = dw_pcie_readl_rc(pp, PCIE_SYS_STATE4);
-
- return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
-}
-
-static int hisi_pcie_link_up(struct pcie_port *pp)
-{
- struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp);
-
- return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie);
-}
-
-static struct pcie_host_ops hisi_pcie_host_ops = {
- .rd_own_conf = hisi_pcie_cfg_read,
- .wr_own_conf = hisi_pcie_cfg_write,
- .link_up = hisi_pcie_link_up,
-};
-
-static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie,
- struct platform_device *pdev)
-{
- struct pcie_port *pp = &hisi_pcie->pp;
- struct device *dev = pp->dev;
- int ret;
- u32 port_id;
-
- if (of_property_read_u32(dev->of_node, "port-id", &port_id)) {
- dev_err(dev, "failed to read port-id\n");
- return -EINVAL;
- }
- if (port_id > 3) {
- dev_err(dev, "Invalid port-id: %d\n", port_id);
- return -EINVAL;
- }
- hisi_pcie->port_id = port_id;
-
- pp->ops = &hisi_pcie_host_ops;
-
- ret = dw_pcie_host_init(pp);
- if (ret) {
- dev_err(dev, "failed to initialize host\n");
- return ret;
- }
-
- return 0;
-}
-
-static int hisi_pcie_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct hisi_pcie *hisi_pcie;
- struct pcie_port *pp;
- const struct of_device_id *match;
- struct resource *reg;
- struct device_driver *driver;
- int ret;
-
- hisi_pcie = devm_kzalloc(dev, sizeof(*hisi_pcie), GFP_KERNEL);
- if (!hisi_pcie)
- return -ENOMEM;
-
- pp = &hisi_pcie->pp;
- pp->dev = dev;
- driver = dev->driver;
-
- match = of_match_device(driver->of_match_table, dev);
- hisi_pcie->soc_ops = (struct pcie_soc_ops *) match->data;
-
- hisi_pcie->subctrl =
- syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl");
- if (IS_ERR(hisi_pcie->subctrl)) {
- dev_err(dev, "cannot get subctrl base\n");
- return PTR_ERR(hisi_pcie->subctrl);
- }
-
- reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi");
- pp->dbi_base = devm_ioremap_resource(dev, reg);
- if (IS_ERR(pp->dbi_base)) {
- dev_err(dev, "cannot get rc_dbi base\n");
- return PTR_ERR(pp->dbi_base);
- }
-
- ret = hisi_add_pcie_port(hisi_pcie, pdev);
- if (ret)
- return ret;
-
- dev_warn(dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
-
- return 0;
-}
-
-static struct pcie_soc_ops hip05_ops = {
- &hisi_pcie_link_up_hip05
-};
-
-static struct pcie_soc_ops hip06_ops = {
- &hisi_pcie_link_up_hip06
-};
-
-static const struct of_device_id hisi_pcie_of_match[] = {
- {
- .compatible = "hisilicon,hip05-pcie",
- .data = (void *) &hip05_ops,
- },
- {
- .compatible = "hisilicon,hip06-pcie",
- .data = (void *) &hip06_ops,
- },
- {},
-};
-
-static struct platform_driver hisi_pcie_driver = {
- .probe = hisi_pcie_probe,
- .driver = {
- .name = "hisi-pcie",
- .of_match_table = hisi_pcie_of_match,
- },
-};
-builtin_platform_driver(hisi_pcie_driver);
diff --git a/drivers/pci/host/pcie-iproc-bcma.c b/drivers/pci/host/pcie-iproc-bcma.c
index 8ce089043a27..bd4c9ec25edc 100644
--- a/drivers/pci/host/pcie-iproc-bcma.c
+++ b/drivers/pci/host/pcie-iproc-bcma.c
@@ -54,6 +54,7 @@ static int iproc_pcie_bcma_probe(struct bcma_device *bdev)
pcie->dev = dev;
+ pcie->type = IPROC_PCIE_PAXB_BCMA;
pcie->base = bdev->io_addr;
if (!pcie->base) {
dev_err(dev, "no controller registers\n");
diff --git a/drivers/pci/host/pcie-iproc-msi.c b/drivers/pci/host/pcie-iproc-msi.c
index 9a2973bdc78a..9fad7915f82a 100644
--- a/drivers/pci/host/pcie-iproc-msi.c
+++ b/drivers/pci/host/pcie-iproc-msi.c
@@ -563,6 +563,7 @@ int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node)
}
switch (pcie->type) {
+ case IPROC_PCIE_PAXB_BCMA:
case IPROC_PCIE_PAXB:
msi->reg_offsets = iproc_msi_reg_paxb;
msi->nr_eq_region = 1;
diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c
index a3de087976b3..f4909bb0b2ad 100644
--- a/drivers/pci/host/pcie-iproc-platform.c
+++ b/drivers/pci/host/pcie-iproc-platform.c
@@ -31,8 +31,14 @@ static const struct of_device_id iproc_pcie_of_match_table[] = {
.compatible = "brcm,iproc-pcie",
.data = (int *)IPROC_PCIE_PAXB,
}, {
+ .compatible = "brcm,iproc-pcie-paxb-v2",
+ .data = (int *)IPROC_PCIE_PAXB_V2,
+ }, {
.compatible = "brcm,iproc-pcie-paxc",
.data = (int *)IPROC_PCIE_PAXC,
+ }, {
+ .compatible = "brcm,iproc-pcie-paxc-v2",
+ .data = (int *)IPROC_PCIE_PAXC_V2,
},
{ /* sentinel */ }
};
@@ -41,7 +47,6 @@ MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- const struct of_device_id *of_id;
struct iproc_pcie *pcie;
struct device_node *np = dev->of_node;
struct resource reg;
@@ -49,16 +54,12 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
LIST_HEAD(res);
int ret;
- of_id = of_match_device(iproc_pcie_of_match_table, dev);
- if (!of_id)
- return -EINVAL;
-
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
if (!pcie)
return -ENOMEM;
pcie->dev = dev;
- pcie->type = (enum iproc_pcie_type)of_id->data;
+ pcie->type = (enum iproc_pcie_type) of_device_get_match_data(dev);
ret = of_address_to_resource(np, 0, &reg);
if (ret < 0) {
@@ -84,19 +85,6 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
return ret;
}
pcie->ob.axi_offset = val;
-
- ret = of_property_read_u32(np, "brcm,pcie-ob-window-size",
- &val);
- if (ret) {
- dev_err(dev,
- "missing brcm,pcie-ob-window-size property\n");
- return ret;
- }
- pcie->ob.window_size = (resource_size_t)val * SZ_1M;
-
- if (of_property_read_bool(np, "brcm,pcie-ob-oarr-size"))
- pcie->ob.set_oarr_size = true;
-
pcie->need_ob_cfg = true;
}
@@ -115,7 +103,14 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
return ret;
}
- pcie->map_irq = of_irq_parse_and_map_pci;
+ /* PAXC doesn't support legacy IRQs, skip mapping */
+ switch (pcie->type) {
+ case IPROC_PCIE_PAXC:
+ case IPROC_PCIE_PAXC_V2:
+ break;
+ default:
+ pcie->map_irq = of_irq_parse_and_map_pci;
+ }
ret = iproc_pcie_setup(pcie, &res);
if (ret)
diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index 0b999a9fb843..0f39bd2a04cb 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/irqchip/arm-gic-v3.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
@@ -38,6 +39,12 @@
#define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT)
#define PAXC_RESET_MASK 0x7f
+#define GIC_V3_CFG_SHIFT 0
+#define GIC_V3_CFG BIT(GIC_V3_CFG_SHIFT)
+
+#define MSI_ENABLE_CFG_SHIFT 0
+#define MSI_ENABLE_CFG BIT(MSI_ENABLE_CFG_SHIFT)
+
#define CFG_IND_ADDR_MASK 0x00001ffc
#define CFG_ADDR_BUS_NUM_SHIFT 20
@@ -58,59 +65,319 @@
#define PCIE_DL_ACTIVE_SHIFT 2
#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT)
+#define APB_ERR_EN_SHIFT 0
+#define APB_ERR_EN BIT(APB_ERR_EN_SHIFT)
+
+/* derive the enum index of the outbound/inbound mapping registers */
+#define MAP_REG(base_reg, index) ((base_reg) + (index) * 2)
+
+/*
+ * Maximum number of outbound mapping window sizes that can be supported by any
+ * OARR/OMAP mapping pair
+ */
+#define MAX_NUM_OB_WINDOW_SIZES 4
+
#define OARR_VALID_SHIFT 0
#define OARR_VALID BIT(OARR_VALID_SHIFT)
#define OARR_SIZE_CFG_SHIFT 1
-#define OARR_SIZE_CFG BIT(OARR_SIZE_CFG_SHIFT)
-#define PCI_EXP_CAP 0xac
+/*
+ * Maximum number of inbound mapping region sizes that can be supported by an
+ * IARR
+ */
+#define MAX_NUM_IB_REGION_SIZES 9
+
+#define IMAP_VALID_SHIFT 0
+#define IMAP_VALID BIT(IMAP_VALID_SHIFT)
-#define MAX_NUM_OB_WINDOWS 2
+#define PCI_EXP_CAP 0xac
#define IPROC_PCIE_REG_INVALID 0xffff
+/**
+ * iProc PCIe outbound mapping controller specific parameters
+ *
+ * @window_sizes: list of supported outbound mapping window sizes in MB
+ * @nr_sizes: number of supported outbound mapping window sizes
+ */
+struct iproc_pcie_ob_map {
+ resource_size_t window_sizes[MAX_NUM_OB_WINDOW_SIZES];
+ unsigned int nr_sizes;
+};
+
+static const struct iproc_pcie_ob_map paxb_ob_map[] = {
+ {
+ /* OARR0/OMAP0 */
+ .window_sizes = { 128, 256 },
+ .nr_sizes = 2,
+ },
+ {
+ /* OARR1/OMAP1 */
+ .window_sizes = { 128, 256 },
+ .nr_sizes = 2,
+ },
+};
+
+static const struct iproc_pcie_ob_map paxb_v2_ob_map[] = {
+ {
+ /* OARR0/OMAP0 */
+ .window_sizes = { 128, 256 },
+ .nr_sizes = 2,
+ },
+ {
+ /* OARR1/OMAP1 */
+ .window_sizes = { 128, 256 },
+ .nr_sizes = 2,
+ },
+ {
+ /* OARR2/OMAP2 */
+ .window_sizes = { 128, 256, 512, 1024 },
+ .nr_sizes = 4,
+ },
+ {
+ /* OARR3/OMAP3 */
+ .window_sizes = { 128, 256, 512, 1024 },
+ .nr_sizes = 4,
+ },
+};
+
+/**
+ * iProc PCIe inbound mapping type
+ */
+enum iproc_pcie_ib_map_type {
+ /* for DDR memory */
+ IPROC_PCIE_IB_MAP_MEM = 0,
+
+ /* for device I/O memory */
+ IPROC_PCIE_IB_MAP_IO,
+
+ /* invalid or unused */
+ IPROC_PCIE_IB_MAP_INVALID
+};
+
+/**
+ * iProc PCIe inbound mapping controller specific parameters
+ *
+ * @type: inbound mapping region type
+ * @size_unit: inbound mapping region size unit, could be SZ_1K, SZ_1M, or
+ * SZ_1G
+ * @region_sizes: list of supported inbound mapping region sizes in KB, MB, or
+ * GB, depedning on the size unit
+ * @nr_sizes: number of supported inbound mapping region sizes
+ * @nr_windows: number of supported inbound mapping windows for the region
+ * @imap_addr_offset: register offset between the upper and lower 32-bit
+ * IMAP address registers
+ * @imap_window_offset: register offset between each IMAP window
+ */
+struct iproc_pcie_ib_map {
+ enum iproc_pcie_ib_map_type type;
+ unsigned int size_unit;
+ resource_size_t region_sizes[MAX_NUM_IB_REGION_SIZES];
+ unsigned int nr_sizes;
+ unsigned int nr_windows;
+ u16 imap_addr_offset;
+ u16 imap_window_offset;
+};
+
+static const struct iproc_pcie_ib_map paxb_v2_ib_map[] = {
+ {
+ /* IARR0/IMAP0 */
+ .type = IPROC_PCIE_IB_MAP_IO,
+ .size_unit = SZ_1K,
+ .region_sizes = { 32 },
+ .nr_sizes = 1,
+ .nr_windows = 8,
+ .imap_addr_offset = 0x40,
+ .imap_window_offset = 0x4,
+ },
+ {
+ /* IARR1/IMAP1 (currently unused) */
+ .type = IPROC_PCIE_IB_MAP_INVALID,
+ },
+ {
+ /* IARR2/IMAP2 */
+ .type = IPROC_PCIE_IB_MAP_MEM,
+ .size_unit = SZ_1M,
+ .region_sizes = { 64, 128, 256, 512, 1024, 2048, 4096, 8192,
+ 16384 },
+ .nr_sizes = 9,
+ .nr_windows = 1,
+ .imap_addr_offset = 0x4,
+ .imap_window_offset = 0x8,
+ },
+ {
+ /* IARR3/IMAP3 */
+ .type = IPROC_PCIE_IB_MAP_MEM,
+ .size_unit = SZ_1G,
+ .region_sizes = { 1, 2, 4, 8, 16, 32 },
+ .nr_sizes = 6,
+ .nr_windows = 8,
+ .imap_addr_offset = 0x4,
+ .imap_window_offset = 0x8,
+ },
+ {
+ /* IARR4/IMAP4 */
+ .type = IPROC_PCIE_IB_MAP_MEM,
+ .size_unit = SZ_1G,
+ .region_sizes = { 32, 64, 128, 256, 512 },
+ .nr_sizes = 5,
+ .nr_windows = 8,
+ .imap_addr_offset = 0x4,
+ .imap_window_offset = 0x8,
+ },
+};
+
+/*
+ * iProc PCIe host registers
+ */
enum iproc_pcie_reg {
+ /* clock/reset signal control */
IPROC_PCIE_CLK_CTRL = 0,
+
+ /*
+ * To allow MSI to be steered to an external MSI controller (e.g., ARM
+ * GICv3 ITS)
+ */
+ IPROC_PCIE_MSI_GIC_MODE,
+
+ /*
+ * IPROC_PCIE_MSI_BASE_ADDR and IPROC_PCIE_MSI_WINDOW_SIZE define the
+ * window where the MSI posted writes are written, for the writes to be
+ * interpreted as MSI writes.
+ */
+ IPROC_PCIE_MSI_BASE_ADDR,
+ IPROC_PCIE_MSI_WINDOW_SIZE,
+
+ /*
+ * To hold the address of the register where the MSI writes are
+ * programed. When ARM GICv3 ITS is used, this should be programmed
+ * with the address of the GITS_TRANSLATER register.
+ */
+ IPROC_PCIE_MSI_ADDR_LO,
+ IPROC_PCIE_MSI_ADDR_HI,
+
+ /* enable MSI */
+ IPROC_PCIE_MSI_EN_CFG,
+
+ /* allow access to root complex configuration space */
IPROC_PCIE_CFG_IND_ADDR,
IPROC_PCIE_CFG_IND_DATA,
+
+ /* allow access to device configuration space */
IPROC_PCIE_CFG_ADDR,
IPROC_PCIE_CFG_DATA,
+
+ /* enable INTx */
IPROC_PCIE_INTX_EN,
- IPROC_PCIE_OARR_LO,
- IPROC_PCIE_OARR_HI,
- IPROC_PCIE_OMAP_LO,
- IPROC_PCIE_OMAP_HI,
+
+ /* outbound address mapping */
+ IPROC_PCIE_OARR0,
+ IPROC_PCIE_OMAP0,
+ IPROC_PCIE_OARR1,
+ IPROC_PCIE_OMAP1,
+ IPROC_PCIE_OARR2,
+ IPROC_PCIE_OMAP2,
+ IPROC_PCIE_OARR3,
+ IPROC_PCIE_OMAP3,
+
+ /* inbound address mapping */
+ IPROC_PCIE_IARR0,
+ IPROC_PCIE_IMAP0,
+ IPROC_PCIE_IARR1,
+ IPROC_PCIE_IMAP1,
+ IPROC_PCIE_IARR2,
+ IPROC_PCIE_IMAP2,
+ IPROC_PCIE_IARR3,
+ IPROC_PCIE_IMAP3,
+ IPROC_PCIE_IARR4,
+ IPROC_PCIE_IMAP4,
+
+ /* link status */
IPROC_PCIE_LINK_STATUS,
+
+ /* enable APB error for unsupported requests */
+ IPROC_PCIE_APB_ERR_EN,
+
+ /* total number of core registers */
+ IPROC_PCIE_MAX_NUM_REG,
+};
+
+/* iProc PCIe PAXB BCMA registers */
+static const u16 iproc_pcie_reg_paxb_bcma[] = {
+ [IPROC_PCIE_CLK_CTRL] = 0x000,
+ [IPROC_PCIE_CFG_IND_ADDR] = 0x120,
+ [IPROC_PCIE_CFG_IND_DATA] = 0x124,
+ [IPROC_PCIE_CFG_ADDR] = 0x1f8,
+ [IPROC_PCIE_CFG_DATA] = 0x1fc,
+ [IPROC_PCIE_INTX_EN] = 0x330,
+ [IPROC_PCIE_LINK_STATUS] = 0xf0c,
};
/* iProc PCIe PAXB registers */
static const u16 iproc_pcie_reg_paxb[] = {
- [IPROC_PCIE_CLK_CTRL] = 0x000,
- [IPROC_PCIE_CFG_IND_ADDR] = 0x120,
- [IPROC_PCIE_CFG_IND_DATA] = 0x124,
- [IPROC_PCIE_CFG_ADDR] = 0x1f8,
- [IPROC_PCIE_CFG_DATA] = 0x1fc,
- [IPROC_PCIE_INTX_EN] = 0x330,
- [IPROC_PCIE_OARR_LO] = 0xd20,
- [IPROC_PCIE_OARR_HI] = 0xd24,
- [IPROC_PCIE_OMAP_LO] = 0xd40,
- [IPROC_PCIE_OMAP_HI] = 0xd44,
- [IPROC_PCIE_LINK_STATUS] = 0xf0c,
+ [IPROC_PCIE_CLK_CTRL] = 0x000,
+ [IPROC_PCIE_CFG_IND_ADDR] = 0x120,
+ [IPROC_PCIE_CFG_IND_DATA] = 0x124,
+ [IPROC_PCIE_CFG_ADDR] = 0x1f8,
+ [IPROC_PCIE_CFG_DATA] = 0x1fc,
+ [IPROC_PCIE_INTX_EN] = 0x330,
+ [IPROC_PCIE_OARR0] = 0xd20,
+ [IPROC_PCIE_OMAP0] = 0xd40,
+ [IPROC_PCIE_OARR1] = 0xd28,
+ [IPROC_PCIE_OMAP1] = 0xd48,
+ [IPROC_PCIE_LINK_STATUS] = 0xf0c,
+ [IPROC_PCIE_APB_ERR_EN] = 0xf40,
+};
+
+/* iProc PCIe PAXB v2 registers */
+static const u16 iproc_pcie_reg_paxb_v2[] = {
+ [IPROC_PCIE_CLK_CTRL] = 0x000,
+ [IPROC_PCIE_CFG_IND_ADDR] = 0x120,
+ [IPROC_PCIE_CFG_IND_DATA] = 0x124,
+ [IPROC_PCIE_CFG_ADDR] = 0x1f8,
+ [IPROC_PCIE_CFG_DATA] = 0x1fc,
+ [IPROC_PCIE_INTX_EN] = 0x330,
+ [IPROC_PCIE_OARR0] = 0xd20,
+ [IPROC_PCIE_OMAP0] = 0xd40,
+ [IPROC_PCIE_OARR1] = 0xd28,
+ [IPROC_PCIE_OMAP1] = 0xd48,
+ [IPROC_PCIE_OARR2] = 0xd60,
+ [IPROC_PCIE_OMAP2] = 0xd68,
+ [IPROC_PCIE_OARR3] = 0xdf0,
+ [IPROC_PCIE_OMAP3] = 0xdf8,
+ [IPROC_PCIE_IARR0] = 0xd00,
+ [IPROC_PCIE_IMAP0] = 0xc00,
+ [IPROC_PCIE_IARR2] = 0xd10,
+ [IPROC_PCIE_IMAP2] = 0xcc0,
+ [IPROC_PCIE_IARR3] = 0xe00,
+ [IPROC_PCIE_IMAP3] = 0xe08,
+ [IPROC_PCIE_IARR4] = 0xe68,
+ [IPROC_PCIE_IMAP4] = 0xe70,
+ [IPROC_PCIE_LINK_STATUS] = 0xf0c,
+ [IPROC_PCIE_APB_ERR_EN] = 0xf40,
};
/* iProc PCIe PAXC v1 registers */
static const u16 iproc_pcie_reg_paxc[] = {
- [IPROC_PCIE_CLK_CTRL] = 0x000,
- [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0,
- [IPROC_PCIE_CFG_IND_DATA] = 0x1f4,
- [IPROC_PCIE_CFG_ADDR] = 0x1f8,
- [IPROC_PCIE_CFG_DATA] = 0x1fc,
- [IPROC_PCIE_INTX_EN] = IPROC_PCIE_REG_INVALID,
- [IPROC_PCIE_OARR_LO] = IPROC_PCIE_REG_INVALID,
- [IPROC_PCIE_OARR_HI] = IPROC_PCIE_REG_INVALID,
- [IPROC_PCIE_OMAP_LO] = IPROC_PCIE_REG_INVALID,
- [IPROC_PCIE_OMAP_HI] = IPROC_PCIE_REG_INVALID,
- [IPROC_PCIE_LINK_STATUS] = IPROC_PCIE_REG_INVALID,
+ [IPROC_PCIE_CLK_CTRL] = 0x000,
+ [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0,
+ [IPROC_PCIE_CFG_IND_DATA] = 0x1f4,
+ [IPROC_PCIE_CFG_ADDR] = 0x1f8,
+ [IPROC_PCIE_CFG_DATA] = 0x1fc,
+};
+
+/* iProc PCIe PAXC v2 registers */
+static const u16 iproc_pcie_reg_paxc_v2[] = {
+ [IPROC_PCIE_MSI_GIC_MODE] = 0x050,
+ [IPROC_PCIE_MSI_BASE_ADDR] = 0x074,
+ [IPROC_PCIE_MSI_WINDOW_SIZE] = 0x078,
+ [IPROC_PCIE_MSI_ADDR_LO] = 0x07c,
+ [IPROC_PCIE_MSI_ADDR_HI] = 0x080,
+ [IPROC_PCIE_MSI_EN_CFG] = 0x09c,
+ [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0,
+ [IPROC_PCIE_CFG_IND_DATA] = 0x1f4,
+ [IPROC_PCIE_CFG_ADDR] = 0x1f8,
+ [IPROC_PCIE_CFG_DATA] = 0x1fc,
};
static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
@@ -159,16 +426,26 @@ static inline void iproc_pcie_write_reg(struct iproc_pcie *pcie,
writel(val, pcie->base + offset);
}
-static inline void iproc_pcie_ob_write(struct iproc_pcie *pcie,
- enum iproc_pcie_reg reg,
- unsigned window, u32 val)
+/**
+ * APB error forwarding can be disabled during access of configuration
+ * registers of the endpoint device, to prevent unsupported requests
+ * (typically seen during enumeration with multi-function devices) from
+ * triggering a system exception.
+ */
+static inline void iproc_pcie_apb_err_disable(struct pci_bus *bus,
+ bool disable)
{
- u16 offset = iproc_pcie_reg_offset(pcie, reg);
-
- if (iproc_pcie_reg_is_invalid(offset))
- return;
+ struct iproc_pcie *pcie = iproc_data(bus);
+ u32 val;
- writel(val, pcie->base + offset + (window * 8));
+ if (bus->number && pcie->has_apb_err_disable) {
+ val = iproc_pcie_read_reg(pcie, IPROC_PCIE_APB_ERR_EN);
+ if (disable)
+ val &= ~APB_ERR_EN;
+ else
+ val |= APB_ERR_EN;
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_APB_ERR_EN, val);
+ }
}
/**
@@ -204,7 +481,7 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus,
* PAXC is connected to an internally emulated EP within the SoC. It
* allows only one device.
*/
- if (pcie->type == IPROC_PCIE_PAXC)
+ if (pcie->ep_is_internal)
if (slot > 0)
return NULL;
@@ -222,26 +499,47 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus,
return (pcie->base + offset);
}
+static int iproc_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ int ret;
+
+ iproc_pcie_apb_err_disable(bus, true);
+ ret = pci_generic_config_read32(bus, devfn, where, size, val);
+ iproc_pcie_apb_err_disable(bus, false);
+
+ return ret;
+}
+
+static int iproc_pcie_config_write32(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ int ret;
+
+ iproc_pcie_apb_err_disable(bus, true);
+ ret = pci_generic_config_write32(bus, devfn, where, size, val);
+ iproc_pcie_apb_err_disable(bus, false);
+
+ return ret;
+}
+
static struct pci_ops iproc_pcie_ops = {
.map_bus = iproc_pcie_map_cfg_bus,
- .read = pci_generic_config_read32,
- .write = pci_generic_config_write32,
+ .read = iproc_pcie_config_read32,
+ .write = iproc_pcie_config_write32,
};
static void iproc_pcie_reset(struct iproc_pcie *pcie)
{
u32 val;
- if (pcie->type == IPROC_PCIE_PAXC) {
- val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
- val &= ~PAXC_RESET_MASK;
- iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
- udelay(100);
- val |= PAXC_RESET_MASK;
- iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
- udelay(100);
+ /*
+ * PAXC and the internal emulated endpoint device downstream should not
+ * be reset. If firmware has been loaded on the endpoint device at an
+ * earlier boot stage, reset here causes issues.
+ */
+ if (pcie->ep_is_internal)
return;
- }
/*
* Select perst_b signal as reset source. Put the device into reset,
@@ -270,7 +568,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
* PAXC connects to emulated endpoint devices directly and does not
* have a Serdes. Therefore skip the link detection logic here.
*/
- if (pcie->type == IPROC_PCIE_PAXC)
+ if (pcie->ep_is_internal)
return 0;
val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS);
@@ -334,6 +632,58 @@ static void iproc_pcie_enable(struct iproc_pcie *pcie)
iproc_pcie_write_reg(pcie, IPROC_PCIE_INTX_EN, SYS_RC_INTX_MASK);
}
+static inline bool iproc_pcie_ob_is_valid(struct iproc_pcie *pcie,
+ int window_idx)
+{
+ u32 val;
+
+ val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_OARR0, window_idx));
+
+ return !!(val & OARR_VALID);
+}
+
+static inline int iproc_pcie_ob_write(struct iproc_pcie *pcie, int window_idx,
+ int size_idx, u64 axi_addr, u64 pci_addr)
+{
+ struct device *dev = pcie->dev;
+ u16 oarr_offset, omap_offset;
+
+ /*
+ * Derive the OARR/OMAP offset from the first pair (OARR0/OMAP0) based
+ * on window index.
+ */
+ oarr_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OARR0,
+ window_idx));
+ omap_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OMAP0,
+ window_idx));
+ if (iproc_pcie_reg_is_invalid(oarr_offset) ||
+ iproc_pcie_reg_is_invalid(omap_offset))
+ return -EINVAL;
+
+ /*
+ * Program the OARR registers. The upper 32-bit OARR register is
+ * always right after the lower 32-bit OARR register.
+ */
+ writel(lower_32_bits(axi_addr) | (size_idx << OARR_SIZE_CFG_SHIFT) |
+ OARR_VALID, pcie->base + oarr_offset);
+ writel(upper_32_bits(axi_addr), pcie->base + oarr_offset + 4);
+
+ /* now program the OMAP registers */
+ writel(lower_32_bits(pci_addr), pcie->base + omap_offset);
+ writel(upper_32_bits(pci_addr), pcie->base + omap_offset + 4);
+
+ dev_info(dev, "ob window [%d]: offset 0x%x axi %pap pci %pap\n",
+ window_idx, oarr_offset, &axi_addr, &pci_addr);
+ dev_info(dev, "oarr lo 0x%x oarr hi 0x%x\n",
+ readl(pcie->base + oarr_offset),
+ readl(pcie->base + oarr_offset + 4));
+ dev_info(dev, "omap lo 0x%x omap hi 0x%x\n",
+ readl(pcie->base + omap_offset),
+ readl(pcie->base + omap_offset + 4));
+
+ return 0;
+}
+
/**
* Some iProc SoCs require the SW to configure the outbound address mapping
*
@@ -350,24 +700,7 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
{
struct iproc_pcie_ob *ob = &pcie->ob;
struct device *dev = pcie->dev;
- unsigned i;
- u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS;
- u64 remainder;
-
- if (size > max_size) {
- dev_err(dev,
- "res size %pap exceeds max supported size 0x%llx\n",
- &size, max_size);
- return -EINVAL;
- }
-
- div64_u64_rem(size, ob->window_size, &remainder);
- if (remainder) {
- dev_err(dev,
- "res size %pap needs to be multiple of window size %pap\n",
- &size, &ob->window_size);
- return -EINVAL;
- }
+ int ret = -EINVAL, window_idx, size_idx;
if (axi_addr < ob->axi_offset) {
dev_err(dev, "axi address %pap less than offset %pap\n",
@@ -381,26 +714,70 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
*/
axi_addr -= ob->axi_offset;
- for (i = 0; i < MAX_NUM_OB_WINDOWS; i++) {
- iproc_pcie_ob_write(pcie, IPROC_PCIE_OARR_LO, i,
- lower_32_bits(axi_addr) | OARR_VALID |
- (ob->set_oarr_size ? 1 : 0));
- iproc_pcie_ob_write(pcie, IPROC_PCIE_OARR_HI, i,
- upper_32_bits(axi_addr));
- iproc_pcie_ob_write(pcie, IPROC_PCIE_OMAP_LO, i,
- lower_32_bits(pci_addr));
- iproc_pcie_ob_write(pcie, IPROC_PCIE_OMAP_HI, i,
- upper_32_bits(pci_addr));
-
- size -= ob->window_size;
- if (size == 0)
+ /* iterate through all OARR/OMAP mapping windows */
+ for (window_idx = ob->nr_windows - 1; window_idx >= 0; window_idx--) {
+ const struct iproc_pcie_ob_map *ob_map =
+ &pcie->ob_map[window_idx];
+
+ /*
+ * If current outbound window is already in use, move on to the
+ * next one.
+ */
+ if (iproc_pcie_ob_is_valid(pcie, window_idx))
+ continue;
+
+ /*
+ * Iterate through all supported window sizes within the
+ * OARR/OMAP pair to find a match. Go through the window sizes
+ * in a descending order.
+ */
+ for (size_idx = ob_map->nr_sizes - 1; size_idx >= 0;
+ size_idx--) {
+ resource_size_t window_size =
+ ob_map->window_sizes[size_idx] * SZ_1M;
+
+ if (size < window_size)
+ continue;
+
+ if (!IS_ALIGNED(axi_addr, window_size) ||
+ !IS_ALIGNED(pci_addr, window_size)) {
+ dev_err(dev,
+ "axi %pap or pci %pap not aligned\n",
+ &axi_addr, &pci_addr);
+ return -EINVAL;
+ }
+
+ /*
+ * Match found! Program both OARR and OMAP and mark
+ * them as a valid entry.
+ */
+ ret = iproc_pcie_ob_write(pcie, window_idx, size_idx,
+ axi_addr, pci_addr);
+ if (ret)
+ goto err_ob;
+
+ size -= window_size;
+ if (size == 0)
+ return 0;
+
+ /*
+ * If we are here, we are done with the current window,
+ * but not yet finished all mappings. Need to move on
+ * to the next window.
+ */
+ axi_addr += window_size;
+ pci_addr += window_size;
break;
-
- axi_addr += ob->window_size;
- pci_addr += ob->window_size;
+ }
}
- return 0;
+err_ob:
+ dev_err(dev, "unable to configure outbound mapping\n");
+ dev_err(dev,
+ "axi %pap, axi offset %pap, pci %pap, res size %pap\n",
+ &axi_addr, &ob->axi_offset, &pci_addr, &size);
+
+ return ret;
}
static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
@@ -434,13 +811,323 @@ static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
return 0;
}
+static inline bool iproc_pcie_ib_is_in_use(struct iproc_pcie *pcie,
+ int region_idx)
+{
+ const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx];
+ u32 val;
+
+ val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_IARR0, region_idx));
+
+ return !!(val & (BIT(ib_map->nr_sizes) - 1));
+}
+
+static inline bool iproc_pcie_ib_check_type(const struct iproc_pcie_ib_map *ib_map,
+ enum iproc_pcie_ib_map_type type)
+{
+ return !!(ib_map->type == type);
+}
+
+static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
+ int size_idx, int nr_windows, u64 axi_addr,
+ u64 pci_addr, resource_size_t size)
+{
+ struct device *dev = pcie->dev;
+ const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx];
+ u16 iarr_offset, imap_offset;
+ u32 val;
+ int window_idx;
+
+ iarr_offset = iproc_pcie_reg_offset(pcie,
+ MAP_REG(IPROC_PCIE_IARR0, region_idx));
+ imap_offset = iproc_pcie_reg_offset(pcie,
+ MAP_REG(IPROC_PCIE_IMAP0, region_idx));
+ if (iproc_pcie_reg_is_invalid(iarr_offset) ||
+ iproc_pcie_reg_is_invalid(imap_offset))
+ return -EINVAL;
+
+ dev_info(dev, "ib region [%d]: offset 0x%x axi %pap pci %pap\n",
+ region_idx, iarr_offset, &axi_addr, &pci_addr);
+
+ /*
+ * Program the IARR registers. The upper 32-bit IARR register is
+ * always right after the lower 32-bit IARR register.
+ */
+ writel(lower_32_bits(pci_addr) | BIT(size_idx),
+ pcie->base + iarr_offset);
+ writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4);
+
+ dev_info(dev, "iarr lo 0x%x iarr hi 0x%x\n",
+ readl(pcie->base + iarr_offset),
+ readl(pcie->base + iarr_offset + 4));
+
+ /*
+ * Now program the IMAP registers. Each IARR region may have one or
+ * more IMAP windows.
+ */
+ size >>= ilog2(nr_windows);
+ for (window_idx = 0; window_idx < nr_windows; window_idx++) {
+ val = readl(pcie->base + imap_offset);
+ val |= lower_32_bits(axi_addr) | IMAP_VALID;
+ writel(val, pcie->base + imap_offset);
+ writel(upper_32_bits(axi_addr),
+ pcie->base + imap_offset + ib_map->imap_addr_offset);
+
+ dev_info(dev, "imap window [%d] lo 0x%x hi 0x%x\n",
+ window_idx, readl(pcie->base + imap_offset),
+ readl(pcie->base + imap_offset +
+ ib_map->imap_addr_offset));
+
+ imap_offset += ib_map->imap_window_offset;
+ axi_addr += size;
+ }
+
+ return 0;
+}
+
+static int iproc_pcie_setup_ib(struct iproc_pcie *pcie,
+ struct of_pci_range *range,
+ enum iproc_pcie_ib_map_type type)
+{
+ struct device *dev = pcie->dev;
+ struct iproc_pcie_ib *ib = &pcie->ib;
+ int ret;
+ unsigned int region_idx, size_idx;
+ u64 axi_addr = range->cpu_addr, pci_addr = range->pci_addr;
+ resource_size_t size = range->size;
+
+ /* iterate through all IARR mapping regions */
+ for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) {
+ const struct iproc_pcie_ib_map *ib_map =
+ &pcie->ib_map[region_idx];
+
+ /*
+ * If current inbound region is already in use or not a
+ * compatible type, move on to the next.
+ */
+ if (iproc_pcie_ib_is_in_use(pcie, region_idx) ||
+ !iproc_pcie_ib_check_type(ib_map, type))
+ continue;
+
+ /* iterate through all supported region sizes to find a match */
+ for (size_idx = 0; size_idx < ib_map->nr_sizes; size_idx++) {
+ resource_size_t region_size =
+ ib_map->region_sizes[size_idx] * ib_map->size_unit;
+
+ if (size != region_size)
+ continue;
+
+ if (!IS_ALIGNED(axi_addr, region_size) ||
+ !IS_ALIGNED(pci_addr, region_size)) {
+ dev_err(dev,
+ "axi %pap or pci %pap not aligned\n",
+ &axi_addr, &pci_addr);
+ return -EINVAL;
+ }
+
+ /* Match found! Program IARR and all IMAP windows. */
+ ret = iproc_pcie_ib_write(pcie, region_idx, size_idx,
+ ib_map->nr_windows, axi_addr,
+ pci_addr, size);
+ if (ret)
+ goto err_ib;
+ else
+ return 0;
+
+ }
+ }
+ ret = -EINVAL;
+
+err_ib:
+ dev_err(dev, "unable to configure inbound mapping\n");
+ dev_err(dev, "axi %pap, pci %pap, res size %pap\n",
+ &axi_addr, &pci_addr, &size);
+
+ return ret;
+}
+
+static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
+ struct device_node *node)
+{
+ const int na = 3, ns = 2;
+ int rlen;
+
+ parser->node = node;
+ parser->pna = of_n_addr_cells(node);
+ parser->np = parser->pna + na + ns;
+
+ parser->range = of_get_property(node, "dma-ranges", &rlen);
+ if (!parser->range)
+ return -ENOENT;
+
+ parser->end = parser->range + rlen / sizeof(__be32);
+ return 0;
+}
+
+static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie)
+{
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ int ret;
+
+ /* Get the dma-ranges from DT */
+ ret = pci_dma_range_parser_init(&parser, pcie->dev->of_node);
+ if (ret)
+ return ret;
+
+ for_each_of_pci_range(&parser, &range) {
+ /* Each range entry corresponds to an inbound mapping region */
+ ret = iproc_pcie_setup_ib(pcie, &range, IPROC_PCIE_IB_MAP_MEM);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int iproce_pcie_get_msi(struct iproc_pcie *pcie,
+ struct device_node *msi_node,
+ u64 *msi_addr)
+{
+ struct device *dev = pcie->dev;
+ int ret;
+ struct resource res;
+
+ /*
+ * Check if 'msi-map' points to ARM GICv3 ITS, which is the only
+ * supported external MSI controller that requires steering.
+ */
+ if (!of_device_is_compatible(msi_node, "arm,gic-v3-its")) {
+ dev_err(dev, "unable to find compatible MSI controller\n");
+ return -ENODEV;
+ }
+
+ /* derive GITS_TRANSLATER address from GICv3 */
+ ret = of_address_to_resource(msi_node, 0, &res);
+ if (ret < 0) {
+ dev_err(dev, "unable to obtain MSI controller resources\n");
+ return ret;
+ }
+
+ *msi_addr = res.start + GITS_TRANSLATER;
+ return 0;
+}
+
+static int iproc_pcie_paxb_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr)
+{
+ int ret;
+ struct of_pci_range range;
+
+ memset(&range, 0, sizeof(range));
+ range.size = SZ_32K;
+ range.pci_addr = range.cpu_addr = msi_addr & ~(range.size - 1);
+
+ ret = iproc_pcie_setup_ib(pcie, &range, IPROC_PCIE_IB_MAP_IO);
+ return ret;
+}
+
+static void iproc_pcie_paxc_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr)
+{
+ u32 val;
+
+ /*
+ * Program bits [43:13] of address of GITS_TRANSLATER register into
+ * bits [30:0] of the MSI base address register. In fact, in all iProc
+ * based SoCs, all I/O register bases are well below the 32-bit
+ * boundary, so we can safely assume bits [43:32] are always zeros.
+ */
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_BASE_ADDR,
+ (u32)(msi_addr >> 13));
+
+ /* use a default 8K window size */
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_WINDOW_SIZE, 0);
+
+ /* steering MSI to GICv3 ITS */
+ val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_GIC_MODE);
+ val |= GIC_V3_CFG;
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_GIC_MODE, val);
+
+ /*
+ * Program bits [43:2] of address of GITS_TRANSLATER register into the
+ * iProc MSI address registers.
+ */
+ msi_addr >>= 2;
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_ADDR_HI,
+ upper_32_bits(msi_addr));
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_ADDR_LO,
+ lower_32_bits(msi_addr));
+
+ /* enable MSI */
+ val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_EN_CFG);
+ val |= MSI_ENABLE_CFG;
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_EN_CFG, val);
+}
+
+static int iproc_pcie_msi_steer(struct iproc_pcie *pcie,
+ struct device_node *msi_node)
+{
+ struct device *dev = pcie->dev;
+ int ret;
+ u64 msi_addr;
+
+ ret = iproce_pcie_get_msi(pcie, msi_node, &msi_addr);
+ if (ret < 0) {
+ dev_err(dev, "msi steering failed\n");
+ return ret;
+ }
+
+ switch (pcie->type) {
+ case IPROC_PCIE_PAXB_V2:
+ ret = iproc_pcie_paxb_v2_msi_steer(pcie, msi_addr);
+ if (ret)
+ return ret;
+ break;
+ case IPROC_PCIE_PAXC_V2:
+ iproc_pcie_paxc_v2_msi_steer(pcie, msi_addr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int iproc_pcie_msi_enable(struct iproc_pcie *pcie)
{
struct device_node *msi_node;
+ int ret;
+
+ /*
+ * Either the "msi-parent" or the "msi-map" phandle needs to exist
+ * for us to obtain the MSI node.
+ */
msi_node = of_parse_phandle(pcie->dev->of_node, "msi-parent", 0);
- if (!msi_node)
- return -ENODEV;
+ if (!msi_node) {
+ const __be32 *msi_map = NULL;
+ int len;
+ u32 phandle;
+
+ msi_map = of_get_property(pcie->dev->of_node, "msi-map", &len);
+ if (!msi_map)
+ return -ENODEV;
+
+ phandle = be32_to_cpup(msi_map + 1);
+ msi_node = of_find_node_by_phandle(phandle);
+ if (!msi_node)
+ return -ENODEV;
+ }
+
+ /*
+ * Certain revisions of the iProc PCIe controller require additional
+ * configurations to steer the MSI writes towards an external MSI
+ * controller.
+ */
+ if (pcie->need_msi_steer) {
+ ret = iproc_pcie_msi_steer(pcie, msi_node);
+ if (ret)
+ return ret;
+ }
/*
* If another MSI controller is being used, the call below should fail
@@ -454,14 +1141,80 @@ static void iproc_pcie_msi_disable(struct iproc_pcie *pcie)
iproc_msi_exit(pcie);
}
+static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
+{
+ struct device *dev = pcie->dev;
+ unsigned int reg_idx;
+ const u16 *regs;
+
+ switch (pcie->type) {
+ case IPROC_PCIE_PAXB_BCMA:
+ regs = iproc_pcie_reg_paxb_bcma;
+ break;
+ case IPROC_PCIE_PAXB:
+ regs = iproc_pcie_reg_paxb;
+ pcie->has_apb_err_disable = true;
+ if (pcie->need_ob_cfg) {
+ pcie->ob_map = paxb_ob_map;
+ pcie->ob.nr_windows = ARRAY_SIZE(paxb_ob_map);
+ }
+ break;
+ case IPROC_PCIE_PAXB_V2:
+ regs = iproc_pcie_reg_paxb_v2;
+ pcie->has_apb_err_disable = true;
+ if (pcie->need_ob_cfg) {
+ pcie->ob_map = paxb_v2_ob_map;
+ pcie->ob.nr_windows = ARRAY_SIZE(paxb_v2_ob_map);
+ }
+ pcie->ib.nr_regions = ARRAY_SIZE(paxb_v2_ib_map);
+ pcie->ib_map = paxb_v2_ib_map;
+ pcie->need_msi_steer = true;
+ break;
+ case IPROC_PCIE_PAXC:
+ regs = iproc_pcie_reg_paxc;
+ pcie->ep_is_internal = true;
+ break;
+ case IPROC_PCIE_PAXC_V2:
+ regs = iproc_pcie_reg_paxc_v2;
+ pcie->ep_is_internal = true;
+ pcie->need_msi_steer = true;
+ break;
+ default:
+ dev_err(dev, "incompatible iProc PCIe interface\n");
+ return -EINVAL;
+ }
+
+ pcie->reg_offsets = devm_kcalloc(dev, IPROC_PCIE_MAX_NUM_REG,
+ sizeof(*pcie->reg_offsets),
+ GFP_KERNEL);
+ if (!pcie->reg_offsets)
+ return -ENOMEM;
+
+ /* go through the register table and populate all valid registers */
+ pcie->reg_offsets[0] = (pcie->type == IPROC_PCIE_PAXC_V2) ?
+ IPROC_PCIE_REG_INVALID : regs[0];
+ for (reg_idx = 1; reg_idx < IPROC_PCIE_MAX_NUM_REG; reg_idx++)
+ pcie->reg_offsets[reg_idx] = regs[reg_idx] ?
+ regs[reg_idx] : IPROC_PCIE_REG_INVALID;
+
+ return 0;
+}
+
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
{
struct device *dev;
int ret;
void *sysdata;
- struct pci_bus *bus;
+ struct pci_bus *bus, *child;
dev = pcie->dev;
+
+ ret = iproc_pcie_rev_init(pcie);
+ if (ret) {
+ dev_err(dev, "unable to initialize controller parameters\n");
+ return ret;
+ }
+
ret = devm_request_pci_bus_resources(dev, res);
if (ret)
return ret;
@@ -478,19 +1231,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
goto err_exit_phy;
}
- switch (pcie->type) {
- case IPROC_PCIE_PAXB:
- pcie->reg_offsets = iproc_pcie_reg_paxb;
- break;
- case IPROC_PCIE_PAXC:
- pcie->reg_offsets = iproc_pcie_reg_paxc;
- break;
- default:
- dev_err(dev, "incompatible iProc PCIe interface\n");
- ret = -EINVAL;
- goto err_power_off_phy;
- }
-
iproc_pcie_reset(pcie);
if (pcie->need_ob_cfg) {
@@ -501,6 +1241,10 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
}
}
+ ret = iproc_pcie_map_dma_ranges(pcie);
+ if (ret && ret != -ENOENT)
+ goto err_power_off_phy;
+
#ifdef CONFIG_ARM
pcie->sysdata.private_data = pcie;
sysdata = &pcie->sysdata;
@@ -530,7 +1274,13 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
pci_scan_child_bus(bus);
pci_assign_unassigned_bus_resources(bus);
- pci_fixup_irqs(pci_common_swizzle, pcie->map_irq);
+
+ if (pcie->map_irq)
+ pci_fixup_irqs(pci_common_swizzle, pcie->map_irq);
+
+ list_for_each_entry(child, &bus->children, node)
+ pcie_bus_configure_settings(child);
+
pci_bus_add_devices(bus);
return 0;
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h
index e84d93c53c7b..04fed8e907f1 100644
--- a/drivers/pci/host/pcie-iproc.h
+++ b/drivers/pci/host/pcie-iproc.h
@@ -24,23 +24,34 @@
* endpoint devices.
*/
enum iproc_pcie_type {
- IPROC_PCIE_PAXB = 0,
+ IPROC_PCIE_PAXB_BCMA = 0,
+ IPROC_PCIE_PAXB,
+ IPROC_PCIE_PAXB_V2,
IPROC_PCIE_PAXC,
+ IPROC_PCIE_PAXC_V2,
};
/**
* iProc PCIe outbound mapping
- * @set_oarr_size: indicates the OARR size bit needs to be set
* @axi_offset: offset from the AXI address to the internal address used by
* the iProc PCIe core
- * @window_size: outbound window size
+ * @nr_windows: total number of supported outbound mapping windows
*/
struct iproc_pcie_ob {
- bool set_oarr_size;
resource_size_t axi_offset;
- resource_size_t window_size;
+ unsigned int nr_windows;
};
+/**
+ * iProc PCIe inbound mapping
+ * @nr_regions: total number of supported inbound mapping regions
+ */
+struct iproc_pcie_ib {
+ unsigned int nr_regions;
+};
+
+struct iproc_pcie_ob_map;
+struct iproc_pcie_ib_map;
struct iproc_msi;
/**
@@ -55,14 +66,25 @@ struct iproc_msi;
* @root_bus: pointer to root bus
* @phy: optional PHY device that controls the Serdes
* @map_irq: function callback to map interrupts
+ * @ep_is_internal: indicates an internal emulated endpoint device is connected
+ * @has_apb_err_disable: indicates the controller can be configured to prevent
+ * unsupported request from being forwarded as an APB bus error
+ *
* @need_ob_cfg: indicates SW needs to configure the outbound mapping window
- * @ob: outbound mapping parameters
+ * @ob: outbound mapping related parameters
+ * @ob_map: outbound mapping related parameters specific to the controller
+ *
+ * @ib: inbound mapping related parameters
+ * @ib_map: outbound mapping region related parameters
+ *
+ * @need_msi_steer: indicates additional configuration of the iProc PCIe
+ * controller is required to steer MSI writes to external interrupt controller
* @msi: MSI data
*/
struct iproc_pcie {
struct device *dev;
enum iproc_pcie_type type;
- const u16 *reg_offsets;
+ u16 *reg_offsets;
void __iomem *base;
phys_addr_t base_addr;
#ifdef CONFIG_ARM
@@ -71,8 +93,17 @@ struct iproc_pcie {
struct pci_bus *root_bus;
struct phy *phy;
int (*map_irq)(const struct pci_dev *, u8, u8);
+ bool ep_is_internal;
+ bool has_apb_err_disable;
+
bool need_ob_cfg;
struct iproc_pcie_ob ob;
+ const struct iproc_pcie_ob_map *ob_map;
+
+ struct iproc_pcie_ib ib;
+ const struct iproc_pcie_ib_map *ib_map;
+
+ bool need_msi_steer;
struct iproc_msi *msi;
};
diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c
index 62700d1896f4..cb07c45c1858 100644
--- a/drivers/pci/host/pcie-rcar.c
+++ b/drivers/pci/host/pcie-rcar.c
@@ -1071,13 +1071,14 @@ static int rcar_pcie_parse_map_dma_ranges(struct rcar_pcie *pcie,
static const struct of_device_id rcar_pcie_of_match[] = {
{ .compatible = "renesas,pcie-r8a7779", .data = rcar_pcie_hw_init_h1 },
- { .compatible = "renesas,pcie-rcar-gen2",
- .data = rcar_pcie_hw_init_gen2 },
{ .compatible = "renesas,pcie-r8a7790",
.data = rcar_pcie_hw_init_gen2 },
{ .compatible = "renesas,pcie-r8a7791",
.data = rcar_pcie_hw_init_gen2 },
+ { .compatible = "renesas,pcie-rcar-gen2",
+ .data = rcar_pcie_hw_init_gen2 },
{ .compatible = "renesas,pcie-r8a7795", .data = rcar_pcie_hw_init },
+ { .compatible = "renesas,pcie-rcar-gen3", .data = rcar_pcie_hw_init },
{},
};
@@ -1124,7 +1125,6 @@ static int rcar_pcie_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct rcar_pcie *pcie;
unsigned int data;
- const struct of_device_id *of_id;
int err;
int (*hw_init_fn)(struct rcar_pcie *);
@@ -1148,11 +1148,6 @@ static int rcar_pcie_probe(struct platform_device *pdev)
if (err)
return err;
- of_id = of_match_device(rcar_pcie_of_match, dev);
- if (!of_id || !of_id->data)
- return -EINVAL;
- hw_init_fn = of_id->data;
-
pm_runtime_enable(dev);
err = pm_runtime_get_sync(dev);
if (err < 0) {
@@ -1161,10 +1156,11 @@ static int rcar_pcie_probe(struct platform_device *pdev)
}
/* Failure to get a link might just be that no cards are inserted */
+ hw_init_fn = of_device_get_match_data(dev);
err = hw_init_fn(pcie);
if (err) {
dev_info(dev, "PCIe link down\n");
- err = 0;
+ err = -ENODEV;
goto err_pm_put;
}
diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
index e04f69beb42d..26ddd3535272 100644
--- a/drivers/pci/host/pcie-rockchip.c
+++ b/drivers/pci/host/pcie-rockchip.c
@@ -20,6 +20,7 @@
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
@@ -53,7 +54,12 @@
#define PCIE_CLIENT_ARI_ENABLE HIWORD_UPDATE_BIT(0x0008)
#define PCIE_CLIENT_CONF_LANE_NUM(x) HIWORD_UPDATE(0x0030, ENCODE_LANES(x))
#define PCIE_CLIENT_MODE_RC HIWORD_UPDATE_BIT(0x0040)
+#define PCIE_CLIENT_GEN_SEL_1 HIWORD_UPDATE(0x0080, 0)
#define PCIE_CLIENT_GEN_SEL_2 HIWORD_UPDATE_BIT(0x0080)
+#define PCIE_CLIENT_DEBUG_OUT_0 (PCIE_CLIENT_BASE + 0x3c)
+#define PCIE_CLIENT_DEBUG_LTSSM_MASK GENMASK(5, 0)
+#define PCIE_CLIENT_DEBUG_LTSSM_L1 0x18
+#define PCIE_CLIENT_DEBUG_LTSSM_L2 0x19
#define PCIE_CLIENT_BASIC_STATUS1 (PCIE_CLIENT_BASE + 0x48)
#define PCIE_CLIENT_LINK_STATUS_UP 0x00300000
#define PCIE_CLIENT_LINK_STATUS_MASK 0x00300000
@@ -119,6 +125,7 @@
#define PCIE_CORE_INT_CT BIT(11)
#define PCIE_CORE_INT_UTC BIT(18)
#define PCIE_CORE_INT_MMVC BIT(19)
+#define PCIE_CORE_CONFIG_VENDOR (PCIE_CORE_CTRL_MGMT_BASE + 0x44)
#define PCIE_CORE_INT_MASK (PCIE_CORE_CTRL_MGMT_BASE + 0x210)
#define PCIE_RC_BAR_CONF (PCIE_CORE_CTRL_MGMT_BASE + 0x300)
@@ -132,16 +139,18 @@
PCIE_CORE_INT_MMVC)
#define PCIE_RC_CONFIG_BASE 0xa00000
-#define PCIE_RC_CONFIG_VENDOR (PCIE_RC_CONFIG_BASE + 0x00)
#define PCIE_RC_CONFIG_RID_CCR (PCIE_RC_CONFIG_BASE + 0x08)
#define PCIE_RC_CONFIG_SCC_SHIFT 16
+#define PCIE_RC_CONFIG_DCR (PCIE_RC_CONFIG_BASE + 0xc4)
+#define PCIE_RC_CONFIG_DCR_CSPL_SHIFT 18
+#define PCIE_RC_CONFIG_DCR_CSPL_LIMIT 0xff
+#define PCIE_RC_CONFIG_DCR_CPLS_SHIFT 26
+#define PCIE_RC_CONFIG_LINK_CAP (PCIE_RC_CONFIG_BASE + 0xcc)
+#define PCIE_RC_CONFIG_LINK_CAP_L0S BIT(10)
#define PCIE_RC_CONFIG_LCS (PCIE_RC_CONFIG_BASE + 0xd0)
-#define PCIE_RC_CONFIG_LCS_RETRAIN_LINK BIT(5)
-#define PCIE_RC_CONFIG_LCS_LBMIE BIT(10)
-#define PCIE_RC_CONFIG_LCS_LABIE BIT(11)
-#define PCIE_RC_CONFIG_LCS_LBMS BIT(30)
-#define PCIE_RC_CONFIG_LCS_LAMS BIT(31)
#define PCIE_RC_CONFIG_L1_SUBSTATE_CTRL2 (PCIE_RC_CONFIG_BASE + 0x90c)
+#define PCIE_RC_CONFIG_THP_CAP (PCIE_RC_CONFIG_BASE + 0x274)
+#define PCIE_RC_CONFIG_THP_CAP_NEXT_MASK GENMASK(31, 20)
#define PCIE_CORE_AXI_CONF_BASE 0xc00000
#define PCIE_CORE_OB_REGION_ADDR0 (PCIE_CORE_AXI_CONF_BASE + 0x0)
@@ -165,9 +174,11 @@
#define IB_ROOT_PORT_REG_SIZE_SHIFT 3
#define AXI_WRAPPER_IO_WRITE 0x6
#define AXI_WRAPPER_MEM_WRITE 0x2
+#define AXI_WRAPPER_NOR_MSG 0xc
#define MAX_AXI_IB_ROOTPORT_REGION_NUM 3
#define MIN_AXI_ADDR_BITS_PASSED 8
+#define PCIE_RC_SEND_PME_OFF 0x11960
#define ROCKCHIP_VENDOR_ID 0x1d87
#define PCIE_ECAM_BUS(x) (((x) & 0xff) << 20)
#define PCIE_ECAM_DEV(x) (((x) & 0x1f) << 15)
@@ -176,6 +187,12 @@
#define PCIE_ECAM_ADDR(bus, dev, func, reg) \
(PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \
PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg))
+#define PCIE_LINK_IS_L2(x) \
+ (((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == PCIE_CLIENT_DEBUG_LTSSM_L2)
+#define PCIE_LINK_UP(x) \
+ (((x) & PCIE_CLIENT_LINK_STATUS_MASK) == PCIE_CLIENT_LINK_STATUS_UP)
+#define PCIE_LINK_IS_GEN2(x) \
+ (((x) & PCIE_CORE_PL_CONF_SPEED_MASK) == PCIE_CORE_PL_CONF_SPEED_5G)
#define RC_REGION_0_ADDR_TRANS_H 0x00000000
#define RC_REGION_0_ADDR_TRANS_L 0x00000000
@@ -203,8 +220,16 @@ struct rockchip_pcie {
struct gpio_desc *ep_gpio;
u32 lanes;
u8 root_bus_nr;
+ int link_gen;
struct device *dev;
struct irq_domain *irq_domain;
+ u32 io_size;
+ int offset;
+ phys_addr_t io_bus_addr;
+ void __iomem *msg_region;
+ u32 mem_size;
+ phys_addr_t msg_bus_addr;
+ phys_addr_t mem_bus_addr;
};
static u32 rockchip_pcie_read(struct rockchip_pcie *rockchip, u32 reg)
@@ -223,7 +248,7 @@ static void rockchip_pcie_enable_bw_int(struct rockchip_pcie *rockchip)
u32 status;
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS);
- status |= (PCIE_RC_CONFIG_LCS_LBMIE | PCIE_RC_CONFIG_LCS_LABIE);
+ status |= (PCI_EXP_LNKCTL_LBMIE | PCI_EXP_LNKCTL_LABIE);
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS);
}
@@ -232,7 +257,7 @@ static void rockchip_pcie_clr_bw_int(struct rockchip_pcie *rockchip)
u32 status;
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS);
- status |= (PCIE_RC_CONFIG_LCS_LBMS | PCIE_RC_CONFIG_LCS_LAMS);
+ status |= (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_LABS) << 16;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS);
}
@@ -398,6 +423,40 @@ static struct pci_ops rockchip_pcie_ops = {
.write = rockchip_pcie_wr_conf,
};
+static void rockchip_pcie_set_power_limit(struct rockchip_pcie *rockchip)
+{
+ u32 status, curr, scale, power;
+
+ if (IS_ERR(rockchip->vpcie3v3))
+ return;
+
+ /*
+ * Set RC's captured slot power limit and scale if
+ * vpcie3v3 available. The default values are both zero
+ * which means the software should set these two according
+ * to the actual power supply.
+ */
+ curr = regulator_get_current_limit(rockchip->vpcie3v3);
+ if (curr > 0) {
+ scale = 3; /* 0.001x */
+ curr = curr / 1000; /* convert to mA */
+ power = (curr * 3300) / 1000; /* milliwatt */
+ while (power > PCIE_RC_CONFIG_DCR_CSPL_LIMIT) {
+ if (!scale) {
+ dev_warn(rockchip->dev, "invalid power supply\n");
+ return;
+ }
+ scale--;
+ power = power / 10;
+ }
+
+ status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_DCR);
+ status |= (power << PCIE_RC_CONFIG_DCR_CSPL_SHIFT) |
+ (scale << PCIE_RC_CONFIG_DCR_CPLS_SHIFT);
+ rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_DCR);
+ }
+}
+
/**
* rockchip_pcie_init_port - Initialize hardware
* @rockchip: PCIe port information
@@ -407,7 +466,6 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
struct device *dev = rockchip->dev;
int err;
u32 status;
- unsigned long timeout;
gpiod_set_value(rockchip->ep_gpio, 0);
@@ -429,26 +487,6 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
return err;
}
- udelay(10);
-
- err = reset_control_deassert(rockchip->pm_rst);
- if (err) {
- dev_err(dev, "deassert pm_rst err %d\n", err);
- return err;
- }
-
- err = reset_control_deassert(rockchip->aclk_rst);
- if (err) {
- dev_err(dev, "deassert mgmt_sticky_rst err %d\n", err);
- return err;
- }
-
- err = reset_control_deassert(rockchip->pclk_rst);
- if (err) {
- dev_err(dev, "deassert mgmt_sticky_rst err %d\n", err);
- return err;
- }
-
err = phy_init(rockchip->phy);
if (err < 0) {
dev_err(dev, "fail to init phy, err %d\n", err);
@@ -479,14 +517,40 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
return err;
}
+ udelay(10);
+
+ err = reset_control_deassert(rockchip->pm_rst);
+ if (err) {
+ dev_err(dev, "deassert pm_rst err %d\n", err);
+ return err;
+ }
+
+ err = reset_control_deassert(rockchip->aclk_rst);
+ if (err) {
+ dev_err(dev, "deassert aclk_rst err %d\n", err);
+ return err;
+ }
+
+ err = reset_control_deassert(rockchip->pclk_rst);
+ if (err) {
+ dev_err(dev, "deassert pclk_rst err %d\n", err);
+ return err;
+ }
+
+ if (rockchip->link_gen == 2)
+ rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_2,
+ PCIE_CLIENT_CONFIG);
+ else
+ rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_1,
+ PCIE_CLIENT_CONFIG);
+
rockchip_pcie_write(rockchip,
PCIE_CLIENT_CONF_ENABLE |
PCIE_CLIENT_LINK_TRAIN_ENABLE |
PCIE_CLIENT_ARI_ENABLE |
PCIE_CLIENT_CONF_LANE_NUM(rockchip->lanes) |
- PCIE_CLIENT_MODE_RC |
- PCIE_CLIENT_GEN_SEL_2,
- PCIE_CLIENT_CONFIG);
+ PCIE_CLIENT_MODE_RC,
+ PCIE_CLIENT_CONFIG);
err = phy_power_on(rockchip->phy);
if (err) {
@@ -522,21 +586,19 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
return err;
}
- /*
- * We need to read/write PCIE_RC_CONFIG_L1_SUBSTATE_CTRL2 before
- * enabling ASPM. Otherwise L1PwrOnSc and L1PwrOnVal isn't
- * reliable and enabling ASPM doesn't work. This is a controller
- * bug we need to work around.
- */
- status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_L1_SUBSTATE_CTRL2);
- rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_L1_SUBSTATE_CTRL2);
-
/* Fix the transmitted FTS count desired to exit from L0s. */
status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL_PLC1);
- status = (status & PCIE_CORE_CTRL_PLC1_FTS_MASK) |
+ status = (status & ~PCIE_CORE_CTRL_PLC1_FTS_MASK) |
(PCIE_CORE_CTRL_PLC1_FTS_CNT << PCIE_CORE_CTRL_PLC1_FTS_SHIFT);
rockchip_pcie_write(rockchip, status, PCIE_CORE_CTRL_PLC1);
+ rockchip_pcie_set_power_limit(rockchip);
+
+ /* Set RC's clock architecture as common clock */
+ status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS);
+ status |= PCI_EXP_LNKCTL_CCC;
+ rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS);
+
/* Enable Gen1 training */
rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_ENABLE,
PCIE_CLIENT_CONFIG);
@@ -544,61 +606,54 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
gpiod_set_value(rockchip->ep_gpio, 1);
/* 500ms timeout value should be enough for Gen1/2 training */
- timeout = jiffies + msecs_to_jiffies(500);
-
- for (;;) {
- status = rockchip_pcie_read(rockchip,
- PCIE_CLIENT_BASIC_STATUS1);
- if ((status & PCIE_CLIENT_LINK_STATUS_MASK) ==
- PCIE_CLIENT_LINK_STATUS_UP) {
- dev_dbg(dev, "PCIe link training gen1 pass!\n");
- break;
- }
-
- if (time_after(jiffies, timeout)) {
- dev_err(dev, "PCIe link training gen1 timeout!\n");
- return -ETIMEDOUT;
- }
-
- msleep(20);
- }
-
- /*
- * Enable retrain for gen2. This should be configured only after
- * gen1 finished.
- */
- status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS);
- status |= PCIE_RC_CONFIG_LCS_RETRAIN_LINK;
- rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS);
-
- timeout = jiffies + msecs_to_jiffies(500);
- for (;;) {
- status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL);
- if ((status & PCIE_CORE_PL_CONF_SPEED_MASK) ==
- PCIE_CORE_PL_CONF_SPEED_5G) {
- dev_dbg(dev, "PCIe link training gen2 pass!\n");
- break;
- }
-
- if (time_after(jiffies, timeout)) {
+ err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_BASIC_STATUS1,
+ status, PCIE_LINK_UP(status), 20,
+ 500 * USEC_PER_MSEC);
+ if (err) {
+ dev_err(dev, "PCIe link training gen1 timeout!\n");
+ return -ETIMEDOUT;
+ }
+
+ if (rockchip->link_gen == 2) {
+ /*
+ * Enable retrain for gen2. This should be configured only after
+ * gen1 finished.
+ */
+ status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS);
+ status |= PCI_EXP_LNKCTL_RL;
+ rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS);
+
+ err = readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL,
+ status, PCIE_LINK_IS_GEN2(status), 20,
+ 500 * USEC_PER_MSEC);
+ if (err)
dev_dbg(dev, "PCIe link training gen2 timeout, fall back to gen1!\n");
- break;
- }
-
- msleep(20);
}
/* Check the final link width from negotiated lane counter from MGMT */
status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL);
- status = 0x1 << ((status & PCIE_CORE_PL_CONF_LANE_MASK) >>
- PCIE_CORE_PL_CONF_LANE_MASK);
+ status = 0x1 << ((status & PCIE_CORE_PL_CONF_LANE_MASK) >>
+ PCIE_CORE_PL_CONF_LANE_SHIFT);
dev_dbg(dev, "current link width is x%d\n", status);
rockchip_pcie_write(rockchip, ROCKCHIP_VENDOR_ID,
- PCIE_RC_CONFIG_VENDOR);
+ PCIE_CORE_CONFIG_VENDOR);
rockchip_pcie_write(rockchip,
PCI_CLASS_BRIDGE_PCI << PCIE_RC_CONFIG_SCC_SHIFT,
PCIE_RC_CONFIG_RID_CCR);
+
+ /* Clear THP cap's next cap pointer to remove L1 substate cap */
+ status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_THP_CAP);
+ status &= ~PCIE_RC_CONFIG_THP_CAP_NEXT_MASK;
+ rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_THP_CAP);
+
+ /* Clear L0s from RC's link cap */
+ if (of_property_read_bool(dev->of_node, "aspm-no-l0s")) {
+ status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LINK_CAP);
+ status &= ~PCIE_RC_CONFIG_LINK_CAP_L0S;
+ rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LINK_CAP);
+ }
+
rockchip_pcie_write(rockchip, 0x0, PCIE_RC_BAR_CONF);
rockchip_pcie_write(rockchip,
@@ -794,6 +849,10 @@ static int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip)
rockchip->lanes = 1;
}
+ rockchip->link_gen = of_pci_get_max_link_speed(node);
+ if (rockchip->link_gen < 0 || rockchip->link_gen > 2)
+ rockchip->link_gen = 2;
+
rockchip->core_rst = devm_reset_control_get(dev, "core");
if (IS_ERR(rockchip->core_rst)) {
if (PTR_ERR(rockchip->core_rst) != -EPROBE_DEFER)
@@ -1087,6 +1146,129 @@ static int rockchip_pcie_prog_ib_atu(struct rockchip_pcie *rockchip,
return 0;
}
+static int rockchip_cfg_atu(struct rockchip_pcie *rockchip)
+{
+ struct device *dev = rockchip->dev;
+ int offset;
+ int err;
+ int reg_no;
+
+ for (reg_no = 0; reg_no < (rockchip->mem_size >> 20); reg_no++) {
+ err = rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1,
+ AXI_WRAPPER_MEM_WRITE,
+ 20 - 1,
+ rockchip->mem_bus_addr +
+ (reg_no << 20),
+ 0);
+ if (err) {
+ dev_err(dev, "program RC mem outbound ATU failed\n");
+ return err;
+ }
+ }
+
+ err = rockchip_pcie_prog_ib_atu(rockchip, 2, 32 - 1, 0x0, 0);
+ if (err) {
+ dev_err(dev, "program RC mem inbound ATU failed\n");
+ return err;
+ }
+
+ offset = rockchip->mem_size >> 20;
+ for (reg_no = 0; reg_no < (rockchip->io_size >> 20); reg_no++) {
+ err = rockchip_pcie_prog_ob_atu(rockchip,
+ reg_no + 1 + offset,
+ AXI_WRAPPER_IO_WRITE,
+ 20 - 1,
+ rockchip->io_bus_addr +
+ (reg_no << 20),
+ 0);
+ if (err) {
+ dev_err(dev, "program RC io outbound ATU failed\n");
+ return err;
+ }
+ }
+
+ /* assign message regions */
+ rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1 + offset,
+ AXI_WRAPPER_NOR_MSG,
+ 20 - 1, 0, 0);
+
+ rockchip->msg_bus_addr = rockchip->mem_bus_addr +
+ ((reg_no + offset) << 20);
+ return err;
+}
+
+static int rockchip_pcie_wait_l2(struct rockchip_pcie *rockchip)
+{
+ u32 value;
+ int err;
+
+ /* send PME_TURN_OFF message */
+ writel(0x0, rockchip->msg_region + PCIE_RC_SEND_PME_OFF);
+
+ /* read LTSSM and wait for falling into L2 link state */
+ err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_DEBUG_OUT_0,
+ value, PCIE_LINK_IS_L2(value), 20,
+ jiffies_to_usecs(5 * HZ));
+ if (err) {
+ dev_err(rockchip->dev, "PCIe link enter L2 timeout!\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev)
+{
+ struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
+ int ret;
+
+ /* disable core and cli int since we don't need to ack PME_ACK */
+ rockchip_pcie_write(rockchip, (PCIE_CLIENT_INT_CLI << 16) |
+ PCIE_CLIENT_INT_CLI, PCIE_CLIENT_INT_MASK);
+ rockchip_pcie_write(rockchip, (u32)PCIE_CORE_INT, PCIE_CORE_INT_MASK);
+
+ ret = rockchip_pcie_wait_l2(rockchip);
+ if (ret) {
+ rockchip_pcie_enable_interrupts(rockchip);
+ return ret;
+ }
+
+ phy_power_off(rockchip->phy);
+ phy_exit(rockchip->phy);
+
+ clk_disable_unprepare(rockchip->clk_pcie_pm);
+ clk_disable_unprepare(rockchip->hclk_pcie);
+ clk_disable_unprepare(rockchip->aclk_perf_pcie);
+ clk_disable_unprepare(rockchip->aclk_pcie);
+
+ return ret;
+}
+
+static int __maybe_unused rockchip_pcie_resume_noirq(struct device *dev)
+{
+ struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
+ int err;
+
+ clk_prepare_enable(rockchip->clk_pcie_pm);
+ clk_prepare_enable(rockchip->hclk_pcie);
+ clk_prepare_enable(rockchip->aclk_perf_pcie);
+ clk_prepare_enable(rockchip->aclk_pcie);
+
+ err = rockchip_pcie_init_port(rockchip);
+ if (err)
+ return err;
+
+ err = rockchip_cfg_atu(rockchip);
+ if (err)
+ return err;
+
+ /* Need this to enter L1 again */
+ rockchip_pcie_update_txcredit_mui(rockchip);
+ rockchip_pcie_enable_interrupts(rockchip);
+
+ return 0;
+}
+
static int rockchip_pcie_probe(struct platform_device *pdev)
{
struct rockchip_pcie *rockchip;
@@ -1096,13 +1278,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
resource_size_t io_base;
struct resource *mem;
struct resource *io;
- phys_addr_t io_bus_addr = 0;
- u32 io_size;
- phys_addr_t mem_bus_addr = 0;
- u32 mem_size = 0;
- int reg_no;
int err;
- int offset;
LIST_HEAD(res);
@@ -1113,6 +1289,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
if (!rockchip)
return -ENOMEM;
+ platform_set_drvdata(pdev, rockchip);
rockchip->dev = dev;
err = rockchip_pcie_parse_dt(rockchip);
@@ -1166,17 +1343,16 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
err = devm_request_pci_bus_resources(dev, &res);
if (err)
- goto err_vpcie;
+ goto err_free_res;
/* Get the I/O and memory ranges from DT */
- io_size = 0;
resource_list_for_each_entry(win, &res) {
switch (resource_type(win->res)) {
case IORESOURCE_IO:
io = win->res;
io->name = "I/O";
- io_size = resource_size(io);
- io_bus_addr = io->start - win->offset;
+ rockchip->io_size = resource_size(io);
+ rockchip->io_bus_addr = io->start - win->offset;
err = pci_remap_iospace(io, io_base);
if (err) {
dev_warn(dev, "error %d: failed to map resource %pR\n",
@@ -1187,8 +1363,8 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
case IORESOURCE_MEM:
mem = win->res;
mem->name = "MEM";
- mem_size = resource_size(mem);
- mem_bus_addr = mem->start - win->offset;
+ rockchip->mem_size = resource_size(mem);
+ rockchip->mem_bus_addr = mem->start - win->offset;
break;
case IORESOURCE_BUS:
rockchip->root_bus_nr = win->res->start;
@@ -1198,49 +1374,21 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
}
}
- if (mem_size) {
- for (reg_no = 0; reg_no < (mem_size >> 20); reg_no++) {
- err = rockchip_pcie_prog_ob_atu(rockchip, reg_no + 1,
- AXI_WRAPPER_MEM_WRITE,
- 20 - 1,
- mem_bus_addr +
- (reg_no << 20),
- 0);
- if (err) {
- dev_err(dev, "program RC mem outbound ATU failed\n");
- goto err_vpcie;
- }
- }
- }
-
- err = rockchip_pcie_prog_ib_atu(rockchip, 2, 32 - 1, 0x0, 0);
- if (err) {
- dev_err(dev, "program RC mem inbound ATU failed\n");
- goto err_vpcie;
- }
-
- offset = mem_size >> 20;
+ err = rockchip_cfg_atu(rockchip);
+ if (err)
+ goto err_free_res;
- if (io_size) {
- for (reg_no = 0; reg_no < (io_size >> 20); reg_no++) {
- err = rockchip_pcie_prog_ob_atu(rockchip,
- reg_no + 1 + offset,
- AXI_WRAPPER_IO_WRITE,
- 20 - 1,
- io_bus_addr +
- (reg_no << 20),
- 0);
- if (err) {
- dev_err(dev, "program RC io outbound ATU failed\n");
- goto err_vpcie;
- }
- }
+ rockchip->msg_region = devm_ioremap(rockchip->dev,
+ rockchip->msg_bus_addr, SZ_1M);
+ if (!rockchip->msg_region) {
+ err = -ENOMEM;
+ goto err_free_res;
}
bus = pci_scan_root_bus(&pdev->dev, 0, &rockchip_pcie_ops, rockchip, &res);
if (!bus) {
err = -ENOMEM;
- goto err_vpcie;
+ goto err_free_res;
}
pci_bus_size_bridges(bus);
@@ -1249,11 +1397,10 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
pcie_bus_configure_settings(child);
pci_bus_add_devices(bus);
-
- dev_warn(dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
-
return err;
+err_free_res:
+ pci_free_resource_list(&res);
err_vpcie:
if (!IS_ERR(rockchip->vpcie3v3))
regulator_disable(rockchip->vpcie3v3);
@@ -1273,6 +1420,11 @@ err_aclk_pcie:
return err;
}
+static const struct dev_pm_ops rockchip_pcie_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_pcie_suspend_noirq,
+ rockchip_pcie_resume_noirq)
+};
+
static const struct of_device_id rockchip_pcie_of_match[] = {
{ .compatible = "rockchip,rk3399-pcie", },
{}
@@ -1282,6 +1434,7 @@ static struct platform_driver rockchip_pcie_driver = {
.driver = {
.name = "rockchip-pcie",
.of_match_table = rockchip_pcie_of_match,
+ .pm = &rockchip_pcie_pm_ops,
},
.probe = rockchip_pcie_probe,
diff --git a/drivers/pci/host/pcie-xilinx-nwl.c b/drivers/pci/host/pcie-xilinx-nwl.c
index 43eaa4afab94..4c3e0ab35496 100644
--- a/drivers/pci/host/pcie-xilinx-nwl.c
+++ b/drivers/pci/host/pcie-xilinx-nwl.c
@@ -62,21 +62,9 @@
#define CFG_ENABLE_PM_MSG_FWD BIT(1)
#define CFG_ENABLE_INT_MSG_FWD BIT(2)
#define CFG_ENABLE_ERR_MSG_FWD BIT(3)
-#define CFG_ENABLE_SLT_MSG_FWD BIT(5)
-#define CFG_ENABLE_VEN_MSG_FWD BIT(7)
-#define CFG_ENABLE_OTH_MSG_FWD BIT(13)
-#define CFG_ENABLE_VEN_MSG_EN BIT(14)
-#define CFG_ENABLE_VEN_MSG_VEN_INV BIT(15)
-#define CFG_ENABLE_VEN_MSG_VEN_ID GENMASK(31, 16)
#define CFG_ENABLE_MSG_FILTER_MASK (CFG_ENABLE_PM_MSG_FWD | \
CFG_ENABLE_INT_MSG_FWD | \
- CFG_ENABLE_ERR_MSG_FWD | \
- CFG_ENABLE_SLT_MSG_FWD | \
- CFG_ENABLE_VEN_MSG_FWD | \
- CFG_ENABLE_OTH_MSG_FWD | \
- CFG_ENABLE_VEN_MSG_EN | \
- CFG_ENABLE_VEN_MSG_VEN_INV | \
- CFG_ENABLE_VEN_MSG_VEN_ID)
+ CFG_ENABLE_ERR_MSG_FWD)
/* Misc interrupt status mask bits */
#define MSGF_MISC_SR_RXMSG_AVAIL BIT(0)
diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c
index c8616fadccf1..7f030f5d750b 100644
--- a/drivers/pci/host/pcie-xilinx.c
+++ b/drivers/pci/host/pcie-xilinx.c
@@ -632,7 +632,7 @@ static int xilinx_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct xilinx_pcie_port *port;
- struct pci_bus *bus;
+ struct pci_bus *bus, *child;
int err;
resource_size_t iobase = 0;
LIST_HEAD(res);
@@ -686,6 +686,8 @@ static int xilinx_pcie_probe(struct platform_device *pdev)
#ifndef CONFIG_MICROBLAZE
pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
#endif
+ list_for_each_entry(child, &bus->children, node)
+ pcie_bus_configure_settings(child);
pci_bus_add_devices(bus);
return 0;
diff --git a/drivers/pci/host/vmd.c b/drivers/pci/host/vmd.c
index 37e29b580be3..e27ad2a3bd33 100644
--- a/drivers/pci/host/vmd.c
+++ b/drivers/pci/host/vmd.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/pci.h>
+#include <linux/srcu.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
@@ -39,7 +40,6 @@ static DEFINE_RAW_SPINLOCK(list_lock);
/**
* struct vmd_irq - private data to map driver IRQ to the VMD shared vector
* @node: list item for parent traversal.
- * @rcu: RCU callback item for freeing.
* @irq: back pointer to parent.
* @enabled: true if driver enabled IRQ
* @virq: the virtual IRQ value provided to the requesting driver.
@@ -49,7 +49,6 @@ static DEFINE_RAW_SPINLOCK(list_lock);
*/
struct vmd_irq {
struct list_head node;
- struct rcu_head rcu;
struct vmd_irq_list *irq;
bool enabled;
unsigned int virq;
@@ -58,11 +57,13 @@ struct vmd_irq {
/**
* struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector
* @irq_list: the list of irq's the VMD one demuxes to.
+ * @srcu: SRCU struct for local synchronization.
* @count: number of child IRQs assigned to this vector; used to track
* sharing.
*/
struct vmd_irq_list {
struct list_head irq_list;
+ struct srcu_struct srcu;
unsigned int count;
};
@@ -224,14 +225,14 @@ static void vmd_msi_free(struct irq_domain *domain,
struct vmd_irq *vmdirq = irq_get_chip_data(virq);
unsigned long flags;
- synchronize_rcu();
+ synchronize_srcu(&vmdirq->irq->srcu);
/* XXX: Potential optimization to rebalance */
raw_spin_lock_irqsave(&list_lock, flags);
vmdirq->irq->count--;
raw_spin_unlock_irqrestore(&list_lock, flags);
- kfree_rcu(vmdirq, rcu);
+ kfree(vmdirq);
}
static int vmd_msi_prepare(struct irq_domain *domain, struct device *dev,
@@ -281,7 +282,7 @@ static struct device *to_vmd_dev(struct device *dev)
return &vmd->dev->dev;
}
-static struct dma_map_ops *vmd_dma_ops(struct device *dev)
+static const struct dma_map_ops *vmd_dma_ops(struct device *dev)
{
return get_dma_ops(to_vmd_dev(dev));
}
@@ -646,11 +647,12 @@ static irqreturn_t vmd_irq(int irq, void *data)
{
struct vmd_irq_list *irqs = data;
struct vmd_irq *vmdirq;
+ int idx;
- rcu_read_lock();
+ idx = srcu_read_lock(&irqs->srcu);
list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node)
generic_handle_irq(vmdirq->virq);
- rcu_read_unlock();
+ srcu_read_unlock(&irqs->srcu, idx);
return IRQ_HANDLED;
}
@@ -696,6 +698,10 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
return -ENOMEM;
for (i = 0; i < vmd->msix_count; i++) {
+ err = init_srcu_struct(&vmd->irqs[i].srcu);
+ if (err)
+ return err;
+
INIT_LIST_HEAD(&vmd->irqs[i].irq_list);
err = devm_request_irq(&dev->dev, pci_irq_vector(dev, i),
vmd_irq, 0, "vmd", &vmd->irqs[i]);
@@ -714,12 +720,20 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
return 0;
}
+static void vmd_cleanup_srcu(struct vmd_dev *vmd)
+{
+ int i;
+
+ for (i = 0; i < vmd->msix_count; i++)
+ cleanup_srcu_struct(&vmd->irqs[i].srcu);
+}
+
static void vmd_remove(struct pci_dev *dev)
{
struct vmd_dev *vmd = pci_get_drvdata(dev);
vmd_detach_resources(vmd);
- pci_set_drvdata(dev, NULL);
+ vmd_cleanup_srcu(vmd);
sysfs_remove_link(&vmd->dev->dev.kobj, "domain");
pci_stop_root_bus(vmd->bus);
pci_remove_root_bus(vmd->bus);
@@ -727,7 +741,7 @@ static void vmd_remove(struct pci_dev *dev)
irq_domain_remove(vmd->irq_domain);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int vmd_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index a46b585fae31..5ed2dcaa8e27 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -222,35 +222,6 @@ static void acpiphp_post_dock_fixup(struct acpi_device *adev)
acpiphp_let_context_go(context);
}
-/* Check whether the PCI device is managed by native PCIe hotplug driver */
-static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
-{
- u32 reg32;
- acpi_handle tmp;
- struct acpi_pci_root *root;
-
- /* Check whether the PCIe port supports native PCIe hotplug */
- if (pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &reg32))
- return false;
- if (!(reg32 & PCI_EXP_SLTCAP_HPC))
- return false;
-
- /*
- * Check whether native PCIe hotplug has been enabled for
- * this PCIe hierarchy.
- */
- tmp = acpi_find_root_bridge_handle(pdev);
- if (!tmp)
- return false;
- root = acpi_pci_find_root(tmp);
- if (!root)
- return false;
- if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
- return false;
-
- return true;
-}
-
/**
* acpiphp_add_context - Add ACPIPHP context to an ACPI device object.
* @handle: ACPI handle of the object to add a context to.
@@ -334,7 +305,7 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
* expose slots to user space in those cases.
*/
if ((acpi_pci_check_ejectable(pbus, handle) || is_dock_device(adev))
- && !(pdev && device_is_managed_by_native_pciehp(pdev))) {
+ && !(pdev && pdev->is_hotplug_bridge && pciehp_is_native(pdev))) {
unsigned long long sun;
int retval;
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index f6221d739f59..984c7e8cec5a 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -35,7 +35,7 @@
#include <linux/kobject.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "acpiphp.h"
#include "../pci.h"
@@ -107,7 +107,7 @@ static void __exit ibm_acpiphp_exit(void);
static acpi_handle ibm_acpi_handle;
static struct notification ibm_note;
-static struct bin_attribute ibm_apci_table_attr = {
+static struct bin_attribute ibm_apci_table_attr __ro_after_init = {
.attr = {
.name = "apci_table",
.mode = S_IRUGO,
diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c
index 7ec8a8f72c69..95f689f53920 100644
--- a/drivers/pci/hotplug/cpci_hotplug_core.c
+++ b/drivers/pci/hotplug/cpci_hotplug_core.c
@@ -27,6 +27,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h
index 9103a7b9f3b9..48c8a066a6b7 100644
--- a/drivers/pci/hotplug/cpqphp.h
+++ b/drivers/pci/hotplug/cpqphp.h
@@ -32,7 +32,7 @@
#include <asm/io.h> /* for read? and write? functions */
#include <linux/delay.h> /* for delays */
#include <linux/mutex.h>
-#include <linux/sched.h> /* for signal_pending() */
+#include <linux/sched/signal.h> /* for signal_pending() */
#define MY_NAME "cpqphp"
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
index 74f3a0695b43..33d300d12411 100644
--- a/drivers/pci/hotplug/cpqphp_core.c
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -40,7 +40,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "cpqphp.h"
#include "cpqphp_nvram.h"
@@ -867,7 +867,8 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
*/
if ((pdev->revision <= 2) && (vendor_id != PCI_VENDOR_ID_INTEL)) {
err(msg_HPC_not_supported);
- return -ENODEV;
+ rc = -ENODEV;
+ goto err_disable_device;
}
/* TODO: This code can be made to support non-Compaq or Intel
diff --git a/drivers/pci/hotplug/cpqphp_nvram.c b/drivers/pci/hotplug/cpqphp_nvram.c
index c25fc9061059..daae8071a156 100644
--- a/drivers/pci/hotplug/cpqphp_nvram.c
+++ b/drivers/pci/hotplug/cpqphp_nvram.c
@@ -34,7 +34,7 @@
#include <linux/workqueue.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "cpqphp.h"
#include "cpqphp_nvram.h"
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index fea0b8b33589..7b0e97be9063 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -23,6 +23,9 @@
*
* Send feedback to <kristen.c.accardi@intel.com>
*
+ * Authors:
+ * Greg Kroah-Hartman <greg@kroah.com>
+ * Scott Murray <scottm@somanetworks.com>
*/
#include <linux/module.h> /* try_module_get & module_put */
@@ -39,7 +42,7 @@
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "../pci.h"
#include "cpci_hotplug.h"
@@ -50,15 +53,9 @@
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
-
/* local variables */
static bool debug;
-#define DRIVER_VERSION "0.5"
-#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>"
-#define DRIVER_DESC "PCI Hot Plug PCI Core"
-
-
static LIST_HEAD(pci_hotplug_slot_list);
static DEFINE_MUTEX(pci_hp_mutex);
@@ -534,7 +531,6 @@ static int __init pci_hotplug_init(void)
return result;
}
- info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
return result;
}
device_initcall(pci_hotplug_init);
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index 37d70b5ad22f..06109d40c4ac 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -33,7 +33,7 @@
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <linux/delay.h>
-#include <linux/sched.h> /* signal_pending() */
+#include <linux/sched/signal.h> /* signal_pending() */
#include <linux/pcieport_if.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 7d32fa33dcef..35d84845d5af 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -25,6 +25,10 @@
*
* Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
*
+ * Authors:
+ * Dan Zink <dan.zink@compaq.com>
+ * Greg Kroah-Hartman <greg@kroah.com>
+ * Dely Sy <dely.l.sy@intel.com>"
*/
#include <linux/moduleparam.h>
@@ -42,10 +46,6 @@ bool pciehp_poll_mode;
int pciehp_poll_time;
static bool pciehp_force;
-#define DRIVER_VERSION "0.4"
-#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
-#define DRIVER_DESC "PCI Express Hot Plug Controller Driver"
-
/*
* not really modular, but the easiest way to keep compat with existing
* bootargs behaviour is to continue using module_param here.
@@ -333,7 +333,6 @@ static int __init pcied_init(void)
retval = pcie_port_service_register(&hpdriver_portdrv);
dbg("pcie_port_service_register = %d\n", retval);
- info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
if (retval)
dbg("Failure to register service\n");
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index efe69e879455..ec0b4c11ccd9 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -410,7 +410,7 @@ int pciehp_enable_slot(struct slot *p_slot)
if (getstatus) {
ctrl_info(ctrl, "Slot(%s): Already enabled\n",
slot_name(p_slot));
- return -EINVAL;
+ return 0;
}
}
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index b57fc6d6e28a..026830a138ae 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -620,8 +620,18 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS);
}
- /* Check Presence Detect Changed */
- if (events & PCI_EXP_SLTSTA_PDC) {
+ /*
+ * Check Link Status Changed at higher precedence than Presence
+ * Detect Changed. The PDS value may be set to "card present" from
+ * out-of-band detection, which may be in conflict with a Link Down
+ * and cause the wrong event to queue.
+ */
+ if (events & PCI_EXP_SLTSTA_DLLSC) {
+ ctrl_info(ctrl, "Slot(%s): Link %s\n", slot_name(slot),
+ link ? "Up" : "Down");
+ pciehp_queue_interrupt_event(slot, link ? INT_LINK_UP :
+ INT_LINK_DOWN);
+ } else if (events & PCI_EXP_SLTSTA_PDC) {
present = !!(status & PCI_EXP_SLTSTA_PDS);
ctrl_info(ctrl, "Slot(%s): Card %spresent\n", slot_name(slot),
present ? "" : "not ");
@@ -636,13 +646,6 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
pciehp_queue_interrupt_event(slot, INT_POWER_FAULT);
}
- if (events & PCI_EXP_SLTSTA_DLLSC) {
- ctrl_info(ctrl, "Slot(%s): Link %s\n", slot_name(slot),
- link ? "Up" : "Down");
- pciehp_queue_interrupt_event(slot, link ? INT_LINK_UP :
- INT_LINK_DOWN);
- }
-
return IRQ_HANDLED;
}
diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c
index 56efaf72d08e..7c203198b582 100644
--- a/drivers/pci/hotplug/pnv_php.c
+++ b/drivers/pci/hotplug/pnv_php.c
@@ -35,9 +35,11 @@ static void pnv_php_register(struct device_node *dn);
static void pnv_php_unregister_one(struct device_node *dn);
static void pnv_php_unregister(struct device_node *dn);
-static void pnv_php_disable_irq(struct pnv_php_slot *php_slot)
+static void pnv_php_disable_irq(struct pnv_php_slot *php_slot,
+ bool disable_device)
{
struct pci_dev *pdev = php_slot->pdev;
+ int irq = php_slot->irq;
u16 ctrl;
if (php_slot->irq > 0) {
@@ -56,10 +58,14 @@ static void pnv_php_disable_irq(struct pnv_php_slot *php_slot)
php_slot->wq = NULL;
}
- if (pdev->msix_enabled)
- pci_disable_msix(pdev);
- else if (pdev->msi_enabled)
- pci_disable_msi(pdev);
+ if (disable_device || irq > 0) {
+ if (pdev->msix_enabled)
+ pci_disable_msix(pdev);
+ else if (pdev->msi_enabled)
+ pci_disable_msi(pdev);
+
+ pci_disable_device(pdev);
+ }
}
static void pnv_php_free_slot(struct kref *kref)
@@ -68,7 +74,7 @@ static void pnv_php_free_slot(struct kref *kref)
struct pnv_php_slot, kref);
WARN_ON(!list_empty(&php_slot->children));
- pnv_php_disable_irq(php_slot);
+ pnv_php_disable_irq(php_slot, false);
kfree(php_slot->name);
kfree(php_slot);
}
@@ -76,7 +82,7 @@ static void pnv_php_free_slot(struct kref *kref)
static inline void pnv_php_put_slot(struct pnv_php_slot *php_slot)
{
- if (WARN_ON(!php_slot))
+ if (!php_slot)
return;
kref_put(&php_slot->kref, pnv_php_free_slot);
@@ -155,7 +161,7 @@ static void pnv_php_detach_device_nodes(struct device_node *parent)
pnv_php_detach_device_nodes(dn);
of_node_put(dn);
- refcount = atomic_read(&dn->kobj.kref.refcount);
+ refcount = kref_read(&dn->kobj.kref);
if (refcount != 1)
pr_warn("Invalid refcount %d on <%s>\n",
refcount, of_node_full_name(dn));
@@ -430,9 +436,21 @@ static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan)
if (ret)
return ret;
- /* Proceed if there have nothing behind the slot */
- if (presence == OPAL_PCI_SLOT_EMPTY)
+ /*
+ * Proceed if there have nothing behind the slot. However,
+ * we should leave the slot in registered state at the
+ * beginning. Otherwise, the PCI devices inserted afterwards
+ * won't be probed and populated.
+ */
+ if (presence == OPAL_PCI_SLOT_EMPTY) {
+ if (!php_slot->power_state_check) {
+ php_slot->power_state_check = true;
+
+ return 0;
+ }
+
goto scan;
+ }
/*
* If the power supply to the slot is off, we can't detect
@@ -705,10 +723,15 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data)
if (sts & PCI_EXP_SLTSTA_DLLSC) {
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts);
added = !!(lsts & PCI_EXP_LNKSTA_DLLLA);
- } else if (sts & PCI_EXP_SLTSTA_PDC) {
+ } else if (!(php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) &&
+ (sts & PCI_EXP_SLTSTA_PDC)) {
ret = pnv_pci_get_presence_state(php_slot->id, &presence);
- if (!ret)
+ if (ret) {
+ dev_warn(&pdev->dev, "PCI slot [%s] error %d getting presence (0x%04x), to retry the operation.\n",
+ php_slot->name, ret, sts);
return IRQ_HANDLED;
+ }
+
added = !!(presence == OPAL_PCI_SLOT_PRESENT);
} else {
return IRQ_NONE;
@@ -752,6 +775,7 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data)
static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
{
struct pci_dev *pdev = php_slot->pdev;
+ u32 broken_pdc = 0;
u16 sts, ctrl;
int ret;
@@ -759,29 +783,44 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name);
if (!php_slot->wq) {
dev_warn(&pdev->dev, "Cannot alloc workqueue\n");
- pnv_php_disable_irq(php_slot);
+ pnv_php_disable_irq(php_slot, true);
return;
}
+ /* Check PDC (Presence Detection Change) is broken or not */
+ ret = of_property_read_u32(php_slot->dn, "ibm,slot-broken-pdc",
+ &broken_pdc);
+ if (!ret && broken_pdc)
+ php_slot->flags |= PNV_PHP_FLAG_BROKEN_PDC;
+
/* Clear pending interrupts */
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts);
- sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
+ if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC)
+ sts |= PCI_EXP_SLTSTA_DLLSC;
+ else
+ sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts);
/* Request the interrupt */
ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED,
php_slot->name, php_slot);
if (ret) {
- pnv_php_disable_irq(php_slot);
+ pnv_php_disable_irq(php_slot, true);
dev_warn(&pdev->dev, "Error %d enabling IRQ %d\n", ret, irq);
return;
}
/* Enable the interrupts */
pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl);
- ctrl |= (PCI_EXP_SLTCTL_HPIE |
- PCI_EXP_SLTCTL_PDCE |
- PCI_EXP_SLTCTL_DLLSCE);
+ if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) {
+ ctrl &= ~PCI_EXP_SLTCTL_PDCE;
+ ctrl |= (PCI_EXP_SLTCTL_HPIE |
+ PCI_EXP_SLTCTL_DLLSCE);
+ } else {
+ ctrl |= (PCI_EXP_SLTCTL_HPIE |
+ PCI_EXP_SLTCTL_PDCE |
+ PCI_EXP_SLTCTL_DLLSCE);
+ }
pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl);
/* The interrupt is initialized successfully when @irq is valid */
@@ -793,6 +832,14 @@ static void pnv_php_enable_irq(struct pnv_php_slot *php_slot)
struct pci_dev *pdev = php_slot->pdev;
int irq, ret;
+ /*
+ * The MSI/MSIx interrupt might have been occupied by other
+ * drivers. Don't populate the surprise hotplug capability
+ * in that case.
+ */
+ if (pci_dev_msi_enabled(pdev))
+ return;
+
ret = pci_enable_device(pdev);
if (ret) {
dev_warn(&pdev->dev, "Error %d enabling device\n", ret);
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
index dc67f39779ec..3f93a4e79595 100644
--- a/drivers/pci/hotplug/rpadlpar_core.c
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -257,8 +257,13 @@ static int dlpar_add_phb(char *drc_name, struct device_node *dn)
static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
{
- if (vio_find_node(dn))
+ struct vio_dev *vio_dev;
+
+ vio_dev = vio_find_node(dn);
+ if (vio_dev) {
+ put_device(&vio_dev->dev);
return -EINVAL;
+ }
if (!vio_register_device_node(dn)) {
printk(KERN_ERR
@@ -334,6 +339,9 @@ static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn)
return -EINVAL;
vio_unregister_device(vio_dev);
+
+ put_device(&vio_dev->dev);
+
return 0;
}
@@ -455,7 +463,6 @@ static inline int is_dlpar_capable(void)
int __init rpadlpar_io_init(void)
{
- int rc = 0;
if (!is_dlpar_capable()) {
printk(KERN_WARNING "%s: partition not DLPAR capable\n",
@@ -463,8 +470,7 @@ int __init rpadlpar_io_init(void)
return -EPERM;
}
- rc = dlpar_sysfs_init();
- return rc;
+ return dlpar_sysfs_init();
}
void rpadlpar_io_exit(void)
diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c
index 50b8b7d54416..530d0e49f2ed 100644
--- a/drivers/pci/hotplug/s390_pci_hpc.c
+++ b/drivers/pci/hotplug/s390_pci_hpc.c
@@ -5,12 +5,13 @@
*
* Author(s):
* Jan Glauber <jang@linux.vnet.ibm.com>
+ *
+ * License: GPL
*/
#define KMSG_COMPONENT "zpci"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
@@ -21,10 +22,6 @@
#define SLOT_NAME_SIZE 10
static LIST_HEAD(s390_hotplug_slot_list);
-MODULE_AUTHOR("Jan Glauber <jang@linux.vnet.ibm.com");
-MODULE_DESCRIPTION("Hot Plug PCI Controller for System z");
-MODULE_LICENSE("GPL");
-
static int zpci_fn_configured(enum zpci_state state)
{
return state == ZPCI_FN_STATE_CONFIGURED ||
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
index 4da8fc601467..70c7ea6af034 100644
--- a/drivers/pci/hotplug/shpchp.h
+++ b/drivers/pci/hotplug/shpchp.h
@@ -33,7 +33,7 @@
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <linux/delay.h>
-#include <linux/sched.h> /* signal_pending(), struct timer_list */
+#include <linux/sched/signal.h> /* signal_pending(), struct timer_list */
#include <linux/mutex.h>
#include <linux/workqueue.h>
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index e30f05c8517f..2479ae876482 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -124,7 +124,6 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset)
struct pci_sriov *iov = dev->sriov;
struct pci_bus *bus;
- mutex_lock(&iov->dev->sriov->lock);
bus = virtfn_add_bus(dev->bus, pci_iov_virtfn_bus(dev, id));
if (!bus)
goto failed;
@@ -162,7 +161,6 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset)
__pci_reset_function(virtfn);
pci_device_add(virtfn, virtfn->bus);
- mutex_unlock(&iov->dev->sriov->lock);
pci_bus_add_device(virtfn);
sprintf(buf, "virtfn%u", id);
@@ -181,12 +179,10 @@ failed2:
sysfs_remove_link(&dev->dev.kobj, buf);
failed1:
pci_dev_put(dev);
- mutex_lock(&iov->dev->sriov->lock);
pci_stop_and_remove_bus_device(virtfn);
failed0:
virtfn_remove_bus(dev->bus, bus);
failed:
- mutex_unlock(&iov->dev->sriov->lock);
return rc;
}
@@ -195,7 +191,6 @@ void pci_iov_remove_virtfn(struct pci_dev *dev, int id, int reset)
{
char buf[VIRTFN_ID_LEN];
struct pci_dev *virtfn;
- struct pci_sriov *iov = dev->sriov;
virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
pci_iov_virtfn_bus(dev, id),
@@ -218,10 +213,8 @@ void pci_iov_remove_virtfn(struct pci_dev *dev, int id, int reset)
if (virtfn->dev.kobj.sd)
sysfs_remove_link(&virtfn->dev.kobj, "physfn");
- mutex_lock(&iov->dev->sriov->lock);
pci_stop_and_remove_bus_device(virtfn);
virtfn_remove_bus(dev->bus, virtfn->bus);
- mutex_unlock(&iov->dev->sriov->lock);
/* balance pci_get_domain_bus_and_slot() */
pci_dev_put(virtfn);
@@ -306,13 +299,6 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
return rc;
}
- pci_iov_set_numvfs(dev, 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);
- msleep(100);
- pci_cfg_access_unlock(dev);
-
iov->initial_VFs = initial;
if (nr_virtfn < initial)
initial = nr_virtfn;
@@ -323,6 +309,13 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn)
goto err_pcibios;
}
+ pci_iov_set_numvfs(dev, 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);
+ msleep(100);
+ pci_cfg_access_unlock(dev);
+
for (i = 0; i < initial; i++) {
rc = pci_iov_add_virtfn(dev, i, 0);
if (rc)
@@ -554,21 +547,61 @@ void pci_iov_release(struct pci_dev *dev)
}
/**
- * pci_iov_resource_bar - get position of the SR-IOV BAR
+ * pci_iov_update_resource - update a VF BAR
* @dev: the PCI device
* @resno: the resource number
*
- * Returns position of the BAR encapsulated in the SR-IOV capability.
+ * Update a VF BAR in the SR-IOV capability of a PF.
*/
-int pci_iov_resource_bar(struct pci_dev *dev, int resno)
+void pci_iov_update_resource(struct pci_dev *dev, int resno)
{
- if (resno < PCI_IOV_RESOURCES || resno > PCI_IOV_RESOURCE_END)
- return 0;
+ struct pci_sriov *iov = dev->is_physfn ? dev->sriov : NULL;
+ struct resource *res = dev->resource + resno;
+ int vf_bar = resno - PCI_IOV_RESOURCES;
+ struct pci_bus_region region;
+ u16 cmd;
+ u32 new;
+ int reg;
- BUG_ON(!dev->is_physfn);
+ /*
+ * The generic pci_restore_bars() path calls this for all devices,
+ * including VFs and non-SR-IOV devices. If this is not a PF, we
+ * have nothing to do.
+ */
+ if (!iov)
+ return;
+
+ pci_read_config_word(dev, iov->pos + PCI_SRIOV_CTRL, &cmd);
+ if ((cmd & PCI_SRIOV_CTRL_VFE) && (cmd & PCI_SRIOV_CTRL_MSE)) {
+ dev_WARN(&dev->dev, "can't update enabled VF BAR%d %pR\n",
+ vf_bar, res);
+ return;
+ }
+
+ /*
+ * Ignore unimplemented BARs, unused resource slots for 64-bit
+ * BARs, and non-movable resources, e.g., those described via
+ * Enhanced Allocation.
+ */
+ if (!res->flags)
+ return;
+
+ if (res->flags & IORESOURCE_UNSET)
+ return;
- return dev->sriov->pos + PCI_SRIOV_BAR +
- 4 * (resno - PCI_IOV_RESOURCES);
+ if (res->flags & IORESOURCE_PCI_FIXED)
+ return;
+
+ pcibios_resource_to_bus(dev->bus, &region, res);
+ new = region.start;
+ new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK;
+
+ reg = iov->pos + PCI_SRIOV_BAR + 4 * vf_bar;
+ pci_write_config_dword(dev, reg, new);
+ if (res->flags & IORESOURCE_MEM_64) {
+ new = region.start >> 16 >> 16;
+ pci_write_config_dword(dev, reg + 4, new);
+ }
}
resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev,
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index ad70507cfb56..d571bc330686 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -32,32 +32,13 @@ int pci_msi_ignore_mask;
#define msix_table_size(flags) ((flags & PCI_MSIX_FLAGS_QSIZE) + 1)
#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
-static struct irq_domain *pci_msi_default_domain;
-static DEFINE_MUTEX(pci_msi_domain_lock);
-
-struct irq_domain * __weak arch_get_pci_msi_domain(struct pci_dev *dev)
-{
- return pci_msi_default_domain;
-}
-
-static struct irq_domain *pci_msi_get_domain(struct pci_dev *dev)
-{
- struct irq_domain *domain;
-
- domain = dev_get_msi_domain(&dev->dev);
- if (domain)
- return domain;
-
- return arch_get_pci_msi_domain(dev);
-}
-
static int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{
struct irq_domain *domain;
- domain = pci_msi_get_domain(dev);
+ domain = dev_get_msi_domain(&dev->dev);
if (domain && irq_domain_is_hierarchy(domain))
- return pci_msi_domain_alloc_irqs(domain, dev, nvec, type);
+ return msi_domain_alloc_irqs(domain, &dev->dev, nvec);
return arch_setup_msi_irqs(dev, nvec, type);
}
@@ -66,9 +47,9 @@ static void pci_msi_teardown_msi_irqs(struct pci_dev *dev)
{
struct irq_domain *domain;
- domain = pci_msi_get_domain(dev);
+ domain = dev_get_msi_domain(&dev->dev);
if (domain && irq_domain_is_hierarchy(domain))
- pci_msi_domain_free_irqs(domain, dev);
+ msi_domain_free_irqs(domain, &dev->dev);
else
arch_teardown_msi_irqs(dev);
}
@@ -379,7 +360,7 @@ static void free_msi_irqs(struct pci_dev *dev)
}
list_del(&entry->list);
- kfree(entry);
+ free_msi_entry(entry);
}
if (dev->msi_irq_groups) {
@@ -551,14 +532,14 @@ error_attrs:
}
static struct msi_desc *
-msi_setup_entry(struct pci_dev *dev, int nvec, bool affinity)
+msi_setup_entry(struct pci_dev *dev, int nvec, const struct irq_affinity *affd)
{
struct cpumask *masks = NULL;
struct msi_desc *entry;
u16 control;
- if (affinity) {
- masks = irq_create_affinity_masks(dev->irq_affinity, nvec);
+ if (affd) {
+ masks = irq_create_affinity_masks(nvec, affd);
if (!masks)
pr_err("Unable to allocate affinity masks, ignoring\n");
}
@@ -610,7 +591,7 @@ static int msi_verify_entries(struct pci_dev *dev)
* msi_capability_init - configure device's MSI capability structure
* @dev: pointer to the pci_dev data structure of MSI device function
* @nvec: number of interrupts to allocate
- * @affinity: flag to indicate cpu irq affinity mask should be set
+ * @affd: description of automatic irq affinity assignments (may be %NULL)
*
* Setup the MSI capability structure of the device with the requested
* number of interrupts. A return value of zero indicates the successful
@@ -618,7 +599,8 @@ static int msi_verify_entries(struct pci_dev *dev)
* an error, and a positive return value indicates the number of interrupts
* which could have been allocated.
*/
-static int msi_capability_init(struct pci_dev *dev, int nvec, bool affinity)
+static int msi_capability_init(struct pci_dev *dev, int nvec,
+ const struct irq_affinity *affd)
{
struct msi_desc *entry;
int ret;
@@ -626,7 +608,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec, bool affinity)
pci_msi_set_enable(dev, 0); /* Disable MSI during set up */
- entry = msi_setup_entry(dev, nvec, affinity);
+ entry = msi_setup_entry(dev, nvec, affd);
if (!entry)
return -ENOMEM;
@@ -690,14 +672,14 @@ static void __iomem *msix_map_region(struct pci_dev *dev, unsigned nr_entries)
static int msix_setup_entries(struct pci_dev *dev, void __iomem *base,
struct msix_entry *entries, int nvec,
- bool affinity)
+ const struct irq_affinity *affd)
{
struct cpumask *curmsk, *masks = NULL;
struct msi_desc *entry;
int ret, i;
- if (affinity) {
- masks = irq_create_affinity_masks(dev->irq_affinity, nvec);
+ if (affd) {
+ masks = irq_create_affinity_masks(nvec, affd);
if (!masks)
pr_err("Unable to allocate affinity masks, ignoring\n");
}
@@ -730,7 +712,7 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base,
ret = 0;
out:
kfree(masks);
- return 0;
+ return ret;
}
static void msix_program_entries(struct pci_dev *dev,
@@ -753,14 +735,14 @@ static void msix_program_entries(struct pci_dev *dev,
* @dev: pointer to the pci_dev data structure of MSI-X device function
* @entries: pointer to an array of struct msix_entry entries
* @nvec: number of @entries
- * @affinity: flag to indicate cpu irq affinity mask should be set
+ * @affd: Optional pointer to enable automatic affinity assignement
*
* Setup the MSI-X capability structure of device function with a
* single MSI-X irq. A return of zero indicates the successful setup of
* requested MSI-X entries with allocated irqs or non-zero for otherwise.
**/
static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries,
- int nvec, bool affinity)
+ int nvec, const struct irq_affinity *affd)
{
int ret;
u16 control;
@@ -775,7 +757,7 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries,
if (!base)
return -ENOMEM;
- ret = msix_setup_entries(dev, base, entries, nvec, affinity);
+ ret = msix_setup_entries(dev, base, entries, nvec, affd);
if (ret)
return ret;
@@ -956,7 +938,7 @@ int pci_msix_vec_count(struct pci_dev *dev)
EXPORT_SYMBOL(pci_msix_vec_count);
static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries,
- int nvec, bool affinity)
+ int nvec, const struct irq_affinity *affd)
{
int nr_entries;
int i, j;
@@ -988,7 +970,7 @@ static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries,
dev_info(&dev->dev, "can't enable MSI-X (MSI IRQ already assigned)\n");
return -EINVAL;
}
- return msix_capability_init(dev, entries, nvec, affinity);
+ return msix_capability_init(dev, entries, nvec, affd);
}
/**
@@ -1008,7 +990,7 @@ static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries,
**/
int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec)
{
- return __pci_enable_msix(dev, entries, nvec, false);
+ return __pci_enable_msix(dev, entries, nvec, NULL);
}
EXPORT_SYMBOL(pci_enable_msix);
@@ -1059,9 +1041,8 @@ int pci_msi_enabled(void)
EXPORT_SYMBOL(pci_msi_enabled);
static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
- unsigned int flags)
+ const struct irq_affinity *affd)
{
- bool affinity = flags & PCI_IRQ_AFFINITY;
int nvec;
int rc;
@@ -1084,20 +1065,19 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
if (nvec < 0)
return nvec;
if (nvec < minvec)
- return -EINVAL;
+ return -ENOSPC;
if (nvec > maxvec)
nvec = maxvec;
for (;;) {
- if (affinity) {
- nvec = irq_calc_affinity_vectors(dev->irq_affinity,
- nvec);
+ if (affd) {
+ nvec = irq_calc_affinity_vectors(nvec, affd);
if (nvec < minvec)
return -ENOSPC;
}
- rc = msi_capability_init(dev, nvec, affinity);
+ rc = msi_capability_init(dev, nvec, affd);
if (rc == 0)
return nvec;
@@ -1110,43 +1090,33 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
}
}
-/**
- * pci_enable_msi_range - configure device's MSI capability structure
- * @dev: device to configure
- * @minvec: minimal number of interrupts to configure
- * @maxvec: maximum number of interrupts to configure
- *
- * This function tries to allocate a maximum possible number of interrupts in a
- * range between @minvec and @maxvec. It returns a negative errno if an error
- * occurs. If it succeeds, it returns the actual number of interrupts allocated
- * and updates the @dev's irq member to the lowest new interrupt number;
- * the other interrupt numbers allocated to this device are consecutive.
- **/
-int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec)
+/* deprecated, don't use */
+int pci_enable_msi(struct pci_dev *dev)
{
- return __pci_enable_msi_range(dev, minvec, maxvec, 0);
+ int rc = __pci_enable_msi_range(dev, 1, 1, NULL);
+ if (rc < 0)
+ return rc;
+ return 0;
}
-EXPORT_SYMBOL(pci_enable_msi_range);
+EXPORT_SYMBOL(pci_enable_msi);
static int __pci_enable_msix_range(struct pci_dev *dev,
- struct msix_entry *entries, int minvec, int maxvec,
- unsigned int flags)
+ struct msix_entry *entries, int minvec,
+ int maxvec, const struct irq_affinity *affd)
{
- bool affinity = flags & PCI_IRQ_AFFINITY;
int rc, nvec = maxvec;
if (maxvec < minvec)
return -ERANGE;
for (;;) {
- if (affinity) {
- nvec = irq_calc_affinity_vectors(dev->irq_affinity,
- nvec);
+ if (affd) {
+ nvec = irq_calc_affinity_vectors(nvec, affd);
if (nvec < minvec)
return -ENOSPC;
}
- rc = __pci_enable_msix(dev, entries, nvec, affinity);
+ rc = __pci_enable_msix(dev, entries, nvec, affd);
if (rc == 0)
return nvec;
@@ -1177,16 +1147,17 @@ static int __pci_enable_msix_range(struct pci_dev *dev,
int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
int minvec, int maxvec)
{
- return __pci_enable_msix_range(dev, entries, minvec, maxvec, 0);
+ return __pci_enable_msix_range(dev, entries, minvec, maxvec, NULL);
}
EXPORT_SYMBOL(pci_enable_msix_range);
/**
- * pci_alloc_irq_vectors - allocate multiple IRQs for a device
+ * pci_alloc_irq_vectors_affinity - allocate multiple IRQs for a device
* @dev: PCI device to operate on
* @min_vecs: minimum number of vectors required (must be >= 1)
* @max_vecs: maximum (desired) number of vectors
* @flags: flags or quirks for the allocation
+ * @affd: optional description of the affinity requirements
*
* Allocate up to @max_vecs interrupt vectors for @dev, using MSI-X or MSI
* vectors if available, and fall back to a single legacy vector
@@ -1198,33 +1169,55 @@ EXPORT_SYMBOL(pci_enable_msix_range);
* To get the Linux IRQ number used for a vector that can be passed to
* request_irq() use the pci_irq_vector() helper.
*/
-int pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs,
- unsigned int max_vecs, unsigned int flags)
+int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs,
+ unsigned int max_vecs, unsigned int flags,
+ const struct irq_affinity *affd)
{
+ static const struct irq_affinity msi_default_affd;
int vecs = -ENOSPC;
+ if (flags & PCI_IRQ_AFFINITY) {
+ if (!affd)
+ affd = &msi_default_affd;
+
+ if (affd->pre_vectors + affd->post_vectors > min_vecs)
+ return -EINVAL;
+
+ /*
+ * If there aren't any vectors left after applying the pre/post
+ * vectors don't bother with assigning affinity.
+ */
+ if (affd->pre_vectors + affd->post_vectors == min_vecs)
+ affd = NULL;
+ } else {
+ if (WARN_ON(affd))
+ affd = NULL;
+ }
+
if (flags & PCI_IRQ_MSIX) {
vecs = __pci_enable_msix_range(dev, NULL, min_vecs, max_vecs,
- flags);
+ affd);
if (vecs > 0)
return vecs;
}
if (flags & PCI_IRQ_MSI) {
- vecs = __pci_enable_msi_range(dev, min_vecs, max_vecs, flags);
+ vecs = __pci_enable_msi_range(dev, min_vecs, max_vecs, affd);
if (vecs > 0)
return vecs;
}
/* use legacy irq if allowed */
- if ((flags & PCI_IRQ_LEGACY) && min_vecs == 1) {
- pci_intx(dev, 1);
- return 1;
+ if (flags & PCI_IRQ_LEGACY) {
+ if (min_vecs == 1 && dev->irq) {
+ pci_intx(dev, 1);
+ return 1;
+ }
}
return vecs;
}
-EXPORT_SYMBOL(pci_alloc_irq_vectors);
+EXPORT_SYMBOL(pci_alloc_irq_vectors_affinity);
/**
* pci_free_irq_vectors - free previously allocated IRQs for a device
@@ -1294,7 +1287,8 @@ const struct cpumask *pci_irq_get_affinity(struct pci_dev *dev, int nr)
} else if (dev->msi_enabled) {
struct msi_desc *entry = first_pci_msi_entry(dev);
- if (WARN_ON_ONCE(!entry || nr >= entry->nvec_used))
+ if (WARN_ON_ONCE(!entry || !entry->affinity ||
+ nr >= entry->nvec_used))
return NULL;
return &entry->affinity[nr];
@@ -1304,6 +1298,22 @@ const struct cpumask *pci_irq_get_affinity(struct pci_dev *dev, int nr)
}
EXPORT_SYMBOL(pci_irq_get_affinity);
+/**
+ * pci_irq_get_node - return the numa node of a particular msi vector
+ * @pdev: PCI device to operate on
+ * @vec: device-relative interrupt vector index (0-based).
+ */
+int pci_irq_get_node(struct pci_dev *pdev, int vec)
+{
+ const struct cpumask *mask;
+
+ mask = pci_irq_get_affinity(pdev, vec);
+ if (mask)
+ return local_memory_node(cpu_to_node(cpumask_first(mask)));
+ return dev_to_node(&pdev->dev);
+}
+EXPORT_SYMBOL(pci_irq_get_node);
+
struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
{
return to_pci_dev(desc->dev);
@@ -1372,7 +1382,7 @@ int pci_msi_domain_check_cap(struct irq_domain *domain,
{
struct msi_desc *desc = first_pci_msi_entry(to_pci_dev(dev));
- /* Special handling to support pci_enable_msi_range() */
+ /* Special handling to support __pci_enable_msi_range() */
if (pci_msi_desc_is_multi_msi(desc) &&
!(info->flags & MSI_FLAG_MULTI_PCI_MSI))
return 1;
@@ -1385,7 +1395,7 @@ int pci_msi_domain_check_cap(struct irq_domain *domain,
static int pci_msi_domain_handle_error(struct irq_domain *domain,
struct msi_desc *desc, int error)
{
- /* Special handling to support pci_enable_msi_range() */
+ /* Special handling to support __pci_enable_msi_range() */
if (pci_msi_desc_is_multi_msi(desc) && error == -ENOSPC)
return 1;
@@ -1472,59 +1482,6 @@ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
}
EXPORT_SYMBOL_GPL(pci_msi_create_irq_domain);
-/**
- * pci_msi_domain_alloc_irqs - Allocate interrupts for @dev in @domain
- * @domain: The interrupt domain to allocate from
- * @dev: The device for which to allocate
- * @nvec: The number of interrupts to allocate
- * @type: Unused to allow simpler migration from the arch_XXX interfaces
- *
- * Returns:
- * A virtual interrupt number or an error code in case of failure
- */
-int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev,
- int nvec, int type)
-{
- return msi_domain_alloc_irqs(domain, &dev->dev, nvec);
-}
-
-/**
- * pci_msi_domain_free_irqs - Free interrupts for @dev in @domain
- * @domain: The interrupt domain
- * @dev: The device for which to free interrupts
- */
-void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev)
-{
- msi_domain_free_irqs(domain, &dev->dev);
-}
-
-/**
- * pci_msi_create_default_irq_domain - Create a default MSI interrupt domain
- * @fwnode: Optional fwnode of the interrupt controller
- * @info: MSI domain info
- * @parent: Parent irq domain
- *
- * Returns: A domain pointer or NULL in case of failure. If successful
- * the default PCI/MSI irqdomain pointer is updated.
- */
-struct irq_domain *pci_msi_create_default_irq_domain(struct fwnode_handle *fwnode,
- struct msi_domain_info *info, struct irq_domain *parent)
-{
- struct irq_domain *domain;
-
- mutex_lock(&pci_msi_domain_lock);
- if (pci_msi_default_domain) {
- pr_err("PCI: default irq domain for PCI MSI has already been created.\n");
- domain = NULL;
- } else {
- domain = pci_msi_create_irq_domain(fwnode, info, parent);
- pci_msi_default_domain = domain;
- }
- mutex_unlock(&pci_msi_domain_lock);
-
- return domain;
-}
-
static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data)
{
u32 *pa = data;
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index d966d47c9e80..001860361434 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -29,6 +29,82 @@ const u8 pci_acpi_dsm_uuid[] = {
0x91, 0x17, 0xea, 0x4d, 0x19, 0xc3, 0x43, 0x4d
};
+#if defined(CONFIG_PCI_QUIRKS) && defined(CONFIG_ARM64)
+static int acpi_get_rc_addr(struct acpi_device *adev, struct resource *res)
+{
+ struct device *dev = &adev->dev;
+ struct resource_entry *entry;
+ struct list_head list;
+ unsigned long flags;
+ int ret;
+
+ INIT_LIST_HEAD(&list);
+ flags = IORESOURCE_MEM;
+ ret = acpi_dev_get_resources(adev, &list,
+ acpi_dev_filter_resource_type_cb,
+ (void *) flags);
+ if (ret < 0) {
+ dev_err(dev, "failed to parse _CRS method, error code %d\n",
+ ret);
+ return ret;
+ }
+
+ if (ret == 0) {
+ dev_err(dev, "no IO and memory resources present in _CRS\n");
+ return -EINVAL;
+ }
+
+ entry = list_first_entry(&list, struct resource_entry, node);
+ *res = *entry->res;
+ acpi_dev_free_resource_list(&list);
+ return 0;
+}
+
+static acpi_status acpi_match_rc(acpi_handle handle, u32 lvl, void *context,
+ void **retval)
+{
+ u16 *segment = context;
+ unsigned long long uid;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(handle, "_UID", NULL, &uid);
+ if (ACPI_FAILURE(status) || uid != *segment)
+ return AE_CTRL_DEPTH;
+
+ *(acpi_handle *)retval = handle;
+ return AE_CTRL_TERMINATE;
+}
+
+int acpi_get_rc_resources(struct device *dev, const char *hid, u16 segment,
+ struct resource *res)
+{
+ struct acpi_device *adev;
+ acpi_status status;
+ acpi_handle handle;
+ int ret;
+
+ status = acpi_get_devices(hid, acpi_match_rc, &segment, &handle);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev, "can't find _HID %s device to locate resources\n",
+ hid);
+ return -ENODEV;
+ }
+
+ ret = acpi_bus_get_device(handle, &adev);
+ if (ret)
+ return ret;
+
+ ret = acpi_get_rc_addr(adev, res);
+ if (ret) {
+ dev_err(dev, "can't get resource from %s\n",
+ dev_name(&adev->dev));
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
{
acpi_status status = AE_NOT_EXIST;
@@ -294,6 +370,30 @@ int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp)
EXPORT_SYMBOL_GPL(pci_get_hp_params);
/**
+ * pciehp_is_native - Check whether a hotplug port is handled by the OS
+ * @pdev: Hotplug port to check
+ *
+ * Walk up from @pdev to the host bridge, obtain its cached _OSC Control Field
+ * and return the value of the "PCI Express Native Hot Plug control" bit.
+ * On failure to obtain the _OSC Control Field return %false.
+ */
+bool pciehp_is_native(struct pci_dev *pdev)
+{
+ struct acpi_pci_root *root;
+ acpi_handle handle;
+
+ handle = acpi_find_root_bridge_handle(pdev);
+ if (!handle)
+ return false;
+
+ root = acpi_pci_find_root(handle);
+ if (!root)
+ return false;
+
+ return root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL;
+}
+
+/**
* pci_acpi_wake_bus - Root bus wakeup notification fork function.
* @work: Work item to handle.
*/
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 1ccce1cd6aca..afa72717a979 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -381,8 +381,6 @@ static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
id = pci_match_device(drv, pci_dev);
if (id)
error = pci_call_probe(drv, pci_dev, id);
- if (error >= 0)
- error = 0;
}
return error;
}
@@ -1432,6 +1430,11 @@ static int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
+static int pci_bus_num_vf(struct device *dev)
+{
+ return pci_num_vf(to_pci_dev(dev));
+}
+
struct bus_type pci_bus_type = {
.name = "pci",
.match = pci_bus_match,
@@ -1443,6 +1446,7 @@ struct bus_type pci_bus_type = {
.bus_groups = pci_bus_groups,
.drv_groups = pci_drv_groups,
.pm = PCI_PM_OPS_PTR,
+ .num_vf = pci_bus_num_vf,
};
EXPORT_SYMBOL(pci_bus_type);
diff --git a/drivers/pci/pci-mid.c b/drivers/pci/pci-mid.c
index c7f3408e3148..1c4af7227bca 100644
--- a/drivers/pci/pci-mid.c
+++ b/drivers/pci/pci-mid.c
@@ -54,7 +54,7 @@ static bool mid_pci_need_resume(struct pci_dev *dev)
return false;
}
-static struct pci_platform_pm_ops mid_pci_platform_pm = {
+static const struct pci_platform_pm_ops mid_pci_platform_pm = {
.is_manageable = mid_pci_power_manageable,
.set_state = mid_pci_set_power_state,
.get_state = mid_pci_get_power_state,
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index bcd10c795284..25d010d449a3 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -50,6 +50,7 @@ pci_config_attr(vendor, "0x%04x\n");
pci_config_attr(device, "0x%04x\n");
pci_config_attr(subsystem_vendor, "0x%04x\n");
pci_config_attr(subsystem_device, "0x%04x\n");
+pci_config_attr(revision, "0x%02x\n");
pci_config_attr(class, "0x%06x\n");
pci_config_attr(irq, "%u\n");
@@ -471,6 +472,7 @@ static ssize_t sriov_numvfs_store(struct device *dev,
const char *buf, size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
+ struct pci_sriov *iov = pdev->sriov;
int ret;
u16 num_vfs;
@@ -481,38 +483,46 @@ static ssize_t sriov_numvfs_store(struct device *dev,
if (num_vfs > pci_sriov_get_totalvfs(pdev))
return -ERANGE;
+ mutex_lock(&iov->dev->sriov->lock);
+
if (num_vfs == pdev->sriov->num_VFs)
- return count; /* no change */
+ goto exit;
/* is PF driver loaded w/callback */
if (!pdev->driver || !pdev->driver->sriov_configure) {
dev_info(&pdev->dev, "Driver doesn't support SRIOV configuration via sysfs\n");
- return -ENOSYS;
+ ret = -ENOENT;
+ goto exit;
}
if (num_vfs == 0) {
/* disable VFs */
ret = pdev->driver->sriov_configure(pdev, 0);
- if (ret < 0)
- return ret;
- return count;
+ goto exit;
}
/* enable VFs */
if (pdev->sriov->num_VFs) {
dev_warn(&pdev->dev, "%d VFs already enabled. Disable before enabling %d VFs\n",
pdev->sriov->num_VFs, num_vfs);
- return -EBUSY;
+ ret = -EBUSY;
+ goto exit;
}
ret = pdev->driver->sriov_configure(pdev, num_vfs);
if (ret < 0)
- return ret;
+ goto exit;
if (ret != num_vfs)
dev_warn(&pdev->dev, "%d VFs requested; only %d enabled\n",
num_vfs, ret);
+exit:
+ mutex_unlock(&iov->dev->sriov->lock);
+
+ if (ret < 0)
+ return ret;
+
return count;
}
@@ -568,6 +578,7 @@ static struct attribute *pci_dev_attrs[] = {
&dev_attr_device.attr,
&dev_attr_subsystem_vendor.attr,
&dev_attr_subsystem_device.attr,
+ &dev_attr_revision.attr,
&dev_attr_class.attr,
&dev_attr_irq.attr,
&dev_attr_local_cpus.attr,
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index ba34907538f6..7904d02ffdb9 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -564,10 +564,6 @@ static void pci_restore_bars(struct pci_dev *dev)
{
int i;
- /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */
- if (dev->is_virtfn)
- return;
-
for (i = 0; i < PCI_BRIDGE_RESOURCES; i++)
pci_update_resource(dev, i);
}
@@ -2106,6 +2102,10 @@ bool pci_dev_run_wake(struct pci_dev *dev)
if (!dev->pme_support)
return false;
+ /* PME-capable in principle, but not from the intended sleep state */
+ if (!pci_pme_capable(dev, pci_target_state(dev)))
+ return false;
+
while (bus->parent) {
struct pci_dev *bridge = bus->self;
@@ -2226,7 +2226,7 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
* This function checks if it is possible to move the bridge to D3.
* Currently we only allow D3 for recent enough PCIe ports.
*/
-static bool pci_bridge_d3_possible(struct pci_dev *bridge)
+bool pci_bridge_d3_possible(struct pci_dev *bridge)
{
unsigned int year;
@@ -2239,6 +2239,17 @@ static bool pci_bridge_d3_possible(struct pci_dev *bridge)
case PCI_EXP_TYPE_DOWNSTREAM:
if (pci_bridge_d3_disable)
return false;
+
+ /*
+ * Hotplug interrupts cannot be delivered if the link is down,
+ * so parents of a hotplug port must stay awake. In addition,
+ * hotplug ports handled by firmware in System Management Mode
+ * may not be put into D3 by the OS (Thunderbolt on non-Macs).
+ * For simplicity, disallow in general for now.
+ */
+ if (bridge->is_hotplug_bridge)
+ return false;
+
if (pci_bridge_d3_force)
return true;
@@ -2259,32 +2270,33 @@ static bool pci_bridge_d3_possible(struct pci_dev *bridge)
static int pci_dev_check_d3cold(struct pci_dev *dev, void *data)
{
bool *d3cold_ok = data;
- bool no_d3cold;
- /*
- * The device needs to be allowed to go D3cold and if it is wake
- * capable to do so from D3cold.
- */
- no_d3cold = dev->no_d3cold || !dev->d3cold_allowed ||
- (device_may_wakeup(&dev->dev) && !pci_pme_capable(dev, PCI_D3cold)) ||
- !pci_power_manageable(dev);
+ if (/* The device needs to be allowed to go D3cold ... */
+ dev->no_d3cold || !dev->d3cold_allowed ||
+
+ /* ... and if it is wakeup capable to do so from D3cold. */
+ (device_may_wakeup(&dev->dev) &&
+ !pci_pme_capable(dev, PCI_D3cold)) ||
+
+ /* If it is a bridge it must be allowed to go to D3. */
+ !pci_power_manageable(dev))
- *d3cold_ok = !no_d3cold;
+ *d3cold_ok = false;
- return no_d3cold;
+ return !*d3cold_ok;
}
/*
* pci_bridge_d3_update - Update bridge D3 capabilities
* @dev: PCI device which is changed
- * @remove: Is the device being removed
*
* Update upstream bridge PM capabilities accordingly depending on if the
* device PM configuration was changed or the device is being removed. The
* change is also propagated upstream.
*/
-static void pci_bridge_d3_update(struct pci_dev *dev, bool remove)
+void pci_bridge_d3_update(struct pci_dev *dev)
{
+ bool remove = !device_is_registered(&dev->dev);
struct pci_dev *bridge;
bool d3cold_ok = true;
@@ -2292,55 +2304,39 @@ static void pci_bridge_d3_update(struct pci_dev *dev, bool remove)
if (!bridge || !pci_bridge_d3_possible(bridge))
return;
- pci_dev_get(bridge);
/*
- * If the device is removed we do not care about its D3cold
- * capabilities.
+ * If D3 is currently allowed for the bridge, removing one of its
+ * children won't change that.
+ */
+ if (remove && bridge->bridge_d3)
+ return;
+
+ /*
+ * If D3 is currently allowed for the bridge and a child is added or
+ * changed, disallowance of D3 can only be caused by that child, so
+ * we only need to check that single device, not any of its siblings.
+ *
+ * If D3 is currently not allowed for the bridge, checking the device
+ * first may allow us to skip checking its siblings.
*/
if (!remove)
pci_dev_check_d3cold(dev, &d3cold_ok);
- if (d3cold_ok) {
- /*
- * We need to go through all children to find out if all of
- * them can still go to D3cold.
- */
+ /*
+ * If D3 is currently not allowed for the bridge, this may be caused
+ * either by the device being changed/removed or any of its siblings,
+ * so we need to go through all children to find out if one of them
+ * continues to block D3.
+ */
+ if (d3cold_ok && !bridge->bridge_d3)
pci_walk_bus(bridge->subordinate, pci_dev_check_d3cold,
&d3cold_ok);
- }
if (bridge->bridge_d3 != d3cold_ok) {
bridge->bridge_d3 = d3cold_ok;
/* Propagate change to upstream bridges */
- pci_bridge_d3_update(bridge, false);
+ pci_bridge_d3_update(bridge);
}
-
- pci_dev_put(bridge);
-}
-
-/**
- * pci_bridge_d3_device_changed - Update bridge D3 capabilities on change
- * @dev: PCI device that was changed
- *
- * If a device is added or its PM configuration, such as is it allowed to
- * enter D3cold, is changed this function updates upstream bridge PM
- * capabilities accordingly.
- */
-void pci_bridge_d3_device_changed(struct pci_dev *dev)
-{
- pci_bridge_d3_update(dev, false);
-}
-
-/**
- * pci_bridge_d3_device_removed - Update bridge D3 capabilities on remove
- * @dev: PCI device being removed
- *
- * Function updates upstream bridge PM capabilities based on other devices
- * still left on the bus.
- */
-void pci_bridge_d3_device_removed(struct pci_dev *dev)
-{
- pci_bridge_d3_update(dev, true);
}
/**
@@ -2355,7 +2351,7 @@ void pci_d3cold_enable(struct pci_dev *dev)
{
if (dev->no_d3cold) {
dev->no_d3cold = false;
- pci_bridge_d3_device_changed(dev);
+ pci_bridge_d3_update(dev);
}
}
EXPORT_SYMBOL_GPL(pci_d3cold_enable);
@@ -2372,7 +2368,7 @@ void pci_d3cold_disable(struct pci_dev *dev)
{
if (!dev->no_d3cold) {
dev->no_d3cold = true;
- pci_bridge_d3_device_changed(dev);
+ pci_bridge_d3_update(dev);
}
}
EXPORT_SYMBOL_GPL(pci_d3cold_disable);
@@ -4831,36 +4827,6 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags)
}
EXPORT_SYMBOL(pci_select_bars);
-/**
- * pci_resource_bar - get position of the BAR associated with a resource
- * @dev: the PCI device
- * @resno: the resource number
- * @type: the BAR type to be filled in
- *
- * Returns BAR position in config space, or 0 if the BAR is invalid.
- */
-int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type)
-{
- int reg;
-
- if (resno < PCI_ROM_RESOURCE) {
- *type = pci_bar_unknown;
- return PCI_BASE_ADDRESS_0 + 4 * resno;
- } else if (resno == PCI_ROM_RESOURCE) {
- *type = pci_bar_mem32;
- return dev->rom_base_reg;
- } else if (resno < PCI_BRIDGE_RESOURCES) {
- /* device specific resource */
- *type = pci_bar_unknown;
- reg = pci_iov_resource_bar(dev, resno);
- if (reg)
- return reg;
- }
-
- dev_err(&dev->dev, "BAR %d: invalid resource\n", resno);
- return 0;
-}
-
/* Some architectures require additional programming to enable VGA */
static arch_set_vga_state_t arch_set_vga_state;
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 451856210e18..8dd38e69d6f2 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1,9 +1,6 @@
#ifndef DRIVERS_PCI_H
#define DRIVERS_PCI_H
-#define PCI_CFG_SPACE_SIZE 256
-#define PCI_CFG_SPACE_EXP_SIZE 4096
-
#define PCI_FIND_CAP_TTL 48
extern const unsigned char pcie_link_speed[];
@@ -85,8 +82,8 @@ void pci_pm_init(struct pci_dev *dev);
void pci_ea_init(struct pci_dev *dev);
void pci_allocate_cap_save_buffers(struct pci_dev *dev);
void pci_free_cap_save_buffers(struct pci_dev *dev);
-void pci_bridge_d3_device_changed(struct pci_dev *dev);
-void pci_bridge_d3_device_removed(struct pci_dev *dev);
+bool pci_bridge_d3_possible(struct pci_dev *dev);
+void pci_bridge_d3_update(struct pci_dev *dev);
static inline void pci_wakeup_event(struct pci_dev *dev)
{
@@ -245,7 +242,6 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
int pci_setup_device(struct pci_dev *dev);
int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
struct resource *res, unsigned int reg);
-int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type);
void pci_configure_ari(struct pci_dev *dev);
void __pci_bus_size_bridges(struct pci_bus *bus,
struct list_head *realloc_head);
@@ -274,7 +270,7 @@ struct pci_sriov {
u16 driver_max_VFs; /* max num VFs driver supports */
struct pci_dev *dev; /* lowest numbered PF */
struct pci_dev *self; /* this PF */
- struct mutex lock; /* lock for VF bus */
+ struct mutex lock; /* lock for setting sriov_numvfs in sysfs */
resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */
};
@@ -289,7 +285,7 @@ static inline void pci_restore_ats_state(struct pci_dev *dev)
#ifdef CONFIG_PCI_IOV
int pci_iov_init(struct pci_dev *dev);
void pci_iov_release(struct pci_dev *dev);
-int pci_iov_resource_bar(struct pci_dev *dev, int resno);
+void pci_iov_update_resource(struct pci_dev *dev, int resno);
resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
void pci_restore_iov_state(struct pci_dev *dev);
int pci_iov_bus_range(struct pci_bus *bus);
@@ -303,10 +299,6 @@ static inline void pci_iov_release(struct pci_dev *dev)
{
}
-static inline int pci_iov_resource_bar(struct pci_dev *dev, int resno)
-{
- return 0;
-}
static inline void pci_restore_iov_state(struct pci_dev *dev)
{
}
@@ -356,4 +348,9 @@ static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe)
}
#endif
+#if defined(CONFIG_PCI_QUIRKS) && defined(CONFIG_ARM64)
+int acpi_get_rc_resources(struct device *dev, const char *hid, u16 segment,
+ struct resource *res);
+#endif
+
#endif /* DRIVERS_PCI_H */
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 7ce77635e5ad..ac53edbc9613 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -71,6 +71,14 @@ config PCIEASPM_POWERSAVE
Enable PCI Express ASPM L0s and L1 where possible, even if the
BIOS did not.
+config PCIEASPM_POWER_SUPERSAVE
+ bool "Power Supersave"
+ depends on PCIEASPM
+ help
+ Same as PCIEASPM_POWERSAVE, except it also enables L1 substates where
+ possible. This would result in higher power savings while staying in L1
+ where the components support it.
+
config PCIEASPM_PERFORMANCE
bool "Performance"
depends on PCIEASPM
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index 139150b2bdfd..dea186a9d6b6 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -30,13 +30,6 @@
#include "aerdrv.h"
#include "../../pci.h"
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.0"
-#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
-#define DRIVER_DESC "Root Port Advanced Error Reporting Driver"
-
static int aer_probe(struct pcie_device *dev);
static void aer_remove(struct pcie_device *dev);
static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
@@ -297,12 +290,12 @@ static int aer_probe(struct pcie_device *dev)
{
int status;
struct aer_rpc *rpc;
- struct device *device = &dev->device;
+ struct device *device = &dev->port->dev;
/* Alloc rpc data structure */
rpc = aer_alloc_rpc(dev);
if (!rpc) {
- dev_printk(KERN_DEBUG, device, "alloc rpc failed\n");
+ dev_printk(KERN_DEBUG, device, "alloc AER rpc failed\n");
aer_remove(dev);
return -ENOMEM;
}
@@ -310,7 +303,8 @@ static int aer_probe(struct pcie_device *dev)
/* Request IRQ ISR */
status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev);
if (status) {
- dev_printk(KERN_DEBUG, device, "request IRQ failed\n");
+ dev_printk(KERN_DEBUG, device, "request AER IRQ %d failed\n",
+ dev->irq);
aer_remove(dev);
return status;
}
@@ -318,8 +312,8 @@ static int aer_probe(struct pcie_device *dev)
rpc->isr = 1;
aer_enable_rootport(rpc);
-
- return status;
+ dev_info(device, "AER enabled with IRQ %d\n", dev->irq);
+ return 0;
}
/**
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 0ec649d961d7..973472c23d89 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -30,8 +30,29 @@
#define ASPM_STATE_L0S_UP (1) /* Upstream direction L0s state */
#define ASPM_STATE_L0S_DW (2) /* Downstream direction L0s state */
#define ASPM_STATE_L1 (4) /* L1 state */
+#define ASPM_STATE_L1_1 (8) /* ASPM L1.1 state */
+#define ASPM_STATE_L1_2 (0x10) /* ASPM L1.2 state */
+#define ASPM_STATE_L1_1_PCIPM (0x20) /* PCI PM L1.1 state */
+#define ASPM_STATE_L1_2_PCIPM (0x40) /* PCI PM L1.2 state */
+#define ASPM_STATE_L1_SS_PCIPM (ASPM_STATE_L1_1_PCIPM | ASPM_STATE_L1_2_PCIPM)
+#define ASPM_STATE_L1_2_MASK (ASPM_STATE_L1_2 | ASPM_STATE_L1_2_PCIPM)
+#define ASPM_STATE_L1SS (ASPM_STATE_L1_1 | ASPM_STATE_L1_1_PCIPM |\
+ ASPM_STATE_L1_2_MASK)
#define ASPM_STATE_L0S (ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW)
-#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1)
+#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1 | \
+ ASPM_STATE_L1SS)
+
+/*
+ * When L1 substates are enabled, the LTR L1.2 threshold is a timing parameter
+ * that decides whether L1.1 or L1.2 is entered (Refer PCIe spec for details).
+ * Not sure is there is a way to "calculate" this on the fly, but maybe we
+ * could turn it into a parameter in future. This value has been taken from
+ * the following files from Intel's coreboot (which is the only code I found
+ * to have used this):
+ * https://www.coreboot.org/pipermail/coreboot-gerrit/2015-March/021134.html
+ * https://review.coreboot.org/#/c/8832/
+ */
+#define LTR_L1_2_THRESHOLD_BITS ((1 << 21) | (1 << 23) | (1 << 30))
struct aspm_latency {
u32 l0s; /* L0s latency (nsec) */
@@ -40,6 +61,7 @@ struct aspm_latency {
struct pcie_link_state {
struct pci_dev *pdev; /* Upstream component of the Link */
+ struct pci_dev *downstream; /* Downstream component, function 0 */
struct pcie_link_state *root; /* pointer to the root port link */
struct pcie_link_state *parent; /* pointer to the parent Link state */
struct list_head sibling; /* node in link_list */
@@ -47,11 +69,11 @@ struct pcie_link_state {
struct list_head link; /* node in parent's children list */
/* ASPM state */
- u32 aspm_support:3; /* Supported ASPM state */
- u32 aspm_enabled:3; /* Enabled ASPM state */
- u32 aspm_capable:3; /* Capable ASPM state with latency */
- u32 aspm_default:3; /* Default ASPM state by BIOS */
- u32 aspm_disable:3; /* Disabled ASPM state */
+ u32 aspm_support:7; /* Supported ASPM state */
+ u32 aspm_enabled:7; /* Enabled ASPM state */
+ u32 aspm_capable:7; /* Capable ASPM state with latency */
+ u32 aspm_default:7; /* Default ASPM state by BIOS */
+ u32 aspm_disable:7; /* Disabled ASPM state */
/* Clock PM state */
u32 clkpm_capable:1; /* Clock PM capable? */
@@ -66,6 +88,14 @@ struct pcie_link_state {
* has one slot under it, so at most there are 8 functions.
*/
struct aspm_latency acceptable[8];
+
+ /* L1 PM Substate info */
+ struct {
+ u32 up_cap_ptr; /* L1SS cap ptr in upstream dev */
+ u32 dw_cap_ptr; /* L1SS cap ptr in downstream dev */
+ u32 ctl1; /* value to be programmed in ctl1 */
+ u32 ctl2; /* value to be programmed in ctl2 */
+ } l1ss;
};
static int aspm_disabled, aspm_force;
@@ -76,11 +106,14 @@ static LIST_HEAD(link_list);
#define POLICY_DEFAULT 0 /* BIOS default setting */
#define POLICY_PERFORMANCE 1 /* high performance */
#define POLICY_POWERSAVE 2 /* high power saving */
+#define POLICY_POWER_SUPERSAVE 3 /* possibly even more power saving */
#ifdef CONFIG_PCIEASPM_PERFORMANCE
static int aspm_policy = POLICY_PERFORMANCE;
#elif defined CONFIG_PCIEASPM_POWERSAVE
static int aspm_policy = POLICY_POWERSAVE;
+#elif defined CONFIG_PCIEASPM_POWER_SUPERSAVE
+static int aspm_policy = POLICY_POWER_SUPERSAVE;
#else
static int aspm_policy;
#endif
@@ -88,7 +121,8 @@ static int aspm_policy;
static const char *policy_str[] = {
[POLICY_DEFAULT] = "default",
[POLICY_PERFORMANCE] = "performance",
- [POLICY_POWERSAVE] = "powersave"
+ [POLICY_POWERSAVE] = "powersave",
+ [POLICY_POWER_SUPERSAVE] = "powersupersave"
};
#define LINK_RETRAIN_TIMEOUT HZ
@@ -101,6 +135,9 @@ static int policy_to_aspm_state(struct pcie_link_state *link)
return 0;
case POLICY_POWERSAVE:
/* Enable ASPM L0s/L1 */
+ return (ASPM_STATE_L0S | ASPM_STATE_L1);
+ case POLICY_POWER_SUPERSAVE:
+ /* Enable Everything */
return ASPM_STATE_ALL;
case POLICY_DEFAULT:
return link->aspm_default;
@@ -115,7 +152,8 @@ static int policy_to_clkpm_state(struct pcie_link_state *link)
/* Disable ASPM and Clock PM */
return 0;
case POLICY_POWERSAVE:
- /* Disable Clock PM */
+ case POLICY_POWER_SUPERSAVE:
+ /* Enable Clock PM */
return 1;
case POLICY_DEFAULT:
return link->clkpm_default;
@@ -278,11 +316,33 @@ static u32 calc_l1_acceptable(u32 encoding)
return (1000 << encoding);
}
+/* Convert L1SS T_pwr encoding to usec */
+static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val)
+{
+ switch (scale) {
+ case 0:
+ return val * 2;
+ case 1:
+ return val * 10;
+ case 2:
+ return val * 100;
+ }
+ dev_err(&pdev->dev, "%s: Invalid T_PwrOn scale: %u\n",
+ __func__, scale);
+ return 0;
+}
+
struct aspm_register_info {
u32 support:2;
u32 enabled:2;
u32 latency_encoding_l0s;
u32 latency_encoding_l1;
+
+ /* L1 substates */
+ u32 l1ss_cap_ptr;
+ u32 l1ss_cap;
+ u32 l1ss_ctl1;
+ u32 l1ss_ctl2;
};
static void pcie_get_aspm_reg(struct pci_dev *pdev,
@@ -297,6 +357,22 @@ static void pcie_get_aspm_reg(struct pci_dev *pdev,
info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &reg16);
info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC;
+
+ /* Read L1 PM substate capabilities */
+ info->l1ss_cap = info->l1ss_ctl1 = info->l1ss_ctl2 = 0;
+ info->l1ss_cap_ptr = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS);
+ if (!info->l1ss_cap_ptr)
+ return;
+ pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CAP,
+ &info->l1ss_cap);
+ if (!(info->l1ss_cap & PCI_L1SS_CAP_L1_PM_SS)) {
+ info->l1ss_cap = 0;
+ return;
+ }
+ pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL1,
+ &info->l1ss_ctl1);
+ pci_read_config_dword(pdev, info->l1ss_cap_ptr + PCI_L1SS_CTL2,
+ &info->l1ss_ctl2);
}
static void pcie_aspm_check_latency(struct pci_dev *endpoint)
@@ -327,6 +403,14 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
* Check L1 latency.
* Every switch on the path to root complex need 1
* more microsecond for L1. Spec doesn't mention L0s.
+ *
+ * The exit latencies for L1 substates are not advertised
+ * by a device. Since the spec also doesn't mention a way
+ * to determine max latencies introduced by enabling L1
+ * substates on the components, it is not clear how to do
+ * a L1 substate exit latency check. We assume that the
+ * L1 exit latencies advertised by a device include L1
+ * substate latencies (and hence do not do any check).
*/
latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1);
if ((link->aspm_capable & ASPM_STATE_L1) &&
@@ -338,6 +422,60 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
}
}
+/*
+ * The L1 PM substate capability is only implemented in function 0 in a
+ * multi function device.
+ */
+static struct pci_dev *pci_function_0(struct pci_bus *linkbus)
+{
+ struct pci_dev *child;
+
+ list_for_each_entry(child, &linkbus->devices, bus_list)
+ if (PCI_FUNC(child->devfn) == 0)
+ return child;
+ return NULL;
+}
+
+/* Calculate L1.2 PM substate timing parameters */
+static void aspm_calc_l1ss_info(struct pcie_link_state *link,
+ struct aspm_register_info *upreg,
+ struct aspm_register_info *dwreg)
+{
+ u32 val1, val2, scale1, scale2;
+
+ link->l1ss.up_cap_ptr = upreg->l1ss_cap_ptr;
+ link->l1ss.dw_cap_ptr = dwreg->l1ss_cap_ptr;
+ link->l1ss.ctl1 = link->l1ss.ctl2 = 0;
+
+ if (!(link->aspm_support & ASPM_STATE_L1_2_MASK))
+ return;
+
+ /* Choose the greater of the two T_cmn_mode_rstr_time */
+ val1 = (upreg->l1ss_cap >> 8) & 0xFF;
+ val2 = (upreg->l1ss_cap >> 8) & 0xFF;
+ if (val1 > val2)
+ link->l1ss.ctl1 |= val1 << 8;
+ else
+ link->l1ss.ctl1 |= val2 << 8;
+ /*
+ * We currently use LTR L1.2 threshold to be fixed constant picked from
+ * Intel's coreboot.
+ */
+ link->l1ss.ctl1 |= LTR_L1_2_THRESHOLD_BITS;
+
+ /* Choose the greater of the two T_pwr_on */
+ val1 = (upreg->l1ss_cap >> 19) & 0x1F;
+ scale1 = (upreg->l1ss_cap >> 16) & 0x03;
+ val2 = (dwreg->l1ss_cap >> 19) & 0x1F;
+ scale2 = (dwreg->l1ss_cap >> 16) & 0x03;
+
+ if (calc_l1ss_pwron(link->pdev, scale1, val1) >
+ calc_l1ss_pwron(link->downstream, scale2, val2))
+ link->l1ss.ctl2 |= scale1 | (val1 << 3);
+ else
+ link->l1ss.ctl2 |= scale2 | (val2 << 3);
+}
+
static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
{
struct pci_dev *child, *parent = link->pdev;
@@ -351,12 +489,27 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
return;
}
+ /* Get upstream/downstream components' register state */
+ pcie_get_aspm_reg(parent, &upreg);
+ child = pci_function_0(linkbus);
+ pcie_get_aspm_reg(child, &dwreg);
+ link->downstream = child;
+
+ /*
+ * If ASPM not supported, don't mess with the clocks and link,
+ * bail out now.
+ */
+ if (!(upreg.support & dwreg.support))
+ return;
+
/* Configure common clock before checking latencies */
pcie_aspm_configure_common_clock(link);
- /* Get upstream/downstream components' register state */
+ /*
+ * Re-read upstream/downstream components' register state
+ * after clock configuration
+ */
pcie_get_aspm_reg(parent, &upreg);
- child = list_entry(linkbus->devices.next, struct pci_dev, bus_list);
pcie_get_aspm_reg(child, &dwreg);
/*
@@ -383,6 +536,28 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1);
link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1);
+ /* Setup L1 substate */
+ if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_ASPM_L1_1)
+ link->aspm_support |= ASPM_STATE_L1_1;
+ if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_ASPM_L1_2)
+ link->aspm_support |= ASPM_STATE_L1_2;
+ if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_1)
+ link->aspm_support |= ASPM_STATE_L1_1_PCIPM;
+ if (upreg.l1ss_cap & dwreg.l1ss_cap & PCI_L1SS_CAP_PCIPM_L1_2)
+ link->aspm_support |= ASPM_STATE_L1_2_PCIPM;
+
+ if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_1)
+ link->aspm_enabled |= ASPM_STATE_L1_1;
+ if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_ASPM_L1_2)
+ link->aspm_enabled |= ASPM_STATE_L1_2;
+ if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_1)
+ link->aspm_enabled |= ASPM_STATE_L1_1_PCIPM;
+ if (upreg.l1ss_ctl1 & dwreg.l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_2)
+ link->aspm_enabled |= ASPM_STATE_L1_2_PCIPM;
+
+ if (link->aspm_support & ASPM_STATE_L1SS)
+ aspm_calc_l1ss_info(link, &upreg, &dwreg);
+
/* Save default state */
link->aspm_default = link->aspm_enabled;
@@ -421,6 +596,92 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
}
}
+static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos,
+ u32 clear, u32 set)
+{
+ u32 val;
+
+ pci_read_config_dword(pdev, pos, &val);
+ val &= ~clear;
+ val |= set;
+ pci_write_config_dword(pdev, pos, val);
+}
+
+/* Configure the ASPM L1 substates */
+static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state)
+{
+ u32 val, enable_req;
+ struct pci_dev *child = link->downstream, *parent = link->pdev;
+ u32 up_cap_ptr = link->l1ss.up_cap_ptr;
+ u32 dw_cap_ptr = link->l1ss.dw_cap_ptr;
+
+ enable_req = (link->aspm_enabled ^ state) & state;
+
+ /*
+ * Here are the rules specified in the PCIe spec for enabling L1SS:
+ * - When enabling L1.x, enable bit at parent first, then at child
+ * - When disabling L1.x, disable bit at child first, then at parent
+ * - When enabling ASPM L1.x, need to disable L1
+ * (at child followed by parent).
+ * - The ASPM/PCIPM L1.2 must be disabled while programming timing
+ * parameters
+ *
+ * To keep it simple, disable all L1SS bits first, and later enable
+ * what is needed.
+ */
+
+ /* Disable all L1 substates */
+ pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_L1SS_MASK, 0);
+ pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_L1SS_MASK, 0);
+ /*
+ * If needed, disable L1, and it gets enabled later
+ * in pcie_config_aspm_link().
+ */
+ if (enable_req & (ASPM_STATE_L1_1 | ASPM_STATE_L1_2)) {
+ pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_ASPM_L1, 0);
+ pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_ASPM_L1, 0);
+ }
+
+ if (enable_req & ASPM_STATE_L1_2_MASK) {
+
+ /* Program T_pwr_on in both ports */
+ pci_write_config_dword(parent, up_cap_ptr + PCI_L1SS_CTL2,
+ link->l1ss.ctl2);
+ pci_write_config_dword(child, dw_cap_ptr + PCI_L1SS_CTL2,
+ link->l1ss.ctl2);
+
+ /* Program T_cmn_mode in parent */
+ pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1,
+ 0xFF00, link->l1ss.ctl1);
+
+ /* Program LTR L1.2 threshold in both ports */
+ pci_clear_and_set_dword(parent, dw_cap_ptr + PCI_L1SS_CTL1,
+ 0xE3FF0000, link->l1ss.ctl1);
+ pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1,
+ 0xE3FF0000, link->l1ss.ctl1);
+ }
+
+ val = 0;
+ if (state & ASPM_STATE_L1_1)
+ val |= PCI_L1SS_CTL1_ASPM_L1_1;
+ if (state & ASPM_STATE_L1_2)
+ val |= PCI_L1SS_CTL1_ASPM_L1_2;
+ if (state & ASPM_STATE_L1_1_PCIPM)
+ val |= PCI_L1SS_CTL1_PCIPM_L1_1;
+ if (state & ASPM_STATE_L1_2_PCIPM)
+ val |= PCI_L1SS_CTL1_PCIPM_L1_2;
+
+ /* Enable what we need to enable */
+ pci_clear_and_set_dword(parent, up_cap_ptr + PCI_L1SS_CTL1,
+ PCI_L1SS_CAP_L1_PM_SS, val);
+ pci_clear_and_set_dword(child, dw_cap_ptr + PCI_L1SS_CTL1,
+ PCI_L1SS_CAP_L1_PM_SS, val);
+}
+
static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
{
pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
@@ -430,11 +691,23 @@ static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
{
u32 upstream = 0, dwstream = 0;
- struct pci_dev *child, *parent = link->pdev;
+ struct pci_dev *child = link->downstream, *parent = link->pdev;
struct pci_bus *linkbus = parent->subordinate;
- /* Nothing to do if the link is already in the requested state */
+ /* Enable only the states that were not explicitly disabled */
state &= (link->aspm_capable & ~link->aspm_disable);
+
+ /* Can't enable any substates if L1 is not enabled */
+ if (!(state & ASPM_STATE_L1))
+ state &= ~ASPM_STATE_L1SS;
+
+ /* Spec says both ports must be in D0 before enabling PCI PM substates*/
+ if (parent->current_state != PCI_D0 || child->current_state != PCI_D0) {
+ state &= ~ASPM_STATE_L1_SS_PCIPM;
+ state |= (link->aspm_enabled & ASPM_STATE_L1_SS_PCIPM);
+ }
+
+ /* Nothing to do if the link is already in the requested state */
if (link->aspm_enabled == state)
return;
/* Convert ASPM state to upstream/downstream ASPM register state */
@@ -446,6 +719,10 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
upstream |= PCI_EXP_LNKCTL_ASPM_L1;
dwstream |= PCI_EXP_LNKCTL_ASPM_L1;
}
+
+ if (link->aspm_capable & ASPM_STATE_L1SS)
+ pcie_config_aspm_l1ss(link, state);
+
/*
* Spec 2.0 suggests all functions should be configured the
* same setting for ASPM. Enabling ASPM L1 should be done in
@@ -518,25 +795,32 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
link = kzalloc(sizeof(*link), GFP_KERNEL);
if (!link)
return NULL;
+
INIT_LIST_HEAD(&link->sibling);
INIT_LIST_HEAD(&link->children);
INIT_LIST_HEAD(&link->link);
link->pdev = pdev;
- if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) {
+
+ /*
+ * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe
+ * hierarchies.
+ */
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE) {
+ link->root = link;
+ } else {
struct pcie_link_state *parent;
+
parent = pdev->bus->parent->self->link_state;
if (!parent) {
kfree(link);
return NULL;
}
+
link->parent = parent;
+ link->root = link->parent->root;
list_add(&link->link, &parent->children);
}
- /* Setup a pointer to the root port link */
- if (!link->parent)
- link->root = link;
- else
- link->root = link->parent->root;
list_add(&link->sibling, &link_list);
pdev->link_state = link;
@@ -598,7 +882,8 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
* the BIOS's expectation, we'll do so once pci_enable_device() is
* called.
*/
- if (aspm_policy != POLICY_POWERSAVE) {
+ if (aspm_policy != POLICY_POWERSAVE &&
+ aspm_policy != POLICY_POWER_SUPERSAVE) {
pcie_config_aspm_path(link);
pcie_set_clkpm(link, policy_to_clkpm_state(link));
}
@@ -698,7 +983,8 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
if (aspm_disabled || !link)
return;
- if (aspm_policy != POLICY_POWERSAVE)
+ if (aspm_policy != POLICY_POWERSAVE &&
+ aspm_policy != POLICY_POWER_SUPERSAVE)
return;
down_read(&pci_bus_sem);
@@ -886,8 +1172,8 @@ static ssize_t clk_ctl_store(struct device *dev,
return n;
}
-static DEVICE_ATTR(link_state, 0644, link_state_show, link_state_store);
-static DEVICE_ATTR(clk_ctl, 0644, clk_ctl_show, clk_ctl_store);
+static DEVICE_ATTR_RW(link_state);
+static DEVICE_ATTR_RW(clk_ctl);
static char power_group[] = "power";
void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev)
diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c
index 9811b14d9ad8..d4d70ef4a2d7 100644
--- a/drivers/pci/pcie/pcie-dpc.c
+++ b/drivers/pci/pcie/pcie-dpc.c
@@ -19,8 +19,28 @@ struct dpc_dev {
struct pcie_device *dev;
struct work_struct work;
int cap_pos;
+ bool rp;
};
+static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
+{
+ unsigned long timeout = jiffies + HZ;
+ struct pci_dev *pdev = dpc->dev->port;
+ u16 status;
+
+ pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status);
+ while (status & PCI_EXP_DPC_RP_BUSY &&
+ !time_after(jiffies, timeout)) {
+ msleep(10);
+ pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status);
+ }
+ if (status & PCI_EXP_DPC_RP_BUSY) {
+ dev_warn(&pdev->dev, "DPC root port still busy\n");
+ return -EBUSY;
+ }
+ return 0;
+}
+
static void dpc_wait_link_inactive(struct pci_dev *pdev)
{
unsigned long timeout = jiffies + HZ;
@@ -33,7 +53,7 @@ static void dpc_wait_link_inactive(struct pci_dev *pdev)
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
}
if (lnk_status & PCI_EXP_LNKSTA_DLLLA)
- dev_warn(&pdev->dev, "Link state not disabled for DPC event");
+ dev_warn(&pdev->dev, "Link state not disabled for DPC event\n");
}
static void interrupt_event_handler(struct work_struct *work)
@@ -52,6 +72,8 @@ static void interrupt_event_handler(struct work_struct *work)
pci_unlock_rescan_remove();
dpc_wait_link_inactive(pdev);
+ if (dpc->rp && dpc_wait_rp_inactive(dpc))
+ return;
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS,
PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
}
@@ -73,11 +95,15 @@ static irqreturn_t dpc_irq(int irq, void *context)
if (status & PCI_EXP_DPC_STATUS_TRIGGER) {
u16 reason = (status >> 1) & 0x3;
+ u16 ext_reason = (status >> 5) & 0x3;
- dev_warn(&dpc->dev->device, "DPC %s triggered, remove downstream devices\n",
+ dev_warn(&dpc->dev->device, "DPC %s detected, remove downstream devices\n",
(reason == 0) ? "unmasked uncorrectable error" :
(reason == 1) ? "ERR_NONFATAL" :
- (reason == 2) ? "ERR_FATAL" : "extended error");
+ (reason == 2) ? "ERR_FATAL" :
+ (ext_reason == 0) ? "RP PIO error" :
+ (ext_reason == 1) ? "software trigger" :
+ "reserved error");
schedule_work(&dpc->work);
}
return IRQ_HANDLED;
@@ -111,6 +137,8 @@ static int dpc_probe(struct pcie_device *dev)
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap);
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
+ dpc->rp = (cap & PCI_EXP_DPC_CAP_RP_EXT);
+
ctl |= PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN;
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index 884bad5320f8..2dd1c68e6de8 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -300,8 +300,6 @@ static irqreturn_t pcie_pme_irq(int irq, void *context)
*/
static int pcie_pme_set_native(struct pci_dev *dev, void *ign)
{
- dev_info(&dev->dev, "Signaling PME through PCIe PME interrupt\n");
-
device_set_run_wake(&dev->dev, true);
dev->pme_interrupt = true;
return 0;
@@ -319,23 +317,8 @@ static int pcie_pme_set_native(struct pci_dev *dev, void *ign)
static void pcie_pme_mark_devices(struct pci_dev *port)
{
pcie_pme_set_native(port, NULL);
- if (port->subordinate) {
+ if (port->subordinate)
pci_walk_bus(port->subordinate, pcie_pme_set_native, NULL);
- } else {
- struct pci_bus *bus = port->bus;
- struct pci_dev *dev;
-
- /* Check if this is a root port event collector. */
- if (pci_pcie_type(port) != PCI_EXP_TYPE_RC_EC || !bus)
- return;
-
- down_read(&pci_bus_sem);
- list_for_each_entry(dev, &bus->devices, bus_list)
- if (pci_is_pcie(dev)
- && pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
- pcie_pme_set_native(dev, NULL);
- up_read(&pci_bus_sem);
- }
}
/**
@@ -364,12 +347,14 @@ static int pcie_pme_probe(struct pcie_device *srv)
ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv);
if (ret) {
kfree(data);
- } else {
- pcie_pme_mark_devices(port);
- pcie_pme_interrupt_enable(port, true);
+ return ret;
}
- return ret;
+ dev_info(&port->dev, "Signaling PME with IRQ %d\n", srv->irq);
+
+ pcie_pme_mark_devices(port);
+ pcie_pme_interrupt_enable(port, true);
+ return 0;
}
static bool pcie_pme_check_wakeup(struct pci_bus *bus)
@@ -448,6 +433,17 @@ static int pcie_pme_resume(struct pcie_device *srv)
return 0;
}
+/**
+ * pcie_pme_remove - Prepare PCIe PME service device for removal.
+ * @srv - PCIe service device to remove.
+ */
+static void pcie_pme_remove(struct pcie_device *srv)
+{
+ pcie_pme_suspend(srv);
+ free_irq(srv->irq, srv);
+ kfree(get_service_data(srv));
+}
+
static struct pcie_port_service_driver pcie_pme_driver = {
.name = "pcie_pme",
.port_type = PCI_EXP_TYPE_ROOT_PORT,
@@ -456,6 +452,7 @@ static struct pcie_port_service_driver pcie_pme_driver = {
.probe = pcie_pme_probe,
.suspend = pcie_pme_suspend,
.resume = pcie_pme_resume,
+ .remove = pcie_pme_remove,
};
/**
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index e9270b4026f3..cea504f6f478 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -44,52 +44,16 @@ static void release_pcie_device(struct device *dev)
}
/**
- * pcie_port_msix_add_entry - add entry to given array of MSI-X entries
- * @entries: Array of MSI-X entries
- * @new_entry: Index of the entry to add to the array
- * @nr_entries: Number of entries already in the array
- *
- * Return value: Position of the added entry in the array
- */
-static int pcie_port_msix_add_entry(
- struct msix_entry *entries, int new_entry, int nr_entries)
-{
- int j;
-
- for (j = 0; j < nr_entries; j++)
- if (entries[j].entry == new_entry)
- return j;
-
- entries[j].entry = new_entry;
- return j;
-}
-
-/**
* pcie_port_enable_msix - try to set up MSI-X as interrupt mode for given port
* @dev: PCI Express port to handle
- * @vectors: Array of interrupt vectors to populate
+ * @irqs: Array of interrupt vectors to populate
* @mask: Bitmask of port capabilities returned by get_port_device_capability()
*
* Return value: 0 on success, error code on failure
*/
-static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
+static int pcie_port_enable_msix(struct pci_dev *dev, int *irqs, int mask)
{
- struct msix_entry *msix_entries;
- int idx[PCIE_PORT_DEVICE_MAXSERVICES];
- int nr_entries, status, pos, i, nvec;
- u16 reg16;
- u32 reg32;
-
- nr_entries = pci_msix_vec_count(dev);
- if (nr_entries < 0)
- return nr_entries;
- BUG_ON(!nr_entries);
- if (nr_entries > PCIE_PORT_MAX_MSIX_ENTRIES)
- nr_entries = PCIE_PORT_MAX_MSIX_ENTRIES;
-
- msix_entries = kzalloc(sizeof(*msix_entries) * nr_entries, GFP_KERNEL);
- if (!msix_entries)
- return -ENOMEM;
+ int nr_entries, entry, nvec = 0;
/*
* Allocate as many entries as the port wants, so that we can check
@@ -97,20 +61,13 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
* equal to the number of entries this port actually uses, we'll happily
* go through without any tricks.
*/
- for (i = 0; i < nr_entries; i++)
- msix_entries[i].entry = i;
-
- status = pci_enable_msix_exact(dev, msix_entries, nr_entries);
- if (status)
- goto Exit;
-
- for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
- idx[i] = -1;
- status = -EIO;
- nvec = 0;
+ nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSIX_ENTRIES,
+ PCI_IRQ_MSIX);
+ if (nr_entries < 0)
+ return nr_entries;
if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
- int entry;
+ u16 reg16;
/*
* The code below follows the PCI Express Base Specification 2.0
@@ -125,18 +82,16 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16);
entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
if (entry >= nr_entries)
- goto Error;
+ goto out_free_irqs;
- i = pcie_port_msix_add_entry(msix_entries, entry, nvec);
- if (i == nvec)
- nvec++;
+ irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, entry);
+ irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, entry);
- idx[PCIE_PORT_SERVICE_PME_SHIFT] = i;
- idx[PCIE_PORT_SERVICE_HP_SHIFT] = i;
+ nvec = max(nvec, entry + 1);
}
if (mask & PCIE_PORT_SERVICE_AER) {
- int entry;
+ u32 reg32, pos;
/*
* The code below follows Section 7.10.10 of the PCI Express
@@ -151,13 +106,11 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
entry = reg32 >> 27;
if (entry >= nr_entries)
- goto Error;
+ goto out_free_irqs;
- i = pcie_port_msix_add_entry(msix_entries, entry, nvec);
- if (i == nvec)
- nvec++;
+ irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, entry);
- idx[PCIE_PORT_SERVICE_AER_SHIFT] = i;
+ nvec = max(nvec, entry + 1);
}
/*
@@ -165,41 +118,39 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
* what we have. Otherwise, the port has some extra entries not for the
* services we know and we need to work around that.
*/
- if (nvec == nr_entries) {
- status = 0;
- } else {
+ if (nvec != nr_entries) {
/* Drop the temporary MSI-X setup */
- pci_disable_msix(dev);
+ pci_free_irq_vectors(dev);
/* Now allocate the MSI-X vectors for real */
- status = pci_enable_msix_exact(dev, msix_entries, nvec);
- if (status)
- goto Exit;
+ nr_entries = pci_alloc_irq_vectors(dev, nvec, nvec,
+ PCI_IRQ_MSIX);
+ if (nr_entries < 0)
+ return nr_entries;
}
- for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
- vectors[i] = idx[i] >= 0 ? msix_entries[idx[i]].vector : -1;
-
- Exit:
- kfree(msix_entries);
- return status;
+ return 0;
- Error:
- pci_disable_msix(dev);
- goto Exit;
+out_free_irqs:
+ pci_free_irq_vectors(dev);
+ return -EIO;
}
/**
- * init_service_irqs - initialize irqs for PCI Express port services
+ * pcie_init_service_irqs - initialize irqs for PCI Express port services
* @dev: PCI Express port to handle
* @irqs: Array of irqs to populate
* @mask: Bitmask of port capabilities returned by get_port_device_capability()
*
* Return value: Interrupt mode associated with the port
*/
-static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
+static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
{
- int i, irq = -1;
+ unsigned flags = PCI_IRQ_LEGACY | PCI_IRQ_MSI;
+ int ret, i;
+
+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
+ irqs[i] = -1;
/*
* If MSI cannot be used for PCIe PME or hotplug, we have to use
@@ -207,41 +158,25 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
*/
if (((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) ||
((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())) {
- if (dev->irq)
- irq = dev->irq;
- goto no_msi;
+ flags &= ~PCI_IRQ_MSI;
+ } else {
+ /* Try to use MSI-X if supported */
+ if (!pcie_port_enable_msix(dev, irqs, mask))
+ return 0;
}
- /* Try to use MSI-X if supported */
- if (!pcie_port_enable_msix(dev, irqs, mask))
- return 0;
-
- /*
- * We're not going to use MSI-X, so try MSI and fall back to INTx.
- * If neither MSI/MSI-X nor INTx available, try other interrupt. On
- * some platforms, root port doesn't support MSI/MSI-X/INTx in RC mode.
- */
- if (!pci_enable_msi(dev) || dev->irq)
- irq = dev->irq;
+ ret = pci_alloc_irq_vectors(dev, 1, 1, flags);
+ if (ret < 0)
+ return -ENODEV;
- no_msi:
- for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
- irqs[i] = irq;
- irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1;
+ for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
+ if (i != PCIE_PORT_SERVICE_VC_SHIFT)
+ irqs[i] = pci_irq_vector(dev, 0);
+ }
- if (irq < 0)
- return -ENODEV;
return 0;
}
-static void cleanup_service_irqs(struct pci_dev *dev)
-{
- if (dev->msix_enabled)
- pci_disable_msix(dev);
- else if (dev->msi_enabled)
- pci_disable_msi(dev);
-}
-
/**
* get_port_device_capability - discover capabilities of a PCI Express port
* @dev: PCI Express port to examine
@@ -378,7 +313,7 @@ int pcie_port_device_register(struct pci_dev *dev)
* that can be used in the absence of irqs. Allow them to determine
* if that is to be used.
*/
- status = init_service_irqs(dev, irqs, capabilities);
+ status = pcie_init_service_irqs(dev, irqs, capabilities);
if (status) {
capabilities &= PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_HP;
if (!capabilities)
@@ -401,7 +336,7 @@ int pcie_port_device_register(struct pci_dev *dev)
return 0;
error_cleanup_irqs:
- cleanup_service_irqs(dev);
+ pci_free_irq_vectors(dev);
error_disable:
pci_disable_device(dev);
return status;
@@ -469,7 +404,7 @@ static int remove_iter(struct device *dev, void *data)
void pcie_port_device_remove(struct pci_dev *dev)
{
device_for_each_child(&dev->dev, NULL, remove_iter);
- cleanup_service_irqs(dev);
+ pci_free_irq_vectors(dev);
pci_disable_device(dev);
}
@@ -499,7 +434,6 @@ static int pcie_port_probe_service(struct device *dev)
if (status)
return status;
- dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n", driver->name);
get_device(dev);
return 0;
}
@@ -524,8 +458,6 @@ static int pcie_port_remove_service(struct device *dev)
pciedev = to_pcie_device(dev);
driver = to_service_driver(dev->driver);
if (driver && driver->remove) {
- dev_printk(KERN_DEBUG, dev, "unloading service driver %s\n",
- driver->name);
driver->remove(pciedev);
put_device(dev);
}
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index 79327cc14e7d..8aa3f14bc87d 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -19,6 +19,7 @@
#include <linux/dmi.h>
#include <linux/pci-aspm.h>
+#include "../pci.h"
#include "portdrv.h"
#include "aer/aerdrv.h"
@@ -149,15 +150,7 @@ static int pcie_portdrv_probe(struct pci_dev *dev,
pci_save_state(dev);
- /*
- * Prevent runtime PM if the port is advertising support for PCIe
- * hotplug. Otherwise the BIOS hotplug SMI code might not be able
- * to enumerate devices behind this port properly (the port is
- * powered down preventing all config space accesses to the
- * subordinate devices). We can't be sure for native PCIe hotplug
- * either so prevent that as well.
- */
- if (!dev->is_hotplug_bridge) {
+ if (pci_bridge_d3_possible(dev)) {
/*
* Keep the port resumed 100ms to make sure things like
* config space accesses from userspace (lspci) will not
@@ -175,7 +168,7 @@ static int pcie_portdrv_probe(struct pci_dev *dev,
static void pcie_portdrv_remove(struct pci_dev *dev)
{
- if (!dev->is_hotplug_bridge) {
+ if (pci_bridge_d3_possible(dev)) {
pm_runtime_forbid(&dev->dev);
pm_runtime_get_noresume(&dev->dev);
pm_runtime_dont_use_autosuspend(&dev->dev);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 104c46d53121..dfc9a2794141 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -227,7 +227,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
mask64 = (u32)PCI_BASE_ADDRESS_MEM_MASK;
}
} else {
- res->flags |= (l & IORESOURCE_ROM_ENABLE);
+ if (l & PCI_ROM_ADDRESS_ENABLE)
+ res->flags |= IORESOURCE_ROM_ENABLE;
l64 = l & PCI_ROM_ADDRESS_MASK;
sz64 = sz & PCI_ROM_ADDRESS_MASK;
mask64 = (u32)PCI_ROM_ADDRESS_MASK;
@@ -521,18 +522,19 @@ static void pci_release_host_bridge_dev(struct device *dev)
kfree(bridge);
}
-static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b)
+struct pci_host_bridge *pci_alloc_host_bridge(size_t priv)
{
struct pci_host_bridge *bridge;
- bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
+ bridge = kzalloc(sizeof(*bridge) + priv, GFP_KERNEL);
if (!bridge)
return NULL;
INIT_LIST_HEAD(&bridge->windows);
- bridge->bus = b;
+
return bridge;
}
+EXPORT_SYMBOL(pci_alloc_host_bridge);
static const unsigned char pcix_bus_speed[] = {
PCI_SPEED_UNKNOWN, /* 0 */
@@ -717,6 +719,123 @@ static void pci_set_bus_msi_domain(struct pci_bus *bus)
dev_set_msi_domain(&bus->dev, d);
}
+int pci_register_host_bridge(struct pci_host_bridge *bridge)
+{
+ struct device *parent = bridge->dev.parent;
+ struct resource_entry *window, *n;
+ struct pci_bus *bus, *b;
+ resource_size_t offset;
+ LIST_HEAD(resources);
+ struct resource *res;
+ char addr[64], *fmt;
+ const char *name;
+ int err;
+
+ bus = pci_alloc_bus(NULL);
+ if (!bus)
+ return -ENOMEM;
+
+ bridge->bus = bus;
+
+ /* temporarily move resources off the list */
+ list_splice_init(&bridge->windows, &resources);
+ bus->sysdata = bridge->sysdata;
+ bus->msi = bridge->msi;
+ bus->ops = bridge->ops;
+ bus->number = bus->busn_res.start = bridge->busnr;
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+ bus->domain_nr = pci_bus_find_domain_nr(bus, parent);
+#endif
+
+ b = pci_find_bus(pci_domain_nr(bus), bridge->busnr);
+ if (b) {
+ /* If we already got to this bus through a different bridge, ignore it */
+ dev_dbg(&b->dev, "bus already known\n");
+ err = -EEXIST;
+ goto free;
+ }
+
+ dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(bus),
+ bridge->busnr);
+
+ err = pcibios_root_bridge_prepare(bridge);
+ if (err)
+ goto free;
+
+ err = device_register(&bridge->dev);
+ if (err)
+ put_device(&bridge->dev);
+
+ bus->bridge = get_device(&bridge->dev);
+ device_enable_async_suspend(bus->bridge);
+ pci_set_bus_of_node(bus);
+ pci_set_bus_msi_domain(bus);
+
+ if (!parent)
+ set_dev_node(bus->bridge, pcibus_to_node(bus));
+
+ bus->dev.class = &pcibus_class;
+ bus->dev.parent = bus->bridge;
+
+ dev_set_name(&bus->dev, "%04x:%02x", pci_domain_nr(bus), bus->number);
+ name = dev_name(&bus->dev);
+
+ err = device_register(&bus->dev);
+ if (err)
+ goto unregister;
+
+ pcibios_add_bus(bus);
+
+ /* Create legacy_io and legacy_mem files for this bus */
+ pci_create_legacy_files(bus);
+
+ if (parent)
+ dev_info(parent, "PCI host bridge to bus %s\n", name);
+ else
+ pr_info("PCI host bridge to bus %s\n", name);
+
+ /* Add initial resources to the bus */
+ resource_list_for_each_entry_safe(window, n, &resources) {
+ list_move_tail(&window->node, &bridge->windows);
+ offset = window->offset;
+ res = window->res;
+
+ if (res->flags & IORESOURCE_BUS)
+ pci_bus_insert_busn_res(bus, bus->number, res->end);
+ else
+ pci_bus_add_resource(bus, res, 0);
+
+ if (offset) {
+ if (resource_type(res) == IORESOURCE_IO)
+ fmt = " (bus address [%#06llx-%#06llx])";
+ else
+ fmt = " (bus address [%#010llx-%#010llx])";
+
+ snprintf(addr, sizeof(addr), fmt,
+ (unsigned long long)(res->start - offset),
+ (unsigned long long)(res->end - offset));
+ } else
+ addr[0] = '\0';
+
+ dev_info(&bus->dev, "root bus resource %pR%s\n", res, addr);
+ }
+
+ down_write(&pci_bus_sem);
+ list_add_tail(&bus->node, &pci_root_buses);
+ up_write(&pci_bus_sem);
+
+ return 0;
+
+unregister:
+ put_device(&bridge->dev);
+ device_unregister(&bridge->dev);
+
+free:
+ kfree(bus);
+ return err;
+}
+EXPORT_SYMBOL(pci_register_host_bridge);
+
static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
struct pci_dev *bridge, int busnr)
{
@@ -1050,6 +1169,7 @@ void set_pcie_port_type(struct pci_dev *pdev)
pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
if (!pos)
return;
+
pdev->pcie_cap = pos;
pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
pdev->pcie_flags_reg = reg16;
@@ -1057,13 +1177,14 @@ void set_pcie_port_type(struct pci_dev *pdev)
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
/*
- * A Root Port is always the upstream end of a Link. No PCIe
- * component has two Links. Two Links are connected by a Switch
- * that has a Port on each Link and internal logic to connect the
- * two Ports.
+ * A Root Port or a PCI-to-PCIe bridge is always the upstream end
+ * of a Link. No PCIe component has two Links. Two Links are
+ * connected by a Switch that has a Port on each Link and internal
+ * logic to connect the two Ports.
*/
type = pci_pcie_type(pdev);
- if (type == PCI_EXP_TYPE_ROOT_PORT)
+ if (type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_PCIE_BRIDGE)
pdev->has_secondary_link = 1;
else if (type == PCI_EXP_TYPE_UPSTREAM ||
type == PCI_EXP_TYPE_DOWNSTREAM) {
@@ -1435,8 +1556,16 @@ static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp)
static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp)
{
- if (hpp)
- dev_warn(&dev->dev, "PCI-X settings not supported\n");
+ int pos;
+
+ if (!hpp)
+ return;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
+ if (!pos)
+ return;
+
+ dev_warn(&dev->dev, "PCI-X settings not supported\n");
}
static bool pcie_root_rcb_set(struct pci_dev *dev)
@@ -1462,6 +1591,9 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
if (!hpp)
return;
+ if (!pci_is_pcie(dev))
+ return;
+
if (hpp->revision > 1) {
dev_warn(&dev->dev, "PCIe settings rev %d not supported\n",
hpp->revision);
@@ -1531,12 +1663,30 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
*/
}
+static void pci_configure_extended_tags(struct pci_dev *dev)
+{
+ u32 dev_cap;
+ int ret;
+
+ if (!pci_is_pcie(dev))
+ return;
+
+ ret = pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &dev_cap);
+ if (ret)
+ return;
+
+ if (dev_cap & PCI_EXP_DEVCAP_EXT_TAG)
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_EXT_TAG);
+}
+
static void pci_configure_device(struct pci_dev *dev)
{
struct hotplug_params hpp;
int ret;
pci_configure_mps(dev);
+ pci_configure_extended_tags(dev);
memset(&hpp, 0, sizeof(hpp));
ret = pci_get_hp_params(dev, &hpp);
@@ -1764,8 +1914,7 @@ static void pci_dma_configure(struct pci_dev *dev)
if (attr == DEV_DMA_NOT_SUPPORTED)
dev_warn(&dev->dev, "DMA not supported.\n");
else
- arch_setup_dma_ops(&dev->dev, 0, 0, NULL,
- attr == DEV_DMA_COHERENT);
+ acpi_dma_configure(&dev->dev, attr);
}
pci_put_host_bridge_device(bridge);
@@ -2156,113 +2305,43 @@ void __weak pcibios_remove_bus(struct pci_bus *bus)
{
}
-struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
- struct pci_ops *ops, void *sysdata, struct list_head *resources)
+static struct pci_bus *pci_create_root_bus_msi(struct device *parent,
+ int bus, struct pci_ops *ops, void *sysdata,
+ struct list_head *resources, struct msi_controller *msi)
{
int error;
struct pci_host_bridge *bridge;
- struct pci_bus *b, *b2;
- struct resource_entry *window, *n;
- struct resource *res;
- resource_size_t offset;
- char bus_addr[64];
- char *fmt;
- b = pci_alloc_bus(NULL);
- if (!b)
- return NULL;
-
- b->sysdata = sysdata;
- b->ops = ops;
- b->number = b->busn_res.start = bus;
-#ifdef CONFIG_PCI_DOMAINS_GENERIC
- b->domain_nr = pci_bus_find_domain_nr(b, parent);
-#endif
- b2 = pci_find_bus(pci_domain_nr(b), bus);
- if (b2) {
- /* If we already got to this bus through a different bridge, ignore it */
- dev_dbg(&b2->dev, "bus already known\n");
- goto err_out;
- }
-
- bridge = pci_alloc_host_bridge(b);
+ bridge = pci_alloc_host_bridge(0);
if (!bridge)
- goto err_out;
+ return NULL;
bridge->dev.parent = parent;
bridge->dev.release = pci_release_host_bridge_dev;
- dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
- error = pcibios_root_bridge_prepare(bridge);
- if (error) {
- kfree(bridge);
- goto err_out;
- }
-
- error = device_register(&bridge->dev);
- if (error) {
- put_device(&bridge->dev);
- goto err_out;
- }
- b->bridge = get_device(&bridge->dev);
- device_enable_async_suspend(b->bridge);
- pci_set_bus_of_node(b);
- pci_set_bus_msi_domain(b);
- if (!parent)
- set_dev_node(b->bridge, pcibus_to_node(b));
+ list_splice_init(resources, &bridge->windows);
+ bridge->sysdata = sysdata;
+ bridge->busnr = bus;
+ bridge->ops = ops;
+ bridge->msi = msi;
- b->dev.class = &pcibus_class;
- b->dev.parent = b->bridge;
- dev_set_name(&b->dev, "%04x:%02x", pci_domain_nr(b), bus);
- error = device_register(&b->dev);
- if (error)
- goto class_dev_reg_err;
-
- pcibios_add_bus(b);
-
- /* Create legacy_io and legacy_mem files for this bus */
- pci_create_legacy_files(b);
-
- if (parent)
- dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev));
- else
- printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev));
-
- /* Add initial resources to the bus */
- resource_list_for_each_entry_safe(window, n, resources) {
- list_move_tail(&window->node, &bridge->windows);
- res = window->res;
- offset = window->offset;
- if (res->flags & IORESOURCE_BUS)
- pci_bus_insert_busn_res(b, bus, res->end);
- else
- pci_bus_add_resource(b, res, 0);
- if (offset) {
- if (resource_type(res) == IORESOURCE_IO)
- fmt = " (bus address [%#06llx-%#06llx])";
- else
- fmt = " (bus address [%#010llx-%#010llx])";
- snprintf(bus_addr, sizeof(bus_addr), fmt,
- (unsigned long long) (res->start - offset),
- (unsigned long long) (res->end - offset));
- } else
- bus_addr[0] = '\0';
- dev_info(&b->dev, "root bus resource %pR%s\n", res, bus_addr);
- }
-
- down_write(&pci_bus_sem);
- list_add_tail(&b->node, &pci_root_buses);
- up_write(&pci_bus_sem);
+ error = pci_register_host_bridge(bridge);
+ if (error < 0)
+ goto err_out;
- return b;
+ return bridge->bus;
-class_dev_reg_err:
- put_device(&bridge->dev);
- device_unregister(&bridge->dev);
err_out:
- kfree(b);
+ kfree(bridge);
return NULL;
}
+
+struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
+ struct pci_ops *ops, void *sysdata, struct list_head *resources)
+{
+ return pci_create_root_bus_msi(parent, bus, ops, sysdata, resources,
+ NULL);
+}
EXPORT_SYMBOL_GPL(pci_create_root_bus);
int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max)
@@ -2343,12 +2422,10 @@ struct pci_bus *pci_scan_root_bus_msi(struct device *parent, int bus,
break;
}
- b = pci_create_root_bus(parent, bus, ops, sysdata, resources);
+ b = pci_create_root_bus_msi(parent, bus, ops, sysdata, resources, msi);
if (!b)
return NULL;
- b->msi = msi;
-
if (!found) {
dev_info(&b->dev,
"No busn resource found for root bus, will use [bus %02x-ff]\n",
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 2408abe4ee8c..f82710a8694d 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -11,7 +11,7 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/capability.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/byteorder.h>
#include "pci.h"
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index c232729f5b1b..f754453fe754 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -1634,6 +1634,7 @@ static void quirk_pcie_mch(struct pci_dev *pdev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7520_MCH, quirk_pcie_mch);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7320_MCH, quirk_pcie_mch);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7525_MCH, quirk_pcie_mch);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_HUAWEI, 0x1610, quirk_pcie_mch);
/*
@@ -2156,7 +2157,7 @@ static void quirk_blacklist_vpd(struct pci_dev *dev)
{
if (dev->vpd) {
dev->vpd->len = 0;
- dev_warn(&dev->dev, FW_BUG "VPD access disabled\n");
+ dev_warn(&dev->dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n");
}
}
@@ -2239,6 +2240,27 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_BROADCOM,
PCI_DEVICE_ID_TIGON3_5719,
quirk_brcm_5719_limit_mrrs);
+#ifdef CONFIG_PCIE_IPROC_PLATFORM
+static void quirk_paxc_bridge(struct pci_dev *pdev)
+{
+ /* The PCI config space is shared with the PAXC root port and the first
+ * Ethernet device. So, we need to workaround this by telling the PCI
+ * code that the bridge is not an Ethernet device.
+ */
+ if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
+ pdev->class = PCI_CLASS_BRIDGE_PCI << 8;
+
+ /* MPSS is not being set properly (as it is currently 0). This is
+ * because that area of the PCI config space is hard coded to zero, and
+ * is not modifiable by firmware. Set this to 2 (e.g., 512 byte MPS)
+ * so that the MPS can be set to the real max value.
+ */
+ pdev->pcie_mpss = 2;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge);
+#endif
+
/* Originally in EDAC sources for i82875P:
* Intel tells BIOS developers to hide device 6 which
* configures the overflow device access containing
@@ -3044,7 +3066,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb);
static ktime_t fixup_debug_start(struct pci_dev *dev,
void (*fn)(struct pci_dev *dev))
{
- ktime_t calltime = ktime_set(0, 0);
+ ktime_t calltime = 0;
dev_dbg(&dev->dev, "calling %pF\n", fn);
if (initcall_debug) {
@@ -3113,32 +3135,35 @@ static void quirk_remove_d3_delay(struct pci_dev *dev)
{
dev->d3_delay = 0;
}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c00, quirk_remove_d3_delay);
+/* C600 Series devices do not need 10ms d3_delay */
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0412, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c00, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0c0c, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c31, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3a, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3d, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c2d, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c20, quirk_remove_d3_delay);
+/* Lynxpoint-H PCH devices do not need 10ms d3_delay */
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c02, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c18, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c1c, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c20, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c22, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c26, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c2d, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c31, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3a, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c3d, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c4e, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c02, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x8c22, quirk_remove_d3_delay);
/* Intel Cherrytrail devices do not need 10ms d3_delay */
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2280, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2298, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x229c, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b0, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b5, quirk_remove_d3_delay);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b7, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b8, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22d8, quirk_remove_d3_delay);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22dc, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b5, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x22b7, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2298, quirk_remove_d3_delay);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x229c, quirk_remove_d3_delay);
+
/*
- * Some devices may pass our check in pci_intx_mask_supported if
+ * Some devices may pass our check in pci_intx_mask_supported() if
* PCI_COMMAND_INTX_DISABLE works though they actually do not properly
* support this feature.
*/
@@ -3146,53 +3171,139 @@ static void quirk_broken_intx_masking(struct pci_dev *dev)
{
dev->broken_intx_masking = 1;
}
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CHELSIO, 0x0030,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(0x1814, 0x0601, /* Ralink RT2800 802.11n PCI */
- quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x0030,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(0x1814, 0x0601, /* Ralink RT2800 802.11n PCI */
+ quirk_broken_intx_masking);
+
/*
* Realtek RTL8169 PCI Gigabit Ethernet Controller (rev 10)
* Subsystem: Realtek RTL8169/8110 Family PCI Gigabit Ethernet NIC
*
* RTL8110SC - Fails under PCI device assignment using DisINTx masking.
*/
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x8169,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MELLANOX, PCI_ANY_ID,
- quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_REALTEK, 0x8169,
+ quirk_broken_intx_masking);
/*
* Intel i40e (XL710/X710) 10/20/40GbE NICs all have broken INTx masking,
* DisINTx can be set but the interrupt status bit is non-functional.
*/
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1572,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1574,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1580,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1581,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1583,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1584,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1585,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1586,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1587,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1588,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1589,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x37d0,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x37d1,
- quirk_broken_intx_masking);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x37d2,
- quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1572,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1574,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1580,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1581,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1583,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1584,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1585,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1586,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1587,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1588,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1589,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x37d0,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x37d1,
+ quirk_broken_intx_masking);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x37d2,
+ quirk_broken_intx_masking);
+
+static u16 mellanox_broken_intx_devs[] = {
+ PCI_DEVICE_ID_MELLANOX_HERMON_SDR,
+ PCI_DEVICE_ID_MELLANOX_HERMON_DDR,
+ PCI_DEVICE_ID_MELLANOX_HERMON_QDR,
+ PCI_DEVICE_ID_MELLANOX_HERMON_DDR_GEN2,
+ PCI_DEVICE_ID_MELLANOX_HERMON_QDR_GEN2,
+ PCI_DEVICE_ID_MELLANOX_HERMON_EN,
+ PCI_DEVICE_ID_MELLANOX_HERMON_EN_GEN2,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX_EN,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_T_GEN2,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_GEN2,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX_EN_5_GEN2,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX2,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX3,
+ PCI_DEVICE_ID_MELLANOX_CONNECTX3_PRO,
+};
+
+#define CONNECTX_4_CURR_MAX_MINOR 99
+#define CONNECTX_4_INTX_SUPPORT_MINOR 14
+
+/*
+ * Check ConnectX-4/LX FW version to see if it supports legacy interrupts.
+ * If so, don't mark it as broken.
+ * FW minor > 99 means older FW version format and no INTx masking support.
+ * FW minor < 14 means new FW version format and no INTx masking support.
+ */
+static void mellanox_check_broken_intx_masking(struct pci_dev *pdev)
+{
+ __be32 __iomem *fw_ver;
+ u16 fw_major;
+ u16 fw_minor;
+ u16 fw_subminor;
+ u32 fw_maj_min;
+ u32 fw_sub_min;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mellanox_broken_intx_devs); i++) {
+ if (pdev->device == mellanox_broken_intx_devs[i]) {
+ pdev->broken_intx_masking = 1;
+ return;
+ }
+ }
+
+ /* Getting here means Connect-IB cards and up. Connect-IB has no INTx
+ * support so shouldn't be checked further
+ */
+ if (pdev->device == PCI_DEVICE_ID_MELLANOX_CONNECTIB)
+ return;
+
+ if (pdev->device != PCI_DEVICE_ID_MELLANOX_CONNECTX4 &&
+ pdev->device != PCI_DEVICE_ID_MELLANOX_CONNECTX4_LX)
+ return;
+
+ /* For ConnectX-4 and ConnectX-4LX, need to check FW support */
+ if (pci_enable_device_mem(pdev)) {
+ dev_warn(&pdev->dev, "Can't enable device memory\n");
+ return;
+ }
+
+ fw_ver = ioremap(pci_resource_start(pdev, 0), 4);
+ if (!fw_ver) {
+ dev_warn(&pdev->dev, "Can't map ConnectX-4 initialization segment\n");
+ goto out;
+ }
+
+ /* Reading from resource space should be 32b aligned */
+ fw_maj_min = ioread32be(fw_ver);
+ fw_sub_min = ioread32be(fw_ver + 1);
+ fw_major = fw_maj_min & 0xffff;
+ fw_minor = fw_maj_min >> 16;
+ fw_subminor = fw_sub_min & 0xffff;
+ if (fw_minor > CONNECTX_4_CURR_MAX_MINOR ||
+ fw_minor < CONNECTX_4_INTX_SUPPORT_MINOR) {
+ dev_warn(&pdev->dev, "ConnectX-4: FW %u.%u.%u doesn't support INTx masking, disabling. Please upgrade FW to %d.14.1100 and up for INTx support\n",
+ fw_major, fw_minor, fw_subminor, pdev->device ==
+ PCI_DEVICE_ID_MELLANOX_CONNECTX4 ? 12 : 14);
+ pdev->broken_intx_masking = 1;
+ }
+
+ iounmap(fw_ver);
+
+out:
+ pci_disable_device(pdev);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX, PCI_ANY_ID,
+ mellanox_check_broken_intx_masking);
static void quirk_no_bus_reset(struct pci_dev *dev)
{
@@ -3255,6 +3366,25 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PORT_RIDGE,
quirk_thunderbolt_hotplug_msi);
+static void quirk_chelsio_extend_vpd(struct pci_dev *dev)
+{
+ pci_set_vpd_size(dev, 8192);
+}
+
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x20, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x21, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x22, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x23, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x24, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x25, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x26, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x30, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x31, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x32, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x35, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x36, quirk_chelsio_extend_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, 0x37, quirk_chelsio_extend_vpd);
+
#ifdef CONFIG_ACPI
/*
* Apple: Shutdown Cactus Ridge Thunderbolt controller.
@@ -3500,7 +3630,7 @@ static int __init pci_apply_final_quirks(void)
fs_initcall_sync(pci_apply_final_quirks);
/*
- * Followings are device-specific reset methods which can be used to
+ * Following are device-specific reset methods which can be used to
* reset a single function if other methods (e.g. FLR, PM D0->D3) are
* not available.
*/
@@ -4030,6 +4160,26 @@ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags)
}
/*
+ * These QCOM root ports do provide ACS-like features to disable peer
+ * transactions and validate bus numbers in requests, but do not provide an
+ * actual PCIe ACS capability. Hardware supports source validation but it
+ * will report the issue as Completer Abort instead of ACS Violation.
+ * Hardware doesn't support peer-to-peer and each root port is a root
+ * complex with unique segment numbers. It is not possible for one root
+ * port to pass traffic to another root port. All PCIe transactions are
+ * terminated inside the root port.
+ */
+static int pci_quirk_qcom_rp_acs(struct pci_dev *dev, u16 acs_flags)
+{
+ u16 flags = (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_SV);
+ int ret = acs_flags & ~flags ? 0 : 1;
+
+ dev_info(&dev->dev, "Using QCOM ACS Quirk (%d)\n", ret);
+
+ return ret;
+}
+
+/*
* Sunrise Point PCH root ports implement ACS, but unfortunately as shown in
* the datasheet (Intel 100 Series Chipset Family PCH Datasheet, Vol. 2,
* 12.1.46, 12.1.47)[1] this chipset uses dwords for the ACS capability and
@@ -4044,15 +4194,35 @@ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags)
*
* N.B. This doesn't fix what lspci shows.
*
+ * The 100 series chipset specification update includes this as errata #23[3].
+ *
+ * The 200 series chipset (Union Point) has the same bug according to the
+ * specification update (Intel 200 Series Chipset Family Platform Controller
+ * Hub, Specification Update, January 2017, Revision 001, Document# 335194-001,
+ * Errata 22)[4]. Per the datasheet[5], root port PCI Device IDs for this
+ * chipset include:
+ *
+ * 0xa290-0xa29f PCI Express Root port #{0-16}
+ * 0xa2e7-0xa2ee PCI Express Root port #{17-24}
+ *
* [1] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-2.html
* [2] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-1.html
+ * [3] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-spec-update.html
+ * [4] http://www.intel.com/content/www/us/en/chipsets/200-series-chipset-pch-spec-update.html
+ * [5] http://www.intel.com/content/www/us/en/chipsets/200-series-chipset-pch-datasheet-vol-1.html
*/
static bool pci_quirk_intel_spt_pch_acs_match(struct pci_dev *dev)
{
- return pci_is_pcie(dev) &&
- pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT &&
- ((dev->device & ~0xf) == 0xa110 ||
- (dev->device >= 0xa167 && dev->device <= 0xa16a));
+ if (!pci_is_pcie(dev) || pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT)
+ return false;
+
+ switch (dev->device) {
+ case 0xa110 ... 0xa11f: case 0xa167 ... 0xa16a: /* Sunrise Point */
+ case 0xa290 ... 0xa29f: case 0xa2e7 ... 0xa2ee: /* Union Point */
+ return true;
+ }
+
+ return false;
}
#define INTEL_SPT_ACS_CTRL (PCI_ACS_CAP + 4)
@@ -4165,6 +4335,9 @@ static const struct pci_dev_acs_enabled {
/* I219 */
{ PCI_VENDOR_ID_INTEL, 0x15b7, pci_quirk_mf_endpoint_acs },
{ PCI_VENDOR_ID_INTEL, 0x15b8, pci_quirk_mf_endpoint_acs },
+ /* QCOM QDF2xxx root ports */
+ { 0x17cb, 0x400, pci_quirk_qcom_rp_acs },
+ { 0x17cb, 0x401, pci_quirk_qcom_rp_acs },
/* Intel PCH root ports */
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs },
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs },
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index f9357e09e9b3..73a03d382590 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -40,7 +40,7 @@ static void pci_destroy_dev(struct pci_dev *dev)
list_del(&dev->bus_list);
up_write(&pci_bus_sem);
- pci_bridge_d3_device_removed(dev);
+ pci_bridge_d3_update(dev);
pci_free_resources(dev);
put_device(&dev->dev);
}
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c
index 06663d391b39..b6edb187d160 100644
--- a/drivers/pci/rom.c
+++ b/drivers/pci/rom.c
@@ -35,6 +35,11 @@ int pci_enable_rom(struct pci_dev *pdev)
if (res->flags & IORESOURCE_ROM_SHADOW)
return 0;
+ /*
+ * Ideally pci_update_resource() would update the ROM BAR address,
+ * and we would only set the enable bit here. But apparently some
+ * devices have buggy ROM BARs that read as zero when disabled.
+ */
pcibios_resource_to_bus(pdev->bus, &region, res);
pci_read_config_dword(pdev, pdev->rom_base_reg, &rom_addr);
rom_addr &= ~PCI_ROM_ADDRESS_MASK;
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index f30ca75b5b6c..cb389277df41 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -105,17 +105,8 @@ static struct pci_dev_resource *res_to_dev_res(struct list_head *head,
struct pci_dev_resource *dev_res;
list_for_each_entry(dev_res, head, list) {
- if (dev_res->res == res) {
- int idx = res - &dev_res->dev->resource[0];
-
- dev_printk(KERN_DEBUG, &dev_res->dev->dev,
- "res[%d]=%pR res_to_dev_res add_size %llx min_align %llx\n",
- idx, dev_res->res,
- (unsigned long long)dev_res->add_size,
- (unsigned long long)dev_res->min_align);
-
+ if (dev_res->res == res)
return dev_res;
- }
}
return NULL;
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 9526e341988b..4bc589ee78d0 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -25,21 +25,18 @@
#include <linux/slab.h>
#include "pci.h"
-
-void pci_update_resource(struct pci_dev *dev, int resno)
+static void pci_std_update_resource(struct pci_dev *dev, int resno)
{
struct pci_bus_region region;
bool disable;
u16 cmd;
u32 new, check, mask;
int reg;
- enum pci_bar_type type;
struct resource *res = dev->resource + resno;
- if (dev->is_virtfn) {
- dev_warn(&dev->dev, "can't update VF BAR%d\n", resno);
+ /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */
+ if (dev->is_virtfn)
return;
- }
/*
* Ignore resources for unimplemented BARs and unused resource slots
@@ -60,21 +57,34 @@ void pci_update_resource(struct pci_dev *dev, int resno)
return;
pcibios_resource_to_bus(dev->bus, &region, res);
+ new = region.start;
- new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
- if (res->flags & IORESOURCE_IO)
+ if (res->flags & IORESOURCE_IO) {
mask = (u32)PCI_BASE_ADDRESS_IO_MASK;
- else
+ new |= res->flags & ~PCI_BASE_ADDRESS_IO_MASK;
+ } else if (resno == PCI_ROM_RESOURCE) {
+ mask = (u32)PCI_ROM_ADDRESS_MASK;
+ } else {
mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
+ new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK;
+ }
- reg = pci_resource_bar(dev, resno, &type);
- if (!reg)
- return;
- if (type != pci_bar_unknown) {
+ if (resno < PCI_ROM_RESOURCE) {
+ reg = PCI_BASE_ADDRESS_0 + 4 * resno;
+ } else if (resno == PCI_ROM_RESOURCE) {
+
+ /*
+ * Apparently some Matrox devices have ROM BARs that read
+ * as zero when disabled, so don't update ROM BARs unless
+ * they're enabled. See https://lkml.org/lkml/2005/8/30/138.
+ */
if (!(res->flags & IORESOURCE_ROM_ENABLE))
return;
+
+ reg = dev->rom_base_reg;
new |= PCI_ROM_ADDRESS_ENABLE;
- }
+ } else
+ return;
/*
* We can't update a 64-bit BAR atomically, so when possible,
@@ -110,6 +120,16 @@ void pci_update_resource(struct pci_dev *dev, int resno)
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
+void pci_update_resource(struct pci_dev *dev, int resno)
+{
+ if (resno <= PCI_ROM_RESOURCE)
+ pci_std_update_resource(dev, resno);
+#ifdef CONFIG_PCI_IOV
+ else if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
+ pci_iov_update_resource(dev, resno);
+#endif
+}
+
int pci_claim_resource(struct pci_dev *dev, int resource)
{
struct resource *res = &dev->resource[resource];
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index 429d34c348b9..e42909524dee 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -345,7 +345,7 @@ EXPORT_SYMBOL_GPL(pci_create_slot);
void pci_destroy_slot(struct pci_slot *slot)
{
dev_dbg(&slot->bus->dev, "dev %02x, dec refcount to %d\n",
- slot->number, atomic_read(&slot->kobj.kref.refcount) - 1);
+ slot->number, kref_read(&slot->kobj.kref) - 1);
mutex_lock(&pci_slot_mutex);
kobject_put(&slot->kobj);
diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c
index b91c4da68365..9bf993e1f71e 100644
--- a/drivers/pci/syscall.c
+++ b/drivers/pci/syscall.c
@@ -10,7 +10,7 @@
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/syscalls.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "pci.h"
SYSCALL_DEFINE5(pciconfig_read, unsigned long, bus, unsigned long, dfn,
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index d6ff5e82377d..8fc2e9532575 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -1038,10 +1038,8 @@ static int pcifront_detach_devices(struct pcifront_device *pdev)
err = -ENOMEM;
goto out;
}
- err = xenbus_scanf(XBT_NIL, pdev->xdev->otherend, str, "%d",
- &state);
- if (err != 1)
- state = XenbusStateUnknown;
+ state = xenbus_read_unsigned(pdev->xdev->otherend, str,
+ XenbusStateUnknown);
if (state != XenbusStateClosing)
continue;