| /* |
| * arch/arm/mach-tegra/common.c |
| * |
| * Copyright (C) 2010 Google, Inc. |
| * Copyright (C) 2010-2012 NVIDIA Corporation |
| * |
| * Author: |
| * Colin Cross <ccross@android.com> |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License version 2, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| |
| #include <linux/platform_device.h> |
| #include <linux/console.h> |
| #include <linux/init.h> |
| #include <linux/io.h> |
| #include <linux/clk.h> |
| #include <linux/delay.h> |
| #include <linux/irqchip.h> |
| #include <linux/clk/tegra.h> |
| #include <linux/highmem.h> |
| #include <linux/memblock.h> |
| #include <linux/bitops.h> |
| #include <linux/sched.h> |
| #include <linux/of.h> |
| #include <linux/pstore_ram.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/sys_soc.h> |
| |
| #include <asm/hardware/cache-l2x0.h> |
| #include <asm/system.h> |
| #include <asm/dma-mapping.h> |
| |
| #include <mach/hardware.h> |
| #include <mach/powergate.h> |
| #include <mach/tegra_smmu.h> |
| #include <mach/gpio-tegra.h> |
| |
| #include "apbio.h" |
| #include "board.h" |
| #include "clock.h" |
| #include "common.h" |
| #include "dvfs.h" |
| #include "fuse.h" |
| #include "iomap.h" |
| #include "pm.h" |
| #include "sleep.h" |
| #include "reset.h" |
| #include "devices.h" |
| #include "pmc.h" |
| |
| #define MC_SECURITY_CFG2 0x7c |
| |
| #define AHB_ARBITRATION_PRIORITY_CTRL 0x4 |
| #define AHB_PRIORITY_WEIGHT(x) (((x) & 0x7) << 29) |
| #define PRIORITY_SELECT_USB BIT(6) |
| #define PRIORITY_SELECT_USB2 BIT(18) |
| #define PRIORITY_SELECT_USB3 BIT(17) |
| |
| #define AHB_GIZMO_AHB_MEM 0xc |
| #define ENB_FAST_REARBITRATE BIT(2) |
| #define DONT_SPLIT_AHB_WR BIT(7) |
| |
| #define RECOVERY_MODE BIT(31) |
| #define BOOTLOADER_MODE BIT(30) |
| #define FORCED_RECOVERY_MODE BIT(1) |
| |
| #define AHB_GIZMO_USB 0x1c |
| #define AHB_GIZMO_USB2 0x78 |
| #define AHB_GIZMO_USB3 0x7c |
| #define IMMEDIATE BIT(18) |
| |
| #define AHB_MEM_PREFETCH_CFG3 0xe0 |
| #define AHB_MEM_PREFETCH_CFG4 0xe4 |
| #define AHB_MEM_PREFETCH_CFG1 0xec |
| #define AHB_MEM_PREFETCH_CFG2 0xf0 |
| #define PREFETCH_ENB BIT(31) |
| #define MST_ID(x) (((x) & 0x1f) << 26) |
| #define AHBDMA_MST_ID MST_ID(5) |
| #define USB_MST_ID MST_ID(6) |
| #define USB2_MST_ID MST_ID(18) |
| #define USB3_MST_ID MST_ID(17) |
| #define ADDR_BNDRY(x) (((x) & 0xf) << 21) |
| #define INACTIVITY_TIMEOUT(x) (((x) & 0xffff) << 0) |
| |
| unsigned long tegra_avp_kernel_start; |
| unsigned long tegra_avp_kernel_size; |
| unsigned long tegra_bootloader_fb_start; |
| unsigned long tegra_bootloader_fb_size; |
| unsigned long tegra_fb_start; |
| unsigned long tegra_fb_size; |
| unsigned long tegra_fb2_start; |
| unsigned long tegra_fb2_size; |
| unsigned long tegra_carveout_start; |
| unsigned long tegra_carveout_size; |
| unsigned long tegra_vpr_start; |
| unsigned long tegra_vpr_size; |
| unsigned long tegra_tsec_start; |
| unsigned long tegra_tsec_size; |
| unsigned long tegra_lp0_vec_start; |
| unsigned long tegra_lp0_vec_size; |
| bool tegra_lp0_vec_relocate; |
| unsigned long tegra_grhost_aperture = ~0ul; |
| static bool is_tegra_debug_uart_hsport; |
| static struct board_info pmu_board_info; |
| static struct board_info display_board_info; |
| static struct board_info camera_board_info; |
| |
| static int pmu_core_edp = 1200; /* default 1.2V EDP limit */ |
| static int board_panel_type; |
| static enum power_supply_type pow_supply_type = POWER_SUPPLY_TYPE_MAINS; |
| |
| /* |
| * Storage for debug-macro.S's state. |
| * |
| * This must be in .data not .bss so that it gets initialized each time the |
| * 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] = { |
| /* Debug UART initialization required */ |
| 1, |
| /* Debug UART physical address */ |
| 0, |
| /* Debug UART virtual address */ |
| 0, |
| /* Scratch space for debug macro */ |
| 0, |
| }; |
| |
| #ifdef CONFIG_OF |
| void __init tegra_dt_init_irq(void) |
| { |
| tegra_clocks_init(); |
| tegra_pmc_init(); |
| tegra_init_irq(); |
| irqchip_init(); |
| } |
| #endif |
| |
| #define NEVER_RESET 0 |
| |
| void tegra_assert_system_reset(char mode, const char *cmd) |
| { |
| #if defined(CONFIG_TEGRA_FPGA_PLATFORM) || NEVER_RESET |
| pr_info("tegra_assert_system_reset() ignored....."); |
| do { } while (1); |
| #else |
| void __iomem *reset = IO_ADDRESS(TEGRA_PMC_BASE + 0); |
| u32 reg; |
| |
| reg = readl_relaxed(reset + PMC_SCRATCH0); |
| /* Writing recovery kernel or Bootloader mode in SCRATCH0 31:30:1 */ |
| if (cmd) { |
| if (!strcmp(cmd, "recovery")) |
| reg |= RECOVERY_MODE; |
| else if (!strcmp(cmd, "bootloader")) |
| reg |= BOOTLOADER_MODE; |
| else if (!strcmp(cmd, "forced-recovery")) |
| reg |= FORCED_RECOVERY_MODE; |
| else |
| reg &= ~(BOOTLOADER_MODE | RECOVERY_MODE | FORCED_RECOVERY_MODE); |
| } |
| else { |
| /* Clearing SCRATCH0 31:30:1 on default reboot */ |
| reg &= ~(BOOTLOADER_MODE | RECOVERY_MODE | FORCED_RECOVERY_MODE); |
| } |
| writel_relaxed(reg, reset + PMC_SCRATCH0); |
| reg = readl_relaxed(reset); |
| reg |= 0x10; |
| writel_relaxed(reg, reset); |
| #endif |
| } |
| static int modem_id; |
| static int commchip_id; |
| static int sku_override; |
| static int debug_uart_port_id; |
| static enum audio_codec_type audio_codec_name; |
| static enum image_type board_image_type = system_image; |
| static int max_cpu_current; |
| |
| #ifdef CONFIG_ARCH_TEGRA_11x_SOC |
| static __initdata struct tegra_clk_init_table tegra11x_clk_init_table[] = { |
| /* name parent rate enabled */ |
| { "clk_m", NULL, 0, true }, |
| { "emc", NULL, 0, true }, |
| { "cpu", NULL, 0, true }, |
| { "kfuse", NULL, 0, true }, |
| { "fuse", NULL, 0, true }, |
| { "sclk", NULL, 0, true }, |
| { "pll_p", NULL, 0, true }, |
| { "pll_p_out1", "pll_p", 0, false }, |
| { "pll_p_out3", "pll_p", 0, true }, |
| #ifdef CONFIG_TEGRA_SILICON_PLATFORM |
| { "pll_m_out1", "pll_m", 275000000, false }, |
| { "pll_p_out2", "pll_p", 102000000, false }, |
| { "sclk", "pll_p_out2", 102000000, true }, |
| { "pll_p_out4", "pll_p", 204000000, true }, |
| { "hclk", "sclk", 102000000, true }, |
| { "pclk", "hclk", 51000000, true }, |
| { "mselect", "pll_p", 102000000, true }, |
| { "host1x", "pll_p", 102000000, false }, |
| { "cl_dvfs_ref", "pll_p", 51000000, true }, |
| { "cl_dvfs_soc", "pll_p", 51000000, true }, |
| #else |
| { "pll_m_out1", "pll_m", 275000000, true }, |
| { "pll_p_out2", "pll_p", 108000000, false }, |
| { "sclk", "pll_p_out2", 108000000, true }, |
| { "pll_p_out4", "pll_p", 216000000, true }, |
| { "hclk", "sclk", 108000000, true }, |
| { "pclk", "hclk", 54000000, true }, |
| { "mselect", "pll_p", 108000000, true }, |
| { "host1x", "pll_p", 108000000, false }, |
| { "cl_dvfs_ref", "clk_m", 13000000, false }, |
| { "cl_dvfs_soc", "clk_m", 13000000, false }, |
| #endif |
| #ifdef CONFIG_TEGRA_SLOW_CSITE |
| { "csite", "clk_m", 1000000, true }, |
| #else |
| { "csite", NULL, 0, true }, |
| #endif |
| { "pll_u", NULL, 480000000, true }, |
| { "pll_u_12M", NULL, 0, true }, |
| { "pll_u_48M", NULL, 0, true }, |
| { "pll_u_60M", NULL, 0, true }, |
| { "pll_u_480M", NULL, 0, true }, |
| { "sdmmc1", "pll_p", 48000000, false}, |
| { "sdmmc3", "pll_p", 48000000, true}, |
| { "sdmmc4", "pll_p", 48000000, false}, |
| { "sbc1.sclk", NULL, 40000000, false}, |
| { "sbc2.sclk", NULL, 40000000, false}, |
| { "sbc3.sclk", NULL, 40000000, false}, |
| { "sbc4.sclk", NULL, 40000000, false}, |
| { "sbc5.sclk", NULL, 40000000, false}, |
| { "sbc6.sclk", NULL, 40000000, false}, |
| #ifdef CONFIG_TEGRA_DUAL_CBUS |
| { "c2bus", "pll_c2", 250000000, false }, |
| { "c3bus", "pll_c3", 250000000, false }, |
| #else |
| { "cbus", "pll_c", 250000000, false }, |
| { "pll_c_out1", "pll_c", 150000000, false }, |
| #endif |
| { NULL, NULL, 0, 0}, |
| }; |
| #endif |
| |
| #ifdef CONFIG_CACHE_L2X0 |
| #if defined(CONFIG_ARCH_TEGRA_3x_SOC) || defined(CONFIG_ARCH_TEGRA_2x_SOC) |
| #ifdef CONFIG_TRUSTED_FOUNDATIONS |
| static void tegra_cache_smc(bool enable, u32 arg) |
| { |
| void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000; |
| bool need_affinity_switch; |
| bool can_switch_affinity; |
| bool l2x0_enabled; |
| cpumask_t local_cpu_mask; |
| cpumask_t saved_cpu_mask; |
| unsigned long flags; |
| long ret; |
| |
| /* |
| * ISSUE : Some registers of PL310 controler must be written |
| * from Secure context (and from CPU0)! |
| * |
| * When called form Normal we obtain an abort or do nothing. |
| * Instructions that must be called in Secure: |
| * - Write to Control register (L2X0_CTRL==0x100) |
| * - Write in Auxiliary controler (L2X0_AUX_CTRL==0x104) |
| * - Invalidate all entries (L2X0_INV_WAY==0x77C), |
| * mandatory at boot time. |
| * - Tag and Data RAM Latency Control Registers |
| * (0x108 & 0x10C) must be written in Secure. |
| */ |
| need_affinity_switch = (smp_processor_id() != 0); |
| can_switch_affinity = !irqs_disabled(); |
| |
| WARN_ON(need_affinity_switch && !can_switch_affinity); |
| if (need_affinity_switch && can_switch_affinity) { |
| cpu_set(0, local_cpu_mask); |
| sched_getaffinity(0, &saved_cpu_mask); |
| ret = sched_setaffinity(0, &local_cpu_mask); |
| WARN_ON(ret != 0); |
| } |
| |
| local_irq_save(flags); |
| l2x0_enabled = readl_relaxed(p + L2X0_CTRL) & 1; |
| if (enable && !l2x0_enabled) |
| tegra_generic_smc(0xFFFFF100, 0x00000001, arg); |
| else if (!enable && l2x0_enabled) |
| tegra_generic_smc(0xFFFFF100, 0x00000002, arg); |
| local_irq_restore(flags); |
| |
| if (need_affinity_switch && can_switch_affinity) { |
| ret = sched_setaffinity(0, &saved_cpu_mask); |
| WARN_ON(ret != 0); |
| } |
| } |
| |
| static void tegra_l2x0_disable(void) |
| { |
| unsigned long flags; |
| static u32 l2x0_way_mask; |
| |
| if (!l2x0_way_mask) { |
| void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000; |
| u32 aux_ctrl; |
| u32 ways; |
| |
| aux_ctrl = readl_relaxed(p + L2X0_AUX_CTRL); |
| ways = (aux_ctrl & (1 << 16)) ? 16 : 8; |
| l2x0_way_mask = (1 << ways) - 1; |
| } |
| |
| local_irq_save(flags); |
| tegra_cache_smc(false, l2x0_way_mask); |
| local_irq_restore(flags); |
| } |
| #endif /* CONFIG_TRUSTED_FOUNDATIONS */ |
| |
| void tegra_init_cache(bool init) |
| { |
| void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000; |
| u32 aux_ctrl; |
| #ifndef CONFIG_TRUSTED_FOUNDATIONS |
| u32 cache_type; |
| u32 tag_latency, data_latency; |
| #endif |
| |
| #ifdef CONFIG_TRUSTED_FOUNDATIONS |
| /* issue the SMC to enable the L2 */ |
| aux_ctrl = readl_relaxed(p + L2X0_AUX_CTRL); |
| tegra_cache_smc(true, aux_ctrl); |
| |
| /* after init, reread aux_ctrl and register handlers */ |
| aux_ctrl = readl_relaxed(p + L2X0_AUX_CTRL); |
| l2x0_init(p, aux_ctrl, 0xFFFFFFFF); |
| |
| /* override outer_disable() with our disable */ |
| outer_cache.disable = tegra_l2x0_disable; |
| #else |
| #if defined(CONFIG_ARCH_TEGRA_2x_SOC) |
| tag_latency = 0x331; |
| data_latency = 0x441; |
| #elif defined(CONFIG_ARCH_TEGRA_3x_SOC) |
| #ifdef CONFIG_TEGRA_SILICON_PLATFORM |
| if (is_lp_cluster()) { |
| tag_latency = 0x221; |
| data_latency = 0x221; |
| } else { |
| u32 speedo; |
| |
| /* relax l2-cache latency for speedos 4,5,6 (T33's chips) */ |
| speedo = tegra_cpu_speedo_id(); |
| if (speedo == 4 || speedo == 5 || speedo == 6 || |
| speedo == 12 || speedo == 13) { |
| tag_latency = 0x442; |
| data_latency = 0x552; |
| } else { |
| tag_latency = 0x441; |
| data_latency = 0x551; |
| } |
| } |
| #else |
| tag_latency = 0x770; |
| data_latency = 0x770; |
| #endif |
| #endif |
| writel_relaxed(tag_latency, p + L2X0_TAG_LATENCY_CTRL); |
| writel_relaxed(data_latency, p + L2X0_DATA_LATENCY_CTRL); |
| |
| #if defined(CONFIG_ARCH_TEGRA_3x_SOC) |
| #ifndef CONFIG_TEGRA_FPGA_PLATFORM |
| writel(7, p + L2X0_PREFETCH_CTRL); |
| writel(2, p + L2X0_POWER_CTRL); |
| #endif |
| #endif |
| |
| writel(0x3, p + L2X0_POWER_CTRL); |
| cache_type = readl(p + L2X0_CACHE_TYPE); |
| aux_ctrl = (cache_type & 0x700) << (17-8); |
| aux_ctrl |= 0x7C400001; |
| if (init) { |
| l2x0_init(p, aux_ctrl, 0x8200c3fe); |
| } else { |
| u32 tmp; |
| |
| tmp = aux_ctrl; |
| aux_ctrl = readl(p + L2X0_AUX_CTRL); |
| aux_ctrl &= 0x8200c3fe; |
| aux_ctrl |= tmp; |
| writel(aux_ctrl, p + L2X0_AUX_CTRL); |
| } |
| l2x0_enable(); |
| #endif |
| } |
| #else |
| void tegra_init_cache(bool init) |
| { |
| } |
| #endif |
| #endif |
| |
| static void __init tegra_init_power(void) |
| { |
| #ifdef CONFIG_ARCH_TEGRA_HAS_SATA |
| tegra_powergate_partition_with_clk_off(TEGRA_POWERGATE_SATA); |
| #endif |
| #ifdef CONFIG_ARCH_TEGRA_HAS_PCIE |
| tegra_powergate_partition_with_clk_off(TEGRA_POWERGATE_PCIE); |
| #endif |
| } |
| |
| static inline unsigned long gizmo_readl(unsigned long offset) |
| { |
| return readl(IO_TO_VIRT(TEGRA_AHB_GIZMO_BASE + offset)); |
| } |
| |
| static inline void gizmo_writel(unsigned long value, unsigned long offset) |
| { |
| writel(value, IO_TO_VIRT(TEGRA_AHB_GIZMO_BASE + offset)); |
| } |
| |
| static void __init tegra_init_ahb_gizmo_settings(void) |
| { |
| unsigned long val; |
| |
| val = gizmo_readl(AHB_GIZMO_AHB_MEM); |
| val |= ENB_FAST_REARBITRATE | IMMEDIATE | DONT_SPLIT_AHB_WR; |
| gizmo_writel(val, AHB_GIZMO_AHB_MEM); |
| |
| val = gizmo_readl(AHB_GIZMO_USB); |
| val |= IMMEDIATE; |
| gizmo_writel(val, AHB_GIZMO_USB); |
| |
| val = gizmo_readl(AHB_GIZMO_USB2); |
| val |= IMMEDIATE; |
| gizmo_writel(val, AHB_GIZMO_USB2); |
| |
| val = gizmo_readl(AHB_GIZMO_USB3); |
| val |= IMMEDIATE; |
| gizmo_writel(val, AHB_GIZMO_USB3); |
| |
| val = gizmo_readl(AHB_ARBITRATION_PRIORITY_CTRL); |
| val |= PRIORITY_SELECT_USB | PRIORITY_SELECT_USB2 | PRIORITY_SELECT_USB3 |
| | AHB_PRIORITY_WEIGHT(7); |
| gizmo_writel(val, AHB_ARBITRATION_PRIORITY_CTRL); |
| |
| val = gizmo_readl(AHB_MEM_PREFETCH_CFG1); |
| val &= ~MST_ID(~0); |
| val |= PREFETCH_ENB | AHBDMA_MST_ID | |
| ADDR_BNDRY(0xc) | INACTIVITY_TIMEOUT(0x1000); |
| gizmo_writel(val, AHB_MEM_PREFETCH_CFG1); |
| |
| val = gizmo_readl(AHB_MEM_PREFETCH_CFG2); |
| val &= ~MST_ID(~0); |
| val |= PREFETCH_ENB | USB_MST_ID | ADDR_BNDRY(0xc) | |
| INACTIVITY_TIMEOUT(0x1000); |
| gizmo_writel(val, AHB_MEM_PREFETCH_CFG2); |
| |
| val = gizmo_readl(AHB_MEM_PREFETCH_CFG3); |
| val &= ~MST_ID(~0); |
| val |= PREFETCH_ENB | USB3_MST_ID | ADDR_BNDRY(0xc) | |
| INACTIVITY_TIMEOUT(0x1000); |
| gizmo_writel(val, AHB_MEM_PREFETCH_CFG3); |
| |
| val = gizmo_readl(AHB_MEM_PREFETCH_CFG4); |
| val &= ~MST_ID(~0); |
| val |= PREFETCH_ENB | USB2_MST_ID | ADDR_BNDRY(0xc) | |
| INACTIVITY_TIMEOUT(0x1000); |
| gizmo_writel(val, AHB_MEM_PREFETCH_CFG4); |
| } |
| |
| #ifdef CONFIG_ARCH_TEGRA_2x_SOC |
| void __init tegra20_init_early(void) |
| { |
| #ifndef CONFIG_SMP |
| /* For SMP system, initializing the reset handler here is too |
| late. For non-SMP systems, the function that calls the reset |
| handler initializer is not called, so do it here for non-SMP. */ |
| tegra_cpu_reset_handler_init(); |
| #endif |
| tegra_apb_io_init(); |
| tegra_init_fuse(); |
| tegra_init_cache(true); |
| tegra_powergate_init(); |
| tegra20_hotplug_init(); |
| tegra_init_power(); |
| tegra_init_ahb_gizmo_settings(); |
| tegra_init_debug_uart_rate(); |
| tegra_gpio_resume_init(); |
| tegra_ram_console_debug_reserve(SZ_1M); |
| } |
| #endif |
| #ifdef CONFIG_ARCH_TEGRA_3x_SOC |
| void __init tegra30_init_early(void) |
| { |
| #ifndef CONFIG_SMP |
| /* For SMP system, initializing the reset handler here is too |
| late. For non-SMP systems, the function that calls the reset |
| handler initializer is not called, so do it here for non-SMP. */ |
| tegra_cpu_reset_handler_init(); |
| #endif |
| tegra_apb_io_init(); |
| tegra_init_fuse(); |
| tegra_init_cache(true); |
| tegra_pmc_init(); |
| tegra_powergate_init(); |
| tegra30_hotplug_init(); |
| tegra_init_power(); |
| tegra_init_ahb_gizmo_settings(); |
| tegra_init_debug_uart_rate(); |
| tegra_gpio_resume_init(); |
| tegra_ram_console_debug_reserve(SZ_1M); |
| |
| init_dma_coherent_pool_size(SZ_1M); |
| } |
| #endif |
| #ifdef CONFIG_ARCH_TEGRA_11x_SOC |
| void __init tegra11x_init_early(void) |
| { |
| #ifndef CONFIG_SMP |
| /* For SMP system, initializing the reset handler here is too |
| late. For non-SMP systems, the function that calls the reset |
| handler initializer is not called, so do it here for non-SMP. */ |
| tegra_cpu_reset_handler_init(); |
| #endif |
| tegra_init_fuse(); |
| tegra11x_init_clocks(); |
| tegra11x_init_dvfs(); |
| tegra_common_init_clock(); |
| tegra_clk_init_from_table(tegra11x_clk_init_table); |
| tegra11x_clk_init_la(); |
| tegra_pmc_init(); |
| tegra_powergate_init(); |
| tegra_init_power(); |
| tegra_init_ahb_gizmo_settings(); |
| tegra_init_debug_uart_rate(); |
| tegra_gpio_resume_init(); |
| |
| init_dma_coherent_pool_size(SZ_1M); |
| } |
| #endif |
| static int __init tegra_lp0_vec_arg(char *options) |
| { |
| char *p = options; |
| |
| tegra_lp0_vec_size = memparse(p, &p); |
| if (*p == '@') |
| tegra_lp0_vec_start = memparse(p+1, &p); |
| if (!tegra_lp0_vec_size || !tegra_lp0_vec_start) { |
| tegra_lp0_vec_size = 0; |
| tegra_lp0_vec_start = 0; |
| } |
| |
| return 0; |
| } |
| early_param("lp0_vec", tegra_lp0_vec_arg); |
| |
| static int __init tegra_bootloader_fb_arg(char *options) |
| { |
| char *p = options; |
| |
| tegra_bootloader_fb_size = memparse(p, &p); |
| if (*p == '@') |
| tegra_bootloader_fb_start = memparse(p+1, &p); |
| |
| pr_info("Found tegra_fbmem: %08lx@%08lx\n", |
| tegra_bootloader_fb_size, tegra_bootloader_fb_start); |
| |
| return 0; |
| } |
| early_param("tegra_fbmem", tegra_bootloader_fb_arg); |
| |
| static int __init tegra_sku_override(char *id) |
| { |
| char *p = id; |
| |
| sku_override = memparse(p, &p); |
| |
| return 0; |
| } |
| early_param("sku_override", tegra_sku_override); |
| |
| int tegra_get_sku_override(void) |
| { |
| return sku_override; |
| } |
| |
| static int __init tegra_vpr_arg(char *options) |
| { |
| char *p = options; |
| |
| tegra_vpr_size = memparse(p, &p); |
| if (*p == '@') |
| tegra_vpr_start = memparse(p+1, &p); |
| pr_info("Found vpr, start=0x%lx size=%lx", |
| tegra_vpr_start, tegra_vpr_size); |
| return 0; |
| } |
| early_param("vpr", tegra_vpr_arg); |
| |
| static int __init tegra_tsec_arg(char *options) |
| { |
| char *p = options; |
| |
| tegra_tsec_size = memparse(p, &p); |
| if (*p == '@') |
| tegra_tsec_start = memparse(p+1, &p); |
| pr_info("Found tsec, start=0x%lx size=%lx", |
| tegra_tsec_start, tegra_tsec_size); |
| return 0; |
| } |
| early_param("tsec", tegra_tsec_arg); |
| |
| enum panel_type get_panel_type(void) |
| { |
| return board_panel_type; |
| } |
| static int __init tegra_board_panel_type(char *options) |
| { |
| if (!strcmp(options, "lvds")) |
| board_panel_type = panel_type_lvds; |
| else if (!strcmp(options, "dsi")) |
| board_panel_type = panel_type_dsi; |
| else |
| return 0; |
| return 1; |
| } |
| __setup("panel=", tegra_board_panel_type); |
| |
| enum power_supply_type get_power_supply_type(void) |
| { |
| return pow_supply_type; |
| } |
| static int __init tegra_board_power_supply_type(char *options) |
| { |
| if (!strcmp(options, "Adapter")) |
| pow_supply_type = POWER_SUPPLY_TYPE_MAINS; |
| if (!strcmp(options, "Mains")) |
| pow_supply_type = POWER_SUPPLY_TYPE_MAINS; |
| else if (!strcmp(options, "Battery")) |
| pow_supply_type = POWER_SUPPLY_TYPE_BATTERY; |
| else |
| return 0; |
| return 1; |
| } |
| __setup("power_supply=", tegra_board_power_supply_type); |
| |
| int get_core_edp(void) |
| { |
| return pmu_core_edp; |
| } |
| static int __init tegra_pmu_core_edp(char *options) |
| { |
| char *p = options; |
| int core_edp = memparse(p, &p); |
| if (core_edp != 0) |
| pmu_core_edp = core_edp; |
| return 0; |
| } |
| early_param("core_edp_mv", tegra_pmu_core_edp); |
| |
| int get_maximum_cpu_current_supported(void) |
| { |
| return max_cpu_current; |
| } |
| static int __init tegra_max_cpu_current(char *options) |
| { |
| char *p = options; |
| max_cpu_current = memparse(p, &p); |
| return 1; |
| } |
| __setup("max_cpu_cur_ma=", tegra_max_cpu_current); |
| |
| static int __init tegra_debug_uartport(char *info) |
| { |
| char *p = info; |
| unsigned long long port_id; |
| if (!strncmp(p, "hsport", 6)) |
| is_tegra_debug_uart_hsport = true; |
| else if (!strncmp(p, "lsport", 6)) |
| is_tegra_debug_uart_hsport = false; |
| |
| if (p[6] == ',') { |
| if (p[7] == '-') { |
| debug_uart_port_id = -1; |
| } else { |
| port_id = memparse(p + 7, &p); |
| debug_uart_port_id = (int) port_id; |
| } |
| } else { |
| debug_uart_port_id = -1; |
| } |
| |
| return 1; |
| } |
| |
| bool is_tegra_debug_uartport_hs(void) |
| { |
| return is_tegra_debug_uart_hsport; |
| } |
| |
| int get_tegra_uart_debug_port_id(void) |
| { |
| return debug_uart_port_id; |
| } |
| __setup("debug_uartport=", tegra_debug_uartport); |
| |
| static int __init tegra_image_type(char *options) |
| { |
| if (!strcmp(options, "RCK")) |
| board_image_type = rck_image; |
| |
| return 0; |
| } |
| |
| enum image_type get_tegra_image_type(void) |
| { |
| return board_image_type; |
| } |
| |
| __setup("image=", tegra_image_type); |
| |
| static int __init tegra_audio_codec_type(char *info) |
| { |
| char *p = info; |
| if (!strncmp(p, "wm8903", 6)) |
| audio_codec_name = audio_codec_wm8903; |
| else |
| audio_codec_name = audio_codec_none; |
| |
| return 1; |
| } |
| |
| enum audio_codec_type get_audio_codec_type(void) |
| { |
| return audio_codec_name; |
| } |
| __setup("audio_codec=", tegra_audio_codec_type); |
| |
| |
| void tegra_get_board_info(struct board_info *bi) |
| { |
| #ifdef CONFIG_OF |
| struct device_node *board_info; |
| u32 prop_val; |
| int err; |
| |
| board_info = of_find_node_by_path("/chosen/board_info"); |
| if (!IS_ERR_OR_NULL(board_info)) { |
| memset(bi, 0, sizeof(*bi)); |
| |
| err = of_property_read_u32(board_info, "id", &prop_val); |
| if (err) |
| pr_err("failed to read /chosen/board_info/id\n"); |
| else |
| bi->board_id = prop_val; |
| |
| err = of_property_read_u32(board_info, "sku", &prop_val); |
| if (err) |
| pr_err("failed to read /chosen/board_info/sku\n"); |
| else |
| bi->sku = prop_val; |
| |
| err = of_property_read_u32(board_info, "fab", &prop_val); |
| if (err) |
| pr_err("failed to read /chosen/board_info/fab\n"); |
| else |
| bi->fab = prop_val; |
| |
| err = of_property_read_u32(board_info, "major_revision", &prop_val); |
| if (err) |
| pr_err("failed to read /chosen/board_info/major_revision\n"); |
| else |
| bi->major_revision = prop_val; |
| |
| err = of_property_read_u32(board_info, "minor_revision", &prop_val); |
| if (err) |
| pr_err("failed to read /chosen/board_info/minor_revision\n"); |
| else |
| bi->minor_revision = prop_val; |
| } else { |
| #endif |
| bi->board_id = (system_serial_high >> 16) & 0xFFFF; |
| bi->sku = (system_serial_high) & 0xFFFF; |
| bi->fab = (system_serial_low >> 24) & 0xFF; |
| bi->major_revision = (system_serial_low >> 16) & 0xFF; |
| bi->minor_revision = (system_serial_low >> 8) & 0xFF; |
| #ifdef CONFIG_OF |
| } |
| #endif |
| } |
| |
| static int __init tegra_pmu_board_info(char *info) |
| { |
| char *p = info; |
| pmu_board_info.board_id = memparse(p, &p); |
| pmu_board_info.sku = memparse(p+1, &p); |
| pmu_board_info.fab = memparse(p+1, &p); |
| pmu_board_info.major_revision = memparse(p+1, &p); |
| pmu_board_info.minor_revision = memparse(p+1, &p); |
| return 1; |
| } |
| |
| void tegra_get_pmu_board_info(struct board_info *bi) |
| { |
| memcpy(bi, &pmu_board_info, sizeof(struct board_info)); |
| } |
| |
| __setup("pmuboard=", tegra_pmu_board_info); |
| |
| static int __init tegra_display_board_info(char *info) |
| { |
| char *p = info; |
| display_board_info.board_id = memparse(p, &p); |
| display_board_info.sku = memparse(p+1, &p); |
| display_board_info.fab = memparse(p+1, &p); |
| display_board_info.major_revision = memparse(p+1, &p); |
| display_board_info.minor_revision = memparse(p+1, &p); |
| return 1; |
| } |
| |
| void tegra_get_display_board_info(struct board_info *bi) |
| { |
| memcpy(bi, &display_board_info, sizeof(struct board_info)); |
| } |
| |
| __setup("displayboard=", tegra_display_board_info); |
| |
| static int __init tegra_camera_board_info(char *info) |
| { |
| char *p = info; |
| camera_board_info.board_id = memparse(p, &p); |
| camera_board_info.sku = memparse(p+1, &p); |
| camera_board_info.fab = memparse(p+1, &p); |
| camera_board_info.major_revision = memparse(p+1, &p); |
| camera_board_info.minor_revision = memparse(p+1, &p); |
| return 1; |
| } |
| |
| void tegra_get_camera_board_info(struct board_info *bi) |
| { |
| memcpy(bi, &camera_board_info, sizeof(struct board_info)); |
| } |
| |
| __setup("cameraboard=", tegra_camera_board_info); |
| |
| static int __init tegra_modem_id(char *id) |
| { |
| char *p = id; |
| |
| modem_id = memparse(p, &p); |
| return 1; |
| } |
| |
| int tegra_get_modem_id(void) |
| { |
| return modem_id; |
| } |
| |
| __setup("modem_id=", tegra_modem_id); |
| |
| static int __init tegra_commchip_id(char *id) |
| { |
| char *p = id; |
| |
| if (get_option(&p, &commchip_id) != 1) |
| return 0; |
| return 1; |
| } |
| |
| int tegra_get_commchip_id(void) |
| { |
| return commchip_id; |
| } |
| |
| __setup("commchip_id=", tegra_commchip_id); |
| |
| /* |
| * Tegra has a protected aperture that prevents access by most non-CPU |
| * memory masters to addresses above the aperture value. Enabling it |
| * secures the CPU's memory from the GPU, except through the GART. |
| */ |
| void __init tegra_protected_aperture_init(unsigned long aperture) |
| { |
| #ifndef CONFIG_NVMAP_ALLOW_SYSMEM |
| void __iomem *mc_base = IO_ADDRESS(TEGRA_MC_BASE); |
| pr_info("Enabling Tegra protected aperture at 0x%08lx\n", aperture); |
| writel(aperture, mc_base + MC_SECURITY_CFG2); |
| #else |
| pr_err("Tegra protected aperture disabled because nvmap is using " |
| "system memory\n"); |
| #endif |
| } |
| |
| /* |
| * Due to conflicting restrictions on the placement of the framebuffer, |
| * the bootloader is likely to leave the framebuffer pointed at a location |
| * in memory that is outside the grhost aperture. This function will move |
| * the framebuffer contents from a physical address that is anywhere (lowmem, |
| * highmem, or outside the memory map) to a physical address that is outside |
| * the memory map. |
| */ |
| void tegra_move_framebuffer(unsigned long to, unsigned long from, |
| unsigned long size) |
| { |
| struct page *page; |
| void __iomem *to_io; |
| void *from_virt; |
| unsigned long i, addr[] = { to, from, }; |
| |
| BUG_ON(PAGE_ALIGN((unsigned long)to) != (unsigned long)to); |
| BUG_ON(PAGE_ALIGN(from) != from); |
| BUG_ON(PAGE_ALIGN(size) != size); |
| |
| to_io = ioremap(to, size); |
| if (!to_io) { |
| pr_err("%s: Failed to map target framebuffer\n", __func__); |
| return; |
| } |
| |
| if (pfn_valid(page_to_pfn(phys_to_page(from)))) { |
| for (i = 0 ; i < size; i += PAGE_SIZE) { |
| page = phys_to_page(from + i); |
| from_virt = kmap(page); |
| memcpy(to_io + i, from_virt, PAGE_SIZE); |
| kunmap(page); |
| } |
| } else { |
| void __iomem *from_io = ioremap(from, size); |
| if (!from_io) { |
| pr_err("%s: Failed to map source framebuffer\n", |
| __func__); |
| goto out; |
| } |
| |
| for (i = 0; i < size; i += 4) |
| writel(readl(from_io + i), to_io + i); |
| |
| iounmap(from_io); |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(addr); i++) |
| dma_map_linear_at(NULL, addr[i], size, DMA_TO_DEVICE); |
| out: |
| iounmap(to_io); |
| } |
| |
| void __init tegra_reserve(unsigned long carveout_size, unsigned long fb_size, |
| unsigned long fb2_size) |
| { |
| const size_t avp_kernel_reserve = SZ_32M; |
| #if !defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) /* Tegra2 with AVP MMU */ && \ |
| !defined(CONFIG_TEGRA_AVP_KERNEL_ON_SMMU) /* Tegra3 & up with SMMU */ |
| /* Reserve hardcoded AVP kernel load area starting at 0xXe000000*/ |
| tegra_avp_kernel_size = SZ_1M; |
| tegra_avp_kernel_start = memblock_end_of_DRAM() - avp_kernel_reserve; |
| if (memblock_remove(tegra_avp_kernel_start, avp_kernel_reserve)) { |
| pr_err("Failed to remove AVP kernel load area %08lx@%08lx " |
| "from memory map\n", |
| (unsigned long)avp_kernel_reserve, |
| tegra_avp_kernel_start); |
| tegra_avp_kernel_size = 0; |
| } |
| #endif |
| |
| if (carveout_size) { |
| tegra_carveout_start = memblock_end_of_DRAM() - carveout_size; |
| if (memblock_remove(tegra_carveout_start, carveout_size)) { |
| pr_err("Failed to remove carveout %08lx@%08lx " |
| "from memory map\n", |
| carveout_size, tegra_carveout_start); |
| tegra_carveout_start = 0; |
| tegra_carveout_size = 0; |
| } else |
| tegra_carveout_size = carveout_size; |
| } |
| |
| if (fb2_size) { |
| tegra_fb2_start = memblock_end_of_DRAM() - fb2_size; |
| if (memblock_remove(tegra_fb2_start, fb2_size)) { |
| pr_err("Failed to remove second framebuffer " |
| "%08lx@%08lx from memory map\n", |
| fb2_size, tegra_fb2_start); |
| tegra_fb2_start = 0; |
| tegra_fb2_size = 0; |
| } else |
| tegra_fb2_size = fb2_size; |
| } |
| |
| if (fb_size) { |
| tegra_fb_start = memblock_end_of_DRAM() - fb_size; |
| if (memblock_remove(tegra_fb_start, fb_size)) { |
| pr_err("Failed to remove framebuffer %08lx@%08lx " |
| "from memory map\n", |
| fb_size, tegra_fb_start); |
| tegra_fb_start = 0; |
| tegra_fb_size = 0; |
| } else |
| tegra_fb_size = fb_size; |
| } |
| |
| if (tegra_fb_size) |
| tegra_grhost_aperture = tegra_fb_start; |
| |
| if (tegra_fb2_size && tegra_fb2_start < tegra_grhost_aperture) |
| tegra_grhost_aperture = tegra_fb2_start; |
| |
| if (tegra_carveout_size && tegra_carveout_start < tegra_grhost_aperture) |
| tegra_grhost_aperture = tegra_carveout_start; |
| |
| if (tegra_lp0_vec_size && |
| (tegra_lp0_vec_start < memblock_end_of_DRAM())) { |
| if (memblock_reserve(tegra_lp0_vec_start, tegra_lp0_vec_size)) { |
| pr_err("Failed to reserve lp0_vec %08lx@%08lx\n", |
| tegra_lp0_vec_size, tegra_lp0_vec_start); |
| tegra_lp0_vec_start = 0; |
| tegra_lp0_vec_size = 0; |
| } |
| tegra_lp0_vec_relocate = false; |
| } else |
| tegra_lp0_vec_relocate = true; |
| |
| /* |
| * We copy the bootloader's framebuffer to the framebuffer allocated |
| * above, and then free this one. |
| * */ |
| if (tegra_bootloader_fb_size) { |
| tegra_bootloader_fb_size = PAGE_ALIGN(tegra_bootloader_fb_size); |
| if (memblock_reserve(tegra_bootloader_fb_start, |
| tegra_bootloader_fb_size)) { |
| pr_err("Failed to reserve bootloader frame buffer " |
| "%08lx@%08lx\n", tegra_bootloader_fb_size, |
| tegra_bootloader_fb_start); |
| tegra_bootloader_fb_start = 0; |
| tegra_bootloader_fb_size = 0; |
| } |
| } |
| |
| pr_info("Tegra reserved memory:\n" |
| "LP0: %08lx - %08lx\n" |
| "Bootloader framebuffer: %08lx - %08lx\n" |
| "Framebuffer: %08lx - %08lx\n" |
| "2nd Framebuffer: %08lx - %08lx\n" |
| "Carveout: %08lx - %08lx\n" |
| "Vpr: %08lx - %08lx\n" |
| "Tsec: %08lx - %08lx\n", |
| tegra_lp0_vec_start, |
| tegra_lp0_vec_size ? |
| tegra_lp0_vec_start + tegra_lp0_vec_size - 1 : 0, |
| tegra_bootloader_fb_start, |
| tegra_bootloader_fb_size ? |
| tegra_bootloader_fb_start + tegra_bootloader_fb_size - 1 |
| : 0, |
| tegra_fb_start, |
| tegra_fb_size ? |
| tegra_fb_start + tegra_fb_size - 1 : 0, |
| tegra_fb2_start, |
| tegra_fb2_size ? |
| tegra_fb2_start + tegra_fb2_size - 1 : 0, |
| tegra_carveout_start, |
| tegra_carveout_size ? |
| tegra_carveout_start + tegra_carveout_size - 1 : 0, |
| tegra_vpr_start, |
| tegra_vpr_size ? |
| tegra_vpr_start + tegra_vpr_size - 1 : 0, |
| tegra_tsec_start, |
| tegra_tsec_size ? |
| tegra_tsec_start + tegra_tsec_size - 1 : 0); |
| |
| if (tegra_avp_kernel_size) { |
| /* Return excessive memory reserved for AVP kernel */ |
| if (tegra_avp_kernel_size < avp_kernel_reserve) |
| memblock_add( |
| tegra_avp_kernel_start + tegra_avp_kernel_size, |
| avp_kernel_reserve - tegra_avp_kernel_size); |
| pr_info( |
| "AVP kernel: %08lx - %08lx\n", |
| tegra_avp_kernel_start, |
| tegra_avp_kernel_start + tegra_avp_kernel_size - 1); |
| } |
| } |
| |
| #ifdef CONFIG_PSTORE_RAM |
| static struct persistent_ram_descriptor desc = { |
| .name = "ramoops", |
| }; |
| |
| static struct persistent_ram ram = { |
| .descs = &desc, |
| .num_descs = 1, |
| }; |
| |
| void __init tegra_ram_console_debug_reserve(unsigned long ram_console_size) |
| { |
| int ret; |
| |
| ram.start = memblock_end_of_DRAM() - ram_console_size; |
| ram.size = ram_console_size; |
| ram.descs->size = ram_console_size; |
| |
| INIT_LIST_HEAD(&ram.node); |
| |
| ret = persistent_ram_early_init(&ram); |
| if (ret) |
| goto fail; |
| |
| return; |
| |
| fail: |
| pr_err("Failed to reserve memory block for ram console\n"); |
| } |
| #endif |
| |
| void __init tegra_release_bootloader_fb(void) |
| { |
| /* Since bootloader fb is reserved in common.c, it is freed here. */ |
| if (tegra_bootloader_fb_size) |
| if (memblock_free(tegra_bootloader_fb_start, |
| tegra_bootloader_fb_size)) |
| pr_err("Failed to free bootloader fb.\n"); |
| } |
| |
| static struct platform_device *pinmux_devices[] = { |
| &tegra_gpio_device, |
| &tegra_pinmux_device, |
| }; |
| |
| void tegra_enable_pinmux(void) |
| { |
| platform_add_devices(pinmux_devices, ARRAY_SIZE(pinmux_devices)); |
| } |
| |
| static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { |
| [TEGRA_REVISION_UNKNOWN] = "unknown", |
| [TEGRA_REVISION_A01] = "A01", |
| [TEGRA_REVISION_A02] = "A02", |
| [TEGRA_REVISION_A03] = "A03", |
| [TEGRA_REVISION_A03p] = "A03 prime", |
| [TEGRA_REVISION_A04] = "A04", |
| [TEGRA_REVISION_A04p] = "A04 prime", |
| [TEGRA_REVISION_QT] = "QT", |
| }; |
| |
| static const char * __init tegra_get_revision(void) |
| { |
| return kasprintf(GFP_KERNEL, "%s", tegra_revision_name[tegra_revision]); |
| } |
| |
| static const char * __init tegra_get_family(void) |
| { |
| void __iomem *chip_id = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804; |
| u32 cid = readl(chip_id); |
| cid = (cid >> 8) & 0xFF; |
| |
| switch (cid) { |
| case TEGRA_CHIPID_TEGRA2: |
| cid = 2; |
| break; |
| case TEGRA_CHIPID_TEGRA3: |
| cid = 3; |
| break; |
| case TEGRA_CHIPID_TEGRA11: |
| cid = 11; |
| break; |
| |
| case TEGRA_CHIPID_UNKNOWN: |
| default: |
| cid = 0; |
| } |
| return kasprintf(GFP_KERNEL, "Tegra%d", cid); |
| } |
| |
| static const char * __init tegra_get_soc_id(void) |
| { |
| int package_id = tegra_package_id(); |
| return kasprintf(GFP_KERNEL, "REV=%s:SKU=0x%x:PID=0x%x", |
| tegra_revision_name[tegra_revision], tegra_sku_id, package_id); |
| } |
| |
| static void __init tegra_soc_info_populate(struct soc_device_attribute |
| *soc_dev_attr, const char *machine) |
| { |
| soc_dev_attr->soc_id = tegra_get_soc_id(); |
| soc_dev_attr->machine = machine; |
| soc_dev_attr->family = tegra_get_family(); |
| soc_dev_attr->revision = tegra_get_revision(); |
| } |
| |
| int __init tegra_soc_device_init(const char *machine) |
| { |
| struct soc_device *soc_dev; |
| struct soc_device_attribute *soc_dev_attr; |
| |
| soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); |
| if (!soc_dev_attr) |
| return -ENOMEM; |
| |
| tegra_soc_info_populate(soc_dev_attr, machine); |
| |
| soc_dev = soc_device_register(soc_dev_attr); |
| if (IS_ERR_OR_NULL(soc_dev)) { |
| kfree(soc_dev_attr); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| void __init tegra_init_late(void) |
| { |
| #ifndef CONFIG_COMMON_CLK |
| tegra_clk_debugfs_init(); |
| #endif |
| tegra_powergate_debugfs_init(); |
| } |