| /* |
| * arch/arm/mach-tegra/common.c |
| * |
| * Copyright (C) 2010 Google, Inc. |
| * |
| * 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/highmem.h> |
| #include <linux/memblock.h> |
| |
| #include <asm/hardware/cache-l2x0.h> |
| #include <asm/system.h> |
| |
| #include <mach/gpio.h> |
| #include <mach/iomap.h> |
| #include <mach/pinmux.h> |
| #include <mach/powergate.h> |
| #include <mach/system.h> |
| |
| #include "apbio.h" |
| #include "board.h" |
| #include "clock.h" |
| #include "fuse.h" |
| #include "pm.h" |
| |
| #define MC_SECURITY_CFG2 0x7c |
| |
| 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_lp0_vec_start; |
| unsigned long tegra_lp0_vec_size; |
| unsigned long tegra_grhost_aperture = ~0ul; |
| static bool is_tegra_debug_uart_hsport; |
| |
| void (*arch_reset)(char mode, const char *cmd) = tegra_assert_system_reset; |
| |
| void tegra_assert_system_reset(char mode, const char *cmd) |
| { |
| void __iomem *reset = IO_ADDRESS(TEGRA_PMC_BASE + 0x00); |
| u32 reg; |
| |
| /* use *_related to avoid spinlock since caches are off */ |
| reg = readl_relaxed(reset); |
| reg |= 0x10; |
| writel_relaxed(reg, reset); |
| } |
| |
| static __initdata struct tegra_clk_init_table common_clk_init_table[] = { |
| /* name parent rate enabled */ |
| { "clk_m", NULL, 0, true }, |
| { "pll_p", NULL, 216000000, true }, |
| { "pll_p_out1", "pll_p", 28800000, true }, |
| { "pll_p_out2", "pll_p", 48000000, true }, |
| { "pll_p_out3", "pll_p", 72000000, true }, |
| { "pll_p_out4", "pll_p", 108000000, true }, |
| #ifdef CONFIG_ARCH_TEGRA_3x_SOC |
| { "pll_m_out1", "pll_m", 275000000, true }, |
| { "pll_c", NULL, 832000000, true }, |
| { "pll_c_out1", "pll_c", 278000000, true }, |
| { "sclk", "pll_c_out1", 278000000, true }, |
| { "hclk", "sclk", 278000000, true }, |
| { "pclk", "hclk", 139000000, true }, |
| #else |
| { "pll_m_out1", "pll_m", 120000000, true }, |
| { "sclk", "pll_m_out1", 120000000, true }, |
| { "hclk", "sclk", 120000000, true }, |
| { "pclk", "hclk", 60000000, true }, |
| #endif |
| { "csite", NULL, 0, true }, |
| { "emc", NULL, 0, true }, |
| { "cpu", NULL, 0, true }, |
| { "kfuse", NULL, 0, true }, |
| { "pll_u", NULL, 480000000, false }, |
| { "sdmmc1", "pll_c", 48000000, false}, |
| { "sdmmc3", "pll_p", 48000000, false}, |
| { "sdmmc4", "pll_p", 48000000, false}, |
| #ifdef CONFIG_ARCH_TEGRA_3x_SOC |
| { "vde", "pll_c", 416000000, false }, |
| { "host1x", "pll_c", 208000000, false }, |
| { "mpe", "pll_c", 0, false }, |
| { "3d", "pll_c", 0, false }, |
| { "3d2", "pll_c", 0, false }, |
| { "2d", "pll_c", 0, false }, |
| { "epp", "pll_c", 0, false }, |
| { "se", "pll_c", 0, false }, |
| #endif |
| { NULL, NULL, 0, 0}, |
| }; |
| |
| void tegra_init_cache(void) |
| { |
| #ifdef CONFIG_CACHE_L2X0 |
| void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000; |
| u32 aux_ctrl; |
| |
| #if defined(CONFIG_ARCH_TEGRA_2x_SOC) |
| writel_relaxed(0x331, p + L2X0_TAG_LATENCY_CTRL); |
| writel_relaxed(0x441, p + L2X0_DATA_LATENCY_CTRL); |
| |
| #elif defined(CONFIG_ARCH_TEGRA_3x_SOC) |
| #ifdef CONFIG_TEGRA_FPGA_PLATFORM |
| writel(0x770, p + L2X0_TAG_LATENCY_CTRL); |
| writel(0x770, p + L2X0_DATA_LATENCY_CTRL); |
| #else |
| /* PL310 RAM latency is CPU dependent. NOTE: Changes here |
| must also be reflected in __cortex_a9_l2x0_restart */ |
| |
| if (is_lp_cluster()) { |
| writel(0x221, p + L2X0_TAG_LATENCY_CTRL); |
| writel(0x221, p + L2X0_DATA_LATENCY_CTRL); |
| } else { |
| writel(0x331, p + L2X0_TAG_LATENCY_CTRL); |
| writel(0x441, p + L2X0_DATA_LATENCY_CTRL); |
| } |
| #endif |
| |
| /* Enable PL310 double line fill feature. */ |
| writel(((1<<30) | 7), p + L2X0_PREFETCH_CTRL); |
| #endif |
| aux_ctrl = readl(p + L2X0_CACHE_TYPE); |
| aux_ctrl = (aux_ctrl & 0x700) << (17-8); |
| aux_ctrl |= 0x6C000001; |
| l2x0_init(p, aux_ctrl, 0x8200c3fe); |
| #endif |
| } |
| |
| static void __init tegra_init_power(void) |
| { |
| tegra_powergate_power_off(TEGRA_POWERGATE_MPE); |
| tegra_powergate_power_off(TEGRA_POWERGATE_3D); |
| tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); |
| } |
| |
| static bool console_flushed; |
| |
| static void tegra_pm_flush_console(void) |
| { |
| if (console_flushed) |
| return; |
| console_flushed = true; |
| |
| printk("\n"); |
| pr_emerg("Restarting %s\n", linux_banner); |
| if (console_trylock()) { |
| console_unlock(); |
| return; |
| } |
| |
| mdelay(50); |
| |
| local_irq_disable(); |
| if (!console_trylock()) |
| pr_emerg("tegra_restart: Console was locked! Busting\n"); |
| else |
| pr_emerg("tegra_restart: Console was locked!\n"); |
| console_unlock(); |
| } |
| |
| static void tegra_pm_restart(char mode, const char *cmd) |
| { |
| tegra_pm_flush_console(); |
| arm_machine_restart(mode, cmd); |
| } |
| |
| void __init tegra_init_early(void) |
| { |
| arm_pm_restart = tegra_pm_restart; |
| tegra_init_fuse(); |
| tegra_gpio_resume_init(); |
| tegra_init_clock(); |
| tegra_init_pinmux(); |
| tegra_clk_init_from_table(common_clk_init_table); |
| tegra_init_power(); |
| tegra_init_cache(); |
| #ifndef CONFIG_ARCH_TEGRA_2x_SOC |
| tegra_mc_init(); /* !!!FIXME!!! Change Tegra3 behavior to match Tegra2 */ |
| #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; |
| unsigned long start; |
| unsigned long size; |
| |
| 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_debug_uartport(char *info) |
| { |
| if (!strcmp(info, "hsport")) |
| is_tegra_debug_uart_hsport = true; |
| else if (!strcmp(info, "lsport")) |
| is_tegra_debug_uart_hsport = false; |
| |
| return 1; |
| } |
| |
| bool is_tegra_debug_uartport_hs(void) |
| { |
| return is_tegra_debug_uart_hsport; |
| } |
| |
| __setup("debug_uartport=", tegra_debug_uartport); |
| |
| void tegra_get_board_info(struct board_info *bi) |
| { |
| 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; |
| } |
| |
| /* |
| * 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 anywher (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; |
| |
| 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_toio(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); |
| } |
| out: |
| iounmap(to_io); |
| } |
| |
| #ifdef CONFIG_TEGRA_IOVMM_SMMU |
| /* Support for Tegra3 A01 chip mask that needs to have SMMU IOVA reside in |
| * the upper half of 4GB IOVA space. A02 and after use the bottom 1GB and |
| * do not need to reserve memory. |
| */ |
| #define SUPPORT_TEGRA_3_IOVMM_SMMU_A01 |
| #endif |
| |
| void __init tegra_reserve(unsigned long carveout_size, unsigned long fb_size, |
| unsigned long fb2_size) |
| { |
| #ifdef SUPPORT_TEGRA_3_IOVMM_SMMU_A01 |
| extern struct platform_device tegra_smmu_device; |
| int smmu_reserved = 0; |
| size_t smmu_window_size; |
| struct resource *smmu_window = |
| platform_get_resource_byname(&tegra_smmu_device, |
| IORESOURCE_MEM, "smmu"); |
| #endif |
| if (tegra_lp0_vec_size) |
| 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; |
| } |
| |
| 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_size); |
| 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; |
| |
| #ifdef SUPPORT_TEGRA_3_IOVMM_SMMU_A01 |
| if (!smmu_window) { |
| pr_err("No SMMU resources\n"); |
| } else { |
| if (tegra_get_revision() == TEGRA_REVISION_A01) { |
| smmu_window->start = TEGRA_SMMU_BASE_A01; |
| smmu_window->end = TEGRA_SMMU_BASE_A01 + |
| TEGRA_SMMU_SIZE_A01 - 1; |
| } |
| smmu_window_size = smmu_window->end + 1 - smmu_window->start; |
| if (smmu_window->start >= 0x80000000) { |
| if (memblock_reserve(smmu_window->start, |
| smmu_window_size)) |
| pr_err( |
| "Failed to reserve SMMU I/O VA window %08lx@%08lx\n", |
| (unsigned long)smmu_window->start, |
| (unsigned long)smmu_window_size); |
| else |
| smmu_reserved = 1; |
| } |
| } |
| #endif |
| |
| /* |
| * TODO: We should copy the bootloader's framebuffer to the framebuffer |
| * allocated above, and then free this one. |
| */ |
| if (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", |
| 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); |
| |
| #ifdef SUPPORT_TEGRA_3_IOVMM_SMMU_A01 |
| if (smmu_reserved) |
| pr_info("SMMU: %08x - %08x\n", |
| smmu_window->start, smmu_window->end); |
| #endif |
| } |