From 31b87f2eeb928657d8f5f9202d5f62ea55d7dd9c Mon Sep 17 00:00:00 2001 From: balrog Date: Sun, 16 Dec 2007 12:13:51 +0000 Subject: [PATCH] Initial PXA27x keypad support, by Armin Kuster. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3818 c046a42c-6fe2-441c-8c8c-71466251a162 --- Makefile.target | 2 +- hw/pxa.h | 13 ++ hw/pxa2xx.c | 2 + hw/pxa2xx_keypad.c | 344 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 360 insertions(+), 1 deletion(-) create mode 100644 hw/pxa2xx_keypad.c diff --git a/Makefile.target b/Makefile.target index 53607e4010..5cedc98957 100644 --- a/Makefile.target +++ b/Makefile.target @@ -498,7 +498,7 @@ VL_OBJS+= armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o VL_OBJS+= pl061.o VL_OBJS+= arm-semi.o VL_OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o -VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o +VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o VL_OBJS+= pflash_cfi01.o gumstix.o VL_OBJS+= spitz.o ide.o serial.o nand.o ecc.o VL_OBJS+= omap.o omap_lcdc.o omap1_clk.o omap_mmc.o omap_i2c.o diff --git a/hw/pxa.h b/hw/pxa.h index 101cfe0688..16a68d936c 100644 --- a/hw/pxa.h +++ b/hw/pxa.h @@ -13,6 +13,7 @@ # define PXA2XX_PIC_SSP3 0 # define PXA2XX_PIC_USBH2 2 # define PXA2XX_PIC_USBH1 3 +# define PXA2XX_PIC_KEYPAD 4 # define PXA2XX_PIC_PWRI2C 6 # define PXA25X_PIC_HWUART 7 # define PXA27X_PIC_OST_4_11 7 @@ -106,6 +107,17 @@ int pxa2xx_pcmcia_attach(void *opaque, struct pcmcia_card_s *card); int pxa2xx_pcmcia_dettach(void *opaque); void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq); +/* pxa2xx_keypad.c */ +struct keymap { + int column; + int row; +}; +struct pxa2xx_keypad_s; +struct pxa2xx_keypad_s *pxa27x_keypad_init(target_phys_addr_t base, + qemu_irq irq); +void pxa27x_register_keypad(struct pxa2xx_keypad_s *kp, struct keymap *map, + int size); + /* pxa2xx.c */ struct pxa2xx_ssp_s; void pxa2xx_ssp_attach(struct pxa2xx_ssp_s *port, @@ -133,6 +145,7 @@ struct pxa2xx_state_s { struct pxa2xx_pcmcia_s *pcmcia[2]; struct pxa2xx_i2s_s *i2s; struct pxa2xx_fir_s *fir; + struct pxa2xx_keypad_s *kp; /* Power management */ target_phys_addr_t pm_base; diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c index a67ffefd0d..14e3b9573e 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -2153,6 +2153,8 @@ struct pxa2xx_state_s *pxa270_init(unsigned int sdram_size, s->i2s = pxa2xx_i2s_init(0x40400000, s->pic[PXA2XX_PIC_I2S], s->dma); + s->kp = pxa27x_keypad_init(0x41500000, s->pic[PXA2XX_PIC_KEYPAD]); + /* GPIO1 resets the processor */ /* The handler can be overridden by board-specific code */ pxa2xx_gpio_out_set(s->gpio, 1, s->reset); diff --git a/hw/pxa2xx_keypad.c b/hw/pxa2xx_keypad.c new file mode 100644 index 0000000000..3bd988a21b --- /dev/null +++ b/hw/pxa2xx_keypad.c @@ -0,0 +1,344 @@ +/* + * Intel PXA27X Keypad Controller emulation. + * + * Copyright (c) 2007 MontaVista Software, Inc + * Written by Armin Kuster + * or + * + * 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 + +struct pxa2xx_keypad_s{ + target_phys_addr_t base; + qemu_irq irq; + struct keymap *map; + + uint32_t kpc; + uint32_t kpdk; + uint32_t kprec; + uint32_t kpmk; + uint32_t kpas; + uint32_t kpasmkp0; + uint32_t kpasmkp1; + uint32_t kpasmkp2; + uint32_t kpasmkp3; + uint32_t kpkdi; +}; + +static void pxa27x_keyboard_event (struct pxa2xx_keypad_s *kp, int keycode) +{ + int row, col,rel; + + 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 */ + row = kp->map[keycode].row; + col = kp->map[keycode].column; + if(row == -1 || col == -1) + return; + switch (col) { + case 0: + case 1: + if(rel) + kp->kpasmkp0 = ~(0xffffffff); + else + kp->kpasmkp0 |= KPASMKPx_MKC(row,col); + break; + case 2: + case 3: + if(rel) + kp->kpasmkp1 = ~(0xffffffff); + else + kp->kpasmkp1 |= KPASMKPx_MKC(row,col); + break; + case 4: + case 5: + if(rel) + kp->kpasmkp2 = ~(0xffffffff); + else + kp->kpasmkp2 |= KPASMKPx_MKC(row,col); + break; + case 6: + case 7: + if(rel) + kp->kpasmkp3 = ~(0xffffffff); + else + kp->kpasmkp3 |= KPASMKPx_MKC(row,col); + break; + } /* switch */ + goto out; + } + return; + +out: + if(kp->kpc & KPC_MIE) { + kp->kpc |= KPC_MI; + qemu_irq_raise(kp->irq); + } + return; +} + +static uint32_t pxa2xx_keypad_read(void *opaque, target_phys_addr_t offset) +{ + struct pxa2xx_keypad_s *s = (struct pxa2xx_keypad_s *) opaque; + uint32_t tmp; + offset -= s->base; + + 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: + return s->kpasmkp0; + break; + case KPASMKP1: + return s->kpasmkp1; + break; + case KPASMKP2: + return s->kpasmkp2; + break; + case KPASMKP3: + return s->kpasmkp3; + break; + case KPKDI: + return s->kpkdi; + break; + default: + cpu_abort(cpu_single_env, "%s: Bad offset " REG_FMT "\n", + __FUNCTION__, offset); + } + + return 0; +} + +static void pxa2xx_keypad_write(void *opaque, + target_phys_addr_t offset, uint32_t value) +{ + struct pxa2xx_keypad_s *s = (struct pxa2xx_keypad_s *) opaque; + offset -= s->base; + + 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: + s->kpasmkp0 = value; + break; + case KPASMKP1: + s->kpasmkp1 = value; + break; + case KPASMKP2: + s->kpasmkp2 = value; + break; + case KPASMKP3: + s->kpasmkp3 = value; + break; + case KPKDI: + s->kpkdi = value; + break; + + default: + cpu_abort(cpu_single_env, "%s: Bad offset " REG_FMT "\n", + __FUNCTION__, offset); + } +} + +static CPUReadMemoryFunc *pxa2xx_keypad_readfn[] = { + pxa2xx_keypad_read, + pxa2xx_keypad_read, + pxa2xx_keypad_read +}; + +static CPUWriteMemoryFunc *pxa2xx_keypad_writefn[] = { + pxa2xx_keypad_write, + pxa2xx_keypad_write, + pxa2xx_keypad_write +}; + +static void pxa2xx_keypad_save(QEMUFile *f, void *opaque) +{ + struct pxa2xx_keypad_s *s = (struct pxa2xx_keypad_s *) opaque; + + 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); + qemu_put_be32s(f, &s->kpasmkp0); + qemu_put_be32s(f, &s->kpasmkp1); + qemu_put_be32s(f, &s->kpasmkp2); + qemu_put_be32s(f, &s->kpasmkp3); + qemu_put_be32s(f, &s->kpkdi); + +} + +static int pxa2xx_keypad_load(QEMUFile *f, void *opaque, int version_id) +{ + struct pxa2xx_keypad_s *s = (struct pxa2xx_keypad_s *) opaque; + + 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); + qemu_get_be32s(f, &s->kpasmkp0); + qemu_get_be32s(f, &s->kpasmkp1); + qemu_get_be32s(f, &s->kpasmkp2); + qemu_get_be32s(f, &s->kpasmkp3); + qemu_get_be32s(f, &s->kpkdi); + + return 0; +} + +struct pxa2xx_keypad_s *pxa27x_keypad_init(target_phys_addr_t base, + qemu_irq irq) +{ + int iomemtype; + struct pxa2xx_keypad_s *s; + + s = (struct pxa2xx_keypad_s *) qemu_mallocz(sizeof(struct pxa2xx_keypad_s)); + s->base = base; + s->irq = irq; + + iomemtype = cpu_register_io_memory(0, pxa2xx_keypad_readfn, + pxa2xx_keypad_writefn, s); + cpu_register_physical_memory(base, 0x00100000, iomemtype);////check size + + register_savevm("pxa2xx_keypad", 0, 0, + pxa2xx_keypad_save, pxa2xx_keypad_load, s); + + return s; +} + +void pxa27x_register_keypad(struct pxa2xx_keypad_s *kp, struct keymap *map, + int size) +{ + kp->map = (struct keymap *) qemu_mallocz(sizeof(struct keymap) * size); + + if(!map || size < 80) { + 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); +}