qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v2 19/20] arm: add Faraday FTKBC010 support for A369


From: Kuo-Jung Su
Subject: [Qemu-devel] [PATCH v2 19/20] arm: add Faraday FTKBC010 support for A369
Date: Fri, 25 Jan 2013 16:19:55 +0800

From: Kuo-Jung Su <address@hidden>

Faraday keyboard/mouse controller (FTKBC010) is compliant with the
IBM PS/2 interface. The interface uses the bidirectional clock and
data lines to perform the half-duplex synchronous serial interface.

It also provides the configurable scan matrix for the embedded or
external keypad. FTKBC010 performs a serial-to-parallel conversion
to receive the data from the keyboard/mouse interface, and a
parallel-to-serial conversion for transmitting the data to the
keyboard/mouse interface.

Signed-off-by: Kuo-Jung Su <address@hidden>
---
 hw/ftkbc010.c |  226 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 226 insertions(+)
 create mode 100644 hw/ftkbc010.c

diff --git a/hw/ftkbc010.c b/hw/ftkbc010.c
new file mode 100644
index 0000000..c142bd0
--- /dev/null
+++ b/hw/ftkbc010.c
@@ -0,0 +1,226 @@
+/*
+ * Faraday FTKBC010 emulator for A369.
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <address@hidden>
+ *
+ * This code is licensed under the GPL v2.
+ */
+
+#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) */
+
+#define TYPE_FTKBC010           "ftkbc010"
+
+typedef struct Ftkbc010State {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq irq;
+
+    int x;
+    int y;
+
+    /* HW registers */
+    uint32_t cr;
+    uint32_t isr;
+} ftkbc010_state;
+
+#define FTKBC010(obj) \
+    OBJECT_CHECK(ftkbc010_state, obj, TYPE_FTKBC010)
+
+static void ftkbc010_update(ftkbc010_state *s)
+{
+    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(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(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(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 void ftkbc010_reset(DeviceState *ds)
+{
+    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
+    ftkbc010_state *s = FTKBC010(FROM_SYSBUS(ftkbc010_state, busdev));
+
+    qemu_irq_lower(s->irq);
+}
+
+static int ftkbc010_init(SysBusDevice *dev)
+{
+    ftkbc010_state *s = FTKBC010(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,
+                          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(cr, ftkbc010_state),
+        VMSTATE_UINT32(isr, ftkbc010_state),
+        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(ftkbc010_state),
+    .class_init    = ftkbc010_class_init,
+};
+
+static void ftkbc010_register_types(void)
+{
+    type_register_static(&ftkbc010_info);
+}
+
+type_init(ftkbc010_register_types)
-- 
1.7.9.5




reply via email to

[Prev in Thread] Current Thread [Next in Thread]