13bd88451SPaolo Bonzini /*
23bd88451SPaolo Bonzini * Intel PXA27X Keypad Controller emulation.
33bd88451SPaolo Bonzini *
43bd88451SPaolo Bonzini * Copyright (c) 2007 MontaVista Software, Inc
53bd88451SPaolo Bonzini * Written by Armin Kuster <akuster@kama-aina.net>
63bd88451SPaolo Bonzini * or <Akuster@mvista.com>
73bd88451SPaolo Bonzini *
83bd88451SPaolo Bonzini * This code is licensed under the GPLv2.
93bd88451SPaolo Bonzini *
103bd88451SPaolo Bonzini * Contributions after 2012-01-13 are licensed under the terms of the
113bd88451SPaolo Bonzini * GNU GPL, version 2 or (at your option) any later version.
123bd88451SPaolo Bonzini */
133bd88451SPaolo Bonzini
148ef94f0bSPeter Maydell #include "qemu/osdep.h"
153a37f239SPhilippe Mathieu-Daudé #include "qemu/log.h"
1664552b6bSMarkus Armbruster #include "hw/irq.h"
17d6454270SMarkus Armbruster #include "migration/vmstate.h"
183bd88451SPaolo Bonzini #include "hw/arm/pxa.h"
193bd88451SPaolo Bonzini #include "ui/console.h"
203bd88451SPaolo Bonzini
213bd88451SPaolo Bonzini /*
223bd88451SPaolo Bonzini * Keypad
233bd88451SPaolo Bonzini */
243bd88451SPaolo Bonzini #define KPC 0x00 /* Keypad Interface Control register */
253bd88451SPaolo Bonzini #define KPDK 0x08 /* Keypad Interface Direct Key register */
263bd88451SPaolo Bonzini #define KPREC 0x10 /* Keypad Interface Rotary Encoder register */
273bd88451SPaolo Bonzini #define KPMK 0x18 /* Keypad Interface Matrix Key register */
283bd88451SPaolo Bonzini #define KPAS 0x20 /* Keypad Interface Automatic Scan register */
293bd88451SPaolo Bonzini #define KPASMKP0 0x28 /* Keypad Interface Automatic Scan Multiple
303bd88451SPaolo Bonzini Key Presser register 0 */
313bd88451SPaolo Bonzini #define KPASMKP1 0x30 /* Keypad Interface Automatic Scan Multiple
323bd88451SPaolo Bonzini Key Presser register 1 */
333bd88451SPaolo Bonzini #define KPASMKP2 0x38 /* Keypad Interface Automatic Scan Multiple
343bd88451SPaolo Bonzini Key Presser register 2 */
353bd88451SPaolo Bonzini #define KPASMKP3 0x40 /* Keypad Interface Automatic Scan Multiple
363bd88451SPaolo Bonzini Key Presser register 3 */
373bd88451SPaolo Bonzini #define KPKDI 0x48 /* Keypad Interface Key Debounce Interval
383bd88451SPaolo Bonzini register */
393bd88451SPaolo Bonzini
403bd88451SPaolo Bonzini /* Keypad defines */
413bd88451SPaolo Bonzini #define KPC_AS (0x1 << 30) /* Automatic Scan bit */
423bd88451SPaolo Bonzini #define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */
433bd88451SPaolo Bonzini #define KPC_MI (0x1 << 22) /* Matrix interrupt bit */
443bd88451SPaolo Bonzini #define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */
453bd88451SPaolo Bonzini #define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */
463bd88451SPaolo Bonzini #define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */
473bd88451SPaolo Bonzini #define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */
483bd88451SPaolo Bonzini #define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */
493bd88451SPaolo Bonzini #define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */
503bd88451SPaolo Bonzini #define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */
513bd88451SPaolo Bonzini #define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */
523bd88451SPaolo Bonzini #define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */
533bd88451SPaolo Bonzini #define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */
543bd88451SPaolo Bonzini #define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */
553bd88451SPaolo Bonzini #define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */
563bd88451SPaolo Bonzini #define KPC_DI (0x1 << 5) /* Direct key interrupt bit */
573bd88451SPaolo Bonzini #define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */
583bd88451SPaolo Bonzini #define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */
593bd88451SPaolo Bonzini #define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */
603bd88451SPaolo Bonzini #define KPC_DE (0x1 << 1) /* Direct Keypad Enable */
613bd88451SPaolo Bonzini #define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */
623bd88451SPaolo Bonzini
633bd88451SPaolo Bonzini #define KPDK_DKP (0x1 << 31)
643bd88451SPaolo Bonzini #define KPDK_DK7 (0x1 << 7)
653bd88451SPaolo Bonzini #define KPDK_DK6 (0x1 << 6)
663bd88451SPaolo Bonzini #define KPDK_DK5 (0x1 << 5)
673bd88451SPaolo Bonzini #define KPDK_DK4 (0x1 << 4)
683bd88451SPaolo Bonzini #define KPDK_DK3 (0x1 << 3)
693bd88451SPaolo Bonzini #define KPDK_DK2 (0x1 << 2)
703bd88451SPaolo Bonzini #define KPDK_DK1 (0x1 << 1)
713bd88451SPaolo Bonzini #define KPDK_DK0 (0x1 << 0)
723bd88451SPaolo Bonzini
733bd88451SPaolo Bonzini #define KPREC_OF1 (0x1 << 31)
743bd88451SPaolo Bonzini #define KPREC_UF1 (0x1 << 30)
753bd88451SPaolo Bonzini #define KPREC_OF0 (0x1 << 15)
763bd88451SPaolo Bonzini #define KPREC_UF0 (0x1 << 14)
773bd88451SPaolo Bonzini
783bd88451SPaolo Bonzini #define KPMK_MKP (0x1 << 31)
793bd88451SPaolo Bonzini #define KPAS_SO (0x1 << 31)
803bd88451SPaolo Bonzini #define KPASMKPx_SO (0x1 << 31)
813bd88451SPaolo Bonzini
823bd88451SPaolo Bonzini
833bd88451SPaolo Bonzini #define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2)))
843bd88451SPaolo Bonzini
853bd88451SPaolo Bonzini #define PXAKBD_MAXROW 8
863bd88451SPaolo Bonzini #define PXAKBD_MAXCOL 8
873bd88451SPaolo Bonzini
883bd88451SPaolo Bonzini struct PXA2xxKeyPadState {
893bd88451SPaolo Bonzini MemoryRegion iomem;
903bd88451SPaolo Bonzini qemu_irq irq;
9152975c31SStefan Weil const struct keymap *map;
923bd88451SPaolo Bonzini int pressed_cnt;
933bd88451SPaolo Bonzini int alt_code;
943bd88451SPaolo Bonzini
953bd88451SPaolo Bonzini uint32_t kpc;
963bd88451SPaolo Bonzini uint32_t kpdk;
973bd88451SPaolo Bonzini uint32_t kprec;
983bd88451SPaolo Bonzini uint32_t kpmk;
993bd88451SPaolo Bonzini uint32_t kpas;
1003bd88451SPaolo Bonzini uint32_t kpasmkp[4];
1013bd88451SPaolo Bonzini uint32_t kpkdi;
1023bd88451SPaolo Bonzini };
1033bd88451SPaolo Bonzini
pxa27x_keypad_find_pressed_key(PXA2xxKeyPadState * kp,int * row,int * col)1043bd88451SPaolo Bonzini static void pxa27x_keypad_find_pressed_key(PXA2xxKeyPadState *kp, int *row, int *col)
1053bd88451SPaolo Bonzini {
1063bd88451SPaolo Bonzini int i;
1073bd88451SPaolo Bonzini for (i = 0; i < 4; i++)
1083bd88451SPaolo Bonzini {
1093bd88451SPaolo Bonzini *col = i * 2;
1103bd88451SPaolo Bonzini for (*row = 0; *row < 8; (*row)++) {
1113bd88451SPaolo Bonzini if (kp->kpasmkp[i] & (1 << *row))
1123bd88451SPaolo Bonzini return;
1133bd88451SPaolo Bonzini }
1143bd88451SPaolo Bonzini *col = i * 2 + 1;
1153bd88451SPaolo Bonzini for (*row = 0; *row < 8; (*row)++) {
1163bd88451SPaolo Bonzini if (kp->kpasmkp[i] & (1 << (*row + 16)))
1173bd88451SPaolo Bonzini return;
1183bd88451SPaolo Bonzini }
1193bd88451SPaolo Bonzini }
1203bd88451SPaolo Bonzini }
1213bd88451SPaolo Bonzini
pxa27x_keyboard_event(PXA2xxKeyPadState * kp,int keycode)1223bd88451SPaolo Bonzini static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode)
1233bd88451SPaolo Bonzini {
1243bd88451SPaolo Bonzini int row, col, rel, assert_irq = 0;
1253bd88451SPaolo Bonzini uint32_t val;
1263bd88451SPaolo Bonzini
1273bd88451SPaolo Bonzini if (keycode == 0xe0) {
1283bd88451SPaolo Bonzini kp->alt_code = 1;
1293bd88451SPaolo Bonzini return;
1303bd88451SPaolo Bonzini }
1313bd88451SPaolo Bonzini
1323bd88451SPaolo Bonzini if(!(kp->kpc & KPC_ME)) /* skip if not enabled */
1333bd88451SPaolo Bonzini return;
1343bd88451SPaolo Bonzini
1353bd88451SPaolo Bonzini rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */
1363bd88451SPaolo Bonzini keycode &= ~0x80; /* strip qemu key release bit */
1373bd88451SPaolo Bonzini if (kp->alt_code) {
1383bd88451SPaolo Bonzini keycode |= 0x80;
1393bd88451SPaolo Bonzini kp->alt_code = 0;
1403bd88451SPaolo Bonzini }
1413bd88451SPaolo Bonzini
1423bd88451SPaolo Bonzini row = kp->map[keycode].row;
1433bd88451SPaolo Bonzini col = kp->map[keycode].column;
1443bd88451SPaolo Bonzini if (row == -1 || col == -1) {
1453bd88451SPaolo Bonzini return;
1463bd88451SPaolo Bonzini }
1473bd88451SPaolo Bonzini
1483bd88451SPaolo Bonzini val = KPASMKPx_MKC(row, col);
1493bd88451SPaolo Bonzini if (rel) {
1503bd88451SPaolo Bonzini if (kp->kpasmkp[col / 2] & val) {
1513bd88451SPaolo Bonzini kp->kpasmkp[col / 2] &= ~val;
1523bd88451SPaolo Bonzini kp->pressed_cnt--;
1533bd88451SPaolo Bonzini assert_irq = 1;
1543bd88451SPaolo Bonzini }
1553bd88451SPaolo Bonzini } else {
1563bd88451SPaolo Bonzini if (!(kp->kpasmkp[col / 2] & val)) {
1573bd88451SPaolo Bonzini kp->kpasmkp[col / 2] |= val;
1583bd88451SPaolo Bonzini kp->pressed_cnt++;
1593bd88451SPaolo Bonzini assert_irq = 1;
1603bd88451SPaolo Bonzini }
1613bd88451SPaolo Bonzini }
1623bd88451SPaolo Bonzini kp->kpas = ((kp->pressed_cnt & 0x1f) << 26) | (0xf << 4) | 0xf;
1633bd88451SPaolo Bonzini if (kp->pressed_cnt == 1) {
1643bd88451SPaolo Bonzini kp->kpas &= ~((0xf << 4) | 0xf);
1653bd88451SPaolo Bonzini if (rel) {
1663bd88451SPaolo Bonzini pxa27x_keypad_find_pressed_key(kp, &row, &col);
1673bd88451SPaolo Bonzini }
1683bd88451SPaolo Bonzini kp->kpas |= ((row & 0xf) << 4) | (col & 0xf);
1693bd88451SPaolo Bonzini }
1703bd88451SPaolo Bonzini
1713bd88451SPaolo Bonzini if (!(kp->kpc & (KPC_AS | KPC_ASACT)))
1723bd88451SPaolo Bonzini assert_irq = 0;
1733bd88451SPaolo Bonzini
1743bd88451SPaolo Bonzini if (assert_irq && (kp->kpc & KPC_MIE)) {
1753bd88451SPaolo Bonzini kp->kpc |= KPC_MI;
1763bd88451SPaolo Bonzini qemu_irq_raise(kp->irq);
1773bd88451SPaolo Bonzini }
1783bd88451SPaolo Bonzini }
1793bd88451SPaolo Bonzini
pxa2xx_keypad_read(void * opaque,hwaddr offset,unsigned size)1803bd88451SPaolo Bonzini static uint64_t pxa2xx_keypad_read(void *opaque, hwaddr offset,
1813bd88451SPaolo Bonzini unsigned size)
1823bd88451SPaolo Bonzini {
1833bd88451SPaolo Bonzini PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
1843bd88451SPaolo Bonzini uint32_t tmp;
1853bd88451SPaolo Bonzini
1863bd88451SPaolo Bonzini switch (offset) {
1873bd88451SPaolo Bonzini case KPC:
1883bd88451SPaolo Bonzini tmp = s->kpc;
1893bd88451SPaolo Bonzini if(tmp & KPC_MI)
1903bd88451SPaolo Bonzini s->kpc &= ~(KPC_MI);
1913bd88451SPaolo Bonzini if(tmp & KPC_DI)
1923bd88451SPaolo Bonzini s->kpc &= ~(KPC_DI);
1933bd88451SPaolo Bonzini qemu_irq_lower(s->irq);
1943bd88451SPaolo Bonzini return tmp;
1953bd88451SPaolo Bonzini case KPDK:
1963bd88451SPaolo Bonzini return s->kpdk;
1973bd88451SPaolo Bonzini case KPREC:
1983bd88451SPaolo Bonzini tmp = s->kprec;
1993bd88451SPaolo Bonzini if(tmp & KPREC_OF1)
2003bd88451SPaolo Bonzini s->kprec &= ~(KPREC_OF1);
2013bd88451SPaolo Bonzini if(tmp & KPREC_UF1)
2023bd88451SPaolo Bonzini s->kprec &= ~(KPREC_UF1);
2033bd88451SPaolo Bonzini if(tmp & KPREC_OF0)
2043bd88451SPaolo Bonzini s->kprec &= ~(KPREC_OF0);
2053bd88451SPaolo Bonzini if(tmp & KPREC_UF0)
2063bd88451SPaolo Bonzini s->kprec &= ~(KPREC_UF0);
2073bd88451SPaolo Bonzini return tmp;
2083bd88451SPaolo Bonzini case KPMK:
2093bd88451SPaolo Bonzini tmp = s->kpmk;
2103bd88451SPaolo Bonzini if(tmp & KPMK_MKP)
2113bd88451SPaolo Bonzini s->kpmk &= ~(KPMK_MKP);
2123bd88451SPaolo Bonzini return tmp;
2133bd88451SPaolo Bonzini case KPAS:
2143bd88451SPaolo Bonzini return s->kpas;
2153bd88451SPaolo Bonzini case KPASMKP0:
2163bd88451SPaolo Bonzini return s->kpasmkp[0];
2173bd88451SPaolo Bonzini case KPASMKP1:
2183bd88451SPaolo Bonzini return s->kpasmkp[1];
2193bd88451SPaolo Bonzini case KPASMKP2:
2203bd88451SPaolo Bonzini return s->kpasmkp[2];
2213bd88451SPaolo Bonzini case KPASMKP3:
2223bd88451SPaolo Bonzini return s->kpasmkp[3];
2233bd88451SPaolo Bonzini case KPKDI:
2243bd88451SPaolo Bonzini return s->kpkdi;
2253bd88451SPaolo Bonzini default:
2263a37f239SPhilippe Mathieu-Daudé qemu_log_mask(LOG_GUEST_ERROR,
2273a37f239SPhilippe Mathieu-Daudé "%s: Bad read offset 0x%"HWADDR_PRIx"\n",
2283a37f239SPhilippe Mathieu-Daudé __func__, offset);
2293bd88451SPaolo Bonzini }
2303bd88451SPaolo Bonzini
2313bd88451SPaolo Bonzini return 0;
2323bd88451SPaolo Bonzini }
2333bd88451SPaolo Bonzini
pxa2xx_keypad_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)2343bd88451SPaolo Bonzini static void pxa2xx_keypad_write(void *opaque, hwaddr offset,
2353bd88451SPaolo Bonzini uint64_t value, unsigned size)
2363bd88451SPaolo Bonzini {
2373bd88451SPaolo Bonzini PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque;
2383bd88451SPaolo Bonzini
2393bd88451SPaolo Bonzini switch (offset) {
2403bd88451SPaolo Bonzini case KPC:
2413bd88451SPaolo Bonzini s->kpc = value;
2423bd88451SPaolo Bonzini if (s->kpc & KPC_AS) {
2433bd88451SPaolo Bonzini s->kpc &= ~(KPC_AS);
2443bd88451SPaolo Bonzini }
2453bd88451SPaolo Bonzini break;
2463bd88451SPaolo Bonzini case KPDK:
2473bd88451SPaolo Bonzini s->kpdk = value;
2483bd88451SPaolo Bonzini break;
2493bd88451SPaolo Bonzini case KPREC:
2503bd88451SPaolo Bonzini s->kprec = value;
2513bd88451SPaolo Bonzini break;
2523bd88451SPaolo Bonzini case KPMK:
2533bd88451SPaolo Bonzini s->kpmk = value;
2543bd88451SPaolo Bonzini break;
2553bd88451SPaolo Bonzini case KPAS:
2563bd88451SPaolo Bonzini s->kpas = value;
2573bd88451SPaolo Bonzini break;
2583bd88451SPaolo Bonzini case KPASMKP0:
2593bd88451SPaolo Bonzini s->kpasmkp[0] = value;
2603bd88451SPaolo Bonzini break;
2613bd88451SPaolo Bonzini case KPASMKP1:
2623bd88451SPaolo Bonzini s->kpasmkp[1] = value;
2633bd88451SPaolo Bonzini break;
2643bd88451SPaolo Bonzini case KPASMKP2:
2653bd88451SPaolo Bonzini s->kpasmkp[2] = value;
2663bd88451SPaolo Bonzini break;
2673bd88451SPaolo Bonzini case KPASMKP3:
2683bd88451SPaolo Bonzini s->kpasmkp[3] = value;
2693bd88451SPaolo Bonzini break;
2703bd88451SPaolo Bonzini case KPKDI:
2713bd88451SPaolo Bonzini s->kpkdi = value;
2723bd88451SPaolo Bonzini break;
2733bd88451SPaolo Bonzini
2743bd88451SPaolo Bonzini default:
2753a37f239SPhilippe Mathieu-Daudé qemu_log_mask(LOG_GUEST_ERROR,
2763a37f239SPhilippe Mathieu-Daudé "%s: Bad write offset 0x%"HWADDR_PRIx"\n",
2773a37f239SPhilippe Mathieu-Daudé __func__, offset);
2783bd88451SPaolo Bonzini }
2793bd88451SPaolo Bonzini }
2803bd88451SPaolo Bonzini
2813bd88451SPaolo Bonzini static const MemoryRegionOps pxa2xx_keypad_ops = {
2823bd88451SPaolo Bonzini .read = pxa2xx_keypad_read,
2833bd88451SPaolo Bonzini .write = pxa2xx_keypad_write,
2843bd88451SPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN,
2853bd88451SPaolo Bonzini };
2863bd88451SPaolo Bonzini
2873bd88451SPaolo Bonzini static const VMStateDescription vmstate_pxa2xx_keypad = {
2883bd88451SPaolo Bonzini .name = "pxa2xx_keypad",
2893bd88451SPaolo Bonzini .version_id = 0,
2903bd88451SPaolo Bonzini .minimum_version_id = 0,
291*af0f07dfSRichard Henderson .fields = (const VMStateField[]) {
2923bd88451SPaolo Bonzini VMSTATE_UINT32(kpc, PXA2xxKeyPadState),
2933bd88451SPaolo Bonzini VMSTATE_UINT32(kpdk, PXA2xxKeyPadState),
2943bd88451SPaolo Bonzini VMSTATE_UINT32(kprec, PXA2xxKeyPadState),
2953bd88451SPaolo Bonzini VMSTATE_UINT32(kpmk, PXA2xxKeyPadState),
2963bd88451SPaolo Bonzini VMSTATE_UINT32(kpas, PXA2xxKeyPadState),
2973bd88451SPaolo Bonzini VMSTATE_UINT32_ARRAY(kpasmkp, PXA2xxKeyPadState, 4),
2983bd88451SPaolo Bonzini VMSTATE_UINT32(kpkdi, PXA2xxKeyPadState),
2993bd88451SPaolo Bonzini VMSTATE_END_OF_LIST()
3003bd88451SPaolo Bonzini }
3013bd88451SPaolo Bonzini };
3023bd88451SPaolo Bonzini
pxa27x_keypad_init(MemoryRegion * sysmem,hwaddr base,qemu_irq irq)3033bd88451SPaolo Bonzini PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem,
3043bd88451SPaolo Bonzini hwaddr base,
3053bd88451SPaolo Bonzini qemu_irq irq)
3063bd88451SPaolo Bonzini {
3073bd88451SPaolo Bonzini PXA2xxKeyPadState *s;
3083bd88451SPaolo Bonzini
309b21e2380SMarkus Armbruster s = g_new0(PXA2xxKeyPadState, 1);
3103bd88451SPaolo Bonzini s->irq = irq;
3113bd88451SPaolo Bonzini
3122c9b15caSPaolo Bonzini memory_region_init_io(&s->iomem, NULL, &pxa2xx_keypad_ops, s,
3133bd88451SPaolo Bonzini "pxa2xx-keypad", 0x00100000);
3143bd88451SPaolo Bonzini memory_region_add_subregion(sysmem, base, &s->iomem);
3153bd88451SPaolo Bonzini
3163bd88451SPaolo Bonzini vmstate_register(NULL, 0, &vmstate_pxa2xx_keypad, s);
3173bd88451SPaolo Bonzini
3183bd88451SPaolo Bonzini return s;
3193bd88451SPaolo Bonzini }
3203bd88451SPaolo Bonzini
pxa27x_register_keypad(PXA2xxKeyPadState * kp,const struct keymap * map,int size)32152975c31SStefan Weil void pxa27x_register_keypad(PXA2xxKeyPadState *kp,
32252975c31SStefan Weil const struct keymap *map, int size)
3233bd88451SPaolo Bonzini {
3243bd88451SPaolo Bonzini if(!map || size < 0x80) {
325a89f364aSAlistair Francis fprintf(stderr, "%s - No PXA keypad map defined\n", __func__);
3263bd88451SPaolo Bonzini exit(-1);
3273bd88451SPaolo Bonzini }
3283bd88451SPaolo Bonzini
3293bd88451SPaolo Bonzini kp->map = map;
3303bd88451SPaolo Bonzini qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp);
3313bd88451SPaolo Bonzini }
332