1 /* 2 * Chromium OS cros_ec driver - LPC interface 3 * 4 * Copyright (c) 2012 The Chromium OS Authors. 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 */ 8 9 /* 10 * The Matrix Keyboard Protocol driver handles talking to the keyboard 11 * controller chip. Mostly this is for keyboard functions, but some other 12 * things have slipped in, so we provide generic services to talk to the 13 * KBC. 14 */ 15 16 #include <common.h> 17 #include <command.h> 18 #include <cros_ec.h> 19 #include <asm/io.h> 20 21 #ifdef DEBUG_TRACE 22 #define debug_trace(fmt, b...) debug(fmt, ##b) 23 #else 24 #define debug_trace(fmt, b...) 25 #endif 26 27 static int wait_for_sync(struct cros_ec_dev *dev) 28 { 29 unsigned long start; 30 31 start = get_timer(0); 32 while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) { 33 if (get_timer(start) > 1000) { 34 debug("%s: Timeout waiting for CROS_EC sync\n", 35 __func__); 36 return -1; 37 } 38 } 39 40 return 0; 41 } 42 43 int cros_ec_lpc_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, 44 const uint8_t *dout, int dout_len, 45 uint8_t **dinp, int din_len) 46 { 47 const int cmd_addr = EC_LPC_ADDR_HOST_CMD; 48 const int data_addr = EC_LPC_ADDR_HOST_DATA; 49 const int args_addr = EC_LPC_ADDR_HOST_ARGS; 50 const int param_addr = EC_LPC_ADDR_HOST_PARAM; 51 52 struct ec_lpc_host_args args; 53 uint8_t *d; 54 int csum; 55 int i; 56 57 if (dout_len > EC_HOST_PARAM_SIZE) { 58 debug("%s: Cannot send %d bytes\n", __func__, dout_len); 59 return -1; 60 } 61 62 /* Fill in args */ 63 args.flags = EC_HOST_ARGS_FLAG_FROM_HOST; 64 args.command_version = cmd_version; 65 args.data_size = dout_len; 66 67 /* Calculate checksum */ 68 csum = cmd + args.flags + args.command_version + args.data_size; 69 for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) 70 csum += *d; 71 72 args.checksum = (uint8_t)csum; 73 74 if (wait_for_sync(dev)) { 75 debug("%s: Timeout waiting ready\n", __func__); 76 return -1; 77 } 78 79 /* Write args */ 80 for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++) 81 outb(*d, args_addr + i); 82 83 /* Write data, if any */ 84 debug_trace("cmd: %02x, ver: %02x", cmd, cmd_version); 85 for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) { 86 outb(*d, param_addr + i); 87 debug_trace("%02x ", *d); 88 } 89 90 outb(cmd, cmd_addr); 91 debug_trace("\n"); 92 93 if (wait_for_sync(dev)) { 94 debug("%s: Timeout waiting for response\n", __func__); 95 return -1; 96 } 97 98 /* Check result */ 99 i = inb(data_addr); 100 if (i) { 101 debug("%s: CROS_EC result code %d\n", __func__, i); 102 return -i; 103 } 104 105 /* Read back args */ 106 for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++) 107 *d = inb(args_addr + i); 108 109 /* 110 * If EC didn't modify args flags, then somehow we sent a new-style 111 * command to an old EC, which means it would have read its params 112 * from the wrong place. 113 */ 114 if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) { 115 debug("%s: CROS_EC protocol mismatch\n", __func__); 116 return -EC_RES_INVALID_RESPONSE; 117 } 118 119 if (args.data_size > din_len) { 120 debug("%s: CROS_EC returned too much data %d > %d\n", 121 __func__, args.data_size, din_len); 122 return -EC_RES_INVALID_RESPONSE; 123 } 124 125 /* Read data, if any */ 126 for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) { 127 *d = inb(param_addr + i); 128 debug_trace("%02x ", *d); 129 } 130 debug_trace("\n"); 131 132 /* Verify checksum */ 133 csum = cmd + args.flags + args.command_version + args.data_size; 134 for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) 135 csum += *d; 136 137 if (args.checksum != (uint8_t)csum) { 138 debug("%s: CROS_EC response has invalid checksum\n", __func__); 139 return -EC_RES_INVALID_CHECKSUM; 140 } 141 *dinp = dev->din; 142 143 /* Return actual amount of data received */ 144 return args.data_size; 145 } 146 147 /** 148 * Initialize LPC protocol. 149 * 150 * @param dev CROS_EC device 151 * @param blob Device tree blob 152 * @return 0 if ok, -1 on error 153 */ 154 int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob) 155 { 156 int byte, i; 157 158 /* See if we can find an EC at the other end */ 159 byte = 0xff; 160 byte &= inb(EC_LPC_ADDR_HOST_CMD); 161 byte &= inb(EC_LPC_ADDR_HOST_DATA); 162 for (i = 0; i < EC_HOST_PARAM_SIZE && (byte == 0xff); i++) 163 byte &= inb(EC_LPC_ADDR_HOST_PARAM + i); 164 if (byte == 0xff) { 165 debug("%s: CROS_EC device not found on LPC bus\n", 166 __func__); 167 return -1; 168 } 169 170 return 0; 171 } 172 173 /* 174 * Test if LPC command args are supported. 175 * 176 * The cheapest way to do this is by looking for the memory-mapped 177 * flag. This is faster than sending a new-style 'hello' command and 178 * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag 179 * in args when it responds. 180 */ 181 int cros_ec_lpc_check_version(struct cros_ec_dev *dev) 182 { 183 if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) == 'E' && 184 inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) 185 == 'C' && 186 (inb(EC_LPC_ADDR_MEMMAP + 187 EC_MEMMAP_HOST_CMD_FLAGS) & 188 EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED)) { 189 return 0; 190 } 191 192 printf("%s: ERROR: old EC interface not supported\n", __func__); 193 return -1; 194 } 195