bellard | e80cfcf | 2004-12-19 23:18:01 +0000 | [diff] [blame] | 1 | /* |
| 2 | * QEMU Sparc SLAVIO timer controller emulation |
| 3 | * |
| 4 | * Copyright (c) 2003-2004 Fabrice Bellard |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to deal |
| 8 | * in the Software without restriction, including without limitation the rights |
| 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 | * copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in |
| 14 | * all copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 22 | * THE SOFTWARE. |
| 23 | */ |
| 24 | #include "vl.h" |
| 25 | |
| 26 | //#define DEBUG_TIMER |
| 27 | |
| 28 | /* |
| 29 | * Registers of hardware timer in sun4m. |
| 30 | * |
| 31 | * This is the timer/counter part of chip STP2001 (Slave I/O), also |
| 32 | * produced as NCR89C105. See |
| 33 | * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt |
| 34 | * |
| 35 | * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0 |
| 36 | * are zero. Bit 31 is 1 when count has been reached. |
| 37 | * |
| 38 | */ |
| 39 | |
| 40 | typedef struct SLAVIO_TIMERState { |
| 41 | uint32_t limit, count, counthigh; |
| 42 | int64_t count_load_time; |
| 43 | int64_t expire_time; |
| 44 | int64_t stop_time, tick_offset; |
| 45 | QEMUTimer *irq_timer; |
| 46 | int irq; |
| 47 | int reached, stopped; |
| 48 | int mode; // 0 = processor, 1 = user, 2 = system |
| 49 | } SLAVIO_TIMERState; |
| 50 | |
| 51 | #define TIMER_MAXADDR 0x1f |
| 52 | #define CNT_FREQ 2000000 |
| 53 | #define MAX_CPUS 16 |
| 54 | |
| 55 | // Update count, set irq, update expire_time |
| 56 | static void slavio_timer_get_out(SLAVIO_TIMERState *s) |
| 57 | { |
| 58 | int out; |
| 59 | int64_t diff, ticks, count; |
| 60 | uint32_t limit; |
| 61 | |
| 62 | // There are three clock tick units: CPU ticks, register units |
| 63 | // (nanoseconds), and counter ticks (500 ns). |
| 64 | if (s->mode == 1 && s->stopped) |
| 65 | ticks = s->stop_time; |
| 66 | else |
| 67 | ticks = qemu_get_clock(vm_clock) - s->tick_offset; |
| 68 | |
| 69 | out = (ticks >= s->expire_time); |
| 70 | if (out) |
| 71 | s->reached = 0x80000000; |
| 72 | if (!s->limit) |
| 73 | limit = 0x7fffffff; |
| 74 | else |
| 75 | limit = s->limit; |
| 76 | |
| 77 | // Convert register units to counter ticks |
| 78 | limit = limit >> 9; |
| 79 | |
| 80 | // Convert cpu ticks to counter ticks |
| 81 | diff = muldiv64(ticks - s->count_load_time, CNT_FREQ, ticks_per_sec); |
| 82 | |
| 83 | // Calculate what the counter should be, convert to register |
| 84 | // units |
| 85 | count = diff % limit; |
| 86 | s->count = count << 9; |
| 87 | s->counthigh = count >> 22; |
| 88 | |
| 89 | // Expire time: CPU ticks left to next interrupt |
| 90 | // Convert remaining counter ticks to CPU ticks |
| 91 | s->expire_time = ticks + muldiv64(limit - count, ticks_per_sec, CNT_FREQ); |
| 92 | |
| 93 | #ifdef DEBUG_TIMER |
| 94 | term_printf("timer: irq %d limit %d reached %d d %lld count %d s->c %x diff %lld stopped %d mode %d\n", s->irq, limit, s->reached?1:0, (ticks-s->count_load_time), count, s->count, s->expire_time - ticks, s->stopped, s->mode); |
| 95 | #endif |
| 96 | if (s->mode != 1) |
| 97 | pic_set_irq(s->irq, out); |
| 98 | } |
| 99 | |
| 100 | // timer callback |
| 101 | static void slavio_timer_irq(void *opaque) |
| 102 | { |
| 103 | SLAVIO_TIMERState *s = opaque; |
| 104 | |
| 105 | if (!s->irq_timer) |
| 106 | return; |
| 107 | slavio_timer_get_out(s); |
| 108 | if (s->mode != 1) |
| 109 | qemu_mod_timer(s->irq_timer, s->expire_time); |
| 110 | } |
| 111 | |
| 112 | static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr) |
| 113 | { |
| 114 | SLAVIO_TIMERState *s = opaque; |
| 115 | uint32_t saddr; |
| 116 | |
| 117 | saddr = (addr & TIMER_MAXADDR) >> 2; |
| 118 | switch (saddr) { |
| 119 | case 0: |
| 120 | // read limit (system counter mode) or read most signifying |
| 121 | // part of counter (user mode) |
| 122 | if (s->mode != 1) { |
| 123 | // clear irq |
| 124 | pic_set_irq(s->irq, 0); |
| 125 | s->count_load_time = qemu_get_clock(vm_clock); |
| 126 | s->reached = 0; |
| 127 | return s->limit; |
| 128 | } |
| 129 | else { |
| 130 | slavio_timer_get_out(s); |
| 131 | return s->counthigh & 0x7fffffff; |
| 132 | } |
| 133 | case 1: |
| 134 | // read counter and reached bit (system mode) or read lsbits |
| 135 | // of counter (user mode) |
| 136 | slavio_timer_get_out(s); |
| 137 | if (s->mode != 1) |
| 138 | return (s->count & 0x7fffffff) | s->reached; |
| 139 | else |
| 140 | return s->count; |
| 141 | case 3: |
| 142 | // read start/stop status |
| 143 | return s->stopped; |
| 144 | case 4: |
| 145 | // read user/system mode |
| 146 | return s->mode & 1; |
| 147 | default: |
| 148 | return 0; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | static void slavio_timer_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) |
| 153 | { |
| 154 | SLAVIO_TIMERState *s = opaque; |
| 155 | uint32_t saddr; |
| 156 | |
| 157 | saddr = (addr & TIMER_MAXADDR) >> 2; |
| 158 | switch (saddr) { |
| 159 | case 0: |
| 160 | // set limit, reset counter |
| 161 | s->count_load_time = qemu_get_clock(vm_clock); |
| 162 | // fall through |
| 163 | case 2: |
| 164 | // set limit without resetting counter |
| 165 | if (!val) |
| 166 | s->limit = 0x7fffffff; |
| 167 | else |
| 168 | s->limit = val & 0x7fffffff; |
| 169 | slavio_timer_irq(s); |
| 170 | break; |
| 171 | case 3: |
| 172 | // start/stop user counter |
| 173 | if (s->mode == 1) { |
| 174 | if (val & 1) { |
| 175 | s->stop_time = qemu_get_clock(vm_clock); |
| 176 | s->stopped = 1; |
| 177 | } |
| 178 | else { |
| 179 | if (s->stopped) |
| 180 | s->tick_offset += qemu_get_clock(vm_clock) - s->stop_time; |
| 181 | s->stopped = 0; |
| 182 | } |
| 183 | } |
| 184 | break; |
| 185 | case 4: |
| 186 | // bit 0: user (1) or system (0) counter mode |
| 187 | if (s->mode == 0 || s->mode == 1) |
| 188 | s->mode = val & 1; |
| 189 | break; |
| 190 | default: |
| 191 | break; |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | static CPUReadMemoryFunc *slavio_timer_mem_read[3] = { |
| 196 | slavio_timer_mem_readl, |
| 197 | slavio_timer_mem_readl, |
| 198 | slavio_timer_mem_readl, |
| 199 | }; |
| 200 | |
| 201 | static CPUWriteMemoryFunc *slavio_timer_mem_write[3] = { |
| 202 | slavio_timer_mem_writel, |
| 203 | slavio_timer_mem_writel, |
| 204 | slavio_timer_mem_writel, |
| 205 | }; |
| 206 | |
| 207 | static void slavio_timer_save(QEMUFile *f, void *opaque) |
| 208 | { |
| 209 | SLAVIO_TIMERState *s = opaque; |
| 210 | |
| 211 | qemu_put_be32s(f, &s->limit); |
| 212 | qemu_put_be32s(f, &s->count); |
| 213 | qemu_put_be32s(f, &s->counthigh); |
| 214 | qemu_put_be64s(f, &s->count_load_time); |
| 215 | qemu_put_be64s(f, &s->expire_time); |
| 216 | qemu_put_be64s(f, &s->stop_time); |
| 217 | qemu_put_be64s(f, &s->tick_offset); |
| 218 | qemu_put_be32s(f, &s->irq); |
| 219 | qemu_put_be32s(f, &s->reached); |
| 220 | qemu_put_be32s(f, &s->stopped); |
| 221 | qemu_put_be32s(f, &s->mode); |
| 222 | } |
| 223 | |
| 224 | static int slavio_timer_load(QEMUFile *f, void *opaque, int version_id) |
| 225 | { |
| 226 | SLAVIO_TIMERState *s = opaque; |
| 227 | |
| 228 | if (version_id != 1) |
| 229 | return -EINVAL; |
| 230 | |
| 231 | qemu_get_be32s(f, &s->limit); |
| 232 | qemu_get_be32s(f, &s->count); |
| 233 | qemu_get_be32s(f, &s->counthigh); |
| 234 | qemu_get_be64s(f, &s->count_load_time); |
| 235 | qemu_get_be64s(f, &s->expire_time); |
| 236 | qemu_get_be64s(f, &s->stop_time); |
| 237 | qemu_get_be64s(f, &s->tick_offset); |
| 238 | qemu_get_be32s(f, &s->irq); |
| 239 | qemu_get_be32s(f, &s->reached); |
| 240 | qemu_get_be32s(f, &s->stopped); |
| 241 | qemu_get_be32s(f, &s->mode); |
| 242 | return 0; |
| 243 | } |
| 244 | |
| 245 | static void slavio_timer_reset(void *opaque) |
| 246 | { |
| 247 | SLAVIO_TIMERState *s = opaque; |
| 248 | |
| 249 | s->limit = 0; |
| 250 | s->count = 0; |
| 251 | s->count_load_time = qemu_get_clock(vm_clock);; |
| 252 | s->stop_time = s->count_load_time; |
| 253 | s->tick_offset = 0; |
| 254 | s->reached = 0; |
| 255 | s->mode &= 2; |
| 256 | s->stopped = 1; |
| 257 | slavio_timer_get_out(s); |
| 258 | } |
| 259 | |
| 260 | static void slavio_timer_init_internal(uint32_t addr, int irq, int mode) |
| 261 | { |
| 262 | int slavio_timer_io_memory; |
| 263 | SLAVIO_TIMERState *s; |
| 264 | |
| 265 | s = qemu_mallocz(sizeof(SLAVIO_TIMERState)); |
| 266 | if (!s) |
| 267 | return; |
| 268 | s->irq = irq; |
| 269 | s->mode = mode; |
| 270 | s->irq_timer = qemu_new_timer(vm_clock, slavio_timer_irq, s); |
| 271 | |
| 272 | slavio_timer_io_memory = cpu_register_io_memory(0, slavio_timer_mem_read, |
| 273 | slavio_timer_mem_write, s); |
| 274 | cpu_register_physical_memory(addr, TIMER_MAXADDR, slavio_timer_io_memory); |
| 275 | register_savevm("slavio_timer", addr, 1, slavio_timer_save, slavio_timer_load, s); |
| 276 | qemu_register_reset(slavio_timer_reset, s); |
| 277 | slavio_timer_reset(s); |
| 278 | } |
| 279 | |
| 280 | void slavio_timer_init(uint32_t addr1, int irq1, uint32_t addr2, int irq2) |
| 281 | { |
| 282 | int i; |
| 283 | |
| 284 | for (i = 0; i < MAX_CPUS; i++) { |
| 285 | slavio_timer_init_internal(addr1 + i * TARGET_PAGE_SIZE, irq1, 0); |
| 286 | } |
| 287 | |
| 288 | slavio_timer_init_internal(addr2, irq2, 2); |
| 289 | } |