summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabien Chouteau <chouteau@adacore.com>2011-09-13 04:00:32 +0000
committerAlexander Graf <agraf@suse.de>2011-10-06 09:48:09 +0200
commitddd1055b07fdfe488a22c2275adaca75f4206d30 (patch)
tree562b06cea9452d0bdfca3e5a0928eefe736265c9
parent94135e813c14bac3f967e6b5aa35b9d617737e68 (diff)
PPC: booke timers
While working on the emulation of the freescale p2010 (e500v2) I realized that there's no implementation of booke's timers features. Currently mpc8544 uses ppc_emb (ppc_emb_timers_init) which is close but not exactly like booke (for example booke uses different SPR). Signed-off-by: Fabien Chouteau <chouteau@adacore.com> Signed-off-by: Alexander Graf <agraf@suse.de>
-rw-r--r--Makefile.target2
-rw-r--r--hw/ppc.c138
-rw-r--r--hw/ppc.h37
-rw-r--r--hw/ppc4xx_devs.c2
-rw-r--r--hw/ppc_booke.c266
-rw-r--r--hw/ppce500_mpc8544ds.c6
-rw-r--r--hw/virtex_ml507.c11
-rw-r--r--target-ppc/cpu.h27
-rw-r--r--target-ppc/translate_init.c39
9 files changed, 427 insertions, 101 deletions
diff --git a/Makefile.target b/Makefile.target
index fc3dfaabe..1e9815cb2 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -229,7 +229,7 @@ obj-i386-$(CONFIG_KVM) += kvmclock.o
obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
# shared objects
-obj-ppc-y = ppc.o
+obj-ppc-y = ppc.o ppc_booke.o
obj-ppc-y += vga.o
# PREP target
obj-ppc-y += i8259.o mc146818rtc.o
diff --git a/hw/ppc.c b/hw/ppc.c
index 887074822..25b59ddda 100644
--- a/hw/ppc.c
+++ b/hw/ppc.c
@@ -50,7 +50,7 @@
static void cpu_ppc_tb_stop (CPUState *env);
static void cpu_ppc_tb_start (CPUState *env);
-static void ppc_set_irq (CPUState *env, int n_IRQ, int level)
+void ppc_set_irq(CPUState *env, int n_IRQ, int level)
{
unsigned int old_pending = env->pending_interrupts;
@@ -423,25 +423,8 @@ void ppce500_irq_init (CPUState *env)
}
/*****************************************************************************/
/* PowerPC time base and decrementer emulation */
-struct ppc_tb_t {
- /* Time base management */
- int64_t tb_offset; /* Compensation */
- int64_t atb_offset; /* Compensation */
- uint32_t tb_freq; /* TB frequency */
- /* Decrementer management */
- uint64_t decr_next; /* Tick for next decr interrupt */
- uint32_t decr_freq; /* decrementer frequency */
- struct QEMUTimer *decr_timer;
- /* Hypervisor decrementer management */
- uint64_t hdecr_next; /* Tick for next hdecr interrupt */
- struct QEMUTimer *hdecr_timer;
- uint64_t purr_load;
- uint64_t purr_start;
- void *opaque;
-};
-static inline uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk,
- int64_t tb_offset)
+uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset)
{
/* TB time in tb periods */
return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset;
@@ -611,10 +594,13 @@ static inline uint32_t _cpu_ppc_load_decr(CPUState *env, uint64_t next)
int64_t diff;
diff = next - qemu_get_clock_ns(vm_clock);
- if (diff >= 0)
+ if (diff >= 0) {
decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec());
- else
+ } else if (tb_env->flags & PPC_TIMER_BOOKE) {
+ decr = 0;
+ } else {
decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec());
+ }
LOG_TB("%s: %08" PRIx32 "\n", __func__, decr);
return decr;
@@ -678,18 +664,24 @@ static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp,
decr, value);
now = qemu_get_clock_ns(vm_clock);
next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq);
- if (is_excp)
+ if (is_excp) {
next += *nextp - now;
- if (next == now)
+ }
+ if (next == now) {
next++;
+ }
*nextp = next;
/* Adjust timer */
qemu_mod_timer(timer, next);
- /* If we set a negative value and the decrementer was positive,
- * raise an exception.
+
+ /* If we set a negative value and the decrementer was positive, raise an
+ * exception.
*/
- if ((value & 0x80000000) && !(decr & 0x80000000))
+ if ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED)
+ && (value & 0x80000000)
+ && !(decr & 0x80000000)) {
(*raise_excp)(env);
+ }
}
static inline void _cpu_ppc_store_decr(CPUState *env, uint32_t decr,
@@ -763,6 +755,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq)
tb_env = g_malloc0(sizeof(ppc_tb_t));
env->tb_env = tb_env;
+ tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED;
/* Create new timer */
tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_decr_cb, env);
if (0) {
@@ -806,11 +799,11 @@ uint32_t cpu_ppc601_load_rtcl (CPUState *env)
}
/*****************************************************************************/
-/* Embedded PowerPC timers */
+/* PowerPC 40x timers */
/* PIT, FIT & WDT */
-typedef struct ppcemb_timer_t ppcemb_timer_t;
-struct ppcemb_timer_t {
+typedef struct ppc40x_timer_t ppc40x_timer_t;
+struct ppc40x_timer_t {
uint64_t pit_reload; /* PIT auto-reload value */
uint64_t fit_next; /* Tick for next FIT interrupt */
struct QEMUTimer *fit_timer;
@@ -826,12 +819,12 @@ static void cpu_4xx_fit_cb (void *opaque)
{
CPUState *env;
ppc_tb_t *tb_env;
- ppcemb_timer_t *ppcemb_timer;
+ ppc40x_timer_t *ppc40x_timer;
uint64_t now, next;
env = opaque;
tb_env = env->tb_env;
- ppcemb_timer = tb_env->opaque;
+ ppc40x_timer = tb_env->opaque;
now = qemu_get_clock_ns(vm_clock);
switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) {
case 0:
@@ -853,7 +846,7 @@ static void cpu_4xx_fit_cb (void *opaque)
next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq);
if (next == now)
next++;
- qemu_mod_timer(ppcemb_timer->fit_timer, next);
+ qemu_mod_timer(ppc40x_timer->fit_timer, next);
env->spr[SPR_40x_TSR] |= 1 << 26;
if ((env->spr[SPR_40x_TCR] >> 23) & 0x1)
ppc_set_irq(env, PPC_INTERRUPT_FIT, 1);
@@ -865,11 +858,11 @@ static void cpu_4xx_fit_cb (void *opaque)
/* Programmable interval timer */
static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp)
{
- ppcemb_timer_t *ppcemb_timer;
+ ppc40x_timer_t *ppc40x_timer;
uint64_t now, next;
- ppcemb_timer = tb_env->opaque;
- if (ppcemb_timer->pit_reload <= 1 ||
+ ppc40x_timer = tb_env->opaque;
+ if (ppc40x_timer->pit_reload <= 1 ||
!((env->spr[SPR_40x_TCR] >> 26) & 0x1) ||
(is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) {
/* Stop PIT */
@@ -877,9 +870,9 @@ static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp)
qemu_del_timer(tb_env->decr_timer);
} else {
LOG_TB("%s: start PIT %016" PRIx64 "\n",
- __func__, ppcemb_timer->pit_reload);
+ __func__, ppc40x_timer->pit_reload);
now = qemu_get_clock_ns(vm_clock);
- next = now + muldiv64(ppcemb_timer->pit_reload,
+ next = now + muldiv64(ppc40x_timer->pit_reload,
get_ticks_per_sec(), tb_env->decr_freq);
if (is_excp)
next += tb_env->decr_next - now;
@@ -894,21 +887,21 @@ static void cpu_4xx_pit_cb (void *opaque)
{
CPUState *env;
ppc_tb_t *tb_env;
- ppcemb_timer_t *ppcemb_timer;
+ ppc40x_timer_t *ppc40x_timer;
env = opaque;
tb_env = env->tb_env;
- ppcemb_timer = tb_env->opaque;
+ ppc40x_timer = tb_env->opaque;
env->spr[SPR_40x_TSR] |= 1 << 27;
if ((env->spr[SPR_40x_TCR] >> 26) & 0x1)
- ppc_set_irq(env, ppcemb_timer->decr_excp, 1);
+ ppc_set_irq(env, ppc40x_timer->decr_excp, 1);
start_stop_pit(env, tb_env, 1);
LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " "
"%016" PRIx64 "\n", __func__,
(int)((env->spr[SPR_40x_TCR] >> 22) & 0x1),
(int)((env->spr[SPR_40x_TCR] >> 26) & 0x1),
env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR],
- ppcemb_timer->pit_reload);
+ ppc40x_timer->pit_reload);
}
/* Watchdog timer */
@@ -916,12 +909,12 @@ static void cpu_4xx_wdt_cb (void *opaque)
{
CPUState *env;
ppc_tb_t *tb_env;
- ppcemb_timer_t *ppcemb_timer;
+ ppc40x_timer_t *ppc40x_timer;
uint64_t now, next;
env = opaque;
tb_env = env->tb_env;
- ppcemb_timer = tb_env->opaque;
+ ppc40x_timer = tb_env->opaque;
now = qemu_get_clock_ns(vm_clock);
switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) {
case 0:
@@ -948,13 +941,13 @@ static void cpu_4xx_wdt_cb (void *opaque)
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;
+ qemu_mod_timer(ppc40x_timer->wdt_timer, next);
+ ppc40x_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;
+ qemu_mod_timer(ppc40x_timer->wdt_timer, next);
+ ppc40x_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);
@@ -982,12 +975,12 @@ static void cpu_4xx_wdt_cb (void *opaque)
void store_40x_pit (CPUState *env, target_ulong val)
{
ppc_tb_t *tb_env;
- ppcemb_timer_t *ppcemb_timer;
+ ppc40x_timer_t *ppc40x_timer;
tb_env = env->tb_env;
- ppcemb_timer = tb_env->opaque;
+ ppc40x_timer = tb_env->opaque;
LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val);
- ppcemb_timer->pit_reload = val;
+ ppc40x_timer->pit_reload = val;
start_stop_pit(env, tb_env, 0);
}
@@ -996,31 +989,7 @@ target_ulong load_40x_pit (CPUState *env)
return cpu_ppc_load_decr(env);
}
-void store_booke_tsr (CPUState *env, target_ulong val)
-{
- ppc_tb_t *tb_env = env->tb_env;
- ppcemb_timer_t *ppcemb_timer;
-
- ppcemb_timer = tb_env->opaque;
-
- LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val);
- env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000);
- if (val & 0x80000000)
- ppc_set_irq(env, ppcemb_timer->decr_excp, 0);
-}
-
-void store_booke_tcr (CPUState *env, target_ulong val)
-{
- ppc_tb_t *tb_env;
-
- tb_env = env->tb_env;
- LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val);
- env->spr[SPR_40x_TCR] = val & 0xFFC00000;
- start_stop_pit(env, tb_env, 1);
- cpu_4xx_wdt_cb(env);
-}
-
-static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq)
+static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq)
{
CPUState *env = opaque;
ppc_tb_t *tb_env = env->tb_env;
@@ -1032,30 +1001,31 @@ static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq)
/* XXX: we should also update all timers */
}
-clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq,
+clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq,
unsigned int decr_excp)
{
ppc_tb_t *tb_env;
- ppcemb_timer_t *ppcemb_timer;
+ ppc40x_timer_t *ppc40x_timer;
tb_env = g_malloc0(sizeof(ppc_tb_t));
env->tb_env = tb_env;
- ppcemb_timer = g_malloc0(sizeof(ppcemb_timer_t));
+ tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED;
+ ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t));
tb_env->tb_freq = freq;
tb_env->decr_freq = freq;
- tb_env->opaque = ppcemb_timer;
+ tb_env->opaque = ppc40x_timer;
LOG_TB("%s freq %" PRIu32 "\n", __func__, freq);
- if (ppcemb_timer != NULL) {
+ if (ppc40x_timer != NULL) {
/* We use decr timer for PIT */
tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_pit_cb, env);
- ppcemb_timer->fit_timer =
+ ppc40x_timer->fit_timer =
qemu_new_timer_ns(vm_clock, &cpu_4xx_fit_cb, env);
- ppcemb_timer->wdt_timer =
+ ppc40x_timer->wdt_timer =
qemu_new_timer_ns(vm_clock, &cpu_4xx_wdt_cb, env);
- ppcemb_timer->decr_excp = decr_excp;
+ ppc40x_timer->decr_excp = decr_excp;
}
- return &ppc_emb_set_tb_clk;
+ return &ppc_40x_set_tb_clk;
}
/*****************************************************************************/
diff --git a/hw/ppc.h b/hw/ppc.h
index 3ccf13479..9f911704a 100644
--- a/hw/ppc.h
+++ b/hw/ppc.h
@@ -1,3 +1,5 @@
+void ppc_set_irq (CPUState *env, int n_IRQ, int level);
+
/* PowerPC hardware exceptions management helpers */
typedef void (*clk_setup_cb)(void *opaque, uint32_t freq);
typedef struct clk_setup_t clk_setup_t;
@@ -11,6 +13,36 @@ static inline void clk_setup (clk_setup_t *clk, uint32_t freq)
(*clk->cb)(clk->opaque, freq);
}
+struct ppc_tb_t {
+ /* Time base management */
+ int64_t tb_offset; /* Compensation */
+ int64_t atb_offset; /* Compensation */
+ uint32_t tb_freq; /* TB frequency */
+ /* Decrementer management */
+ uint64_t decr_next; /* Tick for next decr interrupt */
+ uint32_t decr_freq; /* decrementer frequency */
+ struct QEMUTimer *decr_timer;
+ /* Hypervisor decrementer management */
+ uint64_t hdecr_next; /* Tick for next hdecr interrupt */
+ struct QEMUTimer *hdecr_timer;
+ uint64_t purr_load;
+ uint64_t purr_start;
+ void *opaque;
+ uint32_t flags;
+};
+
+/* PPC Timers flags */
+#define PPC_TIMER_BOOKE (1 << 0) /* Enable Booke support */
+#define PPC_TIMER_E500 (1 << 1) /* Enable e500 support */
+#define PPC_DECR_UNDERFLOW_TRIGGERED (1 << 2) /* Decr interrupt triggered when
+ * the most significant bit
+ * changes from 0 to 1.
+ */
+#define PPC_DECR_ZERO_TRIGGERED (1 << 3) /* Decr interrupt triggered when
+ * the decrementer reaches zero.
+ */
+
+uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset);
clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq);
/* Embedded PowerPC DCR management */
typedef uint32_t (*dcr_read_cb)(void *opaque, int dcrn);
@@ -19,7 +51,7 @@ int ppc_dcr_init (CPUState *env, int (*dcr_read_error)(int dcrn),
int (*dcr_write_error)(int dcrn));
int ppc_dcr_register (CPUState *env, int dcrn, void *opaque,
dcr_read_cb drc_read, dcr_write_cb dcr_write);
-clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq,
+clk_setup_cb ppc_40x_timers_init (CPUState *env, uint32_t freq,
unsigned int decr_excp);
/* Embedded PowerPC reset */
@@ -55,3 +87,6 @@ enum {
#define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07)
#define PPC_SERIAL_MM_BAUDBASE 399193
+
+/* ppc_booke.c */
+void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags);
diff --git a/hw/ppc4xx_devs.c b/hw/ppc4xx_devs.c
index 349f046b2..d18caa419 100644
--- a/hw/ppc4xx_devs.c
+++ b/hw/ppc4xx_devs.c
@@ -56,7 +56,7 @@ CPUState *ppc4xx_init (const char *cpu_model,
cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
cpu_clk->opaque = env;
/* Set time-base frequency to sysclk */
- tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_PIT);
+ tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT);
tb_clk->opaque = env;
ppc_dcr_init(env, NULL, NULL);
/* Register qemu callbacks */
diff --git a/hw/ppc_booke.c b/hw/ppc_booke.c
new file mode 100644
index 000000000..3e2fff588
--- /dev/null
+++ b/hw/ppc_booke.c
@@ -0,0 +1,266 @@
+/*
+ * QEMU PowerPC Booke hardware System Emulator
+ *
+ * Copyright (c) 2011 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "ppc.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+#include "nvram.h"
+#include "qemu-log.h"
+#include "loader.h"
+
+
+/* Timer Control Register */
+
+#define TCR_WP_SHIFT 30 /* Watchdog Timer Period */
+#define TCR_WP_MASK (0x3 << TCR_WP_SHIFT)
+#define TCR_WRC_SHIFT 28 /* Watchdog Timer Reset Control */
+#define TCR_WRC_MASK (0x3 << TCR_WRC_SHIFT)
+#define TCR_WIE (1 << 27) /* Watchdog Timer Interrupt Enable */
+#define TCR_DIE (1 << 26) /* Decrementer Interrupt Enable */
+#define TCR_FP_SHIFT 24 /* Fixed-Interval Timer Period */
+#define TCR_FP_MASK (0x3 << TCR_FP_SHIFT)
+#define TCR_FIE (1 << 23) /* Fixed-Interval Timer Interrupt Enable */
+#define TCR_ARE (1 << 22) /* Auto-Reload Enable */
+
+/* Timer Control Register (e500 specific fields) */
+
+#define TCR_E500_FPEXT_SHIFT 13 /* Fixed-Interval Timer Period Extension */
+#define TCR_E500_FPEXT_MASK (0xf << TCR_E500_FPEXT_SHIFT)
+#define TCR_E500_WPEXT_SHIFT 17 /* Watchdog Timer Period Extension */
+#define TCR_E500_WPEXT_MASK (0xf << TCR_E500_WPEXT_SHIFT)
+
+/* Timer Status Register */
+
+#define TSR_FIS (1 << 26) /* Fixed-Interval Timer Interrupt Status */
+#define TSR_DIS (1 << 27) /* Decrementer Interrupt Status */
+#define TSR_WRS_SHIFT 28 /* Watchdog Timer Reset Status */
+#define TSR_WRS_MASK (0x3 << TSR_WRS_SHIFT)
+#define TSR_WIS (1 << 30) /* Watchdog Timer Interrupt Status */
+#define TSR_ENW (1 << 31) /* Enable Next Watchdog Timer */
+
+typedef struct booke_timer_t booke_timer_t;
+struct booke_timer_t {
+
+ uint64_t fit_next;
+ struct QEMUTimer *fit_timer;
+
+ uint64_t wdt_next;
+ struct QEMUTimer *wdt_timer;
+
+ uint32_t flags;
+};
+
+static void booke_update_irq(CPUState *env)
+{
+ ppc_set_irq(env, PPC_INTERRUPT_DECR,
+ (env->spr[SPR_BOOKE_TSR] & TSR_DIS
+ && env->spr[SPR_BOOKE_TCR] & TCR_DIE));
+
+ ppc_set_irq(env, PPC_INTERRUPT_WDT,
+ (env->spr[SPR_BOOKE_TSR] & TSR_WIS
+ && env->spr[SPR_BOOKE_TCR] & TCR_WIE));
+
+ ppc_set_irq(env, PPC_INTERRUPT_FIT,
+ (env->spr[SPR_BOOKE_TSR] & TSR_FIS
+ && env->spr[SPR_BOOKE_TCR] & TCR_FIE));
+}
+
+/* Return the location of the bit of time base at which the FIT will raise an
+ interrupt */
+static uint8_t booke_get_fit_target(CPUState *env, ppc_tb_t *tb_env)
+{
+ uint8_t fp = (env->spr[SPR_BOOKE_TCR] & TCR_FP_MASK) >> TCR_FP_SHIFT;
+
+ if (tb_env->flags & PPC_TIMER_E500) {
+ /* e500 Fixed-interval timer period extension */
+ uint32_t fpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_FPEXT_MASK)
+ >> TCR_E500_FPEXT_SHIFT;
+ fp = 63 - (fp | fpext << 2);
+ } else {
+ fp = env->fit_period[fp];
+ }
+
+ return fp;
+}
+
+/* Return the location of the bit of time base at which the WDT will raise an
+ interrupt */
+static uint8_t booke_get_wdt_target(CPUState *env, ppc_tb_t *tb_env)
+{
+ uint8_t wp = (env->spr[SPR_BOOKE_TCR] & TCR_WP_MASK) >> TCR_WP_SHIFT;
+
+ if (tb_env->flags & PPC_TIMER_E500) {
+ /* e500 Watchdog timer period extension */
+ uint32_t wpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_WPEXT_MASK)
+ >> TCR_E500_WPEXT_SHIFT;
+ wp = 63 - (wp | wpext << 2);
+ } else {
+ wp = env->wdt_period[wp];
+ }
+
+ return wp;
+}
+
+static void booke_update_fixed_timer(CPUState *env,
+ uint8_t target_bit,
+ uint64_t *next,
+ struct QEMUTimer *timer)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ uint64_t lapse;
+ uint64_t tb;
+ uint64_t period = 1 << (target_bit + 1);
+ uint64_t now;
+
+ now = qemu_get_clock_ns(vm_clock);
+ tb = cpu_ppc_get_tb(tb_env, now, tb_env->tb_offset);
+
+ lapse = period - ((tb - (1 << target_bit)) & (period - 1));
+
+ *next = now + muldiv64(lapse, get_ticks_per_sec(), tb_env->tb_freq);
+
+ /* XXX: If expire time is now. We can't run the callback because we don't
+ * have access to it. So we just set the timer one nanosecond later.
+ */
+
+ if (*next == now) {
+ (*next)++;
+ }
+
+ qemu_mod_timer(timer, *next);
+}
+
+static void booke_decr_cb(void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ booke_timer_t *booke_timer;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ booke_timer = tb_env->opaque;
+ env->spr[SPR_BOOKE_TSR] |= TSR_DIS;
+
+ booke_update_irq(env);
+
+ if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) {
+ /* Auto Reload */
+ cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]);
+ }
+}
+
+static void booke_fit_cb(void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ booke_timer_t *booke_timer;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ booke_timer = tb_env->opaque;
+ env->spr[SPR_BOOKE_TSR] |= TSR_FIS;
+
+ booke_update_irq(env);
+
+ booke_update_fixed_timer(env,
+ booke_get_fit_target(env, tb_env),
+ &booke_timer->fit_next,
+ booke_timer->fit_timer);
+}
+
+static void booke_wdt_cb(void *opaque)
+{
+ CPUState *env;
+ ppc_tb_t *tb_env;
+ booke_timer_t *booke_timer;
+
+ env = opaque;
+ tb_env = env->tb_env;
+ booke_timer = tb_env->opaque;
+
+ /* TODO: There's lots of complicated stuff to do here */
+
+ booke_update_irq(env);
+
+ booke_update_fixed_timer(env,
+ booke_get_wdt_target(env, tb_env),
+ &booke_timer->wdt_next,
+ booke_timer->wdt_timer);
+}
+
+void store_booke_tsr(CPUState *env, target_ulong val)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ booke_timer_t *booke_timer;
+
+ booke_timer = tb_env->opaque;
+
+ env->spr[SPR_BOOKE_TSR] &= ~val;
+
+ booke_update_irq(env);
+}
+
+void store_booke_tcr(CPUState *env, target_ulong val)
+{
+ ppc_tb_t *tb_env = env->tb_env;
+ booke_timer_t *booke_timer = tb_env->opaque;
+
+ tb_env = env->tb_env;
+ env->spr[SPR_BOOKE_TCR] = val;
+
+ booke_update_irq(env);
+
+ booke_update_fixed_timer(env,
+ booke_get_fit_target(env, tb_env),
+ &booke_timer->fit_next,
+ booke_timer->fit_timer);
+
+ booke_update_fixed_timer(env,
+ booke_get_wdt_target(env, tb_env),
+ &booke_timer->wdt_next,
+ booke_timer->wdt_timer);
+
+}
+
+void ppc_booke_timers_init(CPUState *env, uint32_t freq, uint32_t flags)
+{
+ ppc_tb_t *tb_env;
+ booke_timer_t *booke_timer;
+
+ tb_env = g_malloc0(sizeof(ppc_tb_t));
+ booke_timer = g_malloc0(sizeof(booke_timer_t));
+
+ env->tb_env = tb_env;
+ tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED;
+
+ tb_env->tb_freq = freq;
+ tb_env->decr_freq = freq;
+ tb_env->opaque = booke_timer;
+ tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &booke_decr_cb, env);
+
+ booke_timer->fit_timer =
+ qemu_new_timer_ns(vm_clock, &booke_fit_cb, env);
+ booke_timer->wdt_timer =
+ qemu_new_timer_ns(vm_clock, &booke_wdt_cb, env);
+}
diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c
index 8095516f4..f00367ed7 100644
--- a/hw/ppce500_mpc8544ds.c
+++ b/hw/ppce500_mpc8544ds.c
@@ -268,11 +268,7 @@ static void mpc8544ds_init(ram_addr_t ram_size,
irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT];
env->spr[SPR_BOOKE_PIR] = env->cpu_index = i;
- /* XXX register timer? */
- ppc_emb_timers_init(env, 400000000, PPC_INTERRUPT_DECR);
- ppc_dcr_init(env, NULL, NULL);
- /* XXX Enable DEC interrupts - probably wrong in the backend */
- env->spr[SPR_40x_TCR] = 1 << 26;
+ ppc_booke_timers_init(env, 400000000, PPC_TIMER_E500);
/* Register reset handler */
if (!i) {
diff --git a/hw/virtex_ml507.c b/hw/virtex_ml507.c
index 7459b0bbe..66df27a55 100644
--- a/hw/virtex_ml507.c
+++ b/hw/virtex_ml507.c
@@ -81,7 +81,6 @@ static void mmubooke_create_initial_mapping(CPUState *env,
static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size,
int do_init,
const char *cpu_model,
- clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
uint32_t sysclk)
{
CPUState *env;
@@ -93,11 +92,7 @@ static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size,
exit(1);
}
- cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */
- cpu_clk->opaque = env;
- /* Set time-base frequency to sysclk */
- tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_DECR);
- tb_clk->opaque = env;
+ ppc_booke_timers_init(env, sysclk, 0/* no flags */);
ppc_dcr_init(env, NULL, NULL);
@@ -197,7 +192,6 @@ static void virtex_init(ram_addr_t ram_size,
DriveInfo *dinfo;
ram_addr_t phys_ram;
qemu_irq irq[32], *cpu_irq;
- clk_setup_t clk_setup[7];
int kernel_size;
int i;
@@ -207,8 +201,7 @@ static void virtex_init(ram_addr_t ram_size,
}
memset(clk_setup, 0, sizeof(clk_setup));
- env = ppc440_init_xilinx(&ram_size, 1, cpu_model, &clk_setup[0],
- &clk_setup[1], 400000000);
+ env = ppc440_init_xilinx(&ram_size, 1, cpu_model, 400000000);
qemu_register_reset(main_cpu_reset, env);
phys_ram = qemu_ram_alloc(NULL, "ram", ram_size);
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 3f4af2239..3f77e308a 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -1018,8 +1018,35 @@ struct CPUPPCState {
#if !defined(CONFIG_USER_ONLY)
void *load_info; /* Holds boot loading state. */
#endif
+
+ /* booke timers */
+
+ /* Specifies bit locations of the Time Base used to signal a fixed timer
+ * exception on a transition from 0 to 1. (watchdog or fixed-interval timer)
+ *
+ * 0 selects the least significant bit.
+ * 63 selects the most significant bit.
+ */
+ uint8_t fit_period[4];
+ uint8_t wdt_period[4];
};
+#define SET_FIT_PERIOD(a_, b_, c_, d_) \
+do { \
+ env->fit_period[0] = (a_); \
+ env->fit_period[1] = (b_); \
+ env->fit_period[2] = (c_); \
+ env->fit_period[3] = (d_); \
+ } while (0)
+
+#define SET_WDT_PERIOD(a_, b_, c_, d_) \
+do { \
+ env->wdt_period[0] = (a_); \
+ env->wdt_period[1] = (b_); \
+ env->wdt_period[2] = (c_); \
+ env->wdt_period[3] = (d_); \
+ } while (0)
+
#if !defined(CONFIG_USER_ONLY)
/* Context used internally during MMU translations */
typedef struct mmu_ctx_t mmu_ctx_t;
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index d09c7ca98..ca0d8525c 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -3266,6 +3266,9 @@ static void init_proc_401 (CPUPPCState *env)
env->icache_line_size = 32;
/* Allocate hardware IRQ controller */
ppc40x_irq_init(env);
+
+ SET_FIT_PERIOD(12, 16, 20, 24);
+ SET_WDT_PERIOD(16, 20, 24, 28);
}
/* PowerPC 401x2 */
@@ -3304,6 +3307,9 @@ static void init_proc_401x2 (CPUPPCState *env)
env->icache_line_size = 32;
/* Allocate hardware IRQ controller */
ppc40x_irq_init(env);
+
+ SET_FIT_PERIOD(12, 16, 20, 24);
+ SET_WDT_PERIOD(16, 20, 24, 28);
}
/* PowerPC 401x3 */
@@ -3337,6 +3343,9 @@ static void init_proc_401x3 (CPUPPCState *env)
env->icache_line_size = 32;
/* Allocate hardware IRQ controller */
ppc40x_irq_init(env);
+
+ SET_FIT_PERIOD(12, 16, 20, 24);
+ SET_WDT_PERIOD(16, 20, 24, 28);
}
/* IOP480 */
@@ -3375,6 +3384,9 @@ static void init_proc_IOP480 (CPUPPCState *env)
env->icache_line_size = 32;
/* Allocate hardware IRQ controller */
ppc40x_irq_init(env);
+
+ SET_FIT_PERIOD(8, 12, 16, 20);
+ SET_WDT_PERIOD(16, 20, 24, 28);
}
/* PowerPC 403 */
@@ -3405,6 +3417,9 @@ static void init_proc_403 (CPUPPCState *env)
env->icache_line_size = 32;
/* Allocate hardware IRQ controller */
ppc40x_irq_init(env);
+
+ SET_FIT_PERIOD(8, 12, 16, 20);
+ SET_WDT_PERIOD(16, 20, 24, 28);
}
/* PowerPC 403 GCX */
@@ -3455,6 +3470,9 @@ static void init_proc_403GCX (CPUPPCState *env)
env->icache_line_size = 32;
/* Allocate hardware IRQ controller */
ppc40x_irq_init(env);
+
+ SET_FIT_PERIOD(8, 12, 16, 20);
+ SET_WDT_PERIOD(16, 20, 24, 28);
}
/* PowerPC 405 */
@@ -3504,6 +3522,9 @@ static void init_proc_405 (CPUPPCState *env)
env->icache_line_size = 32;
/* Allocate hardware IRQ controller */
ppc40x_irq_init(env);
+
+ SET_FIT_PERIOD(8, 12, 16, 20);
+ SET_WDT_PERIOD(16, 20, 24, 28);
}
/* PowerPC 440 EP */
@@ -3586,6 +3607,9 @@ static void init_proc_440EP (CPUPPCState *env)
env->dcache_line_size = 32;
env->icache_line_size = 32;
/* XXX: TODO: allocate internal IRQ controller */
+
+ SET_FIT_PERIOD(12, 16, 20, 24);
+ SET_WDT_PERIOD(20, 24, 28, 32);
}
/* PowerPC 440 GP */
@@ -3650,6 +3674,9 @@ static void init_proc_440GP (CPUPPCState *env)
env->dcache_line_size = 32;
env->icache_line_size = 32;
/* XXX: TODO: allocate internal IRQ controller */
+
+ SET_FIT_PERIOD(12, 16, 20, 24);
+ SET_WDT_PERIOD(20, 24, 28, 32);
}
/* PowerPC 440x4 */
@@ -3714,6 +3741,9 @@ static void init_proc_440x4 (CPUPPCState *env)
env->dcache_line_size = 32;
env->icache_line_size = 32;
/* XXX: TODO: allocate internal IRQ controller */
+
+ SET_FIT_PERIOD(12, 16, 20, 24);
+ SET_WDT_PERIOD(20, 24, 28, 32);
}
/* PowerPC 440x5 */
@@ -3795,6 +3825,9 @@ static void init_proc_440x5 (CPUPPCState *env)
env->dcache_line_size = 32;
env->icache_line_size = 32;
ppc40x_irq_init(env);
+
+ SET_FIT_PERIOD(12, 16, 20, 24);
+ SET_WDT_PERIOD(20, 24, 28, 32);
}
/* PowerPC 460 (guessed) */
@@ -3883,6 +3916,9 @@ static void init_proc_460 (CPUPPCState *env)
env->dcache_line_size = 32;
env->icache_line_size = 32;
/* XXX: TODO: allocate internal IRQ controller */
+
+ SET_FIT_PERIOD(12, 16, 20, 24);
+ SET_WDT_PERIOD(20, 24, 28, 32);
}
/* PowerPC 460F (guessed) */
@@ -3974,6 +4010,9 @@ static void init_proc_460F (CPUPPCState *env)
env->dcache_line_size = 32;
env->icache_line_size = 32;
/* XXX: TODO: allocate internal IRQ controller */
+
+ SET_FIT_PERIOD(12, 16, 20, 24);
+ SET_WDT_PERIOD(20, 24, 28, 32);
}
/* Freescale 5xx cores (aka RCPU) */