2013/2/8 Igor Mitsyanko <i.mitsya...@gmail.com> > > > On 02/06/2013 01:45 PM, Kuo-Jung Su wrote: > > From: Kuo-Jung Su <dant...@faraday-tech.com> > > The Faraday A369 EVB is a Faraday SoC platform evalution board used for > Faraday IP functional verification based on the well-known ARM AMBA 2.0 > architecture. > > Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com> > --- > hw/arm/Makefile.objs | 1 + > hw/arm/faraday_a369.c | 161 +++++++++++++++++++++++++++++ > hw/arm/faraday_a369_keypad.c | 234 > ++++++++++++++++++++++++++++++++++++++++++ > hw/arm/faraday_a369_scu.c | 188 +++++++++++++++++++++++++++++++++ > hw/arm/ftkbc010.h | 26 +++++ > 5 files changed, 610 insertions(+) > create mode 100644 hw/arm/faraday_a369.c > create mode 100644 hw/arm/faraday_a369_keypad.c > create mode 100644 hw/arm/faraday_a369_scu.c > create mode 100644 hw/arm/ftkbc010.h > > diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs > index 59d7023..02d1a7b 100644 > --- a/hw/arm/Makefile.objs > +++ b/hw/arm/Makefile.objs > @@ -34,3 +34,4 @@ obj-$(CONFIG_FDT) += ../device_tree.o > > obj-y := $(addprefix ../,$(obj-y)) > obj-y += faraday_a360.o faraday_a360_pmu.o > +obj-y += faraday_a369.o faraday_a369_scu.o faraday_a369_keypad.o > diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c > new file mode 100644 > index 0000000..e32dc7f > --- /dev/null > +++ b/hw/arm/faraday_a369.c > @@ -0,0 +1,161 @@ > +/* > + * Faraday A369 Evalution Board > + * > + * Copyright (c) 2012 Faraday Technology > + * Written by Dante Su <dant...@faraday-tech.com> > + * > + * This code is licensed under GNU GPL v2+. > + */ > + > +#include <hw/sysbus.h> > +#include <hw/arm-misc.h> > +#include <hw/devices.h> > +#include <hw/i2c.h> > +#include <hw/boards.h> > +#include <hw/flash.h> > +#include <hw/serial.h> > +#include <hw/ssi.h> > +#include <net/net.h> > +#include <sysemu/sysemu.h> > +#include <sysemu/blockdev.h> > +#include <exec/address-spaces.h> > + > +#include "faraday.h" > + > +typedef FaradayMachState A369State; > + > +/* Board init. */ > + > +static void > +a369_device_init(A369State *s) > +{ > + /* Serial (FTUART010 which is 16550A compatible) */ > + if (serial_hds[0]) { > + serial_mm_init(s->as, > + 0x92b00000, > + 2, > + NULL, > + 18432000 / 16, > + serial_hds[0], > + DEVICE_LITTLE_ENDIAN); > + } > + if (serial_hds[1]) { > + serial_mm_init(s->as, > + 0x92c00000, > + 2, > + NULL, > + 18432000 / 16, > + serial_hds[1], > + DEVICE_LITTLE_ENDIAN); > + } > + > + /* ftscu010 */ > + s->scu = sysbus_create_simple("a369.scu", 0x92000000, NULL); > + > + /* ftkbc010 */ > + sysbus_create_simple("a369.keypad", 0x92f00000, NULL); > +} > + > +static void > +a369_board_init(QEMUMachineInitArgs *args) > +{ > + DriveInfo *dinfo; > + struct arm_boot_info *bi = NULL; > + A369State *s = g_new(A369State, 1); > + > + s->as = get_system_memory(); > + s->ram = g_new(MemoryRegion, 1); > + s->sram = g_new(MemoryRegion, 1); > + > + /* CPU */ > + if (!args->cpu_model) { > + args->cpu_model = "fa626te"; > + } > + > + s->cpu = cpu_arm_init(args->cpu_model); > + if (!s->cpu) { > + args->cpu_model = "arm926"; > + s->cpu = cpu_arm_init(args->cpu_model); > + if (!s->cpu) { > + hw_error("a369: Unable to find CPU definition\n"); > + exit(1); > + } > + } > + > + s->ahb_slave4 = 0x00080000; /* ROM: base=0x00000000, size=256MB */ > + s->ahb_slave6 = 0x10090000; /* RAM: base=0x10000000, size=512MB */ > > > Does this register provide information on max allowable RAM size or amount of > RAM actually accessible in a system? I mean, > should this register be modified accordingly if args->ram_size value is less > then 512MB? > >
Yes, the values to the registers define both the base address and max. size to the corresponding salve devices. Although these registers are all R/W, they should be treated as read-only to the softwares. Modifying the setting to slave devices would alter the system memory mapped and might cause un-predictable issues. P.S: These registers are designed to be writable for Faraday internal test only. Sometimes we'll have A36x mounted with a new external AHB daughter board with built-in CPU and peripheral, and make A36X as an expansion bus. Only in such case, we'll update the AHB slave settings for FPGA test. > + > + /* A369 supports upto 512MB ram space */ > + if (args->ram_size > 0x20000000) { > + args->ram_size = 0x20000000; > + } > > + > + /* Use parallel NOR flash for ROM emulation */ > + dinfo = drive_get_next(IF_PFLASH); > + s->rom = pflash_cfi01_register( > + 0, /* base address */ > + NULL, > + "a369.rom", > + 6144, /* 6 KB */ > + dinfo ? dinfo->bdrv : NULL, > > > I think you should also consider a case when we're booting with bootstrap > code in ROM (no kernel_image specified). Right now, > if no rom image is specified, QEMU will abort with "trying to execute code > outside RAM" error. You could check for this case here > and abort QEMU with a more descriptive error. > got it, thanks > > + 1024, /* 1 KB sector */ > + 6, /* 6 sector per chip */ > + 4, /* 32 bits */ > + 0, 0, 0, 0, /* id */ > + 0 /* Little Endian */); > + if (!s->rom) { > + hw_error("a369: failed to init ROM device.\n"); > + exit(1); > + } > + > + /* Embedded RAM Init */ > + memory_region_init_ram(s->sram, "a369.sram", 0x4000); > + vmstate_register_ram_global(s->sram); > + memory_region_add_subregion(s->as, 0xA0000000, s->sram); > + > + /* RAM Init */ > + memory_region_init_ram(s->ram, "a369.ram", args->ram_size); > + vmstate_register_ram_global(s->ram); > + > + a369_device_init(s); > + > + if (args->kernel_filename) { > + bi = g_new0(struct arm_boot_info, 1); > + > + /* RAM Address Binding */ > + memory_region_add_subregion(s->as, 0x00000000, s->ram); > + > > > pflash_cfi01_register() already mapped ROM at address 0x0, this line causes > both RAM and ROM to be mapped > at 0x0. You should unmap ROM first. > Even though we have memory_region_add_subregion() and > memory_region_add_subregion_overlap(), looks like currently there is no > difference in behaviour > of memregions initialised with either of these two functions. > > Got it, thanks > > + /* Boot Info */ > + bi->ram_size = args->ram_size; > + bi->kernel_filename = args->kernel_filename; > + bi->kernel_cmdline = args->kernel_cmdline; > + bi->initrd_filename = args->initrd_filename; > + bi->board_id = 0xa369; > + arm_load_kernel(s->cpu, bi); > > > So, I assume this case models operation just after debootstrap loaded uboot > (or kernel) into memory and remapped ROM and RAM? > Shouldn't you set ahb_remapped and ddr_inited here then, or uboot/kernel will > do it anyway? > > Yes, both the ahb_remapped and ddr_inited should be set here. The u-boot/linux-kernel would never do the AHB remap, instead the bootstrap would do it for them. > + } else { > + /* ROM Address Binding */ > + sysbus_mmio_map(SYS_BUS_DEVICE(s->rom), 0, 0x00000000); > + /* Partial RAM (before ahb remapped) Address Binding */ > + s->ram_alias = g_new(MemoryRegion, 1); > + /* The address window is restricted to 256MB before remap */ > + memory_region_init_alias(s->ram_alias, "a369.ram_alias", > + s->ram, > + 0, > + MIN(0x10000000, args->ram_size)); > > > > + } > +} > + > +static QEMUMachine a369_machine = { > + .name = "a369", > + .desc = "Faraday A369 (fa626te)", > + .init = a369_board_init, > + DEFAULT_MACHINE_OPTIONS, > +}; > + > +static void > +a369_machine_init(void) > +{ > + qemu_register_machine(&a369_machine); > +} > + > +machine_init(a369_machine_init); > diff --git a/hw/arm/faraday_a369_keypad.c b/hw/arm/faraday_a369_keypad.c > new file mode 100644 > index 0000000..0983d7e > --- /dev/null > +++ b/hw/arm/faraday_a369_keypad.c > @@ -0,0 +1,234 @@ > +/* > + * Faraday FTKBC010 emulator for A369. > + * > + * Copyright (c) 2012 Faraday Technology > + * Written by Dante Su <dant...@faraday-tech.com> > + * > + * The FTKBC010 is configured as a keypad controller for A369. > + * It's a group of hard wired buttons on the board, each of them > + * is monitored by the FTKBC010, and coordinated as (x, y). > + * However in A369, there is a pinmux issue that the Y-axis usually > + * malfunctioned, so there are only 3 button emulated here. > + * > + * This code is licensed under GNU GPL v2+ > + */ > + > +#include <hw/hw.h> > +#include <hw/sysbus.h> > +#include <hw/devices.h> > +#include <ui/console.h> > +#include <qemu/timer.h> > +#include <sysemu/sysemu.h> > + > +#include "ftkbc010.h" > + > +/* Key codes */ > +#define KEYCODE_ESC 1 > +#define KEYCODE_BACKSPACE 14 > +#define KEYCODE_ENTER 28 > +#define KEYCODE_SPACE 57 > +#define KEYCODE_MENU 139 /* Menu (show menu) */ > + > +#define TYPE_FTKBC010 "a369.keypad" > + > +typedef struct Ftkbc010State { > + SysBusDevice busdev; > + MemoryRegion iomem; > + qemu_irq irq; > + > + int x; > + int y; > + > + /* HW registers */ > + uint32_t cr; > + uint32_t isr; > +} Ftkbc010State; > + > +#define FTKBC010(obj) \ > + OBJECT_CHECK(Ftkbc010State, obj, TYPE_FTKBC010) > + > +static void ftkbc010_update(Ftkbc010State *s) > +{ > + uint32_t ier = 0; > + > + /* keypad interrupt */ > + ier |= (s->cr & (1 << 8)) ? (1 << 2) : 0; > + /* tx interrupt */ > + ier |= (s->cr & (1 << 3)) ? (1 << 1) : 0; > + /* rx interrupt */ > + ier |= (s->cr & (1 << 4)) ? (1 << 0) : 0; > + > + if (ier & s->isr) { > + qemu_irq_raise(s->irq); > + } else { > + qemu_irq_lower(s->irq); > + } > +} > + > +static uint64_t ftkbc010_mem_read(void *opaque, hwaddr addr, unsigned size) > +{ > + Ftkbc010State *s = FTKBC010(opaque); > + > + switch (addr) { > + case REG_CR: > + return s->cr; > + case REG_ISR: > + return s->isr; > + case REG_KPDXR: > + return ~(1 << s->x); > + case REG_KPDYR: > + return ~(1 << s->y); > + case REG_REVR: > + return 0x00010403; /* rev 1.4.3 */ > + case REG_FEAR: > + return 0x00000808; /* 8x8 scan code for keypad */ > + default: > + break; > + } > + > + return 0; > +} > + > +static void ftkbc010_mem_write(void *opaque, > + hwaddr addr, > + uint64_t val, > + unsigned size) > +{ > + Ftkbc010State *s = FTKBC010(opaque); > + > + switch (addr) { > + case REG_CR: > + s->cr = (uint32_t)val; > + /* if ftkbc010 enabled */ > + if (!(s->cr & (1 << 2))) { > + break; > + } > + /* if keypad interrupt cleared */ > + if (s->cr & (1 << 10)) { > + s->cr &= ~(1 << 10); > + s->isr &= ~(1 << 2); > + } > + /* if rx interrupt cleared */ > + if (s->cr & (1 << 7)) { > + s->cr &= ~(1 << 7); > + s->isr &= ~(1 << 0); > + } > + /* if tx interrupt cleared */ > + if (s->cr & (1 << 6)) { > + s->cr &= ~(1 << 6); > + s->isr &= ~(1 << 1); > + } > + ftkbc010_update(s); > + break; > + default: > + break; > + } > +} > + > +static const MemoryRegionOps ftkbc010_mem_ops = { > + .read = ftkbc010_mem_read, > + .write = ftkbc010_mem_write, > + .endianness = DEVICE_LITTLE_ENDIAN, > +}; > + > +static void ftkbc010_key_event(void *opaque, int scancode) > +{ > + Ftkbc010State *s = FTKBC010(opaque); > + int released = 0; > + > + /* key release from qemu */ > + if (scancode & 0x80) { > + released = 1; > + } > + > + /* strip qemu key release bit */ > + scancode &= ~0x80; > + > + /* keypad interrupt */ > + if (!released && (s->cr & (1 << 8))) { > + switch (scancode) { > + case KEYCODE_ESC: > + case KEYCODE_BACKSPACE: > + s->x = 1; > + break; > + case KEYCODE_ENTER: > + case KEYCODE_MENU: > + case KEYCODE_SPACE: > + s->x = 3; > + break; > + default: > + s->x = 2; /* KEY_HOME */ > + break; > + } > + s->y = 0; > + s->isr |= (1 << 2); > + ftkbc010_update(s); > + } > +} > + > +static void ftkbc010_reset(DeviceState *ds) > +{ > + SysBusDevice *busdev = SYS_BUS_DEVICE(ds); > + Ftkbc010State *s = FTKBC010(FROM_SYSBUS(Ftkbc010State, busdev)); > + > + qemu_irq_lower(s->irq); > +} > + > +static int ftkbc010_init(SysBusDevice *dev) > +{ > + Ftkbc010State *s = FTKBC010(FROM_SYSBUS(Ftkbc010State, dev)); > + > + s->cr = 0; > + s->isr = 0; > + s->x = 0; > + s->y = 0; > + > + memory_region_init_io(&s->iomem, > + &ftkbc010_mem_ops, > + s, > + TYPE_FTKBC010, > + 0x1000); > + sysbus_init_mmio(dev, &s->iomem); > + sysbus_init_irq(dev, &s->irq); > + > + qemu_add_kbd_event_handler(ftkbc010_key_event, s); > + > + return 0; > +} > + > +static const VMStateDescription vmstate_ftkbc010 = { > + .name = TYPE_FTKBC010, > + .version_id = 1, > + .minimum_version_id = 1, > + .minimum_version_id_old = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT32(cr, Ftkbc010State), > + VMSTATE_UINT32(isr, Ftkbc010State), > + VMSTATE_END_OF_LIST(), > + } > +}; > + > +static void ftkbc010_class_init(ObjectClass *klass, void *data) > +{ > + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + k->init = ftkbc010_init; > + dc->desc = TYPE_FTKBC010; > + dc->vmsd = &vmstate_ftkbc010; > + dc->reset = ftkbc010_reset; > +} > + > +static const TypeInfo ftkbc010_info = { > + .name = TYPE_FTKBC010, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(Ftkbc010State), > + .class_init = ftkbc010_class_init, > +}; > + > +static void ftkbc010_register_types(void) > +{ > + type_register_static(&ftkbc010_info); > +} > + > +type_init(ftkbc010_register_types) > diff --git a/hw/arm/faraday_a369_scu.c b/hw/arm/faraday_a369_scu.c > new file mode 100644 > index 0000000..b1c34e8 > --- /dev/null > +++ b/hw/arm/faraday_a369_scu.c > @@ -0,0 +1,188 @@ > +/* > + * Faraday A369 SCU > + * > + * Copyright (c) 2012 Faraday Technology > + * Written by Dante Su <dant...@faraday-tech.com> > + * > + * The system control unit (SCU) is responsible for > + * power, clock and pinmux management. Since most of > + * the features are useless to QEMU, only partial clock > + * and pinmux management are implemented as a set of R/W values. > + * > + * This code is licensed under GNU GPL v2+ > + */ > + > +#include <hw/hw.h> > +#include <hw/sysbus.h> > +#include <hw/devices.h> > +#include <ui/console.h> > +#include <qemu/timer.h> > +#include <sysemu/sysemu.h> > + > +#include "faraday.h" > + > +#define REG_CHIPID 0x000 /* SoC chip id */ > +#define REG_REVISON 0x004 /* SCU revision */ > +#define REG_HWCFG 0x008 /* HW configuration strap */ > +#define REG_PLL1CR 0x020 /* PLL1 control register */ > +#define REG_GPINMUX 0x200 /* General PINMUX */ > +#define REG_EXTHWCFG 0x204 /* Extended HW configuration strap */ > +#define REG_CLKCFG0 0x228 /* Clock configuration 0 */ > +#define REG_CLKCFG1 0x22C /* Clock configuration 1 */ > +#define REG_MFPINMUX0 0x238 /* Multi-function pinmux 0 */ > +#define REG_MFPINMUX1 0x23C /* Multi-function pinmux 1 */ > + > +#define TYPE_A369SCU "a369.scu" > + > +typedef struct A369SCUState { > + SysBusDevice busdev; > + MemoryRegion iomem; > + > + /* HW registers */ > + uint32_t general_cfg; > + uint32_t sclk_cfg0; > + uint32_t sclk_cfg1; > + uint32_t mfpsr0; > + uint32_t mfpsr1; > +} A369SCUState; > + > +#define A369SCU(obj) \ > + OBJECT_CHECK(A369SCUState, obj, TYPE_A369SCU) > + > +static uint64_t > +a369scu_mem_read(void *opaque, hwaddr addr, unsigned size) > +{ > + A369SCUState *s = A369SCU(opaque); > + uint64_t ret = 0; > + > + switch (addr) { > + case REG_CHIPID: > + ret = 0x00003369; /* FIE3369 = A369 */ > + break; > + case REG_REVISON: > + ret = 0x00010000; > + break; > + case REG_HWCFG: > + ret = 0x00000c10; > + break; > + case REG_PLL1CR: > + ret = 0x20010003; > + break; > + case REG_GPINMUX: > + ret = s->general_cfg; > + break; > + case REG_EXTHWCFG: > + ret = 0x00001cc8; > + break; > + case REG_CLKCFG0: > + ret = s->sclk_cfg0; > + break; > + case REG_CLKCFG1: > + ret = s->sclk_cfg1; > + break; > + case REG_MFPINMUX0: > + ret = s->mfpsr0; > + break; > + case REG_MFPINMUX1: > + ret = s->mfpsr1; > + break; > + } > + > + return ret; > +} > + > +static void > +a369scu_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) > +{ > + A369SCUState *s = A369SCU(opaque); > + > + switch (addr) { > + case REG_GPINMUX: > + s->general_cfg = (uint32_t)val; > + break; > + case REG_CLKCFG0: > + s->sclk_cfg0 = (uint32_t)val; > + break; > + case REG_CLKCFG1: > + s->sclk_cfg1 = (uint32_t)val; > + break; > + case REG_MFPINMUX0: > + s->mfpsr0 = (uint32_t)val; > + break; > + case REG_MFPINMUX1: > + s->mfpsr1 = (uint32_t)val; > + break; > + } > +} > + > +static const MemoryRegionOps a369scu_mem_ops = { > + .read = a369scu_mem_read, > + .write = a369scu_mem_write, > + .endianness = DEVICE_LITTLE_ENDIAN, > +}; > + > +static void a369scu_reset(DeviceState *ds) > +{ > + SysBusDevice *busdev = SYS_BUS_DEVICE(ds); > + A369SCUState *s = A369SCU(FROM_SYSBUS(A369SCUState, busdev)); > + > + s->general_cfg = 0x00001078; > + s->sclk_cfg0 = 0x26877330; > + s->sclk_cfg1 = 0x000a0a0a; > + s->mfpsr0 = 0x00000241; > + s->mfpsr1 = 0x00000000; > +} > + > +static int a369scu_init(SysBusDevice *dev) > +{ > + A369SCUState *s = A369SCU(FROM_SYSBUS(A369SCUState, dev)); > + > + memory_region_init_io(&s->iomem, > + &a369scu_mem_ops, > + s, > + TYPE_A369SCU, > + 0x1000); > + sysbus_init_mmio(dev, &s->iomem); > + return 0; > +} > + > +static const VMStateDescription vmstate_a369scu = { > + .name = TYPE_A369SCU, > + .version_id = 1, > + .minimum_version_id = 1, > + .minimum_version_id_old = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT32(general_cfg, A369SCUState), > + VMSTATE_UINT32(sclk_cfg0, A369SCUState), > + VMSTATE_UINT32(sclk_cfg1, A369SCUState), > + VMSTATE_UINT32(mfpsr0, A369SCUState), > + VMSTATE_UINT32(mfpsr1, A369SCUState), > + VMSTATE_END_OF_LIST(), > + } > +}; > + > +static void a369scu_class_init(ObjectClass *klass, void *data) > +{ > + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + k->init = a369scu_init; > + dc->desc = TYPE_A369SCU; > + dc->vmsd = &vmstate_a369scu; > + dc->reset = a369scu_reset; > + dc->no_user = 1; > +} > + > +static const TypeInfo a369scu_info = { > + .name = TYPE_A369SCU, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(A369SCUState), > + .class_init = a369scu_class_init, > +}; > + > +static void a369scu_register_types(void) > +{ > + type_register_static(&a369scu_info); > +} > + > +type_init(a369scu_register_types) > diff --git a/hw/arm/ftkbc010.h b/hw/arm/ftkbc010.h > new file mode 100644 > index 0000000..5d25ffa > --- /dev/null > +++ b/hw/arm/ftkbc010.h > @@ -0,0 +1,26 @@ > +/* > + * Faraday FTKBC010 Keyboard/Keypad Controller > + * > + * Copyright (c) 2012 Faraday Technology > + * Written by Dante Su <dant...@faraday-tech.com> > + * > + * This code is licensed under GNU GPL v2+ > + */ > +#ifndef HW_ARM_FTKBC010_H > +#define HW_ARM_FTKBC010_H > + > +#define REG_CR 0x00 /* control register */ > +#define REG_SRDR 0x04 /* sample rate division register */ > +#define REG_RSCR 0x08 /* request to send counter register */ > +#define REG_SR 0x0C /* status register */ > +#define REG_ISR 0x10 /* interrupt status register */ > +#define REG_KBDRR 0x14 /* keyboard receive register */ > +#define REG_KBDTR 0x18 /* keyboard transmit register */ > +#define REG_IMR 0x1C /* interrupt mask register */ > +#define REG_KPDXR 0x30 /* keypad X-Axis register */ > +#define REG_KPDYR 0x34 /* keypad Y-Axis register */ > +#define REG_ASPR 0x38 /* auto-scan period register */ > +#define REG_REVR 0x50 /* revision register */ > +#define REG_FEAR 0x54 /* feature register */ > + > +#endif > > -- Best wishes, Kuo-Jung Su