Add Battery Charger & Fuel Gauge dirvers
* Add Fuel gauge configuration fix * Add Battery de-sync fix * Fuel gauge registers dumping * Add help and battery status in menu
This commit is contained in:
373
ipl/max17050.c
Normal file
373
ipl/max17050.c
Normal file
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Fuel gauge driver for Nintendo Switch's Maxim 17050
|
||||
*
|
||||
* Copyright (C) 2011 Samsung Electronics
|
||||
* MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
* Copyright (C) 2018 CTCaer
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* This driver is based on max17040_battery.c
|
||||
*/
|
||||
|
||||
#include "max17050.h"
|
||||
#include "i2c.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Status register bits */
|
||||
#define STATUS_POR_BIT (1 << 1)
|
||||
#define STATUS_BST_BIT (1 << 3)
|
||||
#define STATUS_VMN_BIT (1 << 8)
|
||||
#define STATUS_TMN_BIT (1 << 9)
|
||||
#define STATUS_SMN_BIT (1 << 10)
|
||||
#define STATUS_BI_BIT (1 << 11)
|
||||
#define STATUS_VMX_BIT (1 << 12)
|
||||
#define STATUS_TMX_BIT (1 << 13)
|
||||
#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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _max17050_write_verify_reg(u8 reg, u16 value)
|
||||
{
|
||||
int retries = 8;
|
||||
int ret;
|
||||
u16 read_value;
|
||||
|
||||
do {
|
||||
ret = i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, reg, (u8 *)&value, 2);
|
||||
i2c_recv_buf_small((u8 *)&read_value, 2, I2C_1, MAXIM17050_I2C_ADDR, reg);
|
||||
if (read_value != value) {
|
||||
ret = -1;
|
||||
retries--;
|
||||
}
|
||||
} while (retries && read_value != value);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _max17050_override_por(u8 reg, u16 value)
|
||||
{
|
||||
if (value)
|
||||
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, reg, (u8 *)&value, 2);
|
||||
}
|
||||
|
||||
static void _max17050_load_new_capacity_params()
|
||||
{
|
||||
u16 fullcap, repSoc, dq_acc, dp_acc;
|
||||
|
||||
fullcap = 0x2476; // 4667mAh design capacity.
|
||||
dq_acc = 0x10bc; // From a healthy fuel gauge.
|
||||
dp_acc = 0x5e09; // =||=
|
||||
repSoc = 0x6400; // 100%.
|
||||
|
||||
|
||||
_max17050_write_verify_reg(MAX17050_RemCap, fullcap);
|
||||
_max17050_write_verify_reg(MAX17050_RepCap, fullcap);
|
||||
|
||||
_max17050_write_verify_reg(MAX17050_dQacc, dq_acc);
|
||||
_max17050_write_verify_reg(MAX17050_dPacc, dp_acc);
|
||||
|
||||
_max17050_write_verify_reg(MAX17050_FullCAP, fullcap);
|
||||
//i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_DesignCap, (u8 *)&fullcap, 2);
|
||||
_max17050_write_verify_reg(MAX17050_FullCAPNom, fullcap);
|
||||
/* Update SOC register with new SOC */
|
||||
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_RepSOC, (u8 *)&repSoc, 2);
|
||||
}
|
||||
|
||||
static void _max17050_reset_vfsoc0_reg()
|
||||
{
|
||||
u16 lockVal = 0;
|
||||
u16 vfSoc = 0x6440; // >100% for fully charged battery
|
||||
|
||||
lockVal = VFSOC0_UNLOCK;
|
||||
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_VFSOC0Enable, (u8 *)&lockVal, 2);
|
||||
|
||||
_max17050_write_verify_reg(MAX17050_VFSOC0, vfSoc);
|
||||
|
||||
lockVal = VFSOC0_LOCK;
|
||||
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_VFSOC0Enable, (u8 *)&lockVal, 2);
|
||||
}
|
||||
|
||||
static void _max17050_update_capacity_regs()
|
||||
{
|
||||
u16 value = 0x2476; //Set to 4667mAh design capacity.
|
||||
_max17050_write_verify_reg(MAX17050_FullCAP, value);
|
||||
_max17050_write_verify_reg(MAX17050_FullCAPNom, value);
|
||||
//i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_DesignCap, config->design_cap, 2);
|
||||
}
|
||||
|
||||
static void _max17050_write_config_regs()
|
||||
{
|
||||
u16 value = 0;
|
||||
|
||||
value = 0x7254;
|
||||
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_CONFIG, (u8 *)&value, 2);
|
||||
value = 0x2473;
|
||||
i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_LearnCFG, (u8 *)&value, 2);
|
||||
//i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_FilterCFG, (u8 *)&value, 2)
|
||||
//i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_RelaxCFG, (u8 *)&value, 2)
|
||||
//i2c_send_buf_small(I2C_1, MAXIM17050_I2C_ADDR, MAX17050_FullSOCThr, (u8 *)&value, 2)
|
||||
}
|
||||
|
||||
/*
|
||||
* Block write all the override values coming from platform data.
|
||||
* This function MUST be called before the POR initialization proceedure
|
||||
* specified by maxim.
|
||||
*/
|
||||
static void _max17050_override_por_values()
|
||||
{
|
||||
u16 dq_acc = 0x10bc; // From a healthy fuel gauge.
|
||||
u16 dp_acc = 0x5e09; // =||=
|
||||
|
||||
_max17050_override_por(MAX17050_dQacc, dq_acc);
|
||||
_max17050_override_por(MAX17050_dPacc, dp_acc);
|
||||
|
||||
//_max17050_override_por(MAX17050_RCOMP0, config->rcomp0); //0x58
|
||||
//_max17050_override_por(MAX17050_TempCo, config->tcompc0); //0x1b22
|
||||
|
||||
//u16 k_empty0 = 0x439;
|
||||
//_max17050_override_por(map, MAX17050_K_empty0, k_empty0); // unknown cell data
|
||||
}
|
||||
|
||||
static void _max17050_set_por_bit(u16 value)
|
||||
{
|
||||
_max17050_write_verify_reg(MAX17050_STATUS, value);
|
||||
}
|
||||
|
||||
int max17050_fix_configuration()
|
||||
{
|
||||
/* Init phase, set the POR bit */
|
||||
_max17050_set_por_bit(STATUS_POR_BIT);
|
||||
|
||||
/* Override POR values */
|
||||
_max17050_override_por_values();
|
||||
/* After Power up, the MAX17050 requires 500ms in order
|
||||
* to perform signal debouncing and initial SOC reporting
|
||||
*/
|
||||
sleep(500000);
|
||||
|
||||
/* Initialize configaration */
|
||||
_max17050_write_config_regs();
|
||||
|
||||
|
||||
/* update capacity params */
|
||||
_max17050_update_capacity_regs();
|
||||
|
||||
/* delay must be atleast 350mS to allow VFSOC
|
||||
* to be calculated from the new configuration
|
||||
*/
|
||||
sleep(350000);
|
||||
|
||||
/* reset vfsoc0 reg */
|
||||
_max17050_reset_vfsoc0_reg();
|
||||
|
||||
/* load new capacity params */
|
||||
_max17050_load_new_capacity_params();
|
||||
|
||||
/* Init complete, Clear the POR bit */
|
||||
//_max17050_set_por_bit(0); // Should we? Or let the switch to reconfigure POR?
|
||||
|
||||
// Sets POR, BI, BR.
|
||||
_max17050_set_por_bit(0x8801);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user