]> git.proxmox.com Git - qemu.git/commitdiff
Add callbacks to allow dynamic change of PowerPC clocks (to be improved)
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Mon, 16 Apr 2007 20:09:45 +0000 (20:09 +0000)
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Mon, 16 Apr 2007 20:09:45 +0000 (20:09 +0000)
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

Makefile.target
hw/ppc.c
hw/ppc405_uc.c [new file with mode: 0644]
target-ppc/cpu.h
target-ppc/exec.h
target-ppc/op.c
target-ppc/translate_init.c
vl.h

index 7129de0a422f8231d851365b21415bfef7dfa54a..45a8ab4d2588e25a56ebdebaf6feeb5f31148ac4 100644 (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)
index 62e9e1b1e7cec4d3bf8b8253a11b9c295b31e633..1f6efee139a8e686d0a36db875867a40472f464c 100644 (file)
--- a/hw/ppc.c
+++ b/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;
 }
 
 /*****************************************************************************/
diff --git a/hw/ppc405_uc.c b/hw/ppc405_uc.c
new file mode 100644 (file)
index 0000000..0b7ef12
--- /dev/null
@@ -0,0 +1,2756 @@
+/*
+ * QEMU PowerPC 405 embedded processors emulation
+ * 
+ * Copyright (c) 2007 Jocelyn Mayer
+ * 
+ * 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 "vl.h"
+
+extern int loglevel;
+extern FILE *logfile;
+
+#define DEBUG_MMIO
+#define DEBUG_OPBA
+#define DEBUG_SDRAM
+#define DEBUG_GPIO
+#define DEBUG_SERIAL
+#define DEBUG_OCM
+#define DEBUG_I2C
+#define DEBUG_UIC
+#define DEBUG_CLOCKS
+#define DEBUG_UNASSIGNED
+
+/*****************************************************************************/
+/* Generic PowerPC 405 processor instanciation */
+CPUState *ppc405_init (const unsigned char *cpu_model,
+                       clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
+                       uint32_t sysclk)
+{
+    CPUState *env;
+    ppc_def_t *def;
+
+    /* init CPUs */
+    env = cpu_init();
+    qemu_register_reset(&cpu_ppc_reset, env);
+    register_savevm("cpu", 0, 3, cpu_save, cpu_load, env);
+    ppc_find_by_name(cpu_model, &def);
+    if (def == NULL) {
+        cpu_abort(env, "Unable to find PowerPC %s CPU definition\n",
+                  cpu_model);
+    }
+    cpu_ppc_register(env, def);
+    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);
+    tb_clk->opaque = env;
+    ppc_dcr_init(env, NULL, NULL);
+
+    return env;
+}
+
+/*****************************************************************************/
+/* Shared peripherals */
+
+/*****************************************************************************/
+/* Fake device used to map multiple devices in a single memory page */
+#define MMIO_AREA_BITS 8
+#define MMIO_AREA_LEN (1 << MMIO_AREA_BITS)
+#define MMIO_AREA_NB (1 << (TARGET_PAGE_BITS - MMIO_AREA_BITS))
+#define MMIO_IDX(addr) (((addr) >> MMIO_AREA_BITS) & (MMIO_AREA_NB - 1))
+struct ppc4xx_mmio_t {
+    uint32_t base;
+    CPUReadMemoryFunc **mem_read[MMIO_AREA_NB];
+    CPUWriteMemoryFunc **mem_write[MMIO_AREA_NB];
+    void *opaque[MMIO_AREA_NB];
+};
+
+static uint32_t unassigned_mem_readb (void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_UNASSIGNED
+    printf("Unassigned mem read 0x" PADDRX "\n", addr);
+#endif
+
+    return 0;
+}
+
+static void unassigned_mem_writeb (void *opaque,
+                                   target_phys_addr_t addr, uint32_t val)
+{
+#ifdef DEBUG_UNASSIGNED
+    printf("Unassigned mem write 0x" PADDRX " = 0x%x\n", addr, val);
+#endif
+}
+
+static CPUReadMemoryFunc *unassigned_mem_read[3] = {
+    unassigned_mem_readb,
+    unassigned_mem_readb,
+    unassigned_mem_readb,
+};
+
+static CPUWriteMemoryFunc *unassigned_mem_write[3] = {
+    unassigned_mem_writeb,
+    unassigned_mem_writeb,
+    unassigned_mem_writeb,
+};
+
+static uint32_t mmio_readlen (ppc4xx_mmio_t *mmio,
+                              target_phys_addr_t addr, int len)
+{
+    CPUReadMemoryFunc **mem_read;
+    uint32_t ret;
+    int idx;
+
+    idx = MMIO_IDX(addr - mmio->base);
+#if defined(DEBUG_MMIO)
+    printf("%s: mmio %p len %d addr " PADDRX " idx %d\n", __func__,
+           mmio, len, addr, idx);
+#endif
+    mem_read = mmio->mem_read[idx];
+    ret = (*mem_read[len])(mmio->opaque[idx], addr);
+
+    return ret;
+}
+
+static void mmio_writelen (ppc4xx_mmio_t *mmio,
+                           target_phys_addr_t addr, uint32_t value, int len)
+{
+    CPUWriteMemoryFunc **mem_write;
+    int idx;
+
+    idx = MMIO_IDX(addr - mmio->base);
+#if defined(DEBUG_MMIO)
+    printf("%s: mmio %p len %d addr " PADDRX " idx %d value %08x\n", __func__,
+           mmio, len, addr, idx, value);
+#endif
+    mem_write = mmio->mem_write[idx];
+    (*mem_write[len])(mmio->opaque[idx], addr, value);
+}
+
+static uint32_t mmio_readb (void *opaque, target_phys_addr_t addr)
+{
+#if defined(DEBUG_MMIO)
+    printf("%s: addr " PADDRX "\n", __func__, addr);
+#endif
+
+    return mmio_readlen(opaque, addr, 0);
+}
+
+static void mmio_writeb (void *opaque,
+                         target_phys_addr_t addr, uint32_t value)
+{
+#if defined(DEBUG_MMIO)
+    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
+#endif
+    mmio_writelen(opaque, addr, value, 0);
+}
+
+static uint32_t mmio_readw (void *opaque, target_phys_addr_t addr)
+{
+#if defined(DEBUG_MMIO)
+    printf("%s: addr " PADDRX "\n", __func__, addr);
+#endif
+
+    return mmio_readlen(opaque, addr, 1);
+}
+
+static void mmio_writew (void *opaque,
+                         target_phys_addr_t addr, uint32_t value)
+{
+#if defined(DEBUG_MMIO)
+    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
+#endif
+    mmio_writelen(opaque, addr, value, 1);
+}
+
+static uint32_t mmio_readl (void *opaque, target_phys_addr_t addr)
+{
+#if defined(DEBUG_MMIO)
+    printf("%s: addr " PADDRX "\n", __func__, addr);
+#endif
+
+    return mmio_readlen(opaque, addr, 2);
+}
+
+static void mmio_writel (void *opaque,
+                         target_phys_addr_t addr, uint32_t value)
+{
+#if defined(DEBUG_MMIO)
+    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
+#endif
+    mmio_writelen(opaque, addr, value, 2);
+}
+
+static CPUReadMemoryFunc *mmio_read[] = {
+    &mmio_readb,
+    &mmio_readw,
+    &mmio_readl,
+};
+
+static CPUWriteMemoryFunc *mmio_write[] = {
+    &mmio_writeb,
+    &mmio_writew,
+    &mmio_writel,
+};
+
+int ppc4xx_mmio_register (CPUState *env, ppc4xx_mmio_t *mmio,
+                          uint32_t offset, uint32_t len,
+                          CPUReadMemoryFunc **mem_read,
+                          CPUWriteMemoryFunc **mem_write, void *opaque)
+{
+    uint32_t end;
+    int idx, eidx;
+
+    if ((offset + len) > TARGET_PAGE_SIZE)
+        return -1;
+    idx = MMIO_IDX(offset);
+    end = offset + len - 1;
+    eidx = MMIO_IDX(end);
+#if defined(DEBUG_MMIO)
+    printf("%s: offset %08x len %08x %08x %d %d\n", __func__, offset, len,
+           end, idx, eidx);
+#endif
+    for (; idx <= eidx; idx++) {
+        mmio->mem_read[idx] = mem_read;
+        mmio->mem_write[idx] = mem_write;
+        mmio->opaque[idx] = opaque;
+    }
+
+    return 0;
+}
+
+ppc4xx_mmio_t *ppc4xx_mmio_init (CPUState *env, uint32_t base)
+{
+    ppc4xx_mmio_t *mmio;
+    int mmio_memory;
+
+    mmio = qemu_mallocz(sizeof(ppc4xx_mmio_t));
+    if (mmio != NULL) {
+        mmio->base = base;
+        mmio_memory = cpu_register_io_memory(0, mmio_read, mmio_write, mmio);
+#if defined(DEBUG_MMIO)
+        printf("%s: %p base %08x len %08x %d\n", __func__,
+               mmio, base, TARGET_PAGE_SIZE, mmio_memory);
+#endif
+        cpu_register_physical_memory(base, TARGET_PAGE_SIZE, mmio_memory);
+        ppc4xx_mmio_register(env, mmio, 0, TARGET_PAGE_SIZE,
+                             unassigned_mem_read, unassigned_mem_write, NULL);
+    }
+
+    return mmio;
+}
+
+/*****************************************************************************/
+/* Peripheral local bus arbitrer */
+enum {
+    PLB0_BESR = 0x084,
+    PLB0_BEAR = 0x086,
+    PLB0_ACR  = 0x087,
+};
+
+typedef struct ppc4xx_plb_t ppc4xx_plb_t;
+struct ppc4xx_plb_t {
+    uint32_t acr;
+    uint32_t bear;
+    uint32_t besr;
+};
+
+static target_ulong dcr_read_plb (void *opaque, int dcrn)
+{
+    ppc4xx_plb_t *plb;
+    target_ulong ret;
+
+    plb = opaque;
+    switch (dcrn) {
+    case PLB0_ACR:
+        ret = plb->acr;
+        break;
+    case PLB0_BEAR:
+        ret = plb->bear;
+        break;
+    case PLB0_BESR:
+        ret = plb->besr;
+        break;
+    default:
+        /* Avoid gcc warning */
+        ret = 0;
+        break;
+    }
+
+    return ret;
+}
+
+static void dcr_write_plb (void *opaque, int dcrn, target_ulong val)
+{
+    ppc4xx_plb_t *plb;
+
+    plb = opaque;
+    switch (dcrn) {
+    case PLB0_ACR:
+        plb->acr = val & 0xFC000000;
+        break;
+    case PLB0_BEAR:
+        /* Read only */
+        break;
+    case PLB0_BESR:
+        /* Write-clear */
+        plb->besr &= ~val;
+        break;
+    }
+}
+
+static void ppc4xx_plb_reset (void *opaque)
+{
+    ppc4xx_plb_t *plb;
+
+    plb = opaque;
+    plb->acr = 0x00000000;
+    plb->bear = 0x00000000;
+    plb->besr = 0x00000000;
+}
+
+void ppc4xx_plb_init (CPUState *env)
+{
+    ppc4xx_plb_t *plb;
+
+    plb = qemu_mallocz(sizeof(ppc4xx_plb_t));
+    if (plb != NULL) {
+        ppc_dcr_register(env, PLB0_ACR, plb, &dcr_read_plb, &dcr_write_plb);
+        ppc_dcr_register(env, PLB0_BEAR, plb, &dcr_read_plb, &dcr_write_plb);
+        ppc_dcr_register(env, PLB0_BESR, plb, &dcr_read_plb, &dcr_write_plb);
+        ppc4xx_plb_reset(plb);
+        qemu_register_reset(ppc4xx_plb_reset, plb);
+    }
+}
+
+/*****************************************************************************/
+/* PLB to OPB bridge */
+enum {
+    POB0_BESR0 = 0x0A0,
+    POB0_BESR1 = 0x0A2,
+    POB0_BEAR  = 0x0A4,
+};
+
+typedef struct ppc4xx_pob_t ppc4xx_pob_t;
+struct ppc4xx_pob_t {
+    uint32_t bear;
+    uint32_t besr[2];
+};
+
+static target_ulong dcr_read_pob (void *opaque, int dcrn)
+{
+    ppc4xx_pob_t *pob;
+    target_ulong ret;
+
+    pob = opaque;
+    switch (dcrn) {
+    case POB0_BEAR:
+        ret = pob->bear;
+        break;
+    case POB0_BESR0:
+    case POB0_BESR1:
+        ret = pob->besr[dcrn - POB0_BESR0];
+        break;
+    default:
+        /* Avoid gcc warning */
+        ret = 0;
+        break;
+    }
+
+    return ret;
+}
+
+static void dcr_write_pob (void *opaque, int dcrn, target_ulong val)
+{
+    ppc4xx_pob_t *pob;
+
+    pob = opaque;
+    switch (dcrn) {
+    case POB0_BEAR:
+        /* Read only */
+        break;
+    case POB0_BESR0:
+    case POB0_BESR1:
+        /* Write-clear */
+        pob->besr[dcrn - POB0_BESR0] &= ~val;
+        break;
+    }
+}
+
+static void ppc4xx_pob_reset (void *opaque)
+{
+    ppc4xx_pob_t *pob;
+
+    pob = opaque;
+    /* No error */
+    pob->bear = 0x00000000;
+    pob->besr[0] = 0x0000000;
+    pob->besr[1] = 0x0000000;
+}
+
+void ppc4xx_pob_init (CPUState *env)
+{
+    ppc4xx_pob_t *pob;
+
+    pob = qemu_mallocz(sizeof(ppc4xx_pob_t));
+    if (pob != NULL) {
+        ppc_dcr_register(env, POB0_BEAR, pob, &dcr_read_pob, &dcr_write_pob);
+        ppc_dcr_register(env, POB0_BESR0, pob, &dcr_read_pob, &dcr_write_pob);
+        ppc_dcr_register(env, POB0_BESR1, pob, &dcr_read_pob, &dcr_write_pob);
+        qemu_register_reset(ppc4xx_pob_reset, pob);
+        ppc4xx_pob_reset(env);
+    }
+}
+
+/*****************************************************************************/
+/* OPB arbitrer */
+typedef struct ppc4xx_opba_t ppc4xx_opba_t;
+struct ppc4xx_opba_t {
+    target_ulong base;
+    uint8_t cr;
+    uint8_t pr;
+};
+
+static uint32_t opba_readb (void *opaque, target_phys_addr_t addr)
+{
+    ppc4xx_opba_t *opba;
+    uint32_t ret;
+
+#ifdef DEBUG_OPBA
+    printf("%s: addr " PADDRX "\n", __func__, addr);
+#endif
+    opba = opaque;
+    switch (addr - opba->base) {
+    case 0x00:
+        ret = opba->cr;
+        break;
+    case 0x01:
+        ret = opba->pr;
+        break;
+    default:
+        ret = 0x00;
+        break;
+    }
+
+    return ret;
+}
+
+static void opba_writeb (void *opaque,
+                         target_phys_addr_t addr, uint32_t value)
+{
+    ppc4xx_opba_t *opba;
+
+#ifdef DEBUG_OPBA
+    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
+#endif
+    opba = opaque;
+    switch (addr - opba->base) {
+    case 0x00:
+        opba->cr = value & 0xF8;
+        break;
+    case 0x01:
+        opba->pr = value & 0xFF;
+        break;
+    default:
+        break;
+    }
+}
+
+static uint32_t opba_readw (void *opaque, target_phys_addr_t addr)
+{
+    uint32_t ret;
+
+#ifdef DEBUG_OPBA
+    printf("%s: addr " PADDRX "\n", __func__, addr);
+#endif
+    ret = opba_readb(opaque, addr) << 8;
+    ret |= opba_readb(opaque, addr + 1);
+
+    return ret;
+}
+
+static void opba_writew (void *opaque,
+                         target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_OPBA
+    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
+#endif
+    opba_writeb(opaque, addr, value >> 8);
+    opba_writeb(opaque, addr + 1, value);
+}
+
+static uint32_t opba_readl (void *opaque, target_phys_addr_t addr)
+{
+    uint32_t ret;
+
+#ifdef DEBUG_OPBA
+    printf("%s: addr " PADDRX "\n", __func__, addr);
+#endif
+    ret = opba_readb(opaque, addr) << 24;
+    ret |= opba_readb(opaque, addr + 1) << 16;
+
+    return ret;
+}
+
+static void opba_writel (void *opaque,
+                         target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_OPBA
+    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
+#endif
+    opba_writeb(opaque, addr, value >> 24);
+    opba_writeb(opaque, addr + 1, value >> 16);
+}
+
+static CPUReadMemoryFunc *opba_read[] = {
+    &opba_readb,
+    &opba_readw,
+    &opba_readl,
+};
+
+static CPUWriteMemoryFunc *opba_write[] = {
+    &opba_writeb,
+    &opba_writew,
+    &opba_writel,
+};
+
+static void ppc4xx_opba_reset (void *opaque)
+{
+    ppc4xx_opba_t *opba;
+
+    opba = opaque;
+    opba->cr = 0x00; /* No dynamic priorities - park disabled */
+    opba->pr = 0x11;
+}
+
+void ppc4xx_opba_init (CPUState *env, ppc4xx_mmio_t *mmio, uint32_t offset)
+{
+    ppc4xx_opba_t *opba;
+
+    opba = qemu_mallocz(sizeof(ppc4xx_opba_t));
+    if (opba != NULL) {
+        opba->base = mmio->base + offset;
+#ifdef DEBUG_OPBA
+        printf("%s: offset=%08x\n", __func__, offset);
+#endif
+        ppc4xx_mmio_register(env, mmio, offset, 0x002,
+                             opba_read, opba_write, opba);
+        qemu_register_reset(ppc4xx_opba_reset, opba);
+        ppc4xx_opba_reset(opba);
+    }
+}
+
+/*****************************************************************************/
+/* "Universal" Interrupt controller */
+enum {
+    DCR_UICSR  = 0x000,
+    DCR_UICSRS = 0x001,
+    DCR_UICER  = 0x002,
+    DCR_UICCR  = 0x003,
+    DCR_UICPR  = 0x004,
+    DCR_UICTR  = 0x005,
+    DCR_UICMSR = 0x006,
+    DCR_UICVR  = 0x007,
+    DCR_UICVCR = 0x008,
+    DCR_UICMAX = 0x009,
+};
+
+#define UIC_MAX_IRQ 32
+typedef struct ppcuic_t ppcuic_t;
+struct ppcuic_t {
+    uint32_t dcr_base;
+    int use_vectors;
+    uint32_t uicsr;  /* Status register */
+    uint32_t uicer;  /* Enable register */
+    uint32_t uiccr;  /* Critical register */
+    uint32_t uicpr;  /* Polarity register */
+    uint32_t uictr;  /* Triggering register */
+    uint32_t uicvcr; /* Vector configuration register */
+    uint32_t uicvr;
+    qemu_irq *irqs;
+};
+
+static void ppcuic_trigger_irq (ppcuic_t *uic)
+{
+    uint32_t ir, cr;
+    int start, end, inc, i;
+
+    /* Trigger interrupt if any is pending */
+    ir = uic->uicsr & uic->uicer & (~uic->uiccr);
+    cr = uic->uicsr & uic->uicer & uic->uiccr;
+#ifdef DEBUG_UIC
+    if (loglevel & CPU_LOG_INT) {
+        fprintf(logfile, "%s: uicsr %08x uicer %08x uiccr %08x\n"
+                "   %08x ir %08x cr %08x\n", __func__,
+                uic->uicsr, uic->uicer, uic->uiccr,
+                uic->uicsr & uic->uicer, ir, cr);
+    }
+#endif
+    if (ir != 0x0000000) {
+#ifdef DEBUG_UIC
+        if (loglevel & CPU_LOG_INT) {
+            fprintf(logfile, "Raise UIC interrupt\n");
+        }
+#endif
+        qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]);
+    } else {
+#ifdef DEBUG_UIC
+        if (loglevel & CPU_LOG_INT) {
+            fprintf(logfile, "Lower UIC interrupt\n");
+        }
+#endif
+        qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]);
+    }
+    /* Trigger critical interrupt if any is pending and update vector */
+    if (cr != 0x0000000) {
+        qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]);
+        if (uic->use_vectors) {
+            /* Compute critical IRQ vector */
+            if (uic->uicvcr & 1) {
+                start = 31;
+                end = 0;
+                inc = -1;
+            } else {
+                start = 0;
+                end = 31;
+                inc = 1;
+            }
+            uic->uicvr = uic->uicvcr & 0xFFFFFFFC;
+            for (i = start; i <= end; i += inc) {
+                if (cr & (1 << i)) {
+                    uic->uicvr += (i - start) * 512 * inc;
+                    break;
+                }
+            }
+        }
+#ifdef DEBUG_UIC
+        if (loglevel & CPU_LOG_INT) {
+            fprintf(logfile, "Raise UIC critical interrupt - vector %08x\n",
+                    uic->uicvr);
+        }
+#endif
+    } else {
+#ifdef DEBUG_UIC
+        if (loglevel & CPU_LOG_INT) {
+            fprintf(logfile, "Lower UIC critical interrupt\n");
+        }
+#endif
+        qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]);
+        uic->uicvr = 0x00000000;
+    }
+}
+
+static void ppcuic_set_irq (void *opaque, int irq_num, int level)
+{
+    ppcuic_t *uic;
+    uint32_t mask, sr;
+
+    uic = opaque;
+    mask = 1 << irq_num;
+#ifdef DEBUG_UIC
+    if (loglevel & CPU_LOG_INT) {
+        fprintf(logfile, "%s: irq %d level %d uicsr %08x mask %08x => %08x "
+                "%08x\n", __func__, irq_num, level,
+                uic->uicsr, mask, uic->uicsr & mask, level << irq_num);
+    }
+#endif
+    if (irq_num < 0 || irq_num > 31)
+        return;
+    sr = uic->uicsr;
+    if (!(uic->uicpr & mask)) {
+        /* Negatively asserted IRQ */
+        level = level == 0 ? 1 : 0;
+    }
+    /* Update status register */
+    if (uic->uictr & mask) {
+        /* Edge sensitive interrupt */
+        if (level == 1)
+            uic->uicsr |= mask;
+    } else {
+        /* Level sensitive interrupt */
+        if (level == 1)
+            uic->uicsr |= mask;
+        else
+            uic->uicsr &= ~mask;
+    }
+#ifdef DEBUG_UIC
+    if (loglevel & CPU_LOG_INT) {
+        fprintf(logfile, "%s: irq %d level %d sr %08x => %08x\n", __func__,
+                irq_num, level, uic->uicsr, sr);
+    }
+#endif
+    if (sr != uic->uicsr)
+        ppcuic_trigger_irq(uic);
+}
+
+static target_ulong dcr_read_uic (void *opaque, int dcrn)
+{
+    ppcuic_t *uic;
+    target_ulong ret;
+
+    uic = opaque;
+    dcrn -= uic->dcr_base;
+    switch (dcrn) {
+    case DCR_UICSR:
+    case DCR_UICSRS:
+        ret = uic->uicsr;
+        break;
+    case DCR_UICER:
+        ret = uic->uicer;
+        break;
+    case DCR_UICCR:
+        ret = uic->uiccr;
+        break;
+    case DCR_UICPR:
+        ret = uic->uicpr;
+        break;
+    case DCR_UICTR:
+        ret = uic->uictr;
+        break;
+    case DCR_UICMSR:
+        ret = uic->uicsr & uic->uicer;
+        break;
+    case DCR_UICVR:
+        if (!uic->use_vectors)
+            goto no_read;
+        ret = uic->uicvr;
+        break;
+    case DCR_UICVCR:
+        if (!uic->use_vectors)
+            goto no_read;
+        ret = uic->uicvcr;
+        break;
+    default:
+    no_read:
+        ret = 0x00000000;
+        break;
+    }
+
+    return ret;
+}
+
+static void dcr_write_uic (void *opaque, int dcrn, target_ulong val)
+{
+    ppcuic_t *uic;
+
+    uic = opaque;
+    dcrn -= uic->dcr_base;
+#ifdef DEBUG_UIC
+    if (loglevel & CPU_LOG_INT) {
+        fprintf(logfile, "%s: dcr %d val " ADDRX "\n", __func__, dcrn, val);
+    }
+#endif
+    switch (dcrn) {
+    case DCR_UICSR:
+        uic->uicsr &= ~val;
+        ppcuic_trigger_irq(uic);
+        break;
+    case DCR_UICSRS:
+        uic->uicsr |= val;
+        ppcuic_trigger_irq(uic);
+        break;
+    case DCR_UICER:
+        uic->uicer = val;
+        ppcuic_trigger_irq(uic);
+        break;
+    case DCR_UICCR:
+        uic->uiccr = val;
+        ppcuic_trigger_irq(uic);
+        break;
+    case DCR_UICPR:
+        uic->uicpr = val;
+        ppcuic_trigger_irq(uic);
+        break;
+    case DCR_UICTR:
+        uic->uictr = val;
+        ppcuic_trigger_irq(uic);
+        break;
+    case DCR_UICMSR:
+        break;
+    case DCR_UICVR:
+        break;
+    case DCR_UICVCR:
+        uic->uicvcr = val & 0xFFFFFFFD;
+        ppcuic_trigger_irq(uic);
+        break;
+    }
+}
+
+static void ppcuic_reset (void *opaque)
+{
+    ppcuic_t *uic;
+
+    uic = opaque;
+    uic->uiccr = 0x00000000;
+    uic->uicer = 0x00000000;
+    uic->uicpr = 0x00000000;
+    uic->uicsr = 0x00000000;
+    uic->uictr = 0x00000000;
+    if (uic->use_vectors) {
+        uic->uicvcr = 0x00000000;
+        uic->uicvr = 0x0000000;
+    }
+}
+
+qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs,
+                       uint32_t dcr_base, int has_ssr, int has_vr)
+{
+    ppcuic_t *uic;
+    int i;
+
+    uic = qemu_mallocz(sizeof(ppcuic_t));
+    if (uic != NULL) {
+        uic->dcr_base = dcr_base;
+        uic->irqs = irqs;
+        if (has_vr)
+            uic->use_vectors = 1;
+        for (i = 0; i < DCR_UICMAX; i++) {
+            ppc_dcr_register(env, dcr_base + i, uic,
+                             &dcr_read_uic, &dcr_write_uic);
+        }
+        qemu_register_reset(ppcuic_reset, uic);
+        ppcuic_reset(uic);
+    }
+
+    return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ);
+}
+
+/*****************************************************************************/
+/* Code decompression controller */
+/* XXX: TODO */
+
+/*****************************************************************************/
+/* SDRAM controller */
+typedef struct ppc4xx_sdram_t ppc4xx_sdram_t;
+struct ppc4xx_sdram_t {
+    uint32_t addr;
+    int nbanks;
+    target_ulong ram_bases[4];
+    target_ulong ram_sizes[4];
+    uint32_t besr0;
+    uint32_t besr1;
+    uint32_t bear;
+    uint32_t cfg;
+    uint32_t status;
+    uint32_t rtr;
+    uint32_t pmit;
+    uint32_t bcr[4];
+    uint32_t tr;
+    uint32_t ecccfg;
+    uint32_t eccesr;
+    qemu_irq irq;
+};
+
+enum {
+    SDRAM0_CFGADDR = 0x010,
+    SDRAM0_CFGDATA = 0x011,
+};
+
+static uint32_t sdram_bcr (target_ulong ram_base, target_ulong ram_size)
+{
+    uint32_t bcr;
+
+    switch (ram_size) {
+    case (4 * 1024 * 1024):
+        bcr = 0x00000000;
+        break;
+    case (8 * 1024 * 1024):
+        bcr = 0x00020000;
+        break;
+    case (16 * 1024 * 1024):
+        bcr = 0x00040000;
+        break;
+    case (32 * 1024 * 1024):
+        bcr = 0x00060000;
+        break;
+    case (64 * 1024 * 1024):
+        bcr = 0x00080000;
+        break;
+    case (128 * 1024 * 1024):
+        bcr = 0x000A0000;
+        break;
+    case (256 * 1024 * 1024):
+        bcr = 0x000C0000;
+        break;
+    default:
+        printf("%s: invalid RAM size " TARGET_FMT_ld "\n", __func__, ram_size);
+        return 0x00000000;
+    }
+    bcr |= ram_base & 0xFF800000;
+    bcr |= 1;
+
+    return bcr;
+}
+
+static inline target_ulong sdram_base (uint32_t bcr)
+{
+    return bcr & 0xFF800000;
+}
+
+static target_ulong sdram_size (uint32_t bcr)
+{
+    target_ulong size;
+    int sh;
+
+    sh = (bcr >> 17) & 0x7;
+    if (sh == 7)
+        size = -1;
+    else
+        size = (4 * 1024 * 1024) << sh;
+
+    return size;
+}
+
+static void sdram_set_bcr (uint32_t *bcrp, uint32_t bcr, int enabled)
+{
+    if (*bcrp & 0x00000001) {
+        /* Unmap RAM */
+#ifdef DEBUG_SDRAM
+        printf("%s: unmap RAM area " ADDRX " " ADDRX "\n", __func__,
+               sdram_base(*bcrp), sdram_size(*bcrp));
+#endif
+        cpu_register_physical_memory(sdram_base(*bcrp), sdram_size(*bcrp),
+                                     IO_MEM_UNASSIGNED);
+    }
+    *bcrp = bcr & 0xFFDEE001;
+    if (enabled && (bcr & 0x00000001)) {
+#ifdef DEBUG_SDRAM
+        printf("%s: Map RAM area " ADDRX " " ADDRX "\n", __func__,
+               sdram_base(bcr), sdram_size(bcr));
+#endif
+        cpu_register_physical_memory(sdram_base(bcr), sdram_size(bcr),
+                                     sdram_base(bcr) | IO_MEM_RAM);
+    }
+}
+
+static void sdram_map_bcr (ppc4xx_sdram_t *sdram)
+{
+    int i;
+
+    for (i = 0; i < sdram->nbanks; i++) {
+        if (sdram->ram_sizes[i] != 0) {
+            sdram_set_bcr(&sdram->bcr[i],
+                          sdram_bcr(sdram->ram_bases[i], sdram->ram_sizes[i]),
+                          1);
+        } else {
+            sdram_set_bcr(&sdram->bcr[i], 0x00000000, 0);
+        }
+    }
+}
+
+static void sdram_unmap_bcr (ppc4xx_sdram_t *sdram)
+{
+    int i;
+
+    for (i = 0; i < sdram->nbanks; i++) {
+        cpu_register_physical_memory(sdram_base(sdram->bcr[i]),
+                                     sdram_size(sdram->bcr[i]),
+                                     IO_MEM_UNASSIGNED);
+    }
+}
+
+static target_ulong dcr_read_sdram (void *opaque, int dcrn)
+{
+    ppc4xx_sdram_t *sdram;
+    target_ulong ret;
+
+    sdram = opaque;
+    switch (dcrn) {
+    case SDRAM0_CFGADDR:
+        ret = sdram->addr;
+        break;
+    case SDRAM0_CFGDATA:
+        switch (sdram->addr) {
+        case 0x00: /* SDRAM_BESR0 */
+            ret = sdram->besr0;
+            break;
+        case 0x08: /* SDRAM_BESR1 */
+            ret = sdram->besr1;
+            break;
+        case 0x10: /* SDRAM_BEAR */
+            ret = sdram->bear;
+            break;
+        case 0x20: /* SDRAM_CFG */
+            ret = sdram->cfg;
+            break;
+        case 0x24: /* SDRAM_STATUS */
+            ret = sdram->status;
+            break;
+        case 0x30: /* SDRAM_RTR */
+            ret = sdram->rtr;
+            break;
+        case 0x34: /* SDRAM_PMIT */
+            ret = sdram->pmit;
+            break;
+        case 0x40: /* SDRAM_B0CR */
+            ret = sdram->bcr[0];
+            break;
+        case 0x44: /* SDRAM_B1CR */
+            ret = sdram->bcr[1];
+            break;
+        case 0x48: /* SDRAM_B2CR */
+            ret = sdram->bcr[2];
+            break;
+        case 0x4C: /* SDRAM_B3CR */
+            ret = sdram->bcr[3];
+            break;
+        case 0x80: /* SDRAM_TR */
+            ret = -1; /* ? */
+            break;
+        case 0x94: /* SDRAM_ECCCFG */
+            ret = sdram->ecccfg;
+            break;
+        case 0x98: /* SDRAM_ECCESR */
+            ret = sdram->eccesr;
+            break;
+        default: /* Error */
+            ret = -1;
+            break;
+        }
+        break;
+    default:
+        /* Avoid gcc warning */
+        ret = 0x00000000;
+        break;
+    }
+
+    return ret;
+}
+
+static void dcr_write_sdram (void *opaque, int dcrn, target_ulong val)
+{
+    ppc4xx_sdram_t *sdram;
+
+    sdram = opaque;
+    switch (dcrn) {
+    case SDRAM0_CFGADDR:
+        sdram->addr = val;
+        break;
+    case SDRAM0_CFGDATA:
+        switch (sdram->addr) {
+        case 0x00: /* SDRAM_BESR0 */
+            sdram->besr0 &= ~val;
+            break;
+        case 0x08: /* SDRAM_BESR1 */
+            sdram->besr1 &= ~val;
+            break;
+        case 0x10: /* SDRAM_BEAR */
+            sdram->bear = val;
+            break;
+        case 0x20: /* SDRAM_CFG */
+            val &= 0xFFE00000;
+            if (!(sdram->cfg & 0x80000000) && (val & 0x80000000)) {
+#ifdef DEBUG_SDRAM
+                printf("%s: enable SDRAM controller\n", __func__);
+#endif
+                /* validate all RAM mappings */
+                sdram_map_bcr(sdram);
+                sdram->status &= ~0x80000000;
+            } else if ((sdram->cfg & 0x80000000) && !(val & 0x80000000)) {
+#ifdef DEBUG_SDRAM
+                printf("%s: disable SDRAM controller\n", __func__);
+#endif
+                /* invalidate all RAM mappings */
+                sdram_unmap_bcr(sdram);
+                sdram->status |= 0x80000000;
+            }
+            if (!(sdram->cfg & 0x40000000) && (val & 0x40000000))
+                sdram->status |= 0x40000000;
+            else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000))
+                sdram->status &= ~0x40000000;
+            sdram->cfg = val;
+            break;
+        case 0x24: /* SDRAM_STATUS */
+            /* Read-only register */
+            break;
+        case 0x30: /* SDRAM_RTR */
+            sdram->rtr = val & 0x3FF80000;
+            break;
+        case 0x34: /* SDRAM_PMIT */
+            sdram->pmit = (val & 0xF8000000) | 0x07C00000;
+            break;
+        case 0x40: /* SDRAM_B0CR */
+            sdram_set_bcr(&sdram->bcr[0], val, sdram->cfg & 0x80000000);
+            break;
+        case 0x44: /* SDRAM_B1CR */
+            sdram_set_bcr(&sdram->bcr[1], val, sdram->cfg & 0x80000000);
+            break;
+        case 0x48: /* SDRAM_B2CR */
+            sdram_set_bcr(&sdram->bcr[2], val, sdram->cfg & 0x80000000);
+            break;
+        case 0x4C: /* SDRAM_B3CR */
+            sdram_set_bcr(&sdram->bcr[3], val, sdram->cfg & 0x80000000);
+            break;
+        case 0x80: /* SDRAM_TR */
+            sdram->tr = val & 0x018FC01F;
+            break;
+        case 0x94: /* SDRAM_ECCCFG */
+            sdram->ecccfg = val & 0x00F00000;
+            break;
+        case 0x98: /* SDRAM_ECCESR */
+            val &= 0xFFF0F000;
+            if (sdram->eccesr == 0 && val != 0)
+                qemu_irq_raise(sdram->irq);
+            else if (sdram->eccesr != 0 && val == 0)
+                qemu_irq_lower(sdram->irq);
+            sdram->eccesr = val;
+            break;
+        default: /* Error */
+            break;
+        }
+        break;
+    }
+}
+
+static void sdram_reset (void *opaque)
+{
+    ppc4xx_sdram_t *sdram;
+
+    sdram = opaque;
+    sdram->addr = 0x00000000;
+    sdram->bear = 0x00000000;
+    sdram->besr0 = 0x00000000; /* No error */
+    sdram->besr1 = 0x00000000; /* No error */
+    sdram->cfg = 0x00000000;
+    sdram->ecccfg = 0x00000000; /* No ECC */
+    sdram->eccesr = 0x00000000; /* No error */
+    sdram->pmit = 0x07C00000;
+    sdram->rtr = 0x05F00000;
+    sdram->tr = 0x00854009;
+    /* We pre-initialize RAM banks */
+    sdram->status = 0x00000000;
+    sdram->cfg = 0x00800000;
+    sdram_unmap_bcr(sdram);
+}
+
+void ppc405_sdram_init (CPUState *env, qemu_irq irq, int nbanks,
+                        target_ulong *ram_bases, target_ulong *ram_sizes)
+{
+    ppc4xx_sdram_t *sdram;
+
+    sdram = qemu_mallocz(sizeof(ppc4xx_sdram_t));
+    if (sdram != NULL) {
+        sdram->irq = irq;
+        sdram->nbanks = nbanks;
+        memset(sdram->ram_bases, 0, 4 * sizeof(target_ulong));
+        memcpy(sdram->ram_bases, ram_bases, nbanks * sizeof(target_ulong));
+        memset(sdram->ram_sizes, 0, 4 * sizeof(target_ulong));
+        memcpy(sdram->ram_sizes, ram_sizes, nbanks * sizeof(target_ulong));
+        sdram_reset(sdram);
+        qemu_register_reset(&sdram_reset, sdram);
+        ppc_dcr_register(env, SDRAM0_CFGADDR,
+                         sdram, &dcr_read_sdram, &dcr_write_sdram);
+        ppc_dcr_register(env, SDRAM0_CFGDATA,
+                         sdram, &dcr_read_sdram, &dcr_write_sdram);
+    }
+}
+
+/*****************************************************************************/
+/* Peripheral controller */
+typedef struct ppc4xx_ebc_t ppc4xx_ebc_t;
+struct ppc4xx_ebc_t {
+    uint32_t addr;
+    uint32_t bcr[8];
+    uint32_t bap[8];
+    uint32_t bear;
+    uint32_t besr0;
+    uint32_t besr1;
+    uint32_t cfg;
+};
+
+enum {
+    EBC0_CFGADDR = 0x012,
+    EBC0_CFGDATA = 0x013,
+};
+
+static target_ulong dcr_read_ebc (void *opaque, int dcrn)
+{
+    ppc4xx_ebc_t *ebc;
+    target_ulong ret;
+
+    ebc = opaque;
+    switch (dcrn) {
+    case EBC0_CFGADDR:
+        ret = ebc->addr;
+        break;
+    case EBC0_CFGDATA:
+        switch (ebc->addr) {
+        case 0x00: /* B0CR */
+            ret = ebc->bcr[0];
+            break;
+        case 0x01: /* B1CR */
+            ret = ebc->bcr[1];
+            break;
+        case 0x02: /* B2CR */
+            ret = ebc->bcr[2];
+            break;
+        case 0x03: /* B3CR */
+            ret = ebc->bcr[3];
+            break;
+        case 0x04: /* B4CR */
+            ret = ebc->bcr[4];
+            break;
+        case 0x05: /* B5CR */
+            ret = ebc->bcr[5];
+            break;
+        case 0x06: /* B6CR */
+            ret = ebc->bcr[6];
+            break;
+        case 0x07: /* B7CR */
+            ret = ebc->bcr[7];
+            break;
+        case 0x10: /* B0AP */
+            ret = ebc->bap[0];
+            break;
+        case 0x11: /* B1AP */
+            ret = ebc->bap[1];
+            break;
+        case 0x12: /* B2AP */
+            ret = ebc->bap[2];
+            break;
+        case 0x13: /* B3AP */
+            ret = ebc->bap[3];
+            break;
+        case 0x14: /* B4AP */
+            ret = ebc->bap[4];
+            break;
+        case 0x15: /* B5AP */
+            ret = ebc->bap[5];
+            break;
+        case 0x16: /* B6AP */
+            ret = ebc->bap[6];
+            break;
+        case 0x17: /* B7AP */
+            ret = ebc->bap[7];
+            break;
+        case 0x20: /* BEAR */
+            ret = ebc->bear;
+            break;
+        case 0x21: /* BESR0 */
+            ret = ebc->besr0;
+            break;
+        case 0x22: /* BESR1 */
+            ret = ebc->besr1;
+            break;
+        case 0x23: /* CFG */
+            ret = ebc->cfg;
+            break;
+        default:
+            ret = 0x00000000;
+            break;
+        }
+    default:
+        ret = 0x00000000;
+        break;
+    }
+
+    return ret;
+}
+
+static void dcr_write_ebc (void *opaque, int dcrn, target_ulong val)
+{
+    ppc4xx_ebc_t *ebc;
+
+    ebc = opaque;
+    switch (dcrn) {
+    case EBC0_CFGADDR:
+        ebc->addr = val;
+        break;
+    case EBC0_CFGDATA:
+        switch (ebc->addr) {
+        case 0x00: /* B0CR */
+            break;
+        case 0x01: /* B1CR */
+            break;
+        case 0x02: /* B2CR */
+            break;
+        case 0x03: /* B3CR */
+            break;
+        case 0x04: /* B4CR */
+            break;
+        case 0x05: /* B5CR */
+            break;
+        case 0x06: /* B6CR */
+            break;
+        case 0x07: /* B7CR */
+            break;
+        case 0x10: /* B0AP */
+            break;
+        case 0x11: /* B1AP */
+            break;
+        case 0x12: /* B2AP */
+            break;
+        case 0x13: /* B3AP */
+            break;
+        case 0x14: /* B4AP */
+            break;
+        case 0x15: /* B5AP */
+            break;
+        case 0x16: /* B6AP */
+            break;
+        case 0x17: /* B7AP */
+            break;
+        case 0x20: /* BEAR */
+            break;
+        case 0x21: /* BESR0 */
+            break;
+        case 0x22: /* BESR1 */
+            break;
+        case 0x23: /* CFG */
+            break;
+        default:
+            break;
+        }
+        break;
+    default:
+        break;
+    }
+}
+
+static void ebc_reset (void *opaque)
+{
+    ppc4xx_ebc_t *ebc;
+    int i;
+
+    ebc = opaque;
+    ebc->addr = 0x00000000;
+    ebc->bap[0] = 0x7F8FFE80;
+    ebc->bcr[0] = 0xFFE28000;
+    for (i = 0; i < 8; i++) {
+        ebc->bap[i] = 0x00000000;
+        ebc->bcr[i] = 0x00000000;
+    }
+    ebc->besr0 = 0x00000000;
+    ebc->besr1 = 0x00000000;
+    ebc->cfg = 0x07C00000;
+}
+
+void ppc405_ebc_init (CPUState *env)
+{
+    ppc4xx_ebc_t *ebc;
+
+    ebc = qemu_mallocz(sizeof(ppc4xx_ebc_t));
+    if (ebc != NULL) {
+        ebc_reset(ebc);
+        qemu_register_reset(&ebc_reset, ebc);
+        ppc_dcr_register(env, EBC0_CFGADDR,
+                         ebc, &dcr_read_ebc, &dcr_write_ebc);
+        ppc_dcr_register(env, EBC0_CFGDATA,
+                         ebc, &dcr_read_ebc, &dcr_write_ebc);
+    }
+}
+
+/*****************************************************************************/
+/* DMA controller */
+enum {
+    DMA0_CR0 = 0x100,
+    DMA0_CT0 = 0x101,
+    DMA0_DA0 = 0x102,
+    DMA0_SA0 = 0x103,
+    DMA0_SG0 = 0x104,
+    DMA0_CR1 = 0x108,
+    DMA0_CT1 = 0x109,
+    DMA0_DA1 = 0x10A,
+    DMA0_SA1 = 0x10B,
+    DMA0_SG1 = 0x10C,
+    DMA0_CR2 = 0x110,
+    DMA0_CT2 = 0x111,
+    DMA0_DA2 = 0x112,
+    DMA0_SA2 = 0x113,
+    DMA0_SG2 = 0x114,
+    DMA0_CR3 = 0x118,
+    DMA0_CT3 = 0x119,
+    DMA0_DA3 = 0x11A,
+    DMA0_SA3 = 0x11B,
+    DMA0_SG3 = 0x11C,
+    DMA0_SR  = 0x120,
+    DMA0_SGC = 0x123,
+    DMA0_SLP = 0x125,
+    DMA0_POL = 0x126,
+};
+
+typedef struct ppc405_dma_t ppc405_dma_t;
+struct ppc405_dma_t {
+    qemu_irq irqs[4];
+    uint32_t cr[4];
+    uint32_t ct[4];
+    uint32_t da[4];
+    uint32_t sa[4];
+    uint32_t sg[4];
+    uint32_t sr;
+    uint32_t sgc;
+    uint32_t slp;
+    uint32_t pol;
+};
+
+static target_ulong dcr_read_dma (void *opaque, int dcrn)
+{
+    ppc405_dma_t *dma;
+
+    dma = opaque;
+
+    return 0;
+}
+
+static void dcr_write_dma (void *opaque, int dcrn, target_ulong val)
+{
+    ppc405_dma_t *dma;
+
+    dma = opaque;
+}
+
+static void ppc405_dma_reset (void *opaque)
+{
+    ppc405_dma_t *dma;
+    int i;
+
+    dma = opaque;
+    for (i = 0; i < 4; i++) {
+        dma->cr[i] = 0x00000000;
+        dma->ct[i] = 0x00000000;
+        dma->da[i] = 0x00000000;
+        dma->sa[i] = 0x00000000;
+        dma->sg[i] = 0x00000000;
+    }
+    dma->sr = 0x00000000;
+    dma->sgc = 0x00000000;
+    dma->slp = 0x7C000000;
+    dma->pol = 0x00000000;
+}
+
+void ppc405_dma_init (CPUState *env, qemu_irq irqs[4])
+{
+    ppc405_dma_t *dma;
+
+    dma = qemu_mallocz(sizeof(ppc405_dma_t));
+    if (dma != NULL) {
+        memcpy(dma->irqs, irqs, 4 * sizeof(qemu_irq));
+        ppc405_dma_reset(dma);
+        qemu_register_reset(&ppc405_dma_reset, dma);
+        ppc_dcr_register(env, DMA0_CR0,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_CT0,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_DA0,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_SA0,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_SG0,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_CR1,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_CT1,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_DA1,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_SA1,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_SG1,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_CR2,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_CT2,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_DA2,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_SA2,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_SG2,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_CR3,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_CT3,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_DA3,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_SA3,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_SG3,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_SR,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_SGC,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_SLP,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+        ppc_dcr_register(env, DMA0_POL,
+                         dma, &dcr_read_dma, &dcr_write_dma);
+    }
+}
+
+/*****************************************************************************/
+/* GPIO */
+typedef struct ppc405_gpio_t ppc405_gpio_t;
+struct ppc405_gpio_t {
+    uint32_t base;
+    uint32_t or;
+    uint32_t tcr;
+    uint32_t osrh;
+    uint32_t osrl;
+    uint32_t tsrh;
+    uint32_t tsrl;
+    uint32_t odr;
+    uint32_t ir;
+    uint32_t rr1;
+    uint32_t isr1h;
+    uint32_t isr1l;
+};
+
+static uint32_t ppc405_gpio_readb (void *opaque, target_phys_addr_t addr)
+{
+    ppc405_gpio_t *gpio;
+
+    gpio = opaque;
+#ifdef DEBUG_GPIO
+    printf("%s: addr " PADDRX "\n", __func__, addr);
+#endif
+
+    return 0;
+}
+
+static void ppc405_gpio_writeb (void *opaque,
+                                target_phys_addr_t addr, uint32_t value)
+{
+    ppc405_gpio_t *gpio;
+
+    gpio = opaque;
+#ifdef DEBUG_GPIO
+    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
+#endif
+}
+
+static uint32_t ppc405_gpio_readw (void *opaque, target_phys_addr_t addr)
+{
+    ppc405_gpio_t *gpio;
+
+    gpio = opaque;
+#ifdef DEBUG_GPIO
+    printf("%s: addr " PADDRX "\n", __func__, addr);
+#endif
+
+    return 0;
+}
+
+static void ppc405_gpio_writew (void *opaque,
+                                target_phys_addr_t addr, uint32_t value)
+{
+    ppc405_gpio_t *gpio;
+
+    gpio = opaque;
+#ifdef DEBUG_GPIO
+    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
+#endif
+}
+
+static uint32_t ppc405_gpio_readl (void *opaque, target_phys_addr_t addr)
+{
+    ppc405_gpio_t *gpio;
+
+    gpio = opaque;
+#ifdef DEBUG_GPIO
+    printf("%s: addr " PADDRX "\n", __func__, addr);
+#endif
+
+    return 0;
+}
+
+static void ppc405_gpio_writel (void *opaque,
+                                target_phys_addr_t addr, uint32_t value)
+{
+    ppc405_gpio_t *gpio;
+
+    gpio = opaque;
+#ifdef DEBUG_GPIO
+    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
+#endif
+}
+
+static CPUReadMemoryFunc *ppc405_gpio_read[] = {
+    &ppc405_gpio_readb,
+    &ppc405_gpio_readw,
+    &ppc405_gpio_readl,
+};
+
+static CPUWriteMemoryFunc *ppc405_gpio_write[] = {
+    &ppc405_gpio_writeb,
+    &ppc405_gpio_writew,
+    &ppc405_gpio_writel,
+};
+
+static void ppc405_gpio_reset (void *opaque)
+{
+    ppc405_gpio_t *gpio;
+
+    gpio = opaque;
+}
+
+void ppc405_gpio_init (CPUState *env, ppc4xx_mmio_t *mmio, uint32_t offset)
+{
+    ppc405_gpio_t *gpio;
+
+    gpio = qemu_mallocz(sizeof(ppc405_gpio_t));
+    if (gpio != NULL) {
+        gpio->base = mmio->base + offset;
+        ppc405_gpio_reset(gpio);
+        qemu_register_reset(&ppc405_gpio_reset, gpio);
+#ifdef DEBUG_GPIO
+        printf("%s: offset=%08x\n", __func__, offset);
+#endif
+        ppc4xx_mmio_register(env, mmio, offset, 0x038,
+                             ppc405_gpio_read, ppc405_gpio_write, gpio);
+    }
+}
+
+/*****************************************************************************/
+/* Serial ports */
+static CPUReadMemoryFunc *serial_mm_read[] = {
+    &serial_mm_readb,
+    &serial_mm_readw,
+    &serial_mm_readl,
+};
+
+static CPUWriteMemoryFunc *serial_mm_write[] = {
+    &serial_mm_writeb,
+    &serial_mm_writew,
+    &serial_mm_writel,
+};
+
+void ppc405_serial_init (CPUState *env, ppc4xx_mmio_t *mmio,
+                         uint32_t offset, qemu_irq irq,
+                         CharDriverState *chr)
+{
+    void *serial;
+
+#ifdef DEBUG_SERIAL
+    printf("%s: offset=%08x\n", __func__, offset);
+#endif
+    serial = serial_mm_init(mmio->base + offset, 0, irq, chr, 0);
+    ppc4xx_mmio_register(env, mmio, offset, 0x008,
+                         serial_mm_read, serial_mm_write, serial);
+}
+
+/*****************************************************************************/
+/* On Chip Memory */
+enum {
+    OCM0_ISARC   = 0x018,
+    OCM0_ISACNTL = 0x019,
+    OCM0_DSARC   = 0x01A,
+    OCM0_DSACNTL = 0x01B,
+};
+
+typedef struct ppc405_ocm_t ppc405_ocm_t;
+struct ppc405_ocm_t {
+    target_ulong offset;
+    uint32_t isarc;
+    uint32_t isacntl;
+    uint32_t dsarc;
+    uint32_t dsacntl;
+};
+
+static void ocm_update_mappings (ppc405_ocm_t *ocm,
+                                 uint32_t isarc, uint32_t isacntl,
+                                 uint32_t dsarc, uint32_t dsacntl)
+{
+#ifdef DEBUG_OCM
+    printf("OCM update ISA %08x %08x (%08x %08x) DSA %08x %08x (%08x %08x)\n",
+           isarc, isacntl, dsarc, dsacntl,
+           ocm->isarc, ocm->isacntl, ocm->dsarc, ocm->dsacntl);
+#endif
+    if (ocm->isarc != isarc ||
+        (ocm->isacntl & 0x80000000) != (isacntl & 0x80000000)) {
+        if (ocm->isacntl & 0x80000000) {
+            /* Unmap previously assigned memory region */
+            printf("OCM unmap ISA %08x\n", ocm->isarc);
+            cpu_register_physical_memory(ocm->isarc, 0x04000000,
+                                         IO_MEM_UNASSIGNED);
+        }
+        if (isacntl & 0x80000000) {
+            /* Map new instruction memory region */
+#ifdef DEBUG_OCM
+            printf("OCM map ISA %08x\n", isarc);
+#endif
+            cpu_register_physical_memory(isarc, 0x04000000,
+                                         ocm->offset | IO_MEM_RAM);
+        }
+    }
+    if (ocm->dsarc != dsarc ||
+        (ocm->dsacntl & 0x80000000) != (dsacntl & 0x80000000)) {
+        if (ocm->dsacntl & 0x80000000) {
+            /* Beware not to unmap the region we just mapped */
+            if (!(isacntl & 0x80000000) || ocm->dsarc != isarc) {
+                /* Unmap previously assigned memory region */
+#ifdef DEBUG_OCM
+                printf("OCM unmap DSA %08x\n", ocm->dsarc);
+#endif
+                cpu_register_physical_memory(ocm->dsarc, 0x04000000,
+                                             IO_MEM_UNASSIGNED);
+            }
+        }
+        if (dsacntl & 0x80000000) {
+            /* Beware not to remap the region we just mapped */
+            if (!(isacntl & 0x80000000) || dsarc != isarc) {
+                /* Map new data memory region */
+#ifdef DEBUG_OCM
+                printf("OCM map DSA %08x\n", dsarc);
+#endif
+                cpu_register_physical_memory(dsarc, 0x04000000,
+                                             ocm->offset | IO_MEM_RAM);
+            }
+        }
+    }
+}
+
+static target_ulong dcr_read_ocm (void *opaque, int dcrn)
+{
+    ppc405_ocm_t *ocm;
+    target_ulong ret;
+
+    ocm = opaque;
+    switch (dcrn) {
+    case OCM0_ISARC:
+        ret = ocm->isarc;
+        break;
+    case OCM0_ISACNTL:
+        ret = ocm->isacntl;
+        break;
+    case OCM0_DSARC:
+        ret = ocm->dsarc;
+        break;
+    case OCM0_DSACNTL:
+        ret = ocm->dsacntl;
+        break;
+    default:
+        ret = 0;
+        break;
+    }
+
+    return ret;
+}
+
+static void dcr_write_ocm (void *opaque, int dcrn, target_ulong val)
+{
+    ppc405_ocm_t *ocm;
+    uint32_t isarc, dsarc, isacntl, dsacntl;
+
+    ocm = opaque;
+    isarc = ocm->isarc;
+    dsarc = ocm->dsarc;
+    isacntl = ocm->isacntl;
+    dsacntl = ocm->dsacntl;
+    switch (dcrn) {
+    case OCM0_ISARC:
+        isarc = val & 0xFC000000;
+        break;
+    case OCM0_ISACNTL:
+        isacntl = val & 0xC0000000;
+        break;
+    case OCM0_DSARC:
+        isarc = val & 0xFC000000;
+        break;
+    case OCM0_DSACNTL:
+        isacntl = val & 0xC0000000;
+        break;
+    }
+    ocm_update_mappings(ocm, isarc, isacntl, dsarc, dsacntl);
+    ocm->isarc = isarc;
+    ocm->dsarc = dsarc;
+    ocm->isacntl = isacntl;
+    ocm->dsacntl = dsacntl;
+}
+
+static void ocm_reset (void *opaque)
+{
+    ppc405_ocm_t *ocm;
+    uint32_t isarc, dsarc, isacntl, dsacntl;
+
+    ocm = opaque;
+    isarc = 0x00000000;
+    isacntl = 0x00000000;
+    dsarc = 0x00000000;
+    dsacntl = 0x00000000;
+    ocm_update_mappings(ocm, isarc, isacntl, dsarc, dsacntl);
+    ocm->isarc = isarc;
+    ocm->dsarc = dsarc;
+    ocm->isacntl = isacntl;
+    ocm->dsacntl = dsacntl;
+}
+
+void ppc405_ocm_init (CPUState *env, unsigned long offset)
+{
+    ppc405_ocm_t *ocm;
+
+    ocm = qemu_mallocz(sizeof(ppc405_ocm_t));
+    if (ocm != NULL) {
+        ocm->offset = offset;
+        ocm_reset(ocm);
+        qemu_register_reset(&ocm_reset, ocm);
+        ppc_dcr_register(env, OCM0_ISARC,
+                         ocm, &dcr_read_ocm, &dcr_write_ocm);
+        ppc_dcr_register(env, OCM0_ISACNTL,
+                         ocm, &dcr_read_ocm, &dcr_write_ocm);
+        ppc_dcr_register(env, OCM0_DSARC,
+                         ocm, &dcr_read_ocm, &dcr_write_ocm);
+        ppc_dcr_register(env, OCM0_DSACNTL,
+                         ocm, &dcr_read_ocm, &dcr_write_ocm);
+    }
+}
+
+/*****************************************************************************/
+/* I2C controller */
+typedef struct ppc4xx_i2c_t ppc4xx_i2c_t;
+struct ppc4xx_i2c_t {
+    uint32_t base;
+    uint8_t mdata;
+    uint8_t lmadr;
+    uint8_t hmadr;
+    uint8_t cntl;
+    uint8_t mdcntl;
+    uint8_t sts;
+    uint8_t extsts;
+    uint8_t sdata;
+    uint8_t lsadr;
+    uint8_t hsadr;
+    uint8_t clkdiv;
+    uint8_t intrmsk;
+    uint8_t xfrcnt;
+    uint8_t xtcntlss;
+    uint8_t directcntl;
+};
+
+static uint32_t ppc4xx_i2c_readb (void *opaque, target_phys_addr_t addr)
+{
+    ppc4xx_i2c_t *i2c;
+    uint32_t ret;
+
+#ifdef DEBUG_I2C
+    printf("%s: addr " PADDRX "\n", __func__, addr);
+#endif
+    i2c = opaque;
+    switch (addr - i2c->base) {
+    case 0x00:
+        //        i2c_readbyte(&i2c->mdata);
+        ret = i2c->mdata;
+        break;
+    case 0x02:
+        ret = i2c->sdata;
+        break;
+    case 0x04:
+        ret = i2c->lmadr;
+        break;
+    case 0x05:
+        ret = i2c->hmadr;
+        break;
+    case 0x06:
+        ret = i2c->cntl;
+        break;
+    case 0x07:
+        ret = i2c->mdcntl;
+        break;
+    case 0x08:
+        ret = i2c->sts;
+        break;
+    case 0x09:
+        ret = i2c->extsts;
+        break;
+    case 0x0A:
+        ret = i2c->lsadr;
+        break;
+    case 0x0B:
+        ret = i2c->hsadr;
+        break;
+    case 0x0C:
+        ret = i2c->clkdiv;
+        break;
+    case 0x0D:
+        ret = i2c->intrmsk;
+        break;
+    case 0x0E:
+        ret = i2c->xfrcnt;
+        break;
+    case 0x0F:
+        ret = i2c->xtcntlss;
+        break;
+    case 0x10:
+        ret = i2c->directcntl;
+        break;
+    default:
+        ret = 0x00;
+        break;
+    }
+#ifdef DEBUG_I2C
+    printf("%s: addr " PADDRX " %02x\n", __func__, addr, ret);
+#endif
+
+    return ret;
+}
+
+static void ppc4xx_i2c_writeb (void *opaque,
+                               target_phys_addr_t addr, uint32_t value)
+{
+    ppc4xx_i2c_t *i2c;
+
+#ifdef DEBUG_I2C
+    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
+#endif
+    i2c = opaque;
+    switch (addr - i2c->base) {
+    case 0x00:
+        i2c->mdata = value;
+        //        i2c_sendbyte(&i2c->mdata);
+        break;
+    case 0x02:
+        i2c->sdata = value;
+        break;
+    case 0x04:
+        i2c->lmadr = value;
+        break;
+    case 0x05:
+        i2c->hmadr = value;
+        break;
+    case 0x06:
+        i2c->cntl = value;
+        break;
+    case 0x07:
+        i2c->mdcntl = value & 0xDF;
+        break;
+    case 0x08:
+        i2c->sts &= ~(value & 0x0A);
+        break;
+    case 0x09:
+        i2c->extsts &= ~(value & 0x8F);
+        break;
+    case 0x0A:
+        i2c->lsadr = value;
+        break;
+    case 0x0B:
+        i2c->hsadr = value;
+        break;
+    case 0x0C:
+        i2c->clkdiv = value;
+        break;
+    case 0x0D:
+        i2c->intrmsk = value;
+        break;
+    case 0x0E:
+        i2c->xfrcnt = value & 0x77;
+        break;
+    case 0x0F:
+        i2c->xtcntlss = value;
+        break;
+    case 0x10:
+        i2c->directcntl = value & 0x7;
+        break;
+    }
+}
+
+static uint32_t ppc4xx_i2c_readw (void *opaque, target_phys_addr_t addr)
+{
+    uint32_t ret;
+
+#ifdef DEBUG_I2C
+    printf("%s: addr " PADDRX "\n", __func__, addr);
+#endif
+    ret = ppc4xx_i2c_readb(opaque, addr) << 8;
+    ret |= ppc4xx_i2c_readb(opaque, addr + 1);
+
+    return ret;
+}
+
+static void ppc4xx_i2c_writew (void *opaque,
+                               target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_I2C
+    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
+#endif
+    ppc4xx_i2c_writeb(opaque, addr, value >> 8);
+    ppc4xx_i2c_writeb(opaque, addr + 1, value);
+}
+
+static uint32_t ppc4xx_i2c_readl (void *opaque, target_phys_addr_t addr)
+{
+    uint32_t ret;
+
+#ifdef DEBUG_I2C
+    printf("%s: addr " PADDRX "\n", __func__, addr);
+#endif
+    ret = ppc4xx_i2c_readb(opaque, addr) << 24;
+    ret |= ppc4xx_i2c_readb(opaque, addr + 1) << 16;
+    ret |= ppc4xx_i2c_readb(opaque, addr + 2) << 8;
+    ret |= ppc4xx_i2c_readb(opaque, addr + 3);
+
+    return ret;
+}
+
+static void ppc4xx_i2c_writel (void *opaque,
+                               target_phys_addr_t addr, uint32_t value)
+{
+#ifdef DEBUG_I2C
+    printf("%s: addr " PADDRX " val %08x\n", __func__, addr, value);
+#endif
+    ppc4xx_i2c_writeb(opaque, addr, value >> 24);
+    ppc4xx_i2c_writeb(opaque, addr + 1, value >> 16);
+    ppc4xx_i2c_writeb(opaque, addr + 2, value >> 8);
+    ppc4xx_i2c_writeb(opaque, addr + 3, value);
+}
+
+static CPUReadMemoryFunc *i2c_read[] = {
+    &ppc4xx_i2c_readb,
+    &ppc4xx_i2c_readw,
+    &ppc4xx_i2c_readl,
+};
+
+static CPUWriteMemoryFunc *i2c_write[] = {
+    &ppc4xx_i2c_writeb,
+    &ppc4xx_i2c_writew,
+    &ppc4xx_i2c_writel,
+};
+
+static void ppc4xx_i2c_reset (void *opaque)
+{
+    ppc4xx_i2c_t *i2c;
+
+    i2c = opaque;
+    i2c->mdata = 0x00;
+    i2c->sdata = 0x00;
+    i2c->cntl = 0x00;
+    i2c->mdcntl = 0x00;
+    i2c->sts = 0x00;
+    i2c->extsts = 0x00;
+    i2c->clkdiv = 0x00;
+    i2c->xfrcnt = 0x00;
+    i2c->directcntl = 0x0F;
+}
+
+void ppc405_i2c_init (CPUState *env, ppc4xx_mmio_t *mmio, uint32_t offset)
+{
+    ppc4xx_i2c_t *i2c;
+
+    i2c = qemu_mallocz(sizeof(ppc4xx_i2c_t));
+    if (i2c != NULL) {
+        i2c->base = mmio->base + offset;
+        ppc4xx_i2c_reset(i2c);
+#ifdef DEBUG_I2C
+        printf("%s: offset=%08x\n", __func__, offset);
+#endif
+        ppc4xx_mmio_register(env, mmio, offset, 0x011,
+                             i2c_read, i2c_write, i2c);
+        qemu_register_reset(ppc4xx_i2c_reset, i2c);
+    }
+}
+
+/*****************************************************************************/
+/* SPR */
+void ppc40x_core_reset (CPUState *env)
+{
+    target_ulong dbsr;
+
+    printf("Reset PowerPC core\n");
+    cpu_ppc_reset(env);
+    dbsr = env->spr[SPR_40x_DBSR];
+    dbsr &= ~0x00000300;
+    dbsr |= 0x00000100;
+    env->spr[SPR_40x_DBSR] = dbsr;
+    cpu_loop_exit();
+}
+
+void ppc40x_chip_reset (CPUState *env)
+{
+    target_ulong dbsr;
+
+    printf("Reset PowerPC chip\n");
+    cpu_ppc_reset(env);
+    /* XXX: TODO reset all internal peripherals */
+    dbsr = env->spr[SPR_40x_DBSR];
+    dbsr &= ~0x00000300;
+    dbsr |= 0x00000100;
+    env->spr[SPR_40x_DBSR] = dbsr;
+    cpu_loop_exit();
+}
+
+void ppc40x_system_reset (CPUState *env)
+{
+    printf("Reset PowerPC system\n");
+    qemu_system_reset_request();
+}
+
+void store_40x_dbcr0 (CPUState *env, uint32_t val)
+{
+    switch ((val >> 28) & 0x3) {
+    case 0x0:
+        /* No action */
+        break;
+    case 0x1:
+        /* Core reset */
+        ppc40x_core_reset(env);
+        break;
+    case 0x2:
+        /* Chip reset */
+        ppc40x_chip_reset(env);
+        break;
+    case 0x3:
+        /* System reset */
+        ppc40x_system_reset(env);
+        break;
+    }
+}
+
+/*****************************************************************************/
+/* PowerPC 405CR */
+enum {
+    PPC405CR_CPC0_PLLMR  = 0x0B0,
+    PPC405CR_CPC0_CR0    = 0x0B1,
+    PPC405CR_CPC0_CR1    = 0x0B2,
+    PPC405CR_CPC0_PSR    = 0x0B4,
+    PPC405CR_CPC0_JTAGID = 0x0B5,
+    PPC405CR_CPC0_ER     = 0x0B9,
+    PPC405CR_CPC0_FR     = 0x0BA,
+    PPC405CR_CPC0_SR     = 0x0BB,
+};
+
+typedef struct ppc405cr_cpc_t ppc405cr_cpc_t;
+struct ppc405cr_cpc_t {
+    clk_setup_t clk_setup[7];
+    uint32_t sysclk;
+    uint32_t psr;
+    uint32_t cr0;
+    uint32_t cr1;
+    uint32_t jtagid;
+    uint32_t pllmr;
+    uint32_t er;
+    uint32_t fr;
+};
+
+static void ppc405cr_clk_setup (ppc405cr_cpc_t *cpc)
+{
+    uint64_t VCO_out, PLL_out;
+    uint32_t CPU_clk, TMR_clk, SDRAM_clk, PLB_clk, OPB_clk, EXT_clk, UART_clk;
+    int M, D0, D1, D2;
+
+    D0 = ((cpc->pllmr >> 26) & 0x3) + 1; /* CBDV */
+    if (cpc->pllmr & 0x80000000) {
+        D1 = (((cpc->pllmr >> 20) - 1) & 0xF) + 1; /* FBDV */
+        D2 = 8 - ((cpc->pllmr >> 16) & 0x7); /* FWDVA */
+        M = D0 * D1 * D2;
+        VCO_out = cpc->sysclk * M;
+        if (VCO_out < 400000000 || VCO_out > 800000000) {
+            /* PLL cannot lock */
+            cpc->pllmr &= ~0x80000000;
+            goto bypass_pll;
+        }
+        PLL_out = VCO_out / D2;
+    } else {
+        /* Bypass PLL */
+    bypass_pll:
+        M = D0;
+        PLL_out = cpc->sysclk * M;
+    }
+    CPU_clk = PLL_out;
+    if (cpc->cr1 & 0x00800000)
+        TMR_clk = cpc->sysclk; /* Should have a separate clock */
+    else
+        TMR_clk = CPU_clk;
+    PLB_clk = CPU_clk / D0;
+    SDRAM_clk = PLB_clk;
+    D0 = ((cpc->pllmr >> 10) & 0x3) + 1;
+    OPB_clk = PLB_clk / D0;
+    D0 = ((cpc->pllmr >> 24) & 0x3) + 2;
+    EXT_clk = PLB_clk / D0;
+    D0 = ((cpc->cr0 >> 1) & 0x1F) + 1;
+    UART_clk = CPU_clk / D0;
+    /* Setup CPU clocks */
+    clk_setup(&cpc->clk_setup[0], CPU_clk);
+    /* Setup time-base clock */
+    clk_setup(&cpc->clk_setup[1], TMR_clk);
+    /* Setup PLB clock */
+    clk_setup(&cpc->clk_setup[2], PLB_clk);
+    /* Setup SDRAM clock */
+    clk_setup(&cpc->clk_setup[3], SDRAM_clk);
+    /* Setup OPB clock */
+    clk_setup(&cpc->clk_setup[4], OPB_clk);
+    /* Setup external clock */
+    clk_setup(&cpc->clk_setup[5], EXT_clk);
+    /* Setup UART clock */
+    clk_setup(&cpc->clk_setup[6], UART_clk);
+}
+
+static target_ulong dcr_read_crcpc (void *opaque, int dcrn)
+{
+    ppc405cr_cpc_t *cpc;
+    target_ulong ret;
+
+    cpc = opaque;
+    switch (dcrn) {
+    case PPC405CR_CPC0_PLLMR:
+        ret = cpc->pllmr;
+        break;
+    case PPC405CR_CPC0_CR0:
+        ret = cpc->cr0;
+        break;
+    case PPC405CR_CPC0_CR1:
+        ret = cpc->cr1;
+        break;
+    case PPC405CR_CPC0_PSR:
+        ret = cpc->psr;
+        break;
+    case PPC405CR_CPC0_JTAGID:
+        ret = cpc->jtagid;
+        break;
+    case PPC405CR_CPC0_ER:
+        ret = cpc->er;
+        break;
+    case PPC405CR_CPC0_FR:
+        ret = cpc->fr;
+        break;
+    case PPC405CR_CPC0_SR:
+        ret = ~(cpc->er | cpc->fr) & 0xFFFF0000;
+        break;
+    default:
+        /* Avoid gcc warning */
+        ret = 0;
+        break;
+    }
+
+    return ret;
+}
+
+static void dcr_write_crcpc (void *opaque, int dcrn, target_ulong val)
+{
+    ppc405cr_cpc_t *cpc;
+
+    cpc = opaque;
+    switch (dcrn) {
+    case PPC405CR_CPC0_PLLMR:
+        cpc->pllmr = val & 0xFFF77C3F;
+        break;
+    case PPC405CR_CPC0_CR0:
+        cpc->cr0 = val & 0x0FFFFFFE;
+        break;
+    case PPC405CR_CPC0_CR1:
+        cpc->cr1 = val & 0x00800000;
+        break;
+    case PPC405CR_CPC0_PSR:
+        /* Read-only */
+        break;
+    case PPC405CR_CPC0_JTAGID:
+        /* Read-only */
+        break;
+    case PPC405CR_CPC0_ER:
+        cpc->er = val & 0xBFFC0000;
+        break;
+    case PPC405CR_CPC0_FR:
+        cpc->fr = val & 0xBFFC0000;
+        break;
+    case PPC405CR_CPC0_SR:
+        /* Read-only */
+        break;
+    }
+}
+
+static void ppc405cr_cpc_reset (void *opaque)
+{
+    ppc405cr_cpc_t *cpc;
+    int D;
+
+    cpc = opaque;
+    /* Compute PLLMR value from PSR settings */
+    cpc->pllmr = 0x80000000;
+    /* PFWD */
+    switch ((cpc->psr >> 30) & 3) {
+    case 0:
+        /* Bypass */
+        cpc->pllmr &= ~0x80000000;
+        break;
+    case 1:
+        /* Divide by 3 */
+        cpc->pllmr |= 5 << 16;
+        break;
+    case 2:
+        /* Divide by 4 */
+        cpc->pllmr |= 4 << 16;
+        break;
+    case 3:
+        /* Divide by 6 */
+        cpc->pllmr |= 2 << 16;
+        break;
+    }
+    /* PFBD */
+    D = (cpc->psr >> 28) & 3;
+    cpc->pllmr |= (D + 1) << 20;
+    /* PT   */
+    D = (cpc->psr >> 25) & 7;
+    switch (D) {
+    case 0x2:
+        cpc->pllmr |= 0x13;
+        break;
+    case 0x4:
+        cpc->pllmr |= 0x15;
+        break;
+    case 0x5:
+        cpc->pllmr |= 0x16;
+        break;
+    default:
+        break;
+    }
+    /* PDC  */
+    D = (cpc->psr >> 23) & 3;
+    cpc->pllmr |= D << 26;
+    /* ODP  */
+    D = (cpc->psr >> 21) & 3;
+    cpc->pllmr |= D << 10;
+    /* EBPD */
+    D = (cpc->psr >> 17) & 3;
+    cpc->pllmr |= D << 24;
+    cpc->cr0 = 0x0000003C;
+    cpc->cr1 = 0x2B0D8800;
+    cpc->er = 0x00000000;
+    cpc->fr = 0x00000000;
+    ppc405cr_clk_setup(cpc);
+}
+
+static void ppc405cr_clk_init (ppc405cr_cpc_t *cpc)
+{
+    int D;
+
+    /* XXX: this should be read from IO pins */
+    cpc->psr = 0x00000000; /* 8 bits ROM */
+    /* PFWD */
+    D = 0x2; /* Divide by 4 */
+    cpc->psr |= D << 30;
+    /* PFBD */
+    D = 0x1; /* Divide by 2 */
+    cpc->psr |= D << 28;
+    /* PDC */
+    D = 0x1; /* Divide by 2 */
+    cpc->psr |= D << 23;
+    /* PT */
+    D = 0x5; /* M = 16 */
+    cpc->psr |= D << 25;
+    /* ODP */
+    D = 0x1; /* Divide by 2 */
+    cpc->psr |= D << 21;
+    /* EBDP */
+    D = 0x2; /* Divide by 4 */
+    cpc->psr |= D << 17;
+}
+
+static void ppc405cr_cpc_init (CPUState *env, clk_setup_t clk_setup[7],
+                               uint32_t sysclk)
+{
+    ppc405cr_cpc_t *cpc;
+
+    cpc = qemu_mallocz(sizeof(ppc405cr_cpc_t));
+    if (cpc != NULL) {
+        memcpy(cpc->clk_setup, clk_setup, 7 * sizeof(clk_setup_t));
+        cpc->sysclk = sysclk;
+        cpc->jtagid = 0x42051049;
+        ppc_dcr_register(env, PPC405CR_CPC0_PSR, cpc,
+                         &dcr_read_crcpc, &dcr_write_crcpc);
+        ppc_dcr_register(env, PPC405CR_CPC0_CR0, cpc,
+                         &dcr_read_crcpc, &dcr_write_crcpc);
+        ppc_dcr_register(env, PPC405CR_CPC0_CR1, cpc,
+                         &dcr_read_crcpc, &dcr_write_crcpc);
+        ppc_dcr_register(env, PPC405CR_CPC0_JTAGID, cpc,
+                         &dcr_read_crcpc, &dcr_write_crcpc);
+        ppc_dcr_register(env, PPC405CR_CPC0_PLLMR, cpc,
+                         &dcr_read_crcpc, &dcr_write_crcpc);
+        ppc_dcr_register(env, PPC405CR_CPC0_ER, cpc,
+                         &dcr_read_crcpc, &dcr_write_crcpc);
+        ppc_dcr_register(env, PPC405CR_CPC0_FR, cpc,
+                         &dcr_read_crcpc, &dcr_write_crcpc);
+        ppc_dcr_register(env, PPC405CR_CPC0_SR, cpc,
+                         &dcr_read_crcpc, &dcr_write_crcpc);
+        ppc405cr_clk_init(cpc);
+        qemu_register_reset(ppc405cr_cpc_reset, cpc);
+        ppc405cr_cpc_reset(cpc);
+    }
+}
+
+CPUState *ppc405cr_init (target_ulong ram_bases[4], target_ulong ram_sizes[4],
+                         uint32_t sysclk, qemu_irq **picp,
+                         ram_addr_t *offsetp)
+{
+    clk_setup_t clk_setup[7];
+    qemu_irq dma_irqs[4];
+    CPUState *env;
+    ppc4xx_mmio_t *mmio;
+    qemu_irq *pic, *irqs;
+    ram_addr_t offset;
+    int i;
+
+    memset(clk_setup, 0, sizeof(clk_setup));
+    env = ppc405_init("405cr", &clk_setup[0], &clk_setup[1], sysclk);
+    /* Memory mapped devices registers */
+    mmio = ppc4xx_mmio_init(env, 0xEF600000);
+    /* PLB arbitrer */
+    ppc4xx_plb_init(env);
+    /* PLB to OPB bridge */
+    ppc4xx_pob_init(env);
+    /* OBP arbitrer */
+    ppc4xx_opba_init(env, mmio, 0x600);
+    /* Universal interrupt controller */
+    irqs = qemu_mallocz(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB);
+    irqs[PPCUIC_OUTPUT_INT] =
+        ((qemu_irq *)env->irq_inputs)[PPC405_INPUT_INT];
+    irqs[PPCUIC_OUTPUT_CINT] =
+        ((qemu_irq *)env->irq_inputs)[PPC405_INPUT_CINT];
+    pic = ppcuic_init(env, irqs, 0x0C0, 0, 1);
+    *picp = pic;
+    /* SDRAM controller */
+    ppc405_sdram_init(env, pic[17], 1, ram_bases, ram_sizes);
+    offset = 0;
+    for (i = 0; i < 4; i++)
+        offset += ram_sizes[i];
+    /* External bus controller */
+    ppc405_ebc_init(env);
+    /* DMA controller */
+    dma_irqs[0] = pic[5];
+    dma_irqs[1] = pic[6];
+    dma_irqs[2] = pic[7];
+    dma_irqs[3] = pic[8];
+    ppc405_dma_init(env, dma_irqs);
+    /* Serial ports */
+    if (serial_hds[0] != NULL) {
+        ppc405_serial_init(env, mmio, 0x400, pic[0], serial_hds[0]);
+    }
+    if (serial_hds[1] != NULL) {
+        ppc405_serial_init(env, mmio, 0x300, pic[1], serial_hds[1]);
+    }
+    /* IIC controller */
+    ppc405_i2c_init(env, mmio, 0x500);
+    /* GPIO */
+    ppc405_gpio_init(env, mmio, 0x700);
+    /* CPU control */
+    ppc405cr_cpc_init(env, clk_setup, sysclk);
+    *offsetp = offset;
+
+    return env;
+}
+
+/*****************************************************************************/
+/* PowerPC 405EP */
+/* CPU control */
+enum {
+    PPC405EP_CPC0_PLLMR0 = 0x0F0,
+    PPC405EP_CPC0_BOOT   = 0x0F1,
+    PPC405EP_CPC0_EPCTL  = 0x0F3,
+    PPC405EP_CPC0_PLLMR1 = 0x0F4,
+    PPC405EP_CPC0_UCR    = 0x0F5,
+    PPC405EP_CPC0_SRR    = 0x0F6,
+    PPC405EP_CPC0_JTAGID = 0x0F7,
+    PPC405EP_CPC0_PCI    = 0x0F9,
+};
+
+typedef struct ppc405ep_cpc_t ppc405ep_cpc_t;
+struct ppc405ep_cpc_t {
+    uint32_t sysclk;
+    clk_setup_t clk_setup[8];
+    uint32_t boot;
+    uint32_t epctl;
+    uint32_t pllmr[2];
+    uint32_t ucr;
+    uint32_t srr;
+    uint32_t jtagid;
+    uint32_t pci;
+};
+
+static void ppc405ep_compute_clocks (ppc405ep_cpc_t *cpc)
+{
+    uint32_t CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk;
+    uint32_t UART0_clk, UART1_clk;
+    uint64_t VCO_out, PLL_out;
+    int M, D;
+
+    VCO_out = 0;
+    if ((cpc->pllmr[1] & 0x80000000) && !(cpc->pllmr[1] & 0x40000000)) {
+        M = (((cpc->pllmr[1] >> 20) - 1) & 0xF) + 1; /* FBMUL */
+        //        printf("FBMUL %01x %d\n", (cpc->pllmr[1] >> 20) & 0xF, M);
+        D = 8 - ((cpc->pllmr[1] >> 16) & 0x7); /* FWDA */
+        //        printf("FWDA %01x %d\n", (cpc->pllmr[1] >> 16) & 0x7, D);
+        VCO_out = cpc->sysclk * M * D;
+        if (VCO_out < 500000000UL || VCO_out > 1000000000UL) {
+            /* Error - unlock the PLL */
+            printf("VCO out of range %" PRIu64 "\n", VCO_out);
+#if 0
+            cpc->pllmr[1] &= ~0x80000000;
+            goto pll_bypass;
+#endif
+        }
+        PLL_out = VCO_out / D;
+    } else {
+#if 0
+    pll_bypass:
+#endif
+        PLL_out = cpc->sysclk;
+    }
+    /* Now, compute all other clocks */
+    D = ((cpc->pllmr[0] >> 20) & 0x3) + 1; /* CCDV */
+#ifdef DEBUG_CLOCKS
+    //    printf("CCDV %01x %d\n", (cpc->pllmr[0] >> 20) & 0x3, D);
+#endif
+    CPU_clk = PLL_out / D;
+    D = ((cpc->pllmr[0] >> 16) & 0x3) + 1; /* CBDV */
+#ifdef DEBUG_CLOCKS
+    //    printf("CBDV %01x %d\n", (cpc->pllmr[0] >> 16) & 0x3, D);
+#endif
+    PLB_clk = CPU_clk / D;
+    D = ((cpc->pllmr[0] >> 12) & 0x3) + 1; /* OPDV */
+#ifdef DEBUG_CLOCKS
+    //    printf("OPDV %01x %d\n", (cpc->pllmr[0] >> 12) & 0x3, D);
+#endif
+    OPB_clk = PLB_clk / D;
+    D = ((cpc->pllmr[0] >> 8) & 0x3) + 2; /* EPDV */
+#ifdef DEBUG_CLOCKS
+    //    printf("EPDV %01x %d\n", (cpc->pllmr[0] >> 8) & 0x3, D);
+#endif
+    EBC_clk = PLB_clk / D;
+    D = ((cpc->pllmr[0] >> 4) & 0x3) + 1; /* MPDV */
+#ifdef DEBUG_CLOCKS
+    //    printf("MPDV %01x %d\n", (cpc->pllmr[0] >> 4) & 0x3, D);
+#endif
+    MAL_clk = PLB_clk / D;
+    D = (cpc->pllmr[0] & 0x3) + 1; /* PPDV */
+#ifdef DEBUG_CLOCKS
+    //    printf("PPDV %01x %d\n", cpc->pllmr[0] & 0x3, D);
+#endif
+    PCI_clk = PLB_clk / D;
+    D = ((cpc->ucr - 1) & 0x7F) + 1; /* U0DIV */
+#ifdef DEBUG_CLOCKS
+    //    printf("U0DIV %01x %d\n", cpc->ucr & 0x7F, D);
+#endif
+    UART0_clk = PLL_out / D;
+    D = (((cpc->ucr >> 8) - 1) & 0x7F) + 1; /* U1DIV */
+#ifdef DEBUG_CLOCKS
+    //    printf("U1DIV %01x %d\n", (cpc->ucr >> 8) & 0x7F, D);
+#endif
+    UART1_clk = PLL_out / D;
+#ifdef DEBUG_CLOCKS
+    printf("Setup PPC405EP clocks - sysclk %d VCO %" PRIu64
+           " PLL out %" PRIu64 " Hz\n", cpc->sysclk, VCO_out, PLL_out);
+    printf("CPU %d PLB %d OPB %d EBC %d MAL %d PCI %d UART0 %d UART1 %d\n",
+           CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk,
+           UART0_clk, UART1_clk);
+#endif
+    /* Setup CPU clocks */
+    clk_setup(&cpc->clk_setup[0], CPU_clk);
+    /* Setup PLB clock */
+    clk_setup(&cpc->clk_setup[1], PLB_clk);
+    /* Setup OPB clock */
+    clk_setup(&cpc->clk_setup[2], OPB_clk);
+    /* Setup external clock */
+    clk_setup(&cpc->clk_setup[3], EBC_clk);
+    /* Setup MAL clock */
+    clk_setup(&cpc->clk_setup[4], MAL_clk);
+    /* Setup PCI clock */
+    clk_setup(&cpc->clk_setup[5], PCI_clk);
+    /* Setup UART0 clock */
+    clk_setup(&cpc->clk_setup[6], UART0_clk);
+    /* Setup UART1 clock */
+    clk_setup(&cpc->clk_setup[7], UART0_clk);
+}
+
+static target_ulong dcr_read_epcpc (void *opaque, int dcrn)
+{
+    ppc405ep_cpc_t *cpc;
+    target_ulong ret;
+
+    cpc = opaque;
+    switch (dcrn) {
+    case PPC405EP_CPC0_BOOT:
+        ret = cpc->boot;
+        break;
+    case PPC405EP_CPC0_EPCTL:
+        ret = cpc->epctl;
+        break;
+    case PPC405EP_CPC0_PLLMR0:
+        ret = cpc->pllmr[0];
+        break;
+    case PPC405EP_CPC0_PLLMR1:
+        ret = cpc->pllmr[1];
+        break;
+    case PPC405EP_CPC0_UCR:
+        ret = cpc->ucr;
+        break;
+    case PPC405EP_CPC0_SRR:
+        ret = cpc->srr;
+        break;
+    case PPC405EP_CPC0_JTAGID:
+        ret = cpc->jtagid;
+        break;
+    case PPC405EP_CPC0_PCI:
+        ret = cpc->pci;
+        break;
+    default:
+        /* Avoid gcc warning */
+        ret = 0;
+        break;
+    }
+
+    return ret;
+}
+
+static void dcr_write_epcpc (void *opaque, int dcrn, target_ulong val)
+{
+    ppc405ep_cpc_t *cpc;
+
+    cpc = opaque;
+    switch (dcrn) {
+    case PPC405EP_CPC0_BOOT:
+        /* Read-only register */
+        break;
+    case PPC405EP_CPC0_EPCTL:
+        /* Don't care for now */
+        cpc->epctl = val & 0xC00000F3;
+        break;
+    case PPC405EP_CPC0_PLLMR0:
+        cpc->pllmr[0] = val & 0x00633333;
+        ppc405ep_compute_clocks(cpc);
+        break;
+    case PPC405EP_CPC0_PLLMR1:
+        cpc->pllmr[1] = val & 0xC0F73FFF;
+        ppc405ep_compute_clocks(cpc);
+        break;
+    case PPC405EP_CPC0_UCR:
+        /* UART control - don't care for now */
+        cpc->ucr = val & 0x003F7F7F;
+        break;
+    case PPC405EP_CPC0_SRR:
+        cpc->srr = val;
+        break;
+    case PPC405EP_CPC0_JTAGID:
+        /* Read-only */
+        break;
+    case PPC405EP_CPC0_PCI:
+        cpc->pci = val;
+        break;
+    }
+}
+
+static void ppc405ep_cpc_reset (void *opaque)
+{
+    ppc405ep_cpc_t *cpc = opaque;
+
+    cpc->boot = 0x00000010;     /* Boot from PCI - IIC EEPROM disabled */
+    cpc->epctl = 0x00000000;
+    cpc->pllmr[0] = 0x00011010;
+    cpc->pllmr[1] = 0x40000000;
+    cpc->ucr = 0x00000000;
+    cpc->srr = 0x00040000;
+    cpc->pci = 0x00000000;
+    ppc405ep_compute_clocks(cpc);
+}
+
+/* XXX: sysclk should be between 25 and 100 MHz */
+static void ppc405ep_cpc_init (CPUState *env, clk_setup_t clk_setup[8],
+                               uint32_t sysclk)
+{
+    ppc405ep_cpc_t *cpc;
+
+    cpc = qemu_mallocz(sizeof(ppc405ep_cpc_t));
+    if (cpc != NULL) {
+        memcpy(cpc->clk_setup, clk_setup, 7 * sizeof(clk_setup_t));
+        cpc->jtagid = 0x20267049;
+        cpc->sysclk = sysclk;
+        ppc405ep_cpc_reset(cpc);
+        qemu_register_reset(&ppc405ep_cpc_reset, cpc);
+        ppc_dcr_register(env, PPC405EP_CPC0_BOOT, cpc,
+                         &dcr_read_epcpc, &dcr_write_epcpc);
+        ppc_dcr_register(env, PPC405EP_CPC0_EPCTL, cpc,
+                         &dcr_read_epcpc, &dcr_write_epcpc);
+        ppc_dcr_register(env, PPC405EP_CPC0_PLLMR0, cpc,
+                         &dcr_read_epcpc, &dcr_write_epcpc);
+        ppc_dcr_register(env, PPC405EP_CPC0_PLLMR1, cpc,
+                         &dcr_read_epcpc, &dcr_write_epcpc);
+        ppc_dcr_register(env, PPC405EP_CPC0_UCR, cpc,
+                         &dcr_read_epcpc, &dcr_write_epcpc);
+        ppc_dcr_register(env, PPC405EP_CPC0_SRR, cpc,
+                         &dcr_read_epcpc, &dcr_write_epcpc);
+        ppc_dcr_register(env, PPC405EP_CPC0_JTAGID, cpc,
+                         &dcr_read_epcpc, &dcr_write_epcpc);
+        ppc_dcr_register(env, PPC405EP_CPC0_PCI, cpc,
+                         &dcr_read_epcpc, &dcr_write_epcpc);
+    }
+}
+
+CPUState *ppc405ep_init (target_ulong ram_bases[2], target_ulong ram_sizes[2],
+                         uint32_t sysclk, qemu_irq **picp,
+                         ram_addr_t *offsetp)
+{
+    clk_setup_t clk_setup[8];
+    qemu_irq dma_irqs[4];
+    CPUState *env;
+    ppc4xx_mmio_t *mmio;
+    qemu_irq *pic, *irqs;
+    ram_addr_t offset;
+    int i;
+
+    memset(clk_setup, 0, sizeof(clk_setup));
+    /* init CPUs */
+    env = ppc405_init("405ep", &clk_setup[0], &clk_setup[1], sysclk);
+    /* Internal devices init */
+    /* Memory mapped devices registers */
+    mmio = ppc4xx_mmio_init(env, 0xEF600000);
+    /* PLB arbitrer */
+    ppc4xx_plb_init(env);
+    /* PLB to OPB bridge */
+    ppc4xx_pob_init(env);
+    /* OBP arbitrer */
+    ppc4xx_opba_init(env, mmio, 0x600);
+    /* Universal interrupt controller */
+    irqs = qemu_mallocz(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB);
+    irqs[PPCUIC_OUTPUT_INT] =
+        ((qemu_irq *)env->irq_inputs)[PPC405_INPUT_INT];
+    irqs[PPCUIC_OUTPUT_CINT] =
+        ((qemu_irq *)env->irq_inputs)[PPC405_INPUT_CINT];
+    pic = ppcuic_init(env, irqs, 0x0C0, 0, 1);
+    *picp = pic;
+    /* SDRAM controller */
+    ppc405_sdram_init(env, pic[17], 2, ram_bases, ram_sizes);
+    offset = 0;
+    for (i = 0; i < 2; i++)
+        offset += ram_sizes[i];
+    /* External bus controller */
+    ppc405_ebc_init(env);
+    /* DMA controller */
+    dma_irqs[0] = pic[5];
+    dma_irqs[1] = pic[6];
+    dma_irqs[2] = pic[7];
+    dma_irqs[3] = pic[8];
+    ppc405_dma_init(env, dma_irqs);
+    /* IIC controller */
+    ppc405_i2c_init(env, mmio, 0x500);
+    /* GPIO */
+    ppc405_gpio_init(env, mmio, 0x700);
+    /* Serial ports */
+    if (serial_hds[0] != NULL) {
+        ppc405_serial_init(env, mmio, 0x300, pic[0], serial_hds[0]);
+    }
+    if (serial_hds[1] != NULL) {
+        ppc405_serial_init(env, mmio, 0x400, pic[1], serial_hds[1]);
+    }
+    /* OCM */
+    ppc405_ocm_init(env, ram_sizes[0] + ram_sizes[1]);
+    offset += 4096;
+    /* PCI */
+    /* CPU control */
+    ppc405ep_cpc_init(env, clk_setup, sysclk);
+    *offsetp = offset;
+
+    return env;
+}
index eb3340c52846234198c00cfebaf242003b4fb812..f17f84641ce5455c263836fec81ae680915de2b1 100644 (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);
index 87e8c1836c3091283f76b1bdd719fe22efa0082d..a0f91ccd405f799e4818f185fadcaf093179a4b8 100644 (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)
 {
index 68828f5f17b14ec338e22c4309479185a587bbdf..b8498f1827355099455d7f614070b42317ea53b4 100644 (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);
index 215bdcbe6f93ab6d6aff541702323d2f103968bd..cb40dfbddebc3be8cd8eb49fe36a699b63ec529a 100644 (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 */
diff --git a/vl.h b/vl.h
index b0e37dd78376ff5f55b96a202f574255dd0ae540..fbad5e2a19d808467862fe9449ca9cd8d8b1336b 100644 (file)
--- a/vl.h
+++ b/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);