blob: d907cb9a299931d913710ad893b2c9b17471c050 [file] [log] [blame]
/*
* 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
}