Share devices that might be useful for all PowerPC 40x & 440 implementations
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 7 Oct 2007 14:21:26 +0000 (14:21 +0000)
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 7 Oct 2007 14:21:26 +0000 (14:21 +0000)
 (mostly CPU registration and UIC, for now).

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3340 c046a42c-6fe2-441c-8c8c-71466251a162

hw/ppc405.h
hw/ppc405_uc.c
hw/ppc4xx.h [new file with mode: 0644]
hw/ppc4xx_devs.c [new file with mode: 0644]

index eacbefe..e032170 100644 (file)
@@ -25,6 +25,8 @@
 #if !defined(PPC_405_H)
 #define PPC_405_H
 
+#include "ppc4xx.h"
+
 /* Bootinfo as set-up by u-boot */
 typedef struct ppc4xx_bd_info_t ppc4xx_bd_info_t;
 struct ppc4xx_bd_info_t {
@@ -54,19 +56,9 @@ struct ppc4xx_bd_info_t {
 };
 
 /* PowerPC 405 core */
-CPUState *ppc405_init (const unsigned char *cpu_model,
-                       clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
-                       uint32_t sysclk);
 ram_addr_t ppc405_set_bootinfo (CPUState *env, ppc4xx_bd_info_t *bd,
                                 uint32_t flags);
 
-/* */
-typedef struct ppc4xx_mmio_t ppc4xx_mmio_t;
-int ppc4xx_mmio_register (CPUState *env, ppc4xx_mmio_t *mmio,
-                          target_phys_addr_t offset, uint32_t len,
-                          CPUReadMemoryFunc **mem_read,
-                          CPUWriteMemoryFunc **mem_write, void *opaque);
-ppc4xx_mmio_t *ppc4xx_mmio_init (CPUState *env, target_phys_addr_t base);
 /* PowerPC 4xx peripheral local bus arbitrer */
 void ppc4xx_plb_init (CPUState *env);
 /* PLB to OPB bridge */
@@ -74,14 +66,6 @@ void ppc4xx_pob_init (CPUState *env);
 /* OPB arbitrer */
 void ppc4xx_opba_init (CPUState *env, ppc4xx_mmio_t *mmio,
                        target_phys_addr_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_phys_addr_t *ram_bases,
index 8a31475..55f4bcc 100644 (file)
@@ -27,7 +27,6 @@
 extern int loglevel;
 extern FILE *logfile;
 
-//#define DEBUG_MMIO
 #define DEBUG_OPBA
 #define DEBUG_SDRAM
 #define DEBUG_GPIO
@@ -36,41 +35,9 @@ extern FILE *logfile;
 //#define DEBUG_I2C
 #define DEBUG_GPT
 #define DEBUG_MAL
-#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();
-    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_ppc_reset(env);
-    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);
-    /* Register Qemu callbacks */
-    qemu_register_reset(&cpu_ppc_reset, env);
-    register_savevm("cpu", 0, 3, cpu_save, cpu_load, env);
-
-    return env;
-}
-
 ram_addr_t ppc405_set_bootinfo (CPUState *env, ppc4xx_bd_info_t *bd,
                                 uint32_t flags)
 {
@@ -124,203 +91,6 @@ ram_addr_t ppc405_set_bootinfo (CPUState *env, ppc4xx_bd_info_t *bd,
 /* 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 {
-    target_phys_addr_t base;
-    CPUReadMemoryFunc **mem_read[MMIO_AREA_NB];
-    CPUWriteMemoryFunc **mem_write[MMIO_AREA_NB];
-    void *opaque[MMIO_AREA_NB];
-};
-
-static uint32_t unassigned_mmio_readb (void *opaque, target_phys_addr_t addr)
-{
-#ifdef DEBUG_UNASSIGNED
-    ppc4xx_mmio_t *mmio;
-
-    mmio = opaque;
-    printf("Unassigned mmio read 0x" PADDRX " base " PADDRX "\n",
-           addr, mmio->base);
-#endif
-
-    return 0;
-}
-
-static void unassigned_mmio_writeb (void *opaque,
-                                   target_phys_addr_t addr, uint32_t val)
-{
-#ifdef DEBUG_UNASSIGNED
-    ppc4xx_mmio_t *mmio;
-
-    mmio = opaque;
-    printf("Unassigned mmio write 0x" PADDRX " = 0x%x base " PADDRX "\n",
-           addr, val, mmio->base);
-#endif
-}
-
-static CPUReadMemoryFunc *unassigned_mmio_read[3] = {
-    unassigned_mmio_readb,
-    unassigned_mmio_readb,
-    unassigned_mmio_readb,
-};
-
-static CPUWriteMemoryFunc *unassigned_mmio_write[3] = {
-    unassigned_mmio_writeb,
-    unassigned_mmio_writeb,
-    unassigned_mmio_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 - mmio->base);
-
-    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 - mmio->base, 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,
-                          target_phys_addr_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, target_phys_addr_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_mmio_read, unassigned_mmio_write,
-                             mmio);
-    }
-
-    return mmio;
-}
-
-/*****************************************************************************/
 /* Peripheral local bus arbitrer */
 enum {
     PLB0_BESR = 0x084,
@@ -625,281 +395,6 @@ void ppc4xx_opba_init (CPUState *env, ppc4xx_mmio_t *mmio,
 }
 
 /*****************************************************************************/
-/* "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 */
 
@@ -3040,7 +2535,7 @@ CPUState *ppc405cr_init (target_phys_addr_t ram_bases[4],
     int i;
 
     memset(clk_setup, 0, sizeof(clk_setup));
-    env = ppc405_init("405cr", &clk_setup[PPC405CR_CPU_CLK],
+    env = ppc4xx_init("405cr", &clk_setup[PPC405CR_CPU_CLK],
                       &clk_setup[PPC405CR_TMR_CLK], sysclk);
     /* Memory mapped devices registers */
     mmio = ppc4xx_mmio_init(env, 0xEF600000);
@@ -3390,7 +2885,7 @@ CPUState *ppc405ep_init (target_phys_addr_t ram_bases[2],
 
     memset(clk_setup, 0, sizeof(clk_setup));
     /* init CPUs */
-    env = ppc405_init("405ep", &clk_setup[PPC405EP_CPU_CLK],
+    env = ppc4xx_init("405ep", &clk_setup[PPC405EP_CPU_CLK],
                       &tlb_clk_setup, sysclk);
     clk_setup[PPC405EP_CPU_CLK].cb = tlb_clk_setup.cb;
     clk_setup[PPC405EP_CPU_CLK].opaque = tlb_clk_setup.opaque;
diff --git a/hw/ppc4xx.h b/hw/ppc4xx.h
new file mode 100644 (file)
index 0000000..8d7863c
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * QEMU PowerPC 4xx emulation shared definitions
+ *
+ * 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.
+ */
+
+#if !defined(PPC_4XX_H)
+#define PPC_4XX_H
+
+/* PowerPC 4xx core initialization */
+CPUState *ppc4xx_init (const unsigned char *cpu_model,
+                       clk_setup_t *cpu_clk, clk_setup_t *tb_clk,
+                       uint32_t sysclk);
+
+typedef struct ppc4xx_mmio_t ppc4xx_mmio_t;
+int ppc4xx_mmio_register (CPUState *env, ppc4xx_mmio_t *mmio,
+                          target_phys_addr_t offset, uint32_t len,
+                          CPUReadMemoryFunc **mem_read,
+                          CPUWriteMemoryFunc **mem_write, void *opaque);
+ppc4xx_mmio_t *ppc4xx_mmio_init (CPUState *env, target_phys_addr_t base);
+
+/* 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);
+
+#endif /* !defined(PPC_4XX_H) */
diff --git a/hw/ppc4xx_devs.c b/hw/ppc4xx_devs.c
new file mode 100644 (file)
index 0000000..8cefd74
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * QEMU PowerPC 4xx embedded processors shared devices 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"
+#include "ppc4xx.h"
+
+extern int loglevel;
+extern FILE *logfile;
+
+//#define DEBUG_MMIO
+#define DEBUG_UIC
+
+/*****************************************************************************/
+/* Generic PowerPC 4xx processor instanciation */
+CPUState *ppc4xx_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();
+    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_ppc_reset(env);
+    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);
+    /* Register qemu callbacks */
+    qemu_register_reset(&cpu_ppc_reset, env);
+    register_savevm("cpu", 0, 3, cpu_save, cpu_load, env);
+
+    return env;
+}
+
+/*****************************************************************************/
+/* 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 {
+    target_phys_addr_t base;
+    CPUReadMemoryFunc **mem_read[MMIO_AREA_NB];
+    CPUWriteMemoryFunc **mem_write[MMIO_AREA_NB];
+    void *opaque[MMIO_AREA_NB];
+};
+
+static uint32_t unassigned_mmio_readb (void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_UNASSIGNED
+    ppc4xx_mmio_t *mmio;
+
+    mmio = opaque;
+    printf("Unassigned mmio read 0x" PADDRX " base " PADDRX "\n",
+           addr, mmio->base);
+#endif
+
+    return 0;
+}
+
+static void unassigned_mmio_writeb (void *opaque,
+                                    target_phys_addr_t addr, uint32_t val)
+{
+#ifdef DEBUG_UNASSIGNED
+    ppc4xx_mmio_t *mmio;
+
+    mmio = opaque;
+    printf("Unassigned mmio write 0x" PADDRX " = 0x%x base " PADDRX "\n",
+           addr, val, mmio->base);
+#endif
+}
+
+static CPUReadMemoryFunc *unassigned_mmio_read[3] = {
+    unassigned_mmio_readb,
+    unassigned_mmio_readb,
+    unassigned_mmio_readb,
+};
+
+static CPUWriteMemoryFunc *unassigned_mmio_write[3] = {
+    unassigned_mmio_writeb,
+    unassigned_mmio_writeb,
+    unassigned_mmio_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 - mmio->base);
+
+    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 - mmio->base, 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,
+                          target_phys_addr_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, target_phys_addr_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_mmio_read, unassigned_mmio_write,
+                             mmio);
+    }
+
+    return mmio;
+}
+
+/*****************************************************************************/
+/* "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);
+}