]> git.proxmox.com Git - qemu.git/blobdiff - hw/ppce500_mpc8544ds.c
PPC: e500: dt: create serial nodes dynamically
[qemu.git] / hw / ppce500_mpc8544ds.c
index d69f78cf33c56ebedd63f60b298e2fd98dcf0b14..c68e9949b8bd3919c4ef383480f48d9b531d1976 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Qemu PowerPC MPC8544DS board emualtion
+ * QEMU PowerPC MPC8544DS board emulation
  *
  * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved.
  *
@@ -31,6 +31,7 @@
 #include "elf.h"
 #include "sysbus.h"
 #include "exec-memory.h"
+#include "host-utils.h"
 
 #define BINARY_DEVICE_TREE_FILE    "mpc8544ds.dtb"
 #define UIMAGE_LOAD_BASE           0
@@ -42,6 +43,8 @@
 #define RAM_SIZES_ALIGN            (64UL << 20)
 
 #define MPC8544_CCSRBAR_BASE       0xE0000000
+#define MPC8544_CCSRBAR_REGSIZE    0x00001000
+#define MPC8544_CCSRBAR_SIZE       0x00100000
 #define MPC8544_MPIC_REGS_BASE     (MPC8544_CCSRBAR_BASE + 0x40000)
 #define MPC8544_SERIAL0_REGS_BASE  (MPC8544_CCSRBAR_BASE + 0x4500)
 #define MPC8544_SERIAL1_REGS_BASE  (MPC8544_CCSRBAR_BASE + 0x4600)
 struct boot_info
 {
     uint32_t dt_base;
+    uint32_t dt_size;
     uint32_t entry;
 };
 
-static int mpc8544_load_device_tree(CPUState *env,
+static int mpc8544_load_device_tree(CPUPPCState *env,
                                     target_phys_addr_t addr,
                                     uint32_t ramsize,
                                     target_phys_addr_t initrd_base,
@@ -66,7 +70,6 @@ static int mpc8544_load_device_tree(CPUState *env,
                                     const char *kernel_cmdline)
 {
     int ret = -1;
-#ifdef CONFIG_FDT
     uint32_t mem_reg_property[] = {0, cpu_to_be32(ramsize)};
     char *filename;
     int fdt_size;
@@ -75,6 +78,11 @@ static int mpc8544_load_device_tree(CPUState *env,
     uint32_t clock_freq = 400000000;
     uint32_t tb_freq = 400000000;
     int i;
+    char compatible[] = "MPC8544DS\0MPC85xxDS";
+    char model[] = "MPC8544DS";
+    char soc[128];
+    char ser0[128];
+    char ser1[128];
 
     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
     if (!filename) {
@@ -87,11 +95,18 @@ static int mpc8544_load_device_tree(CPUState *env,
     }
 
     /* Manipulate device tree in memory. */
-    ret = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
-                               sizeof(mem_reg_property));
-    if (ret < 0)
-        fprintf(stderr, "couldn't set /memory/reg\n");
-
+    qemu_devtree_setprop_string(fdt, "/", "model", model);
+    qemu_devtree_setprop(fdt, "/", "compatible", compatible,
+                         sizeof(compatible));
+    qemu_devtree_setprop_cell(fdt, "/", "#address-cells", 1);
+    qemu_devtree_setprop_cell(fdt, "/", "#size-cells", 1);
+
+    qemu_devtree_add_subnode(fdt, "/memory");
+    qemu_devtree_setprop_string(fdt, "/memory", "device_type", "memory");
+    qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
+                         sizeof(mem_reg_property));
+
+    qemu_devtree_add_subnode(fdt, "/chosen");
     if (initrd_size) {
         ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
                                         initrd_base);
@@ -117,6 +132,7 @@ static int mpc8544_load_device_tree(CPUState *env,
         tb_freq = kvmppc_get_tbfreq();
 
         /* indicate KVM hypercall interface */
+        qemu_devtree_add_subnode(fdt, "/hypervisor");
         qemu_devtree_setprop_string(fdt, "/hypervisor", "compatible",
                                     "linux,kvm");
         kvmppc_get_hypercall(env, hypercall, sizeof(hypercall));
@@ -124,6 +140,11 @@ static int mpc8544_load_device_tree(CPUState *env,
                              hypercall, sizeof(hypercall));
     }
 
+    /* Create CPU nodes */
+    qemu_devtree_add_subnode(fdt, "/cpus");
+    qemu_devtree_setprop_cell(fdt, "/cpus", "#address-cells", 1);
+    qemu_devtree_setprop_cell(fdt, "/cpus", "#size-cells", 0);
+
     /* We need to generate the cpu nodes in reverse order, so Linux can pick
        the first node as boot node and be happy */
     for (i = smp_cpus - 1; i >= 0; i--) {
@@ -163,32 +184,86 @@ static int mpc8544_load_device_tree(CPUState *env,
         }
     }
 
+    qemu_devtree_add_subnode(fdt, "/aliases");
+    /* XXX These should go into their respective devices' code */
+    snprintf(soc, sizeof(soc), "/soc8544@%x", MPC8544_CCSRBAR_BASE);
+    qemu_devtree_add_subnode(fdt, soc);
+    qemu_devtree_setprop_string(fdt, soc, "device_type", "soc");
+    qemu_devtree_setprop_string(fdt, soc, "compatible", "simple-bus");
+    qemu_devtree_setprop_cell(fdt, soc, "#address-cells", 1);
+    qemu_devtree_setprop_cell(fdt, soc, "#size-cells", 1);
+    qemu_devtree_setprop_cells(fdt, soc, "ranges", 0x0, MPC8544_CCSRBAR_BASE,
+                               MPC8544_CCSRBAR_SIZE);
+    qemu_devtree_setprop_cells(fdt, soc, "reg", MPC8544_CCSRBAR_BASE,
+                               MPC8544_CCSRBAR_REGSIZE);
+    /* XXX should contain a reasonable value */
+    qemu_devtree_setprop_cell(fdt, soc, "bus-frequency", 0);
+
+    /*
+     * We have to generate ser1 first, because Linux takes the first
+     * device it finds in the dt as serial output device. And we generate
+     * devices in reverse order to the dt.
+     */
+    snprintf(ser1, sizeof(ser1), "%s/serial@%x", soc,
+             MPC8544_SERIAL1_REGS_BASE - MPC8544_CCSRBAR_BASE);
+    qemu_devtree_add_subnode(fdt, ser1);
+    qemu_devtree_setprop_string(fdt, ser1, "device_type", "serial");
+    qemu_devtree_setprop_string(fdt, ser1, "compatible", "ns16550");
+    qemu_devtree_setprop_cells(fdt, ser1, "reg", MPC8544_SERIAL1_REGS_BASE -
+                               MPC8544_CCSRBAR_BASE, 0x100);
+    qemu_devtree_setprop_cell(fdt, ser1, "cell-index", 1);
+    qemu_devtree_setprop_cell(fdt, ser1, "clock-frequency", 0);
+    qemu_devtree_setprop_cells(fdt, ser1, "interrupts", 42, 2);
+    qemu_devtree_setprop_phandle(fdt, ser1, "interrupt-parent", mpic);
+    qemu_devtree_setprop_string(fdt, "/aliases", "serial1", ser1);
+
+    snprintf(ser0, sizeof(ser0), "%s/serial@%x", soc,
+             MPC8544_SERIAL0_REGS_BASE - MPC8544_CCSRBAR_BASE);
+    qemu_devtree_add_subnode(fdt, ser0);
+    qemu_devtree_setprop_string(fdt, ser0, "device_type", "serial");
+    qemu_devtree_setprop_string(fdt, ser0, "compatible", "ns16550");
+    qemu_devtree_setprop_cells(fdt, ser0, "reg", MPC8544_SERIAL0_REGS_BASE -
+                               MPC8544_CCSRBAR_BASE, 0x100);
+    qemu_devtree_setprop_cell(fdt, ser0, "cell-index", 0);
+    qemu_devtree_setprop_cell(fdt, ser0, "clock-frequency", 0);
+    qemu_devtree_setprop_cells(fdt, ser0, "interrupts", 42, 2);
+    qemu_devtree_setprop_phandle(fdt, ser0, "interrupt-parent", mpic);
+    qemu_devtree_setprop_string(fdt, "/aliases", "serial0", ser0);
+    qemu_devtree_setprop_string(fdt, "/chosen", "linux,stdout-path", ser0);
+
     ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
+    if (ret < 0) {
+        goto out;
+    }
     g_free(fdt);
+    ret = fdt_size;
 
 out:
-#endif
 
     return ret;
 }
 
-/* Create -kernel TLB entries for BookE, linearly spanning 256MB.  */
+/* Create -kernel TLB entries for BookE.  */
 static inline target_phys_addr_t booke206_page_size_to_tlb(uint64_t size)
 {
-    return ffs(size >> 10) - 1;
+    return 63 - clz64(size >> 10);
 }
 
-static void mmubooke_create_initial_mapping(CPUState *env,
-                                     target_ulong va,
-                                     target_phys_addr_t pa)
+static void mmubooke_create_initial_mapping(CPUPPCState *env)
 {
+    struct boot_info *bi = env->load_info;
     ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0);
-    target_phys_addr_t size;
-
-    size = (booke206_page_size_to_tlb(256 * 1024 * 1024) << MAS1_TSIZE_SHIFT);
+    target_phys_addr_t size, dt_end;
+    int ps;
+
+    /* Our initial TLB entry needs to cover everything from 0 to
+       the device tree top */
+    dt_end = bi->dt_base + bi->dt_size;
+    ps = booke206_page_size_to_tlb(dt_end) + 1;
+    size = (ps << MAS1_TSIZE_SHIFT);
     tlb->mas1 = MAS1_VALID | size;
-    tlb->mas2 = va & TARGET_PAGE_MASK;
-    tlb->mas7_3 = pa & TARGET_PAGE_MASK;
+    tlb->mas2 = 0;
+    tlb->mas7_3 = 0;
     tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX;
 
     env->tlb_dirty = true;
@@ -196,9 +271,10 @@ static void mmubooke_create_initial_mapping(CPUState *env,
 
 static void mpc8544ds_cpu_reset_sec(void *opaque)
 {
-    CPUState *env = opaque;
+    PowerPCCPU *cpu = opaque;
+    CPUPPCState *env = &cpu->env;
 
-    cpu_reset(env);
+    cpu_reset(CPU(cpu));
 
     /* Secondary CPU starts in halted state for now. Needs to change when
        implementing non-kernel boot. */
@@ -208,17 +284,18 @@ static void mpc8544ds_cpu_reset_sec(void *opaque)
 
 static void mpc8544ds_cpu_reset(void *opaque)
 {
-    CPUState *env = opaque;
+    PowerPCCPU *cpu = opaque;
+    CPUPPCState *env = &cpu->env;
     struct boot_info *bi = env->load_info;
 
-    cpu_reset(env);
+    cpu_reset(CPU(cpu));
 
     /* Set initial guest state. */
     env->halted = 0;
     env->gpr[1] = (16<<20) - 8;
     env->gpr[3] = bi->dt_base;
     env->nip = bi->entry;
-    mmubooke_create_initial_mapping(env, 0, 0);
+    mmubooke_create_initial_mapping(env);
 }
 
 static void mpc8544ds_init(ram_addr_t ram_size,
@@ -231,7 +308,7 @@ static void mpc8544ds_init(ram_addr_t ram_size,
     MemoryRegion *address_space_mem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     PCIBus *pci_bus;
-    CPUState *env = NULL;
+    CPUPPCState *env = NULL;
     uint64_t elf_entry;
     uint64_t elf_lowaddr;
     target_phys_addr_t entry=0;
@@ -244,7 +321,7 @@ static void mpc8544ds_init(ram_addr_t ram_size,
     unsigned int pci_irq_nrs[4] = {1, 2, 3, 4};
     qemu_irq **irqs, *mpic;
     DeviceState *dev;
-    CPUState *firstenv = NULL;
+    CPUPPCState *firstenv = NULL;
 
     /* Setup CPUs */
     if (cpu_model == NULL) {
@@ -254,12 +331,15 @@ static void mpc8544ds_init(ram_addr_t ram_size,
     irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *));
     irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB);
     for (i = 0; i < smp_cpus; i++) {
+        PowerPCCPU *cpu;
         qemu_irq *input;
-        env = cpu_ppc_init(cpu_model);
-        if (!env) {
+
+        cpu = cpu_ppc_init(cpu_model);
+        if (cpu == NULL) {
             fprintf(stderr, "Unable to initialize CPU!\n");
             exit(1);
         }
+        env = &cpu->env;
 
         if (!firstenv) {
             firstenv = env;
@@ -278,11 +358,11 @@ static void mpc8544ds_init(ram_addr_t ram_size,
             /* Primary CPU */
             struct boot_info *boot_info;
             boot_info = g_malloc0(sizeof(struct boot_info));
-            qemu_register_reset(mpc8544ds_cpu_reset, env);
+            qemu_register_reset(mpc8544ds_cpu_reset, cpu);
             env->load_info = boot_info;
         } else {
             /* Secondary CPUs */
-            qemu_register_reset(mpc8544ds_cpu_reset_sec, env);
+            qemu_register_reset(mpc8544ds_cpu_reset_sec, cpu);
         }
     }
 
@@ -374,13 +454,12 @@ static void mpc8544ds_init(ram_addr_t ram_size,
     /* If we're loading a kernel directly, we must load the device tree too. */
     if (kernel_filename) {
         struct boot_info *boot_info;
+        int dt_size;
 
-#ifndef CONFIG_FDT
-        cpu_abort(env, "Compiled without FDT support - can't load kernel\n");
-#endif
-        dt_base = (kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK;
-        if (mpc8544_load_device_tree(env, dt_base, ram_size,
-                    initrd_base, initrd_size, kernel_cmdline) < 0) {
+        dt_base = (loadaddr + kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK;
+        dt_size = mpc8544_load_device_tree(env, dt_base, ram_size, initrd_base,
+                                           initrd_size, kernel_cmdline);
+        if (dt_size < 0) {
             fprintf(stderr, "couldn't load device tree\n");
             exit(1);
         }
@@ -388,6 +467,7 @@ static void mpc8544ds_init(ram_addr_t ram_size,
         boot_info = env->load_info;
         boot_info->entry = entry;
         boot_info->dt_base = dt_base;
+        boot_info->dt_size = dt_size;
     }
 
     if (kvm_enabled()) {