/* $Id: s3c2410_intc.c,v 1.8 2008/12/11 12:18:17 ecd Exp $ */ #include #include #include #include #include #include "x49gp.h" #include "s3c2410.h" #include "s3c2410_intc.h" #include "s3c2410_power.h" #include "byteorder.h" extern int do_trace; typedef struct { int sel_shift; uint32_t mode_bit; int index; int req[ 6 ]; } s3c2410_arb_t; typedef struct { uint32_t sel; uint32_t mode; } s3c2410_arb_data_t; static const int s3c2410_arb_order[ 4 ][ 6 ] = { {0, 1, 2, 3, 4, 5}, {0, 2, 3, 4, 1, 5}, {0, 3, 4, 1, 2, 5}, {0, 4, 1, 2, 3, 5} }; static const s3c2410_arb_t s3c2410_arb_table[] = { [0] = {ARB0_SEL_SHIFT, ARB0_MODE, 0, { -1, EINT0, EINT1, EINT2, EINT3, -1 } }, [1] = {ARB1_SEL_SHIFT, ARB1_MODE, 1, { EINT4_7, EINT8_23, -1, nBATT_FLT, INT_TICK, INT_WDT } }, [2] = {ARB2_SEL_SHIFT, ARB2_MODE, 2, { INT_TIMER0, INT_TIMER1, INT_TIMER2, INT_TIMER3, INT_TIMER4, INT_UART2 }}, [3] = {ARB3_SEL_SHIFT, ARB3_MODE, 3, { INT_LCD, INT_DMA0, INT_DMA1, INT_DMA2, INT_DMA3, INT_SDI } }, [4] = {ARB4_SEL_SHIFT, ARB4_MODE, 4, { INT_SPI0, INT_UART1, -1, INT_USBD, INT_USBH, INT_IIC } }, [5] = {ARB5_SEL_SHIFT, ARB5_MODE, 5, { -1, INT_UART0, INT_SPI1, INT_RTC, INT_ADC, -1, } }, [6] = {ARB6_SEL_SHIFT, ARB6_MODE, 6, { 0, 1, 2, 3, 4, 5 } }, }; #define INTC_NR_ARB ( sizeof( s3c2410_arb_table ) / sizeof( s3c2410_arb_table[ 0 ] ) ) typedef struct { uint32_t srcpnd; uint32_t intmod; uint32_t intmsk; uint32_t priority; uint32_t intpnd; uint32_t intoffset; uint32_t subsrcpnd; uint32_t intsubmsk; s3c2410_arb_data_t arb_data[ INTC_NR_ARB ]; x49gp_t* x49gp; uint32_t src_pending; uint32_t subsrc_pending; unsigned int nr_regs; s3c2410_offset_t* regs; } s3c2410_intc_t; static void s3c2410_intc_gen_int( s3c2410_intc_t* intc ); static void s3c2410_intc_gen_int_from_sub_int( s3c2410_intc_t* intc ); static int s3c2410_intc_data_init( s3c2410_intc_t* intc ) { int i; s3c2410_offset_t regs[] = { S3C2410_OFFSET( INTC, SRCPND, 0x00000000, intc->srcpnd ), S3C2410_OFFSET( INTC, INTMOD, 0x00000000, intc->intmod ), S3C2410_OFFSET( INTC, INTMSK, 0xffffffff, intc->intmsk ), S3C2410_OFFSET( INTC, PRIORITY, 0x0000007f, intc->priority ), S3C2410_OFFSET( INTC, INTPND, 0x00000000, intc->intpnd ), S3C2410_OFFSET( INTC, INTOFFSET, 0x00000000, intc->intoffset ), S3C2410_OFFSET( INTC, SUBSRCPND, 0x00000000, intc->subsrcpnd ), S3C2410_OFFSET( INTC, INTSUBMSK, 0x000007ff, intc->intsubmsk ) }; memset( intc, 0, sizeof( s3c2410_intc_t ) ); intc->regs = malloc( sizeof( regs ) ); if ( NULL == intc->regs ) { fprintf( stderr, "%s:%u: Out of memory\n", __FUNCTION__, __LINE__ ); return -ENOMEM; } memcpy( intc->regs, regs, sizeof( regs ) ); intc->nr_regs = sizeof( regs ) / sizeof( regs[ 0 ] ); for ( i = 0; i < INTC_NR_ARB; i++ ) { intc->arb_data[ i ].sel = 0; intc->arb_data[ i ].mode = s3c2410_arb_table[ i ].mode_bit; } return 0; } static void srcpnd_put_word( s3c2410_intc_t* intc, uint32_t data ) { intc->srcpnd &= ~( data ); intc->srcpnd |= intc->src_pending; if ( intc->src_pending & data ) { s3c2410_intc_gen_int( intc ); } } static void intmod_put_word( s3c2410_intc_t* intc, uint32_t data ) { intc->intmod = data & 0xfeffffbf; s3c2410_intc_gen_int( intc ); } static void intmsk_put_word( s3c2410_intc_t* intc, uint32_t data ) { #ifdef DEBUG_X49GP_ENABLE_IRQ uint32_t change; int i; change = intc->intmsk ^ data; #endif intc->intmsk = data | 0x01000040; #ifdef DEBUG_X49GP_ENABLE_IRQ for ( i = 0; i < 32; i++ ) { if ( ( change & ( 1 << i ) ) && !( intc->intmsk & ( 1 << i ) ) ) { printf( "INTC: Enable IRQ %u\n", i ); } } #endif s3c2410_intc_gen_int( intc ); } static uint32_t priority_get_word( s3c2410_intc_t* intc ) { const s3c2410_arb_t* arb; s3c2410_arb_data_t* arb_data; int i; intc->priority = 0; for ( i = 0; i < INTC_NR_ARB; i++ ) { arb = &s3c2410_arb_table[ i ]; arb_data = &intc->arb_data[ i ]; intc->priority |= ( arb_data->sel << arb->sel_shift ) | arb_data->mode; } return intc->priority; } static void priority_put_word( s3c2410_intc_t* intc, uint32_t data ) { const s3c2410_arb_t* arb; s3c2410_arb_data_t* arb_data; int i; intc->priority = data & 0x001fffff; for ( i = 0; i < INTC_NR_ARB; i++ ) { arb = &s3c2410_arb_table[ i ]; arb_data = &intc->arb_data[ i ]; arb_data->sel = ( intc->priority >> arb->sel_shift ) & ARBx_SEL_MASK; arb_data->mode = intc->priority & arb->mode_bit; } s3c2410_intc_gen_int( intc ); } static void intpnd_put_word( s3c2410_intc_t* intc, uint32_t data ) { intc->intpnd &= ~( data ); s3c2410_intc_gen_int( intc ); } static void subsrcpnd_put_word( s3c2410_intc_t* intc, uint32_t data ) { intc->subsrcpnd &= ~( data ); intc->subsrcpnd |= intc->subsrc_pending; if ( intc->subsrc_pending & data ) { s3c2410_intc_gen_int_from_sub_int( intc ); } } static void intsubmsk_put_word( s3c2410_intc_t* intc, uint32_t data ) { intc->intsubmsk = data & 0x000007ff; s3c2410_intc_gen_int_from_sub_int( intc ); } static uint32_t s3c2410_intc_select_int( s3c2410_intc_t* intc, const s3c2410_arb_t* arb, uint32_t service, int* offset ) { s3c2410_arb_data_t* arb_data = &intc->arb_data[ arb->index ]; const int* order; int i, req; order = s3c2410_arb_order[ arb_data->sel ]; for ( i = 0; i < 6; i++ ) { req = order[ i ]; if ( -1 == arb->req[ req ] ) continue; if ( service & ( 1 << arb->req[ req ] ) ) { if ( arb_data->mode ) arb_data->sel = ( arb_data->sel + 1 ) & ARBx_SEL_MASK; *offset = arb->req[ req ]; return ( 1 << arb->index ); } } *offset = -1; return 0; } void s3c2410_FIQ( CPUState* env ) { cpu_interrupt( env, CPU_INTERRUPT_FIQ ); } void s3c2410_IRQ( CPUState* env ) { cpu_interrupt( env, CPU_INTERRUPT_HARD ); } static void s3c2410_intc_gen_int( s3c2410_intc_t* intc ) { x49gp_t* x49gp = intc->x49gp; uint32_t fiq, service; int offset[ 6 ], index; const s3c2410_arb_t* arb; uint32_t request; int i; fiq = intc->srcpnd & intc->intmod; #ifdef DEBUG_S3C2410_INTC0 printf( "INTC: FIQ service request: %08x\n", fiq ); #endif if ( fiq ) { /* * Generate FIQ. */ #ifdef DEBUG_S3C2410_INTC printf( "INTC: vector to %08x\n", 0x1c ); #endif cpu_interrupt( x49gp->env, CPU_INTERRUPT_FIQ ); x49gp_set_idle( x49gp, 0 ); return; } else { cpu_reset_interrupt( x49gp->env, CPU_INTERRUPT_FIQ ); } #ifdef DEBUG_S3C2410_INTC0 printf( "INTC: IRQ pending request: %08x\n", intc->intpnd ); #endif if ( intc->intpnd ) { /* * Generate IRQ. */ #ifdef DEBUG_S3C2410_INTC printf( "INTC: vector to %08x\n", 0x18 ); #endif cpu_interrupt( x49gp->env, CPU_INTERRUPT_HARD ); x49gp_set_idle( x49gp, 0 ); return; } #ifdef DEBUG_S3C2410_INTC0 printf( "INTC: srcpnd %08x, intmsk: %08x\n", intc->srcpnd, intc->intmsk ); #endif service = intc->srcpnd & ~( intc->intmsk ); #ifdef DEBUG_S3C2410_INTC0 printf( "INTC: IRQ service request: %08x\n", service ); #endif if ( 0 == service ) { cpu_reset_interrupt( x49gp->env, CPU_INTERRUPT_HARD ); return; } request = 0; for ( i = 0; i < 6; i++ ) { arb = &s3c2410_arb_table[ i ]; request |= s3c2410_intc_select_int( intc, arb, service, &offset[ i ] ); #ifdef DEBUG_S3C2410_INTC0 printf( "INTC: ARB%u highest %d\n", i, offset[ i ] ); #endif } arb = &s3c2410_arb_table[ 6 ]; #ifdef DEBUG_S3C2410_INTC0 printf( "INTC: ARB%u request: %08x\n", 6, request ); #endif if ( s3c2410_intc_select_int( intc, arb, request, &index ) ) { intc->intoffset = offset[ index ]; intc->intpnd |= ( 1 << intc->intoffset ); #ifdef DEBUG_S3C2410_INTC printf( "INTC: irq pending: %u (%08x)\n", intc->intoffset, intc->intpnd ); #endif /* * Generate IRQ. */ #ifdef DEBUG_S3C2410_INTC printf( "INTC: vector to %08x\n", 0x18 ); #endif cpu_interrupt( x49gp->env, CPU_INTERRUPT_HARD ); x49gp_set_idle( x49gp, 0 ); return; } #ifdef DEBUG_S3C2410_INTC0 printf( "INTC: No irq pending\n" ); #endif cpu_reset_interrupt( x49gp->env, CPU_INTERRUPT_HARD ); } void s3c2410_intc_assert( x49gp_t* x49gp, int irq, int level ) { s3c2410_intc_t* intc = x49gp->s3c2410_intc; if ( irq > 31 ) return; #ifdef DEBUG_S3C2410_INTC printf( "INTC: assert irq %u (%08x)\n", irq, 1 << irq ); #endif if ( !( intc->src_pending & ( 1 << irq ) ) ) { if ( level ) intc->src_pending |= ( 1 << irq ); intc->srcpnd |= ( 1 << irq ); s3c2410_intc_gen_int( intc ); } if ( x49gp->arm_idle == 2 ) { if ( irq == EINT0 || irq == INT_RTC ) x49gp_set_idle( x49gp, 0 ); } } void s3c2410_intc_deassert( x49gp_t* x49gp, int irq ) { s3c2410_intc_t* intc = x49gp->s3c2410_intc; if ( irq > 31 ) return; #ifdef DEBUG_S3C2410_INTC printf( "INTC: deassert irq %u (%08x)\n", irq, 1 << irq ); #endif intc->src_pending &= ~( 1 << irq ); } static void s3c2410_intc_gen_int_from_sub_int( s3c2410_intc_t* intc ) { x49gp_t* x49gp = intc->x49gp; uint32_t service; service = intc->subsrcpnd & ~( intc->intsubmsk ); #ifdef DEBUG_S3C2410_INTC printf( "INTC: subirq service request: %08x\n", service ); #endif if ( service & ( ( 1 << SUB_INT_ERR0 ) | ( 1 << SUB_INT_TXD0 ) | ( 1 << SUB_INT_RXD0 ) ) ) { s3c2410_intc_assert( x49gp, INT_UART0, 1 ); } else { s3c2410_intc_deassert( x49gp, INT_UART0 ); } if ( service & ( ( 1 << SUB_INT_ERR1 ) | ( 1 << SUB_INT_TXD1 ) | ( 1 << SUB_INT_RXD1 ) ) ) { s3c2410_intc_assert( x49gp, INT_UART1, 1 ); } else { s3c2410_intc_deassert( x49gp, INT_UART1 ); } if ( service & ( ( 1 << SUB_INT_ERR2 ) | ( 1 << SUB_INT_TXD2 ) | ( 1 << SUB_INT_RXD2 ) ) ) { s3c2410_intc_assert( x49gp, INT_UART2, 1 ); } else { s3c2410_intc_deassert( x49gp, INT_UART2 ); } if ( service & ( ( 1 << SUB_INT_ADC ) | ( 1 << SUB_INT_TC ) ) ) { s3c2410_intc_assert( x49gp, INT_ADC, 1 ); } else { s3c2410_intc_deassert( x49gp, INT_ADC ); } intc->subsrcpnd = intc->subsrc_pending; } void s3c2410_intc_sub_assert( x49gp_t* x49gp, int sub_irq, int level ) { s3c2410_intc_t* intc = x49gp->s3c2410_intc; if ( sub_irq > 31 ) return; #ifdef DEBUG_S3C2410_INTC printf( "INTC: assert subirq %u (%08x)\n", sub_irq, 1 << sub_irq ); #endif if ( !( intc->subsrc_pending & ( 1 << sub_irq ) ) ) { if ( level ) intc->subsrc_pending |= ( 1 << sub_irq ); intc->subsrcpnd |= ( 1 << sub_irq ); s3c2410_intc_gen_int_from_sub_int( intc ); } } void s3c2410_intc_sub_deassert( x49gp_t* x49gp, int sub_irq ) { s3c2410_intc_t* intc = x49gp->s3c2410_intc; if ( sub_irq > 31 ) return; #ifdef DEBUG_S3C2410_INTC printf( "INTC: deassert subirq %u (%08x)\n", sub_irq, 1 << sub_irq ); #endif intc->subsrc_pending &= ~( 1 << sub_irq ); } static uint32_t s3c2410_intc_read( void* opaque, target_phys_addr_t offset ) { s3c2410_intc_t* intc = opaque; s3c2410_offset_t* reg; uint32_t data; if ( !S3C2410_OFFSET_OK( intc, offset ) ) { return ~( 0 ); } reg = S3C2410_OFFSET_ENTRY( intc, offset ); switch ( offset ) { case S3C2410_INTC_PRIORITY: data = priority_get_word( intc ); break; default: data = *( reg->datap ); break; } #ifdef DEBUG_S3C2410_INTC printf( "read %s [%08x] %s [%08lx] data %08x\n", "s3c2410-intc", S3C2410_INTC_BASE, reg->name, ( unsigned long )offset, data ); #endif return data; } static void s3c2410_intc_write( void* opaque, target_phys_addr_t offset, uint32_t data ) { #ifdef DEBUG_S3C2410_INTC s3c2410_offset_t* reg; #endif s3c2410_intc_t* intc = opaque; if ( !S3C2410_OFFSET_OK( intc, offset ) ) { return; } #ifdef DEBUG_S3C2410_INTC reg = S3C2410_OFFSET_ENTRY( intc, offset ); printf( "write %s [%08x] %s [%08lx] data %08x\n", "s3c2410-intc", S3C2410_INTC_BASE, reg->name, ( unsigned long )offset, data ); #endif switch ( offset ) { case S3C2410_INTC_SRCPND: srcpnd_put_word( intc, data ); break; case S3C2410_INTC_INTMOD: intmod_put_word( intc, data ); break; case S3C2410_INTC_INTMSK: intmsk_put_word( intc, data ); break; case S3C2410_INTC_PRIORITY: priority_put_word( intc, data ); break; case S3C2410_INTC_INTPND: intpnd_put_word( intc, data ); break; case S3C2410_INTC_SUBSRCPND: subsrcpnd_put_word( intc, data ); break; case S3C2410_INTC_INTSUBMSK: intsubmsk_put_word( intc, data ); break; default: break; } } static int s3c2410_intc_load( x49gp_module_t* module, GKeyFile* key ) { s3c2410_intc_t* intc = 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 < intc->nr_regs; i++ ) { reg = &intc->regs[ i ]; if ( NULL == reg->name ) continue; if ( x49gp_module_get_u32( module, key, reg->name, reg->reset, reg->datap ) ) error = -EAGAIN; } intc->src_pending = intc->srcpnd; intc->subsrc_pending = intc->subsrcpnd; intc->srcpnd = 0; intc->subsrcpnd = 0; priority_put_word( intc, intc->priority ); intsubmsk_put_word( intc, intc->intsubmsk ); intmsk_put_word( intc, intc->intmsk ); return error; } static int s3c2410_intc_save( x49gp_module_t* module, GKeyFile* key ) { s3c2410_intc_t* intc = module->user_data; s3c2410_offset_t* reg; int i; #ifdef DEBUG_X49GP_MODULES printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ ); #endif intc->srcpnd = intc->src_pending; intc->subsrcpnd = intc->subsrc_pending; for ( i = 0; i < intc->nr_regs; i++ ) { reg = &intc->regs[ i ]; if ( NULL == reg->name ) continue; x49gp_module_set_u32( module, key, reg->name, *( reg->datap ) ); } return 0; } static int s3c2410_intc_reset( x49gp_module_t* module, x49gp_reset_t reset ) { s3c2410_intc_t* intc = module->user_data; s3c2410_offset_t* reg; int i; #ifdef DEBUG_X49GP_MODULES printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ ); #endif if ( reset == X49GP_RESET_POWER_OFF ) { return 0; } for ( i = 0; i < intc->nr_regs; i++ ) { reg = &intc->regs[ i ]; if ( NULL == reg->name ) continue; *( reg->datap ) = reg->reset; } return 0; } static CPUReadMemoryFunc* s3c2410_intc_readfn[] = { s3c2410_intc_read, s3c2410_intc_read, s3c2410_intc_read }; static CPUWriteMemoryFunc* s3c2410_intc_writefn[] = { s3c2410_intc_write, s3c2410_intc_write, s3c2410_intc_write }; static int s3c2410_intc_init( x49gp_module_t* module ) { s3c2410_intc_t* intc; int iotype; #ifdef DEBUG_X49GP_MODULES printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ ); #endif intc = malloc( sizeof( s3c2410_intc_t ) ); if ( NULL == intc ) { fprintf( stderr, "%s:%u: Out of memory\n", __FUNCTION__, __LINE__ ); return -ENOMEM; } if ( s3c2410_intc_data_init( intc ) ) { free( intc ); return -ENOMEM; } module->user_data = intc; intc->x49gp = module->x49gp; intc->x49gp->s3c2410_intc = intc; iotype = cpu_register_io_memory( s3c2410_intc_readfn, s3c2410_intc_writefn, intc ); #ifdef DEBUG_S3C2410_INTC printf( "%s: iotype %08x\n", __FUNCTION__, iotype ); #endif cpu_register_physical_memory( S3C2410_INTC_BASE, S3C2410_MAP_SIZE, iotype ); return 0; } static int s3c2410_intc_exit( x49gp_module_t* module ) { s3c2410_intc_t* intc; #ifdef DEBUG_X49GP_MODULES printf( "%s: %s:%u\n", module->name, __FUNCTION__, __LINE__ ); #endif if ( module->user_data ) { intc = module->user_data; if ( intc->regs ) free( intc->regs ); free( intc ); } x49gp_module_unregister( module ); free( module ); return 0; } int x49gp_s3c2410_intc_init( x49gp_t* x49gp ) { x49gp_module_t* module; if ( x49gp_module_init( x49gp, "s3c2410-intc", s3c2410_intc_init, s3c2410_intc_exit, s3c2410_intc_reset, s3c2410_intc_load, s3c2410_intc_save, NULL, &module ) ) { return -1; } return x49gp_module_register( module ); }