Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com> --- hw/ftkbc010.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 hw/ftkbc010.c
diff --git a/hw/ftkbc010.c b/hw/ftkbc010.c new file mode 100644 index 0000000..0727b5a --- /dev/null +++ b/hw/ftkbc010.c @@ -0,0 +1,214 @@ +/* + * Faraday FTKBC010 emulator for A369. + * + * Copyright (c) 2012 Faraday Technology + * + * Written by Dante Su <dant...@faraday-tech.com> + * + * This code is licensed under the GPL. + */ + +#include "hw.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "sysbus.h" +#include "ui/console.h" +#include "devices.h" + +/* Key codes */ +#define KEYCODE_ESC 1 +#define KEYCODE_BACKSPACE 14 +#define KEYCODE_ENTER 28 +#define KEYCODE_SPACE 57 +#define KEYCODE_MENU 139 /* Menu (show menu) */ + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq irq; + + int x; + int y; + + /* HW registers */ + uint32_t cr; + uint32_t isr; +} ftkbc010_state; + +static inline void ftkbc010_update(void *opaque) +{ + ftkbc010_state *s = (ftkbc010_state *) opaque; + uint32_t ier = 0; + + /* keypad interrupt */ + ier |= (s->cr & (1 << 8)) ? (1 << 2) : 0; + /* tx interrupt */ + ier |= (s->cr & (1 << 3)) ? (1 << 1) : 0; + /* rx interrupt */ + ier |= (s->cr & (1 << 4)) ? (1 << 0) : 0; + + if (ier & s->isr) + qemu_irq_raise(s->irq); + else + qemu_irq_lower(s->irq); +} + +static uint64_t ftkbc010_mem_read(void *opaque, hwaddr addr, unsigned size) +{ + ftkbc010_state *s = (ftkbc010_state *) opaque; + + switch (addr) { + case 0x00: + return s->cr; + case 0x10: + return s->isr; + case 0x30: + return ~(1 << s->x); + case 0x34: + return ~(1 << s->y); + case 0x50: /* revision */ + return 0x00010403; + case 0x54: /* feature */ + return 0x00000808; + default: + break; + } + + return 0; +} + +static void ftkbc010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +{ + ftkbc010_state *s = (ftkbc010_state *) opaque; + + switch (addr) { + case 0x00: + s->cr = (uint32_t)val; + /* if ftkbc010 enabled */ + if (!(s->cr & (1 << 2))) + break; + /* if keypad interrupt cleared */ + if (s->cr & (1 << 10)) { + s->cr &= ~(1 << 10); + s->isr &= ~(1 << 2); + } + /* if rx interrupt cleared */ + if (s->cr & (1 << 7)) { + s->cr &= ~(1 << 7); + s->isr &= ~(1 << 0); + } + /* if tx interrupt cleared */ + if (s->cr & (1 << 6)) { + s->cr &= ~(1 << 6); + s->isr &= ~(1 << 1); + } + ftkbc010_update(s); + break; + default: + break; + } +} + +static const MemoryRegionOps ftkbc010_mem_ops = { + .read = ftkbc010_mem_read, + .write = ftkbc010_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ftkbc010_key_event(void *opaque, int scancode) +{ + ftkbc010_state *s = (ftkbc010_state *) opaque; + int released = 0; + + /* key release from qemu */ + if (scancode & 0x80) + released = 1; + + /* strip qemu key release bit */ + scancode &= ~0x80; + + /* keypad interrupt */ + if (!released && (s->cr & (1 << 8))) { + switch(scancode) { + case KEYCODE_ESC: + case KEYCODE_BACKSPACE: + s->x = 1; + break; + case KEYCODE_ENTER: + case KEYCODE_MENU: + case KEYCODE_SPACE: + s->x = 3; + break; + default: + s->x = 2; /* KEY_HOME */ + break; + } + s->y = 0; + s->isr |= (1 << 2); + ftkbc010_update(s); + } +} + +static int ftkbc010_post_load(void *opaque, int version_id) +{ + ftkbc010_state *s = (ftkbc010_state *) opaque; + + qemu_irq_lower(s->irq); + + return 0; +} + +static int ftkbc010_init(SysBusDevice *dev) +{ + ftkbc010_state *s = FROM_SYSBUS(ftkbc010_state, dev); + + s->cr = 0; + s->isr = 0; + s->x = 0; + s->y = 0; + + memory_region_init_io(&s->iomem, &ftkbc010_mem_ops, s, "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_regs = { + .name = "ftkbc010", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = ftkbc010_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr, ftkbc010_state), + VMSTATE_UINT32(isr, ftkbc010_state), + VMSTATE_END_OF_LIST(), + } +}; + +static void ftkbc010_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = ftkbc010_init; + dc->desc = "ftkbc010"; + dc->vmsd = &vmstate_ftkbc010_regs; +} + +static TypeInfo ftkbc010_dev_info = { + .name = "ftkbc010", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ftkbc010_state), + .class_init = ftkbc010_dev_class_init, +}; + +static void ftkbc010_register_types(void) +{ + type_register_static(&ftkbc010_dev_info); +} + +type_init(ftkbc010_register_types) -- 1.7.9.5 ********************* Confidentiality Notice ************************ This electronic message and any attachments may contain confidential and legally privileged information or information which is otherwise protected from disclosure. If you are not the intended recipient,please do not disclose the contents, either in whole or in part, to anyone,and immediately delete the message and any attachments from your computer system and destroy all hard copies. Thank you for your cooperation. ***********************************************************************