diff options
author | Aaron Massey <aaronmassey@google.com> | 2023-04-03 14:57:28 +0000 |
---|---|---|
committer | Aaron Massey <aaronmassey@google.com> | 2023-04-03 15:01:03 +0000 |
commit | 9bdc64603cbe1871b510a77168e4e7ec745652a4 (patch) | |
tree | 73fb613a5bf12f155780467318ea4de829c1869f /util | |
parent | 48fd3571354bfb9ae56839f5dace3b2c80fc1c01 (diff) | |
download | chrome-ec-9bdc64603cbe1871b510a77168e4e7ec745652a4.tar.gz |
Revert "battery: Finish past cleanup of sb fw update"
This reverts commit 33885596426b7f419709d81529f3e4d02f960ddf.
Reason for revert: http://b/276733475
Original change's description:
> battery: Finish past cleanup of sb fw update
>
> We removed the SB_FW_UPDATE host command code in CL:792013 without
> cleaning up all now dead references.
>
> Remove all dead code that references the SB_FW_UPDATE command and
> associated sources.
>
> BRANCH=none
> BUG=b:276458241
> TEST=CQ
>
> Change-Id: I66a79eb42508aaa7ba3a64f6022df777608a497b
> Signed-off-by: Aaron Massey <aaronmassey@google.com>
> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4388382
> Reviewed-by: Abe Levkoy <alevkoy@chromium.org>
Bug: b:276458241
Change-Id: I06eb724e597e7c3a6e05faa346f84a635a57aea1
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4391204
Reviewed-by: Jack Rosenthal <jrosenth@chromium.org>
Tested-by: Aaron Massey <aaronmassey@google.com>
Commit-Queue: Aaron Massey <aaronmassey@google.com>
Owners-Override: Aaron Massey <aaronmassey@google.com>
Diffstat (limited to 'util')
-rw-r--r-- | util/build.mk | 5 | ||||
-rw-r--r-- | util/ec_sb_firmware_update.cc | 803 | ||||
-rw-r--r-- | util/ec_sb_firmware_update.h | 126 |
3 files changed, 933 insertions, 1 deletions
diff --git a/util/build.mk b/util/build.mk index 1b692ccb9f..38801d2f77 100644 --- a/util/build.mk +++ b/util/build.mk @@ -8,7 +8,8 @@ # See Makefile for description. host-util-bin-y += cbi-util iteflash -host-util-bin-cxx-y += ectool ec_parse_panicinfo lbplay stm32mon lbcc +host-util-bin-cxx-y += ectool ec_parse_panicinfo lbplay ec_sb_firmware_update \ + stm32mon lbcc build-util-art-y += util/export_taskinfo.so build-util-bin-$(CHIP_NPCX) += ecst @@ -34,6 +35,8 @@ ectool-objs=ectool.o ectool_keyscan.o ec_flash.o $(comm-objs) ectool-objs+=ectool_i2c.o ectool-objs+=../common/crc.o ectool_servo-objs=$(ectool-objs) comm-servo-spi.o +ec_sb_firmware_update-objs=ec_sb_firmware_update.o $(comm-objs) misc_util.o +ec_sb_firmware_update-objs+=powerd_lock.o lbplay-objs=lbplay.o $(comm-objs) util/ectool.cc: $(out)/ec_version.h diff --git a/util/ec_sb_firmware_update.cc b/util/ec_sb_firmware_update.cc new file mode 100644 index 0000000000..8cf7db54a5 --- /dev/null +++ b/util/ec_sb_firmware_update.cc @@ -0,0 +1,803 @@ +/* Copyright 2014 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "comm-host.h" +#include "compile_time_macros.h" +#include "ec_commands.h" +#include "ec_sb_firmware_update.h" +#include "lock/gec_lock.h" +#include "misc_util.h" +#include "powerd_lock.h" + +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <unistd.h> + +/* Subcommands: [check|update] */ +enum { + OP_UNKNOWN = 0, + OP_CHECK = 1, + OP_UPDATE = 2, +}; + +struct delay_value { + uint32_t steps; + uint32_t value; +}; + +/* Default retry counter on errors */ +#define SB_FW_UPDATE_DEFAULT_RETRY_CNT 3 +/* Default delay value */ +#define SB_FW_UPDATE_DEFAULT_DELAY 1000 + +#define DELAY_US_BEGIN 500000 +#define DELAY_US_END 1000000 +#define DELAY_US_BUSY 1000000 +#define DELAY_US_WRITE_END 50000 + +static struct delay_value sb_delays[] = { + { 1, 100000 }, { 2, 9000000 }, { 4, 100000 }, + { 771, 30000 }, { 2200, 10000 }, { 0xFFFFFF, 50000 }, +}; + +enum fw_update_state { + S0_READ_STATUS = 0, + S1_READ_INFO = 1, + S2_WRITE_PREPARE = 2, + S3_READ_STATUS = 3, + S4_WRITE_UPDATE = 4, + S5_READ_STATUS = 5, + S6_WRITE_BLOCK = 6, + S7_READ_STATUS = 7, + S8_WRITE_END = 8, + S9_READ_STATUS = 9, + S10_TERMINAL = 10 +}; + +#define MAX_FW_IMAGE_NAME_SIZE 80 + +/* Firmware Update Control Flags */ +enum { + F_AC_PRESENT = 0x1, /* AC Present */ + F_VERSION_CHECK = 0x2, /* do firmware version check */ + F_UPDATE = 0x4, /* do firmware update */ + F_NEED_UPDATE = 0x8, /* need firmware update */ + F_POWERD_DISABLED = 0x10, /* powerd is disabled */ + F_LFCC_ZERO = 0x20, /* last full charge is zero */ + F_BATT_DISCHARGE = 0x40 /* battery discharging */ +}; + +struct fw_update_ctrl { + uint32_t flags; /* fw update control flags */ + int size; /* size of battery firmware image */ + char *ptr; /* current read pointer of the firmware image */ + int offset; /* current block write offset */ + struct sb_fw_header *fw_img_hdr; /*pointer to firmware image header*/ + struct sb_fw_update_status status; + struct sb_fw_update_info info; + int err_retry_cnt; + int fec_err_retry_cnt; + int busy_retry_cnt; + int step_size; + int rv; + char image_name[MAX_FW_IMAGE_NAME_SIZE]; + char msg[256]; +}; + +/* + * Global Firmware Update Control Data Structure + */ +static struct fw_update_ctrl fw_update; + +static uint32_t get_delay_value(uint32_t offset, uint32_t step_size) +{ + int sz = ARRAY_SIZE(sb_delays); + int i; + for (i = 0; i < sz; i++) { + if (offset <= sb_delays[i].steps * step_size) + return sb_delays[i].value; + } + return sb_delays[sz - 1].value; +} + +static void print_battery_firmware_image_hdr(struct sb_fw_header *hdr) +{ + printf("Latest Battery Firmware:\n"); + printf("\t%c%c%c%c hdr_ver:%04x major_minor:%04x\n", hdr->signature[0], + hdr->signature[1], hdr->signature[2], hdr->signature[3], + hdr->hdr_version, hdr->pkg_version_major_minor); + + printf("\tmaker:0x%04x hwid:0x%04x fw_ver:0x%04x tbl_ver:0x%04x\n", + hdr->vendor_id, hdr->battery_type, hdr->fw_version, + hdr->data_table_version); + + printf("\tbinary offset:0x%08x size:0x%08x chk_sum:0x%02x\n", + hdr->fw_binary_offset, hdr->fw_binary_size, hdr->checksum); +} + +static void print_info(struct sb_fw_update_info *info) +{ + printf("\nCurrent Battery Firmware:\n"); + printf("\tmaker:0x%04x hwid:0x%04x fw_ver:0x%04x tbl_ver:0x%04x\n", + info->maker_id, info->hardware_id, info->fw_version, + info->data_version); + return; +} + +static void print_status(struct sb_fw_update_status *sts) +{ + printf("f_maker_id:%d f_hw_id:%d f_fw_ver:%d f_permnent:%d\n", + sts->v_fail_maker_id, sts->v_fail_hw_id, sts->v_fail_fw_version, + sts->v_fail_permanent); + printf("permanent failure:%d abnormal:%d fw_update:%d\n", + sts->permanent_failure, sts->abnormal_condition, + sts->fw_update_supported); + printf("fw_update_mode:%d fw_corrupted:%d cmd_reject:%d\n", + sts->fw_update_mode, sts->fw_corrupted, sts->cmd_reject); + printf("invliad data:%d fw_fatal_err:%d fec_err:%d busy:%d\n", + sts->invalid_data, sts->fw_fatal_error, sts->fec_error, + sts->busy); + printf("\n"); + return; +} + +/* @return 1 (True) if img signature is valid */ +static int check_battery_firmware_image_signature(struct sb_fw_header *hdr) +{ + return (hdr->signature[0] == 'B') && (hdr->signature[1] == 'T') && + (hdr->signature[2] == 'F') && (hdr->signature[3] == 'W'); +} + +/* @return 1 (True) if img checksum is valid. */ +static int check_battery_firmware_image_checksum(struct sb_fw_header *hdr) +{ + int i; + uint8_t sum = 0; + uint8_t *img = (uint8_t *)hdr; + + img += hdr->fw_binary_offset; + for (i = 0; i < hdr->fw_binary_size; i++) + sum += img[i]; + sum += hdr->checksum; + return sum == 0; +} + +/* @return 1 (True) if img versions are ok to update. */ +static int check_battery_firmware_image_version(struct sb_fw_header *hdr, + struct sb_fw_update_info *p) +{ + /* + * If the battery firmware has a newer fw version + * or a newer data table version, then it is ok to update. + */ + return (hdr->fw_version > p->fw_version) || + (hdr->data_table_version > p->data_version); +} + +static int check_battery_firmware_ids(struct sb_fw_header *hdr, + struct sb_fw_update_info *p) +{ + return ((hdr->vendor_id == p->maker_id) && + (hdr->battery_type == p->hardware_id)); +} + +/* check_if_need_update_fw + * @return 1 (true) if need; 0 (false) if not. + */ +static int check_if_valid_fw(struct sb_fw_header *hdr, + struct sb_fw_update_info *info) +{ + return check_battery_firmware_image_signature(hdr) && + check_battery_firmware_ids(hdr, info) && + check_battery_firmware_image_checksum(hdr); +} + +/* check_if_need_update_fw + * @return 1 (true) if need; 0 (false) if not. + */ +static int check_if_need_update_fw(struct sb_fw_header *hdr, + struct sb_fw_update_info *info) +{ + return check_battery_firmware_image_version(hdr, info); +} + +static void log_msg(struct fw_update_ctrl *fw_update, + enum fw_update_state state, const char *msg) +{ + sprintf(fw_update->msg, "Battery Firmware Updater State:%d %s", state, + msg); +} + +static char *read_fw_image(struct fw_update_ctrl *fw_update) +{ + int size; + char *buf; + fw_update->size = 0; + fw_update->ptr = NULL; + fw_update->fw_img_hdr = (struct sb_fw_header *)NULL; + + /* Read the input file */ + buf = read_file(fw_update->image_name, &size); + if (!buf) + return NULL; + + fw_update->size = size; + fw_update->ptr = buf; + fw_update->fw_img_hdr = (struct sb_fw_header *)buf; + print_battery_firmware_image_hdr(fw_update->fw_img_hdr); + + if (fw_update->fw_img_hdr->fw_binary_offset >= fw_update->size || + fw_update->size < 256) { + printf("Load Firmware Image[%s] Error offset:%d size:%d\n", + fw_update->image_name, + fw_update->fw_img_hdr->fw_binary_offset, + fw_update->size); + free(buf); + return NULL; + } + return buf; +} + +static int get_status(struct sb_fw_update_status *status) +{ + int rv = EC_RES_SUCCESS; + int cnt = 0; + + struct ec_params_sb_fw_update *param = + (struct ec_params_sb_fw_update *)ec_outbuf; + + struct ec_response_sb_fw_update *resp = + (struct ec_response_sb_fw_update *)ec_inbuf; + + param->hdr.subcmd = EC_SB_FW_UPDATE_STATUS; + do { + usleep(SB_FW_UPDATE_DEFAULT_DELAY); + rv = ec_command(EC_CMD_SB_FW_UPDATE, 0, param, + sizeof(struct ec_sb_fw_update_header), resp, + SB_FW_UPDATE_CMD_STATUS_SIZE); + } while ((rv < 0) && (cnt++ < SB_FW_UPDATE_DEFAULT_RETRY_CNT)); + + if (rv < 0) { + memset(status, 0, SB_FW_UPDATE_CMD_STATUS_SIZE); + return -EC_RES_ERROR; + } + + memcpy(status, resp->status.data, SB_FW_UPDATE_CMD_STATUS_SIZE); + return EC_RES_SUCCESS; +} + +static int get_info(struct sb_fw_update_info *info) +{ + int rv = EC_RES_SUCCESS; + int cnt = 0; + + struct ec_params_sb_fw_update *param = + (struct ec_params_sb_fw_update *)ec_outbuf; + + struct ec_response_sb_fw_update *resp = + (struct ec_response_sb_fw_update *)ec_inbuf; + + param->hdr.subcmd = EC_SB_FW_UPDATE_INFO; + do { + usleep(SB_FW_UPDATE_DEFAULT_DELAY); + rv = ec_command(EC_CMD_SB_FW_UPDATE, 0, param, + sizeof(struct ec_sb_fw_update_header), resp, + SB_FW_UPDATE_CMD_INFO_SIZE); + } while ((rv < 0) && (cnt++ < SB_FW_UPDATE_DEFAULT_RETRY_CNT)); + + if (rv < 0) { + memset(info, 0, SB_FW_UPDATE_CMD_INFO_SIZE); + return -EC_RES_ERROR; + } + + memcpy(info, resp->info.data, SB_FW_UPDATE_CMD_INFO_SIZE); + return EC_RES_SUCCESS; +} + +static int send_subcmd(int subcmd) +{ + int rv = EC_RES_SUCCESS; + + struct ec_params_sb_fw_update *param = + (struct ec_params_sb_fw_update *)ec_outbuf; + + param->hdr.subcmd = subcmd; + rv = ec_command(EC_CMD_SB_FW_UPDATE, 0, param, + sizeof(struct ec_sb_fw_update_header), NULL, 0); + if (rv < 0) { + printf("Firmware Update subcmd:%d Error\n", subcmd); + return -EC_RES_ERROR; + } + return EC_RES_SUCCESS; +} + +static int write_block(struct fw_update_ctrl *fw_update, int offset, int bsize) +{ + int rv; + + struct ec_params_sb_fw_update *param = + (struct ec_params_sb_fw_update *)ec_outbuf; + + memcpy(param->write.data, fw_update->ptr + offset, bsize); + + param->hdr.subcmd = EC_SB_FW_UPDATE_WRITE; + rv = ec_command(EC_CMD_SB_FW_UPDATE, 0, param, + sizeof(struct ec_params_sb_fw_update), NULL, 0); + if (rv < 0) { + printf("Firmware Update Write Error ptr:%p offset@%x\n", + fw_update->ptr, offset); + return -EC_RES_ERROR; + } + return EC_RES_SUCCESS; +} + +static void dump_data(char *data, int offset, int size) +{ + int i = 0; + + if (data == NULL) + return; + + printf("Offset:0x%X\n", offset); + for (i = 0; i < size; i++) { + if ((i % 16) == 0) + printf("\n"); + printf("%02X ", data[i]); + } + printf("\n"); +} + +static enum fw_update_state s0_read_status(struct fw_update_ctrl *fw_update) +{ + if (fw_update->busy_retry_cnt == 0) { + fw_update->rv = -1; + log_msg(fw_update, S0_READ_STATUS, "Busy"); + return S10_TERMINAL; + } + + fw_update->busy_retry_cnt--; + + fw_update->rv = get_status(&fw_update->status); + if (fw_update->rv) { + fw_update->rv = -1; + log_msg(fw_update, S0_READ_STATUS, "Interface Error"); + return S10_TERMINAL; + } + + if (!((fw_update->status.abnormal_condition == 0) && + (fw_update->status.fw_update_supported == 1))) { + return S0_READ_STATUS; + } + + if (fw_update->status.busy) { + usleep(DELAY_US_BUSY); + return S0_READ_STATUS; + } else + return S1_READ_INFO; +} + +static enum fw_update_state +s1_read_battery_info(struct fw_update_ctrl *fw_update) +{ + int rv; + + if (fw_update->err_retry_cnt == 0) { + fw_update->rv = -1; + log_msg(fw_update, S1_READ_INFO, "Retry Error"); + return S10_TERMINAL; + } + + fw_update->err_retry_cnt--; + + rv = get_info(&fw_update->info); + if (rv) { + fw_update->rv = -1; + log_msg(fw_update, S1_READ_INFO, "Interface Error"); + return S10_TERMINAL; + } + print_info(&fw_update->info); + + sprintf(fw_update->image_name, + "/lib/firmware/battery/maker.%04x.hwid.%04x.bin", + fw_update->info.maker_id, fw_update->info.hardware_id); + + if (NULL == read_fw_image(fw_update)) { + fw_update->rv = 0; + log_msg(fw_update, S1_READ_INFO, "Open Image File"); + return S10_TERMINAL; + } + + rv = get_status(&fw_update->status); + if (rv) { + fw_update->rv = -1; + log_msg(fw_update, S1_READ_INFO, "Interface Error"); + return S10_TERMINAL; + } + + rv = check_if_valid_fw(fw_update->fw_img_hdr, &fw_update->info); + if (rv == 0) { + fw_update->rv = -EC_RES_INVALID_PARAM; + log_msg(fw_update, S1_READ_INFO, "Invalid Firmware"); + return S10_TERMINAL; + } + + rv = check_if_need_update_fw(fw_update->fw_img_hdr, &fw_update->info); + if (rv == 0 && (fw_update->flags & F_VERSION_CHECK)) { + fw_update->rv = 0; + log_msg(fw_update, S1_READ_INFO, "Latest Firmware"); + return S10_TERMINAL; + } + + fw_update->flags |= F_NEED_UPDATE; + + if (!(fw_update->flags & F_UPDATE)) { + fw_update->rv = 0; + return S10_TERMINAL; + } + + if (!(fw_update->flags & F_AC_PRESENT)) { + fw_update->rv = 0; + log_msg(fw_update, S1_READ_INFO, + "Require AC Adapter Connected."); + return S10_TERMINAL; + } + + if ((fw_update->flags & F_BATT_DISCHARGE) && + (fw_update->flags & F_AC_PRESENT)) { + /* + * If battery discharge due to battery learning mode, + * we can't update battery FW, because device will shutdown + * during FW update. + */ + fw_update->rv = 0; + log_msg(fw_update, S1_READ_INFO, + "battery can't update in learning mode"); + return S10_TERMINAL; + } + + return S2_WRITE_PREPARE; +} + +static enum fw_update_state s2_write_prepare(struct fw_update_ctrl *fw_update) +{ + int rv; + + rv = disable_power_management(); + if (0 == rv) + fw_update->flags |= F_POWERD_DISABLED; + + rv = send_subcmd(EC_SB_FW_UPDATE_PREPARE); + if (rv) { + fw_update->rv = -1; + log_msg(fw_update, S2_WRITE_PREPARE, "Interface Error"); + return S10_TERMINAL; + } + return S3_READ_STATUS; +} + +static enum fw_update_state s3_read_status(struct fw_update_ctrl *fw_update) +{ + int rv; + + rv = get_status(&fw_update->status); + if (rv) { + fw_update->rv = -1; + log_msg(fw_update, S3_READ_STATUS, "Interface Error"); + return S10_TERMINAL; + } + return S4_WRITE_UPDATE; +} + +static enum fw_update_state s4_write_update(struct fw_update_ctrl *fw_update) +{ + int rv; + + rv = send_subcmd(EC_SB_FW_UPDATE_BEGIN); + if (rv) { + fw_update->rv = -1; + log_msg(fw_update, S4_WRITE_UPDATE, "Interface Error"); + return S10_TERMINAL; + } + usleep(DELAY_US_BEGIN); + return S5_READ_STATUS; +} + +static enum fw_update_state s5_read_status(struct fw_update_ctrl *fw_update) +{ + int rv = get_status(&fw_update->status); + + if (rv) { + fw_update->rv = -1; + log_msg(fw_update, S5_READ_STATUS, "Interface Error"); + return S10_TERMINAL; + } + if (fw_update->status.fw_update_mode == 0) + return S2_WRITE_PREPARE; + + /* Init Write Block Loop Controls */ + fw_update->ptr += fw_update->fw_img_hdr->fw_binary_offset; + fw_update->size -= fw_update->fw_img_hdr->fw_binary_offset; + fw_update->offset = 0; + + return S6_WRITE_BLOCK; +} + +static enum fw_update_state s6_write_block(struct fw_update_ctrl *fw_update) +{ + int rv; + int bsize; + int offset = fw_update->offset; + + if (offset >= fw_update->size) + return S8_WRITE_END; + + bsize = fw_update->step_size; + + if ((offset & 0xFFFF) == 0x0) + printf("\n%X\n", offset); + + if (fw_update->fec_err_retry_cnt == 0) { + fw_update->rv = -1; + log_msg(fw_update, S6_WRITE_BLOCK, "FEC Retry Error"); + return S10_TERMINAL; + } + fw_update->fec_err_retry_cnt--; + + rv = write_block(fw_update, offset, bsize); + if (rv) { + fw_update->rv = -1; + log_msg(fw_update, S6_WRITE_BLOCK, "Interface Error"); + return S10_TERMINAL; + } + + /* + * Add more delays after the last few (3) block writes. + * 3 is chosen based on current test results. + */ + if ((offset + 3 * fw_update->step_size) >= fw_update->size) + usleep(DELAY_US_WRITE_END); + + usleep(get_delay_value(offset, fw_update->step_size)); + + return S7_READ_STATUS; +} + +static enum fw_update_state s7_read_status(struct fw_update_ctrl *fw_update) +{ + int rv; + int offset = fw_update->offset; + int bsize; + int cnt = 0; + + bsize = fw_update->step_size; + do { + usleep(SB_FW_UPDATE_DEFAULT_DELAY); + rv = get_status(&fw_update->status); + if (rv) { + dump_data(fw_update->ptr + offset, offset, bsize); + print_status(&fw_update->status); + fw_update->rv = -1; + log_msg(fw_update, S7_READ_STATUS, "Interface Error"); + return S10_TERMINAL; + } + } while (fw_update->status.busy && + (cnt++ < SB_FW_UPDATE_DEFAULT_RETRY_CNT)); + + if (fw_update->status.fec_error) { + dump_data(fw_update->ptr + offset, offset, bsize); + print_status(&fw_update->status); + fw_update->rv = 0; + return S6_WRITE_BLOCK; + } + if (fw_update->status.permanent_failure || + fw_update->status.v_fail_permanent) { + dump_data(fw_update->ptr + offset, offset, bsize); + print_status(&fw_update->status); + fw_update->rv = -1; + log_msg(fw_update, S7_READ_STATUS, "Battery Permanent Error"); + return S8_WRITE_END; + } + if (fw_update->status.v_fail_maker_id || + fw_update->status.v_fail_hw_id || + fw_update->status.v_fail_fw_version || + fw_update->status.fw_corrupted || fw_update->status.cmd_reject || + fw_update->status.invalid_data || + fw_update->status.fw_fatal_error) { + dump_data(fw_update->ptr + offset, offset, bsize); + print_status(&fw_update->status); + fw_update->rv = 0; + return S1_READ_INFO; + } + + fw_update->fec_err_retry_cnt = SB_FW_UPDATE_FEC_ERROR_RETRY_CNT; + fw_update->offset += fw_update->step_size; + return S6_WRITE_BLOCK; +} + +static enum fw_update_state s8_write_end(struct fw_update_ctrl *fw_update) +{ + int rv; + + rv = send_subcmd(EC_SB_FW_UPDATE_END); + if (rv && (0 == fw_update->rv)) { + fw_update->rv = -1; + log_msg(fw_update, S8_WRITE_END, "Interface Error"); + } + + if (fw_update->rv) + return S10_TERMINAL; + + usleep(DELAY_US_END); + fw_update->busy_retry_cnt = SB_FW_UPDATE_BUSY_ERROR_RETRY_CNT; + return S9_READ_STATUS; +} + +static enum fw_update_state s9_read_status(struct fw_update_ctrl *fw_update) +{ + int rv; + + if (fw_update->busy_retry_cnt == 0) { + fw_update->rv = -1; + log_msg(fw_update, S9_READ_STATUS, "Busy"); + return S10_TERMINAL; + } + + rv = get_status(&fw_update->status); + if (rv) { + fw_update->rv = -1; + log_msg(fw_update, S9_READ_STATUS, "Interface Error"); + return S10_TERMINAL; + } + if ((fw_update->status.fw_update_mode == 1) || + (fw_update->status.busy == 1)) { + usleep(SB_FW_UPDATE_DEFAULT_DELAY); + fw_update->busy_retry_cnt--; + return S9_READ_STATUS; + } + log_msg(fw_update, S9_READ_STATUS, "Complete"); + fw_update->flags &= ~F_NEED_UPDATE; + return S10_TERMINAL; +} + +typedef enum fw_update_state (*fw_state_func)(struct fw_update_ctrl *fw_update); + +fw_state_func state_table[] = { s0_read_status, s1_read_battery_info, + s2_write_prepare, s3_read_status, + s4_write_update, s5_read_status, + s6_write_block, s7_read_status, + s8_write_end, s9_read_status }; + +/** + * Update Smart Battery Firmware + * + * @param fw_update struct fw_update_ctrl + * + * @return 0 if success, negative if error. + */ +static int ec_sb_firmware_update(struct fw_update_ctrl *fw_update) +{ + enum fw_update_state state; + + fw_update->err_retry_cnt = SB_FW_UPDATE_ERROR_RETRY_CNT; + fw_update->fec_err_retry_cnt = SB_FW_UPDATE_FEC_ERROR_RETRY_CNT; + fw_update->busy_retry_cnt = SB_FW_UPDATE_BUSY_ERROR_RETRY_CNT; + fw_update->step_size = SB_FW_UPDATE_CMD_WRITE_BLOCK_SIZE; + + state = S0_READ_STATUS; + while (state != S10_TERMINAL) + state = state_table[state](fw_update); + + if (fw_update->fw_img_hdr) + free(fw_update->fw_img_hdr); + + return fw_update->rv; +} + +#define GEC_LOCK_TIMEOUT_SECS 30 /* 30 secs */ +void usage(char *argv[]) +{ + printf("Usage: %s [check|update]\n" + " check: check if AC Adaptor is connected.\n" + " update: trigger battery firmware update.\n", + argv[0]); +} + +int main(int argc, char *argv[]) +{ + int rv = 0; + int op = OP_UNKNOWN; + uint8_t val = 0; + + if (argc != 2) { + usage(argv); + return -1; + } + + if (!strcmp(argv[1], "check")) + op = OP_CHECK; + else if (!strcmp(argv[1], "update")) + op = OP_UPDATE; + else { + op = OP_UNKNOWN; + usage(argv); + return -1; + } + + if (comm_init_dev(NULL)) { + printf("Couldn't initialize /dev.\n"); + return -1; + } + + if (comm_init_buffer()) { + fprintf(stderr, "Couldn't initialize buffers\n"); + return -1; + } + + fw_update.flags = 0; + rv = ec_readmem(EC_MEMMAP_BATT_FLAG, sizeof(val), &val); + if (rv <= 0) { + printf("EC Memmap read error:%d\n", rv); + goto out; + } + + rv = get_status(&fw_update.status); + if (rv) { + fw_update.rv = -1; + log_msg(&fw_update, S1_READ_INFO, "Interface Error"); + return S10_TERMINAL; + } + + if (fw_update.status.fw_update_mode) { + /* + * Previous battery firmware update was interrupted, + * and we may not detect the presence of AC correctly. + * Therefore, lie about our AC status. + */ + fw_update.flags |= F_AC_PRESENT; + printf("Assuming AC_PRESENT due to interrupted FW update\n"); + } else { + if (val & EC_BATT_FLAG_AC_PRESENT) { + fw_update.flags |= F_AC_PRESENT; + printf("AC_PRESENT\n"); + } + } + + if (val & EC_BATT_FLAG_DISCHARGING) { + fw_update.flags |= F_BATT_DISCHARGE; + printf("Battery is in discharge state\n"); + } + rv = ec_readmem(EC_MEMMAP_BATT_LFCC, sizeof(val), &val); + if (rv <= 0) { + printf("EC Memmap read error:%d\n", rv); + goto out; + } + if (val == 0) + fw_update.flags |= F_LFCC_ZERO; + + if (op == OP_UPDATE) + fw_update.flags |= F_UPDATE; + + fw_update.flags |= F_VERSION_CHECK; + + rv = ec_sb_firmware_update(&fw_update); + printf("Battery Firmware Update:0x%02x %s\n%s\n", fw_update.flags, + ((rv) ? "FAIL " : " "), fw_update.msg); + + /* Update battery firmware update interface to be protected */ + if (!(fw_update.flags & F_NEED_UPDATE)) + rv |= send_subcmd(EC_SB_FW_UPDATE_PROTECT); + + if (fw_update.flags & F_POWERD_DISABLED) + rv |= restore_power_management(); +out: + if (rv) + return -1; + else + return fw_update.flags & (F_LFCC_ZERO | F_NEED_UPDATE); +} diff --git a/util/ec_sb_firmware_update.h b/util/ec_sb_firmware_update.h new file mode 100644 index 0000000000..2757dca9a5 --- /dev/null +++ b/util/ec_sb_firmware_update.h @@ -0,0 +1,126 @@ +/* Copyright 2014 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Smart battery Firmware Update driver. + * Ref: Common Smart Battery System Interface Specification v8.0. + * + * cmd.0x35, Write Word + * 0x1000: Prepare to Update + * 0x2000: End of Update + * 0xF000: Update Firmware + * + * cmd.0x35, Read Word + * Firmware Update Status + * + * cmd.0x36 Write Block + * Send 32 byte firmware image + * + * cmd.0x37 Read Word + * Get Battery Information + * sequence:=b1,b0,b3,b2,b5,b5,b7,b6 + * + * Command Sequence for Battery FW Update + * + * 0. cmd.0x35.read + * 1. cmd.0x37.read + * 2. cmd.0x35.write.0x1000 + * 3. cmd.0x35.read.status (optional) + * 4. cmd.0x35.write.0xF000 + * 5. cmd.0x35.read.status + * if bit8-0, go to step 2 + * 6. cmd.0x36.write.32byte + * 7. cmd.0x35.read.status + * if FEC.b13=1, go to step 6 + * if fatal.b12=1, go to step 2 + * if b11,b10,b9,b2,b1,b0; go to step 1 + * if b5,b3; go to step 8 + * (repeat 6,7) + * 8. cmd.0x36.write.0x2000 + * 9. cmd.0x35.read.status + */ + +#ifndef __UTIL_EC_SB_FIRMWARE_UPDATE_H +#define __UTIL_EC_SB_FIRMWARE_UPDATE_H + +struct sb_fw_header { + uint8_t signature[4]; /* "BTFW" */ + uint16_t hdr_version; /* 0x0100 */ + uint16_t pkg_version_major_minor; + + uint16_t vendor_id; /* 8,9 */ + uint16_t battery_type; /* A B */ + + uint16_t fw_version; /* C D */ + uint16_t data_table_version; /* E F */ + uint32_t fw_binary_offset; /*0x10 0x11 0x12 0x13 */ + uint32_t fw_binary_size; /* 0x14 0x15 0x16 0x17 */ + uint8_t checksum; /* 0x18 */ +}; + +/** + * sb.fw.update.cmd.0x35, Read Word + * Firmware Update Status + */ +struct sb_fw_update_status { + uint16_t v_fail_maker_id : 1; /* b0 */ + uint16_t v_fail_hw_id : 1; /* b1 */ + uint16_t v_fail_fw_version : 1; /* b2 */ + uint16_t v_fail_permanent : 1; /* b3 */ + + uint16_t rsvd5 : 1; /* b4 */ + uint16_t permanent_failure : 1; /* b5 */ + uint16_t abnormal_condition : 1; /* b6 */ + uint16_t fw_update_supported : 1; /* b7 */ + + uint16_t fw_update_mode : 1; /* b8 */ + uint16_t fw_corrupted : 1; /* b9 */ + uint16_t cmd_reject : 1; /* b10 */ + uint16_t invalid_data : 1; /* b11 */ + + uint16_t fw_fatal_error : 1; /* b12 */ + uint16_t fec_error : 1; /* b13 */ + uint16_t busy : 1; /* b14 */ + uint16_t rsvd15 : 1; /* b15 */ +} __packed; + +/** + * sb.fw.update.cmd.0x37 Read Word + * Get Battery Information + * sequence:=b1,b0,b3,b2,b5,b5,b7,b6 + */ +struct sb_fw_update_info { + uint16_t maker_id; /* b0, b1 */ + uint16_t hardware_id; /* b2, b3 */ + uint16_t fw_version; /* b4, b5 */ + uint16_t data_version; /* b6, b7 */ +} __packed; + +/** + * smart.battery.maker.id + */ +enum sb_maker_id { + sb_maker_id_lgc = 0x0001, /* b0=0; b1=1 */ + sb_maker_id_panasonic = 0x0002, + sb_maker_id_sanyo = 0x0003, + sb_maker_id_sony = 0x0004, + sb_maker_id_simplo = 0x0005, + sb_maker_id_celxpert = 0x0006, +}; + +/* + * Ref: Common Smart Battery System Interface Specification v8.0 page 21-24. + * case 1. If permanent error:b5,b3, go to setp8. + * case 2. If error:b11,b10,b9 b2,b1,b0, go to step1. Retry < 3 times. + * case 3. If firmware update fatal error:b12, go to step2. Retry < 3 times. + * In order to simply the implementation, I merged case 2 and 3. + * If firmware update fatal error:b12, go to step1 as well. + * case 4. If error.FEC.b13=1, go to step6. Retry < 3 times. + * case 5. If battery interface is busy, retry < 10 times. + * Delay 1 second between retries. + */ +#define SB_FW_UPDATE_ERROR_RETRY_CNT 2 +#define SB_FW_UPDATE_FEC_ERROR_RETRY_CNT 2 +#define SB_FW_UPDATE_BUSY_ERROR_RETRY_CNT 4 + +#endif |