Message ID | 1374774810-18459-9-git-send-email-thierry.reding@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Delegated to: | Bjorn Helgaas |
Headers | show |
On Thu, Jul 25, 2013 at 11:53 AM, Thierry Reding <thierry.reding@gmail.com> wrote: > From: Jay Agarwal <jagarwal@nvidia.com> > > 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> > --- > .../bindings/pci/nvidia,tegra20-pcie.txt | 4 +- > drivers/pci/host/pci-tegra.c | 209 +++++++++++++++++---- > 2 files changed, 179 insertions(+), 34 deletions(-) > > diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt > index 90c112f..6b75107 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" or "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 223069a..5347d7d 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 <mike@compulab.co.il> > @@ -51,7 +51,6 @@ > #include <asm/mach/pci.h> > > #define INT_PCI_MSI_NR (8 * 32) > -#define TEGRA_MAX_PORTS 2 > > /* register definitions */ > > @@ -143,22 +142,30 @@ > #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_222 (0x1 << 20) > +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 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_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) > > @@ -173,7 +180,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_TEGRA20 0x000000B8 > +#define PADS_PLL_CTL_TEGRA30 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,6 +191,10 @@ > #define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20) > #define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 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 msi_chip chip; > @@ -193,6 +205,19 @@ struct tegra_msi { > 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) > { > return container_of(chip, struct tegra_msi, chip); > @@ -217,6 +242,7 @@ struct tegra_pcie { > struct clk *afi_clk; > struct clk *pcie_xclk; > struct clk *pll_e; > + struct clk *cml_clk; > > struct tegra_msi msi; > > @@ -226,6 +252,9 @@ struct tegra_pcie { > > struct regulator *pex_clk_supply; > struct regulator *vdd_supply; > + struct regulator *avdd_supply; > + > + const struct tegra_pcie_soc_data *soc_data; > }; > > struct tegra_pcie_port { > @@ -470,6 +499,10 @@ static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) > case 1: > ret = AFI_PEX1_CTRL; > break; > + > + case 2: > + ret = AFI_PEX2_CTRL; > + break; > } > > return ret; > @@ -494,12 +527,17 @@ static void tegra_pcie_port_reset(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 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); > @@ -552,6 +590,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) > @@ -724,10 +764,15 @@ static void tegra_pcie_setup_translations(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; > unsigned int timeout; > 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 */ > value = afi_readl(pcie, AFI_PCIE_CONFIG); > value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; > @@ -754,27 +799,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, 2000); > if (--timeout == 0) { > pr_err("Tegra PCIe error: timeout waiting for PLL\n"); > @@ -803,6 +847,10 @@ 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); > > @@ -817,6 +865,7 @@ static int tegra_pcie_enable_controller(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; > > /* TODO: disable and unprepare clocks? */ > @@ -828,19 +877,28 @@ 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", > - err); > + 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", > - err); > + dev_warn(pcie->dev, "failed to disable VDD regulator: %d\n", > + err); > } > > static int tegra_pcie_power_on(struct tegra_pcie *pcie) > { > + const struct tegra_pcie_soc_data *soc = pcie->soc_data; > int err; > > tegra_periph_reset_assert(pcie->pcie_xclk); > @@ -864,6 +922,16 @@ 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) { > @@ -881,6 +949,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); > @@ -892,6 +969,8 @@ static int tegra_pcie_power_on(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"); > if (IS_ERR(pcie->pex_clk)) > return PTR_ERR(pcie->pex_clk); > @@ -908,6 +987,12 @@ 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; > } > > @@ -1132,6 +1217,7 @@ static const struct irq_domain_ops msi_domain_ops = { > static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) > { > 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; > unsigned long base; > int err; > @@ -1169,7 +1255,7 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) > 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 >> 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); > @@ -1238,16 +1324,35 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, > { > 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; > + if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { > + switch (lanes) { > + case 0x00000204: > + dev_info(pcie->dev, "4x1, 2x1 configuration\n"); > + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420; > + return 0; > + > + case 0x00020202: > + dev_info(pcie->dev, "2x3 configuration\n"); > + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222; > + 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; > @@ -1255,6 +1360,7 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, > > 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 of_pci_range_parser parser; > struct of_pci_range range; > @@ -1275,6 +1381,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); > > @@ -1321,7 +1433,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 > soc->num_ports) { > dev_err(pcie->dev, "invalid port number: %d\n", index); > return -EINVAL; > } > @@ -1459,17 +1571,54 @@ 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_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) > { > + const struct of_device_id *match; > struct tegra_pcie *pcie; > 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); > if (!pcie) > return -ENOMEM; > > INIT_LIST_HEAD(&pcie->busses); > INIT_LIST_HEAD(&pcie->ports); > + pcie->soc_data = match->data; > pcie->dev = &pdev->dev; > > err = tegra_pcie_parse_dt(pcie); > @@ -1518,12 +1667,6 @@ put_resources: > 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", > -- > 1.8.1.5 > -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt index 90c112f..6b75107 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" or "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 223069a..5347d7d 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 <mike@compulab.co.il> @@ -51,7 +51,6 @@ #include <asm/mach/pci.h> #define INT_PCI_MSI_NR (8 * 32) -#define TEGRA_MAX_PORTS 2 /* register definitions */ @@ -143,22 +142,30 @@ #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_222 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 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_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) @@ -173,7 +180,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_TEGRA20 0x000000B8 +#define PADS_PLL_CTL_TEGRA30 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,6 +191,10 @@ #define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20) #define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 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 msi_chip chip; @@ -193,6 +205,19 @@ struct tegra_msi { 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) { return container_of(chip, struct tegra_msi, chip); @@ -217,6 +242,7 @@ struct tegra_pcie { struct clk *afi_clk; struct clk *pcie_xclk; struct clk *pll_e; + struct clk *cml_clk; struct tegra_msi msi; @@ -226,6 +252,9 @@ struct tegra_pcie { struct regulator *pex_clk_supply; struct regulator *vdd_supply; + struct regulator *avdd_supply; + + const struct tegra_pcie_soc_data *soc_data; }; struct tegra_pcie_port { @@ -470,6 +499,10 @@ static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) case 1: ret = AFI_PEX1_CTRL; break; + + case 2: + ret = AFI_PEX2_CTRL; + break; } return ret; @@ -494,12 +527,17 @@ static void tegra_pcie_port_reset(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 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); @@ -552,6 +590,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) @@ -724,10 +764,15 @@ static void tegra_pcie_setup_translations(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; unsigned int timeout; 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 */ value = afi_readl(pcie, AFI_PCIE_CONFIG); value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; @@ -754,27 +799,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, 2000); if (--timeout == 0) { pr_err("Tegra PCIe error: timeout waiting for PLL\n"); @@ -803,6 +847,10 @@ 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); @@ -817,6 +865,7 @@ static int tegra_pcie_enable_controller(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; /* TODO: disable and unprepare clocks? */ @@ -828,19 +877,28 @@ 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", - err); + 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", - err); + dev_warn(pcie->dev, "failed to disable VDD regulator: %d\n", + err); } static int tegra_pcie_power_on(struct tegra_pcie *pcie) { + const struct tegra_pcie_soc_data *soc = pcie->soc_data; int err; tegra_periph_reset_assert(pcie->pcie_xclk); @@ -864,6 +922,16 @@ 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) { @@ -881,6 +949,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); @@ -892,6 +969,8 @@ static int tegra_pcie_power_on(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"); if (IS_ERR(pcie->pex_clk)) return PTR_ERR(pcie->pex_clk); @@ -908,6 +987,12 @@ 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; } @@ -1132,6 +1217,7 @@ static const struct irq_domain_ops msi_domain_ops = { static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) { 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; unsigned long base; int err; @@ -1169,7 +1255,7 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) 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 >> 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); @@ -1238,16 +1324,35 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, { 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; + if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { + switch (lanes) { + case 0x00000204: + dev_info(pcie->dev, "4x1, 2x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420; + return 0; + + case 0x00020202: + dev_info(pcie->dev, "2x3 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222; + 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; @@ -1255,6 +1360,7 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, 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 of_pci_range_parser parser; struct of_pci_range range; @@ -1275,6 +1381,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); @@ -1321,7 +1433,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 > soc->num_ports) { dev_err(pcie->dev, "invalid port number: %d\n", index); return -EINVAL; } @@ -1459,17 +1571,54 @@ 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_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) { + const struct of_device_id *match; struct tegra_pcie *pcie; 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); if (!pcie) return -ENOMEM; INIT_LIST_HEAD(&pcie->busses); INIT_LIST_HEAD(&pcie->ports); + pcie->soc_data = match->data; pcie->dev = &pdev->dev; err = tegra_pcie_parse_dt(pcie); @@ -1518,12 +1667,6 @@ put_resources: 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",