From 0c5c827d0b67b0c8e5483069c3400152d36ba67c Mon Sep 17 00:00:00 2001 From: Kostas Missos Date: Thu, 28 Jun 2018 04:05:19 +0300 Subject: [PATCH] Add battery/charge info + bugfixes --- README.md | 2 +- ipl/gfx.c | 3 +- ipl/main.c | 146 ++++++++++++++++++++++++++++- ipl/max17050.c | 243 ++++++++++++++----------------------------------- ipl/max17050.h | 17 +--- ipl/tui.c | 2 +- 6 files changed, 216 insertions(+), 197 deletions(-) diff --git a/README.md b/README.md index fe1f1e9..c913b22 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Greetings to: fincs, hexkyz, SciresM, Shiny Quagsire, WinterMute. Open source and free packages used: - FatFs R0.13a, Copyright (C) 2017, ChaN - bcl-1.2.0, Copyright (C) 2003-2006, Marcus Geelnard - - Atmosphére (se_calculate_sha256), Copyright (C) 2018, Atmosphére-NX + - Atmosphère (se_calculate_sha256), Copyright (C) 2018, Atmosphère-NX ___ .-' `'. diff --git a/ipl/gfx.c b/ipl/gfx.c index 59d7658..79bfcef 100755 --- a/ipl/gfx.c +++ b/ipl/gfx.c @@ -218,7 +218,8 @@ void gfx_putc(gfx_con_t *con, char c) con->y = 0; } break; - case 8: + case 8: + default: if (c >= 32 && c <= 126) { u8 *cbuf = (u8 *)&_gfx_font[8 * (c - 32)]; diff --git a/ipl/main.c b/ipl/main.c index c96eb55..6931c5d 100755 --- a/ipl/main.c +++ b/ipl/main.c @@ -365,6 +365,7 @@ void print_fuseinfo() if (sd_mount()) { char fuseFilename[23]; + f_mkdir("Backup"); f_mkdir("Backup/Dumps"); memcpy(fuseFilename, "Backup/Dumps/fuses.bin\0", 23); @@ -399,6 +400,7 @@ void print_kfuseinfo() if (sd_mount()) { char kfuseFilename[24]; + f_mkdir("Backup"); f_mkdir("Backup/Dumps"); memcpy(kfuseFilename, "Backup/Dumps/kfuses.bin\0", 24); @@ -757,6 +759,7 @@ int dump_emmc_verify(sdmmc_storage_t *storage, u32 lba_curr, char* outFilename, res = memcmp32sparse((u32 *)bufEm, (u32 *)bufSd, num << 9); break; case 2: + default: se_calc_sha256(&hashEm, bufEm, num << 9); se_calc_sha256(&hashSd, bufSd, num << 9); res = memcmp(hashEm, hashSd, 0x20); @@ -1284,6 +1287,7 @@ void dump_package1() gfx_printf(&gfx_con, "%kWarmboot ofst: %k0x%05X\n\n", 0xFFC7EA46, 0xFFCCCCCC, hdr->wb_off); // Dump package1. + f_mkdir("Backup"); f_mkdir("Backup/pkg1"); if (sd_save_to_file(pkg1, 0x40000, "Backup/pkg1/pkg1_decr.bin")) { EPRINTF("\nFailed to create pkg1_decr.bin"); @@ -1510,19 +1514,150 @@ void fix_sd_attr(){ btn_wait(); } -void print_fuel_gauge_regs() +void print_fuel_gauge_info() +{ + int value = 0; + + gfx_printf(&gfx_con, "%kFuel Gauge IC Info:\n%k", 0xFF00DDFF, 0xFFCCCCCC); + + max17050_get_property(MAX17050_Age, &value); + gfx_printf(&gfx_con, "Age: %3d%\n", value); + + max17050_get_property(MAX17050_Cycles, &value); + gfx_printf(&gfx_con, "Charge cycle count: %4d\n", value); + + max17050_get_property(MAX17050_TEMP, &value); + if (value >= 0) + gfx_printf(&gfx_con, "Battery temperature: %d.%d oC\n", value / 10, value % 10); + else + gfx_printf(&gfx_con, "Battery temperature: -%d.%d oC\n", ~value / 10, (~value) % 10); + + max17050_get_property(MAX17050_Current, &value); + if (value >= 0) + gfx_printf(&gfx_con, "Current now: %d mA\n", value / 1000); + else + gfx_printf(&gfx_con, "Current now: -%d mA\n", ~value / 1000); + + max17050_get_property(MAX17050_AvgCurrent, &value); + if (value >= 0) + gfx_printf(&gfx_con, "Current average: %d mA\n", value / 1000); + else + gfx_printf(&gfx_con, "Current average: -%d mA\n", ~value / 1000); + + max17050_get_property(MAX17050_MinVolt, &value); + gfx_printf(&gfx_con, "Min voltage reached: %4d mV\n", value); + + max17050_get_property(MAX17050_MaxVolt, &value); + gfx_printf(&gfx_con, "Max voltage reached: %4d mV\n", value); + + max17050_get_property(MAX17050_V_empty, &value); + gfx_printf(&gfx_con, "Empty voltage (design): %4d mV\n", value); + + max17050_get_property(MAX17050_VCELL, &value); + gfx_printf(&gfx_con, "Voltage now: %4d mV\n", value); + + max17050_get_property(MAX17050_OCVInternal, &value); + gfx_printf(&gfx_con, "Voltage open-circuit: %4d mV\n", value); + + max17050_get_property(MAX17050_RepSOC, &value); + gfx_printf(&gfx_con, "Capacity now: %3d%\n", value >> 8); + + max17050_get_property(MAX17050_RepCap, &value); + gfx_printf(&gfx_con, "Capacity now: %4d mAh\n", value); + + max17050_get_property(MAX17050_FullCAP, &value); + gfx_printf(&gfx_con, "Capacity full: %4d mAh\n", value); + + max17050_get_property(MAX17050_DesignCap, &value); + gfx_printf(&gfx_con, "Capacity (design): %4d mAh\n", value); +} + +void print_battery_charger_info() +{ + int value = 0; + + gfx_printf(&gfx_con, "%k\n\nBattery Charger IC Info:\n%k", 0xFF00DDFF, 0xFFCCCCCC); + + bq24193_get_property(BQ24193_InputVoltageLimit, &value); + gfx_printf(&gfx_con, "Input voltage limit: %4d mV\n", value); + + bq24193_get_property(BQ24193_InputCurrentLimit, &value); + gfx_printf(&gfx_con, "Input current limit: %4d mA\n", value); + + bq24193_get_property(BQ24193_SystemMinimumVoltage, &value); + gfx_printf(&gfx_con, "Min voltage limit: %4d mV\n", value); + + bq24193_get_property(BQ24193_FastChargeCurrentLimit, &value); + gfx_printf(&gfx_con, "Fast charge current limit: %4d mA\n", value); + + bq24193_get_property(BQ24193_ChargeVoltageLimit, &value); + gfx_printf(&gfx_con, "Charge voltage limit: %4d mV\n", value); + + bq24193_get_property(BQ24193_ThermalRegulation, &value); + gfx_printf(&gfx_con, "Thermal threshold: %4d oC\n", value); + + bq24193_get_property(BQ24193_ChargeStatus, &value); + gfx_printf(&gfx_con, "Charge status: "); + switch (value) + { + case 0: + gfx_printf(&gfx_con, "Not charging\n"); + break; + case 1: + gfx_printf(&gfx_con, "Pre-charging\n"); + break; + case 2: + gfx_printf(&gfx_con, "Fast charging\n"); + break; + case 3: + gfx_printf(&gfx_con, "Charge terminated\n"); + break; + default: + gfx_printf(&gfx_con, "Unknown (%d)\n", value); + break; + } + bq24193_get_property(BQ24193_TempStatus, &value); + gfx_printf(&gfx_con, "Temperature status: "); + switch (value) + { + case 0: + gfx_printf(&gfx_con, "Normal\n"); + break; + case 2: + gfx_printf(&gfx_con, "Warm\n"); + break; + case 3: + gfx_printf(&gfx_con, "Cool\n"); + break; + case 5: + gfx_printf(&gfx_con, "Cold\n"); + break; + case 6: + gfx_printf(&gfx_con, "Hot\n"); + break; + default: + gfx_printf(&gfx_con, "Unknown (%d)\n", value); + break; + } +} + +void print_battery_info() { gfx_clear_grey(&gfx_ctxt, 0x1B); gfx_con_setpos(&gfx_con, 0, 0); + print_fuel_gauge_info(); + + print_battery_charger_info(); + u8 *buf = (u8 *)malloc(0x100 * 2); - gfx_printf(&gfx_con, "%kBattery Fuel Gauge Registers:\n\n%k", 0xFF00DDFF, 0xFFCCCCCC); + gfx_printf(&gfx_con, "%k\n\nBattery Fuel Gauge Registers:\n%k", 0xFF00DDFF, 0xFFCCCCCC); for (int i = 0; i < 0x200; i += 2) { i2c_recv_buf_small(buf + i, 2, I2C_1, 0x36, i >> 1); - sleep(5000); + sleep(2500); } gfx_hexdump(&gfx_con, 0, (u8 *)buf, 0x200); @@ -1536,6 +1671,7 @@ void print_fuel_gauge_regs() if (sd_mount()) { char fuelFilename[28]; + f_mkdir("Backup"); f_mkdir("Backup/Dumps"); memcpy(fuelFilename, "Backup/Dumps/fuel_gauge.bin\0", 28); @@ -1714,7 +1850,7 @@ ment_t ment_cinfo[] = { MDEF_HANDLER("Print SD Card info", print_sdcard_info), MDEF_CHGLINE(), MDEF_CAPTION("------ Misc ------", 0xFF0AB9E6), - MDEF_HANDLER("Print fuel gauge info", print_fuel_gauge_regs), + MDEF_HANDLER("Print battery info", print_battery_info), MDEF_END() }; menu_t menu_cinfo = { @@ -1791,7 +1927,7 @@ ment_t ment_tools[] = { MDEF_HANDLER("Dump package1", dump_package1), MDEF_HANDLER("Fix SD files attributes", fix_sd_attr), MDEF_HANDLER("Fix battery de-sync", fix_battery_desync), - //MDEF_MENU("Fix fuel gauge configuration", &fix_fuel_gauge_configuration), + //MDEF_HANDLER("Fix fuel gauge configuration", fix_fuel_gauge_configuration), MDEF_CHGLINE(), MDEF_CAPTION("------ Dangerous -----", 0xFFFF0000), MDEF_MENU("AutoRCM", &menu_autorcm), diff --git a/ipl/max17050.c b/ipl/max17050.c index f05365d..c2e3f3c 100644 --- a/ipl/max17050.c +++ b/ipl/max17050.c @@ -38,190 +38,83 @@ #define STATUS_SMX_BIT (1 << 14) #define STATUS_BR_BIT (1 << 15) -/* Interrupt mask bits */ -#define CONFIG_ALRT_BIT_ENBL (1 << 2) -#define STATUS_INTR_SOCMIN_BIT (1 << 10) -#define STATUS_INTR_SOCMAX_BIT (1 << 14) - #define VFSOC0_LOCK 0x0000 #define VFSOC0_UNLOCK 0x0080 -#define dP_ACC_100 0x1900 -#define dP_ACC_200 0x3200 - #define MAX17050_VMAX_TOLERANCE 50 /* 50 mV */ -static int _max17050_get_temperature(int *temp) -{ - u16 data; - - i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_TEMP); - - *temp = (s16)data; - /* The value is converted into deci-centigrade scale */ - /* Units of LSB = 1 / 256 degree Celsius */ - *temp = *temp * 10 / 256; - return 0; -} - -int _max17050_get_status(int *status) -{ - int charge_full, charge_now; - int avg_current; - u16 data; - - /* - * The MAX170xx has builtin end-of-charge detection and will update - * FullCAP to match RepCap when it detects end of charging. - * - * When this cycle the battery gets charged to a higher (calculated) - * capacity then the previous cycle then FullCAP will get updated - * contineously once end-of-charge detection kicks in, so allow the - * 2 to differ a bit. - */ - - i2c_recv_buf_small((u8 *)&charge_full, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_FullCAP); - - i2c_recv_buf_small((u8 *)&charge_now, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_RepCap); - - if ((charge_full - charge_now) <= MAX17050_FULL_THRESHOLD) { - *status = 0xFF; //FULL - return 0; - } - - /* - * Even though we are supplied, we may still be discharging if the - * supply is e.g. only delivering 5V 0.5A. Check current if available. - */ - i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_AvgCurrent); - - avg_current = (s16)data; - avg_current *= 1562500 / MAX17050_DEFAULT_SNS_RESISTOR; - - if (avg_current > 0) - *status = 0x1; //Charging - else - *status = 0x0; //Discharging - - return 0; -} - -int _max17050_get_battery_health(int *health) -{ - int temp, vavg, vbatt; - u16 val; - - i2c_recv_buf_small((u8 *)&val, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_AvgVCELL); - /* bits [0-3] unused */ - vavg = val * 625 / 8; - /* Convert to millivolts */ - vavg /= 1000; - - i2c_recv_buf_small((u8 *)&val, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_VCELL); - /* bits [0-3] unused */ - vbatt = val * 625 / 8; - /* Convert to millivolts */ - vbatt /= 1000; - - if (vavg < MAX17050_DEFAULT_VMIN) { - *health = HEALTH_DEAD; - goto out; - } - - if (vbatt > MAX17050_DEFAULT_VMAX + MAX17050_VMAX_TOLERANCE) { - *health = HEALTH_OVERVOLTAGE; - goto out; - } - - _max17050_get_temperature(&temp); - - if (temp < MAX17050_DEFAULT_TEMP_MIN) { - *health = HEALTH_COLD; - goto out; - } - - if (temp > MAX17050_DEFAULT_TEMP_MAX) { - *health = HEALTH_OVERHEAT; - goto out; - } - - *health = HEALTH_GOOD; // 1 - -out: - return 0; -} - int max17050_get_property(enum MAX17050_reg reg, int *value) { u16 data; - switch (reg) { - //case 0x101://///////////////////////////FIX - // _max17050_get_status(value); - // break; - case MAX17050_Cycles: //Cycle count. - i2c_recv_buf_small((u8 *)value, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_Cycles); - break; - case MAX17050_MinMaxVolt: //Voltage max/min - i2c_recv_buf_small((u8 *)value, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_MinMaxVolt); - //value = (data >> 8) * 20; /* Voltage MAX. Units of LSB = 20mV */ - //value = (data & 0xff) * 20; /* Voltage MIN. Units of 20mV */ - *value = data; - break; - case MAX17050_V_empty: //Voltage min design. - i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_V_empty); - *value = (data >> 7) * 10; /* Units of LSB = 10mV */ - break; - case MAX17050_VCELL: //Voltage now. - i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_VCELL); - *value = data * 625 / 8 / 1000; - break; - case MAX17050_AvgVCELL: //Voltage avg. - i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_AvgVCELL); - *value = data * 625 / 8 / 1000; - break; - case MAX17050_OCVInternal: //Voltage ocv. - i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_OCVInternal); - *value = data * 625 / 8 / 1000; - break; - case MAX17050_RepSOC: //Capacity %. - i2c_recv_buf_small((u8 *)value, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_RepSOC); - break; - case MAX17050_DesignCap: //Charge full design. - i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_DesignCap); - data = data * 5 / 10; - *value = data; - break; - case MAX17050_FullCAP: //Charge full. - i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_FullCAP); - data = data * 5 / 10; - *value = data; - break; - case MAX17050_RepCap: //Charge now. - i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_RepCap); - data = data * 5 / 10; - *value = data; - break; - case MAX17050_TEMP: //Temp. - i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_TEMP); - *value = (s16)(data); - *value = *value * 10 / 256; - break; - //case 0x100: //FIX me - // _max17050_get_battery_health(value); - // break; - case MAX17050_Current: //Current now. - i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_Current); - *value = (s16)data; - *value *= 1562500 / MAX17050_DEFAULT_SNS_RESISTOR; - break; - case MAX17050_AvgCurrent: //Current avg. - i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_AvgCurrent); - *value = (s16)data; - *value *= 1562500 / MAX17050_DEFAULT_SNS_RESISTOR; - break; - default: - return -1; + switch (reg) + { + case MAX17050_Age: //Age (percent). Based on 100% x (FullCAP Register/DesignCap). + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_Age); + *value = data >> 8; /* Show MSB. 1% increments */ + break; + case MAX17050_Cycles: //Cycle count. + i2c_recv_buf_small((u8 *)value, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_Cycles); + break; + case MAX17050_MinVolt: //Voltage max/min + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_MinMaxVolt); + *value = (data & 0xff) * 20; /* Voltage MIN. Units of 20mV */ + break; + case MAX17050_MaxVolt: //Voltage max/min + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_MinMaxVolt); + *value = (data >> 8) * 20; /* Voltage MAX. Units of LSB = 20mV */ + break; + case MAX17050_V_empty: //Voltage min design. + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_V_empty); + *value = (data >> 7) * 10; /* Units of LSB = 10mV */ + break; + case MAX17050_VCELL: //Voltage now. + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_VCELL); + *value = data * 625 / 8 / 1000; + break; + case MAX17050_AvgVCELL: //Voltage avg. + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_AvgVCELL); + *value = data * 625 / 8 / 1000; + break; + case MAX17050_OCVInternal: //Voltage ocv. + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_OCVInternal); + *value = data * 625 / 8 / 1000; + break; + case MAX17050_RepSOC: //Capacity %. + i2c_recv_buf_small((u8 *)value, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_RepSOC); + break; + case MAX17050_DesignCap: //Charge full design. + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_DesignCap); + data = data * 5 / 10; + *value = data; + break; + case MAX17050_FullCAP: //Charge full. + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_FullCAP); + data = data * 5 / 10; + *value = data; + break; + case MAX17050_RepCap: //Charge now. + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_RepCap); + data = data * 5 / 10; + *value = data; + break; + case MAX17050_TEMP: //Temp. + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_TEMP); + *value = (s16)data; + *value = *value * 10 / 256; + break; + case MAX17050_Current: //Current now. + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_Current); + *value = (s16)data; + *value *= 1562500 / MAX17050_DEFAULT_SNS_RESISTOR; + break; + case MAX17050_AvgCurrent: //Current avg. + i2c_recv_buf_small((u8 *)&data, 2, I2C_1, MAXIM17050_I2C_ADDR, MAX17050_AvgCurrent); + *value = (s16)data; + *value *= 1562500 / MAX17050_DEFAULT_SNS_RESISTOR; + break; + default: + return -1; } return 0; } diff --git a/ipl/max17050.h b/ipl/max17050.h index ac99f70..2196b80 100644 --- a/ipl/max17050.h +++ b/ipl/max17050.h @@ -25,12 +25,7 @@ #define __MAX17050_H_ #define MAX17050_STATUS_BattAbsent (1 << 3) -#define MAX17050_BATTERY_FULL 95 /* Recommend. FullSOCThr value */ #define MAX17050_DEFAULT_SNS_RESISTOR 10000 -#define MAX17050_DEFAULT_VMIN 3200 -#define MAX17050_DEFAULT_VMAX 4200 /* LiHV cell max */ -#define MAX17050_DEFAULT_TEMP_MIN 5 /* For sys without temp sensor */ -#define MAX17050_DEFAULT_TEMP_MAX 650 /* 65 degrees Celcius */ /* Consider RepCap which is less then 10 units below FullCAP full */ #define MAX17050_FULL_THRESHOLD 10 @@ -112,6 +107,9 @@ enum MAX17050_reg { MAX17050_QH = 0x4D, MAX17050_QL = 0x4E, + MAX17050_MinVolt = 0x50, // Custom ID. Not to be sent to i2c. + MAX17050_MaxVolt = 0x51, // Custom ID. Not to be sent to i2c. + MAX17050_VFSOC0Enable = 0x60, MAX17050_MODELChrTbl = 0x80, @@ -123,15 +121,6 @@ enum MAX17050_reg { MAX17050_VFSOC = 0xFF, }; -enum { - HEALTH_UNKNOWN = 0, - HEALTH_GOOD, - HEALTH_OVERHEAT, - HEALTH_DEAD, - HEALTH_OVERVOLTAGE, - HEALTH_COLD, -}; - int max17050_get_property(enum MAX17050_reg reg, int *value); int max17050_fix_configuration(); diff --git a/ipl/tui.c b/ipl/tui.c index 11f96c0..c61a7ee 100755 --- a/ipl/tui.c +++ b/ipl/tui.c @@ -121,7 +121,7 @@ void *tui_do_menu(gfx_con_t *con, menu_t *menu) max17050_get_property(MAX17050_VCELL, &battVoltCurr); gfx_printf(con, " (%d mV) ", battVoltCurr); max17050_get_property(MAX17050_AvgCurrent, &battVoltCurr); - if (battVoltCurr > 0) + if (battVoltCurr >= 0) gfx_printf(con, "\n %kCharging:%k %d mA %k\n", 0xFF008000, 0xFF555555, battVoltCurr / 1000, 0xFFCCCCCC); else