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>
This commit is contained in:
Fabien Chouteau 2011-09-13 04:00:32 +00:00 committed by Alexander Graf
parent 94135e813c
commit ddd1055b07
9 changed files with 427 additions and 101 deletions

View File

@ -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

138
hw/ppc.c
View File

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

View File

@ -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);

View File

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

266
hw/ppc_booke.c Normal file
View File

@ -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);
}

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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) */