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