blob: 661a971fc040f2621e541e9a9117fc0842fe74b1 [file] [log] [blame]
/**
* @file
* @brief Provide Generic implementations for OMAP3 architecture
*
* FileName: arch/arm/mach-omap/omap3_generic.c
*
* This file contains the generic implementations of various OMAP3
* relevant functions
* For more info on OMAP34XX, see http://focus.ti.com/pdfs/wtbu/swpu114g.pdf
*
* Important one is @ref a_init which is architecture init code.
* The implemented functions are present in sys_info.h
*
* Originally from http://linux.omap.com/pub/bootloader/3430sdp/u-boot-v1.tar.gz
*/
/*
* (C) Copyright 2006-2008
* Texas Instruments, <www.ti.com>
* Richard Woodruff <r-woodruff2@ti.com>
* Nishanth Menon <x0nishan@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <init.h>
#include <asm/io.h>
#include <mach/silicon.h>
#include <mach/gpmc.h>
#include <mach/sdrc.h>
#include <mach/control.h>
#include <mach/omap3-smx.h>
#include <mach/clocks.h>
#include <mach/wdt.h>
#include <mach/sys_info.h>
#include <mach/syslib.h>
#include <mach/xload.h>
/**
* @brief Reset the CPU
*
* In case of crashes, reset the CPU
*
* @param addr Cause of crash
*
* @return void
*/
void __noreturn reset_cpu(unsigned long addr)
{
/* FIXME: Enable WDT and cause reset */
hang();
}
EXPORT_SYMBOL(reset_cpu);
/**
* @brief Low level CPU type
*
* @return Detected CPU type
*/
u32 get_cpu_type(void)
{
u32 idcode_val;
u16 hawkeye;
idcode_val = readl(IDCODE_REG);
hawkeye = get_hawkeye(idcode_val);
if (hawkeye == OMAP_HAWKEYE_34XX)
return CPU_3430;
if (hawkeye == OMAP_HAWKEYE_36XX)
return CPU_3630;
/*
* Fallback to OMAP3430 as default.
*/
return CPU_3430;
}
/**
* @brief Extract the OMAP ES revision
*
* The significance of the CPU revision depends upon the cpu type.
* Latest known revision is considered default.
*
* @return silicon version
*/
u32 get_cpu_rev(void)
{
u32 idcode_val;
u32 version, retval;
idcode_val = readl(IDCODE_REG);
version = get_version(idcode_val);
switch (get_cpu_type()) {
case CPU_3630:
switch (version) {
case 0:
retval = OMAP36XX_ES1;
break;
case 1:
retval = OMAP36XX_ES1_1;
break;
case 2:
/*
* Fall through the default case.
*/
default:
retval = OMAP36XX_ES1_2;
}
break;
case CPU_3430:
/*
* Same as default case
*/
default:
/*
* On OMAP3430 ES1.0 the IDCODE register is not exposed on L4.
* Use CPU ID to check for the same.
*/
__asm__ __volatile__("mrc p15, 0, %0, c0, c0, 0":"=r"(retval));
if ((retval & 0xf) == 0x0) {
retval = OMAP34XX_ES1;
} else {
switch (version) {
case 0: /* This field was not set in early samples */
case 1:
retval = OMAP34XX_ES2;
break;
case 2:
retval = OMAP34XX_ES2_1;
break;
case 3:
retval = OMAP34XX_ES3;
break;
case 4:
/*
* Same as default case
*/
default:
retval = OMAP34XX_ES3_1;
}
}
}
return retval;
}
/**
* @brief Get size of chip select 0/1
*
* @param[in] offset give the offset if we need CS1
*
* @return return the sdram size.
*/
u32 get_sdr_cs_size(u32 offset)
{
u32 size;
/* get ram size field */
size = readl(SDRC_REG(MCFG_0) + offset) >> 8;
size &= 0x3FF; /* remove unwanted bits */
size *= 2 * (1024 * 1024); /* find size in MB */
return size;
}
/**
* @brief Get the initial SYSBOOT value
*
* SYSBOOT is useful to know which state OMAP booted from.
*
* @return - Return the value of SYSBOOT.
*/
inline u32 get_sysboot_value(void)
{
return (0x0000003F & readl(CONTROL_REG(STATUS)));
}
/**
* @brief Return the current CS0 base address
*
* Return current address hardware will be
* fetching from. The below effectively gives what is correct, its a bit
* mis-leading compared to the TRM. For the most general case the mask
* needs to be also taken into account this does work in practice.
*
* @return base address
*/
u32 get_gpmc0_base(void)
{
u32 b;
b = readl(GPMC_REG(CONFIG7_0));
b &= 0x1F; /* keep base [5:0] */
b = b << 24; /* ret 0x0b000000 */
return b;
}
/**
* @brief Get the upper address of current execution
*
* we can use this to figure out if we are running in SRAM /
* XIP Flash or in SDRAM
*
* @return base address
*/
u32 get_base(void)
{
u32 val;
__asm__ __volatile__("mov %0, pc \n":"=r"(val)::"memory");
val &= 0xF0000000;
val >>= 28;
return val;
}
/**
* @brief Are we running in Flash XIP?
*
* If the base is in GPMC address space, we probably are!
*
* @return 1 if we are running in XIP mode, else return 0
*/
u32 running_in_flash(void)
{
if (get_base() < 4)
return 1; /* in flash */
return 0; /* running in SRAM or SDRAM */
}
/**
* @brief Are we running in OMAP internal SRAM?
*
* If in SRAM address, then yes!
*
* @return 1 if we are running in SRAM, else return 0
*/
u32 running_in_sram(void)
{
if (get_base() == 4)
return 1; /* in SRAM */
return 0; /* running in FLASH or SDRAM */
}
/**
* @brief Are we running in SDRAM?
*
* if we are not in GPMC nor in SRAM address space,
* we are in SDRAM execution area
*
* @return 1 if we are running from SDRAM, else return 0
*/
u32 running_in_sdram(void)
{
if (get_base() > 4)
return 1; /* in sdram */
return 0; /* running in SRAM or FLASH */
}
EXPORT_SYMBOL(running_in_sdram);
/**
* @brief Is this an XIP type device or a stream one
*
* Sysboot bits 4-0 specify type. Bit 5, sys mem/perif
*
* @return Boot type
*/
u32 get_boot_type(void)
{
u32 v;
v = get_sysboot_value() & ((0x1 << 4) | (0x1 << 3) | (0x1 << 2) |
(0x1 << 1) | (0x1 << 0));
return v;
}
/**
* @brief What type of device are we?
*
* are we on a GP/HS/EMU/TEST device?
*
* @return device type
*/
u32 get_device_type(void)
{
int mode;
mode = readl(CONTROL_REG(STATUS)) & (DEVICE_MASK);
return (mode >>= 8);
}
/**
* @brief Setup security registers for access
*
* This can be done for GP Device only. for HS/EMU devices, read TRM.
*
* @return void
*/
static void secure_unlock_mem(void)
{
/* Permission values for registers -Full fledged permissions to all */
#define UNLOCK_1 0xFFFFFFFF
#define UNLOCK_2 0x00000000
#define UNLOCK_3 0x0000FFFF
/* Protection Module Register Target APE (PM_RT) */
writel(UNLOCK_1, RT_REQ_INFO_PERMISSION_1);
writel(UNLOCK_1, RT_READ_PERMISSION_0);
writel(UNLOCK_1, RT_WRITE_PERMISSION_0);
writel(UNLOCK_2, RT_ADDR_MATCH_1);
writel(UNLOCK_3, GPMC_REQ_INFO_PERMISSION_0);
writel(UNLOCK_3, GPMC_READ_PERMISSION_0);
writel(UNLOCK_3, GPMC_WRITE_PERMISSION_0);
writel(UNLOCK_3, OCM_REQ_INFO_PERMISSION_0);
writel(UNLOCK_3, OCM_READ_PERMISSION_0);
writel(UNLOCK_3, OCM_WRITE_PERMISSION_0);
writel(UNLOCK_2, OCM_ADDR_MATCH_2);
/* IVA Changes */
writel(UNLOCK_3, IVA2_REQ_INFO_PERMISSION_0);
writel(UNLOCK_3, IVA2_READ_PERMISSION_0);
writel(UNLOCK_3, IVA2_WRITE_PERMISSION_0);
writel(UNLOCK_1, SMS_RG_ATT0); /* SDRC region 0 public */
}
/**
* @brief Come out of secure mode
* If chip is EMU and boot type is external configure
* secure registers and exit secure world general use.
*
* @return void
*/
static void secureworld_exit(void)
{
unsigned long i;
/* configrue non-secure access control register */
__asm__ __volatile__("mrc p15, 0, %0, c1, c1, 2":"=r"(i));
/* enabling co-processor CP10 and CP11 accesses in NS world */
__asm__ __volatile__("orr %0, %0, #0xC00":"=r"(i));
/* allow allocation of locked TLBs and L2 lines in NS world */
/* allow use of PLE registers in NS world also */
__asm__ __volatile__("orr %0, %0, #0x70000":"=r"(i));
__asm__ __volatile__("mcr p15, 0, %0, c1, c1, 2":"=r"(i));
/* Enable ASA in ACR register */
__asm__ __volatile__("mrc p15, 0, %0, c1, c0, 1":"=r"(i));
__asm__ __volatile__("orr %0, %0, #0x10":"=r"(i));
__asm__ __volatile__("mcr p15, 0, %0, c1, c0, 1":"=r"(i));
/* Exiting secure world */
__asm__ __volatile__("mrc p15, 0, %0, c1, c1, 0":"=r"(i));
__asm__ __volatile__("orr %0, %0, #0x31":"=r"(i));
__asm__ __volatile__("mcr p15, 0, %0, c1, c1, 0":"=r"(i));
}
/**
* @brief Shut down the watchdogs
*
* There are 3 watch dogs WD1=Secure, WD2=MPU, WD3=IVA. WD1 is
* either taken care of by ROM (HS/EMU) or not accessible (GP).
* We need to take care of WD2-MPU or take a PRCM reset. WD3
* should not be running and does not generate a PRCM reset.
*
* @return void
*/
static void watchdog_init(void)
{
int pending = 1;
sr32(CM_REG(FCLKEN_WKUP), 5, 1, 1);
sr32(CM_REG(ICLKEN_WKUP), 5, 1, 1);
wait_on_value((0x1 << 5), 0x20, CM_REG(IDLEST_WKUP), 5);
writel(WDT_DISABLE_CODE1, WDT_REG(WSPR));
do {
pending = readl(WDT_REG(WWPS));
} while (pending);
writel(WDT_DISABLE_CODE2, WDT_REG(WSPR));
}
/**
* @brief Write to AuxCR desired value using SMI.
* general use.
*
* @return void
*/
static void setup_auxcr(void)
{
unsigned long i;
volatile unsigned int j;
/* Save r0, r12 and restore them after usage */
__asm__ __volatile__("mov %0, r12":"=r"(j));
__asm__ __volatile__("mov %0, r0":"=r"(i));
/* GP Device ROM code API usage here */
/* r12 = AUXCR Write function and r0 value */
__asm__ __volatile__("mov r12, #0x3");
__asm__ __volatile__("mrc p15, 0, r0, c1, c0, 1");
/* Enabling ASA */
__asm__ __volatile__("orr r0, r0, #0x10");
/* SMI instruction to call ROM Code API */
__asm__ __volatile__(".word 0xE1600070");
__asm__ __volatile__("mov r0, %0":"=r"(i));
__asm__ __volatile__("mov r12, %0":"=r"(j));
}
/**
* @brief Try to unlock the SRAM for general use
*
* If chip is GP/EMU(special) type, unlock the SRAM for
* general use.
*
* @return void
*/
static void try_unlock_memory(void)
{
int mode;
int in_sdram = running_in_sdram();
/* if GP device unlock device SRAM for general use */
/* secure code breaks for Secure/Emulation device - HS/E/T */
mode = get_device_type();
if (mode == GP_DEVICE)
secure_unlock_mem();
/* If device is EMU and boot is XIP external booting
* Unlock firewalls and disable L2 and put chip
* out of secure world
*/
/* Assuming memories are unlocked by the demon who put us in SDRAM */
if ((mode <= EMU_DEVICE) && (get_boot_type() == 0x1F)
&& (!in_sdram)) {
secure_unlock_mem();
secureworld_exit();
}
return;
}
/**
* @brief OMAP3 Architecture specific Initialization
*
* Does early system init of disabling the watchdog, enable
* memory and configuring the clocks.
*
* prcm_init is called only if CONFIG_OMAP3_CLOCK_CONFIG is defined.
* We depend on link time clean up to remove a_init if no caller exists.
*
* @warning Called path is with SRAM stack
*
* @return void
*/
void a_init(void)
{
watchdog_init();
try_unlock_memory();
/* Writing to AuxCR in barebox using SMI for GP DEV */
/* Currently SMI in Kernel on ES2 devices seems to have an isse
* Once that is resolved, we can postpone this config to kernel
*/
if (get_device_type() == GP_DEVICE)
setup_auxcr();
sdelay(100);
#ifdef CONFIG_OMAP3_CLOCK_CONFIG
prcm_init();
#endif
}
#define OMAP3_TRACING_VECTOR1 0x4020ffb4
enum omap_boot_src omap3_bootsrc(void)
{
u32 bootsrc = readl(OMAP3_TRACING_VECTOR1);
if (bootsrc & (1 << 2))
return OMAP_BOOTSRC_NAND;
if (bootsrc & (1 << 6))
return OMAP_BOOTSRC_MMC1;
return OMAP_BOOTSRC_UNKNOWN;
}