/* $Id: s3c2410_uart.c,v 1.4 2008/12/11 12:18:17 ecd Exp $ */ #include #include #include #include #include #include #include #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; }