1*c8d3328aSHung-ying Tyan /* 2*c8d3328aSHung-ying Tyan * Chromium OS cros_ec driver - LPC interface 3*c8d3328aSHung-ying Tyan * 4*c8d3328aSHung-ying Tyan * Copyright (c) 2012 The Chromium OS Authors. 5*c8d3328aSHung-ying Tyan * See file CREDITS for list of people who contributed to this 6*c8d3328aSHung-ying Tyan * project. 7*c8d3328aSHung-ying Tyan * 8*c8d3328aSHung-ying Tyan * This program is free software; you can redistribute it and/or 9*c8d3328aSHung-ying Tyan * modify it under the terms of the GNU General Public License as 10*c8d3328aSHung-ying Tyan * published by the Free Software Foundation; either version 2 of 11*c8d3328aSHung-ying Tyan * the License, or (at your option) any later version. 12*c8d3328aSHung-ying Tyan * 13*c8d3328aSHung-ying Tyan * This program is distributed in the hope that it will be useful, 14*c8d3328aSHung-ying Tyan * but WITHOUT ANY WARRANTY; without even the implied warranty of 15*c8d3328aSHung-ying Tyan * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16*c8d3328aSHung-ying Tyan * GNU General Public License for more details. 17*c8d3328aSHung-ying Tyan * 18*c8d3328aSHung-ying Tyan * You should have received a copy of the GNU General Public License 19*c8d3328aSHung-ying Tyan * along with this program; if not, write to the Free Software 20*c8d3328aSHung-ying Tyan * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 21*c8d3328aSHung-ying Tyan * MA 02111-1307 USA 22*c8d3328aSHung-ying Tyan */ 23*c8d3328aSHung-ying Tyan 24*c8d3328aSHung-ying Tyan /* 25*c8d3328aSHung-ying Tyan * The Matrix Keyboard Protocol driver handles talking to the keyboard 26*c8d3328aSHung-ying Tyan * controller chip. Mostly this is for keyboard functions, but some other 27*c8d3328aSHung-ying Tyan * things have slipped in, so we provide generic services to talk to the 28*c8d3328aSHung-ying Tyan * KBC. 29*c8d3328aSHung-ying Tyan */ 30*c8d3328aSHung-ying Tyan 31*c8d3328aSHung-ying Tyan #include <common.h> 32*c8d3328aSHung-ying Tyan #include <command.h> 33*c8d3328aSHung-ying Tyan #include <cros_ec.h> 34*c8d3328aSHung-ying Tyan #include <asm/io.h> 35*c8d3328aSHung-ying Tyan 36*c8d3328aSHung-ying Tyan #ifdef DEBUG_TRACE 37*c8d3328aSHung-ying Tyan #define debug_trace(fmt, b...) debug(fmt, ##b) 38*c8d3328aSHung-ying Tyan #else 39*c8d3328aSHung-ying Tyan #define debug_trace(fmt, b...) 40*c8d3328aSHung-ying Tyan #endif 41*c8d3328aSHung-ying Tyan 42*c8d3328aSHung-ying Tyan static int wait_for_sync(struct cros_ec_dev *dev) 43*c8d3328aSHung-ying Tyan { 44*c8d3328aSHung-ying Tyan unsigned long start; 45*c8d3328aSHung-ying Tyan 46*c8d3328aSHung-ying Tyan start = get_timer(0); 47*c8d3328aSHung-ying Tyan while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) { 48*c8d3328aSHung-ying Tyan if (get_timer(start) > 1000) { 49*c8d3328aSHung-ying Tyan debug("%s: Timeout waiting for CROS_EC sync\n", 50*c8d3328aSHung-ying Tyan __func__); 51*c8d3328aSHung-ying Tyan return -1; 52*c8d3328aSHung-ying Tyan } 53*c8d3328aSHung-ying Tyan } 54*c8d3328aSHung-ying Tyan 55*c8d3328aSHung-ying Tyan return 0; 56*c8d3328aSHung-ying Tyan } 57*c8d3328aSHung-ying Tyan 58*c8d3328aSHung-ying Tyan /** 59*c8d3328aSHung-ying Tyan * Send a command to a LPC CROS_EC device and return the reply. 60*c8d3328aSHung-ying Tyan * 61*c8d3328aSHung-ying Tyan * The device's internal input/output buffers are used. 62*c8d3328aSHung-ying Tyan * 63*c8d3328aSHung-ying Tyan * @param dev CROS_EC device 64*c8d3328aSHung-ying Tyan * @param cmd Command to send (EC_CMD_...) 65*c8d3328aSHung-ying Tyan * @param cmd_version Version of command to send (EC_VER_...) 66*c8d3328aSHung-ying Tyan * @param dout Output data (may be NULL If dout_len=0) 67*c8d3328aSHung-ying Tyan * @param dout_len Size of output data in bytes 68*c8d3328aSHung-ying Tyan * @param dinp Place to put pointer to response data 69*c8d3328aSHung-ying Tyan * @param din_len Maximum size of response in bytes 70*c8d3328aSHung-ying Tyan * @return number of bytes in response, or -1 on error 71*c8d3328aSHung-ying Tyan */ 72*c8d3328aSHung-ying Tyan static int old_lpc_command(struct cros_ec_dev *dev, uint8_t cmd, 73*c8d3328aSHung-ying Tyan const uint8_t *dout, int dout_len, 74*c8d3328aSHung-ying Tyan uint8_t **dinp, int din_len) 75*c8d3328aSHung-ying Tyan { 76*c8d3328aSHung-ying Tyan int ret, i; 77*c8d3328aSHung-ying Tyan 78*c8d3328aSHung-ying Tyan if (dout_len > EC_OLD_PARAM_SIZE) { 79*c8d3328aSHung-ying Tyan debug("%s: Cannot send %d bytes\n", __func__, dout_len); 80*c8d3328aSHung-ying Tyan return -1; 81*c8d3328aSHung-ying Tyan } 82*c8d3328aSHung-ying Tyan 83*c8d3328aSHung-ying Tyan if (din_len > EC_OLD_PARAM_SIZE) { 84*c8d3328aSHung-ying Tyan debug("%s: Cannot receive %d bytes\n", __func__, din_len); 85*c8d3328aSHung-ying Tyan return -1; 86*c8d3328aSHung-ying Tyan } 87*c8d3328aSHung-ying Tyan 88*c8d3328aSHung-ying Tyan if (wait_for_sync(dev)) { 89*c8d3328aSHung-ying Tyan debug("%s: Timeout waiting ready\n", __func__); 90*c8d3328aSHung-ying Tyan return -1; 91*c8d3328aSHung-ying Tyan } 92*c8d3328aSHung-ying Tyan 93*c8d3328aSHung-ying Tyan debug_trace("cmd: %02x, ", cmd); 94*c8d3328aSHung-ying Tyan for (i = 0; i < dout_len; i++) { 95*c8d3328aSHung-ying Tyan debug_trace("%02x ", dout[i]); 96*c8d3328aSHung-ying Tyan outb(dout[i], EC_LPC_ADDR_OLD_PARAM + i); 97*c8d3328aSHung-ying Tyan } 98*c8d3328aSHung-ying Tyan outb(cmd, EC_LPC_ADDR_HOST_CMD); 99*c8d3328aSHung-ying Tyan debug_trace("\n"); 100*c8d3328aSHung-ying Tyan 101*c8d3328aSHung-ying Tyan if (wait_for_sync(dev)) { 102*c8d3328aSHung-ying Tyan debug("%s: Timeout waiting ready\n", __func__); 103*c8d3328aSHung-ying Tyan return -1; 104*c8d3328aSHung-ying Tyan } 105*c8d3328aSHung-ying Tyan 106*c8d3328aSHung-ying Tyan ret = inb(EC_LPC_ADDR_HOST_DATA); 107*c8d3328aSHung-ying Tyan if (ret) { 108*c8d3328aSHung-ying Tyan debug("%s: CROS_EC result code %d\n", __func__, ret); 109*c8d3328aSHung-ying Tyan return -ret; 110*c8d3328aSHung-ying Tyan } 111*c8d3328aSHung-ying Tyan 112*c8d3328aSHung-ying Tyan debug_trace("resp: %02x, ", ret); 113*c8d3328aSHung-ying Tyan for (i = 0; i < din_len; i++) { 114*c8d3328aSHung-ying Tyan dev->din[i] = inb(EC_LPC_ADDR_OLD_PARAM + i); 115*c8d3328aSHung-ying Tyan debug_trace("%02x ", dev->din[i]); 116*c8d3328aSHung-ying Tyan } 117*c8d3328aSHung-ying Tyan debug_trace("\n"); 118*c8d3328aSHung-ying Tyan *dinp = dev->din; 119*c8d3328aSHung-ying Tyan 120*c8d3328aSHung-ying Tyan return din_len; 121*c8d3328aSHung-ying Tyan } 122*c8d3328aSHung-ying Tyan 123*c8d3328aSHung-ying Tyan int cros_ec_lpc_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, 124*c8d3328aSHung-ying Tyan const uint8_t *dout, int dout_len, 125*c8d3328aSHung-ying Tyan uint8_t **dinp, int din_len) 126*c8d3328aSHung-ying Tyan { 127*c8d3328aSHung-ying Tyan const int cmd_addr = EC_LPC_ADDR_HOST_CMD; 128*c8d3328aSHung-ying Tyan const int data_addr = EC_LPC_ADDR_HOST_DATA; 129*c8d3328aSHung-ying Tyan const int args_addr = EC_LPC_ADDR_HOST_ARGS; 130*c8d3328aSHung-ying Tyan const int param_addr = EC_LPC_ADDR_HOST_PARAM; 131*c8d3328aSHung-ying Tyan 132*c8d3328aSHung-ying Tyan struct ec_lpc_host_args args; 133*c8d3328aSHung-ying Tyan uint8_t *d; 134*c8d3328aSHung-ying Tyan int csum; 135*c8d3328aSHung-ying Tyan int i; 136*c8d3328aSHung-ying Tyan 137*c8d3328aSHung-ying Tyan /* Fall back to old-style command interface if args aren't supported */ 138*c8d3328aSHung-ying Tyan if (!dev->cmd_version_is_supported) 139*c8d3328aSHung-ying Tyan return old_lpc_command(dev, cmd, dout, dout_len, dinp, 140*c8d3328aSHung-ying Tyan din_len); 141*c8d3328aSHung-ying Tyan 142*c8d3328aSHung-ying Tyan if (dout_len > EC_HOST_PARAM_SIZE) { 143*c8d3328aSHung-ying Tyan debug("%s: Cannot send %d bytes\n", __func__, dout_len); 144*c8d3328aSHung-ying Tyan return -1; 145*c8d3328aSHung-ying Tyan } 146*c8d3328aSHung-ying Tyan 147*c8d3328aSHung-ying Tyan /* Fill in args */ 148*c8d3328aSHung-ying Tyan args.flags = EC_HOST_ARGS_FLAG_FROM_HOST; 149*c8d3328aSHung-ying Tyan args.command_version = cmd_version; 150*c8d3328aSHung-ying Tyan args.data_size = dout_len; 151*c8d3328aSHung-ying Tyan 152*c8d3328aSHung-ying Tyan /* Calculate checksum */ 153*c8d3328aSHung-ying Tyan csum = cmd + args.flags + args.command_version + args.data_size; 154*c8d3328aSHung-ying Tyan for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) 155*c8d3328aSHung-ying Tyan csum += *d; 156*c8d3328aSHung-ying Tyan 157*c8d3328aSHung-ying Tyan args.checksum = (uint8_t)csum; 158*c8d3328aSHung-ying Tyan 159*c8d3328aSHung-ying Tyan if (wait_for_sync(dev)) { 160*c8d3328aSHung-ying Tyan debug("%s: Timeout waiting ready\n", __func__); 161*c8d3328aSHung-ying Tyan return -1; 162*c8d3328aSHung-ying Tyan } 163*c8d3328aSHung-ying Tyan 164*c8d3328aSHung-ying Tyan /* Write args */ 165*c8d3328aSHung-ying Tyan for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++) 166*c8d3328aSHung-ying Tyan outb(*d, args_addr + i); 167*c8d3328aSHung-ying Tyan 168*c8d3328aSHung-ying Tyan /* Write data, if any */ 169*c8d3328aSHung-ying Tyan debug_trace("cmd: %02x, ver: %02x", cmd, cmd_version); 170*c8d3328aSHung-ying Tyan for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) { 171*c8d3328aSHung-ying Tyan outb(*d, param_addr + i); 172*c8d3328aSHung-ying Tyan debug_trace("%02x ", *d); 173*c8d3328aSHung-ying Tyan } 174*c8d3328aSHung-ying Tyan 175*c8d3328aSHung-ying Tyan outb(cmd, cmd_addr); 176*c8d3328aSHung-ying Tyan debug_trace("\n"); 177*c8d3328aSHung-ying Tyan 178*c8d3328aSHung-ying Tyan if (wait_for_sync(dev)) { 179*c8d3328aSHung-ying Tyan debug("%s: Timeout waiting for response\n", __func__); 180*c8d3328aSHung-ying Tyan return -1; 181*c8d3328aSHung-ying Tyan } 182*c8d3328aSHung-ying Tyan 183*c8d3328aSHung-ying Tyan /* Check result */ 184*c8d3328aSHung-ying Tyan i = inb(data_addr); 185*c8d3328aSHung-ying Tyan if (i) { 186*c8d3328aSHung-ying Tyan debug("%s: CROS_EC result code %d\n", __func__, i); 187*c8d3328aSHung-ying Tyan return -i; 188*c8d3328aSHung-ying Tyan } 189*c8d3328aSHung-ying Tyan 190*c8d3328aSHung-ying Tyan /* Read back args */ 191*c8d3328aSHung-ying Tyan for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++) 192*c8d3328aSHung-ying Tyan *d = inb(args_addr + i); 193*c8d3328aSHung-ying Tyan 194*c8d3328aSHung-ying Tyan /* 195*c8d3328aSHung-ying Tyan * If EC didn't modify args flags, then somehow we sent a new-style 196*c8d3328aSHung-ying Tyan * command to an old EC, which means it would have read its params 197*c8d3328aSHung-ying Tyan * from the wrong place. 198*c8d3328aSHung-ying Tyan */ 199*c8d3328aSHung-ying Tyan if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) { 200*c8d3328aSHung-ying Tyan debug("%s: CROS_EC protocol mismatch\n", __func__); 201*c8d3328aSHung-ying Tyan return -EC_RES_INVALID_RESPONSE; 202*c8d3328aSHung-ying Tyan } 203*c8d3328aSHung-ying Tyan 204*c8d3328aSHung-ying Tyan if (args.data_size > din_len) { 205*c8d3328aSHung-ying Tyan debug("%s: CROS_EC returned too much data %d > %d\n", 206*c8d3328aSHung-ying Tyan __func__, args.data_size, din_len); 207*c8d3328aSHung-ying Tyan return -EC_RES_INVALID_RESPONSE; 208*c8d3328aSHung-ying Tyan } 209*c8d3328aSHung-ying Tyan 210*c8d3328aSHung-ying Tyan /* Read data, if any */ 211*c8d3328aSHung-ying Tyan for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) { 212*c8d3328aSHung-ying Tyan *d = inb(param_addr + i); 213*c8d3328aSHung-ying Tyan debug_trace("%02x ", *d); 214*c8d3328aSHung-ying Tyan } 215*c8d3328aSHung-ying Tyan debug_trace("\n"); 216*c8d3328aSHung-ying Tyan 217*c8d3328aSHung-ying Tyan /* Verify checksum */ 218*c8d3328aSHung-ying Tyan csum = cmd + args.flags + args.command_version + args.data_size; 219*c8d3328aSHung-ying Tyan for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) 220*c8d3328aSHung-ying Tyan csum += *d; 221*c8d3328aSHung-ying Tyan 222*c8d3328aSHung-ying Tyan if (args.checksum != (uint8_t)csum) { 223*c8d3328aSHung-ying Tyan debug("%s: CROS_EC response has invalid checksum\n", __func__); 224*c8d3328aSHung-ying Tyan return -EC_RES_INVALID_CHECKSUM; 225*c8d3328aSHung-ying Tyan } 226*c8d3328aSHung-ying Tyan *dinp = dev->din; 227*c8d3328aSHung-ying Tyan 228*c8d3328aSHung-ying Tyan /* Return actual amount of data received */ 229*c8d3328aSHung-ying Tyan return args.data_size; 230*c8d3328aSHung-ying Tyan } 231*c8d3328aSHung-ying Tyan 232*c8d3328aSHung-ying Tyan /** 233*c8d3328aSHung-ying Tyan * Initialize LPC protocol. 234*c8d3328aSHung-ying Tyan * 235*c8d3328aSHung-ying Tyan * @param dev CROS_EC device 236*c8d3328aSHung-ying Tyan * @param blob Device tree blob 237*c8d3328aSHung-ying Tyan * @return 0 if ok, -1 on error 238*c8d3328aSHung-ying Tyan */ 239*c8d3328aSHung-ying Tyan int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob) 240*c8d3328aSHung-ying Tyan { 241*c8d3328aSHung-ying Tyan int byte, i; 242*c8d3328aSHung-ying Tyan 243*c8d3328aSHung-ying Tyan /* See if we can find an EC at the other end */ 244*c8d3328aSHung-ying Tyan byte = 0xff; 245*c8d3328aSHung-ying Tyan byte &= inb(EC_LPC_ADDR_HOST_CMD); 246*c8d3328aSHung-ying Tyan byte &= inb(EC_LPC_ADDR_HOST_DATA); 247*c8d3328aSHung-ying Tyan for (i = 0; i < EC_HOST_PARAM_SIZE && (byte == 0xff); i++) 248*c8d3328aSHung-ying Tyan byte &= inb(EC_LPC_ADDR_HOST_PARAM + i); 249*c8d3328aSHung-ying Tyan if (byte == 0xff) { 250*c8d3328aSHung-ying Tyan debug("%s: CROS_EC device not found on LPC bus\n", 251*c8d3328aSHung-ying Tyan __func__); 252*c8d3328aSHung-ying Tyan return -1; 253*c8d3328aSHung-ying Tyan } 254*c8d3328aSHung-ying Tyan 255*c8d3328aSHung-ying Tyan return 0; 256*c8d3328aSHung-ying Tyan } 257*c8d3328aSHung-ying Tyan 258*c8d3328aSHung-ying Tyan /* 259*c8d3328aSHung-ying Tyan * Test if LPC command args are supported. 260*c8d3328aSHung-ying Tyan * 261*c8d3328aSHung-ying Tyan * The cheapest way to do this is by looking for the memory-mapped 262*c8d3328aSHung-ying Tyan * flag. This is faster than sending a new-style 'hello' command and 263*c8d3328aSHung-ying Tyan * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag 264*c8d3328aSHung-ying Tyan * in args when it responds. 265*c8d3328aSHung-ying Tyan */ 266*c8d3328aSHung-ying Tyan int cros_ec_lpc_check_version(struct cros_ec_dev *dev) 267*c8d3328aSHung-ying Tyan { 268*c8d3328aSHung-ying Tyan if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) == 'E' && 269*c8d3328aSHung-ying Tyan inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) 270*c8d3328aSHung-ying Tyan == 'C' && 271*c8d3328aSHung-ying Tyan (inb(EC_LPC_ADDR_MEMMAP + 272*c8d3328aSHung-ying Tyan EC_MEMMAP_HOST_CMD_FLAGS) & 273*c8d3328aSHung-ying Tyan EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED)) { 274*c8d3328aSHung-ying Tyan dev->cmd_version_is_supported = 1; 275*c8d3328aSHung-ying Tyan } else { 276*c8d3328aSHung-ying Tyan /* We are going to use the old IO ports */ 277*c8d3328aSHung-ying Tyan dev->cmd_version_is_supported = 0; 278*c8d3328aSHung-ying Tyan } 279*c8d3328aSHung-ying Tyan debug("lpc: version %s\n", dev->cmd_version_is_supported ? 280*c8d3328aSHung-ying Tyan "new" : "old"); 281*c8d3328aSHung-ying Tyan 282*c8d3328aSHung-ying Tyan return 0; 283*c8d3328aSHung-ying Tyan } 284