Patchwork [v5,02/24] hw/arm: add Faraday a369 SoC platform support

login
register
mail settings
Submitter Kuo-Jung Su
Date Feb. 27, 2013, 7:15 a.m.
Message ID <1361949350-22241-3-git-send-email-dantesu@gmail.com>
Download mbox | patch
Permalink /patch/223545/
State New
Headers show

Comments

Kuo-Jung Su - Feb. 27, 2013, 7:15 a.m.
From: Kuo-Jung Su <dantesu@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 <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs      |    4 +
 hw/arm/faraday.h          |   65 +++++++++++++
 hw/arm/faraday_a369.c     |   94 ++++++++++++++++++
 hw/arm/faraday_a369_kpd.c |  237 +++++++++++++++++++++++++++++++++++++++++++++
 hw/arm/faraday_a369_scu.c |  187 +++++++++++++++++++++++++++++++++++
 hw/arm/faraday_a369_soc.c |  197 +++++++++++++++++++++++++++++++++++++
 hw/arm/ftkbc010.h         |   42 ++++++++
 7 files changed, 826 insertions(+)
 create mode 100644 hw/arm/faraday.h
 create mode 100644 hw/arm/faraday_a369.c
 create mode 100644 hw/arm/faraday_a369_kpd.c
 create mode 100644 hw/arm/faraday_a369_scu.c
 create mode 100644 hw/arm/faraday_a369_soc.c
 create mode 100644 hw/arm/ftkbc010.h
Igor Mitsyanko - March 1, 2013, 7 p.m.
Hi, Kuo-Jung

On 02/27/2013 11:15 AM, Kuo-Jung Su wrote:
> From: Kuo-Jung Su <dantesu@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 <dantesu@faraday-tech.com>
> ---
>   hw/arm/Makefile.objs      |    4 +
>   hw/arm/faraday.h          |   65 +++++++++++++
>   hw/arm/faraday_a369.c     |   94 ++++++++++++++++++
>   hw/arm/faraday_a369_kpd.c |  237 +++++++++++++++++++++++++++++++++++++++++++++
>   hw/arm/faraday_a369_scu.c |  187 +++++++++++++++++++++++++++++++++++
>   hw/arm/faraday_a369_soc.c |  197 +++++++++++++++++++++++++++++++++++++
>   hw/arm/ftkbc010.h         |   42 ++++++++
>   7 files changed, 826 insertions(+)
>   create mode 100644 hw/arm/faraday.h
>   create mode 100644 hw/arm/faraday_a369.c
>   create mode 100644 hw/arm/faraday_a369_kpd.c
>   create mode 100644 hw/arm/faraday_a369_scu.c
>   create mode 100644 hw/arm/faraday_a369_soc.c
>   create mode 100644 hw/arm/ftkbc010.h
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 6d049e7..f6fd60d 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -33,3 +33,7 @@ obj-y += kzm.o
>   obj-$(CONFIG_FDT) += ../device_tree.o
>
>   obj-y := $(addprefix ../,$(obj-y))
> +obj-y += faraday_a369.o \
> +            faraday_a369_soc.o \
> +            faraday_a369_scu.o \
> +            faraday_a369_kpd.o

Seems that convention for this file is to shift to the next line only
after current line length is > 80 characters.

> diff --git a/hw/arm/faraday.h b/hw/arm/faraday.h
> new file mode 100644
> index 0000000..d6ed860
> --- /dev/null
> +++ b/hw/arm/faraday.h
> @@ -0,0 +1,65 @@
> +/*
> + * Faraday SoC platform support.
> + *
> + * Copyright (c) 2013 Faraday Technology
> + * Written by Kuo-Jung Su <dantesu@gmail.com>
> + *
> + * This code is licensed under the GNU GPL v2.
> + */
> +#ifndef HW_ARM_FARADAY_H
> +#define HW_ARM_FARADAY_H
> +
> +#include "hw/flash.h"
> +#include "qemu/bitops.h"
> +
> +#ifdef DEBUG_FARADAY
> +#define DPRINTF(fmt, ...) \
> +    do { printf("faraday: " fmt , ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...) \
> +    do { } while (0)
> +#endif

Recently there was a discussions over what kind of debug format to use,
you can see an outcome here
http://thread.gmane.org/gmane.comp.emulators.qemu/195996/focus=196975

> +
> +typedef struct FaradaySoCState {
> +    SysBusDevice busdev;
> +    hwaddr       rom_base;
> +    uint64_t     rom_size;
> +    hwaddr       ram_base;
> +    uint64_t     ram_size;
> +    char         *cpu_model;
> +    ARMCPU       *cpu;
> +    DeviceState  *scu;      /* System Control Unit */
> +    DeviceState  *ahbc;     /* AHB controller */
> +    DeviceState  *ddrc;     /* DDR controller */
> +    DeviceState  *hdma[2];  /* AHB DMA */
> +    DeviceState  *pdma[1];  /* APB DMA */
> +    DeviceState  *spi[2];
> +    DeviceState  *i2c[2];
> +    DeviceState  *i2s[2];
> +    DeviceState  *codec;    /* Audio codec */
> +    void (*codec_out)(void *, uint32_t);
> +    uint32_t (*codec_in)(void *);
> +
> +    MemoryRegion *as;
> +    MemoryRegion *ram;
> +    pflash_t     *rom;
> +    MemoryRegion *sram;
> +
> +    void         *priv;
> +
> +    uint32_t ahb_slave[32];
> +    uint32_t apb_slave[32];
> +    bool     ahb_remapped;
> +    bool     ddr_inited;
> +    struct arm_boot_info *bi;
> +} FaradaySoCState;
> +
> +/* SoC common APIs */
> +#define TYPE_FARADAY_SOC    "faraday/soc"

Using "/" here will break qom-list command because "/" symbol is a
delimiter in a QOM canonical path.

> +#define FARADAY_SOC(obj) \
> +    OBJECT_CHECK(FaradaySoCState, obj, TYPE_FARADAY_SOC)
> +#define FARADAY_SOC_GET_CORE() \
> +    FARADAY_SOC(object_resolve_path_component(qdev_get_machine(), \
> +                                              TYPE_FARADAY_SOC))
> +
> +#endif
> diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c
> new file mode 100644
> index 0000000..0b6201a
> --- /dev/null
> +++ b/hw/arm/faraday_a369.c
> @@ -0,0 +1,94 @@
> +/*
> + * Faraday A369 Evalution Board
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@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/ssi.h"
> +#include "sysemu/sysemu.h"
> +
> +#include "faraday.h"
> +
> +/* Board init.  */
> +
> +static void
> +a369_board_init(QEMUMachineInitArgs *args)
> +{
> +    DeviceState *ds;
> +    FaradaySoCState *s;
> +
> +    if (!args->cpu_model) {
> +        args->cpu_model = "fa626te";
> +    }
> +    if (!args->ram_size) {
> +        args->ram_size = 512 << 20;
> +    }
> +
> +    ds = qdev_create(NULL, TYPE_FARADAY_SOC);
> +    qdev_prop_set_string(ds, "cpu_model", args->cpu_model);
> +    qdev_prop_set_uint64(ds, "ram_size", args->ram_size);
> +    /* Setup QOM path for the SoC object (i.e. /machine/faraday/soc) */
> +    object_property_add_child(qdev_get_machine(),
> +                              TYPE_FARADAY_SOC,
> +                              OBJECT(ds),
> +                              NULL);
> +    qdev_init_nofail(ds);
> +
> +    s = FARADAY_SOC(ds);
> +
> +    if (args->kernel_filename) {
> +        s->bi = g_new0(struct arm_boot_info, 1);
> +
> +        s->ddr_inited = true;
> +        s->ahb_remapped = true;
> +
> +        /* Remap AHB slave 4 (ROM) & slave 6 (RAM) */
> +        /* 1. Remap RAM to base of ROM */
> +        s->ram_base = s->ahb_slave[4] & 0xfff00000;
> +        s->ahb_slave[6] = s->ram_base | (s->ahb_slave[6] & 0x000f0000);
> +        /* 2. Remap ROM to base of ROM + size of RAM */
> +        s->rom_base = s->ram_base
> +                    + ((1 << extract32(s->ahb_slave[6], 16, 4)) << 20);
> +        s->ahb_slave[4] = s->rom_base | (s->ahb_slave[4] & 0x000f0000);
> +
> +        /* 3. Update ROM Address */
> +        sysbus_mmio_map(SYS_BUS_DEVICE(s->rom), 0, s->rom_base);
> +
> +        /* 4. RAM Address Binding */
> +        memory_region_add_subregion(s->as, s->ram_base, s->ram);
> +
> +        /* 5. Boot Info */
> +        s->bi->ram_size = s->ram_size;
> +        s->bi->kernel_filename = args->kernel_filename;
> +        s->bi->kernel_cmdline = args->kernel_cmdline;
> +        s->bi->initrd_filename = args->initrd_filename;
> +        s->bi->board_id = 0x3369;
> +        arm_load_kernel(s->cpu, s->bi);
> +    } else if (!drive_get(IF_PFLASH, 0, 0)) {
> +        hw_error("a369: failed to load ROM image!\n");
> +        exit(1);
> +    }
> +}
> +
> +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_kpd.c b/hw/arm/faraday_a369_kpd.c
> new file mode 100644
> index 0000000..967ada6
> --- /dev/null
> +++ b/hw/arm/faraday_a369_kpd.c
> @@ -0,0 +1,237 @@
> +/*
> + * Faraday FTKBC010 emulator for A369.
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@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 "sysemu/sysemu.h"
> +
> +#include "faraday.h"
> +#include "ftkbc010.h"
> +
> +#define CFG_REGSIZE     (0x3c / 4)
> +
> +/* 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;
> +
> +    /* HW registers */
> +    uint32_t regs[CFG_REGSIZE];
> +} Ftkbc010State;
> +
> +#define FTKBC010(obj) \
> +    OBJECT_CHECK(Ftkbc010State, obj, TYPE_FTKBC010)
> +
> +#define KBC_REG32(s, off) \
> +    *(uint32_t *)((uint8_t *)(s)->regs + (off))
> +
> +static void ftkbc010_update_irq(Ftkbc010State *s)
> +{
> +    uint32_t ier = 0;
> +
> +    /* keypad interrupt */
> +    ier |= (KBC_REG32(s, REG_CR) & CR_KPDEN) ? ISR_KPDI : 0;
> +    /* tx interrupt */
> +    ier |= (KBC_REG32(s, REG_CR) & CR_TXIEN) ? ISR_TXI : 0;
> +    /* rx interrupt */
> +    ier |= (KBC_REG32(s, REG_CR) & CR_RXIEN) ? ISR_RXI : 0;
> +
> +    qemu_set_irq(s->irq, (ier & KBC_REG32(s, REG_ISR)) ? 1 : 0);
> +}
> +
> +static uint64_t ftkbc010_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftkbc010State *s = FTKBC010(opaque);
> +    uint64_t ret = 0;
> +
> +    switch (addr) {
> +    case REG_CR ... REG_ASPR:
> +        ret = s->regs[addr / 4];
> +        break;
> +    case REG_REVR:
> +        ret = 0x00010403;  /* rev. = 1.4.3 */
> +        break;
> +    case REG_FEAR:
> +        ret = 0x00000808;  /* 8x8 scan code for keypad */
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "a369kpd: undefined memory access@0x%llx\n", addr);

This doesn't compile on 64-bit machines, you should replace llx with
HWADDR_PRIx.

> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static void ftkbc010_mem_write(void    *opaque,
> +                               hwaddr   addr,
> +                               uint64_t val,
> +                               unsigned size)
> +{
> +    Ftkbc010State *s = FTKBC010(opaque);
> +
> +    switch (addr) {
> +    case REG_CR:
> +        KBC_REG32(s, REG_CR) = (uint32_t)val;
> +        /* if ftkbc010 enabled */
> +        if (!(KBC_REG32(s, REG_CR) & CR_EN)) {
> +            break;
> +        }
> +        /* if keypad interrupt cleared */
> +        if (KBC_REG32(s, REG_CR) & CR_KPDIC) {
> +            KBC_REG32(s, REG_CR) &= ~CR_KPDIC;
> +            KBC_REG32(s, REG_ISR) &= ~ISR_KPDI;
> +        }
> +        /* if rx interrupt cleared */
> +        if (KBC_REG32(s, REG_CR) & CR_RXICLR) {
> +            KBC_REG32(s, REG_CR) &= ~CR_RXICLR;
> +            KBC_REG32(s, REG_ISR) &= ~ISR_RXI;
> +        }
> +        /* if tx interrupt cleared */
> +        if (KBC_REG32(s, REG_CR) & CR_TXICLR) {
> +            KBC_REG32(s, REG_CR) &= ~CR_TXICLR;
> +            KBC_REG32(s, REG_ISR) &= ~ISR_TXI;
> +        }
> +        ftkbc010_update_irq(s);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "a369kpd: undefined memory access@0x%llx\n", addr);

HWADDR_PRIx

> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = ftkbc010_mem_read,
> +    .write = ftkbc010_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    }
> +};
> +
> +static void ftkbc010_key_event(void *opaque, int scancode)
> +{
> +    Ftkbc010State *s = FTKBC010(opaque);
> +    int x, y, released = 0;
> +
> +    /* key release from qemu */
> +    if (scancode & 0x80) {
> +        released = 1;
> +    }
> +
> +    /* strip qemu key release bit */
> +    scancode &= ~0x80;
> +
> +    /* keypad interrupt */
> +    if (!released && (KBC_REG32(s, REG_CR) & CR_KPDEN)) {
> +        switch (scancode) {
> +        case KEYCODE_ESC:
> +        case KEYCODE_BACKSPACE:
> +            x = 1;
> +            break;
> +        case KEYCODE_ENTER:
> +        case KEYCODE_MENU:
> +        case KEYCODE_SPACE:
> +            x = 3;
> +            break;
> +        default:
> +            x = 2;    /* KEY_HOME */
> +            break;
> +        }
> +        y = 0;
> +        KBC_REG32(s, REG_KPDXR) = ~BIT(x);
> +        KBC_REG32(s, REG_KPDYR) = ~BIT(y);
> +        KBC_REG32(s, REG_ISR)  |= ISR_KPDI;
> +        ftkbc010_update_irq(s);
> +    }
> +}
> +
> +static void ftkbc010_reset(DeviceState *ds)
> +{
> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
> +    Ftkbc010State *s = FTKBC010(FROM_SYSBUS(Ftkbc010State, busdev));

You can drop FROM_SYSBUS() completely, here and in several other places.

> +
> +    memset(s->regs, 0, sizeof(s->regs));
> +    KBC_REG32(s, REG_KPDXR) = 0xffffffff;
> +    KBC_REG32(s, REG_KPDYR) = 0xffffffff;
> +
> +    qemu_irq_lower(s->irq);
> +}
> +
> +static int ftkbc010_init(SysBusDevice *dev)
> +{
> +    Ftkbc010State *s = FTKBC010(FROM_SYSBUS(Ftkbc010State, dev));
> +
> +    memory_region_init_io(&s->iomem,
> +                          &mmio_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_ARRAY(regs, Ftkbc010State, CFG_REGSIZE),
> +        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..4c779ab
> --- /dev/null
> +++ b/hw/arm/faraday_a369_scu.c
> @@ -0,0 +1,187 @@
> +/*
> + * Faraday A369 SCU
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@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 "sysemu/sysemu.h"
> +
> +#include "faraday.h"
> +
> +#define REG_CHIPID      0x000   /* SoC chip id */
> +#define REG_REVISON     0x004   /* SCU revision id */
> +#define REG_HWCFG       0x008   /* HW configuration strap */
> +#define REG_CPUMFCR     0x00C   /* CPUM (master) freq. control */
> +#define REG_SCUCR       0x010   /* SCU control register */
> +#define REG_SCUSR       0x014   /* SCU status register */
> +#define REG_OSCCR       0x01C   /* OSC control register */
> +#define REG_PLL1CR      0x020   /* PLL1 control register */
> +#define REG_DLLCR       0x024   /* DLL control register */
> +#define REG_SPR(n)      (0x100 + ((n) << 2)) /* Scratchpad register 0 - 15 */
> +#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_SCER        0x230   /* Special clock enable register */
> +#define REG_MFPINMUX0   0x238   /* Multi-function pinmux 0 */
> +#define REG_MFPINMUX1   0x23C   /* Multi-function pinmux 1 */
> +#define REG_DCSRCR0     0x240   /* Driving cap. & Slew rate control 0 */
> +#define REG_DCSRCR1     0x244   /* Driving cap. & Slew rate control 1 */
> +#define REG_DCCR        0x254   /* Delay chain control register */
> +#define REG_PCR         0x258   /* Power control register */
> +
> +#define TYPE_A369SCU    "a369.scu"
> +#define CFG_REGSIZE     (0x260 / 4)
> +
> +typedef struct A369SCUState {
> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +
> +    /* HW registers */
> +    uint32_t regs[CFG_REGSIZE];
> +} A369SCUState;
> +
> +#define A369SCU(obj) \
> +    OBJECT_CHECK(A369SCUState, obj, TYPE_A369SCU)
> +
> +#define SCU_REG32(s, off) \
> +    *(uint32_t *)((uint8_t *)(s)->regs + (off))
> +
> +static uint64_t
> +a369scu_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    A369SCUState *s = A369SCU(opaque);
> +    uint64_t ret = 0;
> +
> +    switch (addr) {
> +    case 0x000 ... 0x25C:
> +        ret = s->regs[addr / 4];
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "a369scu: undefined memory access@0x%llx\n", addr);

HWADDR_PRIx

> +        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:
> +    case REG_CLKCFG0:
> +    case REG_CLKCFG1:
> +    case REG_MFPINMUX0:
> +    case REG_MFPINMUX1:
> +        s->regs[addr / 4] = (uint32_t)val;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "a369scu: undefined memory access@0x%llx\n", addr);

HWADDR_PRIx

> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = a369scu_mem_read,
> +    .write = a369scu_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    }
> +};
> +
> +static void a369scu_reset(DeviceState *ds)
> +{
> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
> +    A369SCUState *s = A369SCU(FROM_SYSBUS(A369SCUState, busdev));
> +
> +    memset(s->regs, 0, sizeof(s->regs));
> +
> +    SCU_REG32(s, REG_CHIPID)    = 0x00003369; /* A369 */
> +    SCU_REG32(s, REG_REVISON)   = 0x00010000; /* Rev. = 1.0.0 */
> +    SCU_REG32(s, REG_HWCFG)     = 0x00000c10; /* CPU = 4 * HCLK */
> +    SCU_REG32(s, REG_CPUMFCR)   = 0x00000230; /* CPU = 4 * HCLK */
> +    SCU_REG32(s, REG_SCUCR)     = 0x00000083; /* no low power detect */
> +    SCU_REG32(s, REG_SCUSR)     = 0x00000100; /* CPU freq. stable */
> +    SCU_REG32(s, REG_OSCCR)     = 0x00000003; /* OSCH disabled */
> +    SCU_REG32(s, REG_PLL1CR)    = 0x20010003; /* PLL_NS = 32 */
> +    SCU_REG32(s, REG_DLLCR)     = 0x00000003; /* DLL enabled & stable */
> +    SCU_REG32(s, REG_GPINMUX)   = 0x00001078; /* Pinmux */
> +    SCU_REG32(s, REG_EXTHWCFG)  = 0x00001cc8; /* NAND flash boot */
> +    SCU_REG32(s, REG_CLKCFG0)   = 0x26877330; /* LCD = HCLK */
> +    SCU_REG32(s, REG_CLKCFG1)   = 0x000a0a0a; /* SD = HCLK, SPI=PCLK */
> +    SCU_REG32(s, REG_SCER)      = 0x00003fff; /* All clock enabled */
> +    SCU_REG32(s, REG_MFPINMUX0) = 0x00000241; /* Pinmux */
> +    SCU_REG32(s, REG_MFPINMUX1) = 0x00000000; /* Pinmux */
> +    SCU_REG32(s, REG_DCSRCR0)   = 0x11111111; /* Slow slew rate */
> +    SCU_REG32(s, REG_DCSRCR1)   = 0x11111111; /* Slow slew rate */
> +    SCU_REG32(s, REG_DCCR)      = 0x00000303; /* All delay chain = 3 */
> +    SCU_REG32(s, REG_PCR)       = 0x8000007f; /* High performance mode */
> +}
> +
> +static int a369scu_init(SysBusDevice *dev)
> +{
> +    A369SCUState *s = A369SCU(FROM_SYSBUS(A369SCUState, dev));
> +
> +    memory_region_init_io(&s->iomem,
> +                          &mmio_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_ARRAY(regs, A369SCUState, CFG_REGSIZE),
> +        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/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
> new file mode 100644
> index 0000000..0372868
> --- /dev/null
> +++ b/hw/arm/faraday_a369_soc.c
> @@ -0,0 +1,197 @@
> +/*
> + * Faraday A369 SoC
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@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"
> +
> +static void a369soc_reset(DeviceState *ds)
> +{
> +    int i;
> +    uint64_t size;
> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
> +    FaradaySoCState *s = FARADAY_SOC(FROM_SYSBUS(FaradaySoCState, busdev));
> +
> +    /* AHB slave base & window configuration */
> +    memset(s->ahb_slave, 0, sizeof(s->ahb_slave));
> +    s->ahb_slave[0] = 0x94050000;
> +    s->ahb_slave[1] = 0x96040000;
> +    s->ahb_slave[2] = 0x90f00000;
> +    s->ahb_slave[3] = 0x92050000; /* APB: base=0x92000000, size=32MB */
> +    s->ahb_slave[5] = 0xc0080000;
> +    if (!s->bi) {   /* ROM emulation enabled */
> +        s->ahb_slave[4] = 0x00080000; /* ROM: base=0x00000000, size=256MB */
> +        s->ahb_slave[6] = 0x10090000; /* RAM: base=0x10000000, size=512MB */
> +    } else {        /* Direct boot */
> +        s->ahb_slave[4] = s->rom_base | 0x80000; /* ROM: size=256MB */
> +        s->ahb_slave[6] = s->ram_base | 0x90000; /* RAM: size=512MB */
> +    }
> +    for (i = 0; i < 15; ++i) {
> +        s->ahb_slave[7 + i] = 0x90000000 + (i << 20);
> +    }
> +    s->ahb_slave[22] = 0x40080000;
> +    s->ahb_slave[23] = 0x60080000;
> +    s->ahb_slave[24] = 0xa0000000; /* SRAM: base=0xA0000000, size=1MB */
> +
> +    /* APB slave base & window configuration */
> +    memset(s->apb_slave, 0, sizeof(s->apb_slave));
> +    for (i = 0; i < 18; ++i) {
> +        s->apb_slave[i] = 0x12000000 + (i << 20);
> +    }
> +
> +    /* ROM base = salve4 & 0x000fffff, size = 6KB */

A typo: salve

> +    s->rom_base = s->ahb_slave[4] & 0xfff00000;
> +    s->rom_size = 6 << 10;
> +
> +    /* RAM base = salve6 & 0x000fffff, size <= (1 << slave6.BIT[19-16]) MB */

and here too

> +    size = (1 << extract32(s->ahb_slave[6], 16, 4)) << 20;
> +    s->ram_base = s->ahb_slave[6] & 0xfff00000;
> +    if (!s->ram_size || s->ram_size > size) {
> +        s->ram_size = size;
> +    }
> +}
> +
> +static void
> +a369soc_device_init(FaradaySoCState *s)
> +{
> +    DriveInfo *dinfo;
> +    DeviceState *ds;
> +
> +    s->as = get_system_memory();
> +    s->ram = g_new(MemoryRegion, 1);
> +    s->sram = g_new(MemoryRegion, 1);
> +
> +    /* CPU */
> +    if (!s->cpu_model) {
> +        s->cpu_model = (char *)"fa626te";
> +    }
> +    s->cpu = cpu_arm_init(s->cpu_model);
> +    if (!s->cpu) {
> +        hw_error("a369: Unable to find CPU definition\n");
> +        exit(1);
> +    }
> +
> +    /* RAM Init */
> +    memory_region_init_ram(s->ram, "a369.ram", s->ram_size);
> +    vmstate_register_ram_global(s->ram);
> +
> +    /* 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);
> +
> +    /* Embedded ROM Init (Emulated with a parallel NOR flash) */
> +    dinfo = drive_get_next(IF_PFLASH);
> +    s->rom = pflash_cfi01_register(
> +                    s->rom_base,
> +                    NULL,
> +                    "a369.rom",
> +                    s->rom_size,
> +                    dinfo ? dinfo->bdrv : NULL,
> +                    1024,               /* 1 KB sector */
> +                    s->rom_size >> 10,  /* sectors per chip */
> +                    4,                  /* 32 bits */
> +                    0, 0, 0, 0,         /* id */
> +                    0                   /* Little Endian */);
> +    if (!s->rom) {
> +        hw_error("a369soc: failed to init ROM device.\n");
> +        exit(1);
> +    }
> +
> +    /* Serial (FTUART010 which is 16550A compatible) */
> +    if (serial_hds[0]) {
> +        serial_mm_init(s->as,
> +                       0x92b00000,
> +                       2,
> +                       NULL,
> +                       18432000,
> +                       serial_hds[0],
> +                       DEVICE_LITTLE_ENDIAN);
> +    }
> +    if (serial_hds[1]) {
> +        serial_mm_init(s->as,
> +                       0x92c00000,
> +                       2,
> +                       NULL,
> +                       18432000,
> +                       serial_hds[1],
> +                       DEVICE_LITTLE_ENDIAN);
> +    }
> +
> +    /* ftscu010 */
> +    ds = sysbus_create_simple("a369.scu", 0x92000000, NULL);
> +    s->scu = ds;
> +
> +    /* ftkbc010 */
> +    sysbus_create_simple("a369.keypad", 0x92f00000, NULL);
> +}
> +
> +static int a369soc_init(SysBusDevice *busdev)
> +{
> +    FaradaySoCState *s = FARADAY_SOC(FROM_SYSBUS(FaradaySoCState, busdev));
> +
> +    a369soc_reset(DEVICE(busdev));
> +    a369soc_device_init(s);
> +
> +    return 0;
> +}
> +
> +static Property a369soc_properties[] = {
> +    DEFINE_PROP_STRING("cpu_model", FaradaySoCState, cpu_model),
> +    DEFINE_PROP_UINT64("ram_size", FaradaySoCState, ram_size, 0x20000000),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static const VMStateDescription vmstate_a369soc = {
> +    .name = TYPE_FARADAY_SOC,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_END_OF_LIST(),
> +    }
> +};
> +
> +static void a369soc_class_init(ObjectClass *klass, void *data)
> +{
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    k->init   = a369soc_init;
> +    dc->desc  = TYPE_FARADAY_SOC;
> +    dc->vmsd  = &vmstate_a369soc;
> +    dc->props = a369soc_properties;
> +    dc->reset = a369soc_reset;
> +    dc->no_user = 1;
> +}
> +
> +static const TypeInfo a369soc_info = {
> +    .name          = TYPE_FARADAY_SOC,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(FaradaySoCState),
> +    .class_init    = a369soc_class_init,
> +};
> +
> +static void a369soc_register_types(void)
> +{
> +    type_register_static(&a369soc_info);
> +}
> +
> +type_init(a369soc_register_types)
> diff --git a/hw/arm/ftkbc010.h b/hw/arm/ftkbc010.h
> new file mode 100644
> index 0000000..48e39e1
> --- /dev/null
> +++ b/hw/arm/ftkbc010.h
> @@ -0,0 +1,42 @@
> +/*
> + * Faraday FTKBC010 Keyboard/Keypad Controller
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@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 */
> +
> +#define CR_KPDIC    BIT(10) /* Write 1 to clear Keypad interupt */
> +#define CR_KPDAS    BIT(9)  /* Keypad audo-scan enabled */
> +#define CR_KPDEN    BIT(8)  /* Keypad function enabled */
> +#define CR_RXICLR   BIT(7)  /* Write 1 to clear Keyboard/Mouse Rx interrupt */
> +#define CR_TXICLR   BIT(6)  /* Write 1 to clear Keyboard/Mouse Tx interrupt */
> +#define CR_NOLC     BIT(5)  /* No line control bit */
> +#define CR_RXIEN    BIT(4)  /* Keyboard/Mouse Rx interrupt enabled */
> +#define CR_TXIEN    BIT(3)  /* Keyboard/Mouse Tx interrupt enabled */
> +#define CR_EN       BIT(2)  /* Chip enabled */
> +#define CR_DATDN    BIT(1)  /* Data disabled */
> +#define CR_CLKDN    BIT(0)  /* Clock disabled */
> +
> +#define ISR_KPDI    BIT(2)  /* Keypad interupt */
> +#define ISR_TXI     BIT(1)  /* Keyboard/Mouse Tx interrupt enabled */
> +#define ISR_RXI     BIT(0)  /* Keyboard/Mouse Rx interrupt enabled */

I think its a good idea to include a header where BIT() macro is defined.


> +
> +#endif
Peter Crosthwaite - March 2, 2013, 3:43 a.m.
Hi Kuo-Jung,

On Wed, Feb 27, 2013 at 5:15 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
> From: Kuo-Jung Su <dantesu@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 <dantesu@faraday-tech.com>
> ---
>  hw/arm/Makefile.objs      |    4 +
>  hw/arm/faraday.h          |   65 +++++++++++++
>  hw/arm/faraday_a369.c     |   94 ++++++++++++++++++
>  hw/arm/faraday_a369_kpd.c |  237 +++++++++++++++++++++++++++++++++++++++++++++
>  hw/arm/faraday_a369_scu.c |  187 +++++++++++++++++++++++++++++++++++
>  hw/arm/faraday_a369_soc.c |  197 +++++++++++++++++++++++++++++++++++++
>  hw/arm/ftkbc010.h         |   42 ++++++++
>  7 files changed, 826 insertions(+)
>  create mode 100644 hw/arm/faraday.h
>  create mode 100644 hw/arm/faraday_a369.c
>  create mode 100644 hw/arm/faraday_a369_kpd.c
>  create mode 100644 hw/arm/faraday_a369_scu.c
>  create mode 100644 hw/arm/faraday_a369_soc.c
>  create mode 100644 hw/arm/ftkbc010.h
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 6d049e7..f6fd60d 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -33,3 +33,7 @@ obj-y += kzm.o
>  obj-$(CONFIG_FDT) += ../device_tree.o
>
>  obj-y := $(addprefix ../,$(obj-y))
> +obj-y += faraday_a369.o \
> +            faraday_a369_soc.o \
> +            faraday_a369_scu.o \
> +            faraday_a369_kpd.o
> diff --git a/hw/arm/faraday.h b/hw/arm/faraday.h
> new file mode 100644
> index 0000000..d6ed860
> --- /dev/null
> +++ b/hw/arm/faraday.h
> @@ -0,0 +1,65 @@
> +/*
> + * Faraday SoC platform support.
> + *
> + * Copyright (c) 2013 Faraday Technology
> + * Written by Kuo-Jung Su <dantesu@gmail.com>
> + *
> + * This code is licensed under the GNU GPL v2.
> + */
> +#ifndef HW_ARM_FARADAY_H
> +#define HW_ARM_FARADAY_H
> +
> +#include "hw/flash.h"
> +#include "qemu/bitops.h"
> +
> +#ifdef DEBUG_FARADAY
> +#define DPRINTF(fmt, ...) \
> +    do { printf("faraday: " fmt , ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...) \
> +    do { } while (0)
> +#endif
> +
> +typedef struct FaradaySoCState {
> +    SysBusDevice busdev;
> +    hwaddr       rom_base;
> +    uint64_t     rom_size;
> +    hwaddr       ram_base;
> +    uint64_t     ram_size;
> +    char         *cpu_model;
> +    ARMCPU       *cpu;
> +    DeviceState  *scu;      /* System Control Unit */
> +    DeviceState  *ahbc;     /* AHB controller */
> +    DeviceState  *ddrc;     /* DDR controller */
> +    DeviceState  *hdma[2];  /* AHB DMA */
> +    DeviceState  *pdma[1];  /* APB DMA */
> +    DeviceState  *spi[2];

Your two spi controllers are completely unrelated to each other. They
are different devices so I don't see a win in lumping them together in
a single array - there's no scope for iterating over this array. I
think it would be cleaner if they were separate variables as you would
then be able to provide more descriptive names "DeviceState
*spi_flash", or even better, the actual name of the device
"DeviceState *ftspi020". The same may be true for I2C, ill get around
to that shortly!

> +    DeviceState  *i2c[2];
> +    DeviceState  *i2s[2];
> +    DeviceState  *codec;    /* Audio codec */
> +    void (*codec_out)(void *, uint32_t);
> +    uint32_t (*codec_in)(void *);
> +
> +    MemoryRegion *as;
> +    MemoryRegion *ram;
> +    pflash_t     *rom;
> +    MemoryRegion *sram;
> +
> +    void         *priv;
> +
> +    uint32_t ahb_slave[32];
> +    uint32_t apb_slave[32];
> +    bool     ahb_remapped;
> +    bool     ddr_inited;
> +    struct arm_boot_info *bi;
> +} FaradaySoCState;
> +
> +/* SoC common APIs */
> +#define TYPE_FARADAY_SOC    "faraday/soc"
> +#define FARADAY_SOC(obj) \
> +    OBJECT_CHECK(FaradaySoCState, obj, TYPE_FARADAY_SOC)
> +#define FARADAY_SOC_GET_CORE() \
> +    FARADAY_SOC(object_resolve_path_component(qdev_get_machine(), \
> +                                              TYPE_FARADAY_SOC))
> +
> +#endif
> diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c
> new file mode 100644
> index 0000000..0b6201a
> --- /dev/null
> +++ b/hw/arm/faraday_a369.c
> @@ -0,0 +1,94 @@
> +/*
> + * Faraday A369 Evalution Board
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@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/ssi.h"
> +#include "sysemu/sysemu.h"
> +
> +#include "faraday.h"
> +
> +/* Board init.  */
> +
> +static void
> +a369_board_init(QEMUMachineInitArgs *args)
> +{
> +    DeviceState *ds;
> +    FaradaySoCState *s;
> +
> +    if (!args->cpu_model) {
> +        args->cpu_model = "fa626te";
> +    }
> +    if (!args->ram_size) {
> +        args->ram_size = 512 << 20;
> +    }
> +
> +    ds = qdev_create(NULL, TYPE_FARADAY_SOC);
> +    qdev_prop_set_string(ds, "cpu_model", args->cpu_model);
> +    qdev_prop_set_uint64(ds, "ram_size", args->ram_size);
> +    /* Setup QOM path for the SoC object (i.e. /machine/faraday/soc) */
> +    object_property_add_child(qdev_get_machine(),
> +                              TYPE_FARADAY_SOC,
> +                              OBJECT(ds),
> +                              NULL);
> +    qdev_init_nofail(ds);
> +
> +    s = FARADAY_SOC(ds);
> +
> +    if (args->kernel_filename) {
> +        s->bi = g_new0(struct arm_boot_info, 1);
> +
> +        s->ddr_inited = true;
> +        s->ahb_remapped = true;
> +
> +        /* Remap AHB slave 4 (ROM) & slave 6 (RAM) */
> +        /* 1. Remap RAM to base of ROM */
> +        s->ram_base = s->ahb_slave[4] & 0xfff00000;
> +        s->ahb_slave[6] = s->ram_base | (s->ahb_slave[6] & 0x000f0000);
> +        /* 2. Remap ROM to base of ROM + size of RAM */
> +        s->rom_base = s->ram_base
> +                    + ((1 << extract32(s->ahb_slave[6], 16, 4)) << 20);
> +        s->ahb_slave[4] = s->rom_base | (s->ahb_slave[4] & 0x000f0000);
> +
> +        /* 3. Update ROM Address */
> +        sysbus_mmio_map(SYS_BUS_DEVICE(s->rom), 0, s->rom_base);
> +
> +        /* 4. RAM Address Binding */
> +        memory_region_add_subregion(s->as, s->ram_base, s->ram);
> +
> +        /* 5. Boot Info */
> +        s->bi->ram_size = s->ram_size;
> +        s->bi->kernel_filename = args->kernel_filename;
> +        s->bi->kernel_cmdline = args->kernel_cmdline;
> +        s->bi->initrd_filename = args->initrd_filename;
> +        s->bi->board_id = 0x3369;
> +        arm_load_kernel(s->cpu, s->bi);
> +    } else if (!drive_get(IF_PFLASH, 0, 0)) {
> +        hw_error("a369: failed to load ROM image!\n");
> +        exit(1);
> +    }
> +}
> +
> +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_kpd.c b/hw/arm/faraday_a369_kpd.c
> new file mode 100644
> index 0000000..967ada6
> --- /dev/null
> +++ b/hw/arm/faraday_a369_kpd.c
> @@ -0,0 +1,237 @@
> +/*
> + * Faraday FTKBC010 emulator for A369.
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@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 "sysemu/sysemu.h"
> +
> +#include "faraday.h"
> +#include "ftkbc010.h"
> +
> +#define CFG_REGSIZE     (0x3c / 4)
> +
> +/* 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;
> +
> +    /* HW registers */
> +    uint32_t regs[CFG_REGSIZE];
> +} Ftkbc010State;
> +
> +#define FTKBC010(obj) \
> +    OBJECT_CHECK(Ftkbc010State, obj, TYPE_FTKBC010)
> +
> +#define KBC_REG32(s, off) \
> +    *(uint32_t *)((uint8_t *)(s)->regs + (off))
> +
> +static void ftkbc010_update_irq(Ftkbc010State *s)
> +{
> +    uint32_t ier = 0;
> +
> +    /* keypad interrupt */
> +    ier |= (KBC_REG32(s, REG_CR) & CR_KPDEN) ? ISR_KPDI : 0;
> +    /* tx interrupt */
> +    ier |= (KBC_REG32(s, REG_CR) & CR_TXIEN) ? ISR_TXI : 0;
> +    /* rx interrupt */
> +    ier |= (KBC_REG32(s, REG_CR) & CR_RXIEN) ? ISR_RXI : 0;
> +
> +    qemu_set_irq(s->irq, (ier & KBC_REG32(s, REG_ISR)) ? 1 : 0);
> +}
> +
> +static uint64_t ftkbc010_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftkbc010State *s = FTKBC010(opaque);
> +    uint64_t ret = 0;
> +
> +    switch (addr) {
> +    case REG_CR ... REG_ASPR:
> +        ret = s->regs[addr / 4];
> +        break;
> +    case REG_REVR:
> +        ret = 0x00010403;  /* rev. = 1.4.3 */
> +        break;
> +    case REG_FEAR:
> +        ret = 0x00000808;  /* 8x8 scan code for keypad */
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "a369kpd: undefined memory access@0x%llx\n", addr);
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static void ftkbc010_mem_write(void    *opaque,
> +                               hwaddr   addr,
> +                               uint64_t val,
> +                               unsigned size)
> +{
> +    Ftkbc010State *s = FTKBC010(opaque);
> +
> +    switch (addr) {
> +    case REG_CR:
> +        KBC_REG32(s, REG_CR) = (uint32_t)val;
> +        /* if ftkbc010 enabled */
> +        if (!(KBC_REG32(s, REG_CR) & CR_EN)) {
> +            break;
> +        }
> +        /* if keypad interrupt cleared */
> +        if (KBC_REG32(s, REG_CR) & CR_KPDIC) {
> +            KBC_REG32(s, REG_CR) &= ~CR_KPDIC;
> +            KBC_REG32(s, REG_ISR) &= ~ISR_KPDI;
> +        }
> +        /* if rx interrupt cleared */
> +        if (KBC_REG32(s, REG_CR) & CR_RXICLR) {
> +            KBC_REG32(s, REG_CR) &= ~CR_RXICLR;
> +            KBC_REG32(s, REG_ISR) &= ~ISR_RXI;
> +        }
> +        /* if tx interrupt cleared */
> +        if (KBC_REG32(s, REG_CR) & CR_TXICLR) {
> +            KBC_REG32(s, REG_CR) &= ~CR_TXICLR;
> +            KBC_REG32(s, REG_ISR) &= ~ISR_TXI;
> +        }
> +        ftkbc010_update_irq(s);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "a369kpd: undefined memory access@0x%llx\n", addr);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = ftkbc010_mem_read,
> +    .write = ftkbc010_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    }
> +};
> +
> +static void ftkbc010_key_event(void *opaque, int scancode)
> +{
> +    Ftkbc010State *s = FTKBC010(opaque);
> +    int x, y, released = 0;
> +
> +    /* key release from qemu */
> +    if (scancode & 0x80) {
> +        released = 1;
> +    }
> +
> +    /* strip qemu key release bit */
> +    scancode &= ~0x80;
> +
> +    /* keypad interrupt */
> +    if (!released && (KBC_REG32(s, REG_CR) & CR_KPDEN)) {
> +        switch (scancode) {
> +        case KEYCODE_ESC:
> +        case KEYCODE_BACKSPACE:
> +            x = 1;
> +            break;
> +        case KEYCODE_ENTER:
> +        case KEYCODE_MENU:
> +        case KEYCODE_SPACE:
> +            x = 3;
> +            break;
> +        default:
> +            x = 2;    /* KEY_HOME */
> +            break;
> +        }
> +        y = 0;
> +        KBC_REG32(s, REG_KPDXR) = ~BIT(x);
> +        KBC_REG32(s, REG_KPDYR) = ~BIT(y);
> +        KBC_REG32(s, REG_ISR)  |= ISR_KPDI;
> +        ftkbc010_update_irq(s);
> +    }
> +}
> +
> +static void ftkbc010_reset(DeviceState *ds)
> +{
> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
> +    Ftkbc010State *s = FTKBC010(FROM_SYSBUS(Ftkbc010State, busdev));
> +
> +    memset(s->regs, 0, sizeof(s->regs));
> +    KBC_REG32(s, REG_KPDXR) = 0xffffffff;
> +    KBC_REG32(s, REG_KPDYR) = 0xffffffff;
> +
> +    qemu_irq_lower(s->irq);
> +}
> +
> +static int ftkbc010_init(SysBusDevice *dev)
> +{
> +    Ftkbc010State *s = FTKBC010(FROM_SYSBUS(Ftkbc010State, dev));
> +
> +    memory_region_init_io(&s->iomem,
> +                          &mmio_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_ARRAY(regs, Ftkbc010State, CFG_REGSIZE),
> +        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..4c779ab
> --- /dev/null
> +++ b/hw/arm/faraday_a369_scu.c
> @@ -0,0 +1,187 @@
> +/*
> + * Faraday A369 SCU
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@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 "sysemu/sysemu.h"
> +
> +#include "faraday.h"
> +
> +#define REG_CHIPID      0x000   /* SoC chip id */
> +#define REG_REVISON     0x004   /* SCU revision id */
> +#define REG_HWCFG       0x008   /* HW configuration strap */
> +#define REG_CPUMFCR     0x00C   /* CPUM (master) freq. control */
> +#define REG_SCUCR       0x010   /* SCU control register */
> +#define REG_SCUSR       0x014   /* SCU status register */
> +#define REG_OSCCR       0x01C   /* OSC control register */
> +#define REG_PLL1CR      0x020   /* PLL1 control register */
> +#define REG_DLLCR       0x024   /* DLL control register */
> +#define REG_SPR(n)      (0x100 + ((n) << 2)) /* Scratchpad register 0 - 15 */
> +#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_SCER        0x230   /* Special clock enable register */
> +#define REG_MFPINMUX0   0x238   /* Multi-function pinmux 0 */
> +#define REG_MFPINMUX1   0x23C   /* Multi-function pinmux 1 */
> +#define REG_DCSRCR0     0x240   /* Driving cap. & Slew rate control 0 */
> +#define REG_DCSRCR1     0x244   /* Driving cap. & Slew rate control 1 */
> +#define REG_DCCR        0x254   /* Delay chain control register */
> +#define REG_PCR         0x258   /* Power control register */
> +
> +#define TYPE_A369SCU    "a369.scu"
> +#define CFG_REGSIZE     (0x260 / 4)
> +
> +typedef struct A369SCUState {
> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +
> +    /* HW registers */
> +    uint32_t regs[CFG_REGSIZE];
> +} A369SCUState;
> +
> +#define A369SCU(obj) \
> +    OBJECT_CHECK(A369SCUState, obj, TYPE_A369SCU)
> +
> +#define SCU_REG32(s, off) \
> +    *(uint32_t *)((uint8_t *)(s)->regs + (off))
> +
> +static uint64_t
> +a369scu_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    A369SCUState *s = A369SCU(opaque);
> +    uint64_t ret = 0;
> +
> +    switch (addr) {
> +    case 0x000 ... 0x25C:
> +        ret = s->regs[addr / 4];
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "a369scu: undefined memory access@0x%llx\n", addr);
> +        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:
> +    case REG_CLKCFG0:
> +    case REG_CLKCFG1:
> +    case REG_MFPINMUX0:
> +    case REG_MFPINMUX1:
> +        s->regs[addr / 4] = (uint32_t)val;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "a369scu: undefined memory access@0x%llx\n", addr);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = a369scu_mem_read,
> +    .write = a369scu_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    }
> +};
> +
> +static void a369scu_reset(DeviceState *ds)
> +{
> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
> +    A369SCUState *s = A369SCU(FROM_SYSBUS(A369SCUState, busdev));
> +
> +    memset(s->regs, 0, sizeof(s->regs));
> +
> +    SCU_REG32(s, REG_CHIPID)    = 0x00003369; /* A369 */
> +    SCU_REG32(s, REG_REVISON)   = 0x00010000; /* Rev. = 1.0.0 */
> +    SCU_REG32(s, REG_HWCFG)     = 0x00000c10; /* CPU = 4 * HCLK */
> +    SCU_REG32(s, REG_CPUMFCR)   = 0x00000230; /* CPU = 4 * HCLK */
> +    SCU_REG32(s, REG_SCUCR)     = 0x00000083; /* no low power detect */
> +    SCU_REG32(s, REG_SCUSR)     = 0x00000100; /* CPU freq. stable */
> +    SCU_REG32(s, REG_OSCCR)     = 0x00000003; /* OSCH disabled */
> +    SCU_REG32(s, REG_PLL1CR)    = 0x20010003; /* PLL_NS = 32 */
> +    SCU_REG32(s, REG_DLLCR)     = 0x00000003; /* DLL enabled & stable */
> +    SCU_REG32(s, REG_GPINMUX)   = 0x00001078; /* Pinmux */
> +    SCU_REG32(s, REG_EXTHWCFG)  = 0x00001cc8; /* NAND flash boot */
> +    SCU_REG32(s, REG_CLKCFG0)   = 0x26877330; /* LCD = HCLK */
> +    SCU_REG32(s, REG_CLKCFG1)   = 0x000a0a0a; /* SD = HCLK, SPI=PCLK */
> +    SCU_REG32(s, REG_SCER)      = 0x00003fff; /* All clock enabled */
> +    SCU_REG32(s, REG_MFPINMUX0) = 0x00000241; /* Pinmux */
> +    SCU_REG32(s, REG_MFPINMUX1) = 0x00000000; /* Pinmux */
> +    SCU_REG32(s, REG_DCSRCR0)   = 0x11111111; /* Slow slew rate */
> +    SCU_REG32(s, REG_DCSRCR1)   = 0x11111111; /* Slow slew rate */
> +    SCU_REG32(s, REG_DCCR)      = 0x00000303; /* All delay chain = 3 */
> +    SCU_REG32(s, REG_PCR)       = 0x8000007f; /* High performance mode */
> +}
> +
> +static int a369scu_init(SysBusDevice *dev)
> +{
> +    A369SCUState *s = A369SCU(FROM_SYSBUS(A369SCUState, dev));
> +
> +    memory_region_init_io(&s->iomem,
> +                          &mmio_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_ARRAY(regs, A369SCUState, CFG_REGSIZE),
> +        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/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
> new file mode 100644
> index 0000000..0372868
> --- /dev/null
> +++ b/hw/arm/faraday_a369_soc.c
> @@ -0,0 +1,197 @@
> +/*
> + * Faraday A369 SoC
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@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"
> +
> +static void a369soc_reset(DeviceState *ds)
> +{
> +    int i;
> +    uint64_t size;
> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
> +    FaradaySoCState *s = FARADAY_SOC(FROM_SYSBUS(FaradaySoCState, busdev));
> +
> +    /* AHB slave base & window configuration */
> +    memset(s->ahb_slave, 0, sizeof(s->ahb_slave));
> +    s->ahb_slave[0] = 0x94050000;
> +    s->ahb_slave[1] = 0x96040000;
> +    s->ahb_slave[2] = 0x90f00000;
> +    s->ahb_slave[3] = 0x92050000; /* APB: base=0x92000000, size=32MB */
> +    s->ahb_slave[5] = 0xc0080000;
> +    if (!s->bi) {   /* ROM emulation enabled */
> +        s->ahb_slave[4] = 0x00080000; /* ROM: base=0x00000000, size=256MB */
> +        s->ahb_slave[6] = 0x10090000; /* RAM: base=0x10000000, size=512MB */
> +    } else {        /* Direct boot */
> +        s->ahb_slave[4] = s->rom_base | 0x80000; /* ROM: size=256MB */
> +        s->ahb_slave[6] = s->ram_base | 0x90000; /* RAM: size=512MB */
> +    }
> +    for (i = 0; i < 15; ++i) {
> +        s->ahb_slave[7 + i] = 0x90000000 + (i << 20);
> +    }
> +    s->ahb_slave[22] = 0x40080000;
> +    s->ahb_slave[23] = 0x60080000;
> +    s->ahb_slave[24] = 0xa0000000; /* SRAM: base=0xA0000000, size=1MB */
> +
> +    /* APB slave base & window configuration */
> +    memset(s->apb_slave, 0, sizeof(s->apb_slave));
> +    for (i = 0; i < 18; ++i) {
> +        s->apb_slave[i] = 0x12000000 + (i << 20);
> +    }
> +
> +    /* ROM base = salve4 & 0x000fffff, size = 6KB */
> +    s->rom_base = s->ahb_slave[4] & 0xfff00000;
> +    s->rom_size = 6 << 10;
> +
> +    /* RAM base = salve6 & 0x000fffff, size <= (1 << slave6.BIT[19-16]) MB */
> +    size = (1 << extract32(s->ahb_slave[6], 16, 4)) << 20;
> +    s->ram_base = s->ahb_slave[6] & 0xfff00000;
> +    if (!s->ram_size || s->ram_size > size) {
> +        s->ram_size = size;
> +    }
> +}
> +
> +static void
> +a369soc_device_init(FaradaySoCState *s)
> +{
> +    DriveInfo *dinfo;
> +    DeviceState *ds;
> +
> +    s->as = get_system_memory();
> +    s->ram = g_new(MemoryRegion, 1);
> +    s->sram = g_new(MemoryRegion, 1);
> +
> +    /* CPU */
> +    if (!s->cpu_model) {
> +        s->cpu_model = (char *)"fa626te";
> +    }
> +    s->cpu = cpu_arm_init(s->cpu_model);
> +    if (!s->cpu) {
> +        hw_error("a369: Unable to find CPU definition\n");
> +        exit(1);
> +    }
> +
> +    /* RAM Init */
> +    memory_region_init_ram(s->ram, "a369.ram", s->ram_size);
> +    vmstate_register_ram_global(s->ram);
> +
> +    /* 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);
> +
> +    /* Embedded ROM Init (Emulated with a parallel NOR flash) */
> +    dinfo = drive_get_next(IF_PFLASH);
> +    s->rom = pflash_cfi01_register(
> +                    s->rom_base,
> +                    NULL,
> +                    "a369.rom",
> +                    s->rom_size,
> +                    dinfo ? dinfo->bdrv : NULL,
> +                    1024,               /* 1 KB sector */
> +                    s->rom_size >> 10,  /* sectors per chip */
> +                    4,                  /* 32 bits */
> +                    0, 0, 0, 0,         /* id */
> +                    0                   /* Little Endian */);
> +    if (!s->rom) {
> +        hw_error("a369soc: failed to init ROM device.\n");
> +        exit(1);
> +    }
> +
> +    /* Serial (FTUART010 which is 16550A compatible) */
> +    if (serial_hds[0]) {
> +        serial_mm_init(s->as,
> +                       0x92b00000,
> +                       2,
> +                       NULL,
> +                       18432000,
> +                       serial_hds[0],
> +                       DEVICE_LITTLE_ENDIAN);
> +    }
> +    if (serial_hds[1]) {
> +        serial_mm_init(s->as,
> +                       0x92c00000,
> +                       2,
> +                       NULL,
> +                       18432000,
> +                       serial_hds[1],
> +                       DEVICE_LITTLE_ENDIAN);
> +    }
> +
> +    /* ftscu010 */
> +    ds = sysbus_create_simple("a369.scu", 0x92000000, NULL);
> +    s->scu = ds;
> +
> +    /* ftkbc010 */
> +    sysbus_create_simple("a369.keypad", 0x92f00000, NULL);
> +}
> +
> +static int a369soc_init(SysBusDevice *busdev)
> +{
> +    FaradaySoCState *s = FARADAY_SOC(FROM_SYSBUS(FaradaySoCState, busdev));
> +
> +    a369soc_reset(DEVICE(busdev));
> +    a369soc_device_init(s);
> +
> +    return 0;
> +}
> +
> +static Property a369soc_properties[] = {
> +    DEFINE_PROP_STRING("cpu_model", FaradaySoCState, cpu_model),
> +    DEFINE_PROP_UINT64("ram_size", FaradaySoCState, ram_size, 0x20000000),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static const VMStateDescription vmstate_a369soc = {
> +    .name = TYPE_FARADAY_SOC,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_END_OF_LIST(),
> +    }
> +};
> +
> +static void a369soc_class_init(ObjectClass *klass, void *data)
> +{
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    k->init   = a369soc_init;
> +    dc->desc  = TYPE_FARADAY_SOC;
> +    dc->vmsd  = &vmstate_a369soc;
> +    dc->props = a369soc_properties;
> +    dc->reset = a369soc_reset;
> +    dc->no_user = 1;
> +}
> +
> +static const TypeInfo a369soc_info = {
> +    .name          = TYPE_FARADAY_SOC,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(FaradaySoCState),
> +    .class_init    = a369soc_class_init,
> +};
> +
> +static void a369soc_register_types(void)
> +{
> +    type_register_static(&a369soc_info);
> +}
> +
> +type_init(a369soc_register_types)
> diff --git a/hw/arm/ftkbc010.h b/hw/arm/ftkbc010.h
> new file mode 100644
> index 0000000..48e39e1
> --- /dev/null
> +++ b/hw/arm/ftkbc010.h
> @@ -0,0 +1,42 @@
> +/*
> + * Faraday FTKBC010 Keyboard/Keypad Controller
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@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 */
> +
> +#define CR_KPDIC    BIT(10) /* Write 1 to clear Keypad interupt */
> +#define CR_KPDAS    BIT(9)  /* Keypad audo-scan enabled */
> +#define CR_KPDEN    BIT(8)  /* Keypad function enabled */
> +#define CR_RXICLR   BIT(7)  /* Write 1 to clear Keyboard/Mouse Rx interrupt */
> +#define CR_TXICLR   BIT(6)  /* Write 1 to clear Keyboard/Mouse Tx interrupt */
> +#define CR_NOLC     BIT(5)  /* No line control bit */
> +#define CR_RXIEN    BIT(4)  /* Keyboard/Mouse Rx interrupt enabled */
> +#define CR_TXIEN    BIT(3)  /* Keyboard/Mouse Tx interrupt enabled */
> +#define CR_EN       BIT(2)  /* Chip enabled */
> +#define CR_DATDN    BIT(1)  /* Data disabled */
> +#define CR_CLKDN    BIT(0)  /* Clock disabled */
> +
> +#define ISR_KPDI    BIT(2)  /* Keypad interupt */
> +#define ISR_TXI     BIT(1)  /* Keyboard/Mouse Tx interrupt enabled */
> +#define ISR_RXI     BIT(0)  /* Keyboard/Mouse Rx interrupt enabled */
> +
> +#endif
> --
> 1.7.9.5
>
>
Kuo-Jung Su - March 4, 2013, 6:06 a.m.
2013/3/2 Igor Mitsyanko <i.mitsyanko@gmail.com>:
> Hi, Kuo-Jung
>
> On 02/27/2013 11:15 AM, Kuo-Jung Su wrote:
>> From: Kuo-Jung Su <dantesu@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 <dantesu@faraday-tech.com>
>> ---
>>   hw/arm/Makefile.objs      |    4 +
>>   hw/arm/faraday.h          |   65 +++++++++++++
>>   hw/arm/faraday_a369.c     |   94 ++++++++++++++++++
>>   hw/arm/faraday_a369_kpd.c |  237 +++++++++++++++++++++++++++++++++++++++++++++
>>   hw/arm/faraday_a369_scu.c |  187 +++++++++++++++++++++++++++++++++++
>>   hw/arm/faraday_a369_soc.c |  197 +++++++++++++++++++++++++++++++++++++
>>   hw/arm/ftkbc010.h         |   42 ++++++++
>>   7 files changed, 826 insertions(+)
>>   create mode 100644 hw/arm/faraday.h
>>   create mode 100644 hw/arm/faraday_a369.c
>>   create mode 100644 hw/arm/faraday_a369_kpd.c
>>   create mode 100644 hw/arm/faraday_a369_scu.c
>>   create mode 100644 hw/arm/faraday_a369_soc.c
>>   create mode 100644 hw/arm/ftkbc010.h
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index 6d049e7..f6fd60d 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -33,3 +33,7 @@ obj-y += kzm.o
>>   obj-$(CONFIG_FDT) += ../device_tree.o
>>
>>   obj-y := $(addprefix ../,$(obj-y))
>> +obj-y += faraday_a369.o \
>> +            faraday_a369_soc.o \
>> +            faraday_a369_scu.o \
>> +            faraday_a369_kpd.o
>
> Seems that convention for this file is to shift to the next line only
> after current line length is > 80 characters.
>

Got it, thanks

>> diff --git a/hw/arm/faraday.h b/hw/arm/faraday.h
>> new file mode 100644
>> index 0000000..d6ed860
>> --- /dev/null
>> +++ b/hw/arm/faraday.h
>> @@ -0,0 +1,65 @@
>> +/*
>> + * Faraday SoC platform support.
>> + *
>> + * Copyright (c) 2013 Faraday Technology
>> + * Written by Kuo-Jung Su <dantesu@gmail.com>
>> + *
>> + * This code is licensed under the GNU GPL v2.
>> + */
>> +#ifndef HW_ARM_FARADAY_H
>> +#define HW_ARM_FARADAY_H
>> +
>> +#include "hw/flash.h"
>> +#include "qemu/bitops.h"
>> +
>> +#ifdef DEBUG_FARADAY
>> +#define DPRINTF(fmt, ...) \
>> +    do { printf("faraday: " fmt , ## __VA_ARGS__); } while (0)
>> +#else
>> +#define DPRINTF(fmt, ...) \
>> +    do { } while (0)
>> +#endif
>
> Recently there was a discussions over what kind of debug format to use,
> you can see an outcome here
> http://thread.gmane.org/gmane.comp.emulators.qemu/195996/focus=196975
>

Got it, thanks

>> +
>> +typedef struct FaradaySoCState {
>> +    SysBusDevice busdev;
>> +    hwaddr       rom_base;
>> +    uint64_t     rom_size;
>> +    hwaddr       ram_base;
>> +    uint64_t     ram_size;
>> +    char         *cpu_model;
>> +    ARMCPU       *cpu;
>> +    DeviceState  *scu;      /* System Control Unit */
>> +    DeviceState  *ahbc;     /* AHB controller */
>> +    DeviceState  *ddrc;     /* DDR controller */
>> +    DeviceState  *hdma[2];  /* AHB DMA */
>> +    DeviceState  *pdma[1];  /* APB DMA */
>> +    DeviceState  *spi[2];
>> +    DeviceState  *i2c[2];
>> +    DeviceState  *i2s[2];
>> +    DeviceState  *codec;    /* Audio codec */
>> +    void (*codec_out)(void *, uint32_t);
>> +    uint32_t (*codec_in)(void *);
>> +
>> +    MemoryRegion *as;
>> +    MemoryRegion *ram;
>> +    pflash_t     *rom;
>> +    MemoryRegion *sram;
>> +
>> +    void         *priv;
>> +
>> +    uint32_t ahb_slave[32];
>> +    uint32_t apb_slave[32];
>> +    bool     ahb_remapped;
>> +    bool     ddr_inited;
>> +    struct arm_boot_info *bi;
>> +} FaradaySoCState;
>> +
>> +/* SoC common APIs */
>> +#define TYPE_FARADAY_SOC    "faraday/soc"
>
> Using "/" here will break qom-list command because "/" symbol is a
> delimiter in a QOM canonical path.
>

Got it, thanks

>> +#define FARADAY_SOC(obj) \
>> +    OBJECT_CHECK(FaradaySoCState, obj, TYPE_FARADAY_SOC)
>> +#define FARADAY_SOC_GET_CORE() \
>> +    FARADAY_SOC(object_resolve_path_component(qdev_get_machine(), \
>> +                                              TYPE_FARADAY_SOC))
>> +
>> +#endif
>> diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c
>> new file mode 100644
>> index 0000000..0b6201a
>> --- /dev/null
>> +++ b/hw/arm/faraday_a369.c
>> @@ -0,0 +1,94 @@
>> +/*
>> + * Faraday A369 Evalution Board
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@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/ssi.h"
>> +#include "sysemu/sysemu.h"
>> +
>> +#include "faraday.h"
>> +
>> +/* Board init.  */
>> +
>> +static void
>> +a369_board_init(QEMUMachineInitArgs *args)
>> +{
>> +    DeviceState *ds;
>> +    FaradaySoCState *s;
>> +
>> +    if (!args->cpu_model) {
>> +        args->cpu_model = "fa626te";
>> +    }
>> +    if (!args->ram_size) {
>> +        args->ram_size = 512 << 20;
>> +    }
>> +
>> +    ds = qdev_create(NULL, TYPE_FARADAY_SOC);
>> +    qdev_prop_set_string(ds, "cpu_model", args->cpu_model);
>> +    qdev_prop_set_uint64(ds, "ram_size", args->ram_size);
>> +    /* Setup QOM path for the SoC object (i.e. /machine/faraday/soc) */
>> +    object_property_add_child(qdev_get_machine(),
>> +                              TYPE_FARADAY_SOC,
>> +                              OBJECT(ds),
>> +                              NULL);
>> +    qdev_init_nofail(ds);
>> +
>> +    s = FARADAY_SOC(ds);
>> +
>> +    if (args->kernel_filename) {
>> +        s->bi = g_new0(struct arm_boot_info, 1);
>> +
>> +        s->ddr_inited = true;
>> +        s->ahb_remapped = true;
>> +
>> +        /* Remap AHB slave 4 (ROM) & slave 6 (RAM) */
>> +        /* 1. Remap RAM to base of ROM */
>> +        s->ram_base = s->ahb_slave[4] & 0xfff00000;
>> +        s->ahb_slave[6] = s->ram_base | (s->ahb_slave[6] & 0x000f0000);
>> +        /* 2. Remap ROM to base of ROM + size of RAM */
>> +        s->rom_base = s->ram_base
>> +                    + ((1 << extract32(s->ahb_slave[6], 16, 4)) << 20);
>> +        s->ahb_slave[4] = s->rom_base | (s->ahb_slave[4] & 0x000f0000);
>> +
>> +        /* 3. Update ROM Address */
>> +        sysbus_mmio_map(SYS_BUS_DEVICE(s->rom), 0, s->rom_base);
>> +
>> +        /* 4. RAM Address Binding */
>> +        memory_region_add_subregion(s->as, s->ram_base, s->ram);
>> +
>> +        /* 5. Boot Info */
>> +        s->bi->ram_size = s->ram_size;
>> +        s->bi->kernel_filename = args->kernel_filename;
>> +        s->bi->kernel_cmdline = args->kernel_cmdline;
>> +        s->bi->initrd_filename = args->initrd_filename;
>> +        s->bi->board_id = 0x3369;
>> +        arm_load_kernel(s->cpu, s->bi);
>> +    } else if (!drive_get(IF_PFLASH, 0, 0)) {
>> +        hw_error("a369: failed to load ROM image!\n");
>> +        exit(1);
>> +    }
>> +}
>> +
>> +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_kpd.c b/hw/arm/faraday_a369_kpd.c
>> new file mode 100644
>> index 0000000..967ada6
>> --- /dev/null
>> +++ b/hw/arm/faraday_a369_kpd.c
>> @@ -0,0 +1,237 @@
>> +/*
>> + * Faraday FTKBC010 emulator for A369.
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@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 "sysemu/sysemu.h"
>> +
>> +#include "faraday.h"
>> +#include "ftkbc010.h"
>> +
>> +#define CFG_REGSIZE     (0x3c / 4)
>> +
>> +/* 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;
>> +
>> +    /* HW registers */
>> +    uint32_t regs[CFG_REGSIZE];
>> +} Ftkbc010State;
>> +
>> +#define FTKBC010(obj) \
>> +    OBJECT_CHECK(Ftkbc010State, obj, TYPE_FTKBC010)
>> +
>> +#define KBC_REG32(s, off) \
>> +    *(uint32_t *)((uint8_t *)(s)->regs + (off))
>> +
>> +static void ftkbc010_update_irq(Ftkbc010State *s)
>> +{
>> +    uint32_t ier = 0;
>> +
>> +    /* keypad interrupt */
>> +    ier |= (KBC_REG32(s, REG_CR) & CR_KPDEN) ? ISR_KPDI : 0;
>> +    /* tx interrupt */
>> +    ier |= (KBC_REG32(s, REG_CR) & CR_TXIEN) ? ISR_TXI : 0;
>> +    /* rx interrupt */
>> +    ier |= (KBC_REG32(s, REG_CR) & CR_RXIEN) ? ISR_RXI : 0;
>> +
>> +    qemu_set_irq(s->irq, (ier & KBC_REG32(s, REG_ISR)) ? 1 : 0);
>> +}
>> +
>> +static uint64_t ftkbc010_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    Ftkbc010State *s = FTKBC010(opaque);
>> +    uint64_t ret = 0;
>> +
>> +    switch (addr) {
>> +    case REG_CR ... REG_ASPR:
>> +        ret = s->regs[addr / 4];
>> +        break;
>> +    case REG_REVR:
>> +        ret = 0x00010403;  /* rev. = 1.4.3 */
>> +        break;
>> +    case REG_FEAR:
>> +        ret = 0x00000808;  /* 8x8 scan code for keypad */
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "a369kpd: undefined memory access@0x%llx\n", addr);
>
> This doesn't compile on 64-bit machines, you should replace llx with
> HWADDR_PRIx.
>

Got it, thanks

>> +        break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static void ftkbc010_mem_write(void    *opaque,
>> +                               hwaddr   addr,
>> +                               uint64_t val,
>> +                               unsigned size)
>> +{
>> +    Ftkbc010State *s = FTKBC010(opaque);
>> +
>> +    switch (addr) {
>> +    case REG_CR:
>> +        KBC_REG32(s, REG_CR) = (uint32_t)val;
>> +        /* if ftkbc010 enabled */
>> +        if (!(KBC_REG32(s, REG_CR) & CR_EN)) {
>> +            break;
>> +        }
>> +        /* if keypad interrupt cleared */
>> +        if (KBC_REG32(s, REG_CR) & CR_KPDIC) {
>> +            KBC_REG32(s, REG_CR) &= ~CR_KPDIC;
>> +            KBC_REG32(s, REG_ISR) &= ~ISR_KPDI;
>> +        }
>> +        /* if rx interrupt cleared */
>> +        if (KBC_REG32(s, REG_CR) & CR_RXICLR) {
>> +            KBC_REG32(s, REG_CR) &= ~CR_RXICLR;
>> +            KBC_REG32(s, REG_ISR) &= ~ISR_RXI;
>> +        }
>> +        /* if tx interrupt cleared */
>> +        if (KBC_REG32(s, REG_CR) & CR_TXICLR) {
>> +            KBC_REG32(s, REG_CR) &= ~CR_TXICLR;
>> +            KBC_REG32(s, REG_ISR) &= ~ISR_TXI;
>> +        }
>> +        ftkbc010_update_irq(s);
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "a369kpd: undefined memory access@0x%llx\n", addr);
>
> HWADDR_PRIx
>
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = ftkbc010_mem_read,
>> +    .write = ftkbc010_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    }
>> +};
>> +
>> +static void ftkbc010_key_event(void *opaque, int scancode)
>> +{
>> +    Ftkbc010State *s = FTKBC010(opaque);
>> +    int x, y, released = 0;
>> +
>> +    /* key release from qemu */
>> +    if (scancode & 0x80) {
>> +        released = 1;
>> +    }
>> +
>> +    /* strip qemu key release bit */
>> +    scancode &= ~0x80;
>> +
>> +    /* keypad interrupt */
>> +    if (!released && (KBC_REG32(s, REG_CR) & CR_KPDEN)) {
>> +        switch (scancode) {
>> +        case KEYCODE_ESC:
>> +        case KEYCODE_BACKSPACE:
>> +            x = 1;
>> +            break;
>> +        case KEYCODE_ENTER:
>> +        case KEYCODE_MENU:
>> +        case KEYCODE_SPACE:
>> +            x = 3;
>> +            break;
>> +        default:
>> +            x = 2;    /* KEY_HOME */
>> +            break;
>> +        }
>> +        y = 0;
>> +        KBC_REG32(s, REG_KPDXR) = ~BIT(x);
>> +        KBC_REG32(s, REG_KPDYR) = ~BIT(y);
>> +        KBC_REG32(s, REG_ISR)  |= ISR_KPDI;
>> +        ftkbc010_update_irq(s);
>> +    }
>> +}
>> +
>> +static void ftkbc010_reset(DeviceState *ds)
>> +{
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    Ftkbc010State *s = FTKBC010(FROM_SYSBUS(Ftkbc010State, busdev));
>
> You can drop FROM_SYSBUS() completely, here and in several other places.
>

Got it, thanks

>> +
>> +    memset(s->regs, 0, sizeof(s->regs));
>> +    KBC_REG32(s, REG_KPDXR) = 0xffffffff;
>> +    KBC_REG32(s, REG_KPDYR) = 0xffffffff;
>> +
>> +    qemu_irq_lower(s->irq);
>> +}
>> +
>> +static int ftkbc010_init(SysBusDevice *dev)
>> +{
>> +    Ftkbc010State *s = FTKBC010(FROM_SYSBUS(Ftkbc010State, dev));
>> +
>> +    memory_region_init_io(&s->iomem,
>> +                          &mmio_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_ARRAY(regs, Ftkbc010State, CFG_REGSIZE),
>> +        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..4c779ab
>> --- /dev/null
>> +++ b/hw/arm/faraday_a369_scu.c
>> @@ -0,0 +1,187 @@
>> +/*
>> + * Faraday A369 SCU
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@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 "sysemu/sysemu.h"
>> +
>> +#include "faraday.h"
>> +
>> +#define REG_CHIPID      0x000   /* SoC chip id */
>> +#define REG_REVISON     0x004   /* SCU revision id */
>> +#define REG_HWCFG       0x008   /* HW configuration strap */
>> +#define REG_CPUMFCR     0x00C   /* CPUM (master) freq. control */
>> +#define REG_SCUCR       0x010   /* SCU control register */
>> +#define REG_SCUSR       0x014   /* SCU status register */
>> +#define REG_OSCCR       0x01C   /* OSC control register */
>> +#define REG_PLL1CR      0x020   /* PLL1 control register */
>> +#define REG_DLLCR       0x024   /* DLL control register */
>> +#define REG_SPR(n)      (0x100 + ((n) << 2)) /* Scratchpad register 0 - 15 */
>> +#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_SCER        0x230   /* Special clock enable register */
>> +#define REG_MFPINMUX0   0x238   /* Multi-function pinmux 0 */
>> +#define REG_MFPINMUX1   0x23C   /* Multi-function pinmux 1 */
>> +#define REG_DCSRCR0     0x240   /* Driving cap. & Slew rate control 0 */
>> +#define REG_DCSRCR1     0x244   /* Driving cap. & Slew rate control 1 */
>> +#define REG_DCCR        0x254   /* Delay chain control register */
>> +#define REG_PCR         0x258   /* Power control register */
>> +
>> +#define TYPE_A369SCU    "a369.scu"
>> +#define CFG_REGSIZE     (0x260 / 4)
>> +
>> +typedef struct A369SCUState {
>> +    SysBusDevice busdev;
>> +    MemoryRegion iomem;
>> +
>> +    /* HW registers */
>> +    uint32_t regs[CFG_REGSIZE];
>> +} A369SCUState;
>> +
>> +#define A369SCU(obj) \
>> +    OBJECT_CHECK(A369SCUState, obj, TYPE_A369SCU)
>> +
>> +#define SCU_REG32(s, off) \
>> +    *(uint32_t *)((uint8_t *)(s)->regs + (off))
>> +
>> +static uint64_t
>> +a369scu_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    A369SCUState *s = A369SCU(opaque);
>> +    uint64_t ret = 0;
>> +
>> +    switch (addr) {
>> +    case 0x000 ... 0x25C:
>> +        ret = s->regs[addr / 4];
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "a369scu: undefined memory access@0x%llx\n", addr);
>
> HWADDR_PRIx
>
>> +        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:
>> +    case REG_CLKCFG0:
>> +    case REG_CLKCFG1:
>> +    case REG_MFPINMUX0:
>> +    case REG_MFPINMUX1:
>> +        s->regs[addr / 4] = (uint32_t)val;
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "a369scu: undefined memory access@0x%llx\n", addr);
>
> HWADDR_PRIx
>
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = a369scu_mem_read,
>> +    .write = a369scu_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    }
>> +};
>> +
>> +static void a369scu_reset(DeviceState *ds)
>> +{
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    A369SCUState *s = A369SCU(FROM_SYSBUS(A369SCUState, busdev));
>> +
>> +    memset(s->regs, 0, sizeof(s->regs));
>> +
>> +    SCU_REG32(s, REG_CHIPID)    = 0x00003369; /* A369 */
>> +    SCU_REG32(s, REG_REVISON)   = 0x00010000; /* Rev. = 1.0.0 */
>> +    SCU_REG32(s, REG_HWCFG)     = 0x00000c10; /* CPU = 4 * HCLK */
>> +    SCU_REG32(s, REG_CPUMFCR)   = 0x00000230; /* CPU = 4 * HCLK */
>> +    SCU_REG32(s, REG_SCUCR)     = 0x00000083; /* no low power detect */
>> +    SCU_REG32(s, REG_SCUSR)     = 0x00000100; /* CPU freq. stable */
>> +    SCU_REG32(s, REG_OSCCR)     = 0x00000003; /* OSCH disabled */
>> +    SCU_REG32(s, REG_PLL1CR)    = 0x20010003; /* PLL_NS = 32 */
>> +    SCU_REG32(s, REG_DLLCR)     = 0x00000003; /* DLL enabled & stable */
>> +    SCU_REG32(s, REG_GPINMUX)   = 0x00001078; /* Pinmux */
>> +    SCU_REG32(s, REG_EXTHWCFG)  = 0x00001cc8; /* NAND flash boot */
>> +    SCU_REG32(s, REG_CLKCFG0)   = 0x26877330; /* LCD = HCLK */
>> +    SCU_REG32(s, REG_CLKCFG1)   = 0x000a0a0a; /* SD = HCLK, SPI=PCLK */
>> +    SCU_REG32(s, REG_SCER)      = 0x00003fff; /* All clock enabled */
>> +    SCU_REG32(s, REG_MFPINMUX0) = 0x00000241; /* Pinmux */
>> +    SCU_REG32(s, REG_MFPINMUX1) = 0x00000000; /* Pinmux */
>> +    SCU_REG32(s, REG_DCSRCR0)   = 0x11111111; /* Slow slew rate */
>> +    SCU_REG32(s, REG_DCSRCR1)   = 0x11111111; /* Slow slew rate */
>> +    SCU_REG32(s, REG_DCCR)      = 0x00000303; /* All delay chain = 3 */
>> +    SCU_REG32(s, REG_PCR)       = 0x8000007f; /* High performance mode */
>> +}
>> +
>> +static int a369scu_init(SysBusDevice *dev)
>> +{
>> +    A369SCUState *s = A369SCU(FROM_SYSBUS(A369SCUState, dev));
>> +
>> +    memory_region_init_io(&s->iomem,
>> +                          &mmio_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_ARRAY(regs, A369SCUState, CFG_REGSIZE),
>> +        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/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
>> new file mode 100644
>> index 0000000..0372868
>> --- /dev/null
>> +++ b/hw/arm/faraday_a369_soc.c
>> @@ -0,0 +1,197 @@
>> +/*
>> + * Faraday A369 SoC
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@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"
>> +
>> +static void a369soc_reset(DeviceState *ds)
>> +{
>> +    int i;
>> +    uint64_t size;
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    FaradaySoCState *s = FARADAY_SOC(FROM_SYSBUS(FaradaySoCState, busdev));
>> +
>> +    /* AHB slave base & window configuration */
>> +    memset(s->ahb_slave, 0, sizeof(s->ahb_slave));
>> +    s->ahb_slave[0] = 0x94050000;
>> +    s->ahb_slave[1] = 0x96040000;
>> +    s->ahb_slave[2] = 0x90f00000;
>> +    s->ahb_slave[3] = 0x92050000; /* APB: base=0x92000000, size=32MB */
>> +    s->ahb_slave[5] = 0xc0080000;
>> +    if (!s->bi) {   /* ROM emulation enabled */
>> +        s->ahb_slave[4] = 0x00080000; /* ROM: base=0x00000000, size=256MB */
>> +        s->ahb_slave[6] = 0x10090000; /* RAM: base=0x10000000, size=512MB */
>> +    } else {        /* Direct boot */
>> +        s->ahb_slave[4] = s->rom_base | 0x80000; /* ROM: size=256MB */
>> +        s->ahb_slave[6] = s->ram_base | 0x90000; /* RAM: size=512MB */
>> +    }
>> +    for (i = 0; i < 15; ++i) {
>> +        s->ahb_slave[7 + i] = 0x90000000 + (i << 20);
>> +    }
>> +    s->ahb_slave[22] = 0x40080000;
>> +    s->ahb_slave[23] = 0x60080000;
>> +    s->ahb_slave[24] = 0xa0000000; /* SRAM: base=0xA0000000, size=1MB */
>> +
>> +    /* APB slave base & window configuration */
>> +    memset(s->apb_slave, 0, sizeof(s->apb_slave));
>> +    for (i = 0; i < 18; ++i) {
>> +        s->apb_slave[i] = 0x12000000 + (i << 20);
>> +    }
>> +
>> +    /* ROM base = salve4 & 0x000fffff, size = 6KB */
>
> A typo: salve
>

Got it, thanks

>> +    s->rom_base = s->ahb_slave[4] & 0xfff00000;
>> +    s->rom_size = 6 << 10;
>> +
>> +    /* RAM base = salve6 & 0x000fffff, size <= (1 << slave6.BIT[19-16]) MB */
>
> and here too
>
>> +    size = (1 << extract32(s->ahb_slave[6], 16, 4)) << 20;
>> +    s->ram_base = s->ahb_slave[6] & 0xfff00000;
>> +    if (!s->ram_size || s->ram_size > size) {
>> +        s->ram_size = size;
>> +    }
>> +}
>> +
>> +static void
>> +a369soc_device_init(FaradaySoCState *s)
>> +{
>> +    DriveInfo *dinfo;
>> +    DeviceState *ds;
>> +
>> +    s->as = get_system_memory();
>> +    s->ram = g_new(MemoryRegion, 1);
>> +    s->sram = g_new(MemoryRegion, 1);
>> +
>> +    /* CPU */
>> +    if (!s->cpu_model) {
>> +        s->cpu_model = (char *)"fa626te";
>> +    }
>> +    s->cpu = cpu_arm_init(s->cpu_model);
>> +    if (!s->cpu) {
>> +        hw_error("a369: Unable to find CPU definition\n");
>> +        exit(1);
>> +    }
>> +
>> +    /* RAM Init */
>> +    memory_region_init_ram(s->ram, "a369.ram", s->ram_size);
>> +    vmstate_register_ram_global(s->ram);
>> +
>> +    /* 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);
>> +
>> +    /* Embedded ROM Init (Emulated with a parallel NOR flash) */
>> +    dinfo = drive_get_next(IF_PFLASH);
>> +    s->rom = pflash_cfi01_register(
>> +                    s->rom_base,
>> +                    NULL,
>> +                    "a369.rom",
>> +                    s->rom_size,
>> +                    dinfo ? dinfo->bdrv : NULL,
>> +                    1024,               /* 1 KB sector */
>> +                    s->rom_size >> 10,  /* sectors per chip */
>> +                    4,                  /* 32 bits */
>> +                    0, 0, 0, 0,         /* id */
>> +                    0                   /* Little Endian */);
>> +    if (!s->rom) {
>> +        hw_error("a369soc: failed to init ROM device.\n");
>> +        exit(1);
>> +    }
>> +
>> +    /* Serial (FTUART010 which is 16550A compatible) */
>> +    if (serial_hds[0]) {
>> +        serial_mm_init(s->as,
>> +                       0x92b00000,
>> +                       2,
>> +                       NULL,
>> +                       18432000,
>> +                       serial_hds[0],
>> +                       DEVICE_LITTLE_ENDIAN);
>> +    }
>> +    if (serial_hds[1]) {
>> +        serial_mm_init(s->as,
>> +                       0x92c00000,
>> +                       2,
>> +                       NULL,
>> +                       18432000,
>> +                       serial_hds[1],
>> +                       DEVICE_LITTLE_ENDIAN);
>> +    }
>> +
>> +    /* ftscu010 */
>> +    ds = sysbus_create_simple("a369.scu", 0x92000000, NULL);
>> +    s->scu = ds;
>> +
>> +    /* ftkbc010 */
>> +    sysbus_create_simple("a369.keypad", 0x92f00000, NULL);
>> +}
>> +
>> +static int a369soc_init(SysBusDevice *busdev)
>> +{
>> +    FaradaySoCState *s = FARADAY_SOC(FROM_SYSBUS(FaradaySoCState, busdev));
>> +
>> +    a369soc_reset(DEVICE(busdev));
>> +    a369soc_device_init(s);
>> +
>> +    return 0;
>> +}
>> +
>> +static Property a369soc_properties[] = {
>> +    DEFINE_PROP_STRING("cpu_model", FaradaySoCState, cpu_model),
>> +    DEFINE_PROP_UINT64("ram_size", FaradaySoCState, ram_size, 0x20000000),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static const VMStateDescription vmstate_a369soc = {
>> +    .name = TYPE_FARADAY_SOC,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_END_OF_LIST(),
>> +    }
>> +};
>> +
>> +static void a369soc_class_init(ObjectClass *klass, void *data)
>> +{
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    k->init   = a369soc_init;
>> +    dc->desc  = TYPE_FARADAY_SOC;
>> +    dc->vmsd  = &vmstate_a369soc;
>> +    dc->props = a369soc_properties;
>> +    dc->reset = a369soc_reset;
>> +    dc->no_user = 1;
>> +}
>> +
>> +static const TypeInfo a369soc_info = {
>> +    .name          = TYPE_FARADAY_SOC,
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(FaradaySoCState),
>> +    .class_init    = a369soc_class_init,
>> +};
>> +
>> +static void a369soc_register_types(void)
>> +{
>> +    type_register_static(&a369soc_info);
>> +}
>> +
>> +type_init(a369soc_register_types)
>> diff --git a/hw/arm/ftkbc010.h b/hw/arm/ftkbc010.h
>> new file mode 100644
>> index 0000000..48e39e1
>> --- /dev/null
>> +++ b/hw/arm/ftkbc010.h
>> @@ -0,0 +1,42 @@
>> +/*
>> + * Faraday FTKBC010 Keyboard/Keypad Controller
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@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 */
>> +
>> +#define CR_KPDIC    BIT(10) /* Write 1 to clear Keypad interupt */
>> +#define CR_KPDAS    BIT(9)  /* Keypad audo-scan enabled */
>> +#define CR_KPDEN    BIT(8)  /* Keypad function enabled */
>> +#define CR_RXICLR   BIT(7)  /* Write 1 to clear Keyboard/Mouse Rx interrupt */
>> +#define CR_TXICLR   BIT(6)  /* Write 1 to clear Keyboard/Mouse Tx interrupt */
>> +#define CR_NOLC     BIT(5)  /* No line control bit */
>> +#define CR_RXIEN    BIT(4)  /* Keyboard/Mouse Rx interrupt enabled */
>> +#define CR_TXIEN    BIT(3)  /* Keyboard/Mouse Tx interrupt enabled */
>> +#define CR_EN       BIT(2)  /* Chip enabled */
>> +#define CR_DATDN    BIT(1)  /* Data disabled */
>> +#define CR_CLKDN    BIT(0)  /* Clock disabled */
>> +
>> +#define ISR_KPDI    BIT(2)  /* Keypad interupt */
>> +#define ISR_TXI     BIT(1)  /* Keyboard/Mouse Tx interrupt enabled */
>> +#define ISR_RXI     BIT(0)  /* Keyboard/Mouse Rx interrupt enabled */
>
> I think its a good idea to include a header where BIT() macro is defined.
>
>

Got it, thanks

>> +
>> +#endif
Kuo-Jung Su - March 4, 2013, 6:09 a.m.
2013/3/2 Peter Crosthwaite <peter.crosthwaite@xilinx.com>:
> Hi Kuo-Jung,
>
> On Wed, Feb 27, 2013 at 5:15 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>> From: Kuo-Jung Su <dantesu@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 <dantesu@faraday-tech.com>
>> ---
>>  hw/arm/Makefile.objs      |    4 +
>>  hw/arm/faraday.h          |   65 +++++++++++++
>>  hw/arm/faraday_a369.c     |   94 ++++++++++++++++++
>>  hw/arm/faraday_a369_kpd.c |  237 +++++++++++++++++++++++++++++++++++++++++++++
>>  hw/arm/faraday_a369_scu.c |  187 +++++++++++++++++++++++++++++++++++
>>  hw/arm/faraday_a369_soc.c |  197 +++++++++++++++++++++++++++++++++++++
>>  hw/arm/ftkbc010.h         |   42 ++++++++
>>  7 files changed, 826 insertions(+)
>>  create mode 100644 hw/arm/faraday.h
>>  create mode 100644 hw/arm/faraday_a369.c
>>  create mode 100644 hw/arm/faraday_a369_kpd.c
>>  create mode 100644 hw/arm/faraday_a369_scu.c
>>  create mode 100644 hw/arm/faraday_a369_soc.c
>>  create mode 100644 hw/arm/ftkbc010.h
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index 6d049e7..f6fd60d 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -33,3 +33,7 @@ obj-y += kzm.o
>>  obj-$(CONFIG_FDT) += ../device_tree.o
>>
>>  obj-y := $(addprefix ../,$(obj-y))
>> +obj-y += faraday_a369.o \
>> +            faraday_a369_soc.o \
>> +            faraday_a369_scu.o \
>> +            faraday_a369_kpd.o
>> diff --git a/hw/arm/faraday.h b/hw/arm/faraday.h
>> new file mode 100644
>> index 0000000..d6ed860
>> --- /dev/null
>> +++ b/hw/arm/faraday.h
>> @@ -0,0 +1,65 @@
>> +/*
>> + * Faraday SoC platform support.
>> + *
>> + * Copyright (c) 2013 Faraday Technology
>> + * Written by Kuo-Jung Su <dantesu@gmail.com>
>> + *
>> + * This code is licensed under the GNU GPL v2.
>> + */
>> +#ifndef HW_ARM_FARADAY_H
>> +#define HW_ARM_FARADAY_H
>> +
>> +#include "hw/flash.h"
>> +#include "qemu/bitops.h"
>> +
>> +#ifdef DEBUG_FARADAY
>> +#define DPRINTF(fmt, ...) \
>> +    do { printf("faraday: " fmt , ## __VA_ARGS__); } while (0)
>> +#else
>> +#define DPRINTF(fmt, ...) \
>> +    do { } while (0)
>> +#endif
>> +
>> +typedef struct FaradaySoCState {
>> +    SysBusDevice busdev;
>> +    hwaddr       rom_base;
>> +    uint64_t     rom_size;
>> +    hwaddr       ram_base;
>> +    uint64_t     ram_size;
>> +    char         *cpu_model;
>> +    ARMCPU       *cpu;
>> +    DeviceState  *scu;      /* System Control Unit */
>> +    DeviceState  *ahbc;     /* AHB controller */
>> +    DeviceState  *ddrc;     /* DDR controller */
>> +    DeviceState  *hdma[2];  /* AHB DMA */
>> +    DeviceState  *pdma[1];  /* APB DMA */
>> +    DeviceState  *spi[2];
>
> Your two spi controllers are completely unrelated to each other. They
> are different devices so I don't see a win in lumping them together in
> a single array - there's no scope for iterating over this array. I
> think it would be cleaner if they were separate variables as you would
> then be able to provide more descriptive names "DeviceState
> *spi_flash", or even better, the actual name of the device
> "DeviceState *ftspi020". The same may be true for I2C, ill get around
> to that shortly!
>

Got it, thanks.
I'll add a new field 'DeviceState *spi_fl[2]' to the FaradaySoCState() for
the dedicated spi flash controllers.

>> +    DeviceState  *i2c[2];
>> +    DeviceState  *i2s[2];
>> +    DeviceState  *codec;    /* Audio codec */
>> +    void (*codec_out)(void *, uint32_t);
>> +    uint32_t (*codec_in)(void *);
>> +
>> +    MemoryRegion *as;
>> +    MemoryRegion *ram;
>> +    pflash_t     *rom;
>> +    MemoryRegion *sram;
>> +
>> +    void         *priv;
>> +
>> +    uint32_t ahb_slave[32];
>> +    uint32_t apb_slave[32];
>> +    bool     ahb_remapped;
>> +    bool     ddr_inited;
>> +    struct arm_boot_info *bi;
>> +} FaradaySoCState;
>> +
>> +/* SoC common APIs */
>> +#define TYPE_FARADAY_SOC    "faraday/soc"
>> +#define FARADAY_SOC(obj) \
>> +    OBJECT_CHECK(FaradaySoCState, obj, TYPE_FARADAY_SOC)
>> +#define FARADAY_SOC_GET_CORE() \
>> +    FARADAY_SOC(object_resolve_path_component(qdev_get_machine(), \
>> +                                              TYPE_FARADAY_SOC))
>> +
>> +#endif
>> diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c
>> new file mode 100644
>> index 0000000..0b6201a
>> --- /dev/null
>> +++ b/hw/arm/faraday_a369.c
>> @@ -0,0 +1,94 @@
>> +/*
>> + * Faraday A369 Evalution Board
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@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/ssi.h"
>> +#include "sysemu/sysemu.h"
>> +
>> +#include "faraday.h"
>> +
>> +/* Board init.  */
>> +
>> +static void
>> +a369_board_init(QEMUMachineInitArgs *args)
>> +{
>> +    DeviceState *ds;
>> +    FaradaySoCState *s;
>> +
>> +    if (!args->cpu_model) {
>> +        args->cpu_model = "fa626te";
>> +    }
>> +    if (!args->ram_size) {
>> +        args->ram_size = 512 << 20;
>> +    }
>> +
>> +    ds = qdev_create(NULL, TYPE_FARADAY_SOC);
>> +    qdev_prop_set_string(ds, "cpu_model", args->cpu_model);
>> +    qdev_prop_set_uint64(ds, "ram_size", args->ram_size);
>> +    /* Setup QOM path for the SoC object (i.e. /machine/faraday/soc) */
>> +    object_property_add_child(qdev_get_machine(),
>> +                              TYPE_FARADAY_SOC,
>> +                              OBJECT(ds),
>> +                              NULL);
>> +    qdev_init_nofail(ds);
>> +
>> +    s = FARADAY_SOC(ds);
>> +
>> +    if (args->kernel_filename) {
>> +        s->bi = g_new0(struct arm_boot_info, 1);
>> +
>> +        s->ddr_inited = true;
>> +        s->ahb_remapped = true;
>> +
>> +        /* Remap AHB slave 4 (ROM) & slave 6 (RAM) */
>> +        /* 1. Remap RAM to base of ROM */
>> +        s->ram_base = s->ahb_slave[4] & 0xfff00000;
>> +        s->ahb_slave[6] = s->ram_base | (s->ahb_slave[6] & 0x000f0000);
>> +        /* 2. Remap ROM to base of ROM + size of RAM */
>> +        s->rom_base = s->ram_base
>> +                    + ((1 << extract32(s->ahb_slave[6], 16, 4)) << 20);
>> +        s->ahb_slave[4] = s->rom_base | (s->ahb_slave[4] & 0x000f0000);
>> +
>> +        /* 3. Update ROM Address */
>> +        sysbus_mmio_map(SYS_BUS_DEVICE(s->rom), 0, s->rom_base);
>> +
>> +        /* 4. RAM Address Binding */
>> +        memory_region_add_subregion(s->as, s->ram_base, s->ram);
>> +
>> +        /* 5. Boot Info */
>> +        s->bi->ram_size = s->ram_size;
>> +        s->bi->kernel_filename = args->kernel_filename;
>> +        s->bi->kernel_cmdline = args->kernel_cmdline;
>> +        s->bi->initrd_filename = args->initrd_filename;
>> +        s->bi->board_id = 0x3369;
>> +        arm_load_kernel(s->cpu, s->bi);
>> +    } else if (!drive_get(IF_PFLASH, 0, 0)) {
>> +        hw_error("a369: failed to load ROM image!\n");
>> +        exit(1);
>> +    }
>> +}
>> +
>> +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_kpd.c b/hw/arm/faraday_a369_kpd.c
>> new file mode 100644
>> index 0000000..967ada6
>> --- /dev/null
>> +++ b/hw/arm/faraday_a369_kpd.c
>> @@ -0,0 +1,237 @@
>> +/*
>> + * Faraday FTKBC010 emulator for A369.
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@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 "sysemu/sysemu.h"
>> +
>> +#include "faraday.h"
>> +#include "ftkbc010.h"
>> +
>> +#define CFG_REGSIZE     (0x3c / 4)
>> +
>> +/* 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;
>> +
>> +    /* HW registers */
>> +    uint32_t regs[CFG_REGSIZE];
>> +} Ftkbc010State;
>> +
>> +#define FTKBC010(obj) \
>> +    OBJECT_CHECK(Ftkbc010State, obj, TYPE_FTKBC010)
>> +
>> +#define KBC_REG32(s, off) \
>> +    *(uint32_t *)((uint8_t *)(s)->regs + (off))
>> +
>> +static void ftkbc010_update_irq(Ftkbc010State *s)
>> +{
>> +    uint32_t ier = 0;
>> +
>> +    /* keypad interrupt */
>> +    ier |= (KBC_REG32(s, REG_CR) & CR_KPDEN) ? ISR_KPDI : 0;
>> +    /* tx interrupt */
>> +    ier |= (KBC_REG32(s, REG_CR) & CR_TXIEN) ? ISR_TXI : 0;
>> +    /* rx interrupt */
>> +    ier |= (KBC_REG32(s, REG_CR) & CR_RXIEN) ? ISR_RXI : 0;
>> +
>> +    qemu_set_irq(s->irq, (ier & KBC_REG32(s, REG_ISR)) ? 1 : 0);
>> +}
>> +
>> +static uint64_t ftkbc010_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    Ftkbc010State *s = FTKBC010(opaque);
>> +    uint64_t ret = 0;
>> +
>> +    switch (addr) {
>> +    case REG_CR ... REG_ASPR:
>> +        ret = s->regs[addr / 4];
>> +        break;
>> +    case REG_REVR:
>> +        ret = 0x00010403;  /* rev. = 1.4.3 */
>> +        break;
>> +    case REG_FEAR:
>> +        ret = 0x00000808;  /* 8x8 scan code for keypad */
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "a369kpd: undefined memory access@0x%llx\n", addr);
>> +        break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static void ftkbc010_mem_write(void    *opaque,
>> +                               hwaddr   addr,
>> +                               uint64_t val,
>> +                               unsigned size)
>> +{
>> +    Ftkbc010State *s = FTKBC010(opaque);
>> +
>> +    switch (addr) {
>> +    case REG_CR:
>> +        KBC_REG32(s, REG_CR) = (uint32_t)val;
>> +        /* if ftkbc010 enabled */
>> +        if (!(KBC_REG32(s, REG_CR) & CR_EN)) {
>> +            break;
>> +        }
>> +        /* if keypad interrupt cleared */
>> +        if (KBC_REG32(s, REG_CR) & CR_KPDIC) {
>> +            KBC_REG32(s, REG_CR) &= ~CR_KPDIC;
>> +            KBC_REG32(s, REG_ISR) &= ~ISR_KPDI;
>> +        }
>> +        /* if rx interrupt cleared */
>> +        if (KBC_REG32(s, REG_CR) & CR_RXICLR) {
>> +            KBC_REG32(s, REG_CR) &= ~CR_RXICLR;
>> +            KBC_REG32(s, REG_ISR) &= ~ISR_RXI;
>> +        }
>> +        /* if tx interrupt cleared */
>> +        if (KBC_REG32(s, REG_CR) & CR_TXICLR) {
>> +            KBC_REG32(s, REG_CR) &= ~CR_TXICLR;
>> +            KBC_REG32(s, REG_ISR) &= ~ISR_TXI;
>> +        }
>> +        ftkbc010_update_irq(s);
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "a369kpd: undefined memory access@0x%llx\n", addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = ftkbc010_mem_read,
>> +    .write = ftkbc010_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    }
>> +};
>> +
>> +static void ftkbc010_key_event(void *opaque, int scancode)
>> +{
>> +    Ftkbc010State *s = FTKBC010(opaque);
>> +    int x, y, released = 0;
>> +
>> +    /* key release from qemu */
>> +    if (scancode & 0x80) {
>> +        released = 1;
>> +    }
>> +
>> +    /* strip qemu key release bit */
>> +    scancode &= ~0x80;
>> +
>> +    /* keypad interrupt */
>> +    if (!released && (KBC_REG32(s, REG_CR) & CR_KPDEN)) {
>> +        switch (scancode) {
>> +        case KEYCODE_ESC:
>> +        case KEYCODE_BACKSPACE:
>> +            x = 1;
>> +            break;
>> +        case KEYCODE_ENTER:
>> +        case KEYCODE_MENU:
>> +        case KEYCODE_SPACE:
>> +            x = 3;
>> +            break;
>> +        default:
>> +            x = 2;    /* KEY_HOME */
>> +            break;
>> +        }
>> +        y = 0;
>> +        KBC_REG32(s, REG_KPDXR) = ~BIT(x);
>> +        KBC_REG32(s, REG_KPDYR) = ~BIT(y);
>> +        KBC_REG32(s, REG_ISR)  |= ISR_KPDI;
>> +        ftkbc010_update_irq(s);
>> +    }
>> +}
>> +
>> +static void ftkbc010_reset(DeviceState *ds)
>> +{
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    Ftkbc010State *s = FTKBC010(FROM_SYSBUS(Ftkbc010State, busdev));
>> +
>> +    memset(s->regs, 0, sizeof(s->regs));
>> +    KBC_REG32(s, REG_KPDXR) = 0xffffffff;
>> +    KBC_REG32(s, REG_KPDYR) = 0xffffffff;
>> +
>> +    qemu_irq_lower(s->irq);
>> +}
>> +
>> +static int ftkbc010_init(SysBusDevice *dev)
>> +{
>> +    Ftkbc010State *s = FTKBC010(FROM_SYSBUS(Ftkbc010State, dev));
>> +
>> +    memory_region_init_io(&s->iomem,
>> +                          &mmio_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_ARRAY(regs, Ftkbc010State, CFG_REGSIZE),
>> +        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..4c779ab
>> --- /dev/null
>> +++ b/hw/arm/faraday_a369_scu.c
>> @@ -0,0 +1,187 @@
>> +/*
>> + * Faraday A369 SCU
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@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 "sysemu/sysemu.h"
>> +
>> +#include "faraday.h"
>> +
>> +#define REG_CHIPID      0x000   /* SoC chip id */
>> +#define REG_REVISON     0x004   /* SCU revision id */
>> +#define REG_HWCFG       0x008   /* HW configuration strap */
>> +#define REG_CPUMFCR     0x00C   /* CPUM (master) freq. control */
>> +#define REG_SCUCR       0x010   /* SCU control register */
>> +#define REG_SCUSR       0x014   /* SCU status register */
>> +#define REG_OSCCR       0x01C   /* OSC control register */
>> +#define REG_PLL1CR      0x020   /* PLL1 control register */
>> +#define REG_DLLCR       0x024   /* DLL control register */
>> +#define REG_SPR(n)      (0x100 + ((n) << 2)) /* Scratchpad register 0 - 15 */
>> +#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_SCER        0x230   /* Special clock enable register */
>> +#define REG_MFPINMUX0   0x238   /* Multi-function pinmux 0 */
>> +#define REG_MFPINMUX1   0x23C   /* Multi-function pinmux 1 */
>> +#define REG_DCSRCR0     0x240   /* Driving cap. & Slew rate control 0 */
>> +#define REG_DCSRCR1     0x244   /* Driving cap. & Slew rate control 1 */
>> +#define REG_DCCR        0x254   /* Delay chain control register */
>> +#define REG_PCR         0x258   /* Power control register */
>> +
>> +#define TYPE_A369SCU    "a369.scu"
>> +#define CFG_REGSIZE     (0x260 / 4)
>> +
>> +typedef struct A369SCUState {
>> +    SysBusDevice busdev;
>> +    MemoryRegion iomem;
>> +
>> +    /* HW registers */
>> +    uint32_t regs[CFG_REGSIZE];
>> +} A369SCUState;
>> +
>> +#define A369SCU(obj) \
>> +    OBJECT_CHECK(A369SCUState, obj, TYPE_A369SCU)
>> +
>> +#define SCU_REG32(s, off) \
>> +    *(uint32_t *)((uint8_t *)(s)->regs + (off))
>> +
>> +static uint64_t
>> +a369scu_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    A369SCUState *s = A369SCU(opaque);
>> +    uint64_t ret = 0;
>> +
>> +    switch (addr) {
>> +    case 0x000 ... 0x25C:
>> +        ret = s->regs[addr / 4];
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "a369scu: undefined memory access@0x%llx\n", addr);
>> +        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:
>> +    case REG_CLKCFG0:
>> +    case REG_CLKCFG1:
>> +    case REG_MFPINMUX0:
>> +    case REG_MFPINMUX1:
>> +        s->regs[addr / 4] = (uint32_t)val;
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "a369scu: undefined memory access@0x%llx\n", addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = a369scu_mem_read,
>> +    .write = a369scu_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    }
>> +};
>> +
>> +static void a369scu_reset(DeviceState *ds)
>> +{
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    A369SCUState *s = A369SCU(FROM_SYSBUS(A369SCUState, busdev));
>> +
>> +    memset(s->regs, 0, sizeof(s->regs));
>> +
>> +    SCU_REG32(s, REG_CHIPID)    = 0x00003369; /* A369 */
>> +    SCU_REG32(s, REG_REVISON)   = 0x00010000; /* Rev. = 1.0.0 */
>> +    SCU_REG32(s, REG_HWCFG)     = 0x00000c10; /* CPU = 4 * HCLK */
>> +    SCU_REG32(s, REG_CPUMFCR)   = 0x00000230; /* CPU = 4 * HCLK */
>> +    SCU_REG32(s, REG_SCUCR)     = 0x00000083; /* no low power detect */
>> +    SCU_REG32(s, REG_SCUSR)     = 0x00000100; /* CPU freq. stable */
>> +    SCU_REG32(s, REG_OSCCR)     = 0x00000003; /* OSCH disabled */
>> +    SCU_REG32(s, REG_PLL1CR)    = 0x20010003; /* PLL_NS = 32 */
>> +    SCU_REG32(s, REG_DLLCR)     = 0x00000003; /* DLL enabled & stable */
>> +    SCU_REG32(s, REG_GPINMUX)   = 0x00001078; /* Pinmux */
>> +    SCU_REG32(s, REG_EXTHWCFG)  = 0x00001cc8; /* NAND flash boot */
>> +    SCU_REG32(s, REG_CLKCFG0)   = 0x26877330; /* LCD = HCLK */
>> +    SCU_REG32(s, REG_CLKCFG1)   = 0x000a0a0a; /* SD = HCLK, SPI=PCLK */
>> +    SCU_REG32(s, REG_SCER)      = 0x00003fff; /* All clock enabled */
>> +    SCU_REG32(s, REG_MFPINMUX0) = 0x00000241; /* Pinmux */
>> +    SCU_REG32(s, REG_MFPINMUX1) = 0x00000000; /* Pinmux */
>> +    SCU_REG32(s, REG_DCSRCR0)   = 0x11111111; /* Slow slew rate */
>> +    SCU_REG32(s, REG_DCSRCR1)   = 0x11111111; /* Slow slew rate */
>> +    SCU_REG32(s, REG_DCCR)      = 0x00000303; /* All delay chain = 3 */
>> +    SCU_REG32(s, REG_PCR)       = 0x8000007f; /* High performance mode */
>> +}
>> +
>> +static int a369scu_init(SysBusDevice *dev)
>> +{
>> +    A369SCUState *s = A369SCU(FROM_SYSBUS(A369SCUState, dev));
>> +
>> +    memory_region_init_io(&s->iomem,
>> +                          &mmio_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_ARRAY(regs, A369SCUState, CFG_REGSIZE),
>> +        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/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
>> new file mode 100644
>> index 0000000..0372868
>> --- /dev/null
>> +++ b/hw/arm/faraday_a369_soc.c
>> @@ -0,0 +1,197 @@
>> +/*
>> + * Faraday A369 SoC
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@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"
>> +
>> +static void a369soc_reset(DeviceState *ds)
>> +{
>> +    int i;
>> +    uint64_t size;
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    FaradaySoCState *s = FARADAY_SOC(FROM_SYSBUS(FaradaySoCState, busdev));
>> +
>> +    /* AHB slave base & window configuration */
>> +    memset(s->ahb_slave, 0, sizeof(s->ahb_slave));
>> +    s->ahb_slave[0] = 0x94050000;
>> +    s->ahb_slave[1] = 0x96040000;
>> +    s->ahb_slave[2] = 0x90f00000;
>> +    s->ahb_slave[3] = 0x92050000; /* APB: base=0x92000000, size=32MB */
>> +    s->ahb_slave[5] = 0xc0080000;
>> +    if (!s->bi) {   /* ROM emulation enabled */
>> +        s->ahb_slave[4] = 0x00080000; /* ROM: base=0x00000000, size=256MB */
>> +        s->ahb_slave[6] = 0x10090000; /* RAM: base=0x10000000, size=512MB */
>> +    } else {        /* Direct boot */
>> +        s->ahb_slave[4] = s->rom_base | 0x80000; /* ROM: size=256MB */
>> +        s->ahb_slave[6] = s->ram_base | 0x90000; /* RAM: size=512MB */
>> +    }
>> +    for (i = 0; i < 15; ++i) {
>> +        s->ahb_slave[7 + i] = 0x90000000 + (i << 20);
>> +    }
>> +    s->ahb_slave[22] = 0x40080000;
>> +    s->ahb_slave[23] = 0x60080000;
>> +    s->ahb_slave[24] = 0xa0000000; /* SRAM: base=0xA0000000, size=1MB */
>> +
>> +    /* APB slave base & window configuration */
>> +    memset(s->apb_slave, 0, sizeof(s->apb_slave));
>> +    for (i = 0; i < 18; ++i) {
>> +        s->apb_slave[i] = 0x12000000 + (i << 20);
>> +    }
>> +
>> +    /* ROM base = salve4 & 0x000fffff, size = 6KB */
>> +    s->rom_base = s->ahb_slave[4] & 0xfff00000;
>> +    s->rom_size = 6 << 10;
>> +
>> +    /* RAM base = salve6 & 0x000fffff, size <= (1 << slave6.BIT[19-16]) MB */
>> +    size = (1 << extract32(s->ahb_slave[6], 16, 4)) << 20;
>> +    s->ram_base = s->ahb_slave[6] & 0xfff00000;
>> +    if (!s->ram_size || s->ram_size > size) {
>> +        s->ram_size = size;
>> +    }
>> +}
>> +
>> +static void
>> +a369soc_device_init(FaradaySoCState *s)
>> +{
>> +    DriveInfo *dinfo;
>> +    DeviceState *ds;
>> +
>> +    s->as = get_system_memory();
>> +    s->ram = g_new(MemoryRegion, 1);
>> +    s->sram = g_new(MemoryRegion, 1);
>> +
>> +    /* CPU */
>> +    if (!s->cpu_model) {
>> +        s->cpu_model = (char *)"fa626te";
>> +    }
>> +    s->cpu = cpu_arm_init(s->cpu_model);
>> +    if (!s->cpu) {
>> +        hw_error("a369: Unable to find CPU definition\n");
>> +        exit(1);
>> +    }
>> +
>> +    /* RAM Init */
>> +    memory_region_init_ram(s->ram, "a369.ram", s->ram_size);
>> +    vmstate_register_ram_global(s->ram);
>> +
>> +    /* 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);
>> +
>> +    /* Embedded ROM Init (Emulated with a parallel NOR flash) */
>> +    dinfo = drive_get_next(IF_PFLASH);
>> +    s->rom = pflash_cfi01_register(
>> +                    s->rom_base,
>> +                    NULL,
>> +                    "a369.rom",
>> +                    s->rom_size,
>> +                    dinfo ? dinfo->bdrv : NULL,
>> +                    1024,               /* 1 KB sector */
>> +                    s->rom_size >> 10,  /* sectors per chip */
>> +                    4,                  /* 32 bits */
>> +                    0, 0, 0, 0,         /* id */
>> +                    0                   /* Little Endian */);
>> +    if (!s->rom) {
>> +        hw_error("a369soc: failed to init ROM device.\n");
>> +        exit(1);
>> +    }
>> +
>> +    /* Serial (FTUART010 which is 16550A compatible) */
>> +    if (serial_hds[0]) {
>> +        serial_mm_init(s->as,
>> +                       0x92b00000,
>> +                       2,
>> +                       NULL,
>> +                       18432000,
>> +                       serial_hds[0],
>> +                       DEVICE_LITTLE_ENDIAN);
>> +    }
>> +    if (serial_hds[1]) {
>> +        serial_mm_init(s->as,
>> +                       0x92c00000,
>> +                       2,
>> +                       NULL,
>> +                       18432000,
>> +                       serial_hds[1],
>> +                       DEVICE_LITTLE_ENDIAN);
>> +    }
>> +
>> +    /* ftscu010 */
>> +    ds = sysbus_create_simple("a369.scu", 0x92000000, NULL);
>> +    s->scu = ds;
>> +
>> +    /* ftkbc010 */
>> +    sysbus_create_simple("a369.keypad", 0x92f00000, NULL);
>> +}
>> +
>> +static int a369soc_init(SysBusDevice *busdev)
>> +{
>> +    FaradaySoCState *s = FARADAY_SOC(FROM_SYSBUS(FaradaySoCState, busdev));
>> +
>> +    a369soc_reset(DEVICE(busdev));
>> +    a369soc_device_init(s);
>> +
>> +    return 0;
>> +}
>> +
>> +static Property a369soc_properties[] = {
>> +    DEFINE_PROP_STRING("cpu_model", FaradaySoCState, cpu_model),
>> +    DEFINE_PROP_UINT64("ram_size", FaradaySoCState, ram_size, 0x20000000),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static const VMStateDescription vmstate_a369soc = {
>> +    .name = TYPE_FARADAY_SOC,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_END_OF_LIST(),
>> +    }
>> +};
>> +
>> +static void a369soc_class_init(ObjectClass *klass, void *data)
>> +{
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    k->init   = a369soc_init;
>> +    dc->desc  = TYPE_FARADAY_SOC;
>> +    dc->vmsd  = &vmstate_a369soc;
>> +    dc->props = a369soc_properties;
>> +    dc->reset = a369soc_reset;
>> +    dc->no_user = 1;
>> +}
>> +
>> +static const TypeInfo a369soc_info = {
>> +    .name          = TYPE_FARADAY_SOC,
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(FaradaySoCState),
>> +    .class_init    = a369soc_class_init,
>> +};
>> +
>> +static void a369soc_register_types(void)
>> +{
>> +    type_register_static(&a369soc_info);
>> +}
>> +
>> +type_init(a369soc_register_types)
>> diff --git a/hw/arm/ftkbc010.h b/hw/arm/ftkbc010.h
>> new file mode 100644
>> index 0000000..48e39e1
>> --- /dev/null
>> +++ b/hw/arm/ftkbc010.h
>> @@ -0,0 +1,42 @@
>> +/*
>> + * Faraday FTKBC010 Keyboard/Keypad Controller
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@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 */
>> +
>> +#define CR_KPDIC    BIT(10) /* Write 1 to clear Keypad interupt */
>> +#define CR_KPDAS    BIT(9)  /* Keypad audo-scan enabled */
>> +#define CR_KPDEN    BIT(8)  /* Keypad function enabled */
>> +#define CR_RXICLR   BIT(7)  /* Write 1 to clear Keyboard/Mouse Rx interrupt */
>> +#define CR_TXICLR   BIT(6)  /* Write 1 to clear Keyboard/Mouse Tx interrupt */
>> +#define CR_NOLC     BIT(5)  /* No line control bit */
>> +#define CR_RXIEN    BIT(4)  /* Keyboard/Mouse Rx interrupt enabled */
>> +#define CR_TXIEN    BIT(3)  /* Keyboard/Mouse Tx interrupt enabled */
>> +#define CR_EN       BIT(2)  /* Chip enabled */
>> +#define CR_DATDN    BIT(1)  /* Data disabled */
>> +#define CR_CLKDN    BIT(0)  /* Clock disabled */
>> +
>> +#define ISR_KPDI    BIT(2)  /* Keypad interupt */
>> +#define ISR_TXI     BIT(1)  /* Keyboard/Mouse Tx interrupt enabled */
>> +#define ISR_RXI     BIT(0)  /* Keyboard/Mouse Rx interrupt enabled */
>> +
>> +#endif
>> --
>> 1.7.9.5
>>
>>



--
Best wishes,
Kuo-Jung Su

Patch

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 6d049e7..f6fd60d 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -33,3 +33,7 @@  obj-y += kzm.o
 obj-$(CONFIG_FDT) += ../device_tree.o
 
 obj-y := $(addprefix ../,$(obj-y))
+obj-y += faraday_a369.o \
+            faraday_a369_soc.o \
+            faraday_a369_scu.o \
+            faraday_a369_kpd.o
diff --git a/hw/arm/faraday.h b/hw/arm/faraday.h
new file mode 100644
index 0000000..d6ed860
--- /dev/null
+++ b/hw/arm/faraday.h
@@ -0,0 +1,65 @@ 
+/*
+ * Faraday SoC platform support.
+ *
+ * Copyright (c) 2013 Faraday Technology
+ * Written by Kuo-Jung Su <dantesu@gmail.com>
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+#ifndef HW_ARM_FARADAY_H
+#define HW_ARM_FARADAY_H
+
+#include "hw/flash.h"
+#include "qemu/bitops.h"
+
+#ifdef DEBUG_FARADAY
+#define DPRINTF(fmt, ...) \
+    do { printf("faraday: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+    do { } while (0)
+#endif
+
+typedef struct FaradaySoCState {
+    SysBusDevice busdev;
+    hwaddr       rom_base;
+    uint64_t     rom_size;
+    hwaddr       ram_base;
+    uint64_t     ram_size;
+    char         *cpu_model;
+    ARMCPU       *cpu;
+    DeviceState  *scu;      /* System Control Unit */
+    DeviceState  *ahbc;     /* AHB controller */
+    DeviceState  *ddrc;     /* DDR controller */
+    DeviceState  *hdma[2];  /* AHB DMA */
+    DeviceState  *pdma[1];  /* APB DMA */
+    DeviceState  *spi[2];
+    DeviceState  *i2c[2];
+    DeviceState  *i2s[2];
+    DeviceState  *codec;    /* Audio codec */
+    void (*codec_out)(void *, uint32_t);
+    uint32_t (*codec_in)(void *);
+
+    MemoryRegion *as;
+    MemoryRegion *ram;
+    pflash_t     *rom;
+    MemoryRegion *sram;
+
+    void         *priv;
+
+    uint32_t ahb_slave[32];
+    uint32_t apb_slave[32];
+    bool     ahb_remapped;
+    bool     ddr_inited;
+    struct arm_boot_info *bi;
+} FaradaySoCState;
+
+/* SoC common APIs */
+#define TYPE_FARADAY_SOC    "faraday/soc"
+#define FARADAY_SOC(obj) \
+    OBJECT_CHECK(FaradaySoCState, obj, TYPE_FARADAY_SOC)
+#define FARADAY_SOC_GET_CORE() \
+    FARADAY_SOC(object_resolve_path_component(qdev_get_machine(), \
+                                              TYPE_FARADAY_SOC))
+
+#endif
diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c
new file mode 100644
index 0000000..0b6201a
--- /dev/null
+++ b/hw/arm/faraday_a369.c
@@ -0,0 +1,94 @@ 
+/*
+ * Faraday A369 Evalution Board
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@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/ssi.h"
+#include "sysemu/sysemu.h"
+
+#include "faraday.h"
+
+/* Board init.  */
+
+static void
+a369_board_init(QEMUMachineInitArgs *args)
+{
+    DeviceState *ds;
+    FaradaySoCState *s;
+
+    if (!args->cpu_model) {
+        args->cpu_model = "fa626te";
+    }
+    if (!args->ram_size) {
+        args->ram_size = 512 << 20;
+    }
+
+    ds = qdev_create(NULL, TYPE_FARADAY_SOC);
+    qdev_prop_set_string(ds, "cpu_model", args->cpu_model);
+    qdev_prop_set_uint64(ds, "ram_size", args->ram_size);
+    /* Setup QOM path for the SoC object (i.e. /machine/faraday/soc) */
+    object_property_add_child(qdev_get_machine(),
+                              TYPE_FARADAY_SOC,
+                              OBJECT(ds),
+                              NULL);
+    qdev_init_nofail(ds);
+
+    s = FARADAY_SOC(ds);
+
+    if (args->kernel_filename) {
+        s->bi = g_new0(struct arm_boot_info, 1);
+
+        s->ddr_inited = true;
+        s->ahb_remapped = true;
+
+        /* Remap AHB slave 4 (ROM) & slave 6 (RAM) */
+        /* 1. Remap RAM to base of ROM */
+        s->ram_base = s->ahb_slave[4] & 0xfff00000;
+        s->ahb_slave[6] = s->ram_base | (s->ahb_slave[6] & 0x000f0000);
+        /* 2. Remap ROM to base of ROM + size of RAM */
+        s->rom_base = s->ram_base
+                    + ((1 << extract32(s->ahb_slave[6], 16, 4)) << 20);
+        s->ahb_slave[4] = s->rom_base | (s->ahb_slave[4] & 0x000f0000);
+
+        /* 3. Update ROM Address */
+        sysbus_mmio_map(SYS_BUS_DEVICE(s->rom), 0, s->rom_base);
+
+        /* 4. RAM Address Binding */
+        memory_region_add_subregion(s->as, s->ram_base, s->ram);
+
+        /* 5. Boot Info */
+        s->bi->ram_size = s->ram_size;
+        s->bi->kernel_filename = args->kernel_filename;
+        s->bi->kernel_cmdline = args->kernel_cmdline;
+        s->bi->initrd_filename = args->initrd_filename;
+        s->bi->board_id = 0x3369;
+        arm_load_kernel(s->cpu, s->bi);
+    } else if (!drive_get(IF_PFLASH, 0, 0)) {
+        hw_error("a369: failed to load ROM image!\n");
+        exit(1);
+    }
+}
+
+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_kpd.c b/hw/arm/faraday_a369_kpd.c
new file mode 100644
index 0000000..967ada6
--- /dev/null
+++ b/hw/arm/faraday_a369_kpd.c
@@ -0,0 +1,237 @@ 
+/*
+ * Faraday FTKBC010 emulator for A369.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@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 "sysemu/sysemu.h"
+
+#include "faraday.h"
+#include "ftkbc010.h"
+
+#define CFG_REGSIZE     (0x3c / 4)
+
+/* 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;
+
+    /* HW registers */
+    uint32_t regs[CFG_REGSIZE];
+} Ftkbc010State;
+
+#define FTKBC010(obj) \
+    OBJECT_CHECK(Ftkbc010State, obj, TYPE_FTKBC010)
+
+#define KBC_REG32(s, off) \
+    *(uint32_t *)((uint8_t *)(s)->regs + (off))
+
+static void ftkbc010_update_irq(Ftkbc010State *s)
+{
+    uint32_t ier = 0;
+
+    /* keypad interrupt */
+    ier |= (KBC_REG32(s, REG_CR) & CR_KPDEN) ? ISR_KPDI : 0;
+    /* tx interrupt */
+    ier |= (KBC_REG32(s, REG_CR) & CR_TXIEN) ? ISR_TXI : 0;
+    /* rx interrupt */
+    ier |= (KBC_REG32(s, REG_CR) & CR_RXIEN) ? ISR_RXI : 0;
+
+    qemu_set_irq(s->irq, (ier & KBC_REG32(s, REG_ISR)) ? 1 : 0);
+}
+
+static uint64_t ftkbc010_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftkbc010State *s = FTKBC010(opaque);
+    uint64_t ret = 0;
+
+    switch (addr) {
+    case REG_CR ... REG_ASPR:
+        ret = s->regs[addr / 4];
+        break;
+    case REG_REVR:
+        ret = 0x00010403;  /* rev. = 1.4.3 */
+        break;
+    case REG_FEAR:
+        ret = 0x00000808;  /* 8x8 scan code for keypad */
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "a369kpd: undefined memory access@0x%llx\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void ftkbc010_mem_write(void    *opaque,
+                               hwaddr   addr,
+                               uint64_t val,
+                               unsigned size)
+{
+    Ftkbc010State *s = FTKBC010(opaque);
+
+    switch (addr) {
+    case REG_CR:
+        KBC_REG32(s, REG_CR) = (uint32_t)val;
+        /* if ftkbc010 enabled */
+        if (!(KBC_REG32(s, REG_CR) & CR_EN)) {
+            break;
+        }
+        /* if keypad interrupt cleared */
+        if (KBC_REG32(s, REG_CR) & CR_KPDIC) {
+            KBC_REG32(s, REG_CR) &= ~CR_KPDIC;
+            KBC_REG32(s, REG_ISR) &= ~ISR_KPDI;
+        }
+        /* if rx interrupt cleared */
+        if (KBC_REG32(s, REG_CR) & CR_RXICLR) {
+            KBC_REG32(s, REG_CR) &= ~CR_RXICLR;
+            KBC_REG32(s, REG_ISR) &= ~ISR_RXI;
+        }
+        /* if tx interrupt cleared */
+        if (KBC_REG32(s, REG_CR) & CR_TXICLR) {
+            KBC_REG32(s, REG_CR) &= ~CR_TXICLR;
+            KBC_REG32(s, REG_ISR) &= ~ISR_TXI;
+        }
+        ftkbc010_update_irq(s);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "a369kpd: undefined memory access@0x%llx\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftkbc010_mem_read,
+    .write = ftkbc010_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void ftkbc010_key_event(void *opaque, int scancode)
+{
+    Ftkbc010State *s = FTKBC010(opaque);
+    int x, y, released = 0;
+
+    /* key release from qemu */
+    if (scancode & 0x80) {
+        released = 1;
+    }
+
+    /* strip qemu key release bit */
+    scancode &= ~0x80;
+
+    /* keypad interrupt */
+    if (!released && (KBC_REG32(s, REG_CR) & CR_KPDEN)) {
+        switch (scancode) {
+        case KEYCODE_ESC:
+        case KEYCODE_BACKSPACE:
+            x = 1;
+            break;
+        case KEYCODE_ENTER:
+        case KEYCODE_MENU:
+        case KEYCODE_SPACE:
+            x = 3;
+            break;
+        default:
+            x = 2;    /* KEY_HOME */
+            break;
+        }
+        y = 0;
+        KBC_REG32(s, REG_KPDXR) = ~BIT(x);
+        KBC_REG32(s, REG_KPDYR) = ~BIT(y);
+        KBC_REG32(s, REG_ISR)  |= ISR_KPDI;
+        ftkbc010_update_irq(s);
+    }
+}
+
+static void ftkbc010_reset(DeviceState *ds)
+{
+    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
+    Ftkbc010State *s = FTKBC010(FROM_SYSBUS(Ftkbc010State, busdev));
+
+    memset(s->regs, 0, sizeof(s->regs));
+    KBC_REG32(s, REG_KPDXR) = 0xffffffff;
+    KBC_REG32(s, REG_KPDYR) = 0xffffffff;
+
+    qemu_irq_lower(s->irq);
+}
+
+static int ftkbc010_init(SysBusDevice *dev)
+{
+    Ftkbc010State *s = FTKBC010(FROM_SYSBUS(Ftkbc010State, dev));
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_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_ARRAY(regs, Ftkbc010State, CFG_REGSIZE),
+        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..4c779ab
--- /dev/null
+++ b/hw/arm/faraday_a369_scu.c
@@ -0,0 +1,187 @@ 
+/*
+ * Faraday A369 SCU
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@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 "sysemu/sysemu.h"
+
+#include "faraday.h"
+
+#define REG_CHIPID      0x000   /* SoC chip id */
+#define REG_REVISON     0x004   /* SCU revision id */
+#define REG_HWCFG       0x008   /* HW configuration strap */
+#define REG_CPUMFCR     0x00C   /* CPUM (master) freq. control */
+#define REG_SCUCR       0x010   /* SCU control register */
+#define REG_SCUSR       0x014   /* SCU status register */
+#define REG_OSCCR       0x01C   /* OSC control register */
+#define REG_PLL1CR      0x020   /* PLL1 control register */
+#define REG_DLLCR       0x024   /* DLL control register */
+#define REG_SPR(n)      (0x100 + ((n) << 2)) /* Scratchpad register 0 - 15 */
+#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_SCER        0x230   /* Special clock enable register */
+#define REG_MFPINMUX0   0x238   /* Multi-function pinmux 0 */
+#define REG_MFPINMUX1   0x23C   /* Multi-function pinmux 1 */
+#define REG_DCSRCR0     0x240   /* Driving cap. & Slew rate control 0 */
+#define REG_DCSRCR1     0x244   /* Driving cap. & Slew rate control 1 */
+#define REG_DCCR        0x254   /* Delay chain control register */
+#define REG_PCR         0x258   /* Power control register */
+
+#define TYPE_A369SCU    "a369.scu"
+#define CFG_REGSIZE     (0x260 / 4)
+
+typedef struct A369SCUState {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+
+    /* HW registers */
+    uint32_t regs[CFG_REGSIZE];
+} A369SCUState;
+
+#define A369SCU(obj) \
+    OBJECT_CHECK(A369SCUState, obj, TYPE_A369SCU)
+
+#define SCU_REG32(s, off) \
+    *(uint32_t *)((uint8_t *)(s)->regs + (off))
+
+static uint64_t
+a369scu_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    A369SCUState *s = A369SCU(opaque);
+    uint64_t ret = 0;
+
+    switch (addr) {
+    case 0x000 ... 0x25C:
+        ret = s->regs[addr / 4];
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "a369scu: undefined memory access@0x%llx\n", addr);
+        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:
+    case REG_CLKCFG0:
+    case REG_CLKCFG1:
+    case REG_MFPINMUX0:
+    case REG_MFPINMUX1:
+        s->regs[addr / 4] = (uint32_t)val;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "a369scu: undefined memory access@0x%llx\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = a369scu_mem_read,
+    .write = a369scu_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void a369scu_reset(DeviceState *ds)
+{
+    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
+    A369SCUState *s = A369SCU(FROM_SYSBUS(A369SCUState, busdev));
+
+    memset(s->regs, 0, sizeof(s->regs));
+
+    SCU_REG32(s, REG_CHIPID)    = 0x00003369; /* A369 */
+    SCU_REG32(s, REG_REVISON)   = 0x00010000; /* Rev. = 1.0.0 */
+    SCU_REG32(s, REG_HWCFG)     = 0x00000c10; /* CPU = 4 * HCLK */
+    SCU_REG32(s, REG_CPUMFCR)   = 0x00000230; /* CPU = 4 * HCLK */
+    SCU_REG32(s, REG_SCUCR)     = 0x00000083; /* no low power detect */
+    SCU_REG32(s, REG_SCUSR)     = 0x00000100; /* CPU freq. stable */
+    SCU_REG32(s, REG_OSCCR)     = 0x00000003; /* OSCH disabled */
+    SCU_REG32(s, REG_PLL1CR)    = 0x20010003; /* PLL_NS = 32 */
+    SCU_REG32(s, REG_DLLCR)     = 0x00000003; /* DLL enabled & stable */
+    SCU_REG32(s, REG_GPINMUX)   = 0x00001078; /* Pinmux */
+    SCU_REG32(s, REG_EXTHWCFG)  = 0x00001cc8; /* NAND flash boot */
+    SCU_REG32(s, REG_CLKCFG0)   = 0x26877330; /* LCD = HCLK */
+    SCU_REG32(s, REG_CLKCFG1)   = 0x000a0a0a; /* SD = HCLK, SPI=PCLK */
+    SCU_REG32(s, REG_SCER)      = 0x00003fff; /* All clock enabled */
+    SCU_REG32(s, REG_MFPINMUX0) = 0x00000241; /* Pinmux */
+    SCU_REG32(s, REG_MFPINMUX1) = 0x00000000; /* Pinmux */
+    SCU_REG32(s, REG_DCSRCR0)   = 0x11111111; /* Slow slew rate */
+    SCU_REG32(s, REG_DCSRCR1)   = 0x11111111; /* Slow slew rate */
+    SCU_REG32(s, REG_DCCR)      = 0x00000303; /* All delay chain = 3 */
+    SCU_REG32(s, REG_PCR)       = 0x8000007f; /* High performance mode */
+}
+
+static int a369scu_init(SysBusDevice *dev)
+{
+    A369SCUState *s = A369SCU(FROM_SYSBUS(A369SCUState, dev));
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_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_ARRAY(regs, A369SCUState, CFG_REGSIZE),
+        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/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
new file mode 100644
index 0000000..0372868
--- /dev/null
+++ b/hw/arm/faraday_a369_soc.c
@@ -0,0 +1,197 @@ 
+/*
+ * Faraday A369 SoC
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@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"
+
+static void a369soc_reset(DeviceState *ds)
+{
+    int i;
+    uint64_t size;
+    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
+    FaradaySoCState *s = FARADAY_SOC(FROM_SYSBUS(FaradaySoCState, busdev));
+
+    /* AHB slave base & window configuration */
+    memset(s->ahb_slave, 0, sizeof(s->ahb_slave));
+    s->ahb_slave[0] = 0x94050000;
+    s->ahb_slave[1] = 0x96040000;
+    s->ahb_slave[2] = 0x90f00000;
+    s->ahb_slave[3] = 0x92050000; /* APB: base=0x92000000, size=32MB */
+    s->ahb_slave[5] = 0xc0080000;
+    if (!s->bi) {   /* ROM emulation enabled */
+        s->ahb_slave[4] = 0x00080000; /* ROM: base=0x00000000, size=256MB */
+        s->ahb_slave[6] = 0x10090000; /* RAM: base=0x10000000, size=512MB */
+    } else {        /* Direct boot */
+        s->ahb_slave[4] = s->rom_base | 0x80000; /* ROM: size=256MB */
+        s->ahb_slave[6] = s->ram_base | 0x90000; /* RAM: size=512MB */
+    }
+    for (i = 0; i < 15; ++i) {
+        s->ahb_slave[7 + i] = 0x90000000 + (i << 20);
+    }
+    s->ahb_slave[22] = 0x40080000;
+    s->ahb_slave[23] = 0x60080000;
+    s->ahb_slave[24] = 0xa0000000; /* SRAM: base=0xA0000000, size=1MB */
+
+    /* APB slave base & window configuration */
+    memset(s->apb_slave, 0, sizeof(s->apb_slave));
+    for (i = 0; i < 18; ++i) {
+        s->apb_slave[i] = 0x12000000 + (i << 20);
+    }
+
+    /* ROM base = salve4 & 0x000fffff, size = 6KB */
+    s->rom_base = s->ahb_slave[4] & 0xfff00000;
+    s->rom_size = 6 << 10;
+
+    /* RAM base = salve6 & 0x000fffff, size <= (1 << slave6.BIT[19-16]) MB */
+    size = (1 << extract32(s->ahb_slave[6], 16, 4)) << 20;
+    s->ram_base = s->ahb_slave[6] & 0xfff00000;
+    if (!s->ram_size || s->ram_size > size) {
+        s->ram_size = size;
+    }
+}
+
+static void
+a369soc_device_init(FaradaySoCState *s)
+{
+    DriveInfo *dinfo;
+    DeviceState *ds;
+
+    s->as = get_system_memory();
+    s->ram = g_new(MemoryRegion, 1);
+    s->sram = g_new(MemoryRegion, 1);
+
+    /* CPU */
+    if (!s->cpu_model) {
+        s->cpu_model = (char *)"fa626te";
+    }
+    s->cpu = cpu_arm_init(s->cpu_model);
+    if (!s->cpu) {
+        hw_error("a369: Unable to find CPU definition\n");
+        exit(1);
+    }
+
+    /* RAM Init */
+    memory_region_init_ram(s->ram, "a369.ram", s->ram_size);
+    vmstate_register_ram_global(s->ram);
+
+    /* 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);
+
+    /* Embedded ROM Init (Emulated with a parallel NOR flash) */
+    dinfo = drive_get_next(IF_PFLASH);
+    s->rom = pflash_cfi01_register(
+                    s->rom_base,
+                    NULL,
+                    "a369.rom",
+                    s->rom_size,
+                    dinfo ? dinfo->bdrv : NULL,
+                    1024,               /* 1 KB sector */
+                    s->rom_size >> 10,  /* sectors per chip */
+                    4,                  /* 32 bits */
+                    0, 0, 0, 0,         /* id */
+                    0                   /* Little Endian */);
+    if (!s->rom) {
+        hw_error("a369soc: failed to init ROM device.\n");
+        exit(1);
+    }
+
+    /* Serial (FTUART010 which is 16550A compatible) */
+    if (serial_hds[0]) {
+        serial_mm_init(s->as,
+                       0x92b00000,
+                       2,
+                       NULL,
+                       18432000,
+                       serial_hds[0],
+                       DEVICE_LITTLE_ENDIAN);
+    }
+    if (serial_hds[1]) {
+        serial_mm_init(s->as,
+                       0x92c00000,
+                       2,
+                       NULL,
+                       18432000,
+                       serial_hds[1],
+                       DEVICE_LITTLE_ENDIAN);
+    }
+
+    /* ftscu010 */
+    ds = sysbus_create_simple("a369.scu", 0x92000000, NULL);
+    s->scu = ds;
+
+    /* ftkbc010 */
+    sysbus_create_simple("a369.keypad", 0x92f00000, NULL);
+}
+
+static int a369soc_init(SysBusDevice *busdev)
+{
+    FaradaySoCState *s = FARADAY_SOC(FROM_SYSBUS(FaradaySoCState, busdev));
+
+    a369soc_reset(DEVICE(busdev));
+    a369soc_device_init(s);
+
+    return 0;
+}
+
+static Property a369soc_properties[] = {
+    DEFINE_PROP_STRING("cpu_model", FaradaySoCState, cpu_model),
+    DEFINE_PROP_UINT64("ram_size", FaradaySoCState, ram_size, 0x20000000),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_a369soc = {
+    .name = TYPE_FARADAY_SOC,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static void a369soc_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init   = a369soc_init;
+    dc->desc  = TYPE_FARADAY_SOC;
+    dc->vmsd  = &vmstate_a369soc;
+    dc->props = a369soc_properties;
+    dc->reset = a369soc_reset;
+    dc->no_user = 1;
+}
+
+static const TypeInfo a369soc_info = {
+    .name          = TYPE_FARADAY_SOC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(FaradaySoCState),
+    .class_init    = a369soc_class_init,
+};
+
+static void a369soc_register_types(void)
+{
+    type_register_static(&a369soc_info);
+}
+
+type_init(a369soc_register_types)
diff --git a/hw/arm/ftkbc010.h b/hw/arm/ftkbc010.h
new file mode 100644
index 0000000..48e39e1
--- /dev/null
+++ b/hw/arm/ftkbc010.h
@@ -0,0 +1,42 @@ 
+/*
+ * Faraday FTKBC010 Keyboard/Keypad Controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@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 */
+
+#define CR_KPDIC    BIT(10) /* Write 1 to clear Keypad interupt */
+#define CR_KPDAS    BIT(9)  /* Keypad audo-scan enabled */
+#define CR_KPDEN    BIT(8)  /* Keypad function enabled */
+#define CR_RXICLR   BIT(7)  /* Write 1 to clear Keyboard/Mouse Rx interrupt */
+#define CR_TXICLR   BIT(6)  /* Write 1 to clear Keyboard/Mouse Tx interrupt */
+#define CR_NOLC     BIT(5)  /* No line control bit */
+#define CR_RXIEN    BIT(4)  /* Keyboard/Mouse Rx interrupt enabled */
+#define CR_TXIEN    BIT(3)  /* Keyboard/Mouse Tx interrupt enabled */
+#define CR_EN       BIT(2)  /* Chip enabled */
+#define CR_DATDN    BIT(1)  /* Data disabled */
+#define CR_CLKDN    BIT(0)  /* Clock disabled */
+
+#define ISR_KPDI    BIT(2)  /* Keypad interupt */
+#define ISR_TXI     BIT(1)  /* Keyboard/Mouse Tx interrupt enabled */
+#define ISR_RXI     BIT(0)  /* Keyboard/Mouse Rx interrupt enabled */
+
+#endif