diff options
author | Sheng-Liang Song <ssl@chromium.org> | 2014-07-26 19:56:43 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-09-05 07:20:49 +0000 |
commit | b550e7cbb406da519348e483291e31bfddbf5ba7 (patch) | |
tree | fad2819bdf177cc359fecae2461d21dd13c36d14 | |
parent | 1cf5ca93b63dc7d43dcc799bce4436ecaf1d4876 (diff) | |
download | chrome-ec-b550e7cbb406da519348e483291e31bfddbf5ba7.tar.gz |
EC: Add util for battery firmware update
Ref: Common Smart Battery System Inferface Specification v8.0.
Implemented smart battery firmware update util based the above spec.
BUG=chrome-os-partner:24741
CQ-DEPEND=CL:210032
CQ-DEPEND=CL:210033
CQ-DEPEND=CL:215720
BRANCH=ToT
TEST=Verified LGC & Simplo Battery Update on glimmer
Change-Id: Ia61a49f4643ea349d42a4b87d6010c1ac011729b
Signed-off-by: Sheng-Liang Song <ssl@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/205324
Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r-- | util/build.mk | 7 | ||||
-rw-r--r-- | util/ec_sb_firmware_update.c | 652 | ||||
-rw-r--r-- | util/ec_sb_firmware_update.h | 139 |
3 files changed, 796 insertions, 2 deletions
diff --git a/util/build.mk b/util/build.mk index 56cd6a2a59..51e5104304 100644 --- a/util/build.mk +++ b/util/build.mk @@ -1,12 +1,12 @@ # -*- makefile -*- -# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # Host tools build # -host-util-bin=ectool lbplay burn_my_ec stm32mon +host-util-bin=ectool lbplay burn_my_ec stm32mon ec_sb_firmware_update comm-objs=$(util-lock-objs:%=lock/%) comm-host.o comm-dev.o ifeq ($(CHIP),mec1322) @@ -17,6 +17,9 @@ else comm-objs+=comm-i2c.o endif ectool-objs=ectool.o ectool_keyscan.o misc_util.o ec_flash.o $(comm-objs) ../common/sha1.o + +ec_sb_firmware_update-objs=ec_sb_firmware_update.o $(comm-objs) misc_util.o + lbplay-objs=lbplay.o $(comm-objs) burn_my_ec-objs=ec_flash.o $(comm-objs) misc_util.o diff --git a/util/ec_sb_firmware_update.c b/util/ec_sb_firmware_update.c new file mode 100644 index 0000000000..ee538cbc8a --- /dev/null +++ b/util/ec_sb_firmware_update.c @@ -0,0 +1,652 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lock/gec_lock.h" +#include "comm-host.h" +#include "misc_util.h" +#include "ec_sb_firmware_update.h" +#include "ec_commands.h" +#include <unistd.h> + +#define DPRINTF(fmt, ...) \ + do {\ + if (debug)\ + printf("SBFW: " fmt, ## __VA_ARGS__);\ + } while (0) + +/* Debug EC Smart Battery Firmwarwe Update */ +static int debug; + +/* + * Simplo Battery: Required 10 seconds delay for 1st 10 block write + * unit in seconds + */ +static int delay_x_us = 9000000; + +/* + * Simplo Battery: Additional delays are required after each 32-byte write + * unit in useconds + */ +static int delay_y_us = 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 +}; + +struct fw_update_ctrl { + int size; /* size of battery firmware image */ + char *ptr; /* current pointer to 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 msg[256]; +}; + +/* + * Global Firmware Update Control Data Structure + */ +static struct fw_update_ctrl fw_update; + +static void print_battery_firmware_image_hdr( + struct sb_fw_header *hdr) +{ + printf("%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("vendor_id:%04X battery_type:%04X fw_ver:%04X tbl_ver:%04X\n", + hdr->vendor_id, hdr->battery_type, hdr->fw_version, + hdr->data_table_version); + + printf("bin off:%08X size:%08X chk_sum:%02X\n", + hdr->fw_binary_offset, hdr->fw_binary_size, hdr->checksum); +} + +static void print_info(struct sb_fw_update_info *info) +{ + printf("maker_id:0x%X hw_id:0x%X fw_ver:0x%X d_ver:0x%X\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) +{ + return (((hdr->fw_version == 0xFFFF) + || (hdr->fw_version > p->fw_version)) && + ((hdr->data_table_version == 0xFFFF) + || (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_need_update_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_version(hdr, info) + + && check_battery_firmware_image_checksum(hdr); +} + +static int get_status(struct sb_fw_update_status *status) +{ + int rv = EC_RES_SUCCESS; + int i = 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 { + 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) && (i++ < 3)); + + if (rv < 0) { + fprintf(stderr, + "Firmware Update Get Status Error\n"); + 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; + + 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; + rv = ec_command(EC_CMD_SB_FW_UPDATE, 0, + param, sizeof(struct ec_sb_fw_update_header), + resp, SB_FW_UPDATE_CMD_INFO_SIZE); + if (rv < 0) { + fprintf(stderr, + "Firmware Update Get Info Error\n"); + 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) { + fprintf(stderr, + "Firmware Update subcmd:%d Error\n", subcmd); + return -EC_RES_ERROR; + } + return EC_RES_SUCCESS; +} + +static int write_block(const uint8_t *ptr, int bsize) +{ + int rv; + struct ec_params_sb_fw_update *param = + (struct ec_params_sb_fw_update *)ec_outbuf; + + memcpy(param->write.data, ptr, 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) { + fprintf(stderr, + "Firmware Update Write Error offset@%p\n", ptr); + return -EC_RES_ERROR; + } + return EC_RES_SUCCESS; +} + +static void dump_data(uint8_t *data, int offset, int size) +{ + int i = 0; + 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; + sprintf(fw_update->msg, + "Firmware Udpate interface busy retry error!\n"); + return S10_TERMINAL; + } + + fw_update->busy_retry_cnt--; + + fw_update->rv = get_status(&fw_update->status); + if (fw_update->rv) { + fw_update->rv = -1; + sprintf(fw_update->msg, + "Firmware Udpate interface protected!\n"); + return S10_TERMINAL; + } + + if (debug) + print_status(&fw_update->status); + + if (!((fw_update->status.abnormal_condition == 0) + && (fw_update->status.fw_update_supported == 1))) { + sprintf(fw_update->msg, + "Firmware Udpate is not supported!\n"); + return S10_TERMINAL; + } + if (fw_update->status.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; + sprintf(fw_update->msg, + "Firmware Udpate interface busy retry error!\n"); + return S10_TERMINAL; + } + + fw_update->err_retry_cnt--; + + rv = get_info(&fw_update->info); + if (rv) { + fw_update->rv = -1; + return S10_TERMINAL; + } + + if (debug) + print_info(&fw_update->info); + + rv = get_status(&fw_update->status); + if (rv) { + fw_update->rv = -1; + return S10_TERMINAL; + } + + rv = check_if_need_update_fw(fw_update->fw_img_hdr, &fw_update->info); + if (rv == 0) { + printf("ERROR:Battery firmware is not valid!\n"); + print_info(&fw_update->info); + print_battery_firmware_image_hdr(fw_update->fw_img_hdr); + fw_update->rv = EC_RES_INVALID_PARAM; + return S10_TERMINAL; + } + return S2_WRITE_PREPARE; +} + +static enum fw_update_state s2_write_prepare(struct fw_update_ctrl *fw_update) +{ + int rv; + DPRINTF("cmd.0x35 write word 0x1000\n"); + rv = send_subcmd(EC_SB_FW_UPDATE_PREPARE); + if (rv) { + fw_update->rv = -1; + 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; + return S10_TERMINAL; + } + return S4_WRITE_UPDATE; + +} + +static enum fw_update_state s4_write_update(struct fw_update_ctrl *fw_update) +{ + int rv; + DPRINTF("cmd.0x35 write word 0xF000\n"); + rv = send_subcmd(EC_SB_FW_UPDATE_BEGIN); + if (rv) { + fw_update->rv = -1; + return S10_TERMINAL; + } + usleep(500000); + 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; + 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; + + DPRINTF("Write size 0x%X total_size:0x%X\n", + fw_update->step_size, fw_update->size); + + 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 & 0x1FFF) == 0x000) + printf("\n%X\n", offset); + else + printf("."); + + if (fw_update->fec_err_retry_cnt == 0) { + fw_update->rv = -1; + return S10_TERMINAL; + } + fw_update->fec_err_retry_cnt--; + + rv = write_block(fw_update->ptr+offset, bsize); + if (rv) { + fw_update->rv = -1; + return S10_TERMINAL; + } + + if (delay_x_us || delay_y_us) { + if (offset <= fw_update->step_size * 10) + usleep(delay_x_us); + else + usleep(delay_y_us); + } + 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; + + bsize = fw_update->step_size; + do { + 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; + return S10_TERMINAL; + } + } while (fw_update->status.busy); + + if (fw_update->status.fec_error) { + dump_data(fw_update->ptr+offset, offset, bsize); + print_status(&fw_update->status); + fw_update->rv = -1; + return S6_WRITE_BLOCK; + } + if (fw_update->status.fw_fatal_error) { + dump_data(fw_update->ptr+offset, offset, bsize); + print_status(&fw_update->status); + fw_update->rv = -1; + return S2_WRITE_PREPARE; + } + 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; + 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) { + + dump_data(fw_update->ptr+offset, offset, bsize); + print_status(&fw_update->status); + fw_update->rv = -1; + 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) { + fw_update->rv = -1; + sprintf(fw_update->msg, "SB FW Update End Error\n"); + return S10_TERMINAL; + } + + /* Note: Sleep is required! */ + usleep(1000000); + return S9_READ_STATUS; +} + +static enum fw_update_state s9_read_status(struct fw_update_ctrl *fw_update) +{ + int rv; + /* Poll for completion */ + rv = get_status(&fw_update->status); + if (rv) { + fw_update->rv = -1; + sprintf(fw_update->msg, + "SB FW Update End get status Error: rv:%d\n", rv); + return S10_TERMINAL; + } + if ((fw_update->status.fw_update_mode == 1) + || (fw_update->status.busy == 1)) { + return S9_READ_STATUS; + } + 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 +}; + +int ec_sb_firmware_update(const char *fw_image_name) +{ + enum fw_update_state state; + int size; + char *buf; + + 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; + + /* Read the input file */ + DPRINTF("\n\n==> Read File:%s\n", fw_image_name); + buf = read_file(fw_image_name, &size); + if (!buf) { + fprintf(stderr, + "Firmware Update: Load Firmware Image[%s] Error\n", + fw_image_name); + return -1; + } + fw_update.size = size; + fw_update.ptr = buf; + fw_update.fw_img_hdr = (struct sb_fw_header *)buf; + if (debug) + 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) { + fprintf(stderr, + "Load Firmware Image[%s] Error offset:%d size:%d\n", + fw_image_name, + fw_update.fw_img_hdr->fw_binary_offset, + fw_update.size); + return -1; + } + + state = S0_READ_STATUS; + while (state != S10_TERMINAL) + state = state_table[state](&fw_update); + + free(buf); + if (fw_update.rv) + printf("\n\n==> Firmware:%s Update Failed:%d [%s]\n", + fw_image_name, + fw_update.rv, + fw_update.msg); + else + printf("\n\n==> Firmware:%s Update Complete.\n", + fw_image_name); + + return fw_update.rv; +} + +#define GEC_LOCK_TIMEOUT_SECS 30 /* 30 secs */ + +int main(int argc, char *argv[]) +{ + int rv = 0, interfaces = COMM_LPC; + const char *test = "normal"; + if (argc < 2) { + fprintf(stderr, + "Usage: %s <fw_filename> <test> " + "[initial_tx_delay] [tx_delay] [debug]\n", argv[0]); + return -1; + } + + if (argc >= 3) + test = argv[2]; + + if (argc >= 4) + delay_x_us = atoi(argv[3]); + + if (argc >= 5) + delay_y_us = atoi(argv[4]); + + if (argc >= 6) + debug = atoi(argv[5]); + + if (acquire_gec_lock(GEC_LOCK_TIMEOUT_SECS) < 0) { + fprintf(stderr, "Could not acquire GEC lock.\n"); + exit(1); + } + + if (comm_init(interfaces)) { + fprintf(stderr, "Couldn't find EC\n"); + goto out; + } + + DPRINTF("fw_filename:%s\n", argv[1]); + rv = ec_sb_firmware_update(argv[1]); + + /* set to protect mode if not running a fw update test */ + if (strcmp(test, "test")) + rv |= send_subcmd(EC_SB_FW_UPDATE_PROTECT); +out: + release_gec_lock(); + return rv; +} diff --git a/util/ec_sb_firmware_update.h b/util/ec_sb_firmware_update.h new file mode 100644 index 0000000000..8737756cd4 --- /dev/null +++ b/util/ec_sb_firmware_update.h @@ -0,0 +1,139 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * 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 __CROS_EC_SB_FIRMWARE_UPDATE_H__ +#define __CROS_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, +}; + +/* if permanent error:b5,b3; go to setp8 */ +#define SB_FW_UPDATE_PERMANENT_ERROR_MASK 0x0028 + +/* if firmware update fatal error:b12; go to step2 */ +#define SB_FW_UPDATE_FW_FATAL_ERROR_MASK 0x1000 +#define SB_FW_UPDATE_FW_FATAL_ERROR_RETRY_CNT 1 /* Retry Error cnt*/ + +/* if error:b11,b10,b9 b2,b1,b0; go to step1 */ +#define SB_FW_UPDATE_ERROR_MASK 0x0E07 +#define SB_FW_UPDATE_ERROR_RETRY_CNT 1 /* Retry Error cnt*/ + +/* if FEC.b13=1, go to step6 */ +#define SB_FW_UPDATE_FEC_ERROR_MASK 0x2000 /* b13 */ +#define SB_FW_UPDATE_FEC_ERROR_RETRY_CNT 1 /* b13.FEC retry cnt*/ + +/* if busy; retry 10 times */ +#define SB_FW_UPDATE_BUSY_ERROR_MASK 0x4000 /* b14 */ +#define SB_FW_UPDATE_BUSY_ERROR_RETRY_CNT 1 /* b14.busy retry cnt*/ + +/** + * Update Smart Battery Firmware + * + * @param fw_image_name firmware image name + * + * @return 0 if success, negative if error. + */ +int ec_sb_firmware_update(const char *fw_image_name); + +#endif |