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