aboutsummaryrefslogtreecommitdiff
path: root/hw/ppc.c
diff options
context:
space:
mode:
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>2007-04-16 20:09:45 +0000
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>2007-04-16 20:09:45 +0000
commit8ecc7913525ecb6a1a41ceceac93d485a544054f (patch)
treec23ccf196aa6cf9436dcda3c80decad4c1540a92 /hw/ppc.c
parent3142255c62f3e69a1fa75427426a3e64b6349fbd (diff)
Add callbacks to allow dynamic change of PowerPC clocks (to be improved)
Fix embedded PowerPC watchdog and timers Fix PowerPC 405 SPR Add generic PowerPC 405 core instanciation code + resets support. Implement simple peripherals shared by most PowerPC 405 implementations PowerPC 405 EC & EP microcontrollers preliminary support git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2690 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw/ppc.c')
-rw-r--r--hw/ppc.c97
1 files changed, 68 insertions, 29 deletions
diff --git a/hw/ppc.c b/hw/ppc.c
index 62e9e1b1e7..1f6efee139 100644
--- a/hw/ppc.c
+++ b/hw/ppc.c
@@ -290,33 +290,55 @@ static void ppc405_set_irq (void *opaque, int pin, int level)
int cur_level;
#if defined(PPC_DEBUG_IRQ)
- printf("%s: env %p pin %d level %d\n", __func__, env, pin, level);
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "%s: env %p pin %d level %d\n", __func__,
+ env, pin, level);
+ }
#endif
cur_level = (env->irq_input_state >> pin) & 1;
/* Don't generate spurious events */
if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
switch (pin) {
case PPC405_INPUT_RESET_SYS:
- /* XXX: TODO: reset all peripherals */
- /* No break here */
+ if (level) {
+#if defined(PPC_DEBUG_IRQ)
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "%s: reset the PowerPC system\n",
+ __func__);
+ }
+#endif
+ ppc40x_system_reset(env);
+ }
+ break;
case PPC405_INPUT_RESET_CHIP:
- /* XXX: TODO: reset on-chip peripherals */
+ if (level) {
+#if defined(PPC_DEBUG_IRQ)
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "%s: reset the PowerPC chip\n", __func__);
+ }
+#endif
+ ppc40x_chip_reset(env);
+ }
+ break;
/* No break here */
case PPC405_INPUT_RESET_CORE:
/* XXX: TODO: update DBSR[MRR] */
if (level) {
-#if 0 // XXX: TOFIX
#if defined(PPC_DEBUG_IRQ)
- printf("%s: reset the CPU\n", __func__);
-#endif
- cpu_reset(env);
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "%s: reset the PowerPC core\n", __func__);
+ }
#endif
+ ppc40x_core_reset(env);
}
break;
case PPC405_INPUT_CINT:
/* Level sensitive - active high */
#if defined(PPC_DEBUG_IRQ)
- printf("%s: set the critical IRQ state to %d\n", __func__, level);
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "%s: set the critical IRQ state to %d\n",
+ __func__, level);
+ }
#endif
/* XXX: TOFIX */
ppc_set_irq(env, PPC_INTERRUPT_RESET, level);
@@ -538,8 +560,21 @@ static void cpu_ppc_decr_cb (void *opaque)
_cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1);
}
+static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
+{
+ CPUState *env = opaque;
+ ppc_tb_t *tb_env = env->tb_env;
+
+ tb_env->tb_freq = freq;
+ /* There is a bug in Linux 2.4 kernels:
+ * if a decrementer exception is pending when it enables msr_ee at startup,
+ * it's not ready to handle it...
+ */
+ _cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0);
+}
+
/* Set up (once) timebase frequency (in Hz) */
-ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq)
+clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq)
{
ppc_tb_t *tb_env;
@@ -547,23 +582,15 @@ ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq)
if (tb_env == NULL)
return NULL;
env->tb_env = tb_env;
- if (tb_env->tb_freq == 0 || 1) {
- tb_env->tb_freq = freq;
- /* Create new timer */
- tb_env->decr_timer =
- qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env);
- /* There is a bug in Linux 2.4 kernels:
- * if a decrementer exception is pending when it enables msr_ee,
- * it's not ready to handle it...
- */
- _cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0);
- }
+ /* Create new timer */
+ tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env);
+ cpu_ppc_set_tb_clk(env, freq);
- return tb_env;
+ return &cpu_ppc_set_tb_clk;
}
/* Specific helpers for POWER & PowerPC 601 RTC */
-ppc_tb_t *cpu_ppc601_rtc_init (CPUState *env)
+clk_setup_cb cpu_ppc601_rtc_init (CPUState *env)
{
return cpu_ppc_tb_init(env, 7812500);
}
@@ -733,10 +760,14 @@ static void cpu_4xx_wdt_cb (void *opaque)
/* No reset */
break;
case 0x1: /* Core reset */
+ ppc40x_core_reset(env);
+ break;
case 0x2: /* Chip reset */
+ ppc40x_chip_reset(env);
+ break;
case 0x3: /* System reset */
- qemu_system_reset_request();
- return;
+ ppc40x_system_reset(env);
+ break;
}
}
}
@@ -784,20 +815,25 @@ void store_booke_tsr (CPUState *env, target_ulong val)
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;
+ cpu_4xx_wdt_cb(env);
}
-void ppc_emb_timers_init (CPUState *env)
+clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq)
{
ppc_tb_t *tb_env;
ppcemb_timer_t *ppcemb_timer;
- tb_env = env->tb_env;
+ tb_env = qemu_mallocz(sizeof(ppc_tb_t));
+ if (tb_env == NULL)
+ return NULL;
+ env->tb_env = tb_env;
ppcemb_timer = qemu_mallocz(sizeof(ppcemb_timer_t));
+ tb_env->tb_freq = freq;
tb_env->opaque = ppcemb_timer;
- if (loglevel)
+ 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);
@@ -806,6 +842,9 @@ void ppc_emb_timers_init (CPUState *env)
ppcemb_timer->wdt_timer =
qemu_new_timer(vm_clock, &cpu_4xx_wdt_cb, env);
}
+
+ /* XXX: TODO: add callback for clock frequency change */
+ return NULL;
}
/*****************************************************************************/