diff options
-rw-r--r-- | common/cbi.c | 87 | ||||
-rw-r--r-- | include/ec_commands.h | 23 | ||||
-rw-r--r-- | util/ectool.c | 36 |
3 files changed, 144 insertions, 2 deletions
diff --git a/common/cbi.c b/common/cbi.c index b82d0375c2..41e8606487 100644 --- a/common/cbi.c +++ b/common/cbi.c @@ -9,12 +9,14 @@ #include "console.h" #include "crc8.h" #include "cros_board_info.h" +#include "gpio.h" #include "host_command.h" #include "i2c.h" #include "util.h" #define CPRINTS(format, args...) cprints(CC_SYSTEM, "CBI " format, ## args) +#define EEPROM_PAGE_WRITE_SIZE 16 static struct board_info bi; /* TODO: Init it to -1. On error (I2C or bad contents), retry a read and set it * to enum ec_error_list if it still fails. The successive calls can be @@ -95,6 +97,38 @@ static int read_board_info(void) return EC_SUCCESS; } +static int eeprom_is_write_protected(void) +{ + return !gpio_get_level(GPIO_WP_L); +} + +static int write_board_info(void) +{ + uint8_t buf[sizeof(bi) + 1]; + /* The code is only tested for ST M24C02, whose page size for a single + * write is 16 byte. To support different EEPROMs, you may need to + * craft the i2c packets accordingly. */ + _Static_assert(sizeof(bi) <= EEPROM_PAGE_WRITE_SIZE, + "struct board_info exceeds page write size"); + int rv; + + if (eeprom_is_write_protected()) { + CPRINTS("Failed to write for WP"); + return EC_ERROR_ACCESS_DENIED; + } + + buf[0] = 0; /* Offset 0 */ + memcpy(&buf[1], &bi, sizeof(bi)); + rv = i2c_xfer(I2C_PORT_EEPROM, I2C_ADDR_EEPROM, buf, + sizeof(bi) + 1, NULL, 0, I2C_XFER_SINGLE); + if (rv) { + CPRINTS("Failed to write for %d", rv); + return rv; + } + + return EC_SUCCESS; +} + int cbi_get_board_version(uint32_t *version) { if (read_board_info()) @@ -154,3 +188,56 @@ static int hc_cbi_get(struct host_cmd_handler_args *args) DECLARE_HOST_COMMAND(EC_CMD_GET_CROS_BOARD_INFO, hc_cbi_get, EC_VER_MASK(0)); + +static int hc_cbi_set(struct host_cmd_handler_args *args) +{ + const struct __ec_align4 ec_params_set_cbi *p = args->params; + + if (p->flag & CBI_SET_INIT) { + memset(&bi, 0, sizeof(bi)); + memcpy(&bi.head.magic, cbi_magic, sizeof(cbi_magic)); + initialized = 1; + } else { + if (read_board_info()) + return EC_RES_ERROR; + } + + switch (p->type) { + case CBI_DATA_BOARD_VERSION: + if (p->data > UINT16_MAX) + return EC_RES_INVALID_PARAM; + bi.version = p->data; + break; + case CBI_DATA_OEM_ID: + if (p->data > UINT8_MAX) + return EC_RES_INVALID_PARAM; + bi.oem_id = p->data; + break; + case CBI_DATA_SKU_ID: + if (p->data > UINT8_MAX) + return EC_RES_INVALID_PARAM; + bi.sku_id = p->data; + break; + default: + return EC_RES_INVALID_PARAM; + } + + /* Whether we're modifying existing data or creating new one, + * we take over the format. */ + bi.head.major_version = CBI_VERSION_MAJOR; + bi.head.minor_version = CBI_VERSION_MINOR; + bi.head.total_size = sizeof(bi); + bi.head.crc = cbi_crc8(&bi); + + /* Skip write if client asks so. */ + if (p->flag & CBI_SET_NO_SYNC) + return EC_RES_SUCCESS; + + if (write_board_info()) + return EC_RES_ERROR; + + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_SET_CROS_BOARD_INFO, + hc_cbi_set, + EC_VER_MASK(0)); diff --git a/include/ec_commands.h b/include/ec_commands.h index 23307354e1..705e54f2b1 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -4576,6 +4576,11 @@ struct __ec_align1 ec_params_efs_verify { * size to determine how big it is. */ #define EC_CMD_GET_CROS_BOARD_INFO 0x011F +/* + * Write info into Cros Board Info on EEPROM. Write fails if the board has + * hardware write-protect enabled. + */ +#define EC_CMD_SET_CROS_BOARD_INFO 0x0120 enum cbi_data_type { /* integer types */ @@ -4591,6 +4596,24 @@ struct __ec_align4 ec_params_get_cbi { uint32_t type; /* enum cbi_data_type */ }; +/* + * Flags to control write behavior. + * + * NO_SYNC: Makes EC update data in RAM but skip writing to EEPROM. It's + * useful when writing multiple fields in a row. + * INIT: Need to be set when creating a new CBI from scratch. All fields + * will be initialized to zero first. + */ +#define CBI_SET_NO_SYNC (1 << 0) +#define CBI_SET_INIT (1 << 1) + +struct __ec_align1 ec_params_set_cbi { + uint32_t type; /* enum cbi_data_type */ + uint8_t flag; /* CBI_SET_* */ + uint32_t data; /* For numeric value */ + uint8_t raw[]; /* For string and raw data */ +}; + /*****************************************************************************/ /* The command range 0x200-0x2FF is reserved for Rotor. */ diff --git a/util/ectool.c b/util/ectool.c index c694e83c56..4737ac91e3 100644 --- a/util/ectool.c +++ b/util/ectool.c @@ -60,7 +60,7 @@ const char help_str[] = " boardversion\n" " Prints the board version\n" " cbi\n" - " Get Cros Board Info\n" + " Get/Set Cros Board Info\n" " chargecurrentlimit\n" " Set the maximum battery charging current\n" " chargecontrol\n" @@ -6231,10 +6231,14 @@ static void cmd_cbi_help(char *cmd) { fprintf(stderr, " Usage: %s get <type>\n" + " Usage: %s set <type> value [flag]\n" " <type> is one of:\n" " 0: BOARD_VERSION\n" " 1: OEM_ID\n" - " 2: SKU_ID\n", cmd); + " 2: SKU_ID\n" + " [flag] is combination of:\n" + " 01b: Skip write to EEPROM. Use for back-to-back writes\n" + " 10b: Set all fields to defaults first\n", cmd, cmd); } /* @@ -6282,6 +6286,34 @@ static int cmd_cbi(int argc, char *argv[]) return -1; } return 0; + } else if (!strcasecmp(argv[1], "set")) { + struct ec_params_set_cbi p; + if (argc < 4) { + fprintf(stderr, "Invalid number of params\n"); + cmd_cbi_help(argv[0]); + return -1; + } + memset(&p, 0, sizeof(p)); + p.type = type; + p.data = strtol(argv[3], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad value\n"); + return -1; + } + if (argc > 4) { + p.flag = strtol(argv[4], &e, 0); + if (e && *e) { + fprintf(stderr, "Bad flag\n"); + return -1; + } + } + rv = ec_command(EC_CMD_SET_CROS_BOARD_INFO, 0, &p, sizeof(p), + NULL, 0); + if (rv < 0) { + fprintf(stderr, "Error code: %d\n", rv); + return rv; + } + return 0; } fprintf(stderr, "Invalid sub command: %s\n", argv[1]); |