filename | src/sh4/intc.c |
changeset | 1187:266e7a1bae90 |
prev | 975:007bf7eb944f |
author | nkeynes |
date | Mon Feb 13 20:00:27 2012 +1000 (12 years ago) |
permissions | -rw-r--r-- |
last change | Fix MMU on non-translated platforms - reintroduce old VMA translation functions (slightly modified) - modify shadow processing to work on post-translated memory ops |
file | annotate | diff | log | raw |
nkeynes@31 | 1 | /** |
nkeynes@561 | 2 | * $Id$ |
nkeynes@31 | 3 | * |
nkeynes@31 | 4 | * SH4 onboard interrupt controller (INTC) implementation |
nkeynes@31 | 5 | * |
nkeynes@31 | 6 | * Copyright (c) 2005 Nathan Keynes. |
nkeynes@31 | 7 | * |
nkeynes@31 | 8 | * This program is free software; you can redistribute it and/or modify |
nkeynes@31 | 9 | * it under the terms of the GNU General Public License as published by |
nkeynes@31 | 10 | * the Free Software Foundation; either version 2 of the License, or |
nkeynes@31 | 11 | * (at your option) any later version. |
nkeynes@31 | 12 | * |
nkeynes@31 | 13 | * This program is distributed in the hope that it will be useful, |
nkeynes@31 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
nkeynes@31 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
nkeynes@31 | 16 | * GNU General Public License for more details. |
nkeynes@31 | 17 | */ |
nkeynes@31 | 18 | |
nkeynes@1 | 19 | #include <assert.h> |
nkeynes@1 | 20 | #include "sh4mmio.h" |
nkeynes@1 | 21 | #include "sh4core.h" |
nkeynes@1 | 22 | #include "intc.h" |
nkeynes@265 | 23 | #include "eventq.h" |
nkeynes@1 | 24 | |
nkeynes@1 | 25 | struct intc_sources_t { |
nkeynes@1 | 26 | char *name; |
nkeynes@1 | 27 | uint32_t code; |
nkeynes@157 | 28 | } intc_sources[INT_NUM_SOURCES] = { |
nkeynes@736 | 29 | { "IRQ0", 0x200 }, { "IRQ1", 0x220 }, { "IRQ2", 0x240 }, |
nkeynes@736 | 30 | { "IRQ3", 0x260 }, { "IRQ4", 0x280 }, { "IRQ5", 0x2A0 }, |
nkeynes@736 | 31 | { "IRQ6", 0x2C0 }, { "IRQ7", 0x2E0 }, { "IRQ8", 0x300 }, |
nkeynes@736 | 32 | { "IRQ9", 0x320 }, { "IRQ10",0x340 }, { "IRQ11",0x360 }, |
nkeynes@736 | 33 | { "IRQ12",0x380 }, { "IRQ13",0x3A0 }, { "IRQ14",0x3C0 }, |
nkeynes@736 | 34 | { "NMI", 0x1C0 }, { "H-UDI",0x600 }, { "GPIOI",0x620 }, |
nkeynes@736 | 35 | { "DMTE0",0x640 }, { "DMTE1",0x660 }, { "DMTE2",0x680 }, |
nkeynes@736 | 36 | { "DMTE3",0x6A0 }, { "DMTAE",0x6C0 }, { "TUNI0",0x400 }, |
nkeynes@736 | 37 | { "TUNI1",0x420 }, { "TUNI2",0x440 }, { "TICPI2",0x460 }, |
nkeynes@736 | 38 | { "RTC_ATI",0x480 },{ "RTC_PRI",0x4A0 },{ "RTC_CUI",0x4C0 }, |
nkeynes@736 | 39 | { "SCI_ERI",0x4E0 },{ "SCI_RXI",0x500 },{ "SCI_TXI",0x520 }, |
nkeynes@736 | 40 | { "SCI_TEI",0x540 }, |
nkeynes@736 | 41 | { "SCIF_ERI",0x700 },{ "SCIF_RXI",0x720 },{ "SCIF_BRI",0x740 }, |
nkeynes@736 | 42 | { "SCIF_TXI",0x760 }, |
nkeynes@736 | 43 | { "WDT_ITI",0x560 },{ "RCMI",0x580 }, { "ROVI",0x5A0 } }; |
nkeynes@1 | 44 | |
nkeynes@157 | 45 | static int intc_default_priority[INT_NUM_SOURCES] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 16 }; |
nkeynes@157 | 46 | |
nkeynes@157 | 47 | #define PRIORITY(which) intc_state.priority[which] |
nkeynes@1 | 48 | #define INTCODE(which) intc_sources[which].code |
nkeynes@1 | 49 | |
nkeynes@157 | 50 | static struct intc_state { |
nkeynes@157 | 51 | int num_pending; |
nkeynes@157 | 52 | int pending[INT_NUM_SOURCES]; |
nkeynes@157 | 53 | int priority[INT_NUM_SOURCES]; |
nkeynes@157 | 54 | } intc_state; |
nkeynes@1 | 55 | |
nkeynes@929 | 56 | MMIO_REGION_WRITE_FN( INTC, reg, val ) |
nkeynes@1 | 57 | { |
nkeynes@929 | 58 | reg &= 0xFFF; |
nkeynes@1 | 59 | /* Well it saves having to use an intermediate table... */ |
nkeynes@1 | 60 | switch( reg ) { |
nkeynes@736 | 61 | case ICR: /* care about this later */ |
nkeynes@736 | 62 | break; |
nkeynes@736 | 63 | case IPRA: |
nkeynes@736 | 64 | PRIORITY(INT_TMU_TUNI0) = (val>>12)&0x000F; |
nkeynes@736 | 65 | PRIORITY(INT_TMU_TUNI1) = (val>>8)&0x000F; |
nkeynes@736 | 66 | PRIORITY(INT_TMU_TUNI2) = |
nkeynes@736 | 67 | PRIORITY(INT_TMU_TICPI2) = (val>>4)&0x000F; |
nkeynes@736 | 68 | PRIORITY(INT_RTC_ATI) = |
nkeynes@736 | 69 | PRIORITY(INT_RTC_PRI) = |
nkeynes@1 | 70 | PRIORITY(INT_RTC_CUI) = val&0x000F; |
nkeynes@736 | 71 | break; |
nkeynes@736 | 72 | case IPRB: |
nkeynes@736 | 73 | PRIORITY(INT_WDT_ITI) = (val>>12)&0x000F; |
nkeynes@736 | 74 | PRIORITY(INT_REF_RCMI) = |
nkeynes@736 | 75 | PRIORITY(INT_REF_ROVI) = (val>>8)&0x000F; |
nkeynes@736 | 76 | PRIORITY(INT_SCI_ERI) = |
nkeynes@736 | 77 | PRIORITY(INT_SCI_RXI) = |
nkeynes@1 | 78 | PRIORITY(INT_SCI_TXI) = |
nkeynes@736 | 79 | PRIORITY(INT_SCI_TEI) = (val>>4)&0x000F; |
nkeynes@736 | 80 | /* Bits 0-3 reserved */ |
nkeynes@736 | 81 | break; |
nkeynes@736 | 82 | case IPRC: |
nkeynes@736 | 83 | PRIORITY(INT_GPIO) = (val>>12)&0x000F; |
nkeynes@736 | 84 | PRIORITY(INT_DMA_DMTE0) = |
nkeynes@736 | 85 | PRIORITY(INT_DMA_DMTE1) = |
nkeynes@1 | 86 | PRIORITY(INT_DMA_DMTE2) = |
nkeynes@736 | 87 | PRIORITY(INT_DMA_DMTE3) = |
nkeynes@736 | 88 | PRIORITY(INT_DMA_DMAE) = (val>>8)&0x000F; |
nkeynes@736 | 89 | PRIORITY(INT_SCIF_ERI) = |
nkeynes@736 | 90 | PRIORITY(INT_SCIF_RXI) = |
nkeynes@1 | 91 | PRIORITY(INT_SCIF_BRI) = |
nkeynes@736 | 92 | PRIORITY(INT_SCIF_TXI) = (val>>4)&0x000F; |
nkeynes@736 | 93 | PRIORITY(INT_HUDI) = val&0x000F; |
nkeynes@736 | 94 | break; |
nkeynes@1 | 95 | } |
nkeynes@1 | 96 | MMIO_WRITE( INTC, reg, val ); |
nkeynes@1 | 97 | } |
nkeynes@1 | 98 | |
nkeynes@929 | 99 | MMIO_REGION_READ_FN( INTC, reg ) |
nkeynes@1 | 100 | { |
nkeynes@929 | 101 | return MMIO_READ( INTC, reg & 0xFFF ); |
nkeynes@1 | 102 | } |
nkeynes@157 | 103 | |
nkeynes@975 | 104 | MMIO_REGION_READ_DEFSUBFNS(INTC) |
nkeynes@975 | 105 | |
nkeynes@157 | 106 | void INTC_reset() |
nkeynes@157 | 107 | { |
nkeynes@157 | 108 | int i; |
nkeynes@157 | 109 | |
nkeynes@157 | 110 | intc_state.num_pending = 0; |
nkeynes@157 | 111 | for( i=0; i<INT_NUM_SOURCES; i++ ) |
nkeynes@736 | 112 | intc_state.priority[i] = intc_default_priority[i]; |
nkeynes@1187 | 113 | sh4_set_event_pending( event_get_next_time() ); |
nkeynes@265 | 114 | sh4r.event_types &= (~PENDING_IRQ); |
nkeynes@157 | 115 | } |
nkeynes@157 | 116 | |
nkeynes@157 | 117 | |
nkeynes@157 | 118 | void INTC_save_state( FILE *f ) |
nkeynes@157 | 119 | { |
nkeynes@157 | 120 | fwrite( &intc_state, sizeof(intc_state), 1, f ); |
nkeynes@157 | 121 | } |
nkeynes@157 | 122 | |
nkeynes@157 | 123 | int INTC_load_state( FILE *f ) |
nkeynes@157 | 124 | { |
nkeynes@157 | 125 | if( fread(&intc_state, sizeof(intc_state), 1, f) != 1 ) |
nkeynes@736 | 126 | return -1; |
nkeynes@157 | 127 | return 0; |
nkeynes@157 | 128 | } |
nkeynes@157 | 129 | |
nkeynes@1 | 130 | /* We basically maintain a priority queue here, raise_interrupt adds an entry, |
nkeynes@265 | 131 | * accept_interrupt takes it off. At the moment this is done as a simple |
nkeynes@1 | 132 | * ordered array, on the basis that in practice there's unlikely to be more |
nkeynes@1 | 133 | * than one at a time. There are lots of ways to optimize this if it turns out |
nkeynes@1 | 134 | * to be necessary, but I'd doubt it will be... |
nkeynes@1 | 135 | */ |
nkeynes@1 | 136 | |
nkeynes@1 | 137 | void intc_raise_interrupt( int which ) |
nkeynes@1 | 138 | { |
nkeynes@1 | 139 | int i, j, pri; |
nkeynes@736 | 140 | |
nkeynes@1 | 141 | pri = PRIORITY(which); |
nkeynes@1 | 142 | if( pri == 0 ) return; /* masked off */ |
nkeynes@736 | 143 | |
nkeynes@157 | 144 | for( i=0; i<intc_state.num_pending; i++ ) { |
nkeynes@157 | 145 | if( intc_state.pending[i] == which ) return; /* Don't queue more than once */ |
nkeynes@157 | 146 | if( PRIORITY(intc_state.pending[i]) > pri || |
nkeynes@736 | 147 | (PRIORITY(intc_state.pending[i]) == pri && |
nkeynes@736 | 148 | intc_state.pending[i] < which)) |
nkeynes@1 | 149 | break; |
nkeynes@1 | 150 | } |
nkeynes@1 | 151 | /* i == insertion point */ |
nkeynes@157 | 152 | for( j=intc_state.num_pending; j > i; j-- ) |
nkeynes@157 | 153 | intc_state.pending[j] = intc_state.pending[j-1]; |
nkeynes@157 | 154 | intc_state.pending[i] = which; |
nkeynes@1 | 155 | |
nkeynes@265 | 156 | if( i == intc_state.num_pending && (sh4r.sr&SR_BL)==0 && SH4_INTMASK() < pri ) { |
nkeynes@1187 | 157 | sh4_set_event_pending(0); |
nkeynes@736 | 158 | sh4r.event_types |= PENDING_IRQ; |
nkeynes@265 | 159 | } |
nkeynes@1 | 160 | |
nkeynes@157 | 161 | intc_state.num_pending++; |
nkeynes@1 | 162 | } |
nkeynes@1 | 163 | |
nkeynes@19 | 164 | void intc_clear_interrupt( int which ) |
nkeynes@19 | 165 | { |
nkeynes@20 | 166 | int i; |
nkeynes@157 | 167 | for( i=intc_state.num_pending-1; i>=0; i-- ) { |
nkeynes@736 | 168 | if( intc_state.pending[i] == which ) { |
nkeynes@736 | 169 | /* Shift array contents down */ |
nkeynes@736 | 170 | while( i < intc_state.num_pending-1 ) { |
nkeynes@736 | 171 | intc_state.pending[i] = intc_state.pending[i+1]; |
nkeynes@736 | 172 | i++; |
nkeynes@736 | 173 | } |
nkeynes@736 | 174 | intc_state.num_pending--; |
nkeynes@736 | 175 | intc_mask_changed(); |
nkeynes@736 | 176 | break; |
nkeynes@736 | 177 | } |
nkeynes@20 | 178 | } |
nkeynes@736 | 179 | |
nkeynes@19 | 180 | } |
nkeynes@19 | 181 | |
nkeynes@1 | 182 | uint32_t intc_accept_interrupt( void ) |
nkeynes@1 | 183 | { |
nkeynes@157 | 184 | assert(intc_state.num_pending > 0); |
nkeynes@157 | 185 | return INTCODE(intc_state.pending[intc_state.num_pending-1]); |
nkeynes@1 | 186 | } |
nkeynes@1 | 187 | |
nkeynes@1 | 188 | void intc_mask_changed( void ) |
nkeynes@1 | 189 | { |
nkeynes@157 | 190 | if( intc_state.num_pending > 0 && (sh4r.sr&SR_BL)==0 && |
nkeynes@736 | 191 | SH4_INTMASK() < PRIORITY(intc_state.pending[intc_state.num_pending-1]) ) { |
nkeynes@1187 | 192 | sh4_set_event_pending(0); |
nkeynes@736 | 193 | sh4r.event_types |= PENDING_IRQ ; |
nkeynes@265 | 194 | } |
nkeynes@265 | 195 | else { |
nkeynes@1187 | 196 | sh4_set_event_pending(event_get_next_time()); |
nkeynes@736 | 197 | sh4r.event_types &= (~PENDING_IRQ); |
nkeynes@265 | 198 | } |
nkeynes@1 | 199 | } |
nkeynes@736 | 200 | |
nkeynes@1 | 201 | |
nkeynes@1 | 202 | char *intc_get_interrupt_name( int code ) |
nkeynes@1 | 203 | { |
nkeynes@1 | 204 | return intc_sources[code].name; |
nkeynes@1 | 205 | } |
.