diff options
author | Thierry Reding <treding@nvidia.com> | 2013-12-17 17:09:23 (GMT) |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2013-12-17 17:09:23 (GMT) |
commit | a4d86e5e42f5ceea2a076735c887cc49a885a043 (patch) | |
tree | d76eb253bc2b673551992ef9367286758a4050d0 | |
parent | b03bb79d4f3677adef410274fe73e6f16a1b3f41 (diff) | |
parent | 9d4450ae87398e248e8700e3507748e0d1751e25 (diff) | |
download | linux-a4d86e5e42f5ceea2a076735c887cc49a885a043.tar.xz |
Merge tag 'tegra-for-3.14-powergate' into drm/for-next
ARM: tegra: powergate driver changes
This branch includes all the changes to Tegra's powergate driver for 3.14.
These are separate out, since the Tegra DRM changes for 3.14 rely on the
new APIs introduced here.
A few cleanups and fixes are included, plus additions of Tegra124 SoC
support, and a new API for manipulating Tegra's IO rail deep power down
states.
This branch is based on tag tegra-for-3.14-dmas-resets-rework, in order
to avoid conflicts with the addition of common reset controller support
to the powergate driver.
-rw-r--r-- | arch/arm/mach-tegra/powergate.c | 195 | ||||
-rw-r--r-- | include/linux/tegra-powergate.h | 48 |
2 files changed, 240 insertions, 3 deletions
diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index f6f5b54..3d0c537 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,19 @@ #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; static int tegra_num_cpu_domains; static const u8 *tegra_cpu_domains; @@ -59,6 +76,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); @@ -109,6 +133,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) { @@ -129,12 +154,23 @@ int tegra_powergate_remove_clamping(int id) 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 */ - 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); @@ -143,6 +179,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, @@ -204,6 +241,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; @@ -245,12 +287,36 @@ 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", + [TEGRA_POWERGATE_MPE] = "mpe", + [TEGRA_POWERGATE_HEG] = "heg", + [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_DIS] = "dis", + [TEGRA_POWERGATE_DISB] = "disb", + [TEGRA_POWERGATE_XUSBA] = "xusba", + [TEGRA_POWERGATE_XUSBB] = "xusbb", + [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", @@ -258,11 +324,14 @@ static const char * const powergate_name_t114[] = { [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) @@ -309,6 +378,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) { @@ -322,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 afe442d..e6f2ab3 100644 --- a/include/linux/tegra-powergate.h +++ b/include/linux/tegra-powergate.h @@ -38,14 +38,49 @@ 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 +#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); @@ -55,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) { @@ -81,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_ */ |