pxa2xx_keypad.c 9.6 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * Intel PXA27X Keypad Controller emulation.
 *
 * Copyright (c) 2007 MontaVista Software, Inc
 * Written by Armin Kuster <akuster@kama-aina.net>
 *              or  <Akuster@mvista.com>
 *
 * This code is licensed under the GPLv2.
9 10 11
 *
 * Contributions after 2012-01-13 are licensed under the terms of the
 * GNU GPL, version 2 or (at your option) any later version.
12 13
 */

P
Peter Maydell 已提交
14
#include "qemu/osdep.h"
15
#include "hw/hw.h"
P
Paolo Bonzini 已提交
16
#include "hw/arm/pxa.h"
17
#include "ui/console.h"
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

/*
 * Keypad
 */
#define KPC         0x00    /* Keypad Interface Control register */
#define KPDK        0x08    /* Keypad Interface Direct Key register */
#define KPREC       0x10    /* Keypad Interface Rotary Encoder register */
#define KPMK        0x18    /* Keypad Interface Matrix Key register */
#define KPAS        0x20    /* Keypad Interface Automatic Scan register */
#define KPASMKP0    0x28    /* Keypad Interface Automatic Scan Multiple
                                Key Presser register 0 */
#define KPASMKP1    0x30    /* Keypad Interface Automatic Scan Multiple
                                Key Presser register 1 */
#define KPASMKP2    0x38    /* Keypad Interface Automatic Scan Multiple
                                Key Presser register 2 */
#define KPASMKP3    0x40    /* Keypad Interface Automatic Scan Multiple
                                Key Presser register 3 */
#define KPKDI       0x48    /* Keypad Interface Key Debounce Interval
                                register */

/* Keypad defines */
#define KPC_AS          (0x1 << 30)  /* Automatic Scan bit */
#define KPC_ASACT       (0x1 << 29)  /* Automatic Scan on Activity */
#define KPC_MI          (0x1 << 22)  /* Matrix interrupt bit */
#define KPC_IMKP        (0x1 << 21)  /* Ignore Multiple Key Press */
#define KPC_MS7         (0x1 << 20)  /* Matrix scan line 7 */
#define KPC_MS6         (0x1 << 19)  /* Matrix scan line 6 */
#define KPC_MS5         (0x1 << 18)  /* Matrix scan line 5 */
#define KPC_MS4         (0x1 << 17)  /* Matrix scan line 4 */
#define KPC_MS3         (0x1 << 16)  /* Matrix scan line 3 */
#define KPC_MS2         (0x1 << 15)  /* Matrix scan line 2 */
#define KPC_MS1         (0x1 << 14)  /* Matrix scan line 1 */
#define KPC_MS0         (0x1 << 13)  /* Matrix scan line 0 */
#define KPC_ME          (0x1 << 12)  /* Matrix Keypad Enable */
#define KPC_MIE         (0x1 << 11)  /* Matrix Interrupt Enable */
#define KPC_DK_DEB_SEL  (0x1 <<  9)  /* Direct Keypad Debounce Select */
#define KPC_DI          (0x1 <<  5)  /* Direct key interrupt bit */
#define KPC_RE_ZERO_DEB (0x1 <<  4)  /* Rotary Encoder Zero Debounce */
#define KPC_REE1        (0x1 <<  3)  /* Rotary Encoder1 Enable */
#define KPC_REE0        (0x1 <<  2)  /* Rotary Encoder0 Enable */
#define KPC_DE          (0x1 <<  1)  /* Direct Keypad Enable */
#define KPC_DIE         (0x1 <<  0)  /* Direct Keypad interrupt Enable */

#define KPDK_DKP        (0x1 << 31)
#define KPDK_DK7        (0x1 <<  7)
#define KPDK_DK6        (0x1 <<  6)
#define KPDK_DK5        (0x1 <<  5)
#define KPDK_DK4        (0x1 <<  4)
#define KPDK_DK3        (0x1 <<  3)
#define KPDK_DK2        (0x1 <<  2)
#define KPDK_DK1        (0x1 <<  1)
#define KPDK_DK0        (0x1 <<  0)

#define KPREC_OF1       (0x1 << 31)
#define KPREC_UF1       (0x1 << 30)
#define KPREC_OF0       (0x1 << 15)
#define KPREC_UF0       (0x1 << 14)

#define KPMK_MKP        (0x1 << 31)
#define KPAS_SO         (0x1 << 31)
#define KPASMKPx_SO     (0x1 << 31)


#define KPASMKPx_MKC(row, col)  (1 << (row + 16 * (col % 2)))

#define PXAKBD_MAXROW   8
#define PXAKBD_MAXCOL   8

P
Paul Brook 已提交
86
struct PXA2xxKeyPadState {
87
    MemoryRegion iomem;
88
    qemu_irq    irq;
89
    const struct  keymap *map;
90
    int         pressed_cnt;
91
    int         alt_code;
92 93 94 95 96 97

    uint32_t    kpc;
    uint32_t    kpdk;
    uint32_t    kprec;
    uint32_t    kpmk;
    uint32_t    kpas;
98
    uint32_t    kpasmkp[4];
99 100 101
    uint32_t    kpkdi;
};

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
static void pxa27x_keypad_find_pressed_key(PXA2xxKeyPadState *kp, int *row, int *col)
{
    int i;
    for (i = 0; i < 4; i++)
    {
        *col = i * 2;
        for (*row = 0; *row < 8; (*row)++) {
            if (kp->kpasmkp[i] & (1 << *row))
                return;
        }
        *col = i * 2 + 1;
        for (*row = 0; *row < 8; (*row)++) {
            if (kp->kpasmkp[i] & (1 << (*row + 16)))
                return;
        }
    }
}

P
Paul Brook 已提交
120
static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode)
121
{
122 123
    int row, col, rel, assert_irq = 0;
    uint32_t val;
124

125 126 127 128 129
    if (keycode == 0xe0) {
        kp->alt_code = 1;
        return;
    }

130 131 132
    if(!(kp->kpc & KPC_ME)) /* skip if not enabled */
        return;

133 134 135 136 137 138
    rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */
    keycode &= ~0x80; /* strip qemu key release bit */
    if (kp->alt_code) {
        keycode |= 0x80;
        kp->alt_code = 0;
    }
139

140 141 142 143 144
    row = kp->map[keycode].row;
    col = kp->map[keycode].column;
    if (row == -1 || col == -1) {
        return;
    }
145

146 147 148 149 150 151
    val = KPASMKPx_MKC(row, col);
    if (rel) {
        if (kp->kpasmkp[col / 2] & val) {
            kp->kpasmkp[col / 2] &= ~val;
            kp->pressed_cnt--;
            assert_irq = 1;
152
        }
153 154 155 156 157
    } else {
        if (!(kp->kpasmkp[col / 2] & val)) {
            kp->kpasmkp[col / 2] |= val;
            kp->pressed_cnt++;
            assert_irq = 1;
158
        }
159
    }
160 161 162 163 164 165 166 167 168
    kp->kpas = ((kp->pressed_cnt & 0x1f) << 26) | (0xf << 4) | 0xf;
    if (kp->pressed_cnt == 1) {
        kp->kpas &= ~((0xf << 4) | 0xf);
        if (rel) {
            pxa27x_keypad_find_pressed_key(kp, &row, &col);
        }
        kp->kpas |= ((row & 0xf) << 4) | (col & 0xf);
    }

169
    if (!(kp->kpc & (KPC_AS | KPC_ASACT)))
170
        assert_irq = 0;
171

172
    if (assert_irq && (kp->kpc & KPC_MIE)) {
173 174 175 176 177
        kp->kpc |= KPC_MI;
        qemu_irq_raise(kp->irq);
    }
}

A
Avi Kivity 已提交
178
static uint64_t pxa2xx_keypad_read(void *opaque, hwaddr offset,
179
                                   unsigned size)
180
{
P
Paul Brook 已提交
181
    PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
    uint32_t tmp;

    switch (offset) {
    case KPC:
        tmp = s->kpc;
        if(tmp & KPC_MI)
            s->kpc &= ~(KPC_MI);
        if(tmp & KPC_DI)
            s->kpc &= ~(KPC_DI);
        qemu_irq_lower(s->irq);
        return tmp;
        break;
    case KPDK:
        return s->kpdk;
        break;
    case KPREC:
        tmp = s->kprec;
        if(tmp & KPREC_OF1)
            s->kprec &= ~(KPREC_OF1);
        if(tmp & KPREC_UF1)
            s->kprec &= ~(KPREC_UF1);
        if(tmp & KPREC_OF0)
            s->kprec &= ~(KPREC_OF0);
        if(tmp & KPREC_UF0)
            s->kprec &= ~(KPREC_UF0);
        return tmp;
        break;
    case KPMK:
        tmp = s->kpmk;
        if(tmp & KPMK_MKP)
            s->kpmk &= ~(KPMK_MKP);
        return tmp;
        break;
    case KPAS:
        return s->kpas;
        break;
    case KPASMKP0:
219
        return s->kpasmkp[0];
220 221
        break;
    case KPASMKP1:
222
        return s->kpasmkp[1];
223 224
        break;
    case KPASMKP2:
225
        return s->kpasmkp[2];
226 227
        break;
    case KPASMKP3:
228
        return s->kpasmkp[3];
229 230 231 232 233
        break;
    case KPKDI:
        return s->kpkdi;
        break;
    default:
P
Paul Brook 已提交
234
        hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
235 236 237 238 239
    }

    return 0;
}

A
Avi Kivity 已提交
240
static void pxa2xx_keypad_write(void *opaque, hwaddr offset,
241
                                uint64_t value, unsigned size)
242
{
P
Paul Brook 已提交
243
    PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
244 245 246 247

    switch (offset) {
    case KPC:
        s->kpc = value;
248 249 250
        if (s->kpc & KPC_AS) {
            s->kpc &= ~(KPC_AS);
        }
251 252 253 254 255 256 257 258 259 260 261 262 263 264
        break;
    case KPDK:
        s->kpdk = value;
        break;
    case KPREC:
        s->kprec = value;
        break;
    case KPMK:
        s->kpmk = value;
        break;
    case KPAS:
        s->kpas = value;
        break;
    case KPASMKP0:
265
        s->kpasmkp[0] = value;
266 267
        break;
    case KPASMKP1:
268
        s->kpasmkp[1] = value;
269 270
        break;
    case KPASMKP2:
271
        s->kpasmkp[2] = value;
272 273
        break;
    case KPASMKP3:
274
        s->kpasmkp[3] = value;
275 276 277 278 279 280
        break;
    case KPKDI:
        s->kpkdi = value;
        break;

    default:
P
Paul Brook 已提交
281
        hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
282 283 284
    }
}

285 286 287 288
static const MemoryRegionOps pxa2xx_keypad_ops = {
    .read = pxa2xx_keypad_read,
    .write = pxa2xx_keypad_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
289 290
};

J
Juan Quintela 已提交
291 292 293 294
static const VMStateDescription vmstate_pxa2xx_keypad = {
    .name = "pxa2xx_keypad",
    .version_id = 0,
    .minimum_version_id = 0,
295
    .fields = (VMStateField[]) {
J
Juan Quintela 已提交
296 297 298 299 300 301 302 303 304 305
        VMSTATE_UINT32(kpc, PXA2xxKeyPadState),
        VMSTATE_UINT32(kpdk, PXA2xxKeyPadState),
        VMSTATE_UINT32(kprec, PXA2xxKeyPadState),
        VMSTATE_UINT32(kpmk, PXA2xxKeyPadState),
        VMSTATE_UINT32(kpas, PXA2xxKeyPadState),
        VMSTATE_UINT32_ARRAY(kpasmkp, PXA2xxKeyPadState, 4),
        VMSTATE_UINT32(kpkdi, PXA2xxKeyPadState),
        VMSTATE_END_OF_LIST()
    }
};
306

307
PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem,
A
Avi Kivity 已提交
308
                                      hwaddr base,
309
                                      qemu_irq irq)
310
{
P
Paul Brook 已提交
311
    PXA2xxKeyPadState *s;
312

313
    s = (PXA2xxKeyPadState *) g_malloc0(sizeof(PXA2xxKeyPadState));
314 315
    s->irq = irq;

316
    memory_region_init_io(&s->iomem, NULL, &pxa2xx_keypad_ops, s,
317 318
                          "pxa2xx-keypad", 0x00100000);
    memory_region_add_subregion(sysmem, base, &s->iomem);
319

J
Juan Quintela 已提交
320
    vmstate_register(NULL, 0, &vmstate_pxa2xx_keypad, s);
321 322 323 324

    return s;
}

325 326
void pxa27x_register_keypad(PXA2xxKeyPadState *kp,
                            const struct keymap *map, int size)
327
{
328
    if(!map || size < 0x80) {
329 330 331 332 333 334 335
        fprintf(stderr, "%s - No PXA keypad map defined\n", __FUNCTION__);
        exit(-1);
    }

    kp->map = map;
    qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp);
}