diff options
-rw-r--r-- | Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt | 161 | ||||
-rw-r--r-- | arch/arm/mach-tegra/Kconfig | 7 | ||||
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-harmony-pcie.c | 89 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board.h | 8 | ||||
-rw-r--r-- | arch/arm/mach-tegra/iomap.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pcie.c | 886 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra.c | 24 | ||||
-rw-r--r-- | drivers/pci/host/Kconfig | 4 | ||||
-rw-r--r-- | drivers/pci/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/pci/host/pci-tegra.c | 1535 |
11 files changed, 1703 insertions, 1018 deletions
diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt new file mode 100644 index 000000000000..90c112f671da --- /dev/null +++ b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt @@ -0,0 +1,161 @@ +NVIDIA Tegra PCIe controller + +Required properties: +- compatible: "nvidia,tegra20-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. +- reg-names: Must include the following entries: + "pads": PADS registers + "afi": AFI registers + "cs": configuration space region +- interrupts: A list of interrupt outputs of the controller. Must contain an + entry for each entry in the interrupt-names property. +- 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) +- 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: + [23:16]: bus number + [15:11]: device number + - cell 1 denotes the upper 32 address bits and should be 0 + - cell 2 contains the lower 32 address bits and is used to translate to the + CPU address space +- #size-cells: Size representation for root ports (must be 2) +- ranges: Describes the translation of addresses for root ports and standard + PCI regions. The entries must be 6 cells each, where the first three cells + correspond to the address as described for the #address-cells property + above, the fourth cell is the physical CPU address to translate to and the + fifth and six cells are as described for the #size-cells property above. + - The first two entries are expected to translate the addresses for the root + port registers, which are referenced by the assigned-addresses property of + the root port nodes (see below). + - The remaining entries setup the mapping for the standard I/O, memory and + prefetchable PCI regions. The first cell determines the type of region + that is setup: + - 0x81000000: I/O memory region + - 0x82000000: non-prefetchable memory region + - 0xc2000000: prefetchable memory region + Please refer to the standard PCI bus binding document for a more detailed + explanation. +- clocks: List of clock inputs of the controller. Must contain an entry for + each entry in the clock-names property. +- clock-names: Must include the following entries: + "pex": The Tegra clock of that name + "afi": The Tegra clock of that name + "pcie_xclk": The Tegra clock of that name + "pll_e": The Tegra clock of that name + +Root ports are defined as subnodes of the PCIe controller node. + +Required properties: +- device_type: Must be "pci" +- assigned-addresses: Address and size of the port configuration registers +- reg: PCI bus address of the root port +- #address-cells: Must be 3 +- #size-cells: Must be 2 +- ranges: Sub-ranges distributed from the PCIe controller node. An empty + property is sufficient. +- nvidia,num-lanes: Number of lanes to use for this port. Valid combinations + are: + - Root port 0 uses 4 lanes, root port 1 is unused. + - Both root ports use 2 lanes. + +Example: + +SoC DTSI: + + pcie-controller { + compatible = "nvidia,tegra20-pcie"; + device_type = "pci"; + reg = <0x80003000 0x00000800 /* PADS registers */ + 0x80003800 0x00000200 /* AFI registers */ + 0x90000000 0x10000000>; /* configuration space */ + reg-names = "pads", "afi", "cs"; + interrupts = <0 98 0x04 /* controller interrupt */ + 0 99 0x04>; /* MSI interrupt */ + interrupt-names = "intr", "msi"; + + bus-range = <0x00 0xff>; + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x82000000 0 0x80000000 0x80000000 0 0x00001000 /* port 0 registers */ + 0x82000000 0 0x80001000 0x80001000 0 0x00001000 /* port 1 registers */ + 0x81000000 0 0 0x82000000 0 0x00010000 /* downstream I/O */ + 0x82000000 0 0xa0000000 0xa0000000 0 0x10000000 /* non-prefetchable memory */ + 0xc2000000 0 0xb0000000 0xb0000000 0 0x10000000>; /* prefetchable memory */ + + clocks = <&tegra_car 70>, <&tegra_car 72>, <&tegra_car 74>, + <&tegra_car 118>; + clock-names = "pex", "afi", "pcie_xclk", "pll_e"; + status = "disabled"; + + pci@1,0 { + device_type = "pci"; + assigned-addresses = <0x82000800 0 0x80000000 0 0x1000>; + reg = <0x000800 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + + ranges; + + nvidia,num-lanes = <2>; + }; + + pci@2,0 { + device_type = "pci"; + assigned-addresses = <0x82001000 0 0x80001000 0 0x1000>; + reg = <0x001000 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + + ranges; + + nvidia,num-lanes = <2>; + }; + }; + + +Board DTS: + + pcie-controller { + status = "okay"; + + vdd-supply = <&pci_vdd_reg>; + pex-clk-supply = <&pci_clk_reg>; + + /* root port 00:01.0 */ + pci@1,0 { + status = "okay"; + + /* bridge 01:00.0 (optional) */ + pci@0,0 { + reg = <0x010000 0 0 0 0>; + + #address-cells = <3>; + #size-cells = <2>; + + device_type = "pci"; + + /* endpoint 02:00.0 */ + pci@0,0 { + reg = <0x020000 0 0 0 0>; + }; + }; + }; + }; + +Note that devices on the PCI bus are dynamically discovered using PCI's bus +enumeration and therefore don't need corresponding device nodes in DT. However +if a device on the PCI bus provides a non-probeable bus such as I2C or SPI, +device nodes need to be added in order to allow the bus' children to be +instantiated at the proper location in the operating system's device tree (as +illustrated by the optional nodes in the example above). diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index e963db273339..def056493260 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -21,6 +21,8 @@ config ARCH_TEGRA select USB_ULPI if USB_PHY select USB_ULPI_VIEWPORT if USB_PHY select USE_OF + select MIGHT_HAVE_PCI + select ARCH_SUPPORTS_MSI help This enables support for NVIDIA Tegra based systems. @@ -60,11 +62,6 @@ config ARCH_TEGRA_114_SOC Support for NVIDIA Tegra T114 processor family, based on the ARM CortexA15MP CPU -config TEGRA_PCI - bool "PCI Express support" - depends on ARCH_TEGRA_2x_SOC - select PCI - config TEGRA_AHB bool "Enable AHB driver for NVIDIA Tegra SoCs" default y diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index f4e70631626f..e7e5f45c6558 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-tegra30.o endif obj-$(CONFIG_SMP) += platsmp.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o -obj-$(CONFIG_TEGRA_PCI) += pcie.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114_speedo.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += sleep-tegra30.o @@ -38,6 +37,4 @@ ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_ARCH_TEGRA_114_SOC) += cpuidle-tegra114.o endif -obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-harmony-pcie.o - obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-paz00.o diff --git a/arch/arm/mach-tegra/board-harmony-pcie.c b/arch/arm/mach-tegra/board-harmony-pcie.c deleted file mode 100644 index 035b240b9e15..000000000000 --- a/arch/arm/mach-tegra/board-harmony-pcie.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * arch/arm/mach-tegra/board-harmony-pcie.c - * - * Copyright (C) 2010 CompuLab, Ltd. - * Mike Rapoport <mike@compulab.co.il> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/kernel.h> -#include <linux/gpio.h> -#include <linux/err.h> -#include <linux/of_gpio.h> -#include <linux/regulator/consumer.h> - -#include <asm/mach-types.h> - -#include "board.h" - -#ifdef CONFIG_TEGRA_PCI - -int __init harmony_pcie_init(void) -{ - struct device_node *np; - int en_vdd_1v05; - struct regulator *regulator = NULL; - int err; - - np = of_find_node_by_path("/regulators/regulator@3"); - if (!np) { - pr_err("%s: of_find_node_by_path failed\n", __func__); - return -ENODEV; - } - - en_vdd_1v05 = of_get_named_gpio(np, "gpio", 0); - if (en_vdd_1v05 < 0) { - pr_err("%s: of_get_named_gpio failed: %d\n", __func__, - en_vdd_1v05); - return en_vdd_1v05; - } - - err = gpio_request(en_vdd_1v05, "EN_VDD_1V05"); - if (err) { - pr_err("%s: gpio_request failed: %d\n", __func__, err); - return err; - } - - gpio_direction_output(en_vdd_1v05, 1); - - regulator = regulator_get(NULL, "vdd_ldo0,vddio_pex_clk"); - if (IS_ERR(regulator)) { - err = PTR_ERR(regulator); - pr_err("%s: regulator_get failed: %d\n", __func__, err); - goto err_reg; - } - - err = regulator_enable(regulator); - if (err) { - pr_err("%s: regulator_enable failed: %d\n", __func__, err); - goto err_en; - } - - err = tegra_pcie_init(true, true); - if (err) { - pr_err("%s: tegra_pcie_init failed: %d\n", __func__, err); - goto err_pcie; - } - - return 0; - -err_pcie: - regulator_disable(regulator); -err_en: - regulator_put(regulator); -err_reg: - gpio_free(en_vdd_1v05); - - return err; -} - -#endif diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index 9a6659fe2dc2..db6810dc0b3d 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -31,7 +31,6 @@ void __init tegra_init_early(void); void __init tegra_map_common_io(void); void __init tegra_init_irq(void); void __init tegra_dt_init_irq(void); -int __init tegra_pcie_init(bool init_port0, bool init_port1); void tegra_init_late(void); @@ -48,13 +47,6 @@ int __init tegra_powergate_debugfs_init(void); static inline int tegra_powergate_debugfs_init(void) { return 0; } #endif -int __init harmony_regulator_init(void); -#ifdef CONFIG_TEGRA_PCI -int __init harmony_pcie_init(void); -#else -static inline int harmony_pcie_init(void) { return 0; } -#endif - void __init tegra_paz00_wifikill_init(void); #endif diff --git a/arch/arm/mach-tegra/iomap.h b/arch/arm/mach-tegra/iomap.h index aba36291d98c..3f5fa0749bde 100644 --- a/arch/arm/mach-tegra/iomap.h +++ b/arch/arm/mach-tegra/iomap.h @@ -286,9 +286,6 @@ #define IO_APB_VIRT IOMEM(0xFE300000) #define IO_APB_SIZE SZ_1M -#define TEGRA_PCIE_BASE 0x80000000 -#define TEGRA_PCIE_IO_BASE (TEGRA_PCIE_BASE + SZ_4M) - #define IO_TO_VIRT_BETWEEN(p, st, sz) ((p) >= (st) && (p) < ((st) + (sz))) #define IO_TO_VIRT_XLATE(p, pst, vst) (((p) - (pst) + (vst))) diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c deleted file mode 100644 index 46144a19a7e7..000000000000 --- a/arch/arm/mach-tegra/pcie.c +++ /dev/null @@ -1,886 +0,0 @@ -/* - * arch/arm/mach-tegra/pci.c - * - * PCIe host controller driver for TEGRA(2) SOCs - * - * Copyright (c) 2010, CompuLab, Ltd. - * Author: Mike Rapoport <mike@compulab.co.il> - * - * Based on NVIDIA PCIe driver - * Copyright (c) 2008-2009, NVIDIA Corporation. - * - * Bits taken from arch/arm/mach-dove/pcie.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include <linux/kernel.h> -#include <linux/pci.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/export.h> -#include <linux/clk/tegra.h> -#include <linux/tegra-powergate.h> - -#include <asm/sizes.h> -#include <asm/mach/pci.h> - -#include "board.h" -#include "iomap.h" - -/* Hack - need to parse this from DT */ -#define INT_PCIE_INTR 130 - -/* register definitions */ -#define AFI_OFFSET 0x3800 -#define PADS_OFFSET 0x3000 -#define RP0_OFFSET 0x0000 -#define RP1_OFFSET 0x1000 - -#define AFI_AXI_BAR0_SZ 0x00 -#define AFI_AXI_BAR1_SZ 0x04 -#define AFI_AXI_BAR2_SZ 0x08 -#define AFI_AXI_BAR3_SZ 0x0c -#define AFI_AXI_BAR4_SZ 0x10 -#define AFI_AXI_BAR5_SZ 0x14 - -#define AFI_AXI_BAR0_START 0x18 -#define AFI_AXI_BAR1_START 0x1c -#define AFI_AXI_BAR2_START 0x20 -#define AFI_AXI_BAR3_START 0x24 -#define AFI_AXI_BAR4_START 0x28 -#define AFI_AXI_BAR5_START 0x2c - -#define AFI_FPCI_BAR0 0x30 -#define AFI_FPCI_BAR1 0x34 -#define AFI_FPCI_BAR2 0x38 -#define AFI_FPCI_BAR3 0x3c -#define AFI_FPCI_BAR4 0x40 -#define AFI_FPCI_BAR5 0x44 - -#define AFI_CACHE_BAR0_SZ 0x48 -#define AFI_CACHE_BAR0_ST 0x4c -#define AFI_CACHE_BAR1_SZ 0x50 -#define AFI_CACHE_BAR1_ST 0x54 - -#define AFI_MSI_BAR_SZ 0x60 -#define AFI_MSI_FPCI_BAR_ST 0x64 -#define AFI_MSI_AXI_BAR_ST 0x68 - -#define AFI_CONFIGURATION 0xac -#define AFI_CONFIGURATION_EN_FPCI (1 << 0) - -#define AFI_FPCI_ERROR_MASKS 0xb0 - -#define AFI_INTR_MASK 0xb4 -#define AFI_INTR_MASK_INT_MASK (1 << 0) -#define AFI_INTR_MASK_MSI_MASK (1 << 8) - -#define AFI_INTR_CODE 0xb8 -#define AFI_INTR_CODE_MASK 0xf -#define AFI_INTR_MASTER_ABORT 4 -#define AFI_INTR_LEGACY 6 - -#define AFI_INTR_SIGNATURE 0xbc -#define AFI_SM_INTR_ENABLE 0xc4 - -#define AFI_AFI_INTR_ENABLE 0xc8 -#define AFI_INTR_EN_INI_SLVERR (1 << 0) -#define AFI_INTR_EN_INI_DECERR (1 << 1) -#define AFI_INTR_EN_TGT_SLVERR (1 << 2) -#define AFI_INTR_EN_TGT_DECERR (1 << 3) -#define AFI_INTR_EN_TGT_WRERR (1 << 4) -#define AFI_INTR_EN_DFPCI_DECERR (1 << 5) -#define AFI_INTR_EN_AXI_DECERR (1 << 6) -#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) - -#define AFI_PCIE_CONFIG 0x0f8 -#define AFI_PCIE_CONFIG_PCIEC0_DISABLE_DEVICE (1 << 1) -#define AFI_PCIE_CONFIG_PCIEC1_DISABLE_DEVICE (1 << 2) -#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_DUAL (0x1 << 20) - -#define AFI_FUSE 0x104 -#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) - -#define AFI_PEX0_CTRL 0x110 -#define AFI_PEX1_CTRL 0x118 -#define AFI_PEX_CTRL_RST (1 << 0) -#define AFI_PEX_CTRL_REFCLK_EN (1 << 3) - -#define RP_VEND_XP 0x00000F00 -#define RP_VEND_XP_DL_UP (1 << 30) - -#define RP_LINK_CONTROL_STATUS 0x00000090 -#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 - -#define PADS_CTL_SEL 0x0000009C - -#define PADS_CTL 0x000000A0 -#define PADS_CTL_IDDQ_1L (1 << 0) -#define PADS_CTL_TX_DATA_EN_1L (1 << 6) -#define PADS_CTL_RX_DATA_EN_1L (1 << 10) - -#define PADS_PLL_CTL 0x000000B8 -#define PADS_PLL_CTL_RST_B4SM (1 << 1) -#define PADS_PLL_CTL_LOCKDET (1 << 8) -#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16) -#define PADS_PLL_CTL_REFCLK_INTERNAL_CML (0 << 16) -#define PADS_PLL_CTL_REFCLK_INTERNAL_CMOS (1 << 16) -#define PADS_PLL_CTL_REFCLK_EXTERNAL (2 << 16) -#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20) -#define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 20) -#define PADS_PLL_CTL_TXCLKREF_DIV5 (1 << 20) - -/* PMC access is required for PCIE xclk (un)clamping */ -#define PMC_SCRATCH42 0x144 -#define PMC_SCRATCH42_PCX_CLAMP (1 << 0) - -static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); - -#define pmc_writel(value, reg) \ - __raw_writel(value, reg_pmc_base + (reg)) -#define pmc_readl(reg) \ - __raw_readl(reg_pmc_base + (reg)) - -/* - * Tegra2 defines 1GB in the AXI address map for PCIe. - * - * That address space is split into different regions, with sizes and - * offsets as follows: - * - * 0x80000000 - 0x80003fff - PCI controller registers - * 0x80004000 - 0x80103fff - PCI configuration space - * 0x80104000 - 0x80203fff - PCI extended configuration space - * 0x80203fff - 0x803fffff - unused - * 0x80400000 - 0x8040ffff - downstream IO - * 0x80410000 - 0x8fffffff - unused - * 0x90000000 - 0x9fffffff - non-prefetchable memory - * 0xa0000000 - 0xbfffffff - prefetchable memory - */ -#define PCIE_REGS_SZ SZ_16K -#define PCIE_CFG_OFF PCIE_REGS_SZ -#define PCIE_CFG_SZ SZ_1M -#define PCIE_EXT_CFG_OFF (PCIE_CFG_SZ + PCIE_CFG_OFF) -#define PCIE_EXT_CFG_SZ SZ_1M -#define PCIE_IOMAP_SZ (PCIE_REGS_SZ + PCIE_CFG_SZ + PCIE_EXT_CFG_SZ) - -#define MEM_BASE_0 (TEGRA_PCIE_BASE + SZ_256M) -#define MEM_SIZE_0 SZ_128M -#define MEM_BASE_1 (MEM_BASE_0 + MEM_SIZE_0) -#define MEM_SIZE_1 SZ_128M -#define PREFETCH_MEM_BASE_0 (MEM_BASE_1 + MEM_SIZE_1) -#define PREFETCH_MEM_SIZE_0 SZ_128M -#define PREFETCH_MEM_BASE_1 (PREFETCH_MEM_BASE_0 + PREFETCH_MEM_SIZE_0) -#define PREFETCH_MEM_SIZE_1 SZ_128M - -#define PCIE_CONF_BUS(b) ((b) << 16) -#define PCIE_CONF_DEV(d) ((d) << 11) -#define PCIE_CONF_FUNC(f) ((f) << 8) -#define PCIE_CONF_REG(r) \ - (((r) & ~0x3) | (((r) < 256) ? PCIE_CFG_OFF : PCIE_EXT_CFG_OFF)) - -struct tegra_pcie_port { - int index; - u8 root_bus_nr; - void __iomem *base; - - bool link_up; - - char mem_space_name[16]; - char prefetch_space_name[20]; - struct resource res[2]; -}; - -struct tegra_pcie_info { - struct tegra_pcie_port port[2]; - int num_ports; - - void __iomem *regs; - struct resource res_mmio; - - struct clk *pex_clk; - struct clk *afi_clk; - struct clk *pcie_xclk; - struct clk *pll_e; -}; - -static struct tegra_pcie_info tegra_pcie; - -static inline void afi_writel(u32 value, unsigned long offset) -{ - writel(value, offset + AFI_OFFSET + tegra_pcie.regs); -} - -static inline u32 afi_readl(unsigned long offset) -{ - return readl(offset + AFI_OFFSET + tegra_pcie.regs); -} - -static inline void pads_writel(u32 value, unsigned long offset) -{ - writel(value, offset + PADS_OFFSET + tegra_pcie.regs); -} - -static inline u32 pads_readl(unsigned long offset) -{ - return readl(offset + PADS_OFFSET + tegra_pcie.regs); -} - -static struct tegra_pcie_port *bus_to_port(int bus) -{ - int i; - - for (i = tegra_pcie.num_ports - 1; i >= 0; i--) { - int rbus = tegra_pcie.port[i].root_bus_nr; - if (rbus != -1 && rbus == bus) - break; - } - - return i >= 0 ? tegra_pcie.port + i : NULL; -} - -static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - struct tegra_pcie_port *pp = bus_to_port(bus->number); - void __iomem *addr; - - if (pp) { - if (devfn != 0) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - addr = pp->base + (where & ~0x3); - } else { - addr = tegra_pcie.regs + (PCIE_CONF_BUS(bus->number) + - PCIE_CONF_DEV(PCI_SLOT(devfn)) + - PCIE_CONF_FUNC(PCI_FUNC(devfn)) + - PCIE_CONF_REG(where)); - } - - *val = readl(addr); - - if (size == 1) - *val = (*val >> (8 * (where & 3))) & 0xff; - else if (size == 2) - *val = (*val >> (8 * (where & 3))) & 0xffff; - - return PCIBIOS_SUCCESSFUL; -} - -static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - struct tegra_pcie_port *pp = bus_to_port(bus->number); - void __iomem *addr; - - u32 mask; - u32 tmp; - - if (pp) { - if (devfn != 0) - return PCIBIOS_DEVICE_NOT_FOUND; - - addr = pp->base + (where & ~0x3); - } else { - addr = tegra_pcie.regs + (PCIE_CONF_BUS(bus->number) + - PCIE_CONF_DEV(PCI_SLOT(devfn)) + - PCIE_CONF_FUNC(PCI_FUNC(devfn)) + - PCIE_CONF_REG(where)); - } - - if (size == 4) { - writel(val, addr); - return PCIBIOS_SUCCESSFUL; - } - - if (size == 2) - mask = ~(0xffff << ((where & 0x3) * 8)); - else if (size == 1) - mask = ~(0xff << ((where & 0x3) * 8)); - else - return PCIBIOS_BAD_REGISTER_NUMBER; - - tmp = readl(addr) & mask; - tmp |= val << ((where & 0x3) * 8); - writel(tmp, addr); - - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops tegra_pcie_ops = { - .read = tegra_pcie_read_conf, - .write = tegra_pcie_write_conf, -}; - -static void tegra_pcie_fixup_bridge(struct pci_dev *dev) -{ - u16 reg; - - if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) { - pci_read_config_word(dev, PCI_COMMAND, ®); - reg |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER | PCI_COMMAND_SERR); - pci_write_config_word(dev, PCI_COMMAND, reg); - } -} -DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_fixup_bridge); - -/* Tegra PCIE root complex wrongly reports device class */ -static void tegra_pcie_fixup_class(struct pci_dev *dev) -{ - dev->class = PCI_CLASS_BRIDGE_PCI << 8; -} -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_fixup_class); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class); - -/* Tegra PCIE requires relaxed ordering */ -static void tegra_pcie_relax_enable(struct pci_dev *dev) -{ - pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN); -} -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_port *pp; - - if (nr >= tegra_pcie.num_ports) - return 0; - - pp = tegra_pcie.port + nr; - pp->root_bus_nr = sys->busnr; - - pci_ioremap_io(nr * SZ_64K, TEGRA_PCIE_IO_BASE); - - /* - * IORESOURCE_MEM - */ - snprintf(pp->mem_space_name, sizeof(pp->mem_space_name), - "PCIe %d MEM", pp->index); - pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0; - pp->res[0].name = pp->mem_space_name; - if (pp->index == 0) { - pp->res[0].start = MEM_BASE_0; - pp->res[0].end = pp->res[0].start + MEM_SIZE_0 - 1; - } else { - pp->res[0].start = MEM_BASE_1; - pp->res[0].end = pp->res[0].start + MEM_SIZE_1 - 1; - } - pp->res[0].flags = IORESOURCE_MEM; - if (request_resource(&iomem_resource, &pp->res[0])) - panic("Request PCIe Memory resource failed\n"); - pci_add_resource_offset(&sys->resources, &pp->res[0], sys->mem_offset); - - /* - * IORESOURCE_MEM | IORESOURCE_PREFETCH - */ - snprintf(pp->prefetch_space_name, sizeof(pp->prefetch_space_name), - "PCIe %d PREFETCH MEM", pp->index); - pp->prefetch_space_name[sizeof(pp->prefetch_space_name) - 1] = 0; - pp->res[1].name = pp->prefetch_space_name; - if (pp->index == 0) { - pp->res[1].start = PREFETCH_MEM_BASE_0; - pp->res[1].end = pp->res[1].start + PREFETCH_MEM_SIZE_0 - 1; - } else { - pp->res[1].start = PREFETCH_MEM_BASE_1; - pp->res[1].end = pp->res[1].start + PREFETCH_MEM_SIZE_1 - 1; - } - pp->res[1].flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; - if (request_resource(&iomem_resource, &pp->res[1])) - panic("Request PCIe Prefetch Memory resource failed\n"); - pci_add_resource_offset(&sys->resources, &pp->res[1], sys->mem_offset); - - return 1; -} - -static int tegra_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -{ - return INT_PCIE_INTR; -} - -static struct pci_bus __init *tegra_pcie_scan_bus(int nr, - struct pci_sys_data *sys) -{ - struct tegra_pcie_port *pp; - - if (nr >= tegra_pcie.num_ports) - return NULL; - - pp = tegra_pcie.port + nr; - pp->root_bus_nr = sys->busnr; - - return pci_scan_root_bus(NULL, sys->busnr, &tegra_pcie_ops, sys, - &sys->resources); -} - -static struct hw_pci tegra_pcie_hw __initdata = { - .nr_controllers = 2, - .setup = tegra_pcie_setup, - .scan = tegra_pcie_scan_bus, - .map_irq = tegra_pcie_map_irq, -}; - - -static irqreturn_t tegra_pcie_isr(int irq, void *arg) -{ - const char *err_msg[] = { - "Unknown", - "AXI slave error", - "AXI decode error", - "Target abort", - "Master abort", - "Invalid write", - "Response decoding error", - "AXI response decoding error", - "Transcation timeout", - }; - - u32 code, signature; - - code = afi_readl(AFI_INTR_CODE) & AFI_INTR_CODE_MASK; - signature = afi_readl(AFI_INTR_SIGNATURE); - afi_writel(0, AFI_INTR_CODE); - - if (code == AFI_INTR_LEGACY) - return IRQ_NONE; - - if (code >= ARRAY_SIZE(err_msg)) - code = 0; - - /* - * do not pollute kernel log with master abort reports since they - * happen a lot during enumeration - */ - if (code == AFI_INTR_MASTER_ABORT) - pr_debug("PCIE: %s, signature: %08x\n", err_msg[code], signature); - else - pr_err("PCIE: %s, signature: %08x\n", err_msg[code], signature); - - return IRQ_HANDLED; -} - -static void tegra_pcie_setup_translations(void) -{ - u32 fpci_bar; - u32 size; - u32 axi_address; - - /* Bar 0: config Bar */ - fpci_bar = ((u32)0xfdff << 16); - size = PCIE_CFG_SZ; - axi_address = TEGRA_PCIE_BASE + PCIE_CFG_OFF; - afi_writel(axi_address, AFI_AXI_BAR0_START); - afi_writel(size >> 12, AFI_AXI_BAR0_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR0); - - /* Bar 1: extended config Bar */ - fpci_bar = ((u32)0xfe1 << 20); - size = PCIE_EXT_CFG_SZ; - axi_address = TEGRA_PCIE_BASE + PCIE_EXT_CFG_OFF; - afi_writel(axi_address, AFI_AXI_BAR1_START); - afi_writel(size >> 12, AFI_AXI_BAR1_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR1); - - /* Bar 2: downstream IO bar */ - fpci_bar = ((__u32)0xfdfc << 16); - size = SZ_128K; - axi_address = TEGRA_PCIE_IO_BASE; - afi_writel(axi_address, AFI_AXI_BAR2_START); - afi_writel(size >> 12, AFI_AXI_BAR2_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR2); - - /* Bar 3: prefetchable memory BAR */ - fpci_bar = (((PREFETCH_MEM_BASE_0 >> 12) & 0x0fffffff) << 4) | 0x1; - size = PREFETCH_MEM_SIZE_0 + PREFETCH_MEM_SIZE_1; - axi_address = PREFETCH_MEM_BASE_0; - afi_writel(axi_address, AFI_AXI_BAR3_START); - afi_writel(size >> 12, AFI_AXI_BAR3_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR3); - - /* Bar 4: non prefetchable memory BAR */ - fpci_bar = (((MEM_BASE_0 >> 12) & 0x0FFFFFFF) << 4) | 0x1; - size = MEM_SIZE_0 + MEM_SIZE_1; - axi_address = MEM_BASE_0; - afi_writel(axi_address, AFI_AXI_BAR4_START); - afi_writel(size >> 12, AFI_AXI_BAR4_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR4); - - /* Bar 5: NULL out the remaining BAR as it is not used */ - fpci_bar = 0; - size = 0; - axi_address = 0; - afi_writel(axi_address, AFI_AXI_BAR5_START); - afi_writel(size >> 12, AFI_AXI_BAR5_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR5); - - /* map all upstream transactions as uncached */ - afi_writel(PHYS_OFFSET, AFI_CACHE_BAR0_ST); - afi_writel(0, AFI_CACHE_BAR0_SZ); - afi_writel(0, AFI_CACHE_BAR1_ST); - afi_writel(0, AFI_CACHE_BAR1_SZ); - - /* No MSI */ - afi_writel(0, AFI_MSI_FPCI_BAR_ST); - afi_writel(0, AFI_MSI_BAR_SZ); - afi_writel(0, AFI_MSI_AXI_BAR_ST); - afi_writel(0, AFI_MSI_BAR_SZ); -} - -static int tegra_pcie_enable_controller(void) -{ - u32 val, reg; - int i, timeout; - - /* Enable slot clock and pulse the reset signals */ - for (i = 0, reg = AFI_PEX0_CTRL; i < 2; i++, reg += 0x8) { - val = afi_readl(reg) | AFI_PEX_CTRL_REFCLK_EN; - afi_writel(val, reg); - val &= ~AFI_PEX_CTRL_RST; - afi_writel(val, reg); - - val = afi_readl(reg) | AFI_PEX_CTRL_RST; - afi_writel(val, reg); - } - - /* Enable dual controller and both ports */ - val = afi_readl(AFI_PCIE_CONFIG); - val &= ~(AFI_PCIE_CONFIG_PCIEC0_DISABLE_DEVICE | - AFI_PCIE_CONFIG_PCIEC1_DISABLE_DEVICE | - AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK); - val |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL; - afi_writel(val, AFI_PCIE_CONFIG); - - val = afi_readl(AFI_FUSE) & ~AFI_FUSE_PCIE_T0_GEN2_DIS; - afi_writel(val, AFI_FUSE); - - /* Initialze internal PHY, enable up to 16 PCIE lanes */ - pads_writel(0x0, PADS_CTL_SEL); - - /* override IDDQ to 1 on all 4 lanes */ - val = pads_readl(PADS_CTL) | PADS_CTL_IDDQ_1L; - pads_writel(val, PADS_CTL); - - /* - * set up PHY PLL inputs select PLLE output as refclock, - * set TX ref sel to div10 (not div5) - */ - val = pads_readl(PADS_PLL_CTL); - val &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK); - val |= (PADS_PLL_CTL_REFCLK_INTERNAL_CML | PADS_PLL_CTL_TXCLKREF_DIV10); - pads_writel(val, PADS_PLL_CTL); - - /* take PLL out of reset */ - val = pads_readl(PADS_PLL_CTL) | PADS_PLL_CTL_RST_B4SM; - pads_writel(val, PADS_PLL_CTL); - - /* - * Hack, set the clock voltage to the DEFAULT provided by hw folks. - * This doesn't exist in the documentation - */ - pads_writel(0xfa5cfa5c, 0xc8); - - /* Wait for the PLL to lock */ - timeout = 300; - do { - val = pads_readl(PADS_PLL_CTL); - usleep_range(1000, 1000); - if (--timeout == 0) { - pr_err("Tegra PCIe error: timeout waiting for PLL\n"); - return -EBUSY; - } - } while (!(val & PADS_PLL_CTL_LOCKDET)); - - /* turn off IDDQ override */ - val = pads_readl(PADS_CTL) & ~PADS_CTL_IDDQ_1L; - pads_writel(val, PADS_CTL); - - /* enable TX/RX data */ - val = pads_readl(PADS_CTL); - val |= (PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L); - pads_writel(val, PADS_CTL); - - /* Take the PCIe interface module out of reset */ - tegra_periph_reset_deassert(tegra_pcie.pcie_xclk); - - /* Finally enable PCIe */ - val = afi_readl(AFI_CONFIGURATION) | AFI_CONFIGURATION_EN_FPCI; - afi_writel(val, AFI_CONFIGURATION); - - val = (AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR | - AFI_INTR_EN_TGT_SLVERR | AFI_INTR_EN_TGT_DECERR | - AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR); - afi_writel(val, AFI_AFI_INTR_ENABLE); - afi_writel(0xffffffff, AFI_SM_INTR_ENABLE); - - /* FIXME: No MSI for now, only INT */ - afi_writel(AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK); - - /* Disable all execptions */ - afi_writel(0, AFI_FPCI_ERROR_MASKS); - - return 0; -} - -static void tegra_pcie_xclk_clamp(bool clamp) -{ - u32 reg; - - reg = pmc_readl(PMC_SCRATCH42) & ~PMC_SCRATCH42_PCX_CLAMP; - - if (clamp) - reg |= PMC_SCRATCH42_PCX_CLAMP; - - pmc_writel(reg, PMC_SCRATCH42); -} - -static void tegra_pcie_power_off(void) -{ - tegra_periph_reset_assert(tegra_pcie.pcie_xclk); - tegra_periph_reset_assert(tegra_pcie.afi_clk); - tegra_periph_reset_assert(tegra_pcie.pex_clk); - - tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); - tegra_pcie_xclk_clamp(true); -} - -static int tegra_pcie_power_regate(void) -{ - int err; - - tegra_pcie_power_off(); - - tegra_pcie_xclk_clamp(true); - - tegra_periph_reset_assert(tegra_pcie.pcie_xclk); - tegra_periph_reset_assert(tegra_pcie.afi_clk); - - err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, - tegra_pcie.pex_clk); - if (err) { - pr_err("PCIE: powerup sequence failed: %d\n", err); - return err; - } - - tegra_periph_reset_deassert(tegra_pcie.afi_clk); - - tegra_pcie_xclk_clamp(false); - - clk_prepare_enable(tegra_pcie.afi_clk); - clk_prepare_enable(tegra_pcie.pex_clk); - return clk_prepare_enable(tegra_pcie.pll_e); -} - -static int tegra_pcie_clocks_get(void) -{ - int err; - - tegra_pcie.pex_clk = clk_get(NULL, "pex"); - if (IS_ERR(tegra_pcie.pex_clk)) - return PTR_ERR(tegra_pcie.pex_clk); - - tegra_pcie.afi_clk = clk_get(NULL, "afi"); - if (IS_ERR(tegra_pcie.afi_clk)) { - err = PTR_ERR(tegra_pcie.afi_clk); - goto err_afi_clk; - } - - tegra_pcie.pcie_xclk = clk_get(NULL, "pcie_xclk"); - if (IS_ERR(tegra_pcie.pcie_xclk)) { - err = PTR_ERR(tegra_pcie.pcie_xclk); - goto err_pcie_xclk; - } - - tegra_pcie.pll_e = clk_get_sys(NULL, "pll_e"); - if (IS_ERR(tegra_pcie.pll_e)) { - err = PTR_ERR(tegra_pcie.pll_e); - goto err_pll_e; - } - - return 0; - -err_pll_e: - clk_put(tegra_pcie.pcie_xclk); -err_pcie_xclk: - clk_put(tegra_pcie.afi_clk); -err_afi_clk: - clk_put(tegra_pcie.pex_clk); - - return err; -} - -static void tegra_pcie_clocks_put(void) -{ - clk_put(tegra_pcie.pll_e); - clk_put(tegra_pcie.pcie_xclk); - clk_put(tegra_pcie.afi_clk); - clk_put(tegra_pcie.pex_clk); -} - -static int __init tegra_pcie_get_resources(void) -{ - int err; - - err = tegra_pcie_clocks_get(); - if (err) { - pr_err("PCIE: failed to get clocks: %d\n", err); - return err; - } - - err = tegra_pcie_power_regate(); - if (err) { - pr_err("PCIE: failed to power up: %d\n", err); - goto err_pwr_on; - } - - tegra_pcie.regs = ioremap_nocache(TEGRA_PCIE_BASE, PCIE_IOMAP_SZ); - if (tegra_pcie.regs == NULL) { - pr_err("PCIE: Failed to map PCI/AFI registers\n"); - err = -ENOMEM; - goto err_map_reg; - } - - err = request_irq(INT_PCIE_INTR, tegra_pcie_isr, - IRQF_SHARED, "PCIE", &tegra_pcie); - if (err) { - pr_err("PCIE: Failed to register IRQ: %d\n", err); - goto err_req_io; - } - set_irq_flags(INT_PCIE_INTR, IRQF_VALID); - - return 0; - -err_req_io: - iounmap(tegra_pcie.regs); -err_map_reg: - tegra_pcie_power_off(); -err_pwr_on: - tegra_pcie_clocks_put(); - - return err; -} - -/* - * FIXME: If there are no PCIe cards attached, then calling this function - * can result in the increase of the bootup time as there are big timeout - * loops. - */ -#define TEGRA_PCIE_LINKUP_TIMEOUT 200 /* up to 1.2 seconds */ -static bool tegra_pcie_check_link(struct tegra_pcie_port *pp, int idx, - u32 reset_reg) -{ - u32 reg; - int retries = 3; - int timeout; - - do { - timeout = TEGRA_PCIE_LINKUP_TIMEOUT; - while (timeout) { - reg = readl(pp->base + RP_VEND_XP); - - if (reg & RP_VEND_XP_DL_UP) - break; - - mdelay(1); - timeout--; - } - - if (!timeout) { - pr_err("PCIE: port %d: link down, retrying\n", idx); - goto retry; - } - - timeout = TEGRA_PCIE_LINKUP_TIMEOUT; - while (timeout) { - reg = readl(pp->base + RP_LINK_CONTROL_STATUS); - - if (reg & 0x20000000) - return true; - - mdelay(1); - timeout--; - } - -retry: - /* Pulse the PEX reset */ - reg = afi_readl(reset_reg) | AFI_PEX_CTRL_RST; - afi_writel(reg, reset_reg); - mdelay(1); - reg = afi_readl(reset_reg) & ~AFI_PEX_CTRL_RST; - afi_writel(reg, reset_reg); - - retries--; - } while (retries); - - return false; -} - -static void __init tegra_pcie_add_port(int index, u32 offset, u32 reset_reg) -{ - struct tegra_pcie_port *pp; - - pp = tegra_pcie.port + tegra_pcie.num_ports; - - pp->index = -1; - pp->base = tegra_pcie.regs + offset; - pp->link_up = tegra_pcie_check_link(pp, index, reset_reg); - - if (!pp->link_up) { - pp->base = NULL; - printk(KERN_INFO "PCIE: port %d: link down, ignoring\n", index); - return; - } - - tegra_pcie.num_ports++; - pp->index = index; - pp->root_bus_nr = -1; - memset(pp->res, 0, sizeof(pp->res)); -} - -int __init tegra_pcie_init(bool init_port0, bool init_port1) -{ - int err; - - if (!(init_port0 || init_port1)) - return -ENODEV; - - pcibios_min_mem = 0; - - err = tegra_pcie_get_resources(); - if (err) - return err; - - err = tegra_pcie_enable_controller(); - if (err) - return err; - - /* setup the AFI address translations */ - tegra_pcie_setup_translations(); - - if (init_port0) - tegra_pcie_add_port(0, RP0_OFFSET, AFI_PEX0_CTRL); - - if (init_port1) - tegra_pcie_add_port(1, RP1_OFFSET, AFI_PEX1_CTRL); - - pci_common_init(&tegra_pcie_hw); - - return 0; -} diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index 0d1e4128d460..fe56fcafdf15 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -116,28 +116,6 @@ out: tegra20_auxdata_lookup, parent); } -static void __init trimslice_init(void) -{ -#ifdef CONFIG_TEGRA_PCI - int ret; - - ret = tegra_pcie_init(true, true); - if (ret) - pr_err("tegra_pci_init() failed: %d\n", ret); -#endif -} - -static void __init harmony_init(void) -{ -#ifdef CONFIG_TEGRA_PCI - int ret; - - ret = harmony_pcie_init(); - if (ret) - pr_err("harmony_pcie_init() failed: %d\n", ret); -#endif -} - static void __init paz00_init(void) { if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) @@ -148,8 +126,6 @@ static struct { char *machine; void (*init)(void); } board_init_funcs[] = { - { "compulab,trimslice", trimslice_init }, - { "nvidia,harmony", harmony_init }, { "compal,paz00", paz00_init }, }; diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 1184ff6fe864..5f33746c925b 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -14,4 +14,8 @@ config PCI_EXYNOS select PCIEPORTBUS select PCIE_DW +config PCI_TEGRA + bool "NVIDIA Tegra PCIe controller" + depends on ARCH_TEGRA + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 086d8500e849..a733fb0f7856 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCIE_DW) += pcie-designware.o +obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c new file mode 100644 index 000000000000..a54a01138927 --- /dev/null +++ b/drivers/pci/host/pci-tegra.c @@ -0,0 +1,1535 @@ +/* + * PCIe host controller driver for TEGRA(2) SOCs + * + * Copyright (c) 2010, CompuLab, Ltd. + * Author: Mike Rapoport <mike@compulab.co.il> + * + * Based on NVIDIA PCIe driver + * Copyright (c) 2008-2009, NVIDIA Corporation. + * + * Bits taken from arch/arm/mach-dove/pcie.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/clk.h> +#include <linux/clk/tegra.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/tegra-powergate.h> +#include <linux/vmalloc.h> +#include <linux/regulator/consumer.h> + +#include <asm/mach/irq.h> +#include <asm/mach/map.h> +#include <asm/mach/pci.h> + +#define INT_PCI_MSI_NR (8 * 32) +#define TEGRA_MAX_PORTS 2 + +/* register definitions */ + +#define AFI_AXI_BAR0_SZ 0x00 +#define AFI_AXI_BAR1_SZ 0x04 +#define AFI_AXI_BAR2_SZ 0x08 +#define AFI_AXI_BAR3_SZ 0x0c +#define AFI_AXI_BAR4_SZ 0x10 +#define AFI_AXI_BAR5_SZ 0x14 + +#define AFI_AXI_BAR0_START 0x18 +#define AFI_AXI_BAR1_START 0x1c +#define AFI_AXI_BAR2_START 0x20 +#define AFI_AXI_BAR3_START 0x24 +#define AFI_AXI_BAR4_START 0x28 +#define AFI_AXI_BAR5_START 0x2c + +#define AFI_FPCI_BAR0 0x30 +#define AFI_FPCI_BAR1 0x34 +#define AFI_FPCI_BAR2 0x38 +#define AFI_FPCI_BAR3 0x3c +#define AFI_FPCI_BAR4 0x40 +#define AFI_FPCI_BAR5 0x44 + +#define AFI_CACHE_BAR0_SZ 0x48 +#define AFI_CACHE_BAR0_ST 0x4c +#define AFI_CACHE_BAR1_SZ 0x50 +#define AFI_CACHE_BAR1_ST 0x54 + +#define AFI_MSI_BAR_SZ 0x60 +#define AFI_MSI_FPCI_BAR_ST 0x64 +#define AFI_MSI_AXI_BAR_ST 0x68 + +#define AFI_MSI_VEC0 0x6c +#define AFI_MSI_VEC1 0x70 +#define AFI_MSI_VEC2 0x74 +#define AFI_MSI_VEC3 0x78 +#define AFI_MSI_VEC4 0x7c +#define AFI_MSI_VEC5 0x80 +#define AFI_MSI_VEC6 0x84 +#define AFI_MSI_VEC7 0x88 + +#define AFI_MSI_EN_VEC0 0x8c +#define AFI_MSI_EN_VEC1 0x90 +#define AFI_MSI_EN_VEC2 0x94 +#define AFI_MSI_EN_VEC3 0x98 +#define AFI_MSI_EN_VEC4 0x9c +#define AFI_MSI_EN_VEC5 0xa0 +#define AFI_MSI_EN_VEC6 0xa4 +#define AFI_MSI_EN_VEC7 0xa8 + +#define AFI_CONFIGURATION 0xac +#define AFI_CONFIGURATION_EN_FPCI (1 << 0) + +#define AFI_FPCI_ERROR_MASKS 0xb0 + +#define AFI_INTR_MASK 0xb4 +#define AFI_INTR_MASK_INT_MASK (1 << 0) +#define AFI_INTR_MASK_MSI_MASK (1 << 8) + +#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_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_SIGNATURE 0xbc +#define AFI_UPPER_FPCI_ADDRESS 0xc0 +#define AFI_SM_INTR_ENABLE 0xc4 +#define AFI_SM_INTR_INTA_ASSERT (1 << 0) +#define AFI_SM_INTR_INTB_ASSERT (1 << 1) +#define AFI_SM_INTR_INTC_ASSERT (1 << 2) +#define AFI_SM_INTR_INTD_ASSERT (1 << 3) +#define AFI_SM_INTR_INTA_DEASSERT (1 << 4) +#define AFI_SM_INTR_INTB_DEASSERT (1 << 5) +#define AFI_SM_INTR_INTC_DEASSERT (1 << 6) +#define AFI_SM_INTR_INTD_DEASSERT (1 << 7) + +#define AFI_AFI_INTR_ENABLE 0xc8 +#define AFI_INTR_EN_INI_SLVERR (1 << 0) +#define AFI_INTR_EN_INI_DECERR (1 << 1) +#define AFI_INTR_EN_TGT_SLVERR (1 << 2) +#define AFI_INTR_EN_TGT_DECERR (1 << 3) +#define AFI_INTR_EN_TGT_WRERR (1 << 4) +#define AFI_INTR_EN_DFPCI_DECERR (1 << 5) +#define AFI_INTR_EN_AXI_DECERR (1 << 6) +#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) + +#define AFI_PCIE_CONFIG 0x0f8 +#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) +#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe +#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_DUAL (0x1 << 20) + +#define AFI_FUSE 0x104 +#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) + +#define AFI_PEX0_CTRL 0x110 +#define AFI_PEX1_CTRL 0x118 +#define AFI_PEX_CTRL_RST (1 << 0) +#define AFI_PEX_CTRL_REFCLK_EN (1 << 3) + +#define RP_VEND_XP 0x00000F00 +#define RP_VEND_XP_DL_UP (1 << 30) + +#define RP_LINK_CONTROL_STATUS 0x00000090 +#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 +#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 + +#define PADS_CTL_SEL 0x0000009C + +#define PADS_CTL 0x000000A0 +#define PADS_CTL_IDDQ_1L (1 << 0) +#define PADS_CTL_TX_DATA_EN_1L (1 << 6) +#define PADS_CTL_RX_DATA_EN_1L (1 << 10) + +#define PADS_PLL_CTL 0x000000B8 +#define PADS_PLL_CTL_RST_B4SM (1 << 1) +#define PADS_PLL_CTL_LOCKDET (1 << 8) +#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CML (0 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CMOS (1 << 16) +#define PADS_PLL_CTL_REFCLK_EXTERNAL (2 << 16) +#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV5 (1 << 20) + +struct tegra_msi { + struct msi_chip chip; + DECLARE_BITMAP(used, INT_PCI_MSI_NR); + struct irq_domain *domain; + unsigned long pages; + struct mutex lock; + int irq; +}; + +static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip) +{ + return container_of(chip, struct tegra_msi, chip); +} + +struct tegra_pcie { + struct device *dev; + + void __iomem *pads; + void __iomem *afi; + int irq; + + struct list_head busses; + struct resource *cs; + + struct resource io; + struct resource mem; + struct resource prefetch; + struct resource busn; + + struct clk *pex_clk; + struct clk *afi_clk; + struct clk *pcie_xclk; + struct clk *pll_e; + + 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 tegra_pcie_port { + struct tegra_pcie *pcie; + struct list_head list; + struct resource regs; + void __iomem *base; + unsigned int index; + unsigned int lanes; +}; + +struct tegra_pcie_bus { + struct vm_struct *area; + struct list_head list; + 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) +{ + writel(value, pcie->afi + offset); +} + +static inline u32 afi_readl(struct tegra_pcie *pcie, unsigned long offset) +{ + return readl(pcie->afi + offset); +} + +static inline void pads_writel(struct tegra_pcie *pcie, u32 value, + unsigned long offset) +{ + writel(value, pcie->pads + offset); +} + +static inline u32 pads_readl(struct tegra_pcie *pcie, unsigned long offset) +{ + return readl(pcie->pads + offset); +} + +/* + * The configuration space mapping on Tegra is somewhat similar to the ECAM + * defined by PCIe. However it deviates a bit in how the 4 bits for extended + * register accesses are mapped: + * + * [27:24] extended register number + * [23:16] bus number + * [15:11] device number + * [10: 8] function number + * [ 7: 0] register number + * + * Mapping the whole extended configuration space would require 256 MiB of + * virtual address space, only a small part of which will actually be used. + * To work around this, a 1 MiB of virtual addresses are allocated per bus + * when the bus is first accessed. When the physical range is mapped, the + * the bus number bits are hidden so that the extended register number bits + * appear as bits [19:16]. Therefore the virtual mapping looks like this: + * + * [19:16] extended register number + * [15:11] device number + * [10: 8] function number + * [ 7: 0] register number + * + * This is achieved by stitching together 16 chunks of 64 KiB of physical + * address space via the MMU. + */ +static unsigned long tegra_pcie_conf_offset(unsigned int devfn, int where) +{ + return ((where & 0xf00) << 8) | (PCI_SLOT(devfn) << 11) | + (PCI_FUNC(devfn) << 8) | (where & 0xfc); +} + +static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie, + unsigned int busnr) +{ + pgprot_t prot = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_XN | + L_PTE_MT_DEV_SHARED | L_PTE_SHARED; + phys_addr_t cs = pcie->cs->start; + struct tegra_pcie_bus *bus; + unsigned int i; + int err; + + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (!bus) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&bus->list); + bus->nr = busnr; + + /* allocate 1 MiB of virtual addresses */ + bus->area = get_vm_area(SZ_1M, VM_IOREMAP); + if (!bus->area) { + err = -ENOMEM; + goto free; + } + + /* map each of the 16 chunks of 64 KiB each */ + 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; + + err = ioremap_page_range(virt, virt + SZ_64K, phys, prot); + if (err < 0) { + dev_err(pcie->dev, "ioremap_page_range() failed: %d\n", + err); + goto unmap; + } + } + + return bus; + +unmap: + vunmap(bus->area->addr); +free: + kfree(bus); + return ERR_PTR(err); +} + +/* + * Look up a virtual address mapping for the specified bus number. If no such + * mapping existis, try to create one. + */ +static void __iomem *tegra_pcie_bus_map(struct tegra_pcie *pcie, + unsigned int busnr) +{ + struct tegra_pcie_bus *bus; + + list_for_each_entry(bus, &pcie->busses, list) + if (bus->nr == busnr) + return bus->area->addr; + + bus = tegra_pcie_bus_alloc(pcie, busnr); + if (IS_ERR(bus)) + return NULL; + + list_add_tail(&bus->list, &pcie->busses); + + return bus->area->addr; +} + +static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus, + unsigned int devfn, + int where) +{ + struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata); + void __iomem *addr = NULL; + + if (bus->number == 0) { + unsigned int slot = PCI_SLOT(devfn); + struct tegra_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) { + if (port->index + 1 == slot) { + addr = port->base + (where & ~3); + break; + } + } + } else { + addr = tegra_pcie_bus_map(pcie, bus->number); + if (!addr) { + dev_err(pcie->dev, + "failed to map cfg. space for bus %u\n", + bus->number); + return NULL; + } + + addr += tegra_pcie_conf_offset(devfn, where); + } + + return addr; +} + +static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + void __iomem *addr; + + addr = tegra_pcie_conf_address(bus, devfn, where); + if (!addr) { + *value = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + *value = readl(addr); + + if (size == 1) + *value = (*value >> (8 * (where & 3))) & 0xff; + else if (size == 2) + *value = (*value >> (8 * (where & 3))) & 0xffff; + + return PCIBIOS_SUCCESSFUL; +} + +static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + void __iomem *addr; + u32 mask, tmp; + + addr = tegra_pcie_conf_address(bus, devfn, where); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (size == 4) { + writel(value, addr); + return PCIBIOS_SUCCESSFUL; + } + + if (size == 2) + mask = ~(0xffff << ((where & 0x3) * 8)); + else if (size == 1) + mask = ~(0xff << ((where & 0x3) * 8)); + else + return PCIBIOS_BAD_REGISTER_NUMBER; + + tmp = readl(addr) & mask; + tmp |= value << ((where & 0x3) * 8); + writel(tmp, addr); + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops tegra_pcie_ops = { + .read = tegra_pcie_read_conf, + .write = tegra_pcie_write_conf, +}; + +static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) +{ + unsigned long ret = 0; + + switch (port->index) { + case 0: + ret = AFI_PEX0_CTRL; + break; + + case 1: + ret = AFI_PEX1_CTRL; + break; + } + + return ret; +} + +static void tegra_pcie_port_reset(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* pulse reset signal */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); + + usleep_range(1000, 2000); + + value = afi_readl(port->pcie, ctrl); + value |= AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); +} + +static void tegra_pcie_port_enable(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* enable reference clock */ + value = afi_readl(port->pcie, ctrl); + value |= AFI_PEX_CTRL_REFCLK_EN; + afi_writel(port->pcie, value, ctrl); + + tegra_pcie_port_reset(port); +} + +static void tegra_pcie_port_disable(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* assert port reset */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); + + /* disable reference clock */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_REFCLK_EN; + afi_writel(port->pcie, value, ctrl); +} + +static void tegra_pcie_port_free(struct tegra_pcie_port *port) +{ + struct tegra_pcie *pcie = port->pcie; + + devm_iounmap(pcie->dev, port->base); + devm_release_mem_region(pcie->dev, port->regs.start, + resource_size(&port->regs)); + list_del(&port->list); + devm_kfree(pcie->dev, port); +} + +static void tegra_pcie_fixup_bridge(struct pci_dev *dev) +{ + u16 reg; + + if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) { + pci_read_config_word(dev, PCI_COMMAND, ®); + reg |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_SERR); + pci_write_config_word(dev, PCI_COMMAND, reg); + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_fixup_bridge); + +/* Tegra PCIE root complex wrongly reports device class */ +static void tegra_pcie_fixup_class(struct pci_dev *dev) +{ + dev->class = PCI_CLASS_BRIDGE_PCI << 8; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_fixup_class); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class); + +/* Tegra PCIE requires relaxed ordering */ +static void tegra_pcie_relax_enable(struct pci_dev *dev) +{ + pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN); +} +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); + + 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); + + pci_ioremap_io(nr * SZ_64K, pcie->io.start); + + return 1; +} + +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); + + return pcie->irq; +} + +static void tegra_pcie_add_bus(struct pci_bus *bus) +{ + if (IS_ENABLED(CONFIG_PCI_MSI)) { + struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata); + + bus->msi = &pcie->msi.chip; + } +} + +static struct pci_bus *tegra_pcie_scan_bus(int nr, struct pci_sys_data *sys) +{ + struct tegra_pcie *pcie = sys_to_pcie(sys); + struct pci_bus *bus; + + bus = pci_create_root_bus(pcie->dev, sys->busnr, &tegra_pcie_ops, sys, + &sys->resources); + if (!bus) + return NULL; + + pci_scan_child_bus(bus); + + return bus; +} + +static irqreturn_t tegra_pcie_isr(int irq, void *arg) +{ + const char *err_msg[] = { + "Unknown", + "AXI slave error", + "AXI decode error", + "Target abort", + "Master abort", + "Invalid write", + "Response decoding error", + "AXI response decoding error", + "Transaction timeout", + }; + struct tegra_pcie *pcie = arg; + u32 code, signature; + + code = afi_readl(pcie, AFI_INTR_CODE) & AFI_INTR_CODE_MASK; + signature = afi_readl(pcie, AFI_INTR_SIGNATURE); + afi_writel(pcie, 0, AFI_INTR_CODE); + + if (code == AFI_INTR_LEGACY) + return IRQ_NONE; + + if (code >= ARRAY_SIZE(err_msg)) + code = 0; + + /* + * do not pollute kernel log with master abort reports since they + * happen a lot during enumeration + */ + if (code == AFI_INTR_MASTER_ABORT) + dev_dbg(pcie->dev, "%s, signature: %08x\n", err_msg[code], + signature); + else + dev_err(pcie->dev, "%s, signature: %08x\n", err_msg[code], + signature); + + if (code == AFI_INTR_TARGET_ABORT || code == AFI_INTR_MASTER_ABORT || + code == AFI_INTR_FPCI_DECODE_ERROR) { + u32 fpci = afi_readl(pcie, AFI_UPPER_FPCI_ADDRESS) & 0xff; + u64 address = (u64)fpci << 32 | (signature & 0xfffffffc); + + if (code == AFI_INTR_MASTER_ABORT) + dev_dbg(pcie->dev, " FPCI address: %10llx\n", address); + else + dev_err(pcie->dev, " FPCI address: %10llx\n", address); + } + + return IRQ_HANDLED; +} + +/* + * FPCI map is as follows: + * - 0xfdfc000000: I/O space + * - 0xfdfe000000: type 0 configuration space + * - 0xfdff000000: type 1 configuration space + * - 0xfe00000000: type 0 extended configuration space + * - 0xfe10000000: type 1 extended configuration space + */ +static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) +{ + u32 fpci_bar, size, axi_address; + + /* Bar 0: type 1 extended configuration space */ + fpci_bar = 0xfe100000; + size = resource_size(pcie->cs); + axi_address = pcie->cs->start; + afi_writel(pcie, axi_address, AFI_AXI_BAR0_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR0_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR0); + + /* Bar 1: downstream IO bar */ + fpci_bar = 0xfdfc0000; + size = resource_size(&pcie->io); + axi_address = pcie->io.start; + afi_writel(pcie, axi_address, AFI_AXI_BAR1_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1); + + /* Bar 2: prefetchable memory BAR */ + fpci_bar = (((pcie->prefetch.start >> 12) & 0x0fffffff) << 4) | 0x1; + size = resource_size(&pcie->prefetch); + axi_address = pcie->prefetch.start; + afi_writel(pcie, axi_address, AFI_AXI_BAR2_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR2_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR2); + + /* Bar 3: non prefetchable memory BAR */ + fpci_bar = (((pcie->mem.start >> 12) & 0x0fffffff) << 4) | 0x1; + size = resource_size(&pcie->mem); + axi_address = pcie->mem.start; + afi_writel(pcie, axi_address, AFI_AXI_BAR3_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR3_SZ); + afi_writel(pcie, fpci_bar, AFI_FPCI_BAR3); + + /* NULL out the remaining BARs as they are not used */ + afi_writel(pcie, 0, AFI_AXI_BAR4_START); + afi_writel(pcie, 0, AFI_AXI_BAR4_SZ); + afi_writel(pcie, 0, AFI_FPCI_BAR4); + + afi_writel(pcie, 0, AFI_AXI_BAR5_START); + afi_writel(pcie, 0, AFI_AXI_BAR5_SZ); + afi_writel(pcie, 0, AFI_FPCI_BAR5); + + /* map all upstream transactions as uncached */ + afi_writel(pcie, PHYS_OFFSET, AFI_CACHE_BAR0_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ); + afi_writel(pcie, 0, AFI_CACHE_BAR1_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ); + + /* MSI translations are setup only when needed */ + afi_writel(pcie, 0, AFI_MSI_FPCI_BAR_ST); + afi_writel(pcie, 0, AFI_MSI_BAR_SZ); + afi_writel(pcie, 0, AFI_MSI_AXI_BAR_ST); + afi_writel(pcie, 0, AFI_MSI_BAR_SZ); +} + +static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port; + unsigned int timeout; + unsigned long 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; + + list_for_each_entry(port, &pcie->ports, list) + value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index); + + afi_writel(pcie, value, AFI_PCIE_CONFIG); + + value = afi_readl(pcie, AFI_FUSE); + value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; + afi_writel(pcie, value, AFI_FUSE); + + /* initialze internal PHY, enable up to 16 PCIE lanes */ + pads_writel(pcie, 0x0, PADS_CTL_SEL); + + /* override IDDQ to 1 on all 4 lanes */ + value = pads_readl(pcie, PADS_CTL); + value |= PADS_CTL_IDDQ_1L; + pads_writel(pcie, value, PADS_CTL); + + /* + * Set up PHY PLL inputs select PLLE output as refclock, + * set TX ref sel to div10 (not div5). + */ + value = pads_readl(pcie, PADS_PLL_CTL); + value &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK); + value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | + PADS_PLL_CTL_TXCLKREF_DIV10; + pads_writel(pcie, value, PADS_PLL_CTL); + + /* take PLL out of reset */ + value = pads_readl(pcie, PADS_PLL_CTL); + value |= PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, PADS_PLL_CTL); + + /* + * Hack, set the clock voltage to the DEFAULT provided by hw folks. + * This doesn't exist in the documentation. + */ + pads_writel(pcie, 0xfa5cfa5c, 0xc8); + + /* wait for the PLL to lock */ + timeout = 300; + do { + value = pads_readl(pcie, 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)); + + /* turn off IDDQ override */ + value = pads_readl(pcie, PADS_CTL); + value &= ~PADS_CTL_IDDQ_1L; + pads_writel(pcie, value, PADS_CTL); + + /* enable TX/RX data */ + value = pads_readl(pcie, PADS_CTL); + value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L; + pads_writel(pcie, value, PADS_CTL); + + /* take the PCIe interface module out of reset */ + tegra_periph_reset_deassert(pcie->pcie_xclk); + + /* finally enable PCIe */ + value = afi_readl(pcie, AFI_CONFIGURATION); + value |= AFI_CONFIGURATION_EN_FPCI; + afi_writel(pcie, value, AFI_CONFIGURATION); + + value = AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR | + AFI_INTR_EN_TGT_SLVERR | AFI_INTR_EN_TGT_DECERR | + AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR; + afi_writel(pcie, value, AFI_AFI_INTR_ENABLE); + afi_writel(pcie, 0xffffffff, AFI_SM_INTR_ENABLE); + + /* don't enable MSI for now, only when needed */ + afi_writel(pcie, AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK); + + /* disable all exceptions */ + afi_writel(pcie, 0, AFI_FPCI_ERROR_MASKS); + + return 0; +} + +static void tegra_pcie_power_off(struct tegra_pcie *pcie) +{ + int err; + + /* TODO: disable and unprepare clocks? */ + + tegra_periph_reset_assert(pcie->pcie_xclk); + tegra_periph_reset_assert(pcie->afi_clk); + tegra_periph_reset_assert(pcie->pex_clk); + + tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); + + err = regulator_disable(pcie->pex_clk_supply); + if (err < 0) + dev_err(pcie->dev, "failed to disable pex-clk regulator: %d\n", + err); + + err = regulator_disable(pcie->vdd_supply); + if (err < 0) + dev_err(pcie->dev, "failed to disable VDD regulator: %d\n", + err); +} + +static int tegra_pcie_power_on(struct tegra_pcie *pcie) +{ + int err; + + tegra_periph_reset_assert(pcie->pcie_xclk); + tegra_periph_reset_assert(pcie->afi_clk); + tegra_periph_reset_assert(pcie->pex_clk); + + 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; + } + + err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, + pcie->pex_clk); + if (err) { + dev_err(pcie->dev, "powerup sequence failed: %d\n", err); + return err; + } + + tegra_periph_reset_deassert(pcie->afi_clk); + + err = clk_prepare_enable(pcie->afi_clk); + if (err < 0) { + dev_err(pcie->dev, "failed to enable AFI clock: %d\n", err); + return err; + } + + err = clk_prepare_enable(pcie->pll_e); + if (err < 0) { + dev_err(pcie->dev, "failed to enable PLLE clock: %d\n", err); + return err; + } + + return 0; +} + +static int tegra_pcie_clocks_get(struct tegra_pcie *pcie) +{ + pcie->pex_clk = devm_clk_get(pcie->dev, "pex"); + if (IS_ERR(pcie->pex_clk)) + return PTR_ERR(pcie->pex_clk); + + pcie->afi_clk = devm_clk_get(pcie->dev, "afi"); + if (IS_ERR(pcie->afi_clk)) + return PTR_ERR(pcie->afi_clk); + + pcie->pcie_xclk = devm_clk_get(pcie->dev, "pcie_xclk"); + if (IS_ERR(pcie->pcie_xclk)) + return PTR_ERR(pcie->pcie_xclk); + + pcie->pll_e = devm_clk_get(pcie->dev, "pll_e"); + if (IS_ERR(pcie->pll_e)) + return PTR_ERR(pcie->pll_e); + + return 0; +} + +static int tegra_pcie_get_resources(struct tegra_pcie *pcie) +{ + struct platform_device *pdev = to_platform_device(pcie->dev); + struct resource *pads, *afi, *res; + int err; + + err = tegra_pcie_clocks_get(pcie); + if (err) { + dev_err(&pdev->dev, "failed to get clocks: %d\n", err); + return err; + } + + err = tegra_pcie_power_on(pcie); + if (err) { + dev_err(&pdev->dev, "failed to power up: %d\n", err); + return err; + } + + /* request and remap controller registers */ + pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); + if (!pads) { + err = -EADDRNOTAVAIL; + goto poweroff; + } + + afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); + if (!afi) { + err = -EADDRNOTAVAIL; + goto poweroff; + } + + pcie->pads = devm_request_and_ioremap(&pdev->dev, pads); + if (!pcie->pads) { + err = -EADDRNOTAVAIL; + goto poweroff; + } + + pcie->afi = devm_request_and_ioremap(&pdev->dev, afi); + if (!pcie->afi) { + err = -EADDRNOTAVAIL; + goto poweroff; + } + + /* request and remap configuration space */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); + if (!res) { + err = -EADDRNOTAVAIL; + goto poweroff; + } + + pcie->cs = devm_request_mem_region(pcie->dev, res->start, + resource_size(res), res->name); + if (!pcie->cs) { + err = -EADDRNOTAVAIL; + goto poweroff; + } + + /* request interrupt */ + err = platform_get_irq_byname(pdev, "intr"); + if (err < 0) { + dev_err(&pdev->dev, "failed to get IRQ: %d\n", err); + goto poweroff; + } + + pcie->irq = err; + + err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie); + if (err) { + dev_err(&pdev->dev, "failed to register IRQ: %d\n", err); + goto poweroff; + } + + return 0; + +poweroff: + tegra_pcie_power_off(pcie); + return err; +} + +static int tegra_pcie_put_resources(struct tegra_pcie *pcie) +{ + if (pcie->irq > 0) + free_irq(pcie->irq, pcie); + + tegra_pcie_power_off(pcie); + return 0; +} + +static int tegra_msi_alloc(struct tegra_msi *chip) +{ + int msi; + + mutex_lock(&chip->lock); + + msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR); + if (msi < INT_PCI_MSI_NR) + set_bit(msi, chip->used); + else + msi = -ENOSPC; + + mutex_unlock(&chip->lock); + + return msi; +} + +static void tegra_msi_free(struct tegra_msi *chip, unsigned long irq) +{ + struct device *dev = chip->chip.dev; + + mutex_lock(&chip->lock); + + if (!test_bit(irq, chip->used)) + dev_err(dev, "trying to free unused MSI#%lu\n", irq); + else + clear_bit(irq, chip->used); + + mutex_unlock(&chip->lock); +} + +static irqreturn_t tegra_pcie_msi_irq(int irq, void *data) +{ + struct tegra_pcie *pcie = data; + struct tegra_msi *msi = &pcie->msi; + unsigned int i, processed = 0; + + for (i = 0; i < 8; i++) { + unsigned long reg = afi_readl(pcie, AFI_MSI_VEC0 + i * 4); + + while (reg) { + unsigned int offset = find_first_bit(®, 32); + unsigned int index = i * 32 + offset; + unsigned int irq; + + /* clear the interrupt */ + afi_writel(pcie, 1 << offset, AFI_MSI_VEC0 + i * 4); + + irq = irq_find_mapping(msi->domain, index); + if (irq) { + if (test_bit(index, msi->used)) + generic_handle_irq(irq); + else + dev_info(pcie->dev, "unhandled MSI\n"); + } else { + /* + * that's weird who triggered this? + * just clear it + */ + dev_info(pcie->dev, "unexpected MSI\n"); + } + + /* see if there's any more pending in this vector */ + reg = afi_readl(pcie, AFI_MSI_VEC0 + i * 4); + + processed++; + } + } + + return processed > 0 ? IRQ_HANDLED : IRQ_NONE; +} + +static int tegra_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, + struct msi_desc *desc) +{ + struct tegra_msi *msi = to_tegra_msi(chip); + struct msi_msg msg; + unsigned int irq; + int hwirq; + + hwirq = tegra_msi_alloc(msi); + if (hwirq < 0) + return hwirq; + + irq = irq_create_mapping(msi->domain, hwirq); + if (!irq) + return -EINVAL; + + irq_set_msi_desc(irq, desc); + + msg.address_lo = virt_to_phys((void *)msi->pages); + /* 32 bit address only */ + msg.address_hi = 0; + msg.data = hwirq; + + write_msi_msg(irq, &msg); + + return 0; +} + +static void tegra_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) +{ + struct tegra_msi *msi = to_tegra_msi(chip); + struct irq_data *d = irq_get_irq_data(irq); + + tegra_msi_free(msi, d->hwirq); +} + +static struct irq_chip tegra_msi_irq_chip = { + .name = "Tegra PCIe MSI", + .irq_enable = unmask_msi_irq, + .irq_disable = mask_msi_irq, + .irq_mask = mask_msi_irq, + .irq_unmask = unmask_msi_irq, +}; + +static int tegra_msi_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &tegra_msi_irq_chip, handle_simple_irq); + irq_set_chip_data(irq, domain->host_data); + set_irq_flags(irq, IRQF_VALID); + + return 0; +} + +static const struct irq_domain_ops msi_domain_ops = { + .map = tegra_msi_map, +}; + +static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) +{ + struct platform_device *pdev = to_platform_device(pcie->dev); + struct tegra_msi *msi = &pcie->msi; + unsigned long base; + int err; + u32 reg; + + mutex_init(&msi->lock); + + msi->chip.dev = pcie->dev; + msi->chip.setup_irq = tegra_msi_setup_irq; + msi->chip.teardown_irq = tegra_msi_teardown_irq; + + msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR, + &msi_domain_ops, &msi->chip); + if (!msi->domain) { + dev_err(&pdev->dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + err = platform_get_irq_byname(pdev, "msi"); + if (err < 0) { + dev_err(&pdev->dev, "failed to get IRQ: %d\n", err); + goto err; + } + + msi->irq = err; + + err = request_irq(msi->irq, tegra_pcie_msi_irq, 0, + tegra_msi_irq_chip.name, pcie); + if (err < 0) { + dev_err(&pdev->dev, "failed to request IRQ: %d\n", err); + goto err; + } + + /* setup AFI/FPCI range */ + msi->pages = __get_free_pages(GFP_KERNEL, 0); + base = virt_to_phys((void *)msi->pages); + + afi_writel(pcie, base, AFI_MSI_FPCI_BAR_ST); + afi_writel(pcie, base, AFI_MSI_AXI_BAR_ST); + /* this register is in 4K increments */ + afi_writel(pcie, 1, AFI_MSI_BAR_SZ); + + /* enable all MSI vectors */ + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC0); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC1); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC2); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC3); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC4); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC5); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC6); + afi_writel(pcie, 0xffffffff, AFI_MSI_EN_VEC7); + + /* and unmask the MSI interrupt */ + reg = afi_readl(pcie, AFI_INTR_MASK); + reg |= AFI_INTR_MASK_MSI_MASK; + afi_writel(pcie, reg, AFI_INTR_MASK); + + return 0; + +err: + irq_domain_remove(msi->domain); + return err; +} + +static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) +{ + struct tegra_msi *msi = &pcie->msi; + unsigned int i, irq; + u32 value; + + /* mask the MSI interrupt */ + value = afi_readl(pcie, AFI_INTR_MASK); + value &= ~AFI_INTR_MASK_MSI_MASK; + afi_writel(pcie, value, AFI_INTR_MASK); + + /* disable all MSI vectors */ + afi_writel(pcie, 0, AFI_MSI_EN_VEC0); + afi_writel(pcie, 0, AFI_MSI_EN_VEC1); + afi_writel(pcie, 0, AFI_MSI_EN_VEC2); + afi_writel(pcie, 0, AFI_MSI_EN_VEC3); + afi_writel(pcie, 0, AFI_MSI_EN_VEC4); + afi_writel(pcie, 0, AFI_MSI_EN_VEC5); + afi_writel(pcie, 0, AFI_MSI_EN_VEC6); + afi_writel(pcie, 0, AFI_MSI_EN_VEC7); + + free_pages(msi->pages, 0); + + if (msi->irq > 0) + free_irq(msi->irq, pcie); + + for (i = 0; i < INT_PCI_MSI_NR; i++) { + irq = irq_find_mapping(msi->domain, i); + if (irq > 0) + irq_dispose_mapping(irq); + } + + irq_domain_remove(msi->domain); + + return 0; +} + +static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, + u32 *xbar) +{ + struct device_node *np = pcie->dev->of_node; + + switch (lanes) { + case 0x00000004: + dev_info(pcie->dev, "single-mode configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE; + return 0; + + case 0x00000202: + dev_info(pcie->dev, "dual-mode configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL; + return 0; + } + + return -EINVAL; +} + +static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) +{ + struct device_node *np = pcie->dev->of_node, *port; + struct of_pci_range_parser parser; + struct of_pci_range range; + struct resource res; + u32 lanes = 0; + int err; + + 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); + + 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"; + break; + + case IORESOURCE_MEM: + if (res.flags & IORESOURCE_PREFETCH) { + memcpy(&pcie->prefetch, &res, sizeof(res)); + pcie->prefetch.name = "PREFETCH"; + } else { + memcpy(&pcie->mem, &res, sizeof(res)); + pcie->mem.name = "MEM"; + } + break; + } + } + + err = of_pci_parse_bus_range(np, &pcie->busn); + if (err < 0) { + dev_err(pcie->dev, "failed to parse ranges property: %d\n", + err); + pcie->busn.name = np->name; + pcie->busn.start = 0; + pcie->busn.end = 0xff; + pcie->busn.flags = IORESOURCE_BUS; + } + + /* parse root ports */ + for_each_child_of_node(np, port) { + struct tegra_pcie_port *rp; + unsigned int index; + u32 value; + + err = of_pci_get_devfn(port); + if (err < 0) { + dev_err(pcie->dev, "failed to parse address: %d\n", + err); + return err; + } + + index = PCI_SLOT(err); + + if (index < 1 || index > TEGRA_MAX_PORTS) { + dev_err(pcie->dev, "invalid port number: %d\n", index); + return -EINVAL; + } + + index--; + + err = of_property_read_u32(port, "nvidia,num-lanes", &value); + if (err < 0) { + dev_err(pcie->dev, "failed to parse # of lanes: %d\n", + err); + return err; + } + + if (value > 16) { + dev_err(pcie->dev, "invalid # of lanes: %u\n", value); + return -EINVAL; + } + + lanes |= value << (index << 3); + + if (!of_device_is_available(port)) + continue; + + rp = devm_kzalloc(pcie->dev, sizeof(*rp), GFP_KERNEL); + if (!rp) + return -ENOMEM; + + err = of_address_to_resource(port, 0, &rp->regs); + if (err < 0) { + dev_err(pcie->dev, "failed to parse address: %d\n", + err); + return err; + } + + INIT_LIST_HEAD(&rp->list); + rp->index = index; + rp->lanes = value; + rp->pcie = pcie; + + rp->base = devm_request_and_ioremap(pcie->dev, &rp->regs); + if (!rp->base) + return -EADDRNOTAVAIL; + + list_add_tail(&rp->list, &pcie->ports); + } + + err = tegra_pcie_get_xbar_config(pcie, lanes, &pcie->xbar_config); + if (err < 0) { + dev_err(pcie->dev, "invalid lane configuration\n"); + return err; + } + + return 0; +} + +/* + * FIXME: If there are no PCIe cards attached, then calling this function + * can result in the increase of the bootup time as there are big timeout + * loops. + */ +#define TEGRA_PCIE_LINKUP_TIMEOUT 200 /* up to 1.2 seconds */ +static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port) +{ + unsigned int retries = 3; + unsigned long value; + + do { + unsigned int timeout = TEGRA_PCIE_LINKUP_TIMEOUT; + + do { + value = readl(port->base + RP_VEND_XP); + + if (value & RP_VEND_XP_DL_UP) + break; + + usleep_range(1000, 2000); + } while (--timeout); + + if (!timeout) { + dev_err(port->pcie->dev, "link %u down, retrying\n", + port->index); + goto retry; + } + + timeout = TEGRA_PCIE_LINKUP_TIMEOUT; + + do { + value = readl(port->base + RP_LINK_CONTROL_STATUS); + + if (value & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) + return true; + + usleep_range(1000, 2000); + } while (--timeout); + +retry: + tegra_pcie_port_reset(port); + } while (--retries); + + return false; +} + +static int tegra_pcie_enable(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port, *tmp; + struct hw_pci hw; + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + dev_info(pcie->dev, "probing port %u, using %u lanes\n", + port->index, port->lanes); + + tegra_pcie_port_enable(port); + + if (tegra_pcie_port_check_link(port)) + continue; + + dev_info(pcie->dev, "link %u down, ignoring\n", port->index); + + tegra_pcie_port_disable(port); + tegra_pcie_port_free(port); + } + + memset(&hw, 0, sizeof(hw)); + + hw.nr_controllers = 1; + hw.private_data = (void **)&pcie; + hw.setup = tegra_pcie_setup; + hw.map_irq = tegra_pcie_map_irq; + hw.add_bus = tegra_pcie_add_bus; + hw.scan = tegra_pcie_scan_bus; + hw.ops = &tegra_pcie_ops; + + pci_common_init_dev(pcie->dev, &hw); + + return 0; +} + +static int tegra_pcie_probe(struct platform_device *pdev) +{ + struct tegra_pcie *pcie; + int err; + + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + INIT_LIST_HEAD(&pcie->busses); + INIT_LIST_HEAD(&pcie->ports); + pcie->dev = &pdev->dev; + + err = tegra_pcie_parse_dt(pcie); + if (err < 0) + return err; + + pcibios_min_mem = 0; + + err = tegra_pcie_get_resources(pcie); + if (err < 0) { + dev_err(&pdev->dev, "failed to request resources: %d\n", err); + return err; + } + + err = tegra_pcie_enable_controller(pcie); + if (err) + goto put_resources; + + /* setup the AFI address translations */ + tegra_pcie_setup_translations(pcie); + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + err = tegra_pcie_enable_msi(pcie); + if (err < 0) { + dev_err(&pdev->dev, + "failed to enable MSI support: %d\n", + err); + goto put_resources; + } + } + + err = tegra_pcie_enable(pcie); + if (err < 0) { + dev_err(&pdev->dev, "failed to enable PCIe ports: %d\n", err); + goto disable_msi; + } + + platform_set_drvdata(pdev, pcie); + return 0; + +disable_msi: + if (IS_ENABLED(CONFIG_PCI_MSI)) + tegra_pcie_disable_msi(pcie); +put_resources: + tegra_pcie_put_resources(pcie); + return err; +} + +static const struct of_device_id tegra_pcie_of_match[] = { + { .compatible = "nvidia,tegra20-pcie", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_pcie_of_match); + +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, +}; +module_platform_driver(tegra_pcie_driver); + +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); +MODULE_DESCRIPTION("NVIDIA Tegra PCIe driver"); +MODULE_LICENSE("GPLv2"); |