summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorAaron Massey <aaronmassey@google.com>2023-04-03 14:57:28 +0000
committerAaron Massey <aaronmassey@google.com>2023-04-03 15:01:03 +0000
commit9bdc64603cbe1871b510a77168e4e7ec745652a4 (patch)
tree73fb613a5bf12f155780467318ea4de829c1869f /util
parent48fd3571354bfb9ae56839f5dace3b2c80fc1c01 (diff)
downloadchrome-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.mk5
-rw-r--r--util/ec_sb_firmware_update.cc803
-rw-r--r--util/ec_sb_firmware_update.h126
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