uboot/board/liebherr/lwmon5/kbd.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2007
   3 * Stefan Roese, DENX Software Engineering, sr@denx.de.
   4 *
   5 * (C) Copyright 2001, 2002
   6 * DENX Software Engineering
   7 * Wolfgang Denk, wd@denx.de
   8 *
   9 * SPDX-License-Identifier:     GPL-2.0+
  10 */
  11
  12/* define DEBUG for debugging output (obviously ;-)) */
  13#if 0
  14#define DEBUG
  15#endif
  16
  17#include <common.h>
  18#include <i2c.h>
  19#include <command.h>
  20#include <console.h>
  21#include <post.h>
  22#include <serial.h>
  23#include <malloc.h>
  24
  25#include <linux/types.h>
  26#include <linux/string.h>       /* for strdup */
  27
  28DECLARE_GLOBAL_DATA_PTR;
  29
  30static void kbd_init (void);
  31static int compare_magic (uchar *kbd_data, uchar *str);
  32
  33/*--------------------- Local macros and constants --------------------*/
  34#define _NOT_USED_      0xFFFFFFFF
  35
  36/*------------------------- dspic io expander -----------------------*/
  37#define DSPIC_PON_STATUS_REG    0x80A
  38#define DSPIC_PON_INV_STATUS_REG 0x80C
  39#define DSPIC_PON_KEY_REG       0x810
  40/*------------------------- Keyboard controller -----------------------*/
  41/* command codes */
  42#define KEYBD_CMD_READ_KEYS     0x01
  43#define KEYBD_CMD_READ_VERSION  0x02
  44#define KEYBD_CMD_READ_STATUS   0x03
  45#define KEYBD_CMD_RESET_ERRORS  0x10
  46
  47/* status codes */
  48#define KEYBD_STATUS_MASK       0x3F
  49#define KEYBD_STATUS_H_RESET    0x20
  50#define KEYBD_STATUS_BROWNOUT   0x10
  51#define KEYBD_STATUS_WD_RESET   0x08
  52#define KEYBD_STATUS_OVERLOAD   0x04
  53#define KEYBD_STATUS_ILLEGAL_WR 0x02
  54#define KEYBD_STATUS_ILLEGAL_RD 0x01
  55
  56/* Number of bytes returned from Keyboard Controller */
  57#define KEYBD_VERSIONLEN        2       /* version information */
  58
  59/*
  60 * This is different from the "old" lwmon dsPIC kbd controller
  61 * implementation. Now the controller still answers with 9 bytes,
  62 * but the last 3 bytes are always "0x06 0x07 0x08". So we just
  63 * set the length to compare to 6 instead of 9.
  64 */
  65#define KEYBD_DATALEN           6       /* normal key scan data */
  66
  67/* maximum number of "magic" key codes that can be assigned */
  68
  69static uchar kbd_addr = CONFIG_SYS_I2C_KEYBD_ADDR;
  70static uchar dspic_addr = CONFIG_SYS_I2C_DSPIC_IO_ADDR;
  71
  72static uchar *key_match (uchar *);
  73
  74#define KEYBD_SET_DEBUGMODE     '#'     /* Magic key to enable debug output */
  75
  76/***********************************************************************
  77F* Function:     int board_postclk_init (void) P*A*Z*
  78 *
  79P* Parameters:   none
  80P*
  81P* Returnvalue:  int
  82P*                - 0 is always returned.
  83 *
  84Z* Intention:    This function is the board_postclk_init() method implementation
  85Z*               for the lwmon board.
  86 *
  87 ***********************************************************************/
  88int board_postclk_init (void)
  89{
  90        kbd_init();
  91
  92        return (0);
  93}
  94
  95static void kbd_init (void)
  96{
  97        uchar kbd_data[KEYBD_DATALEN];
  98        uchar tmp_data[KEYBD_DATALEN];
  99        uchar val, errcd;
 100        int i;
 101
 102        i2c_set_bus_num(0);
 103
 104        gd->arch.kbd_status = 0;
 105
 106        /* Forced by PIC. Delays <= 175us loose */
 107        udelay(1000);
 108
 109        /* Read initial keyboard error code */
 110        val = KEYBD_CMD_READ_STATUS;
 111        i2c_write (kbd_addr, 0, 0, &val, 1);
 112        i2c_read (kbd_addr, 0, 0, &errcd, 1);
 113        /* clear unused bits */
 114        errcd &= KEYBD_STATUS_MASK;
 115        /* clear "irrelevant" bits. Recommended by Martin Rajek, LWN */
 116        errcd &= ~(KEYBD_STATUS_H_RESET|KEYBD_STATUS_BROWNOUT);
 117        if (errcd) {
 118                gd->arch.kbd_status |= errcd << 8;
 119        }
 120        /* Reset error code and verify */
 121        val = KEYBD_CMD_RESET_ERRORS;
 122        i2c_write (kbd_addr, 0, 0, &val, 1);
 123        udelay(1000);   /* delay NEEDED by keyboard PIC !!! */
 124
 125        val = KEYBD_CMD_READ_STATUS;
 126        i2c_write (kbd_addr, 0, 0, &val, 1);
 127        i2c_read (kbd_addr, 0, 0, &val, 1);
 128
 129        val &= KEYBD_STATUS_MASK;       /* clear unused bits */
 130        if (val) {                      /* permanent error, report it */
 131                gd->arch.kbd_status |= val;
 132                return;
 133        }
 134
 135        /*
 136         * Read current keyboard state.
 137         *
 138         * After the error reset it may take some time before the
 139         * keyboard PIC picks up a valid keyboard scan - the total
 140         * scan time is approx. 1.6 ms (information by Martin Rajek,
 141         * 28 Sep 2002). We read a couple of times for the keyboard
 142         * to stabilize, using a big enough delay.
 143         * 10 times should be enough. If the data is still changing,
 144         * we use what we get :-(
 145         */
 146
 147        memset (tmp_data, 0xFF, KEYBD_DATALEN); /* impossible value */
 148        for (i=0; i<10; ++i) {
 149                val = KEYBD_CMD_READ_KEYS;
 150                i2c_write (kbd_addr, 0, 0, &val, 1);
 151                i2c_read (kbd_addr, 0, 0, kbd_data, KEYBD_DATALEN);
 152
 153                if (memcmp(kbd_data, tmp_data, KEYBD_DATALEN) == 0) {
 154                        /* consistent state, done */
 155                        break;
 156                }
 157                /* remeber last state, delay, and retry */
 158                memcpy (tmp_data, kbd_data, KEYBD_DATALEN);
 159                udelay (5000);
 160        }
 161}
 162
 163
 164/* Read a register from the dsPIC. */
 165int _dspic_read(ushort reg, ushort *data)
 166{
 167        uchar buf[sizeof(*data)];
 168        int rval;
 169
 170        if (i2c_read(dspic_addr, reg, 2, buf, 2))
 171                return -1;
 172
 173        rval = i2c_read(dspic_addr, reg, sizeof(reg), buf, sizeof(*data));
 174        *data = (buf[0] << 8) | buf[1];
 175
 176        return rval;
 177}
 178
 179
 180/***********************************************************************
 181F* Function:     int misc_init_r (void) P*A*Z*
 182 *
 183P* Parameters:   none
 184P*
 185P* Returnvalue:  int
 186P*                - 0 is always returned, even in the case of a keyboard
 187P*                    error.
 188 *
 189Z* Intention:    This function is the misc_init_r() method implementation
 190Z*               for the lwmon board.
 191Z*               The keyboard controller is initialized and the result
 192Z*               of a read copied to the environment variable "keybd".
 193Z*               If KEYBD_SET_DEBUGMODE is defined, a check is made for
 194Z*               this key, and if found display to the LCD will be enabled.
 195Z*               The keys in "keybd" are checked against the magic
 196Z*               keycommands defined in the environment.
 197Z*               See also key_match().
 198 *
 199D* Design:       wd@denx.de
 200C* Coding:       wd@denx.de
 201V* Verification: dzu@denx.de
 202 ***********************************************************************/
 203int misc_init_r_kbd (void)
 204{
 205        uchar kbd_data[KEYBD_DATALEN];
 206        char keybd_env[2 * KEYBD_DATALEN + 1];
 207        uchar kbd_init_status = gd->arch.kbd_status >> 8;
 208        uchar kbd_status = gd->arch.kbd_status;
 209        uchar val;
 210        ushort data, inv_data;
 211        char *str;
 212        int i;
 213
 214        if (kbd_init_status) {
 215                printf ("KEYBD: Error %02X\n", kbd_init_status);
 216        }
 217        if (kbd_status) {               /* permanent error, report it */
 218                printf ("*** Keyboard error code %02X ***\n", kbd_status);
 219                sprintf (keybd_env, "%02X", kbd_status);
 220                setenv ("keybd", keybd_env);
 221                return 0;
 222        }
 223
 224        /*
 225         * Now we know that we have a working  keyboard,  so  disable
 226         * all output to the LCD except when a key press is detected.
 227         */
 228
 229        if ((console_assign (stdout, "serial") < 0) ||
 230                (console_assign (stderr, "serial") < 0)) {
 231                printf ("Can't assign serial port as output device\n");
 232        }
 233
 234        /* Read Version */
 235        val = KEYBD_CMD_READ_VERSION;
 236        i2c_write (kbd_addr, 0, 0, &val, 1);
 237        i2c_read (kbd_addr, 0, 0, kbd_data, KEYBD_VERSIONLEN);
 238        printf ("KEYBD: Version %d.%d\n", kbd_data[0], kbd_data[1]);
 239
 240        /* Read current keyboard state */
 241        val = KEYBD_CMD_READ_KEYS;
 242        i2c_write (kbd_addr, 0, 0, &val, 1);
 243        i2c_read (kbd_addr, 0, 0, kbd_data, KEYBD_DATALEN);
 244
 245        /* read out start key from bse01 received via can */
 246        _dspic_read(DSPIC_PON_STATUS_REG, &data);
 247        /* check highbyte from status register */
 248        if (data > 0xFF) {
 249                _dspic_read(DSPIC_PON_INV_STATUS_REG, &inv_data);
 250
 251                /* check inverse data */
 252                if ((data+inv_data) == 0xFFFF) {
 253                        /* don't overwrite local key */
 254                        if (kbd_data[1] == 0) {
 255                                /* read key value */
 256                                _dspic_read(DSPIC_PON_KEY_REG, &data);
 257                                str = (char *)&data;
 258                                /* swap bytes */
 259                                kbd_data[1] = str[1];
 260                                kbd_data[2] = str[0];
 261                                printf("CAN received startkey: 0x%X\n", data);
 262                        }
 263                }
 264        }
 265
 266        for (i = 0; i < KEYBD_DATALEN; ++i) {
 267                sprintf (keybd_env + i + i, "%02X", kbd_data[i]);
 268        }
 269
 270        setenv ("keybd", keybd_env);
 271
 272        str = strdup ((char *)key_match (kbd_data));    /* decode keys */
 273#ifdef KEYBD_SET_DEBUGMODE
 274        if (kbd_data[0] == KEYBD_SET_DEBUGMODE) {       /* set debug mode */
 275                if ((console_assign (stdout, "lcd") < 0) ||
 276                        (console_assign (stderr, "lcd") < 0)) {
 277                        printf ("Can't assign LCD display as output device\n");
 278                }
 279        }
 280#endif /* KEYBD_SET_DEBUGMODE */
 281#ifdef CONFIG_PREBOOT   /* automatically configure "preboot" command on key match */
 282        setenv ("preboot", str);        /* set or delete definition */
 283#endif /* CONFIG_PREBOOT */
 284        if (str != NULL) {
 285                free (str);
 286        }
 287        return (0);
 288}
 289
 290#ifdef CONFIG_PREBOOT
 291
 292static uchar kbd_magic_prefix[] = "key_magic";
 293static uchar kbd_command_prefix[] = "key_cmd";
 294
 295static int compare_magic (uchar *kbd_data, uchar *str)
 296{
 297        uchar compare[KEYBD_DATALEN-1];
 298        char *nxt;
 299        int i;
 300
 301        /* Don't include modifier byte */
 302        memcpy (compare, kbd_data+1, KEYBD_DATALEN-1);
 303
 304        for (; str != NULL; str = (*nxt) ? (uchar *)(nxt+1) : (uchar *)nxt) {
 305                uchar c;
 306                int k;
 307
 308                c = (uchar) simple_strtoul ((char *)str, (char **) (&nxt), 16);
 309
 310                if (str == (uchar *)nxt) {      /* invalid character */
 311                        break;
 312                }
 313
 314                /*
 315                 * Check if this key matches the input.
 316                 * Set matches to zero, so they match only once
 317                 * and we can find duplicates or extra keys
 318                 */
 319                for (k = 0; k < sizeof(compare); ++k) {
 320                        if (compare[k] == '\0') /* only non-zero entries */
 321                                continue;
 322                        if (c == compare[k]) {  /* found matching key */
 323                                compare[k] = '\0';
 324                                break;
 325                        }
 326                }
 327                if (k == sizeof(compare)) {
 328                        return -1;              /* unmatched key */
 329                }
 330        }
 331
 332        /*
 333         * A full match leaves no keys in the `compare' array,
 334         */
 335        for (i = 0; i < sizeof(compare); ++i) {
 336                if (compare[i])
 337                {
 338                        return -1;
 339                }
 340        }
 341
 342        return 0;
 343}
 344
 345/***********************************************************************
 346F* Function:     static uchar *key_match (uchar *kbd_data) P*A*Z*
 347 *
 348P* Parameters:   uchar *kbd_data
 349P*                - The keys to match against our magic definitions
 350P*
 351P* Returnvalue:  uchar *
 352P*                - != NULL: Pointer to the corresponding command(s)
 353P*                     NULL: No magic is about to happen
 354 *
 355Z* Intention:    Check if pressed key(s) match magic sequence,
 356Z*               and return the command string associated with that key(s).
 357Z*
 358Z*               If no key press was decoded, NULL is returned.
 359Z*
 360Z*               Note: the first character of the argument will be
 361Z*                     overwritten with the "magic charcter code" of the
 362Z*                     decoded key(s), or '\0'.
 363Z*
 364Z*               Note: the string points to static environment data
 365Z*                     and must be saved before you call any function that
 366Z*                     modifies the environment.
 367 *
 368D* Design:       wd@denx.de
 369C* Coding:       wd@denx.de
 370V* Verification: dzu@denx.de
 371 ***********************************************************************/
 372static uchar *key_match (uchar *kbd_data)
 373{
 374        char magic[sizeof (kbd_magic_prefix) + 1];
 375        uchar *suffix;
 376        char *kbd_magic_keys;
 377
 378        /*
 379         * The following string defines the characters that can pe appended
 380         * to "key_magic" to form the names of environment variables that
 381         * hold "magic" key codes, i. e. such key codes that can cause
 382         * pre-boot actions. If the string is empty (""), then only
 383         * "key_magic" is checked (old behaviour); the string "125" causes
 384         * checks for "key_magic1", "key_magic2" and "key_magic5", etc.
 385         */
 386        if ((kbd_magic_keys = getenv ("magic_keys")) == NULL)
 387                kbd_magic_keys = "";
 388
 389        /* loop over all magic keys;
 390         * use '\0' suffix in case of empty string
 391         */
 392        for (suffix=(uchar *)kbd_magic_keys; *suffix || suffix==(uchar *)kbd_magic_keys; ++suffix) {
 393                sprintf (magic, "%s%c", kbd_magic_prefix, *suffix);
 394                debug ("### Check magic \"%s\"\n", magic);
 395                if (compare_magic(kbd_data, (uchar *)getenv(magic)) == 0) {
 396                        char cmd_name[sizeof (kbd_command_prefix) + 1];
 397                        char *cmd;
 398
 399                        sprintf (cmd_name, "%s%c", kbd_command_prefix, *suffix);
 400
 401                        cmd = getenv (cmd_name);
 402                        debug ("### Set PREBOOT to $(%s): \"%s\"\n",
 403                                        cmd_name, cmd ? cmd : "<<NULL>>");
 404                        *kbd_data = *suffix;
 405                        return ((uchar *)cmd);
 406                }
 407        }
 408        debug ("### Delete PREBOOT\n");
 409        *kbd_data = '\0';
 410        return (NULL);
 411}
 412#endif /* CONFIG_PREBOOT */
 413
 414/***********************************************************************
 415F* Function:     int do_kbd (cmd_tbl_t *cmdtp, int flag,
 416F*                           int argc, char * const argv[]) P*A*Z*
 417 *
 418P* Parameters:   cmd_tbl_t *cmdtp
 419P*                - Pointer to our command table entry
 420P*               int flag
 421P*                - If the CMD_FLAG_REPEAT bit is set, then this call is
 422P*                  a repetition
 423P*               int argc
 424P*                - Argument count
 425P*               char * const argv[]
 426P*                - Array of the actual arguments
 427P*
 428P* Returnvalue:  int
 429P*                - 0 is always returned.
 430 *
 431Z* Intention:    Implement the "kbd" command.
 432Z*               The keyboard status is read.  The result is printed on
 433Z*               the console and written into the "keybd" environment
 434Z*               variable.
 435 *
 436D* Design:       wd@denx.de
 437C* Coding:       wd@denx.de
 438V* Verification: dzu@denx.de
 439 ***********************************************************************/
 440int do_kbd (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 441{
 442        uchar kbd_data[KEYBD_DATALEN];
 443        char keybd_env[2 * KEYBD_DATALEN + 1];
 444        uchar val;
 445        int i;
 446
 447#if 0 /* Done in kbd_init */
 448        i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
 449#endif
 450
 451        /* Read keys */
 452        val = KEYBD_CMD_READ_KEYS;
 453        i2c_write (kbd_addr, 0, 0, &val, 1);
 454        i2c_read (kbd_addr, 0, 0, kbd_data, KEYBD_DATALEN);
 455
 456        puts ("Keys:");
 457        for (i = 0; i < KEYBD_DATALEN; ++i) {
 458                sprintf (keybd_env + i + i, "%02X", kbd_data[i]);
 459                printf (" %02x", kbd_data[i]);
 460        }
 461        putc ('\n');
 462        setenv ("keybd", keybd_env);
 463        return 0;
 464}
 465
 466U_BOOT_CMD(
 467        kbd,    1,      1,      do_kbd,
 468        "read keyboard status",
 469        ""
 470);
 471
 472/*----------------------------- Utilities -----------------------------*/
 473
 474#ifdef CONFIG_POST
 475/*
 476 * Returns 1 if keys pressed to start the power-on long-running tests
 477 * Called from board_init_f().
 478 */
 479int post_hotkeys_pressed(void)
 480{
 481        uchar kbd_data[KEYBD_DATALEN];
 482        uchar val;
 483
 484        /* Read keys */
 485        val = KEYBD_CMD_READ_KEYS;
 486        i2c_write (kbd_addr, 0, 0, &val, 1);
 487        i2c_read (kbd_addr, 0, 0, kbd_data, KEYBD_DATALEN);
 488
 489        return (compare_magic(kbd_data, (uchar *)CONFIG_POST_KEY_MAGIC) == 0);
 490}
 491#endif
 492