2009-09-15 20:30:37 +04:00
/*
* Copyright ( C ) 2008 - 2009 ST - Ericsson AB
* License terms : GNU General Public License ( GPL ) version 2
* TCM memory handling for ARM systems
*
* Author : Linus Walleij < linus . walleij @ stericsson . com >
* Author : Rickard Andersson < rickard . andersson @ stericsson . com >
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/stddef.h>
# include <linux/ioport.h>
# include <linux/genalloc.h>
# include <linux/string.h> /* memcpy */
# include <asm/cputype.h>
# include <asm/mach/map.h>
2011-01-04 21:07:14 +03:00
# include <asm/memory.h>
2009-09-15 20:30:37 +04:00
# include "tcm.h"
static struct gen_pool * tcm_pool ;
/* TCM section definitions from the linker */
extern char __itcm_start , __sitcm_text , __eitcm_text ;
extern char __dtcm_start , __sdtcm_data , __edtcm_data ;
2010-07-13 00:53:28 +04:00
/* These will be increased as we run */
u32 dtcm_end = DTCM_OFFSET ;
u32 itcm_end = ITCM_OFFSET ;
2009-09-15 20:30:37 +04:00
/*
* TCM memory resources
*/
static struct resource dtcm_res = {
. name = " DTCM RAM " ,
. start = DTCM_OFFSET ,
2010-07-13 00:53:28 +04:00
. end = DTCM_OFFSET ,
2009-09-15 20:30:37 +04:00
. flags = IORESOURCE_MEM
} ;
static struct resource itcm_res = {
. name = " ITCM RAM " ,
. start = ITCM_OFFSET ,
2010-07-13 00:53:28 +04:00
. end = ITCM_OFFSET ,
2009-09-15 20:30:37 +04:00
. flags = IORESOURCE_MEM
} ;
static struct map_desc dtcm_iomap [ ] __initdata = {
{
. virtual = DTCM_OFFSET ,
. pfn = __phys_to_pfn ( DTCM_OFFSET ) ,
2010-07-13 00:53:28 +04:00
. length = 0 ,
2010-07-13 00:50:59 +04:00
. type = MT_MEMORY_DTCM
2009-09-15 20:30:37 +04:00
}
} ;
static struct map_desc itcm_iomap [ ] __initdata = {
{
. virtual = ITCM_OFFSET ,
. pfn = __phys_to_pfn ( ITCM_OFFSET ) ,
2010-07-13 00:53:28 +04:00
. length = 0 ,
2010-07-13 00:50:59 +04:00
. type = MT_MEMORY_ITCM
2009-09-15 20:30:37 +04:00
}
} ;
/*
* Allocate a chunk of TCM memory
*/
void * tcm_alloc ( size_t len )
{
unsigned long vaddr ;
if ( ! tcm_pool )
return NULL ;
vaddr = gen_pool_alloc ( tcm_pool , len ) ;
if ( ! vaddr )
return NULL ;
return ( void * ) vaddr ;
}
EXPORT_SYMBOL ( tcm_alloc ) ;
/*
* Free a chunk of TCM memory
*/
void tcm_free ( void * addr , size_t len )
{
gen_pool_free ( tcm_pool , ( unsigned long ) addr , len ) ;
}
EXPORT_SYMBOL ( tcm_free ) ;
2010-07-13 00:53:28 +04:00
static int __init setup_tcm_bank ( u8 type , u8 bank , u8 banks ,
u32 * offset )
2009-09-15 20:30:37 +04:00
{
const int tcm_sizes [ 16 ] = { 0 , - 1 , - 1 , 4 , 8 , 16 , 32 , 64 , 128 ,
256 , 512 , 1024 , - 1 , - 1 , - 1 , - 1 } ;
u32 tcm_region ;
int tcm_size ;
2010-07-13 00:51:41 +04:00
/*
* If there are more than one TCM bank of this type ,
* select the TCM bank to operate on in the TCM selection
* register .
*/
if ( banks > 1 )
asm ( " mcr p15, 0, %0, c9, c2, 0 "
: /* No output operands */
: " r " ( bank ) ) ;
2009-09-15 20:30:37 +04:00
/* Read the special TCM region register c9, 0 */
if ( ! type )
asm ( " mrc p15, 0, %0, c9, c1, 0 "
: " =r " ( tcm_region ) ) ;
else
asm ( " mrc p15, 0, %0, c9, c1, 1 "
: " =r " ( tcm_region ) ) ;
tcm_size = tcm_sizes [ ( tcm_region > > 2 ) & 0x0f ] ;
if ( tcm_size < 0 ) {
2010-07-13 00:53:28 +04:00
pr_err ( " CPU: %sTCM%d of unknown size \n " ,
2010-07-13 00:51:41 +04:00
type ? " I " : " D " , bank ) ;
2010-07-13 00:53:28 +04:00
return - EINVAL ;
} else if ( tcm_size > 32 ) {
pr_err ( " CPU: %sTCM%d larger than 32k found \n " ,
type ? " I " : " D " , bank ) ;
return - EINVAL ;
2009-09-15 20:30:37 +04:00
} else {
2010-07-13 00:51:41 +04:00
pr_info ( " CPU: found %sTCM%d %dk @ %08x, %senabled \n " ,
2009-09-15 20:30:37 +04:00
type ? " I " : " D " ,
2010-07-13 00:51:41 +04:00
bank ,
2009-09-15 20:30:37 +04:00
tcm_size ,
( tcm_region & 0xfffff000U ) ,
( tcm_region & 1 ) ? " " : " not " ) ;
}
/* Force move the TCM bank to where we want it, enable */
2010-07-13 00:53:28 +04:00
tcm_region = * offset | ( tcm_region & 0x00000ffeU ) | 1 ;
2009-09-15 20:30:37 +04:00
if ( ! type )
asm ( " mcr p15, 0, %0, c9, c1, 0 "
: /* No output operands */
: " r " ( tcm_region ) ) ;
else
asm ( " mcr p15, 0, %0, c9, c1, 1 "
: /* No output operands */
: " r " ( tcm_region ) ) ;
2010-07-13 00:53:28 +04:00
/* Increase offset */
* offset + = ( tcm_size < < 10 ) ;
2010-07-13 00:51:41 +04:00
pr_info ( " CPU: moved %sTCM%d %dk to %08x, enabled \n " ,
type ? " I " : " D " ,
bank ,
tcm_size ,
( tcm_region & 0xfffff000U ) ) ;
2010-07-13 00:53:28 +04:00
return 0 ;
2009-09-15 20:30:37 +04:00
}
/*
* This initializes the TCM memory
*/
void __init tcm_init ( void )
{
u32 tcm_status = read_cpuid_tcmstatus ( ) ;
2010-07-13 00:51:41 +04:00
u8 dtcm_banks = ( tcm_status > > 16 ) & 0x03 ;
u8 itcm_banks = ( tcm_status & 0x03 ) ;
2009-09-15 20:30:37 +04:00
char * start ;
char * end ;
char * ram ;
2010-07-13 00:53:28 +04:00
int ret ;
2010-07-13 00:51:41 +04:00
int i ;
2009-09-15 20:30:37 +04:00
/* Setup DTCM if present */
2010-07-13 00:53:28 +04:00
if ( dtcm_banks > 0 ) {
for ( i = 0 ; i < dtcm_banks ; i + + ) {
ret = setup_tcm_bank ( 0 , i , dtcm_banks , & dtcm_end ) ;
if ( ret )
return ;
}
dtcm_res . end = dtcm_end - 1 ;
2009-09-15 20:30:37 +04:00
request_resource ( & iomem_resource , & dtcm_res ) ;
2010-07-13 00:53:28 +04:00
dtcm_iomap [ 0 ] . length = dtcm_end - DTCM_OFFSET ;
2009-09-15 20:30:37 +04:00
iotable_init ( dtcm_iomap , 1 ) ;
/* Copy data from RAM to DTCM */
start = & __sdtcm_data ;
end = & __edtcm_data ;
ram = & __dtcm_start ;
2010-07-13 00:53:28 +04:00
/* This means you compiled more code than fits into DTCM */
BUG_ON ( ( end - start ) > ( dtcm_end - DTCM_OFFSET ) ) ;
2009-09-15 20:30:37 +04:00
memcpy ( start , ram , ( end - start ) ) ;
pr_debug ( " CPU DTCM: copied data from %p - %p \n " , start , end ) ;
}
/* Setup ITCM if present */
2010-07-13 00:53:28 +04:00
if ( itcm_banks > 0 ) {
for ( i = 0 ; i < itcm_banks ; i + + ) {
ret = setup_tcm_bank ( 1 , i , itcm_banks , & itcm_end ) ;
if ( ret )
return ;
}
itcm_res . end = itcm_end - 1 ;
2009-09-15 20:30:37 +04:00
request_resource ( & iomem_resource , & itcm_res ) ;
2010-07-13 00:53:28 +04:00
itcm_iomap [ 0 ] . length = itcm_end - ITCM_OFFSET ;
2009-09-15 20:30:37 +04:00
iotable_init ( itcm_iomap , 1 ) ;
/* Copy code from RAM to ITCM */
start = & __sitcm_text ;
end = & __eitcm_text ;
ram = & __itcm_start ;
2010-07-13 00:53:28 +04:00
/* This means you compiled more code than fits into ITCM */
BUG_ON ( ( end - start ) > ( itcm_end - ITCM_OFFSET ) ) ;
2009-09-15 20:30:37 +04:00
memcpy ( start , ram , ( end - start ) ) ;
pr_debug ( " CPU ITCM: copied code from %p - %p \n " , start , end ) ;
}
}
/*
* This creates the TCM memory pool and has to be done later ,
* during the core_initicalls , since the allocator is not yet
* up and running when the first initialization runs .
*/
static int __init setup_tcm_pool ( void )
{
u32 tcm_status = read_cpuid_tcmstatus ( ) ;
u32 dtcm_pool_start = ( u32 ) & __edtcm_data ;
u32 itcm_pool_start = ( u32 ) & __eitcm_text ;
int ret ;
/*
* Set up malloc pool , 2 ^ 2 = 4 bytes granularity since
* the TCM is sometimes just 4 KiB . NB : pages and cache
* line alignments does not matter in TCM !
*/
tcm_pool = gen_pool_create ( 2 , - 1 ) ;
pr_debug ( " Setting up TCM memory pool \n " ) ;
/* Add the rest of DTCM to the TCM pool */
2010-07-13 00:51:41 +04:00
if ( tcm_status & ( 0x03 < < 16 ) ) {
2010-07-13 00:53:28 +04:00
if ( dtcm_pool_start < dtcm_end ) {
2009-09-15 20:30:37 +04:00
ret = gen_pool_add ( tcm_pool , dtcm_pool_start ,
2010-07-13 00:53:28 +04:00
dtcm_end - dtcm_pool_start , - 1 ) ;
2009-09-15 20:30:37 +04:00
if ( ret ) {
pr_err ( " CPU DTCM: could not add DTCM " \
" remainder to pool! \n " ) ;
return ret ;
}
pr_debug ( " CPU DTCM: Added %08x bytes @ %08x to " \
" the TCM memory pool \n " ,
2010-07-13 00:53:28 +04:00
dtcm_end - dtcm_pool_start ,
2009-09-15 20:30:37 +04:00
dtcm_pool_start ) ;
}
}
/* Add the rest of ITCM to the TCM pool */
2010-07-13 00:51:41 +04:00
if ( tcm_status & 0x03 ) {
2010-07-13 00:53:28 +04:00
if ( itcm_pool_start < itcm_end ) {
2009-09-15 20:30:37 +04:00
ret = gen_pool_add ( tcm_pool , itcm_pool_start ,
2010-07-13 00:53:28 +04:00
itcm_end - itcm_pool_start , - 1 ) ;
2009-09-15 20:30:37 +04:00
if ( ret ) {
pr_err ( " CPU ITCM: could not add ITCM " \
" remainder to pool! \n " ) ;
return ret ;
}
pr_debug ( " CPU ITCM: Added %08x bytes @ %08x to " \
" the TCM memory pool \n " ,
2010-07-13 00:53:28 +04:00
itcm_end - itcm_pool_start ,
2009-09-15 20:30:37 +04:00
itcm_pool_start ) ;
}
}
return 0 ;
}
core_initcall ( setup_tcm_pool ) ;