@@ -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
new file mode 100644
@@ -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(µblaze_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);
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