diff options
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r-- | arch/arm/mach-tegra/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/fuse.c | 41 | ||||
-rw-r--r-- | arch/arm/mach-tegra/iomap.h | 14 | ||||
-rw-r--r-- | arch/arm/mach-tegra/powergate.c | 195 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra.c | 4 |
5 files changed, 241 insertions, 14 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 15c09294effa..d1a12a496525 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -65,6 +65,7 @@ config ARCH_TEGRA_124_SOC bool "Enable support for Tegra124 family" select ARM_L1_CACHE_SHIFT_6 select HAVE_ARM_ARCH_TIMER + select PINCTRL_TEGRA124 help Support for NVIDIA Tegra T124 processor family, based on the ARM CortexA15MP CPU diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index 3a9c1f1c219d..c9ac23b385be 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c @@ -22,6 +22,7 @@ #include <linux/io.h> #include <linux/export.h> #include <linux/random.h> +#include <linux/clk.h> #include <linux/tegra-soc.h> #include "fuse.h" @@ -54,6 +55,7 @@ int tegra_cpu_speedo_id; /* only exist in Tegra30 and later */ int tegra_soc_speedo_id; enum tegra_revision tegra_revision; +static struct clk *fuse_clk; static int tegra_fuse_spare_bit; static void (*tegra_init_speedo_data)(void); @@ -77,6 +79,22 @@ static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { [TEGRA_REVISION_A04] = "A04", }; +static void tegra_fuse_enable_clk(void) +{ + if (IS_ERR(fuse_clk)) + fuse_clk = clk_get_sys(NULL, "fuse"); + if (IS_ERR(fuse_clk)) + return; + clk_prepare_enable(fuse_clk); +} + +static void tegra_fuse_disable_clk(void) +{ + if (IS_ERR(fuse_clk)) + return; + clk_disable_unprepare(fuse_clk); +} + u32 tegra_fuse_readl(unsigned long offset) { return tegra_apb_readl(TEGRA_FUSE_BASE + offset); @@ -84,7 +102,15 @@ u32 tegra_fuse_readl(unsigned long offset) bool tegra_spare_fuse(int bit) { - return tegra_fuse_readl(tegra_fuse_spare_bit + bit * 4); + bool ret; + + tegra_fuse_enable_clk(); + + ret = tegra_fuse_readl(tegra_fuse_spare_bit + bit * 4); + + tegra_fuse_disable_clk(); + + return ret; } static enum tegra_revision tegra_get_revision(u32 id) @@ -113,10 +139,14 @@ static void tegra_get_process_id(void) { u32 reg; + tegra_fuse_enable_clk(); + reg = tegra_fuse_readl(tegra_fuse_spare_bit); tegra_cpu_process_id = (reg >> 6) & 3; reg = tegra_fuse_readl(tegra_fuse_spare_bit); tegra_core_process_id = (reg >> 12) & 3; + + tegra_fuse_disable_clk(); } u32 tegra_read_chipid(void) @@ -159,6 +189,15 @@ void __init tegra_init_fuse(void) reg |= 1 << 28; writel(reg, IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x48)); + /* + * Enable FUSE clock. This needs to be hardcoded because the clock + * subsystem is not active during early boot. + */ + reg = readl(IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x14)); + reg |= 1 << 7; + writel(reg, IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x14)); + fuse_clk = ERR_PTR(-EINVAL); + reg = tegra_fuse_readl(FUSE_SKU_INFO); randomness[0] = reg; tegra_sku_id = reg & 0xFF; diff --git a/arch/arm/mach-tegra/iomap.h b/arch/arm/mach-tegra/iomap.h index 26b1c2ad0ceb..ee79808e93a3 100644 --- a/arch/arm/mach-tegra/iomap.h +++ b/arch/arm/mach-tegra/iomap.h @@ -19,6 +19,7 @@ #ifndef __MACH_TEGRA_IOMAP_H #define __MACH_TEGRA_IOMAP_H +#include <asm/pgtable.h> #include <asm/sizes.h> #define TEGRA_IRAM_BASE 0x40000000 @@ -115,27 +116,26 @@ * two 256MB io windows (that actually only use about 64KB * at the start of each). * - * We will just map the first 1MB of each window (to minimize + * We will just map the first MMU section of each window (to minimize * pt entries needed) and provide a macro to transform physical * io addresses to an appropriate void __iomem *. - * */ #define IO_IRAM_PHYS 0x40000000 #define IO_IRAM_VIRT IOMEM(0xFE400000) #define IO_IRAM_SIZE SZ_256K -#define IO_CPU_PHYS 0x50040000 -#define IO_CPU_VIRT IOMEM(0xFE000000) +#define IO_CPU_PHYS 0x50040000 +#define IO_CPU_VIRT IOMEM(0xFE440000) #define IO_CPU_SIZE SZ_16K #define IO_PPSB_PHYS 0x60000000 #define IO_PPSB_VIRT IOMEM(0xFE200000) -#define IO_PPSB_SIZE SZ_1M +#define IO_PPSB_SIZE SECTION_SIZE #define IO_APB_PHYS 0x70000000 -#define IO_APB_VIRT IOMEM(0xFE300000) -#define IO_APB_SIZE SZ_1M +#define IO_APB_VIRT IOMEM(0xFE000000) +#define IO_APB_SIZE SECTION_SIZE #define IO_TO_VIRT_BETWEEN(p, st, sz) ((p) >= (st) && (p) < ((st) + (sz))) #define IO_TO_VIRT_XLATE(p, pst, vst) (((p) - (pst) + (vst))) diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index f6f5b54ff95e..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,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/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index 73368176c6e8..ea14d380fc0c 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -60,15 +60,13 @@ * kernel is loaded. The data is declared here rather than debug-macro.S so * that multiple inclusions of debug-macro.S point at the same data. */ -u32 tegra_uart_config[4] = { +u32 tegra_uart_config[3] = { /* Debug UART initialization required */ 1, /* Debug UART physical address */ 0, /* Debug UART virtual address */ 0, - /* Scratch space for debug macro */ - 0, }; static void __init tegra_init_cache(void) |