Untitled diff

Created Diff never expires
202 removals
449 lines
48 additions
294 lines
/** \file
/** \file
* ARM control registers
* ARM control registers
*/
*/
/*
/*
* Copyright (C) 2009 Trammell Hudson <hudson+ml@osresearch.net>
* Copyright (C) 2009 Trammell Hudson <hudson+ml@osresearch.net>
*
*
* This program is free software; you can redistribute it and/or
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* of the License, or (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor,
* 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
* Boston, MA 02110-1301, USA.
*/
*/


#ifndef _arm_mcr_h_
#ifndef _arm_mcr_h_
#define _arm_mcr_h_
#define _arm_mcr_h_


#define inline inline __attribute__((always_inline))
#define inline inline __attribute__((always_inline))


asm(
asm(
".text\n"
".text\n"
".align 4\n"
".align 4\n"
);
);


#include <stdint.h>
#include <stdint.h>
#include <limits.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/types.h>
#include "compiler.h"
#include "compiler.h"


typedef void (*thunk)(void);
typedef void (*thunk)(void);




#if 0
#if 0
typedef signed long int32_t;
typedef signed long int32_t;
typedef unsigned long uint32_t;
typedef unsigned long uint32_t;
typedef signed short int16_t;
typedef signed short int16_t;
typedef unsigned short uint16_t;
typedef unsigned short uint16_t;
typedef signed char int8_t;
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef unsigned char uint8_t;


typedef uint32_t size_t;
typedef uint32_t size_t;
typedef int32_t ssize_t;
typedef int32_t ssize_t;
#endif
#endif


static inline uint32_t
static inline uint32_t
read_lr( void )
read_lr( void )
{
{
uint32_t lr;
uint32_t lr;
asm( "mov %0, lr" : "=r"(lr) );
asm( "mov %0, lr" : "=r"(lr) );
return lr;
return lr;
}
}




static inline void
static inline void
select_normal_vectors( void )
select_normal_vectors( void )
{
{
uint32_t reg;
uint32_t reg;
asm(
asm(
"mrc p15, 0, %0, c1, c0\n"
"mrc p15, 0, %0, c1, c0\n"
"bic %0, %0, #0x2000\n"
"bic %0, %0, #0x2000\n"
"mcr p15, 0, %0, c1, c0\n"
"mcr p15, 0, %0, c1, c0\n"
: "=r"(reg)
: "=r"(reg)
);
);
}
}


/*
/*
naming conventions
naming conventions
--------------------
--------------------
FLUSH instruction cache:
FLUSH instruction cache:
just mark all entries in the I cache as invalid. no other effect than slowing down code execution until cache is populated again.
just mark all entries in the I cache as invalid. no other effect than slowing down code execution until cache is populated again.
DRAIN write buffer:
DRAIN write buffer:
halt CPU execution until every RAM write operation has finished.
halt CPU execution until every RAM write operation has finished.
FLUSH data cache / cache entry:
FLUSH data cache / cache entry:
invalidating the cache content *without* writing back the dirty data into memory. (dangerous!)
invalidating the cache content *without* writing back the dirty data into memory. (dangerous!)
this will simply mark any data in cache (or in the line) as invalid, no matter if it was written back into RAM.
this will simply mark any data in cache (or in the line) as invalid, no matter if it was written back into RAM.
e.g.
e.g.
mcr p15, 0, Rd, c7, c6, 0 # whole D cache
mcr p15, 0, Rd, c7, c6, 0 # whole D cache
mcr p15, 0, Rd, c7, c6, 1 # single line in D cache
mcr p15, 0, Rd, c7, c6, 1 # single line in D cache
CLEAN data cache entry:
CLEAN data cache entry:
cleaning is the process of checking a single cahe entry and writing it back into RAM if it was not written yet.
cleaning is the process of checking a single cahe entry and writing it back into RAM if it was not written yet.
this will ensure that the contents of the data cache are also in RAM. only possible for a single line, so we have to loop though all lines.
this will ensure that the contents of the data cache are also in RAM. only possible for a single line, so we have to loop though all lines.
e.g.
e.g.
mcr p15, 0, Rd, c7, c10, 1 # Clean data cache entry (by address)
mcr p15, 0, Rd, c7, c10, 1 # Clean data cache entry (by address)
mcr p15, 0, Rd, c7, c14, 1 # Clean data cache entry (by index/segment)
mcr p15, 0, Rd, c7, c14, 1 # Clean data cache entry (by index/segment)
CLEAN and FLUSH data cache entry:
CLEAN and FLUSH data cache entry:
the logical consequence - write it back into RAM and mark cache entry as invalid. As if it never was in cache.
the logical consequence - write it back into RAM and mark cache entry as invalid. As if it never was in cache.
e.g.
e.g.
mcr p15, 0, Rd, c7, c10, 2 # Clean and flush data cache entry (by address)
mcr p15, 0, Rd, c7, c10, 2 # Clean and flush data cache entry (by address)
mcr p15, 0, Rd, c7, c14, 2 # Clean and flush data cache entry (by index/segment)
mcr p15, 0, Rd, c7, c14, 2 # Clean and flush data cache entry (by index/segment)


*/
*/


/* do you really want to call that? */
/* do you really want to call that? */
static inline void _flush_caches()
static inline void _flush_caches()
{
{
uint32_t reg = 0;
uint32_t reg = 0;
asm(
asm(
"mov %0, #0\n"
"mov %0, #0\n"
"mcr p15, 0, %0, c7, c5, 0\n" // entire I cache
"mcr p15, 0, %0, c7, c5, 0\n" // entire I cache
"mov %0, #0\n"
"mov %0, #0\n"
"mcr p15, 0, %0, c7, c6, 0\n" // entire D cache
"mcr p15, 0, %0, c7, c6, 0\n" // entire D cache
"mcr p15, 0, %0, c7, c10, 4\n" // drain write buffer
"mcr p15, 0, %0, c7, c10, 4\n" // drain write buffer
: : "r"(reg)
: : "r"(reg)
);
);
}
}


// ARMv7 cache control (based on U-BOOT cache_v7.c, utils.h, armv7.h)
/* write back all data into RAM and mark as invalid in data cache */

static inline void clean_d_cache()
/* Invalidate entire I-cache and branch predictor array */
static void __attribute__((naked,noinline)) icache_flush_all(void)
{
{
/*
uint32_t segment = 0;
* Invalidate all instruction caches to PoU.
do {
* Also flushes branch target cache.
uint32_t line = 0;
*/
for( ; line != 0x400 ; line += 0x20 )
asm volatile (
{
"mov r1, #0\n"
asm(
"mcr p15, 0, r1, c7, c5, 0\n"
"mcr p15, 0, %0, c7, c14, 2"
"mcr p15, 0, r1, c7, c5, 6\n"
: : "r"( line | segment )
"dsb sy\n"
);
"isb sy\n"
}
"bx lr\n"
} while( segment += 0x40000000 );
/* ensure everything is written into RAM, halts CPU execution until pending operations are done */
uint32_t reg = 0;
asm(
"mcr p15, 0, %0, c7, c10, 4\n" // drain write buffer
: : "r"(reg)
);
);
}
}


/* Values for Ctype fields in CLIDR */
/* mark all entries in I cache as invalid */
#define ARMV7_CLIDR_CTYPE_NO_CACHE 0
static inline void flush_i_cache()
#define ARMV7_CLIDR_CTYPE_INSTRUCTION_ONLY 1
#define ARMV7_CLIDR_CTYPE_DATA_ONLY 2
#define ARMV7_CLIDR_CTYPE_INSTRUCTION_DATA 3
#define ARMV7_CLIDR_CTYPE_UNIFIED 4

#define ARMV7_DCACHE_INVAL_ALL 1
#define ARMV7_DCACHE_CLEAN_INVAL_ALL 2
#define ARMV7_DCACHE_INVAL_RANGE 3
#define ARMV7_DCACHE_CLEAN_INVAL_RANGE 4

#define ARMV7_CSSELR_IND_DATA_UNIFIED 0
#define ARMV7_CSSELR_IND_INSTRUCTION 1

#define CCSIDR_LINE_SIZE_OFFSET 0
#define CCSIDR_LINE_SIZE_MASK 0x7
#define CCSIDR_ASSOCIATIVITY_OFFSET 3
#define CCSIDR_ASSOCIATIVITY_MASK (0x3FF << 3)
#define CCSIDR_NUM_SETS_OFFSET 13
#define CCSIDR_NUM_SETS_MASK (0x7FFF << 13)

typedef unsigned int u32;
typedef int s32;

static inline s32 log_2_n_round_up(u32 n)
{
{
s32 log2n = -1;
asm(
u32 temp = n;
"mov r0, #0\n"
"mcr p15, 0, r0, c7, c5, 0\n" // flush I cache
while (temp) {
: : : "r0"
log2n++;
);
temp >>= 1;
}
if (n & (n - 1))
return log2n + 1; /* not power of 2 - round up */
else
return log2n; /* power of 2 */
}

static u32 get_clidr(void)
{
u32 clidr;

/* Read current CP15 Cache Level ID Register */
asm volatile ("mrc p15,1,%0,c0,c0,1" : "=r" (clidr));
return clidr;
}

static u32 get_ccsidr(void)
{
u32 ccsidr;

/* Read current CP15 Cache Size ID Register */
asm volatile ("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr));
return ccsidr;
}

static void set_csselr(u32 level, u32 type)
{ u32 csselr = level << 1 | type;

/* Write to Cache Size Selection Register(CSSELR) */
asm volatile ("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr));
}

static void v7_inval_dcache_level_setway(u32 level, u32 num_sets,
u32 num_ways, u32 way_shift,
u32 log2_line_len)
{
int way, set, setway;

/*
* For optimal assembly code:
* a. count down
* b. have bigger loop inside
*/
for (way = num_ways - 1; way >= 0 ; way--) {
for (set = num_sets - 1; set >= 0; set--) {
setway = (level << 1) | (set << log2_line_len) |
(way << way_shift);
/* Invalidate data/unified cache line by set/way */
asm volatile (" mcr p15, 0, %0, c7, c6, 2"
: : "r" (setway));
}
}
/* DSB to make sure the operation is complete */
asm volatile("dsb sy\n");
}

static void v7_clean_inval_dcache_level_setway(u32 level, u32 num_sets,
u32 num_ways, u32 way_shift,
u32 log2_line_len)
{
int way, set, setway;

/*
* For optimal assembly code:
* a. count down
* b. have bigger loop inside
*/
for (way = num_ways - 1; way >= 0 ; way--) {
for (set = num_sets - 1; set >= 0; set--) {
setway = (level << 1) | (set << log2_line_len) |
(way << way_shift);
/*
* Clean & Invalidate data/unified
* cache line by set/way
*/
asm volatile (" mcr p15, 0, %0, c7, c14, 2"
: : "r" (setway));
}
}
/* DSB to make sure the operation is complete */
asm volatile("dsb sy\n");
}

static void v7_maint_dcache_level_setway(u32 level, u32 operation)
{
u32 ccsidr;
u32 num_sets, num_ways, log2_line_len, log2_num_ways;
u32 way_shift;

set_csselr(level, ARMV7_CSSELR_IND_DATA_UNIFIED);

ccsidr = get_ccsidr();

log2_line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >>
CCSIDR_LINE_SIZE_OFFSET) + 2;
/* Converting from words to bytes */
log2_line_len += 2;

num_ways = ((ccsidr & CCSIDR_ASSOCIATIVITY_MASK) >>
CCSIDR_ASSOCIATIVITY_OFFSET) + 1;
num_sets = ((ccsidr & CCSIDR_NUM_SETS_MASK) >>
CCSIDR_NUM_SETS_OFFSET) + 1;
/*
* According to ARMv7 ARM number of sets and number of ways need
* not be a power of 2
*/
log2_num_ways = log_2_n_round_up(num_ways);

way_shift = (32 - log2_num_ways);
if (operation == ARMV7_DCACHE_INVAL_ALL) {
v7_inval_dcache_level_setway(level, num_sets, num_ways,
way_shift, log2_line_len);
} else if (operation == ARMV7_DCACHE_CLEAN_INVAL_ALL) {
v7_clean_inval_dcache_level_setway(level, num_sets, num_ways,
way_shift, log2_line_len);
}
}

static void v7_maint_dcache_all(u32 operation)
{
u32 level, cache_type, level_start_bit = 0;

u32 clidr = get_clidr();

for (level = 0; level < 7; level++) {
cache_type = (clidr >> level_start_bit) & 0x7;
if ((cache_type == ARMV7_CLIDR_CTYPE_DATA_ONLY) ||
(cache_type == ARMV7_CLIDR_CTYPE_INSTRUCTION_DATA) ||
(cache_type == ARMV7_CLIDR_CTYPE_UNIFIED))
v7_maint_dcache_level_setway(level, operation);
level_start_bit += 3;
}
}

static void dcache_clean_all(void) {
v7_maint_dcache_all(ARMV7_DCACHE_CLEAN_INVAL_ALL);
/* anything else? */
}
}


/* ensure data is written into RAM and the instruction cache is empty so everything will get fetched again */
/* ensure data is written into RAM and the instruction cache is empty so everything will get fetched again */
static inline void sync_caches()
static inline void sync_caches()
{
{
#ifdef CONFIG_DIGIC_VI
/* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka14041.html */
/* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka14041.html */
/* http://infocenter.arm.com/help/topic/com.arm.doc.ihi0053b/IHI0053B_arm_c_language_extensions_2013.pdf */
/* http://infocenter.arm.com/help/topic/com.arm.doc.ihi0053b/IHI0053B_arm_c_language_extensions_2013.pdf */
/* Self-modifying code (from uncacheable memory) */
/* Self-modifying code (from uncacheable memory) */
// data_cache_clean();
// data_cache_clean();
dcache_clean_all(); /* make sure the data is written to RAM */
asm("DSB SY"); /* make sure the data is written to RAM */
icache_flush_all(); /* make sure the processor fetches new instructions from cache or memory */
asm("ISB SY"); /* make sure the processor fetches new instructions from cache or memory */
#else
clean_d_cache();
flush_i_cache();
#endif
}
}


// This must be a macro
// This must be a macro
#define setup_memory_region( region, value ) \
#define setup_memory_region( region, value ) \
asm __volatile__ ( "mcr p15, 0, %0, c6, c" #region "\n" : : "r"(value) )
asm __volatile__ ( "mcr p15, 0, %0, c6, c" #region "\n" : : "r"(value) )


#define set_d_cache_regions( value ) \
#define set_d_cache_regions( value ) \
asm __volatile__ ( "mcr p15, 0, %0, c2, c0\n" : : "r"(value) )
asm __volatile__ ( "mcr p15, 0, %0, c2, c0\n" : : "r"(value) )


#define set_i_cache_regions( value ) \
#define set_i_cache_regions( value ) \
asm __volatile__ ( "mcr p15, 0, %0, c2, c0, 1\n" : : "r"(value) )
asm __volatile__ ( "mcr p15, 0, %0, c2, c0, 1\n" : : "r"(value) )


#define set_d_buffer_regions( value ) \
#define set_d_buffer_regions( value ) \
asm __volatile__ ( "mcr p15, 0, %0, c3, c0\n" : : "r"(value) )
asm __volatile__ ( "mcr p15, 0, %0, c3, c0\n" : : "r"(value) )


#define set_d_rw_regions( value ) \
#define set_d_rw_regions( value ) \
asm __volatile__ ( "mcr p15, 0, %0, c5, c0, 0\n" : : "r"(value) )
asm __volatile__ ( "mcr p15, 0, %0, c5, c0, 0\n" : : "r"(value) )


#define set_i_rw_regions( value ) \
#define set_i_rw_regions( value ) \
asm __volatile__ ( "mcr p15, 0, %0, c5, c0, 1\n" : : "r"(value) )
asm __volatile__ ( "mcr p15, 0, %0, c5, c0, 1\n" : : "r"(value) )


static inline void
static inline void
set_control_reg( uint32_t value )
set_control_reg( uint32_t value )
{
{
asm __volatile__ ( "mcr p15, 0, %0, c3, c0\n" : : "r"(value) );
asm __volatile__ ( "mcr p15, 0, %0, c3, c0\n" : : "r"(value) );
}
}


static inline uint32_t
static inline uint32_t
read_control_reg( void )
read_control_reg( void )
{
{
uint32_t value;
uint32_t value;
asm __volatile__ ( "mrc p15, 0, %0, c3, c0\n" : "=r"(value) );
asm __volatile__ ( "mrc p15, 0, %0, c3, c0\n" : "=r"(value) );
return value;
return value;
}
}




static inline void
static inline void
set_d_tcm( uint32_t value )
set_d_tcm( uint32_t value )
{
{
asm( "mcr p15, 0, %0, c9, c1, 0\n" : : "r"(value) );
asm( "mcr p15, 0, %0, c9, c1, 0\n" : : "r"(value) );
}
}


static inline void
static inline void
set_i_tcm( uint32_t value )
set_i_tcm( uint32_t value )
{
{
asm( "mcr p15, 0, %0, c9, c1, 1\n" : : "r"(value) );
asm( "mcr p15, 0, %0, c9, c1, 1\n" : : "r"(value) );
}
}




/** Routines to enable / disable interrupts */
/** Routines to enable / disable interrupts */
static inline uint32_t
static inline uint32_t
cli(void)
cli(void)
{
{
uint32_t old_irq;
uint32_t old_irq;
asm __volatile__ (
asm __volatile__ (
"mrs %0, CPSR\n"
"mrs %0, CPSR\n"
"orr r1, %0, #0xC0\n" // set I flag to disable IRQ
"orr r1, %0, #0xC0\n" // set I flag to disable IRQ
"msr CPSR_c, r1\n"
"msr CPSR_c, r1\n"
"and %0, %0, #0xC0\n"
"and %0, %0, #0xC0\n"
: "=r"(old_irq) : : "r1"
: "=r"(old_irq) : : "r1"
);
);
return old_irq; // return the flag itself
return old_irq; // return the flag itself
}
}


static inline void
static inline void
sei( uint32_t old_irq )
sei( uint32_t old_irq )
{
{
asm __volatile__ (
asm __volatile__ (
"mrs r1, CPSR\n"
"mrs r1, CPSR\n"
"bic r1, r1, #0xC0\n"
"bic r1, r1, #0xC0\n"
"and %0, %0, #0xC0\n"
"and %0, %0, #0xC0\n"
"orr r1, r1, %0\n"
"orr r1, r1, %0\n"
"msr CPSR_c, r1" : : "r"(old_irq) : "r1" );
"msr CPSR_c, r1" : : "r"(old_irq) : "r1" );
}
}


/**
/**
* Some common instructions.
* Some common instructions.
* Thanks to ARMada by g3gg0 for some of the black magic :)
* Thanks to ARMada by g3gg0 for some of the black magic :)
*/
*/
#define RET_INSTR 0xe12fff1e // bx lr
#define RET_INSTR 0xe12fff1e // bx lr
#define FAR_CALL_INSTR 0xe51ff004 // ldr pc, [pc,#-4]
#define FAR_CALL_INSTR 0xe51ff004 // ldr pc, [pc,#-4]
#define LOOP_INSTR 0xeafffffe // 1: b 1b
#define LOOP_INSTR 0xeafffffe // 1: b 1b
#define NOP_INSTR 0xe1a00000 // mov r0, r0
#define NOP_INSTR 0xe1a00000 // mov r0, r0
#define MOV_R0_0_INSTR 0xe3a00000
#define MOV_R0_0_INSTR 0xe3a00000
#define MOV_R1_0xC80000_INSTR 0xe3a01732 // mov r1, 0xc80000
#define MOV_R1_0xC80000_INSTR 0xe3a01732 // mov r1, 0xc80000
#define MOV_R1_0xC60000_INSTR 0xE3A018C6 // mov r1, 0xc60000
#define MOV_R1_0xC60000_INSTR 0xE3A018C6 // mov r1, 0xc60000


#define MOV_RD_IMM_INSTR(rd,imm)\
#define MOV_RD_IMM_INSTR(rd,imm)\
( 0xE3A00000 \
( 0xE3A00000 \
| (rd << 15) \
| (rd << 15) \
)
)


#define BL_INSTR(pc,dest) \
#define BL_INSTR(pc,dest) \
( 0xEB000000 \
( 0xEB000000 \
| ((( ((uint32_t)dest) - ((uint32_t)pc) - 8 ) >> 2) & 0x00FFFFFF) \
| ((( ((uint32_t)dest) - ((uint32_t)pc) - 8 ) >> 2) & 0x00FFFFFF) \
)
)


#define B_INSTR(pc,dest) \
#define B_INSTR(pc,dest) \
( 0xEA000000 \
( 0xEA000000 \
| ((( ((uint32_t)dest) - ((uint32_t)pc) - 8 ) >> 2) & 0x00FFFFFF) \
| ((( ((uint32_t)dest) - ((uint32_t)pc) - 8 ) >> 2) & 0x00FFFFFF) \
)
)


#define ROR(val,count) (ROR32(val,(count)%32))
#define ROR(val,count) (ROR32(val,(count)%32))
#define ROR32(val,count) (((val) >> (count))|((val) << (32-(count))))
#define ROR32(val,count) (((val) >> (count))|((val) << (32-(count))))


/** Simple boot loader memcpy.
/** Simple boot loader memcpy.
*
*
* \note This is not general purpose; len must be > 0 and must be % 4
* \note This is not general purpose; len must be > 0 and must be % 4
*/
*/
static inline void
static inline void
blob_memcpy(
blob_memcpy(
void * dest_v,
void * dest_v,
const void * src_v,
const void * src_v,
const void * end
const void * end
)
)
{
{
uint32_t * dest = dest_v;
uint32_t * dest = dest_v;
const uint32_t * src = src_v;
const uint32_t * src = src_v;
const uint32_t len = ((const uint32_t*) end) - src;
const uint32_t len = ((const uint32_t*) end) - src;
uint32_t i;
uint32_t i;


for( i=0 ; i<len ; i++ )
for( i=0 ; i<len ; i++ )
dest[i] = src[i];
dest[i] = src[i];
}
}


#endif
#endif