ppc4xx: Add matrix kbd support to lwmon5 board (440EPx based)
authorStefan Roese <sr@denx.de>
Tue, 21 Aug 2007 14:27:57 +0000 (16:27 +0200)
committerStefan Roese <sr@denx.de>
Tue, 21 Aug 2007 14:27:57 +0000 (16:27 +0200)
This patch adds support for the matrix keyboard on the lwmon5 board.
Since the implementation in the dsPCI is kind of compatible with the
"old" lwmon board, most of the code is copied from the lwmon
board directory.

Signed-off-by: Stefan Roese <sr@denx.de>
board/lwmon5/Makefile
board/lwmon5/kbd.c [new file with mode: 0644]
board/lwmon5/lwmon5.c
include/asm-ppc/global_data.h
include/configs/lwmon5.h

index 06ef7f93317fe7c9d5192ee9b6f1f2cd8790e638..2a9357146c5999e3af7d04fc8f54a7e78aa2da38 100644 (file)
@@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk
 
 LIB    = $(obj)lib$(BOARD).a
 
-COBJS  = $(BOARD).o sdram.o
+COBJS  = $(BOARD).o kbd.o sdram.o
 SOBJS  = init.o
 
 SRCS   := $(SOBJS:.o=.S) $(COBJS:.o=.c)
diff --git a/board/lwmon5/kbd.c b/board/lwmon5/kbd.c
new file mode 100644 (file)
index 0000000..1e5349a
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * (C) Copyright 2007
+ * Stefan Roese, DENX Software Engineering, sr@denx.de.
+ *
+ * (C) Copyright 2001, 2002
+ * DENX Software Engineering
+ * Wolfgang Denk, wd@denx.de
+ *
+ * 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
+ */
+
+/* define DEBUG for debugging output (obviously ;-)) */
+#if 0
+#define DEBUG
+#endif
+
+#include <common.h>
+#include <i2c.h>
+#include <command.h>
+#include <post.h>
+#include <serial.h>
+#include <malloc.h>
+
+#include <linux/types.h>
+#include <linux/string.h>      /* for strdup */
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static void kbd_init (void);
+static int compare_magic (uchar *kbd_data, uchar *str);
+
+/*--------------------- Local macros and constants --------------------*/
+#define        _NOT_USED_      0xFFFFFFFF
+
+/*------------------------- Keyboard controller -----------------------*/
+/* command codes */
+#define        KEYBD_CMD_READ_KEYS     0x01
+#define KEYBD_CMD_READ_VERSION 0x02
+#define KEYBD_CMD_READ_STATUS  0x03
+#define KEYBD_CMD_RESET_ERRORS 0x10
+
+/* status codes */
+#define KEYBD_STATUS_MASK      0x3F
+#define        KEYBD_STATUS_H_RESET    0x20
+#define KEYBD_STATUS_BROWNOUT  0x10
+#define KEYBD_STATUS_WD_RESET  0x08
+#define KEYBD_STATUS_OVERLOAD  0x04
+#define KEYBD_STATUS_ILLEGAL_WR        0x02
+#define KEYBD_STATUS_ILLEGAL_RD        0x01
+
+/* Number of bytes returned from Keyboard Controller */
+#define KEYBD_VERSIONLEN       2       /* version information */
+
+/*
+ * This is different from the "old" lwmon dsPIC kbd controller
+ * implementation. Now the controller still answers with 9 bytes,
+ * but the last 3 bytes are always "0x06 0x07 0x08". So we just
+ * set the length to compare to 6 instead of 9.
+ */
+#define        KEYBD_DATALEN           6       /* normal key scan data */
+
+/* maximum number of "magic" key codes that can be assigned */
+
+static uchar kbd_addr = CFG_I2C_KEYBD_ADDR;
+
+static uchar *key_match (uchar *);
+
+#define        KEYBD_SET_DEBUGMODE     '#'     /* Magic key to enable debug output */
+
+/***********************************************************************
+F* Function:     int board_postclk_init (void) P*A*Z*
+ *
+P* Parameters:   none
+P*
+P* Returnvalue:  int
+P*                - 0 is always returned.
+ *
+Z* Intention:    This function is the board_postclk_init() method implementation
+Z*               for the lwmon board.
+ *
+ ***********************************************************************/
+int board_postclk_init (void)
+{
+       kbd_init();
+
+       return (0);
+}
+
+static void kbd_init (void)
+{
+       uchar kbd_data[KEYBD_DATALEN];
+       uchar tmp_data[KEYBD_DATALEN];
+       uchar val, errcd;
+       int i;
+
+       i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE);
+
+       gd->kbd_status = 0;
+
+       /* Forced by PIC. Delays <= 175us loose */
+       udelay(1000);
+
+       /* Read initial keyboard error code */
+       val = KEYBD_CMD_READ_STATUS;
+       i2c_write (kbd_addr, 0, 0, &val, 1);
+       i2c_read (kbd_addr, 0, 0, &errcd, 1);
+       /* clear unused bits */
+       errcd &= KEYBD_STATUS_MASK;
+       /* clear "irrelevant" bits. Recommended by Martin Rajek, LWN */
+       errcd &= ~(KEYBD_STATUS_H_RESET|KEYBD_STATUS_BROWNOUT);
+       if (errcd) {
+               gd->kbd_status |= errcd << 8;
+       }
+       /* Reset error code and verify */
+       val = KEYBD_CMD_RESET_ERRORS;
+       i2c_write (kbd_addr, 0, 0, &val, 1);
+       udelay(1000);   /* delay NEEDED by keyboard PIC !!! */
+
+       val = KEYBD_CMD_READ_STATUS;
+       i2c_write (kbd_addr, 0, 0, &val, 1);
+       i2c_read (kbd_addr, 0, 0, &val, 1);
+
+       val &= KEYBD_STATUS_MASK;       /* clear unused bits */
+       if (val) {                      /* permanent error, report it */
+               gd->kbd_status |= val;
+               return;
+       }
+
+       /*
+        * Read current keyboard state.
+        *
+        * After the error reset it may take some time before the
+        * keyboard PIC picks up a valid keyboard scan - the total
+        * scan time is approx. 1.6 ms (information by Martin Rajek,
+        * 28 Sep 2002). We read a couple of times for the keyboard
+        * to stabilize, using a big enough delay.
+        * 10 times should be enough. If the data is still changing,
+        * we use what we get :-(
+        */
+
+       memset (tmp_data, 0xFF, KEYBD_DATALEN); /* impossible value */
+       for (i=0; i<10; ++i) {
+               val = KEYBD_CMD_READ_KEYS;
+               i2c_write (kbd_addr, 0, 0, &val, 1);
+               i2c_read (kbd_addr, 0, 0, kbd_data, KEYBD_DATALEN);
+
+               if (memcmp(kbd_data, tmp_data, KEYBD_DATALEN) == 0) {
+                       /* consistent state, done */
+                       break;
+               }
+               /* remeber last state, delay, and retry */
+               memcpy (tmp_data, kbd_data, KEYBD_DATALEN);
+               udelay (5000);
+       }
+}
+
+/***********************************************************************
+F* Function:     int misc_init_r (void) P*A*Z*
+ *
+P* Parameters:   none
+P*
+P* Returnvalue:  int
+P*                - 0 is always returned, even in the case of a keyboard
+P*                    error.
+ *
+Z* Intention:    This function is the misc_init_r() method implementation
+Z*               for the lwmon board.
+Z*               The keyboard controller is initialized and the result
+Z*               of a read copied to the environment variable "keybd".
+Z*               If KEYBD_SET_DEBUGMODE is defined, a check is made for
+Z*               this key, and if found display to the LCD will be enabled.
+Z*               The keys in "keybd" are checked against the magic
+Z*               keycommands defined in the environment.
+Z*               See also key_match().
+ *
+D* Design:       wd@denx.de
+C* Coding:       wd@denx.de
+V* Verification: dzu@denx.de
+ ***********************************************************************/
+int misc_init_r_kbd (void)
+{
+       uchar kbd_data[KEYBD_DATALEN];
+       char keybd_env[2 * KEYBD_DATALEN + 1];
+       uchar kbd_init_status = gd->kbd_status >> 8;
+       uchar kbd_status = gd->kbd_status;
+       uchar val;
+       char *str;
+       int i;
+
+       if (kbd_init_status) {
+               printf ("KEYBD: Error %02X\n", kbd_init_status);
+       }
+       if (kbd_status) {               /* permanent error, report it */
+               printf ("*** Keyboard error code %02X ***\n", kbd_status);
+               sprintf (keybd_env, "%02X", kbd_status);
+               setenv ("keybd", keybd_env);
+               return 0;
+       }
+
+       /*
+        * Now we know that we have a working  keyboard,  so  disable
+        * all output to the LCD except when a key press is detected.
+        */
+
+       if ((console_assign (stdout, "serial") < 0) ||
+               (console_assign (stderr, "serial") < 0)) {
+               printf ("Can't assign serial port as output device\n");
+       }
+
+       /* Read Version */
+       val = KEYBD_CMD_READ_VERSION;
+       i2c_write (kbd_addr, 0, 0, &val, 1);
+       i2c_read (kbd_addr, 0, 0, kbd_data, KEYBD_VERSIONLEN);
+       printf ("KEYBD: Version %d.%d\n", kbd_data[0], kbd_data[1]);
+
+       /* Read current keyboard state */
+       val = KEYBD_CMD_READ_KEYS;
+       i2c_write (kbd_addr, 0, 0, &val, 1);
+       i2c_read (kbd_addr, 0, 0, kbd_data, KEYBD_DATALEN);
+
+       for (i = 0; i < KEYBD_DATALEN; ++i) {
+               sprintf (keybd_env + i + i, "%02X", kbd_data[i]);
+       }
+       setenv ("keybd", keybd_env);
+
+       str = strdup ((char *)key_match (kbd_data));    /* decode keys */
+#ifdef KEYBD_SET_DEBUGMODE
+       if (kbd_data[0] == KEYBD_SET_DEBUGMODE) {       /* set debug mode */
+               if ((console_assign (stdout, "lcd") < 0) ||
+                       (console_assign (stderr, "lcd") < 0)) {
+                       printf ("Can't assign LCD display as output device\n");
+               }
+       }
+#endif /* KEYBD_SET_DEBUGMODE */
+#ifdef CONFIG_PREBOOT  /* automatically configure "preboot" command on key match */
+       setenv ("preboot", str);        /* set or delete definition */
+#endif /* CONFIG_PREBOOT */
+       if (str != NULL) {
+               free (str);
+       }
+       return (0);
+}
+
+#ifdef CONFIG_PREBOOT
+
+static uchar kbd_magic_prefix[] = "key_magic";
+static uchar kbd_command_prefix[] = "key_cmd";
+
+static int compare_magic (uchar *kbd_data, uchar *str)
+{
+       uchar compare[KEYBD_DATALEN-1];
+       char *nxt;
+       int i;
+
+       /* Don't include modifier byte */
+       memcpy (compare, kbd_data+1, KEYBD_DATALEN-1);
+
+       for (; str != NULL; str = (*nxt) ? (uchar *)(nxt+1) : (uchar *)nxt) {
+               uchar c;
+               int k;
+
+               c = (uchar) simple_strtoul ((char *)str, (char **) (&nxt), 16);
+
+               if (str == (uchar *)nxt) {      /* invalid character */
+                       break;
+               }
+
+               /*
+                * Check if this key matches the input.
+                * Set matches to zero, so they match only once
+                * and we can find duplicates or extra keys
+                */
+               for (k = 0; k < sizeof(compare); ++k) {
+                       if (compare[k] == '\0') /* only non-zero entries */
+                               continue;
+                       if (c == compare[k]) {  /* found matching key */
+                               compare[k] = '\0';
+                               break;
+                       }
+               }
+               if (k == sizeof(compare)) {
+                       return -1;              /* unmatched key */
+               }
+       }
+
+       /*
+        * A full match leaves no keys in the `compare' array,
+        */
+       for (i = 0; i < sizeof(compare); ++i) {
+               if (compare[i])
+               {
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+/***********************************************************************
+F* Function:     static uchar *key_match (uchar *kbd_data) P*A*Z*
+ *
+P* Parameters:   uchar *kbd_data
+P*                - The keys to match against our magic definitions
+P*
+P* Returnvalue:  uchar *
+P*                - != NULL: Pointer to the corresponding command(s)
+P*                     NULL: No magic is about to happen
+ *
+Z* Intention:    Check if pressed key(s) match magic sequence,
+Z*               and return the command string associated with that key(s).
+Z*
+Z*               If no key press was decoded, NULL is returned.
+Z*
+Z*               Note: the first character of the argument will be
+Z*                     overwritten with the "magic charcter code" of the
+Z*                     decoded key(s), or '\0'.
+Z*
+Z*               Note: the string points to static environment data
+Z*                     and must be saved before you call any function that
+Z*                     modifies the environment.
+ *
+D* Design:       wd@denx.de
+C* Coding:       wd@denx.de
+V* Verification: dzu@denx.de
+ ***********************************************************************/
+static uchar *key_match (uchar *kbd_data)
+{
+       char magic[sizeof (kbd_magic_prefix) + 1];
+       uchar *suffix;
+       char *kbd_magic_keys;
+
+       /*
+        * The following string defines the characters that can pe appended
+        * to "key_magic" to form the names of environment variables that
+        * hold "magic" key codes, i. e. such key codes that can cause
+        * pre-boot actions. If the string is empty (""), then only
+        * "key_magic" is checked (old behaviour); the string "125" causes
+        * checks for "key_magic1", "key_magic2" and "key_magic5", etc.
+        */
+       if ((kbd_magic_keys = getenv ("magic_keys")) == NULL)
+               kbd_magic_keys = "";
+
+       /* loop over all magic keys;
+        * use '\0' suffix in case of empty string
+        */
+       for (suffix=(uchar *)kbd_magic_keys; *suffix || suffix==(uchar *)kbd_magic_keys; ++suffix) {
+               sprintf (magic, "%s%c", kbd_magic_prefix, *suffix);
+               debug ("### Check magic \"%s\"\n", magic);
+               if (compare_magic(kbd_data, (uchar *)getenv(magic)) == 0) {
+                       char cmd_name[sizeof (kbd_command_prefix) + 1];
+                       char *cmd;
+
+                       sprintf (cmd_name, "%s%c", kbd_command_prefix, *suffix);
+
+                       cmd = getenv (cmd_name);
+                       debug ("### Set PREBOOT to $(%s): \"%s\"\n",
+                                       cmd_name, cmd ? cmd : "<<NULL>>");
+                       *kbd_data = *suffix;
+                       return ((uchar *)cmd);
+               }
+       }
+       debug ("### Delete PREBOOT\n");
+       *kbd_data = '\0';
+       return (NULL);
+}
+#endif /* CONFIG_PREBOOT */
+
+/***********************************************************************
+F* Function:     int do_kbd (cmd_tbl_t *cmdtp, int flag,
+F*                           int argc, char *argv[]) P*A*Z*
+ *
+P* Parameters:   cmd_tbl_t *cmdtp
+P*                - Pointer to our command table entry
+P*               int flag
+P*                - If the CMD_FLAG_REPEAT bit is set, then this call is
+P*                  a repetition
+P*               int argc
+P*                - Argument count
+P*               char *argv[]
+P*                - Array of the actual arguments
+P*
+P* Returnvalue:  int
+P*                - 0 is always returned.
+ *
+Z* Intention:    Implement the "kbd" command.
+Z*               The keyboard status is read.  The result is printed on
+Z*               the console and written into the "keybd" environment
+Z*               variable.
+ *
+D* Design:       wd@denx.de
+C* Coding:       wd@denx.de
+V* Verification: dzu@denx.de
+ ***********************************************************************/
+int do_kbd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+       uchar kbd_data[KEYBD_DATALEN];
+       char keybd_env[2 * KEYBD_DATALEN + 1];
+       uchar val;
+       int i;
+
+#if 0 /* Done in kbd_init */
+       i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE);
+#endif
+
+       /* Read keys */
+       val = KEYBD_CMD_READ_KEYS;
+       i2c_write (kbd_addr, 0, 0, &val, 1);
+       i2c_read (kbd_addr, 0, 0, kbd_data, KEYBD_DATALEN);
+
+       puts ("Keys:");
+       for (i = 0; i < KEYBD_DATALEN; ++i) {
+               sprintf (keybd_env + i + i, "%02X", kbd_data[i]);
+               printf (" %02x", kbd_data[i]);
+       }
+       putc ('\n');
+       setenv ("keybd", keybd_env);
+       return 0;
+}
+
+U_BOOT_CMD(
+       kbd,    1,      1,      do_kbd,
+       "kbd     - read keyboard status\n",
+       NULL
+);
+
+/*----------------------------- Utilities -----------------------------*/
+
+#ifdef CONFIG_POST
+/*
+ * Returns 1 if keys pressed to start the power-on long-running tests
+ * Called from board_init_f().
+ */
+int post_hotkeys_pressed(void)
+{
+       uchar kbd_data[KEYBD_DATALEN];
+       uchar val;
+
+       /* Read keys */
+       val = KEYBD_CMD_READ_KEYS;
+       i2c_write (kbd_addr, 0, 0, &val, 1);
+       i2c_read (kbd_addr, 0, 0, kbd_data, KEYBD_DATALEN);
+
+       return (compare_magic(kbd_data, (uchar *)CONFIG_POST_KEY_MAGIC) == 0);
+}
+#endif
index 830ec1911f68752ccd3c777eb8637df979e7d936..0958194ecaa4cb86c22021b029626e48469cd9dd 100644 (file)
@@ -28,7 +28,8 @@ DECLARE_GLOBAL_DATA_PTR;
 
 extern flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips   */
 
-ulong flash_get_size (ulong base, int banknum);
+ulong flash_get_size(ulong base, int banknum);
+int misc_init_r_kbd(void);
 
 int board_early_init_f(void)
 {
@@ -295,6 +296,11 @@ int misc_init_r(void)
        out_be32((void *)0xc4000024, 0x64);
        out_be32((void *)0xc4000020, 0x701);
 
+       /*
+        * Init matrix keyboard
+        */
+       misc_init_r_kbd();
+
        return 0;
 }
 
@@ -521,14 +527,3 @@ void hw_watchdog_reset(void)
        val = gpio_read_out_bit(CFG_GPIO_WATCHDOG) == 0 ? 1 : 0;
        gpio_write_bit(CFG_GPIO_WATCHDOG, val);
 }
-
-#ifdef CONFIG_POST
-/*
- * Returns 1 if keys pressed to start the power-on long-running tests
- * Called from board_init_f().
- */
-int post_hotkeys_pressed(void)
-{
-       return (ctrlc());
-}
-#endif
index cd2463643ced57c1097c2d4fdfec681bc43975dc..a35013d568f4d2a12daa275665ffb561f51f82ef 100644 (file)
@@ -129,7 +129,7 @@ typedef     struct  global_data {
        unsigned long do_mdm_init;
        unsigned long be_quiet;
 #endif
-#ifdef CONFIG_LWMON
+#if defined(CONFIG_LWMON) || defined(CONFIG_LWMON5)
        unsigned long kbd_status;
 #endif
        void            **jt;           /* jump table */
index 5ebe4404d92826b703583d9e2bcb651af29ceb82..14a200d3e44ac960e6b810d09d82a21028d20c7f 100644 (file)
@@ -34,6 +34,7 @@
 #define CONFIG_SYS_CLK_FREQ    33300000        /* external freq to pll */
 
 #define CONFIG_BOARD_EARLY_INIT_F 1    /* Call board_early_init_f      */
+#define CONFIG_BOARD_POSTCLK_INIT 1    /* Call board_postclk_init      */
 #define CONFIG_MISC_INIT_R     1       /* Call misc_init_r             */
 #define CONFIG_ADD_RAM_INFO    1       /* Print additional info        */
 
 
 #define CONFIG_RTC_PCF8563     1               /* enable Philips PCF8563 RTC   */
 #define CFG_I2C_RTC_ADDR       0x51            /* Philips PCF8563 RTC address  */
+#define CFG_I2C_KEYBD_ADDR     0x56            /* PIC LWE keyboard             */
 
-#define CONFIG_PREBOOT "echo;"                                         \
-       "echo Type \"run flash_nfs\" to mount root filesystem over NFS;" \
-       "echo"
+#define        CONFIG_POST_KEY_MAGIC   "3C+3E" /* press F3 + F5 keys to force POST */
+#if 0
+#define        CONFIG_AUTOBOOT_KEYED           /* Enable "password" protection */
+#define CONFIG_AUTOBOOT_PROMPT "\nEnter password - autoboot in %d sec...\n"
+#define CONFIG_AUTOBOOT_DELAY_STR      "  "    /* "password"   */
+#endif
+
+#define        CONFIG_PREBOOT          "setenv bootdelay 15"
 
 #undef CONFIG_BOOTARGS
 
 #define CONFIG_PHY_ADDR                3       /* PHY address, See schematics  */
 
 #define CONFIG_PHY_RESET        1      /* reset phy upon startup         */
+#define CONFIG_PHY_RESET_DELAY 300
 
 #define CONFIG_HAS_ETH0
 #define CFG_RX_ETH_BUFFER      32      /* Number of ethernet rx buffers & descriptors */