aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>2007-03-31 11:38:38 +0000
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>2007-03-31 11:38:38 +0000
commit636aaad7b5ab72c03451296412249352817167a9 (patch)
treeab44fe06d056967f4b8e3e6b6374edeb72ab81a4 /hw
parenta8dea12f453851d68c968d23faff2f1b9205d811 (diff)
Embedded PowerPC timers support.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2555 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw')
-rw-r--r--hw/ppc.c212
1 files changed, 205 insertions, 7 deletions
diff --git a/hw/ppc.c b/hw/ppc.c
index 04242ac90a..273c75f026 100644
--- a/hw/ppc.c
+++ b/hw/ppc.c
@@ -267,26 +267,224 @@ uint32_t cpu_ppc601_load_rtcl (CPUState *env)
return cpu_ppc_load_tbl(env) & 0x3FFFFF80;
}
+/*****************************************************************************/
/* Embedded PowerPC timers */
-target_ulong load_40x_pit (CPUState *env)
+
+/* PIT, FIT & WDT */
+typedef struct ppcemb_timer_t ppcemb_timer_t;
+struct ppcemb_timer_t {
+ uint64_t pit_reload; /* PIT auto-reload value */
+ uint64_t fit_next; /* Tick for next FIT interrupt */
+ struct QEMUTimer *fit_timer;
+ uint64_t wdt_next; /* Tick for next WDT interrupt */
+ struct QEMUTimer *wdt_timer;
+};
+
+/* Fixed interval timer */
+static void cpu_4xx_fit_cb (void *opaque)
{
- /* XXX: TODO */
- return 0;
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+ uint64_t now, next;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ ppcemb_timer = tb_env->opaque;
+ now = qemu_get_clock(vm_clock);
+ switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) {
+ case 0:
+ next = 1 << 9;
+ break;
+ case 1:
+ next = 1 << 13;
+ break;
+ case 2:
+ next = 1 << 17;
+ break;
+ case 3:
+ next = 1 << 21;
+ break;
+ default:
+ /* Cannot occur, but makes gcc happy */
+ return;
+ }
+ next = now + muldiv64(next, ticks_per_sec, tb_env->tb_freq);
+ if (next == now)
+ next++;
+ qemu_mod_timer(ppcemb_timer->fit_timer, next);
+ tb_env->decr_next = next;
+ env->spr[SPR_40x_TSR] |= 1 << 26;
+ if ((env->spr[SPR_40x_TCR] >> 23) & 0x1)
+ ppc_set_irq(env, PPC_INTERRUPT_FIT, 1);
+ if (loglevel) {
+ fprintf(logfile, "%s: ir %d TCR %08x TSR %08x\n", __func__,
+ (env->spr[SPR_40x_TCR] >> 23) & 0x1,
+ env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
+ }
+}
+
+/* Programmable interval timer */
+static void cpu_4xx_pit_cb (void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+ uint64_t now, next;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ ppcemb_timer = tb_env->opaque;
+ now = qemu_get_clock(vm_clock);
+ if ((env->spr[SPR_40x_TCR] >> 22) & 0x1) {
+ /* Auto reload */
+ next = now + muldiv64(ppcemb_timer->pit_reload,
+ ticks_per_sec, tb_env->tb_freq);
+ if (next == now)
+ next++;
+ qemu_mod_timer(tb_env->decr_timer, next);
+ tb_env->decr_next = next;
+ }
+ env->spr[SPR_40x_TSR] |= 1 << 27;
+ if ((env->spr[SPR_40x_TCR] >> 26) & 0x1)
+ ppc_set_irq(env, PPC_INTERRUPT_PIT, 1);
+ if (loglevel) {
+ fprintf(logfile, "%s: ar %d ir %d TCR %08x TSR %08x %08lx\n", __func__,
+ (env->spr[SPR_40x_TCR] >> 22) & 0x1,
+ (env->spr[SPR_40x_TCR] >> 26) & 0x1,
+ env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR],
+ ppcemb_timer->pit_reload);
+ }
+}
+
+/* Watchdog timer */
+static void cpu_4xx_wdt_cb (void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+ uint64_t now, next;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ ppcemb_timer = tb_env->opaque;
+ now = qemu_get_clock(vm_clock);
+ switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) {
+ case 0:
+ next = 1 << 17;
+ break;
+ case 1:
+ next = 1 << 21;
+ break;
+ case 2:
+ next = 1 << 25;
+ break;
+ case 3:
+ next = 1 << 29;
+ break;
+ default:
+ /* Cannot occur, but makes gcc happy */
+ return;
+ }
+ next = now + muldiv64(next, ticks_per_sec, tb_env->tb_freq);
+ if (next == now)
+ next++;
+ if (loglevel) {
+ fprintf(logfile, "%s: TCR %08x TSR %08x\n", __func__,
+ env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
+ }
+ switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) {
+ case 0x0:
+ case 0x1:
+ qemu_mod_timer(ppcemb_timer->wdt_timer, next);
+ ppcemb_timer->wdt_next = next;
+ env->spr[SPR_40x_TSR] |= 1 << 31;
+ break;
+ case 0x2:
+ qemu_mod_timer(ppcemb_timer->wdt_timer, next);
+ ppcemb_timer->wdt_next = next;
+ env->spr[SPR_40x_TSR] |= 1 << 30;
+ if ((env->spr[SPR_40x_TCR] >> 27) & 0x1)
+ ppc_set_irq(env, PPC_INTERRUPT_WDT, 1);
+ break;
+ case 0x3:
+ env->spr[SPR_40x_TSR] &= ~0x30000000;
+ env->spr[SPR_40x_TSR] |= env->spr[SPR_40x_TCR] & 0x30000000;
+ switch ((env->spr[SPR_40x_TCR] >> 28) & 0x3) {
+ case 0x0:
+ /* No reset */
+ break;
+ case 0x1: /* Core reset */
+ case 0x2: /* Chip reset */
+ case 0x3: /* System reset */
+ qemu_system_reset_request();
+ return;
+ }
+ }
}
void store_40x_pit (CPUState *env, target_ulong val)
{
- /* XXX: TODO */
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+ uint64_t now, next;
+
+ tb_env = env->tb_env;
+ ppcemb_timer = tb_env->opaque;
+ if (loglevel)
+ fprintf(logfile, "%s %p %p\n", __func__, tb_env, ppcemb_timer);
+ ppcemb_timer->pit_reload = val;
+ if (val == 0) {
+ /* Stop PIT */
+ if (loglevel)
+ fprintf(logfile, "%s: stop PIT\n", __func__);
+ qemu_del_timer(tb_env->decr_timer);
+ } else {
+ if (loglevel)
+ fprintf(logfile, "%s: start PIT 0x%08x\n", __func__, val);
+ now = qemu_get_clock(vm_clock);
+ next = now + muldiv64(val, ticks_per_sec, tb_env->tb_freq);
+ if (next == now)
+ next++;
+ qemu_mod_timer(tb_env->decr_timer, next);
+ tb_env->decr_next = next;
+ }
}
-void store_booke_tcr (CPUState *env, target_ulong val)
+target_ulong load_40x_pit (CPUState *env)
{
- /* XXX: TODO */
+ return cpu_ppc_load_decr(env);
}
void store_booke_tsr (CPUState *env, target_ulong val)
{
- /* XXX: TODO */
+ env->spr[SPR_40x_TSR] = val & 0xFC000000;
+}
+
+void store_booke_tcr (CPUState *env, target_ulong val)
+{
+ /* We don't update timers now. Maybe we should... */
+ env->spr[SPR_40x_TCR] = val & 0xFF800000;
+}
+
+void ppc_emb_timers_init (CPUState *env)
+{
+ ppc_tb_t *tb_env;
+ ppcemb_timer_t *ppcemb_timer;
+
+ tb_env = env->tb_env;
+ ppcemb_timer = qemu_mallocz(sizeof(ppcemb_timer_t));
+ tb_env->opaque = ppcemb_timer;
+ if (loglevel)
+ fprintf(logfile, "%s %p %p\n", __func__, tb_env, ppcemb_timer);
+ if (ppcemb_timer != NULL) {
+ /* We use decr timer for PIT */
+ tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_4xx_pit_cb, env);
+ ppcemb_timer->fit_timer =
+ qemu_new_timer(vm_clock, &cpu_4xx_fit_cb, env);
+ ppcemb_timer->wdt_timer =
+ qemu_new_timer(vm_clock, &cpu_4xx_wdt_cb, env);
+ }
}
#if 0