Patchwork [17/18] hw: add QEMU model for Faraday a369 keypad controller

login
register
mail settings
Submitter Dante
Date Jan. 18, 2013, 6:33 a.m.
Message ID <1358490780-18757-1-git-send-email-dantesu@faraday-tech.com>
Download mbox | patch
Permalink /patch/213491/
State New
Headers show

Comments

Dante - Jan. 18, 2013, 6:33 a.m.
Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
---
 hw/ftkbc010.c |  214 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 214 insertions(+)
 create mode 100644 hw/ftkbc010.c

Patch

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 <dantesu@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)