pxa2xx_keypad.c 9.7 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 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 82 83 84
 */

#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 已提交
85
struct PXA2xxKeyPadState {
86
    MemoryRegion iomem;
87 88
    qemu_irq    irq;
    struct  keymap *map;
89
    int         pressed_cnt;
90
    int         alt_code;
91 92 93 94 95 96

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

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

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

129 130 131 132 133 134 135 136 137
    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 */
138 139 140 141
        if (kp->alt_code) {
            keycode |= 0x80;
            kp->alt_code = 0;
        }
142

143 144 145 146
        row = kp->map[keycode].row;
        col = kp->map[keycode].column;
        if(row == -1 || col == -1)
            return;
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

        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);
        }
169 170 171 172 173
        goto out;
    }
    return;

out:
174
    if (assert_irq && (kp->kpc & KPC_MIE)) {
175 176 177 178 179 180
        kp->kpc |= KPC_MI;
        qemu_irq_raise(kp->irq);
    }
    return;
}

181 182
static uint64_t pxa2xx_keypad_read(void *opaque, target_phys_addr_t offset,
                                   unsigned size)
183
{
P
Paul Brook 已提交
184
    PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
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 219 220 221
    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:
222
        return s->kpasmkp[0];
223 224
        break;
    case KPASMKP1:
225
        return s->kpasmkp[1];
226 227
        break;
    case KPASMKP2:
228
        return s->kpasmkp[2];
229 230
        break;
    case KPASMKP3:
231
        return s->kpasmkp[3];
232 233 234 235 236
        break;
    case KPKDI:
        return s->kpkdi;
        break;
    default:
P
Paul Brook 已提交
237
        hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset);
238 239 240 241 242
    }

    return 0;
}

243 244
static void pxa2xx_keypad_write(void *opaque, target_phys_addr_t offset,
                                uint64_t value, unsigned size)
245
{
P
Paul Brook 已提交
246
    PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264

    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:
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 295 296 297 298 299 300 301 302 303 304 305 306
static const VMStateDescription vmstate_pxa2xx_keypad = {
    .name = "pxa2xx_keypad",
    .version_id = 0,
    .minimum_version_id = 0,
    .minimum_version_id_old = 0,
    .fields      = (VMStateField[]) {
        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()
    }
};
307

308 309 310
PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem,
                                      target_phys_addr_t base,
                                      qemu_irq irq)
311
{
P
Paul Brook 已提交
312
    PXA2xxKeyPadState *s;
313

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

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

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

    return s;
}

P
Paul Brook 已提交
326
void pxa27x_register_keypad(PXA2xxKeyPadState *kp, struct keymap *map,
327 328
        int size)
{
329
    if(!map || size < 0x80) {
330 331 332 333 334 335 336
        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);
}