nkeynes@31: /** nkeynes@114: * $Id: intc.c,v 1.5 2006-03-17 12:12:49 nkeynes Exp $ nkeynes@31: * nkeynes@31: * SH4 onboard interrupt controller (INTC) implementation nkeynes@31: * nkeynes@31: * Copyright (c) 2005 Nathan Keynes. nkeynes@31: * nkeynes@31: * This program is free software; you can redistribute it and/or modify nkeynes@31: * it under the terms of the GNU General Public License as published by nkeynes@31: * the Free Software Foundation; either version 2 of the License, or nkeynes@31: * (at your option) any later version. nkeynes@31: * nkeynes@31: * This program is distributed in the hope that it will be useful, nkeynes@31: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@31: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@31: * GNU General Public License for more details. nkeynes@31: */ nkeynes@31: nkeynes@1: #include nkeynes@1: #include "sh4mmio.h" nkeynes@1: #include "sh4core.h" nkeynes@1: #include "intc.h" nkeynes@1: nkeynes@1: int priorities[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; nkeynes@1: nkeynes@1: struct intc_sources_t { nkeynes@1: char *name; nkeynes@1: uint32_t code; nkeynes@1: int priority; nkeynes@1: }; nkeynes@1: nkeynes@1: #define PRIORITY(which) intc_sources[which].priority nkeynes@1: #define INTCODE(which) intc_sources[which].code nkeynes@1: nkeynes@1: static struct intc_sources_t intc_sources[] = { nkeynes@1: { "IRQ0", 0x200, 15 }, { "IRQ1", 0x220, 14 }, { "IRQ2", 0x240, 13 }, nkeynes@1: { "IRQ3", 0x260, 12 }, { "IRQ4", 0x280, 11 }, { "IRQ5", 0x2A0, 10 }, nkeynes@1: { "IRQ6", 0x2C0, 9 }, { "IRQ7", 0x2E0, 8 }, { "IRQ8", 0x300, 7 }, nkeynes@1: { "IRQ9", 0x320, 6 }, { "IRQ10",0x340, 5 }, { "IRQ11",0x360, 4 }, nkeynes@1: { "IRQ12",0x380, 3 }, { "IRQ13",0x3A0, 2 }, { "IRQ14",0x3C0, 1 }, nkeynes@1: { "NMI", 0x1C0, 16 }, { "H-UDI",0x600, 0 }, { "GPIOI",0x620, 0 }, nkeynes@1: { "DMTE0",0x640, 0 }, { "DMTE1",0x660, 0 }, { "DMTE2",0x680, 0 }, nkeynes@1: { "DMTE3",0x6A0, 0 }, { "DMTAE",0x6C0, 0 }, { "TUNI0",0x400, 0 }, nkeynes@1: { "TUNI1",0x420, 0 }, { "TUNI2",0x440, 0 }, { "TICPI2",0x460, 0 }, nkeynes@1: { "RTC_ATI",0x480, 0 },{ "RTC_PRI",0x4A0, 0 },{ "RTC_CUI",0x4C0, 0 }, nkeynes@1: { "SCI_ERI",0x4E0, 0 },{ "SCI_RXI",0x500, 0 },{ "SCI_TXI",0x520, 0 }, nkeynes@1: { "SCI_TEI",0x540, 0 }, nkeynes@1: { "SCIF_ERI",0x700, 0 },{ "SCIF_RXI",0x720, 0 },{ "SCIF_BRI",0x740, 0 }, nkeynes@1: { "SCIF_TXI",0x760, 0 }, nkeynes@1: { "WDT_ITI",0x560, 0 },{ "RCMI",0x580, 0 }, { "ROVI",0x5A0, 0 } }; nkeynes@1: nkeynes@1: int intc_pending[INT_NUM_SOURCES]; nkeynes@1: int intc_num_pending = 0; nkeynes@1: nkeynes@1: void mmio_region_INTC_write( uint32_t reg, uint32_t val ) nkeynes@1: { nkeynes@1: /* Well it saves having to use an intermediate table... */ nkeynes@1: switch( reg ) { nkeynes@1: case ICR: /* care about this later */ nkeynes@1: break; nkeynes@1: case IPRA: nkeynes@1: PRIORITY(INT_TMU_TUNI0) = (val>>12)&0x000F; nkeynes@1: PRIORITY(INT_TMU_TUNI1) = (val>>8)&0x000F; nkeynes@1: PRIORITY(INT_TMU_TUNI2) = nkeynes@1: PRIORITY(INT_TMU_TICPI2) = (val>>4)&0x000F; nkeynes@1: PRIORITY(INT_RTC_ATI) = nkeynes@1: PRIORITY(INT_RTC_PRI) = nkeynes@1: PRIORITY(INT_RTC_CUI) = val&0x000F; nkeynes@1: break; nkeynes@1: case IPRB: nkeynes@1: PRIORITY(INT_WDT_ITI) = (val>>12)&0x000F; nkeynes@1: PRIORITY(INT_REF_RCMI) = nkeynes@1: PRIORITY(INT_REF_ROVI) = (val>>8)&0x000F; nkeynes@1: PRIORITY(INT_SCI_ERI) = nkeynes@1: PRIORITY(INT_SCI_RXI) = nkeynes@1: PRIORITY(INT_SCI_TXI) = nkeynes@1: PRIORITY(INT_SCI_TEI) = (val>>4)&0x000F; nkeynes@1: /* Bits 0-3 reserved */ nkeynes@1: break; nkeynes@1: case IPRC: nkeynes@1: PRIORITY(INT_GPIO) = (val>>12)&0x000F; nkeynes@1: PRIORITY(INT_DMA_DMTE0) = nkeynes@1: PRIORITY(INT_DMA_DMTE1) = nkeynes@1: PRIORITY(INT_DMA_DMTE2) = nkeynes@1: PRIORITY(INT_DMA_DMTE3) = nkeynes@1: PRIORITY(INT_DMA_DMAE) = (val>>8)&0x000F; nkeynes@1: PRIORITY(INT_SCIF_ERI) = nkeynes@1: PRIORITY(INT_SCIF_RXI) = nkeynes@1: PRIORITY(INT_SCIF_BRI) = nkeynes@1: PRIORITY(INT_SCIF_TXI) = (val>>4)&0x000F; nkeynes@1: PRIORITY(INT_HUDI) = val&0x000F; nkeynes@1: break; nkeynes@1: } nkeynes@1: MMIO_WRITE( INTC, reg, val ); nkeynes@1: } nkeynes@1: nkeynes@1: int32_t mmio_region_INTC_read( uint32_t reg ) nkeynes@1: { nkeynes@1: return MMIO_READ( INTC, reg ); nkeynes@1: } nkeynes@1: nkeynes@1: /* We basically maintain a priority queue here, raise_interrupt adds an entry, nkeynes@1: * accept_interrupt takes it off. At the moment this is does as a simple nkeynes@1: * ordered array, on the basis that in practice there's unlikely to be more nkeynes@1: * than one at a time. There are lots of ways to optimize this if it turns out nkeynes@1: * to be necessary, but I'd doubt it will be... nkeynes@1: */ nkeynes@1: nkeynes@1: void intc_raise_interrupt( int which ) nkeynes@1: { nkeynes@1: int i, j, pri; nkeynes@1: nkeynes@1: pri = PRIORITY(which); nkeynes@1: if( pri == 0 ) return; /* masked off */ nkeynes@1: nkeynes@1: for( i=0; i pri || nkeynes@1: (PRIORITY(intc_pending[i]) == pri && nkeynes@1: intc_pending[i] < which)) nkeynes@1: break; nkeynes@1: } nkeynes@1: /* i == insertion point */ nkeynes@1: for( j=intc_num_pending; j > i; j-- ) nkeynes@1: intc_pending[j] = intc_pending[j-1]; nkeynes@1: intc_pending[i] = which; nkeynes@1: nkeynes@1: if( i == intc_num_pending && (sh4r.sr&SR_BL)==0 && SH4_INTMASK() < pri ) nkeynes@1: sh4r.int_pending = 1; nkeynes@1: nkeynes@1: intc_num_pending++; nkeynes@1: } nkeynes@1: nkeynes@19: void intc_clear_interrupt( int which ) nkeynes@19: { nkeynes@20: int i; nkeynes@20: for( i=intc_num_pending-1; i>=0; i-- ) { nkeynes@20: if( intc_pending[i] == which ) { nkeynes@20: /* Shift array contents down */ nkeynes@20: while( i < intc_num_pending-1 ) { nkeynes@20: intc_pending[i] = intc_pending[++i]; nkeynes@20: } nkeynes@20: intc_num_pending--; nkeynes@114: intc_mask_changed(); nkeynes@20: break; nkeynes@20: } nkeynes@20: } nkeynes@20: nkeynes@19: } nkeynes@19: nkeynes@1: uint32_t intc_accept_interrupt( void ) nkeynes@1: { nkeynes@1: assert(intc_num_pending > 0); nkeynes@20: return INTCODE(intc_pending[intc_num_pending-1]); nkeynes@1: } nkeynes@1: nkeynes@1: void intc_mask_changed( void ) nkeynes@1: { nkeynes@1: if( intc_num_pending > 0 && (sh4r.sr&SR_BL)==0 && nkeynes@1: SH4_INTMASK() < PRIORITY(intc_pending[intc_num_pending-1]) ) nkeynes@1: sh4r.int_pending = 1; nkeynes@1: else sh4r.int_pending = 0; nkeynes@1: } nkeynes@1: nkeynes@1: nkeynes@1: char *intc_get_interrupt_name( int code ) nkeynes@1: { nkeynes@1: return intc_sources[code].name; nkeynes@1: } nkeynes@1: nkeynes@1: void intc_reset( void ) nkeynes@1: { nkeynes@1: intc_num_pending = 0; nkeynes@1: sh4r.int_pending = 0; nkeynes@1: }