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

@ -421,9 +421,9 @@ CPPFLAGS += -DHAS_AUDIO
endif
ifeq ($(TARGET_BASE_ARCH), ppc)
VL_OBJS+= ppc.o ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o
VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o pflash_cfi02.o
VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o
VL_OBJS+= grackle_pci.o prep_pci.o unin_pci.o
VL_OBJS+= grackle_pci.o prep_pci.o unin_pci.o ppc405_uc.o
CPPFLAGS += -DHAS_AUDIO
endif
ifeq ($(TARGET_BASE_ARCH), mips)

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

2756
hw/ppc405_uc.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -880,6 +880,7 @@ void cpu_ppc601_store_rtcl (CPUPPCState *env, uint32_t value);
void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value);
target_ulong load_40x_pit (CPUPPCState *env);
void store_40x_pit (CPUPPCState *env, target_ulong val);
void store_40x_dbcr0 (CPUPPCState *env, uint32_t val);
void store_booke_tcr (CPUPPCState *env, target_ulong val);
void store_booke_tsr (CPUPPCState *env, target_ulong val);
void ppc_tlb_invalidate_all (CPUPPCState *env);

View file

@ -109,6 +109,7 @@ void ppc6xx_tlb_invalidate_virt (CPUState *env, target_ulong eaddr,
int is_code);
void ppc6xx_tlb_store (CPUState *env, target_ulong EPN, int way, int is_code,
target_ulong pte0, target_ulong pte1);
void ppc4xx_tlb_invalidate_all (CPUState *env);
static inline void env_to_regs(void)
{

View file

@ -2454,6 +2454,11 @@ void OPPROTO op_store_40x_pit (void)
RETURN();
}
void OPPROTO op_store_40x_dbcr0 (void)
{
store_40x_dbcr0(env, T0);
}
void OPPROTO op_store_booke_tcr (void)
{
store_booke_tcr(env, T0);

View file

@ -344,6 +344,15 @@ static void spr_write_40x_pit (void *opaque, int sprn)
gen_op_store_40x_pit();
}
static void spr_write_40x_dbcr0 (void *opaque, int sprn)
{
DisasContext *ctx = opaque;
gen_op_store_40x_dbcr0();
/* We must stop translation as we may have rebooted */
RET_STOP(ctx);
}
static void spr_write_booke_tcr (void *opaque, int sprn)
{
gen_op_store_booke_tcr();
@ -1175,7 +1184,7 @@ static void gen_spr_BookE (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_BOOKE_DBSR, "DBSR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
&spr_read_generic, &spr_write_clear,
0x00000000);
spr_register(env, SPR_BOOKE_DEAR, "DEAR",
SPR_NOACCESS, SPR_NOACCESS,
@ -1651,13 +1660,13 @@ static void gen_spr_40x (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_40x_DBCR0, "DBCR0",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
&spr_read_generic, &spr_write_40x_dbcr0,
0x00000000);
/* XXX : not implemented */
spr_register(env, SPR_40x_DBSR, "DBSR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
/* Last reset was system reset (system boot */
&spr_read_generic, &spr_write_clear,
/* Last reset was system reset */
0x00000300);
/* XXX : not implemented */
spr_register(env, SPR_40x_IAC1, "IAC1",
@ -1751,17 +1760,6 @@ static void gen_spr_405 (CPUPPCState *env)
&spr_read_ureg, SPR_NOACCESS,
&spr_read_ureg, SPR_NOACCESS,
0x00000000);
/* Debug */
/* XXX : not implemented */
spr_register(env, SPR_40x_DAC2, "DAC2",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
0x00000000);
/* XXX : not implemented */
spr_register(env, SPR_40x_IAC2, "IAC2",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, &spr_write_generic,
0x00000000);
}
/* SPR shared between PowerPC 401 & 403 implementations */

67
vl.h
View file

@ -1151,7 +1151,19 @@ extern QEMUMachine shix_machine;
#ifdef TARGET_PPC
/* PowerPC hardware exceptions management helpers */
ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq);
typedef void (*clk_setup_cb)(void *opaque, uint32_t freq);
typedef struct clk_setup_t clk_setup_t;
struct clk_setup_t {
clk_setup_cb cb;
void *opaque;
};
static inline void clk_setup (clk_setup_t *clk, uint32_t freq)
{
if (clk->cb != NULL)
(*clk->cb)(clk->opaque, freq);
}
clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq);
/* Embedded PowerPC DCR management */
typedef target_ulong (*dcr_read_cb)(void *opaque, int dcrn);
typedef void (*dcr_write_cb)(void *opaque, int dcrn, target_ulong val);
@ -1159,6 +1171,59 @@ 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);
/* PowerPC 405 core */
CPUPPCState *ppc405_init (const unsigned char *cpu_model,
clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
uint32_t sysclk);
void ppc40x_core_reset (CPUState *env);
void ppc40x_chip_reset (CPUState *env);
void ppc40x_system_reset (CPUState *env);
/* */
typedef struct ppc4xx_mmio_t ppc4xx_mmio_t;
int ppc4xx_mmio_register (CPUState *env, ppc4xx_mmio_t *mmio,
uint32_t offset, uint32_t len,
CPUReadMemoryFunc **mem_read,
CPUWriteMemoryFunc **mem_write, void *opaque);
ppc4xx_mmio_t *ppc4xx_mmio_init (CPUState *env, uint32_t base);
/* PowerPC 4xx peripheral local bus arbitrer */
void ppc4xx_plb_init (CPUState *env);
/* PLB to OPB bridge */
void ppc4xx_pob_init (CPUState *env);
/* OPB arbitrer */
void ppc4xx_opba_init (CPUState *env, ppc4xx_mmio_t *mmio, uint32_t offset);
/* PowerPC 4xx universal interrupt controller */
enum {
PPCUIC_OUTPUT_INT = 0,
PPCUIC_OUTPUT_CINT = 1,
PPCUIC_OUTPUT_NB,
};
qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs,
uint32_t dcr_base, int has_ssr, int has_vr);
/* SDRAM controller */
void ppc405_sdram_init (CPUState *env, qemu_irq irq, int nbanks,
target_ulong *ram_bases, target_ulong *ram_sizes);
/* Peripheral controller */
void ppc405_ebc_init (CPUState *env);
/* DMA controller */
void ppc405_dma_init (CPUState *env, qemu_irq irqs[4]);
/* GPIO */
void ppc405_gpio_init (CPUState *env, ppc4xx_mmio_t *mmio, uint32_t offset);
/* Serial ports */
void ppc405_serial_init (CPUState *env, ppc4xx_mmio_t *mmio,
uint32_t offset, qemu_irq irq,
CharDriverState *chr);
/* On Chip Memory */
void ppc405_ocm_init (CPUState *env, unsigned long offset);
/* I2C controller */
void ppc405_i2c_init (CPUState *env, ppc4xx_mmio_t *mmio, uint32_t offset);
/* PowerPC 405 microcontrollers */
CPUState *ppc405cr_init (target_ulong ram_bases[4], target_ulong ram_sizes[4],
uint32_t sysclk, qemu_irq **picp,
ram_addr_t *offsetp);
CPUState *ppc405ep_init (target_ulong ram_bases[2], target_ulong ram_sizes[2],
uint32_t sysclk, qemu_irq **picp,
ram_addr_t *offsetp);
#endif
void PREP_debug_write (void *opaque, uint32_t addr, uint32_t val);