From 7e25eb01e648b4cfde566cc2520a2d7222c63fc8 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2013 16:27:12 +0100 Subject: [PATCH 1/7] ARM: tegra: Fix some whitespace oddities Some of the powergate code uses unusual spacing around == and has a tab instead of a space before an opening parenthesis. Signed-off-by: Thierry Reding Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/powergate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index f6f5b54ff95e..d5f5bd24dd09 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -132,9 +132,9 @@ int tegra_powergate_remove_clamping(int id) * Tegra 2 has a bug where PCIE and VDE clamping masks are * swapped relatively to the partition ids */ - if (id == TEGRA_POWERGATE_VDEC) + if (id == TEGRA_POWERGATE_VDEC) mask = (1 << TEGRA_POWERGATE_PCIE); - else if (id == TEGRA_POWERGATE_PCIE) + else if (id == TEGRA_POWERGATE_PCIE) mask = (1 << TEGRA_POWERGATE_VDEC); else mask = (1 << id); From ccab7983bdb3d78da3fb918902a739fc79e018be Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2013 16:27:13 +0100 Subject: [PATCH 2/7] ARM: tegra: Rename cpu0 powergate to crail This matches the name of the powergate as listed in the TRM. Signed-off-by: Thierry Reding Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/powergate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index d5f5bd24dd09..ed4d3d15b8db 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -245,7 +245,7 @@ static const char * const powergate_name_t30[] = { }; static const char * const powergate_name_t114[] = { - [TEGRA_POWERGATE_CPU] = "cpu0", + [TEGRA_POWERGATE_CPU] = "crail", [TEGRA_POWERGATE_3D] = "3d", [TEGRA_POWERGATE_VENC] = "venc", [TEGRA_POWERGATE_VDEC] = "vdec", From 44374afee87b5384f2ee3c2e5e5b8419c1573efb Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2013 16:49:55 +0100 Subject: [PATCH 3/7] ARM: tegra: Export tegra_powergate_power_off() This function can be used by drivers, which in turn may be built as modules. Export the symbol so it is available to modules. Signed-off-by: Thierry Reding Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/powergate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index ed4d3d15b8db..e8ccb2973333 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -109,6 +109,7 @@ int tegra_powergate_power_off(int id) return tegra_powergate_set(id, false); } +EXPORT_SYMBOL(tegra_powergate_power_off); int tegra_powergate_is_powered(int id) { From 201fc0f9163314d169bb6b9837f922a8a374f81b Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 6 Dec 2013 16:49:56 +0100 Subject: [PATCH 4/7] ARM: tegra: Export tegra_powergate_remove_clamping() Drivers can use the tegra_powergate_remove_clamping() API during initialization. In order to allow such drivers to be built as modules, export the symbol. Signed-off-by: Thierry Reding Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/powergate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index e8ccb2973333..a67c92acb4a6 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -144,6 +144,7 @@ int tegra_powergate_remove_clamping(int id) return 0; } +EXPORT_SYMBOL(tegra_powergate_remove_clamping); /* Must be called with clk disabled, and returns with clk enabled */ int tegra_powergate_sequence_power_up(int id, struct clk *clk, From 9a716579660aa87a4ab4b4d1cac0686fba3707ba Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 13 Dec 2013 17:31:03 +0100 Subject: [PATCH 5/7] ARM: tegra: Add Tegra124 powergate support Three new gates have been added for Tegra124: SOR, VIC and IRAM. In addition, PCIe and SATA gates are again supported, like on Tegra20 and Tegra30. Signed-off-by: Thierry Reding Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/powergate.c | 42 +++++++++++++++++++++++++++++++++ include/linux/tegra-powergate.h | 3 +++ 2 files changed, 45 insertions(+) diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index a67c92acb4a6..a8ac634eda77 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -59,6 +59,13 @@ static const u8 tegra114_cpu_domains[] = { TEGRA_POWERGATE_CPU3, }; +static const u8 tegra124_cpu_domains[] = { + TEGRA_POWERGATE_CPU0, + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; + static DEFINE_SPINLOCK(tegra_powergate_lock); static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); @@ -206,6 +213,11 @@ int __init tegra_powergate_init(void) tegra_num_cpu_domains = 4; tegra_cpu_domains = tegra114_cpu_domains; break; + case TEGRA124: + tegra_num_powerdomains = 25; + tegra_num_cpu_domains = 4; + tegra_cpu_domains = tegra124_cpu_domains; + break; default: /* Unknown Tegra variant. Disable powergating */ tegra_num_powerdomains = 0; @@ -267,6 +279,33 @@ static const char * const powergate_name_t114[] = { [TEGRA_POWERGATE_XUSBC] = "xusbc", }; +static const char * const powergate_name_t124[] = { + [TEGRA_POWERGATE_CPU] = "crail", + [TEGRA_POWERGATE_3D] = "3d", + [TEGRA_POWERGATE_VENC] = "venc", + [TEGRA_POWERGATE_PCIE] = "pcie", + [TEGRA_POWERGATE_VDEC] = "vdec", + [TEGRA_POWERGATE_L2] = "l2", + [TEGRA_POWERGATE_MPE] = "mpe", + [TEGRA_POWERGATE_HEG] = "heg", + [TEGRA_POWERGATE_SATA] = "sata", + [TEGRA_POWERGATE_CPU1] = "cpu1", + [TEGRA_POWERGATE_CPU2] = "cpu2", + [TEGRA_POWERGATE_CPU3] = "cpu3", + [TEGRA_POWERGATE_CELP] = "celp", + [TEGRA_POWERGATE_CPU0] = "cpu0", + [TEGRA_POWERGATE_C0NC] = "c0nc", + [TEGRA_POWERGATE_C1NC] = "c1nc", + [TEGRA_POWERGATE_SOR] = "sor", + [TEGRA_POWERGATE_DIS] = "dis", + [TEGRA_POWERGATE_DISB] = "disb", + [TEGRA_POWERGATE_XUSBA] = "xusba", + [TEGRA_POWERGATE_XUSBB] = "xusbb", + [TEGRA_POWERGATE_XUSBC] = "xusbc", + [TEGRA_POWERGATE_VIC] = "vic", + [TEGRA_POWERGATE_IRAM] = "iram", +}; + static int powergate_show(struct seq_file *s, void *data) { int i; @@ -311,6 +350,9 @@ int __init tegra_powergate_debugfs_init(void) case TEGRA114: powergate_name = powergate_name_t114; break; + case TEGRA124: + powergate_name = powergate_name_t124; + break; } if (powergate_name) { diff --git a/include/linux/tegra-powergate.h b/include/linux/tegra-powergate.h index afe442d2629a..bccad3cfff87 100644 --- a/include/linux/tegra-powergate.h +++ b/include/linux/tegra-powergate.h @@ -38,11 +38,14 @@ struct reset_control; #define TEGRA_POWERGATE_CPU0 14 #define TEGRA_POWERGATE_C0NC 15 #define TEGRA_POWERGATE_C1NC 16 +#define TEGRA_POWERGATE_SOR 17 #define TEGRA_POWERGATE_DIS 18 #define TEGRA_POWERGATE_DISB 19 #define TEGRA_POWERGATE_XUSBA 20 #define TEGRA_POWERGATE_XUSBB 21 #define TEGRA_POWERGATE_XUSBC 22 +#define TEGRA_POWERGATE_VIC 23 +#define TEGRA_POWERGATE_IRAM 24 #define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D From c537376cbbbfea2b741b39ad37d5b44104b66e56 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 13 Dec 2013 17:31:04 +0100 Subject: [PATCH 6/7] ARM: tegra: Special-case the 3D clamps on Tegra124 A separate register is used to remove the clamps for the GPU on Tegra124. In order to be able to use the same API, special-case this particular partition. Signed-off-by: Thierry Reding Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/powergate.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index a8ac634eda77..0026fb6c984b 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -41,6 +41,8 @@ #define PWRGATE_STATUS 0x38 +#define GPU_RG_CNTRL 0x2d4 + static int tegra_num_powerdomains; static int tegra_num_cpu_domains; static const u8 *tegra_cpu_domains; @@ -136,6 +138,17 @@ int tegra_powergate_remove_clamping(int id) if (id < 0 || id >= tegra_num_powerdomains) return -EINVAL; + /* + * The Tegra124 GPU has a separate register (with different semantics) + * to remove clamps. + */ + if (tegra_chip_id == TEGRA124) { + if (id == TEGRA_POWERGATE_3D) { + pmc_write(0, GPU_RG_CNTRL); + return 0; + } + } + /* * Tegra 2 has a bug where PCIE and VDE clamping masks are * swapped relatively to the partition ids From 9d4450ae87398e248e8700e3507748e0d1751e25 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 16 Dec 2013 21:42:28 +0100 Subject: [PATCH 7/7] ARM: tegra: Add IO rail support Add tegra_io_rail_power_off() and tegra_io_rail_power_on() functions to put IO rails into or out of deep powerdown mode, respectively. Signed-off-by: Thierry Reding Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/powergate.c | 132 ++++++++++++++++++++++++++++++++ include/linux/tegra-powergate.h | 45 +++++++++++ 2 files changed, 177 insertions(+) diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index 0026fb6c984b..3d0c537d9b94 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -34,6 +34,10 @@ #include "fuse.h" #include "iomap.h" +#define DPD_SAMPLE 0x020 +#define DPD_SAMPLE_ENABLE (1 << 0) +#define DPD_SAMPLE_DISABLE (0 << 0) + #define PWRGATE_TOGGLE 0x30 #define PWRGATE_TOGGLE_START (1 << 8) @@ -41,6 +45,17 @@ #define PWRGATE_STATUS 0x38 +#define IO_DPD_REQ 0x1b8 +#define IO_DPD_REQ_CODE_IDLE (0 << 30) +#define IO_DPD_REQ_CODE_OFF (1 << 30) +#define IO_DPD_REQ_CODE_ON (2 << 30) +#define IO_DPD_REQ_CODE_MASK (3 << 30) + +#define IO_DPD_STATUS 0x1bc +#define IO_DPD2_REQ 0x1c0 +#define IO_DPD2_STATUS 0x1c4 +#define SEL_DPD_TIM 0x1c8 + #define GPU_RG_CNTRL 0x2d4 static int tegra_num_powerdomains; @@ -379,3 +394,120 @@ int __init tegra_powergate_debugfs_init(void) } #endif + +static int tegra_io_rail_prepare(int id, unsigned long *request, + unsigned long *status, unsigned int *bit) +{ + unsigned long rate, value; + struct clk *clk; + + *bit = id % 32; + + /* + * There are two sets of 30 bits to select IO rails, but bits 30 and + * 31 are control bits rather than IO rail selection bits. + */ + if (id > 63 || *bit == 30 || *bit == 31) + return -EINVAL; + + if (id < 32) { + *status = IO_DPD_STATUS; + *request = IO_DPD_REQ; + } else { + *status = IO_DPD2_STATUS; + *request = IO_DPD2_REQ; + } + + clk = clk_get_sys(NULL, "pclk"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rate = clk_get_rate(clk); + clk_put(clk); + + pmc_write(DPD_SAMPLE_ENABLE, DPD_SAMPLE); + + /* must be at least 200 ns, in APB (PCLK) clock cycles */ + value = DIV_ROUND_UP(1000000000, rate); + value = DIV_ROUND_UP(200, value); + pmc_write(value, SEL_DPD_TIM); + + return 0; +} + +static int tegra_io_rail_poll(unsigned long offset, unsigned long mask, + unsigned long val, unsigned long timeout) +{ + unsigned long value; + + timeout = jiffies + msecs_to_jiffies(timeout); + + while (time_after(timeout, jiffies)) { + value = pmc_read(offset); + if ((value & mask) == val) + return 0; + + usleep_range(250, 1000); + } + + return -ETIMEDOUT; +} + +static void tegra_io_rail_unprepare(void) +{ + pmc_write(DPD_SAMPLE_DISABLE, DPD_SAMPLE); +} + +int tegra_io_rail_power_on(int id) +{ + unsigned long request, status, value; + unsigned int bit, mask; + int err; + + err = tegra_io_rail_prepare(id, &request, &status, &bit); + if (err < 0) + return err; + + mask = 1 << bit; + + value = pmc_read(request); + value |= mask; + value &= ~IO_DPD_REQ_CODE_MASK; + value |= IO_DPD_REQ_CODE_OFF; + pmc_write(value, request); + + err = tegra_io_rail_poll(status, mask, 0, 250); + if (err < 0) + return err; + + tegra_io_rail_unprepare(); + + return 0; +} + +int tegra_io_rail_power_off(int id) +{ + unsigned long request, status, value; + unsigned int bit, mask; + int err; + + err = tegra_io_rail_prepare(id, &request, &status, &bit); + if (err < 0) + return err; + + mask = 1 << bit; + + value = pmc_read(request); + value |= mask; + value &= ~IO_DPD_REQ_CODE_MASK; + value |= IO_DPD_REQ_CODE_ON; + pmc_write(value, request); + + err = tegra_io_rail_poll(status, mask, mask, 250); + if (err < 0) + return err; + + tegra_io_rail_unprepare(); + + return 0; +} diff --git a/include/linux/tegra-powergate.h b/include/linux/tegra-powergate.h index bccad3cfff87..e6f2ab3014a7 100644 --- a/include/linux/tegra-powergate.h +++ b/include/linux/tegra-powergate.h @@ -49,6 +49,38 @@ struct reset_control; #define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D +#define TEGRA_IO_RAIL_CSIA 0 +#define TEGRA_IO_RAIL_CSIB 1 +#define TEGRA_IO_RAIL_DSI 2 +#define TEGRA_IO_RAIL_MIPI_BIAS 3 +#define TEGRA_IO_RAIL_PEX_BIAS 4 +#define TEGRA_IO_RAIL_PEX_CLK1 5 +#define TEGRA_IO_RAIL_PEX_CLK2 6 +#define TEGRA_IO_RAIL_USB0 9 +#define TEGRA_IO_RAIL_USB1 10 +#define TEGRA_IO_RAIL_USB2 11 +#define TEGRA_IO_RAIL_USB_BIAS 12 +#define TEGRA_IO_RAIL_NAND 13 +#define TEGRA_IO_RAIL_UART 14 +#define TEGRA_IO_RAIL_BB 15 +#define TEGRA_IO_RAIL_AUDIO 17 +#define TEGRA_IO_RAIL_HSIC 19 +#define TEGRA_IO_RAIL_COMP 22 +#define TEGRA_IO_RAIL_HDMI 28 +#define TEGRA_IO_RAIL_PEX_CNTRL 32 +#define TEGRA_IO_RAIL_SDMMC1 33 +#define TEGRA_IO_RAIL_SDMMC3 34 +#define TEGRA_IO_RAIL_SDMMC4 35 +#define TEGRA_IO_RAIL_CAM 36 +#define TEGRA_IO_RAIL_RES 37 +#define TEGRA_IO_RAIL_HV 38 +#define TEGRA_IO_RAIL_DSIB 39 +#define TEGRA_IO_RAIL_DSIC 40 +#define TEGRA_IO_RAIL_DSID 41 +#define TEGRA_IO_RAIL_CSIE 44 +#define TEGRA_IO_RAIL_LVDS 57 +#define TEGRA_IO_RAIL_SYS_DDC 58 + #ifdef CONFIG_ARCH_TEGRA int tegra_powergate_is_powered(int id); int tegra_powergate_power_on(int id); @@ -58,6 +90,9 @@ int tegra_powergate_remove_clamping(int id); /* Must be called with clk disabled, and returns with clk enabled */ int tegra_powergate_sequence_power_up(int id, struct clk *clk, struct reset_control *rst); + +int tegra_io_rail_power_on(int id); +int tegra_io_rail_power_off(int id); #else static inline int tegra_powergate_is_powered(int id) { @@ -84,6 +119,16 @@ static inline int tegra_powergate_sequence_power_up(int id, struct clk *clk, { return -ENOSYS; } + +static inline int tegra_io_rail_power_on(int id) +{ + return -ENOSYS; +} + +static inline int tegra_io_rail_power_off(int id) +{ + return -ENOSYS; +} #endif #endif /* _MACH_TEGRA_POWERGATE_H_ */