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