mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
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:
parent
3142255c62
commit
8ecc791352
8 changed files with 2912 additions and 47 deletions
|
@ -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)
|
||||
|
|
97
hw/ppc.c
97
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;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
2756
hw/ppc405_uc.c
Normal file
2756
hw/ppc405_uc.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
67
vl.h
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue