Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

power: supply: Add STC3117 fuel gauge unit driver

Adds initial support for the STC3117 fuel gauge.

The driver provides functionality to monitor key parameters including:
Voltage, Current, State of Charge (SOC), Temperature, Status

Co-developed-by: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
Signed-off-by: Hardevsinh Palaniya <hardevsinh.palaniya@siliconsignals.io>
Signed-off-by: Bhavin Sharma <bhavin.sharma@siliconsignals.io>
Link: https://lore.kernel.org/r/20241220084958.32367-3-bhavin.sharma@siliconsignals.io
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>

authored by

Bhavin Sharma and committed by
Sebastian Reichel
74e3f620 69a37613

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