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

679 lines
17 KiB
C

/* $Id: s3c2410_intc.c,v 1.8 2008/12/11 12:18:17 ecd Exp $
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#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 );
}