Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

656 lines (525 sloc) 15.085 kb
/*
bqupdate.c - a user-space utility for updating bq27x00 fuel gauges.
Please have a look at the README
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "i2c-dev.h"
#include <unistd.h>
//global vars
int i2c_dev_handle;
static void help(void)
{
fprintf(stderr,
"\nUsage: bqupdate I2CBUS GOLDEN_DFI\n"
" I2CBUS is an integer - look in /sys/class/i2c-dev\n"
" FILE is a 'Golden Image' dfi generated by b-queasy\n");
}
//use to write single bytes
int bq_write(char reg, char data)
{
int err = 0;
char i2c_buf[2];
i2c_buf[0] = reg;
i2c_buf[1] = data;
err = write(i2c_dev_handle, i2c_buf, 2);
if (err != 2) {
fprintf(stderr, "\nError: 0x%x - i2c transaction failure! a \n", err);
return -1;
}
return 0;
}
int bq_read(char reg, int bytes, char *i2c_buf)
{
int err = 0;
i2c_buf[0] = reg;
err = write(i2c_dev_handle, i2c_buf, 1);
if (err != 1) {
fprintf(stderr, "\nError: 0x%x - i2c transaction failure! b \n", err);
}
err = read(i2c_dev_handle, i2c_buf, bytes);
if (err != bytes) {
fprintf(stderr, "\nError: 0x%x - i2c transaction failure! bb \n", err);
return -1;
}
return 0;
}
int error_check(void)
{
int err;
char i2c_buf[2];
i2c_buf[0] = 0x66;
err = write(i2c_dev_handle, i2c_buf, 1);
if (err != 1) {
fprintf(stderr, "Error reading 0x66: %x\n", err);
}
err = read(i2c_dev_handle, i2c_buf, 1);
if (err != 1) {
fprintf(stderr, "Error reading 0x66 %x\n", err);
}
if (i2c_buf[0] != 0x00) {
printf("ErrorCheck: Error - 0x66: %x\n", i2c_buf[0]);
return -1;
}
//else {
// printf("ErrorCheck: Clean - 0x66: %x\n", i2c_buf[0]);
//}
return 0;
}
int main(int argc, char *argv[])
{
//i2c vars
char i2c_dev_name[20];
long i2c_bus;
char i2c_buf[100];
int i2c_address;
int error_reg;
//gimage vars
char gimage_name[50];
FILE *gimage_file;
long gimage_length;
char *gimage_data;
//some flash related vars
char yIFRowData_0[97];
char yIFRowData_1[97];
char yRowData[33];
int iRow;
long int checksum;
long int checksum0;
long int checksum1;
long int dfi_checksum;
int dfi_checksum_rd;
FILE *ifrow_file;
//other vars
int i;
int err;
int loops;
int write_loops;
int version;
//Grab the bus ID
if (argc < 2) {
fprintf(stderr, "\nError: Please specify i2c bus ID\n");
help();
exit(1);
}
i2c_bus = strtol(argv[1], NULL, 0);
if (i2c_bus < 0 || i2c_bus > 0xff) {
fprintf(stderr, "\nInvalid bus ID\n");
exit(1);
}
//Grab the golden image file name
if (argc < 3) {
fprintf(stderr, "\nError: Please specify a golden image dfi\n");
help();
exit(1);
}
strcpy(gimage_name, argv[2]);
if (gimage_name == NULL) {
fprintf(stderr, "\nInvalid golden image\n");
exit(1);
}
//Throw error for too many args
if (argc > 3) {
fprintf(stderr, "\nError: Too many arguments!\n");
help();
exit(1);
}
/* Read the golden image */
printf("\nReading %s...\n", gimage_name);
gimage_file = fopen(gimage_name, "r");
// check length
fseek(gimage_file, 0 , SEEK_END);
gimage_length = ftell(gimage_file);
fseek(gimage_file, 0, SEEK_SET);
if (gimage_length != 0x401) {
printf("DFI might be wrong size, could cause problems...\n");
}
// read file
gimage_data = (char*)malloc(0x3ff);
fread(gimage_data, 1, 0x3ff, gimage_file);
fclose(gimage_file);
/* i2c setup */
i2c_address = 0x55;
sprintf(i2c_dev_name, "/dev/i2c-%d", i2c_bus);
i2c_dev_handle = open(i2c_dev_name, O_RDWR);
if (i2c_dev_handle < 0) {
fprintf(stderr, "\nError: Failed to open %s!\n", i2c_dev_name);
goto ESCAPE;
}
err = ioctl(i2c_dev_handle, I2C_SLAVE, i2c_address);
if (err < 0) {
fprintf(stderr, "\nError: Some kind of ioctl error!\n");
goto ESCAPE;
}
/* check firmware version */
version = 0;
err = bq_write(0x00, 0x02);
err = bq_write(0x01, 0x00);
err |= bq_read(0x00, 2, i2c_buf);
if (!err)
version = (i2c_buf[0] + (i2c_buf[1] << 8));
else
printf("Unable to get version.");
printf("\nCurrent firmware version: %x \n", version);
if (version != 0x120) {
printf("Firmware version mismatch! Exiting\n");
exit(1);
}
/* begin IF copy */
printf("Enter ROM mode...\n");
err = bq_write(0x00, 0x00);
err |= bq_write(0x01, 0x0F);
if (err != 0) {
fprintf(stderr, "\nError: Might still be in ROM mode!\n");
goto ESCAPE_ROM;
}
//error_check();
printf("Begin IF copy...\n");
iRow = 0;
while (iRow < 2) {
// setup i2c to use address 0x0b
i2c_address = 0x0b;
err = ioctl(i2c_dev_handle, I2C_SLAVE, i2c_address);
if (err < 0) { fprintf(stderr, "\nError: Some kind of super tragic ioctl error!\n"); goto ESCAPE_ROM; }
usleep(20);
// => write 0x00/0x00
err = bq_write(0x00, 0x00);
// => write 0x00/0x00+iRow
err |= bq_write(0x01, 0x00+iRow);
// => write 0x02/0x00
err |= bq_write(0x02, 0x00);
// => write 0x64/0x00+iRow
err |= bq_write(0x64, 0x00+iRow);
// => write 0x65/0x00
err |= bq_write(0x65, 0x00);
// => read 96 bytes from 0x04 to 0x63 into yIFRowData_iRow
if (iRow == 0)
err |= bq_read(0x04, 96, yIFRowData_0);
else if (iRow == 1)
err |= bq_read(0x04, 96, yIFRowData_1);
if (err != 0x00) {
printf("Error reading from IF row %d...\n", iRow);
goto ESCAPE_ROM;
}
//sleep 20ms
usleep(20000);
iRow++;
}
printf("IF copy successful.\n");
/* save IF rows to a file */
ifrow_file = fopen("ifrows.bin", "wb");
fwrite(yIFRowData_0, sizeof(yIFRowData_0[0]), sizeof(yIFRowData_0)/sizeof(yIFRowData_0[0]), ifrow_file);
fwrite(yIFRowData_1, sizeof(yIFRowData_1[0]), sizeof(yIFRowData_1)/sizeof(yIFRowData_1[0]), ifrow_file);
fclose(ifrow_file);
IF_ERASE:
/* Begin erase */
printf("Begin IF erase...\n");
error_reg = -1;
loops = 0;
while (error_reg != 0) {
// => write 0x00/0x03
err = bq_write(0x00, 0x03);
// => write 0x01/0x00
err = bq_write(0x01, 0x00);
// => write 0x64/0x03
err |= bq_write(0x64, 0x03);
// => write 0x65/0x00
err |= bq_write(0x65, 0x00);
//sleep 20ms
usleep(20000);
if (!err) {
printf("Row %d successfully erased\n", iRow);
}
else {
printf("Encountered an error erasing row %d\n", iRow);
goto ESCAPE;
}
//check 0x66
error_reg = error_check();
if (!error_reg) {
printf("Error check verifies first two lines are clear.\n");
}
else {
loops ++;
printf("Attempt %d: Invalid value in reg 0x66...\n", loops);
}
if (loops > 5) {
printf("Flash erase failed, check setup...\n");
goto ESCAPE;
}
}
//sleep 20ms
usleep(20000);
/* Mass erase data flash */
write_loops = 0;
WRITE_IMG:
printf("Begin mass erase...\n");
checksum = 0;
dfi_checksum = 0;
error_reg = -1;
loops = 0;
while (error_reg != 0) {
// => write 0x00/0x0C
err = bq_write(0x00, 0x0C);
// => write 0x01/0x00
err = bq_write(0x01, 0x00);
// => write 0x04/0x83
err |= bq_write(0x04, 0x83);
// => write 0x05/0xde
err |= bq_write(0x05, 0xde);
// => write 0x64/0x6D
err |= bq_write(0x64, 0x6D);
// => write 0x65/0x01
err |= bq_write(0x65, 0x01);
//sleep 200ms
usleep(200000);
//read back 0x66
error_reg = error_check();
if (!error_reg) {
printf("Mass erase successful.\n");
}
else {
loops ++;
printf("Attempt %d: Invalid value in reg 0x66...\n", loops);
}
if (loops > 5) {
printf("Mass erase failed, check setup...\n");
goto WRITEBACK_ROWS;
}
}
/* Begin writing image */
printf("\nBegin writing image:\n");
iRow = 0;
error_reg = -1;
while (iRow < 32) {
// copy 32 bytes of data from gimage_data into yRowData
checksum = 0;
yRowData[0] = 0x04; //write row command
//printf("Row %2d: ", iRow);
for (i = 0; i < 32; i++) {
yRowData[i+1] = gimage_data[32*iRow+i];
checksum += gimage_data[32*iRow+i];
//printf("%2x ", yRowData[i]);
}
//printf("%2x", yRowData[32]);
// => write 0x00/0x0A (program row command)
err = bq_write(0x00, 0x0A);
// => write 0x01/iRow (designate row to write)
err |= bq_write(0x01, iRow);
// => write row command followed by 32 bytes of data
err = write(i2c_dev_handle, yRowData, 33);
if (err != 33) { fprintf(stderr, "\nError: i2c transaction failure! 3 \n"); goto WRITEBACK_ROWS; }
// generate checksums
dfi_checksum = (dfi_checksum + checksum) % 0x10000;
checksum += 0x0A + iRow;
checksum = checksum % 0x10000;
// => write checksum (LSB)
err = bq_write(0x64, (checksum & ~(0xFF00)));
// => write checksum (MSB)
err |= bq_write(0x65, (checksum >> 8));
//sleep 2ms
usleep(2000);
//read back 0x66
error_reg = error_check();
if (!error_reg) {
printf("x"); //row successfully written
}
else {
write_loops ++;
printf("Write attempt %d failed at Row %d...\n", write_loops, iRow);
if (write_loops > 5) {
printf("Data Flash write failed: check setup...\n");
goto WRITEBACK_ROWS;
}
else {
printf("Retrying DF Write...\n\n\n");
goto WRITE_IMG;
}
}
iRow ++;
}
printf("\n\nImage write completed, verifying checksum...\n");
/* verify checksum */
// => write 0x00/0x08
err = bq_write(0x00, 0x08);
// => write 0x64/0x08
err |= bq_write(0x64, 0x08);
// => write 0x65/0x00
err |= bq_write(0x65, 0x00);
//sleep 20ms
usleep(20000);
//read back checksum
err = bq_read(0x04, 2, i2c_buf);
dfi_checksum_rd = ((i2c_buf[1] << 8) | i2c_buf[0]);
//printf("calculated checksum: 0x%x\n", dfi_checksum);
//printf(" read checksum: 0x%x\n", dfi_checksum_rd);
if (dfi_checksum == dfi_checksum_rd){
printf("Checksum verified - Image write successful.\n");
}
else {
printf("Checksum invalid - write error...\n");
}
loops = 0;
WRITEBACK_ROWS:
/* Begin writing back IF flash */
printf("Begin writing back IF rows...\n");
iRow = 1;
checksum1 = 0;
checksum0 = 0;
//prep yIFRowData_0 and 1
for (i = 96; i > 0; i--) {
checksum0 += yIFRowData_0[i-1];
yIFRowData_0[i] = yIFRowData_0[i-1];
}
yIFRowData_0[0] = 0x04;
checksum0 = (0x02 + 0 + checksum0) % 0x10000;
for (i = 96; i > 0; i--) {
checksum1 += yIFRowData_1[i-1];
yIFRowData_1[i] = yIFRowData_1[i-1];
}
yIFRowData_1[0] = 0x04;
checksum1 = (0x02 + 1 + checksum1) % 0x10000;
while (iRow > -1) {
// => write 0x00/0x02
err = bq_write(0x00, 0x02);
// => write 0x01/0x00+iRow
err |= bq_write(0x01, 0x00+iRow);
// => write 0x02/0x00
err |= bq_write(0x02, 0x00);
// => write 0x04/iRow_data
if (iRow == 1) {
err = write(i2c_dev_handle, yIFRowData_1, 97);
checksum = checksum1;
}
else if (iRow == 0) {
err = write(i2c_dev_handle, yIFRowData_0, 97);
checksum = checksum0;
}
if (err != 97) { fprintf(stderr, "\nError: i2c transaction failure! 4 \n"); goto ESCAPE; }
// => write checksum (LSB)
err = bq_write(0x64, (checksum & ~(0xFF00)));
// => write checksum (MSB)
err |= bq_write(0x65, (checksum >> 8));
//sleep 20ms
usleep(20000);
//read back 0x66
error_reg = error_check();
if (!error_reg) {
printf("IF Row %d written back successfully\n", iRow); //row successfully written
iRow --;
}
else {
loops ++;
printf("Write attempt %d for IF row %d failed: 0x%x...\n", loops, iRow, error_reg);
if (loops > 2) {
printf("IF row write back failed: check setup...\n");
goto ESCAPE;
}
else {
printf("Retrying IF row write back...\n\n");
}
}
}
printf("Finished updating.\n\n");
usleep(20);
/* exit ROM mode */
printf("Exiting ROM mode...\n");
err = bq_write(0x00, 0x0F);
err |= bq_write(0x01, 0x00);
err |= bq_write(0x64, 0x0F);
err |= bq_write(0x65, 0x00);
/* Issue RESET IT ENABLE and SEALED commands */
printf("Issuing RESET, IT_ENABLE and SEALED commands...\n\n");
// Change address back to 0x55
usleep(20000);
i2c_address = 0x55;
err = ioctl(i2c_dev_handle, I2C_SLAVE, i2c_address);
if (err < 0) { fprintf(stderr, "\nError: Some kind of super tragic ioctl error!\n"); goto ESCAPE; }
usleep(20000);
// RESET
err = bq_write(0x00, 0x41);
err |= bq_write(0x01, 0x00);
if (!err)
printf("Successfully issued RESET on address 0x55\n");
else
printf("Unable to issue RESET on address 0x55\n");
usleep(20000);
/*
// IT ENABLE
err = bq_write(0x00, 0x21);
err |= bq_write(0x01, 0x00);
if (!err)
printf("Successfully issued IT ENABLE on address 0x55\n");
else
printf("Unable to issue IT_ENABLE on address 0x55\n");
// SEALED
err = bq_write(0x00, 0x20);
err |= bq_write(0x01, 0x00);
if (!err)
printf("Successfully issued SEALED on address 0x55\n");
else
printf("Unable to issue SEALED on address 0x55\n");
*/
close(i2c_dev_handle);
printf("bqupdate completed.\n");
exit(0);
ESCAPE:
printf("Encountered escape condition: Exiting...\n");
close(i2c_dev_handle);
exit(1);
ESCAPE_ROM:
printf("Encountered escape condition: Attempting to exit ROM mode...\n");
i2c_address = 0x0b;
err = ioctl(i2c_dev_handle, I2C_SLAVE, i2c_address);
if (err < 0) {
fprintf(stderr, "\nError: Some kind of super tragic ioctl error!\n");
goto ESCAPE;
}
err = bq_write(0x00, 0x0F);
err |= bq_write(0x01, 0x00);
err |= bq_write(0x64, 0x0F);
err |= bq_write(0x65, 0x00);
if (!err)
printf("Successfully exitied ROM mode\n");
else
printf("Unable to cleanly exit ROM mode\n");
//error_check();
close(i2c_dev_handle);
exit(1);
READ_IFBACKUP:
/* read the row backup */
printf("\nReading %s...\n", "ifrows.bin");
ifrow_file = fopen("ifrows.bin", "r");
err = fread(yIFRowData_0, 1, 96, ifrow_file);
printf("\n read %d bytes...", err);
fread(yIFRowData_1, 1, 1, ifrow_file);
err = fread(yIFRowData_1, 1, 96, ifrow_file);
printf("\n read %d bytes...\n", err);
fclose(ifrow_file);
printf("Read Row 0: ");
for (i = 0; i < 97; i++) {
printf("%2x ", yIFRowData_0[i]);
}
printf("\n");
printf("Read Row 1: ");
for (i = 0; i < 97; i++) {
printf("%2x ", yIFRowData_1[i]);
}
printf("\n");
loops = 0;
goto IF_ERASE;
}
Jump to Line
Something went wrong with that request. Please try again.