[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v3 02/20] arm: add Faraday a369 SoC platform support
From: |
Kuo-Jung Su |
Subject: |
[Qemu-devel] [PATCH v3 02/20] arm: add Faraday a369 SoC platform support |
Date: |
Wed, 6 Feb 2013 17:45:06 +0800 |
From: Kuo-Jung Su <address@hidden>
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 <address@hidden>
---
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 <address@hidden>
+ *
+ * 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 <address@hidden>
+ *
+ * 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 <address@hidden>
+ *
+ * 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 <address@hidden>
+ *
+ * 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
- [Qemu-devel] [PATCH v3 00/20] Add Faraday A36x SoC platform support, Kuo-Jung Su, 2013/02/06
- [Qemu-devel] [PATCH v3 02/20] arm: add Faraday a369 SoC platform support,
Kuo-Jung Su <=
- [Qemu-devel] [PATCH v3 06/20] arm: add Faraday FTWDT010 watchdog timer support, Kuo-Jung Su, 2013/02/06
- [Qemu-devel] [PATCH v3 09/20] arm: add Faraday FTRTC011 RTC timer support, Kuo-Jung Su, 2013/02/06
- [Qemu-devel] [PATCH v3 12/20] arm: add Faraday FTI2C010 I2C controller support, Kuo-Jung Su, 2013/02/06
- [Qemu-devel] [PATCH v3 13/20] arm: add Faraday FTNANDC021 nand flash controller support, Kuo-Jung Su, 2013/02/06
- [Qemu-devel] [PATCH v3 01/20] arm: add Faraday a360 SoC platform support, Kuo-Jung Su, 2013/02/06
- Re: [Qemu-devel] [PATCH v3 01/20] arm: add Faraday a360 SoC platform support, Andreas Färber, 2013/02/17