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 "qemu/log.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    case KPDK:
 196        return s->kpdk;
 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    case KPMK:
 209        tmp = s->kpmk;
 210        if(tmp & KPMK_MKP)
 211            s->kpmk &= ~(KPMK_MKP);
 212        return tmp;
 213    case KPAS:
 214        return s->kpas;
 215    case KPASMKP0:
 216        return s->kpasmkp[0];
 217    case KPASMKP1:
 218        return s->kpasmkp[1];
 219    case KPASMKP2:
 220        return s->kpasmkp[2];
 221    case KPASMKP3:
 222        return s->kpasmkp[3];
 223    case KPKDI:
 224        return s->kpkdi;
 225    default:
 226        qemu_log_mask(LOG_GUEST_ERROR,
 227                      "%s: Bad read offset 0x%"HWADDR_PRIx"\n",
 228                      __func__, offset);
 229    }
 230
 231    return 0;
 232}
 233
 234static void pxa2xx_keypad_write(void *opaque, hwaddr offset,
 235                                uint64_t value, unsigned size)
 236{
 237    PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
 238
 239    switch (offset) {
 240    case KPC:
 241        s->kpc = value;
 242        if (s->kpc & KPC_AS) {
 243            s->kpc &= ~(KPC_AS);
 244        }
 245        break;
 246    case KPDK:
 247        s->kpdk = value;
 248        break;
 249    case KPREC:
 250        s->kprec = value;
 251        break;
 252    case KPMK:
 253        s->kpmk = value;
 254        break;
 255    case KPAS:
 256        s->kpas = value;
 257        break;
 258    case KPASMKP0:
 259        s->kpasmkp[0] = value;
 260        break;
 261    case KPASMKP1:
 262        s->kpasmkp[1] = value;
 263        break;
 264    case KPASMKP2:
 265        s->kpasmkp[2] = value;
 266        break;
 267    case KPASMKP3:
 268        s->kpasmkp[3] = value;
 269        break;
 270    case KPKDI:
 271        s->kpkdi = value;
 272        break;
 273
 274    default:
 275        qemu_log_mask(LOG_GUEST_ERROR,
 276                      "%s: Bad write offset 0x%"HWADDR_PRIx"\n",
 277                      __func__, offset);
 278    }
 279}
 280
 281static const MemoryRegionOps pxa2xx_keypad_ops = {
 282    .read = pxa2xx_keypad_read,
 283    .write = pxa2xx_keypad_write,
 284    .endianness = DEVICE_NATIVE_ENDIAN,
 285};
 286
 287static const VMStateDescription vmstate_pxa2xx_keypad = {
 288    .name = "pxa2xx_keypad",
 289    .version_id = 0,
 290    .minimum_version_id = 0,
 291    .fields = (VMStateField[]) {
 292        VMSTATE_UINT32(kpc, PXA2xxKeyPadState),
 293        VMSTATE_UINT32(kpdk, PXA2xxKeyPadState),
 294        VMSTATE_UINT32(kprec, PXA2xxKeyPadState),
 295        VMSTATE_UINT32(kpmk, PXA2xxKeyPadState),
 296        VMSTATE_UINT32(kpas, PXA2xxKeyPadState),
 297        VMSTATE_UINT32_ARRAY(kpasmkp, PXA2xxKeyPadState, 4),
 298        VMSTATE_UINT32(kpkdi, PXA2xxKeyPadState),
 299        VMSTATE_END_OF_LIST()
 300    }
 301};
 302
 303PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem,
 304                                      hwaddr base,
 305                                      qemu_irq irq)
 306{
 307    PXA2xxKeyPadState *s;
 308
 309    s = (PXA2xxKeyPadState *) g_malloc0(sizeof(PXA2xxKeyPadState));
 310    s->irq = irq;
 311
 312    memory_region_init_io(&s->iomem, NULL, &pxa2xx_keypad_ops, s,
 313                          "pxa2xx-keypad", 0x00100000);
 314    memory_region_add_subregion(sysmem, base, &s->iomem);
 315
 316    vmstate_register(NULL, 0, &vmstate_pxa2xx_keypad, s);
 317
 318    return s;
 319}
 320
 321void pxa27x_register_keypad(PXA2xxKeyPadState *kp,
 322                            const struct keymap *map, int size)
 323{
 324    if(!map || size < 0x80) {
 325        fprintf(stderr, "%s - No PXA keypad map defined\n", __func__);
 326        exit(-1);
 327    }
 328
 329    kp->map = map;
 330    qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp);
 331}
 332