forked from Miroirs/x49gp
427 lines
14 KiB
C
427 lines
14 KiB
C
/* $Id: s3c2410_uart.c,v 1.4 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_intc.h"
|
|
|
|
typedef struct {
|
|
uint32_t ulcon;
|
|
uint32_t ucon;
|
|
uint32_t ufcon;
|
|
uint32_t umcon;
|
|
uint32_t utrstat;
|
|
uint32_t uerstat;
|
|
uint32_t ufstat;
|
|
uint32_t umstat;
|
|
uint32_t utxh;
|
|
uint32_t urxh;
|
|
uint32_t ubrdiv;
|
|
|
|
int int_err;
|
|
int int_txd;
|
|
int int_rxd;
|
|
|
|
unsigned int nr_regs;
|
|
s3c2410_offset_t* regs;
|
|
|
|
x49gp_t* x49gp;
|
|
} s3c2410_uart_reg_t;
|
|
|
|
typedef struct {
|
|
s3c2410_uart_reg_t uart[ 3 ];
|
|
} s3c2410_uart_t;
|
|
|
|
static int s3c2410_uart_data_init( s3c2410_uart_t* uart )
|
|
{
|
|
s3c2410_offset_t regs0[] = { S3C2410_OFFSET( UART0, ULCON, 0x00000000, uart->uart[ 0 ].ulcon ),
|
|
S3C2410_OFFSET( UART0, UCON, 0x00000000, uart->uart[ 0 ].ucon ),
|
|
S3C2410_OFFSET( UART0, UFCON, 0x00000000, uart->uart[ 0 ].ufcon ),
|
|
S3C2410_OFFSET( UART0, UMCON, 0x00000000, uart->uart[ 0 ].umcon ),
|
|
S3C2410_OFFSET( UART0, UTRSTAT, 0x00000006, uart->uart[ 0 ].utrstat ),
|
|
S3C2410_OFFSET( UART0, UERSTAT, 0x00000000, uart->uart[ 0 ].uerstat ),
|
|
S3C2410_OFFSET( UART0, UFSTAT, 0x00000000, uart->uart[ 0 ].ufstat ),
|
|
S3C2410_OFFSET( UART0, UMSTAT, 0x00000000, uart->uart[ 0 ].umstat ),
|
|
S3C2410_OFFSET( UART0, UTXH, 0, uart->uart[ 0 ].utxh ),
|
|
S3C2410_OFFSET( UART0, URXH, 0, uart->uart[ 0 ].urxh ),
|
|
S3C2410_OFFSET( UART0, UBRDIV, 0, uart->uart[ 0 ].ubrdiv ) };
|
|
s3c2410_offset_t regs1[] = { S3C2410_OFFSET( UART1, ULCON, 0x00000000, uart->uart[ 1 ].ulcon ),
|
|
S3C2410_OFFSET( UART1, UCON, 0x00000000, uart->uart[ 1 ].ucon ),
|
|
S3C2410_OFFSET( UART1, UFCON, 0x00000000, uart->uart[ 1 ].ufcon ),
|
|
S3C2410_OFFSET( UART1, UMCON, 0x00000000, uart->uart[ 1 ].umcon ),
|
|
S3C2410_OFFSET( UART1, UTRSTAT, 0x00000006, uart->uart[ 1 ].utrstat ),
|
|
S3C2410_OFFSET( UART1, UERSTAT, 0x00000000, uart->uart[ 1 ].uerstat ),
|
|
S3C2410_OFFSET( UART1, UFSTAT, 0x00000000, uart->uart[ 1 ].ufstat ),
|
|
S3C2410_OFFSET( UART1, UMSTAT, 0x00000000, uart->uart[ 1 ].umstat ),
|
|
S3C2410_OFFSET( UART1, UTXH, 0, uart->uart[ 1 ].utxh ),
|
|
S3C2410_OFFSET( UART1, URXH, 0, uart->uart[ 1 ].urxh ),
|
|
S3C2410_OFFSET( UART1, UBRDIV, 0, uart->uart[ 1 ].ubrdiv ) };
|
|
s3c2410_offset_t regs2[] = { S3C2410_OFFSET( UART2, ULCON, 0x00000000, uart->uart[ 2 ].ulcon ),
|
|
S3C2410_OFFSET( UART2, UCON, 0x00000000, uart->uart[ 2 ].ucon ),
|
|
S3C2410_OFFSET( UART2, UFCON, 0x00000000, uart->uart[ 2 ].ufcon ),
|
|
S3C2410_OFFSET( UART2, UTRSTAT, 0x00000006, uart->uart[ 2 ].utrstat ),
|
|
S3C2410_OFFSET( UART2, UERSTAT, 0x00000000, uart->uart[ 2 ].uerstat ),
|
|
S3C2410_OFFSET( UART2, UFSTAT, 0x00000000, uart->uart[ 2 ].ufstat ),
|
|
S3C2410_OFFSET( UART2, UTXH, 0, uart->uart[ 2 ].utxh ),
|
|
S3C2410_OFFSET( UART2, URXH, 0, uart->uart[ 2 ].urxh ),
|
|
S3C2410_OFFSET( UART2, UBRDIV, 0, uart->uart[ 2 ].ubrdiv ) };
|
|
|
|
uart->uart[ 0 ].regs = malloc( sizeof( regs0 ) );
|
|
if ( NULL == uart->uart[ 0 ].regs ) {
|
|
fprintf( stderr, "%s:%u: Out of memory\n", __FUNCTION__, __LINE__ );
|
|
return -ENOMEM;
|
|
}
|
|
uart->uart[ 1 ].regs = malloc( sizeof( regs1 ) );
|
|
if ( NULL == uart->uart[ 1 ].regs ) {
|
|
fprintf( stderr, "%s:%u: Out of memory\n", __FUNCTION__, __LINE__ );
|
|
free( uart->uart[ 0 ].regs );
|
|
return -ENOMEM;
|
|
}
|
|
uart->uart[ 2 ].regs = malloc( sizeof( regs2 ) );
|
|
if ( NULL == uart->uart[ 2 ].regs ) {
|
|
fprintf( stderr, "%s:%u: Out of memory\n", __FUNCTION__, __LINE__ );
|
|
free( uart->uart[ 0 ].regs );
|
|
free( uart->uart[ 1 ].regs );
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memcpy( uart->uart[ 0 ].regs, regs0, sizeof( regs0 ) );
|
|
uart->uart[ 0 ].nr_regs = sizeof( regs0 ) / sizeof( regs0[ 0 ] );
|
|
uart->uart[ 0 ].int_err = SUB_INT_ERR0;
|
|
uart->uart[ 0 ].int_txd = SUB_INT_TXD0;
|
|
uart->uart[ 0 ].int_rxd = SUB_INT_RXD0;
|
|
|
|
memcpy( uart->uart[ 1 ].regs, regs1, sizeof( regs1 ) );
|
|
uart->uart[ 1 ].nr_regs = sizeof( regs1 ) / sizeof( regs1[ 0 ] );
|
|
uart->uart[ 1 ].int_err = SUB_INT_ERR1;
|
|
uart->uart[ 1 ].int_txd = SUB_INT_TXD1;
|
|
uart->uart[ 1 ].int_rxd = SUB_INT_RXD1;
|
|
|
|
memcpy( uart->uart[ 2 ].regs, regs2, sizeof( regs2 ) );
|
|
uart->uart[ 2 ].nr_regs = sizeof( regs2 ) / sizeof( regs2[ 0 ] );
|
|
uart->uart[ 2 ].int_err = SUB_INT_ERR2;
|
|
uart->uart[ 2 ].int_txd = SUB_INT_TXD2;
|
|
uart->uart[ 2 ].int_rxd = SUB_INT_RXD2;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t s3c2410_uart_read( void* opaque, target_phys_addr_t offset )
|
|
{
|
|
s3c2410_uart_reg_t* uart_regs = opaque;
|
|
x49gp_t* x49gp = uart_regs->x49gp;
|
|
s3c2410_offset_t* reg;
|
|
#ifdef DEBUG_S3C2410_UART
|
|
const char* module;
|
|
uint32_t mod_offset;
|
|
uint32_t base;
|
|
|
|
base = ( offset & 0x0000c000 ) >> 14;
|
|
|
|
switch ( base ) {
|
|
case 0:
|
|
module = "s3c2410-uart0";
|
|
mod_offset = S3C2410_UART0_BASE;
|
|
break;
|
|
case 1:
|
|
module = "s3c2410-uart1";
|
|
mod_offset = S3C2410_UART1_BASE;
|
|
break;
|
|
case 2:
|
|
module = "s3c2410-uart2";
|
|
mod_offset = S3C2410_UART2_BASE;
|
|
break;
|
|
default:
|
|
return ~( 0 );
|
|
}
|
|
#endif
|
|
|
|
offset &= ~( 0xffffc000 );
|
|
if ( !S3C2410_OFFSET_OK( uart_regs, offset ) ) {
|
|
return ~( 0 );
|
|
}
|
|
|
|
reg = S3C2410_OFFSET_ENTRY( uart_regs, offset );
|
|
|
|
#ifdef DEBUG_S3C2410_UART
|
|
printf( "read %s [%08x] %s [%08lx] data %08x\n", module, mod_offset, reg->name, ( unsigned long )offset, *( reg->datap ) );
|
|
#endif
|
|
|
|
switch ( offset ) {
|
|
case S3C2410_UART0_URXH:
|
|
uart_regs->utrstat &= ~( 1 << 0 );
|
|
|
|
if ( uart_regs->ucon & ( 1 << 8 ) ) {
|
|
s3c2410_intc_sub_deassert( x49gp, uart_regs->int_rxd );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return *( reg->datap );
|
|
}
|
|
|
|
static void s3c2410_uart_write( void* opaque, target_phys_addr_t offset, uint32_t data )
|
|
{
|
|
s3c2410_uart_reg_t* uart_regs = opaque;
|
|
x49gp_t* x49gp = uart_regs->x49gp;
|
|
s3c2410_offset_t* reg;
|
|
uint32_t base;
|
|
#ifdef DEBUG_S3C2410_UART
|
|
const char* module;
|
|
uint32_t mod_offset, ubrdivn, baud;
|
|
#endif
|
|
|
|
base = ( offset & 0x0000c000 ) >> 14;
|
|
|
|
#ifdef DEBUG_S3C2410_UART
|
|
switch ( base ) {
|
|
case 0:
|
|
module = "s3c2410-uart0";
|
|
mod_offset = S3C2410_UART0_BASE;
|
|
break;
|
|
case 1:
|
|
module = "s3c2410-uart1";
|
|
mod_offset = S3C2410_UART1_BASE;
|
|
break;
|
|
case 2:
|
|
module = "s3c2410-uart2";
|
|
mod_offset = S3C2410_UART2_BASE;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
offset &= ~( 0xffffc000 );
|
|
if ( !S3C2410_OFFSET_OK( uart_regs, offset ) ) {
|
|
return;
|
|
}
|
|
|
|
reg = S3C2410_OFFSET_ENTRY( uart_regs, offset );
|
|
|
|
#ifdef DEBUG_S3C2410_UART
|
|
printf( "write %s [%08x] %s [%08lx] data %08x\n", module, mod_offset, reg->name, ( unsigned long )offset, data );
|
|
#endif
|
|
|
|
*( reg->datap ) = data;
|
|
|
|
switch ( offset ) {
|
|
case S3C2410_UART0_UCON:
|
|
if ( *( reg->datap ) & ( 1 << 9 ) )
|
|
s3c2410_intc_sub_assert( x49gp, uart_regs->int_txd, 1 );
|
|
if ( *( reg->datap ) & ( 1 << 8 ) )
|
|
s3c2410_intc_sub_deassert( x49gp, uart_regs->int_rxd );
|
|
break;
|
|
|
|
case S3C2410_UART0_UBRDIV:
|
|
#ifdef DEBUG_S3C2410_UART
|
|
ubrdivn = ( data >> 0 ) & 0xffff;
|
|
if ( uart_regs->ucon & ( 1 << 10 ) ) {
|
|
baud = x49gp->UCLK / 16 / ( ubrdivn + 1 );
|
|
printf( "%s: UEXTCLK %u, ubrdivn %u, baud %u\n", module, x49gp->UCLK, ubrdivn, baud );
|
|
} else {
|
|
baud = x49gp->PCLK / 16 / ( ubrdivn + 1 );
|
|
printf( "%s: PCLK %u, ubrdivn %u, baud %u\n", module, x49gp->PCLK, ubrdivn, baud );
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case S3C2410_UART0_UTXH:
|
|
if ( uart_regs->ucon & ( 1 << 9 ) )
|
|
s3c2410_intc_sub_deassert( x49gp, uart_regs->int_txd );
|
|
|
|
uart_regs->utrstat |= ( 1 << 2 ) | ( 1 << 1 );
|
|
|
|
if ( uart_regs->ucon & ( 1 << 9 ) )
|
|
s3c2410_intc_sub_assert( x49gp, uart_regs->int_txd, 1 );
|
|
else
|
|
s3c2410_intc_sub_assert( x49gp, uart_regs->int_txd, 0 );
|
|
|
|
if ( uart_regs->ucon & ( 1 << 5 ) ) {
|
|
uart_regs->urxh = data;
|
|
uart_regs->utrstat |= ( 1 << 0 );
|
|
|
|
if ( uart_regs->ucon & ( 1 << 8 ) )
|
|
s3c2410_intc_sub_assert( x49gp, uart_regs->int_rxd, 1 );
|
|
else
|
|
s3c2410_intc_sub_assert( x49gp, uart_regs->int_rxd, 0 );
|
|
} else if ( base == 2 ) {
|
|
uart_regs->urxh = data;
|
|
uart_regs->utrstat |= ( 1 << 0 );
|
|
|
|
if ( uart_regs->ucon & ( 1 << 8 ) )
|
|
s3c2410_intc_sub_assert( x49gp, uart_regs->int_rxd, 1 );
|
|
else
|
|
s3c2410_intc_sub_assert( x49gp, uart_regs->int_rxd, 0 );
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int s3c2410_uart_load( x49gp_module_t* module, GKeyFile* key )
|
|
{
|
|
s3c2410_uart_reg_t* uart_regs = 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 < uart_regs->nr_regs; i++ ) {
|
|
reg = &uart_regs->regs[ i ];
|
|
|
|
if ( NULL == reg->name )
|
|
continue;
|
|
|
|
if ( x49gp_module_get_u32( module, key, reg->name, reg->reset, reg->datap ) )
|
|
error = -EAGAIN;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static int s3c2410_uart_save( x49gp_module_t* module, GKeyFile* key )
|
|
{
|
|
s3c2410_uart_reg_t* uart_regs = 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 < uart_regs->nr_regs; i++ ) {
|
|
reg = &uart_regs->regs[ i ];
|
|
|
|
if ( NULL == reg->name )
|
|
continue;
|
|
|
|
x49gp_module_set_u32( module, key, reg->name, *( reg->datap ) );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int s3c2410_uart_reset( x49gp_module_t* module, x49gp_reset_t reset )
|
|
{
|
|
s3c2410_uart_reg_t* uart_regs = 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 < uart_regs->nr_regs; i++ ) {
|
|
reg = &uart_regs->regs[ i ];
|
|
|
|
if ( NULL == reg->name )
|
|
continue;
|
|
|
|
*( reg->datap ) = reg->reset;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static CPUReadMemoryFunc* s3c2410_uart_readfn[] = { s3c2410_uart_read, s3c2410_uart_read, s3c2410_uart_read };
|
|
|
|
static CPUWriteMemoryFunc* s3c2410_uart_writefn[] = { s3c2410_uart_write, s3c2410_uart_write, s3c2410_uart_write };
|
|
|
|
static int s3c2410_uart_init( x49gp_module_t* module )
|
|
{
|
|
s3c2410_uart_reg_t* uart_regs = module->user_data;
|
|
int iotype;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ );
|
|
#endif
|
|
|
|
iotype = cpu_register_io_memory( s3c2410_uart_readfn, s3c2410_uart_writefn, uart_regs );
|
|
#ifdef DEBUG_S3C2410_UART
|
|
printf( "%s: iotype %08x\n", __FUNCTION__, iotype );
|
|
#endif
|
|
cpu_register_physical_memory( S3C2410_UART0_BASE, S3C2410_MAP_SIZE, iotype );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int s3c2410_uart_exit( x49gp_module_t* module )
|
|
{
|
|
s3c2410_uart_reg_t* uart_regs;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ );
|
|
#endif
|
|
|
|
if ( module->user_data ) {
|
|
uart_regs = module->user_data;
|
|
if ( uart_regs->regs )
|
|
free( uart_regs->regs );
|
|
}
|
|
|
|
x49gp_module_unregister( module );
|
|
free( module );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int x49gp_s3c2410_uart_init( x49gp_t* x49gp )
|
|
{
|
|
s3c2410_uart_t* uart;
|
|
x49gp_module_t* module;
|
|
|
|
uart = malloc( sizeof( s3c2410_uart_t ) );
|
|
if ( NULL == uart ) {
|
|
fprintf( stderr, "%s:%u: Out of memory\n", __FUNCTION__, __LINE__ );
|
|
return -ENOMEM;
|
|
}
|
|
memset( uart, 0, sizeof( s3c2410_uart_t ) );
|
|
|
|
if ( s3c2410_uart_data_init( uart ) ) {
|
|
free( uart );
|
|
return -ENOMEM;
|
|
}
|
|
|
|
uart->uart[ 0 ].x49gp = x49gp;
|
|
uart->uart[ 1 ].x49gp = x49gp;
|
|
uart->uart[ 2 ].x49gp = x49gp;
|
|
|
|
if ( x49gp_module_init( x49gp, "s3c2410-uart0", s3c2410_uart_init, s3c2410_uart_exit, s3c2410_uart_reset, s3c2410_uart_load,
|
|
s3c2410_uart_save, &uart->uart[ 0 ], &module ) ) {
|
|
return -1;
|
|
}
|
|
if ( x49gp_module_register( module ) ) {
|
|
return -1;
|
|
}
|
|
|
|
if ( x49gp_module_init( x49gp, "s3c2410-uart1", s3c2410_uart_init, s3c2410_uart_exit, s3c2410_uart_reset, s3c2410_uart_load,
|
|
s3c2410_uart_save, &uart->uart[ 1 ], &module ) ) {
|
|
return -1;
|
|
}
|
|
if ( x49gp_module_register( module ) ) {
|
|
return -1;
|
|
}
|
|
|
|
if ( x49gp_module_init( x49gp, "s3c2410-uart2", s3c2410_uart_init, s3c2410_uart_exit, s3c2410_uart_reset, s3c2410_uart_load,
|
|
s3c2410_uart_save, &uart->uart[ 2 ], &module ) ) {
|
|
return -1;
|
|
}
|
|
if ( x49gp_module_register( module ) ) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|