qemu/hw/input/pxa2xx_keypad.c
<<
>>
Prefs
   1/*
   2 * Intel PXA27X Keypad Controller emulation.
   3 *
   4 * Copyright (c) 2007 MontaVista Software, Inc
   5 * Written by Armin Kuster <akuster@kama-aina.net>
   6 *              or  <Akuster@mvista.com>
   7 *
   8 * This code is licensed under the GPLv2.
   9 *
  10 * Contributions after 2012-01-13 are licensed under the terms of the
  11 * GNU GPL, version 2 or (at your option) any later version.
  12 */
  13
  14#include "qemu/osdep.h"
  15#include "hw/hw.h"
  16#include "hw/irq.h"
  17#include "migration/vmstate.h"
  18#include "hw/arm/pxa.h"
  19#include "ui/console.h"
  20
  21/*
  22 * Keypad
  23 */
  24#define KPC         0x00    /* Keypad Interface Control register */
  25#define KPDK        0x08    /* Keypad Interface Direct Key register */
  26#define KPREC       0x10    /* Keypad Interface Rotary Encoder register */
  27#define KPMK        0x18    /* Keypad Interface Matrix Key register */
  28#define KPAS        0x20    /* Keypad Interface Automatic Scan register */
  29#define KPASMKP0    0x28    /* Keypad Interface Automatic Scan Multiple
  30                                Key Presser register 0 */
  31#define KPASMKP1    0x30    /* Keypad Interface Automatic Scan Multiple
  32                                Key Presser register 1 */
  33#define KPASMKP2    0x38    /* Keypad Interface Automatic Scan Multiple
  34                                Key Presser register 2 */
  35#define KPASMKP3    0x40    /* Keypad Interface Automatic Scan Multiple
  36                                Key Presser register 3 */
  37#define KPKDI       0x48    /* Keypad Interface Key Debounce Interval
  38                                register */
  39
  40/* Keypad defines */
  41#define KPC_AS          (0x1 << 30)  /* Automatic Scan bit */
  42#define KPC_ASACT       (0x1 << 29)  /* Automatic Scan on Activity */
  43#define KPC_MI          (0x1 << 22)  /* Matrix interrupt bit */
  44#define KPC_IMKP        (0x1 << 21)  /* Ignore Multiple Key Press */
  45#define KPC_MS7         (0x1 << 20)  /* Matrix scan line 7 */
  46#define KPC_MS6         (0x1 << 19)  /* Matrix scan line 6 */
  47#define KPC_MS5         (0x1 << 18)  /* Matrix scan line 5 */
  48#define KPC_MS4         (0x1 << 17)  /* Matrix scan line 4 */
  49#define KPC_MS3         (0x1 << 16)  /* Matrix scan line 3 */
  50#define KPC_MS2         (0x1 << 15)  /* Matrix scan line 2 */
  51#define KPC_MS1         (0x1 << 14)  /* Matrix scan line 1 */
  52#define KPC_MS0         (0x1 << 13)  /* Matrix scan line 0 */
  53#define KPC_ME          (0x1 << 12)  /* Matrix Keypad Enable */
  54#define KPC_MIE         (0x1 << 11)  /* Matrix Interrupt Enable */
  55#define KPC_DK_DEB_SEL  (0x1 <<  9)  /* Direct Keypad Debounce Select */
  56#define KPC_DI          (0x1 <<  5)  /* Direct key interrupt bit */
  57#define KPC_RE_ZERO_DEB (0x1 <<  4)  /* Rotary Encoder Zero Debounce */
  58#define KPC_REE1        (0x1 <<  3)  /* Rotary Encoder1 Enable */
  59#define KPC_REE0        (0x1 <<  2)  /* Rotary Encoder0 Enable */
  60#define KPC_DE          (0x1 <<  1)  /* Direct Keypad Enable */
  61#define KPC_DIE         (0x1 <<  0)  /* Direct Keypad interrupt Enable */
  62
  63#define KPDK_DKP        (0x1 << 31)
  64#define KPDK_DK7        (0x1 <<  7)
  65#define KPDK_DK6        (0x1 <<  6)
  66#define KPDK_DK5        (0x1 <<  5)
  67#define KPDK_DK4        (0x1 <<  4)
  68#define KPDK_DK3        (0x1 <<  3)
  69#define KPDK_DK2        (0x1 <<  2)
  70#define KPDK_DK1        (0x1 <<  1)
  71#define KPDK_DK0        (0x1 <<  0)
  72
  73#define KPREC_OF1       (0x1 << 31)
  74#define KPREC_UF1       (0x1 << 30)
  75#define KPREC_OF0       (0x1 << 15)
  76#define KPREC_UF0       (0x1 << 14)
  77
  78#define KPMK_MKP        (0x1 << 31)
  79#define KPAS_SO         (0x1 << 31)
  80#define KPASMKPx_SO     (0x1 << 31)
  81
  82
  83#define KPASMKPx_MKC(row, col)  (1 << (row + 16 * (col % 2)))
  84
  85#define PXAKBD_MAXROW   8
  86#define PXAKBD_MAXCOL   8
  87
  88struct PXA2xxKeyPadState {
  89    MemoryRegion iomem;
  90    qemu_irq    irq;
  91    const struct  keymap *map;
  92    int         pressed_cnt;
  93    int         alt_code;
  94
  95    uint32_t    kpc;
  96    uint32_t    kpdk;
  97    uint32_t    kprec;
  98    uint32_t    kpmk;
  99    uint32_t    kpas;
 100    uint32_t    kpasmkp[4];
 101    uint32_t    kpkdi;
 102};
 103
 104static void pxa27x_keypad_find_pressed_key(PXA2xxKeyPadState *kp, int *row, int *col)
 105{
 106    int i;
 107    for (i = 0; i < 4; i++)
 108    {
 109        *col = i * 2;
 110        for (*row = 0; *row < 8; (*row)++) {
 111            if (kp->kpasmkp[i] & (1 << *row))
 112                return;
 113        }
 114        *col = i * 2 + 1;
 115        for (*row = 0; *row < 8; (*row)++) {
 116            if (kp->kpasmkp[i] & (1 << (*row + 16)))
 117                return;
 118        }
 119    }
 120}
 121
 122static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode)
 123{
 124    int row, col, rel, assert_irq = 0;
 125    uint32_t val;
 126
 127    if (keycode == 0xe0) {
 128        kp->alt_code = 1;
 129        return;
 130    }
 131
 132    if(!(kp->kpc & KPC_ME)) /* skip if not enabled */
 133        return;
 134
 135    rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */
 136    keycode &= ~0x80; /* strip qemu key release bit */
 137    if (kp->alt_code) {
 138        keycode |= 0x80;
 139        kp->alt_code = 0;
 140    }
 141
 142    row = kp->map[keycode].row;
 143    col = kp->map[keycode].column;
 144    if (row == -1 || col == -1) {
 145        return;
 146    }
 147
 148    val = KPASMKPx_MKC(row, col);
 149    if (rel) {
 150        if (kp->kpasmkp[col / 2] & val) {
 151            kp->kpasmkp[col / 2] &= ~val;
 152            kp->pressed_cnt--;
 153            assert_irq = 1;
 154        }
 155    } else {
 156        if (!(kp->kpasmkp[col / 2] & val)) {
 157            kp->kpasmkp[col / 2] |= val;
 158            kp->pressed_cnt++;
 159            assert_irq = 1;
 160        }
 161    }
 162    kp->kpas = ((kp->pressed_cnt & 0x1f) << 26) | (0xf << 4) | 0xf;
 163    if (kp->pressed_cnt == 1) {
 164        kp->kpas &= ~((0xf << 4) | 0xf);
 165        if (rel) {
 166            pxa27x_keypad_find_pressed_key(kp, &row, &col);
 167        }
 168        kp->kpas |= ((row & 0xf) << 4) | (col & 0xf);
 169    }
 170
 171    if (!(kp->kpc & (KPC_AS | KPC_ASACT)))
 172        assert_irq = 0;
 173
 174    if (assert_irq && (kp->kpc & KPC_MIE)) {
 175        kp->kpc |= KPC_MI;
 176        qemu_irq_raise(kp->irq);
 177    }
 178}
 179
 180static uint64_t pxa2xx_keypad_read(void *opaque, hwaddr offset,
 181                                   unsigned size)
 182{
 183    PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
 184    uint32_t tmp;
 185
 186    switch (offset) {
 187    case KPC:
 188        tmp = s->kpc;
 189        if(tmp & KPC_MI)
 190            s->kpc &= ~(KPC_MI);
 191        if(tmp & KPC_DI)
 192            s->kpc &= ~(KPC_DI);
 193        qemu_irq_lower(s->irq);
 194        return tmp;
 195        break;
 196    case KPDK:
 197        return s->kpdk;
 198        break;
 199    case KPREC:
 200        tmp = s->kprec;
 201        if(tmp & KPREC_OF1)
 202            s->kprec &= ~(KPREC_OF1);
 203        if(tmp & KPREC_UF1)
 204            s->kprec &= ~(KPREC_UF1);
 205        if(tmp & KPREC_OF0)
 206            s->kprec &= ~(KPREC_OF0);
 207        if(tmp & KPREC_UF0)
 208            s->kprec &= ~(KPREC_UF0);
 209        return tmp;
 210        break;
 211    case KPMK:
 212        tmp = s->kpmk;
 213        if(tmp & KPMK_MKP)
 214            s->kpmk &= ~(KPMK_MKP);
 215        return tmp;
 216        break;
 217    case KPAS:
 218        return s->kpas;
 219        break;
 220    case KPASMKP0:
 221        return s->kpasmkp[0];
 222        break;
 223    case KPASMKP1:
 224        return s->kpasmkp[1];
 225        break;
 226    case KPASMKP2:
 227        return s->kpasmkp[2];
 228        break;
 229    case KPASMKP3:
 230        return s->kpasmkp[3];
 231        break;
 232    case KPKDI:
 233        return s->kpkdi;
 234        break;
 235    default:
 236        hw_error("%s: Bad offset " REG_FMT "\n", __func__, offset);
 237    }
 238
 239    return 0;
 240}
 241
 242static void pxa2xx_keypad_write(void *opaque, hwaddr offset,
 243                                uint64_t value, unsigned size)
 244{
 245    PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
 246
 247    switch (offset) {
 248    case KPC:
 249        s->kpc = value;
 250        if (s->kpc & KPC_AS) {
 251            s->kpc &= ~(KPC_AS);
 252        }
 253        break;
 254    case KPDK:
 255        s->kpdk = value;
 256        break;
 257    case KPREC:
 258        s->kprec = value;
 259        break;
 260    case KPMK:
 261        s->kpmk = value;
 262        break;
 263    case KPAS:
 264        s->kpas = value;
 265        break;
 266    case KPASMKP0:
 267        s->kpasmkp[0] = value;
 268        break;
 269    case KPASMKP1:
 270        s->kpasmkp[1] = value;
 271        break;
 272    case KPASMKP2:
 273        s->kpasmkp[2] = value;
 274        break;
 275    case KPASMKP3:
 276        s->kpasmkp[3] = value;
 277        break;
 278    case KPKDI:
 279        s->kpkdi = value;
 280        break;
 281
 282    default:
 283        hw_error("%s: Bad offset " REG_FMT "\n", __func__, offset);
 284    }
 285}
 286
 287static const MemoryRegionOps pxa2xx_keypad_ops = {
 288    .read = pxa2xx_keypad_read,
 289    .write = pxa2xx_keypad_write,
 290    .endianness = DEVICE_NATIVE_ENDIAN,
 291};
 292
 293static const VMStateDescription vmstate_pxa2xx_keypad = {
 294    .name = "pxa2xx_keypad",
 295    .version_id = 0,
 296    .minimum_version_id = 0,
 297    .fields = (VMStateField[]) {
 298        VMSTATE_UINT32(kpc, PXA2xxKeyPadState),
 299        VMSTATE_UINT32(kpdk, PXA2xxKeyPadState),
 300        VMSTATE_UINT32(kprec, PXA2xxKeyPadState),
 301        VMSTATE_UINT32(kpmk, PXA2xxKeyPadState),
 302        VMSTATE_UINT32(kpas, PXA2xxKeyPadState),
 303        VMSTATE_UINT32_ARRAY(kpasmkp, PXA2xxKeyPadState, 4),
 304        VMSTATE_UINT32(kpkdi, PXA2xxKeyPadState),
 305        VMSTATE_END_OF_LIST()
 306    }
 307};
 308
 309PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem,
 310                                      hwaddr base,
 311                                      qemu_irq irq)
 312{
 313    PXA2xxKeyPadState *s;
 314
 315    s = (PXA2xxKeyPadState *) g_malloc0(sizeof(PXA2xxKeyPadState));
 316    s->irq = irq;
 317
 318    memory_region_init_io(&s->iomem, NULL, &pxa2xx_keypad_ops, s,
 319                          "pxa2xx-keypad", 0x00100000);
 320    memory_region_add_subregion(sysmem, base, &s->iomem);
 321
 322    vmstate_register(NULL, 0, &vmstate_pxa2xx_keypad, s);
 323
 324    return s;
 325}
 326
 327void pxa27x_register_keypad(PXA2xxKeyPadState *kp,
 328                            const struct keymap *map, int size)
 329{
 330    if(!map || size < 0x80) {
 331        fprintf(stderr, "%s - No PXA keypad map defined\n", __func__);
 332        exit(-1);
 333    }
 334
 335    kp->map = map;
 336    qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp);
 337}
 338