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 */ + + /* 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, + 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); + + /* 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); + } 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 -- 1.7.9.5