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:
parent
94135e813c
commit
ddd1055b07
@ -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
138
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;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
37
hw/ppc.h
37
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);
|
||||
|
@ -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
266
hw/ppc_booke.c
Normal 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);
|
||||
}
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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) */
|
||||
|
Loading…
Reference in New Issue
Block a user