- Make private data structure for each SoC
- Add required Tegra30 clocks and regulators
- Add Tegra30 specific code in enable controller
- Added Tegra30 specific properties in pci binding document
Signed-off-by: Jay Agarwal <[email protected]>
---
Patch is based on remotes/gitorious_thierryreding_linux/tegra/next and should be applied on top of this.
Changes in V4:
As per review comment
- Corrected pci document
- Removed fix in read/write config space
- modified num_max_ports -> num_ports
- Avoided changes in tegra_pcie_remove
---
.../bindings/pci/nvidia,tegra20-pcie.txt | 4 +-
drivers/pci/host/pci-tegra.c | 147 +++++++++++++++++---
2 files changed, 130 insertions(+), 21 deletions(-)
diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
index 90c112f..db673c0 100644
--- a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
@@ -1,7 +1,7 @@
NVIDIA Tegra PCIe controller
Required properties:
-- compatible: "nvidia,tegra20-pcie"
+- compatible: "nvidia,tegra20-pcie" "nvidia,tegra30-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.
@@ -16,6 +16,7 @@ Required properties:
"msi": The Tegra interrupt that is asserted when an MSI is received
- pex-clk-supply: Supply voltage for internal reference clock
- vdd-supply: Power supply for controller (1.05V)
+- avdd-supply: Power supply for controller (1.05V) (not required for tegra20)
- bus-range: Range of bus numbers associated with this controller
- #address-cells: Address representation for root ports (must be 3)
- cell 0 specifies the bus and device numbers of the root port:
@@ -48,6 +49,7 @@ Required properties:
"afi": The Tegra clock of that name
"pcie_xclk": 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.
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index c2d0b22..6a992ef 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -1,5 +1,5 @@
/*
- * PCIe host controller driver for TEGRA(2) SOCs
+ * PCIe host controller driver for TEGRA SOCs
*
* Copyright (c) 2010, CompuLab, Ltd.
* Author: Mike Rapoport <[email protected]>
@@ -50,7 +50,6 @@
#include <asm/mach/pci.h>
#define INT_PCI_MSI_NR (8 * 32)
-#define TEGRA_MAX_PORTS 2
/* register definitions */
@@ -142,14 +141,15 @@
#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_INTR_EN_PRSNT_SENSE (1 << 8)
#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_420 (0x0 << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20)
+#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20)
#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20)
@@ -160,8 +160,11 @@
#define AFI_PEX1_CTRL 0x118
#define AFI_PEX2_CTRL 0x128
#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_PEXBIAS_CTRL_0 0x168
+
#define RP_VEND_XP 0x00000F00
#define RP_VEND_XP_DL_UP (1 << 30)
@@ -175,7 +178,8 @@
#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_T20 0x000000B8
+#define PADS_PLL_CTL_T30 0x000000B4
#define PADS_PLL_CTL_RST_B4SM (1 << 1)
#define PADS_PLL_CTL_LOCKDET (1 << 8)
#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16)
@@ -183,8 +187,11 @@
#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_BUF_EN (1 << 22)
#define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 20)
#define PADS_PLL_CTL_TXCLKREF_DIV5 (1 << 20)
+#define PADS_REFCLK_CFG0 0x000000C8
+#define PADS_REFCLK_CFG1 0x000000CC
struct tegra_msi {
DECLARE_BITMAP(used, INT_PCI_MSI_NR);
@@ -195,6 +202,19 @@ struct tegra_msi {
int irq;
};
+/* used to differentiate tegra chips code */
+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)
{
return container_of(chip, struct tegra_msi, chip);
@@ -219,6 +239,7 @@ struct tegra_pcie {
struct clk *afi_clk;
struct clk *pcie_xclk;
struct clk *pll_e;
+ struct clk *cml_clk;
struct tegra_msi msi;
@@ -228,6 +249,9 @@ struct tegra_pcie {
struct regulator *pex_clk_supply;
struct regulator *vdd_supply;
+ struct regulator *avdd_supply;
+
+ struct tegra_pcie_soc_data *soc_data;
};
struct tegra_pcie_port {
@@ -510,12 +534,15 @@ static void tegra_pcie_port_reset(struct tegra_pcie_port *port)
static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
{
+ struct tegra_pcie_soc_data *soc = port->pcie->soc_data;
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;
+ if (soc->has_pex_clkreq_en)
+ value |= AFI_PEX_CTRL_CLKREQ_EN;
afi_writel(port->pcie, value, ctrl);
tegra_pcie_port_reset(port);
@@ -568,6 +595,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, 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 */
static void tegra_pcie_relax_enable(struct pci_dev *dev)
@@ -748,6 +777,11 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
struct tegra_pcie_port *port;
unsigned int timeout;
unsigned long value;
+ struct tegra_pcie_soc_data *soc = pcie->soc_data;
+
+ /* power down to PCIe slot clock bias pad */
+ if (soc->has_pex_bias_ctrl)
+ afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0);
/* configure mode and disable all ports */
value = afi_readl(pcie, AFI_PCIE_CONFIG);
@@ -775,26 +809,26 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
* 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_readl(pcie, soc->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);
+ value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel;
+ pads_writel(pcie, value, soc->pads_pll_ctl);
/* 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;
- 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.
* 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 */
timeout = 300;
do {
- value = pads_readl(pcie, PADS_PLL_CTL);
+ value = pads_readl(pcie, soc->pads_pll_ctl);
usleep_range(1000, 1000);
if (--timeout == 0) {
pr_err("Tegra PCIe error: timeout waiting for PLL\n");
@@ -823,6 +857,8 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
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;
+ if (soc->has_intr_prsnt_sense)
+ value |= AFI_INTR_EN_PRSNT_SENSE;
afi_writel(pcie, value, AFI_AFI_INTR_ENABLE);
afi_writel(pcie, 0xffffffff, AFI_SM_INTR_ENABLE);
@@ -837,6 +873,7 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
static void tegra_pcie_power_off(struct tegra_pcie *pcie)
{
+ struct tegra_pcie_soc_data *soc = pcie->soc_data;
int err;
/* TODO: disable and unprepare clocks? */
@@ -848,19 +885,26 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie)
tegra_powergate_power_off(TEGRA_POWERGATE_PCIE);
tegra_pmc_pcie_xclk_clamp(true);
+ 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);
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 = regulator_disable(pcie->vdd_supply);
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);
}
static int tegra_pcie_power_on(struct tegra_pcie *pcie)
{
+ struct tegra_pcie_soc_data *soc = pcie->soc_data;
int err;
tegra_periph_reset_assert(pcie->pcie_xclk);
@@ -884,6 +928,15 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
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,
pcie->pex_clk);
if (err) {
@@ -901,6 +954,15 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
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);
if (err < 0) {
dev_err(pcie->dev, "failed to enable PLLE clock: %d\n", err);
@@ -912,6 +974,8 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
{
+ struct tegra_pcie_soc_data *soc = pcie->soc_data;
+
pcie->pex_clk = devm_clk_get(pcie->dev, "pex");
if (IS_ERR(pcie->pex_clk))
return PTR_ERR(pcie->pex_clk);
@@ -928,6 +992,11 @@ static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
if (IS_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;
}
@@ -1150,6 +1219,7 @@ 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;
+ struct tegra_pcie_soc_data *soc = pcie->soc_data;
unsigned long base;
int err;
u32 reg;
@@ -1186,7 +1256,7 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
msi->pages = __get_free_pages(GFP_KERNEL, 3);
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);
/* this register is in 4K increments */
afi_writel(pcie, 1, AFI_MSI_BAR_SZ);
@@ -1283,6 +1353,7 @@ static u32 tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes)
static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
{
struct device_node *np = pcie->dev->of_node, *port;
+ struct tegra_pcie_soc_data *soc = pcie->soc_data;
struct of_pci_range_parser parser;
struct of_pci_range range;
struct resource res;
@@ -1302,6 +1373,12 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
if (IS_ERR(pcie->pex_clk_supply))
return PTR_ERR(pcie->pex_clk_supply);
+ if (soc->has_avdd_supply) {
+ pcie->avdd_supply = devm_regulator_get(pcie->dev, "avdd");
+ if (IS_ERR(pcie->avdd_supply))
+ return PTR_ERR(pcie->avdd_supply);
+ }
+
for_each_of_pci_range(&parser, &range) {
of_pci_range_to_resource(&range, np, &res);
@@ -1348,7 +1425,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
index = PCI_SLOT(err);
- if (index < 1 || index > TEGRA_MAX_PORTS) {
+ if (index < 1 || index > pcie->soc_data->num_ports) {
dev_err(pcie->dev, "invalid port number: %d\n", index);
return -EINVAL;
}
@@ -1484,8 +1561,39 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
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_T20,
+ .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_T30,
+ .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},
+ { },
+};
+
static int tegra_pcie_probe(struct platform_device *pdev)
{
+ const struct of_device_id *match;
struct tegra_pcie *pcie;
int err;
@@ -1496,6 +1604,10 @@ static int tegra_pcie_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&pcie->busses);
INIT_LIST_HEAD(&pcie->ports);
pcie->dev = &pdev->dev;
+ match = of_match_device(tegra_pcie_of_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+ pcie->soc_data = (struct tegra_pcie_soc_data *)match->data;
err = tegra_pcie_parse_dt(pcie);
if (err < 0)
@@ -1567,11 +1679,6 @@ static int tegra_pcie_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id tegra_pcie_of_match[] = {
- { .compatible = "nvidia,tegra20-pcie", },
- { },
-};
-
static struct platform_driver tegra_pcie_driver = {
.driver = {
.name = "tegra-pcie",
--
1.7.0.4
On 06/05/2013 09:13 AM, Jay Agarwal wrote:
> - Make private data structure for each SoC
> - Add required Tegra30 clocks and regulators
> - Add Tegra30 specific code in enable controller
> - Added Tegra30 specific properties in pci binding document
It's a bit odd to send an updated version of just some of the patches;
reposting a whole series is much easier for the person applying the
patches. However, hopefully Thierry can handle it in this case.
> diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
> Required properties:
> -- compatible: "nvidia,tegra20-pcie"
> +- compatible: "nvidia,tegra20-pcie" "nvidia,tegra30-pcie"
perhaps add "or" between those two entries?
> +- avdd-supply: Power supply for controller (1.05V) (not required for tegra20)
...
> + "cml": The Tegra clock of that name (not required for tegra20)
Tegra20 should be capitalized in both those two lines.
I assume that Thierry can fix up these nits when he applies the patches,
so you don't need to repost. Thierry can correct me if I'm wrong.
On Wed, Jun 05, 2013 at 10:36:01AM -0600, Stephen Warren wrote:
> On 06/05/2013 09:13 AM, Jay Agarwal wrote:
> > - Make private data structure for each SoC
> > - Add required Tegra30 clocks and regulators
> > - Add Tegra30 specific code in enable controller
> > - Added Tegra30 specific properties in pci binding document
>
> It's a bit odd to send an updated version of just some of the patches;
> reposting a whole series is much easier for the person applying the
> patches. However, hopefully Thierry can handle it in this case.
Yes, I think I can handle it. It might be good to have Jay look at the
final patches when I'm done, though, to make sure I haven't forgotten
anything.
> > diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
>
> > Required properties:
> > -- compatible: "nvidia,tegra20-pcie"
> > +- compatible: "nvidia,tegra20-pcie" "nvidia,tegra30-pcie"
>
> perhaps add "or" between those two entries?
>
> > +- avdd-supply: Power supply for controller (1.05V) (not required for tegra20)
> ...
> > + "cml": The Tegra clock of that name (not required for tegra20)
>
> Tegra20 should be capitalized in both those two lines.
>
> I assume that Thierry can fix up these nits when he applies the patches,
> so you don't need to repost. Thierry can correct me if I'm wrong.
I've found a few other nits that I can take care of myself when
applying.
Thierry
> * PGP Signed by an unknown key
>
> On Wed, Jun 05, 2013 at 10:36:01AM -0600, Stephen Warren wrote:
> > On 06/05/2013 09:13 AM, Jay Agarwal wrote:
> > > - Make private data structure for each SoC
> > > - Add required Tegra30 clocks and regulators
> > > - Add Tegra30 specific code in enable controller
> > > - Added Tegra30 specific properties in pci binding document
> >
> > It's a bit odd to send an updated version of just some of the patches;
> > reposting a whole series is much easier for the person applying the
> > patches. However, hopefully Thierry can handle it in this case.
>
> Yes, I think I can handle it. It might be good to have Jay look at the final
> patches when I'm done, though, to make sure I haven't forgotten anything.
>
Yes, please let me know whenever you have integrated my patches to your repo, so that I can sync, review & verify again.