arm_coreboot: Add Chromebook keyboard driver.
This commit is contained in:
		
							parent
							
								
									b0b1b81a11
								
							
						
					
					
						commit
						5c3fd1b135
					
				
					 3 changed files with 384 additions and 0 deletions
				
			
		
							
								
								
									
										125
									
								
								grub-core/term/arm/cros.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								grub-core/term/arm/cros.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,125 @@ | |||
| /*
 | ||||
|  *  GRUB  --  GRand Unified Bootloader | ||||
|  * | ||||
|  *  Copyright (C) 2012  Google Inc. | ||||
|  *  Copyright (C) 2016  Free Software Foundation, Inc. | ||||
|  * | ||||
|  *  This is based on depthcharge code. | ||||
|  * | ||||
|  *  GRUB 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 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <grub/ps2.h> | ||||
| #include <grub/fdtbus.h> | ||||
| #include <grub/err.h> | ||||
| #include <grub/machine/kernel.h> | ||||
| #include <grub/misc.h> | ||||
| #include <grub/term.h> | ||||
| #include <grub/time.h> | ||||
| #include <grub/fdtbus.h> | ||||
| #include <grub/arm/cros_ec.h> | ||||
| 
 | ||||
| struct grub_ps2_state ps2_state; | ||||
| 
 | ||||
| struct grub_cros_ec_keyscan old_scan; | ||||
| 
 | ||||
| static const struct grub_fdtbus_dev *cros_ec; | ||||
| 
 | ||||
| static grub_uint8_t map_code[GRUB_CROS_EC_KEYSCAN_COLS][GRUB_CROS_EC_KEYSCAN_ROWS]; | ||||
| 
 | ||||
| static grub_uint8_t e0_translate[16] = | ||||
|   { | ||||
|     0x1c, 0x1d, 0x35, 0x00, | ||||
|     0x38, 0x00, 0x47, 0x48, | ||||
|     0x49, 0x4b, 0x4d, 0x4f, | ||||
|     0x50, 0x51, 0x52, 0x53, | ||||
|   }; | ||||
| 
 | ||||
| /* If there is a character pending, return it;
 | ||||
|    otherwise return GRUB_TERM_NO_KEY.  */ | ||||
| static int | ||||
| grub_cros_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused))) | ||||
| { | ||||
|   struct grub_cros_ec_keyscan scan; | ||||
|   int i, j; | ||||
|   if (grub_cros_ec_scan_keyboard (cros_ec, &scan) < 0) | ||||
|     return GRUB_TERM_NO_KEY; | ||||
|   for (i = 0; i < GRUB_CROS_EC_KEYSCAN_COLS; i++) | ||||
|     if (scan.data[i] ^ old_scan.data[i]) | ||||
|       for (j = 0; j < GRUB_CROS_EC_KEYSCAN_ROWS; j++) | ||||
| 	if ((scan.data[i] ^ old_scan.data[i]) & (1 << j)) | ||||
| 	  { | ||||
| 	    grub_uint8_t code = map_code[i][j]; | ||||
| 	    int ret; | ||||
| 	    grub_uint8_t brk = 0; | ||||
| 	    if (!(scan.data[i] & (1 << j))) | ||||
| 	      brk = 0x80; | ||||
| 	    grub_dprintf ("cros_keyboard", "key <%d, %d> code %x\n", i, j, code); | ||||
| 	    if (code < 0x60) | ||||
| 	      ret = grub_ps2_process_incoming_byte (&ps2_state, code | brk); | ||||
| 	    else if (code >= 0x60 && code < 0x70 && e0_translate[code - 0x60]) | ||||
| 	      { | ||||
| 		grub_ps2_process_incoming_byte (&ps2_state, 0xe0); | ||||
| 		ret = grub_ps2_process_incoming_byte (&ps2_state, e0_translate[code - 0x60] | brk); | ||||
| 	      } | ||||
| 	    else | ||||
| 	      ret = GRUB_TERM_NO_KEY; | ||||
| 	    old_scan.data[i] ^= (1 << j); | ||||
| 	    if (ret != GRUB_TERM_NO_KEY) | ||||
| 	      return ret; | ||||
| 	  } | ||||
|   return GRUB_TERM_NO_KEY; | ||||
| } | ||||
| 
 | ||||
| static struct grub_term_input grub_cros_keyboard_term = | ||||
|   { | ||||
|     .name = "cros_keyboard", | ||||
|     .getkey = grub_cros_keyboard_getkey | ||||
|   }; | ||||
| 
 | ||||
| static grub_err_t | ||||
| cros_attach (const struct grub_fdtbus_dev *dev) | ||||
| { | ||||
|   grub_size_t keymap_size, i; | ||||
|   const grub_uint8_t *keymap = grub_fdtbus_get_prop (dev, "linux,keymap", &keymap_size); | ||||
| 
 | ||||
|   if (!dev->parent || !grub_cros_ec_validate (dev->parent)) | ||||
|     return GRUB_ERR_IO; | ||||
| 
 | ||||
|   if (keymap) | ||||
|     { | ||||
|       for (i = 0; i + 3 < keymap_size; i += 4) | ||||
| 	if (keymap[i+1] < GRUB_CROS_EC_KEYSCAN_COLS && keymap[i] < GRUB_CROS_EC_KEYSCAN_ROWS | ||||
| 	    && keymap[i+2] == 0 && keymap[i+3] < 0x80) | ||||
| 	  map_code[keymap[i+1]][keymap[i]] = keymap[i+3]; | ||||
|     } | ||||
| 
 | ||||
|   cros_ec = dev->parent; | ||||
|   ps2_state.current_set = 1; | ||||
|   ps2_state.at_keyboard_status = 0; | ||||
|   grub_term_register_input ("cros_keyboard", &grub_cros_keyboard_term); | ||||
|   return GRUB_ERR_NONE; | ||||
| } | ||||
| 
 | ||||
| static struct grub_fdtbus_driver cros = | ||||
| { | ||||
|   .compatible = "google,cros-ec-keyb", | ||||
|   .attach = cros_attach | ||||
| }; | ||||
| 
 | ||||
| void | ||||
| grub_cros_init (void) | ||||
| { | ||||
|   grub_fdtbus_register (&cros); | ||||
| } | ||||
							
								
								
									
										238
									
								
								grub-core/term/arm/cros_ec.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								grub-core/term/arm/cros_ec.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,238 @@ | |||
| /*
 | ||||
|  *  GRUB  --  GRand Unified Bootloader | ||||
|  * | ||||
|  *  Copyright (C) 2012  Google Inc. | ||||
|  *  Copyright (C) 2016  Free Software Foundation, Inc. | ||||
|  * | ||||
|  *  This is based on depthcharge code. | ||||
|  * | ||||
|  *  GRUB 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 3 of the License, or | ||||
|  *  (at your option) any later version. | ||||
|  * | ||||
|  *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
|  */ | ||||
| 
 | ||||
| #include <grub/mm.h> | ||||
| #include <grub/time.h> | ||||
| #include <grub/misc.h> | ||||
| #include <grub/arm/cros_ec.h> | ||||
| #include <grub/fdtbus.h> | ||||
| 
 | ||||
| static const grub_uint64_t FRAMING_TIMEOUT_MS = 300; | ||||
| 
 | ||||
| static const grub_uint8_t EC_FRAMING_BYTE = 0xec; | ||||
| 
 | ||||
| #define EC_CMD_MKBP_STATE 0x60 | ||||
| #define EC_CMD_VERSION0 0xdc | ||||
| 
 | ||||
| static grub_uint64_t last_transfer; | ||||
| 
 | ||||
| static void | ||||
| stop_bus (const struct grub_fdtbus_dev *spi) | ||||
| { | ||||
|   spi->driver->stop (spi); | ||||
|   last_transfer = grub_get_time_ms (); | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| wait_for_frame (const struct grub_fdtbus_dev *spi) | ||||
| { | ||||
|   grub_uint64_t start = grub_get_time_ms (); | ||||
|   grub_uint8_t byte; | ||||
|   do | ||||
|     { | ||||
|       if (spi->driver->receive (spi, &byte, 1)) | ||||
| 	return -1; | ||||
|       if (byte != EC_FRAMING_BYTE && | ||||
| 	  grub_get_time_ms () - start > FRAMING_TIMEOUT_MS) | ||||
| 	{ | ||||
| 	  grub_dprintf ("cros", "Timeout waiting for framing byte.\n"); | ||||
| 	  return -1; | ||||
| 	} | ||||
|     } | ||||
|   while (byte != EC_FRAMING_BYTE); | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Calculate a simple 8-bit checksum of a data block | ||||
|  * | ||||
|  * @param data	Data block to checksum | ||||
|  * @param size	Size of data block in bytes | ||||
|  * @return checksum value (0 to 255) | ||||
|  */ | ||||
| static grub_uint8_t | ||||
| cros_ec_calc_checksum (const void *data, int size) | ||||
| { | ||||
|   grub_uint8_t csum; | ||||
|   const grub_uint8_t *bytes = data; | ||||
|   int i; | ||||
| 
 | ||||
|   for (i = csum = 0; i < size; i++) | ||||
|     csum += bytes[i]; | ||||
|   return csum & 0xff; | ||||
| } | ||||
| 
 | ||||
| enum | ||||
| { | ||||
|   /* response, arglen */ | ||||
|   CROS_EC_SPI_IN_HDR_SIZE = 2, | ||||
|   /* version, cmd, arglen */ | ||||
|   CROS_EC_SPI_OUT_HDR_SIZE = 3 | ||||
| }; | ||||
| 
 | ||||
| static grub_uint8_t busbuf[256]; | ||||
| #define MSG_BYTES ((int)sizeof (busbuf)) | ||||
| 
 | ||||
| static int | ||||
| ec_command (const struct grub_fdtbus_dev *dev, int cmd, int cmd_version, | ||||
| 	    const void *dout, int dout_len, void *din, int din_len) | ||||
| { | ||||
|   const struct grub_fdtbus_dev *spi = dev->parent; | ||||
|   grub_uint8_t *bytes; | ||||
| 
 | ||||
|   /* Header + data + checksum. */ | ||||
|   grub_uint32_t out_bytes = CROS_EC_SPI_OUT_HDR_SIZE + dout_len + 1; | ||||
|   grub_uint32_t in_bytes = CROS_EC_SPI_IN_HDR_SIZE + din_len + 1; | ||||
| 
 | ||||
|   /*
 | ||||
|    * Sanity-check I/O sizes given transaction overhead in internal | ||||
|    * buffers. | ||||
|    */ | ||||
|   if (out_bytes > MSG_BYTES) | ||||
|     { | ||||
|       grub_dprintf ("cros", "Cannot send %d bytes\n", dout_len); | ||||
|       return -1; | ||||
|     } | ||||
|   if (in_bytes > MSG_BYTES) | ||||
|     { | ||||
|       grub_dprintf ("cros", "Cannot receive %d bytes\n", din_len); | ||||
|       return -1; | ||||
|     } | ||||
| 
 | ||||
|   /* Prepare the output. */ | ||||
|   bytes = busbuf; | ||||
|   *bytes++ = EC_CMD_VERSION0 + cmd_version; | ||||
|   *bytes++ = cmd; | ||||
|   *bytes++ = dout_len; | ||||
|   grub_memcpy (bytes, dout, dout_len); | ||||
|   bytes += dout_len; | ||||
| 
 | ||||
|   *bytes++ = cros_ec_calc_checksum (busbuf, | ||||
| 				    CROS_EC_SPI_OUT_HDR_SIZE + dout_len); | ||||
| 
 | ||||
|   /* Depthcharge uses 200 us here but GRUB timer resolution is only 1ms,
 | ||||
|      decrease this when we increase timer resolution.  */ | ||||
|   while (grub_get_time_ms () - last_transfer < 1) | ||||
|     ; | ||||
| 
 | ||||
|   if (spi->driver->start (spi)) | ||||
|     return -1; | ||||
| 
 | ||||
|   /* Allow EC to ramp up clock after being awoken. */ | ||||
|   /* Depthcharge only waits 100 us here but GRUB timer resolution is only 1ms,
 | ||||
|      decrease this when we increase timer resolution.  */ | ||||
|   grub_millisleep (1); | ||||
| 
 | ||||
|   if (spi->driver->send (spi, busbuf, out_bytes)) | ||||
|     { | ||||
|       stop_bus (spi); | ||||
|       return -1; | ||||
|     } | ||||
| 
 | ||||
|   /* Wait until the EC is ready. */ | ||||
|   if (wait_for_frame (spi)) | ||||
|     { | ||||
|       stop_bus (spi); | ||||
|       return -1; | ||||
|     } | ||||
| 
 | ||||
|   /* Read the response code and the data length. */ | ||||
|   bytes = busbuf; | ||||
|   if (spi->driver->receive (spi, bytes, 2)) | ||||
|     { | ||||
|       stop_bus (spi); | ||||
|       return -1; | ||||
|     } | ||||
|   grub_uint8_t result = *bytes++; | ||||
|   grub_uint8_t length = *bytes++; | ||||
| 
 | ||||
|   /* Make sure there's enough room for the data. */ | ||||
|   if (CROS_EC_SPI_IN_HDR_SIZE + length + 1 > MSG_BYTES) | ||||
|     { | ||||
|       grub_dprintf ("cros", "Received length %#02x too large\n", length); | ||||
|       stop_bus (spi); | ||||
|       return -1; | ||||
|     } | ||||
| 
 | ||||
|   /* Read the data and the checksum, and finish up. */ | ||||
|   if (spi->driver->receive (spi, bytes, length + 1)) | ||||
|     { | ||||
|       stop_bus (spi); | ||||
|       return -1; | ||||
|     } | ||||
|   bytes += length; | ||||
|   int expected = *bytes++; | ||||
|   stop_bus (spi); | ||||
| 
 | ||||
|   /* Check the integrity of the response. */ | ||||
|   if (result != 0) | ||||
|     { | ||||
|       grub_dprintf ("cros", "Received bad result code %d\n", result); | ||||
|       return -result; | ||||
|     } | ||||
| 
 | ||||
|   int csum = cros_ec_calc_checksum (busbuf, | ||||
| 				    CROS_EC_SPI_IN_HDR_SIZE + length); | ||||
| 
 | ||||
|   if (csum != expected) | ||||
|     { | ||||
|       grub_dprintf ("cros", "Invalid checksum rx %#02x, calced %#02x\n", | ||||
| 		    expected, csum); | ||||
|       return -1; | ||||
|     } | ||||
| 
 | ||||
|   /* If the caller wants the response, copy it out for them. */ | ||||
|   if (length < din_len) | ||||
|     din_len = length; | ||||
|   if (din) | ||||
|     { | ||||
|       grub_memcpy (din, (grub_uint8_t *) busbuf + CROS_EC_SPI_IN_HDR_SIZE, din_len); | ||||
|     } | ||||
| 
 | ||||
|   return din_len; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| grub_cros_ec_scan_keyboard (const struct grub_fdtbus_dev *dev, struct grub_cros_ec_keyscan *scan) | ||||
| { | ||||
|   if (ec_command (dev, EC_CMD_MKBP_STATE, 0, NULL, 0, scan, | ||||
| 		  sizeof (*scan)) < (int) sizeof (*scan)) | ||||
|     return -1; | ||||
| 
 | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| grub_cros_ec_validate (const struct grub_fdtbus_dev *dev) | ||||
| { | ||||
|   if (!grub_fdtbus_is_compatible("google,cros-ec-spi", dev)) | ||||
|     return 0; | ||||
|   if (!dev->parent) | ||||
|     return 0; | ||||
|   if (!dev->parent->driver) | ||||
|     return 0; | ||||
|   if (!dev->parent->driver->send | ||||
|       || !dev->parent->driver->receive) | ||||
|     return 0; | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										21
									
								
								include/grub/arm/cros_ec.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								include/grub/arm/cros_ec.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| #ifndef GRUB_ARM_CROS_EC_H | ||||
| #define GRUB_ARM_CROS_EC_H 1 | ||||
| 
 | ||||
| #include <grub/types.h> | ||||
| #include <grub/fdtbus.h> | ||||
| 
 | ||||
| #define GRUB_CROS_EC_KEYSCAN_COLS 13 | ||||
| #define GRUB_CROS_EC_KEYSCAN_ROWS 8 | ||||
| 
 | ||||
| struct grub_cros_ec_keyscan { | ||||
|   grub_uint8_t data[GRUB_CROS_EC_KEYSCAN_COLS]; | ||||
| }; | ||||
| 
 | ||||
| int | ||||
| grub_cros_ec_scan_keyboard (const struct grub_fdtbus_dev *dev, | ||||
| 			    struct grub_cros_ec_keyscan *scan); | ||||
| 
 | ||||
| int | ||||
| grub_cros_ec_validate (const struct grub_fdtbus_dev *dev); | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue