root / hw / slavio_timer.c @ beb811bd
History | View | Annotate | Download (8.1 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 | 52cc07d0 | blueswir1 | #define pic_set_irq_new(intctl, irq, level) \
|
32 | 52cc07d0 | blueswir1 | do { printf("TIMER: set_irq(%d): %d\n", (irq), (level)); \ |
33 | 52cc07d0 | blueswir1 | pic_set_irq_new((intctl), (irq),(level));} while (0) |
34 | 66321a11 | bellard | #else
|
35 | 66321a11 | bellard | #define DPRINTF(fmt, args...)
|
36 | 66321a11 | bellard | #endif
|
37 | 66321a11 | bellard | |
38 | e80cfcfc | bellard | /*
|
39 | e80cfcfc | bellard | * Registers of hardware timer in sun4m.
|
40 | e80cfcfc | bellard | *
|
41 | e80cfcfc | bellard | * This is the timer/counter part of chip STP2001 (Slave I/O), also
|
42 | e80cfcfc | bellard | * produced as NCR89C105. See
|
43 | e80cfcfc | bellard | * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
|
44 | e80cfcfc | bellard | *
|
45 | e80cfcfc | bellard | * The 31-bit counter is incremented every 500ns by bit 9. Bits 8..0
|
46 | e80cfcfc | bellard | * are zero. Bit 31 is 1 when count has been reached.
|
47 | e80cfcfc | bellard | *
|
48 | ba3c64fb | bellard | * Per-CPU timers interrupt local CPU, system timer uses normal
|
49 | ba3c64fb | bellard | * interrupt routing.
|
50 | ba3c64fb | bellard | *
|
51 | e80cfcfc | bellard | */
|
52 | e80cfcfc | bellard | |
53 | e80cfcfc | bellard | typedef struct SLAVIO_TIMERState { |
54 | e80cfcfc | bellard | uint32_t limit, count, counthigh; |
55 | e80cfcfc | bellard | int64_t count_load_time; |
56 | e80cfcfc | bellard | int64_t expire_time; |
57 | e80cfcfc | bellard | int64_t stop_time, tick_offset; |
58 | e80cfcfc | bellard | QEMUTimer *irq_timer; |
59 | e80cfcfc | bellard | int irq;
|
60 | e80cfcfc | bellard | int reached, stopped;
|
61 | e80cfcfc | bellard | int mode; // 0 = processor, 1 = user, 2 = system |
62 | ba3c64fb | bellard | unsigned int cpu; |
63 | 52cc07d0 | blueswir1 | void *intctl;
|
64 | e80cfcfc | bellard | } SLAVIO_TIMERState; |
65 | e80cfcfc | bellard | |
66 | e80cfcfc | bellard | #define TIMER_MAXADDR 0x1f |
67 | e80cfcfc | bellard | #define CNT_FREQ 2000000 |
68 | e80cfcfc | bellard | |
69 | e80cfcfc | bellard | // Update count, set irq, update expire_time
|
70 | e80cfcfc | bellard | static void slavio_timer_get_out(SLAVIO_TIMERState *s) |
71 | e80cfcfc | bellard | { |
72 | e80cfcfc | bellard | int out;
|
73 | e80cfcfc | bellard | int64_t diff, ticks, count; |
74 | e80cfcfc | bellard | uint32_t limit; |
75 | e80cfcfc | bellard | |
76 | e80cfcfc | bellard | // There are three clock tick units: CPU ticks, register units
|
77 | e80cfcfc | bellard | // (nanoseconds), and counter ticks (500 ns).
|
78 | e80cfcfc | bellard | if (s->mode == 1 && s->stopped) |
79 | e80cfcfc | bellard | ticks = s->stop_time; |
80 | e80cfcfc | bellard | else
|
81 | e80cfcfc | bellard | ticks = qemu_get_clock(vm_clock) - s->tick_offset; |
82 | e80cfcfc | bellard | |
83 | ba3c64fb | bellard | out = (ticks > s->expire_time); |
84 | e80cfcfc | bellard | if (out)
|
85 | e80cfcfc | bellard | s->reached = 0x80000000;
|
86 | e80cfcfc | bellard | if (!s->limit)
|
87 | e80cfcfc | bellard | limit = 0x7fffffff;
|
88 | e80cfcfc | bellard | else
|
89 | e80cfcfc | bellard | limit = s->limit; |
90 | e80cfcfc | bellard | |
91 | e80cfcfc | bellard | // Convert register units to counter ticks
|
92 | e80cfcfc | bellard | limit = limit >> 9;
|
93 | e80cfcfc | bellard | |
94 | e80cfcfc | bellard | // Convert cpu ticks to counter ticks
|
95 | e80cfcfc | bellard | diff = muldiv64(ticks - s->count_load_time, CNT_FREQ, ticks_per_sec); |
96 | e80cfcfc | bellard | |
97 | e80cfcfc | bellard | // Calculate what the counter should be, convert to register
|
98 | e80cfcfc | bellard | // units
|
99 | e80cfcfc | bellard | count = diff % limit; |
100 | e80cfcfc | bellard | s->count = count << 9;
|
101 | e80cfcfc | bellard | s->counthigh = count >> 22;
|
102 | e80cfcfc | bellard | |
103 | e80cfcfc | bellard | // Expire time: CPU ticks left to next interrupt
|
104 | e80cfcfc | bellard | // Convert remaining counter ticks to CPU ticks
|
105 | e80cfcfc | bellard | s->expire_time = ticks + muldiv64(limit - count, ticks_per_sec, CNT_FREQ); |
106 | e80cfcfc | bellard | |
107 | 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); |
108 | 66321a11 | bellard | |
109 | e80cfcfc | bellard | if (s->mode != 1) |
110 | 52cc07d0 | blueswir1 | pic_set_irq_cpu(s->intctl, s->irq, out, s->cpu); |
111 | e80cfcfc | bellard | } |
112 | e80cfcfc | bellard | |
113 | e80cfcfc | bellard | // timer callback
|
114 | e80cfcfc | bellard | static void slavio_timer_irq(void *opaque) |
115 | e80cfcfc | bellard | { |
116 | e80cfcfc | bellard | SLAVIO_TIMERState *s = opaque; |
117 | e80cfcfc | bellard | |
118 | e80cfcfc | bellard | if (!s->irq_timer)
|
119 | e80cfcfc | bellard | return;
|
120 | e80cfcfc | bellard | slavio_timer_get_out(s); |
121 | e80cfcfc | bellard | if (s->mode != 1) |
122 | e80cfcfc | bellard | qemu_mod_timer(s->irq_timer, s->expire_time); |
123 | e80cfcfc | bellard | } |
124 | e80cfcfc | bellard | |
125 | e80cfcfc | bellard | static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr) |
126 | e80cfcfc | bellard | { |
127 | e80cfcfc | bellard | SLAVIO_TIMERState *s = opaque; |
128 | e80cfcfc | bellard | uint32_t saddr; |
129 | e80cfcfc | bellard | |
130 | e80cfcfc | bellard | saddr = (addr & TIMER_MAXADDR) >> 2;
|
131 | e80cfcfc | bellard | switch (saddr) {
|
132 | e80cfcfc | bellard | case 0: |
133 | e80cfcfc | bellard | // read limit (system counter mode) or read most signifying
|
134 | e80cfcfc | bellard | // part of counter (user mode)
|
135 | e80cfcfc | bellard | if (s->mode != 1) { |
136 | e80cfcfc | bellard | // clear irq
|
137 | 52cc07d0 | blueswir1 | pic_set_irq_cpu(s->intctl, s->irq, 0, s->cpu);
|
138 | e80cfcfc | bellard | s->reached = 0;
|
139 | e80cfcfc | bellard | return s->limit;
|
140 | e80cfcfc | bellard | } |
141 | e80cfcfc | bellard | else {
|
142 | e80cfcfc | bellard | slavio_timer_get_out(s); |
143 | e80cfcfc | bellard | return s->counthigh & 0x7fffffff; |
144 | e80cfcfc | bellard | } |
145 | e80cfcfc | bellard | case 1: |
146 | e80cfcfc | bellard | // read counter and reached bit (system mode) or read lsbits
|
147 | e80cfcfc | bellard | // of counter (user mode)
|
148 | e80cfcfc | bellard | slavio_timer_get_out(s); |
149 | e80cfcfc | bellard | if (s->mode != 1) |
150 | e80cfcfc | bellard | return (s->count & 0x7fffffff) | s->reached; |
151 | e80cfcfc | bellard | else
|
152 | e80cfcfc | bellard | return s->count;
|
153 | e80cfcfc | bellard | case 3: |
154 | e80cfcfc | bellard | // read start/stop status
|
155 | e80cfcfc | bellard | return s->stopped;
|
156 | e80cfcfc | bellard | case 4: |
157 | e80cfcfc | bellard | // read user/system mode
|
158 | e80cfcfc | bellard | return s->mode & 1; |
159 | e80cfcfc | bellard | default:
|
160 | e80cfcfc | bellard | return 0; |
161 | e80cfcfc | bellard | } |
162 | e80cfcfc | bellard | } |
163 | e80cfcfc | bellard | |
164 | e80cfcfc | bellard | static void slavio_timer_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) |
165 | e80cfcfc | bellard | { |
166 | e80cfcfc | bellard | SLAVIO_TIMERState *s = opaque; |
167 | e80cfcfc | bellard | uint32_t saddr; |
168 | e80cfcfc | bellard | |
169 | e80cfcfc | bellard | saddr = (addr & TIMER_MAXADDR) >> 2;
|
170 | e80cfcfc | bellard | switch (saddr) {
|
171 | e80cfcfc | bellard | case 0: |
172 | e80cfcfc | bellard | // set limit, reset counter
|
173 | e80cfcfc | bellard | s->count_load_time = qemu_get_clock(vm_clock); |
174 | e80cfcfc | bellard | // fall through
|
175 | e80cfcfc | bellard | case 2: |
176 | e80cfcfc | bellard | // set limit without resetting counter
|
177 | e80cfcfc | bellard | if (!val)
|
178 | e80cfcfc | bellard | s->limit = 0x7fffffff;
|
179 | e80cfcfc | bellard | else
|
180 | e80cfcfc | bellard | s->limit = val & 0x7fffffff;
|
181 | e80cfcfc | bellard | slavio_timer_irq(s); |
182 | e80cfcfc | bellard | break;
|
183 | e80cfcfc | bellard | case 3: |
184 | e80cfcfc | bellard | // start/stop user counter
|
185 | e80cfcfc | bellard | if (s->mode == 1) { |
186 | e80cfcfc | bellard | if (val & 1) { |
187 | e80cfcfc | bellard | s->stop_time = qemu_get_clock(vm_clock); |
188 | e80cfcfc | bellard | s->stopped = 1;
|
189 | e80cfcfc | bellard | } |
190 | e80cfcfc | bellard | else {
|
191 | e80cfcfc | bellard | if (s->stopped)
|
192 | e80cfcfc | bellard | s->tick_offset += qemu_get_clock(vm_clock) - s->stop_time; |
193 | e80cfcfc | bellard | s->stopped = 0;
|
194 | e80cfcfc | bellard | } |
195 | e80cfcfc | bellard | } |
196 | e80cfcfc | bellard | break;
|
197 | e80cfcfc | bellard | case 4: |
198 | e80cfcfc | bellard | // bit 0: user (1) or system (0) counter mode
|
199 | e80cfcfc | bellard | if (s->mode == 0 || s->mode == 1) |
200 | e80cfcfc | bellard | s->mode = val & 1;
|
201 | e80cfcfc | bellard | break;
|
202 | e80cfcfc | bellard | default:
|
203 | e80cfcfc | bellard | break;
|
204 | e80cfcfc | bellard | } |
205 | e80cfcfc | bellard | } |
206 | e80cfcfc | bellard | |
207 | e80cfcfc | bellard | static CPUReadMemoryFunc *slavio_timer_mem_read[3] = { |
208 | e80cfcfc | bellard | slavio_timer_mem_readl, |
209 | e80cfcfc | bellard | slavio_timer_mem_readl, |
210 | e80cfcfc | bellard | slavio_timer_mem_readl, |
211 | e80cfcfc | bellard | }; |
212 | e80cfcfc | bellard | |
213 | e80cfcfc | bellard | static CPUWriteMemoryFunc *slavio_timer_mem_write[3] = { |
214 | e80cfcfc | bellard | slavio_timer_mem_writel, |
215 | e80cfcfc | bellard | slavio_timer_mem_writel, |
216 | e80cfcfc | bellard | slavio_timer_mem_writel, |
217 | e80cfcfc | bellard | }; |
218 | e80cfcfc | bellard | |
219 | e80cfcfc | bellard | static void slavio_timer_save(QEMUFile *f, void *opaque) |
220 | e80cfcfc | bellard | { |
221 | e80cfcfc | bellard | SLAVIO_TIMERState *s = opaque; |
222 | e80cfcfc | bellard | |
223 | e80cfcfc | bellard | qemu_put_be32s(f, &s->limit); |
224 | e80cfcfc | bellard | qemu_put_be32s(f, &s->count); |
225 | e80cfcfc | bellard | qemu_put_be32s(f, &s->counthigh); |
226 | e80cfcfc | bellard | qemu_put_be64s(f, &s->count_load_time); |
227 | e80cfcfc | bellard | qemu_put_be64s(f, &s->expire_time); |
228 | e80cfcfc | bellard | qemu_put_be64s(f, &s->stop_time); |
229 | e80cfcfc | bellard | qemu_put_be64s(f, &s->tick_offset); |
230 | e80cfcfc | bellard | qemu_put_be32s(f, &s->irq); |
231 | e80cfcfc | bellard | qemu_put_be32s(f, &s->reached); |
232 | e80cfcfc | bellard | qemu_put_be32s(f, &s->stopped); |
233 | e80cfcfc | bellard | qemu_put_be32s(f, &s->mode); |
234 | e80cfcfc | bellard | } |
235 | e80cfcfc | bellard | |
236 | e80cfcfc | bellard | static int slavio_timer_load(QEMUFile *f, void *opaque, int version_id) |
237 | e80cfcfc | bellard | { |
238 | e80cfcfc | bellard | SLAVIO_TIMERState *s = opaque; |
239 | e80cfcfc | bellard | |
240 | e80cfcfc | bellard | if (version_id != 1) |
241 | e80cfcfc | bellard | return -EINVAL;
|
242 | e80cfcfc | bellard | |
243 | e80cfcfc | bellard | qemu_get_be32s(f, &s->limit); |
244 | e80cfcfc | bellard | qemu_get_be32s(f, &s->count); |
245 | e80cfcfc | bellard | qemu_get_be32s(f, &s->counthigh); |
246 | e80cfcfc | bellard | qemu_get_be64s(f, &s->count_load_time); |
247 | e80cfcfc | bellard | qemu_get_be64s(f, &s->expire_time); |
248 | e80cfcfc | bellard | qemu_get_be64s(f, &s->stop_time); |
249 | e80cfcfc | bellard | qemu_get_be64s(f, &s->tick_offset); |
250 | e80cfcfc | bellard | qemu_get_be32s(f, &s->irq); |
251 | e80cfcfc | bellard | qemu_get_be32s(f, &s->reached); |
252 | e80cfcfc | bellard | qemu_get_be32s(f, &s->stopped); |
253 | e80cfcfc | bellard | qemu_get_be32s(f, &s->mode); |
254 | e80cfcfc | bellard | return 0; |
255 | e80cfcfc | bellard | } |
256 | e80cfcfc | bellard | |
257 | e80cfcfc | bellard | static void slavio_timer_reset(void *opaque) |
258 | e80cfcfc | bellard | { |
259 | e80cfcfc | bellard | SLAVIO_TIMERState *s = opaque; |
260 | e80cfcfc | bellard | |
261 | e80cfcfc | bellard | s->limit = 0;
|
262 | e80cfcfc | bellard | s->count = 0;
|
263 | e80cfcfc | bellard | s->count_load_time = qemu_get_clock(vm_clock);; |
264 | e80cfcfc | bellard | s->stop_time = s->count_load_time; |
265 | e80cfcfc | bellard | s->tick_offset = 0;
|
266 | e80cfcfc | bellard | s->reached = 0;
|
267 | e80cfcfc | bellard | s->mode &= 2;
|
268 | e80cfcfc | bellard | s->stopped = 1;
|
269 | e80cfcfc | bellard | slavio_timer_get_out(s); |
270 | e80cfcfc | bellard | } |
271 | e80cfcfc | bellard | |
272 | 52cc07d0 | blueswir1 | void slavio_timer_init(uint32_t addr, int irq, int mode, unsigned int cpu, |
273 | 52cc07d0 | blueswir1 | void *intctl)
|
274 | e80cfcfc | bellard | { |
275 | e80cfcfc | bellard | int slavio_timer_io_memory;
|
276 | e80cfcfc | bellard | SLAVIO_TIMERState *s; |
277 | e80cfcfc | bellard | |
278 | e80cfcfc | bellard | s = qemu_mallocz(sizeof(SLAVIO_TIMERState));
|
279 | e80cfcfc | bellard | if (!s)
|
280 | e80cfcfc | bellard | return;
|
281 | e80cfcfc | bellard | s->irq = irq; |
282 | e80cfcfc | bellard | s->mode = mode; |
283 | ba3c64fb | bellard | s->cpu = cpu; |
284 | e80cfcfc | bellard | s->irq_timer = qemu_new_timer(vm_clock, slavio_timer_irq, s); |
285 | 52cc07d0 | blueswir1 | s->intctl = intctl; |
286 | e80cfcfc | bellard | |
287 | e80cfcfc | bellard | slavio_timer_io_memory = cpu_register_io_memory(0, slavio_timer_mem_read,
|
288 | e80cfcfc | bellard | slavio_timer_mem_write, s); |
289 | e80cfcfc | bellard | cpu_register_physical_memory(addr, TIMER_MAXADDR, slavio_timer_io_memory); |
290 | e80cfcfc | bellard | register_savevm("slavio_timer", addr, 1, slavio_timer_save, slavio_timer_load, s); |
291 | e80cfcfc | bellard | qemu_register_reset(slavio_timer_reset, s); |
292 | e80cfcfc | bellard | slavio_timer_reset(s); |
293 | e80cfcfc | bellard | } |