pandora: Add basic twl4030 keyboard support
[pandora-u-boot.git] / board / pandora / kbd.c
diff --git a/board/pandora/kbd.c b/board/pandora/kbd.c
new file mode 100644 (file)
index 0000000..6cc69df
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Based on board/rbc823/kbd.c, which is:
+ *  (C) Copyright 2000
+ *  Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * Also based on mainline board/nokia/rx51/rx51.c, which is:
+ *  (C) Copyright 2012
+ *  Ивайло Димитров <freemangordon@abv.bg>
+ *  (C) Copyright 2011-2012
+ *  Pali Rohár <pali.rohar@gmail.com>
+ *  (C) Copyright 2010
+ *  Alistair Buxton <a.j.buxton@gmail.com>
+ *  Derived from Beagle Board and 3430 SDP code:
+ *  (C) Copyright 2004-2008
+ *  Texas Instruments, <www.ti.com>
+ *
+ * (C) Copyright 2013
+ * Urja Rannikko <urjaman@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <stdio_dev.h>
+#include <lcd.h>
+#include <twl4030.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifdef CONFIG_KEYBOARD
+
+/*
+ * TWL4030 keypad handler
+ */
+
+static unsigned long int twl_i2c_lock;
+
+static const char keymap[] = {
+       /* normal */
+       '9',  '8',  'i',  'j',  'n',   'm',     0,  0,
+       '0',  '7',  'u',  'h',  'b',   ' ',     0,  0,
+        8,   '6',  'y',  'g',  'v',  /*Fn*/0,  0,  0,
+       'o',  '5',  't',  'f',  'c',    0,    0,    0,
+       'p',  '4',  'r',  'd',  'x',    0,    0,    0,
+       'k',  '3',  'e',  's',  'z',    0,    0,    0,
+       'l',  '2',  'w',  'a',  '.',    0,    0,    0,
+       '\r', '1',  'q',/*Sh*/0,',',    0,    0,    0,
+       /* fn */
+        0,    0,    0,    0,  '$',    0,    0,    0,
+        0,    0,    0,    0,  '|',  '\t',   0,    0,
+        0,    0,   '_',  '=', '#',    0,    0,    0,
+        0,    0,   '!',  '+', '\\',   0,    0,    0,
+        0,    0,   ')',  '-', '?',    0,    0,    0,
+        0,    0,   '(',  '"', '/',    0,    0,    0,
+        0,    0,   '@',  '\'', ':',   0,    0,    0,
+        0,    0,    0,   0,    ';',   0,    0,    0,
+       /* shift */
+       '[',  '*',  'I',  'J',  'N',   'M',     0,  0,
+       ']',  '&',  'U',  'H',  'B',   ' ',     0,  0,
+       127,  '^',  'Y',  'G',  'V',    0,      0,  0,
+       'O',  '%',  'T',  'F',  'C',    0,    0,    0,
+       'P',  '~',  'R',  'D',  'X',    0,    0,    0,
+       'K',  '}',  'E',  'S',  'Z',    0,    0,    0,
+       'L',  '{',  'W',  'A',  '>',    0,    0,    0,
+       '\r',  0,   'Q',   0,   '<',    0,    0,    0,
+};
+
+static u8 keys[8];
+static u8 old_keys[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+#define KEYBUF_SIZE 32
+static u8 keybuf[KEYBUF_SIZE];
+static u8 keybuf_head;
+static u8 keybuf_tail;
+
+static void twl4030_kp_fill(u8 k, u8 mods)
+{
+       if (mods & 2) { /* fn meta key was pressed */
+               k = keymap[k+64];
+       } else if (mods & 4) { /* shift key was pressed */
+               k = keymap[k+128];
+       } else {
+               k = keymap[k];
+       }
+       if (k) {
+               keybuf[keybuf_tail++] = k;
+               keybuf_tail %= KEYBUF_SIZE;
+       }
+}
+
+int twl4030_kbd_init (void)
+{
+       int ret = 0;
+       u8 ctrl;
+       ret = twl4030_i2c_read_u8(TWL4030_CHIP_KEYPAD,
+                                 &ctrl, TWL4030_KEYPAD_KEYP_CTRL_REG);
+
+       if (ret)
+               return ret;
+
+       /* turn on keyboard and use hardware scanning */
+       ctrl |= TWL4030_KEYPAD_CTRL_KBD_ON;
+       ctrl |= TWL4030_KEYPAD_CTRL_SOFT_NRST;
+       ctrl |= TWL4030_KEYPAD_CTRL_SOFTMODEN;
+       ret |= twl4030_i2c_write_u8(TWL4030_CHIP_KEYPAD, ctrl,
+                                   TWL4030_KEYPAD_KEYP_CTRL_REG);
+       /* enable key event status */
+       ret |= twl4030_i2c_write_u8(TWL4030_CHIP_KEYPAD, 0xfe,
+                                   TWL4030_KEYPAD_KEYP_IMR1);
+       /* enable interrupt generation on rising and falling */
+       /* this is a workaround for qemu twl4030 emulation */
+       ret |= twl4030_i2c_write_u8(TWL4030_CHIP_KEYPAD, 0x57,
+                                   TWL4030_KEYPAD_KEYP_EDR);
+       /* enable ISR clear on read */
+       ret |= twl4030_i2c_write_u8(TWL4030_CHIP_KEYPAD, 0x05,
+                                   TWL4030_KEYPAD_KEYP_SIH_CTRL);
+
+       twl_i2c_lock = 0;
+       return 0;
+}
+
+int twl4030_kbd_tstc(void)
+{
+       u8 c, r, dk, i;
+       u8 intr;
+       u8 mods;
+
+       /* localy lock twl4030 i2c bus */
+       if (test_and_set_bit(0, &twl_i2c_lock))
+               return 0;
+
+       /* twl4030 remembers up to 2 events */
+       for (i = 0; i < 2; i++) {
+
+               /* check interrupt register for events */
+               twl4030_i2c_read_u8(TWL4030_CHIP_KEYPAD, &intr,
+                                   TWL4030_KEYPAD_KEYP_ISR1 + (2 * i));
+
+               /* no event */
+               if (!(intr&1))
+                       continue;
+
+               /* read the key state */
+               i2c_read(TWL4030_CHIP_KEYPAD,
+                       TWL4030_KEYPAD_FULL_CODE_7_0, 1, keys, 8);
+
+               /* take modifier keys from the keystate */
+               mods = keys[2]&0x20?2:0;     /* Fn */
+               if (keys[7]&0x08) mods |= 4; /* Shift */
+
+               for (c = 0; c < 8; c++) {
+
+                       /* get newly pressed keys only */
+                       dk = ((keys[c] ^ old_keys[c])&keys[c]);
+                       old_keys[c] = keys[c];
+
+                       /* fill the keybuf */
+                       for (r = 0; r < 8; r++) {
+                               if (dk&1)
+                                       twl4030_kp_fill((c*8)+r, mods);
+                               dk = dk >> 1;
+                       }
+
+               }
+
+       }
+
+       /* localy unlock twl4030 i2c bus */
+       test_and_clear_bit(0, &twl_i2c_lock);
+
+       return (KEYBUF_SIZE + keybuf_tail - keybuf_head)%KEYBUF_SIZE;
+}
+
+int twl4030_kbd_getc(void)
+{
+       keybuf_head %= KEYBUF_SIZE;
+       while (!twl4030_kbd_tstc())
+               /* Just pass the time and hope no watchdogs eat us.. */;
+       return keybuf[keybuf_head++];
+}
+
+/* search for keyboard and register it if found */
+int drv_keyboard_init(void)
+{
+       int error = 0;
+       struct stdio_dev kbd_dev;
+
+       if ((error=twl4030_kbd_init())) return error;
+
+       /* register the keyboard */
+       memset (&kbd_dev, 0, sizeof(struct stdio_dev));
+       strcpy(kbd_dev.name, "kbd");
+       kbd_dev.flags =  DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
+       kbd_dev.putc = NULL;
+       kbd_dev.puts = NULL;
+       kbd_dev.getc = twl4030_kbd_getc;
+       kbd_dev.tstc = twl4030_kbd_tstc;
+       error = stdio_register (&kbd_dev);
+       return error;
+}
+
+#endif /* CONFIG_KEYBOARD */