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