PCI: tegra: Add Tegra 30 PCIe support
Introduce a data structure to parameterize the driver according to SoC generation, add Tegra30 specific code and update the device tree binding document for Tegra30 support. Signed-off-by: Jay Agarwal <jagarwal@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com> Acked-by: Bjorn Helgaas <bhelgaas@google.com> Signed-off-by: Stephen Warren <swarren@nvidia.com>
This commit is contained in:
parent
d1523b52bf
commit
94716cddbe
|
@ -1,7 +1,7 @@
|
||||||
NVIDIA Tegra PCIe controller
|
NVIDIA Tegra PCIe controller
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: "nvidia,tegra20-pcie"
|
- compatible: "nvidia,tegra20-pcie" or "nvidia,tegra30-pcie"
|
||||||
- device_type: Must be "pci"
|
- device_type: Must be "pci"
|
||||||
- reg: A list of physical base address and length for each set of controller
|
- 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.
|
registers. Must contain an entry for each entry in the reg-names property.
|
||||||
|
@ -16,6 +16,7 @@ Required properties:
|
||||||
"msi": The Tegra interrupt that is asserted when an MSI is received
|
"msi": The Tegra interrupt that is asserted when an MSI is received
|
||||||
- pex-clk-supply: Supply voltage for internal reference clock
|
- pex-clk-supply: Supply voltage for internal reference clock
|
||||||
- vdd-supply: Power supply for controller (1.05V)
|
- vdd-supply: Power supply for controller (1.05V)
|
||||||
|
- avdd-supply: Power supply for controller (1.05V) (not required for Tegra20)
|
||||||
- bus-range: Range of bus numbers associated with this controller
|
- bus-range: Range of bus numbers associated with this controller
|
||||||
- #address-cells: Address representation for root ports (must be 3)
|
- #address-cells: Address representation for root ports (must be 3)
|
||||||
- cell 0 specifies the bus and device numbers of the root port:
|
- cell 0 specifies the bus and device numbers of the root port:
|
||||||
|
@ -48,6 +49,7 @@ Required properties:
|
||||||
"afi": The Tegra clock of that name
|
"afi": The Tegra clock of that name
|
||||||
"pcie_xclk": The Tegra clock of that name
|
"pcie_xclk": The Tegra clock of that name
|
||||||
"pll_e": The Tegra clock of that name
|
"pll_e": The Tegra clock of that name
|
||||||
|
"cml": The Tegra clock of that name (not required for Tegra20)
|
||||||
|
|
||||||
Root ports are defined as subnodes of the PCIe controller node.
|
Root ports are defined as subnodes of the PCIe controller node.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* PCIe host controller driver for TEGRA(2) SOCs
|
* PCIe host controller driver for Tegra SoCs
|
||||||
*
|
*
|
||||||
* Copyright (c) 2010, CompuLab, Ltd.
|
* Copyright (c) 2010, CompuLab, Ltd.
|
||||||
* Author: Mike Rapoport <mike@compulab.co.il>
|
* Author: Mike Rapoport <mike@compulab.co.il>
|
||||||
|
@ -50,7 +50,6 @@
|
||||||
#include <asm/mach/pci.h>
|
#include <asm/mach/pci.h>
|
||||||
|
|
||||||
#define INT_PCI_MSI_NR (8 * 32)
|
#define INT_PCI_MSI_NR (8 * 32)
|
||||||
#define TEGRA_MAX_PORTS 2
|
|
||||||
|
|
||||||
/* register definitions */
|
/* register definitions */
|
||||||
|
|
||||||
|
@ -142,22 +141,30 @@
|
||||||
#define AFI_INTR_EN_DFPCI_DECERR (1 << 5)
|
#define AFI_INTR_EN_DFPCI_DECERR (1 << 5)
|
||||||
#define AFI_INTR_EN_AXI_DECERR (1 << 6)
|
#define AFI_INTR_EN_AXI_DECERR (1 << 6)
|
||||||
#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7)
|
#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7)
|
||||||
|
#define AFI_INTR_EN_PRSNT_SENSE (1 << 8)
|
||||||
|
|
||||||
#define AFI_PCIE_CONFIG 0x0f8
|
#define AFI_PCIE_CONFIG 0x0f8
|
||||||
#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1))
|
#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1))
|
||||||
#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe
|
#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_MASK (0xf << 20)
|
||||||
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20)
|
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20)
|
||||||
|
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20)
|
||||||
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20)
|
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20)
|
||||||
|
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20)
|
||||||
|
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20)
|
||||||
|
|
||||||
#define AFI_FUSE 0x104
|
#define AFI_FUSE 0x104
|
||||||
#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2)
|
#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2)
|
||||||
|
|
||||||
#define AFI_PEX0_CTRL 0x110
|
#define AFI_PEX0_CTRL 0x110
|
||||||
#define AFI_PEX1_CTRL 0x118
|
#define AFI_PEX1_CTRL 0x118
|
||||||
|
#define AFI_PEX2_CTRL 0x128
|
||||||
#define AFI_PEX_CTRL_RST (1 << 0)
|
#define AFI_PEX_CTRL_RST (1 << 0)
|
||||||
|
#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1)
|
||||||
#define AFI_PEX_CTRL_REFCLK_EN (1 << 3)
|
#define AFI_PEX_CTRL_REFCLK_EN (1 << 3)
|
||||||
|
|
||||||
|
#define AFI_PEXBIAS_CTRL_0 0x168
|
||||||
|
|
||||||
#define RP_VEND_XP 0x00000F00
|
#define RP_VEND_XP 0x00000F00
|
||||||
#define RP_VEND_XP_DL_UP (1 << 30)
|
#define RP_VEND_XP_DL_UP (1 << 30)
|
||||||
|
|
||||||
|
@ -172,7 +179,8 @@
|
||||||
#define PADS_CTL_TX_DATA_EN_1L (1 << 6)
|
#define PADS_CTL_TX_DATA_EN_1L (1 << 6)
|
||||||
#define PADS_CTL_RX_DATA_EN_1L (1 << 10)
|
#define PADS_CTL_RX_DATA_EN_1L (1 << 10)
|
||||||
|
|
||||||
#define PADS_PLL_CTL 0x000000B8
|
#define PADS_PLL_CTL_TEGRA20 0x000000B8
|
||||||
|
#define PADS_PLL_CTL_TEGRA30 0x000000B4
|
||||||
#define PADS_PLL_CTL_RST_B4SM (1 << 1)
|
#define PADS_PLL_CTL_RST_B4SM (1 << 1)
|
||||||
#define PADS_PLL_CTL_LOCKDET (1 << 8)
|
#define PADS_PLL_CTL_LOCKDET (1 << 8)
|
||||||
#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16)
|
#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16)
|
||||||
|
@ -182,6 +190,10 @@
|
||||||
#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20)
|
#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20)
|
||||||
#define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 20)
|
#define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 20)
|
||||||
#define PADS_PLL_CTL_TXCLKREF_DIV5 (1 << 20)
|
#define PADS_PLL_CTL_TXCLKREF_DIV5 (1 << 20)
|
||||||
|
#define PADS_PLL_CTL_TXCLKREF_BUF_EN (1 << 22)
|
||||||
|
|
||||||
|
#define PADS_REFCLK_CFG0 0x000000C8
|
||||||
|
#define PADS_REFCLK_CFG1 0x000000CC
|
||||||
|
|
||||||
struct tegra_msi {
|
struct tegra_msi {
|
||||||
struct msi_chip chip;
|
struct msi_chip chip;
|
||||||
|
@ -192,6 +204,19 @@ struct tegra_msi {
|
||||||
int irq;
|
int irq;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* used to differentiate between Tegra SoC generations */
|
||||||
|
struct tegra_pcie_soc_data {
|
||||||
|
unsigned int num_ports;
|
||||||
|
unsigned int msi_base_shift;
|
||||||
|
u32 pads_pll_ctl;
|
||||||
|
u32 tx_ref_sel;
|
||||||
|
bool has_pex_clkreq_en;
|
||||||
|
bool has_pex_bias_ctrl;
|
||||||
|
bool has_intr_prsnt_sense;
|
||||||
|
bool has_avdd_supply;
|
||||||
|
bool has_cml_clk;
|
||||||
|
};
|
||||||
|
|
||||||
static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip)
|
static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip)
|
||||||
{
|
{
|
||||||
return container_of(chip, struct tegra_msi, chip);
|
return container_of(chip, struct tegra_msi, chip);
|
||||||
|
@ -216,6 +241,7 @@ struct tegra_pcie {
|
||||||
struct clk *afi_clk;
|
struct clk *afi_clk;
|
||||||
struct clk *pcie_xclk;
|
struct clk *pcie_xclk;
|
||||||
struct clk *pll_e;
|
struct clk *pll_e;
|
||||||
|
struct clk *cml_clk;
|
||||||
|
|
||||||
struct tegra_msi msi;
|
struct tegra_msi msi;
|
||||||
|
|
||||||
|
@ -225,6 +251,9 @@ struct tegra_pcie {
|
||||||
|
|
||||||
struct regulator *pex_clk_supply;
|
struct regulator *pex_clk_supply;
|
||||||
struct regulator *vdd_supply;
|
struct regulator *vdd_supply;
|
||||||
|
struct regulator *avdd_supply;
|
||||||
|
|
||||||
|
const struct tegra_pcie_soc_data *soc_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tegra_pcie_port {
|
struct tegra_pcie_port {
|
||||||
|
@ -469,6 +498,10 @@ static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port)
|
||||||
case 1:
|
case 1:
|
||||||
ret = AFI_PEX1_CTRL;
|
ret = AFI_PEX1_CTRL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
ret = AFI_PEX2_CTRL;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -493,12 +526,17 @@ static void tegra_pcie_port_reset(struct tegra_pcie_port *port)
|
||||||
|
|
||||||
static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
|
static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
|
||||||
{
|
{
|
||||||
|
const struct tegra_pcie_soc_data *soc = port->pcie->soc_data;
|
||||||
unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
|
unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
|
||||||
unsigned long value;
|
unsigned long value;
|
||||||
|
|
||||||
/* enable reference clock */
|
/* enable reference clock */
|
||||||
value = afi_readl(port->pcie, ctrl);
|
value = afi_readl(port->pcie, ctrl);
|
||||||
value |= AFI_PEX_CTRL_REFCLK_EN;
|
value |= AFI_PEX_CTRL_REFCLK_EN;
|
||||||
|
|
||||||
|
if (soc->has_pex_clkreq_en)
|
||||||
|
value |= AFI_PEX_CTRL_CLKREQ_EN;
|
||||||
|
|
||||||
afi_writel(port->pcie, value, ctrl);
|
afi_writel(port->pcie, value, ctrl);
|
||||||
|
|
||||||
tegra_pcie_port_reset(port);
|
tegra_pcie_port_reset(port);
|
||||||
|
@ -551,6 +589,8 @@ static void tegra_pcie_fixup_class(struct pci_dev *dev)
|
||||||
}
|
}
|
||||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_fixup_class);
|
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);
|
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class);
|
||||||
|
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1c, tegra_pcie_fixup_class);
|
||||||
|
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1d, tegra_pcie_fixup_class);
|
||||||
|
|
||||||
/* Tegra PCIE requires relaxed ordering */
|
/* Tegra PCIE requires relaxed ordering */
|
||||||
static void tegra_pcie_relax_enable(struct pci_dev *dev)
|
static void tegra_pcie_relax_enable(struct pci_dev *dev)
|
||||||
|
@ -723,10 +763,15 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
|
||||||
|
|
||||||
static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
|
static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
|
||||||
{
|
{
|
||||||
|
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||||
struct tegra_pcie_port *port;
|
struct tegra_pcie_port *port;
|
||||||
unsigned int timeout;
|
unsigned int timeout;
|
||||||
unsigned long value;
|
unsigned long value;
|
||||||
|
|
||||||
|
/* power down PCIe slot clock bias pad */
|
||||||
|
if (soc->has_pex_bias_ctrl)
|
||||||
|
afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
|
||||||
|
|
||||||
/* configure mode and disable all ports */
|
/* configure mode and disable all ports */
|
||||||
value = afi_readl(pcie, AFI_PCIE_CONFIG);
|
value = afi_readl(pcie, AFI_PCIE_CONFIG);
|
||||||
value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
|
value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK;
|
||||||
|
@ -753,27 +798,26 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
|
||||||
* Set up PHY PLL inputs select PLLE output as refclock,
|
* Set up PHY PLL inputs select PLLE output as refclock,
|
||||||
* set TX ref sel to div10 (not div5).
|
* set TX ref sel to div10 (not div5).
|
||||||
*/
|
*/
|
||||||
value = pads_readl(pcie, PADS_PLL_CTL);
|
value = pads_readl(pcie, soc->pads_pll_ctl);
|
||||||
value &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK);
|
value &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK);
|
||||||
value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML |
|
value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel;
|
||||||
PADS_PLL_CTL_TXCLKREF_DIV10;
|
pads_writel(pcie, value, soc->pads_pll_ctl);
|
||||||
pads_writel(pcie, value, PADS_PLL_CTL);
|
|
||||||
|
|
||||||
/* take PLL out of reset */
|
/* take PLL out of reset */
|
||||||
value = pads_readl(pcie, PADS_PLL_CTL);
|
value = pads_readl(pcie, soc->pads_pll_ctl);
|
||||||
value |= PADS_PLL_CTL_RST_B4SM;
|
value |= PADS_PLL_CTL_RST_B4SM;
|
||||||
pads_writel(pcie, value, PADS_PLL_CTL);
|
pads_writel(pcie, value, soc->pads_pll_ctl);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hack, set the clock voltage to the DEFAULT provided by hw folks.
|
* Hack, set the clock voltage to the DEFAULT provided by hw folks.
|
||||||
* This doesn't exist in the documentation.
|
* This doesn't exist in the documentation.
|
||||||
*/
|
*/
|
||||||
pads_writel(pcie, 0xfa5cfa5c, 0xc8);
|
pads_writel(pcie, 0xfa5cfa5c, PADS_REFCLK_CFG0);
|
||||||
|
|
||||||
/* wait for the PLL to lock */
|
/* wait for the PLL to lock */
|
||||||
timeout = 300;
|
timeout = 300;
|
||||||
do {
|
do {
|
||||||
value = pads_readl(pcie, PADS_PLL_CTL);
|
value = pads_readl(pcie, soc->pads_pll_ctl);
|
||||||
usleep_range(1000, 2000);
|
usleep_range(1000, 2000);
|
||||||
if (--timeout == 0) {
|
if (--timeout == 0) {
|
||||||
pr_err("Tegra PCIe error: timeout waiting for PLL\n");
|
pr_err("Tegra PCIe error: timeout waiting for PLL\n");
|
||||||
|
@ -802,6 +846,10 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
|
||||||
value = AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR |
|
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_SLVERR | AFI_INTR_EN_TGT_DECERR |
|
||||||
AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR;
|
AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR;
|
||||||
|
|
||||||
|
if (soc->has_intr_prsnt_sense)
|
||||||
|
value |= AFI_INTR_EN_PRSNT_SENSE;
|
||||||
|
|
||||||
afi_writel(pcie, value, AFI_AFI_INTR_ENABLE);
|
afi_writel(pcie, value, AFI_AFI_INTR_ENABLE);
|
||||||
afi_writel(pcie, 0xffffffff, AFI_SM_INTR_ENABLE);
|
afi_writel(pcie, 0xffffffff, AFI_SM_INTR_ENABLE);
|
||||||
|
|
||||||
|
@ -816,6 +864,7 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
|
||||||
|
|
||||||
static void tegra_pcie_power_off(struct tegra_pcie *pcie)
|
static void tegra_pcie_power_off(struct tegra_pcie *pcie)
|
||||||
{
|
{
|
||||||
|
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* TODO: disable and unprepare clocks? */
|
/* TODO: disable and unprepare clocks? */
|
||||||
|
@ -826,19 +875,28 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie)
|
||||||
|
|
||||||
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
|
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
|
||||||
|
|
||||||
|
if (soc->has_avdd_supply) {
|
||||||
|
err = regulator_disable(pcie->avdd_supply);
|
||||||
|
if (err < 0)
|
||||||
|
dev_warn(pcie->dev,
|
||||||
|
"failed to disable AVDD regulator: %d\n",
|
||||||
|
err);
|
||||||
|
}
|
||||||
|
|
||||||
err = regulator_disable(pcie->pex_clk_supply);
|
err = regulator_disable(pcie->pex_clk_supply);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
dev_err(pcie->dev, "failed to disable pex-clk regulator: %d\n",
|
dev_warn(pcie->dev, "failed to disable pex-clk regulator: %d\n",
|
||||||
err);
|
err);
|
||||||
|
|
||||||
err = regulator_disable(pcie->vdd_supply);
|
err = regulator_disable(pcie->vdd_supply);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
dev_err(pcie->dev, "failed to disable VDD regulator: %d\n",
|
dev_warn(pcie->dev, "failed to disable VDD regulator: %d\n",
|
||||||
err);
|
err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tegra_pcie_power_on(struct tegra_pcie *pcie)
|
static int tegra_pcie_power_on(struct tegra_pcie *pcie)
|
||||||
{
|
{
|
||||||
|
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
tegra_periph_reset_assert(pcie->pcie_xclk);
|
tegra_periph_reset_assert(pcie->pcie_xclk);
|
||||||
|
@ -861,6 +919,16 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (soc->has_avdd_supply) {
|
||||||
|
err = regulator_enable(pcie->avdd_supply);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(pcie->dev,
|
||||||
|
"failed to enable AVDD regulator: %d\n",
|
||||||
|
err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE,
|
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE,
|
||||||
pcie->pex_clk);
|
pcie->pex_clk);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -876,6 +944,15 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (soc->has_cml_clk) {
|
||||||
|
err = clk_prepare_enable(pcie->cml_clk);
|
||||||
|
if (err < 0) {
|
||||||
|
dev_err(pcie->dev, "failed to enable CML clock: %d\n",
|
||||||
|
err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = clk_prepare_enable(pcie->pll_e);
|
err = clk_prepare_enable(pcie->pll_e);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(pcie->dev, "failed to enable PLLE clock: %d\n", err);
|
dev_err(pcie->dev, "failed to enable PLLE clock: %d\n", err);
|
||||||
|
@ -887,6 +964,8 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
|
||||||
|
|
||||||
static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
|
static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
|
||||||
{
|
{
|
||||||
|
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||||
|
|
||||||
pcie->pex_clk = devm_clk_get(pcie->dev, "pex");
|
pcie->pex_clk = devm_clk_get(pcie->dev, "pex");
|
||||||
if (IS_ERR(pcie->pex_clk))
|
if (IS_ERR(pcie->pex_clk))
|
||||||
return PTR_ERR(pcie->pex_clk);
|
return PTR_ERR(pcie->pex_clk);
|
||||||
|
@ -903,6 +982,12 @@ static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
|
||||||
if (IS_ERR(pcie->pll_e))
|
if (IS_ERR(pcie->pll_e))
|
||||||
return PTR_ERR(pcie->pll_e);
|
return PTR_ERR(pcie->pll_e);
|
||||||
|
|
||||||
|
if (soc->has_cml_clk) {
|
||||||
|
pcie->cml_clk = devm_clk_get(pcie->dev, "cml");
|
||||||
|
if (IS_ERR(pcie->cml_clk))
|
||||||
|
return PTR_ERR(pcie->cml_clk);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1127,6 +1212,7 @@ static const struct irq_domain_ops msi_domain_ops = {
|
||||||
static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
|
static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(pcie->dev);
|
struct platform_device *pdev = to_platform_device(pcie->dev);
|
||||||
|
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||||
struct tegra_msi *msi = &pcie->msi;
|
struct tegra_msi *msi = &pcie->msi;
|
||||||
unsigned long base;
|
unsigned long base;
|
||||||
int err;
|
int err;
|
||||||
|
@ -1164,7 +1250,7 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
|
||||||
msi->pages = __get_free_pages(GFP_KERNEL, 0);
|
msi->pages = __get_free_pages(GFP_KERNEL, 0);
|
||||||
base = virt_to_phys((void *)msi->pages);
|
base = virt_to_phys((void *)msi->pages);
|
||||||
|
|
||||||
afi_writel(pcie, base, AFI_MSI_FPCI_BAR_ST);
|
afi_writel(pcie, base >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST);
|
||||||
afi_writel(pcie, base, AFI_MSI_AXI_BAR_ST);
|
afi_writel(pcie, base, AFI_MSI_AXI_BAR_ST);
|
||||||
/* this register is in 4K increments */
|
/* this register is in 4K increments */
|
||||||
afi_writel(pcie, 1, AFI_MSI_BAR_SZ);
|
afi_writel(pcie, 1, AFI_MSI_BAR_SZ);
|
||||||
|
@ -1233,16 +1319,35 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
|
||||||
{
|
{
|
||||||
struct device_node *np = pcie->dev->of_node;
|
struct device_node *np = pcie->dev->of_node;
|
||||||
|
|
||||||
switch (lanes) {
|
if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
|
||||||
case 0x00000004:
|
switch (lanes) {
|
||||||
dev_info(pcie->dev, "single-mode configuration\n");
|
case 0x00000204:
|
||||||
*xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE;
|
dev_info(pcie->dev, "4x1, 2x1 configuration\n");
|
||||||
return 0;
|
*xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420;
|
||||||
|
return 0;
|
||||||
|
|
||||||
case 0x00000202:
|
case 0x00020202:
|
||||||
dev_info(pcie->dev, "dual-mode configuration\n");
|
dev_info(pcie->dev, "2x3 configuration\n");
|
||||||
*xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL;
|
*xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
case 0x00010104:
|
||||||
|
dev_info(pcie->dev, "4x1, 1x2 configuration\n");
|
||||||
|
*xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) {
|
||||||
|
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;
|
return -EINVAL;
|
||||||
|
@ -1250,6 +1355,7 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
|
||||||
|
|
||||||
static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
||||||
{
|
{
|
||||||
|
const struct tegra_pcie_soc_data *soc = pcie->soc_data;
|
||||||
struct device_node *np = pcie->dev->of_node, *port;
|
struct device_node *np = pcie->dev->of_node, *port;
|
||||||
struct of_pci_range_parser parser;
|
struct of_pci_range_parser parser;
|
||||||
struct of_pci_range range;
|
struct of_pci_range range;
|
||||||
|
@ -1270,6 +1376,12 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
||||||
if (IS_ERR(pcie->pex_clk_supply))
|
if (IS_ERR(pcie->pex_clk_supply))
|
||||||
return PTR_ERR(pcie->pex_clk_supply);
|
return PTR_ERR(pcie->pex_clk_supply);
|
||||||
|
|
||||||
|
if (soc->has_avdd_supply) {
|
||||||
|
pcie->avdd_supply = devm_regulator_get(pcie->dev, "avdd");
|
||||||
|
if (IS_ERR(pcie->avdd_supply))
|
||||||
|
return PTR_ERR(pcie->avdd_supply);
|
||||||
|
}
|
||||||
|
|
||||||
for_each_of_pci_range(&parser, &range) {
|
for_each_of_pci_range(&parser, &range) {
|
||||||
of_pci_range_to_resource(&range, np, &res);
|
of_pci_range_to_resource(&range, np, &res);
|
||||||
|
|
||||||
|
@ -1316,7 +1428,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
||||||
|
|
||||||
index = PCI_SLOT(err);
|
index = PCI_SLOT(err);
|
||||||
|
|
||||||
if (index < 1 || index > TEGRA_MAX_PORTS) {
|
if (index < 1 || index > soc->num_ports) {
|
||||||
dev_err(pcie->dev, "invalid port number: %d\n", index);
|
dev_err(pcie->dev, "invalid port number: %d\n", index);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -1454,17 +1566,54 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct tegra_pcie_soc_data tegra20_pcie_data = {
|
||||||
|
.num_ports = 2,
|
||||||
|
.msi_base_shift = 0,
|
||||||
|
.pads_pll_ctl = PADS_PLL_CTL_TEGRA20,
|
||||||
|
.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10,
|
||||||
|
.has_pex_clkreq_en = false,
|
||||||
|
.has_pex_bias_ctrl = false,
|
||||||
|
.has_intr_prsnt_sense = false,
|
||||||
|
.has_avdd_supply = false,
|
||||||
|
.has_cml_clk = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct tegra_pcie_soc_data tegra30_pcie_data = {
|
||||||
|
.num_ports = 3,
|
||||||
|
.msi_base_shift = 8,
|
||||||
|
.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
|
||||||
|
.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
|
||||||
|
.has_pex_clkreq_en = true,
|
||||||
|
.has_pex_bias_ctrl = true,
|
||||||
|
.has_intr_prsnt_sense = true,
|
||||||
|
.has_avdd_supply = true,
|
||||||
|
.has_cml_clk = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id tegra_pcie_of_match[] = {
|
||||||
|
{ .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie_data },
|
||||||
|
{ .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie_data },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, tegra_pcie_of_match);
|
||||||
|
|
||||||
static int tegra_pcie_probe(struct platform_device *pdev)
|
static int tegra_pcie_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
const struct of_device_id *match;
|
||||||
struct tegra_pcie *pcie;
|
struct tegra_pcie *pcie;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
match = of_match_device(tegra_pcie_of_match, &pdev->dev);
|
||||||
|
if (!match)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
|
pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
|
||||||
if (!pcie)
|
if (!pcie)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&pcie->busses);
|
INIT_LIST_HEAD(&pcie->busses);
|
||||||
INIT_LIST_HEAD(&pcie->ports);
|
INIT_LIST_HEAD(&pcie->ports);
|
||||||
|
pcie->soc_data = match->data;
|
||||||
pcie->dev = &pdev->dev;
|
pcie->dev = &pdev->dev;
|
||||||
|
|
||||||
err = tegra_pcie_parse_dt(pcie);
|
err = tegra_pcie_parse_dt(pcie);
|
||||||
|
@ -1513,12 +1662,6 @@ put_resources:
|
||||||
return err;
|
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 = {
|
static struct platform_driver tegra_pcie_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "tegra-pcie",
|
.name = "tegra-pcie",
|
||||||
|
|
Loading…
Reference in New Issue