All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [Qemu-devel] [PATCH v5 02/24] hw/arm: add Faraday a369 SoC platform support
       [not found]   ` <5130F9D8.3030200@gmail.com>
@ 2013-03-01 19:00     ` Igor Mitsyanko
  2013-03-04  6:06       ` Kuo-Jung Su
  0 siblings, 1 reply; 15+ messages in thread
From: Igor Mitsyanko @ 2013-03-01 19:00 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, qemu-devel, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

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

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 02/24] hw/arm: add Faraday a369 SoC platform support
       [not found] ` <1361949350-22241-3-git-send-email-dantesu@gmail.com>
       [not found]   ` <5130F9D8.3030200@gmail.com>
@ 2013-03-02  3:43   ` Peter Crosthwaite
  2013-03-04  6:09     ` Kuo-Jung Su
  1 sibling, 1 reply; 15+ messages in thread
From: Peter Crosthwaite @ 2013-03-02  3:43 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, i.mitsyanko, qemu-devel, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Andreas, fred.konrad

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
>
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 03/24] hw/arm: add Faraday FTINTC020 interrupt controller support
       [not found] ` <1361949350-22241-4-git-send-email-dantesu@gmail.com>
@ 2013-03-02  4:13   ` Peter Crosthwaite
  2013-03-03  4:58     ` Peter Crosthwaite
                       ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Peter Crosthwaite @ 2013-03-02  4:13 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, i.mitsyanko, qemu-devel, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Andreas, fred.konrad

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 FTINTC020 interrupt controller supports both FIQ and IRQ signals
> to the microprocessor.
> It can handle up to 64 configurable IRQ sources and 64 FIQ sources.
> The output signals to the microprocessor can be configured as
> level-high/low active or edge-rising/falling triggered.
>
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> ---
>  hw/arm/Makefile.objs      |    1 +
>  hw/arm/faraday.h          |    3 +
>  hw/arm/faraday_a369_soc.c |   10 +-
>  hw/arm/ftintc020.c        |  366 +++++++++++++++++++++++++++++++++++++++++++++
>  hw/arm/ftintc020.h        |   48 ++++++
>  5 files changed, 425 insertions(+), 3 deletions(-)
>  create mode 100644 hw/arm/ftintc020.c
>  create mode 100644 hw/arm/ftintc020.h
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index f6fd60d..6771072 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -37,3 +37,4 @@ obj-y += faraday_a369.o \
>              faraday_a369_soc.o \
>              faraday_a369_scu.o \
>              faraday_a369_kpd.o
> +obj-y += ftintc020.o
> diff --git a/hw/arm/faraday.h b/hw/arm/faraday.h
> index d6ed860..e5f611d 100644
> --- a/hw/arm/faraday.h
> +++ b/hw/arm/faraday.h
> @@ -62,4 +62,7 @@ typedef struct FaradaySoCState {
>      FARADAY_SOC(object_resolve_path_component(qdev_get_machine(), \
>                                                TYPE_FARADAY_SOC))
>
> +/* ftintc020.c */
> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu);
> +
>  #endif
> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
> index 0372868..3d861d2 100644
> --- a/hw/arm/faraday_a369_soc.c
> +++ b/hw/arm/faraday_a369_soc.c
> @@ -73,6 +73,7 @@ a369soc_device_init(FaradaySoCState *s)
>  {
>      DriveInfo *dinfo;
>      DeviceState *ds;
> +    qemu_irq *pic;
>
>      s->as = get_system_memory();
>      s->ram = g_new(MemoryRegion, 1);
> @@ -115,12 +116,15 @@ a369soc_device_init(FaradaySoCState *s)
>          exit(1);
>      }
>
> +    /* Interrupt Controller */
> +    pic = ftintc020_init(0x90100000, s->cpu);
> +
>      /* Serial (FTUART010 which is 16550A compatible) */
>      if (serial_hds[0]) {
>          serial_mm_init(s->as,
>                         0x92b00000,
>                         2,
> -                       NULL,
> +                       pic[53],
>                         18432000,
>                         serial_hds[0],
>                         DEVICE_LITTLE_ENDIAN);
> @@ -129,7 +133,7 @@ a369soc_device_init(FaradaySoCState *s)
>          serial_mm_init(s->as,
>                         0x92c00000,
>                         2,
> -                       NULL,
> +                       pic[54],
>                         18432000,
>                         serial_hds[1],
>                         DEVICE_LITTLE_ENDIAN);
> @@ -140,7 +144,7 @@ a369soc_device_init(FaradaySoCState *s)
>      s->scu = ds;
>
>      /* ftkbc010 */
> -    sysbus_create_simple("a369.keypad", 0x92f00000, NULL);
> +    sysbus_create_simple("a369.keypad", 0x92f00000, pic[21]);
>  }
>
>  static int a369soc_init(SysBusDevice *busdev)
> diff --git a/hw/arm/ftintc020.c b/hw/arm/ftintc020.c
> new file mode 100644
> index 0000000..a7f6454
> --- /dev/null
> +++ b/hw/arm/ftintc020.c
> @@ -0,0 +1,366 @@
> +/*
> + * Faraday FTINTC020 Programmable Interrupt Controller.
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * This code is licensed under GNU GPL v2+.
> + */
> +
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +
> +#include "faraday.h"
> +#include "ftintc020.h"
> +
> +#define TYPE_FTINTC020  "ftintc020"
> +
> +typedef struct Ftintc020State {
> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +    ARMCPU *cpu;
> +    qemu_irq irqs[64];
> +
> +    uint32_t irq_pin[2];    /* IRQ pin state */
> +    uint32_t fiq_pin[2];    /* IRQ pin state */
> +
> +    /* HW register caches */
> +    uint32_t irq_src[2];    /* IRQ source register */
> +    uint32_t irq_ena[2];    /* IRQ enable register */
> +    uint32_t irq_mod[2];    /* IRQ mode register */
> +    uint32_t irq_lvl[2];    /* IRQ level register */
> +    uint32_t fiq_src[2];    /* FIQ source register */
> +    uint32_t fiq_ena[2];    /* FIQ enable register */
> +    uint32_t fiq_mod[2];    /* FIQ mode register */
> +    uint32_t fiq_lvl[2];    /* FIQ level register */
> +} Ftintc020State;
> +
> +#define FTINTC020(obj) \
> +    OBJECT_CHECK(Ftintc020State, obj, TYPE_FTINTC020)
> +
> +static void
> +ftintc020_update(Ftintc020State *s)
> +{
> +    uint32_t mask[2];
> +
> +    /* FIQ */
> +    mask[0] = s->fiq_src[0] & s->fiq_ena[0];
> +    mask[1] = s->fiq_src[1] & s->fiq_ena[1];
> +
> +    if (mask[0] || mask[1]) {
> +        cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
> +    } else {
> +        cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
> +    }
> +
> +    /* IRQ */
> +    mask[0] = s->irq_src[0] & s->irq_ena[0];
> +    mask[1] = s->irq_src[1] & s->irq_ena[1];
> +
> +    if (mask[0] || mask[1]) {
> +        cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
> +    } else {
> +        cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
> +    }
> +}
> +
> +/* Note: Here level means state of the signal on a pin */
> +static void
> +ftintc020_set_irq(void *opaque, int irq, int level)
> +{
> +    Ftintc020State *s = FTINTC020(opaque);
> +    uint32_t i = irq / 32;
> +    uint32_t mask = 1 << (irq % 32);
> +
> +    if (s->irq_mod[i] & mask) {
> +        /* Edge Triggered */
> +        if (s->irq_lvl[i] & mask) {
> +            /* Falling Active */
> +            if ((s->irq_pin[i] & mask) && !level) {
> +                s->irq_src[i] |=  mask;
> +                s->fiq_src[i] |=  mask;
> +            }
> +        } else {
> +            /* Rising Active */
> +            if (!(s->irq_pin[i] & mask) && level) {
> +                s->irq_src[i] |=  mask;
> +                s->fiq_src[i] |=  mask;
> +            }
> +        }
> +    } else {
> +        /* Level Triggered */
> +        if (s->irq_lvl[i] & mask) {
> +            /* Low Active */
> +            if (level) {
> +                s->irq_src[i] &= ~mask;
> +                s->fiq_src[i] &= ~mask;
> +            } else {
> +                s->irq_src[i] |=  mask;
> +                s->fiq_src[i] |=  mask;
> +            }
> +        } else {
> +            /* High Active */
> +            if (!level) {
> +                s->irq_src[i] &= ~mask;
> +                s->fiq_src[i] &= ~mask;
> +            } else {
> +                s->irq_src[i] |=  mask;
> +                s->fiq_src[i] |=  mask;
> +            }
> +        }
> +    }
> +
> +    /* update IRQ/FIQ pin states */
> +    if (level) {
> +        s->irq_pin[i] |= mask;
> +        s->fiq_pin[i] |= mask;
> +    } else {
> +        s->irq_pin[i] &= ~mask;
> +        s->fiq_pin[i] &= ~mask;
> +    }
> +
> +    ftintc020_update(s);
> +}
> +
> +static uint64_t
> +ftintc020_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftintc020State *s = FTINTC020(opaque);
> +
> +    switch (addr) {
> +    /*
> +     * IRQ
> +     */
> +    case REG_IRQSRC:
> +        return s->irq_src[0];
> +    case REG_IRQENA:
> +        return s->irq_ena[0];
> +    case REG_IRQMDR:
> +        return s->irq_mod[0];
> +    case REG_IRQLVR:
> +        return s->irq_lvl[0];
> +    case REG_IRQSR:
> +        return s->irq_src[0] & s->irq_ena[0];
> +    case REG_EIRQSRC:
> +        return s->irq_src[1];
> +    case REG_EIRQENA:
> +        return s->irq_ena[1];
> +    case REG_EIRQMDR:
> +        return s->irq_mod[1];
> +    case REG_EIRQLVR:
> +        return s->irq_lvl[1];
> +    case REG_EIRQSR:
> +        return s->irq_src[1] & s->irq_ena[1];
> +

AFAICT, index 0 of there arrays in for IRQ and index 1 is for EIRQ.
Can you #define some symbols accrordingly and remove all the magic
numberage with the [0]'s and [1]'s?

> +    /*
> +     * FIQ
> +     */
> +    case REG_FIQSRC:
> +        return s->fiq_src[0];
> +    case REG_FIQENA:
> +        return s->fiq_ena[0];
> +    case REG_FIQMDR:
> +        return s->fiq_mod[0];
> +    case REG_FIQLVR:
> +        return s->fiq_lvl[0];
> +    case REG_FIQSR:
> +        return s->fiq_src[0] & s->fiq_ena[0];
> +    case REG_EFIQSRC:
> +        return s->fiq_src[1];
> +    case REG_EFIQENA:
> +        return s->fiq_ena[1];
> +    case REG_EFIQMDR:
> +        return s->fiq_mod[1];
> +    case REG_EFIQLVR:
> +        return s->fiq_lvl[1];
> +    case REG_EFIQSR:
> +        return s->fiq_src[1] & s->fiq_ena[1];
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
> +        return 0;
> +    }
> +}
> +
> +static void
> +ftintc020_mem_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
> +{
> +    Ftintc020State *s = FTINTC020(opaque);
> +
> +    switch (addr) {
> +    /*
> +     * IRQ
> +     */

Ok to use one line comment. And elsewhere

> +    case REG_IRQENA:
> +        s->irq_ena[0] = (uint32_t)value;
> +        break;
> +    case REG_IRQSCR:
> +        value = ~(value & s->irq_mod[0]);
> +        s->irq_src[0] &= (uint32_t)value;
> +        break;
> +    case REG_IRQMDR:
> +        s->irq_mod[0] = (uint32_t)value;
> +        break;
> +    case REG_IRQLVR:
> +        s->irq_lvl[0] = (uint32_t)value;
> +        break;
> +    case REG_EIRQENA:
> +        s->irq_ena[1] = (uint32_t)value;
> +        break;
> +    case REG_EIRQSCR:
> +        value = ~(value & s->irq_mod[1]);
> +        s->irq_src[1] &= (uint32_t)value;
> +        break;
> +    case REG_EIRQMDR:
> +        s->irq_mod[1] = (uint32_t)value;
> +        break;
> +    case REG_EIRQLVR:
> +        s->irq_lvl[1] = (uint32_t)value;
> +        break;
> +
> +    /*
> +     * FIQ
> +     */
> +    case REG_FIQENA:
> +        s->fiq_ena[0] = (uint32_t)value;
> +        break;
> +    case REG_FIQSCR:
> +        value = ~(value & s->fiq_mod[0]);
> +        s->fiq_src[0] &= (uint32_t)value;
> +        break;
> +    case REG_FIQMDR:
> +        s->fiq_mod[0] = (uint32_t)value;
> +        break;
> +    case REG_FIQLVR:
> +        s->fiq_lvl[0] = (uint32_t)value;
> +        break;
> +    case REG_EFIQENA:
> +        s->fiq_ena[1] = (uint32_t)value;
> +        break;
> +    case REG_EFIQSCR:
> +        value = ~(value & s->fiq_mod[1]);
> +        s->fiq_src[1] &= (uint32_t)value;
> +        break;
> +    case REG_EFIQMDR:
> +        s->fiq_mod[1] = (uint32_t)value;
> +        break;
> +    case REG_EFIQLVR:
> +        s->fiq_lvl[1] = (uint32_t)value;
> +        break;
> +
> +    /* don't care */
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
> +        return;
> +    }
> +
> +    ftintc020_update(s);
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = ftintc020_mem_read,
> +    .write = ftintc020_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    }
> +};
> +
> +static void ftintc020_reset(DeviceState *ds)
> +{
> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
> +    Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev));
> +
> +    s->irq_pin[0] = 0;
> +    s->irq_pin[1] = 0;
> +    s->fiq_pin[0] = 0;
> +    s->fiq_pin[1] = 0;
> +
> +    s->irq_src[0] = 0;
> +    s->irq_src[1] = 0;
> +    s->irq_ena[0] = 0;
> +    s->irq_ena[1] = 0;
> +    s->fiq_src[0] = 0;
> +    s->fiq_src[1] = 0;
> +    s->fiq_ena[0] = 0;
> +    s->fiq_ena[1] = 0;
> +
> +    ftintc020_update(s);
> +}
> +
> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu)

I'm not sure this is the right place for this, I think device creation
helpers belong on the machine / SoC level. Keep the device model self
contained and clients call qdev_init themselves. Andreas have the
rules change on this recently?

> +{
> +    int i;
> +    DeviceState *ds = qdev_create(NULL, TYPE_FTINTC020);

As the device is intended for use in an SoC, the SoC potentially needs
to hold a pointer to it in order to call device destructors. This
function should return ds for future use by the instantiator.

> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
> +    Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev));
> +

Use an Object cast macro. Andreas, can we make this easier for
reviewers and developers by adding blacklisted identifiers to
checkpatch perhaps? throw a warning on +FROM_SYSBUS, DO_UPCAST etc?

> +    s->cpu = cpu;

Im thinking this is a QOM link. Its a connection from one device to
another and the machine should be aware of it.

> +    ftintc020_reset(ds);

Doesn't look right but this is just the already registered
DeviceClass::reset function anyways. You should just be able to delete
this. Does your system function without it?

> +    qdev_init_nofail(ds);

Cutting from here ...

> +    qdev_init_gpio_in(ds, ftintc020_set_irq, 64);
> +    for (i = 0; i < 64; ++i) {
> +        s->irqs[i] = qdev_get_gpio_in(ds, i);
> +    }
> +
> +    /* Enable IC memory-mapped registers access.  */
> +    memory_region_init_io(&s->iomem,
> +                          &mmio_ops,
> +                          s,
> +                          TYPE_FTINTC020,
> +                          0x1000);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(ds), &s->iomem);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, base);
> +

... Im pritty sure all of this belongs in either the ObjectClass::init
or Device::realize functions.

> +    return s->irqs;
> +}
> +
> +static VMStateDescription vmstate_ftintc020 = {
> +    .name = TYPE_FTINTC020,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(irq_src, Ftintc020State, 2),
> +        VMSTATE_UINT32_ARRAY(irq_ena, Ftintc020State, 2),
> +        VMSTATE_UINT32_ARRAY(irq_mod, Ftintc020State, 2),
> +        VMSTATE_UINT32_ARRAY(irq_lvl, Ftintc020State, 2),
> +        VMSTATE_UINT32_ARRAY(fiq_src, Ftintc020State, 2),
> +        VMSTATE_UINT32_ARRAY(fiq_ena, Ftintc020State, 2),
> +        VMSTATE_UINT32_ARRAY(fiq_mod, Ftintc020State, 2),
> +        VMSTATE_UINT32_ARRAY(fiq_lvl, Ftintc020State, 2),
> +        VMSTATE_END_OF_LIST(),
> +    },
> +};
> +
> +static int ftintc020_initfn(SysBusDevice *dev)
> +{
> +    return 0;
> +}
> +
> +static void ftintc020_class_init(ObjectClass *klass, void *data)
> +{
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    k->init     = ftintc020_initfn;

SysBusDevice::init is deprecated in favour of the Device::init. Your
SBD::init doesnt do anything so you can just delete it. But you should
add a device Init here to bring the sysbus foo from your helper
instantiator into the device model.

Regards,
Peter

> +    dc->vmsd    = &vmstate_ftintc020;
> +    dc->reset   = ftintc020_reset;
> +    dc->no_user = 1;
> +}
> +
> +static const TypeInfo ftintc020_info = {
> +    .name          = TYPE_FTINTC020,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(Ftintc020State),
> +    .class_init    = ftintc020_class_init,
> +};
> +
> +static void ftintc020_register_types(void)
> +{
> +    type_register_static(&ftintc020_info);
> +}
> +
> +type_init(ftintc020_register_types)
> diff --git a/hw/arm/ftintc020.h b/hw/arm/ftintc020.h
> new file mode 100644
> index 0000000..f17fb58
> --- /dev/null
> +++ b/hw/arm/ftintc020.h
> @@ -0,0 +1,48 @@
> +/*
> + * Faraday FTINTC020 Programmable Interrupt 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_FTINTC020_H
> +#define HW_ARM_FTINTC020_H
> +
> +/*
> + * IRQ/FIO: 0 ~ 31
> + */
> +#define REG_IRQSRC      0x00    /* IRQ source register */
> +#define REG_IRQENA      0x04    /* IRQ enable register */
> +#define REG_IRQSCR      0x08    /* IRQ status clear register */
> +#define REG_IRQMDR      0x0C    /* IRQ trigger mode register */
> +#define REG_IRQLVR      0x10    /* IRQ trigger level register */
> +#define REG_IRQSR       0x14    /* IRQ status register */
> +
> +#define REG_FIQSRC      0x20    /* FIQ source register */
> +#define REG_FIQENA      0x24    /* FIQ enable register */
> +#define REG_FIQSCR      0x28    /* FIQ status clear register */
> +#define REG_FIQMDR      0x2C    /* FIQ trigger mode register */
> +#define REG_FIQLVR      0x30    /* FIQ trigger level register */
> +#define REG_FIQSR       0x34    /* FIQ status register */
> +
> +/*
> + * Extended IRQ/FIO: 32 ~ 63
> + */
> +
> +#define REG_EIRQSRC      0x60   /* Extended IRQ source register */
> +#define REG_EIRQENA      0x64   /* Extended IRQ enable register */
> +#define REG_EIRQSCR      0x68   /* Extended IRQ status clear register */
> +#define REG_EIRQMDR      0x6C   /* Extended IRQ trigger mode register */
> +#define REG_EIRQLVR      0x70   /* Extended IRQ trigger level register */
> +#define REG_EIRQSR       0x74   /* Extended IRQ status register */
> +
> +#define REG_EFIQSRC      0x80   /* Extended FIQ source register */
> +#define REG_EFIQENA      0x84   /* Extended FIQ enable register */
> +#define REG_EFIQSCR      0x88   /* Extended FIQ status clear register */
> +#define REG_EFIQMDR      0x8C   /* Extended FIQ trigger mode register */
> +#define REG_EFIQLVR      0x90   /* Extended FIQ trigger level register */
> +#define REG_EFIQSR       0x94   /* Extended FIQ status register */
> +
> +#endif
> --
> 1.7.9.5
>
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 03/24] hw/arm: add Faraday FTINTC020 interrupt controller support
  2013-03-02  4:13   ` [Qemu-devel] [PATCH v5 03/24] hw/arm: add Faraday FTINTC020 interrupt controller support Peter Crosthwaite
@ 2013-03-03  4:58     ` Peter Crosthwaite
  2013-03-03  6:29     ` Peter Crosthwaite
  2013-03-04  6:20     ` Kuo-Jung Su
  2 siblings, 0 replies; 15+ messages in thread
From: Peter Crosthwaite @ 2013-03-03  4:58 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, i.mitsyanko, qemu-devel, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Andreas, fred.konrad

Hi All,

On Sat, Mar 2, 2013 at 2:13 PM, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> 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 FTINTC020 interrupt controller supports both FIQ and IRQ signals
>> to the microprocessor.
>> It can handle up to 64 configurable IRQ sources and 64 FIQ sources.
>> The output signals to the microprocessor can be configured as
>> level-high/low active or edge-rising/falling triggered.
>>
>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>> ---
>>  hw/arm/Makefile.objs      |    1 +
>>  hw/arm/faraday.h          |    3 +
>>  hw/arm/faraday_a369_soc.c |   10 +-
>>  hw/arm/ftintc020.c        |  366 +++++++++++++++++++++++++++++++++++++++++++++
>>  hw/arm/ftintc020.h        |   48 ++++++
>>  5 files changed, 425 insertions(+), 3 deletions(-)
>>  create mode 100644 hw/arm/ftintc020.c
>>  create mode 100644 hw/arm/ftintc020.h
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index f6fd60d..6771072 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -37,3 +37,4 @@ obj-y += faraday_a369.o \
>>              faraday_a369_soc.o \
>>              faraday_a369_scu.o \
>>              faraday_a369_kpd.o
>> +obj-y += ftintc020.o
>> diff --git a/hw/arm/faraday.h b/hw/arm/faraday.h
>> index d6ed860..e5f611d 100644
>> --- a/hw/arm/faraday.h
>> +++ b/hw/arm/faraday.h
>> @@ -62,4 +62,7 @@ typedef struct FaradaySoCState {
>>      FARADAY_SOC(object_resolve_path_component(qdev_get_machine(), \
>>                                                TYPE_FARADAY_SOC))
>>
>> +/* ftintc020.c */
>> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu);
>> +
>>  #endif
>> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
>> index 0372868..3d861d2 100644
>> --- a/hw/arm/faraday_a369_soc.c
>> +++ b/hw/arm/faraday_a369_soc.c
>> @@ -73,6 +73,7 @@ a369soc_device_init(FaradaySoCState *s)
>>  {
>>      DriveInfo *dinfo;
>>      DeviceState *ds;
>> +    qemu_irq *pic;
>>
>>      s->as = get_system_memory();
>>      s->ram = g_new(MemoryRegion, 1);
>> @@ -115,12 +116,15 @@ a369soc_device_init(FaradaySoCState *s)
>>          exit(1);
>>      }
>>
>> +    /* Interrupt Controller */
>> +    pic = ftintc020_init(0x90100000, s->cpu);
>> +
>>      /* Serial (FTUART010 which is 16550A compatible) */
>>      if (serial_hds[0]) {
>>          serial_mm_init(s->as,
>>                         0x92b00000,
>>                         2,
>> -                       NULL,
>> +                       pic[53],
>>                         18432000,
>>                         serial_hds[0],
>>                         DEVICE_LITTLE_ENDIAN);
>> @@ -129,7 +133,7 @@ a369soc_device_init(FaradaySoCState *s)
>>          serial_mm_init(s->as,
>>                         0x92c00000,
>>                         2,
>> -                       NULL,
>> +                       pic[54],
>>                         18432000,
>>                         serial_hds[1],
>>                         DEVICE_LITTLE_ENDIAN);
>> @@ -140,7 +144,7 @@ a369soc_device_init(FaradaySoCState *s)
>>      s->scu = ds;
>>
>>      /* ftkbc010 */
>> -    sysbus_create_simple("a369.keypad", 0x92f00000, NULL);
>> +    sysbus_create_simple("a369.keypad", 0x92f00000, pic[21]);
>>  }
>>
>>  static int a369soc_init(SysBusDevice *busdev)
>> diff --git a/hw/arm/ftintc020.c b/hw/arm/ftintc020.c
>> new file mode 100644
>> index 0000000..a7f6454
>> --- /dev/null
>> +++ b/hw/arm/ftintc020.c
>> @@ -0,0 +1,366 @@
>> +/*
>> + * Faraday FTINTC020 Programmable Interrupt Controller.
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This code is licensed under GNU GPL v2+.
>> + */
>> +
>> +#include "hw/hw.h"
>> +#include "hw/sysbus.h"
>> +
>> +#include "faraday.h"
>> +#include "ftintc020.h"
>> +
>> +#define TYPE_FTINTC020  "ftintc020"
>> +
>> +typedef struct Ftintc020State {
>> +    SysBusDevice busdev;
>> +    MemoryRegion iomem;
>> +    ARMCPU *cpu;
>> +    qemu_irq irqs[64];
>> +
>> +    uint32_t irq_pin[2];    /* IRQ pin state */
>> +    uint32_t fiq_pin[2];    /* IRQ pin state */
>> +
>> +    /* HW register caches */
>> +    uint32_t irq_src[2];    /* IRQ source register */
>> +    uint32_t irq_ena[2];    /* IRQ enable register */
>> +    uint32_t irq_mod[2];    /* IRQ mode register */
>> +    uint32_t irq_lvl[2];    /* IRQ level register */
>> +    uint32_t fiq_src[2];    /* FIQ source register */
>> +    uint32_t fiq_ena[2];    /* FIQ enable register */
>> +    uint32_t fiq_mod[2];    /* FIQ mode register */
>> +    uint32_t fiq_lvl[2];    /* FIQ level register */
>> +} Ftintc020State;
>> +
>> +#define FTINTC020(obj) \
>> +    OBJECT_CHECK(Ftintc020State, obj, TYPE_FTINTC020)
>> +
>> +static void
>> +ftintc020_update(Ftintc020State *s)
>> +{
>> +    uint32_t mask[2];
>> +
>> +    /* FIQ */
>> +    mask[0] = s->fiq_src[0] & s->fiq_ena[0];
>> +    mask[1] = s->fiq_src[1] & s->fiq_ena[1];
>> +
>> +    if (mask[0] || mask[1]) {
>> +        cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
>> +    } else {
>> +        cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
>> +    }
>> +
>> +    /* IRQ */
>> +    mask[0] = s->irq_src[0] & s->irq_ena[0];
>> +    mask[1] = s->irq_src[1] & s->irq_ena[1];
>> +
>> +    if (mask[0] || mask[1]) {
>> +        cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
>> +    } else {
>> +        cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
>> +    }
>> +}
>> +
>> +/* Note: Here level means state of the signal on a pin */
>> +static void
>> +ftintc020_set_irq(void *opaque, int irq, int level)
>> +{
>> +    Ftintc020State *s = FTINTC020(opaque);
>> +    uint32_t i = irq / 32;
>> +    uint32_t mask = 1 << (irq % 32);
>> +
>> +    if (s->irq_mod[i] & mask) {
>> +        /* Edge Triggered */
>> +        if (s->irq_lvl[i] & mask) {
>> +            /* Falling Active */
>> +            if ((s->irq_pin[i] & mask) && !level) {
>> +                s->irq_src[i] |=  mask;
>> +                s->fiq_src[i] |=  mask;
>> +            }
>> +        } else {
>> +            /* Rising Active */
>> +            if (!(s->irq_pin[i] & mask) && level) {
>> +                s->irq_src[i] |=  mask;
>> +                s->fiq_src[i] |=  mask;
>> +            }
>> +        }
>> +    } else {
>> +        /* Level Triggered */
>> +        if (s->irq_lvl[i] & mask) {
>> +            /* Low Active */
>> +            if (level) {
>> +                s->irq_src[i] &= ~mask;
>> +                s->fiq_src[i] &= ~mask;
>> +            } else {
>> +                s->irq_src[i] |=  mask;
>> +                s->fiq_src[i] |=  mask;
>> +            }
>> +        } else {
>> +            /* High Active */
>> +            if (!level) {
>> +                s->irq_src[i] &= ~mask;
>> +                s->fiq_src[i] &= ~mask;
>> +            } else {
>> +                s->irq_src[i] |=  mask;
>> +                s->fiq_src[i] |=  mask;
>> +            }
>> +        }
>> +    }
>> +
>> +    /* update IRQ/FIQ pin states */
>> +    if (level) {
>> +        s->irq_pin[i] |= mask;
>> +        s->fiq_pin[i] |= mask;
>> +    } else {
>> +        s->irq_pin[i] &= ~mask;
>> +        s->fiq_pin[i] &= ~mask;
>> +    }
>> +
>> +    ftintc020_update(s);
>> +}
>> +
>> +static uint64_t
>> +ftintc020_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    Ftintc020State *s = FTINTC020(opaque);
>> +
>> +    switch (addr) {
>> +    /*
>> +     * IRQ
>> +     */
>> +    case REG_IRQSRC:
>> +        return s->irq_src[0];
>> +    case REG_IRQENA:
>> +        return s->irq_ena[0];
>> +    case REG_IRQMDR:
>> +        return s->irq_mod[0];
>> +    case REG_IRQLVR:
>> +        return s->irq_lvl[0];
>> +    case REG_IRQSR:
>> +        return s->irq_src[0] & s->irq_ena[0];
>> +    case REG_EIRQSRC:
>> +        return s->irq_src[1];
>> +    case REG_EIRQENA:
>> +        return s->irq_ena[1];
>> +    case REG_EIRQMDR:
>> +        return s->irq_mod[1];
>> +    case REG_EIRQLVR:
>> +        return s->irq_lvl[1];
>> +    case REG_EIRQSR:
>> +        return s->irq_src[1] & s->irq_ena[1];
>> +
>
> AFAICT, index 0 of there arrays in for IRQ and index 1 is for EIRQ.
> Can you #define some symbols accrordingly and remove all the magic
> numberage with the [0]'s and [1]'s?
>
>> +    /*
>> +     * FIQ
>> +     */
>> +    case REG_FIQSRC:
>> +        return s->fiq_src[0];
>> +    case REG_FIQENA:
>> +        return s->fiq_ena[0];
>> +    case REG_FIQMDR:
>> +        return s->fiq_mod[0];
>> +    case REG_FIQLVR:
>> +        return s->fiq_lvl[0];
>> +    case REG_FIQSR:
>> +        return s->fiq_src[0] & s->fiq_ena[0];
>> +    case REG_EFIQSRC:
>> +        return s->fiq_src[1];
>> +    case REG_EFIQENA:
>> +        return s->fiq_ena[1];
>> +    case REG_EFIQMDR:
>> +        return s->fiq_mod[1];
>> +    case REG_EFIQLVR:
>> +        return s->fiq_lvl[1];
>> +    case REG_EFIQSR:
>> +        return s->fiq_src[1] & s->fiq_ena[1];
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
>> +        return 0;
>> +    }
>> +}
>> +
>> +static void
>> +ftintc020_mem_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
>> +{
>> +    Ftintc020State *s = FTINTC020(opaque);
>> +
>> +    switch (addr) {
>> +    /*
>> +     * IRQ
>> +     */
>
> Ok to use one line comment. And elsewhere
>
>> +    case REG_IRQENA:
>> +        s->irq_ena[0] = (uint32_t)value;
>> +        break;
>> +    case REG_IRQSCR:
>> +        value = ~(value & s->irq_mod[0]);
>> +        s->irq_src[0] &= (uint32_t)value;
>> +        break;
>> +    case REG_IRQMDR:
>> +        s->irq_mod[0] = (uint32_t)value;
>> +        break;
>> +    case REG_IRQLVR:
>> +        s->irq_lvl[0] = (uint32_t)value;
>> +        break;
>> +    case REG_EIRQENA:
>> +        s->irq_ena[1] = (uint32_t)value;
>> +        break;
>> +    case REG_EIRQSCR:
>> +        value = ~(value & s->irq_mod[1]);
>> +        s->irq_src[1] &= (uint32_t)value;
>> +        break;
>> +    case REG_EIRQMDR:
>> +        s->irq_mod[1] = (uint32_t)value;
>> +        break;
>> +    case REG_EIRQLVR:
>> +        s->irq_lvl[1] = (uint32_t)value;
>> +        break;
>> +
>> +    /*
>> +     * FIQ
>> +     */
>> +    case REG_FIQENA:
>> +        s->fiq_ena[0] = (uint32_t)value;
>> +        break;
>> +    case REG_FIQSCR:
>> +        value = ~(value & s->fiq_mod[0]);
>> +        s->fiq_src[0] &= (uint32_t)value;
>> +        break;
>> +    case REG_FIQMDR:
>> +        s->fiq_mod[0] = (uint32_t)value;
>> +        break;
>> +    case REG_FIQLVR:
>> +        s->fiq_lvl[0] = (uint32_t)value;
>> +        break;
>> +    case REG_EFIQENA:
>> +        s->fiq_ena[1] = (uint32_t)value;
>> +        break;
>> +    case REG_EFIQSCR:
>> +        value = ~(value & s->fiq_mod[1]);
>> +        s->fiq_src[1] &= (uint32_t)value;
>> +        break;
>> +    case REG_EFIQMDR:
>> +        s->fiq_mod[1] = (uint32_t)value;
>> +        break;
>> +    case REG_EFIQLVR:
>> +        s->fiq_lvl[1] = (uint32_t)value;
>> +        break;
>> +
>> +    /* don't care */
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
>> +        return;
>> +    }
>> +
>> +    ftintc020_update(s);
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = ftintc020_mem_read,
>> +    .write = ftintc020_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    }
>> +};
>> +
>> +static void ftintc020_reset(DeviceState *ds)
>> +{
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev));
>> +
>> +    s->irq_pin[0] = 0;
>> +    s->irq_pin[1] = 0;
>> +    s->fiq_pin[0] = 0;
>> +    s->fiq_pin[1] = 0;
>> +
>> +    s->irq_src[0] = 0;
>> +    s->irq_src[1] = 0;
>> +    s->irq_ena[0] = 0;
>> +    s->irq_ena[1] = 0;
>> +    s->fiq_src[0] = 0;
>> +    s->fiq_src[1] = 0;
>> +    s->fiq_ena[0] = 0;
>> +    s->fiq_ena[1] = 0;
>> +
>> +    ftintc020_update(s);
>> +}
>> +
>> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu)
>
> I'm not sure this is the right place for this, I think device creation
> helpers belong on the machine / SoC level. Keep the device model self
> contained and clients call qdev_init themselves. Andreas have the
> rules change on this recently?
>
>> +{
>> +    int i;
>> +    DeviceState *ds = qdev_create(NULL, TYPE_FTINTC020);
>
> As the device is intended for use in an SoC, the SoC potentially needs
> to hold a pointer to it in order to call device destructors. This
> function should return ds for future use by the instantiator.
>
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev));
>> +
>
> Use an Object cast macro. Andreas, can we make this easier for
> reviewers and developers by adding blacklisted identifiers to
> checkpatch perhaps? throw a warning on +FROM_SYSBUS, DO_UPCAST etc?
>
>> +    s->cpu = cpu;
>
> Im thinking this is a QOM link. Its a connection from one device to
> another and the machine should be aware of it.
>
>> +    ftintc020_reset(ds);
>
> Doesn't look right but this is just the already registered
> DeviceClass::reset function anyways. You should just be able to delete
> this. Does your system function without it?
>
>> +    qdev_init_nofail(ds);
>
> Cutting from here ...
>
>> +    qdev_init_gpio_in(ds, ftintc020_set_irq, 64);
>> +    for (i = 0; i < 64; ++i) {
>> +        s->irqs[i] = qdev_get_gpio_in(ds, i);
>> +    }
>> +
>> +    /* Enable IC memory-mapped registers access.  */
>> +    memory_region_init_io(&s->iomem,
>> +                          &mmio_ops,
>> +                          s,
>> +                          TYPE_FTINTC020,
>> +                          0x1000);
>> +    sysbus_init_mmio(SYS_BUS_DEVICE(ds), &s->iomem);
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, base);
>> +
>
> ... Im pritty sure all of this belongs in either the ObjectClass::init
> or Device::realize functions.
>
>> +    return s->irqs;
>> +}
>> +
>> +static VMStateDescription vmstate_ftintc020 = {
>> +    .name = TYPE_FTINTC020,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT32_ARRAY(irq_src, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(irq_ena, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(irq_mod, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(irq_lvl, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(fiq_src, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(fiq_ena, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(fiq_mod, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(fiq_lvl, Ftintc020State, 2),
>> +        VMSTATE_END_OF_LIST(),
>> +    },
>> +};
>> +
>> +static int ftintc020_initfn(SysBusDevice *dev)
>> +{
>> +    return 0;
>> +}
>> +
>> +static void ftintc020_class_init(ObjectClass *klass, void *data)
>> +{
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    k->init     = ftintc020_initfn;
>
> SysBusDevice::init is deprecated in favour of the Device::init. Your
> SBD::init doesnt do anything so you can just delete it. But you should

I did some further digging on this and it looks like the dummy
SysBusDevice::init function is needed only in the absence of a
Device::realize fn. Doesnt seem right to me. Posting fix to list
shortly.

Regards,
Peter

> add a device Init here to bring the sysbus foo from your helper
> instantiator into the device model.
>
> Regards,
> Peter
>
>> +    dc->vmsd    = &vmstate_ftintc020;
>> +    dc->reset   = ftintc020_reset;
>> +    dc->no_user = 1;
>> +}
>> +
>> +static const TypeInfo ftintc020_info = {
>> +    .name          = TYPE_FTINTC020,
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(Ftintc020State),
>> +    .class_init    = ftintc020_class_init,
>> +};
>> +
>> +static void ftintc020_register_types(void)
>> +{
>> +    type_register_static(&ftintc020_info);
>> +}
>> +
>> +type_init(ftintc020_register_types)
>> diff --git a/hw/arm/ftintc020.h b/hw/arm/ftintc020.h
>> new file mode 100644
>> index 0000000..f17fb58
>> --- /dev/null
>> +++ b/hw/arm/ftintc020.h
>> @@ -0,0 +1,48 @@
>> +/*
>> + * Faraday FTINTC020 Programmable Interrupt 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_FTINTC020_H
>> +#define HW_ARM_FTINTC020_H
>> +
>> +/*
>> + * IRQ/FIO: 0 ~ 31
>> + */
>> +#define REG_IRQSRC      0x00    /* IRQ source register */
>> +#define REG_IRQENA      0x04    /* IRQ enable register */
>> +#define REG_IRQSCR      0x08    /* IRQ status clear register */
>> +#define REG_IRQMDR      0x0C    /* IRQ trigger mode register */
>> +#define REG_IRQLVR      0x10    /* IRQ trigger level register */
>> +#define REG_IRQSR       0x14    /* IRQ status register */
>> +
>> +#define REG_FIQSRC      0x20    /* FIQ source register */
>> +#define REG_FIQENA      0x24    /* FIQ enable register */
>> +#define REG_FIQSCR      0x28    /* FIQ status clear register */
>> +#define REG_FIQMDR      0x2C    /* FIQ trigger mode register */
>> +#define REG_FIQLVR      0x30    /* FIQ trigger level register */
>> +#define REG_FIQSR       0x34    /* FIQ status register */
>> +
>> +/*
>> + * Extended IRQ/FIO: 32 ~ 63
>> + */
>> +
>> +#define REG_EIRQSRC      0x60   /* Extended IRQ source register */
>> +#define REG_EIRQENA      0x64   /* Extended IRQ enable register */
>> +#define REG_EIRQSCR      0x68   /* Extended IRQ status clear register */
>> +#define REG_EIRQMDR      0x6C   /* Extended IRQ trigger mode register */
>> +#define REG_EIRQLVR      0x70   /* Extended IRQ trigger level register */
>> +#define REG_EIRQSR       0x74   /* Extended IRQ status register */
>> +
>> +#define REG_EFIQSRC      0x80   /* Extended FIQ source register */
>> +#define REG_EFIQENA      0x84   /* Extended FIQ enable register */
>> +#define REG_EFIQSCR      0x88   /* Extended FIQ status clear register */
>> +#define REG_EFIQMDR      0x8C   /* Extended FIQ trigger mode register */
>> +#define REG_EFIQLVR      0x90   /* Extended FIQ trigger level register */
>> +#define REG_EFIQSR       0x94   /* Extended FIQ status register */
>> +
>> +#endif
>> --
>> 1.7.9.5
>>
>>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 03/24] hw/arm: add Faraday FTINTC020 interrupt controller support
  2013-03-02  4:13   ` [Qemu-devel] [PATCH v5 03/24] hw/arm: add Faraday FTINTC020 interrupt controller support Peter Crosthwaite
  2013-03-03  4:58     ` Peter Crosthwaite
@ 2013-03-03  6:29     ` Peter Crosthwaite
  2013-03-04  6:29       ` Kuo-Jung Su
  2013-03-04  6:20     ` Kuo-Jung Su
  2 siblings, 1 reply; 15+ messages in thread
From: Peter Crosthwaite @ 2013-03-03  6:29 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, i.mitsyanko, qemu-devel, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Andreas, fred.konrad

Hi Kuo-Jung,

On Sat, Mar 2, 2013 at 2:13 PM, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> 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 FTINTC020 interrupt controller supports both FIQ and IRQ signals
>> to the microprocessor.
>> It can handle up to 64 configurable IRQ sources and 64 FIQ sources.
>> The output signals to the microprocessor can be configured as
>> level-high/low active or edge-rising/falling triggered.
>>
>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>> ---
>>  hw/arm/Makefile.objs      |    1 +
>>  hw/arm/faraday.h          |    3 +
>>  hw/arm/faraday_a369_soc.c |   10 +-
>>  hw/arm/ftintc020.c        |  366 +++++++++++++++++++++++++++++++++++++++++++++
>>  hw/arm/ftintc020.h        |   48 ++++++
>>  5 files changed, 425 insertions(+), 3 deletions(-)
>>  create mode 100644 hw/arm/ftintc020.c
>>  create mode 100644 hw/arm/ftintc020.h
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index f6fd60d..6771072 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -37,3 +37,4 @@ obj-y += faraday_a369.o \
>>              faraday_a369_soc.o \
>>              faraday_a369_scu.o \
>>              faraday_a369_kpd.o
>> +obj-y += ftintc020.o
>> diff --git a/hw/arm/faraday.h b/hw/arm/faraday.h
>> index d6ed860..e5f611d 100644
>> --- a/hw/arm/faraday.h
>> +++ b/hw/arm/faraday.h
>> @@ -62,4 +62,7 @@ typedef struct FaradaySoCState {
>>      FARADAY_SOC(object_resolve_path_component(qdev_get_machine(), \
>>                                                TYPE_FARADAY_SOC))
>>
>> +/* ftintc020.c */
>> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu);
>> +
>>  #endif
>> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
>> index 0372868..3d861d2 100644
>> --- a/hw/arm/faraday_a369_soc.c
>> +++ b/hw/arm/faraday_a369_soc.c
>> @@ -73,6 +73,7 @@ a369soc_device_init(FaradaySoCState *s)
>>  {
>>      DriveInfo *dinfo;
>>      DeviceState *ds;
>> +    qemu_irq *pic;
>>
>>      s->as = get_system_memory();
>>      s->ram = g_new(MemoryRegion, 1);
>> @@ -115,12 +116,15 @@ a369soc_device_init(FaradaySoCState *s)
>>          exit(1);
>>      }
>>
>> +    /* Interrupt Controller */
>> +    pic = ftintc020_init(0x90100000, s->cpu);
>> +
>>      /* Serial (FTUART010 which is 16550A compatible) */
>>      if (serial_hds[0]) {
>>          serial_mm_init(s->as,
>>                         0x92b00000,
>>                         2,
>> -                       NULL,
>> +                       pic[53],
>>                         18432000,
>>                         serial_hds[0],
>>                         DEVICE_LITTLE_ENDIAN);
>> @@ -129,7 +133,7 @@ a369soc_device_init(FaradaySoCState *s)
>>          serial_mm_init(s->as,
>>                         0x92c00000,
>>                         2,
>> -                       NULL,
>> +                       pic[54],
>>                         18432000,
>>                         serial_hds[1],
>>                         DEVICE_LITTLE_ENDIAN);
>> @@ -140,7 +144,7 @@ a369soc_device_init(FaradaySoCState *s)
>>      s->scu = ds;
>>
>>      /* ftkbc010 */
>> -    sysbus_create_simple("a369.keypad", 0x92f00000, NULL);
>> +    sysbus_create_simple("a369.keypad", 0x92f00000, pic[21]);
>>  }
>>
>>  static int a369soc_init(SysBusDevice *busdev)
>> diff --git a/hw/arm/ftintc020.c b/hw/arm/ftintc020.c
>> new file mode 100644
>> index 0000000..a7f6454
>> --- /dev/null
>> +++ b/hw/arm/ftintc020.c
>> @@ -0,0 +1,366 @@
>> +/*
>> + * Faraday FTINTC020 Programmable Interrupt Controller.
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This code is licensed under GNU GPL v2+.
>> + */
>> +
>> +#include "hw/hw.h"
>> +#include "hw/sysbus.h"
>> +
>> +#include "faraday.h"
>> +#include "ftintc020.h"
>> +
>> +#define TYPE_FTINTC020  "ftintc020"
>> +
>> +typedef struct Ftintc020State {
>> +    SysBusDevice busdev;
>> +    MemoryRegion iomem;
>> +    ARMCPU *cpu;

So Ive looked into your init routine problem a little more and
checkout out how its handled by other ARM interrupt controllers. I
think its gone wrong here, in that an interrupt controller should not
have a handle to a CPU at all. It should just have GPIO outputs for
the interrupt wires. Replace this with GPIO outputs for your intcs IRQ
and FIQ output. This removes the need for your machine model to pass
in an ARMCPU to the device (whether that be via your Ad-Hoc creation
helper or via a QOM link).

>> +    qemu_irq irqs[64];
>> +
>> +    uint32_t irq_pin[2];    /* IRQ pin state */
>> +    uint32_t fiq_pin[2];    /* IRQ pin state */
>> +
>> +    /* HW register caches */
>> +    uint32_t irq_src[2];    /* IRQ source register */
>> +    uint32_t irq_ena[2];    /* IRQ enable register */
>> +    uint32_t irq_mod[2];    /* IRQ mode register */
>> +    uint32_t irq_lvl[2];    /* IRQ level register */
>> +    uint32_t fiq_src[2];    /* FIQ source register */
>> +    uint32_t fiq_ena[2];    /* FIQ enable register */
>> +    uint32_t fiq_mod[2];    /* FIQ mode register */
>> +    uint32_t fiq_lvl[2];    /* FIQ level register */
>> +} Ftintc020State;
>> +
>> +#define FTINTC020(obj) \
>> +    OBJECT_CHECK(Ftintc020State, obj, TYPE_FTINTC020)
>> +
>> +static void
>> +ftintc020_update(Ftintc020State *s)
>> +{
>> +    uint32_t mask[2];
>> +
>> +    /* FIQ */
>> +    mask[0] = s->fiq_src[0] & s->fiq_ena[0];
>> +    mask[1] = s->fiq_src[1] & s->fiq_ena[1];
>> +
>> +    if (mask[0] || mask[1]) {
>> +        cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);

Access to the cpu->env from device land is generally bad. Replacing
this logic with GPIO outputs would remove usage of the env.

>> +    } else {
>> +        cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
>> +    }
>> +
>> +    /* IRQ */
>> +    mask[0] = s->irq_src[0] & s->irq_ena[0];
>> +    mask[1] = s->irq_src[1] & s->irq_ena[1];
>> +
>> +    if (mask[0] || mask[1]) {
>> +        cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
>> +    } else {
>> +        cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
>> +    }
>> +}
>> +
>> +/* Note: Here level means state of the signal on a pin */
>> +static void
>> +ftintc020_set_irq(void *opaque, int irq, int level)
>> +{
>> +    Ftintc020State *s = FTINTC020(opaque);
>> +    uint32_t i = irq / 32;
>> +    uint32_t mask = 1 << (irq % 32);
>> +
>> +    if (s->irq_mod[i] & mask) {
>> +        /* Edge Triggered */
>> +        if (s->irq_lvl[i] & mask) {
>> +            /* Falling Active */
>> +            if ((s->irq_pin[i] & mask) && !level) {
>> +                s->irq_src[i] |=  mask;
>> +                s->fiq_src[i] |=  mask;
>> +            }
>> +        } else {
>> +            /* Rising Active */
>> +            if (!(s->irq_pin[i] & mask) && level) {
>> +                s->irq_src[i] |=  mask;
>> +                s->fiq_src[i] |=  mask;
>> +            }
>> +        }
>> +    } else {
>> +        /* Level Triggered */
>> +        if (s->irq_lvl[i] & mask) {
>> +            /* Low Active */
>> +            if (level) {
>> +                s->irq_src[i] &= ~mask;
>> +                s->fiq_src[i] &= ~mask;
>> +            } else {
>> +                s->irq_src[i] |=  mask;
>> +                s->fiq_src[i] |=  mask;
>> +            }
>> +        } else {
>> +            /* High Active */
>> +            if (!level) {
>> +                s->irq_src[i] &= ~mask;
>> +                s->fiq_src[i] &= ~mask;
>> +            } else {
>> +                s->irq_src[i] |=  mask;
>> +                s->fiq_src[i] |=  mask;
>> +            }
>> +        }
>> +    }
>> +
>> +    /* update IRQ/FIQ pin states */
>> +    if (level) {
>> +        s->irq_pin[i] |= mask;
>> +        s->fiq_pin[i] |= mask;
>> +    } else {
>> +        s->irq_pin[i] &= ~mask;
>> +        s->fiq_pin[i] &= ~mask;
>> +    }
>> +
>> +    ftintc020_update(s);
>> +}
>> +
>> +static uint64_t
>> +ftintc020_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    Ftintc020State *s = FTINTC020(opaque);
>> +
>> +    switch (addr) {
>> +    /*
>> +     * IRQ
>> +     */
>> +    case REG_IRQSRC:
>> +        return s->irq_src[0];
>> +    case REG_IRQENA:
>> +        return s->irq_ena[0];
>> +    case REG_IRQMDR:
>> +        return s->irq_mod[0];
>> +    case REG_IRQLVR:
>> +        return s->irq_lvl[0];
>> +    case REG_IRQSR:
>> +        return s->irq_src[0] & s->irq_ena[0];
>> +    case REG_EIRQSRC:
>> +        return s->irq_src[1];
>> +    case REG_EIRQENA:
>> +        return s->irq_ena[1];
>> +    case REG_EIRQMDR:
>> +        return s->irq_mod[1];
>> +    case REG_EIRQLVR:
>> +        return s->irq_lvl[1];
>> +    case REG_EIRQSR:
>> +        return s->irq_src[1] & s->irq_ena[1];
>> +
>
> AFAICT, index 0 of there arrays in for IRQ and index 1 is for EIRQ.
> Can you #define some symbols accrordingly and remove all the magic
> numberage with the [0]'s and [1]'s?
>
>> +    /*
>> +     * FIQ
>> +     */
>> +    case REG_FIQSRC:
>> +        return s->fiq_src[0];
>> +    case REG_FIQENA:
>> +        return s->fiq_ena[0];
>> +    case REG_FIQMDR:
>> +        return s->fiq_mod[0];
>> +    case REG_FIQLVR:
>> +        return s->fiq_lvl[0];
>> +    case REG_FIQSR:
>> +        return s->fiq_src[0] & s->fiq_ena[0];
>> +    case REG_EFIQSRC:
>> +        return s->fiq_src[1];
>> +    case REG_EFIQENA:
>> +        return s->fiq_ena[1];
>> +    case REG_EFIQMDR:
>> +        return s->fiq_mod[1];
>> +    case REG_EFIQLVR:
>> +        return s->fiq_lvl[1];
>> +    case REG_EFIQSR:
>> +        return s->fiq_src[1] & s->fiq_ena[1];
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
>> +        return 0;
>> +    }
>> +}
>> +
>> +static void
>> +ftintc020_mem_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
>> +{
>> +    Ftintc020State *s = FTINTC020(opaque);
>> +
>> +    switch (addr) {
>> +    /*
>> +     * IRQ
>> +     */
>
> Ok to use one line comment. And elsewhere
>
>> +    case REG_IRQENA:
>> +        s->irq_ena[0] = (uint32_t)value;
>> +        break;
>> +    case REG_IRQSCR:
>> +        value = ~(value & s->irq_mod[0]);
>> +        s->irq_src[0] &= (uint32_t)value;
>> +        break;
>> +    case REG_IRQMDR:
>> +        s->irq_mod[0] = (uint32_t)value;
>> +        break;
>> +    case REG_IRQLVR:
>> +        s->irq_lvl[0] = (uint32_t)value;
>> +        break;
>> +    case REG_EIRQENA:
>> +        s->irq_ena[1] = (uint32_t)value;
>> +        break;
>> +    case REG_EIRQSCR:
>> +        value = ~(value & s->irq_mod[1]);
>> +        s->irq_src[1] &= (uint32_t)value;
>> +        break;
>> +    case REG_EIRQMDR:
>> +        s->irq_mod[1] = (uint32_t)value;
>> +        break;
>> +    case REG_EIRQLVR:
>> +        s->irq_lvl[1] = (uint32_t)value;
>> +        break;
>> +
>> +    /*
>> +     * FIQ
>> +     */
>> +    case REG_FIQENA:
>> +        s->fiq_ena[0] = (uint32_t)value;
>> +        break;
>> +    case REG_FIQSCR:
>> +        value = ~(value & s->fiq_mod[0]);
>> +        s->fiq_src[0] &= (uint32_t)value;
>> +        break;
>> +    case REG_FIQMDR:
>> +        s->fiq_mod[0] = (uint32_t)value;
>> +        break;
>> +    case REG_FIQLVR:
>> +        s->fiq_lvl[0] = (uint32_t)value;
>> +        break;
>> +    case REG_EFIQENA:
>> +        s->fiq_ena[1] = (uint32_t)value;
>> +        break;
>> +    case REG_EFIQSCR:
>> +        value = ~(value & s->fiq_mod[1]);
>> +        s->fiq_src[1] &= (uint32_t)value;
>> +        break;
>> +    case REG_EFIQMDR:
>> +        s->fiq_mod[1] = (uint32_t)value;
>> +        break;
>> +    case REG_EFIQLVR:
>> +        s->fiq_lvl[1] = (uint32_t)value;
>> +        break;
>> +
>> +    /* don't care */
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
>> +        return;
>> +    }
>> +
>> +    ftintc020_update(s);
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = ftintc020_mem_read,
>> +    .write = ftintc020_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    }
>> +};
>> +
>> +static void ftintc020_reset(DeviceState *ds)
>> +{
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev));
>> +
>> +    s->irq_pin[0] = 0;
>> +    s->irq_pin[1] = 0;
>> +    s->fiq_pin[0] = 0;
>> +    s->fiq_pin[1] = 0;
>> +
>> +    s->irq_src[0] = 0;
>> +    s->irq_src[1] = 0;
>> +    s->irq_ena[0] = 0;
>> +    s->irq_ena[1] = 0;
>> +    s->fiq_src[0] = 0;
>> +    s->fiq_src[1] = 0;
>> +    s->fiq_ena[0] = 0;
>> +    s->fiq_ena[1] = 0;
>> +
>> +    ftintc020_update(s);
>> +}
>> +
>> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu)


This problematic function then goes away completely. The
machine-model/SoC just instantiates your intc and connects your GPIO
outputs to the ARM cpu. You will need to add a call to
arm_pic_init_cpu to your machine-model/SoC. Have a look at vexpress.c
for an example of a machine that does this. ARM gic and mpcore both
export FIQ and IRQ as GPIOs which allows for this much cleaner
solution.

Regards,
Peter

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 04/24] hw/arm: add Faraday FTAHBC020 support
       [not found] ` <1361949350-22241-5-git-send-email-dantesu@gmail.com>
@ 2013-03-04  3:35   ` Peter Crosthwaite
  2013-03-04  6:30     ` Kuo-Jung Su
  0 siblings, 1 reply; 15+ messages in thread
From: Peter Crosthwaite @ 2013-03-04  3:35 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, i.mitsyanko, qemu-devel, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Andreas, fred.konrad

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>
>
> It's used to perform AHB remap and if the SDRAM is initialized
> before AHB remap process activated, then it would also perform
> the QEMU RAM initialization.
>
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> ---
>  hw/arm/Makefile.objs      |    1 +
>  hw/arm/faraday_a369_soc.c |    3 +
>  hw/arm/ftahbc020.c        |  189 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 193 insertions(+)
>  create mode 100644 hw/arm/ftahbc020.c
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 6771072..33c9482 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -38,3 +38,4 @@ obj-y += faraday_a369.o \
>              faraday_a369_scu.o \
>              faraday_a369_kpd.o
>  obj-y += ftintc020.o
> +obj-y += ftahbc020.o
> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
> index 3d861d2..e7343d9 100644
> --- a/hw/arm/faraday_a369_soc.c
> +++ b/hw/arm/faraday_a369_soc.c
> @@ -145,6 +145,9 @@ a369soc_device_init(FaradaySoCState *s)
>
>      /* ftkbc010 */
>      sysbus_create_simple("a369.keypad", 0x92f00000, pic[21]);
> +
> +    /* ftahbc020 */
> +    s->ahbc = sysbus_create_simple("ftahbc020", 0x94000000, NULL);
>  }
>
>  static int a369soc_init(SysBusDevice *busdev)
> diff --git a/hw/arm/ftahbc020.c b/hw/arm/ftahbc020.c
> new file mode 100644
> index 0000000..b558e90
> --- /dev/null
> +++ b/hw/arm/ftahbc020.c
> @@ -0,0 +1,189 @@
> +/*
> + * Faraday AHB controller
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * 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_SLAVE(n)    ((n) * 4) /* Slave config (base & size) */
> +#define REG_PRIR        0x80    /* Priority register */
> +#define REG_IDLECR      0x84    /* IDLE count register */
> +#define REG_CR          0x88    /* Control register */
> +#define REG_REVR        0x8c    /* Revision register */
> +
> +#define CR_REMAP        0x01    /* Enable AHB remap for slave 4 & 6 */
> +
> +#define TYPE_FTAHBC020  "ftahbc020"
> +
> +typedef struct Ftahbc020State {
> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +
> +    /* HW register cache */
> +    uint32_t cr;
> +} Ftahbc020State;
> +
> +#define FTAHBC020(obj) \
> +    OBJECT_CHECK(Ftahbc020State, obj, TYPE_FTAHBC020)
> +
> +static uint64_t
> +ftahbc020_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftahbc020State *s = FTAHBC020(opaque);
> +    FaradaySoCState *soc = FARADAY_SOC_GET_CORE();



> +    uint64_t ret = 0;
> +
> +    switch (addr) {
> +    /* slave address & window configuration */
> +    case REG_SLAVE(0) ... REG_SLAVE(3):
> +    case REG_SLAVE(5):

I think its required to have a comment of fall through case statements.

/* fall-through - because of foo */

Regards,
Peter

> +    case REG_SLAVE(7) ... REG_SLAVE(31):
> +        ret = soc->ahb_slave[addr / 4];
> +        break;
> +    case REG_SLAVE(4):
> +        ret = soc->rom_base | (soc->ahb_slave[4] & 0x000f0000);
> +        break;
> +    case REG_SLAVE(6):
> +        ret = soc->ram_base | (soc->ahb_slave[6] & 0x000f0000);
> +        break;
> +    /* control register */
> +    case REG_CR:
> +        if (soc->ahb_remapped) {
> +            s->cr |= CR_REMAP;
> +        }
> +        ret = s->cr;
> +        break;
> +    case REG_REVR:
> +        ret = 0x00010301;   /* rev. 1.3.1 */
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "ftahbc020: undefined memory access@0x%llx\n", addr);
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static void
> +ftahbc020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
> +{
> +    Ftahbc020State *s = FTAHBC020(opaque);
> +    FaradaySoCState *soc = FARADAY_SOC_GET_CORE();
> +
> +    switch (addr) {
> +    case REG_CR:    /* control register */
> +        s->cr = (uint32_t)val;
> +        if (soc->ahb_remapped && !(s->cr & CR_REMAP)) {
> +            hw_error("ftahbc020: Once AHB remap is enabled, "
> +                     "it could not be disabled!\n");
> +            exit(1);
> +        }
> +        if (!soc->ahb_remapped && (s->cr & CR_REMAP)) {
> +            /* Remap AHB slave 4 (ROM) & slave 6 (RAM) */
> +            /* 1. Remap RAM to base of ROM */
> +            soc->ram_base = soc->ahb_slave[4] & 0xfff00000;
> +            /* 2. Remap ROM to base of ROM + size of RAM */
> +            soc->rom_base = soc->ram_base
> +                          + ((1 << extract32(soc->ahb_slave[6], 16, 4)) << 20);
> +            /* 3. Update ROM memory map */
> +            sysbus_mmio_map(SYS_BUS_DEVICE(soc->rom), 0, soc->rom_base);
> +            /* 4. Update RAM memory map if it has been initialized. */
> +            if (soc->ddr_inited) {
> +                memory_region_del_subregion(soc->as, soc->ram);
> +                memory_region_add_subregion(soc->as, soc->ram_base, soc->ram);
> +            }
> +            soc->ahb_remapped = true;
> +        }
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "ftahbc020: undefined memory access@0x%llx\n", addr);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = ftahbc020_mem_read,
> +    .write = ftahbc020_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    }
> +};
> +
> +static void ftahbc020_reset(DeviceState *ds)
> +{
> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
> +    Ftahbc020State *s = FTAHBC020(FROM_SYSBUS(Ftahbc020State, busdev));
> +    FaradaySoCState *soc = FARADAY_SOC_GET_CORE();
> +
> +    if (soc->ahb_remapped && !soc->bi) {
> +        soc->rom_base = soc->ahb_slave[4] & 0xfff00000;
> +        soc->ram_base = soc->ahb_slave[6] & 0xfff00000;
> +        sysbus_mmio_map(SYS_BUS_DEVICE(soc->rom), 0, soc->rom_base);
> +        soc->ahb_remapped = false;
> +    }
> +
> +    s->cr = 0;
> +}
> +
> +static int ftahbc020_init(SysBusDevice *dev)
> +{
> +    Ftahbc020State *s = FTAHBC020(FROM_SYSBUS(Ftahbc020State, dev));
> +
> +    memory_region_init_io(&s->iomem,
> +                          &mmio_ops,
> +                          s,
> +                          TYPE_FTAHBC020,
> +                          0x1000);
> +    sysbus_init_mmio(dev, &s->iomem);
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_ftahbc020 = {
> +    .name = TYPE_FTAHBC020,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(cr, Ftahbc020State),
> +        VMSTATE_END_OF_LIST(),
> +    }
> +};
> +
> +static void ftahbc020_class_init(ObjectClass *klass, void *data)
> +{
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    k->init   = ftahbc020_init;
> +    dc->desc  = TYPE_FTAHBC020;
> +    dc->vmsd  = &vmstate_ftahbc020;
> +    dc->reset = ftahbc020_reset;
> +    dc->no_user = 1;
> +}
> +
> +static const TypeInfo ftahbc020_info = {
> +    .name          = TYPE_FTAHBC020,
> +    .parent        = TYPE_FARADAY_SOC,
> +    .instance_size = sizeof(Ftahbc020State),
> +    .class_init    = ftahbc020_class_init,
> +};
> +
> +static void ftahbc020_register_types(void)
> +{
> +    type_register_static(&ftahbc020_info);
> +}
> +
> +type_init(ftahbc020_register_types)
> --
> 1.7.9.5
>
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 05/24] hw/arm: add Faraday FTDDRII030 support
       [not found] ` <1361949350-22241-6-git-send-email-dantesu@gmail.com>
@ 2013-03-04  3:56   ` Peter Crosthwaite
  2013-03-04  6:32     ` Kuo-Jung Su
  0 siblings, 1 reply; 15+ messages in thread
From: Peter Crosthwaite @ 2013-03-04  3:56 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, i.mitsyanko, qemu-devel, Blue Swirl, Paul Brook,
	Kuo-Jung Su, Andreas, fred.konrad

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 FTDDRII030 is a DDRII SDRAM controller which is responsible for
> SDRAM initialization.
> In QEMU we emualte only the SDRAM enable function.
>

"emulate"

> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> ---
>  hw/arm/Makefile.objs      |    1 +
>  hw/arm/faraday_a369_soc.c |    3 +
>  hw/arm/ftddrii030.c       |  171 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 175 insertions(+)
>  create mode 100644 hw/arm/ftddrii030.c
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 33c9482..2a4c7d6 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -39,3 +39,4 @@ obj-y += faraday_a369.o \
>              faraday_a369_kpd.o
>  obj-y += ftintc020.o
>  obj-y += ftahbc020.o
> +obj-y += ftddrii030.o
> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
> index e7343d9..fdf13f8 100644
> --- a/hw/arm/faraday_a369_soc.c
> +++ b/hw/arm/faraday_a369_soc.c
> @@ -148,6 +148,9 @@ a369soc_device_init(FaradaySoCState *s)
>
>      /* ftahbc020 */
>      s->ahbc = sysbus_create_simple("ftahbc020", 0x94000000, NULL);
> +
> +    /* ftddrii030 */
> +    s->ddrc = sysbus_create_simple("ftddrii030", 0x93100000, NULL);
>  }
>
>  static int a369soc_init(SysBusDevice *busdev)
> diff --git a/hw/arm/ftddrii030.c b/hw/arm/ftddrii030.c
> new file mode 100644
> index 0000000..1679bef
> --- /dev/null
> +++ b/hw/arm/ftddrii030.c
> @@ -0,0 +1,171 @@
> +/*
> + * Faraday DDRII controller
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * 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_MCR             0x00    /* memory configuration register */
> +#define REG_MSR             0x04    /* memory status register */
> +#define REG_REVR            0x50    /* revision register */
> +
> +#define MSR_INIT_OK         BIT(8)  /* DDR2 initial is completed */
> +#define MSR_CMD_MRS         BIT(0)  /* start MRS command */
> +
> +#define CFG_REGSIZE         (0x50 / 4)
> +
> +#define TYPE_FTDDRII030     "ftddrii030"
> +
> +typedef struct Ftddrii030State {
> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +
> +    /* HW register cache */
> +    uint32_t regs[CFG_REGSIZE];
> +} Ftddrii030State;
> +
> +#define FTDDRII030(obj) \
> +    OBJECT_CHECK(Ftddrii030State, obj, TYPE_FTDDRII030)
> +
> +#define DDR_REG32(s, off) \
> +    *(uint32_t *)((uint8_t *)(s)->regs + (off))

Strange. You are forcing alignment in your memory region ops so Im not
sure I see the need for this. Cant you just index directly ...

> +
> +static uint64_t
> +ftddrii030_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftddrii030State *s = FTDDRII030(opaque);
> +    FaradaySoCState *soc = FARADAY_SOC_GET_CORE();
> +    uint64_t ret = 0;
> +
> +    if (soc->ddr_inited) {
> +        DDR_REG32(s, REG_MSR) |= MSR_INIT_OK;

... like this?

s->regs[REG_MSR/4] |= MSR_INIT_OK

> +    }
> +
> +    switch (addr) {
> +    case REG_MCR ... 0x4c:

s/0x4c/CFG_REGSIZE or something like it. I think you can define you
macros to get rid of this magic number that is really just the end of
your CFG_REGSIZE definition.

> +        ret = s->regs[addr / 4];
> +        break;
> +    case REG_REVR:
> +        ret = 0x100;    /* rev. = 0.1.0 */
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "ftddrii030: undefined memory access@0x%llx\n", addr);
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static void
> +ftddrii030_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
> +{
> +    Ftddrii030State *s = FTDDRII030(opaque);
> +    FaradaySoCState *soc = FARADAY_SOC_GET_CORE();
> +
> +    switch (addr) {
> +    case REG_MCR:
> +        DDR_REG32(s, REG_MCR) = (uint32_t)val & 0xffff;
> +        break;
> +    case REG_MSR:
> +        val = (val & 0x3f) | (DDR_REG32(s, REG_MSR) & MSR_INIT_OK);
> +        if (!soc->ddr_inited && (val & MSR_CMD_MRS)) {
> +            val &= ~MSR_CMD_MRS;
> +            val |= MSR_INIT_OK;
> +            memory_region_add_subregion(soc->as, soc->ram_base, soc->ram);
> +            soc->ddr_inited = true;
> +        }
> +        DDR_REG32(s, REG_MSR) = (uint32_t)val;
> +        break;
> +    case 0x08 ... 0x4c: /* DDRII Timing, ECC ...etc. */
> +        s->regs[addr / 4] = (uint32_t)val;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "ftddrii030: undefined memory access@0x%llx\n", addr);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = ftddrii030_mem_read,
> +    .write = ftddrii030_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    }
> +};
> +
> +static void ftddrii030_reset(DeviceState *ds)
> +{
> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
> +    Ftddrii030State *s = FTDDRII030(FROM_SYSBUS(Ftddrii030State, busdev));
> +    FaradaySoCState *soc = FARADAY_SOC_GET_CORE();
> +
> +    if (soc->ddr_inited && !soc->bi) {
> +        memory_region_del_subregion(soc->as, soc->ram);
> +        soc->ddr_inited = false;
> +    }
> +
> +    memset(s->regs, 0, sizeof(s->regs));
> +}
> +
> +static int ftddrii030_init(SysBusDevice *dev)
> +{
> +    Ftddrii030State *s = FTDDRII030(FROM_SYSBUS(Ftddrii030State, dev));
> +
> +    memory_region_init_io(&s->iomem,
> +                          &mmio_ops,
> +                          s,
> +                          TYPE_FTDDRII030,
> +                          0x1000);
> +    sysbus_init_mmio(dev, &s->iomem);
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_ftddrii030 = {
> +    .name = TYPE_FTDDRII030,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, Ftddrii030State, CFG_REGSIZE),
> +        VMSTATE_END_OF_LIST(),
> +    }
> +};
> +
> +static void ftddrii030_class_init(ObjectClass *klass, void *data)
> +{
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    k->init   = ftddrii030_init;
> +    dc->desc  = TYPE_FTDDRII030;
> +    dc->vmsd  = &vmstate_ftddrii030;
> +    dc->reset = ftddrii030_reset;
> +    dc->no_user = 1;
> +}
> +
> +static const TypeInfo ftddrii030_info = {
> +    .name          = TYPE_FTDDRII030,
> +    .parent        = TYPE_FARADAY_SOC,
> +    .instance_size = sizeof(Ftddrii030State),
> +    .class_init    = ftddrii030_class_init,
> +};
> +
> +static void ftddrii030_register_types(void)
> +{
> +    type_register_static(&ftddrii030_info);
> +}
> +
> +type_init(ftddrii030_register_types)
> --
> 1.7.9.5
>
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 02/24] hw/arm: add Faraday a369 SoC platform support
  2013-03-01 19:00     ` [Qemu-devel] [PATCH v5 02/24] hw/arm: add Faraday a369 SoC platform support Igor Mitsyanko
@ 2013-03-04  6:06       ` Kuo-Jung Su
  0 siblings, 0 replies; 15+ messages in thread
From: Kuo-Jung Su @ 2013-03-04  6:06 UTC (permalink / raw)
  To: Igor Mitsyanko
  Cc: Peter Maydell, qemu-devel, Blue Swirl, Paul Brook, Kuo-Jung Su,
	Andreas, fred.konrad

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



-- 
Best wishes,
Kuo-Jung Su

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 02/24] hw/arm: add Faraday a369 SoC platform support
  2013-03-02  3:43   ` Peter Crosthwaite
@ 2013-03-04  6:09     ` Kuo-Jung Su
  0 siblings, 0 replies; 15+ messages in thread
From: Kuo-Jung Su @ 2013-03-04  6:09 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred.konrad

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

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 03/24] hw/arm: add Faraday FTINTC020 interrupt controller support
  2013-03-02  4:13   ` [Qemu-devel] [PATCH v5 03/24] hw/arm: add Faraday FTINTC020 interrupt controller support Peter Crosthwaite
  2013-03-03  4:58     ` Peter Crosthwaite
  2013-03-03  6:29     ` Peter Crosthwaite
@ 2013-03-04  6:20     ` Kuo-Jung Su
  2013-03-04  6:33       ` Peter Crosthwaite
  2 siblings, 1 reply; 15+ messages in thread
From: Kuo-Jung Su @ 2013-03-04  6:20 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred.konrad

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 FTINTC020 interrupt controller supports both FIQ and IRQ signals
>> to the microprocessor.
>> It can handle up to 64 configurable IRQ sources and 64 FIQ sources.
>> The output signals to the microprocessor can be configured as
>> level-high/low active or edge-rising/falling triggered.
>>
>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>> ---
>>  hw/arm/Makefile.objs      |    1 +
>>  hw/arm/faraday.h          |    3 +
>>  hw/arm/faraday_a369_soc.c |   10 +-
>>  hw/arm/ftintc020.c        |  366 +++++++++++++++++++++++++++++++++++++++++++++
>>  hw/arm/ftintc020.h        |   48 ++++++
>>  5 files changed, 425 insertions(+), 3 deletions(-)
>>  create mode 100644 hw/arm/ftintc020.c
>>  create mode 100644 hw/arm/ftintc020.h
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index f6fd60d..6771072 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -37,3 +37,4 @@ obj-y += faraday_a369.o \
>>              faraday_a369_soc.o \
>>              faraday_a369_scu.o \
>>              faraday_a369_kpd.o
>> +obj-y += ftintc020.o
>> diff --git a/hw/arm/faraday.h b/hw/arm/faraday.h
>> index d6ed860..e5f611d 100644
>> --- a/hw/arm/faraday.h
>> +++ b/hw/arm/faraday.h
>> @@ -62,4 +62,7 @@ typedef struct FaradaySoCState {
>>      FARADAY_SOC(object_resolve_path_component(qdev_get_machine(), \
>>                                                TYPE_FARADAY_SOC))
>>
>> +/* ftintc020.c */
>> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu);
>> +
>>  #endif
>> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
>> index 0372868..3d861d2 100644
>> --- a/hw/arm/faraday_a369_soc.c
>> +++ b/hw/arm/faraday_a369_soc.c
>> @@ -73,6 +73,7 @@ a369soc_device_init(FaradaySoCState *s)
>>  {
>>      DriveInfo *dinfo;
>>      DeviceState *ds;
>> +    qemu_irq *pic;
>>
>>      s->as = get_system_memory();
>>      s->ram = g_new(MemoryRegion, 1);
>> @@ -115,12 +116,15 @@ a369soc_device_init(FaradaySoCState *s)
>>          exit(1);
>>      }
>>
>> +    /* Interrupt Controller */
>> +    pic = ftintc020_init(0x90100000, s->cpu);
>> +
>>      /* Serial (FTUART010 which is 16550A compatible) */
>>      if (serial_hds[0]) {
>>          serial_mm_init(s->as,
>>                         0x92b00000,
>>                         2,
>> -                       NULL,
>> +                       pic[53],
>>                         18432000,
>>                         serial_hds[0],
>>                         DEVICE_LITTLE_ENDIAN);
>> @@ -129,7 +133,7 @@ a369soc_device_init(FaradaySoCState *s)
>>          serial_mm_init(s->as,
>>                         0x92c00000,
>>                         2,
>> -                       NULL,
>> +                       pic[54],
>>                         18432000,
>>                         serial_hds[1],
>>                         DEVICE_LITTLE_ENDIAN);
>> @@ -140,7 +144,7 @@ a369soc_device_init(FaradaySoCState *s)
>>      s->scu = ds;
>>
>>      /* ftkbc010 */
>> -    sysbus_create_simple("a369.keypad", 0x92f00000, NULL);
>> +    sysbus_create_simple("a369.keypad", 0x92f00000, pic[21]);
>>  }
>>
>>  static int a369soc_init(SysBusDevice *busdev)
>> diff --git a/hw/arm/ftintc020.c b/hw/arm/ftintc020.c
>> new file mode 100644
>> index 0000000..a7f6454
>> --- /dev/null
>> +++ b/hw/arm/ftintc020.c
>> @@ -0,0 +1,366 @@
>> +/*
>> + * Faraday FTINTC020 Programmable Interrupt Controller.
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This code is licensed under GNU GPL v2+.
>> + */
>> +
>> +#include "hw/hw.h"
>> +#include "hw/sysbus.h"
>> +
>> +#include "faraday.h"
>> +#include "ftintc020.h"
>> +
>> +#define TYPE_FTINTC020  "ftintc020"
>> +
>> +typedef struct Ftintc020State {
>> +    SysBusDevice busdev;
>> +    MemoryRegion iomem;
>> +    ARMCPU *cpu;
>> +    qemu_irq irqs[64];
>> +
>> +    uint32_t irq_pin[2];    /* IRQ pin state */
>> +    uint32_t fiq_pin[2];    /* IRQ pin state */
>> +
>> +    /* HW register caches */
>> +    uint32_t irq_src[2];    /* IRQ source register */
>> +    uint32_t irq_ena[2];    /* IRQ enable register */
>> +    uint32_t irq_mod[2];    /* IRQ mode register */
>> +    uint32_t irq_lvl[2];    /* IRQ level register */
>> +    uint32_t fiq_src[2];    /* FIQ source register */
>> +    uint32_t fiq_ena[2];    /* FIQ enable register */
>> +    uint32_t fiq_mod[2];    /* FIQ mode register */
>> +    uint32_t fiq_lvl[2];    /* FIQ level register */
>> +} Ftintc020State;
>> +
>> +#define FTINTC020(obj) \
>> +    OBJECT_CHECK(Ftintc020State, obj, TYPE_FTINTC020)
>> +
>> +static void
>> +ftintc020_update(Ftintc020State *s)
>> +{
>> +    uint32_t mask[2];
>> +
>> +    /* FIQ */
>> +    mask[0] = s->fiq_src[0] & s->fiq_ena[0];
>> +    mask[1] = s->fiq_src[1] & s->fiq_ena[1];
>> +
>> +    if (mask[0] || mask[1]) {
>> +        cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
>> +    } else {
>> +        cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
>> +    }
>> +
>> +    /* IRQ */
>> +    mask[0] = s->irq_src[0] & s->irq_ena[0];
>> +    mask[1] = s->irq_src[1] & s->irq_ena[1];
>> +
>> +    if (mask[0] || mask[1]) {
>> +        cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
>> +    } else {
>> +        cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
>> +    }
>> +}
>> +
>> +/* Note: Here level means state of the signal on a pin */
>> +static void
>> +ftintc020_set_irq(void *opaque, int irq, int level)
>> +{
>> +    Ftintc020State *s = FTINTC020(opaque);
>> +    uint32_t i = irq / 32;
>> +    uint32_t mask = 1 << (irq % 32);
>> +
>> +    if (s->irq_mod[i] & mask) {
>> +        /* Edge Triggered */
>> +        if (s->irq_lvl[i] & mask) {
>> +            /* Falling Active */
>> +            if ((s->irq_pin[i] & mask) && !level) {
>> +                s->irq_src[i] |=  mask;
>> +                s->fiq_src[i] |=  mask;
>> +            }
>> +        } else {
>> +            /* Rising Active */
>> +            if (!(s->irq_pin[i] & mask) && level) {
>> +                s->irq_src[i] |=  mask;
>> +                s->fiq_src[i] |=  mask;
>> +            }
>> +        }
>> +    } else {
>> +        /* Level Triggered */
>> +        if (s->irq_lvl[i] & mask) {
>> +            /* Low Active */
>> +            if (level) {
>> +                s->irq_src[i] &= ~mask;
>> +                s->fiq_src[i] &= ~mask;
>> +            } else {
>> +                s->irq_src[i] |=  mask;
>> +                s->fiq_src[i] |=  mask;
>> +            }
>> +        } else {
>> +            /* High Active */
>> +            if (!level) {
>> +                s->irq_src[i] &= ~mask;
>> +                s->fiq_src[i] &= ~mask;
>> +            } else {
>> +                s->irq_src[i] |=  mask;
>> +                s->fiq_src[i] |=  mask;
>> +            }
>> +        }
>> +    }
>> +
>> +    /* update IRQ/FIQ pin states */
>> +    if (level) {
>> +        s->irq_pin[i] |= mask;
>> +        s->fiq_pin[i] |= mask;
>> +    } else {
>> +        s->irq_pin[i] &= ~mask;
>> +        s->fiq_pin[i] &= ~mask;
>> +    }
>> +
>> +    ftintc020_update(s);
>> +}
>> +
>> +static uint64_t
>> +ftintc020_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    Ftintc020State *s = FTINTC020(opaque);
>> +
>> +    switch (addr) {
>> +    /*
>> +     * IRQ
>> +     */
>> +    case REG_IRQSRC:
>> +        return s->irq_src[0];
>> +    case REG_IRQENA:
>> +        return s->irq_ena[0];
>> +    case REG_IRQMDR:
>> +        return s->irq_mod[0];
>> +    case REG_IRQLVR:
>> +        return s->irq_lvl[0];
>> +    case REG_IRQSR:
>> +        return s->irq_src[0] & s->irq_ena[0];
>> +    case REG_EIRQSRC:
>> +        return s->irq_src[1];
>> +    case REG_EIRQENA:
>> +        return s->irq_ena[1];
>> +    case REG_EIRQMDR:
>> +        return s->irq_mod[1];
>> +    case REG_EIRQLVR:
>> +        return s->irq_lvl[1];
>> +    case REG_EIRQSR:
>> +        return s->irq_src[1] & s->irq_ena[1];
>> +
>
> AFAICT, index 0 of there arrays in for IRQ and index 1 is for EIRQ.
> Can you #define some symbols accrordingly and remove all the magic
> numberage with the [0]'s and [1]'s?
>

Sure, the ftintc020 is going to be redesigned with the 'hw/pl190.c' as template.
And all the coding style issues would be updated.

>> +    /*
>> +     * FIQ
>> +     */
>> +    case REG_FIQSRC:
>> +        return s->fiq_src[0];
>> +    case REG_FIQENA:
>> +        return s->fiq_ena[0];
>> +    case REG_FIQMDR:
>> +        return s->fiq_mod[0];
>> +    case REG_FIQLVR:
>> +        return s->fiq_lvl[0];
>> +    case REG_FIQSR:
>> +        return s->fiq_src[0] & s->fiq_ena[0];
>> +    case REG_EFIQSRC:
>> +        return s->fiq_src[1];
>> +    case REG_EFIQENA:
>> +        return s->fiq_ena[1];
>> +    case REG_EFIQMDR:
>> +        return s->fiq_mod[1];
>> +    case REG_EFIQLVR:
>> +        return s->fiq_lvl[1];
>> +    case REG_EFIQSR:
>> +        return s->fiq_src[1] & s->fiq_ena[1];
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
>> +        return 0;
>> +    }
>> +}
>> +
>> +static void
>> +ftintc020_mem_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
>> +{
>> +    Ftintc020State *s = FTINTC020(opaque);
>> +
>> +    switch (addr) {
>> +    /*
>> +     * IRQ
>> +     */
>
> Ok to use one line comment. And elsewhere
>
>> +    case REG_IRQENA:
>> +        s->irq_ena[0] = (uint32_t)value;
>> +        break;
>> +    case REG_IRQSCR:
>> +        value = ~(value & s->irq_mod[0]);
>> +        s->irq_src[0] &= (uint32_t)value;
>> +        break;
>> +    case REG_IRQMDR:
>> +        s->irq_mod[0] = (uint32_t)value;
>> +        break;
>> +    case REG_IRQLVR:
>> +        s->irq_lvl[0] = (uint32_t)value;
>> +        break;
>> +    case REG_EIRQENA:
>> +        s->irq_ena[1] = (uint32_t)value;
>> +        break;
>> +    case REG_EIRQSCR:
>> +        value = ~(value & s->irq_mod[1]);
>> +        s->irq_src[1] &= (uint32_t)value;
>> +        break;
>> +    case REG_EIRQMDR:
>> +        s->irq_mod[1] = (uint32_t)value;
>> +        break;
>> +    case REG_EIRQLVR:
>> +        s->irq_lvl[1] = (uint32_t)value;
>> +        break;
>> +
>> +    /*
>> +     * FIQ
>> +     */
>> +    case REG_FIQENA:
>> +        s->fiq_ena[0] = (uint32_t)value;
>> +        break;
>> +    case REG_FIQSCR:
>> +        value = ~(value & s->fiq_mod[0]);
>> +        s->fiq_src[0] &= (uint32_t)value;
>> +        break;
>> +    case REG_FIQMDR:
>> +        s->fiq_mod[0] = (uint32_t)value;
>> +        break;
>> +    case REG_FIQLVR:
>> +        s->fiq_lvl[0] = (uint32_t)value;
>> +        break;
>> +    case REG_EFIQENA:
>> +        s->fiq_ena[1] = (uint32_t)value;
>> +        break;
>> +    case REG_EFIQSCR:
>> +        value = ~(value & s->fiq_mod[1]);
>> +        s->fiq_src[1] &= (uint32_t)value;
>> +        break;
>> +    case REG_EFIQMDR:
>> +        s->fiq_mod[1] = (uint32_t)value;
>> +        break;
>> +    case REG_EFIQLVR:
>> +        s->fiq_lvl[1] = (uint32_t)value;
>> +        break;
>> +
>> +    /* don't care */
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
>> +        return;
>> +    }
>> +
>> +    ftintc020_update(s);
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = ftintc020_mem_read,
>> +    .write = ftintc020_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    }
>> +};
>> +
>> +static void ftintc020_reset(DeviceState *ds)
>> +{
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev));
>> +
>> +    s->irq_pin[0] = 0;
>> +    s->irq_pin[1] = 0;
>> +    s->fiq_pin[0] = 0;
>> +    s->fiq_pin[1] = 0;
>> +
>> +    s->irq_src[0] = 0;
>> +    s->irq_src[1] = 0;
>> +    s->irq_ena[0] = 0;
>> +    s->irq_ena[1] = 0;
>> +    s->fiq_src[0] = 0;
>> +    s->fiq_src[1] = 0;
>> +    s->fiq_ena[0] = 0;
>> +    s->fiq_ena[1] = 0;
>> +
>> +    ftintc020_update(s);
>> +}
>> +
>> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu)
>
> I'm not sure this is the right place for this, I think device creation
> helpers belong on the machine / SoC level. Keep the device model self
> contained and clients call qdev_init themselves. Andreas have the
> rules change on this recently?
>
>> +{
>> +    int i;
>> +    DeviceState *ds = qdev_create(NULL, TYPE_FTINTC020);
>
> As the device is intended for use in an SoC, the SoC potentially needs
> to hold a pointer to it in order to call device destructors. This
> function should return ds for future use by the instantiator.
>
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev));
>> +
>
> Use an Object cast macro. Andreas, can we make this easier for
> reviewers and developers by adding blacklisted identifiers to
> checkpatch perhaps? throw a warning on +FROM_SYSBUS, DO_UPCAST etc?
>
>> +    s->cpu = cpu;
>
> Im thinking this is a QOM link. Its a connection from one device to
> another and the machine should be aware of it.
>

Sorry, I can't get you well....
Did you mean adding the QOM link to cpu like this?

    .........
    s->cpu = cpu_arm_init(s->cpu_model);
    if (!s->cpu) {
        hw_error("a369: Unable to find CPU definition\n");
        exit(1);
    }
    object_property_add_child(qdev_get_machine(),
                              "cpu",
                              OBJECT(s->cpu),
                              NULL);
    .........

However this would lead to an error like this:

**
ERROR:qom/object.c:899:object_property_add_child: assertion failed:
(child->parent == NULL)

>> +    ftintc020_reset(ds);
>
> Doesn't look right but this is just the already registered
> DeviceClass::reset function anyways. You should just be able to delete
> this. Does your system function without it?
>
>> +    qdev_init_nofail(ds);
>
> Cutting from here ...
>
>> +    qdev_init_gpio_in(ds, ftintc020_set_irq, 64);
>> +    for (i = 0; i < 64; ++i) {
>> +        s->irqs[i] = qdev_get_gpio_in(ds, i);
>> +    }
>> +
>> +    /* Enable IC memory-mapped registers access.  */
>> +    memory_region_init_io(&s->iomem,
>> +                          &mmio_ops,
>> +                          s,
>> +                          TYPE_FTINTC020,
>> +                          0x1000);
>> +    sysbus_init_mmio(SYS_BUS_DEVICE(ds), &s->iomem);
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(ds), 0, base);
>> +
>
> ... Im pritty sure all of this belongs in either the ObjectClass::init
> or Device::realize functions.
>
>> +    return s->irqs;
>> +}
>> +
>> +static VMStateDescription vmstate_ftintc020 = {
>> +    .name = TYPE_FTINTC020,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT32_ARRAY(irq_src, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(irq_ena, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(irq_mod, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(irq_lvl, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(fiq_src, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(fiq_ena, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(fiq_mod, Ftintc020State, 2),
>> +        VMSTATE_UINT32_ARRAY(fiq_lvl, Ftintc020State, 2),
>> +        VMSTATE_END_OF_LIST(),
>> +    },
>> +};
>> +
>> +static int ftintc020_initfn(SysBusDevice *dev)
>> +{
>> +    return 0;
>> +}
>> +
>> +static void ftintc020_class_init(ObjectClass *klass, void *data)
>> +{
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    k->init     = ftintc020_initfn;
>
> SysBusDevice::init is deprecated in favour of the Device::init. Your
> SBD::init doesnt do anything so you can just delete it. But you should
> add a device Init here to bring the sysbus foo from your helper
> instantiator into the device model.
>
> Regards,
> Peter
>
>> +    dc->vmsd    = &vmstate_ftintc020;
>> +    dc->reset   = ftintc020_reset;
>> +    dc->no_user = 1;
>> +}
>> +
>> +static const TypeInfo ftintc020_info = {
>> +    .name          = TYPE_FTINTC020,
>> +    .parent        = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size = sizeof(Ftintc020State),
>> +    .class_init    = ftintc020_class_init,
>> +};
>> +
>> +static void ftintc020_register_types(void)
>> +{
>> +    type_register_static(&ftintc020_info);
>> +}
>> +
>> +type_init(ftintc020_register_types)
>> diff --git a/hw/arm/ftintc020.h b/hw/arm/ftintc020.h
>> new file mode 100644
>> index 0000000..f17fb58
>> --- /dev/null
>> +++ b/hw/arm/ftintc020.h
>> @@ -0,0 +1,48 @@
>> +/*
>> + * Faraday FTINTC020 Programmable Interrupt 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_FTINTC020_H
>> +#define HW_ARM_FTINTC020_H
>> +
>> +/*
>> + * IRQ/FIO: 0 ~ 31
>> + */
>> +#define REG_IRQSRC      0x00    /* IRQ source register */
>> +#define REG_IRQENA      0x04    /* IRQ enable register */
>> +#define REG_IRQSCR      0x08    /* IRQ status clear register */
>> +#define REG_IRQMDR      0x0C    /* IRQ trigger mode register */
>> +#define REG_IRQLVR      0x10    /* IRQ trigger level register */
>> +#define REG_IRQSR       0x14    /* IRQ status register */
>> +
>> +#define REG_FIQSRC      0x20    /* FIQ source register */
>> +#define REG_FIQENA      0x24    /* FIQ enable register */
>> +#define REG_FIQSCR      0x28    /* FIQ status clear register */
>> +#define REG_FIQMDR      0x2C    /* FIQ trigger mode register */
>> +#define REG_FIQLVR      0x30    /* FIQ trigger level register */
>> +#define REG_FIQSR       0x34    /* FIQ status register */
>> +
>> +/*
>> + * Extended IRQ/FIO: 32 ~ 63
>> + */
>> +
>> +#define REG_EIRQSRC      0x60   /* Extended IRQ source register */
>> +#define REG_EIRQENA      0x64   /* Extended IRQ enable register */
>> +#define REG_EIRQSCR      0x68   /* Extended IRQ status clear register */
>> +#define REG_EIRQMDR      0x6C   /* Extended IRQ trigger mode register */
>> +#define REG_EIRQLVR      0x70   /* Extended IRQ trigger level register */
>> +#define REG_EIRQSR       0x74   /* Extended IRQ status register */
>> +
>> +#define REG_EFIQSRC      0x80   /* Extended FIQ source register */
>> +#define REG_EFIQENA      0x84   /* Extended FIQ enable register */
>> +#define REG_EFIQSCR      0x88   /* Extended FIQ status clear register */
>> +#define REG_EFIQMDR      0x8C   /* Extended FIQ trigger mode register */
>> +#define REG_EFIQLVR      0x90   /* Extended FIQ trigger level register */
>> +#define REG_EFIQSR       0x94   /* Extended FIQ status register */
>> +
>> +#endif
>> --
>> 1.7.9.5
>>
>>



--
Best wishes,
Kuo-Jung Su

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 03/24] hw/arm: add Faraday FTINTC020 interrupt controller support
  2013-03-03  6:29     ` Peter Crosthwaite
@ 2013-03-04  6:29       ` Kuo-Jung Su
  0 siblings, 0 replies; 15+ messages in thread
From: Kuo-Jung Su @ 2013-03-04  6:29 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred.konrad

2013/3/3 Peter Crosthwaite <peter.crosthwaite@xilinx.com>:
> Hi Kuo-Jung,
>
> On Sat, Mar 2, 2013 at 2:13 PM, Peter Crosthwaite
> <peter.crosthwaite@xilinx.com> wrote:
>> 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 FTINTC020 interrupt controller supports both FIQ and IRQ signals
>>> to the microprocessor.
>>> It can handle up to 64 configurable IRQ sources and 64 FIQ sources.
>>> The output signals to the microprocessor can be configured as
>>> level-high/low active or edge-rising/falling triggered.
>>>
>>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>>> ---
>>>  hw/arm/Makefile.objs      |    1 +
>>>  hw/arm/faraday.h          |    3 +
>>>  hw/arm/faraday_a369_soc.c |   10 +-
>>>  hw/arm/ftintc020.c        |  366 +++++++++++++++++++++++++++++++++++++++++++++
>>>  hw/arm/ftintc020.h        |   48 ++++++
>>>  5 files changed, 425 insertions(+), 3 deletions(-)
>>>  create mode 100644 hw/arm/ftintc020.c
>>>  create mode 100644 hw/arm/ftintc020.h
>>>
>>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>>> index f6fd60d..6771072 100644
>>> --- a/hw/arm/Makefile.objs
>>> +++ b/hw/arm/Makefile.objs
>>> @@ -37,3 +37,4 @@ obj-y += faraday_a369.o \
>>>              faraday_a369_soc.o \
>>>              faraday_a369_scu.o \
>>>              faraday_a369_kpd.o
>>> +obj-y += ftintc020.o
>>> diff --git a/hw/arm/faraday.h b/hw/arm/faraday.h
>>> index d6ed860..e5f611d 100644
>>> --- a/hw/arm/faraday.h
>>> +++ b/hw/arm/faraday.h
>>> @@ -62,4 +62,7 @@ typedef struct FaradaySoCState {
>>>      FARADAY_SOC(object_resolve_path_component(qdev_get_machine(), \
>>>                                                TYPE_FARADAY_SOC))
>>>
>>> +/* ftintc020.c */
>>> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu);
>>> +
>>>  #endif
>>> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
>>> index 0372868..3d861d2 100644
>>> --- a/hw/arm/faraday_a369_soc.c
>>> +++ b/hw/arm/faraday_a369_soc.c
>>> @@ -73,6 +73,7 @@ a369soc_device_init(FaradaySoCState *s)
>>>  {
>>>      DriveInfo *dinfo;
>>>      DeviceState *ds;
>>> +    qemu_irq *pic;
>>>
>>>      s->as = get_system_memory();
>>>      s->ram = g_new(MemoryRegion, 1);
>>> @@ -115,12 +116,15 @@ a369soc_device_init(FaradaySoCState *s)
>>>          exit(1);
>>>      }
>>>
>>> +    /* Interrupt Controller */
>>> +    pic = ftintc020_init(0x90100000, s->cpu);
>>> +
>>>      /* Serial (FTUART010 which is 16550A compatible) */
>>>      if (serial_hds[0]) {
>>>          serial_mm_init(s->as,
>>>                         0x92b00000,
>>>                         2,
>>> -                       NULL,
>>> +                       pic[53],
>>>                         18432000,
>>>                         serial_hds[0],
>>>                         DEVICE_LITTLE_ENDIAN);
>>> @@ -129,7 +133,7 @@ a369soc_device_init(FaradaySoCState *s)
>>>          serial_mm_init(s->as,
>>>                         0x92c00000,
>>>                         2,
>>> -                       NULL,
>>> +                       pic[54],
>>>                         18432000,
>>>                         serial_hds[1],
>>>                         DEVICE_LITTLE_ENDIAN);
>>> @@ -140,7 +144,7 @@ a369soc_device_init(FaradaySoCState *s)
>>>      s->scu = ds;
>>>
>>>      /* ftkbc010 */
>>> -    sysbus_create_simple("a369.keypad", 0x92f00000, NULL);
>>> +    sysbus_create_simple("a369.keypad", 0x92f00000, pic[21]);
>>>  }
>>>
>>>  static int a369soc_init(SysBusDevice *busdev)
>>> diff --git a/hw/arm/ftintc020.c b/hw/arm/ftintc020.c
>>> new file mode 100644
>>> index 0000000..a7f6454
>>> --- /dev/null
>>> +++ b/hw/arm/ftintc020.c
>>> @@ -0,0 +1,366 @@
>>> +/*
>>> + * Faraday FTINTC020 Programmable Interrupt Controller.
>>> + *
>>> + * Copyright (c) 2012 Faraday Technology
>>> + * Written by Dante Su <dantesu@faraday-tech.com>
>>> + *
>>> + * This code is licensed under GNU GPL v2+.
>>> + */
>>> +
>>> +#include "hw/hw.h"
>>> +#include "hw/sysbus.h"
>>> +
>>> +#include "faraday.h"
>>> +#include "ftintc020.h"
>>> +
>>> +#define TYPE_FTINTC020  "ftintc020"
>>> +
>>> +typedef struct Ftintc020State {
>>> +    SysBusDevice busdev;
>>> +    MemoryRegion iomem;
>>> +    ARMCPU *cpu;
>
> So Ive looked into your init routine problem a little more and
> checkout out how its handled by other ARM interrupt controllers. I
> think its gone wrong here, in that an interrupt controller should not
> have a handle to a CPU at all. It should just have GPIO outputs for
> the interrupt wires. Replace this with GPIO outputs for your intcs IRQ
> and FIQ output. This removes the need for your machine model to pass
> in an ARMCPU to the device (whether that be via your Ad-Hoc creation
> helper or via a QOM link).
>

At the time I was writing the model for FTINTC020, I found that
hw/pxa2xx_pic.c is more straightforward to me.
However it turns out that I've made a bad decision.

>>> +    qemu_irq irqs[64];
>>> +
>>> +    uint32_t irq_pin[2];    /* IRQ pin state */
>>> +    uint32_t fiq_pin[2];    /* IRQ pin state */
>>> +
>>> +    /* HW register caches */
>>> +    uint32_t irq_src[2];    /* IRQ source register */
>>> +    uint32_t irq_ena[2];    /* IRQ enable register */
>>> +    uint32_t irq_mod[2];    /* IRQ mode register */
>>> +    uint32_t irq_lvl[2];    /* IRQ level register */
>>> +    uint32_t fiq_src[2];    /* FIQ source register */
>>> +    uint32_t fiq_ena[2];    /* FIQ enable register */
>>> +    uint32_t fiq_mod[2];    /* FIQ mode register */
>>> +    uint32_t fiq_lvl[2];    /* FIQ level register */
>>> +} Ftintc020State;
>>> +
>>> +#define FTINTC020(obj) \
>>> +    OBJECT_CHECK(Ftintc020State, obj, TYPE_FTINTC020)
>>> +
>>> +static void
>>> +ftintc020_update(Ftintc020State *s)
>>> +{
>>> +    uint32_t mask[2];
>>> +
>>> +    /* FIQ */
>>> +    mask[0] = s->fiq_src[0] & s->fiq_ena[0];
>>> +    mask[1] = s->fiq_src[1] & s->fiq_ena[1];
>>> +
>>> +    if (mask[0] || mask[1]) {
>>> +        cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
>
> Access to the cpu->env from device land is generally bad. Replacing
> this logic with GPIO outputs would remove usage of the env.
>
>>> +    } else {
>>> +        cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_FIQ);
>>> +    }
>>> +
>>> +    /* IRQ */
>>> +    mask[0] = s->irq_src[0] & s->irq_ena[0];
>>> +    mask[1] = s->irq_src[1] & s->irq_ena[1];
>>> +
>>> +    if (mask[0] || mask[1]) {
>>> +        cpu_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
>>> +    } else {
>>> +        cpu_reset_interrupt(&s->cpu->env, CPU_INTERRUPT_HARD);
>>> +    }
>>> +}
>>> +
>>> +/* Note: Here level means state of the signal on a pin */
>>> +static void
>>> +ftintc020_set_irq(void *opaque, int irq, int level)
>>> +{
>>> +    Ftintc020State *s = FTINTC020(opaque);
>>> +    uint32_t i = irq / 32;
>>> +    uint32_t mask = 1 << (irq % 32);
>>> +
>>> +    if (s->irq_mod[i] & mask) {
>>> +        /* Edge Triggered */
>>> +        if (s->irq_lvl[i] & mask) {
>>> +            /* Falling Active */
>>> +            if ((s->irq_pin[i] & mask) && !level) {
>>> +                s->irq_src[i] |=  mask;
>>> +                s->fiq_src[i] |=  mask;
>>> +            }
>>> +        } else {
>>> +            /* Rising Active */
>>> +            if (!(s->irq_pin[i] & mask) && level) {
>>> +                s->irq_src[i] |=  mask;
>>> +                s->fiq_src[i] |=  mask;
>>> +            }
>>> +        }
>>> +    } else {
>>> +        /* Level Triggered */
>>> +        if (s->irq_lvl[i] & mask) {
>>> +            /* Low Active */
>>> +            if (level) {
>>> +                s->irq_src[i] &= ~mask;
>>> +                s->fiq_src[i] &= ~mask;
>>> +            } else {
>>> +                s->irq_src[i] |=  mask;
>>> +                s->fiq_src[i] |=  mask;
>>> +            }
>>> +        } else {
>>> +            /* High Active */
>>> +            if (!level) {
>>> +                s->irq_src[i] &= ~mask;
>>> +                s->fiq_src[i] &= ~mask;
>>> +            } else {
>>> +                s->irq_src[i] |=  mask;
>>> +                s->fiq_src[i] |=  mask;
>>> +            }
>>> +        }
>>> +    }
>>> +
>>> +    /* update IRQ/FIQ pin states */
>>> +    if (level) {
>>> +        s->irq_pin[i] |= mask;
>>> +        s->fiq_pin[i] |= mask;
>>> +    } else {
>>> +        s->irq_pin[i] &= ~mask;
>>> +        s->fiq_pin[i] &= ~mask;
>>> +    }
>>> +
>>> +    ftintc020_update(s);
>>> +}
>>> +
>>> +static uint64_t
>>> +ftintc020_mem_read(void *opaque, hwaddr addr, unsigned size)
>>> +{
>>> +    Ftintc020State *s = FTINTC020(opaque);
>>> +
>>> +    switch (addr) {
>>> +    /*
>>> +     * IRQ
>>> +     */
>>> +    case REG_IRQSRC:
>>> +        return s->irq_src[0];
>>> +    case REG_IRQENA:
>>> +        return s->irq_ena[0];
>>> +    case REG_IRQMDR:
>>> +        return s->irq_mod[0];
>>> +    case REG_IRQLVR:
>>> +        return s->irq_lvl[0];
>>> +    case REG_IRQSR:
>>> +        return s->irq_src[0] & s->irq_ena[0];
>>> +    case REG_EIRQSRC:
>>> +        return s->irq_src[1];
>>> +    case REG_EIRQENA:
>>> +        return s->irq_ena[1];
>>> +    case REG_EIRQMDR:
>>> +        return s->irq_mod[1];
>>> +    case REG_EIRQLVR:
>>> +        return s->irq_lvl[1];
>>> +    case REG_EIRQSR:
>>> +        return s->irq_src[1] & s->irq_ena[1];
>>> +
>>
>> AFAICT, index 0 of there arrays in for IRQ and index 1 is for EIRQ.
>> Can you #define some symbols accrordingly and remove all the magic
>> numberage with the [0]'s and [1]'s?
>>
>>> +    /*
>>> +     * FIQ
>>> +     */
>>> +    case REG_FIQSRC:
>>> +        return s->fiq_src[0];
>>> +    case REG_FIQENA:
>>> +        return s->fiq_ena[0];
>>> +    case REG_FIQMDR:
>>> +        return s->fiq_mod[0];
>>> +    case REG_FIQLVR:
>>> +        return s->fiq_lvl[0];
>>> +    case REG_FIQSR:
>>> +        return s->fiq_src[0] & s->fiq_ena[0];
>>> +    case REG_EFIQSRC:
>>> +        return s->fiq_src[1];
>>> +    case REG_EFIQENA:
>>> +        return s->fiq_ena[1];
>>> +    case REG_EFIQMDR:
>>> +        return s->fiq_mod[1];
>>> +    case REG_EFIQLVR:
>>> +        return s->fiq_lvl[1];
>>> +    case REG_EFIQSR:
>>> +        return s->fiq_src[1] & s->fiq_ena[1];
>>> +    default:
>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
>>> +        return 0;
>>> +    }
>>> +}
>>> +
>>> +static void
>>> +ftintc020_mem_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
>>> +{
>>> +    Ftintc020State *s = FTINTC020(opaque);
>>> +
>>> +    switch (addr) {
>>> +    /*
>>> +     * IRQ
>>> +     */
>>
>> Ok to use one line comment. And elsewhere
>>
>>> +    case REG_IRQENA:
>>> +        s->irq_ena[0] = (uint32_t)value;
>>> +        break;
>>> +    case REG_IRQSCR:
>>> +        value = ~(value & s->irq_mod[0]);
>>> +        s->irq_src[0] &= (uint32_t)value;
>>> +        break;
>>> +    case REG_IRQMDR:
>>> +        s->irq_mod[0] = (uint32_t)value;
>>> +        break;
>>> +    case REG_IRQLVR:
>>> +        s->irq_lvl[0] = (uint32_t)value;
>>> +        break;
>>> +    case REG_EIRQENA:
>>> +        s->irq_ena[1] = (uint32_t)value;
>>> +        break;
>>> +    case REG_EIRQSCR:
>>> +        value = ~(value & s->irq_mod[1]);
>>> +        s->irq_src[1] &= (uint32_t)value;
>>> +        break;
>>> +    case REG_EIRQMDR:
>>> +        s->irq_mod[1] = (uint32_t)value;
>>> +        break;
>>> +    case REG_EIRQLVR:
>>> +        s->irq_lvl[1] = (uint32_t)value;
>>> +        break;
>>> +
>>> +    /*
>>> +     * FIQ
>>> +     */
>>> +    case REG_FIQENA:
>>> +        s->fiq_ena[0] = (uint32_t)value;
>>> +        break;
>>> +    case REG_FIQSCR:
>>> +        value = ~(value & s->fiq_mod[0]);
>>> +        s->fiq_src[0] &= (uint32_t)value;
>>> +        break;
>>> +    case REG_FIQMDR:
>>> +        s->fiq_mod[0] = (uint32_t)value;
>>> +        break;
>>> +    case REG_FIQLVR:
>>> +        s->fiq_lvl[0] = (uint32_t)value;
>>> +        break;
>>> +    case REG_EFIQENA:
>>> +        s->fiq_ena[1] = (uint32_t)value;
>>> +        break;
>>> +    case REG_EFIQSCR:
>>> +        value = ~(value & s->fiq_mod[1]);
>>> +        s->fiq_src[1] &= (uint32_t)value;
>>> +        break;
>>> +    case REG_EFIQMDR:
>>> +        s->fiq_mod[1] = (uint32_t)value;
>>> +        break;
>>> +    case REG_EFIQLVR:
>>> +        s->fiq_lvl[1] = (uint32_t)value;
>>> +        break;
>>> +
>>> +    /* don't care */
>>> +    default:
>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
>>> +        return;
>>> +    }
>>> +
>>> +    ftintc020_update(s);
>>> +}
>>> +
>>> +static const MemoryRegionOps mmio_ops = {
>>> +    .read  = ftintc020_mem_read,
>>> +    .write = ftintc020_mem_write,
>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>> +    .valid = {
>>> +        .min_access_size = 4,
>>> +        .max_access_size = 4,
>>> +    }
>>> +};
>>> +
>>> +static void ftintc020_reset(DeviceState *ds)
>>> +{
>>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>>> +    Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev));
>>> +
>>> +    s->irq_pin[0] = 0;
>>> +    s->irq_pin[1] = 0;
>>> +    s->fiq_pin[0] = 0;
>>> +    s->fiq_pin[1] = 0;
>>> +
>>> +    s->irq_src[0] = 0;
>>> +    s->irq_src[1] = 0;
>>> +    s->irq_ena[0] = 0;
>>> +    s->irq_ena[1] = 0;
>>> +    s->fiq_src[0] = 0;
>>> +    s->fiq_src[1] = 0;
>>> +    s->fiq_ena[0] = 0;
>>> +    s->fiq_ena[1] = 0;
>>> +
>>> +    ftintc020_update(s);
>>> +}
>>> +
>>> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu)
>
>
> This problematic function then goes away completely. The
> machine-model/SoC just instantiates your intc and connects your GPIO
> outputs to the ARM cpu. You will need to add a call to
> arm_pic_init_cpu to your machine-model/SoC. Have a look at vexpress.c
> for an example of a machine that does this. ARM gic and mpcore both
> export FIQ and IRQ as GPIOs which allows for this much cleaner
> solution.
>

Got it, thanks

The FTINTC020 has been re-written with vexpress.c, versatilepb.c and
pl190.c as templates

> Regards,
> Peter



--
Best wishes,
Kuo-Jung Su

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 04/24] hw/arm: add Faraday FTAHBC020 support
  2013-03-04  3:35   ` [Qemu-devel] [PATCH v5 04/24] hw/arm: add Faraday FTAHBC020 support Peter Crosthwaite
@ 2013-03-04  6:30     ` Kuo-Jung Su
  0 siblings, 0 replies; 15+ messages in thread
From: Kuo-Jung Su @ 2013-03-04  6:30 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred.konrad

2013/3/4 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>
>>
>> It's used to perform AHB remap and if the SDRAM is initialized
>> before AHB remap process activated, then it would also perform
>> the QEMU RAM initialization.
>>
>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>> ---
>>  hw/arm/Makefile.objs      |    1 +
>>  hw/arm/faraday_a369_soc.c |    3 +
>>  hw/arm/ftahbc020.c        |  189 +++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 193 insertions(+)
>>  create mode 100644 hw/arm/ftahbc020.c
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index 6771072..33c9482 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -38,3 +38,4 @@ obj-y += faraday_a369.o \
>>              faraday_a369_scu.o \
>>              faraday_a369_kpd.o
>>  obj-y += ftintc020.o
>> +obj-y += ftahbc020.o
>> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
>> index 3d861d2..e7343d9 100644
>> --- a/hw/arm/faraday_a369_soc.c
>> +++ b/hw/arm/faraday_a369_soc.c
>> @@ -145,6 +145,9 @@ a369soc_device_init(FaradaySoCState *s)
>>
>>      /* ftkbc010 */
>>      sysbus_create_simple("a369.keypad", 0x92f00000, pic[21]);
>> +
>> +    /* ftahbc020 */
>> +    s->ahbc = sysbus_create_simple("ftahbc020", 0x94000000, NULL);
>>  }
>>
>>  static int a369soc_init(SysBusDevice *busdev)
>> diff --git a/hw/arm/ftahbc020.c b/hw/arm/ftahbc020.c
>> new file mode 100644
>> index 0000000..b558e90
>> --- /dev/null
>> +++ b/hw/arm/ftahbc020.c
>> @@ -0,0 +1,189 @@
>> +/*
>> + * Faraday AHB controller
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * 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_SLAVE(n)    ((n) * 4) /* Slave config (base & size) */
>> +#define REG_PRIR        0x80    /* Priority register */
>> +#define REG_IDLECR      0x84    /* IDLE count register */
>> +#define REG_CR          0x88    /* Control register */
>> +#define REG_REVR        0x8c    /* Revision register */
>> +
>> +#define CR_REMAP        0x01    /* Enable AHB remap for slave 4 & 6 */
>> +
>> +#define TYPE_FTAHBC020  "ftahbc020"
>> +
>> +typedef struct Ftahbc020State {
>> +    SysBusDevice busdev;
>> +    MemoryRegion iomem;
>> +
>> +    /* HW register cache */
>> +    uint32_t cr;
>> +} Ftahbc020State;
>> +
>> +#define FTAHBC020(obj) \
>> +    OBJECT_CHECK(Ftahbc020State, obj, TYPE_FTAHBC020)
>> +
>> +static uint64_t
>> +ftahbc020_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    Ftahbc020State *s = FTAHBC020(opaque);
>> +    FaradaySoCState *soc = FARADAY_SOC_GET_CORE();
>
>
>
>> +    uint64_t ret = 0;
>> +
>> +    switch (addr) {
>> +    /* slave address & window configuration */
>> +    case REG_SLAVE(0) ... REG_SLAVE(3):
>> +    case REG_SLAVE(5):
>
> I think its required to have a comment of fall through case statements.
>
> /* fall-through - because of foo */
>

Got it, thanks

> Regards,
> Peter
>
>> +    case REG_SLAVE(7) ... REG_SLAVE(31):
>> +        ret = soc->ahb_slave[addr / 4];
>> +        break;
>> +    case REG_SLAVE(4):
>> +        ret = soc->rom_base | (soc->ahb_slave[4] & 0x000f0000);
>> +        break;
>> +    case REG_SLAVE(6):
>> +        ret = soc->ram_base | (soc->ahb_slave[6] & 0x000f0000);
>> +        break;
>> +    /* control register */
>> +    case REG_CR:
>> +        if (soc->ahb_remapped) {
>> +            s->cr |= CR_REMAP;
>> +        }
>> +        ret = s->cr;
>> +        break;
>> +    case REG_REVR:
>> +        ret = 0x00010301;   /* rev. 1.3.1 */
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "ftahbc020: undefined memory access@0x%llx\n", addr);
>> +        break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static void
>> +ftahbc020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
>> +{
>> +    Ftahbc020State *s = FTAHBC020(opaque);
>> +    FaradaySoCState *soc = FARADAY_SOC_GET_CORE();
>> +
>> +    switch (addr) {
>> +    case REG_CR:    /* control register */
>> +        s->cr = (uint32_t)val;
>> +        if (soc->ahb_remapped && !(s->cr & CR_REMAP)) {
>> +            hw_error("ftahbc020: Once AHB remap is enabled, "
>> +                     "it could not be disabled!\n");
>> +            exit(1);
>> +        }
>> +        if (!soc->ahb_remapped && (s->cr & CR_REMAP)) {
>> +            /* Remap AHB slave 4 (ROM) & slave 6 (RAM) */
>> +            /* 1. Remap RAM to base of ROM */
>> +            soc->ram_base = soc->ahb_slave[4] & 0xfff00000;
>> +            /* 2. Remap ROM to base of ROM + size of RAM */
>> +            soc->rom_base = soc->ram_base
>> +                          + ((1 << extract32(soc->ahb_slave[6], 16, 4)) << 20);
>> +            /* 3. Update ROM memory map */
>> +            sysbus_mmio_map(SYS_BUS_DEVICE(soc->rom), 0, soc->rom_base);
>> +            /* 4. Update RAM memory map if it has been initialized. */
>> +            if (soc->ddr_inited) {
>> +                memory_region_del_subregion(soc->as, soc->ram);
>> +                memory_region_add_subregion(soc->as, soc->ram_base, soc->ram);
>> +            }
>> +            soc->ahb_remapped = true;
>> +        }
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "ftahbc020: undefined memory access@0x%llx\n", addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = ftahbc020_mem_read,
>> +    .write = ftahbc020_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    }
>> +};
>> +
>> +static void ftahbc020_reset(DeviceState *ds)
>> +{
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    Ftahbc020State *s = FTAHBC020(FROM_SYSBUS(Ftahbc020State, busdev));
>> +    FaradaySoCState *soc = FARADAY_SOC_GET_CORE();
>> +
>> +    if (soc->ahb_remapped && !soc->bi) {
>> +        soc->rom_base = soc->ahb_slave[4] & 0xfff00000;
>> +        soc->ram_base = soc->ahb_slave[6] & 0xfff00000;
>> +        sysbus_mmio_map(SYS_BUS_DEVICE(soc->rom), 0, soc->rom_base);
>> +        soc->ahb_remapped = false;
>> +    }
>> +
>> +    s->cr = 0;
>> +}
>> +
>> +static int ftahbc020_init(SysBusDevice *dev)
>> +{
>> +    Ftahbc020State *s = FTAHBC020(FROM_SYSBUS(Ftahbc020State, dev));
>> +
>> +    memory_region_init_io(&s->iomem,
>> +                          &mmio_ops,
>> +                          s,
>> +                          TYPE_FTAHBC020,
>> +                          0x1000);
>> +    sysbus_init_mmio(dev, &s->iomem);
>> +    return 0;
>> +}
>> +
>> +static const VMStateDescription vmstate_ftahbc020 = {
>> +    .name = TYPE_FTAHBC020,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT32(cr, Ftahbc020State),
>> +        VMSTATE_END_OF_LIST(),
>> +    }
>> +};
>> +
>> +static void ftahbc020_class_init(ObjectClass *klass, void *data)
>> +{
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    k->init   = ftahbc020_init;
>> +    dc->desc  = TYPE_FTAHBC020;
>> +    dc->vmsd  = &vmstate_ftahbc020;
>> +    dc->reset = ftahbc020_reset;
>> +    dc->no_user = 1;
>> +}
>> +
>> +static const TypeInfo ftahbc020_info = {
>> +    .name          = TYPE_FTAHBC020,
>> +    .parent        = TYPE_FARADAY_SOC,
>> +    .instance_size = sizeof(Ftahbc020State),
>> +    .class_init    = ftahbc020_class_init,
>> +};
>> +
>> +static void ftahbc020_register_types(void)
>> +{
>> +    type_register_static(&ftahbc020_info);
>> +}
>> +
>> +type_init(ftahbc020_register_types)
>> --
>> 1.7.9.5
>>
>>



-- 
Best wishes,
Kuo-Jung Su

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 05/24] hw/arm: add Faraday FTDDRII030 support
  2013-03-04  3:56   ` [Qemu-devel] [PATCH v5 05/24] hw/arm: add Faraday FTDDRII030 support Peter Crosthwaite
@ 2013-03-04  6:32     ` Kuo-Jung Su
  0 siblings, 0 replies; 15+ messages in thread
From: Kuo-Jung Su @ 2013-03-04  6:32 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred.konrad

2013/3/4 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 FTDDRII030 is a DDRII SDRAM controller which is responsible for
>> SDRAM initialization.
>> In QEMU we emualte only the SDRAM enable function.
>>
>
> "emulate"
>

Got it, thanks

>> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
>> ---
>>  hw/arm/Makefile.objs      |    1 +
>>  hw/arm/faraday_a369_soc.c |    3 +
>>  hw/arm/ftddrii030.c       |  171 +++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 175 insertions(+)
>>  create mode 100644 hw/arm/ftddrii030.c
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index 33c9482..2a4c7d6 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -39,3 +39,4 @@ obj-y += faraday_a369.o \
>>              faraday_a369_kpd.o
>>  obj-y += ftintc020.o
>>  obj-y += ftahbc020.o
>> +obj-y += ftddrii030.o
>> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
>> index e7343d9..fdf13f8 100644
>> --- a/hw/arm/faraday_a369_soc.c
>> +++ b/hw/arm/faraday_a369_soc.c
>> @@ -148,6 +148,9 @@ a369soc_device_init(FaradaySoCState *s)
>>
>>      /* ftahbc020 */
>>      s->ahbc = sysbus_create_simple("ftahbc020", 0x94000000, NULL);
>> +
>> +    /* ftddrii030 */
>> +    s->ddrc = sysbus_create_simple("ftddrii030", 0x93100000, NULL);
>>  }
>>
>>  static int a369soc_init(SysBusDevice *busdev)
>> diff --git a/hw/arm/ftddrii030.c b/hw/arm/ftddrii030.c
>> new file mode 100644
>> index 0000000..1679bef
>> --- /dev/null
>> +++ b/hw/arm/ftddrii030.c
>> @@ -0,0 +1,171 @@
>> +/*
>> + * Faraday DDRII controller
>> + *
>> + * Copyright (c) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * 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_MCR             0x00    /* memory configuration register */
>> +#define REG_MSR             0x04    /* memory status register */
>> +#define REG_REVR            0x50    /* revision register */
>> +
>> +#define MSR_INIT_OK         BIT(8)  /* DDR2 initial is completed */
>> +#define MSR_CMD_MRS         BIT(0)  /* start MRS command */
>> +
>> +#define CFG_REGSIZE         (0x50 / 4)
>> +
>> +#define TYPE_FTDDRII030     "ftddrii030"
>> +
>> +typedef struct Ftddrii030State {
>> +    SysBusDevice busdev;
>> +    MemoryRegion iomem;
>> +
>> +    /* HW register cache */
>> +    uint32_t regs[CFG_REGSIZE];
>> +} Ftddrii030State;
>> +
>> +#define FTDDRII030(obj) \
>> +    OBJECT_CHECK(Ftddrii030State, obj, TYPE_FTDDRII030)
>> +
>> +#define DDR_REG32(s, off) \
>> +    *(uint32_t *)((uint8_t *)(s)->regs + (off))
>
> Strange. You are forcing alignment in your memory region ops so Im not
> sure I see the need for this. Cant you just index directly ...
>

Sure, it's just a coding style which I used to write hardware access
macros for chip drivers.

>> +
>> +static uint64_t
>> +ftddrii030_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    Ftddrii030State *s = FTDDRII030(opaque);
>> +    FaradaySoCState *soc = FARADAY_SOC_GET_CORE();
>> +    uint64_t ret = 0;
>> +
>> +    if (soc->ddr_inited) {
>> +        DDR_REG32(s, REG_MSR) |= MSR_INIT_OK;
>
> ... like this?
>
> s->regs[REG_MSR/4] |= MSR_INIT_OK
>
>> +    }
>> +
>> +    switch (addr) {
>> +    case REG_MCR ... 0x4c:
>
> s/0x4c/CFG_REGSIZE or something like it. I think you can define you
> macros to get rid of this magic number that is really just the end of
> your CFG_REGSIZE definition.
>

Got it, thanks

>> +        ret = s->regs[addr / 4];
>> +        break;
>> +    case REG_REVR:
>> +        ret = 0x100;    /* rev. = 0.1.0 */
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "ftddrii030: undefined memory access@0x%llx\n", addr);
>> +        break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static void
>> +ftddrii030_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
>> +{
>> +    Ftddrii030State *s = FTDDRII030(opaque);
>> +    FaradaySoCState *soc = FARADAY_SOC_GET_CORE();
>> +
>> +    switch (addr) {
>> +    case REG_MCR:
>> +        DDR_REG32(s, REG_MCR) = (uint32_t)val & 0xffff;
>> +        break;
>> +    case REG_MSR:
>> +        val = (val & 0x3f) | (DDR_REG32(s, REG_MSR) & MSR_INIT_OK);
>> +        if (!soc->ddr_inited && (val & MSR_CMD_MRS)) {
>> +            val &= ~MSR_CMD_MRS;
>> +            val |= MSR_INIT_OK;
>> +            memory_region_add_subregion(soc->as, soc->ram_base, soc->ram);
>> +            soc->ddr_inited = true;
>> +        }
>> +        DDR_REG32(s, REG_MSR) = (uint32_t)val;
>> +        break;
>> +    case 0x08 ... 0x4c: /* DDRII Timing, ECC ...etc. */
>> +        s->regs[addr / 4] = (uint32_t)val;
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +                      "ftddrii030: undefined memory access@0x%llx\n", addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = ftddrii030_mem_read,
>> +    .write = ftddrii030_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4,
>> +    }
>> +};
>> +
>> +static void ftddrii030_reset(DeviceState *ds)
>> +{
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    Ftddrii030State *s = FTDDRII030(FROM_SYSBUS(Ftddrii030State, busdev));
>> +    FaradaySoCState *soc = FARADAY_SOC_GET_CORE();
>> +
>> +    if (soc->ddr_inited && !soc->bi) {
>> +        memory_region_del_subregion(soc->as, soc->ram);
>> +        soc->ddr_inited = false;
>> +    }
>> +
>> +    memset(s->regs, 0, sizeof(s->regs));
>> +}
>> +
>> +static int ftddrii030_init(SysBusDevice *dev)
>> +{
>> +    Ftddrii030State *s = FTDDRII030(FROM_SYSBUS(Ftddrii030State, dev));
>> +
>> +    memory_region_init_io(&s->iomem,
>> +                          &mmio_ops,
>> +                          s,
>> +                          TYPE_FTDDRII030,
>> +                          0x1000);
>> +    sysbus_init_mmio(dev, &s->iomem);
>> +    return 0;
>> +}
>> +
>> +static const VMStateDescription vmstate_ftddrii030 = {
>> +    .name = TYPE_FTDDRII030,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT32_ARRAY(regs, Ftddrii030State, CFG_REGSIZE),
>> +        VMSTATE_END_OF_LIST(),
>> +    }
>> +};
>> +
>> +static void ftddrii030_class_init(ObjectClass *klass, void *data)
>> +{
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    k->init   = ftddrii030_init;
>> +    dc->desc  = TYPE_FTDDRII030;
>> +    dc->vmsd  = &vmstate_ftddrii030;
>> +    dc->reset = ftddrii030_reset;
>> +    dc->no_user = 1;
>> +}
>> +
>> +static const TypeInfo ftddrii030_info = {
>> +    .name          = TYPE_FTDDRII030,
>> +    .parent        = TYPE_FARADAY_SOC,
>> +    .instance_size = sizeof(Ftddrii030State),
>> +    .class_init    = ftddrii030_class_init,
>> +};
>> +
>> +static void ftddrii030_register_types(void)
>> +{
>> +    type_register_static(&ftddrii030_info);
>> +}
>> +
>> +type_init(ftddrii030_register_types)
>> --
>> 1.7.9.5
>>
>>



-- 
Best wishes,
Kuo-Jung Su

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 03/24] hw/arm: add Faraday FTINTC020 interrupt controller support
  2013-03-04  6:20     ` Kuo-Jung Su
@ 2013-03-04  6:33       ` Peter Crosthwaite
  2013-03-04  6:54         ` Kuo-Jung Su
  0 siblings, 1 reply; 15+ messages in thread
From: Peter Crosthwaite @ 2013-03-04  6:33 UTC (permalink / raw)
  To: Kuo-Jung Su
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred.konrad

Hi Kuo-Jung,

On Mon, Mar 4, 2013 at 4:20 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
> 2013/3/2 Peter Crosthwaite <peter.crosthwaite@xilinx.com>:
>> Hi Kuo-Jung,
[Snip]
>>> +        return s->irq_lvl[1];
>>> +    case REG_EIRQSR:
>>> +        return s->irq_src[1] & s->irq_ena[1];
>>> +
>>
>> AFAICT, index 0 of there arrays in for IRQ and index 1 is for EIRQ.
>> Can you #define some symbols accrordingly and remove all the magic
>> numberage with the [0]'s and [1]'s?
>>
>
> Sure, the ftintc020 is going to be redesigned with the 'hw/pl190.c' as template.
> And all the coding style issues would be updated.
>
>>> +    /*
>>> +     * FIQ
>>> +     */
>>> +    case REG_FIQSRC:
>>> +        return s->fiq_src[0];
>>> +    case REG_FIQENA:
>>> +        return s->fiq_ena[0];
>>> +    case REG_FIQMDR:
>>> +        return s->fiq_mod[0];
>>> +    case REG_FIQLVR:
>>> +        return s->fiq_lvl[0];
>>> +    case REG_FIQSR:
>>> +        return s->fiq_src[0] & s->fiq_ena[0];
>>> +    case REG_EFIQSRC:
>>> +        return s->fiq_src[1];
>>> +    case REG_EFIQENA:
>>> +        return s->fiq_ena[1];
>>> +    case REG_EFIQMDR:
>>> +        return s->fiq_mod[1];
>>> +    case REG_EFIQLVR:
>>> +        return s->fiq_lvl[1];
>>> +    case REG_EFIQSR:
>>> +        return s->fiq_src[1] & s->fiq_ena[1];
>>> +    default:
>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
>>> +        return 0;
>>> +    }
>>> +}
>>> +
>>> +static void
>>> +ftintc020_mem_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
>>> +{
>>> +    Ftintc020State *s = FTINTC020(opaque);
>>> +
>>> +    switch (addr) {
>>> +    /*
>>> +     * IRQ
>>> +     */
>>
>> Ok to use one line comment. And elsewhere
>>
>>> +    case REG_IRQENA:
>>> +        s->irq_ena[0] = (uint32_t)value;
>>> +        break;
>>> +    case REG_IRQSCR:
>>> +        value = ~(value & s->irq_mod[0]);
>>> +        s->irq_src[0] &= (uint32_t)value;
>>> +        break;
>>> +    case REG_IRQMDR:
>>> +        s->irq_mod[0] = (uint32_t)value;
>>> +        break;
>>> +    case REG_IRQLVR:
>>> +        s->irq_lvl[0] = (uint32_t)value;
>>> +        break;
>>> +    case REG_EIRQENA:
>>> +        s->irq_ena[1] = (uint32_t)value;
>>> +        break;
>>> +    case REG_EIRQSCR:
>>> +        value = ~(value & s->irq_mod[1]);
>>> +        s->irq_src[1] &= (uint32_t)value;
>>> +        break;
>>> +    case REG_EIRQMDR:
>>> +        s->irq_mod[1] = (uint32_t)value;
>>> +        break;
>>> +    case REG_EIRQLVR:
>>> +        s->irq_lvl[1] = (uint32_t)value;
>>> +        break;
>>> +
>>> +    /*
>>> +     * FIQ
>>> +     */
>>> +    case REG_FIQENA:
>>> +        s->fiq_ena[0] = (uint32_t)value;
>>> +        break;
>>> +    case REG_FIQSCR:
>>> +        value = ~(value & s->fiq_mod[0]);
>>> +        s->fiq_src[0] &= (uint32_t)value;
>>> +        break;
>>> +    case REG_FIQMDR:
>>> +        s->fiq_mod[0] = (uint32_t)value;
>>> +        break;
>>> +    case REG_FIQLVR:
>>> +        s->fiq_lvl[0] = (uint32_t)value;
>>> +        break;
>>> +    case REG_EFIQENA:
>>> +        s->fiq_ena[1] = (uint32_t)value;
>>> +        break;
>>> +    case REG_EFIQSCR:
>>> +        value = ~(value & s->fiq_mod[1]);
>>> +        s->fiq_src[1] &= (uint32_t)value;
>>> +        break;
>>> +    case REG_EFIQMDR:
>>> +        s->fiq_mod[1] = (uint32_t)value;
>>> +        break;
>>> +    case REG_EFIQLVR:
>>> +        s->fiq_lvl[1] = (uint32_t)value;
>>> +        break;
>>> +
>>> +    /* don't care */
>>> +    default:
>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
>>> +        return;
>>> +    }
>>> +
>>> +    ftintc020_update(s);
>>> +}
>>> +
>>> +static const MemoryRegionOps mmio_ops = {
>>> +    .read  = ftintc020_mem_read,
>>> +    .write = ftintc020_mem_write,
>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>> +    .valid = {
>>> +        .min_access_size = 4,
>>> +        .max_access_size = 4,
>>> +    }
>>> +};
>>> +
>>> +static void ftintc020_reset(DeviceState *ds)
>>> +{
>>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>>> +    Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev));
>>> +
>>> +    s->irq_pin[0] = 0;
>>> +    s->irq_pin[1] = 0;
>>> +    s->fiq_pin[0] = 0;
>>> +    s->fiq_pin[1] = 0;
>>> +
>>> +    s->irq_src[0] = 0;
>>> +    s->irq_src[1] = 0;
>>> +    s->irq_ena[0] = 0;
>>> +    s->irq_ena[1] = 0;
>>> +    s->fiq_src[0] = 0;
>>> +    s->fiq_src[1] = 0;
>>> +    s->fiq_ena[0] = 0;
>>> +    s->fiq_ena[1] = 0;
>>> +
>>> +    ftintc020_update(s);
>>> +}
>>> +
>>> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu)
>>
>> I'm not sure this is the right place for this, I think device creation
>> helpers belong on the machine / SoC level. Keep the device model self
>> contained and clients call qdev_init themselves. Andreas have the
>> rules change on this recently?
>>
>>> +{
>>> +    int i;
>>> +    DeviceState *ds = qdev_create(NULL, TYPE_FTINTC020);
>>
>> As the device is intended for use in an SoC, the SoC potentially needs
>> to hold a pointer to it in order to call device destructors. This
>> function should return ds for future use by the instantiator.
>>
>>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>>> +    Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev));
>>> +
>>
>> Use an Object cast macro. Andreas, can we make this easier for
>> reviewers and developers by adding blacklisted identifiers to
>> checkpatch perhaps? throw a warning on +FROM_SYSBUS, DO_UPCAST etc?
>>
>>> +    s->cpu = cpu;
>>
>> Im thinking this is a QOM link. Its a connection from one device to
>> another and the machine should be aware of it.
>>
>
> Sorry, I can't get you well....
> Did you mean adding the QOM link to cpu like this?
>
>     .........
>     s->cpu = cpu_arm_init(s->cpu_model);
>     if (!s->cpu) {
>         hw_error("a369: Unable to find CPU definition\n");
>         exit(1);
>     }
>     object_property_add_child(qdev_get_machine(),
>                               "cpu",
>                               OBJECT(s->cpu),
>                               NULL);
>     .........
>

Definately not a child note. object_property_add_link.

> However this would lead to an error like this:
>
> **
> ERROR:qom/object.c:899:object_property_add_child: assertion failed:
> (child->parent == NULL)
>

The CPU is already parented, which is why this is asserting. You can't
adopt it as your own child here.

But I wouldn't bother with a link approach at all anymore, see my
later reply regarding changing over to a GPIO out approach which is
more modern. Removes the need for this cpu linkage completely. Your
rewrite based on the pl190 VIC should fix this anyway. This will all
go away.

Regards,
Peter

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH v5 03/24] hw/arm: add Faraday FTINTC020 interrupt controller support
  2013-03-04  6:33       ` Peter Crosthwaite
@ 2013-03-04  6:54         ` Kuo-Jung Su
  0 siblings, 0 replies; 15+ messages in thread
From: Kuo-Jung Su @ 2013-03-04  6:54 UTC (permalink / raw)
  To: Peter Crosthwaite
  Cc: Peter Maydell, Igor Mitsyanko, qemu-devel, Blue Swirl,
	Paul Brook, Kuo-Jung Su, Andreas, fred.konrad

2013/3/4 Peter Crosthwaite <peter.crosthwaite@xilinx.com>:
> Hi Kuo-Jung,
>
> On Mon, Mar 4, 2013 at 4:20 PM, Kuo-Jung Su <dantesu@gmail.com> wrote:
>> 2013/3/2 Peter Crosthwaite <peter.crosthwaite@xilinx.com>:
>>> Hi Kuo-Jung,
> [Snip]
>>>> +        return s->irq_lvl[1];
>>>> +    case REG_EIRQSR:
>>>> +        return s->irq_src[1] & s->irq_ena[1];
>>>> +
>>>
>>> AFAICT, index 0 of there arrays in for IRQ and index 1 is for EIRQ.
>>> Can you #define some symbols accrordingly and remove all the magic
>>> numberage with the [0]'s and [1]'s?
>>>
>>
>> Sure, the ftintc020 is going to be redesigned with the 'hw/pl190.c' as template.
>> And all the coding style issues would be updated.
>>
>>>> +    /*
>>>> +     * FIQ
>>>> +     */
>>>> +    case REG_FIQSRC:
>>>> +        return s->fiq_src[0];
>>>> +    case REG_FIQENA:
>>>> +        return s->fiq_ena[0];
>>>> +    case REG_FIQMDR:
>>>> +        return s->fiq_mod[0];
>>>> +    case REG_FIQLVR:
>>>> +        return s->fiq_lvl[0];
>>>> +    case REG_FIQSR:
>>>> +        return s->fiq_src[0] & s->fiq_ena[0];
>>>> +    case REG_EFIQSRC:
>>>> +        return s->fiq_src[1];
>>>> +    case REG_EFIQENA:
>>>> +        return s->fiq_ena[1];
>>>> +    case REG_EFIQMDR:
>>>> +        return s->fiq_mod[1];
>>>> +    case REG_EFIQLVR:
>>>> +        return s->fiq_lvl[1];
>>>> +    case REG_EFIQSR:
>>>> +        return s->fiq_src[1] & s->fiq_ena[1];
>>>> +    default:
>>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>>> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
>>>> +        return 0;
>>>> +    }
>>>> +}
>>>> +
>>>> +static void
>>>> +ftintc020_mem_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
>>>> +{
>>>> +    Ftintc020State *s = FTINTC020(opaque);
>>>> +
>>>> +    switch (addr) {
>>>> +    /*
>>>> +     * IRQ
>>>> +     */
>>>
>>> Ok to use one line comment. And elsewhere
>>>
>>>> +    case REG_IRQENA:
>>>> +        s->irq_ena[0] = (uint32_t)value;
>>>> +        break;
>>>> +    case REG_IRQSCR:
>>>> +        value = ~(value & s->irq_mod[0]);
>>>> +        s->irq_src[0] &= (uint32_t)value;
>>>> +        break;
>>>> +    case REG_IRQMDR:
>>>> +        s->irq_mod[0] = (uint32_t)value;
>>>> +        break;
>>>> +    case REG_IRQLVR:
>>>> +        s->irq_lvl[0] = (uint32_t)value;
>>>> +        break;
>>>> +    case REG_EIRQENA:
>>>> +        s->irq_ena[1] = (uint32_t)value;
>>>> +        break;
>>>> +    case REG_EIRQSCR:
>>>> +        value = ~(value & s->irq_mod[1]);
>>>> +        s->irq_src[1] &= (uint32_t)value;
>>>> +        break;
>>>> +    case REG_EIRQMDR:
>>>> +        s->irq_mod[1] = (uint32_t)value;
>>>> +        break;
>>>> +    case REG_EIRQLVR:
>>>> +        s->irq_lvl[1] = (uint32_t)value;
>>>> +        break;
>>>> +
>>>> +    /*
>>>> +     * FIQ
>>>> +     */
>>>> +    case REG_FIQENA:
>>>> +        s->fiq_ena[0] = (uint32_t)value;
>>>> +        break;
>>>> +    case REG_FIQSCR:
>>>> +        value = ~(value & s->fiq_mod[0]);
>>>> +        s->fiq_src[0] &= (uint32_t)value;
>>>> +        break;
>>>> +    case REG_FIQMDR:
>>>> +        s->fiq_mod[0] = (uint32_t)value;
>>>> +        break;
>>>> +    case REG_FIQLVR:
>>>> +        s->fiq_lvl[0] = (uint32_t)value;
>>>> +        break;
>>>> +    case REG_EFIQENA:
>>>> +        s->fiq_ena[1] = (uint32_t)value;
>>>> +        break;
>>>> +    case REG_EFIQSCR:
>>>> +        value = ~(value & s->fiq_mod[1]);
>>>> +        s->fiq_src[1] &= (uint32_t)value;
>>>> +        break;
>>>> +    case REG_EFIQMDR:
>>>> +        s->fiq_mod[1] = (uint32_t)value;
>>>> +        break;
>>>> +    case REG_EFIQLVR:
>>>> +        s->fiq_lvl[1] = (uint32_t)value;
>>>> +        break;
>>>> +
>>>> +    /* don't care */
>>>> +    default:
>>>> +        qemu_log_mask(LOG_GUEST_ERROR,
>>>> +                      "ftintc020: undefined memory access@0x%llx\n", addr);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    ftintc020_update(s);
>>>> +}
>>>> +
>>>> +static const MemoryRegionOps mmio_ops = {
>>>> +    .read  = ftintc020_mem_read,
>>>> +    .write = ftintc020_mem_write,
>>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>>> +    .valid = {
>>>> +        .min_access_size = 4,
>>>> +        .max_access_size = 4,
>>>> +    }
>>>> +};
>>>> +
>>>> +static void ftintc020_reset(DeviceState *ds)
>>>> +{
>>>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>>>> +    Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev));
>>>> +
>>>> +    s->irq_pin[0] = 0;
>>>> +    s->irq_pin[1] = 0;
>>>> +    s->fiq_pin[0] = 0;
>>>> +    s->fiq_pin[1] = 0;
>>>> +
>>>> +    s->irq_src[0] = 0;
>>>> +    s->irq_src[1] = 0;
>>>> +    s->irq_ena[0] = 0;
>>>> +    s->irq_ena[1] = 0;
>>>> +    s->fiq_src[0] = 0;
>>>> +    s->fiq_src[1] = 0;
>>>> +    s->fiq_ena[0] = 0;
>>>> +    s->fiq_ena[1] = 0;
>>>> +
>>>> +    ftintc020_update(s);
>>>> +}
>>>> +
>>>> +qemu_irq *ftintc020_init(hwaddr base, ARMCPU *cpu)
>>>
>>> I'm not sure this is the right place for this, I think device creation
>>> helpers belong on the machine / SoC level. Keep the device model self
>>> contained and clients call qdev_init themselves. Andreas have the
>>> rules change on this recently?
>>>
>>>> +{
>>>> +    int i;
>>>> +    DeviceState *ds = qdev_create(NULL, TYPE_FTINTC020);
>>>
>>> As the device is intended for use in an SoC, the SoC potentially needs
>>> to hold a pointer to it in order to call device destructors. This
>>> function should return ds for future use by the instantiator.
>>>
>>>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>>>> +    Ftintc020State *s = FTINTC020(FROM_SYSBUS(Ftintc020State, busdev));
>>>> +
>>>
>>> Use an Object cast macro. Andreas, can we make this easier for
>>> reviewers and developers by adding blacklisted identifiers to
>>> checkpatch perhaps? throw a warning on +FROM_SYSBUS, DO_UPCAST etc?
>>>
>>>> +    s->cpu = cpu;
>>>
>>> Im thinking this is a QOM link. Its a connection from one device to
>>> another and the machine should be aware of it.
>>>
>>
>> Sorry, I can't get you well....
>> Did you mean adding the QOM link to cpu like this?
>>
>>     .........
>>     s->cpu = cpu_arm_init(s->cpu_model);
>>     if (!s->cpu) {
>>         hw_error("a369: Unable to find CPU definition\n");
>>         exit(1);
>>     }
>>     object_property_add_child(qdev_get_machine(),
>>                               "cpu",
>>                               OBJECT(s->cpu),
>>                               NULL);
>>     .........
>>
>
> Definately not a child note. object_property_add_link.
>

Got it, thanks.

>> However this would lead to an error like this:
>>
>> **
>> ERROR:qom/object.c:899:object_property_add_child: assertion failed:
>> (child->parent == NULL)
>>
>
> The CPU is already parented, which is why this is asserting. You can't
> adopt it as your own child here.
>
> But I wouldn't bother with a link approach at all anymore, see my
> later reply regarding changing over to a GPIO out approach which is
> more modern. Removes the need for this cpu linkage completely. Your
> rewrite based on the pl190 VIC should fix this anyway. This will all
> go away.
>

There is one more place that the 'cpu' pointer would be referenced,
please check here:

hw/arm/faraday_a369.c:

......
    if (args->kernel_filename) {
        ......
        arm_load_kernel(s->cpu, s->bi);
        ......
    }
......

I guess I still have to make a QOM link to the cpu pointer.

> Regards,
> Peter



--
Best wishes,
Kuo-Jung Su

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2013-03-04  6:54 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <1361949350-22241-1-git-send-email-dantesu@gmail.com>
     [not found] ` <1361949350-22241-3-git-send-email-dantesu@gmail.com>
     [not found]   ` <5130F9D8.3030200@gmail.com>
2013-03-01 19:00     ` [Qemu-devel] [PATCH v5 02/24] hw/arm: add Faraday a369 SoC platform support Igor Mitsyanko
2013-03-04  6:06       ` Kuo-Jung Su
2013-03-02  3:43   ` Peter Crosthwaite
2013-03-04  6:09     ` Kuo-Jung Su
     [not found] ` <1361949350-22241-4-git-send-email-dantesu@gmail.com>
2013-03-02  4:13   ` [Qemu-devel] [PATCH v5 03/24] hw/arm: add Faraday FTINTC020 interrupt controller support Peter Crosthwaite
2013-03-03  4:58     ` Peter Crosthwaite
2013-03-03  6:29     ` Peter Crosthwaite
2013-03-04  6:29       ` Kuo-Jung Su
2013-03-04  6:20     ` Kuo-Jung Su
2013-03-04  6:33       ` Peter Crosthwaite
2013-03-04  6:54         ` Kuo-Jung Su
     [not found] ` <1361949350-22241-5-git-send-email-dantesu@gmail.com>
2013-03-04  3:35   ` [Qemu-devel] [PATCH v5 04/24] hw/arm: add Faraday FTAHBC020 support Peter Crosthwaite
2013-03-04  6:30     ` Kuo-Jung Su
     [not found] ` <1361949350-22241-6-git-send-email-dantesu@gmail.com>
2013-03-04  3:56   ` [Qemu-devel] [PATCH v5 05/24] hw/arm: add Faraday FTDDRII030 support Peter Crosthwaite
2013-03-04  6:32     ` Kuo-Jung Su

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.