On Thu, Jul 25, 2013 at 11:53 AM, Thierry Reding <thierry.reding@xxxxxxxxx> wrote: > From: Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx> > > Move the PCIe driver from arch/arm/mach-tegra into the drivers/pci/host > directory. The motivation is to collect various host controller drivers > in the same location in order to facilitate refactoring. > > The Tegra PCIe driver has been largely rewritten, both in order to turn > it into a proper platform driver and to add MSI (based on code by > Krishna Kishore <kthota@xxxxxxxxxx>) as well as device tree support. > > Signed-off-by: Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx> > Signed-off-by: Thierry Reding <treding@xxxxxxxxxx> Acked-by: Bjorn Helgaas <bhelgaas@xxxxxxxxxx> > --- > Changes in v2: > - remove Harmony and TrimSlice leftovers > - use symbolic names for interrupt codes > - fix root port reset sequence > - depend on ARCH_TEGRA > - only enable configured root ports > - make regulators mandatory > - fix build with !PCI_MSI > - derive XBAR configuration from nvidia,num-lanes property > - remap configuration space on a per-bus basis > > Changes in v3: > - move Tegra30 specific bits into a follow-up patch > - update DT binding to be more spec-conformant > - update for recent OF ranges parser changes > - use new MSI chip infrastructure > > Changes in v4: > - update binding documentation as per Stephen's comments > > Changes in v5: > - update for latest OF ranges parsing API > - use pci_common_init_dev() instead of pci_common_init() > - add hw_pci.add_bus implementation to attach MSI chip > - fixup any remaining checkpatch warnings > - use define for link active status bit > - move msi_chip structure to the beginning of struct tegra_msi to maybe > save some bytes when upcasting > - refactor common code from tegra_pcie_{read,write}_conf() > - don't use potentially dangerous devm_request_irq() > - return IRQ_NONE if the MSI handler doesn't detect any pending > interrupts > - don't read AFI_MSI_AXI_BAR_ST when setting up MSI messages (the same > data is available in msi->pages) > - request a single page (instead of 8) for the MSI BAR > - return an error when detecting an invalid lane configuration > - remove driver's .remove() function and set .suppress_bind_attrs to > prevent the driver from being unbound from the device via sysfs > - add MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE > - export OF device table > - use symbolic constants for interrupts and clocks > > .../bindings/pci/nvidia,tegra20-pcie.txt | 161 ++ > arch/arm/boot/dts/tegra20.dtsi | 55 + > arch/arm/mach-tegra/Kconfig | 7 +- > arch/arm/mach-tegra/Makefile | 3 - > arch/arm/mach-tegra/board-harmony-pcie.c | 89 -- > arch/arm/mach-tegra/board.h | 8 - > arch/arm/mach-tegra/iomap.h | 3 - > arch/arm/mach-tegra/pcie.c | 864 ----------- > arch/arm/mach-tegra/tegra.c | 24 - > drivers/pci/host/Kconfig | 4 + > drivers/pci/host/Makefile | 1 + > drivers/pci/host/pci-tegra.c | 1540 ++++++++++++++++++++ > 12 files changed, 1763 insertions(+), 996 deletions(-) > create mode 100644 Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt > delete mode 100644 arch/arm/mach-tegra/board-harmony-pcie.c > delete mode 100644 arch/arm/mach-tegra/pcie.c > create mode 100644 drivers/pci/host/pci-tegra.c > > 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 0000000..90c112f > --- /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/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi > index 9653fd8..ecd016a 100644 > --- a/arch/arm/boot/dts/tegra20.dtsi > +++ b/arch/arm/boot/dts/tegra20.dtsi > @@ -455,6 +455,61 @@ > #size-cells = <0>; > }; > > + 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 = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH /* controller interrupt */ > + GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* 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 TEGRA20_CLK_PEX>, > + <&tegra_car TEGRA20_CLK_AFI>, > + <&tegra_car TEGRA20_CLK_PCIE_XCLK>, > + <&tegra_car TEGRA20_CLK_PLL_E>; > + 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>; > + }; > + }; > + > usb@c5000000 { > compatible = "nvidia,tegra20-ehci", "usb-ehci"; > reg = <0xc5000000 0x4000>; > diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig > index 80d3b82..f3ba4c6 100644 > --- a/arch/arm/mach-tegra/Kconfig > +++ b/arch/arm/mach-tegra/Kconfig > @@ -15,6 +15,8 @@ config ARCH_TEGRA > select SOC_BUS > select SPARSE_IRQ > select USE_OF > + select MIGHT_HAVE_PCI > + select ARCH_SUPPORTS_MSI > help > This enables support for NVIDIA Tegra based systems. > > @@ -69,11 +71,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 98b184e..01a0e6f 100644 > --- a/arch/arm/mach-tegra/Makefile > +++ b/arch/arm/mach-tegra/Makefile > @@ -27,7 +27,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 > @@ -35,6 +34,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 035b240..0000000 > --- 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@xxxxxxxxxxxxxx> > - * > - * 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 9a6659f..db6810d 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 399fbca..d228421 100644 > --- a/arch/arm/mach-tegra/iomap.h > +++ b/arch/arm/mach-tegra/iomap.h > @@ -278,9 +278,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 6c1989b..0000000 > --- a/arch/arm/mach-tegra/pcie.c > +++ /dev/null > @@ -1,864 +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@xxxxxxxxxxxxxx> > - * > - * 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" > -#include "pmc.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) > - > -/* > - * 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_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_pmc_pcie_xclk_clamp(true); > -} > - > -static int tegra_pcie_power_regate(void) > -{ > - int err; > - > - tegra_pcie_power_off(); > - > - tegra_pmc_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_pmc_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 0d1e412..fe56fca 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 1184ff6..5f33746 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 086d850..a733fb0 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 0000000..223069a > --- /dev/null > +++ b/drivers/pci/host/pci-tegra.c > @@ -0,0 +1,1540 @@ > +/* > + * PCIe host controller driver for TEGRA(2) SOCs > + * > + * Copyright (c) 2010, CompuLab, Ltd. > + * Author: Mike Rapoport <mike@xxxxxxxxxxxxxx> > + * > + * 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-pmc.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); > + tegra_pmc_pcie_xclk_clamp(true); > + > + 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); > + tegra_pmc_pcie_xclk_clamp(true); > + > + /* 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); > + > + tegra_pmc_pcie_xclk_clamp(false); > + > + 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@xxxxxxxxxx>"); > +MODULE_DESCRIPTION("NVIDIA Tegra PCIe driver"); > +MODULE_LICENSE("GPLv2"); > -- > 1.8.1.5 > -- To unsubscribe from this list: send the line "unsubscribe linux-tegra" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html