root / hw / slavio_timer.c @ 5fafdf24
History | View | Annotate | Download (7 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 | 5fafdf24 | ths | *
|
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 | 5fafdf24 | ths | *
|
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 | d7edfd27 | blueswir1 | qemu_irq irq; |
52 | 8d05ea8a | blueswir1 | ptimer_state *timer; |
53 | 8d05ea8a | blueswir1 | uint32_t count, counthigh, reached; |
54 | 8d05ea8a | blueswir1 | uint64_t limit; |
55 | 8d05ea8a | blueswir1 | int stopped;
|
56 | e80cfcfc | bellard | int mode; // 0 = processor, 1 = user, 2 = system |
57 | e80cfcfc | bellard | } SLAVIO_TIMERState; |
58 | e80cfcfc | bellard | |
59 | e80cfcfc | bellard | #define TIMER_MAXADDR 0x1f |
60 | 5aca8c3b | blueswir1 | #define TIMER_SIZE (TIMER_MAXADDR + 1) |
61 | e80cfcfc | bellard | |
62 | e80cfcfc | bellard | // Update count, set irq, update expire_time
|
63 | 8d05ea8a | blueswir1 | // Convert from ptimer countdown units
|
64 | e80cfcfc | bellard | static void slavio_timer_get_out(SLAVIO_TIMERState *s) |
65 | e80cfcfc | bellard | { |
66 | 8d05ea8a | blueswir1 | uint64_t count; |
67 | e80cfcfc | bellard | |
68 | 8d05ea8a | blueswir1 | count = s->limit - (ptimer_get_count(s->timer) << 9);
|
69 | 8d05ea8a | blueswir1 | DPRINTF("get_out: limit %" PRIx64 " count %x%08x\n", s->limit, s->counthigh, |
70 | 8d05ea8a | blueswir1 | s->count); |
71 | 8d05ea8a | blueswir1 | s->count = count & 0xfffffe00;
|
72 | 8d05ea8a | blueswir1 | s->counthigh = count >> 32;
|
73 | e80cfcfc | bellard | } |
74 | e80cfcfc | bellard | |
75 | e80cfcfc | bellard | // timer callback
|
76 | e80cfcfc | bellard | static void slavio_timer_irq(void *opaque) |
77 | e80cfcfc | bellard | { |
78 | e80cfcfc | bellard | SLAVIO_TIMERState *s = opaque; |
79 | e80cfcfc | bellard | |
80 | e80cfcfc | bellard | slavio_timer_get_out(s); |
81 | 8d05ea8a | blueswir1 | DPRINTF("callback: count %x%08x\n", s->counthigh, s->count);
|
82 | 8d05ea8a | blueswir1 | s->reached = 0x80000000;
|
83 | e80cfcfc | bellard | if (s->mode != 1) |
84 | d7edfd27 | blueswir1 | qemu_irq_raise(s->irq); |
85 | e80cfcfc | bellard | } |
86 | e80cfcfc | bellard | |
87 | e80cfcfc | bellard | static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr) |
88 | e80cfcfc | bellard | { |
89 | e80cfcfc | bellard | SLAVIO_TIMERState *s = opaque; |
90 | 8d05ea8a | blueswir1 | uint32_t saddr, ret; |
91 | e80cfcfc | bellard | |
92 | e80cfcfc | bellard | saddr = (addr & TIMER_MAXADDR) >> 2;
|
93 | e80cfcfc | bellard | switch (saddr) {
|
94 | e80cfcfc | bellard | case 0: |
95 | e80cfcfc | bellard | // read limit (system counter mode) or read most signifying
|
96 | e80cfcfc | bellard | // part of counter (user mode)
|
97 | e80cfcfc | bellard | if (s->mode != 1) { |
98 | e80cfcfc | bellard | // clear irq
|
99 | d7edfd27 | blueswir1 | qemu_irq_lower(s->irq); |
100 | e80cfcfc | bellard | s->reached = 0;
|
101 | 8d05ea8a | blueswir1 | ret = s->limit & 0x7fffffff;
|
102 | e80cfcfc | bellard | } |
103 | e80cfcfc | bellard | else {
|
104 | e80cfcfc | bellard | slavio_timer_get_out(s); |
105 | 8d05ea8a | blueswir1 | ret = s->counthigh & 0x7fffffff;
|
106 | e80cfcfc | bellard | } |
107 | 8d05ea8a | blueswir1 | break;
|
108 | e80cfcfc | bellard | case 1: |
109 | e80cfcfc | bellard | // read counter and reached bit (system mode) or read lsbits
|
110 | e80cfcfc | bellard | // of counter (user mode)
|
111 | e80cfcfc | bellard | slavio_timer_get_out(s); |
112 | e80cfcfc | bellard | if (s->mode != 1) |
113 | 8d05ea8a | blueswir1 | ret = (s->count & 0x7fffffff) | s->reached;
|
114 | e80cfcfc | bellard | else
|
115 | 8d05ea8a | blueswir1 | ret = s->count; |
116 | 8d05ea8a | blueswir1 | break;
|
117 | e80cfcfc | bellard | case 3: |
118 | e80cfcfc | bellard | // read start/stop status
|
119 | 8d05ea8a | blueswir1 | ret = s->stopped; |
120 | 8d05ea8a | blueswir1 | break;
|
121 | e80cfcfc | bellard | case 4: |
122 | e80cfcfc | bellard | // read user/system mode
|
123 | 8d05ea8a | blueswir1 | ret = s->mode & 1;
|
124 | 8d05ea8a | blueswir1 | break;
|
125 | e80cfcfc | bellard | default:
|
126 | 8d05ea8a | blueswir1 | ret = 0;
|
127 | 8d05ea8a | blueswir1 | break;
|
128 | e80cfcfc | bellard | } |
129 | 8d05ea8a | blueswir1 | DPRINTF("read " TARGET_FMT_plx " = %08x\n", addr, ret); |
130 | 8d05ea8a | blueswir1 | |
131 | 8d05ea8a | blueswir1 | return ret;
|
132 | e80cfcfc | bellard | } |
133 | e80cfcfc | bellard | |
134 | e80cfcfc | bellard | static void slavio_timer_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) |
135 | e80cfcfc | bellard | { |
136 | e80cfcfc | bellard | SLAVIO_TIMERState *s = opaque; |
137 | e80cfcfc | bellard | uint32_t saddr; |
138 | 8d05ea8a | blueswir1 | int reload = 0; |
139 | e80cfcfc | bellard | |
140 | 8d05ea8a | blueswir1 | DPRINTF("write " TARGET_FMT_plx " %08x\n", addr, val); |
141 | e80cfcfc | bellard | saddr = (addr & TIMER_MAXADDR) >> 2;
|
142 | e80cfcfc | bellard | switch (saddr) {
|
143 | e80cfcfc | bellard | case 0: |
144 | e80cfcfc | bellard | // set limit, reset counter
|
145 | 8d05ea8a | blueswir1 | reload = 1;
|
146 | d7edfd27 | blueswir1 | qemu_irq_lower(s->irq); |
147 | e80cfcfc | bellard | // fall through
|
148 | e80cfcfc | bellard | case 2: |
149 | e80cfcfc | bellard | // set limit without resetting counter
|
150 | 8d05ea8a | blueswir1 | s->limit = val & 0x7ffffe00ULL;
|
151 | 8d05ea8a | blueswir1 | if (!s->limit)
|
152 | 8d05ea8a | blueswir1 | s->limit = 0x7ffffe00ULL;
|
153 | 8d05ea8a | blueswir1 | ptimer_set_limit(s->timer, s->limit >> 9, reload);
|
154 | e80cfcfc | bellard | break;
|
155 | e80cfcfc | bellard | case 3: |
156 | e80cfcfc | bellard | // start/stop user counter
|
157 | e80cfcfc | bellard | if (s->mode == 1) { |
158 | e80cfcfc | bellard | if (val & 1) { |
159 | 8d05ea8a | blueswir1 | ptimer_stop(s->timer); |
160 | e80cfcfc | bellard | s->stopped = 1;
|
161 | e80cfcfc | bellard | } |
162 | e80cfcfc | bellard | else {
|
163 | 8d05ea8a | blueswir1 | ptimer_run(s->timer, 0);
|
164 | e80cfcfc | bellard | s->stopped = 0;
|
165 | e80cfcfc | bellard | } |
166 | e80cfcfc | bellard | } |
167 | e80cfcfc | bellard | break;
|
168 | e80cfcfc | bellard | case 4: |
169 | e80cfcfc | bellard | // bit 0: user (1) or system (0) counter mode
|
170 | e80cfcfc | bellard | if (s->mode == 0 || s->mode == 1) |
171 | e80cfcfc | bellard | s->mode = val & 1;
|
172 | 8d05ea8a | blueswir1 | if (s->mode == 1) { |
173 | d7edfd27 | blueswir1 | qemu_irq_lower(s->irq); |
174 | 8d05ea8a | blueswir1 | s->limit = -1ULL;
|
175 | 8d05ea8a | blueswir1 | } |
176 | 8d05ea8a | blueswir1 | ptimer_set_limit(s->timer, s->limit >> 9, 1); |
177 | e80cfcfc | bellard | break;
|
178 | e80cfcfc | bellard | default:
|
179 | e80cfcfc | bellard | break;
|
180 | e80cfcfc | bellard | } |
181 | e80cfcfc | bellard | } |
182 | e80cfcfc | bellard | |
183 | e80cfcfc | bellard | static CPUReadMemoryFunc *slavio_timer_mem_read[3] = { |
184 | e80cfcfc | bellard | slavio_timer_mem_readl, |
185 | e80cfcfc | bellard | slavio_timer_mem_readl, |
186 | e80cfcfc | bellard | slavio_timer_mem_readl, |
187 | e80cfcfc | bellard | }; |
188 | e80cfcfc | bellard | |
189 | e80cfcfc | bellard | static CPUWriteMemoryFunc *slavio_timer_mem_write[3] = { |
190 | e80cfcfc | bellard | slavio_timer_mem_writel, |
191 | e80cfcfc | bellard | slavio_timer_mem_writel, |
192 | e80cfcfc | bellard | slavio_timer_mem_writel, |
193 | e80cfcfc | bellard | }; |
194 | e80cfcfc | bellard | |
195 | e80cfcfc | bellard | static void slavio_timer_save(QEMUFile *f, void *opaque) |
196 | e80cfcfc | bellard | { |
197 | e80cfcfc | bellard | SLAVIO_TIMERState *s = opaque; |
198 | e80cfcfc | bellard | |
199 | 8d05ea8a | blueswir1 | qemu_put_be64s(f, &s->limit); |
200 | e80cfcfc | bellard | qemu_put_be32s(f, &s->count); |
201 | e80cfcfc | bellard | qemu_put_be32s(f, &s->counthigh); |
202 | d7edfd27 | blueswir1 | qemu_put_be32(f, 0); // Was irq |
203 | e80cfcfc | bellard | qemu_put_be32s(f, &s->reached); |
204 | e80cfcfc | bellard | qemu_put_be32s(f, &s->stopped); |
205 | e80cfcfc | bellard | qemu_put_be32s(f, &s->mode); |
206 | 8d05ea8a | blueswir1 | qemu_put_ptimer(f, s->timer); |
207 | e80cfcfc | bellard | } |
208 | e80cfcfc | bellard | |
209 | e80cfcfc | bellard | static int slavio_timer_load(QEMUFile *f, void *opaque, int version_id) |
210 | e80cfcfc | bellard | { |
211 | e80cfcfc | bellard | SLAVIO_TIMERState *s = opaque; |
212 | d7edfd27 | blueswir1 | uint32_t tmp; |
213 | 5fafdf24 | ths | |
214 | 8d05ea8a | blueswir1 | if (version_id != 2) |
215 | e80cfcfc | bellard | return -EINVAL;
|
216 | e80cfcfc | bellard | |
217 | 8d05ea8a | blueswir1 | qemu_get_be64s(f, &s->limit); |
218 | e80cfcfc | bellard | qemu_get_be32s(f, &s->count); |
219 | e80cfcfc | bellard | qemu_get_be32s(f, &s->counthigh); |
220 | d7edfd27 | blueswir1 | qemu_get_be32s(f, &tmp); // Was irq
|
221 | e80cfcfc | bellard | qemu_get_be32s(f, &s->reached); |
222 | e80cfcfc | bellard | qemu_get_be32s(f, &s->stopped); |
223 | e80cfcfc | bellard | qemu_get_be32s(f, &s->mode); |
224 | 8d05ea8a | blueswir1 | qemu_get_ptimer(f, s->timer); |
225 | 8d05ea8a | blueswir1 | |
226 | e80cfcfc | bellard | return 0; |
227 | e80cfcfc | bellard | } |
228 | e80cfcfc | bellard | |
229 | e80cfcfc | bellard | static void slavio_timer_reset(void *opaque) |
230 | e80cfcfc | bellard | { |
231 | e80cfcfc | bellard | SLAVIO_TIMERState *s = opaque; |
232 | e80cfcfc | bellard | |
233 | 8d05ea8a | blueswir1 | s->limit = 0x7ffffe00ULL;
|
234 | e80cfcfc | bellard | s->count = 0;
|
235 | e80cfcfc | bellard | s->reached = 0;
|
236 | e80cfcfc | bellard | s->mode &= 2;
|
237 | 8d05ea8a | blueswir1 | ptimer_set_limit(s->timer, s->limit >> 9, 1); |
238 | 8d05ea8a | blueswir1 | ptimer_run(s->timer, 0);
|
239 | e80cfcfc | bellard | s->stopped = 1;
|
240 | d7edfd27 | blueswir1 | qemu_irq_lower(s->irq); |
241 | e80cfcfc | bellard | } |
242 | e80cfcfc | bellard | |
243 | d7edfd27 | blueswir1 | void slavio_timer_init(target_phys_addr_t addr, qemu_irq irq, int mode) |
244 | e80cfcfc | bellard | { |
245 | e80cfcfc | bellard | int slavio_timer_io_memory;
|
246 | e80cfcfc | bellard | SLAVIO_TIMERState *s; |
247 | 8d05ea8a | blueswir1 | QEMUBH *bh; |
248 | e80cfcfc | bellard | |
249 | e80cfcfc | bellard | s = qemu_mallocz(sizeof(SLAVIO_TIMERState));
|
250 | e80cfcfc | bellard | if (!s)
|
251 | e80cfcfc | bellard | return;
|
252 | e80cfcfc | bellard | s->irq = irq; |
253 | e80cfcfc | bellard | s->mode = mode; |
254 | 8d05ea8a | blueswir1 | bh = qemu_bh_new(slavio_timer_irq, s); |
255 | 8d05ea8a | blueswir1 | s->timer = ptimer_init(bh); |
256 | 8d05ea8a | blueswir1 | ptimer_set_period(s->timer, 500ULL);
|
257 | e80cfcfc | bellard | |
258 | e80cfcfc | bellard | slavio_timer_io_memory = cpu_register_io_memory(0, slavio_timer_mem_read,
|
259 | e80cfcfc | bellard | slavio_timer_mem_write, s); |
260 | 5aca8c3b | blueswir1 | cpu_register_physical_memory(addr, TIMER_SIZE, slavio_timer_io_memory); |
261 | 8d05ea8a | blueswir1 | register_savevm("slavio_timer", addr, 2, slavio_timer_save, slavio_timer_load, s); |
262 | e80cfcfc | bellard | qemu_register_reset(slavio_timer_reset, s); |
263 | e80cfcfc | bellard | slavio_timer_reset(s); |
264 | e80cfcfc | bellard | } |