[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 8/9] MicroBlaze: Add a config that is dynamicall
From: |
Blue Swirl |
Subject: |
Re: [Qemu-devel] [PATCH 8/9] MicroBlaze: Add a config that is dynamically set up by a device tree file. |
Date: |
Tue, 11 Sep 2012 19:27:49 +0000 |
On Mon, Sep 10, 2012 at 12:20 AM, <address@hidden> wrote:
> From: Chris Wulff <address@hidden>
>
> Signed-off-by: Chris Wulff <address@hidden>
> ---
> default-configs/microblaze-softmmu.mak | 1 +
> hw/labx_microblaze_devicetree.c | 597
> ++++++++++++++++++++++++++++++++
> hw/microblaze/Makefile.objs | 3 +-
> 3 files changed, 600 insertions(+), 1 deletion(-)
> create mode 100644 hw/labx_microblaze_devicetree.c
>
> diff --git a/default-configs/microblaze-softmmu.mak
> b/default-configs/microblaze-softmmu.mak
> index 64c9485..7e4cab8 100644
> --- a/default-configs/microblaze-softmmu.mak
> +++ b/default-configs/microblaze-softmmu.mak
> @@ -5,3 +5,4 @@ CONFIG_PFLASH_CFI01=y
> CONFIG_SERIAL=y
> CONFIG_XILINX=y
> CONFIG_XILINX_AXI=y
> +CONFIG_LABX=y
> diff --git a/hw/labx_microblaze_devicetree.c b/hw/labx_microblaze_devicetree.c
> new file mode 100644
> index 0000000..581ad14
> --- /dev/null
> +++ b/hw/labx_microblaze_devicetree.c
> @@ -0,0 +1,597 @@
> +/*
> + * Flexible model of microblaze designs that use a device-tree to determine
> + * the hardware configuration.
> + *
> + * Copyright (c) 2010 LabX Technologies, LLC
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "hw.h"
> +#include "net.h"
> +#include "flash.h"
> +#include "sysemu.h"
> +#include "devices.h"
> +#include "boards.h"
> +#include "device_tree.h"
> +#include "xilinx.h"
> +#include "labx_devices.h"
> +#include "loader.h"
> +#include "elf.h"
> +#include "blockdev.h"
> +#include "exec-memory.h"
> +#include "microblaze_pic_cpu.h"
> +
> +#define LMB_BRAM_SIZE (128 * 1024)
> +
> +static struct
> +{
> + uint32_t bootstrap_pc;
> + uint32_t cmdline;
> + uint32_t initrd;
> + uint32_t fdt;
> +} boot_info;
Please read CODING_STYLE, struct names must use CamelCase.
> +
> +/* Current ethernet device index for multiple network interfaces */
> +static int eth_dev_index = -1;
This looks fishy.
> +
> +static void main_cpu_reset(void *opaque)
> +{
> + MicroBlazeCPU *cpu = opaque;
> + CPUMBState *env = &cpu->env;
> +
> + cpu_reset(CPU(cpu));
> +
> + env->regs[5] = boot_info.cmdline;
> + env->regs[6] = boot_info.initrd;
> + env->regs[7] = boot_info.fdt;
> + env->sregs[SR_PC] = boot_info.bootstrap_pc;
> +}
> +
> +#ifndef CONFIG_FDT
> +#error "Device-tree support is required for this target to function"
> +#endif
> +
> +static uint32_t fdt_get_int_from_array(void *fdt, int node,
> + const char *name, int index)
> +{
> + int array_length;
> + const void *array = qemu_devtree_getprop_offset(fdt, node, name,
> + &array_length);
> + if (index >= array_length) {
> + printf("fdt_get_int_from_array: requesting %s from node %d, "
> + "index %d out of range (%d max)\n",
> + name, node, index, array_length);
> + return 0;
> + } else {
> + return qemu_devtree_int_array_index(array, index);
> + }
> +}
> +
> +#define BINARY_DEVICE_TREE_FILE "labx-microblaze.dtb"
> +static void *get_device_tree(int *fdt_size)
> +{
> + char *path;
> + void *fdt;
> +
> + /* Try the local "mb.dtb" override. */
> + fdt = load_device_tree("mb.dtb", fdt_size);
> + if (!fdt) {
> + path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
> + if (path) {
> + fdt = load_device_tree(path, fdt_size);
> + g_free(path);
> + }
> + }
> +
> + return fdt;
> +}
> +
> +static int labx_microblaze_load_device_tree(target_phys_addr_t addr,
> + uint32_t ramsize,
> + target_phys_addr_t initrd_base,
> + target_phys_addr_t initrd_size,
> + const char *kernel_cmdline)
> +{
> + int fdt_size;
> + void *fdt;
> + int r;
> +
> + fdt = get_device_tree(&fdt_size);
> +
> + if (!fdt) {
> + return 0;
> + }
> +
> + if (kernel_cmdline && strlen(kernel_cmdline)) {
> + r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
> + kernel_cmdline);
> + if (r < 0) {
> + fprintf(stderr, "couldn't set /chosen/bootargs\n");
> + }
> + }
> + cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
> +
> + return fdt_size;
> +}
> +
> +static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
> +{
> + return addr - 0x30000000LL;
ULL
> +}
> +
> +static ram_addr_t get_dram_base(void *fdt)
> +{
> + int root = qemu_devtree_node_offset(fdt, "/");
> + int memory = qemu_devtree_subnode_offset_namelen(fdt, root, "memory", 6);
> + if (memory > 0) {
> + int reglen;
> + const void *reg =
> + qemu_devtree_getprop_offset(fdt, memory, "reg", ®len);
> +
> + if (reglen >= 4) {
> + printf("DRAM base %08X, size %08X\n",
> + qemu_devtree_int_array_index(reg, 0),
> + qemu_devtree_int_array_index(reg, 1));
> + return qemu_devtree_int_array_index(reg, 0);
> + }
> + }
> +
> + printf("DRAM base not found. Defaulting to 0x88000000\n");
> +
> + return 0x88000000; /* Default to something reasonable */
> +}
> +
> +typedef void (*device_init_func_t)(void *fdt, int node);
> +
> +typedef struct {
> + device_init_func_t probe;
> + int pass;
> + const char **compat;
> +
> +} devinfo_t;
Although a common error, _t suffix is reserved.
> +
> +/*
> + * Xilinx interrupt controller device
> + */
> +MicroBlazeCPU *g_cpu;
> +static qemu_irq irq[32] = {};
> +static qemu_irq *cpu_irq;
These should form the device state structure, passed around instead of
static state.
> +
> +static void xilinx_interrupt_controller_probe(void *fdt, int node)
> +{
> + int i;
> + DeviceState *dev;
> + int reglen;
> + const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", ®len);
> + uint32_t irq_addr = qemu_devtree_int_array_index(reg, 0);
> + int nrIrqLen;
CamelCase is not used for other identifiers.
> + const void *nrIrq =
> + qemu_devtree_getprop_offset(fdt, node, "xlnx,num-intr-inputs",
> + &nrIrqLen);
> + uint32_t nrIrqs = qemu_devtree_int_array_index(nrIrq, 0);
> +
> + printf(" IRQ BASE %08X NIRQS %d\n", irq_addr, nrIrqs);
Leftover debugging?
> +
> + cpu_irq = microblaze_pic_init_cpu(&g_cpu->env);
> + dev = xilinx_intc_create(irq_addr, cpu_irq[0], 2);
> + for (i = 0; i < nrIrqs; i++) {
> + irq[i] = qdev_get_gpio_in(dev, i);
> + }
> +}
> +
> +devinfo_t xilinx_interrupt_controller_device = {
static const?
> + .probe = &xilinx_interrupt_controller_probe,
> + .pass = 0,
> + .compat = (const char * []) { "xlnx,xps-intc-1.00.a", NULL }
> +};
> +
> +/*
> + * Flash device
> + */
> +static void flash_probe(void *fdt, int node)
> +{
> + int reglen;
> + const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", ®len);
> + uint32_t flash_addr = qemu_devtree_int_array_index(reg, 0);
> + uint32_t flash_size = qemu_devtree_int_array_index(reg, 1);
> +
> + DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
> + pflash_cfi01_register(flash_addr, NULL,
> + qemu_devtree_get_name(fdt, node, NULL), flash_size,
> + dinfo ? dinfo->bdrv : NULL, (64 * 1024),
> + flash_size >> 16,
> + 1, 0x89, 0x18, 0x0000, 0x0, 1);
> + printf("-- loaded %d bytes to %08X\n",
Leftover debugging?
> + load_image_targphys(qemu_devtree_get_name(fdt, node, NULL),
> + flash_addr, flash_size),
> + flash_addr);
> +}
> +
> +devinfo_t flash_device = {
static const?
> + .probe = &flash_probe,
> + .pass = 1,
> + .compat = (const char * []) { "cfi-flash", NULL }
> +};
> +
> +/*
> + * Xilinx timer device
> + */
> +static void xilinx_timer_probe(void *fdt, int node)
> +{
> + uint32_t timer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t timer_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> + printf(" TIMER BASE %08X IRQ %d\n", timer_addr, timer_irq);
Please fix all of these.
> +
> + /* 2 timers at irq 2 @ 62 Mhz. */
> + xilinx_timer_create(timer_addr, irq[timer_irq], 0, 62 * 1000000);
> +}
> +
> +devinfo_t xilinx_timer_device = {
Likewise.
> + .probe = &xilinx_timer_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,xps-timer-1.00.a", NULL }
> +};
> +
> +/*
> + * Xilinx uartlite device
> + */
> +static void xilinx_uartlite_probe(void *fdt, int node)
> +{
> + uint32_t uart_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t uart_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> + printf(" UART BASE %08X IRQ %d\n", uart_addr, uart_irq);
> +
> + sysbus_create_simple("xlnx.xps-uartlite", uart_addr, irq[uart_irq]);
> +}
> +
> +devinfo_t xilinx_uartlite_device = {
> + .probe = &xilinx_uartlite_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,xps-uartlite-1.00.a", NULL }
> +};
> +
> +/*
> + * Xilinx ethernetlite device
> + */
> +static void xilinx_ethlite_probe(void *fdt, int node)
> +{
> + uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> + xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
> + irq[eth_irq], 0, 0);
The device should not access nd_table, this should be handled at board level.
> +}
> +
> +devinfo_t xilinx_ethlite_device = {
> + .probe = &xilinx_ethlite_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
> +};
> +
> +/*
> + * LabX audio packetizer device
> + */
> +static void labx_audio_packetizer_probe(void *fdt, int node)
> +{
> + uint32_t packetizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t packetizer_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t clock_domains =
> + fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> + uint32_t cache_words =
> + fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> +
> + labx_audio_packetizer_create(packetizer_addr, irq[packetizer_irq],
> + clock_domains, cache_words);
> +}
> +
> +devinfo_t labx_audio_packetizer_device = {
> + .probe = &labx_audio_packetizer_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,labx-audio-packetizer-1.00.a", NULL }
> +};
> +
> +/*
> + * LabX audio depacketizer device
> + */
> +static void labx_audio_depacketizer_probe(void *fdt, int node)
> +{
> + uint32_t depacketizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t depacketizer_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t clock_domains =
> + fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> + uint32_t cache_words =
> + fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> +
> + int ifLen;
> + const void *ifType =
> + qemu_devtree_getprop_offset(fdt, node, "xlnx,interface-type",
> &ifLen);
> + int hasDMA = (0 != strncmp("CACHE_RAM", (const char *)ifType, ifLen));
> +
> + labx_audio_depacketizer_create(depacketizer_addr, irq[depacketizer_irq],
> + clock_domains, cache_words, hasDMA);
> +}
> +
> +devinfo_t labx_audio_depacketizer_device = {
> + .probe = &labx_audio_depacketizer_probe,
> + .pass = 1,
> + .compat = (const char * []) {
> + "xlnx,labx-audio-depacketizer-1.00.a",
> + "xlnx,labx-audio-depacketizer-1.01.a",
> + NULL
> + }
> +};
> +
> +/*
> + * LabX dma device
> + */
> +static void labx_dma_probe(void *fdt, int node)
> +{
> + uint32_t dma_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +
> + labx_dma_create(dma_addr, 1024);
> +}
> +
> +devinfo_t labx_dma_device = {
> + .probe = &labx_dma_probe,
> + .pass = 1,
> + .compat = (const char * []) {
> + "xlnx,labx-dma-1.00.a",
> + "xlnx,labx-dma-1.01.a",
> + "xlnx,labx-local-audio-1.00.a",
> + NULL
> + }
> +};
> +
> +/*
> + * LabX ethernet device
> + */
> +static void labx_ethernet_probe(void *fdt, int node)
> +{
> + uint32_t ethernet_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t host_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t fifo_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 2);
> + uint32_t phy_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 4);
> +
> + labx_ethernet_create(&nd_table[++eth_dev_index], ethernet_addr,
> + irq[host_irq], irq[fifo_irq], irq[phy_irq]);
> +}
> +
> +devinfo_t labx_ethernet_device = {
> + .probe = &labx_ethernet_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,labx-ethernet-1.00.a", NULL }
> +};
> +
> +/*
> + * LabX ptp device
> + */
> +static void labx_ptp_probe(void *fdt, int node)
> +{
> + uint32_t ptp_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +
> + labx_ptp_create(ptp_addr);
> +}
> +
> +devinfo_t labx_ptp_device = {
> + .probe = &labx_ptp_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,labx-ptp-1.00.a", NULL }
> +};
> +
> +/*
> + * Table of available devices
> + */
> +devinfo_t *devices[] = {
> + &xilinx_interrupt_controller_device,
> + &flash_device,
> + &xilinx_timer_device,
> + &xilinx_uartlite_device,
> + &xilinx_ethlite_device,
> + &labx_audio_packetizer_device,
> + &labx_audio_depacketizer_device,
> + &labx_dma_device,
> + &labx_ethernet_device,
> + &labx_ptp_device,
> + NULL
> +};
> +
> +static int plb_device_probe(void *fdt, int node, int pass)
> +{
> + devinfo_t **dev = &(devices[0]);
Unnecessary parentheses.
> +
> + while (*dev) {
> + const char **compat = &((*dev)->compat[0]);
> + while (*compat) {
> + if (0 == qemu_devtree_node_check_compatible(fdt, node, *compat))
> {
Usually the constant is on right hand side.
> + if (pass == (*dev)->pass) {
> + printf("Adding a device for node %s\n",
> + qemu_devtree_get_name(fdt, node, NULL));
> +
> + (*dev)->probe(fdt, node);
> + return 0;
> + }
> +
> + if (pass < (*dev)->pass) {
> + /* Probe again on the next pass */
> + return 1;
> + }
> + }
> +
> + compat++;
> + }
> +
> + dev++;
> + }
> +
> + return 0;
> +}
> +
> +static void plb_bus_probe(void *fdt)
> +{
> + int root = qemu_devtree_node_offset(fdt, "/");
> + int plb = qemu_devtree_subnode_offset_namelen(fdt, root, "plb", 3);
> + if (plb > 0) {
> + /* Do multiple passes through the devices. Some have dependencies
> + on others being first */
> + int pass = 0;
> + int again = 0;
> + do {
> + int child = plb;
> + again = 0;
> + do {
> + child = qemu_devtree_next_child_offset(fdt, plb, child);
> + if (child < 0) {
> + break;
> + }
> +
> + again |= plb_device_probe(fdt, child, pass);
> +
> + } while (1);
> +
> + pass++;
> +
> + } while (again);
> + }
> +}
> +
> +static void
> +labx_microblaze_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)
> +{
> + MemoryRegion *address_space_mem = get_system_memory();
> +
> + int kernel_size;
> + int fdt_size;
> + void *fdt = get_device_tree(&fdt_size);
> + target_phys_addr_t ddr_base = get_dram_base(fdt);
> + MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
> + MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
> + CPUMBState *env;
> +
> + /* init CPUs */
> + if (cpu_model == NULL) {
> + cpu_model = "microblaze";
> + }
> + g_cpu = cpu_mb_init(cpu_model);
> + env = &g_cpu->env;
> +
> + env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */
> + qemu_register_reset(main_cpu_reset, g_cpu);
> +
> + /* Attach emulated BRAM through the LMB. LMB size is not specified in the
> + device-tree but there must be one to hold the vector table. */
> + memory_region_init_ram(phys_lmb_bram, "labx_microblaze.lmb_bram",
> + LMB_BRAM_SIZE);
> + vmstate_register_ram_global(phys_lmb_bram);
> + memory_region_add_subregion(address_space_mem, 0x00000000,
> phys_lmb_bram);
> +
> + memory_region_init_ram(phys_ram, "labx_microblaze.ram", ram_size);
> + vmstate_register_ram_global(phys_ram);
> + memory_region_add_subregion(address_space_mem, ddr_base, phys_ram);
> +
> +
> + /* Create other devices listed in the device-tree */
> + plb_bus_probe(fdt);
> +
> + if (kernel_filename) {
> + uint64_t entry, low, high;
> + uint32_t base32;
> +
> + /* Boots a kernel elf binary. */
> + kernel_size = load_elf(kernel_filename, NULL, NULL,
> + &entry, &low, &high,
> + 1, ELF_MACHINE, 0);
> + base32 = entry;
> + if (base32 == 0xc0000000) {
> + kernel_size = load_elf(kernel_filename, translate_kernel_address,
> + NULL, &entry, NULL, NULL,
> + 1, ELF_MACHINE, 0);
> + }
> + /* Always boot into physical ram. */
> + boot_info.bootstrap_pc = ddr_base + (entry & 0x07ffffff);
> +
> + /* If it wasn't an ELF image, try an u-boot image. */
> + if (kernel_size < 0) {
> + target_phys_addr_t uentry, loadaddr;
> +
> + kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr,
> 0);
> + boot_info.bootstrap_pc = uentry;
> + high = (loadaddr + kernel_size + 3) & ~3;
The constants should use ULL so that they are not truncated to 32 bits.
> + }
> +
> + /* Not an ELF image nor an u-boot image, try a RAW image. */
> + if (kernel_size < 0) {
> + kernel_size = load_image_targphys(kernel_filename, ddr_base,
> + ram_size);
> + boot_info.bootstrap_pc = ddr_base;
> + high = (ddr_base + kernel_size + 3) & ~3;
Also here.
> + }
> +
> + if (initrd_filename) {
> + uint32_t initrd_base = 0x88c00000;
> + uint32_t initrd_size =
> + load_image_targphys(initrd_filename, initrd_base,
> + ram_size - initrd_base);
> + if (initrd_size <= 0) {
> + fprintf(stderr, "qemu: could not load initial ram disk
> '%s'\n",
> + initrd_filename);
> + exit(1);
> + }
> +
> + boot_info.initrd = initrd_base;
> + } else {
> + boot_info.initrd = 0x00000000;
> + }
> +
> + boot_info.cmdline = high + 4096;
> + if (kernel_cmdline && strlen(kernel_cmdline)) {
> + pstrcpy_targphys("cmdline", boot_info.cmdline, 256,
> kernel_cmdline);
> + }
> + /* Provide a device-tree. */
> + boot_info.fdt = boot_info.cmdline + 4096;
> + labx_microblaze_load_device_tree(boot_info.fdt, ram_size,
> + 0, 0,
> + kernel_cmdline);
> + }
> +}
> +
> +static QEMUMachine labx_microblaze_machine = {
> + .name = "labx-microblaze-devicetree",
> + .desc = "Microblaze design based on the peripherals specified "
> + "in the device-tree.",
> + .init = labx_microblaze_init,
> + .is_default = 1
> +};
> +
> +static void labx_microblaze_machine_init(void)
> +{
> + qemu_register_machine(&labx_microblaze_machine);
> +}
> +
> +machine_init(labx_microblaze_machine_init);
> +
Useless newline before EOF.
> diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
> index 274d2c5..632cd60 100644
> --- a/hw/microblaze/Makefile.objs
> +++ b/hw/microblaze/Makefile.objs
> @@ -1,4 +1,5 @@
> -obj-y = petalogix_s3adsp1800_mmu.o
> +obj-y = labx_microblaze_devicetree.o
> +obj-y += petalogix_s3adsp1800_mmu.o
> obj-y += petalogix_ml605_mmu.o
> obj-y += microblaze_boot.o
>
> --
> 1.7.9.5
>
>