summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2014-06-04 12:09:56 +0200
committerThierry Reding <treding@nvidia.com>2014-06-04 12:09:56 +0200
commit896eb8484342b93c419d275979629e9e1c2044bb (patch)
tree18288d2b3d07d07c334c312017983f530a2ceaa7
parent571010715abc1a007b7f2c842fab2638a2688c20 (diff)
parent8f91a4b9cac1153fa1622530598973998e3511f3 (diff)
Merge branch 'staging/pci' into staging/master
-rw-r--r--Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt55
-rw-r--r--Documentation/driver-model/devres.txt2
-rw-r--r--arch/arm/boot/dts/tegra20-harmony.dts8
-rw-r--r--arch/arm/boot/dts/tegra20-tamonten.dtsi7
-rw-r--r--arch/arm/boot/dts/tegra20-trimslice.dts8
-rw-r--r--arch/arm/boot/dts/tegra30-beaver.dts12
-rw-r--r--arch/arm/boot/dts/tegra30-cardhu.dtsi11
-rw-r--r--arch/arm/include/asm/io.h1
-rw-r--r--arch/arm/include/asm/mach/pci.h6
-rw-r--r--arch/arm/kernel/bios32.c36
-rw-r--r--arch/arm/mach-tegra/cpuidle.c1
-rw-r--r--arch/arm/mm/ioremap.c6
-rw-r--r--drivers/pci/host/Kconfig2
-rw-r--r--drivers/pci/host/pci-mvebu.c2
-rw-r--r--drivers/pci/host/pci-tegra.c568
-rw-r--r--drivers/pci/msi.c3
-rw-r--r--drivers/pci/probe.c1
-rw-r--r--drivers/pci/quirks.c2
-rw-r--r--include/linux/ioport.h5
-rw-r--r--kernel/irq/chip.c1
-rw-r--r--kernel/resource.c73
-rw-r--r--mm/vmalloc.c1
22 files changed, 692 insertions, 119 deletions
diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
index c300391e8d3..bc0b09b0dfb 100644
--- a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
@@ -1,7 +1,10 @@
NVIDIA Tegra PCIe controller
Required properties:
-- compatible: "nvidia,tegra20-pcie" or "nvidia,tegra30-pcie"
+- compatible: Must be one of:
+ - "nvidia,tegra20-pcie"
+ - "nvidia,tegra30-pcie"
+ - "nvidia,tegra124-pcie"
- device_type: Must be "pci"
- reg: A list of physical base address and length for each set of controller
registers. Must contain an entry for each entry in the reg-names property.
@@ -14,9 +17,6 @@ Required properties:
- interrupt-names: Must include the following entries:
"intr": The Tegra interrupt that is asserted for controller interrupts
"msi": The Tegra interrupt that is asserted when an MSI is received
-- pex-clk-supply: Supply voltage for internal reference clock
-- vdd-supply: Power supply for controller (1.05V)
-- avdd-supply: Power supply for controller (1.05V) (not required for Tegra20)
- bus-range: Range of bus numbers associated with this controller
- #address-cells: Address representation for root ports (must be 3)
- cell 0 specifies the bus and device numbers of the root port:
@@ -60,6 +60,53 @@ Required properties:
- afi
- pcie_x
+Required properties on Tegra124 and later:
+- phys: Must contain an entry for each entry in phy-names.
+- phy-names: Must include the following entries:
+ - pcie
+
+Power supplies for Tegra20:
+- avdd-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
+- vdd-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
+- avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
+ supply 1.05 V.
+- avdd-plle-supply: Power supply for PLLE, which is shared with SATA. Must
+ supply 1.05 V.
+- vddio-pex-clk-supply: Power supply for PCIe clock. Must supply 3.3 V.
+
+Power supplies for Tegra30:
+- Required:
+ - avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
+ supply 1.05 V.
+ - avdd-plle-supply: Power supply for PLLE, which is shared with SATA. Must
+ supply 1.05 V.
+ - vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
+ supply 1.8 V.
+ - hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
+ Must supply 3.3 V.
+- Optional:
+ - If lanes 0 to 3 are used:
+ - avdd-pexa-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
+ - vdd-pexa-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
+ - If lanes 4 or 5 are used:
+ - avdd-pexb-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
+ - vdd-pexb-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
+
+Power supplies for Tegra124:
+- Required:
+ - avddio-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
+ - vddio-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
+ - avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
+ supply 1.05 V.
+ - hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
+ Must supply 3.3 V.
+ - hvdd-pex-plle-supply: High-voltage supply for PLLE (shared with USB3).
+ Must supply 3.3 V.
+ - vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
+ supply 2.8-3.3 V.
+ - avdd-plle-supply: Power supply for PLLE (shared with USB3). Must
+ supply 1.05 V.
+
Root ports are defined as subnodes of the PCIe controller node.
Required properties:
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index df2613c73b1..77284ed9fea 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -249,6 +249,8 @@ IIO
devm_iio_device_unregister()
IO region
+ devm_request_resource()
+ devm_release_resource()
devm_request_region()
devm_request_mem_region()
devm_release_region()
diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts
index f45aad688d9..a37279af687 100644
--- a/arch/arm/boot/dts/tegra20-harmony.dts
+++ b/arch/arm/boot/dts/tegra20-harmony.dts
@@ -562,10 +562,14 @@
};
pcie-controller@80003000 {
- pex-clk-supply = <&pci_clk_reg>;
- vdd-supply = <&pci_vdd_reg>;
status = "okay";
+ avdd-pex-supply = <&pci_vdd_reg>;
+ vdd-pex-supply = <&pci_vdd_reg>;
+ avdd-pex-pll-supply = <&pci_vdd_reg>;
+ avdd-plle-supply = <&pci_vdd_reg>;
+ vddio-pex-clk-supply = <&pci_clk_reg>;
+
pci@1,0 {
status = "okay";
};
diff --git a/arch/arm/boot/dts/tegra20-tamonten.dtsi b/arch/arm/boot/dts/tegra20-tamonten.dtsi
index a1b0d965757..a4914bba1a3 100644
--- a/arch/arm/boot/dts/tegra20-tamonten.dtsi
+++ b/arch/arm/boot/dts/tegra20-tamonten.dtsi
@@ -473,8 +473,11 @@
};
pcie-controller@80003000 {
- pex-clk-supply = <&pci_clk_reg>;
- vdd-supply = <&pci_vdd_reg>;
+ avdd-pex-supply = <&pci_vdd_reg>;
+ vdd-pex-supply = <&pci_vdd_reg>;
+ avdd-pex-pll-supply = <&pci_vdd_reg>;
+ avdd-plle-supply = <&pci_vdd_reg>;
+ vddio-pex-clk-supply = <&pci_clk_reg>;
};
usb@c5008000 {
diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts
index 216fa6d50c6..5ad87979ab1 100644
--- a/arch/arm/boot/dts/tegra20-trimslice.dts
+++ b/arch/arm/boot/dts/tegra20-trimslice.dts
@@ -318,8 +318,12 @@
pcie-controller@80003000 {
status = "okay";
- pex-clk-supply = <&pci_clk_reg>;
- vdd-supply = <&pci_vdd_reg>;
+
+ avdd-pex-supply = <&pci_vdd_reg>;
+ vdd-pex-supply = <&pci_vdd_reg>;
+ avdd-pex-pll-supply = <&pci_vdd_reg>;
+ avdd-plle-supply = <&pci_vdd_reg>;
+ vddio-pex-clk-supply = <&pci_clk_reg>;
pci@1,0 {
status = "okay";
diff --git a/arch/arm/boot/dts/tegra30-beaver.dts b/arch/arm/boot/dts/tegra30-beaver.dts
index 3189791a928..cee8f2246fd 100644
--- a/arch/arm/boot/dts/tegra30-beaver.dts
+++ b/arch/arm/boot/dts/tegra30-beaver.dts
@@ -17,9 +17,15 @@
pcie-controller@00003000 {
status = "okay";
- pex-clk-supply = <&sys_3v3_pexs_reg>;
- vdd-supply = <&ldo1_reg>;
- avdd-supply = <&ldo2_reg>;
+
+ avdd-pexa-supply = <&ldo1_reg>;
+ vdd-pexa-supply = <&ldo1_reg>;
+ avdd-pexb-supply = <&ldo1_reg>;
+ vdd-pexb-supply = <&ldo1_reg>;
+ avdd-pex-pll-supply = <&ldo1_reg>;
+ avdd-plle-supply = <&ldo1_reg>;
+ vddio-pex-ctl-supply = <&sys_3v3_reg>;
+ hvdd-pex-supply = <&sys_3v3_pexs_reg>;
pci@1,0 {
status = "okay";
diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi
index 0cf0848a82d..20637954624 100644
--- a/arch/arm/boot/dts/tegra30-cardhu.dtsi
+++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi
@@ -38,9 +38,14 @@
pcie-controller@00003000 {
status = "okay";
- pex-clk-supply = <&pex_hvdd_3v3_reg>;
- vdd-supply = <&ldo1_reg>;
- avdd-supply = <&ldo2_reg>;
+
+ /* AVDD_PEXA and VDD_PEXA inputs are grounded on Cardhu. */
+ avdd-pexb-supply = <&ldo1_reg>;
+ vdd-pexb-supply = <&ldo1_reg>;
+ avdd-pex-pll-supply = <&ldo1_reg>;
+ hvdd-pex-supply = <&pex_hvdd_3v3_reg>;
+ vddio-pex-ctl-supply = <&sys_3v3_reg>;
+ avdd-plle-supply = <&ldo2_reg>;
pci@1,0 {
nvidia,num-lanes = <4>;
diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
index 8aa4cca7450..851d80ee10b 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -180,6 +180,7 @@ static inline void __iomem *__typesafe_io(unsigned long addr)
#define PCI_IO_VIRT_BASE 0xfee00000
extern int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr);
+extern void pci_iounmap_io(unsigned int offset);
/*
* Now, pick up the machine-defined IO definitions
diff --git a/arch/arm/include/asm/mach/pci.h b/arch/arm/include/asm/mach/pci.h
index 7fc42784bec..bf4ff74afd8 100644
--- a/arch/arm/include/asm/mach/pci.h
+++ b/arch/arm/include/asm/mach/pci.h
@@ -25,6 +25,7 @@ struct hw_pci {
struct pci_ops *ops;
int nr_controllers;
void **private_data;
+ struct list_head *sys;
int (*setup)(int nr, struct pci_sys_data *);
struct pci_bus *(*scan)(int nr, struct pci_sys_data *);
void (*preinit)(void);
@@ -38,6 +39,7 @@ struct hw_pci {
resource_size_t align);
void (*add_bus)(struct pci_bus *bus);
void (*remove_bus)(struct pci_bus *bus);
+ void (*teardown)(int nr, struct pci_sys_data *);
};
/*
@@ -49,6 +51,7 @@ struct pci_sys_data {
#endif
struct list_head node;
int busnr; /* primary bus number */
+ int nr; /* controller number */
u64 mem_offset; /* bus->cpu memory mapping offset */
unsigned long io_offset; /* bus->cpu IO mapping offset */
struct pci_bus *bus; /* PCI bus */
@@ -67,6 +70,7 @@ struct pci_sys_data {
resource_size_t align);
void (*add_bus)(struct pci_bus *bus);
void (*remove_bus)(struct pci_bus *bus);
+ void (*teardown)(int nr, struct pci_sys_data *);
void *private_data; /* platform controller private data */
};
@@ -84,6 +88,8 @@ static inline void pci_common_init(struct hw_pci *hw)
pci_common_init_dev(NULL, hw);
}
+void pci_common_exit(struct list_head *head);
+
/*
* Setup early fixed I/O mapping.
*/
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
index 17a26c17f7f..570a248e073 100644
--- a/arch/arm/kernel/bios32.c
+++ b/arch/arm/kernel/bios32.c
@@ -472,11 +472,13 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw,
sys->domain = hw->domain;
#endif
sys->busnr = busnr;
+ sys->nr = nr;
sys->swizzle = hw->swizzle;
sys->map_irq = hw->map_irq;
sys->align_resource = hw->align_resource;
sys->add_bus = hw->add_bus;
sys->remove_bus = hw->remove_bus;
+ sys->teardown = hw->teardown;
INIT_LIST_HEAD(&sys->resources);
if (hw->private_data)
@@ -514,18 +516,24 @@ static void pcibios_init_hw(struct device *parent, struct hw_pci *hw,
void pci_common_init_dev(struct device *parent, struct hw_pci *hw)
{
struct pci_sys_data *sys;
- LIST_HEAD(head);
+ struct list_head *head;
+ LIST_HEAD(list);
+
+ if (hw->sys)
+ head = hw->sys;
+ else
+ head = &list;
pci_add_flags(PCI_REASSIGN_ALL_RSRC);
if (hw->preinit)
hw->preinit();
- pcibios_init_hw(parent, hw, &head);
+ pcibios_init_hw(parent, hw, head);
if (hw->postinit)
hw->postinit();
pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq);
- list_for_each_entry(sys, &head, node) {
+ list_for_each_entry(sys, head, node) {
struct pci_bus *bus = sys->bus;
if (!pci_has_flag(PCI_PROBE_ONLY)) {
@@ -546,7 +554,7 @@ void pci_common_init_dev(struct device *parent, struct hw_pci *hw)
pci_bus_add_devices(bus);
}
- list_for_each_entry(sys, &head, node) {
+ list_for_each_entry(sys, head, node) {
struct pci_bus *bus = sys->bus;
/* Configure PCI Express settings */
@@ -558,6 +566,26 @@ void pci_common_init_dev(struct device *parent, struct hw_pci *hw)
}
}
}
+EXPORT_SYMBOL(pci_common_init_dev);
+
+void pci_common_exit(struct list_head *head)
+{
+ struct pci_sys_data *sys, *tmp;
+
+ list_for_each_entry_safe(sys, tmp, head, node) {
+ pci_stop_root_bus(sys->bus);
+ pci_remove_root_bus(sys->bus);
+ list_del(&sys->node);
+
+ release_resource(&sys->io_res);
+
+ if (sys->teardown)
+ sys->teardown(sys->nr, sys);
+
+ kfree(sys);
+ }
+}
+EXPORT_SYMBOL(pci_common_exit);
#ifndef CONFIG_PCI_HOST_ITE8152
void pcibios_set_master(struct pci_dev *dev)
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
index 7bc5d8d667f..3f76825df02 100644
--- a/arch/arm/mach-tegra/cpuidle.c
+++ b/arch/arm/mach-tegra/cpuidle.c
@@ -56,3 +56,4 @@ void tegra_cpuidle_pcie_irqs_in_use(void)
break;
}
}
+EXPORT_SYMBOL(tegra_cpuidle_pcie_irqs_in_use);
diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c
index f9c32ba7354..b7c7a34e2e6 100644
--- a/arch/arm/mm/ioremap.c
+++ b/arch/arm/mm/ioremap.c
@@ -448,4 +448,10 @@ int pci_ioremap_io(unsigned int offset, phys_addr_t phys_addr)
__pgprot(get_mem_type(MT_DEVICE)->prot_pte));
}
EXPORT_SYMBOL_GPL(pci_ioremap_io);
+
+void pci_iounmap_io(unsigned int offset)
+{
+ unmap_kernel_range(PCI_IO_VIRT_BASE + offset, SZ_64K);
+}
+EXPORT_SYMBOL_GPL(pci_iounmap_io);
#endif
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 21df477be0c..f214f8074e3 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -22,7 +22,7 @@ config PCI_IMX6
select PCIE_DW
config PCI_TEGRA
- bool "NVIDIA Tegra PCIe controller"
+ tristate "NVIDIA Tegra PCIe controller"
depends on ARCH_TEGRA
config PCI_RCAR_GEN2
diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c
index e384e253459..500b1dd63b2 100644
--- a/drivers/pci/host/pci-mvebu.c
+++ b/drivers/pci/host/pci-mvebu.c
@@ -1099,4 +1099,4 @@ module_platform_driver(mvebu_pcie_driver);
MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
MODULE_DESCRIPTION("Marvell EBU PCIe driver");
-MODULE_LICENSE("GPLv2");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index 083cf37ca04..35c05ca3e39 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -25,6 +25,7 @@
*/
#include <linux/clk.h>
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/interrupt.h>
@@ -37,6 +38,7 @@
#include <linux/of_pci.h>
#include <linux/of_platform.h>
#include <linux/pci.h>
+#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/sizes.h>
@@ -113,13 +115,20 @@
#define AFI_INTR_CODE 0xb8
#define AFI_INTR_CODE_MASK 0xf
-#define AFI_INTR_AXI_SLAVE_ERROR 1
-#define AFI_INTR_AXI_DECODE_ERROR 2
+#define AFI_INTR_INI_SLAVE_ERROR 1
+#define AFI_INTR_INI_DECODE_ERROR 2
#define AFI_INTR_TARGET_ABORT 3
#define AFI_INTR_MASTER_ABORT 4
#define AFI_INTR_INVALID_WRITE 5
#define AFI_INTR_LEGACY 6
#define AFI_INTR_FPCI_DECODE_ERROR 7
+#define AFI_INTR_AXI_DECODE_ERROR 8
+#define AFI_INTR_FPCI_TIMEOUT 9
+#define AFI_INTR_PE_PRSNT_SENSE 10
+#define AFI_INTR_PE_CLKREQ_SENSE 11
+#define AFI_INTR_CLKCLAMP_SENSE 12
+#define AFI_INTR_RDY4PD_SENSE 13
+#define AFI_INTR_P2P_ERROR 14
#define AFI_INTR_SIGNATURE 0xbc
#define AFI_UPPER_FPCI_ADDRESS 0xc0
@@ -150,8 +159,10 @@
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1 (0x0 << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1 (0x1 << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20)
#define AFI_FUSE 0x104
@@ -163,12 +174,21 @@
#define AFI_PEX_CTRL_RST (1 << 0)
#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1)
#define AFI_PEX_CTRL_REFCLK_EN (1 << 3)
+#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4)
+
+#define AFI_PLLE_CONTROL 0x160
+#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9)
+#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1)
#define AFI_PEXBIAS_CTRL_0 0x168
#define RP_VEND_XP 0x00000F00
#define RP_VEND_XP_DL_UP (1 << 30)
+#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)
+
#define RP_LINK_CONTROL_STATUS 0x00000090
#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000
#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000
@@ -195,6 +215,7 @@
#define PADS_REFCLK_CFG0 0x000000C8
#define PADS_REFCLK_CFG1 0x000000CC
+#define PADS_REFCLK_BIAS 0x000000D0
/*
* Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit
@@ -233,8 +254,8 @@ struct tegra_pcie_soc_data {
bool has_pex_clkreq_en;
bool has_pex_bias_ctrl;
bool has_intr_prsnt_sense;
- bool has_avdd_supply;
bool has_cml_clk;
+ bool has_gen2;
};
static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip)
@@ -250,8 +271,10 @@ struct tegra_pcie {
int irq;
struct list_head buses;
+ struct list_head sys;
struct resource *cs;
+ struct resource all;
struct resource io;
struct resource mem;
struct resource prefetch;
@@ -266,17 +289,19 @@ struct tegra_pcie {
struct reset_control *afi_rst;
struct reset_control *pcie_xrst;
+ struct phy *phy;
+
struct tegra_msi msi;
struct list_head ports;
unsigned int num_ports;
u32 xbar_config;
- struct regulator *pex_clk_supply;
- struct regulator *vdd_supply;
- struct regulator *avdd_supply;
+ struct regulator_bulk_data *supplies;
+ unsigned int num_supplies;
const struct tegra_pcie_soc_data *soc_data;
+ struct dentry *debugfs;
};
struct tegra_pcie_port {
@@ -381,7 +406,7 @@ static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
for (i = 0; i < 16; i++) {
unsigned long virt = (unsigned long)bus->area->addr +
i * SZ_64K;
- phys_addr_t phys = cs + i * SZ_1M + busnr * SZ_64K;
+ phys_addr_t phys = cs + i * SZ_16M + busnr * SZ_64K;
err = ioremap_page_range(virt, virt + SZ_64K, phys, prot);
if (err < 0) {
@@ -560,6 +585,8 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
if (soc->has_pex_clkreq_en)
value |= AFI_PEX_CTRL_CLKREQ_EN;
+ value |= AFI_PEX_CTRL_OVERRIDE_EN;
+
afi_writel(port->pcie, value, ctrl);
tegra_pcie_port_reset(port);
@@ -567,6 +594,7 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
{
+ const struct tegra_pcie_soc_data *soc = port->pcie->soc_data;
unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
unsigned long value;
@@ -577,6 +605,10 @@ static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
/* disable reference clock */
value = afi_readl(port->pcie, ctrl);
+
+ if (soc->has_pex_clkreq_en)
+ value &= ~AFI_PEX_CTRL_CLKREQ_EN;
+
value &= ~AFI_PEX_CTRL_REFCLK_EN;
afi_writel(port->pcie, value, ctrl);
}
@@ -625,6 +657,15 @@ 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)
{
struct tegra_pcie *pcie = sys_to_pcie(sys);
+ int err;
+
+ err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem);
+ if (err < 0)
+ return err;
+
+ err = devm_request_resource(pcie->dev, &pcie->all, &pcie->prefetch);
+ if (err)
+ return err;
pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
pci_add_resource_offset(&sys->resources, &pcie->prefetch,
@@ -674,6 +715,11 @@ static struct pci_bus *tegra_pcie_scan_bus(int nr, struct pci_sys_data *sys)
return bus;
}
+static void tegra_pcie_teardown(int nr, struct pci_sys_data *sys)
+{
+ pci_iounmap_io(nr * SZ_64K);
+}
+
static irqreturn_t tegra_pcie_isr(int irq, void *arg)
{
const char *err_msg[] = {
@@ -683,9 +729,15 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
"Target abort",
"Master abort",
"Invalid write",
+ "Legacy interrupt",
"Response decoding error",
"AXI response decoding error",
"Transaction timeout",
+ "Slot present pin change",
+ "Slot clock request change",
+ "TMS clock ramp change",
+ "TMS ready for power down",
+ "Peer2Peer error",
};
struct tegra_pcie *pcie = arg;
u32 code, signature;
@@ -791,30 +843,27 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
afi_writel(pcie, 0, AFI_MSI_BAR_SZ);
}
-static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
+static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout)
{
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
- struct tegra_pcie_port *port;
- unsigned int timeout;
- unsigned long value;
-
- /* power down PCIe slot clock bias pad */
- if (soc->has_pex_bias_ctrl)
- afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
+ u32 value;
- /* configure mode and disable all ports */
- value = afi_readl(pcie, AFI_PCIE_CONFIG);
- value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
- value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config;
+ timeout = jiffies + msecs_to_jiffies(timeout);
- list_for_each_entry(port, &pcie->ports, list)
- value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index);
+ while (time_before(jiffies, timeout)) {
+ value = pads_readl(pcie, soc->pads_pll_ctl);
+ if (value & PADS_PLL_CTL_LOCKDET)
+ return 0;
+ }
- afi_writel(pcie, value, AFI_PCIE_CONFIG);
+ return -ETIMEDOUT;
+}
- value = afi_readl(pcie, AFI_FUSE);
- value |= AFI_FUSE_PCIE_T0_GEN2_DIS;
- afi_writel(pcie, value, AFI_FUSE);
+static int tegra_pcie_phy_enable(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ u32 value;
+ int err;
/* initialize internal PHY, enable up to 16 PCIE lanes */
pads_writel(pcie, 0x0, PADS_CTL_SEL);
@@ -833,6 +882,13 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel;
pads_writel(pcie, value, soc->pads_pll_ctl);
+ /* reset PLL */
+ value = pads_readl(pcie, soc->pads_pll_ctl);
+ value &= ~PADS_PLL_CTL_RST_B4SM;
+ pads_writel(pcie, value, soc->pads_pll_ctl);
+
+ usleep_range(20, 100);
+
/* take PLL out of reset */
value = pads_readl(pcie, soc->pads_pll_ctl);
value |= PADS_PLL_CTL_RST_B4SM;
@@ -845,15 +901,11 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
pads_writel(pcie, PADS_REFCLK_CFG_VALUE, PADS_REFCLK_CFG1);
/* wait for the PLL to lock */
- timeout = 300;
- do {
- value = pads_readl(pcie, soc->pads_pll_ctl);
- usleep_range(1000, 2000);
- if (--timeout == 0) {
- pr_err("Tegra PCIe error: timeout waiting for PLL\n");
- return -EBUSY;
- }
- } while (!(value & PADS_PLL_CTL_LOCKDET));
+ err = tegra_pcie_pll_wait(pcie, 500);
+ if (err < 0) {
+ dev_err(pcie->dev, "PLL failed to lock: %d\n", err);
+ return err;
+ }
/* turn off IDDQ override */
value = pads_readl(pcie, PADS_CTL);
@@ -865,6 +917,58 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L;
pads_writel(pcie, value, PADS_CTL);
+ return 0;
+}
+
+static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
+{
+ const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+ struct tegra_pcie_port *port;
+ unsigned long value;
+ int err;
+
+ /* enable PLL power down */
+ if (pcie->phy) {
+ value = afi_readl(pcie, AFI_PLLE_CONTROL);
+ value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL;
+ value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN;
+ afi_writel(pcie, value, AFI_PLLE_CONTROL);
+ }
+
+ /* power down PCIe slot clock bias pad */
+ if (soc->has_pex_bias_ctrl)
+ afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
+
+ /* configure mode and disable all ports */
+ value = afi_readl(pcie, AFI_PCIE_CONFIG);
+ value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
+ value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config;
+
+ list_for_each_entry(port, &pcie->ports, list)
+ value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index);
+
+ afi_writel(pcie, value, AFI_PCIE_CONFIG);
+
+ if (soc->has_gen2) {
+ value = afi_readl(pcie, AFI_FUSE);
+ value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS;
+ afi_writel(pcie, value, AFI_FUSE);
+ } else {
+ value = afi_readl(pcie, AFI_FUSE);
+ value |= AFI_FUSE_PCIE_T0_GEN2_DIS;
+ afi_writel(pcie, value, AFI_FUSE);
+ }
+
+ if (!pcie->phy)
+ err = tegra_pcie_phy_enable(pcie);
+ else
+ err = phy_power_on(pcie->phy);
+
+ if (err < 0) {
+ dev_err(pcie->dev, "failed to power on PHY: %d\n", err);
+ return err;
+ }
+
/* take the PCIe interface module out of reset */
reset_control_deassert(pcie->pcie_xrst);
@@ -894,34 +998,23 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
static void tegra_pcie_power_off(struct tegra_pcie *pcie)
{
- const struct tegra_pcie_soc_data *soc = pcie->soc_data;
int err;
/* TODO: disable and unprepare clocks? */
+ err = phy_power_off(pcie->phy);
+ if (err < 0)
+ dev_warn(pcie->dev, "failed to power off PHY: %d\n", err);
+
reset_control_assert(pcie->pcie_xrst);
reset_control_assert(pcie->afi_rst);
reset_control_assert(pcie->pex_rst);
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
- if (soc->has_avdd_supply) {
- err = regulator_disable(pcie->avdd_supply);
- if (err < 0)
- dev_warn(pcie->dev,
- "failed to disable AVDD regulator: %d\n",
- err);
- }
-
- err = regulator_disable(pcie->pex_clk_supply);
+ err = regulator_bulk_disable(pcie->num_supplies, pcie->supplies);
if (err < 0)
- dev_warn(pcie->dev, "failed to disable pex-clk regulator: %d\n",
- err);
-
- err = regulator_disable(pcie->vdd_supply);
- if (err < 0)
- dev_warn(pcie->dev, "failed to disable VDD regulator: %d\n",
- err);
+ dev_warn(pcie->dev, "failed to disable regulators: %d\n", err);
}
static int tegra_pcie_power_on(struct tegra_pcie *pcie)
@@ -936,28 +1029,9 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
/* enable regulators */
- err = regulator_enable(pcie->vdd_supply);
- if (err < 0) {
- dev_err(pcie->dev, "failed to enable VDD regulator: %d\n", err);
- return err;
- }
-
- err = regulator_enable(pcie->pex_clk_supply);
- if (err < 0) {
- dev_err(pcie->dev, "failed to enable pex-clk regulator: %d\n",
- err);
- return err;
- }
-
- if (soc->has_avdd_supply) {
- err = regulator_enable(pcie->avdd_supply);
- if (err < 0) {
- dev_err(pcie->dev,
- "failed to enable AVDD regulator: %d\n",
- err);
- return err;
- }
- }
+ err = regulator_bulk_enable(pcie->num_supplies, pcie->supplies);
+ if (err < 0)
+ dev_err(pcie->dev, "failed to enable regulators: %d\n", err);
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE,
pcie->pex_clk,
@@ -1053,6 +1127,19 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
return err;
}
+ pcie->phy = devm_phy_optional_get(pcie->dev, "pcie");
+ if (IS_ERR(pcie->phy)) {
+ err = PTR_ERR(pcie->phy);
+ dev_err(&pdev->dev, "failed to get PHY: %d\n", err);
+ return err;
+ }
+
+ err = phy_init(pcie->phy);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to initialize PHY: %d\n", err);
+ return err;
+ }
+
err = tegra_pcie_power_on(pcie);
if (err) {
dev_err(&pdev->dev, "failed to power up: %d\n", err);
@@ -1111,10 +1198,17 @@ poweroff:
static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
{
+ int err;
+
if (pcie->irq > 0)
free_irq(pcie->irq, pcie);
tegra_pcie_power_off(pcie);
+
+ err = phy_exit(pcie->phy);
+ if (err < 0)
+ dev_err(pcie->dev, "failed to teardown PHY: %d\n", err);
+
return 0;
}
@@ -1360,7 +1454,19 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
{
struct device_node *np = pcie->dev->of_node;
- if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
+ if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
+ switch (lanes) {
+ case 0x0000104:
+ dev_info(pcie->dev, "4x1, 1x1 configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1;
+ return 0;
+
+ case 0x0000102:
+ dev_info(pcie->dev, "2x1, 1x1 configuration\n");
+ *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1;
+ return 0;
+ }
+ } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
switch (lanes) {
case 0x00000204:
dev_info(pcie->dev, "4x1, 2x1 configuration\n");
@@ -1394,56 +1500,143 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
return -EINVAL;
}
+/*
+ * Obtains the list of regulators required for a particular generation of the
+ * IP block.
+ *
+ * This would've been nice to do simply by providing static tables for use
+ * with the regulator_bulk_*() API, but unfortunately Tegra30 is a bit quirky
+ * in that it has two pairs or AVDD_PEX and VDD_PEX supplies (PEXA and PEXB)
+ * and either seems to be optional depending on which ports are being used.
+ */
+static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
+{
+ struct device_node *np = pcie->dev->of_node;
+ unsigned int i = 0;
+
+ if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
+ pcie->num_supplies = 7;
+
+ pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+ sizeof(*pcie->supplies),
+ GFP_KERNEL);
+ if (!pcie->supplies)
+ return -ENOMEM;
+
+ pcie->supplies[i++].supply = "avddio-pex";
+ pcie->supplies[i++].supply = "vddio-pex";
+ pcie->supplies[i++].supply = "avdd-pex-pll";
+ pcie->supplies[i++].supply = "hvdd-pex";
+ pcie->supplies[i++].supply = "hvdd-pex-plle";
+ pcie->supplies[i++].supply = "vddio-pex-ctl";
+ pcie->supplies[i++].supply = "avdd-plle";
+ } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
+ bool need_pexa = false, need_pexb = false;
+
+ /* VDD_PEXA and AVDD_PEXA supply lanes 0 to 3 */
+ if (lane_mask & 0x0f)
+ need_pexa = true;
+
+ /* VDD_PEXB and AVDD_PEXB supply lanes 4 to 5 */
+ if (lane_mask & 0x30)
+ need_pexb = true;
+
+ pcie->num_supplies = 4 + (need_pexa ? 2 : 0) +
+ (need_pexb ? 2 : 0);
+
+ pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+ sizeof(*pcie->supplies),
+ GFP_KERNEL);
+ if (!pcie->supplies)
+ return -ENOMEM;
+
+ pcie->supplies[i++].supply = "avdd-pex-pll";
+ pcie->supplies[i++].supply = "hvdd-pex";
+ pcie->supplies[i++].supply = "vddio-pex-ctl";
+ pcie->supplies[i++].supply = "avdd-plle";
+
+ if (need_pexa) {
+ pcie->supplies[i++].supply = "avdd-pexa";
+ pcie->supplies[i++].supply = "vdd-pexa";
+ }
+
+ if (need_pexb) {
+ pcie->supplies[i++].supply = "avdd-pexb";
+ pcie->supplies[i++].supply = "vdd-pexb";
+ }
+ } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) {
+ pcie->num_supplies = 5;
+
+ pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+ sizeof(*pcie->supplies),
+ GFP_KERNEL);
+ if (!pcie->supplies)
+ return -ENOMEM;
+
+ pcie->supplies[0].supply = "avdd-pex";
+ pcie->supplies[1].supply = "vdd-pex";
+ pcie->supplies[2].supply = "avdd-pex-pll";
+ pcie->supplies[3].supply = "avdd-plle";
+ pcie->supplies[4].supply = "vddio-pex-clk";
+ }
+
+ return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies,
+ pcie->supplies);
+}
+
static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
{
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
struct device_node *np = pcie->dev->of_node, *port;
struct of_pci_range_parser parser;
struct of_pci_range range;
+ u32 lanes = 0, mask = 0;
+ unsigned int lane = 0;
struct resource res;
- u32 lanes = 0;
int err;
+ memset(&pcie->all, 0, sizeof(pcie->all));
+ pcie->all.flags = IORESOURCE_MEM;
+ pcie->all.name = np->full_name;
+ pcie->all.start = ~0;
+ pcie->all.end = 0;
+
if (of_pci_range_parser_init(&parser, np)) {
dev_err(pcie->dev, "missing \"ranges\" property\n");
return -EINVAL;
}
- pcie->vdd_supply = devm_regulator_get(pcie->dev, "vdd");
- if (IS_ERR(pcie->vdd_supply))
- return PTR_ERR(pcie->vdd_supply);
-
- pcie->pex_clk_supply = devm_regulator_get(pcie->dev, "pex-clk");
- if (IS_ERR(pcie->pex_clk_supply))
- return PTR_ERR(pcie->pex_clk_supply);
-
- if (soc->has_avdd_supply) {
- pcie->avdd_supply = devm_regulator_get(pcie->dev, "avdd");
- if (IS_ERR(pcie->avdd_supply))
- return PTR_ERR(pcie->avdd_supply);
- }
-
for_each_of_pci_range(&parser, &range) {
of_pci_range_to_resource(&range, np, &res);
switch (res.flags & IORESOURCE_TYPE_BITS) {
case IORESOURCE_IO:
memcpy(&pcie->io, &res, sizeof(res));
- pcie->io.name = "I/O";
+ pcie->io.name = np->full_name;
break;
case IORESOURCE_MEM:
if (res.flags & IORESOURCE_PREFETCH) {
memcpy(&pcie->prefetch, &res, sizeof(res));
- pcie->prefetch.name = "PREFETCH";
+ pcie->prefetch.name = "prefetchable";
} else {
memcpy(&pcie->mem, &res, sizeof(res));
- pcie->mem.name = "MEM";
+ pcie->mem.name = "non-prefetchable";
}
break;
}
+
+ if (res.start <= pcie->all.start)
+ pcie->all.start = res.start;
+
+ if (res.end >= pcie->all.end)
+ pcie->all.end = res.end;
}
+ err = devm_request_resource(pcie->dev, &iomem_resource, &pcie->all);
+ if (err < 0)
+ return err;
+
err = of_pci_parse_bus_range(np, &pcie->busn);
if (err < 0) {
dev_err(pcie->dev, "failed to parse ranges property: %d\n",
@@ -1490,8 +1683,13 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
lanes |= value << (index << 3);
- if (!of_device_is_available(port))
+ if (!of_device_is_available(port)) {
+ lane += value;
continue;
+ }
+
+ mask |= ((1 << value) - 1) << lane;
+ lane += value;
rp = devm_kzalloc(pcie->dev, sizeof(*rp), GFP_KERNEL);
if (!rp)
@@ -1522,6 +1720,10 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
return err;
}
+ err = tegra_pcie_get_regulators(pcie, mask);
+ if (err < 0)
+ return err;
+
return 0;
}
@@ -1536,6 +1738,12 @@ static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port)
unsigned int retries = 3;
unsigned long value;
+ /* override presence detection */
+ value = readl(port->base + RP_PRIV_MISC);
+ value &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT;
+ value |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT;
+ writel(value, port->base + RP_PRIV_MISC);
+
do {
unsigned int timeout = TEGRA_PCIE_LINKUP_TIMEOUT;
@@ -1600,7 +1808,9 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
hw.map_irq = tegra_pcie_map_irq;
hw.add_bus = tegra_pcie_add_bus;
hw.scan = tegra_pcie_scan_bus;
+ hw.teardown = tegra_pcie_teardown;
hw.ops = &tegra_pcie_ops;
+ hw.sys = &pcie->sys;
pci_common_init_dev(pcie->dev, &hw);
@@ -1615,8 +1825,8 @@ static const struct tegra_pcie_soc_data tegra20_pcie_data = {
.has_pex_clkreq_en = false,
.has_pex_bias_ctrl = false,
.has_intr_prsnt_sense = false,
- .has_avdd_supply = false,
.has_cml_clk = false,
+ .has_gen2 = false,
};
static const struct tegra_pcie_soc_data tegra30_pcie_data = {
@@ -1627,17 +1837,144 @@ static const struct tegra_pcie_soc_data tegra30_pcie_data = {
.has_pex_clkreq_en = true,
.has_pex_bias_ctrl = true,
.has_intr_prsnt_sense = true,
- .has_avdd_supply = true,
.has_cml_clk = true,
+ .has_gen2 = false,
+};
+
+static const struct tegra_pcie_soc_data tegra124_pcie_data = {
+ .num_ports = 2,
+ .msi_base_shift = 8,
+ .pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
+ .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
+ .has_pex_clkreq_en = true,
+ .has_pex_bias_ctrl = true,
+ .has_intr_prsnt_sense = true,
+ .has_cml_clk = true,
+ .has_gen2 = true,
};
static const struct of_device_id tegra_pcie_of_match[] = {
+ { .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie_data },
{ .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie_data },
{ .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie_data },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_pcie_of_match);
+static void *tegra_pcie_ports_seq_start(struct seq_file *s, loff_t *pos)
+{
+ struct tegra_pcie *pcie = s->private;
+
+ if (list_empty(&pcie->ports))
+ return NULL;
+
+ seq_printf(s, "Index Status\n");
+
+ return seq_list_start(&pcie->ports, *pos);
+}
+
+static void *tegra_pcie_ports_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct tegra_pcie *pcie = s->private;
+
+ return seq_list_next(v, &pcie->ports, pos);
+}
+
+static void tegra_pcie_ports_seq_stop(struct seq_file *s, void *v)
+{
+}
+
+static int tegra_pcie_ports_seq_show(struct seq_file *s, void *v)
+{
+ bool up = false, active = false;
+ struct tegra_pcie_port *port;
+ unsigned int value;
+
+ port = list_entry(v, struct tegra_pcie_port, list);
+
+ value = readl(port->base + RP_VEND_XP);
+
+ if (value & RP_VEND_XP_DL_UP)
+ up = true;
+
+ value = readl(port->base + RP_LINK_CONTROL_STATUS);
+
+ if (value & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE)
+ active = true;
+
+ seq_printf(s, "%2u ", port->index);
+
+ if (up)
+ seq_printf(s, "up");
+
+ if (active) {
+ if (up)
+ seq_printf(s, ", ");
+
+ seq_printf(s, "active");
+ }
+
+ seq_printf(s, "\n");
+ return 0;
+}
+
+static const struct seq_operations tegra_pcie_ports_seq_ops = {
+ .start = tegra_pcie_ports_seq_start,
+ .next = tegra_pcie_ports_seq_next,
+ .stop = tegra_pcie_ports_seq_stop,
+ .show = tegra_pcie_ports_seq_show,
+};
+
+static int tegra_pcie_ports_open(struct inode *inode, struct file *file)
+{
+ struct tegra_pcie *pcie = inode->i_private;
+ struct seq_file *s;
+ int err;
+
+ err = seq_open(file, &tegra_pcie_ports_seq_ops);
+ if (err)
+ return err;
+
+ s = file->private_data;
+ s->private = pcie;
+
+ return 0;
+}
+
+static const struct file_operations tegra_pcie_ports_ops = {
+ .owner = THIS_MODULE,
+ .open = tegra_pcie_ports_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie)
+{
+ struct dentry *file;
+
+ pcie->debugfs = debugfs_create_dir("pcie", NULL);
+ if (!pcie->debugfs)
+ return -ENOMEM;
+
+ file = debugfs_create_file("ports", S_IFREG | S_IRUGO, pcie->debugfs,
+ pcie, &tegra_pcie_ports_ops);
+ if (!file)
+ goto remove;
+
+ return 0;
+
+remove:
+ debugfs_remove_recursive(pcie->debugfs);
+ pcie->debugfs = NULL;
+ return -ENOMEM;
+}
+
+static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie)
+{
+ debugfs_remove_recursive(pcie->debugfs);
+}
+
static int tegra_pcie_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
@@ -1654,6 +1991,7 @@ static int tegra_pcie_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&pcie->buses);
INIT_LIST_HEAD(&pcie->ports);
+ INIT_LIST_HEAD(&pcie->sys);
pcie->soc_data = match->data;
pcie->dev = &pdev->dev;
@@ -1692,6 +2030,13 @@ static int tegra_pcie_probe(struct platform_device *pdev)
goto disable_msi;
}
+ if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+ err = tegra_pcie_debugfs_init(pcie);
+ if (err < 0)
+ dev_err(&pdev->dev, "failed to setup debugfs: %d\n",
+ err);
+ }
+
platform_set_drvdata(pdev, pcie);
return 0;
@@ -1703,17 +2048,46 @@ put_resources:
return err;
}
+static int tegra_pcie_remove(struct platform_device *pdev)
+{
+ struct tegra_pcie *pcie = platform_get_drvdata(pdev);
+ struct tegra_pcie_bus *bus, *tmp;
+ int err;
+
+ if (IS_ENABLED(CONFIG_DEBUG_FS))
+ tegra_pcie_debugfs_exit(pcie);
+
+ pci_common_exit(&pcie->sys);
+
+ list_for_each_entry_safe(bus, tmp, &pcie->buses, list) {
+ vunmap(bus->area->addr);
+ kfree(bus);
+ }
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ err = tegra_pcie_disable_msi(pcie);
+ if (err < 0)
+ return err;
+ }
+
+ err = tegra_pcie_put_resources(pcie);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
static struct platform_driver tegra_pcie_driver = {
.driver = {
.name = "tegra-pcie",
.owner = THIS_MODULE,
.of_match_table = tegra_pcie_of_match,
- .suppress_bind_attrs = true,
},
.probe = tegra_pcie_probe,
+ .remove = tegra_pcie_remove,
};
module_platform_driver(tegra_pcie_driver);
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra PCIe driver");
-MODULE_LICENSE("GPLv2");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 27a7e67ddfe..a5d38962754 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -255,11 +255,13 @@ void mask_msi_irq(struct irq_data *data)
{
msi_set_mask_bit(data, 1);
}
+EXPORT_SYMBOL(mask_msi_irq);
void unmask_msi_irq(struct irq_data *data)
{
msi_set_mask_bit(data, 0);
}
+EXPORT_SYMBOL(unmask_msi_irq);
void default_restore_msi_irqs(struct pci_dev *dev)
{
@@ -367,6 +369,7 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg)
__write_msi_msg(entry, msg);
}
+EXPORT_SYMBOL(write_msi_msg);
static void free_msi_irqs(struct pci_dev *dev)
{
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 2bbf5221afb..70b3f143a62 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1856,6 +1856,7 @@ err_out:
kfree(b);
return NULL;
}
+EXPORT_SYMBOL(pci_create_root_bus);
int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max)
{
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index d3f29dd2987..6644fc5ccd3 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -2322,6 +2322,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA,
PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V4,
nvbridge_check_legacy_irq_routing);
+#ifndef CONFIG_ARCH_TEGRA
static int ht_check_msi_mapping(struct pci_dev *dev)
{
int pos, ttl = 48;
@@ -2533,6 +2534,7 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, nv_msi_ht_cap_q
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk_all);
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk_all);
+#endif
static void quirk_msi_intx_disable_bug(struct pci_dev *dev)
{
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 5e3a906cc08..21132ea7789 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -215,6 +215,11 @@ static inline int __deprecated check_region(resource_size_t s,
/* Wrappers for managed devices */
struct device;
+
+extern int devm_request_resource(struct device *dev, struct resource *root,
+ struct resource *new);
+extern void devm_release_resource(struct device *dev, struct resource *new);
+
#define devm_request_region(dev,start,n,name) \
__devm_request_region(dev, &ioport_resource, (start), (n), (name))
#define devm_request_mem_region(dev,start,n,name) \
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index a2b28a2fd7b..f0eff578f3c 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -122,6 +122,7 @@ int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry)
{
return irq_set_msi_desc_off(irq, 0, entry);
}
+EXPORT_SYMBOL(irq_set_msi_desc);
/**
* irq_set_chip_data - set irq chip data for an irq
diff --git a/kernel/resource.c b/kernel/resource.c
index 3c2237ac32d..496e1e3547f 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -1165,6 +1165,79 @@ int release_mem_region_adjustable(struct resource *parent,
/*
* Managed region resource
*/
+static void devm_resource_release(struct device *dev, void *ptr)
+{
+ struct resource **r = ptr;
+
+ release_resource(*r);
+}
+
+/**
+ * devm_request_resource() - request and reserve an I/O or memory resource
+ * @dev: device for which to request the resource
+ * @root: root of the resource tree from which to request the resource
+ * @new: descriptor of the resource to request
+ *
+ * This is a device-managed version of request_resource(). There is usually
+ * no need to release resources requested by this function explicitly since
+ * that will be taken care of when the device is unbound from its driver.
+ * If for some reason the resource needs to be released explicitly, because
+ * of ordering issues for example, drivers must call devm_release_resource()
+ * rather than the regular release_resource().
+ *
+ * When a conflict is detected between any existing resources and the newly
+ * requested resource, an error message will be printed.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int devm_request_resource(struct device *dev, struct resource *root,
+ struct resource *new)
+{
+ struct resource *conflict, **ptr;
+
+ ptr = devres_alloc(devm_resource_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ *ptr = new;
+
+ conflict = request_resource_conflict(root, new);
+ if (!conflict) {
+ devres_add(dev, ptr);
+ return 0;
+ }
+
+ dev_err(dev, "resource collision: %pR conflicts with %s %pR\n", new,
+ conflict->name, conflict);
+ devres_free(ptr);
+ return -EBUSY;
+}
+EXPORT_SYMBOL(devm_request_resource);
+
+static int devm_resource_match(struct device *dev, void *res, void *data)
+{
+ struct resource **ptr = res;
+
+ if (WARN_ON(!ptr || !*ptr))
+ return 0;
+
+ return *ptr == data;
+}
+
+/**
+ * devm_release_resource() - release a previously requested resource
+ * @dev: device for which to release the resource
+ * @new: descriptor of the resource to release
+ *
+ * Releases a resource previously requested using devm_request_resource().
+ */
+void devm_release_resource(struct device *dev, struct resource *new)
+{
+ WARN_ON(devres_release(dev, devm_resource_release, devm_resource_match,
+ new));
+}
+EXPORT_SYMBOL(devm_release_resource);
+
struct region_devres {
struct resource *parent;
resource_size_t start;
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index f64632b6719..0b65a41e065 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -1376,6 +1376,7 @@ struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)
NUMA_NO_NODE, GFP_KERNEL,
__builtin_return_address(0));
}
+EXPORT_SYMBOL_GPL(get_vm_area);
struct vm_struct *get_vm_area_caller(unsigned long size, unsigned long flags,
const void *caller)