[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAErSpo403JN+49WCTCSZ6rDtwTXbHb=R58p8pVvxx=6Ghg_ACg@mail.gmail.com>
Date: Thu, 25 Jul 2013 12:00:08 -0600
From: Bjorn Helgaas <bhelgaas@...gle.com>
To: Thierry Reding <thierry.reding@...il.com>
Cc: Stephen Warren <swarren@...dotorg.org>,
Russell King <linux@....linux.org.uk>,
Jason Cooper <jason@...edaemon.net>,
Thomas Petazzoni <thomas.petazzoni@...e-electrons.com>,
Jay Agarwal <jagarwal@...dia.com>,
"linux-pci@...r.kernel.org" <linux-pci@...r.kernel.org>,
"linux-tegra@...r.kernel.org" <linux-tegra@...r.kernel.org>,
devicetree@...r.kernel.org,
linux-arm <linux-arm-kernel@...ts.infradead.org>,
"linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH v5 03/16] PCI: tegra: Move PCIe driver to drivers/pci/host
On Thu, Jul 25, 2013 at 11:53 AM, Thierry Reding
<thierry.reding@...il.com> wrote:
> From: Thierry Reding <thierry.reding@...onic-design.de>
>
> 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@...dia.com>) as well as device tree support.
>
> Signed-off-by: Thierry Reding <thierry.reding@...onic-design.de>
> Signed-off-by: Thierry Reding <treding@...dia.com>
Acked-by: Bjorn Helgaas <bhelgaas@...gle.com>
> ---
> 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@...00000 {
> 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@...pulab.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 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@...pulab.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"
> -#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@...pulab.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-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@...dia.com>");
> +MODULE_DESCRIPTION("NVIDIA Tegra PCIe driver");
> +MODULE_LICENSE("GPLv2");
> --
> 1.8.1.5
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists