Commit 636aaad7b5ab72c03451296412249352817167a9

Authored by j_mayer
1 parent a8dea12f

Embedded PowerPC timers support.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2555 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 205 additions and 7 deletions
hw/ppc.c
... ... @@ -267,26 +267,224 @@ uint32_t cpu_ppc601_load_rtcl (CPUState *env)
267 267 return cpu_ppc_load_tbl(env) & 0x3FFFFF80;
268 268 }
269 269  
  270 +/*****************************************************************************/
270 271 /* Embedded PowerPC timers */
271   -target_ulong load_40x_pit (CPUState *env)
  272 +
  273 +/* PIT, FIT & WDT */
  274 +typedef struct ppcemb_timer_t ppcemb_timer_t;
  275 +struct ppcemb_timer_t {
  276 + uint64_t pit_reload; /* PIT auto-reload value */
  277 + uint64_t fit_next; /* Tick for next FIT interrupt */
  278 + struct QEMUTimer *fit_timer;
  279 + uint64_t wdt_next; /* Tick for next WDT interrupt */
  280 + struct QEMUTimer *wdt_timer;
  281 +};
  282 +
  283 +/* Fixed interval timer */
  284 +static void cpu_4xx_fit_cb (void *opaque)
272 285 {
273   - /* XXX: TODO */
274   - return 0;
  286 + CPUState *env;
  287 + ppc_tb_t *tb_env;
  288 + ppcemb_timer_t *ppcemb_timer;
  289 + uint64_t now, next;
  290 +
  291 + env = opaque;
  292 + tb_env = env->tb_env;
  293 + ppcemb_timer = tb_env->opaque;
  294 + now = qemu_get_clock(vm_clock);
  295 + switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) {
  296 + case 0:
  297 + next = 1 << 9;
  298 + break;
  299 + case 1:
  300 + next = 1 << 13;
  301 + break;
  302 + case 2:
  303 + next = 1 << 17;
  304 + break;
  305 + case 3:
  306 + next = 1 << 21;
  307 + break;
  308 + default:
  309 + /* Cannot occur, but makes gcc happy */
  310 + return;
  311 + }
  312 + next = now + muldiv64(next, ticks_per_sec, tb_env->tb_freq);
  313 + if (next == now)
  314 + next++;
  315 + qemu_mod_timer(ppcemb_timer->fit_timer, next);
  316 + tb_env->decr_next = next;
  317 + env->spr[SPR_40x_TSR] |= 1 << 26;
  318 + if ((env->spr[SPR_40x_TCR] >> 23) & 0x1)
  319 + ppc_set_irq(env, PPC_INTERRUPT_FIT, 1);
  320 + if (loglevel) {
  321 + fprintf(logfile, "%s: ir %d TCR %08x TSR %08x\n", __func__,
  322 + (env->spr[SPR_40x_TCR] >> 23) & 0x1,
  323 + env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
  324 + }
  325 +}
  326 +
  327 +/* Programmable interval timer */
  328 +static void cpu_4xx_pit_cb (void *opaque)
  329 +{
  330 + CPUState *env;
  331 + ppc_tb_t *tb_env;
  332 + ppcemb_timer_t *ppcemb_timer;
  333 + uint64_t now, next;
  334 +
  335 + env = opaque;
  336 + tb_env = env->tb_env;
  337 + ppcemb_timer = tb_env->opaque;
  338 + now = qemu_get_clock(vm_clock);
  339 + if ((env->spr[SPR_40x_TCR] >> 22) & 0x1) {
  340 + /* Auto reload */
  341 + next = now + muldiv64(ppcemb_timer->pit_reload,
  342 + ticks_per_sec, tb_env->tb_freq);
  343 + if (next == now)
  344 + next++;
  345 + qemu_mod_timer(tb_env->decr_timer, next);
  346 + tb_env->decr_next = next;
  347 + }
  348 + env->spr[SPR_40x_TSR] |= 1 << 27;
  349 + if ((env->spr[SPR_40x_TCR] >> 26) & 0x1)
  350 + ppc_set_irq(env, PPC_INTERRUPT_PIT, 1);
  351 + if (loglevel) {
  352 + fprintf(logfile, "%s: ar %d ir %d TCR %08x TSR %08x %08lx\n", __func__,
  353 + (env->spr[SPR_40x_TCR] >> 22) & 0x1,
  354 + (env->spr[SPR_40x_TCR] >> 26) & 0x1,
  355 + env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR],
  356 + ppcemb_timer->pit_reload);
  357 + }
  358 +}
  359 +
  360 +/* Watchdog timer */
  361 +static void cpu_4xx_wdt_cb (void *opaque)
  362 +{
  363 + CPUState *env;
  364 + ppc_tb_t *tb_env;
  365 + ppcemb_timer_t *ppcemb_timer;
  366 + uint64_t now, next;
  367 +
  368 + env = opaque;
  369 + tb_env = env->tb_env;
  370 + ppcemb_timer = tb_env->opaque;
  371 + now = qemu_get_clock(vm_clock);
  372 + switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) {
  373 + case 0:
  374 + next = 1 << 17;
  375 + break;
  376 + case 1:
  377 + next = 1 << 21;
  378 + break;
  379 + case 2:
  380 + next = 1 << 25;
  381 + break;
  382 + case 3:
  383 + next = 1 << 29;
  384 + break;
  385 + default:
  386 + /* Cannot occur, but makes gcc happy */
  387 + return;
  388 + }
  389 + next = now + muldiv64(next, ticks_per_sec, tb_env->tb_freq);
  390 + if (next == now)
  391 + next++;
  392 + if (loglevel) {
  393 + fprintf(logfile, "%s: TCR %08x TSR %08x\n", __func__,
  394 + env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
  395 + }
  396 + switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) {
  397 + case 0x0:
  398 + case 0x1:
  399 + qemu_mod_timer(ppcemb_timer->wdt_timer, next);
  400 + ppcemb_timer->wdt_next = next;
  401 + env->spr[SPR_40x_TSR] |= 1 << 31;
  402 + break;
  403 + case 0x2:
  404 + qemu_mod_timer(ppcemb_timer->wdt_timer, next);
  405 + ppcemb_timer->wdt_next = next;
  406 + env->spr[SPR_40x_TSR] |= 1 << 30;
  407 + if ((env->spr[SPR_40x_TCR] >> 27) & 0x1)
  408 + ppc_set_irq(env, PPC_INTERRUPT_WDT, 1);
  409 + break;
  410 + case 0x3:
  411 + env->spr[SPR_40x_TSR] &= ~0x30000000;
  412 + env->spr[SPR_40x_TSR] |= env->spr[SPR_40x_TCR] & 0x30000000;
  413 + switch ((env->spr[SPR_40x_TCR] >> 28) & 0x3) {
  414 + case 0x0:
  415 + /* No reset */
  416 + break;
  417 + case 0x1: /* Core reset */
  418 + case 0x2: /* Chip reset */
  419 + case 0x3: /* System reset */
  420 + qemu_system_reset_request();
  421 + return;
  422 + }
  423 + }
275 424 }
276 425  
277 426 void store_40x_pit (CPUState *env, target_ulong val)
278 427 {
279   - /* XXX: TODO */
  428 + ppc_tb_t *tb_env;
  429 + ppcemb_timer_t *ppcemb_timer;
  430 + uint64_t now, next;
  431 +
  432 + tb_env = env->tb_env;
  433 + ppcemb_timer = tb_env->opaque;
  434 + if (loglevel)
  435 + fprintf(logfile, "%s %p %p\n", __func__, tb_env, ppcemb_timer);
  436 + ppcemb_timer->pit_reload = val;
  437 + if (val == 0) {
  438 + /* Stop PIT */
  439 + if (loglevel)
  440 + fprintf(logfile, "%s: stop PIT\n", __func__);
  441 + qemu_del_timer(tb_env->decr_timer);
  442 + } else {
  443 + if (loglevel)
  444 + fprintf(logfile, "%s: start PIT 0x%08x\n", __func__, val);
  445 + now = qemu_get_clock(vm_clock);
  446 + next = now + muldiv64(val, ticks_per_sec, tb_env->tb_freq);
  447 + if (next == now)
  448 + next++;
  449 + qemu_mod_timer(tb_env->decr_timer, next);
  450 + tb_env->decr_next = next;
  451 + }
280 452 }
281 453  
282   -void store_booke_tcr (CPUState *env, target_ulong val)
  454 +target_ulong load_40x_pit (CPUState *env)
283 455 {
284   - /* XXX: TODO */
  456 + return cpu_ppc_load_decr(env);
285 457 }
286 458  
287 459 void store_booke_tsr (CPUState *env, target_ulong val)
288 460 {
289   - /* XXX: TODO */
  461 + env->spr[SPR_40x_TSR] = val & 0xFC000000;
  462 +}
  463 +
  464 +void store_booke_tcr (CPUState *env, target_ulong val)
  465 +{
  466 + /* We don't update timers now. Maybe we should... */
  467 + env->spr[SPR_40x_TCR] = val & 0xFF800000;
  468 +}
  469 +
  470 +void ppc_emb_timers_init (CPUState *env)
  471 +{
  472 + ppc_tb_t *tb_env;
  473 + ppcemb_timer_t *ppcemb_timer;
  474 +
  475 + tb_env = env->tb_env;
  476 + ppcemb_timer = qemu_mallocz(sizeof(ppcemb_timer_t));
  477 + tb_env->opaque = ppcemb_timer;
  478 + if (loglevel)
  479 + fprintf(logfile, "%s %p %p\n", __func__, tb_env, ppcemb_timer);
  480 + if (ppcemb_timer != NULL) {
  481 + /* We use decr timer for PIT */
  482 + tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_4xx_pit_cb, env);
  483 + ppcemb_timer->fit_timer =
  484 + qemu_new_timer(vm_clock, &cpu_4xx_fit_cb, env);
  485 + ppcemb_timer->wdt_timer =
  486 + qemu_new_timer(vm_clock, &cpu_4xx_wdt_cb, env);
  487 + }
290 488 }
291 489  
292 490 #if 0
... ...