188364387SHung-ying Tyan /* 288364387SHung-ying Tyan * Chromium OS cros_ec driver 388364387SHung-ying Tyan * 488364387SHung-ying Tyan * Copyright (c) 2012 The Chromium OS Authors. 588364387SHung-ying Tyan * 61a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 788364387SHung-ying Tyan */ 888364387SHung-ying Tyan 988364387SHung-ying Tyan /* 1088364387SHung-ying Tyan * The Matrix Keyboard Protocol driver handles talking to the keyboard 1188364387SHung-ying Tyan * controller chip. Mostly this is for keyboard functions, but some other 1288364387SHung-ying Tyan * things have slipped in, so we provide generic services to talk to the 1388364387SHung-ying Tyan * KBC. 1488364387SHung-ying Tyan */ 1588364387SHung-ying Tyan 1688364387SHung-ying Tyan #include <common.h> 1788364387SHung-ying Tyan #include <command.h> 1888364387SHung-ying Tyan #include <i2c.h> 1988364387SHung-ying Tyan #include <cros_ec.h> 2088364387SHung-ying Tyan #include <fdtdec.h> 2188364387SHung-ying Tyan #include <malloc.h> 2288364387SHung-ying Tyan #include <spi.h> 2388364387SHung-ying Tyan #include <asm/io.h> 2488364387SHung-ying Tyan #include <asm-generic/gpio.h> 2588364387SHung-ying Tyan 2688364387SHung-ying Tyan #ifdef DEBUG_TRACE 2788364387SHung-ying Tyan #define debug_trace(fmt, b...) debug(fmt, #b) 2888364387SHung-ying Tyan #else 2988364387SHung-ying Tyan #define debug_trace(fmt, b...) 3088364387SHung-ying Tyan #endif 3188364387SHung-ying Tyan 3288364387SHung-ying Tyan enum { 3388364387SHung-ying Tyan /* Timeout waiting for a flash erase command to complete */ 3488364387SHung-ying Tyan CROS_EC_CMD_TIMEOUT_MS = 5000, 3588364387SHung-ying Tyan /* Timeout waiting for a synchronous hash to be recomputed */ 3688364387SHung-ying Tyan CROS_EC_CMD_HASH_TIMEOUT_MS = 2000, 3788364387SHung-ying Tyan }; 3888364387SHung-ying Tyan 3988364387SHung-ying Tyan static struct cros_ec_dev static_dev, *last_dev; 4088364387SHung-ying Tyan 4188364387SHung-ying Tyan DECLARE_GLOBAL_DATA_PTR; 4288364387SHung-ying Tyan 4388364387SHung-ying Tyan /* Note: depends on enum ec_current_image */ 4488364387SHung-ying Tyan static const char * const ec_current_image_name[] = {"unknown", "RO", "RW"}; 4588364387SHung-ying Tyan 4688364387SHung-ying Tyan void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data, int len) 4788364387SHung-ying Tyan { 4888364387SHung-ying Tyan #ifdef DEBUG 4988364387SHung-ying Tyan int i; 5088364387SHung-ying Tyan 5188364387SHung-ying Tyan printf("%s: ", name); 5288364387SHung-ying Tyan if (cmd != -1) 5388364387SHung-ying Tyan printf("cmd=%#x: ", cmd); 5488364387SHung-ying Tyan for (i = 0; i < len; i++) 5588364387SHung-ying Tyan printf("%02x ", data[i]); 5688364387SHung-ying Tyan printf("\n"); 5788364387SHung-ying Tyan #endif 5888364387SHung-ying Tyan } 5988364387SHung-ying Tyan 6088364387SHung-ying Tyan /* 6188364387SHung-ying Tyan * Calculate a simple 8-bit checksum of a data block 6288364387SHung-ying Tyan * 6388364387SHung-ying Tyan * @param data Data block to checksum 6488364387SHung-ying Tyan * @param size Size of data block in bytes 6588364387SHung-ying Tyan * @return checksum value (0 to 255) 6688364387SHung-ying Tyan */ 6788364387SHung-ying Tyan int cros_ec_calc_checksum(const uint8_t *data, int size) 6888364387SHung-ying Tyan { 6988364387SHung-ying Tyan int csum, i; 7088364387SHung-ying Tyan 7188364387SHung-ying Tyan for (i = csum = 0; i < size; i++) 7288364387SHung-ying Tyan csum += data[i]; 7388364387SHung-ying Tyan return csum & 0xff; 7488364387SHung-ying Tyan } 7588364387SHung-ying Tyan 7688364387SHung-ying Tyan static int send_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, 7788364387SHung-ying Tyan const void *dout, int dout_len, 7888364387SHung-ying Tyan uint8_t **dinp, int din_len) 7988364387SHung-ying Tyan { 8088364387SHung-ying Tyan int ret; 8188364387SHung-ying Tyan 8288364387SHung-ying Tyan switch (dev->interface) { 8388364387SHung-ying Tyan #ifdef CONFIG_CROS_EC_SPI 8488364387SHung-ying Tyan case CROS_EC_IF_SPI: 8588364387SHung-ying Tyan ret = cros_ec_spi_command(dev, cmd, cmd_version, 8688364387SHung-ying Tyan (const uint8_t *)dout, dout_len, 8788364387SHung-ying Tyan dinp, din_len); 8888364387SHung-ying Tyan break; 8988364387SHung-ying Tyan #endif 9088364387SHung-ying Tyan #ifdef CONFIG_CROS_EC_I2C 9188364387SHung-ying Tyan case CROS_EC_IF_I2C: 9288364387SHung-ying Tyan ret = cros_ec_i2c_command(dev, cmd, cmd_version, 9388364387SHung-ying Tyan (const uint8_t *)dout, dout_len, 9488364387SHung-ying Tyan dinp, din_len); 9588364387SHung-ying Tyan break; 9688364387SHung-ying Tyan #endif 9788364387SHung-ying Tyan #ifdef CONFIG_CROS_EC_LPC 9888364387SHung-ying Tyan case CROS_EC_IF_LPC: 9988364387SHung-ying Tyan ret = cros_ec_lpc_command(dev, cmd, cmd_version, 10088364387SHung-ying Tyan (const uint8_t *)dout, dout_len, 10188364387SHung-ying Tyan dinp, din_len); 10288364387SHung-ying Tyan break; 10388364387SHung-ying Tyan #endif 10488364387SHung-ying Tyan case CROS_EC_IF_NONE: 10588364387SHung-ying Tyan default: 10688364387SHung-ying Tyan ret = -1; 10788364387SHung-ying Tyan } 10888364387SHung-ying Tyan 10988364387SHung-ying Tyan return ret; 11088364387SHung-ying Tyan } 11188364387SHung-ying Tyan 11288364387SHung-ying Tyan /** 11388364387SHung-ying Tyan * Send a command to the CROS-EC device and return the reply. 11488364387SHung-ying Tyan * 11588364387SHung-ying Tyan * The device's internal input/output buffers are used. 11688364387SHung-ying Tyan * 11788364387SHung-ying Tyan * @param dev CROS-EC device 11888364387SHung-ying Tyan * @param cmd Command to send (EC_CMD_...) 11988364387SHung-ying Tyan * @param cmd_version Version of command to send (EC_VER_...) 12088364387SHung-ying Tyan * @param dout Output data (may be NULL If dout_len=0) 12188364387SHung-ying Tyan * @param dout_len Size of output data in bytes 12288364387SHung-ying Tyan * @param dinp Response data (may be NULL If din_len=0). 12388364387SHung-ying Tyan * If not NULL, it will be updated to point to the data 12488364387SHung-ying Tyan * and will always be double word aligned (64-bits) 12588364387SHung-ying Tyan * @param din_len Maximum size of response in bytes 12688364387SHung-ying Tyan * @return number of bytes in response, or -1 on error 12788364387SHung-ying Tyan */ 12888364387SHung-ying Tyan static int ec_command_inptr(struct cros_ec_dev *dev, uint8_t cmd, 12988364387SHung-ying Tyan int cmd_version, const void *dout, int dout_len, uint8_t **dinp, 13088364387SHung-ying Tyan int din_len) 13188364387SHung-ying Tyan { 13288364387SHung-ying Tyan uint8_t *din; 13388364387SHung-ying Tyan int len; 13488364387SHung-ying Tyan 13588364387SHung-ying Tyan if (cmd_version != 0 && !dev->cmd_version_is_supported) { 13688364387SHung-ying Tyan debug("%s: Command version >0 unsupported\n", __func__); 13788364387SHung-ying Tyan return -1; 13888364387SHung-ying Tyan } 13988364387SHung-ying Tyan len = send_command(dev, cmd, cmd_version, dout, dout_len, 14088364387SHung-ying Tyan &din, din_len); 14188364387SHung-ying Tyan 14288364387SHung-ying Tyan /* If the command doesn't complete, wait a while */ 14388364387SHung-ying Tyan if (len == -EC_RES_IN_PROGRESS) { 14488364387SHung-ying Tyan struct ec_response_get_comms_status *resp; 14588364387SHung-ying Tyan ulong start; 14688364387SHung-ying Tyan 14788364387SHung-ying Tyan /* Wait for command to complete */ 14888364387SHung-ying Tyan start = get_timer(0); 14988364387SHung-ying Tyan do { 15088364387SHung-ying Tyan int ret; 15188364387SHung-ying Tyan 15288364387SHung-ying Tyan mdelay(50); /* Insert some reasonable delay */ 15388364387SHung-ying Tyan ret = send_command(dev, EC_CMD_GET_COMMS_STATUS, 0, 15488364387SHung-ying Tyan NULL, 0, 15588364387SHung-ying Tyan (uint8_t **)&resp, sizeof(*resp)); 15688364387SHung-ying Tyan if (ret < 0) 15788364387SHung-ying Tyan return ret; 15888364387SHung-ying Tyan 15988364387SHung-ying Tyan if (get_timer(start) > CROS_EC_CMD_TIMEOUT_MS) { 16088364387SHung-ying Tyan debug("%s: Command %#02x timeout\n", 16188364387SHung-ying Tyan __func__, cmd); 16288364387SHung-ying Tyan return -EC_RES_TIMEOUT; 16388364387SHung-ying Tyan } 16488364387SHung-ying Tyan } while (resp->flags & EC_COMMS_STATUS_PROCESSING); 16588364387SHung-ying Tyan 16688364387SHung-ying Tyan /* OK it completed, so read the status response */ 16788364387SHung-ying Tyan /* not sure why it was 0 for the last argument */ 16888364387SHung-ying Tyan len = send_command(dev, EC_CMD_RESEND_RESPONSE, 0, 16988364387SHung-ying Tyan NULL, 0, &din, din_len); 17088364387SHung-ying Tyan } 17188364387SHung-ying Tyan 17288364387SHung-ying Tyan debug("%s: len=%d, dinp=%p, *dinp=%p\n", __func__, len, dinp, *dinp); 17388364387SHung-ying Tyan if (dinp) { 17488364387SHung-ying Tyan /* If we have any data to return, it must be 64bit-aligned */ 17588364387SHung-ying Tyan assert(len <= 0 || !((uintptr_t)din & 7)); 17688364387SHung-ying Tyan *dinp = din; 17788364387SHung-ying Tyan } 17888364387SHung-ying Tyan 17988364387SHung-ying Tyan return len; 18088364387SHung-ying Tyan } 18188364387SHung-ying Tyan 18288364387SHung-ying Tyan /** 18388364387SHung-ying Tyan * Send a command to the CROS-EC device and return the reply. 18488364387SHung-ying Tyan * 18588364387SHung-ying Tyan * The device's internal input/output buffers are used. 18688364387SHung-ying Tyan * 18788364387SHung-ying Tyan * @param dev CROS-EC device 18888364387SHung-ying Tyan * @param cmd Command to send (EC_CMD_...) 18988364387SHung-ying Tyan * @param cmd_version Version of command to send (EC_VER_...) 19088364387SHung-ying Tyan * @param dout Output data (may be NULL If dout_len=0) 19188364387SHung-ying Tyan * @param dout_len Size of output data in bytes 19288364387SHung-ying Tyan * @param din Response data (may be NULL If din_len=0). 19388364387SHung-ying Tyan * It not NULL, it is a place for ec_command() to copy the 19488364387SHung-ying Tyan * data to. 19588364387SHung-ying Tyan * @param din_len Maximum size of response in bytes 19688364387SHung-ying Tyan * @return number of bytes in response, or -1 on error 19788364387SHung-ying Tyan */ 19888364387SHung-ying Tyan static int ec_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, 19988364387SHung-ying Tyan const void *dout, int dout_len, 20088364387SHung-ying Tyan void *din, int din_len) 20188364387SHung-ying Tyan { 20288364387SHung-ying Tyan uint8_t *in_buffer; 20388364387SHung-ying Tyan int len; 20488364387SHung-ying Tyan 20588364387SHung-ying Tyan assert((din_len == 0) || din); 20688364387SHung-ying Tyan len = ec_command_inptr(dev, cmd, cmd_version, dout, dout_len, 20788364387SHung-ying Tyan &in_buffer, din_len); 20888364387SHung-ying Tyan if (len > 0) { 20988364387SHung-ying Tyan /* 21088364387SHung-ying Tyan * If we were asked to put it somewhere, do so, otherwise just 21188364387SHung-ying Tyan * disregard the result. 21288364387SHung-ying Tyan */ 21388364387SHung-ying Tyan if (din && in_buffer) { 21488364387SHung-ying Tyan assert(len <= din_len); 21588364387SHung-ying Tyan memmove(din, in_buffer, len); 21688364387SHung-ying Tyan } 21788364387SHung-ying Tyan } 21888364387SHung-ying Tyan return len; 21988364387SHung-ying Tyan } 22088364387SHung-ying Tyan 22188364387SHung-ying Tyan int cros_ec_scan_keyboard(struct cros_ec_dev *dev, struct mbkp_keyscan *scan) 22288364387SHung-ying Tyan { 22388364387SHung-ying Tyan if (ec_command(dev, EC_CMD_CROS_EC_STATE, 0, NULL, 0, scan, 22488364387SHung-ying Tyan sizeof(scan->data)) < sizeof(scan->data)) 22588364387SHung-ying Tyan return -1; 22688364387SHung-ying Tyan 22788364387SHung-ying Tyan return 0; 22888364387SHung-ying Tyan } 22988364387SHung-ying Tyan 23088364387SHung-ying Tyan int cros_ec_read_id(struct cros_ec_dev *dev, char *id, int maxlen) 23188364387SHung-ying Tyan { 23288364387SHung-ying Tyan struct ec_response_get_version *r; 23388364387SHung-ying Tyan 23488364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, 23588364387SHung-ying Tyan (uint8_t **)&r, sizeof(*r)) < sizeof(*r)) 23688364387SHung-ying Tyan return -1; 23788364387SHung-ying Tyan 23888364387SHung-ying Tyan if (maxlen > sizeof(r->version_string_ro)) 23988364387SHung-ying Tyan maxlen = sizeof(r->version_string_ro); 24088364387SHung-ying Tyan 24188364387SHung-ying Tyan switch (r->current_image) { 24288364387SHung-ying Tyan case EC_IMAGE_RO: 24388364387SHung-ying Tyan memcpy(id, r->version_string_ro, maxlen); 24488364387SHung-ying Tyan break; 24588364387SHung-ying Tyan case EC_IMAGE_RW: 24688364387SHung-ying Tyan memcpy(id, r->version_string_rw, maxlen); 24788364387SHung-ying Tyan break; 24888364387SHung-ying Tyan default: 24988364387SHung-ying Tyan return -1; 25088364387SHung-ying Tyan } 25188364387SHung-ying Tyan 25288364387SHung-ying Tyan id[maxlen - 1] = '\0'; 25388364387SHung-ying Tyan return 0; 25488364387SHung-ying Tyan } 25588364387SHung-ying Tyan 25688364387SHung-ying Tyan int cros_ec_read_version(struct cros_ec_dev *dev, 25788364387SHung-ying Tyan struct ec_response_get_version **versionp) 25888364387SHung-ying Tyan { 25988364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, 26088364387SHung-ying Tyan (uint8_t **)versionp, sizeof(**versionp)) 26188364387SHung-ying Tyan < sizeof(**versionp)) 26288364387SHung-ying Tyan return -1; 26388364387SHung-ying Tyan 26488364387SHung-ying Tyan return 0; 26588364387SHung-ying Tyan } 26688364387SHung-ying Tyan 26788364387SHung-ying Tyan int cros_ec_read_build_info(struct cros_ec_dev *dev, char **strp) 26888364387SHung-ying Tyan { 26988364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_GET_BUILD_INFO, 0, NULL, 0, 27088364387SHung-ying Tyan (uint8_t **)strp, EC_HOST_PARAM_SIZE) < 0) 27188364387SHung-ying Tyan return -1; 27288364387SHung-ying Tyan 27388364387SHung-ying Tyan return 0; 27488364387SHung-ying Tyan } 27588364387SHung-ying Tyan 27688364387SHung-ying Tyan int cros_ec_read_current_image(struct cros_ec_dev *dev, 27788364387SHung-ying Tyan enum ec_current_image *image) 27888364387SHung-ying Tyan { 27988364387SHung-ying Tyan struct ec_response_get_version *r; 28088364387SHung-ying Tyan 28188364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, 28288364387SHung-ying Tyan (uint8_t **)&r, sizeof(*r)) < sizeof(*r)) 28388364387SHung-ying Tyan return -1; 28488364387SHung-ying Tyan 28588364387SHung-ying Tyan *image = r->current_image; 28688364387SHung-ying Tyan return 0; 28788364387SHung-ying Tyan } 28888364387SHung-ying Tyan 28988364387SHung-ying Tyan static int cros_ec_wait_on_hash_done(struct cros_ec_dev *dev, 29088364387SHung-ying Tyan struct ec_response_vboot_hash *hash) 29188364387SHung-ying Tyan { 29288364387SHung-ying Tyan struct ec_params_vboot_hash p; 29388364387SHung-ying Tyan ulong start; 29488364387SHung-ying Tyan 29588364387SHung-ying Tyan start = get_timer(0); 29688364387SHung-ying Tyan while (hash->status == EC_VBOOT_HASH_STATUS_BUSY) { 29788364387SHung-ying Tyan mdelay(50); /* Insert some reasonable delay */ 29888364387SHung-ying Tyan 29988364387SHung-ying Tyan p.cmd = EC_VBOOT_HASH_GET; 30088364387SHung-ying Tyan if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), 30188364387SHung-ying Tyan hash, sizeof(*hash)) < 0) 30288364387SHung-ying Tyan return -1; 30388364387SHung-ying Tyan 30488364387SHung-ying Tyan if (get_timer(start) > CROS_EC_CMD_HASH_TIMEOUT_MS) { 30588364387SHung-ying Tyan debug("%s: EC_VBOOT_HASH_GET timeout\n", __func__); 30688364387SHung-ying Tyan return -EC_RES_TIMEOUT; 30788364387SHung-ying Tyan } 30888364387SHung-ying Tyan } 30988364387SHung-ying Tyan return 0; 31088364387SHung-ying Tyan } 31188364387SHung-ying Tyan 31288364387SHung-ying Tyan 31388364387SHung-ying Tyan int cros_ec_read_hash(struct cros_ec_dev *dev, 31488364387SHung-ying Tyan struct ec_response_vboot_hash *hash) 31588364387SHung-ying Tyan { 31688364387SHung-ying Tyan struct ec_params_vboot_hash p; 31788364387SHung-ying Tyan int rv; 31888364387SHung-ying Tyan 31988364387SHung-ying Tyan p.cmd = EC_VBOOT_HASH_GET; 32088364387SHung-ying Tyan if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), 32188364387SHung-ying Tyan hash, sizeof(*hash)) < 0) 32288364387SHung-ying Tyan return -1; 32388364387SHung-ying Tyan 32488364387SHung-ying Tyan /* If the EC is busy calculating the hash, fidget until it's done. */ 32588364387SHung-ying Tyan rv = cros_ec_wait_on_hash_done(dev, hash); 32688364387SHung-ying Tyan if (rv) 32788364387SHung-ying Tyan return rv; 32888364387SHung-ying Tyan 32988364387SHung-ying Tyan /* If the hash is valid, we're done. Otherwise, we have to kick it off 33088364387SHung-ying Tyan * again and wait for it to complete. Note that we explicitly assume 33188364387SHung-ying Tyan * that hashing zero bytes is always wrong, even though that would 33288364387SHung-ying Tyan * produce a valid hash value. */ 33388364387SHung-ying Tyan if (hash->status == EC_VBOOT_HASH_STATUS_DONE && hash->size) 33488364387SHung-ying Tyan return 0; 33588364387SHung-ying Tyan 33688364387SHung-ying Tyan debug("%s: No valid hash (status=%d size=%d). Compute one...\n", 33788364387SHung-ying Tyan __func__, hash->status, hash->size); 33888364387SHung-ying Tyan 33988364387SHung-ying Tyan p.cmd = EC_VBOOT_HASH_RECALC; 34088364387SHung-ying Tyan p.hash_type = EC_VBOOT_HASH_TYPE_SHA256; 34188364387SHung-ying Tyan p.nonce_size = 0; 34288364387SHung-ying Tyan p.offset = EC_VBOOT_HASH_OFFSET_RW; 34388364387SHung-ying Tyan 34488364387SHung-ying Tyan if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), 34588364387SHung-ying Tyan hash, sizeof(*hash)) < 0) 34688364387SHung-ying Tyan return -1; 34788364387SHung-ying Tyan 34888364387SHung-ying Tyan rv = cros_ec_wait_on_hash_done(dev, hash); 34988364387SHung-ying Tyan if (rv) 35088364387SHung-ying Tyan return rv; 35188364387SHung-ying Tyan 35288364387SHung-ying Tyan debug("%s: hash done\n", __func__); 35388364387SHung-ying Tyan 35488364387SHung-ying Tyan return 0; 35588364387SHung-ying Tyan } 35688364387SHung-ying Tyan 35788364387SHung-ying Tyan static int cros_ec_invalidate_hash(struct cros_ec_dev *dev) 35888364387SHung-ying Tyan { 35988364387SHung-ying Tyan struct ec_params_vboot_hash p; 36088364387SHung-ying Tyan struct ec_response_vboot_hash *hash; 36188364387SHung-ying Tyan 36288364387SHung-ying Tyan /* We don't have an explict command for the EC to discard its current 36388364387SHung-ying Tyan * hash value, so we'll just tell it to calculate one that we know is 36488364387SHung-ying Tyan * wrong (we claim that hashing zero bytes is always invalid). 36588364387SHung-ying Tyan */ 36688364387SHung-ying Tyan p.cmd = EC_VBOOT_HASH_RECALC; 36788364387SHung-ying Tyan p.hash_type = EC_VBOOT_HASH_TYPE_SHA256; 36888364387SHung-ying Tyan p.nonce_size = 0; 36988364387SHung-ying Tyan p.offset = 0; 37088364387SHung-ying Tyan p.size = 0; 37188364387SHung-ying Tyan 37288364387SHung-ying Tyan debug("%s:\n", __func__); 37388364387SHung-ying Tyan 37488364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), 37588364387SHung-ying Tyan (uint8_t **)&hash, sizeof(*hash)) < 0) 37688364387SHung-ying Tyan return -1; 37788364387SHung-ying Tyan 37888364387SHung-ying Tyan /* No need to wait for it to finish */ 37988364387SHung-ying Tyan return 0; 38088364387SHung-ying Tyan } 38188364387SHung-ying Tyan 38288364387SHung-ying Tyan int cros_ec_reboot(struct cros_ec_dev *dev, enum ec_reboot_cmd cmd, 38388364387SHung-ying Tyan uint8_t flags) 38488364387SHung-ying Tyan { 38588364387SHung-ying Tyan struct ec_params_reboot_ec p; 38688364387SHung-ying Tyan 38788364387SHung-ying Tyan p.cmd = cmd; 38888364387SHung-ying Tyan p.flags = flags; 38988364387SHung-ying Tyan 39088364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_REBOOT_EC, 0, &p, sizeof(p), NULL, 0) 39188364387SHung-ying Tyan < 0) 39288364387SHung-ying Tyan return -1; 39388364387SHung-ying Tyan 39488364387SHung-ying Tyan if (!(flags & EC_REBOOT_FLAG_ON_AP_SHUTDOWN)) { 39588364387SHung-ying Tyan /* 39688364387SHung-ying Tyan * EC reboot will take place immediately so delay to allow it 39788364387SHung-ying Tyan * to complete. Note that some reboot types (EC_REBOOT_COLD) 39888364387SHung-ying Tyan * will reboot the AP as well, in which case we won't actually 39988364387SHung-ying Tyan * get to this point. 40088364387SHung-ying Tyan */ 40188364387SHung-ying Tyan /* 40288364387SHung-ying Tyan * TODO(rspangler@chromium.org): Would be nice if we had a 40388364387SHung-ying Tyan * better way to determine when the reboot is complete. Could 40488364387SHung-ying Tyan * we poll a memory-mapped LPC value? 40588364387SHung-ying Tyan */ 40688364387SHung-ying Tyan udelay(50000); 40788364387SHung-ying Tyan } 40888364387SHung-ying Tyan 40988364387SHung-ying Tyan return 0; 41088364387SHung-ying Tyan } 41188364387SHung-ying Tyan 41288364387SHung-ying Tyan int cros_ec_interrupt_pending(struct cros_ec_dev *dev) 41388364387SHung-ying Tyan { 41488364387SHung-ying Tyan /* no interrupt support : always poll */ 41588364387SHung-ying Tyan if (!fdt_gpio_isvalid(&dev->ec_int)) 41688364387SHung-ying Tyan return 1; 41788364387SHung-ying Tyan 41888364387SHung-ying Tyan return !gpio_get_value(dev->ec_int.gpio); 41988364387SHung-ying Tyan } 42088364387SHung-ying Tyan 42188364387SHung-ying Tyan int cros_ec_info(struct cros_ec_dev *dev, struct ec_response_cros_ec_info *info) 42288364387SHung-ying Tyan { 42388364387SHung-ying Tyan if (ec_command(dev, EC_CMD_CROS_EC_INFO, 0, NULL, 0, info, 42488364387SHung-ying Tyan sizeof(*info)) < sizeof(*info)) 42588364387SHung-ying Tyan return -1; 42688364387SHung-ying Tyan 42788364387SHung-ying Tyan return 0; 42888364387SHung-ying Tyan } 42988364387SHung-ying Tyan 43088364387SHung-ying Tyan int cros_ec_get_host_events(struct cros_ec_dev *dev, uint32_t *events_ptr) 43188364387SHung-ying Tyan { 43288364387SHung-ying Tyan struct ec_response_host_event_mask *resp; 43388364387SHung-ying Tyan 43488364387SHung-ying Tyan /* 43588364387SHung-ying Tyan * Use the B copy of the event flags, because the main copy is already 43688364387SHung-ying Tyan * used by ACPI/SMI. 43788364387SHung-ying Tyan */ 43888364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_HOST_EVENT_GET_B, 0, NULL, 0, 43988364387SHung-ying Tyan (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) 44088364387SHung-ying Tyan return -1; 44188364387SHung-ying Tyan 44288364387SHung-ying Tyan if (resp->mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_INVALID)) 44388364387SHung-ying Tyan return -1; 44488364387SHung-ying Tyan 44588364387SHung-ying Tyan *events_ptr = resp->mask; 44688364387SHung-ying Tyan return 0; 44788364387SHung-ying Tyan } 44888364387SHung-ying Tyan 44988364387SHung-ying Tyan int cros_ec_clear_host_events(struct cros_ec_dev *dev, uint32_t events) 45088364387SHung-ying Tyan { 45188364387SHung-ying Tyan struct ec_params_host_event_mask params; 45288364387SHung-ying Tyan 45388364387SHung-ying Tyan params.mask = events; 45488364387SHung-ying Tyan 45588364387SHung-ying Tyan /* 45688364387SHung-ying Tyan * Use the B copy of the event flags, so it affects the data returned 45788364387SHung-ying Tyan * by cros_ec_get_host_events(). 45888364387SHung-ying Tyan */ 45988364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_HOST_EVENT_CLEAR_B, 0, 46088364387SHung-ying Tyan ¶ms, sizeof(params), NULL, 0) < 0) 46188364387SHung-ying Tyan return -1; 46288364387SHung-ying Tyan 46388364387SHung-ying Tyan return 0; 46488364387SHung-ying Tyan } 46588364387SHung-ying Tyan 46688364387SHung-ying Tyan int cros_ec_flash_protect(struct cros_ec_dev *dev, 46788364387SHung-ying Tyan uint32_t set_mask, uint32_t set_flags, 46888364387SHung-ying Tyan struct ec_response_flash_protect *resp) 46988364387SHung-ying Tyan { 47088364387SHung-ying Tyan struct ec_params_flash_protect params; 47188364387SHung-ying Tyan 47288364387SHung-ying Tyan params.mask = set_mask; 47388364387SHung-ying Tyan params.flags = set_flags; 47488364387SHung-ying Tyan 47588364387SHung-ying Tyan if (ec_command(dev, EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT, 47688364387SHung-ying Tyan ¶ms, sizeof(params), 47788364387SHung-ying Tyan resp, sizeof(*resp)) < sizeof(*resp)) 47888364387SHung-ying Tyan return -1; 47988364387SHung-ying Tyan 48088364387SHung-ying Tyan return 0; 48188364387SHung-ying Tyan } 48288364387SHung-ying Tyan 48388364387SHung-ying Tyan static int cros_ec_check_version(struct cros_ec_dev *dev) 48488364387SHung-ying Tyan { 48588364387SHung-ying Tyan struct ec_params_hello req; 48688364387SHung-ying Tyan struct ec_response_hello *resp; 48788364387SHung-ying Tyan 48888364387SHung-ying Tyan #ifdef CONFIG_CROS_EC_LPC 48988364387SHung-ying Tyan /* LPC has its own way of doing this */ 49088364387SHung-ying Tyan if (dev->interface == CROS_EC_IF_LPC) 49188364387SHung-ying Tyan return cros_ec_lpc_check_version(dev); 49288364387SHung-ying Tyan #endif 49388364387SHung-ying Tyan 49488364387SHung-ying Tyan /* 49588364387SHung-ying Tyan * TODO(sjg@chromium.org). 49688364387SHung-ying Tyan * There is a strange oddity here with the EC. We could just ignore 49788364387SHung-ying Tyan * the response, i.e. pass the last two parameters as NULL and 0. 49888364387SHung-ying Tyan * In this case we won't read back very many bytes from the EC. 49988364387SHung-ying Tyan * On the I2C bus the EC gets upset about this and will try to send 50088364387SHung-ying Tyan * the bytes anyway. This means that we will have to wait for that 50188364387SHung-ying Tyan * to complete before continuing with a new EC command. 50288364387SHung-ying Tyan * 50388364387SHung-ying Tyan * This problem is probably unique to the I2C bus. 50488364387SHung-ying Tyan * 50588364387SHung-ying Tyan * So for now, just read all the data anyway. 50688364387SHung-ying Tyan */ 50788364387SHung-ying Tyan dev->cmd_version_is_supported = 1; 50888364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), 50988364387SHung-ying Tyan (uint8_t **)&resp, sizeof(*resp)) > 0) { 51088364387SHung-ying Tyan /* It appears to understand new version commands */ 51188364387SHung-ying Tyan dev->cmd_version_is_supported = 1; 51288364387SHung-ying Tyan } else { 51388364387SHung-ying Tyan dev->cmd_version_is_supported = 0; 51488364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, 51588364387SHung-ying Tyan sizeof(req), (uint8_t **)&resp, 51688364387SHung-ying Tyan sizeof(*resp)) < 0) { 51788364387SHung-ying Tyan debug("%s: Failed both old and new command style\n", 51888364387SHung-ying Tyan __func__); 51988364387SHung-ying Tyan return -1; 52088364387SHung-ying Tyan } 52188364387SHung-ying Tyan } 52288364387SHung-ying Tyan 52388364387SHung-ying Tyan return 0; 52488364387SHung-ying Tyan } 52588364387SHung-ying Tyan 52688364387SHung-ying Tyan int cros_ec_test(struct cros_ec_dev *dev) 52788364387SHung-ying Tyan { 52888364387SHung-ying Tyan struct ec_params_hello req; 52988364387SHung-ying Tyan struct ec_response_hello *resp; 53088364387SHung-ying Tyan 53188364387SHung-ying Tyan req.in_data = 0x12345678; 53288364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), 53388364387SHung-ying Tyan (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) { 53488364387SHung-ying Tyan printf("ec_command_inptr() returned error\n"); 53588364387SHung-ying Tyan return -1; 53688364387SHung-ying Tyan } 53788364387SHung-ying Tyan if (resp->out_data != req.in_data + 0x01020304) { 53888364387SHung-ying Tyan printf("Received invalid handshake %x\n", resp->out_data); 53988364387SHung-ying Tyan return -1; 54088364387SHung-ying Tyan } 54188364387SHung-ying Tyan 54288364387SHung-ying Tyan return 0; 54388364387SHung-ying Tyan } 54488364387SHung-ying Tyan 54588364387SHung-ying Tyan int cros_ec_flash_offset(struct cros_ec_dev *dev, enum ec_flash_region region, 54688364387SHung-ying Tyan uint32_t *offset, uint32_t *size) 54788364387SHung-ying Tyan { 54888364387SHung-ying Tyan struct ec_params_flash_region_info p; 54988364387SHung-ying Tyan struct ec_response_flash_region_info *r; 55088364387SHung-ying Tyan int ret; 55188364387SHung-ying Tyan 55288364387SHung-ying Tyan p.region = region; 55388364387SHung-ying Tyan ret = ec_command_inptr(dev, EC_CMD_FLASH_REGION_INFO, 55488364387SHung-ying Tyan EC_VER_FLASH_REGION_INFO, 55588364387SHung-ying Tyan &p, sizeof(p), (uint8_t **)&r, sizeof(*r)); 55688364387SHung-ying Tyan if (ret != sizeof(*r)) 55788364387SHung-ying Tyan return -1; 55888364387SHung-ying Tyan 55988364387SHung-ying Tyan if (offset) 56088364387SHung-ying Tyan *offset = r->offset; 56188364387SHung-ying Tyan if (size) 56288364387SHung-ying Tyan *size = r->size; 56388364387SHung-ying Tyan 56488364387SHung-ying Tyan return 0; 56588364387SHung-ying Tyan } 56688364387SHung-ying Tyan 56788364387SHung-ying Tyan int cros_ec_flash_erase(struct cros_ec_dev *dev, uint32_t offset, uint32_t size) 56888364387SHung-ying Tyan { 56988364387SHung-ying Tyan struct ec_params_flash_erase p; 57088364387SHung-ying Tyan 57188364387SHung-ying Tyan p.offset = offset; 57288364387SHung-ying Tyan p.size = size; 57388364387SHung-ying Tyan return ec_command_inptr(dev, EC_CMD_FLASH_ERASE, 0, &p, sizeof(p), 57488364387SHung-ying Tyan NULL, 0); 57588364387SHung-ying Tyan } 57688364387SHung-ying Tyan 57788364387SHung-ying Tyan /** 57888364387SHung-ying Tyan * Write a single block to the flash 57988364387SHung-ying Tyan * 58088364387SHung-ying Tyan * Write a block of data to the EC flash. The size must not exceed the flash 58188364387SHung-ying Tyan * write block size which you can obtain from cros_ec_flash_write_burst_size(). 58288364387SHung-ying Tyan * 58388364387SHung-ying Tyan * The offset starts at 0. You can obtain the region information from 58488364387SHung-ying Tyan * cros_ec_flash_offset() to find out where to write for a particular region. 58588364387SHung-ying Tyan * 58688364387SHung-ying Tyan * Attempting to write to the region where the EC is currently running from 58788364387SHung-ying Tyan * will result in an error. 58888364387SHung-ying Tyan * 58988364387SHung-ying Tyan * @param dev CROS-EC device 59088364387SHung-ying Tyan * @param data Pointer to data buffer to write 59188364387SHung-ying Tyan * @param offset Offset within flash to write to. 59288364387SHung-ying Tyan * @param size Number of bytes to write 59388364387SHung-ying Tyan * @return 0 if ok, -1 on error 59488364387SHung-ying Tyan */ 59588364387SHung-ying Tyan static int cros_ec_flash_write_block(struct cros_ec_dev *dev, 59688364387SHung-ying Tyan const uint8_t *data, uint32_t offset, uint32_t size) 59788364387SHung-ying Tyan { 59888364387SHung-ying Tyan struct ec_params_flash_write p; 59988364387SHung-ying Tyan 60088364387SHung-ying Tyan p.offset = offset; 60188364387SHung-ying Tyan p.size = size; 60288364387SHung-ying Tyan assert(data && p.size <= sizeof(p.data)); 60388364387SHung-ying Tyan memcpy(p.data, data, p.size); 60488364387SHung-ying Tyan 60588364387SHung-ying Tyan return ec_command_inptr(dev, EC_CMD_FLASH_WRITE, 0, 60688364387SHung-ying Tyan &p, sizeof(p), NULL, 0) >= 0 ? 0 : -1; 60788364387SHung-ying Tyan } 60888364387SHung-ying Tyan 60988364387SHung-ying Tyan /** 61088364387SHung-ying Tyan * Return optimal flash write burst size 61188364387SHung-ying Tyan */ 61288364387SHung-ying Tyan static int cros_ec_flash_write_burst_size(struct cros_ec_dev *dev) 61388364387SHung-ying Tyan { 61488364387SHung-ying Tyan struct ec_params_flash_write p; 61588364387SHung-ying Tyan return sizeof(p.data); 61688364387SHung-ying Tyan } 61788364387SHung-ying Tyan 61888364387SHung-ying Tyan /** 61988364387SHung-ying Tyan * Check if a block of data is erased (all 0xff) 62088364387SHung-ying Tyan * 62188364387SHung-ying Tyan * This function is useful when dealing with flash, for checking whether a 62288364387SHung-ying Tyan * data block is erased and thus does not need to be programmed. 62388364387SHung-ying Tyan * 62488364387SHung-ying Tyan * @param data Pointer to data to check (must be word-aligned) 62588364387SHung-ying Tyan * @param size Number of bytes to check (must be word-aligned) 62688364387SHung-ying Tyan * @return 0 if erased, non-zero if any word is not erased 62788364387SHung-ying Tyan */ 62888364387SHung-ying Tyan static int cros_ec_data_is_erased(const uint32_t *data, int size) 62988364387SHung-ying Tyan { 63088364387SHung-ying Tyan assert(!(size & 3)); 63188364387SHung-ying Tyan size /= sizeof(uint32_t); 63288364387SHung-ying Tyan for (; size > 0; size -= 4, data++) 63388364387SHung-ying Tyan if (*data != -1U) 63488364387SHung-ying Tyan return 0; 63588364387SHung-ying Tyan 63688364387SHung-ying Tyan return 1; 63788364387SHung-ying Tyan } 63888364387SHung-ying Tyan 63988364387SHung-ying Tyan int cros_ec_flash_write(struct cros_ec_dev *dev, const uint8_t *data, 64088364387SHung-ying Tyan uint32_t offset, uint32_t size) 64188364387SHung-ying Tyan { 64288364387SHung-ying Tyan uint32_t burst = cros_ec_flash_write_burst_size(dev); 64388364387SHung-ying Tyan uint32_t end, off; 64488364387SHung-ying Tyan int ret; 64588364387SHung-ying Tyan 64688364387SHung-ying Tyan /* 64788364387SHung-ying Tyan * TODO: round up to the nearest multiple of write size. Can get away 64888364387SHung-ying Tyan * without that on link right now because its write size is 4 bytes. 64988364387SHung-ying Tyan */ 65088364387SHung-ying Tyan end = offset + size; 65188364387SHung-ying Tyan for (off = offset; off < end; off += burst, data += burst) { 65288364387SHung-ying Tyan uint32_t todo; 65388364387SHung-ying Tyan 65488364387SHung-ying Tyan /* If the data is empty, there is no point in programming it */ 65588364387SHung-ying Tyan todo = min(end - off, burst); 65688364387SHung-ying Tyan if (dev->optimise_flash_write && 65788364387SHung-ying Tyan cros_ec_data_is_erased((uint32_t *)data, todo)) 65888364387SHung-ying Tyan continue; 65988364387SHung-ying Tyan 66088364387SHung-ying Tyan ret = cros_ec_flash_write_block(dev, data, off, todo); 66188364387SHung-ying Tyan if (ret) 66288364387SHung-ying Tyan return ret; 66388364387SHung-ying Tyan } 66488364387SHung-ying Tyan 66588364387SHung-ying Tyan return 0; 66688364387SHung-ying Tyan } 66788364387SHung-ying Tyan 66888364387SHung-ying Tyan /** 66988364387SHung-ying Tyan * Read a single block from the flash 67088364387SHung-ying Tyan * 67188364387SHung-ying Tyan * Read a block of data from the EC flash. The size must not exceed the flash 67288364387SHung-ying Tyan * write block size which you can obtain from cros_ec_flash_write_burst_size(). 67388364387SHung-ying Tyan * 67488364387SHung-ying Tyan * The offset starts at 0. You can obtain the region information from 67588364387SHung-ying Tyan * cros_ec_flash_offset() to find out where to read for a particular region. 67688364387SHung-ying Tyan * 67788364387SHung-ying Tyan * @param dev CROS-EC device 67888364387SHung-ying Tyan * @param data Pointer to data buffer to read into 67988364387SHung-ying Tyan * @param offset Offset within flash to read from 68088364387SHung-ying Tyan * @param size Number of bytes to read 68188364387SHung-ying Tyan * @return 0 if ok, -1 on error 68288364387SHung-ying Tyan */ 68388364387SHung-ying Tyan static int cros_ec_flash_read_block(struct cros_ec_dev *dev, uint8_t *data, 68488364387SHung-ying Tyan uint32_t offset, uint32_t size) 68588364387SHung-ying Tyan { 68688364387SHung-ying Tyan struct ec_params_flash_read p; 68788364387SHung-ying Tyan 68888364387SHung-ying Tyan p.offset = offset; 68988364387SHung-ying Tyan p.size = size; 69088364387SHung-ying Tyan 69188364387SHung-ying Tyan return ec_command(dev, EC_CMD_FLASH_READ, 0, 69288364387SHung-ying Tyan &p, sizeof(p), data, size) >= 0 ? 0 : -1; 69388364387SHung-ying Tyan } 69488364387SHung-ying Tyan 69588364387SHung-ying Tyan int cros_ec_flash_read(struct cros_ec_dev *dev, uint8_t *data, uint32_t offset, 69688364387SHung-ying Tyan uint32_t size) 69788364387SHung-ying Tyan { 69888364387SHung-ying Tyan uint32_t burst = cros_ec_flash_write_burst_size(dev); 69988364387SHung-ying Tyan uint32_t end, off; 70088364387SHung-ying Tyan int ret; 70188364387SHung-ying Tyan 70288364387SHung-ying Tyan end = offset + size; 70388364387SHung-ying Tyan for (off = offset; off < end; off += burst, data += burst) { 70488364387SHung-ying Tyan ret = cros_ec_flash_read_block(dev, data, off, 70588364387SHung-ying Tyan min(end - off, burst)); 70688364387SHung-ying Tyan if (ret) 70788364387SHung-ying Tyan return ret; 70888364387SHung-ying Tyan } 70988364387SHung-ying Tyan 71088364387SHung-ying Tyan return 0; 71188364387SHung-ying Tyan } 71288364387SHung-ying Tyan 71388364387SHung-ying Tyan int cros_ec_flash_update_rw(struct cros_ec_dev *dev, 71488364387SHung-ying Tyan const uint8_t *image, int image_size) 71588364387SHung-ying Tyan { 71688364387SHung-ying Tyan uint32_t rw_offset, rw_size; 71788364387SHung-ying Tyan int ret; 71888364387SHung-ying Tyan 71988364387SHung-ying Tyan if (cros_ec_flash_offset(dev, EC_FLASH_REGION_RW, &rw_offset, &rw_size)) 72088364387SHung-ying Tyan return -1; 72188364387SHung-ying Tyan if (image_size > rw_size) 72288364387SHung-ying Tyan return -1; 72388364387SHung-ying Tyan 72488364387SHung-ying Tyan /* Invalidate the existing hash, just in case the AP reboots 72588364387SHung-ying Tyan * unexpectedly during the update. If that happened, the EC RW firmware 72688364387SHung-ying Tyan * would be invalid, but the EC would still have the original hash. 72788364387SHung-ying Tyan */ 72888364387SHung-ying Tyan ret = cros_ec_invalidate_hash(dev); 72988364387SHung-ying Tyan if (ret) 73088364387SHung-ying Tyan return ret; 73188364387SHung-ying Tyan 73288364387SHung-ying Tyan /* 73388364387SHung-ying Tyan * Erase the entire RW section, so that the EC doesn't see any garbage 73488364387SHung-ying Tyan * past the new image if it's smaller than the current image. 73588364387SHung-ying Tyan * 73688364387SHung-ying Tyan * TODO: could optimize this to erase just the current image, since 73788364387SHung-ying Tyan * presumably everything past that is 0xff's. But would still need to 73888364387SHung-ying Tyan * round up to the nearest multiple of erase size. 73988364387SHung-ying Tyan */ 74088364387SHung-ying Tyan ret = cros_ec_flash_erase(dev, rw_offset, rw_size); 74188364387SHung-ying Tyan if (ret) 74288364387SHung-ying Tyan return ret; 74388364387SHung-ying Tyan 74488364387SHung-ying Tyan /* Write the image */ 74588364387SHung-ying Tyan ret = cros_ec_flash_write(dev, image, rw_offset, image_size); 74688364387SHung-ying Tyan if (ret) 74788364387SHung-ying Tyan return ret; 74888364387SHung-ying Tyan 74988364387SHung-ying Tyan return 0; 75088364387SHung-ying Tyan } 75188364387SHung-ying Tyan 75288364387SHung-ying Tyan int cros_ec_read_vbnvcontext(struct cros_ec_dev *dev, uint8_t *block) 75388364387SHung-ying Tyan { 75488364387SHung-ying Tyan struct ec_params_vbnvcontext p; 75588364387SHung-ying Tyan int len; 75688364387SHung-ying Tyan 75788364387SHung-ying Tyan p.op = EC_VBNV_CONTEXT_OP_READ; 75888364387SHung-ying Tyan 75988364387SHung-ying Tyan len = ec_command(dev, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT, 76088364387SHung-ying Tyan &p, sizeof(p), block, EC_VBNV_BLOCK_SIZE); 76188364387SHung-ying Tyan if (len < EC_VBNV_BLOCK_SIZE) 76288364387SHung-ying Tyan return -1; 76388364387SHung-ying Tyan 76488364387SHung-ying Tyan return 0; 76588364387SHung-ying Tyan } 76688364387SHung-ying Tyan 76788364387SHung-ying Tyan int cros_ec_write_vbnvcontext(struct cros_ec_dev *dev, const uint8_t *block) 76888364387SHung-ying Tyan { 76988364387SHung-ying Tyan struct ec_params_vbnvcontext p; 77088364387SHung-ying Tyan int len; 77188364387SHung-ying Tyan 77288364387SHung-ying Tyan p.op = EC_VBNV_CONTEXT_OP_WRITE; 77388364387SHung-ying Tyan memcpy(p.block, block, sizeof(p.block)); 77488364387SHung-ying Tyan 77588364387SHung-ying Tyan len = ec_command_inptr(dev, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT, 77688364387SHung-ying Tyan &p, sizeof(p), NULL, 0); 77788364387SHung-ying Tyan if (len < 0) 77888364387SHung-ying Tyan return -1; 77988364387SHung-ying Tyan 78088364387SHung-ying Tyan return 0; 78188364387SHung-ying Tyan } 78288364387SHung-ying Tyan 78388364387SHung-ying Tyan int cros_ec_set_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t state) 78488364387SHung-ying Tyan { 78588364387SHung-ying Tyan struct ec_params_ldo_set params; 78688364387SHung-ying Tyan 78788364387SHung-ying Tyan params.index = index; 78888364387SHung-ying Tyan params.state = state; 78988364387SHung-ying Tyan 79088364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_LDO_SET, 0, 79188364387SHung-ying Tyan ¶ms, sizeof(params), 79288364387SHung-ying Tyan NULL, 0)) 79388364387SHung-ying Tyan return -1; 79488364387SHung-ying Tyan 79588364387SHung-ying Tyan return 0; 79688364387SHung-ying Tyan } 79788364387SHung-ying Tyan 79888364387SHung-ying Tyan int cros_ec_get_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t *state) 79988364387SHung-ying Tyan { 80088364387SHung-ying Tyan struct ec_params_ldo_get params; 80188364387SHung-ying Tyan struct ec_response_ldo_get *resp; 80288364387SHung-ying Tyan 80388364387SHung-ying Tyan params.index = index; 80488364387SHung-ying Tyan 80588364387SHung-ying Tyan if (ec_command_inptr(dev, EC_CMD_LDO_GET, 0, 80688364387SHung-ying Tyan ¶ms, sizeof(params), 80788364387SHung-ying Tyan (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) 80888364387SHung-ying Tyan return -1; 80988364387SHung-ying Tyan 81088364387SHung-ying Tyan *state = resp->state; 81188364387SHung-ying Tyan 81288364387SHung-ying Tyan return 0; 81388364387SHung-ying Tyan } 81488364387SHung-ying Tyan 81588364387SHung-ying Tyan /** 81688364387SHung-ying Tyan * Decode MBKP details from the device tree and allocate a suitable device. 81788364387SHung-ying Tyan * 81888364387SHung-ying Tyan * @param blob Device tree blob 81988364387SHung-ying Tyan * @param node Node to decode from 82088364387SHung-ying Tyan * @param devp Returns a pointer to the new allocated device 82188364387SHung-ying Tyan * @return 0 if ok, -1 on error 82288364387SHung-ying Tyan */ 82388364387SHung-ying Tyan static int cros_ec_decode_fdt(const void *blob, int node, 82488364387SHung-ying Tyan struct cros_ec_dev **devp) 82588364387SHung-ying Tyan { 82688364387SHung-ying Tyan enum fdt_compat_id compat; 82788364387SHung-ying Tyan struct cros_ec_dev *dev; 82888364387SHung-ying Tyan int parent; 82988364387SHung-ying Tyan 83088364387SHung-ying Tyan /* See what type of parent we are inside (this is expensive) */ 83188364387SHung-ying Tyan parent = fdt_parent_offset(blob, node); 83288364387SHung-ying Tyan if (parent < 0) { 83388364387SHung-ying Tyan debug("%s: Cannot find node parent\n", __func__); 83488364387SHung-ying Tyan return -1; 83588364387SHung-ying Tyan } 83688364387SHung-ying Tyan 83788364387SHung-ying Tyan dev = &static_dev; 83888364387SHung-ying Tyan dev->node = node; 83988364387SHung-ying Tyan dev->parent_node = parent; 84088364387SHung-ying Tyan 84188364387SHung-ying Tyan compat = fdtdec_lookup(blob, parent); 84288364387SHung-ying Tyan switch (compat) { 84388364387SHung-ying Tyan #ifdef CONFIG_CROS_EC_SPI 84488364387SHung-ying Tyan case COMPAT_SAMSUNG_EXYNOS_SPI: 84588364387SHung-ying Tyan dev->interface = CROS_EC_IF_SPI; 84688364387SHung-ying Tyan if (cros_ec_spi_decode_fdt(dev, blob)) 84788364387SHung-ying Tyan return -1; 84888364387SHung-ying Tyan break; 84988364387SHung-ying Tyan #endif 85088364387SHung-ying Tyan #ifdef CONFIG_CROS_EC_I2C 85188364387SHung-ying Tyan case COMPAT_SAMSUNG_S3C2440_I2C: 85288364387SHung-ying Tyan dev->interface = CROS_EC_IF_I2C; 85388364387SHung-ying Tyan if (cros_ec_i2c_decode_fdt(dev, blob)) 85488364387SHung-ying Tyan return -1; 85588364387SHung-ying Tyan break; 85688364387SHung-ying Tyan #endif 85788364387SHung-ying Tyan #ifdef CONFIG_CROS_EC_LPC 85888364387SHung-ying Tyan case COMPAT_INTEL_LPC: 85988364387SHung-ying Tyan dev->interface = CROS_EC_IF_LPC; 86088364387SHung-ying Tyan break; 86188364387SHung-ying Tyan #endif 86288364387SHung-ying Tyan default: 86388364387SHung-ying Tyan debug("%s: Unknown compat id %d\n", __func__, compat); 86488364387SHung-ying Tyan return -1; 86588364387SHung-ying Tyan } 86688364387SHung-ying Tyan 86788364387SHung-ying Tyan fdtdec_decode_gpio(blob, node, "ec-interrupt", &dev->ec_int); 86888364387SHung-ying Tyan dev->optimise_flash_write = fdtdec_get_bool(blob, node, 86988364387SHung-ying Tyan "optimise-flash-write"); 87088364387SHung-ying Tyan *devp = dev; 87188364387SHung-ying Tyan 87288364387SHung-ying Tyan return 0; 87388364387SHung-ying Tyan } 87488364387SHung-ying Tyan 87588364387SHung-ying Tyan int cros_ec_init(const void *blob, struct cros_ec_dev **cros_ecp) 87688364387SHung-ying Tyan { 87788364387SHung-ying Tyan char id[MSG_BYTES]; 87888364387SHung-ying Tyan struct cros_ec_dev *dev; 87988364387SHung-ying Tyan int node = 0; 88088364387SHung-ying Tyan 88188364387SHung-ying Tyan *cros_ecp = NULL; 88288364387SHung-ying Tyan do { 88388364387SHung-ying Tyan node = fdtdec_next_compatible(blob, node, 88488364387SHung-ying Tyan COMPAT_GOOGLE_CROS_EC); 88588364387SHung-ying Tyan if (node < 0) { 88688364387SHung-ying Tyan debug("%s: Node not found\n", __func__); 88788364387SHung-ying Tyan return 0; 88888364387SHung-ying Tyan } 88988364387SHung-ying Tyan } while (!fdtdec_get_is_enabled(blob, node)); 89088364387SHung-ying Tyan 89188364387SHung-ying Tyan if (cros_ec_decode_fdt(blob, node, &dev)) { 89288364387SHung-ying Tyan debug("%s: Failed to decode device.\n", __func__); 89388364387SHung-ying Tyan return -CROS_EC_ERR_FDT_DECODE; 89488364387SHung-ying Tyan } 89588364387SHung-ying Tyan 89688364387SHung-ying Tyan switch (dev->interface) { 89788364387SHung-ying Tyan #ifdef CONFIG_CROS_EC_SPI 89888364387SHung-ying Tyan case CROS_EC_IF_SPI: 89988364387SHung-ying Tyan if (cros_ec_spi_init(dev, blob)) { 90088364387SHung-ying Tyan debug("%s: Could not setup SPI interface\n", __func__); 90188364387SHung-ying Tyan return -CROS_EC_ERR_DEV_INIT; 90288364387SHung-ying Tyan } 90388364387SHung-ying Tyan break; 90488364387SHung-ying Tyan #endif 90588364387SHung-ying Tyan #ifdef CONFIG_CROS_EC_I2C 90688364387SHung-ying Tyan case CROS_EC_IF_I2C: 90788364387SHung-ying Tyan if (cros_ec_i2c_init(dev, blob)) 90888364387SHung-ying Tyan return -CROS_EC_ERR_DEV_INIT; 90988364387SHung-ying Tyan break; 91088364387SHung-ying Tyan #endif 91188364387SHung-ying Tyan #ifdef CONFIG_CROS_EC_LPC 91288364387SHung-ying Tyan case CROS_EC_IF_LPC: 91388364387SHung-ying Tyan if (cros_ec_lpc_init(dev, blob)) 91488364387SHung-ying Tyan return -CROS_EC_ERR_DEV_INIT; 91588364387SHung-ying Tyan break; 91688364387SHung-ying Tyan #endif 91788364387SHung-ying Tyan case CROS_EC_IF_NONE: 91888364387SHung-ying Tyan default: 91988364387SHung-ying Tyan return 0; 92088364387SHung-ying Tyan } 92188364387SHung-ying Tyan 92288364387SHung-ying Tyan /* we will poll the EC interrupt line */ 92388364387SHung-ying Tyan fdtdec_setup_gpio(&dev->ec_int); 92488364387SHung-ying Tyan if (fdt_gpio_isvalid(&dev->ec_int)) 92588364387SHung-ying Tyan gpio_direction_input(dev->ec_int.gpio); 92688364387SHung-ying Tyan 92788364387SHung-ying Tyan if (cros_ec_check_version(dev)) { 92888364387SHung-ying Tyan debug("%s: Could not detect CROS-EC version\n", __func__); 92988364387SHung-ying Tyan return -CROS_EC_ERR_CHECK_VERSION; 93088364387SHung-ying Tyan } 93188364387SHung-ying Tyan 93288364387SHung-ying Tyan if (cros_ec_read_id(dev, id, sizeof(id))) { 93388364387SHung-ying Tyan debug("%s: Could not read KBC ID\n", __func__); 93488364387SHung-ying Tyan return -CROS_EC_ERR_READ_ID; 93588364387SHung-ying Tyan } 93688364387SHung-ying Tyan 93788364387SHung-ying Tyan /* Remember this device for use by the cros_ec command */ 93888364387SHung-ying Tyan last_dev = *cros_ecp = dev; 93988364387SHung-ying Tyan debug("Google Chrome EC CROS-EC driver ready, id '%s'\n", id); 94088364387SHung-ying Tyan 94188364387SHung-ying Tyan return 0; 94288364387SHung-ying Tyan } 94388364387SHung-ying Tyan 94488364387SHung-ying Tyan #ifdef CONFIG_CMD_CROS_EC 94588364387SHung-ying Tyan int cros_ec_decode_region(int argc, char * const argv[]) 94688364387SHung-ying Tyan { 94788364387SHung-ying Tyan if (argc > 0) { 94888364387SHung-ying Tyan if (0 == strcmp(*argv, "rw")) 94988364387SHung-ying Tyan return EC_FLASH_REGION_RW; 95088364387SHung-ying Tyan else if (0 == strcmp(*argv, "ro")) 95188364387SHung-ying Tyan return EC_FLASH_REGION_RO; 95288364387SHung-ying Tyan 95388364387SHung-ying Tyan debug("%s: Invalid region '%s'\n", __func__, *argv); 95488364387SHung-ying Tyan } else { 95588364387SHung-ying Tyan debug("%s: Missing region parameter\n", __func__); 95688364387SHung-ying Tyan } 95788364387SHung-ying Tyan 95888364387SHung-ying Tyan return -1; 95988364387SHung-ying Tyan } 96088364387SHung-ying Tyan 961*d7f25f35SSimon Glass int cros_ec_decode_ec_flash(const void *blob, struct fdt_cros_ec *config) 962*d7f25f35SSimon Glass { 963*d7f25f35SSimon Glass int flash_node, node; 964*d7f25f35SSimon Glass 965*d7f25f35SSimon Glass node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_CROS_EC); 966*d7f25f35SSimon Glass if (node < 0) { 967*d7f25f35SSimon Glass debug("Failed to find chrome-ec node'\n"); 968*d7f25f35SSimon Glass return -1; 969*d7f25f35SSimon Glass } 970*d7f25f35SSimon Glass 971*d7f25f35SSimon Glass flash_node = fdt_subnode_offset(blob, node, "flash"); 972*d7f25f35SSimon Glass if (flash_node < 0) { 973*d7f25f35SSimon Glass debug("Failed to find flash node\n"); 974*d7f25f35SSimon Glass return -1; 975*d7f25f35SSimon Glass } 976*d7f25f35SSimon Glass 977*d7f25f35SSimon Glass if (fdtdec_read_fmap_entry(blob, flash_node, "flash", 978*d7f25f35SSimon Glass &config->flash)) { 979*d7f25f35SSimon Glass debug("Failed to decode flash node in chrome-ec'\n"); 980*d7f25f35SSimon Glass return -1; 981*d7f25f35SSimon Glass } 982*d7f25f35SSimon Glass 983*d7f25f35SSimon Glass config->flash_erase_value = fdtdec_get_int(blob, flash_node, 984*d7f25f35SSimon Glass "erase-value", -1); 985*d7f25f35SSimon Glass for (node = fdt_first_subnode(blob, flash_node); node >= 0; 986*d7f25f35SSimon Glass node = fdt_next_subnode(blob, node)) { 987*d7f25f35SSimon Glass const char *name = fdt_get_name(blob, node, NULL); 988*d7f25f35SSimon Glass enum ec_flash_region region; 989*d7f25f35SSimon Glass 990*d7f25f35SSimon Glass if (0 == strcmp(name, "ro")) { 991*d7f25f35SSimon Glass region = EC_FLASH_REGION_RO; 992*d7f25f35SSimon Glass } else if (0 == strcmp(name, "rw")) { 993*d7f25f35SSimon Glass region = EC_FLASH_REGION_RW; 994*d7f25f35SSimon Glass } else if (0 == strcmp(name, "wp-ro")) { 995*d7f25f35SSimon Glass region = EC_FLASH_REGION_WP_RO; 996*d7f25f35SSimon Glass } else { 997*d7f25f35SSimon Glass debug("Unknown EC flash region name '%s'\n", name); 998*d7f25f35SSimon Glass return -1; 999*d7f25f35SSimon Glass } 1000*d7f25f35SSimon Glass 1001*d7f25f35SSimon Glass if (fdtdec_read_fmap_entry(blob, node, "reg", 1002*d7f25f35SSimon Glass &config->region[region])) { 1003*d7f25f35SSimon Glass debug("Failed to decode flash region in chrome-ec'\n"); 1004*d7f25f35SSimon Glass return -1; 1005*d7f25f35SSimon Glass } 1006*d7f25f35SSimon Glass } 1007*d7f25f35SSimon Glass 1008*d7f25f35SSimon Glass return 0; 1009*d7f25f35SSimon Glass } 1010*d7f25f35SSimon Glass 101188364387SHung-ying Tyan /** 101288364387SHung-ying Tyan * Perform a flash read or write command 101388364387SHung-ying Tyan * 101488364387SHung-ying Tyan * @param dev CROS-EC device to read/write 101588364387SHung-ying Tyan * @param is_write 1 do to a write, 0 to do a read 101688364387SHung-ying Tyan * @param argc Number of arguments 101788364387SHung-ying Tyan * @param argv Arguments (2 is region, 3 is address) 101888364387SHung-ying Tyan * @return 0 for ok, 1 for a usage error or -ve for ec command error 101988364387SHung-ying Tyan * (negative EC_RES_...) 102088364387SHung-ying Tyan */ 102188364387SHung-ying Tyan static int do_read_write(struct cros_ec_dev *dev, int is_write, int argc, 102288364387SHung-ying Tyan char * const argv[]) 102388364387SHung-ying Tyan { 102488364387SHung-ying Tyan uint32_t offset, size = -1U, region_size; 102588364387SHung-ying Tyan unsigned long addr; 102688364387SHung-ying Tyan char *endp; 102788364387SHung-ying Tyan int region; 102888364387SHung-ying Tyan int ret; 102988364387SHung-ying Tyan 103088364387SHung-ying Tyan region = cros_ec_decode_region(argc - 2, argv + 2); 103188364387SHung-ying Tyan if (region == -1) 103288364387SHung-ying Tyan return 1; 103388364387SHung-ying Tyan if (argc < 4) 103488364387SHung-ying Tyan return 1; 103588364387SHung-ying Tyan addr = simple_strtoul(argv[3], &endp, 16); 103688364387SHung-ying Tyan if (*argv[3] == 0 || *endp != 0) 103788364387SHung-ying Tyan return 1; 103888364387SHung-ying Tyan if (argc > 4) { 103988364387SHung-ying Tyan size = simple_strtoul(argv[4], &endp, 16); 104088364387SHung-ying Tyan if (*argv[4] == 0 || *endp != 0) 104188364387SHung-ying Tyan return 1; 104288364387SHung-ying Tyan } 104388364387SHung-ying Tyan 104488364387SHung-ying Tyan ret = cros_ec_flash_offset(dev, region, &offset, ®ion_size); 104588364387SHung-ying Tyan if (ret) { 104688364387SHung-ying Tyan debug("%s: Could not read region info\n", __func__); 104788364387SHung-ying Tyan return ret; 104888364387SHung-ying Tyan } 104988364387SHung-ying Tyan if (size == -1U) 105088364387SHung-ying Tyan size = region_size; 105188364387SHung-ying Tyan 105288364387SHung-ying Tyan ret = is_write ? 105388364387SHung-ying Tyan cros_ec_flash_write(dev, (uint8_t *)addr, offset, size) : 105488364387SHung-ying Tyan cros_ec_flash_read(dev, (uint8_t *)addr, offset, size); 105588364387SHung-ying Tyan if (ret) { 105688364387SHung-ying Tyan debug("%s: Could not %s region\n", __func__, 105788364387SHung-ying Tyan is_write ? "write" : "read"); 105888364387SHung-ying Tyan return ret; 105988364387SHung-ying Tyan } 106088364387SHung-ying Tyan 106188364387SHung-ying Tyan return 0; 106288364387SHung-ying Tyan } 106388364387SHung-ying Tyan 106488364387SHung-ying Tyan static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 106588364387SHung-ying Tyan { 106688364387SHung-ying Tyan struct cros_ec_dev *dev = last_dev; 106788364387SHung-ying Tyan const char *cmd; 106888364387SHung-ying Tyan int ret = 0; 106988364387SHung-ying Tyan 107088364387SHung-ying Tyan if (argc < 2) 107188364387SHung-ying Tyan return CMD_RET_USAGE; 107288364387SHung-ying Tyan 107388364387SHung-ying Tyan cmd = argv[1]; 107488364387SHung-ying Tyan if (0 == strcmp("init", cmd)) { 107588364387SHung-ying Tyan ret = cros_ec_init(gd->fdt_blob, &dev); 107688364387SHung-ying Tyan if (ret) { 107788364387SHung-ying Tyan printf("Could not init cros_ec device (err %d)\n", ret); 107888364387SHung-ying Tyan return 1; 107988364387SHung-ying Tyan } 108088364387SHung-ying Tyan return 0; 108188364387SHung-ying Tyan } 108288364387SHung-ying Tyan 108388364387SHung-ying Tyan /* Just use the last allocated device; there should be only one */ 108488364387SHung-ying Tyan if (!last_dev) { 108588364387SHung-ying Tyan printf("No CROS-EC device available\n"); 108688364387SHung-ying Tyan return 1; 108788364387SHung-ying Tyan } 108888364387SHung-ying Tyan if (0 == strcmp("id", cmd)) { 108988364387SHung-ying Tyan char id[MSG_BYTES]; 109088364387SHung-ying Tyan 109188364387SHung-ying Tyan if (cros_ec_read_id(dev, id, sizeof(id))) { 109288364387SHung-ying Tyan debug("%s: Could not read KBC ID\n", __func__); 109388364387SHung-ying Tyan return 1; 109488364387SHung-ying Tyan } 109588364387SHung-ying Tyan printf("%s\n", id); 109688364387SHung-ying Tyan } else if (0 == strcmp("info", cmd)) { 109788364387SHung-ying Tyan struct ec_response_cros_ec_info info; 109888364387SHung-ying Tyan 109988364387SHung-ying Tyan if (cros_ec_info(dev, &info)) { 110088364387SHung-ying Tyan debug("%s: Could not read KBC info\n", __func__); 110188364387SHung-ying Tyan return 1; 110288364387SHung-ying Tyan } 110388364387SHung-ying Tyan printf("rows = %u\n", info.rows); 110488364387SHung-ying Tyan printf("cols = %u\n", info.cols); 110588364387SHung-ying Tyan printf("switches = %#x\n", info.switches); 110688364387SHung-ying Tyan } else if (0 == strcmp("curimage", cmd)) { 110788364387SHung-ying Tyan enum ec_current_image image; 110888364387SHung-ying Tyan 110988364387SHung-ying Tyan if (cros_ec_read_current_image(dev, &image)) { 111088364387SHung-ying Tyan debug("%s: Could not read KBC image\n", __func__); 111188364387SHung-ying Tyan return 1; 111288364387SHung-ying Tyan } 111388364387SHung-ying Tyan printf("%d\n", image); 111488364387SHung-ying Tyan } else if (0 == strcmp("hash", cmd)) { 111588364387SHung-ying Tyan struct ec_response_vboot_hash hash; 111688364387SHung-ying Tyan int i; 111788364387SHung-ying Tyan 111888364387SHung-ying Tyan if (cros_ec_read_hash(dev, &hash)) { 111988364387SHung-ying Tyan debug("%s: Could not read KBC hash\n", __func__); 112088364387SHung-ying Tyan return 1; 112188364387SHung-ying Tyan } 112288364387SHung-ying Tyan 112388364387SHung-ying Tyan if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256) 112488364387SHung-ying Tyan printf("type: SHA-256\n"); 112588364387SHung-ying Tyan else 112688364387SHung-ying Tyan printf("type: %d\n", hash.hash_type); 112788364387SHung-ying Tyan 112888364387SHung-ying Tyan printf("offset: 0x%08x\n", hash.offset); 112988364387SHung-ying Tyan printf("size: 0x%08x\n", hash.size); 113088364387SHung-ying Tyan 113188364387SHung-ying Tyan printf("digest: "); 113288364387SHung-ying Tyan for (i = 0; i < hash.digest_size; i++) 113388364387SHung-ying Tyan printf("%02x", hash.hash_digest[i]); 113488364387SHung-ying Tyan printf("\n"); 113588364387SHung-ying Tyan } else if (0 == strcmp("reboot", cmd)) { 113688364387SHung-ying Tyan int region; 113788364387SHung-ying Tyan enum ec_reboot_cmd cmd; 113888364387SHung-ying Tyan 113988364387SHung-ying Tyan if (argc >= 3 && !strcmp(argv[2], "cold")) 114088364387SHung-ying Tyan cmd = EC_REBOOT_COLD; 114188364387SHung-ying Tyan else { 114288364387SHung-ying Tyan region = cros_ec_decode_region(argc - 2, argv + 2); 114388364387SHung-ying Tyan if (region == EC_FLASH_REGION_RO) 114488364387SHung-ying Tyan cmd = EC_REBOOT_JUMP_RO; 114588364387SHung-ying Tyan else if (region == EC_FLASH_REGION_RW) 114688364387SHung-ying Tyan cmd = EC_REBOOT_JUMP_RW; 114788364387SHung-ying Tyan else 114888364387SHung-ying Tyan return CMD_RET_USAGE; 114988364387SHung-ying Tyan } 115088364387SHung-ying Tyan 115188364387SHung-ying Tyan if (cros_ec_reboot(dev, cmd, 0)) { 115288364387SHung-ying Tyan debug("%s: Could not reboot KBC\n", __func__); 115388364387SHung-ying Tyan return 1; 115488364387SHung-ying Tyan } 115588364387SHung-ying Tyan } else if (0 == strcmp("events", cmd)) { 115688364387SHung-ying Tyan uint32_t events; 115788364387SHung-ying Tyan 115888364387SHung-ying Tyan if (cros_ec_get_host_events(dev, &events)) { 115988364387SHung-ying Tyan debug("%s: Could not read host events\n", __func__); 116088364387SHung-ying Tyan return 1; 116188364387SHung-ying Tyan } 116288364387SHung-ying Tyan printf("0x%08x\n", events); 116388364387SHung-ying Tyan } else if (0 == strcmp("clrevents", cmd)) { 116488364387SHung-ying Tyan uint32_t events = 0x7fffffff; 116588364387SHung-ying Tyan 116688364387SHung-ying Tyan if (argc >= 3) 116788364387SHung-ying Tyan events = simple_strtol(argv[2], NULL, 0); 116888364387SHung-ying Tyan 116988364387SHung-ying Tyan if (cros_ec_clear_host_events(dev, events)) { 117088364387SHung-ying Tyan debug("%s: Could not clear host events\n", __func__); 117188364387SHung-ying Tyan return 1; 117288364387SHung-ying Tyan } 117388364387SHung-ying Tyan } else if (0 == strcmp("read", cmd)) { 117488364387SHung-ying Tyan ret = do_read_write(dev, 0, argc, argv); 117588364387SHung-ying Tyan if (ret > 0) 117688364387SHung-ying Tyan return CMD_RET_USAGE; 117788364387SHung-ying Tyan } else if (0 == strcmp("write", cmd)) { 117888364387SHung-ying Tyan ret = do_read_write(dev, 1, argc, argv); 117988364387SHung-ying Tyan if (ret > 0) 118088364387SHung-ying Tyan return CMD_RET_USAGE; 118188364387SHung-ying Tyan } else if (0 == strcmp("erase", cmd)) { 118288364387SHung-ying Tyan int region = cros_ec_decode_region(argc - 2, argv + 2); 118388364387SHung-ying Tyan uint32_t offset, size; 118488364387SHung-ying Tyan 118588364387SHung-ying Tyan if (region == -1) 118688364387SHung-ying Tyan return CMD_RET_USAGE; 118788364387SHung-ying Tyan if (cros_ec_flash_offset(dev, region, &offset, &size)) { 118888364387SHung-ying Tyan debug("%s: Could not read region info\n", __func__); 118988364387SHung-ying Tyan ret = -1; 119088364387SHung-ying Tyan } else { 119188364387SHung-ying Tyan ret = cros_ec_flash_erase(dev, offset, size); 119288364387SHung-ying Tyan if (ret) { 119388364387SHung-ying Tyan debug("%s: Could not erase region\n", 119488364387SHung-ying Tyan __func__); 119588364387SHung-ying Tyan } 119688364387SHung-ying Tyan } 119788364387SHung-ying Tyan } else if (0 == strcmp("regioninfo", cmd)) { 119888364387SHung-ying Tyan int region = cros_ec_decode_region(argc - 2, argv + 2); 119988364387SHung-ying Tyan uint32_t offset, size; 120088364387SHung-ying Tyan 120188364387SHung-ying Tyan if (region == -1) 120288364387SHung-ying Tyan return CMD_RET_USAGE; 120388364387SHung-ying Tyan ret = cros_ec_flash_offset(dev, region, &offset, &size); 120488364387SHung-ying Tyan if (ret) { 120588364387SHung-ying Tyan debug("%s: Could not read region info\n", __func__); 120688364387SHung-ying Tyan } else { 120788364387SHung-ying Tyan printf("Region: %s\n", region == EC_FLASH_REGION_RO ? 120888364387SHung-ying Tyan "RO" : "RW"); 120988364387SHung-ying Tyan printf("Offset: %x\n", offset); 121088364387SHung-ying Tyan printf("Size: %x\n", size); 121188364387SHung-ying Tyan } 121288364387SHung-ying Tyan } else if (0 == strcmp("vbnvcontext", cmd)) { 121388364387SHung-ying Tyan uint8_t block[EC_VBNV_BLOCK_SIZE]; 121488364387SHung-ying Tyan char buf[3]; 121588364387SHung-ying Tyan int i, len; 121688364387SHung-ying Tyan unsigned long result; 121788364387SHung-ying Tyan 121888364387SHung-ying Tyan if (argc <= 2) { 121988364387SHung-ying Tyan ret = cros_ec_read_vbnvcontext(dev, block); 122088364387SHung-ying Tyan if (!ret) { 122188364387SHung-ying Tyan printf("vbnv_block: "); 122288364387SHung-ying Tyan for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) 122388364387SHung-ying Tyan printf("%02x", block[i]); 122488364387SHung-ying Tyan putc('\n'); 122588364387SHung-ying Tyan } 122688364387SHung-ying Tyan } else { 122788364387SHung-ying Tyan /* 122888364387SHung-ying Tyan * TODO(clchiou): Move this to a utility function as 122988364387SHung-ying Tyan * cmd_spi might want to call it. 123088364387SHung-ying Tyan */ 123188364387SHung-ying Tyan memset(block, 0, EC_VBNV_BLOCK_SIZE); 123288364387SHung-ying Tyan len = strlen(argv[2]); 123388364387SHung-ying Tyan buf[2] = '\0'; 123488364387SHung-ying Tyan for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) { 123588364387SHung-ying Tyan if (i * 2 >= len) 123688364387SHung-ying Tyan break; 123788364387SHung-ying Tyan buf[0] = argv[2][i * 2]; 123888364387SHung-ying Tyan if (i * 2 + 1 >= len) 123988364387SHung-ying Tyan buf[1] = '0'; 124088364387SHung-ying Tyan else 124188364387SHung-ying Tyan buf[1] = argv[2][i * 2 + 1]; 124288364387SHung-ying Tyan strict_strtoul(buf, 16, &result); 124388364387SHung-ying Tyan block[i] = result; 124488364387SHung-ying Tyan } 124588364387SHung-ying Tyan ret = cros_ec_write_vbnvcontext(dev, block); 124688364387SHung-ying Tyan } 124788364387SHung-ying Tyan if (ret) { 124888364387SHung-ying Tyan debug("%s: Could not %s VbNvContext\n", __func__, 124988364387SHung-ying Tyan argc <= 2 ? "read" : "write"); 125088364387SHung-ying Tyan } 125188364387SHung-ying Tyan } else if (0 == strcmp("test", cmd)) { 125288364387SHung-ying Tyan int result = cros_ec_test(dev); 125388364387SHung-ying Tyan 125488364387SHung-ying Tyan if (result) 125588364387SHung-ying Tyan printf("Test failed with error %d\n", result); 125688364387SHung-ying Tyan else 125788364387SHung-ying Tyan puts("Test passed\n"); 125888364387SHung-ying Tyan } else if (0 == strcmp("version", cmd)) { 125988364387SHung-ying Tyan struct ec_response_get_version *p; 126088364387SHung-ying Tyan char *build_string; 126188364387SHung-ying Tyan 126288364387SHung-ying Tyan ret = cros_ec_read_version(dev, &p); 126388364387SHung-ying Tyan if (!ret) { 126488364387SHung-ying Tyan /* Print versions */ 126588364387SHung-ying Tyan printf("RO version: %1.*s\n", 126688364387SHung-ying Tyan sizeof(p->version_string_ro), 126788364387SHung-ying Tyan p->version_string_ro); 126888364387SHung-ying Tyan printf("RW version: %1.*s\n", 126988364387SHung-ying Tyan sizeof(p->version_string_rw), 127088364387SHung-ying Tyan p->version_string_rw); 127188364387SHung-ying Tyan printf("Firmware copy: %s\n", 127288364387SHung-ying Tyan (p->current_image < 127388364387SHung-ying Tyan ARRAY_SIZE(ec_current_image_name) ? 127488364387SHung-ying Tyan ec_current_image_name[p->current_image] : 127588364387SHung-ying Tyan "?")); 127688364387SHung-ying Tyan ret = cros_ec_read_build_info(dev, &build_string); 127788364387SHung-ying Tyan if (!ret) 127888364387SHung-ying Tyan printf("Build info: %s\n", build_string); 127988364387SHung-ying Tyan } 128088364387SHung-ying Tyan } else if (0 == strcmp("ldo", cmd)) { 128188364387SHung-ying Tyan uint8_t index, state; 128288364387SHung-ying Tyan char *endp; 128388364387SHung-ying Tyan 128488364387SHung-ying Tyan if (argc < 3) 128588364387SHung-ying Tyan return CMD_RET_USAGE; 128688364387SHung-ying Tyan index = simple_strtoul(argv[2], &endp, 10); 128788364387SHung-ying Tyan if (*argv[2] == 0 || *endp != 0) 128888364387SHung-ying Tyan return CMD_RET_USAGE; 128988364387SHung-ying Tyan if (argc > 3) { 129088364387SHung-ying Tyan state = simple_strtoul(argv[3], &endp, 10); 129188364387SHung-ying Tyan if (*argv[3] == 0 || *endp != 0) 129288364387SHung-ying Tyan return CMD_RET_USAGE; 129388364387SHung-ying Tyan ret = cros_ec_set_ldo(dev, index, state); 129488364387SHung-ying Tyan } else { 129588364387SHung-ying Tyan ret = cros_ec_get_ldo(dev, index, &state); 129688364387SHung-ying Tyan if (!ret) { 129788364387SHung-ying Tyan printf("LDO%d: %s\n", index, 129888364387SHung-ying Tyan state == EC_LDO_STATE_ON ? 129988364387SHung-ying Tyan "on" : "off"); 130088364387SHung-ying Tyan } 130188364387SHung-ying Tyan } 130288364387SHung-ying Tyan 130388364387SHung-ying Tyan if (ret) { 130488364387SHung-ying Tyan debug("%s: Could not access LDO%d\n", __func__, index); 130588364387SHung-ying Tyan return ret; 130688364387SHung-ying Tyan } 130788364387SHung-ying Tyan } else { 130888364387SHung-ying Tyan return CMD_RET_USAGE; 130988364387SHung-ying Tyan } 131088364387SHung-ying Tyan 131188364387SHung-ying Tyan if (ret < 0) { 131288364387SHung-ying Tyan printf("Error: CROS-EC command failed (error %d)\n", ret); 131388364387SHung-ying Tyan ret = 1; 131488364387SHung-ying Tyan } 131588364387SHung-ying Tyan 131688364387SHung-ying Tyan return ret; 131788364387SHung-ying Tyan } 131888364387SHung-ying Tyan 131988364387SHung-ying Tyan U_BOOT_CMD( 132088364387SHung-ying Tyan crosec, 5, 1, do_cros_ec, 132188364387SHung-ying Tyan "CROS-EC utility command", 132288364387SHung-ying Tyan "init Re-init CROS-EC (done on startup automatically)\n" 132388364387SHung-ying Tyan "crosec id Read CROS-EC ID\n" 132488364387SHung-ying Tyan "crosec info Read CROS-EC info\n" 132588364387SHung-ying Tyan "crosec curimage Read CROS-EC current image\n" 132688364387SHung-ying Tyan "crosec hash Read CROS-EC hash\n" 132788364387SHung-ying Tyan "crosec reboot [rw | ro | cold] Reboot CROS-EC\n" 132888364387SHung-ying Tyan "crosec events Read CROS-EC host events\n" 132988364387SHung-ying Tyan "crosec clrevents [mask] Clear CROS-EC host events\n" 133088364387SHung-ying Tyan "crosec regioninfo <ro|rw> Read image info\n" 133188364387SHung-ying Tyan "crosec erase <ro|rw> Erase EC image\n" 133288364387SHung-ying Tyan "crosec read <ro|rw> <addr> [<size>] Read EC image\n" 133388364387SHung-ying Tyan "crosec write <ro|rw> <addr> [<size>] Write EC image\n" 133488364387SHung-ying Tyan "crosec vbnvcontext [hexstring] Read [write] VbNvContext from EC\n" 133588364387SHung-ying Tyan "crosec ldo <idx> [<state>] Switch/Read LDO state\n" 133688364387SHung-ying Tyan "crosec test run tests on cros_ec\n" 133788364387SHung-ying Tyan "crosec version Read CROS-EC version" 133888364387SHung-ying Tyan ); 133988364387SHung-ying Tyan #endif 1340