diff options
author | Thierry Reding <treding@nvidia.com> | 2014-06-04 12:09:56 +0200 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2014-06-04 12:09:56 +0200 |
commit | 896eb8484342b93c419d275979629e9e1c2044bb (patch) | |
tree | 18288d2b3d07d07c334c312017983f530a2ceaa7 | |
parent | 571010715abc1a007b7f2c842fab2638a2688c20 (diff) | |
parent | 8f91a4b9cac1153fa1622530598973998e3511f3 (diff) |
Merge branch 'staging/pci' into staging/master
-rw-r--r-- | Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt | 55 | ||||
-rw-r--r-- | Documentation/driver-model/devres.txt | 2 | ||||
-rw-r--r-- | arch/arm/boot/dts/tegra20-harmony.dts | 8 | ||||
-rw-r--r-- | arch/arm/boot/dts/tegra20-tamonten.dtsi | 7 | ||||
-rw-r--r-- | arch/arm/boot/dts/tegra20-trimslice.dts | 8 | ||||
-rw-r--r-- | arch/arm/boot/dts/tegra30-beaver.dts | 12 | ||||
-rw-r--r-- | arch/arm/boot/dts/tegra30-cardhu.dtsi | 11 | ||||
-rw-r--r-- | arch/arm/include/asm/io.h | 1 | ||||
-rw-r--r-- | arch/arm/include/asm/mach/pci.h | 6 | ||||
-rw-r--r-- | arch/arm/kernel/bios32.c | 36 | ||||
-rw-r--r-- | arch/arm/mach-tegra/cpuidle.c | 1 | ||||
-rw-r--r-- | arch/arm/mm/ioremap.c | 6 | ||||
-rw-r--r-- | drivers/pci/host/Kconfig | 2 | ||||
-rw-r--r-- | drivers/pci/host/pci-mvebu.c | 2 | ||||
-rw-r--r-- | drivers/pci/host/pci-tegra.c | 568 | ||||
-rw-r--r-- | drivers/pci/msi.c | 3 | ||||
-rw-r--r-- | drivers/pci/probe.c | 1 | ||||
-rw-r--r-- | drivers/pci/quirks.c | 2 | ||||
-rw-r--r-- | include/linux/ioport.h | 5 | ||||
-rw-r--r-- | kernel/irq/chip.c | 1 | ||||
-rw-r--r-- | kernel/resource.c | 73 | ||||
-rw-r--r-- | mm/vmalloc.c | 1 |
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) |