diff options
Diffstat (limited to 'drivers/pci/host/pci-tegra.c')
-rw-r--r-- | drivers/pci/host/pci-tegra.c | 277 |
1 files changed, 237 insertions, 40 deletions
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 0fb0fdb223d..3d43874319b 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -38,6 +38,7 @@ #include <linux/of_pci.h> #include <linux/of_platform.h> #include <linux/pci.h> +#include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/reset.h> #include <linux/sizes.h> @@ -115,13 +116,20 @@ #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_INI_SLAVE_ERROR 1 +#define AFI_INTR_INI_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_AXI_DECODE_ERROR 8 +#define AFI_INTR_FPCI_TIMEOUT 9 +#define AFI_INTR_PE_PRSNT_SENSE 10 +#define AFI_INTR_PE_CLKREQ_SENSE 11 +#define AFI_INTR_CLKCLAMP_SENSE 12 +#define AFI_INTR_RDY4PD_SENSE 13 +#define AFI_INTR_P2P_ERROR 14 #define AFI_INTR_SIGNATURE 0xbc #define AFI_UPPER_FPCI_ADDRESS 0xc0 @@ -152,8 +160,10 @@ #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_X2_X1 (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_X4_X1 (0x1 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20) #define AFI_FUSE 0x104 @@ -165,12 +175,21 @@ #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_OVERRIDE_EN (1 << 4) + +#define AFI_PLLE_CONTROL 0x160 +#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9) +#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1) #define AFI_PEXBIAS_CTRL_0 0x168 #define RP_VEND_XP 0x00000F00 #define RP_VEND_XP_DL_UP (1 << 30) +#define RP_PRIV_MISC 0x00000FE0 +#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0) +#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0) + #define RP_LINK_CONTROL_STATUS 0x00000090 #define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 #define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 @@ -197,6 +216,7 @@ #define PADS_REFCLK_CFG0 0x000000C8 #define PADS_REFCLK_CFG1 0x000000CC +#define PADS_REFCLK_BIAS 0x000000D0 /* * Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit @@ -236,6 +256,7 @@ struct tegra_pcie_soc_data { bool has_pex_bias_ctrl; bool has_intr_prsnt_sense; bool has_cml_clk; + bool has_gen2; }; static inline struct tegra_msi *to_tegra_msi(struct msi_chip *chip) @@ -253,6 +274,7 @@ struct tegra_pcie { struct list_head buses; struct resource *cs; + struct resource all; struct resource io; struct resource mem; struct resource prefetch; @@ -267,6 +289,8 @@ struct tegra_pcie { struct reset_control *afi_rst; struct reset_control *pcie_xrst; + struct phy *phy; + struct tegra_msi msi; struct list_head ports; @@ -382,7 +406,7 @@ static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie, 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; + phys_addr_t phys = cs + i * SZ_16M + busnr * SZ_64K; err = ioremap_page_range(virt, virt + SZ_64K, phys, prot); if (err < 0) { @@ -561,6 +585,8 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port) if (soc->has_pex_clkreq_en) value |= AFI_PEX_CTRL_CLKREQ_EN; + value |= AFI_PEX_CTRL_OVERRIDE_EN; + afi_writel(port->pcie, value, ctrl); tegra_pcie_port_reset(port); @@ -568,6 +594,7 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port) static void tegra_pcie_port_disable(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; @@ -578,6 +605,10 @@ static void tegra_pcie_port_disable(struct tegra_pcie_port *port) /* disable reference clock */ value = afi_readl(port->pcie, ctrl); + + if (soc->has_pex_clkreq_en) + value &= ~AFI_PEX_CTRL_CLKREQ_EN; + value &= ~AFI_PEX_CTRL_REFCLK_EN; afi_writel(port->pcie, value, ctrl); } @@ -626,13 +657,25 @@ 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); + int err; + phys_addr_t io_start; + + err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem); + if (err < 0) + return err; + + err = devm_request_resource(pcie->dev, &pcie->all, &pcie->prefetch); + if (err) + return err; + + io_start = pci_pio_to_address(pcie->io.start); 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); + pci_ioremap_io(nr * SZ_64K, io_start); return 1; } @@ -684,9 +727,15 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) "Target abort", "Master abort", "Invalid write", + "Legacy interrupt", "Response decoding error", "AXI response decoding error", "Transaction timeout", + "Slot present pin change", + "Slot clock request change", + "TMS clock ramp change", + "TMS ready for power down", + "Peer2Peer error", }; struct tegra_pcie *pcie = arg; u32 code, signature; @@ -737,6 +786,7 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) { u32 fpci_bar, size, axi_address; + phys_addr_t io_start = pci_pio_to_address(pcie->io.start); /* Bar 0: type 1 extended configuration space */ fpci_bar = 0xfe100000; @@ -749,7 +799,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) /* Bar 1: downstream IO bar */ fpci_bar = 0xfdfc0000; size = resource_size(&pcie->io); - axi_address = pcie->io.start; + axi_address = 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); @@ -792,30 +842,27 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) afi_writel(pcie, 0, AFI_MSI_BAR_SZ); } -static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) +static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout) { const struct tegra_pcie_soc_data *soc = pcie->soc_data; - struct tegra_pcie_port *port; - unsigned int timeout; - unsigned long value; + u32 value; - /* power down PCIe slot clock bias pad */ - if (soc->has_pex_bias_ctrl) - afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0); + timeout = jiffies + msecs_to_jiffies(timeout); - /* 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); + while (time_before(jiffies, timeout)) { + value = pads_readl(pcie, soc->pads_pll_ctl); + if (value & PADS_PLL_CTL_LOCKDET) + return 0; + } - afi_writel(pcie, value, AFI_PCIE_CONFIG); + return -ETIMEDOUT; +} - value = afi_readl(pcie, AFI_FUSE); - value |= AFI_FUSE_PCIE_T0_GEN2_DIS; - afi_writel(pcie, value, AFI_FUSE); +static int tegra_pcie_phy_enable(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc_data *soc = pcie->soc_data; + u32 value; + int err; /* initialize internal PHY, enable up to 16 PCIE lanes */ pads_writel(pcie, 0x0, PADS_CTL_SEL); @@ -834,6 +881,13 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel; pads_writel(pcie, value, soc->pads_pll_ctl); + /* reset PLL */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value &= ~PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, soc->pads_pll_ctl); + + usleep_range(20, 100); + /* take PLL out of reset */ value = pads_readl(pcie, soc->pads_pll_ctl); value |= PADS_PLL_CTL_RST_B4SM; @@ -846,15 +900,11 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) pads_writel(pcie, PADS_REFCLK_CFG_VALUE, PADS_REFCLK_CFG1); /* wait for the PLL to lock */ - timeout = 300; - do { - 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"); - return -EBUSY; - } - } while (!(value & PADS_PLL_CTL_LOCKDET)); + err = tegra_pcie_pll_wait(pcie, 500); + if (err < 0) { + dev_err(pcie->dev, "PLL failed to lock: %d\n", err); + return err; + } /* turn off IDDQ override */ value = pads_readl(pcie, PADS_CTL); @@ -866,6 +916,58 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L; pads_writel(pcie, value, PADS_CTL); + return 0; +} + +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 long value; + int err; + + /* enable PLL power down */ + if (pcie->phy) { + value = afi_readl(pcie, AFI_PLLE_CONTROL); + value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL; + value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN; + afi_writel(pcie, value, AFI_PLLE_CONTROL); + } + + /* 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; + 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); + + if (soc->has_gen2) { + value = afi_readl(pcie, AFI_FUSE); + value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; + afi_writel(pcie, value, AFI_FUSE); + } else { + value = afi_readl(pcie, AFI_FUSE); + value |= AFI_FUSE_PCIE_T0_GEN2_DIS; + afi_writel(pcie, value, AFI_FUSE); + } + + if (!pcie->phy) + err = tegra_pcie_phy_enable(pcie); + else + err = phy_power_on(pcie->phy); + + if (err < 0) { + dev_err(pcie->dev, "failed to power on PHY: %d\n", err); + return err; + } + /* take the PCIe interface module out of reset */ reset_control_deassert(pcie->pcie_xrst); @@ -899,6 +1001,10 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie) /* TODO: disable and unprepare clocks? */ + err = phy_power_off(pcie->phy); + if (err < 0) + dev_warn(pcie->dev, "failed to power off PHY: %d\n", err); + reset_control_assert(pcie->pcie_xrst); reset_control_assert(pcie->afi_rst); reset_control_assert(pcie->pex_rst); @@ -1020,6 +1126,19 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) return err; } + pcie->phy = devm_phy_optional_get(pcie->dev, "pcie"); + if (IS_ERR(pcie->phy)) { + err = PTR_ERR(pcie->phy); + dev_err(&pdev->dev, "failed to get PHY: %d\n", err); + return err; + } + + err = phy_init(pcie->phy); + if (err < 0) { + dev_err(&pdev->dev, "failed to initialize PHY: %d\n", err); + return err; + } + err = tegra_pcie_power_on(pcie); if (err) { dev_err(&pdev->dev, "failed to power up: %d\n", err); @@ -1078,10 +1197,17 @@ poweroff: static int tegra_pcie_put_resources(struct tegra_pcie *pcie) { + int err; + if (pcie->irq > 0) free_irq(pcie->irq, pcie); tegra_pcie_power_off(pcie); + + err = phy_exit(pcie->phy); + if (err < 0) + dev_err(pcie->dev, "failed to teardown PHY: %d\n", err); + return 0; } @@ -1170,8 +1296,10 @@ static int tegra_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, return hwirq; irq = irq_create_mapping(msi->domain, hwirq); - if (!irq) + if (!irq) { + tegra_msi_free(msi, hwirq); return -EINVAL; + } irq_set_msi_desc(irq, desc); @@ -1189,8 +1317,10 @@ 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); + irq_hw_number_t hwirq = irqd_to_hwirq(d); - tegra_msi_free(msi, d->hwirq); + irq_dispose_mapping(irq); + tegra_msi_free(msi, hwirq); } static struct irq_chip tegra_msi_irq_chip = { @@ -1327,7 +1457,19 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, { struct device_node *np = pcie->dev->of_node; - if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { + if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) { + switch (lanes) { + case 0x0000104: + dev_info(pcie->dev, "4x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1; + return 0; + + case 0x0000102: + dev_info(pcie->dev, "2x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1; + return 0; + } + } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { switch (lanes) { case 0x00000204: dev_info(pcie->dev, "4x1, 2x1 configuration\n"); @@ -1435,7 +1577,23 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) struct device_node *np = pcie->dev->of_node; unsigned int i = 0; - if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { + if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) { + pcie->num_supplies = 7; + + pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, + sizeof(*pcie->supplies), + GFP_KERNEL); + if (!pcie->supplies) + return -ENOMEM; + + pcie->supplies[i++].supply = "avddio-pex"; + pcie->supplies[i++].supply = "dvddio-pex"; + pcie->supplies[i++].supply = "avdd-pex-pll"; + pcie->supplies[i++].supply = "hvdd-pex"; + pcie->supplies[i++].supply = "hvdd-pex-pll-e"; + pcie->supplies[i++].supply = "vddio-pex-ctl"; + pcie->supplies[i++].supply = "avdd-pll-erefe"; + } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { bool need_pexa = false, need_pexb = false; /* VDD_PEXA and AVDD_PEXA supply lanes 0 to 3 */ @@ -1514,32 +1672,50 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) struct resource res; int err; + memset(&pcie->all, 0, sizeof(pcie->all)); + pcie->all.flags = IORESOURCE_MEM; + pcie->all.name = np->full_name; + pcie->all.start = ~0; + pcie->all.end = 0; + if (of_pci_range_parser_init(&parser, np)) { dev_err(pcie->dev, "missing \"ranges\" property\n"); return -EINVAL; } for_each_of_pci_range(&parser, &range) { - of_pci_range_to_resource(&range, np, &res); + err = of_pci_range_to_resource(&range, np, &res); + if (err < 0) + return err; switch (res.flags & IORESOURCE_TYPE_BITS) { case IORESOURCE_IO: memcpy(&pcie->io, &res, sizeof(res)); - pcie->io.name = "I/O"; + pcie->io.name = np->full_name; break; case IORESOURCE_MEM: if (res.flags & IORESOURCE_PREFETCH) { memcpy(&pcie->prefetch, &res, sizeof(res)); - pcie->prefetch.name = "PREFETCH"; + pcie->prefetch.name = "prefetchable"; } else { memcpy(&pcie->mem, &res, sizeof(res)); - pcie->mem.name = "MEM"; + pcie->mem.name = "non-prefetchable"; } break; } + + if (res.start <= pcie->all.start) + pcie->all.start = res.start; + + if (res.end >= pcie->all.end) + pcie->all.end = res.end; } + err = devm_request_resource(pcie->dev, &iomem_resource, &pcie->all); + if (err < 0) + return err; + err = of_pci_parse_bus_range(np, &pcie->busn); if (err < 0) { dev_err(pcie->dev, "failed to parse ranges property: %d\n", @@ -1641,6 +1817,12 @@ static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port) unsigned int retries = 3; unsigned long value; + /* override presence detection */ + value = readl(port->base + RP_PRIV_MISC); + value &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT; + value |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT; + writel(value, port->base + RP_PRIV_MISC); + do { unsigned int timeout = TEGRA_PCIE_LINKUP_TIMEOUT; @@ -1721,6 +1903,7 @@ static const struct tegra_pcie_soc_data tegra20_pcie_data = { .has_pex_bias_ctrl = false, .has_intr_prsnt_sense = false, .has_cml_clk = false, + .has_gen2 = false, }; static const struct tegra_pcie_soc_data tegra30_pcie_data = { @@ -1732,9 +1915,23 @@ static const struct tegra_pcie_soc_data tegra30_pcie_data = { .has_pex_bias_ctrl = true, .has_intr_prsnt_sense = true, .has_cml_clk = true, + .has_gen2 = false, +}; + +static const struct tegra_pcie_soc_data tegra124_pcie_data = { + .num_ports = 2, + .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_cml_clk = true, + .has_gen2 = true, }; static const struct of_device_id tegra_pcie_of_match[] = { + { .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie_data }, { .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie_data }, { .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie_data }, { }, |