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