Revision 115646b6 hw/slavio_timer.c
b/hw/slavio_timer.c | ||
---|---|---|
54 | 54 |
ptimer_state *timer; |
55 | 55 |
uint32_t count, counthigh, reached; |
56 | 56 |
uint64_t limit; |
57 |
int stopped; |
|
58 |
int mode; // 0 = processor, 1 = user, 2 = system |
|
57 |
// processor only |
|
58 |
int running; |
|
59 |
struct SLAVIO_TIMERState *master; |
|
60 |
int slave_index; |
|
61 |
// system only |
|
59 | 62 |
struct SLAVIO_TIMERState *slave[MAX_CPUS]; |
60 | 63 |
uint32_t slave_mode; |
61 | 64 |
} SLAVIO_TIMERState; |
62 | 65 |
|
63 | 66 |
#define TIMER_MAXADDR 0x1f |
64 |
#define TIMER_SIZE (TIMER_MAXADDR + 1)
|
|
67 |
#define SYS_TIMER_SIZE 0x14
|
|
65 | 68 |
#define CPU_TIMER_SIZE 0x10 |
66 | 69 |
|
70 |
static int slavio_timer_is_user(SLAVIO_TIMERState *s) |
|
71 |
{ |
|
72 |
return s->master && (s->master->slave_mode & (1 << s->slave_index)); |
|
73 |
} |
|
74 |
|
|
67 | 75 |
// Update count, set irq, update expire_time |
68 | 76 |
// Convert from ptimer countdown units |
69 | 77 |
static void slavio_timer_get_out(SLAVIO_TIMERState *s) |
... | ... | |
84 | 92 |
|
85 | 93 |
slavio_timer_get_out(s); |
86 | 94 |
DPRINTF("callback: count %x%08x\n", s->counthigh, s->count); |
87 |
s->reached = 0x80000000;
|
|
88 |
if (s->mode != 1)
|
|
95 |
if (!slavio_timer_is_user(s)) {
|
|
96 |
s->reached = 0x80000000;
|
|
89 | 97 |
qemu_irq_raise(s->irq); |
98 |
} |
|
90 | 99 |
} |
91 | 100 |
|
92 | 101 |
static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr) |
... | ... | |
99 | 108 |
case 0: |
100 | 109 |
// read limit (system counter mode) or read most signifying |
101 | 110 |
// part of counter (user mode) |
102 |
if (s->mode != 1) { |
|
111 |
if (slavio_timer_is_user(s)) { |
|
112 |
// read user timer MSW |
|
113 |
slavio_timer_get_out(s); |
|
114 |
ret = s->counthigh; |
|
115 |
} else { |
|
116 |
// read limit |
|
103 | 117 |
// clear irq |
104 | 118 |
qemu_irq_lower(s->irq); |
105 | 119 |
s->reached = 0; |
106 | 120 |
ret = s->limit & 0x7fffffff; |
107 | 121 |
} |
108 |
else { |
|
109 |
slavio_timer_get_out(s); |
|
110 |
ret = s->counthigh & 0x7fffffff; |
|
111 |
} |
|
112 | 122 |
break; |
113 | 123 |
case 1: |
114 | 124 |
// read counter and reached bit (system mode) or read lsbits |
115 | 125 |
// of counter (user mode) |
116 | 126 |
slavio_timer_get_out(s); |
117 |
if (s->mode != 1)
|
|
118 |
ret = (s->count & 0x7fffffff) | s->reached;
|
|
119 |
else |
|
120 |
ret = s->count;
|
|
127 |
if (slavio_timer_is_user(s)) // read user timer LSW
|
|
128 |
ret = s->count & 0xffffffe00;
|
|
129 |
else // read limit
|
|
130 |
ret = (s->count & 0x7ffffe00) | s->reached;
|
|
121 | 131 |
break; |
122 | 132 |
case 3: |
133 |
// only available in processor counter/timer |
|
123 | 134 |
// read start/stop status |
124 |
ret = s->stopped;
|
|
135 |
ret = s->running;
|
|
125 | 136 |
break; |
126 | 137 |
case 4: |
138 |
// only available in system counter |
|
127 | 139 |
// read user/system mode |
128 | 140 |
ret = s->slave_mode; |
129 | 141 |
break; |
130 | 142 |
default: |
143 |
DPRINTF("invalid read address " TARGET_FMT_plx "\n", addr); |
|
131 | 144 |
ret = 0; |
132 | 145 |
break; |
133 | 146 |
} |
... | ... | |
146 | 159 |
saddr = (addr & TIMER_MAXADDR) >> 2; |
147 | 160 |
switch (saddr) { |
148 | 161 |
case 0: |
149 |
if (s->mode == 1) {
|
|
150 |
// set user counter limit MSW, reset counter
|
|
162 |
if (slavio_timer_is_user(s)) {
|
|
163 |
// set user counter MSW, reset counter |
|
151 | 164 |
qemu_irq_lower(s->irq); |
152 |
s->limit &= 0xfffffe00ULL; |
|
153 |
s->limit |= (uint64_t)val << 32; |
|
165 |
s->limit = 0x7ffffffffffffe00ULL; |
|
166 |
DPRINTF("processor %d user timer reset\n", s->slave_index); |
|
167 |
ptimer_set_limit(s->timer, s->limit >> 9, 1); |
|
168 |
} else { |
|
169 |
// set limit, reset counter |
|
170 |
qemu_irq_lower(s->irq); |
|
171 |
s->limit = val & 0x7ffffe00ULL; |
|
154 | 172 |
if (!s->limit) |
155 |
s->limit = 0x7ffffffffffffe00ULL;
|
|
173 |
s->limit = 0x7ffffe00ULL; |
|
156 | 174 |
ptimer_set_limit(s->timer, s->limit >> 9, 1); |
157 |
break; |
|
158 | 175 |
} |
159 |
// set limit, reset counter |
|
160 |
reload = 1; |
|
161 |
qemu_irq_lower(s->irq); |
|
162 |
// fall through |
|
176 |
break; |
|
177 |
case 1: |
|
178 |
if (slavio_timer_is_user(s)) { |
|
179 |
// set user counter LSW, reset counter |
|
180 |
qemu_irq_lower(s->irq); |
|
181 |
s->limit = 0x7ffffffffffffe00ULL; |
|
182 |
DPRINTF("processor %d user timer reset\n", s->slave_index); |
|
183 |
ptimer_set_limit(s->timer, s->limit >> 9, 1); |
|
184 |
} else |
|
185 |
DPRINTF("not user timer\n"); |
|
186 |
break; |
|
163 | 187 |
case 2: |
164 | 188 |
// set limit without resetting counter |
165 | 189 |
s->limit = val & 0x7ffffe00ULL; |
... | ... | |
167 | 191 |
s->limit = 0x7ffffe00ULL; |
168 | 192 |
ptimer_set_limit(s->timer, s->limit >> 9, reload); |
169 | 193 |
break; |
170 |
case 1: |
|
171 |
// set user counter limit LSW, reset counter |
|
172 |
if (s->mode == 1) { |
|
173 |
qemu_irq_lower(s->irq); |
|
174 |
s->limit &= 0x7fffffff00000000ULL; |
|
175 |
s->limit |= val & 0xfffffe00ULL; |
|
176 |
if (!s->limit) |
|
177 |
s->limit = 0x7ffffffffffffe00ULL; |
|
178 |
ptimer_set_limit(s->timer, s->limit >> 9, 1); |
|
179 |
} |
|
180 |
break; |
|
181 | 194 |
case 3: |
182 |
// start/stop user counter |
|
183 |
if (s->mode == 1) { |
|
184 |
if (val & 1) { |
|
185 |
ptimer_stop(s->timer); |
|
186 |
s->stopped = 1; |
|
187 |
} |
|
188 |
else { |
|
195 |
if (slavio_timer_is_user(s)) { |
|
196 |
// start/stop user counter |
|
197 |
if ((val & 1) && !s->running) { |
|
198 |
DPRINTF("processor %d user timer started\n", s->slave_index); |
|
189 | 199 |
ptimer_run(s->timer, 0); |
190 |
s->stopped = 0; |
|
200 |
s->running = 1; |
|
201 |
} else if (!(val & 1) && s->running) { |
|
202 |
DPRINTF("processor %d user timer stopped\n", s->slave_index); |
|
203 |
ptimer_stop(s->timer); |
|
204 |
s->running = 0; |
|
191 | 205 |
} |
192 | 206 |
} |
193 | 207 |
break; |
194 | 208 |
case 4: |
195 |
// bit 0: user (1) or system (0) counter mode |
|
196 |
{ |
|
209 |
if (s->master == NULL) { |
|
197 | 210 |
unsigned int i; |
198 | 211 |
|
199 | 212 |
for (i = 0; i < MAX_CPUS; i++) { |
200 | 213 |
if (val & (1 << i)) { |
201 | 214 |
qemu_irq_lower(s->slave[i]->irq); |
202 | 215 |
s->slave[i]->limit = -1ULL; |
203 |
s->slave[i]->mode = 1; |
|
204 |
} else { |
|
205 |
s->slave[i]->mode = 0; |
|
206 | 216 |
} |
207 |
ptimer_stop(s->slave[i]->timer); |
|
208 |
ptimer_set_limit(s->slave[i]->timer, s->slave[i]->limit >> 9, |
|
209 |
1); |
|
210 |
ptimer_run(s->slave[i]->timer, 0); |
|
217 |
if ((val & (1 << i)) != (s->slave_mode & (1 << i))) { |
|
218 |
ptimer_stop(s->slave[i]->timer); |
|
219 |
ptimer_set_limit(s->slave[i]->timer, s->slave[i]->limit >> 9, 1); |
|
220 |
DPRINTF("processor %d timer changed\n", s->slave[i]->slave_index); |
|
221 |
ptimer_run(s->slave[i]->timer, 0); |
|
222 |
} |
|
211 | 223 |
} |
212 | 224 |
s->slave_mode = val & ((1 << MAX_CPUS) - 1); |
213 |
} |
|
225 |
} else |
|
226 |
DPRINTF("not system timer\n"); |
|
214 | 227 |
break; |
215 | 228 |
default: |
229 |
DPRINTF("invalid write address " TARGET_FMT_plx "\n", addr); |
|
216 | 230 |
break; |
217 | 231 |
} |
218 | 232 |
} |
... | ... | |
238 | 252 |
qemu_put_be32s(f, &s->counthigh); |
239 | 253 |
qemu_put_be32(f, 0); // Was irq |
240 | 254 |
qemu_put_be32s(f, &s->reached); |
241 |
qemu_put_be32s(f, &s->stopped);
|
|
242 |
qemu_put_be32s(f, &s->mode);
|
|
255 |
qemu_put_be32s(f, &s->running);
|
|
256 |
qemu_put_be32s(f, 0); // Was mode
|
|
243 | 257 |
qemu_put_ptimer(f, s->timer); |
244 | 258 |
} |
245 | 259 |
|
... | ... | |
256 | 270 |
qemu_get_be32s(f, &s->counthigh); |
257 | 271 |
qemu_get_be32s(f, &tmp); // Was irq |
258 | 272 |
qemu_get_be32s(f, &s->reached); |
259 |
qemu_get_be32s(f, &s->stopped);
|
|
260 |
qemu_get_be32s(f, &s->mode);
|
|
273 |
qemu_get_be32s(f, &s->running);
|
|
274 |
qemu_get_be32s(f, &tmp); // Was mode
|
|
261 | 275 |
qemu_get_ptimer(f, s->timer); |
262 | 276 |
|
263 | 277 |
return 0; |
... | ... | |
267 | 281 |
{ |
268 | 282 |
SLAVIO_TIMERState *s = opaque; |
269 | 283 |
|
270 |
s->limit = 0x7ffffe00ULL; |
|
284 |
if (slavio_timer_is_user(s)) |
|
285 |
s->limit = 0x7ffffffffffffe00ULL; |
|
286 |
else |
|
287 |
s->limit = 0x7ffffe00ULL; |
|
271 | 288 |
s->count = 0; |
272 | 289 |
s->reached = 0; |
273 |
s->mode &= 2; |
|
274 | 290 |
ptimer_set_limit(s->timer, s->limit >> 9, 1); |
275 | 291 |
ptimer_run(s->timer, 0); |
276 |
s->stopped = 1;
|
|
292 |
s->running = 1;
|
|
277 | 293 |
qemu_irq_lower(s->irq); |
278 | 294 |
} |
279 | 295 |
|
280 | 296 |
static SLAVIO_TIMERState *slavio_timer_init(target_phys_addr_t addr, |
281 |
qemu_irq irq, int mode) |
|
297 |
qemu_irq irq, |
|
298 |
SLAVIO_TIMERState *master, |
|
299 |
int slave_index) |
|
282 | 300 |
{ |
283 | 301 |
int slavio_timer_io_memory; |
284 | 302 |
SLAVIO_TIMERState *s; |
... | ... | |
288 | 306 |
if (!s) |
289 | 307 |
return s; |
290 | 308 |
s->irq = irq; |
291 |
s->mode = mode; |
|
309 |
s->master = master; |
|
310 |
s->slave_index = slave_index; |
|
292 | 311 |
bh = qemu_bh_new(slavio_timer_irq, s); |
293 | 312 |
s->timer = ptimer_init(bh); |
294 | 313 |
ptimer_set_period(s->timer, 500ULL); |
295 | 314 |
|
296 | 315 |
slavio_timer_io_memory = cpu_register_io_memory(0, slavio_timer_mem_read, |
297 | 316 |
slavio_timer_mem_write, s); |
298 |
if (mode < 2)
|
|
317 |
if (master)
|
|
299 | 318 |
cpu_register_physical_memory(addr, CPU_TIMER_SIZE, slavio_timer_io_memory); |
300 | 319 |
else |
301 |
cpu_register_physical_memory(addr, TIMER_SIZE, |
|
302 |
slavio_timer_io_memory); |
|
320 |
cpu_register_physical_memory(addr, SYS_TIMER_SIZE, slavio_timer_io_memory); |
|
303 | 321 |
register_savevm("slavio_timer", addr, 2, slavio_timer_save, slavio_timer_load, s); |
304 | 322 |
qemu_register_reset(slavio_timer_reset, s); |
305 | 323 |
slavio_timer_reset(s); |
... | ... | |
313 | 331 |
SLAVIO_TIMERState *master; |
314 | 332 |
unsigned int i; |
315 | 333 |
|
316 |
master = slavio_timer_init(base + 0x10000ULL, master_irq, 2);
|
|
334 |
master = slavio_timer_init(base + 0x10000ULL, master_irq, NULL, 0);
|
|
317 | 335 |
|
318 | 336 |
for (i = 0; i < MAX_CPUS; i++) { |
319 | 337 |
master->slave[i] = slavio_timer_init(base + (target_phys_addr_t) |
320 | 338 |
(i * TARGET_PAGE_SIZE), |
321 |
cpu_irqs[i], 0);
|
|
339 |
cpu_irqs[i], master, i);
|
|
322 | 340 |
} |
323 | 341 |
} |
Also available in: Unified diff