Patchwork [RFC,V1,14/14] microblaze_generic_fdt: first revision

login
register
mail settings
Submitter Peter A. G. Crosthwaite
Date Aug. 25, 2011, 6:41 a.m.
Message ID <1314254480-22438-15-git-send-email-peter.crosthwaite@petalogix.com>
Download mbox | patch
Permalink /patch/111474/
State New
Headers show

Comments

Peter A. G. Crosthwaite - Aug. 25, 2011, 6:41 a.m.
First revision of fdt generic platform for xilinx microblaze platforms.
Adds machine model "microblaze-fdt" which can be used along with the --hw-dtb
option to lauch dts driven machine models for microblaze platforms.

Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
---
 Makefile.target             |    1 +
 hw/microblaze_generic_fdt.c |  439 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 440 insertions(+), 0 deletions(-)
 create mode 100644 hw/microblaze_generic_fdt.c

Patch

diff --git a/Makefile.target b/Makefile.target
index e280bf6..6a0ee92 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -296,6 +296,7 @@  obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o
 
 obj-microblaze-y = petalogix_s3adsp1800_mmu.o
 obj-microblaze-y += petalogix_ml605_mmu.o
+obj-microblaze-$(CONFIG_FDT) += microblaze_generic_fdt.o
 
 obj-microblaze-y += microblaze_pic_cpu.o
 obj-microblaze-y += xilinx_intc.o
diff --git a/hw/microblaze_generic_fdt.c b/hw/microblaze_generic_fdt.c
new file mode 100644
index 0000000..08852ff
--- /dev/null
+++ b/hw/microblaze_generic_fdt.c
@@ -0,0 +1,439 @@ 
+/*
+ * Model of Petalogix linux reference design targetting
+ * Xilinx Spartan 3ADSP-1800 boards.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ * Copyright (c) 2009 Michal Simek.
+ *
+ * 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.
+ */
+
+/* TODO slim down these includes */
+
+#include <time.h>
+#include <sys/time.h>
+#include "sysbus.h"
+#include "hw.h"
+#include "pc.h"
+#include "net.h"
+#include "flash.h"
+#include "sysemu.h"
+#include "devices.h"
+#include "boards.h"
+#include "device_tree.h"
+#include "qdev-addr.h"
+#include "loader.h"
+#include "elf.h"
+#include "fdt_generic_util.h"
+#include "fdt_generic_devices.h"
+
+#include "microblaze_pic_cpu.h"
+
+#define VAL(name) qemu_devtree_getprop(fdt, NULL, node_path, name, 0, 0)
+
+static inline void
+microblaze_pvr_fdt_init(CPUState *env, void *fdt)
+{
+    char node_path [DT_PATH_LENGTH];
+    qemu_devtree_get_node_by_name(fdt, node_path, "cpu@");
+    int t;
+    int use_exc = 0;
+
+    env->pvr.regs[0] = 0;
+    env->pvr.regs[2] = PVR2_D_OPB_MASK \
+                        | PVR2_D_LMB_MASK \
+                        | PVR2_I_OPB_MASK \
+                        | PVR2_I_LMB_MASK \
+                        | 0;
+
+    if (VAL("xlnx,pvr")) {
+        env->sregs[SR_MSR] |= MSR_PVR;
+    }
+
+    /* Even if we don't have PVR's, we fill out everything
+       because QEMU will internally follow what the pvr regs
+       state about the HW.  */
+
+    if (VAL("xlnx,pvr") == 2) {
+        env->pvr.regs[0] |= PVR0_PVR_FULL_MASK;
+    }
+
+    if (VAL("xlnx,endianness")) {
+        env->pvr.regs[0] |= PVR0_ENDI;
+    }
+
+    if (VAL("xlnx,use-barrel")) {
+        env->pvr.regs[0] |= PVR0_USE_BARREL_MASK;
+        env->pvr.regs[2] |= PVR2_USE_BARREL_MASK;
+    }
+
+    if (VAL("xlnx,use-div")) {
+        env->pvr.regs[0] |= PVR0_USE_DIV_MASK;
+        env->pvr.regs[2] |= PVR2_USE_DIV_MASK;
+    }
+
+    t = VAL("xlnx,use-hw-mul");
+    if (t) {
+        env->pvr.regs[0] |= PVR0_USE_HW_MUL_MASK;
+        env->pvr.regs[2] |= PVR2_USE_HW_MUL_MASK;
+        if (t >= 2) {
+            env->pvr.regs[2] |= PVR2_USE_MUL64_MASK;
+        }
+    }
+
+    t = VAL("xlnx,use-fpu");
+    if (t) {
+        env->pvr.regs[0] |= PVR0_USE_FPU_MASK;
+        env->pvr.regs[2] |= PVR2_USE_FPU_MASK;
+        if (t > 1) {
+            env->pvr.regs[2] |= PVR2_USE_FPU2_MASK;
+        }
+    }
+
+    if (VAL("xlnx,use-msr-instr")) {
+        env->pvr.regs[2] |= PVR2_USE_MSR_INSTR;
+    }
+
+    if (VAL("xlnx,use-pcmp-instr")) {
+        env->pvr.regs[2] |= PVR2_USE_PCMP_INSTR;
+    }
+
+    if (VAL("xlnx,opcode-0x0-illegal")) {
+        env->pvr.regs[2] |= PVR2_OPCODE_0x0_ILL_MASK;
+    }
+
+    if (VAL("xlnx,unaligned-exceptions")) {
+        env->pvr.regs[2] |= PVR2_UNALIGNED_EXC_MASK;
+        use_exc = 1;
+    }
+
+    if (VAL("xlnx,ill-opcode-exception")) {
+        env->pvr.regs[2] |= PVR2_ILL_OPCODE_EXC_MASK;
+        use_exc = 1;
+    }
+
+    if (VAL("xlnx,iopb-bus-exception")) {
+        env->pvr.regs[2] |= PVR2_IOPB_BUS_EXC_MASK;
+        use_exc = 1;
+    }
+
+    if (VAL("xlnx,dopb-bus-exception")) {
+        env->pvr.regs[2] |= PVR2_DOPB_BUS_EXC_MASK;
+        use_exc = 1;
+    }
+
+    if (VAL("xlnx,div-zero-exception")) {
+        env->pvr.regs[2] |= PVR2_DIV_ZERO_EXC_MASK;
+        use_exc = 1;
+    }
+
+    if (VAL("xlnx,fpu-exception")) {
+        env->pvr.regs[2] |= PVR2_FPU_EXC_MASK;
+        use_exc = 1;
+    }
+
+    env->pvr.regs[0] |= VAL("xlnx,pvr-user1") & 0xff;
+    env->pvr.regs[1] = VAL("xlnx,pvr-user2");
+
+    /* MMU regs.  */
+    t = VAL("xlnx,use-mmu");
+    if (use_exc || t) {
+        env->pvr.regs[0] |= PVR0_USE_EXC_MASK ;
+    }
+
+    if (t) {
+        env->pvr.regs[0] |= PVR0_USE_MMU;
+    }
+    env->pvr.regs[11] = t << 30;
+    t = VAL("xlnx,mmu-zones");
+    env->pvr.regs[11] |= t << 17;
+    env->mmu.c_mmu_zones = t;
+
+    t = VAL("xlnx,mmu-tlb-access");
+    env->mmu.c_mmu_tlb_access = t;
+    env->pvr.regs[11] |= t << 22;
+
+    {
+        const char *str;
+        const struct {
+            const char *name;
+            unsigned int arch;
+        } arch_lookup[] = {
+            {"virtex2", 0x4},
+            {"virtex2pro", 0x5},
+            {"spartan3", 0x6},
+            {"virtex4", 0x7},
+            {"virtex5", 0x8},
+            {"spartan3e", 0x9},
+            {"spartan3a", 0xa},
+            {"spartan3an", 0xb},
+            {"spartan3adsp", 0xc},
+            {"spartan6", 0xd},
+            {"virtex6", 0xe},
+            {"spartan2", 0xf0},
+            {NULL, 0},
+        };
+        unsigned int i = 0;
+
+        str = qemu_devtree_getprop_string(fdt, node_path, "xlnx,family", 0,
+            NULL, 0);
+        while (arch_lookup[i].name && str) {
+            if (strcmp(arch_lookup[i].name, str) == 0) {
+                break;
+            }
+            i++;
+        }
+        if (!str || !arch_lookup[i].arch) {
+            env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family.  */
+        } else
+            env->pvr.regs[10] = arch_lookup[i].arch << 24;
+    }
+
+    {
+        const char *str;
+        const struct {
+            const char *name;
+            unsigned int arch;
+        } cpu_lookup[] = {
+            /* These key value are as per MBV field in PVR0 */
+            {"5.00.a", 0x01},
+            {"5.00.b", 0x02},
+            {"5.00.c", 0x03},
+            {"6.00.a", 0x04},
+            {"6.00.b", 0x06},
+            {"7.00.a", 0x05},
+            {"7.00.b", 0x07},
+            {"7.10.a", 0x08},
+            {"7.10.b", 0x09},
+            {"7.10.c", 0x0a},
+            {"7.10.d", 0x0b},
+            {"7.20.a", 0x0c},
+            {"7.20.b", 0x0d},
+            {"7.20.c", 0x0e},
+            {"7.20.d", 0x0f},
+            {"7.30.a", 0x10},
+            {"7.30.b", 0x11},
+            {"8.00.a", 0x12},
+            {"8.00.b", 0x13},
+            {"8.10.a", 0x14},
+            /* FIXME There is no keycode defined in MBV for these versions */
+            {"2.10.a", 0x10},
+            {"3.00.a", 0x20},
+            {"4.00.a", 0x30},
+            {"4.00.b", 0x40},
+            {NULL, 0},
+        };
+        unsigned int i = 0;
+
+        str = qemu_devtree_getprop_string(fdt, node_path, "model", 0, NULL, 0);
+        if (str)
+            str += strlen("microblaze,");
+
+        while (cpu_lookup[i].name && str) {
+            if (strcmp(cpu_lookup[i].name, str) == 0) {
+                break;
+            }
+            i++;
+        }
+        if (!str || !cpu_lookup[i].arch) {
+            fprintf(stderr, "unable to find MicroBlaze model.\n");
+            env->pvr.regs[0] |= 0xb << 8;
+        } else
+            env->pvr.regs[0] |= cpu_lookup[i].arch << 8;
+    }
+
+    {
+        env->pvr.regs[4] = PVR4_USE_ICACHE_MASK
+                           | (21 << 26) /* Tag size.  */
+                           | (4 << 21)
+                           | (11 << 16);
+        env->pvr.regs[6] = VAL("d-cache-baseaddr");
+        env->pvr.regs[7] = VAL("d-cache-highaddr");
+        env->pvr.regs[5] = PVR5_USE_DCACHE_MASK
+                           | (21 << 26) /* Tag size.  */
+                           | (4 << 21)
+                           | (11 << 16);
+
+        if(VAL("xlnx,dcache-use-writeback"))
+            env->pvr.regs[5] |= PVR5_DCACHE_WRITEBACK_MASK;
+
+        env->pvr.regs[8] = VAL("i-cache-baseaddr");
+        env->pvr.regs[9] = VAL("i-cache-highaddr");
+    }
+}
+
+#define LMB_BRAM_SIZE  (128 * 1024)
+
+static struct
+{
+    uint32_t bootstrap_pc;
+    uint32_t cmdline;
+    uint32_t fdt;
+    void *vfdt;
+} boot_info;
+
+static void main_cpu_reset(void *opaque)
+{
+    CPUState *env = opaque;
+
+    cpu_reset(env);
+    microblaze_pvr_fdt_init(env, boot_info.vfdt);
+
+    env->regs[5] = boot_info.cmdline;
+    env->regs[7] = boot_info.fdt;
+    env->sregs[SR_PC] = boot_info.bootstrap_pc;
+}
+
+#ifdef TARGET_WORDS_BIGENDIAN
+int endian = 1;
+#else
+int endian = 0;
+#endif
+
+static void
+microblaze_generic_fdt_init(ram_addr_t ram_size,
+                          const char *boot_device,
+                          const char *kernel_filename,
+                          const char *kernel_cmdline,
+                          const char *initrd_filename, const char *cpu_model)
+{
+    CPUState *env;
+    int kernel_size;
+    target_phys_addr_t ram_base;
+    ram_addr_t phys_lmb_bram;
+    ram_addr_t phys_ram;
+    void *fdt = NULL, *kfdt = NULL;
+    int fdt_size, kfdt_size;
+    int r;
+
+    /* for memory node */
+    char node_path[DT_PATH_LENGTH];
+
+    const char *dtb = "mb.dtb";
+
+    /* If a non-default DTB is specified, try to load it first */
+    if (qemu_hwdtb)
+        dtb = qemu_hwdtb;
+
+    fdt = boot_info.vfdt = load_device_tree(dtb, &fdt_size);
+    if (!fdt) {
+        fprintf(stderr, "Error: Unable to load Device Tree %s\n", dtb);
+        return;
+    }
+
+    /* find memory node */
+    /* FIXME it could be good to fix case when you don't find memory node */
+    qemu_devtree_get_node_by_name(fdt, node_path, "memory@");
+    ram_base = qemu_devtree_getprop(fdt, NULL, node_path, "reg", 0, 0);
+    ram_size = qemu_devtree_getprop(fdt, NULL, node_path, "reg", 1, 0);
+
+    /* init CPUs */
+    if (cpu_model == NULL) {
+        cpu_model = "microblaze";
+    }
+    env = cpu_init(cpu_model);
+    microblaze_pvr_fdt_init(env, fdt);
+
+    qemu_register_reset(main_cpu_reset, env);
+
+    /* Attach emulated BRAM through the LMB.  */
+    phys_lmb_bram = qemu_ram_alloc(NULL, "mb.lmb", LMB_BRAM_SIZE);
+    cpu_register_physical_memory(0x00000000, LMB_BRAM_SIZE,
+                                 phys_lmb_bram | IO_MEM_RAM);
+
+    phys_ram = qemu_ram_alloc(NULL, "mb.ram", ram_size);
+    cpu_register_physical_memory(ram_base, ram_size, phys_ram | IO_MEM_RAM);
+
+    /* Instantiate peripherals from the FDT.  */
+    fdt_init_destroy_fdti(
+        fdt_generic_create_machine(fdt, microblaze_pic_init_cpu(env)) );
+
+    if (kernel_filename) {
+        uint64_t entry, low, high;
+        int kcmdline_len;
+
+        kernel_size = load_elf(kernel_filename, NULL, NULL,
+                               &entry, &low, &high, endian, ELF_MACHINE, 0);
+
+        /* If we failed loading ELF's try a raw image.  */
+        if (kernel_size < 0) {
+            kernel_size = load_image_targphys(kernel_filename, ram_base,
+                                              ram_size);
+            boot_info.bootstrap_pc = ram_base;
+            high = ram_base + kernel_size;
+        }
+        boot_info.bootstrap_pc = (uint32_t)entry;
+
+        kernel_cmdline = fdt_generic_chosen_kcmdline(fdt, kernel_cmdline);
+
+        boot_info.cmdline = high + 8192;
+        if (kernel_cmdline && (kcmdline_len = strlen(kernel_cmdline))) {
+            pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
+        }
+        /* Provide a device-tree.  */
+        boot_info.fdt = boot_info.cmdline + 8192;
+
+        if (kernel_cmdline && (kcmdline_len = strlen(kernel_cmdline))) {
+            r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
+                                            kernel_cmdline);
+           if (r < 0) {
+               fprintf(stderr, "couldn't set /chosen/bootargs\n");
+           }
+        }
+
+        /* Check for a specific device tree intended for the kernel */
+        if (qemu_kerndtb) {
+            if (!strncmp(qemu_kerndtb, "none", 5)) {
+                fprintf(stderr, "No kernel FDT - use compiled-in FDT\n");
+                boot_info.fdt = 0x0;
+            } else {
+                kfdt = load_device_tree(qemu_kerndtb, &kfdt_size);
+                if (!kfdt) {
+                    fprintf(stderr, "Unable to load kernel DTB: %s, "
+                           "reverting to HW DTB\n", qemu_kerndtb);
+                } else {
+                    dtb = qemu_kerndtb;
+                    fdt = kfdt;
+                    fdt_size = kfdt_size;
+                }
+            }
+        }
+        if (boot_info.fdt) {
+            cpu_physical_memory_write (boot_info.fdt, (void *)fdt, fdt_size);
+        }
+    }
+
+}
+
+static QEMUMachine microblaze_generic_fdt = {
+    .name = "microblaze-fdt",
+    .desc = "Petalogix linux refdesign for all MMU boards",
+    .init = microblaze_generic_fdt_init,
+};
+
+static void microblaze_fdt_init(void)
+{
+    qemu_register_machine(&microblaze_generic_fdt);
+}
+
+machine_init(microblaze_fdt_init);
+
+fdt_register_compatibility(simple_bus_fdt_init, "xlnx,compound");
+fdt_register_compatibility_opaque(pflash_cfi01_fdt_init, "cfi-flash",0 ,
+    &endian);