1
0
Fork 0
forked from Miroirs/x49gp
x50ng/src/s3c2410_power.c
2024-10-22 19:36:52 +02:00

326 lines
9.1 KiB
C

/* $Id: s3c2410_power.c,v 1.5 2008/12/11 12:18:17 ecd Exp $
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include "x49gp.h"
#include "s3c2410.h"
#include "s3c2410_power.h"
static const uint32_t EXTCLK = 12000000;
typedef struct {
uint32_t locktime;
uint32_t mpllcon;
uint32_t upllcon;
uint32_t clkcon;
uint32_t clkslow;
uint32_t clkdivn;
unsigned int nr_regs;
s3c2410_offset_t* regs;
x49gp_t* x49gp;
} s3c2410_power_t;
static int s3c2410_power_data_init( s3c2410_power_t* power )
{
s3c2410_offset_t regs[] = {
S3C2410_OFFSET( POWER, LOCKTIME, 0x00ffffff, power->locktime ), S3C2410_OFFSET( POWER, MPLLCON, 0x0005c080, power->mpllcon ),
S3C2410_OFFSET( POWER, UPLLCON, 0x00028080, power->upllcon ), S3C2410_OFFSET( POWER, CLKCON, 0x0007fff0, power->clkcon ),
S3C2410_OFFSET( POWER, CLKSLOW, 0x00000004, power->clkslow ), S3C2410_OFFSET( POWER, CLKDIVN, 0x00000000, power->clkdivn ) };
memset( power, 0, sizeof( s3c2410_power_t ) );
power->regs = malloc( sizeof( regs ) );
if ( NULL == power->regs ) {
fprintf( stderr, "%s:%u: Out of memory\n", __FUNCTION__, __LINE__ );
return -ENOMEM;
}
memcpy( power->regs, regs, sizeof( regs ) );
power->nr_regs = sizeof( regs ) / sizeof( regs[ 0 ] );
return 0;
}
static uint32_t s3c2410_power_read( void* opaque, target_phys_addr_t offset )
{
s3c2410_power_t* power = opaque;
s3c2410_offset_t* reg;
if ( !S3C2410_OFFSET_OK( power, offset ) ) {
return ~( 0 );
}
reg = S3C2410_OFFSET_ENTRY( power, offset );
#ifdef DEBUG_S3C2410_POWER
printf( "read %s [%08x] %s [%08lx] data %08x\n", "s3c2410-power", S3C2410_POWER_BASE, reg->name, ( unsigned long )offset,
*( reg->datap ) );
#endif
return *( reg->datap );
}
static void s3c2410_power_write( void* opaque, target_phys_addr_t offset, uint32_t data )
{
s3c2410_power_t* power = opaque;
x49gp_t* x49gp = power->x49gp;
s3c2410_offset_t* reg;
uint32_t mMdiv, mPdiv, mSdiv;
uint32_t uMdiv, uPdiv, uSdiv;
uint32_t slow_bit, slow_val;
if ( !S3C2410_OFFSET_OK( power, offset ) ) {
return;
}
reg = S3C2410_OFFSET_ENTRY( power, offset );
#ifdef DEBUG_S3C2410_POWER
printf( "write %s [%08x] %s [%08lx] data %08x\n", "s3c2410-power", S3C2410_POWER_BASE, reg->name, ( unsigned long )offset, data );
#endif
switch ( offset ) {
case S3C2410_POWER_CLKCON:
if ( data & CLKCON_POWER_OFF ) {
*( reg->datap ) = 0x7fff0;
#ifdef DEBUG_S3C2410_POWER
printf( "POWER: enter POWER_OFF\n" );
#endif
x49gp_modules_reset( x49gp, X49GP_RESET_POWER_OFF );
// if (x49gp->arm->NresetSig != LOW) {
// x49gp->arm->NresetSig = LOW;
// x49gp->arm->Exception++;
// }
x49gp_set_idle( x49gp, X49GP_ARM_OFF );
return;
}
if ( !( power->clkcon & CLKCON_IDLE ) && ( data & CLKCON_IDLE ) ) {
*( reg->datap ) = data;
#ifdef DEBUG_S3C2410_POWER
printf( "POWER: enter IDLE\n" );
#endif
x49gp_set_idle( x49gp, X49GP_ARM_SLEEP );
return;
}
*( reg->datap ) = data;
return;
case S3C2410_POWER_LOCKTIME:
*( reg->datap ) = data;
return;
default:
*( reg->datap ) = data;
break;
}
mMdiv = ( power->mpllcon >> 12 ) & 0xff;
mPdiv = ( power->mpllcon >> 4 ) & 0x3f;
mSdiv = ( power->mpllcon >> 0 ) & 0x03;
x49gp->MCLK = ( ( ( u64 )EXTCLK ) * ( ( u64 )( mMdiv + 8 ) ) ) / ( ( u64 )( ( mPdiv + 2 ) * ( 1 << mSdiv ) ) );
uMdiv = ( power->upllcon >> 12 ) & 0xff;
uPdiv = ( power->upllcon >> 4 ) & 0x3f;
uSdiv = ( power->upllcon >> 0 ) & 0x03;
x49gp->UCLK = ( ( ( u64 )EXTCLK ) * ( ( u64 )( uMdiv + 8 ) ) ) / ( ( u64 )( ( uPdiv + 2 ) * ( 1 << uSdiv ) ) );
slow_bit = ( power->clkslow & 0x10 );
if ( slow_bit ) {
slow_val = ( power->clkslow >> 0 ) & 0x07;
if ( 0 == slow_val )
x49gp->FCLK = EXTCLK;
else
x49gp->FCLK = EXTCLK / ( 2 * slow_val );
} else {
x49gp->FCLK = x49gp->MCLK;
}
if ( power->clkdivn & 4 ) {
x49gp->HCLK = x49gp->FCLK / 4;
x49gp->PCLK = x49gp->FCLK / 4;
x49gp->PCLK_ratio = 4;
} else {
switch ( power->clkdivn & 3 ) {
case 0:
x49gp->HCLK = x49gp->FCLK;
x49gp->PCLK = x49gp->HCLK;
x49gp->PCLK_ratio = 1;
break;
case 1:
x49gp->HCLK = x49gp->FCLK;
x49gp->PCLK = x49gp->HCLK / 2;
x49gp->PCLK_ratio = 2;
break;
case 2:
x49gp->HCLK = x49gp->FCLK / 2;
x49gp->PCLK = x49gp->HCLK;
x49gp->PCLK_ratio = 2;
break;
case 3:
x49gp->HCLK = x49gp->FCLK / 2;
x49gp->PCLK = x49gp->HCLK / 2;
x49gp->PCLK_ratio = 4;
break;
}
}
#ifdef DEBUG_S3C2410_POWER
printf( "%s: EXTCLK %u, mdiv %u, pdiv %u, sdiv %u: MCLK %u\n", __FUNCTION__, EXTCLK, mMdiv, mPdiv, mSdiv, x49gp->MCLK );
printf( "%s: EXTCLK %u, mdiv %u, pdiv %u, sdiv %u: UCLK %u\n", __FUNCTION__, EXTCLK, uMdiv, uPdiv, uSdiv, x49gp->UCLK );
printf( "%s: FCLK %s: %u\n", __FUNCTION__, slow_bit ? "(slow)" : "", x49gp->FCLK );
printf( "%s: HCLK %u, PCLK %u\n", __FUNCTION__, x49gp->HCLK, x49gp->PCLK );
#endif
}
static int s3c2410_power_load( x49gp_module_t* module, GKeyFile* key )
{
s3c2410_power_t* power = module->user_data;
s3c2410_offset_t* reg;
int error = 0;
int i;
#ifdef DEBUG_X49GP_MODULES
printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ );
#endif
for ( i = 0; i < power->nr_regs; i++ ) {
reg = &power->regs[ i ];
if ( NULL == reg->name )
continue;
if ( x49gp_module_get_u32( module, key, reg->name, reg->reset, reg->datap ) )
error = -EAGAIN;
}
if ( error ) {
return error;
}
s3c2410_power_write( power, S3C2410_POWER_BASE | S3C2410_POWER_CLKDIVN, power->clkdivn );
return 0;
}
static int s3c2410_power_save( x49gp_module_t* module, GKeyFile* key )
{
s3c2410_power_t* power = module->user_data;
s3c2410_offset_t* reg;
int i;
#ifdef DEBUG_X49GP_MODULES
printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ );
#endif
for ( i = 0; i < power->nr_regs; i++ ) {
reg = &power->regs[ i ];
if ( NULL == reg->name )
continue;
x49gp_module_set_u32( module, key, reg->name, *( reg->datap ) );
}
return 0;
}
static int s3c2410_power_reset( x49gp_module_t* module, x49gp_reset_t reset )
{
s3c2410_power_t* power = module->user_data;
s3c2410_offset_t* reg;
int i;
#ifdef DEBUG_X49GP_MODULES
printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ );
#endif
for ( i = 0; i < power->nr_regs; i++ ) {
reg = &power->regs[ i ];
if ( NULL == reg->name )
continue;
*( reg->datap ) = reg->reset;
}
return 0;
}
static CPUReadMemoryFunc* s3c2410_power_readfn[] = { s3c2410_power_read, s3c2410_power_read, s3c2410_power_read };
static CPUWriteMemoryFunc* s3c2410_power_writefn[] = { s3c2410_power_write, s3c2410_power_write, s3c2410_power_write };
static int s3c2410_power_init( x49gp_module_t* module )
{
s3c2410_power_t* power;
int iotype;
#ifdef DEBUG_X49GP_MODULES
printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ );
#endif
power = malloc( sizeof( s3c2410_power_t ) );
if ( NULL == power ) {
fprintf( stderr, "%s:%u: Out of memory\n", __FUNCTION__, __LINE__ );
return -ENOMEM;
}
if ( s3c2410_power_data_init( power ) ) {
free( power );
return -ENOMEM;
}
module->user_data = power;
power->x49gp = module->x49gp;
iotype = cpu_register_io_memory( s3c2410_power_readfn, s3c2410_power_writefn, power );
#ifdef DEBUG_S3C2410_POWER
printf( "%s: iotype %08x\n", __FUNCTION__, iotype );
#endif
cpu_register_physical_memory( S3C2410_POWER_BASE, S3C2410_MAP_SIZE, iotype );
return 0;
}
static int s3c2410_power_exit( x49gp_module_t* module )
{
s3c2410_power_t* power;
#ifdef DEBUG_X49GP_MODULES
printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ );
#endif
if ( module->user_data ) {
power = module->user_data;
if ( power->regs )
free( power->regs );
free( power );
}
x49gp_module_unregister( module );
free( module );
return 0;
}
int x49gp_s3c2410_power_init( x49gp_t* x49gp )
{
x49gp_module_t* module;
if ( x49gp_module_init( x49gp, "s3c2410-power", s3c2410_power_init, s3c2410_power_exit, s3c2410_power_reset, s3c2410_power_load,
s3c2410_power_save, NULL, &module ) ) {
return -1;
}
return x49gp_module_register( module );
}