1*74e3f620SBhavin Sharma // SPDX-License-Identifier: GPL-2.0 2*74e3f620SBhavin Sharma /* 3*74e3f620SBhavin Sharma * stc3117_fuel_gauge.c - STMicroelectronics STC3117 Fuel Gauge Driver 4*74e3f620SBhavin Sharma * 5*74e3f620SBhavin Sharma * Copyright (c) 2024 Silicon Signals Pvt Ltd. 6*74e3f620SBhavin Sharma * Author: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io> 7*74e3f620SBhavin Sharma * Bhavin Sharma <bhavin.sharma@siliconsignals.io> 8*74e3f620SBhavin Sharma */ 9*74e3f620SBhavin Sharma 10*74e3f620SBhavin Sharma #include <linux/crc8.h> 11*74e3f620SBhavin Sharma #include <linux/devm-helpers.h> 12*74e3f620SBhavin Sharma #include <linux/i2c.h> 13*74e3f620SBhavin Sharma #include <linux/power_supply.h> 14*74e3f620SBhavin Sharma #include <linux/regmap.h> 15*74e3f620SBhavin Sharma #include <linux/workqueue.h> 16*74e3f620SBhavin Sharma 17*74e3f620SBhavin Sharma #define STC3117_ADDR_MODE 0x00 18*74e3f620SBhavin Sharma #define STC3117_ADDR_CTRL 0x01 19*74e3f620SBhavin Sharma #define STC3117_ADDR_SOC_L 0x02 20*74e3f620SBhavin Sharma #define STC3117_ADDR_SOC_H 0x03 21*74e3f620SBhavin Sharma #define STC3117_ADDR_COUNTER_L 0x04 22*74e3f620SBhavin Sharma #define STC3117_ADDR_COUNTER_H 0x05 23*74e3f620SBhavin Sharma #define STC3117_ADDR_CURRENT_L 0x06 24*74e3f620SBhavin Sharma #define STC3117_ADDR_CURRENT_H 0x07 25*74e3f620SBhavin Sharma #define STC3117_ADDR_VOLTAGE_L 0x08 26*74e3f620SBhavin Sharma #define STC3117_ADDR_VOLTAGE_H 0x09 27*74e3f620SBhavin Sharma #define STC3117_ADDR_TEMPERATURE 0x0A 28*74e3f620SBhavin Sharma #define STC3117_ADDR_AVG_CURRENT_L 0x0B 29*74e3f620SBhavin Sharma #define STC3117_ADDR_AVG_CURRENT_H 0x0C 30*74e3f620SBhavin Sharma #define STC3117_ADDR_OCV_L 0x0D 31*74e3f620SBhavin Sharma #define STC3117_ADDR_OCV_H 0x0E 32*74e3f620SBhavin Sharma #define STC3117_ADDR_CC_CNF_L 0x0F 33*74e3f620SBhavin Sharma #define STC3117_ADDR_CC_CNF_H 0x10 34*74e3f620SBhavin Sharma #define STC3117_ADDR_VM_CNF_L 0x11 35*74e3f620SBhavin Sharma #define STC3117_ADDR_VM_CNF_H 0x12 36*74e3f620SBhavin Sharma #define STC3117_ADDR_ALARM_soc 0x13 37*74e3f620SBhavin Sharma #define STC3117_ADDR_ALARM_VOLTAGE 0x14 38*74e3f620SBhavin Sharma #define STC3117_ADDR_ID 0x18 39*74e3f620SBhavin Sharma #define STC3117_ADDR_CC_ADJ_L 0x1B 40*74e3f620SBhavin Sharma #define STC3117_ADDR_CC_ADJ_H 0x1C 41*74e3f620SBhavin Sharma #define STC3117_ADDR_VM_ADJ_L 0x1D 42*74e3f620SBhavin Sharma #define STC3117_ADDR_VM_ADJ_H 0x1E 43*74e3f620SBhavin Sharma #define STC3117_ADDR_RAM 0x20 44*74e3f620SBhavin Sharma #define STC3117_ADDR_OCV_TABLE 0x30 45*74e3f620SBhavin Sharma #define STC3117_ADDR_SOC_TABLE 0x30 46*74e3f620SBhavin Sharma 47*74e3f620SBhavin Sharma /* Bit mask definition */ 48*74e3f620SBhavin Sharma #define STC3117_ID 0x16 49*74e3f620SBhavin Sharma #define STC3117_MIXED_MODE 0x00 50*74e3f620SBhavin Sharma #define STC3117_VMODE BIT(0) 51*74e3f620SBhavin Sharma #define STC3117_GG_RUN BIT(4) 52*74e3f620SBhavin Sharma #define STC3117_CC_MODE BIT(5) 53*74e3f620SBhavin Sharma #define STC3117_BATFAIL BIT(3) 54*74e3f620SBhavin Sharma #define STC3117_PORDET BIT(4) 55*74e3f620SBhavin Sharma #define STC3117_RAM_SIZE 16 56*74e3f620SBhavin Sharma #define STC3117_OCV_TABLE_SIZE 16 57*74e3f620SBhavin Sharma #define STC3117_RAM_TESTWORD 0x53A9 58*74e3f620SBhavin Sharma #define STC3117_SOFT_RESET 0x11 59*74e3f620SBhavin Sharma #define STC3117_NOMINAL_CAPACITY 2600 60*74e3f620SBhavin Sharma 61*74e3f620SBhavin Sharma #define VOLTAGE_LSB_VALUE 9011 62*74e3f620SBhavin Sharma #define CURRENT_LSB_VALUE 24084 63*74e3f620SBhavin Sharma #define APP_CUTOFF_VOLTAGE 2500 64*74e3f620SBhavin Sharma #define MAX_HRSOC 51200 65*74e3f620SBhavin Sharma #define MAX_SOC 1000 66*74e3f620SBhavin Sharma #define CHG_MIN_CURRENT 200 67*74e3f620SBhavin Sharma #define CHG_END_CURRENT 20 68*74e3f620SBhavin Sharma #define APP_MIN_CURRENT (-5) 69*74e3f620SBhavin Sharma #define BATTERY_FULL 95 70*74e3f620SBhavin Sharma #define CRC8_POLYNOMIAL 0x07 71*74e3f620SBhavin Sharma #define CRC8_INIT 0x00 72*74e3f620SBhavin Sharma 73*74e3f620SBhavin Sharma DECLARE_CRC8_TABLE(stc3117_crc_table); 74*74e3f620SBhavin Sharma 75*74e3f620SBhavin Sharma enum stc3117_state { 76*74e3f620SBhavin Sharma STC3117_INIT, 77*74e3f620SBhavin Sharma STC3117_RUNNING, 78*74e3f620SBhavin Sharma STC3117_POWERDN, 79*74e3f620SBhavin Sharma }; 80*74e3f620SBhavin Sharma 81*74e3f620SBhavin Sharma /* Default ocv curve Li-ion battery */ 82*74e3f620SBhavin Sharma static const int ocv_value[16] = { 83*74e3f620SBhavin Sharma 3400, 3582, 3669, 3676, 3699, 3737, 3757, 3774, 84*74e3f620SBhavin Sharma 3804, 3844, 3936, 3984, 4028, 4131, 4246, 4320 85*74e3f620SBhavin Sharma }; 86*74e3f620SBhavin Sharma 87*74e3f620SBhavin Sharma union stc3117_internal_ram { 88*74e3f620SBhavin Sharma u8 ram_bytes[STC3117_RAM_SIZE]; 89*74e3f620SBhavin Sharma struct { 90*74e3f620SBhavin Sharma u16 testword; /* 0-1 Bytes */ 91*74e3f620SBhavin Sharma u16 hrsoc; /* 2-3 Bytes */ 92*74e3f620SBhavin Sharma u16 cc_cnf; /* 4-5 Bytes */ 93*74e3f620SBhavin Sharma u16 vm_cnf; /* 6-7 Bytes */ 94*74e3f620SBhavin Sharma u8 soc; /* 8 Byte */ 95*74e3f620SBhavin Sharma u8 state; /* 9 Byte */ 96*74e3f620SBhavin Sharma u8 unused[5]; /* 10-14 Bytes */ 97*74e3f620SBhavin Sharma u8 crc; /* 15 Byte */ 98*74e3f620SBhavin Sharma } reg; 99*74e3f620SBhavin Sharma }; 100*74e3f620SBhavin Sharma 101*74e3f620SBhavin Sharma struct stc3117_battery_info { 102*74e3f620SBhavin Sharma int voltage_min_mv; 103*74e3f620SBhavin Sharma int voltage_max_mv; 104*74e3f620SBhavin Sharma int battery_capacity_mah; 105*74e3f620SBhavin Sharma int sense_resistor; 106*74e3f620SBhavin Sharma }; 107*74e3f620SBhavin Sharma 108*74e3f620SBhavin Sharma struct stc3117_data { 109*74e3f620SBhavin Sharma struct i2c_client *client; 110*74e3f620SBhavin Sharma struct regmap *regmap; 111*74e3f620SBhavin Sharma struct delayed_work update_work; 112*74e3f620SBhavin Sharma struct power_supply *battery; 113*74e3f620SBhavin Sharma union stc3117_internal_ram ram_data; 114*74e3f620SBhavin Sharma struct stc3117_battery_info battery_info; 115*74e3f620SBhavin Sharma 116*74e3f620SBhavin Sharma u8 soc_tab[16]; 117*74e3f620SBhavin Sharma int cc_cnf; 118*74e3f620SBhavin Sharma int vm_cnf; 119*74e3f620SBhavin Sharma int cc_adj; 120*74e3f620SBhavin Sharma int vm_adj; 121*74e3f620SBhavin Sharma int avg_current; 122*74e3f620SBhavin Sharma int avg_voltage; 123*74e3f620SBhavin Sharma int batt_current; 124*74e3f620SBhavin Sharma int voltage; 125*74e3f620SBhavin Sharma int temp; 126*74e3f620SBhavin Sharma int soc; 127*74e3f620SBhavin Sharma int ocv; 128*74e3f620SBhavin Sharma int hrsoc; 129*74e3f620SBhavin Sharma int presence; 130*74e3f620SBhavin Sharma }; 131*74e3f620SBhavin Sharma 132*74e3f620SBhavin Sharma static int stc3117_convert(int value, int factor) 133*74e3f620SBhavin Sharma { 134*74e3f620SBhavin Sharma value = (value * factor) / 4096; 135*74e3f620SBhavin Sharma return value * 1000; 136*74e3f620SBhavin Sharma } 137*74e3f620SBhavin Sharma 138*74e3f620SBhavin Sharma static int stc3117_get_battery_data(struct stc3117_data *data) 139*74e3f620SBhavin Sharma { 140*74e3f620SBhavin Sharma u8 reg_list[16]; 141*74e3f620SBhavin Sharma u8 data_adjust[4]; 142*74e3f620SBhavin Sharma int value, mode; 143*74e3f620SBhavin Sharma 144*74e3f620SBhavin Sharma regmap_bulk_read(data->regmap, STC3117_ADDR_MODE, 145*74e3f620SBhavin Sharma reg_list, sizeof(reg_list)); 146*74e3f620SBhavin Sharma 147*74e3f620SBhavin Sharma /* soc */ 148*74e3f620SBhavin Sharma value = (reg_list[3] << 8) + reg_list[2]; 149*74e3f620SBhavin Sharma data->hrsoc = value; 150*74e3f620SBhavin Sharma data->soc = (value * 10 + 256) / 512; 151*74e3f620SBhavin Sharma 152*74e3f620SBhavin Sharma /* current in uA*/ 153*74e3f620SBhavin Sharma value = (reg_list[7] << 8) + reg_list[6]; 154*74e3f620SBhavin Sharma data->batt_current = stc3117_convert(value, 155*74e3f620SBhavin Sharma CURRENT_LSB_VALUE / data->battery_info.sense_resistor); 156*74e3f620SBhavin Sharma 157*74e3f620SBhavin Sharma /* voltage in uV */ 158*74e3f620SBhavin Sharma value = (reg_list[9] << 8) + reg_list[8]; 159*74e3f620SBhavin Sharma data->voltage = stc3117_convert(value, VOLTAGE_LSB_VALUE); 160*74e3f620SBhavin Sharma 161*74e3f620SBhavin Sharma /* temp in 1/10 °C */ 162*74e3f620SBhavin Sharma data->temp = reg_list[10] * 10; 163*74e3f620SBhavin Sharma 164*74e3f620SBhavin Sharma /* Avg current in uA */ 165*74e3f620SBhavin Sharma value = (reg_list[12] << 8) + reg_list[11]; 166*74e3f620SBhavin Sharma regmap_read(data->regmap, STC3117_ADDR_MODE, &mode); 167*74e3f620SBhavin Sharma if (!(mode & STC3117_VMODE)) { 168*74e3f620SBhavin Sharma value = stc3117_convert(value, 169*74e3f620SBhavin Sharma CURRENT_LSB_VALUE / data->battery_info.sense_resistor); 170*74e3f620SBhavin Sharma value = value / 4; 171*74e3f620SBhavin Sharma } else { 172*74e3f620SBhavin Sharma value = stc3117_convert(value, 36 * STC3117_NOMINAL_CAPACITY); 173*74e3f620SBhavin Sharma } 174*74e3f620SBhavin Sharma data->avg_current = value; 175*74e3f620SBhavin Sharma 176*74e3f620SBhavin Sharma /* ocv in uV */ 177*74e3f620SBhavin Sharma value = (reg_list[14] << 8) + reg_list[13]; 178*74e3f620SBhavin Sharma value = stc3117_convert(value, VOLTAGE_LSB_VALUE); 179*74e3f620SBhavin Sharma value = (value + 2) / 4; 180*74e3f620SBhavin Sharma data->ocv = value; 181*74e3f620SBhavin Sharma 182*74e3f620SBhavin Sharma /* CC & VM adjustment counters */ 183*74e3f620SBhavin Sharma regmap_bulk_read(data->regmap, STC3117_ADDR_CC_ADJ_L, 184*74e3f620SBhavin Sharma data_adjust, sizeof(data_adjust)); 185*74e3f620SBhavin Sharma value = (data_adjust[1] << 8) + data_adjust[0]; 186*74e3f620SBhavin Sharma data->cc_adj = value; 187*74e3f620SBhavin Sharma 188*74e3f620SBhavin Sharma value = (data_adjust[3] << 8) + data_adjust[2]; 189*74e3f620SBhavin Sharma data->vm_adj = value; 190*74e3f620SBhavin Sharma 191*74e3f620SBhavin Sharma return 0; 192*74e3f620SBhavin Sharma } 193*74e3f620SBhavin Sharma 194*74e3f620SBhavin Sharma static int ram_write(struct stc3117_data *data) 195*74e3f620SBhavin Sharma { 196*74e3f620SBhavin Sharma int ret; 197*74e3f620SBhavin Sharma 198*74e3f620SBhavin Sharma ret = regmap_bulk_write(data->regmap, STC3117_ADDR_RAM, 199*74e3f620SBhavin Sharma data->ram_data.ram_bytes, STC3117_RAM_SIZE); 200*74e3f620SBhavin Sharma if (ret) 201*74e3f620SBhavin Sharma return ret; 202*74e3f620SBhavin Sharma 203*74e3f620SBhavin Sharma return 0; 204*74e3f620SBhavin Sharma }; 205*74e3f620SBhavin Sharma 206*74e3f620SBhavin Sharma static int ram_read(struct stc3117_data *data) 207*74e3f620SBhavin Sharma { 208*74e3f620SBhavin Sharma int ret; 209*74e3f620SBhavin Sharma 210*74e3f620SBhavin Sharma ret = regmap_bulk_read(data->regmap, STC3117_ADDR_RAM, 211*74e3f620SBhavin Sharma data->ram_data.ram_bytes, STC3117_RAM_SIZE); 212*74e3f620SBhavin Sharma if (ret) 213*74e3f620SBhavin Sharma return ret; 214*74e3f620SBhavin Sharma 215*74e3f620SBhavin Sharma return 0; 216*74e3f620SBhavin Sharma }; 217*74e3f620SBhavin Sharma 218*74e3f620SBhavin Sharma static int stc3117_set_para(struct stc3117_data *data) 219*74e3f620SBhavin Sharma { 220*74e3f620SBhavin Sharma int ret; 221*74e3f620SBhavin Sharma 222*74e3f620SBhavin Sharma ret = regmap_write(data->regmap, STC3117_ADDR_MODE, STC3117_VMODE); 223*74e3f620SBhavin Sharma 224*74e3f620SBhavin Sharma for (int i = 0; i < STC3117_OCV_TABLE_SIZE; i++) 225*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_TABLE + i, 226*74e3f620SBhavin Sharma ocv_value[i] * 100 / 55); 227*74e3f620SBhavin Sharma if (data->soc_tab[1] != 0) 228*74e3f620SBhavin Sharma ret |= regmap_bulk_write(data->regmap, STC3117_ADDR_SOC_TABLE, 229*74e3f620SBhavin Sharma data->soc_tab, STC3117_OCV_TABLE_SIZE); 230*74e3f620SBhavin Sharma 231*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_CC_CNF_H, 232*74e3f620SBhavin Sharma (data->ram_data.reg.cc_cnf >> 8) & 0xFF); 233*74e3f620SBhavin Sharma 234*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_CC_CNF_L, 235*74e3f620SBhavin Sharma data->ram_data.reg.cc_cnf & 0xFF); 236*74e3f620SBhavin Sharma 237*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_VM_CNF_H, 238*74e3f620SBhavin Sharma (data->ram_data.reg.vm_cnf >> 8) & 0xFF); 239*74e3f620SBhavin Sharma 240*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_VM_CNF_L, 241*74e3f620SBhavin Sharma data->ram_data.reg.vm_cnf & 0xFF); 242*74e3f620SBhavin Sharma 243*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_CTRL, 0x03); 244*74e3f620SBhavin Sharma 245*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_MODE, 246*74e3f620SBhavin Sharma STC3117_MIXED_MODE | STC3117_GG_RUN); 247*74e3f620SBhavin Sharma 248*74e3f620SBhavin Sharma return ret; 249*74e3f620SBhavin Sharma }; 250*74e3f620SBhavin Sharma 251*74e3f620SBhavin Sharma static int stc3117_init(struct stc3117_data *data) 252*74e3f620SBhavin Sharma { 253*74e3f620SBhavin Sharma int id, ret; 254*74e3f620SBhavin Sharma int ctrl; 255*74e3f620SBhavin Sharma int ocv_m, ocv_l; 256*74e3f620SBhavin Sharma 257*74e3f620SBhavin Sharma regmap_read(data->regmap, STC3117_ADDR_ID, &id); 258*74e3f620SBhavin Sharma if (id != STC3117_ID) 259*74e3f620SBhavin Sharma return -EINVAL; 260*74e3f620SBhavin Sharma 261*74e3f620SBhavin Sharma data->cc_cnf = (data->battery_info.battery_capacity_mah * 262*74e3f620SBhavin Sharma data->battery_info.sense_resistor * 250 + 6194) / 12389; 263*74e3f620SBhavin Sharma data->vm_cnf = (data->battery_info.battery_capacity_mah 264*74e3f620SBhavin Sharma * 200 * 50 + 24444) / 48889; 265*74e3f620SBhavin Sharma 266*74e3f620SBhavin Sharma /* Battery has not been removed */ 267*74e3f620SBhavin Sharma data->presence = 1; 268*74e3f620SBhavin Sharma 269*74e3f620SBhavin Sharma /* Read RAM data */ 270*74e3f620SBhavin Sharma ret = ram_read(data); 271*74e3f620SBhavin Sharma if (ret) 272*74e3f620SBhavin Sharma return ret; 273*74e3f620SBhavin Sharma 274*74e3f620SBhavin Sharma if (data->ram_data.reg.testword != STC3117_RAM_TESTWORD || 275*74e3f620SBhavin Sharma (crc8(stc3117_crc_table, data->ram_data.ram_bytes, 276*74e3f620SBhavin Sharma STC3117_RAM_SIZE, CRC8_INIT)) != 0) { 277*74e3f620SBhavin Sharma data->ram_data.reg.testword = STC3117_RAM_TESTWORD; 278*74e3f620SBhavin Sharma data->ram_data.reg.cc_cnf = data->cc_cnf; 279*74e3f620SBhavin Sharma data->ram_data.reg.vm_cnf = data->vm_cnf; 280*74e3f620SBhavin Sharma data->ram_data.reg.crc = crc8(stc3117_crc_table, 281*74e3f620SBhavin Sharma data->ram_data.ram_bytes, 282*74e3f620SBhavin Sharma STC3117_RAM_SIZE - 1, CRC8_INIT); 283*74e3f620SBhavin Sharma 284*74e3f620SBhavin Sharma ret = regmap_read(data->regmap, STC3117_ADDR_OCV_H, &ocv_m); 285*74e3f620SBhavin Sharma 286*74e3f620SBhavin Sharma ret |= regmap_read(data->regmap, STC3117_ADDR_OCV_L, &ocv_l); 287*74e3f620SBhavin Sharma 288*74e3f620SBhavin Sharma ret |= stc3117_set_para(data); 289*74e3f620SBhavin Sharma 290*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_H, ocv_m); 291*74e3f620SBhavin Sharma 292*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_L, ocv_l); 293*74e3f620SBhavin Sharma if (ret) 294*74e3f620SBhavin Sharma return ret; 295*74e3f620SBhavin Sharma } else { 296*74e3f620SBhavin Sharma ret = regmap_read(data->regmap, STC3117_ADDR_CTRL, &ctrl); 297*74e3f620SBhavin Sharma if (ret) 298*74e3f620SBhavin Sharma return ret; 299*74e3f620SBhavin Sharma 300*74e3f620SBhavin Sharma if ((ctrl & STC3117_BATFAIL) != 0 || 301*74e3f620SBhavin Sharma (ctrl & STC3117_PORDET) != 0) { 302*74e3f620SBhavin Sharma ret = regmap_read(data->regmap, 303*74e3f620SBhavin Sharma STC3117_ADDR_OCV_H, &ocv_m); 304*74e3f620SBhavin Sharma 305*74e3f620SBhavin Sharma ret |= regmap_read(data->regmap, 306*74e3f620SBhavin Sharma STC3117_ADDR_OCV_L, &ocv_l); 307*74e3f620SBhavin Sharma 308*74e3f620SBhavin Sharma ret |= stc3117_set_para(data); 309*74e3f620SBhavin Sharma 310*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, 311*74e3f620SBhavin Sharma STC3117_ADDR_OCV_H, ocv_m); 312*74e3f620SBhavin Sharma 313*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, 314*74e3f620SBhavin Sharma STC3117_ADDR_OCV_L, ocv_l); 315*74e3f620SBhavin Sharma if (ret) 316*74e3f620SBhavin Sharma return ret; 317*74e3f620SBhavin Sharma } else { 318*74e3f620SBhavin Sharma ret = stc3117_set_para(data); 319*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_SOC_H, 320*74e3f620SBhavin Sharma (data->ram_data.reg.hrsoc >> 8 & 0xFF)); 321*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_SOC_L, 322*74e3f620SBhavin Sharma (data->ram_data.reg.hrsoc & 0xFF)); 323*74e3f620SBhavin Sharma if (ret) 324*74e3f620SBhavin Sharma return ret; 325*74e3f620SBhavin Sharma } 326*74e3f620SBhavin Sharma } 327*74e3f620SBhavin Sharma 328*74e3f620SBhavin Sharma data->ram_data.reg.state = STC3117_INIT; 329*74e3f620SBhavin Sharma data->ram_data.reg.crc = crc8(stc3117_crc_table, 330*74e3f620SBhavin Sharma data->ram_data.ram_bytes, 331*74e3f620SBhavin Sharma STC3117_RAM_SIZE - 1, CRC8_INIT); 332*74e3f620SBhavin Sharma ret = ram_write(data); 333*74e3f620SBhavin Sharma if (ret) 334*74e3f620SBhavin Sharma return ret; 335*74e3f620SBhavin Sharma 336*74e3f620SBhavin Sharma return 0; 337*74e3f620SBhavin Sharma }; 338*74e3f620SBhavin Sharma 339*74e3f620SBhavin Sharma static int stc3117_task(struct stc3117_data *data) 340*74e3f620SBhavin Sharma { 341*74e3f620SBhavin Sharma int id, mode, ret; 342*74e3f620SBhavin Sharma int count_l, count_m; 343*74e3f620SBhavin Sharma int ocv_l, ocv_m; 344*74e3f620SBhavin Sharma 345*74e3f620SBhavin Sharma regmap_read(data->regmap, STC3117_ADDR_ID, &id); 346*74e3f620SBhavin Sharma if (id != STC3117_ID) { 347*74e3f620SBhavin Sharma data->presence = 0; 348*74e3f620SBhavin Sharma return -EINVAL; 349*74e3f620SBhavin Sharma } 350*74e3f620SBhavin Sharma 351*74e3f620SBhavin Sharma stc3117_get_battery_data(data); 352*74e3f620SBhavin Sharma 353*74e3f620SBhavin Sharma /* Read RAM data */ 354*74e3f620SBhavin Sharma ret = ram_read(data); 355*74e3f620SBhavin Sharma if (ret) 356*74e3f620SBhavin Sharma return ret; 357*74e3f620SBhavin Sharma 358*74e3f620SBhavin Sharma if (data->ram_data.reg.testword != STC3117_RAM_TESTWORD || 359*74e3f620SBhavin Sharma (crc8(stc3117_crc_table, data->ram_data.ram_bytes, 360*74e3f620SBhavin Sharma STC3117_RAM_SIZE, CRC8_INIT) != 0)) { 361*74e3f620SBhavin Sharma data->ram_data.reg.testword = STC3117_RAM_TESTWORD; 362*74e3f620SBhavin Sharma data->ram_data.reg.cc_cnf = data->cc_cnf; 363*74e3f620SBhavin Sharma data->ram_data.reg.vm_cnf = data->vm_cnf; 364*74e3f620SBhavin Sharma data->ram_data.reg.crc = crc8(stc3117_crc_table, 365*74e3f620SBhavin Sharma data->ram_data.ram_bytes, 366*74e3f620SBhavin Sharma STC3117_RAM_SIZE - 1, CRC8_INIT); 367*74e3f620SBhavin Sharma data->ram_data.reg.state = STC3117_INIT; 368*74e3f620SBhavin Sharma } 369*74e3f620SBhavin Sharma 370*74e3f620SBhavin Sharma /* check battery presence status */ 371*74e3f620SBhavin Sharma ret = regmap_read(data->regmap, STC3117_ADDR_CTRL, &mode); 372*74e3f620SBhavin Sharma if ((mode & STC3117_BATFAIL) != 0) { 373*74e3f620SBhavin Sharma data->presence = 0; 374*74e3f620SBhavin Sharma data->ram_data.reg.testword = 0; 375*74e3f620SBhavin Sharma data->ram_data.reg.state = STC3117_INIT; 376*74e3f620SBhavin Sharma ret = ram_write(data); 377*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_CTRL, STC3117_PORDET); 378*74e3f620SBhavin Sharma if (ret) 379*74e3f620SBhavin Sharma return ret; 380*74e3f620SBhavin Sharma } 381*74e3f620SBhavin Sharma 382*74e3f620SBhavin Sharma data->presence = 1; 383*74e3f620SBhavin Sharma 384*74e3f620SBhavin Sharma ret = regmap_read(data->regmap, STC3117_ADDR_MODE, &mode); 385*74e3f620SBhavin Sharma if (ret) 386*74e3f620SBhavin Sharma return ret; 387*74e3f620SBhavin Sharma if ((mode & STC3117_GG_RUN) == 0) { 388*74e3f620SBhavin Sharma if (data->ram_data.reg.state > STC3117_INIT) { 389*74e3f620SBhavin Sharma ret = stc3117_set_para(data); 390*74e3f620SBhavin Sharma 391*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_SOC_H, 392*74e3f620SBhavin Sharma (data->ram_data.reg.hrsoc >> 8 & 0xFF)); 393*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_SOC_L, 394*74e3f620SBhavin Sharma (data->ram_data.reg.hrsoc & 0xFF)); 395*74e3f620SBhavin Sharma if (ret) 396*74e3f620SBhavin Sharma return ret; 397*74e3f620SBhavin Sharma } else { 398*74e3f620SBhavin Sharma ret = regmap_read(data->regmap, STC3117_ADDR_OCV_H, &ocv_m); 399*74e3f620SBhavin Sharma 400*74e3f620SBhavin Sharma ret |= regmap_read(data->regmap, STC3117_ADDR_OCV_L, &ocv_l); 401*74e3f620SBhavin Sharma 402*74e3f620SBhavin Sharma ret |= stc3117_set_para(data); 403*74e3f620SBhavin Sharma 404*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_H, ocv_m); 405*74e3f620SBhavin Sharma 406*74e3f620SBhavin Sharma ret |= regmap_write(data->regmap, STC3117_ADDR_OCV_L, ocv_l); 407*74e3f620SBhavin Sharma if (ret) 408*74e3f620SBhavin Sharma return ret; 409*74e3f620SBhavin Sharma } 410*74e3f620SBhavin Sharma data->ram_data.reg.state = STC3117_INIT; 411*74e3f620SBhavin Sharma } 412*74e3f620SBhavin Sharma 413*74e3f620SBhavin Sharma regmap_read(data->regmap, STC3117_ADDR_COUNTER_L, &count_l); 414*74e3f620SBhavin Sharma regmap_read(data->regmap, STC3117_ADDR_COUNTER_H, &count_m); 415*74e3f620SBhavin Sharma 416*74e3f620SBhavin Sharma count_m = (count_m << 8) + count_l; 417*74e3f620SBhavin Sharma 418*74e3f620SBhavin Sharma /* INIT state, wait for batt_current & temperature value available: */ 419*74e3f620SBhavin Sharma if (data->ram_data.reg.state == STC3117_INIT && count_m > 4) { 420*74e3f620SBhavin Sharma data->avg_voltage = data->voltage; 421*74e3f620SBhavin Sharma data->avg_current = data->batt_current; 422*74e3f620SBhavin Sharma data->ram_data.reg.state = STC3117_RUNNING; 423*74e3f620SBhavin Sharma } 424*74e3f620SBhavin Sharma 425*74e3f620SBhavin Sharma if (data->ram_data.reg.state != STC3117_RUNNING) { 426*74e3f620SBhavin Sharma data->batt_current = -ENODATA; 427*74e3f620SBhavin Sharma data->temp = -ENODATA; 428*74e3f620SBhavin Sharma } else { 429*74e3f620SBhavin Sharma if (data->voltage < APP_CUTOFF_VOLTAGE) 430*74e3f620SBhavin Sharma data->soc = -ENODATA; 431*74e3f620SBhavin Sharma 432*74e3f620SBhavin Sharma if (mode & STC3117_VMODE) { 433*74e3f620SBhavin Sharma data->avg_current = -ENODATA; 434*74e3f620SBhavin Sharma data->batt_current = -ENODATA; 435*74e3f620SBhavin Sharma } 436*74e3f620SBhavin Sharma } 437*74e3f620SBhavin Sharma 438*74e3f620SBhavin Sharma data->ram_data.reg.hrsoc = data->hrsoc; 439*74e3f620SBhavin Sharma data->ram_data.reg.soc = (data->soc + 5) / 10; 440*74e3f620SBhavin Sharma data->ram_data.reg.crc = crc8(stc3117_crc_table, 441*74e3f620SBhavin Sharma data->ram_data.ram_bytes, 442*74e3f620SBhavin Sharma STC3117_RAM_SIZE - 1, CRC8_INIT); 443*74e3f620SBhavin Sharma 444*74e3f620SBhavin Sharma ret = ram_write(data); 445*74e3f620SBhavin Sharma if (ret) 446*74e3f620SBhavin Sharma return ret; 447*74e3f620SBhavin Sharma return 0; 448*74e3f620SBhavin Sharma }; 449*74e3f620SBhavin Sharma 450*74e3f620SBhavin Sharma static void fuel_gauge_update_work(struct work_struct *work) 451*74e3f620SBhavin Sharma { 452*74e3f620SBhavin Sharma struct stc3117_data *data = 453*74e3f620SBhavin Sharma container_of(work, struct stc3117_data, update_work.work); 454*74e3f620SBhavin Sharma 455*74e3f620SBhavin Sharma stc3117_task(data); 456*74e3f620SBhavin Sharma 457*74e3f620SBhavin Sharma /* Schedule the work to run again in 2 seconds */ 458*74e3f620SBhavin Sharma schedule_delayed_work(&data->update_work, msecs_to_jiffies(2000)); 459*74e3f620SBhavin Sharma } 460*74e3f620SBhavin Sharma 461*74e3f620SBhavin Sharma static int stc3117_get_property(struct power_supply *psy, 462*74e3f620SBhavin Sharma enum power_supply_property psp, union power_supply_propval *val) 463*74e3f620SBhavin Sharma { 464*74e3f620SBhavin Sharma struct stc3117_data *data = power_supply_get_drvdata(psy); 465*74e3f620SBhavin Sharma 466*74e3f620SBhavin Sharma switch (psp) { 467*74e3f620SBhavin Sharma case POWER_SUPPLY_PROP_STATUS: 468*74e3f620SBhavin Sharma if (data->soc > BATTERY_FULL) 469*74e3f620SBhavin Sharma val->intval = POWER_SUPPLY_STATUS_FULL; 470*74e3f620SBhavin Sharma else if (data->batt_current < 0) 471*74e3f620SBhavin Sharma val->intval = POWER_SUPPLY_STATUS_CHARGING; 472*74e3f620SBhavin Sharma else if (data->batt_current > 0) 473*74e3f620SBhavin Sharma val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 474*74e3f620SBhavin Sharma else 475*74e3f620SBhavin Sharma val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 476*74e3f620SBhavin Sharma break; 477*74e3f620SBhavin Sharma case POWER_SUPPLY_PROP_VOLTAGE_NOW: 478*74e3f620SBhavin Sharma val->intval = data->voltage; 479*74e3f620SBhavin Sharma break; 480*74e3f620SBhavin Sharma case POWER_SUPPLY_PROP_CURRENT_NOW: 481*74e3f620SBhavin Sharma val->intval = data->batt_current; 482*74e3f620SBhavin Sharma break; 483*74e3f620SBhavin Sharma case POWER_SUPPLY_PROP_VOLTAGE_OCV: 484*74e3f620SBhavin Sharma val->intval = data->ocv; 485*74e3f620SBhavin Sharma break; 486*74e3f620SBhavin Sharma case POWER_SUPPLY_PROP_CURRENT_AVG: 487*74e3f620SBhavin Sharma val->intval = data->avg_current; 488*74e3f620SBhavin Sharma break; 489*74e3f620SBhavin Sharma case POWER_SUPPLY_PROP_CAPACITY: 490*74e3f620SBhavin Sharma val->intval = data->soc; 491*74e3f620SBhavin Sharma break; 492*74e3f620SBhavin Sharma case POWER_SUPPLY_PROP_TEMP: 493*74e3f620SBhavin Sharma val->intval = data->temp; 494*74e3f620SBhavin Sharma break; 495*74e3f620SBhavin Sharma case POWER_SUPPLY_PROP_PRESENT: 496*74e3f620SBhavin Sharma val->intval = data->presence; 497*74e3f620SBhavin Sharma break; 498*74e3f620SBhavin Sharma default: 499*74e3f620SBhavin Sharma return -EINVAL; 500*74e3f620SBhavin Sharma } 501*74e3f620SBhavin Sharma return 0; 502*74e3f620SBhavin Sharma } 503*74e3f620SBhavin Sharma 504*74e3f620SBhavin Sharma static enum power_supply_property stc3117_battery_props[] = { 505*74e3f620SBhavin Sharma POWER_SUPPLY_PROP_STATUS, 506*74e3f620SBhavin Sharma POWER_SUPPLY_PROP_VOLTAGE_NOW, 507*74e3f620SBhavin Sharma POWER_SUPPLY_PROP_CURRENT_NOW, 508*74e3f620SBhavin Sharma POWER_SUPPLY_PROP_VOLTAGE_OCV, 509*74e3f620SBhavin Sharma POWER_SUPPLY_PROP_CURRENT_AVG, 510*74e3f620SBhavin Sharma POWER_SUPPLY_PROP_CAPACITY, 511*74e3f620SBhavin Sharma POWER_SUPPLY_PROP_TEMP, 512*74e3f620SBhavin Sharma POWER_SUPPLY_PROP_PRESENT, 513*74e3f620SBhavin Sharma }; 514*74e3f620SBhavin Sharma 515*74e3f620SBhavin Sharma static const struct power_supply_desc stc3117_battery_desc = { 516*74e3f620SBhavin Sharma .name = "stc3117-battery", 517*74e3f620SBhavin Sharma .type = POWER_SUPPLY_TYPE_BATTERY, 518*74e3f620SBhavin Sharma .get_property = stc3117_get_property, 519*74e3f620SBhavin Sharma .properties = stc3117_battery_props, 520*74e3f620SBhavin Sharma .num_properties = ARRAY_SIZE(stc3117_battery_props), 521*74e3f620SBhavin Sharma }; 522*74e3f620SBhavin Sharma 523*74e3f620SBhavin Sharma static const struct regmap_config stc3117_regmap_config = { 524*74e3f620SBhavin Sharma .reg_bits = 8, 525*74e3f620SBhavin Sharma .val_bits = 8, 526*74e3f620SBhavin Sharma }; 527*74e3f620SBhavin Sharma 528*74e3f620SBhavin Sharma static int stc3117_probe(struct i2c_client *client) 529*74e3f620SBhavin Sharma { 530*74e3f620SBhavin Sharma struct stc3117_data *data; 531*74e3f620SBhavin Sharma struct power_supply_config psy_cfg = {}; 532*74e3f620SBhavin Sharma struct power_supply_battery_info *info; 533*74e3f620SBhavin Sharma int ret; 534*74e3f620SBhavin Sharma 535*74e3f620SBhavin Sharma data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 536*74e3f620SBhavin Sharma if (!data) 537*74e3f620SBhavin Sharma return -ENOMEM; 538*74e3f620SBhavin Sharma 539*74e3f620SBhavin Sharma data->client = client; 540*74e3f620SBhavin Sharma data->regmap = devm_regmap_init_i2c(client, &stc3117_regmap_config); 541*74e3f620SBhavin Sharma if (IS_ERR(data->regmap)) 542*74e3f620SBhavin Sharma return PTR_ERR(data->regmap); 543*74e3f620SBhavin Sharma 544*74e3f620SBhavin Sharma psy_cfg.drv_data = data; 545*74e3f620SBhavin Sharma psy_cfg.fwnode = dev_fwnode(&client->dev); 546*74e3f620SBhavin Sharma 547*74e3f620SBhavin Sharma crc8_populate_msb(stc3117_crc_table, CRC8_POLYNOMIAL); 548*74e3f620SBhavin Sharma 549*74e3f620SBhavin Sharma data->battery = devm_power_supply_register(&client->dev, 550*74e3f620SBhavin Sharma &stc3117_battery_desc, &psy_cfg); 551*74e3f620SBhavin Sharma if (IS_ERR(data->battery)) 552*74e3f620SBhavin Sharma return dev_err_probe(&client->dev, PTR_ERR(data->battery), 553*74e3f620SBhavin Sharma "failed to register battery\n"); 554*74e3f620SBhavin Sharma 555*74e3f620SBhavin Sharma ret = device_property_read_u32(&client->dev, "shunt-resistor-micro-ohms", 556*74e3f620SBhavin Sharma &data->battery_info.sense_resistor); 557*74e3f620SBhavin Sharma if (ret) 558*74e3f620SBhavin Sharma return dev_err_probe(&client->dev, ret, 559*74e3f620SBhavin Sharma "failed to get shunt-resistor-micro-ohms\n"); 560*74e3f620SBhavin Sharma data->battery_info.sense_resistor = data->battery_info.sense_resistor / 1000; 561*74e3f620SBhavin Sharma 562*74e3f620SBhavin Sharma ret = power_supply_get_battery_info(data->battery, &info); 563*74e3f620SBhavin Sharma if (ret) 564*74e3f620SBhavin Sharma return dev_err_probe(&client->dev, ret, 565*74e3f620SBhavin Sharma "failed to get battery information\n"); 566*74e3f620SBhavin Sharma 567*74e3f620SBhavin Sharma data->battery_info.battery_capacity_mah = info->charge_full_design_uah / 1000; 568*74e3f620SBhavin Sharma data->battery_info.voltage_min_mv = info->voltage_min_design_uv / 1000; 569*74e3f620SBhavin Sharma data->battery_info.voltage_max_mv = info->voltage_max_design_uv / 1000; 570*74e3f620SBhavin Sharma 571*74e3f620SBhavin Sharma ret = stc3117_init(data); 572*74e3f620SBhavin Sharma if (ret) 573*74e3f620SBhavin Sharma return dev_err_probe(&client->dev, ret, 574*74e3f620SBhavin Sharma "failed to initialize of stc3117\n"); 575*74e3f620SBhavin Sharma 576*74e3f620SBhavin Sharma ret = devm_delayed_work_autocancel(&client->dev, &data->update_work, 577*74e3f620SBhavin Sharma fuel_gauge_update_work); 578*74e3f620SBhavin Sharma if (ret) 579*74e3f620SBhavin Sharma return ret; 580*74e3f620SBhavin Sharma 581*74e3f620SBhavin Sharma schedule_delayed_work(&data->update_work, 0); 582*74e3f620SBhavin Sharma 583*74e3f620SBhavin Sharma return 0; 584*74e3f620SBhavin Sharma } 585*74e3f620SBhavin Sharma 586*74e3f620SBhavin Sharma static const struct i2c_device_id stc3117_id[] = { 587*74e3f620SBhavin Sharma { "stc3117", 0 }, 588*74e3f620SBhavin Sharma { } 589*74e3f620SBhavin Sharma }; 590*74e3f620SBhavin Sharma MODULE_DEVICE_TABLE(i2c, stc3117_id); 591*74e3f620SBhavin Sharma 592*74e3f620SBhavin Sharma static const struct of_device_id stc3117_of_match[] = { 593*74e3f620SBhavin Sharma { .compatible = "st,stc3117" }, 594*74e3f620SBhavin Sharma { } 595*74e3f620SBhavin Sharma }; 596*74e3f620SBhavin Sharma MODULE_DEVICE_TABLE(of, stc3117_of_match); 597*74e3f620SBhavin Sharma 598*74e3f620SBhavin Sharma static struct i2c_driver stc3117_i2c_driver = { 599*74e3f620SBhavin Sharma .driver = { 600*74e3f620SBhavin Sharma .name = "stc3117_i2c_driver", 601*74e3f620SBhavin Sharma .of_match_table = stc3117_of_match, 602*74e3f620SBhavin Sharma }, 603*74e3f620SBhavin Sharma .probe = stc3117_probe, 604*74e3f620SBhavin Sharma .id_table = stc3117_id, 605*74e3f620SBhavin Sharma }; 606*74e3f620SBhavin Sharma 607*74e3f620SBhavin Sharma module_i2c_driver(stc3117_i2c_driver); 608*74e3f620SBhavin Sharma 609*74e3f620SBhavin Sharma MODULE_LICENSE("GPL"); 610*74e3f620SBhavin Sharma MODULE_AUTHOR("Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>"); 611*74e3f620SBhavin Sharma MODULE_AUTHOR("Bhavin Sharma <bhavin.sharma@siliconsignals.io>"); 612*74e3f620SBhavin Sharma MODULE_DESCRIPTION("STC3117 Fuel Gauge Driver"); 613