pxa2xx_keypad.c 9.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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
/*
 * 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.
 */

#include "hw.h"
#include "pxa.h"
#include "console.h"

/*
 * 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 已提交
82
struct PXA2xxKeyPadState {
83 84
    qemu_irq    irq;
    struct  keymap *map;
85
    int         pressed_cnt;
86 87 88 89 90 91

    uint32_t    kpc;
    uint32_t    kpdk;
    uint32_t    kprec;
    uint32_t    kpmk;
    uint32_t    kpas;
92
    uint32_t    kpasmkp[4];
93 94 95
    uint32_t    kpkdi;
};

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
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 已提交
114
static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode)
115
{
116 117
    int row, col, rel, assert_irq = 0;
    uint32_t val;
118 119 120 121 122 123 124 125 126 127

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

    if(kp->kpc & KPC_AS || kp->kpc & KPC_ASACT) {
        if(kp->kpc & KPC_AS)
            kp->kpc &= ~(KPC_AS);

        rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */
        keycode &= ~(0x80); /* strip qemu key release bit */
128

129 130 131 132
        row = kp->map[keycode].row;
        col = kp->map[keycode].column;
        if(row == -1 || col == -1)
            return;
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154

        val = KPASMKPx_MKC(row, col);
        if (rel) {
            if (kp->kpasmkp[col / 2] & val) {
                kp->kpasmkp[col / 2] &= ~val;
                kp->pressed_cnt--;
                assert_irq = 1;
            }
        } else {
            if (!(kp->kpasmkp[col / 2] & val)) {
                kp->kpasmkp[col / 2] |= val;
                kp->pressed_cnt++;
                assert_irq = 1;
            }
        }
        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);
        }
155 156 157 158 159
        goto out;
    }
    return;

out:
160
    if (assert_irq && (kp->kpc & KPC_MIE)) {
161 162 163 164 165 166
        kp->kpc |= KPC_MI;
        qemu_irq_raise(kp->irq);
    }
    return;
}

A
Anthony Liguori 已提交
167
static uint32_t pxa2xx_keypad_read(void *opaque, target_phys_addr_t offset)
168
{
P
Paul Brook 已提交
169
    PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
170 171 172 173 174 175 176 177 178 179 180 181 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
    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:
207
        return s->kpasmkp[0];
208 209
        break;
    case KPASMKP1:
210
        return s->kpasmkp[1];
211 212
        break;
    case KPASMKP2:
213
        return s->kpasmkp[2];
214 215
        break;
    case KPASMKP3:
216
        return s->kpasmkp[3];
217 218 219 220 221
        break;
    case KPKDI:
        return s->kpkdi;
        break;
    default:
P
Paul Brook 已提交
222
        hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
223 224 225 226 227 228
    }

    return 0;
}

static void pxa2xx_keypad_write(void *opaque,
A
Anthony Liguori 已提交
229
                target_phys_addr_t offset, uint32_t value)
230
{
P
Paul Brook 已提交
231
    PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249

    switch (offset) {
    case KPC:
        s->kpc = value;
        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:
250
        s->kpasmkp[0] = value;
251 252
        break;
    case KPASMKP1:
253
        s->kpasmkp[1] = value;
254 255
        break;
    case KPASMKP2:
256
        s->kpasmkp[2] = value;
257 258
        break;
    case KPASMKP3:
259
        s->kpasmkp[3] = value;
260 261 262 263 264 265
        break;
    case KPKDI:
        s->kpkdi = value;
        break;

    default:
P
Paul Brook 已提交
266
        hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
267 268 269
    }
}

270
static CPUReadMemoryFunc * const pxa2xx_keypad_readfn[] = {
271 272 273 274 275
    pxa2xx_keypad_read,
    pxa2xx_keypad_read,
    pxa2xx_keypad_read
};

276
static CPUWriteMemoryFunc * const pxa2xx_keypad_writefn[] = {
277 278 279 280 281 282 283
    pxa2xx_keypad_write,
    pxa2xx_keypad_write,
    pxa2xx_keypad_write
};

static void pxa2xx_keypad_save(QEMUFile *f, void *opaque)
{
P
Paul Brook 已提交
284
    PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
285 286 287 288 289 290

    qemu_put_be32s(f, &s->kpc);
    qemu_put_be32s(f, &s->kpdk);
    qemu_put_be32s(f, &s->kprec);
    qemu_put_be32s(f, &s->kpmk);
    qemu_put_be32s(f, &s->kpas);
291 292 293 294
    qemu_put_be32s(f, &s->kpasmkp[0]);
    qemu_put_be32s(f, &s->kpasmkp[1]);
    qemu_put_be32s(f, &s->kpasmkp[2]);
    qemu_put_be32s(f, &s->kpasmkp[3]);
295 296 297 298 299 300
    qemu_put_be32s(f, &s->kpkdi);

}

static int pxa2xx_keypad_load(QEMUFile *f, void *opaque, int version_id)
{
P
Paul Brook 已提交
301
    PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
302 303 304 305 306 307

    qemu_get_be32s(f, &s->kpc);
    qemu_get_be32s(f, &s->kpdk);
    qemu_get_be32s(f, &s->kprec);
    qemu_get_be32s(f, &s->kpmk);
    qemu_get_be32s(f, &s->kpas);
308 309 310 311
    qemu_get_be32s(f, &s->kpasmkp[0]);
    qemu_get_be32s(f, &s->kpasmkp[1]);
    qemu_get_be32s(f, &s->kpasmkp[2]);
    qemu_get_be32s(f, &s->kpasmkp[3]);
312 313 314 315 316
    qemu_get_be32s(f, &s->kpkdi);

    return 0;
}

A
Anthony Liguori 已提交
317
PXA2xxKeyPadState *pxa27x_keypad_init(target_phys_addr_t base,
318 319 320
        qemu_irq irq)
{
    int iomemtype;
P
Paul Brook 已提交
321
    PXA2xxKeyPadState *s;
322

P
Paul Brook 已提交
323
    s = (PXA2xxKeyPadState *) qemu_mallocz(sizeof(PXA2xxKeyPadState));
324 325
    s->irq = irq;

326
    iomemtype = cpu_register_io_memory(pxa2xx_keypad_readfn,
327
                    pxa2xx_keypad_writefn, s, DEVICE_NATIVE_ENDIAN);
328
    cpu_register_physical_memory(base, 0x00100000, iomemtype);
329

A
Alex Williamson 已提交
330
    register_savevm(NULL, "pxa2xx_keypad", 0, 0,
331 332 333 334 335
                    pxa2xx_keypad_save, pxa2xx_keypad_load, s);

    return s;
}

P
Paul Brook 已提交
336
void pxa27x_register_keypad(PXA2xxKeyPadState *kp, struct keymap *map,
337 338
        int size)
{
339
    if(!map || size < 0x80) {
340 341 342 343 344 345 346
        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);
}