filename | src/sh4/intc.c |
changeset | 20:3ffb66aa25c7 |
prev | 19:9da7a8e38f9d |
next | 31:495e480360d7 |
author | nkeynes |
date | Thu Dec 22 13:28:16 2005 +0000 (18 years ago) |
permissions | -rw-r--r-- |
last change | Add scif.c (oops) Convert interrupts to be level-triggered rather than edge-triggered (although shouldn't be any visible difference) |
file | annotate | diff | log | raw |
nkeynes@1 | 1 | #include <assert.h> |
nkeynes@1 | 2 | #include "sh4mmio.h" |
nkeynes@1 | 3 | #include "sh4core.h" |
nkeynes@1 | 4 | #include "intc.h" |
nkeynes@1 | 5 | |
nkeynes@1 | 6 | int priorities[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; |
nkeynes@1 | 7 | |
nkeynes@1 | 8 | struct intc_sources_t { |
nkeynes@1 | 9 | char *name; |
nkeynes@1 | 10 | uint32_t code; |
nkeynes@1 | 11 | int priority; |
nkeynes@1 | 12 | }; |
nkeynes@1 | 13 | |
nkeynes@1 | 14 | #define PRIORITY(which) intc_sources[which].priority |
nkeynes@1 | 15 | #define INTCODE(which) intc_sources[which].code |
nkeynes@1 | 16 | |
nkeynes@1 | 17 | static struct intc_sources_t intc_sources[] = { |
nkeynes@1 | 18 | { "IRQ0", 0x200, 15 }, { "IRQ1", 0x220, 14 }, { "IRQ2", 0x240, 13 }, |
nkeynes@1 | 19 | { "IRQ3", 0x260, 12 }, { "IRQ4", 0x280, 11 }, { "IRQ5", 0x2A0, 10 }, |
nkeynes@1 | 20 | { "IRQ6", 0x2C0, 9 }, { "IRQ7", 0x2E0, 8 }, { "IRQ8", 0x300, 7 }, |
nkeynes@1 | 21 | { "IRQ9", 0x320, 6 }, { "IRQ10",0x340, 5 }, { "IRQ11",0x360, 4 }, |
nkeynes@1 | 22 | { "IRQ12",0x380, 3 }, { "IRQ13",0x3A0, 2 }, { "IRQ14",0x3C0, 1 }, |
nkeynes@1 | 23 | { "NMI", 0x1C0, 16 }, { "H-UDI",0x600, 0 }, { "GPIOI",0x620, 0 }, |
nkeynes@1 | 24 | { "DMTE0",0x640, 0 }, { "DMTE1",0x660, 0 }, { "DMTE2",0x680, 0 }, |
nkeynes@1 | 25 | { "DMTE3",0x6A0, 0 }, { "DMTAE",0x6C0, 0 }, { "TUNI0",0x400, 0 }, |
nkeynes@1 | 26 | { "TUNI1",0x420, 0 }, { "TUNI2",0x440, 0 }, { "TICPI2",0x460, 0 }, |
nkeynes@1 | 27 | { "RTC_ATI",0x480, 0 },{ "RTC_PRI",0x4A0, 0 },{ "RTC_CUI",0x4C0, 0 }, |
nkeynes@1 | 28 | { "SCI_ERI",0x4E0, 0 },{ "SCI_RXI",0x500, 0 },{ "SCI_TXI",0x520, 0 }, |
nkeynes@1 | 29 | { "SCI_TEI",0x540, 0 }, |
nkeynes@1 | 30 | { "SCIF_ERI",0x700, 0 },{ "SCIF_RXI",0x720, 0 },{ "SCIF_BRI",0x740, 0 }, |
nkeynes@1 | 31 | { "SCIF_TXI",0x760, 0 }, |
nkeynes@1 | 32 | { "WDT_ITI",0x560, 0 },{ "RCMI",0x580, 0 }, { "ROVI",0x5A0, 0 } }; |
nkeynes@1 | 33 | |
nkeynes@1 | 34 | int intc_pending[INT_NUM_SOURCES]; |
nkeynes@1 | 35 | int intc_num_pending = 0; |
nkeynes@1 | 36 | |
nkeynes@1 | 37 | void mmio_region_INTC_write( uint32_t reg, uint32_t val ) |
nkeynes@1 | 38 | { |
nkeynes@1 | 39 | /* Well it saves having to use an intermediate table... */ |
nkeynes@1 | 40 | switch( reg ) { |
nkeynes@1 | 41 | case ICR: /* care about this later */ |
nkeynes@1 | 42 | break; |
nkeynes@1 | 43 | case IPRA: |
nkeynes@1 | 44 | PRIORITY(INT_TMU_TUNI0) = (val>>12)&0x000F; |
nkeynes@1 | 45 | PRIORITY(INT_TMU_TUNI1) = (val>>8)&0x000F; |
nkeynes@1 | 46 | PRIORITY(INT_TMU_TUNI2) = |
nkeynes@1 | 47 | PRIORITY(INT_TMU_TICPI2) = (val>>4)&0x000F; |
nkeynes@1 | 48 | PRIORITY(INT_RTC_ATI) = |
nkeynes@1 | 49 | PRIORITY(INT_RTC_PRI) = |
nkeynes@1 | 50 | PRIORITY(INT_RTC_CUI) = val&0x000F; |
nkeynes@1 | 51 | break; |
nkeynes@1 | 52 | case IPRB: |
nkeynes@1 | 53 | PRIORITY(INT_WDT_ITI) = (val>>12)&0x000F; |
nkeynes@1 | 54 | PRIORITY(INT_REF_RCMI) = |
nkeynes@1 | 55 | PRIORITY(INT_REF_ROVI) = (val>>8)&0x000F; |
nkeynes@1 | 56 | PRIORITY(INT_SCI_ERI) = |
nkeynes@1 | 57 | PRIORITY(INT_SCI_RXI) = |
nkeynes@1 | 58 | PRIORITY(INT_SCI_TXI) = |
nkeynes@1 | 59 | PRIORITY(INT_SCI_TEI) = (val>>4)&0x000F; |
nkeynes@1 | 60 | /* Bits 0-3 reserved */ |
nkeynes@1 | 61 | break; |
nkeynes@1 | 62 | case IPRC: |
nkeynes@1 | 63 | PRIORITY(INT_GPIO) = (val>>12)&0x000F; |
nkeynes@1 | 64 | PRIORITY(INT_DMA_DMTE0) = |
nkeynes@1 | 65 | PRIORITY(INT_DMA_DMTE1) = |
nkeynes@1 | 66 | PRIORITY(INT_DMA_DMTE2) = |
nkeynes@1 | 67 | PRIORITY(INT_DMA_DMTE3) = |
nkeynes@1 | 68 | PRIORITY(INT_DMA_DMAE) = (val>>8)&0x000F; |
nkeynes@1 | 69 | PRIORITY(INT_SCIF_ERI) = |
nkeynes@1 | 70 | PRIORITY(INT_SCIF_RXI) = |
nkeynes@1 | 71 | PRIORITY(INT_SCIF_BRI) = |
nkeynes@1 | 72 | PRIORITY(INT_SCIF_TXI) = (val>>4)&0x000F; |
nkeynes@1 | 73 | PRIORITY(INT_HUDI) = val&0x000F; |
nkeynes@1 | 74 | break; |
nkeynes@1 | 75 | } |
nkeynes@1 | 76 | MMIO_WRITE( INTC, reg, val ); |
nkeynes@1 | 77 | } |
nkeynes@1 | 78 | |
nkeynes@1 | 79 | int32_t mmio_region_INTC_read( uint32_t reg ) |
nkeynes@1 | 80 | { |
nkeynes@1 | 81 | return MMIO_READ( INTC, reg ); |
nkeynes@1 | 82 | } |
nkeynes@1 | 83 | |
nkeynes@1 | 84 | /* We basically maintain a priority queue here, raise_interrupt adds an entry, |
nkeynes@1 | 85 | * accept_interrupt takes it off. At the moment this is does as a simple |
nkeynes@1 | 86 | * ordered array, on the basis that in practice there's unlikely to be more |
nkeynes@1 | 87 | * than one at a time. There are lots of ways to optimize this if it turns out |
nkeynes@1 | 88 | * to be necessary, but I'd doubt it will be... |
nkeynes@1 | 89 | */ |
nkeynes@1 | 90 | |
nkeynes@1 | 91 | void intc_raise_interrupt( int which ) |
nkeynes@1 | 92 | { |
nkeynes@1 | 93 | int i, j, pri; |
nkeynes@1 | 94 | |
nkeynes@1 | 95 | pri = PRIORITY(which); |
nkeynes@1 | 96 | if( pri == 0 ) return; /* masked off */ |
nkeynes@1 | 97 | |
nkeynes@1 | 98 | for( i=0; i<intc_num_pending; i++ ) { |
nkeynes@1 | 99 | if( intc_pending[i] == which ) return; /* Don't queue more than once */ |
nkeynes@1 | 100 | if( PRIORITY(intc_pending[i]) > pri || |
nkeynes@1 | 101 | (PRIORITY(intc_pending[i]) == pri && |
nkeynes@1 | 102 | intc_pending[i] < which)) |
nkeynes@1 | 103 | break; |
nkeynes@1 | 104 | } |
nkeynes@1 | 105 | /* i == insertion point */ |
nkeynes@1 | 106 | for( j=intc_num_pending; j > i; j-- ) |
nkeynes@1 | 107 | intc_pending[j] = intc_pending[j-1]; |
nkeynes@1 | 108 | intc_pending[i] = which; |
nkeynes@1 | 109 | |
nkeynes@1 | 110 | if( i == intc_num_pending && (sh4r.sr&SR_BL)==0 && SH4_INTMASK() < pri ) |
nkeynes@1 | 111 | sh4r.int_pending = 1; |
nkeynes@1 | 112 | |
nkeynes@1 | 113 | intc_num_pending++; |
nkeynes@1 | 114 | } |
nkeynes@1 | 115 | |
nkeynes@19 | 116 | void intc_clear_interrupt( int which ) |
nkeynes@19 | 117 | { |
nkeynes@20 | 118 | int i; |
nkeynes@20 | 119 | for( i=intc_num_pending-1; i>=0; i-- ) { |
nkeynes@20 | 120 | if( intc_pending[i] == which ) { |
nkeynes@20 | 121 | /* Shift array contents down */ |
nkeynes@20 | 122 | while( i < intc_num_pending-1 ) { |
nkeynes@20 | 123 | intc_pending[i] = intc_pending[++i]; |
nkeynes@20 | 124 | } |
nkeynes@20 | 125 | intc_num_pending--; |
nkeynes@20 | 126 | if( intc_num_pending == 0 ) |
nkeynes@20 | 127 | sh4r.int_pending = 0; |
nkeynes@20 | 128 | else |
nkeynes@20 | 129 | sh4r.int_pending = PRIORITY(intc_pending[intc_num_pending-1]); |
nkeynes@20 | 130 | break; |
nkeynes@20 | 131 | } |
nkeynes@20 | 132 | } |
nkeynes@20 | 133 | |
nkeynes@19 | 134 | } |
nkeynes@19 | 135 | |
nkeynes@1 | 136 | uint32_t intc_accept_interrupt( void ) |
nkeynes@1 | 137 | { |
nkeynes@1 | 138 | assert(intc_num_pending > 0); |
nkeynes@20 | 139 | return INTCODE(intc_pending[intc_num_pending-1]); |
nkeynes@20 | 140 | /* |
nkeynes@1 | 141 | intc_num_pending--; |
nkeynes@1 | 142 | if( intc_num_pending > 0 ) |
nkeynes@1 | 143 | sh4r.int_pending = PRIORITY(intc_pending[intc_num_pending-1]); |
nkeynes@1 | 144 | else |
nkeynes@1 | 145 | sh4r.int_pending = 0; |
nkeynes@1 | 146 | return INTCODE(intc_pending[intc_num_pending]); |
nkeynes@20 | 147 | */ |
nkeynes@1 | 148 | } |
nkeynes@1 | 149 | |
nkeynes@1 | 150 | void intc_mask_changed( void ) |
nkeynes@1 | 151 | { |
nkeynes@1 | 152 | if( intc_num_pending > 0 && (sh4r.sr&SR_BL)==0 && |
nkeynes@1 | 153 | SH4_INTMASK() < PRIORITY(intc_pending[intc_num_pending-1]) ) |
nkeynes@1 | 154 | sh4r.int_pending = 1; |
nkeynes@1 | 155 | else sh4r.int_pending = 0; |
nkeynes@1 | 156 | } |
nkeynes@1 | 157 | |
nkeynes@1 | 158 | |
nkeynes@1 | 159 | char *intc_get_interrupt_name( int code ) |
nkeynes@1 | 160 | { |
nkeynes@1 | 161 | return intc_sources[code].name; |
nkeynes@1 | 162 | } |
nkeynes@1 | 163 | |
nkeynes@1 | 164 | void intc_reset( void ) |
nkeynes@1 | 165 | { |
nkeynes@1 | 166 | intc_num_pending = 0; |
nkeynes@1 | 167 | sh4r.int_pending = 0; |
nkeynes@1 | 168 | } |
.