From dfa551e49c072b9f4e1b0486a4091cd80733733b Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 Oct 2015 16:58:52 -0600 Subject: ARM: tegra210: implement PLLE init procedure from TRM Implement the procedure that the TRM mandates to initialize PLLREFE and PLLE. This makes the PLL actually lock. Note that this section of the TRM is being cleaned up to remove some confusion. The set of register accesses in this patch should be final, although the step numbers/descriptions might still change. Signed-off-by: Stephen Warren Signed-off-by: Tom Warren diff --git a/arch/arm/mach-tegra/tegra210/clock.c b/arch/arm/mach-tegra/tegra210/clock.c index 6d75d37..df92bdc 100644 --- a/arch/arm/mach-tegra/tegra210/clock.c +++ b/arch/arm/mach-tegra/tegra210/clock.c @@ -8,6 +8,7 @@ /* Tegra210 Clock control functions */ #include +#include #include #include #include @@ -1030,6 +1031,59 @@ void arch_timer_init(void) debug("%s: TSC CNTCR = 0x%08X\n", __func__, val); } +#define PLLREFE_MISC 0x4c8 +#define PLLREFE_MISC_LOCK BIT(27) +#define PLLREFE_MISC_IDDQ BIT(24) + +#define PLLREFE_BASE 0x4c4 +#define PLLREFE_BASE_BYPASS BIT(31) +#define PLLREFE_BASE_ENABLE BIT(30) +#define PLLREFE_BASE_REF_DIS BIT(29) +#define PLLREFE_BASE_KCP(kcp) (((kcp) & 0x3) << 27) +#define PLLREFE_BASE_KVCO BIT(26) +#define PLLREFE_BASE_DIVP(p) (((p) & 0x1f) << 16) +#define PLLREFE_BASE_DIVN(n) (((n) & 0xff) << 8) +#define PLLREFE_BASE_DIVM(m) (((m) & 0xff) << 0) + +static int tegra_pllref_enable(void) +{ + u32 value; + unsigned long start; + + /* + * This sequence comes from Tegra X1 TRM section "Cold Boot, with no + * Recovery Mode or Boot from USB", sub-section "PLLREFE". + */ + + value = readl(NV_PA_CLK_RST_BASE + PLLREFE_MISC); + value &= ~PLLREFE_MISC_IDDQ; + writel(value, NV_PA_CLK_RST_BASE + PLLREFE_MISC); + + udelay(5); + + value = PLLREFE_BASE_ENABLE | + PLLREFE_BASE_KCP(0) | + PLLREFE_BASE_DIVP(0) | + PLLREFE_BASE_DIVN(0x41) | + PLLREFE_BASE_DIVM(4); + writel(value, NV_PA_CLK_RST_BASE + PLLREFE_BASE); + + debug("waiting for pllrefe lock\n"); + start = get_timer(0); + while (get_timer(start) < 250) { + value = readl(NV_PA_CLK_RST_BASE + PLLREFE_MISC); + if (value & PLLREFE_MISC_LOCK) + break; + } + if (!(value & PLLREFE_MISC_LOCK)) { + debug(" timeout\n"); + return -ETIMEDOUT; + } + debug(" done\n"); + + return 0; +} + #define PLLE_SS_CNTL 0x68 #define PLLE_SS_CNTL_SSCINCINTR(x) (((x) & 0x3f) << 24) #define PLLE_SS_CNTL_SSCINC(x) (((x) & 0xff) << 16) @@ -1041,100 +1095,131 @@ void arch_timer_init(void) #define PLLE_SS_CNTL_SSCMAX(x) (((x) & 0x1ff) << 0) #define PLLE_BASE 0x0e8 -#define PLLE_BASE_ENABLE (1 << 30) -#define PLLE_BASE_LOCK_OVERRIDE (1 << 29) -#define PLLE_BASE_PLDIV_CML(x) (((x) & 0xf) << 24) +#define PLLE_BASE_ENABLE (1 << 31) +#define PLLE_BASE_PLDIV_CML(x) (((x) & 0x1f) << 24) #define PLLE_BASE_NDIV(x) (((x) & 0xff) << 8) #define PLLE_BASE_MDIV(x) (((x) & 0xff) << 0) #define PLLE_MISC 0x0ec #define PLLE_MISC_IDDQ_SWCTL (1 << 14) -#define PLLE_MISC_IDDQ_OVERRIDE (1 << 13) -#define PLLE_MISC_LOCK_ENABLE (1 << 9) -#define PLLE_MISC_PTS (1 << 8) -#define PLLE_MISC_VREG_BG_CTRL(x) (((x) & 0x3) << 4) +#define PLLE_MISC_IDDQ_OVERRIDE_VALUE (1 << 13) +#define PLLE_MISC_LOCK (1 << 11) +#define PLLE_MISC_KCP(x) (((x) & 0x3) << 6) #define PLLE_MISC_VREG_CTRL(x) (((x) & 0x3) << 2) +#define PLLE_MISC_KVCO (1 << 0) #define PLLE_AUX 0x48c +#define PLLE_AUX_SS_SEQ_INCLUDE (1 << 31) +#define PLLE_AUX_REF_SEL_PLLREFE (1 << 28) #define PLLE_AUX_SEQ_ENABLE (1 << 24) +#define PLLE_AUX_SS_SWCTL (1 << 6) #define PLLE_AUX_ENABLE_SWCTL (1 << 4) +#define PLLE_AUX_USE_LOCKDET (1 << 3) int tegra_plle_enable(void) { - unsigned int m = 1, n = 200, cpcon = 13; u32 value; + unsigned long start; - value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); - value &= ~PLLE_BASE_LOCK_OVERRIDE; - writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); + /* PLLREF feeds PLLE */ + tegra_pllref_enable(); + + /* + * This sequence comes from Tegra X1 TRM section "Cold Boot, with no + * Recovery Mode or Boot from USB", sub-section "PLLEs". + */ + + /* 1. Select XTAL as the source */ value = readl(NV_PA_CLK_RST_BASE + PLLE_AUX); - value |= PLLE_AUX_ENABLE_SWCTL; - value &= ~PLLE_AUX_SEQ_ENABLE; + value &= ~PLLE_AUX_REF_SEL_PLLREFE; writel(value, NV_PA_CLK_RST_BASE + PLLE_AUX); - udelay(1); - value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); - value |= PLLE_MISC_IDDQ_SWCTL; - value &= ~PLLE_MISC_IDDQ_OVERRIDE; - value |= PLLE_MISC_LOCK_ENABLE; - value |= PLLE_MISC_PTS; - value |= PLLE_MISC_VREG_BG_CTRL(3); - value |= PLLE_MISC_VREG_CTRL(2); + value &= ~PLLE_MISC_IDDQ_OVERRIDE_VALUE; writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC); + /* 2. Wait 5 us */ udelay(5); - value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); - value |= PLLE_SS_CNTL_SSCBYP | PLLE_SS_CNTL_INTERP_RESET | - PLLE_SS_CNTL_BYPASS_SS; - writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + /* + * 3. Program the following registers to generate a low jitter 100MHz + * clock. + */ value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); - value &= ~PLLE_BASE_PLDIV_CML(0xf); + value &= ~PLLE_BASE_PLDIV_CML(0x1f); value &= ~PLLE_BASE_NDIV(0xff); value &= ~PLLE_BASE_MDIV(0xff); - value |= PLLE_BASE_PLDIV_CML(cpcon); - value |= PLLE_BASE_NDIV(n); - value |= PLLE_BASE_MDIV(m); + value |= PLLE_BASE_PLDIV_CML(0xe); + value |= PLLE_BASE_NDIV(0x7d); + value |= PLLE_BASE_MDIV(2); writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); - udelay(1); + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + value &= ~PLLE_MISC_KCP(3); + value &= ~PLLE_MISC_VREG_CTRL(3); + value &= ~PLLE_MISC_KVCO; + writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC); value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); value |= PLLE_BASE_ENABLE; writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); - /* wait for lock */ - udelay(300); - - value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); - value &= ~PLLE_SS_CNTL_SSCINVERT; - value &= ~PLLE_SS_CNTL_SSCCENTER; - - value &= ~PLLE_SS_CNTL_SSCINCINTR(0x3f); - value &= ~PLLE_SS_CNTL_SSCINC(0xff); - value &= ~PLLE_SS_CNTL_SSCMAX(0x1ff); + /* 4. Wait for LOCK */ - value |= PLLE_SS_CNTL_SSCINCINTR(0x20); - value |= PLLE_SS_CNTL_SSCINC(0x01); - value |= PLLE_SS_CNTL_SSCMAX(0x25); + debug("waiting for plle lock\n"); + start = get_timer(0); + while (get_timer(start) < 250) { + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + if (value & PLLE_MISC_LOCK) + break; + } + if (!(value & PLLE_MISC_LOCK)) { + debug(" timeout\n"); + return -ETIMEDOUT; + } + debug(" done\n"); - writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + /* 5. Enable SSA */ value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); - value &= ~PLLE_SS_CNTL_SSCBYP; + value &= ~PLLE_SS_CNTL_SSCINC(0xff); + value |= PLLE_SS_CNTL_SSCINC(1); + value &= ~PLLE_SS_CNTL_SSCINCINTR(0x3f); + value |= PLLE_SS_CNTL_SSCINCINTR(0x23); + value &= ~PLLE_SS_CNTL_SSCMAX(0x1fff); + value |= PLLE_SS_CNTL_SSCMAX(0x21); + value &= ~PLLE_SS_CNTL_SSCINVERT; + value &= ~PLLE_SS_CNTL_SSCCENTER; value &= ~PLLE_SS_CNTL_BYPASS_SS; + value &= ~PLLE_SS_CNTL_SSCBYP; writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); - udelay(1); + /* 6. Wait 300 ns */ - value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + udelay(1); value &= ~PLLE_SS_CNTL_INTERP_RESET; writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + /* 7. Enable HW power sequencer for PLLE */ + + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + value &= ~PLLE_MISC_IDDQ_SWCTL; + writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_AUX); + value &= ~PLLE_AUX_SS_SWCTL; + value &= ~PLLE_AUX_ENABLE_SWCTL; + value |= PLLE_AUX_SS_SEQ_INCLUDE; + value |= PLLE_AUX_USE_LOCKDET; + writel(value, NV_PA_CLK_RST_BASE + PLLE_AUX); + + /* 8. Wait 1 us */ + udelay(1); + value |= PLLE_AUX_SEQ_ENABLE; + writel(value, NV_PA_CLK_RST_BASE + PLLE_AUX); return 0; } -- cgit v0.10.2 From bec05246f637d2c6f46e6b6a8073d4fa1d6141ec Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 Oct 2015 17:00:40 -0600 Subject: pci: tegra: clip RAM size to 32-bits Tegra peripherals can generally access a 32-bit physical address space, and I believe this applies to PCIe. Clip the PCI region that refers to DRAM so it fits into 32-bits to avoid issues. Signed-off-by: Stephen Warren Signed-off-by: Tom Warren diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c index ebb959f..357e779 100644 --- a/drivers/pci/pci_tegra.c +++ b/drivers/pci/pci_tegra.c @@ -997,6 +997,14 @@ static const struct tegra_pcie_soc tegra124_pcie_soc = { static int process_nodes(const void *fdt, int nodes[], unsigned int count) { unsigned int i; + uint64_t dram_end; + uint32_t pci_dram_size; + + /* Clip PCI-accessible DRAM to 32-bits */ + dram_end = ((uint64_t)NV_PA_SDRAM_BASE) + gd->ram_size; + if (dram_end > 0x100000000) + dram_end = 0x100000000; + pci_dram_size = dram_end - NV_PA_SDRAM_BASE; for (i = 0; i < count; i++) { const struct tegra_pcie_soc *soc; @@ -1069,7 +1077,7 @@ static int process_nodes(const void *fdt, int nodes[], unsigned int count) pcie->hose.last_busno = 0; pci_set_region(&pcie->hose.regions[0], NV_PA_SDRAM_BASE, - NV_PA_SDRAM_BASE, gd->ram_size, + NV_PA_SDRAM_BASE, pci_dram_size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); pci_set_region(&pcie->hose.regions[1], pcie->io.start, -- cgit v0.10.2 From 223ddbcb09f6449d6d56ccacc4112ab61589fd29 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 Oct 2015 17:00:41 -0600 Subject: pci: tegra: use #address-/size-cells from DT The number of cells used by each entry in the DT ranges property is determined by the #address-cells/#size-cells properties. Fix the code to respect this. Signed-off-by: Stephen Warren Signed-off-by: Tom Warren diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c index 357e779..c110366 100644 --- a/drivers/pci/pci_tegra.c +++ b/drivers/pci/pci_tegra.c @@ -406,9 +406,34 @@ static int tegra_pcie_get_xbar_config(const void *fdt, int node, u32 lanes, static int tegra_pcie_parse_dt_ranges(const void *fdt, int node, struct tegra_pcie *pcie) { + int parent, na_parent, na_pcie, ns_pcie; const u32 *ptr, *end; int len; + parent = fdt_parent_offset(fdt, node); + if (parent < 0) { + error("Can't find PCI parent node\n"); + return -FDT_ERR_NOTFOUND; + } + + na_parent = fdt_address_cells(fdt, parent); + if (na_parent < 1) { + error("bad #address-cells for PCIE parent\n"); + return -FDT_ERR_NOTFOUND; + } + + na_pcie = fdt_address_cells(fdt, node); + if (na_pcie < 1) { + error("bad #address-cells for PCIE\n"); + return -FDT_ERR_NOTFOUND; + } + + ns_pcie = fdt_size_cells(fdt, node); + if (ns_pcie < 1) { + error("bad #size-cells for PCIE\n"); + return -FDT_ERR_NOTFOUND; + } + ptr = fdt_getprop(fdt, node, "ranges", &len); if (!ptr) { error("missing \"ranges\" property"); @@ -437,11 +462,13 @@ static int tegra_pcie_parse_dt_ranges(const void *fdt, int node, } if (res) { - res->start = fdt32_to_cpu(ptr[3]); - res->end = res->start + fdt32_to_cpu(ptr[5]); + int start_low = na_pcie + (na_parent - 1); + int size_low = na_pcie + na_parent + (ns_pcie - 1); + res->start = fdt32_to_cpu(ptr[start_low]); + res->end = res->start + fdt32_to_cpu(ptr[size_low]); } - ptr += 3 + 1 + 2; + ptr += na_pcie + na_parent + ns_pcie; } debug("PCI regions:\n"); -- cgit v0.10.2 From 514e19138af26682dfaec0f92d24406b2e846f98 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 Oct 2015 17:00:42 -0600 Subject: pci: tegra: implement PCA enable workaround Tegra210's PCIe controller has a bug that requires the PCA (performance counter) feature to be enabled. If this isn't done, accesses to device configuration space will hang the chip for tens of seconds. Implement the workaround. Signed-off-by: Stephen Warren Signed-off-by: Tom Warren diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c index c110366..177d48d 100644 --- a/drivers/pci/pci_tegra.c +++ b/drivers/pci/pci_tegra.c @@ -166,6 +166,9 @@ DECLARE_GLOBAL_DATA_PTR; #define RP_VEND_XP 0x00000F00 #define RP_VEND_XP_DL_UP (1 << 30) +#define RP_VEND_CTL2 0x00000FA8 +#define RP_VEND_CTL2_PCA_ENABLE (1 << 7) + #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) @@ -194,6 +197,7 @@ struct tegra_pcie_soc { bool has_pex_bias_ctrl; bool has_cml_clk; bool has_gen2; + bool force_pca_enable; }; struct tegra_pcie { @@ -887,6 +891,7 @@ 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 *soc = port->pcie->soc; unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); unsigned long value; @@ -902,6 +907,12 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port) afi_writel(port->pcie, value, ctrl); tegra_pcie_port_reset(port); + + if (soc->force_pca_enable) { + value = rp_readl(port, RP_VEND_CTL2); + value |= RP_VEND_CTL2_PCA_ENABLE; + rp_writel(port, value, RP_VEND_CTL2); + } } static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port) @@ -999,6 +1010,7 @@ static const struct tegra_pcie_soc tegra20_pcie_soc = { .has_pex_bias_ctrl = false, .has_cml_clk = false, .has_gen2 = false, + .force_pca_enable = false, }; static const struct tegra_pcie_soc tegra30_pcie_soc = { @@ -1009,6 +1021,7 @@ static const struct tegra_pcie_soc tegra30_pcie_soc = { .has_pex_bias_ctrl = true, .has_cml_clk = true, .has_gen2 = false, + .force_pca_enable = false, }; static const struct tegra_pcie_soc tegra124_pcie_soc = { @@ -1019,6 +1032,7 @@ static const struct tegra_pcie_soc tegra124_pcie_soc = { .has_pex_bias_ctrl = true, .has_cml_clk = true, .has_gen2 = true, + .force_pca_enable = false, }; static int process_nodes(const void *fdt, int nodes[], unsigned int count) -- cgit v0.10.2 From a02e2635480e66a2921d62792fbc4a1e83dc4425 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 Oct 2015 17:00:43 -0600 Subject: pci: tegra: call tegra_pcie_board_init() earlier The board PCI setup code may control regulators that are required simply to bring up the PCI controller itself (or PLLs, IOs, ... it uses). Move the call to this function earlier so that all board-provided resources are ready early enough for everything to work. Signed-off-by: Stephen Warren Signed-off-by: Tom Warren diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c index 177d48d..bf078b3 100644 --- a/drivers/pci/pci_tegra.c +++ b/drivers/pci/pci_tegra.c @@ -618,8 +618,6 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie) return err; } - tegra_pcie_board_init(); - err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, PERIPH_ID_PCIE); if (err < 0) { @@ -1164,6 +1162,8 @@ void pci_init_board(void) const void *fdt = gd->fdt_blob; int count, nodes[1]; + tegra_pcie_board_init(); + count = fdtdec_find_aliases_for_id(fdt, "pcie-controller", COMPAT_NVIDIA_TEGRA124_PCIE, nodes, ARRAY_SIZE(nodes)); -- cgit v0.10.2 From d9eda6c441ad627731059d1696292cab7b6e9d5c Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 Oct 2015 17:00:44 -0600 Subject: pci: tegra: add/enable support for Tegra210 This needs a separate compatible value from Tegra124 since the new HW version has bugs that would prevent a driver for previous HW versions from operating at all. Signed-off-by: Stephen Warren Signed-off-by: Tom Warren diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c index bf078b3..690896f 100644 --- a/drivers/pci/pci_tegra.c +++ b/drivers/pci/pci_tegra.c @@ -387,6 +387,7 @@ static int tegra_pcie_get_xbar_config(const void *fdt, int node, u32 lanes, break; case COMPAT_NVIDIA_TEGRA124_PCIE: + case COMPAT_NVIDIA_TEGRA210_PCIE: switch (lanes) { case 0x0000104: debug("4x1, 1x1 configuration\n"); @@ -1033,6 +1034,17 @@ static const struct tegra_pcie_soc tegra124_pcie_soc = { .force_pca_enable = false, }; +static const struct tegra_pcie_soc tegra210_pcie_soc = { + .num_ports = 2, + .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_cml_clk = true, + .has_gen2 = true, + .force_pca_enable = true, +}; + static int process_nodes(const void *fdt, int nodes[], unsigned int count) { unsigned int i; @@ -1068,6 +1080,10 @@ static int process_nodes(const void *fdt, int nodes[], unsigned int count) soc = &tegra124_pcie_soc; break; + case COMPAT_NVIDIA_TEGRA210_PCIE: + soc = &tegra210_pcie_soc; + break; + default: error("unsupported compatible: %s", fdtdec_get_compatible(id)); @@ -1165,6 +1181,12 @@ void pci_init_board(void) tegra_pcie_board_init(); count = fdtdec_find_aliases_for_id(fdt, "pcie-controller", + COMPAT_NVIDIA_TEGRA210_PCIE, + nodes, ARRAY_SIZE(nodes)); + if (process_nodes(fdt, nodes, count)) + return; + + count = fdtdec_find_aliases_for_id(fdt, "pcie-controller", COMPAT_NVIDIA_TEGRA124_PCIE, nodes, ARRAY_SIZE(nodes)); if (process_nodes(fdt, nodes, count)) diff --git a/include/fdtdec.h b/include/fdtdec.h index 0e36664..3a6ff1f 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -130,6 +130,7 @@ enum fdt_compat_id { COMPAT_NVIDIA_TEGRA30_SDMMC, /* Tegra30 SDMMC controller */ COMPAT_NVIDIA_TEGRA20_SDMMC, /* Tegra20 SDMMC controller */ COMPAT_NVIDIA_TEGRA124_PCIE, /* Tegra 124 PCIe controller */ + COMPAT_NVIDIA_TEGRA210_PCIE, /* Tegra 210 PCIe controller */ COMPAT_NVIDIA_TEGRA30_PCIE, /* Tegra 30 PCIe controller */ COMPAT_NVIDIA_TEGRA20_PCIE, /* Tegra 20 PCIe controller */ COMPAT_NVIDIA_TEGRA124_XUSB_PADCTL, diff --git a/lib/fdtdec.c b/lib/fdtdec.c index c1b5177..f1849bc 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -36,6 +36,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(NVIDIA_TEGRA30_SDMMC, "nvidia,tegra30-sdhci"), COMPAT(NVIDIA_TEGRA20_SDMMC, "nvidia,tegra20-sdhci"), COMPAT(NVIDIA_TEGRA124_PCIE, "nvidia,tegra124-pcie"), + COMPAT(NVIDIA_TEGRA210_PCIE, "nvidia,tegra210-pcie"), COMPAT(NVIDIA_TEGRA30_PCIE, "nvidia,tegra30-pcie"), COMPAT(NVIDIA_TEGRA20_PCIE, "nvidia,tegra20-pcie"), COMPAT(NVIDIA_TEGRA124_XUSB_PADCTL, "nvidia,tegra124-xusb-padctl"), -- cgit v0.10.2 From d0af234130db0cc98ec81ed9d4d75c3bff7b4796 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 Oct 2015 17:02:39 -0600 Subject: ARM: tegra: add PCI to Tegra210 SoC DT Tegra210's PCI controller is largely identical to Tegra124, and hence shares the same binding. However, it has a unique compatible value due to the existence of at least one new HW bug that would prevent any driver for a previous HW version from operating correctly. Signed-off-by: Stephen Warren Signed-off-by: Tom Warren diff --git a/arch/arm/dts/tegra210.dtsi b/arch/arm/dts/tegra210.dtsi index f3874a1..a8c2f19 100644 --- a/arch/arm/dts/tegra210.dtsi +++ b/arch/arm/dts/tegra210.dtsi @@ -12,6 +12,72 @@ #address-cells = <2>; #size-cells = <2>; + pcie-controller@0,01003000 { + compatible = "nvidia,tegra210-pcie"; + device_type = "pci"; + reg = <0x0 0x01003000 0x0 0x00000800 /* PADS registers */ + 0x0 0x01003800 0x0 0x00000800 /* AFI registers */ + 0x0 0x02000000 0x0 0x10000000>; /* configuration space */ + reg-names = "pads", "afi", "cs"; + interrupts = , /* controller interrupt */ + ; /* MSI interrupt */ + interrupt-names = "intr", "msi"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>; + + bus-range = <0x00 0xff>; + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x82000000 0 0x01000000 0x0 0x01000000 0 0x00001000 /* port 0 configuration space */ + 0x82000000 0 0x01001000 0x0 0x01001000 0 0x00001000 /* port 1 configuration space */ + 0x81000000 0 0x0 0x0 0x12000000 0 0x00010000 /* downstream I/O (64 KiB) */ + 0x82000000 0 0x13000000 0x0 0x13000000 0 0x0d000000 /* non-prefetchable memory (208 MiB) */ + 0xc2000000 0 0x20000000 0x0 0x20000000 0 0x20000000>; /* prefetchable memory (512 MiB) */ + + clocks = <&tegra_car TEGRA210_CLK_PCIE>, + <&tegra_car TEGRA210_CLK_AFI>, + <&tegra_car TEGRA210_CLK_PLL_E>, + <&tegra_car TEGRA210_CLK_CML0>; + clock-names = "pex", "afi", "pll_e", "cml"; + resets = <&tegra_car 70>, + <&tegra_car 72>, + <&tegra_car 74>; + reset-names = "pex", "afi", "pcie_x"; + status = "disabled"; + + phys = <&padctl TEGRA_XUSB_PADCTL_PCIE>; + phy-names = "pcie"; + + pci@1,0 { + device_type = "pci"; + assigned-addresses = <0x82000800 0 0x01000000 0 0x1000>; + reg = <0x000800 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <4>; + }; + + pci@2,0 { + device_type = "pci"; + assigned-addresses = <0x82001000 0 0x01001000 0 0x1000>; + reg = <0x001000 0 0 0 0>; + status = "disabled"; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + nvidia,num-lanes = <1>; + }; + }; + gic: interrupt-controller@0,50041000 { compatible = "arm,gic-400"; #interrupt-cells = <3>; -- cgit v0.10.2 From 019bc6259d4f096219cfe1b9e91fe033b62ae645 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 5 Oct 2015 17:02:40 -0600 Subject: ARM: tegra: enable PCI support of p2371-2180 p2371-2180 has two PCI ports; a regular x4 slot and a x1 M.2 slot. This patch adds the relevant DT to enable the PCI controller and configure the XUSB padctl pin muxing, and code to turn on the PCI power and enable PCI features in U-Boot. I have only tested the x4 slot. Signed-off-by: Stephen Warren Signed-off-by: Tom Warren diff --git a/arch/arm/dts/tegra210-p2371-2180.dts b/arch/arm/dts/tegra210-p2371-2180.dts index 5d9adcf..bf35497 100644 --- a/arch/arm/dts/tegra210-p2371-2180.dts +++ b/arch/arm/dts/tegra210-p2371-2180.dts @@ -21,6 +21,56 @@ reg = <0x0 0x80000000 0x0 0xc0000000>; }; + pcie-controller@0,01003000 { + status = "okay"; + + pci@1,0 { + status = "okay"; + }; + + pci@2,0 { + status = "okay"; + }; + }; + + padctl@0,7009f000 { + pinctrl-0 = <&padctl_default>; + pinctrl-names = "default"; + + padctl_default: pinmux { + xusb { + nvidia,lanes = "otg-1", "otg-2"; + nvidia,function = "xusb"; + nvidia,iddq = <0>; + }; + + usb3 { + nvidia,lanes = "pcie-5", "pcie-6"; + nvidia,function = "usb3"; + nvidia,iddq = <0>; + }; + + pcie-x1 { + nvidia,lanes = "pcie-0"; + nvidia,function = "pcie-x1"; + nvidia,iddq = <0>; + }; + + pcie-x4 { + nvidia,lanes = "pcie-1", "pcie-2", + "pcie-3", "pcie-4"; + nvidia,function = "pcie-x4"; + nvidia,iddq = <0>; + }; + + sata { + nvidia,lanes = "sata-0"; + nvidia,function = "sata"; + nvidia,iddq = <0>; + }; + }; + }; + sdhci@0,700b0000 { status = "okay"; cd-gpios = <&gpio TEGRA_GPIO(Z, 1) GPIO_ACTIVE_LOW>; diff --git a/board/nvidia/p2371-2180/p2371-2180.c b/board/nvidia/p2371-2180/p2371-2180.c index cf2dd0b..57f577d8 100644 --- a/board/nvidia/p2371-2180/p2371-2180.c +++ b/board/nvidia/p2371-2180/p2371-2180.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -49,3 +50,32 @@ void pinmux_init(void) pinmux_config_drvgrp_table(p2371_2180_drvgrps, ARRAY_SIZE(p2371_2180_drvgrps)); } + +#ifdef CONFIG_PCI_TEGRA +int tegra_pcie_board_init(void) +{ + struct udevice *dev; + uchar val; + int ret; + + /* Turn on MAX77620 LDO1 to 1.05V for PEX power */ + debug("%s: Set LDO1 for PEX power to 1.05V\n", __func__); + ret = i2c_get_chip_for_busnum(0, MAX77620_I2C_ADDR_7BIT, 1, &dev); + if (ret) { + printf("%s: Cannot find MAX77620 I2C chip\n", __func__); + return -1; + } + /* 0xCA for 1.05v, enabled: bit7:6 = 11 = enable, bit5:0 = voltage */ + val = 0xCA; + ret = dm_i2c_write(dev, MAX77620_CNFG1_L1_REG, &val, 1); + if (ret) + printf("i2c_write 0 0x3c 0x25 failed: %d\n", ret); + + return 0; +} + +int board_eth_init(bd_t *bis) +{ + return pci_eth_init(bis); +} +#endif /* PCI */ diff --git a/include/configs/p2371-2180.h b/include/configs/p2371-2180.h index 3bdf196..94f8085 100644 --- a/include/configs/p2371-2180.h +++ b/include/configs/p2371-2180.h @@ -53,6 +53,16 @@ #define CONFIG_USB_HOST_ETHER #define CONFIG_USB_ETHER_ASIX +/* PCI host support */ +#define CONFIG_PCI +#define CONFIG_PCI_TEGRA +#define CONFIG_PCI_PNP +#define CONFIG_CMD_PCI +#define CONFIG_CMD_PCI_ENUM + +/* PCI networking support */ +#define CONFIG_RTL8169 + /* General networking support */ #define CONFIG_CMD_DHCP -- cgit v0.10.2 From aae52c000f9b778248e8fa5d63cf1ad125b48a06 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 23 Oct 2015 10:50:47 -0600 Subject: ARM: tegra: rename dummy XUSB padctl implementation A future patch will soon move some of the XUSB padctl code into a common file in arch/arm/mach-tegra. Rename the existing dummy XUSB padctl file to avoid conflicting with that, or being confusing. Signed-off-by: Stephen Warren Reviewed-by: Simon Glass Signed-off-by: Tom Warren diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 98431a9..2be6ef4 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -23,7 +23,7 @@ obj-y += clock.o obj-y += lowlevel_init.o obj-y += pinmux-common.o obj-y += powergate.o -obj-y += xusb-padctl.o +obj-y += xusb-padctl-dummy.o obj-$(CONFIG_DISPLAY_CPUINFO) += sys_info.o obj-$(CONFIG_TEGRA_GPU) += gpu.o obj-$(CONFIG_TEGRA_CLOCK_SCALING) += emc.o diff --git a/arch/arm/mach-tegra/xusb-padctl-dummy.c b/arch/arm/mach-tegra/xusb-padctl-dummy.c new file mode 100644 index 0000000..65f8d2e --- /dev/null +++ b/arch/arm/mach-tegra/xusb-padctl-dummy.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include + +#include + +struct tegra_xusb_phy * __weak tegra_xusb_phy_get(unsigned int type) +{ + return NULL; +} + +int __weak tegra_xusb_phy_prepare(struct tegra_xusb_phy *phy) +{ + return -ENOSYS; +} + +int __weak tegra_xusb_phy_enable(struct tegra_xusb_phy *phy) +{ + return -ENOSYS; +} + +int __weak tegra_xusb_phy_disable(struct tegra_xusb_phy *phy) +{ + return -ENOSYS; +} + +int __weak tegra_xusb_phy_unprepare(struct tegra_xusb_phy *phy) +{ + return -ENOSYS; +} + +void __weak tegra_xusb_padctl_init(const void *fdt) +{ +} diff --git a/arch/arm/mach-tegra/xusb-padctl.c b/arch/arm/mach-tegra/xusb-padctl.c deleted file mode 100644 index 65f8d2e..0000000 --- a/arch/arm/mach-tegra/xusb-padctl.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. - * - * SPDX-License-Identifier: GPL-2.0 - */ - -#include -#include - -#include - -struct tegra_xusb_phy * __weak tegra_xusb_phy_get(unsigned int type) -{ - return NULL; -} - -int __weak tegra_xusb_phy_prepare(struct tegra_xusb_phy *phy) -{ - return -ENOSYS; -} - -int __weak tegra_xusb_phy_enable(struct tegra_xusb_phy *phy) -{ - return -ENOSYS; -} - -int __weak tegra_xusb_phy_disable(struct tegra_xusb_phy *phy) -{ - return -ENOSYS; -} - -int __weak tegra_xusb_phy_unprepare(struct tegra_xusb_phy *phy) -{ - return -ENOSYS; -} - -void __weak tegra_xusb_padctl_init(const void *fdt) -{ -} -- cgit v0.10.2 From 057fd32ffce459038a1707366a7373fc3255ddc4 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 23 Oct 2015 10:50:48 -0600 Subject: ARM: tegra: clean up XUSB padctl error() calls This file defines pr_fmt(), so the individual error() calls don't need to include the prefix in their format strings. Doing so results in duplicate text in any error messages. Remove the duplication. Signed-off-by: Stephen Warren Reviewed-by: Simon Glass Signed-off-by: Tom Warren diff --git a/arch/arm/mach-tegra/tegra124/xusb-padctl.c b/arch/arm/mach-tegra/tegra124/xusb-padctl.c index 43af883..ce857b8 100644 --- a/arch/arm/mach-tegra/tegra124/xusb-padctl.c +++ b/arch/arm/mach-tegra/tegra124/xusb-padctl.c @@ -220,7 +220,7 @@ static int tegra_xusb_padctl_disable(struct tegra_xusb_padctl *padctl) u32 value; if (padctl->enable == 0) { - error("tegra-xusb-padctl: unbalanced enable/disable"); + error("unbalanced enable/disable"); return 0; } @@ -415,7 +415,7 @@ tegra_xusb_padctl_group_parse_dt(struct tegra_xusb_padctl *padctl, len = fdt_count_strings(fdt, node, "nvidia,lanes"); if (len < 0) { - error("tegra-xusb-padctl: failed to parse \"nvidia,lanes\" property"); + error("failed to parse \"nvidia,lanes\" property"); return -EINVAL; } @@ -425,7 +425,7 @@ tegra_xusb_padctl_group_parse_dt(struct tegra_xusb_padctl *padctl, err = fdt_get_string_index(fdt, node, "nvidia,lanes", i, &group->pins[i]); if (err < 0) { - error("tegra-xusb-padctl: failed to read string from \"nvidia,lanes\" property"); + error("failed to read string from \"nvidia,lanes\" property"); return -EINVAL; } } @@ -434,7 +434,7 @@ tegra_xusb_padctl_group_parse_dt(struct tegra_xusb_padctl *padctl, err = fdt_get_string(fdt, node, "nvidia,function", &group->func); if (err < 0) { - error("tegra-xusb-padctl: failed to parse \"nvidia,func\" property"); + error("failed to parse \"nvidia,func\" property"); return -EINVAL; } @@ -487,15 +487,14 @@ tegra_xusb_padctl_group_apply(struct tegra_xusb_padctl *padctl, lane = tegra_xusb_padctl_find_lane(padctl, group->pins[i]); if (!lane) { - error("tegra-xusb-padctl: no lane for pin %s", - group->pins[i]); + error("no lane for pin %s", group->pins[i]); continue; } func = tegra_xusb_padctl_lane_find_function(padctl, lane, group->func); if (func < 0) { - error("tegra-xusb-padctl: function %s invalid for lane %s: %d", + error("function %s invalid for lane %s: %d", group->func, lane->name, func); continue; } @@ -537,8 +536,7 @@ tegra_xusb_padctl_config_apply(struct tegra_xusb_padctl *padctl, err = tegra_xusb_padctl_group_apply(padctl, group); if (err < 0) { - error("tegra-xusb-padctl: failed to apply group %s: %d", - group->name, err); + error("failed to apply group %s: %d", group->name, err); continue; } } @@ -564,8 +562,7 @@ tegra_xusb_padctl_config_parse_dt(struct tegra_xusb_padctl *padctl, err = tegra_xusb_padctl_group_parse_dt(padctl, group, fdt, subnode); if (err < 0) { - error("tegra-xusb-padctl: failed to parse group %s", - group->name); + error("failed to parse group %s", group->name); return err; } @@ -582,7 +579,7 @@ static int tegra_xusb_padctl_parse_dt(struct tegra_xusb_padctl *padctl, err = fdt_get_resource(fdt, node, "reg", 0, &padctl->regs); if (err < 0) { - error("tegra-xusb-padctl: registers not found"); + error("registers not found"); return err; } @@ -592,8 +589,8 @@ static int tegra_xusb_padctl_parse_dt(struct tegra_xusb_padctl *padctl, err = tegra_xusb_padctl_config_parse_dt(padctl, config, fdt, subnode); if (err < 0) { - error("tegra-xusb-padctl: failed to parse entry %s: %d", - config->name, err); + error("failed to parse entry %s: %d", config->name, + err); continue; } } @@ -618,7 +615,7 @@ static int process_nodes(const void *fdt, int nodes[], unsigned int count) break; default: - error("tegra-xusb-padctl: unsupported compatible: %s", + error("unsupported compatible: %s", fdtdec_get_compatible(id)); continue; } @@ -631,8 +628,7 @@ static int process_nodes(const void *fdt, int nodes[], unsigned int count) err = tegra_xusb_padctl_parse_dt(padctl, fdt, nodes[i]); if (err < 0) { - error("tegra-xusb-padctl: failed to parse DT: %d", - err); + error("failed to parse DT: %d", err); continue; } @@ -641,8 +637,7 @@ static int process_nodes(const void *fdt, int nodes[], unsigned int count) err = tegra_xusb_padctl_config_apply(padctl, &padctl->config); if (err < 0) { - error("tegra-xusb-padctl: failed to apply pinmux: %d", - err); + error("failed to apply pinmux: %d", err); continue; } -- cgit v0.10.2 From 1680d7b6de2c63333d3a67c2f5f852a127e412cd Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 23 Oct 2015 10:50:49 -0600 Subject: ARM: tegra: create common XUSB padctl driver file A fair amount of the XUSB padctl driver will be common between Tegra124 and Tegra210. To avoid cut/paste between the two chips, create a new file that will contain the common code, and convert the Tegra124 code to use it. This change doesn't move every last piece of code that can/will be shared, but rather concentrates on moving code that can be moved with zero changes, so there are no other diffs mixed in. Signed-off-by: Stephen Warren Reviewed-by: Simon Glass Signed-off-by: Tom Warren diff --git a/arch/arm/mach-tegra/tegra124/Makefile b/arch/arm/mach-tegra/tegra124/Makefile index f577f45..c00de61 100644 --- a/arch/arm/mach-tegra/tegra124/Makefile +++ b/arch/arm/mach-tegra/tegra124/Makefile @@ -11,6 +11,7 @@ obj-y += clock.o obj-y += funcmux.o obj-y += pinmux.o obj-y += xusb-padctl.o +obj-y += ../xusb-padctl-common.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_ARMV7_NONSEC) += psci.o diff --git a/arch/arm/mach-tegra/tegra124/xusb-padctl.c b/arch/arm/mach-tegra/tegra124/xusb-padctl.c index ce857b8..b3715d8 100644 --- a/arch/arm/mach-tegra/tegra124/xusb-padctl.c +++ b/arch/arm/mach-tegra/tegra124/xusb-padctl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: GPL-2.0 */ @@ -11,6 +11,8 @@ #include #include +#include "../xusb-padctl-common.h" + #include #include @@ -83,18 +85,6 @@ static const unsigned int tegra124_pci_functions[] = { TEGRA124_FUNC_RSVD, }; -struct tegra_xusb_padctl_lane { - const char *name; - - unsigned int offset; - unsigned int shift; - unsigned int mask; - unsigned int iddq; - - const unsigned int *funcs; - unsigned int num_funcs; -}; - #define TEGRA124_LANE(_name, _offset, _shift, _mask, _iddq, _funcs) \ { \ .name = _name, \ @@ -121,74 +111,6 @@ static const struct tegra_xusb_padctl_lane tegra124_lanes[] = { TEGRA124_LANE("sata-0", 0x134, 26, 0x3, 6, pci), }; -struct tegra_xusb_phy_ops { - int (*prepare)(struct tegra_xusb_phy *phy); - int (*enable)(struct tegra_xusb_phy *phy); - int (*disable)(struct tegra_xusb_phy *phy); - int (*unprepare)(struct tegra_xusb_phy *phy); -}; - -struct tegra_xusb_phy { - const struct tegra_xusb_phy_ops *ops; - - struct tegra_xusb_padctl *padctl; -}; - -struct tegra_xusb_padctl_pin { - const struct tegra_xusb_padctl_lane *lane; - - unsigned int func; - int iddq; -}; - -#define MAX_GROUPS 3 -#define MAX_PINS 6 - -struct tegra_xusb_padctl_group { - const char *name; - - const char *pins[MAX_PINS]; - unsigned int num_pins; - - const char *func; - int iddq; -}; - -struct tegra_xusb_padctl_config { - const char *name; - - struct tegra_xusb_padctl_group groups[MAX_GROUPS]; - unsigned int num_groups; -}; - -struct tegra_xusb_padctl { - struct fdt_resource regs; - - unsigned int enable; - - struct tegra_xusb_phy phys[2]; - - const struct tegra_xusb_padctl_lane *lanes; - unsigned int num_lanes; - - const char *const *functions; - unsigned int num_functions; - - struct tegra_xusb_padctl_config config; -}; - -static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl, - unsigned long offset) -{ - return readl(padctl->regs.start + offset); -} - -static inline void padctl_writel(struct tegra_xusb_padctl *padctl, - u32 value, unsigned long offset) -{ - writel(value, padctl->regs.start + offset); -} - static int tegra_xusb_padctl_enable(struct tegra_xusb_padctl *padctl) { u32 value; @@ -380,7 +302,7 @@ static const struct tegra_xusb_phy_ops sata_phy_ops = { .unprepare = phy_unprepare, }; -static struct tegra_xusb_padctl *padctl = &(struct tegra_xusb_padctl) { +struct tegra_xusb_padctl *padctl = &(struct tegra_xusb_padctl) { .phys = { [0] = { .ops = &pcie_phy_ops, @@ -391,214 +313,7 @@ static struct tegra_xusb_padctl *padctl = &(struct tegra_xusb_padctl) { }, }; -static const struct tegra_xusb_padctl_lane * -tegra_xusb_padctl_find_lane(struct tegra_xusb_padctl *padctl, const char *name) -{ - unsigned int i; - - for (i = 0; i < padctl->num_lanes; i++) - if (strcmp(name, padctl->lanes[i].name) == 0) - return &padctl->lanes[i]; - - return NULL; -} - -static int -tegra_xusb_padctl_group_parse_dt(struct tegra_xusb_padctl *padctl, - struct tegra_xusb_padctl_group *group, - const void *fdt, int node) -{ - unsigned int i; - int len, err; - - group->name = fdt_get_name(fdt, node, &len); - - len = fdt_count_strings(fdt, node, "nvidia,lanes"); - if (len < 0) { - error("failed to parse \"nvidia,lanes\" property"); - return -EINVAL; - } - - group->num_pins = len; - - for (i = 0; i < group->num_pins; i++) { - err = fdt_get_string_index(fdt, node, "nvidia,lanes", i, - &group->pins[i]); - if (err < 0) { - error("failed to read string from \"nvidia,lanes\" property"); - return -EINVAL; - } - } - - group->num_pins = len; - - err = fdt_get_string(fdt, node, "nvidia,function", &group->func); - if (err < 0) { - error("failed to parse \"nvidia,func\" property"); - return -EINVAL; - } - - group->iddq = fdtdec_get_int(fdt, node, "nvidia,iddq", -1); - - return 0; -} - -static int tegra_xusb_padctl_find_function(struct tegra_xusb_padctl *padctl, - const char *name) -{ - unsigned int i; - - for (i = 0; i < padctl->num_functions; i++) - if (strcmp(name, padctl->functions[i]) == 0) - return i; - - return -ENOENT; -} - -static int -tegra_xusb_padctl_lane_find_function(struct tegra_xusb_padctl *padctl, - const struct tegra_xusb_padctl_lane *lane, - const char *name) -{ - unsigned int i; - int func; - - func = tegra_xusb_padctl_find_function(padctl, name); - if (func < 0) - return func; - - for (i = 0; i < lane->num_funcs; i++) - if (lane->funcs[i] == func) - return i; - - return -ENOENT; -} - -static int -tegra_xusb_padctl_group_apply(struct tegra_xusb_padctl *padctl, - const struct tegra_xusb_padctl_group *group) -{ - unsigned int i; - - for (i = 0; i < group->num_pins; i++) { - const struct tegra_xusb_padctl_lane *lane; - unsigned int func; - u32 value; - - lane = tegra_xusb_padctl_find_lane(padctl, group->pins[i]); - if (!lane) { - error("no lane for pin %s", group->pins[i]); - continue; - } - - func = tegra_xusb_padctl_lane_find_function(padctl, lane, - group->func); - if (func < 0) { - error("function %s invalid for lane %s: %d", - group->func, lane->name, func); - continue; - } - - value = padctl_readl(padctl, lane->offset); - - /* set pin function */ - value &= ~(lane->mask << lane->shift); - value |= func << lane->shift; - - /* - * Set IDDQ if supported on the lane and specified in the - * configuration. - */ - if (lane->iddq > 0 && group->iddq >= 0) { - if (group->iddq != 0) - value &= ~(1 << lane->iddq); - else - value |= 1 << lane->iddq; - } - - padctl_writel(padctl, value, lane->offset); - } - - return 0; -} - -static int -tegra_xusb_padctl_config_apply(struct tegra_xusb_padctl *padctl, - struct tegra_xusb_padctl_config *config) -{ - unsigned int i; - - for (i = 0; i < config->num_groups; i++) { - const struct tegra_xusb_padctl_group *group; - int err; - - group = &config->groups[i]; - - err = tegra_xusb_padctl_group_apply(padctl, group); - if (err < 0) { - error("failed to apply group %s: %d", group->name, err); - continue; - } - } - - return 0; -} - -static int -tegra_xusb_padctl_config_parse_dt(struct tegra_xusb_padctl *padctl, - struct tegra_xusb_padctl_config *config, - const void *fdt, int node) -{ - int subnode; - - config->name = fdt_get_name(fdt, node, NULL); - - fdt_for_each_subnode(fdt, subnode, node) { - struct tegra_xusb_padctl_group *group; - int err; - - group = &config->groups[config->num_groups]; - - err = tegra_xusb_padctl_group_parse_dt(padctl, group, fdt, - subnode); - if (err < 0) { - error("failed to parse group %s", group->name); - return err; - } - - config->num_groups++; - } - - return 0; -} - -static int tegra_xusb_padctl_parse_dt(struct tegra_xusb_padctl *padctl, - const void *fdt, int node) -{ - int subnode, err; - - err = fdt_get_resource(fdt, node, "reg", 0, &padctl->regs); - if (err < 0) { - error("registers not found"); - return err; - } - - fdt_for_each_subnode(fdt, subnode, node) { - struct tegra_xusb_padctl_config *config = &padctl->config; - - err = tegra_xusb_padctl_config_parse_dt(padctl, config, fdt, - subnode); - if (err < 0) { - error("failed to parse entry %s: %d", config->name, - err); - continue; - } - } - - return 0; -} - -static int process_nodes(const void *fdt, int nodes[], unsigned int count) +int process_nodes(const void *fdt, int nodes[], unsigned int count) { unsigned int i; @@ -648,57 +363,6 @@ static int process_nodes(const void *fdt, int nodes[], unsigned int count) return 0; } -struct tegra_xusb_phy *tegra_xusb_phy_get(unsigned int type) -{ - struct tegra_xusb_phy *phy = NULL; - - switch (type) { - case TEGRA_XUSB_PADCTL_PCIE: - phy = &padctl->phys[0]; - phy->padctl = padctl; - break; - - case TEGRA_XUSB_PADCTL_SATA: - phy = &padctl->phys[1]; - phy->padctl = padctl; - break; - } - - return phy; -} - -int tegra_xusb_phy_prepare(struct tegra_xusb_phy *phy) -{ - if (phy && phy->ops && phy->ops->prepare) - return phy->ops->prepare(phy); - - return phy ? -ENOSYS : -EINVAL; -} - -int tegra_xusb_phy_enable(struct tegra_xusb_phy *phy) -{ - if (phy && phy->ops && phy->ops->enable) - return phy->ops->enable(phy); - - return phy ? -ENOSYS : -EINVAL; -} - -int tegra_xusb_phy_disable(struct tegra_xusb_phy *phy) -{ - if (phy && phy->ops && phy->ops->disable) - return phy->ops->disable(phy); - - return phy ? -ENOSYS : -EINVAL; -} - -int tegra_xusb_phy_unprepare(struct tegra_xusb_phy *phy) -{ - if (phy && phy->ops && phy->ops->unprepare) - return phy->ops->unprepare(phy); - - return phy ? -ENOSYS : -EINVAL; -} - void tegra_xusb_padctl_init(const void *fdt) { int count, nodes[1]; diff --git a/arch/arm/mach-tegra/xusb-padctl-common.c b/arch/arm/mach-tegra/xusb-padctl-common.c new file mode 100644 index 0000000..18ad7bf --- /dev/null +++ b/arch/arm/mach-tegra/xusb-padctl-common.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#define pr_fmt(fmt) "tegra-xusb-padctl: " fmt + +#include +#include + +#include "xusb-padctl-common.h" + +#include + +int tegra_xusb_phy_prepare(struct tegra_xusb_phy *phy) +{ + if (phy && phy->ops && phy->ops->prepare) + return phy->ops->prepare(phy); + + return phy ? -ENOSYS : -EINVAL; +} + +int tegra_xusb_phy_enable(struct tegra_xusb_phy *phy) +{ + if (phy && phy->ops && phy->ops->enable) + return phy->ops->enable(phy); + + return phy ? -ENOSYS : -EINVAL; +} + +int tegra_xusb_phy_disable(struct tegra_xusb_phy *phy) +{ + if (phy && phy->ops && phy->ops->disable) + return phy->ops->disable(phy); + + return phy ? -ENOSYS : -EINVAL; +} + +int tegra_xusb_phy_unprepare(struct tegra_xusb_phy *phy) +{ + if (phy && phy->ops && phy->ops->unprepare) + return phy->ops->unprepare(phy); + + return phy ? -ENOSYS : -EINVAL; +} + +struct tegra_xusb_phy *tegra_xusb_phy_get(unsigned int type) +{ + struct tegra_xusb_phy *phy; + int i; + + for (i = 0; i < padctl.socdata->num_phys; i++) { + phy = &padctl.socdata->phys[i]; + if (phy->type != type) + continue; + return phy; + } + + return NULL; +} + +static const struct tegra_xusb_padctl_lane * +tegra_xusb_padctl_find_lane(struct tegra_xusb_padctl *padctl, const char *name) +{ + unsigned int i; + + for (i = 0; i < padctl->socdata->num_lanes; i++) + if (strcmp(name, padctl->socdata->lanes[i].name) == 0) + return &padctl->socdata->lanes[i]; + + return NULL; +} + +static int +tegra_xusb_padctl_group_parse_dt(struct tegra_xusb_padctl *padctl, + struct tegra_xusb_padctl_group *group, + const void *fdt, int node) +{ + unsigned int i; + int len, err; + + group->name = fdt_get_name(fdt, node, &len); + + len = fdt_count_strings(fdt, node, "nvidia,lanes"); + if (len < 0) { + error("failed to parse \"nvidia,lanes\" property"); + return -EINVAL; + } + + group->num_pins = len; + + for (i = 0; i < group->num_pins; i++) { + err = fdt_get_string_index(fdt, node, "nvidia,lanes", i, + &group->pins[i]); + if (err < 0) { + error("failed to read string from \"nvidia,lanes\" property"); + return -EINVAL; + } + } + + group->num_pins = len; + + err = fdt_get_string(fdt, node, "nvidia,function", &group->func); + if (err < 0) { + error("failed to parse \"nvidia,func\" property"); + return -EINVAL; + } + + group->iddq = fdtdec_get_int(fdt, node, "nvidia,iddq", -1); + + return 0; +} + +static int tegra_xusb_padctl_find_function(struct tegra_xusb_padctl *padctl, + const char *name) +{ + unsigned int i; + + for (i = 0; i < padctl->socdata->num_functions; i++) + if (strcmp(name, padctl->socdata->functions[i]) == 0) + return i; + + return -ENOENT; +} + +static int +tegra_xusb_padctl_lane_find_function(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_padctl_lane *lane, + const char *name) +{ + unsigned int i; + int func; + + func = tegra_xusb_padctl_find_function(padctl, name); + if (func < 0) + return func; + + for (i = 0; i < lane->num_funcs; i++) + if (lane->funcs[i] == func) + return i; + + return -ENOENT; +} + +static int +tegra_xusb_padctl_group_apply(struct tegra_xusb_padctl *padctl, + const struct tegra_xusb_padctl_group *group) +{ + unsigned int i; + + for (i = 0; i < group->num_pins; i++) { + const struct tegra_xusb_padctl_lane *lane; + unsigned int func; + u32 value; + + lane = tegra_xusb_padctl_find_lane(padctl, group->pins[i]); + if (!lane) { + error("no lane for pin %s", group->pins[i]); + continue; + } + + func = tegra_xusb_padctl_lane_find_function(padctl, lane, + group->func); + if (func < 0) { + error("function %s invalid for lane %s: %d", + group->func, lane->name, func); + continue; + } + + value = padctl_readl(padctl, lane->offset); + + /* set pin function */ + value &= ~(lane->mask << lane->shift); + value |= func << lane->shift; + + /* + * Set IDDQ if supported on the lane and specified in the + * configuration. + */ + if (lane->iddq > 0 && group->iddq >= 0) { + if (group->iddq != 0) + value &= ~(1 << lane->iddq); + else + value |= 1 << lane->iddq; + } + + padctl_writel(padctl, value, lane->offset); + } + + return 0; +} + +static int +tegra_xusb_padctl_config_apply(struct tegra_xusb_padctl *padctl, + struct tegra_xusb_padctl_config *config) +{ + unsigned int i; + + for (i = 0; i < config->num_groups; i++) { + const struct tegra_xusb_padctl_group *group; + int err; + + group = &config->groups[i]; + + err = tegra_xusb_padctl_group_apply(padctl, group); + if (err < 0) { + error("failed to apply group %s: %d", + group->name, err); + continue; + } + } + + return 0; +} + +static int +tegra_xusb_padctl_config_parse_dt(struct tegra_xusb_padctl *padctl, + struct tegra_xusb_padctl_config *config, + const void *fdt, int node) +{ + int subnode; + + config->name = fdt_get_name(fdt, node, NULL); + + fdt_for_each_subnode(fdt, subnode, node) { + struct tegra_xusb_padctl_group *group; + int err; + + group = &config->groups[config->num_groups]; + + err = tegra_xusb_padctl_group_parse_dt(padctl, group, fdt, + subnode); + if (err < 0) { + error("failed to parse group %s", group->name); + return err; + } + + config->num_groups++; + } + + return 0; +} + +static int tegra_xusb_padctl_parse_dt(struct tegra_xusb_padctl *padctl, + const void *fdt, int node) +{ + int subnode, err; + + err = fdt_get_resource(fdt, node, "reg", 0, &padctl->regs); + if (err < 0) { + error("registers not found"); + return err; + } + + fdt_for_each_subnode(fdt, subnode, node) { + struct tegra_xusb_padctl_config *config = &padctl->config; + + err = tegra_xusb_padctl_config_parse_dt(padctl, config, fdt, + subnode); + if (err < 0) { + error("failed to parse entry %s: %d", + config->name, err); + continue; + } + } + + return 0; +} + +struct tegra_xusb_padctl padctl; + +int tegra_xusb_process_nodes(const void *fdt, int nodes[], unsigned int count, + const struct tegra_xusb_padctl_soc *socdata) +{ + unsigned int i; + int err; + + for (i = 0; i < count; i++) { + if (!fdtdec_get_is_enabled(fdt, nodes[i])) + continue; + + padctl.socdata = socdata; + + err = tegra_xusb_padctl_parse_dt(&padctl, fdt, nodes[i]); + if (err < 0) { + error("failed to parse DT: %d", err); + continue; + } + + /* deassert XUSB padctl reset */ + reset_set_enable(PERIPH_ID_XUSB_PADCTL, 0); + + err = tegra_xusb_padctl_config_apply(&padctl, &padctl.config); + if (err < 0) { + error("failed to apply pinmux: %d", err); + continue; + } + + /* only a single instance is supported */ + break; + } + + return 0; +} diff --git a/arch/arm/mach-tegra/xusb-padctl-common.h b/arch/arm/mach-tegra/xusb-padctl-common.h new file mode 100644 index 0000000..a65b754 --- /dev/null +++ b/arch/arm/mach-tegra/xusb-padctl-common.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _TEGRA_XUSB_PADCTL_COMMON_H_ +#define _TEGRA_XUSB_PADCTL_COMMON_H_ + +#include +#include + +#include +#include + +struct tegra_xusb_padctl_lane { + const char *name; + + unsigned int offset; + unsigned int shift; + unsigned int mask; + unsigned int iddq; + + const unsigned int *funcs; + unsigned int num_funcs; +}; + +struct tegra_xusb_phy_ops { + int (*prepare)(struct tegra_xusb_phy *phy); + int (*enable)(struct tegra_xusb_phy *phy); + int (*disable)(struct tegra_xusb_phy *phy); + int (*unprepare)(struct tegra_xusb_phy *phy); +}; + +struct tegra_xusb_phy { + const struct tegra_xusb_phy_ops *ops; + + struct tegra_xusb_padctl *padctl; +}; + +struct tegra_xusb_padctl_pin { + const struct tegra_xusb_padctl_lane *lane; + + unsigned int func; + int iddq; +}; + +#define MAX_GROUPS 3 +#define MAX_PINS 6 + +struct tegra_xusb_padctl_group { + const char *name; + + const char *pins[MAX_PINS]; + unsigned int num_pins; + + const char *func; + int iddq; +}; + +struct tegra_xusb_padctl_config { + const char *name; + + struct tegra_xusb_padctl_group groups[MAX_GROUPS]; + unsigned int num_groups; +}; + +struct tegra_xusb_padctl { + struct fdt_resource regs; + + unsigned int enable; + + struct tegra_xusb_phy phys[2]; + + const struct tegra_xusb_padctl_lane *lanes; + unsigned int num_lanes; + + const char *const *functions; + unsigned int num_functions; + + struct tegra_xusb_padctl_config config; +}; + +static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl, + unsigned long offset) +{ + return readl(padctl->regs.start + offset); +} + +static inline void padctl_writel(struct tegra_xusb_padctl *padctl, + u32 value, unsigned long offset) +{ + writel(value, padctl->regs.start + offset); +} + +extern struct tegra_xusb_padctl *padctl; + +int tegra_xusb_padctl_parse_dt(struct tegra_xusb_padctl *padctl, + const void *fdt, int node); +int tegra_xusb_padctl_config_apply(struct tegra_xusb_padctl *padctl, + struct tegra_xusb_padctl_config *config); + +#endif -- cgit v0.10.2 From 095e65839e0732b7fe67c76c6f2b604a66e5ee38 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 23 Oct 2015 10:50:50 -0600 Subject: ARM: tegra: parameterize common XUSB code There are some differences between the Tegra124 and Tegra210 XUSB padctl code. So far, the common XUSB padctl code only supports Tegra124. Add some parameters etc. so that it can work for both chips. This also allows moving Tegra124's process_nodes() into the common file; something that would have requires edits during the move if done in the previous commit. Signed-off-by: Stephen Warren Reviewed-by: Simon Glass Signed-off-by: Tom Warren diff --git a/arch/arm/mach-tegra/tegra124/xusb-padctl.c b/arch/arm/mach-tegra/tegra124/xusb-padctl.c index b3715d8..76af924 100644 --- a/arch/arm/mach-tegra/tegra124/xusb-padctl.c +++ b/arch/arm/mach-tegra/tegra124/xusb-padctl.c @@ -8,16 +8,9 @@ #include #include -#include -#include #include "../xusb-padctl-common.h" -#include - -#include -#include - #include #define XUSB_PADCTL_ELPG_PROGRAM 0x01c @@ -302,66 +295,27 @@ static const struct tegra_xusb_phy_ops sata_phy_ops = { .unprepare = phy_unprepare, }; -struct tegra_xusb_padctl *padctl = &(struct tegra_xusb_padctl) { - .phys = { - [0] = { - .ops = &pcie_phy_ops, - }, - [1] = { - .ops = &sata_phy_ops, - }, +static struct tegra_xusb_phy tegra124_phys[] = { + { + .type = TEGRA_XUSB_PADCTL_PCIE, + .ops = &pcie_phy_ops, + .padctl = &padctl, + }, + { + .type = TEGRA_XUSB_PADCTL_SATA, + .ops = &sata_phy_ops, + .padctl = &padctl, }, }; -int process_nodes(const void *fdt, int nodes[], unsigned int count) -{ - unsigned int i; - - for (i = 0; i < count; i++) { - enum fdt_compat_id id; - int err; - - if (!fdtdec_get_is_enabled(fdt, nodes[i])) - continue; - - id = fdtdec_lookup(fdt, nodes[i]); - switch (id) { - case COMPAT_NVIDIA_TEGRA124_XUSB_PADCTL: - break; - - default: - error("unsupported compatible: %s", - fdtdec_get_compatible(id)); - continue; - } - - padctl->num_lanes = ARRAY_SIZE(tegra124_lanes); - padctl->lanes = tegra124_lanes; - - padctl->num_functions = ARRAY_SIZE(tegra124_functions); - padctl->functions = tegra124_functions; - - err = tegra_xusb_padctl_parse_dt(padctl, fdt, nodes[i]); - if (err < 0) { - error("failed to parse DT: %d", err); - continue; - } - - /* deassert XUSB padctl reset */ - reset_set_enable(PERIPH_ID_XUSB_PADCTL, 0); - - err = tegra_xusb_padctl_config_apply(padctl, &padctl->config); - if (err < 0) { - error("failed to apply pinmux: %d", err); - continue; - } - - /* only a single instance is supported */ - break; - } - - return 0; -} +static const struct tegra_xusb_padctl_soc tegra124_socdata = { + .lanes = tegra124_lanes, + .num_lanes = ARRAY_SIZE(tegra124_lanes), + .functions = tegra124_functions, + .num_functions = ARRAY_SIZE(tegra124_functions), + .phys = tegra124_phys, + .num_phys = ARRAY_SIZE(tegra124_phys), +}; void tegra_xusb_padctl_init(const void *fdt) { @@ -370,6 +324,6 @@ void tegra_xusb_padctl_init(const void *fdt) count = fdtdec_find_aliases_for_id(fdt, "padctl", COMPAT_NVIDIA_TEGRA124_XUSB_PADCTL, nodes, ARRAY_SIZE(nodes)); - if (process_nodes(fdt, nodes, count)) + if (tegra_xusb_process_nodes(fdt, nodes, count, &tegra124_socdata)) return; } diff --git a/arch/arm/mach-tegra/xusb-padctl-common.h b/arch/arm/mach-tegra/xusb-padctl-common.h index a65b754..11ecb99 100644 --- a/arch/arm/mach-tegra/xusb-padctl-common.h +++ b/arch/arm/mach-tegra/xusb-padctl-common.h @@ -33,8 +33,8 @@ struct tegra_xusb_phy_ops { }; struct tegra_xusb_phy { + unsigned int type; const struct tegra_xusb_phy_ops *ops; - struct tegra_xusb_padctl *padctl; }; @@ -58,6 +58,15 @@ struct tegra_xusb_padctl_group { int iddq; }; +struct tegra_xusb_padctl_soc { + const struct tegra_xusb_padctl_lane *lanes; + unsigned int num_lanes; + const char *const *functions; + unsigned int num_functions; + struct tegra_xusb_phy *phys; + unsigned int num_phys; +}; + struct tegra_xusb_padctl_config { const char *name; @@ -66,20 +75,13 @@ struct tegra_xusb_padctl_config { }; struct tegra_xusb_padctl { + const struct tegra_xusb_padctl_soc *socdata; + struct tegra_xusb_padctl_config config; struct fdt_resource regs; - unsigned int enable; - struct tegra_xusb_phy phys[2]; - - const struct tegra_xusb_padctl_lane *lanes; - unsigned int num_lanes; - - const char *const *functions; - unsigned int num_functions; - - struct tegra_xusb_padctl_config config; }; +extern struct tegra_xusb_padctl padctl; static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl, unsigned long offset) @@ -93,11 +95,7 @@ static inline void padctl_writel(struct tegra_xusb_padctl *padctl, writel(value, padctl->regs.start + offset); } -extern struct tegra_xusb_padctl *padctl; - -int tegra_xusb_padctl_parse_dt(struct tegra_xusb_padctl *padctl, - const void *fdt, int node); -int tegra_xusb_padctl_config_apply(struct tegra_xusb_padctl *padctl, - struct tegra_xusb_padctl_config *config); +int tegra_xusb_process_nodes(const void *fdt, int nodes[], unsigned int count, + const struct tegra_xusb_padctl_soc *socdata); #endif -- cgit v0.10.2 From 7a908c7e01faa978d1ae9991016e5581735f3006 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 23 Oct 2015 10:50:51 -0600 Subject: ARM: tegra: switch Tegra210 to common XUSB padctl This change simply deletes code from the Tegra210 XUSB padctl driver that is already present in the common XUSB padctl code. Since all the arrays in tegra210_socdata are empty, this update may leave the Tegra210 XUSB padctl driver non-functional at run-time. However, (a) this driver is not used yet so no regression can be observed and (b) the next commit will immediately fix this up. Signed-off-by: Stephen Warren Reviewed-by: Simon Glass Signed-off-by: Tom Warren diff --git a/arch/arm/mach-tegra/tegra210/Makefile b/arch/arm/mach-tegra/tegra210/Makefile index 1fb8d1a..b6012fc 100644 --- a/arch/arm/mach-tegra/tegra210/Makefile +++ b/arch/arm/mach-tegra/tegra210/Makefile @@ -9,3 +9,4 @@ obj-y += clock.o obj-y += funcmux.o obj-y += pinmux.o obj-y += xusb-padctl.o +obj-y += ../xusb-padctl-common.o diff --git a/arch/arm/mach-tegra/tegra210/xusb-padctl.c b/arch/arm/mach-tegra/tegra210/xusb-padctl.c index 3c10a96..5033543 100644 --- a/arch/arm/mach-tegra/tegra210/xusb-padctl.c +++ b/arch/arm/mach-tegra/tegra210/xusb-padctl.c @@ -8,52 +8,13 @@ #include #include -#include -#include -#include +#include "../xusb-padctl-common.h" #include -#include #include -struct tegra_xusb_phy_ops { - int (*prepare)(struct tegra_xusb_phy *phy); - int (*enable)(struct tegra_xusb_phy *phy); - int (*disable)(struct tegra_xusb_phy *phy); - int (*unprepare)(struct tegra_xusb_phy *phy); -}; - -struct tegra_xusb_phy { - const struct tegra_xusb_phy_ops *ops; - - struct tegra_xusb_padctl *padctl; -}; - -struct tegra_xusb_padctl { - struct fdt_resource regs; - - unsigned int enable; - - struct tegra_xusb_phy phys[2]; -}; - -static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl, - unsigned long offset) -{ - u32 value = readl(padctl->regs.start + offset); - debug("padctl: %08lx > %08x\n", offset, value); - return value; -} - -static inline void padctl_writel(struct tegra_xusb_padctl *padctl, - u32 value, unsigned long offset) -{ - debug("padctl: %08lx < %08x\n", offset, value); - writel(value, padctl->regs.start + offset); -} - #define XUSB_PADCTL_ELPG_PROGRAM 0x024 #define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 31) #define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30) @@ -358,120 +319,22 @@ static const struct tegra_xusb_phy_ops pcie_phy_ops = { .unprepare = phy_unprepare, }; -static struct tegra_xusb_padctl *padctl = &(struct tegra_xusb_padctl) { - .phys = { - [0] = { - .ops = &pcie_phy_ops, - }, +static struct tegra_xusb_phy tegra210_phys[] = { + { + .type = TEGRA_XUSB_PADCTL_PCIE, + .ops = &pcie_phy_ops, + .padctl = &padctl, }, }; -static int tegra_xusb_padctl_parse_dt(struct tegra_xusb_padctl *padctl, - const void *fdt, int node) -{ - int err; - - err = fdt_get_resource(fdt, node, "reg", 0, &padctl->regs); - if (err < 0) { - error("registers not found"); - return err; - } - - debug("regs: %pa-%pa\n", &padctl->regs.start, - &padctl->regs.end); - - return 0; -} - -static int process_nodes(const void *fdt, int nodes[], unsigned int count) -{ - unsigned int i; - int err; - - debug("> %s(fdt=%p, nodes=%p, count=%u)\n", __func__, fdt, nodes, - count); - - for (i = 0; i < count; i++) { - enum fdt_compat_id id; - - if (!fdtdec_get_is_enabled(fdt, nodes[i])) - continue; - - id = fdtdec_lookup(fdt, nodes[i]); - switch (id) { - case COMPAT_NVIDIA_TEGRA124_XUSB_PADCTL: - case COMPAT_NVIDIA_TEGRA210_XUSB_PADCTL: - break; - - default: - error("unsupported compatible: %s", - fdtdec_get_compatible(id)); - continue; - } - - err = tegra_xusb_padctl_parse_dt(padctl, fdt, nodes[i]); - if (err < 0) { - error("failed to parse DT: %d", - err); - continue; - } - - /* deassert XUSB padctl reset */ - reset_set_enable(PERIPH_ID_XUSB_PADCTL, 0); - - /* only a single instance is supported */ - break; - } - - debug("< %s()\n", __func__); - return 0; -} - -struct tegra_xusb_phy *tegra_xusb_phy_get(unsigned int type) -{ - struct tegra_xusb_phy *phy = NULL; - - switch (type) { - case TEGRA_XUSB_PADCTL_PCIE: - phy = &padctl->phys[0]; - phy->padctl = padctl; - break; - } - - return phy; -} - -int tegra_xusb_phy_prepare(struct tegra_xusb_phy *phy) -{ - if (phy && phy->ops && phy->ops->prepare) - return phy->ops->prepare(phy); - - return phy ? -ENOSYS : -EINVAL; -} - -int tegra_xusb_phy_enable(struct tegra_xusb_phy *phy) -{ - if (phy && phy->ops && phy->ops->enable) - return phy->ops->enable(phy); - - return phy ? -ENOSYS : -EINVAL; -} - -int tegra_xusb_phy_disable(struct tegra_xusb_phy *phy) -{ - if (phy && phy->ops && phy->ops->disable) - return phy->ops->disable(phy); - - return phy ? -ENOSYS : -EINVAL; -} - -int tegra_xusb_phy_unprepare(struct tegra_xusb_phy *phy) -{ - if (phy && phy->ops && phy->ops->unprepare) - return phy->ops->unprepare(phy); - - return phy ? -ENOSYS : -EINVAL; -} +static const struct tegra_xusb_padctl_soc tegra210_socdata = { + .lanes = NULL, + .num_lanes = 0, + .functions = NULL, + .num_functions = 0, + .phys = tegra210_phys, + .num_phys = ARRAY_SIZE(tegra210_phys), +}; void tegra_xusb_padctl_init(const void *fdt) { @@ -482,13 +345,7 @@ void tegra_xusb_padctl_init(const void *fdt) count = fdtdec_find_aliases_for_id(fdt, "padctl", COMPAT_NVIDIA_TEGRA210_XUSB_PADCTL, nodes, ARRAY_SIZE(nodes)); - if (process_nodes(fdt, nodes, count)) - return; - - count = fdtdec_find_aliases_for_id(fdt, "padctl", - COMPAT_NVIDIA_TEGRA124_XUSB_PADCTL, - nodes, ARRAY_SIZE(nodes)); - if (process_nodes(fdt, nodes, count)) + if (tegra_xusb_process_nodes(fdt, nodes, count, &tegra210_socdata)) return; debug("< %s()\n", __func__); -- cgit v0.10.2 From 4e4b5574fb2a0536f133a36f2fc96bd43ed92f14 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 23 Oct 2015 10:50:52 -0600 Subject: ARM: tegra: add lane tables to Tegra210 XUSB padctl Add the tables defining which pads and mux options exist in the Tegra210 XUSB padctl hardware. Signed-off-by: Stephen Warren Reviewed-by: Simon Glass Signed-off-by: Tom Warren diff --git a/arch/arm/mach-tegra/tegra210/xusb-padctl.c b/arch/arm/mach-tegra/tegra210/xusb-padctl.c index 5033543..6022f16 100644 --- a/arch/arm/mach-tegra/tegra210/xusb-padctl.c +++ b/arch/arm/mach-tegra/tegra210/xusb-padctl.c @@ -15,6 +15,76 @@ #include +enum tegra210_function { + TEGRA210_FUNC_SNPS, + TEGRA210_FUNC_XUSB, + TEGRA210_FUNC_UART, + TEGRA210_FUNC_PCIE_X1, + TEGRA210_FUNC_PCIE_X4, + TEGRA210_FUNC_USB3, + TEGRA210_FUNC_SATA, + TEGRA210_FUNC_RSVD, +}; + +static const char *const tegra210_functions[] = { + "snps", + "xusb", + "uart", + "pcie-x1", + "pcie-x4", + "usb3", + "sata", + "rsvd", +}; + +static const unsigned int tegra210_otg_functions[] = { + TEGRA210_FUNC_SNPS, + TEGRA210_FUNC_XUSB, + TEGRA210_FUNC_UART, + TEGRA210_FUNC_RSVD, +}; + +static const unsigned int tegra210_usb_functions[] = { + TEGRA210_FUNC_SNPS, + TEGRA210_FUNC_XUSB, +}; + +static const unsigned int tegra210_pci_functions[] = { + TEGRA210_FUNC_PCIE_X1, + TEGRA210_FUNC_USB3, + TEGRA210_FUNC_SATA, + TEGRA210_FUNC_PCIE_X4, +}; + +#define TEGRA210_LANE(_name, _offset, _shift, _mask, _iddq, _funcs) \ + { \ + .name = _name, \ + .offset = _offset, \ + .shift = _shift, \ + .mask = _mask, \ + .iddq = _iddq, \ + .num_funcs = ARRAY_SIZE(tegra210_##_funcs##_functions), \ + .funcs = tegra210_##_funcs##_functions, \ + } + +static const struct tegra_xusb_padctl_lane tegra210_lanes[] = { + TEGRA210_LANE("otg-0", 0x004, 0, 0x3, 0, otg), + TEGRA210_LANE("otg-1", 0x004, 2, 0x3, 0, otg), + TEGRA210_LANE("otg-2", 0x004, 4, 0x3, 0, otg), + TEGRA210_LANE("otg-3", 0x004, 6, 0x3, 0, otg), + TEGRA210_LANE("usb2-bias", 0x004, 18, 0x3, 0, otg), + TEGRA210_LANE("hsic-0", 0x004, 14, 0x1, 0, usb), + TEGRA210_LANE("hsic-1", 0x004, 15, 0x1, 0, usb), + TEGRA210_LANE("pcie-0", 0x028, 12, 0x3, 1, pci), + TEGRA210_LANE("pcie-1", 0x028, 14, 0x3, 2, pci), + TEGRA210_LANE("pcie-2", 0x028, 16, 0x3, 3, pci), + TEGRA210_LANE("pcie-3", 0x028, 18, 0x3, 4, pci), + TEGRA210_LANE("pcie-4", 0x028, 20, 0x3, 5, pci), + TEGRA210_LANE("pcie-5", 0x028, 22, 0x3, 6, pci), + TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, 7, pci), + TEGRA210_LANE("sata-0", 0x028, 30, 0x3, 8, pci), +}; + #define XUSB_PADCTL_ELPG_PROGRAM 0x024 #define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 31) #define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30) @@ -328,10 +398,10 @@ static struct tegra_xusb_phy tegra210_phys[] = { }; static const struct tegra_xusb_padctl_soc tegra210_socdata = { - .lanes = NULL, - .num_lanes = 0, - .functions = NULL, - .num_functions = 0, + .lanes = tegra210_lanes, + .num_lanes = ARRAY_SIZE(tegra210_lanes), + .functions = tegra210_functions, + .num_functions = ARRAY_SIZE(tegra210_functions), .phys = tegra210_phys, .num_phys = ARRAY_SIZE(tegra210_phys), }; diff --git a/arch/arm/mach-tegra/xusb-padctl-common.h b/arch/arm/mach-tegra/xusb-padctl-common.h index 11ecb99..f44790a 100644 --- a/arch/arm/mach-tegra/xusb-padctl-common.h +++ b/arch/arm/mach-tegra/xusb-padctl-common.h @@ -45,8 +45,8 @@ struct tegra_xusb_padctl_pin { int iddq; }; -#define MAX_GROUPS 3 -#define MAX_PINS 6 +#define MAX_GROUPS 5 +#define MAX_PINS 7 struct tegra_xusb_padctl_group { const char *name; -- cgit v0.10.2 From f35cb12511f5e0fe608adfab38ed44a29e9578ab Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 23 Oct 2015 10:50:53 -0600 Subject: ARM: tegra: error check Tegra210 XUSB padctl waits Add code to detect timeouts when waiting for HW events such as PLL lock done. Any errors are logged and trigger an error return code. Signed-off-by: Stephen Warren Reviewed-by: Simon Glass Signed-off-by: Tom Warren diff --git a/arch/arm/mach-tegra/tegra210/xusb-padctl.c b/arch/arm/mach-tegra/tegra210/xusb-padctl.c index 6022f16..9ec93e7 100644 --- a/arch/arm/mach-tegra/tegra210/xusb-padctl.c +++ b/arch/arm/mach-tegra/tegra210/xusb-padctl.c @@ -279,7 +279,10 @@ static int pcie_phy_enable(struct tegra_xusb_phy *phy) if (value & XUSB_PADCTL_UPHY_PLL_P0_CTL2_CAL_DONE) break; } - + if (!(value & XUSB_PADCTL_UPHY_PLL_P0_CTL2_CAL_DONE)) { + debug(" timeout\n"); + return -ETIMEDOUT; + } debug(" done\n"); value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2); @@ -295,7 +298,10 @@ static int pcie_phy_enable(struct tegra_xusb_phy *phy) if ((value & XUSB_PADCTL_UPHY_PLL_P0_CTL2_CAL_DONE) == 0) break; } - + if (value & XUSB_PADCTL_UPHY_PLL_P0_CTL2_CAL_DONE) { + debug(" timeout\n"); + return -ETIMEDOUT; + } debug(" done\n"); value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1); @@ -310,7 +316,10 @@ static int pcie_phy_enable(struct tegra_xusb_phy *phy) if (value & XUSB_PADCTL_UPHY_PLL_P0_CTL1_LOCKDET_STATUS) break; } - + if (!(value & XUSB_PADCTL_UPHY_PLL_P0_CTL1_LOCKDET_STATUS)) { + debug(" timeout\n"); + return -ETIMEDOUT; + } debug(" done\n"); value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8); @@ -326,7 +335,10 @@ static int pcie_phy_enable(struct tegra_xusb_phy *phy) if (value & XUSB_PADCTL_UPHY_PLL_P0_CTL8_RCAL_DONE) break; } - + if (!(value & XUSB_PADCTL_UPHY_PLL_P0_CTL8_RCAL_DONE)) { + debug(" timeout\n"); + return -ETIMEDOUT; + } debug(" done\n"); value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8); @@ -341,7 +353,10 @@ static int pcie_phy_enable(struct tegra_xusb_phy *phy) if ((value & XUSB_PADCTL_UPHY_PLL_P0_CTL8_RCAL_DONE) == 0) break; } - + if (value & XUSB_PADCTL_UPHY_PLL_P0_CTL8_RCAL_DONE) { + debug(" timeout\n"); + return -ETIMEDOUT; + } debug(" done\n"); value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8); -- cgit v0.10.2 From 36e5f7ce1c64b4fa0a12c82f84086f5be560b706 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 19 Oct 2015 13:57:01 +0900 Subject: ARM: tegra: remove vpr_configured() function There is no justification for this function, especially in exported form. Signed-off-by: Alexandre Courbot Signed-off-by: Tom Warren diff --git a/arch/arm/include/asm/arch-tegra/gpu.h b/arch/arm/include/asm/arch-tegra/gpu.h index 52280f4..2fdb2c5 100644 --- a/arch/arm/include/asm/arch-tegra/gpu.h +++ b/arch/arm/include/asm/arch-tegra/gpu.h @@ -11,7 +11,6 @@ #if defined(CONFIG_TEGRA_GPU) void config_gpu(void); -bool gpu_configured(void); #else /* CONFIG_TEGRA_GPU */ @@ -19,11 +18,6 @@ static inline void config_gpu(void) { } -static inline bool gpu_configured(void) -{ - return false; -} - #endif /* CONFIG_TEGRA_GPU */ #if defined(CONFIG_OF_LIBFDT) diff --git a/arch/arm/mach-tegra/gpu.c b/arch/arm/mach-tegra/gpu.c index 4ea046d..dc29b79 100644 --- a/arch/arm/mach-tegra/gpu.c +++ b/arch/arm/mach-tegra/gpu.c @@ -41,18 +41,13 @@ void config_gpu(void) _configured = true; } -bool vpr_configured(void) -{ - return _configured; -} - #if defined(CONFIG_OF_LIBFDT) int gpu_enable_node(void *blob, const char *gpupath) { int offset; - if (vpr_configured()) { + if (_configured) { offset = fdt_path_offset(blob, gpupath); if (offset > 0) { fdt_status_okay(blob, offset); -- cgit v0.10.2 From d6bf06c0c7edf347354b208adf7618c96fd61605 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 19 Oct 2015 13:57:02 +0900 Subject: ARM: tegra: simplify GPU setup Enable the GPU node in the system-wide ft_system_setup() hook instead of the board-specific ft_board_hook(). This allows us to enable GPU per SoC generation instead of per-board as we did initially. Reported-by: Stephen Warren Signed-off-by: Alexandre Courbot Signed-off-by: Tom Warren diff --git a/arch/arm/mach-tegra/board2.c b/arch/arm/mach-tegra/board2.c index 8ecc674..ff9e77c 100644 --- a/arch/arm/mach-tegra/board2.c +++ b/arch/arm/mach-tegra/board2.c @@ -403,3 +403,23 @@ ulong board_get_usable_ram_top(ulong total_size) { return CONFIG_SYS_SDRAM_BASE + usable_ram_size_below_4g(); } + +/* + * This function is called right before the kernel is booted. "blob" is the + * device tree that will be passed to the kernel. + */ +int ft_system_setup(void *blob, bd_t *bd) +{ + const char *gpu_path = +#if defined(CONFIG_TEGRA124) || defined(CONFIG_TEGRA210) + "/gpu@0,57000000"; +#else + NULL; +#endif + + /* Enable GPU node if GPU setup has been performed */ + if (gpu_path != NULL) + return gpu_enable_node(blob, gpu_path); + + return 0; +} diff --git a/board/nvidia/jetson-tk1/jetson-tk1.c b/board/nvidia/jetson-tk1/jetson-tk1.c index 3c21767..52425a8 100644 --- a/board/nvidia/jetson-tk1/jetson-tk1.c +++ b/board/nvidia/jetson-tk1/jetson-tk1.c @@ -11,7 +11,6 @@ #include #include -#include #include "pinmux-config-jetson-tk1.h" @@ -80,10 +79,3 @@ int board_eth_init(bd_t *bis) return pci_eth_init(bis); } #endif /* PCI */ - -int ft_board_setup(void *blob, bd_t *bd) -{ - gpu_enable_node(blob, "/gpu@0,57000000"); - - return 0; -} diff --git a/board/nvidia/p2571/p2571.c b/board/nvidia/p2571/p2571.c index d33e4d1..d80a7d0 100644 --- a/board/nvidia/p2571/p2571.c +++ b/board/nvidia/p2571/p2571.c @@ -11,7 +11,6 @@ #include #include #include "max77620_init.h" -#include #include "pinmux-config-p2571.h" void pin_mux_mmc(void) @@ -62,9 +61,3 @@ void start_cpu_fan(void) gpio_request(GPIO_PE4, "FAN_VDD"); gpio_direction_output(GPIO_PE4, 1); } - -int ft_board_setup(void *blob, bd_t *bd) -{ - gpu_enable_node(blob, "/gpu@0,57000000"); - return 0; -} diff --git a/board/nvidia/venice2/venice2.c b/board/nvidia/venice2/venice2.c index 3e2b9a7..c56ef12 100644 --- a/board/nvidia/venice2/venice2.c +++ b/board/nvidia/venice2/venice2.c @@ -8,7 +8,6 @@ #include #include #include -#include #include "pinmux-config-venice2.h" /* @@ -28,10 +27,3 @@ void pinmux_init(void) pinmux_config_drvgrp_table(venice2_drvgrps, ARRAY_SIZE(venice2_drvgrps)); } - -int ft_board_setup(void *blob, bd_t *bd) -{ - gpu_enable_node(blob, "/gpu@0,57000000"); - - return 0; -} diff --git a/include/configs/jetson-tk1.h b/include/configs/jetson-tk1.h index e87a010..f63957a 100644 --- a/include/configs/jetson-tk1.h +++ b/include/configs/jetson-tk1.h @@ -78,6 +78,4 @@ #define CONFIG_ARMV7_SECURE_BASE 0xfff00000 #define CONFIG_ARMV7_SECURE_RESERVE_SIZE 0x00100000 -#define CONFIG_OF_BOARD_SETUP - #endif /* __CONFIG_H */ diff --git a/include/configs/p2571.h b/include/configs/p2571.h index c65d3e5..a5de411 100644 --- a/include/configs/p2571.h +++ b/include/configs/p2571.h @@ -60,6 +60,4 @@ #include "tegra-common-usb-gadget.h" #include "tegra-common-post.h" -#define CONFIG_OF_BOARD_SETUP - #endif /* _P2571_H */ diff --git a/include/configs/tegra-common.h b/include/configs/tegra-common.h index a005e6a..32cc39b 100644 --- a/include/configs/tegra-common.h +++ b/include/configs/tegra-common.h @@ -143,4 +143,6 @@ #define CONFIG_FAT_WRITE #endif +#define CONFIG_OF_SYSTEM_SETUP + #endif /* _TEGRA_COMMON_H_ */ diff --git a/include/configs/venice2.h b/include/configs/venice2.h index 0fc8cf7..a374cd9 100644 --- a/include/configs/venice2.h +++ b/include/configs/venice2.h @@ -60,6 +60,4 @@ #include "tegra-common-usb-gadget.h" #include "tegra-common-post.h" -#define CONFIG_OF_BOARD_SETUP - #endif /* __CONFIG_H */ -- cgit v0.10.2 From eca676bd673f7737d1e85391d846f014d9c197da Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Mon, 19 Oct 2015 13:57:03 +0900 Subject: ARM: tegra: rename GPU functions Rename GPU functions to less generic names to avoid potential name collisions. Signed-off-by: Alexandre Courbot Acked-by: Stephen Warren Signed-off-by: Tom Warren diff --git a/arch/arm/include/asm/arch-tegra/gpu.h b/arch/arm/include/asm/arch-tegra/gpu.h index 2fdb2c5..4423386 100644 --- a/arch/arm/include/asm/arch-tegra/gpu.h +++ b/arch/arm/include/asm/arch-tegra/gpu.h @@ -10,11 +10,11 @@ #if defined(CONFIG_TEGRA_GPU) -void config_gpu(void); +void tegra_gpu_config(void); #else /* CONFIG_TEGRA_GPU */ -static inline void config_gpu(void) +static inline void tegra_gpu_config(void) { } @@ -22,11 +22,11 @@ static inline void config_gpu(void) #if defined(CONFIG_OF_LIBFDT) -int gpu_enable_node(void *blob, const char *gpupath); +int tegra_gpu_enable_node(void *blob, const char *gpupath); #else /* CONFIG_OF_LIBFDT */ -static inline int gpu_enable_node(void *blob, const char *gpupath) +static inline int tegra_gpu_enable_node(void *blob, const char *gpupath) { return 0; } diff --git a/arch/arm/mach-tegra/board2.c b/arch/arm/mach-tegra/board2.c index ff9e77c..8ba143d 100644 --- a/arch/arm/mach-tegra/board2.c +++ b/arch/arm/mach-tegra/board2.c @@ -128,7 +128,7 @@ int board_init(void) clock_init(); clock_verify(); - config_gpu(); + tegra_gpu_config(); #ifdef CONFIG_TEGRA_SPI pin_mux_spi(); @@ -419,7 +419,7 @@ int ft_system_setup(void *blob, bd_t *bd) /* Enable GPU node if GPU setup has been performed */ if (gpu_path != NULL) - return gpu_enable_node(blob, gpu_path); + return tegra_gpu_enable_node(blob, gpu_path); return 0; } diff --git a/arch/arm/mach-tegra/gpu.c b/arch/arm/mach-tegra/gpu.c index dc29b79..c7d705d 100644 --- a/arch/arm/mach-tegra/gpu.c +++ b/arch/arm/mach-tegra/gpu.c @@ -25,7 +25,7 @@ static bool _configured; -void config_gpu(void) +void tegra_gpu_config(void) { struct mc_ctlr *mc = (struct mc_ctlr *)NV_PA_MC_BASE; @@ -43,7 +43,7 @@ void config_gpu(void) #if defined(CONFIG_OF_LIBFDT) -int gpu_enable_node(void *blob, const char *gpupath) +int tegra_gpu_enable_node(void *blob, const char *gpupath) { int offset; -- cgit v0.10.2 From 4e675ff2442a8856587f800aa49c28321ca5d6d6 Mon Sep 17 00:00:00 2001 From: Tom Warren Date: Mon, 12 Oct 2015 14:50:54 -0700 Subject: Tegra: T210: Add QSPI driver This is the normal Tegra SPI driver modified to work with the QSPI controller in Tegra210. It does not do 2x/4x transfers or any other QSPI protocol. Signed-off-by: Yen Lin Signed-off-by: Tom Warren Reviewed-by: Jagan Teki diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 7d7a9d0..a0dbd8b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -123,6 +123,13 @@ config TEGRA20_SLINK be used to access the SPI NOR flash on platforms embedding this nVidia Tegra20/Tegra30 IP cores. +config TEGRA210_QSPI + bool "nVidia Tegra210 QSPI driver" + help + Enable the Tegra Quad-SPI (QSPI) driver for T210. This driver + be used to access SPI chips on platforms embedding this + NVIDIA Tegra210 IP core. + config XILINX_SPI bool "Xilinx SPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 637fea8..3eca745 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_SH_QSPI) += sh_qspi.o obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o obj-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o obj-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o +obj-$(CONFIG_TEGRA210_QSPI) += tegra210_qspi.o obj-$(CONFIG_TI_QSPI) += ti_qspi.o obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o obj-$(CONFIG_ZYNQ_SPI) += zynq_spi.o diff --git a/drivers/spi/tegra210_qspi.c b/drivers/spi/tegra210_qspi.c new file mode 100644 index 0000000..6bbbe93 --- /dev/null +++ b/drivers/spi/tegra210_qspi.c @@ -0,0 +1,417 @@ +/* + * NVIDIA Tegra210 QSPI controller driver + * + * (C) Copyright 2015 NVIDIA Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include "tegra_spi.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* COMMAND1 */ +#define QSPI_CMD1_GO BIT(31) +#define QSPI_CMD1_M_S BIT(30) +#define QSPI_CMD1_MODE_MASK GENMASK(1,0) +#define QSPI_CMD1_MODE_SHIFT 28 +#define QSPI_CMD1_CS_SEL_MASK GENMASK(1,0) +#define QSPI_CMD1_CS_SEL_SHIFT 26 +#define QSPI_CMD1_CS_POL_INACTIVE0 BIT(22) +#define QSPI_CMD1_CS_SW_HW BIT(21) +#define QSPI_CMD1_CS_SW_VAL BIT(20) +#define QSPI_CMD1_IDLE_SDA_MASK GENMASK(1,0) +#define QSPI_CMD1_IDLE_SDA_SHIFT 18 +#define QSPI_CMD1_BIDIR BIT(17) +#define QSPI_CMD1_LSBI_FE BIT(16) +#define QSPI_CMD1_LSBY_FE BIT(15) +#define QSPI_CMD1_BOTH_EN_BIT BIT(14) +#define QSPI_CMD1_BOTH_EN_BYTE BIT(13) +#define QSPI_CMD1_RX_EN BIT(12) +#define QSPI_CMD1_TX_EN BIT(11) +#define QSPI_CMD1_PACKED BIT(5) +#define QSPI_CMD1_BITLEN_MASK GENMASK(4,0) +#define QSPI_CMD1_BITLEN_SHIFT 0 + +/* COMMAND2 */ +#define QSPI_CMD2_TX_CLK_TAP_DELAY BIT(6) +#define QSPI_CMD2_TX_CLK_TAP_DELAY_MASK GENMASK(11,6) +#define QSPI_CMD2_RX_CLK_TAP_DELAY BIT(0) +#define QSPI_CMD2_RX_CLK_TAP_DELAY_MASK GENMASK(5,0) + +/* TRANSFER STATUS */ +#define QSPI_XFER_STS_RDY BIT(30) + +/* FIFO STATUS */ +#define QSPI_FIFO_STS_CS_INACTIVE BIT(31) +#define QSPI_FIFO_STS_FRAME_END BIT(30) +#define QSPI_FIFO_STS_RX_FIFO_FLUSH BIT(15) +#define QSPI_FIFO_STS_TX_FIFO_FLUSH BIT(14) +#define QSPI_FIFO_STS_ERR BIT(8) +#define QSPI_FIFO_STS_TX_FIFO_OVF BIT(7) +#define QSPI_FIFO_STS_TX_FIFO_UNR BIT(6) +#define QSPI_FIFO_STS_RX_FIFO_OVF BIT(5) +#define QSPI_FIFO_STS_RX_FIFO_UNR BIT(4) +#define QSPI_FIFO_STS_TX_FIFO_FULL BIT(3) +#define QSPI_FIFO_STS_TX_FIFO_EMPTY BIT(2) +#define QSPI_FIFO_STS_RX_FIFO_FULL BIT(1) +#define QSPI_FIFO_STS_RX_FIFO_EMPTY BIT(0) + +#define QSPI_TIMEOUT 1000 + +struct qspi_regs { + u32 command1; /* 000:QSPI_COMMAND1 register */ + u32 command2; /* 004:QSPI_COMMAND2 register */ + u32 timing1; /* 008:QSPI_CS_TIM1 register */ + u32 timing2; /* 00c:QSPI_CS_TIM2 register */ + u32 xfer_status;/* 010:QSPI_TRANS_STATUS register */ + u32 fifo_status;/* 014:QSPI_FIFO_STATUS register */ + u32 tx_data; /* 018:QSPI_TX_DATA register */ + u32 rx_data; /* 01c:QSPI_RX_DATA register */ + u32 dma_ctl; /* 020:QSPI_DMA_CTL register */ + u32 dma_blk; /* 024:QSPI_DMA_BLK register */ + u32 rsvd[56]; /* 028-107 reserved */ + u32 tx_fifo; /* 108:QSPI_FIFO1 register */ + u32 rsvd2[31]; /* 10c-187 reserved */ + u32 rx_fifo; /* 188:QSPI_FIFO2 register */ + u32 spare_ctl; /* 18c:QSPI_SPARE_CTRL register */ +}; + +struct tegra210_qspi_priv { + struct qspi_regs *regs; + unsigned int freq; + unsigned int mode; + int periph_id; + int valid; + int last_transaction_us; +}; + +static int tegra210_qspi_ofdata_to_platdata(struct udevice *bus) +{ + struct tegra_spi_platdata *plat = bus->platdata; + const void *blob = gd->fdt_blob; + int node = bus->of_offset; + + plat->base = dev_get_addr(bus); + plat->periph_id = clock_decode_periph_id(blob, node); + + if (plat->periph_id == PERIPH_ID_NONE) { + debug("%s: could not decode periph id %d\n", __func__, + plat->periph_id); + return -FDT_ERR_NOTFOUND; + } + + /* Use 500KHz as a suitable default */ + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 500000); + plat->deactivate_delay_us = fdtdec_get_int(blob, node, + "spi-deactivate-delay", 0); + debug("%s: base=%#08lx, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n", + __func__, plat->base, plat->periph_id, plat->frequency, + plat->deactivate_delay_us); + + return 0; +} + +static int tegra210_qspi_probe(struct udevice *bus) +{ + struct tegra_spi_platdata *plat = dev_get_platdata(bus); + struct tegra210_qspi_priv *priv = dev_get_priv(bus); + + priv->regs = (struct qspi_regs *)plat->base; + + priv->last_transaction_us = timer_get_us(); + priv->freq = plat->frequency; + priv->periph_id = plat->periph_id; + + return 0; +} + +static int tegra210_qspi_claim_bus(struct udevice *bus) +{ + struct tegra210_qspi_priv *priv = dev_get_priv(bus); + struct qspi_regs *regs = priv->regs; + + /* Change SPI clock to correct frequency, PLLP_OUT0 source */ + clock_start_periph_pll(priv->periph_id, CLOCK_ID_PERIPH, priv->freq); + + debug("%s: FIFO STATUS = %08x\n", __func__, readl(®s->fifo_status)); + + /* Set master mode and sw controlled CS */ + setbits_le32(®s->command1, QSPI_CMD1_M_S | QSPI_CMD1_CS_SW_HW | + (priv->mode << QSPI_CMD1_MODE_SHIFT)); + debug("%s: COMMAND1 = %08x\n", __func__, readl(®s->command1)); + + return 0; +} + +/** + * Activate the CS by driving it LOW + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +static void spi_cs_activate(struct udevice *dev) +{ + struct udevice *bus = dev->parent; + struct tegra_spi_platdata *pdata = dev_get_platdata(bus); + struct tegra210_qspi_priv *priv = dev_get_priv(bus); + + /* If it's too soon to do another transaction, wait */ + if (pdata->deactivate_delay_us && + priv->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - priv->last_transaction_us; + if (delay_us < pdata->deactivate_delay_us) + udelay(pdata->deactivate_delay_us - delay_us); + } + + clrbits_le32(&priv->regs->command1, QSPI_CMD1_CS_SW_VAL); +} + +/** + * Deactivate the CS by driving it HIGH + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +static void spi_cs_deactivate(struct udevice *dev) +{ + struct udevice *bus = dev->parent; + struct tegra_spi_platdata *pdata = dev_get_platdata(bus); + struct tegra210_qspi_priv *priv = dev_get_priv(bus); + + setbits_le32(&priv->regs->command1, QSPI_CMD1_CS_SW_VAL); + + /* Remember time of this transaction so we can honour the bus delay */ + if (pdata->deactivate_delay_us) + priv->last_transaction_us = timer_get_us(); + + debug("Deactivate CS, bus '%s'\n", bus->name); +} + +static int tegra210_qspi_xfer(struct udevice *dev, unsigned int bitlen, + const void *data_out, void *data_in, + unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct tegra210_qspi_priv *priv = dev_get_priv(bus); + struct qspi_regs *regs = priv->regs; + u32 reg, tmpdout, tmpdin = 0; + const u8 *dout = data_out; + u8 *din = data_in; + int num_bytes, tm, ret; + + debug("%s: slave %u:%u dout %p din %p bitlen %u\n", + __func__, bus->seq, spi_chip_select(dev), dout, din, bitlen); + if (bitlen % 8) + return -1; + num_bytes = bitlen / 8; + + ret = 0; + + /* clear all error status bits */ + reg = readl(®s->fifo_status); + writel(reg, ®s->fifo_status); + + /* flush RX/TX FIFOs */ + setbits_le32(®s->fifo_status, + (QSPI_FIFO_STS_RX_FIFO_FLUSH | + QSPI_FIFO_STS_TX_FIFO_FLUSH)); + + tm = QSPI_TIMEOUT; + while ((tm && readl(®s->fifo_status) & + (QSPI_FIFO_STS_RX_FIFO_FLUSH | + QSPI_FIFO_STS_TX_FIFO_FLUSH))) { + tm--; + udelay(1); + } + + if (!tm) { + printf("%s: timeout during QSPI FIFO flush!\n", + __func__); + return -1; + } + + /* + * Notes: + * 1. don't set LSBY_FE, so no need to swap bytes from/to TX/RX FIFOs; + * 2. don't set RX_EN and TX_EN yet. + * (SW needs to make sure that while programming the blk_size, + * tx_en and rx_en bits must be zero) + * [TODO] I (Yen Lin) have problems when both RX/TX EN bits are set + * i.e., both dout and din are not NULL. + */ + clrsetbits_le32(®s->command1, + (QSPI_CMD1_LSBI_FE | QSPI_CMD1_LSBY_FE | + QSPI_CMD1_RX_EN | QSPI_CMD1_TX_EN), + (spi_chip_select(dev) << QSPI_CMD1_CS_SEL_SHIFT)); + + /* set xfer size to 1 block (32 bits) */ + writel(0, ®s->dma_blk); + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(dev); + + /* handle data in 32-bit chunks */ + while (num_bytes > 0) { + int bytes; + + tmpdout = 0; + bytes = (num_bytes > 4) ? 4 : num_bytes; + + if (dout != NULL) { + memcpy((void *)&tmpdout, (void *)dout, bytes); + dout += bytes; + num_bytes -= bytes; + writel(tmpdout, ®s->tx_fifo); + setbits_le32(®s->command1, QSPI_CMD1_TX_EN); + } + + if (din != NULL) + setbits_le32(®s->command1, QSPI_CMD1_RX_EN); + + /* clear ready bit */ + setbits_le32(®s->xfer_status, QSPI_XFER_STS_RDY); + + clrsetbits_le32(®s->command1, + QSPI_CMD1_BITLEN_MASK << QSPI_CMD1_BITLEN_SHIFT, + (bytes * 8 - 1) << QSPI_CMD1_BITLEN_SHIFT); + + /* Need to stabilize other reg bits before GO bit set. + * As per the TRM: + * "For successful operation at various freq combinations, + * a minimum of 4-5 spi_clk cycle delay might be required + * before enabling the PIO or DMA bits. The worst case delay + * calculation can be done considering slowest qspi_clk as + * 1MHz. Based on that 1us delay should be enough before + * enabling PIO or DMA." Padded another 1us for safety. + */ + udelay(2); + setbits_le32(®s->command1, QSPI_CMD1_GO); + udelay(1); + + /* + * Wait for SPI transmit FIFO to empty, or to time out. + * The RX FIFO status will be read and cleared last + */ + for (tm = 0; tm < QSPI_TIMEOUT; ++tm) { + u32 fifo_status, xfer_status; + + xfer_status = readl(®s->xfer_status); + if (!(xfer_status & QSPI_XFER_STS_RDY)) + continue; + + fifo_status = readl(®s->fifo_status); + if (fifo_status & QSPI_FIFO_STS_ERR) { + debug("%s: got a fifo error: ", __func__); + if (fifo_status & QSPI_FIFO_STS_TX_FIFO_OVF) + debug("tx FIFO overflow "); + if (fifo_status & QSPI_FIFO_STS_TX_FIFO_UNR) + debug("tx FIFO underrun "); + if (fifo_status & QSPI_FIFO_STS_RX_FIFO_OVF) + debug("rx FIFO overflow "); + if (fifo_status & QSPI_FIFO_STS_RX_FIFO_UNR) + debug("rx FIFO underrun "); + if (fifo_status & QSPI_FIFO_STS_TX_FIFO_FULL) + debug("tx FIFO full "); + if (fifo_status & QSPI_FIFO_STS_TX_FIFO_EMPTY) + debug("tx FIFO empty "); + if (fifo_status & QSPI_FIFO_STS_RX_FIFO_FULL) + debug("rx FIFO full "); + if (fifo_status & QSPI_FIFO_STS_RX_FIFO_EMPTY) + debug("rx FIFO empty "); + debug("\n"); + break; + } + + if (!(fifo_status & QSPI_FIFO_STS_RX_FIFO_EMPTY)) { + tmpdin = readl(®s->rx_fifo); + if (din != NULL) { + memcpy(din, &tmpdin, bytes); + din += bytes; + num_bytes -= bytes; + } + } + break; + } + + if (tm >= QSPI_TIMEOUT) + ret = tm; + + /* clear ACK RDY, etc. bits */ + writel(readl(®s->fifo_status), ®s->fifo_status); + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(dev); + + debug("%s: transfer ended. Value=%08x, fifo_status = %08x\n", + __func__, tmpdin, readl(®s->fifo_status)); + + if (ret) { + printf("%s: timeout during SPI transfer, tm %d\n", + __func__, ret); + return -1; + } + + return ret; +} + +static int tegra210_qspi_set_speed(struct udevice *bus, uint speed) +{ + struct tegra_spi_platdata *plat = bus->platdata; + struct tegra210_qspi_priv *priv = dev_get_priv(bus); + + if (speed > plat->frequency) + speed = plat->frequency; + priv->freq = speed; + debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq); + + return 0; +} + +static int tegra210_qspi_set_mode(struct udevice *bus, uint mode) +{ + struct tegra210_qspi_priv *priv = dev_get_priv(bus); + + priv->mode = mode; + debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode); + + return 0; +} + +static const struct dm_spi_ops tegra210_qspi_ops = { + .claim_bus = tegra210_qspi_claim_bus, + .xfer = tegra210_qspi_xfer, + .set_speed = tegra210_qspi_set_speed, + .set_mode = tegra210_qspi_set_mode, + /* + * cs_info is not needed, since we require all chip selects to be + * in the device tree explicitly + */ +}; + +static const struct udevice_id tegra210_qspi_ids[] = { + { .compatible = "nvidia,tegra210-qspi" }, + { } +}; + +U_BOOT_DRIVER(tegra210_qspi) = { + .name = "tegra210-qspi", + .id = UCLASS_SPI, + .of_match = tegra210_qspi_ids, + .ops = &tegra210_qspi_ops, + .ofdata_to_platdata = tegra210_qspi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata), + .priv_auto_alloc_size = sizeof(struct tegra210_qspi_priv), + .per_child_auto_alloc_size = sizeof(struct spi_slave), + .probe = tegra210_qspi_probe, +}; -- cgit v0.10.2 From e1cf5278024eb5c72abd69d6bda266ffc5832941 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 12 Nov 2015 08:58:22 -0700 Subject: ARM: tegra: note that p2371-2180 is Jetson TX1 p2371-2180 is the engineering board name for the Jetson TX1 developer kit. Update Kconfig description and help text to make this obvious to everyone. Signed-off-by: Stephen Warren Reviewed-by: Simon Glass Signed-off-by: Tom Warren diff --git a/arch/arm/mach-tegra/tegra210/Kconfig b/arch/arm/mach-tegra/tegra210/Kconfig index b07363a..055fb12 100644 --- a/arch/arm/mach-tegra/tegra210/Kconfig +++ b/arch/arm/mach-tegra/tegra210/Kconfig @@ -19,12 +19,12 @@ config TARGET_P2371_0000 a GPIO expansion header, and an analog audio jack. config TARGET_P2371_2180 - bool "NVIDIA Tegra210 P2371-2180 board" + bool "NVIDIA Tegra210 P2371-2180 (Jetson TX1) board" help - P2371-2180 is a P2180 CPU board married to a P2597 I/O board. The - combination contains SoC, DRAM, eMMC, SD card slot, HDMI, USB - micro-B port, Ethernet via USB3, USB3 host port, SATA, PCIe, and - two GPIO expansion headers. + P2371-2180 (Jetson TX1 developer kit) is a P2180 CPU board married + to a P2597 I/O board. The combination contains SoC, DRAM, eMMC, SD + card slot, HDMI, USB micro-B port, Ethernet via USB3, USB3 host + port, SATA, PCIe, and two GPIO expansion headers. config TARGET_P2571 bool "NVIDIA Tegra210 P2571 base board" -- cgit v0.10.2