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

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;
}