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
This commit is contained in:
j_mayer 2007-04-16 20:09:45 +00:00
parent 3142255c62
commit 8ecc791352
8 changed files with 2912 additions and 47 deletions

View file

@ -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;
}
/*****************************************************************************/