162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DA9150 Fuel-Gauge Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2015 Dialog Semiconductor 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/power_supply.h> 1862306a36Sopenharmony_ci#include <linux/list.h> 1962306a36Sopenharmony_ci#include <asm/div64.h> 2062306a36Sopenharmony_ci#include <linux/mfd/da9150/core.h> 2162306a36Sopenharmony_ci#include <linux/mfd/da9150/registers.h> 2262306a36Sopenharmony_ci#include <linux/devm-helpers.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Core2Wire */ 2562306a36Sopenharmony_ci#define DA9150_QIF_READ (0x0 << 7) 2662306a36Sopenharmony_ci#define DA9150_QIF_WRITE (0x1 << 7) 2762306a36Sopenharmony_ci#define DA9150_QIF_CODE_MASK 0x7F 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define DA9150_QIF_BYTE_SIZE 8 3062306a36Sopenharmony_ci#define DA9150_QIF_BYTE_MASK 0xFF 3162306a36Sopenharmony_ci#define DA9150_QIF_SHORT_SIZE 2 3262306a36Sopenharmony_ci#define DA9150_QIF_LONG_SIZE 4 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* QIF Codes */ 3562306a36Sopenharmony_ci#define DA9150_QIF_UAVG 6 3662306a36Sopenharmony_ci#define DA9150_QIF_UAVG_SIZE DA9150_QIF_LONG_SIZE 3762306a36Sopenharmony_ci#define DA9150_QIF_IAVG 8 3862306a36Sopenharmony_ci#define DA9150_QIF_IAVG_SIZE DA9150_QIF_LONG_SIZE 3962306a36Sopenharmony_ci#define DA9150_QIF_NTCAVG 12 4062306a36Sopenharmony_ci#define DA9150_QIF_NTCAVG_SIZE DA9150_QIF_LONG_SIZE 4162306a36Sopenharmony_ci#define DA9150_QIF_SHUNT_VAL 36 4262306a36Sopenharmony_ci#define DA9150_QIF_SHUNT_VAL_SIZE DA9150_QIF_SHORT_SIZE 4362306a36Sopenharmony_ci#define DA9150_QIF_SD_GAIN 38 4462306a36Sopenharmony_ci#define DA9150_QIF_SD_GAIN_SIZE DA9150_QIF_LONG_SIZE 4562306a36Sopenharmony_ci#define DA9150_QIF_FCC_MAH 40 4662306a36Sopenharmony_ci#define DA9150_QIF_FCC_MAH_SIZE DA9150_QIF_SHORT_SIZE 4762306a36Sopenharmony_ci#define DA9150_QIF_SOC_PCT 43 4862306a36Sopenharmony_ci#define DA9150_QIF_SOC_PCT_SIZE DA9150_QIF_SHORT_SIZE 4962306a36Sopenharmony_ci#define DA9150_QIF_CHARGE_LIMIT 44 5062306a36Sopenharmony_ci#define DA9150_QIF_CHARGE_LIMIT_SIZE DA9150_QIF_SHORT_SIZE 5162306a36Sopenharmony_ci#define DA9150_QIF_DISCHARGE_LIMIT 45 5262306a36Sopenharmony_ci#define DA9150_QIF_DISCHARGE_LIMIT_SIZE DA9150_QIF_SHORT_SIZE 5362306a36Sopenharmony_ci#define DA9150_QIF_FW_MAIN_VER 118 5462306a36Sopenharmony_ci#define DA9150_QIF_FW_MAIN_VER_SIZE DA9150_QIF_SHORT_SIZE 5562306a36Sopenharmony_ci#define DA9150_QIF_E_FG_STATUS 126 5662306a36Sopenharmony_ci#define DA9150_QIF_E_FG_STATUS_SIZE DA9150_QIF_SHORT_SIZE 5762306a36Sopenharmony_ci#define DA9150_QIF_SYNC 127 5862306a36Sopenharmony_ci#define DA9150_QIF_SYNC_SIZE DA9150_QIF_SHORT_SIZE 5962306a36Sopenharmony_ci#define DA9150_QIF_MAX_CODES 128 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* QIF Sync Timeout */ 6262306a36Sopenharmony_ci#define DA9150_QIF_SYNC_TIMEOUT 1000 6362306a36Sopenharmony_ci#define DA9150_QIF_SYNC_RETRIES 10 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* QIF E_FG_STATUS */ 6662306a36Sopenharmony_ci#define DA9150_FG_IRQ_LOW_SOC_MASK (1 << 0) 6762306a36Sopenharmony_ci#define DA9150_FG_IRQ_HIGH_SOC_MASK (1 << 1) 6862306a36Sopenharmony_ci#define DA9150_FG_IRQ_SOC_MASK \ 6962306a36Sopenharmony_ci (DA9150_FG_IRQ_LOW_SOC_MASK | DA9150_FG_IRQ_HIGH_SOC_MASK) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* Private data */ 7262306a36Sopenharmony_cistruct da9150_fg { 7362306a36Sopenharmony_ci struct da9150 *da9150; 7462306a36Sopenharmony_ci struct device *dev; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci struct mutex io_lock; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci struct power_supply *battery; 7962306a36Sopenharmony_ci struct delayed_work work; 8062306a36Sopenharmony_ci u32 interval; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci int warn_soc; 8362306a36Sopenharmony_ci int crit_soc; 8462306a36Sopenharmony_ci int soc; 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* Battery Properties */ 8862306a36Sopenharmony_cistatic u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci u8 buf[DA9150_QIF_LONG_SIZE]; 9262306a36Sopenharmony_ci u8 read_addr; 9362306a36Sopenharmony_ci u32 res = 0; 9462306a36Sopenharmony_ci int i; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* Set QIF code (READ mode) */ 9762306a36Sopenharmony_ci read_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_READ; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci da9150_read_qif(fg->da9150, read_addr, size, buf); 10062306a36Sopenharmony_ci for (i = 0; i < size; ++i) 10162306a36Sopenharmony_ci res |= (buf[i] << (i * DA9150_QIF_BYTE_SIZE)); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return res; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size, 10762306a36Sopenharmony_ci u32 val) 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci u8 buf[DA9150_QIF_LONG_SIZE]; 11162306a36Sopenharmony_ci u8 write_addr; 11262306a36Sopenharmony_ci int i; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* Set QIF code (WRITE mode) */ 11562306a36Sopenharmony_ci write_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_WRITE; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci for (i = 0; i < size; ++i) { 11862306a36Sopenharmony_ci buf[i] = (val >> (i * DA9150_QIF_BYTE_SIZE)) & 11962306a36Sopenharmony_ci DA9150_QIF_BYTE_MASK; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci da9150_write_qif(fg->da9150, write_addr, size, buf); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* Trigger QIF Sync to update QIF readable data */ 12562306a36Sopenharmony_cistatic void da9150_fg_read_sync_start(struct da9150_fg *fg) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci int i = 0; 12862306a36Sopenharmony_ci u32 res = 0; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci mutex_lock(&fg->io_lock); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Check if QIF sync already requested, and write to sync if not */ 13362306a36Sopenharmony_ci res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC, 13462306a36Sopenharmony_ci DA9150_QIF_SYNC_SIZE); 13562306a36Sopenharmony_ci if (res > 0) 13662306a36Sopenharmony_ci da9150_fg_write_attr(fg, DA9150_QIF_SYNC, 13762306a36Sopenharmony_ci DA9150_QIF_SYNC_SIZE, 0); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Wait for sync to complete */ 14062306a36Sopenharmony_ci res = 0; 14162306a36Sopenharmony_ci while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) { 14262306a36Sopenharmony_ci usleep_range(DA9150_QIF_SYNC_TIMEOUT, 14362306a36Sopenharmony_ci DA9150_QIF_SYNC_TIMEOUT * 2); 14462306a36Sopenharmony_ci res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC, 14562306a36Sopenharmony_ci DA9150_QIF_SYNC_SIZE); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Check if sync completed */ 14962306a36Sopenharmony_ci if (res == 0) 15062306a36Sopenharmony_ci dev_err(fg->dev, "Failed to perform QIF read sync!\n"); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * Should always be called after QIF sync read has been performed, and all 15562306a36Sopenharmony_ci * attributes required have been accessed. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cistatic inline void da9150_fg_read_sync_end(struct da9150_fg *fg) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci mutex_unlock(&fg->io_lock); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* Sync read of single QIF attribute */ 16362306a36Sopenharmony_cistatic u32 da9150_fg_read_attr_sync(struct da9150_fg *fg, u8 code, u8 size) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci u32 val; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci da9150_fg_read_sync_start(fg); 16862306a36Sopenharmony_ci val = da9150_fg_read_attr(fg, code, size); 16962306a36Sopenharmony_ci da9150_fg_read_sync_end(fg); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return val; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* Wait for QIF Sync, write QIF data and wait for ack */ 17562306a36Sopenharmony_cistatic void da9150_fg_write_attr_sync(struct da9150_fg *fg, u8 code, u8 size, 17662306a36Sopenharmony_ci u32 val) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci int i = 0; 17962306a36Sopenharmony_ci u32 res = 0, sync_val; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci mutex_lock(&fg->io_lock); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Check if QIF sync already requested */ 18462306a36Sopenharmony_ci res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC, 18562306a36Sopenharmony_ci DA9150_QIF_SYNC_SIZE); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* Wait for an existing sync to complete */ 18862306a36Sopenharmony_ci while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) { 18962306a36Sopenharmony_ci usleep_range(DA9150_QIF_SYNC_TIMEOUT, 19062306a36Sopenharmony_ci DA9150_QIF_SYNC_TIMEOUT * 2); 19162306a36Sopenharmony_ci res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC, 19262306a36Sopenharmony_ci DA9150_QIF_SYNC_SIZE); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (res == 0) { 19662306a36Sopenharmony_ci dev_err(fg->dev, "Timeout waiting for existing QIF sync!\n"); 19762306a36Sopenharmony_ci mutex_unlock(&fg->io_lock); 19862306a36Sopenharmony_ci return; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Write value for QIF code */ 20262306a36Sopenharmony_ci da9150_fg_write_attr(fg, code, size, val); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Wait for write acknowledgment */ 20562306a36Sopenharmony_ci i = 0; 20662306a36Sopenharmony_ci sync_val = res; 20762306a36Sopenharmony_ci while ((res == sync_val) && (i++ < DA9150_QIF_SYNC_RETRIES)) { 20862306a36Sopenharmony_ci usleep_range(DA9150_QIF_SYNC_TIMEOUT, 20962306a36Sopenharmony_ci DA9150_QIF_SYNC_TIMEOUT * 2); 21062306a36Sopenharmony_ci res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC, 21162306a36Sopenharmony_ci DA9150_QIF_SYNC_SIZE); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci mutex_unlock(&fg->io_lock); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* Check write was actually successful */ 21762306a36Sopenharmony_ci if (res != (sync_val + 1)) 21862306a36Sopenharmony_ci dev_err(fg->dev, "Error performing QIF sync write for code %d\n", 21962306a36Sopenharmony_ci code); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/* Power Supply attributes */ 22362306a36Sopenharmony_cistatic int da9150_fg_capacity(struct da9150_fg *fg, 22462306a36Sopenharmony_ci union power_supply_propval *val) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT, 22762306a36Sopenharmony_ci DA9150_QIF_SOC_PCT_SIZE); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (val->intval > 100) 23062306a36Sopenharmony_ci val->intval = 100; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int da9150_fg_current_avg(struct da9150_fg *fg, 23662306a36Sopenharmony_ci union power_supply_propval *val) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci u32 iavg, sd_gain, shunt_val; 23962306a36Sopenharmony_ci u64 div, res; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci da9150_fg_read_sync_start(fg); 24262306a36Sopenharmony_ci iavg = da9150_fg_read_attr(fg, DA9150_QIF_IAVG, 24362306a36Sopenharmony_ci DA9150_QIF_IAVG_SIZE); 24462306a36Sopenharmony_ci shunt_val = da9150_fg_read_attr(fg, DA9150_QIF_SHUNT_VAL, 24562306a36Sopenharmony_ci DA9150_QIF_SHUNT_VAL_SIZE); 24662306a36Sopenharmony_ci sd_gain = da9150_fg_read_attr(fg, DA9150_QIF_SD_GAIN, 24762306a36Sopenharmony_ci DA9150_QIF_SD_GAIN_SIZE); 24862306a36Sopenharmony_ci da9150_fg_read_sync_end(fg); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci div = (u64) (sd_gain * shunt_val * 65536ULL); 25162306a36Sopenharmony_ci do_div(div, 1000000); 25262306a36Sopenharmony_ci res = (u64) (iavg * 1000000ULL); 25362306a36Sopenharmony_ci do_div(res, div); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci val->intval = (int) res; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic int da9150_fg_voltage_avg(struct da9150_fg *fg, 26162306a36Sopenharmony_ci union power_supply_propval *val) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci u64 res; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_UAVG, 26662306a36Sopenharmony_ci DA9150_QIF_UAVG_SIZE); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci res = (u64) (val->intval * 186ULL); 26962306a36Sopenharmony_ci do_div(res, 10000); 27062306a36Sopenharmony_ci val->intval = (int) res; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return 0; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic int da9150_fg_charge_full(struct da9150_fg *fg, 27662306a36Sopenharmony_ci union power_supply_propval *val) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_FCC_MAH, 27962306a36Sopenharmony_ci DA9150_QIF_FCC_MAH_SIZE); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci val->intval = val->intval * 1000; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* 28762306a36Sopenharmony_ci * Temperature reading from device is only valid if battery/system provides 28862306a36Sopenharmony_ci * valid NTC to associated pin of DA9150 chip. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_cistatic int da9150_fg_temp(struct da9150_fg *fg, 29162306a36Sopenharmony_ci union power_supply_propval *val) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_NTCAVG, 29462306a36Sopenharmony_ci DA9150_QIF_NTCAVG_SIZE); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci val->intval = (val->intval * 10) / 1048576; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic enum power_supply_property da9150_fg_props[] = { 30262306a36Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 30362306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_AVG, 30462306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_AVG, 30562306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL, 30662306a36Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, 30762306a36Sopenharmony_ci}; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int da9150_fg_get_prop(struct power_supply *psy, 31062306a36Sopenharmony_ci enum power_supply_property psp, 31162306a36Sopenharmony_ci union power_supply_propval *val) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct da9150_fg *fg = dev_get_drvdata(psy->dev.parent); 31462306a36Sopenharmony_ci int ret; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci switch (psp) { 31762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 31862306a36Sopenharmony_ci ret = da9150_fg_capacity(fg, val); 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 32162306a36Sopenharmony_ci ret = da9150_fg_current_avg(fg, val); 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_AVG: 32462306a36Sopenharmony_ci ret = da9150_fg_voltage_avg(fg, val); 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL: 32762306a36Sopenharmony_ci ret = da9150_fg_charge_full(fg, val); 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 33062306a36Sopenharmony_ci ret = da9150_fg_temp(fg, val); 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci default: 33362306a36Sopenharmony_ci ret = -EINVAL; 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return ret; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/* Repeated SOC check */ 34162306a36Sopenharmony_cistatic bool da9150_fg_soc_changed(struct da9150_fg *fg) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci union power_supply_propval val; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci da9150_fg_capacity(fg, &val); 34662306a36Sopenharmony_ci if (val.intval != fg->soc) { 34762306a36Sopenharmony_ci fg->soc = val.intval; 34862306a36Sopenharmony_ci return true; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return false; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic void da9150_fg_work(struct work_struct *work) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct da9150_fg *fg = container_of(work, struct da9150_fg, work.work); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* Report if SOC has changed */ 35962306a36Sopenharmony_ci if (da9150_fg_soc_changed(fg)) 36062306a36Sopenharmony_ci power_supply_changed(fg->battery); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci schedule_delayed_work(&fg->work, msecs_to_jiffies(fg->interval)); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci/* SOC level event configuration */ 36662306a36Sopenharmony_cistatic void da9150_fg_soc_event_config(struct da9150_fg *fg) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci int soc; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci soc = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT, 37162306a36Sopenharmony_ci DA9150_QIF_SOC_PCT_SIZE); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (soc > fg->warn_soc) { 37462306a36Sopenharmony_ci /* If SOC > warn level, set discharge warn level event */ 37562306a36Sopenharmony_ci da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT, 37662306a36Sopenharmony_ci DA9150_QIF_DISCHARGE_LIMIT_SIZE, 37762306a36Sopenharmony_ci fg->warn_soc + 1); 37862306a36Sopenharmony_ci } else if ((soc <= fg->warn_soc) && (soc > fg->crit_soc)) { 37962306a36Sopenharmony_ci /* 38062306a36Sopenharmony_ci * If SOC <= warn level, set discharge crit level event, 38162306a36Sopenharmony_ci * and set charge warn level event. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT, 38462306a36Sopenharmony_ci DA9150_QIF_DISCHARGE_LIMIT_SIZE, 38562306a36Sopenharmony_ci fg->crit_soc + 1); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT, 38862306a36Sopenharmony_ci DA9150_QIF_CHARGE_LIMIT_SIZE, 38962306a36Sopenharmony_ci fg->warn_soc); 39062306a36Sopenharmony_ci } else if (soc <= fg->crit_soc) { 39162306a36Sopenharmony_ci /* If SOC <= crit level, set charge crit level event */ 39262306a36Sopenharmony_ci da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT, 39362306a36Sopenharmony_ci DA9150_QIF_CHARGE_LIMIT_SIZE, 39462306a36Sopenharmony_ci fg->crit_soc); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic irqreturn_t da9150_fg_irq(int irq, void *data) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct da9150_fg *fg = data; 40162306a36Sopenharmony_ci u32 e_fg_status; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* Read FG IRQ status info */ 40462306a36Sopenharmony_ci e_fg_status = da9150_fg_read_attr(fg, DA9150_QIF_E_FG_STATUS, 40562306a36Sopenharmony_ci DA9150_QIF_E_FG_STATUS_SIZE); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* Handle warning/critical threhold events */ 40862306a36Sopenharmony_ci if (e_fg_status & DA9150_FG_IRQ_SOC_MASK) 40962306a36Sopenharmony_ci da9150_fg_soc_event_config(fg); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* Clear any FG IRQs */ 41262306a36Sopenharmony_ci da9150_fg_write_attr(fg, DA9150_QIF_E_FG_STATUS, 41362306a36Sopenharmony_ci DA9150_QIF_E_FG_STATUS_SIZE, e_fg_status); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return IRQ_HANDLED; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic struct da9150_fg_pdata *da9150_fg_dt_pdata(struct device *dev) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct device_node *fg_node = dev->of_node; 42162306a36Sopenharmony_ci struct da9150_fg_pdata *pdata; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(struct da9150_fg_pdata), GFP_KERNEL); 42462306a36Sopenharmony_ci if (!pdata) 42562306a36Sopenharmony_ci return NULL; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci of_property_read_u32(fg_node, "dlg,update-interval", 42862306a36Sopenharmony_ci &pdata->update_interval); 42962306a36Sopenharmony_ci of_property_read_u8(fg_node, "dlg,warn-soc-level", 43062306a36Sopenharmony_ci &pdata->warn_soc_lvl); 43162306a36Sopenharmony_ci of_property_read_u8(fg_node, "dlg,crit-soc-level", 43262306a36Sopenharmony_ci &pdata->crit_soc_lvl); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return pdata; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic const struct power_supply_desc fg_desc = { 43862306a36Sopenharmony_ci .name = "da9150-fg", 43962306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 44062306a36Sopenharmony_ci .properties = da9150_fg_props, 44162306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(da9150_fg_props), 44262306a36Sopenharmony_ci .get_property = da9150_fg_get_prop, 44362306a36Sopenharmony_ci}; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic int da9150_fg_probe(struct platform_device *pdev) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 44862306a36Sopenharmony_ci struct da9150 *da9150 = dev_get_drvdata(dev->parent); 44962306a36Sopenharmony_ci struct da9150_fg_pdata *fg_pdata = dev_get_platdata(dev); 45062306a36Sopenharmony_ci struct da9150_fg *fg; 45162306a36Sopenharmony_ci int ver, irq, ret = 0; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL); 45462306a36Sopenharmony_ci if (fg == NULL) 45562306a36Sopenharmony_ci return -ENOMEM; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci platform_set_drvdata(pdev, fg); 45862306a36Sopenharmony_ci fg->da9150 = da9150; 45962306a36Sopenharmony_ci fg->dev = dev; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci mutex_init(&fg->io_lock); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* Enable QIF */ 46462306a36Sopenharmony_ci da9150_set_bits(da9150, DA9150_CORE2WIRE_CTRL_A, DA9150_FG_QIF_EN_MASK, 46562306a36Sopenharmony_ci DA9150_FG_QIF_EN_MASK); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci fg->battery = devm_power_supply_register(dev, &fg_desc, NULL); 46862306a36Sopenharmony_ci if (IS_ERR(fg->battery)) { 46962306a36Sopenharmony_ci ret = PTR_ERR(fg->battery); 47062306a36Sopenharmony_ci return ret; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci ver = da9150_fg_read_attr(fg, DA9150_QIF_FW_MAIN_VER, 47462306a36Sopenharmony_ci DA9150_QIF_FW_MAIN_VER_SIZE); 47562306a36Sopenharmony_ci dev_info(dev, "Version: 0x%x\n", ver); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* Handle DT data if provided */ 47862306a36Sopenharmony_ci if (dev->of_node) { 47962306a36Sopenharmony_ci fg_pdata = da9150_fg_dt_pdata(dev); 48062306a36Sopenharmony_ci dev->platform_data = fg_pdata; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* Handle any pdata provided */ 48462306a36Sopenharmony_ci if (fg_pdata) { 48562306a36Sopenharmony_ci fg->interval = fg_pdata->update_interval; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (fg_pdata->warn_soc_lvl > 100) 48862306a36Sopenharmony_ci dev_warn(dev, "Invalid SOC warning level provided, Ignoring"); 48962306a36Sopenharmony_ci else 49062306a36Sopenharmony_ci fg->warn_soc = fg_pdata->warn_soc_lvl; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if ((fg_pdata->crit_soc_lvl > 100) || 49362306a36Sopenharmony_ci (fg_pdata->crit_soc_lvl >= fg_pdata->warn_soc_lvl)) 49462306a36Sopenharmony_ci dev_warn(dev, "Invalid SOC critical level provided, Ignoring"); 49562306a36Sopenharmony_ci else 49662306a36Sopenharmony_ci fg->crit_soc = fg_pdata->crit_soc_lvl; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* Configure initial SOC level events */ 50262306a36Sopenharmony_ci da9150_fg_soc_event_config(fg); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* 50562306a36Sopenharmony_ci * If an interval period has been provided then setup repeating 50662306a36Sopenharmony_ci * work for reporting data updates. 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_ci if (fg->interval) { 50962306a36Sopenharmony_ci ret = devm_delayed_work_autocancel(dev, &fg->work, 51062306a36Sopenharmony_ci da9150_fg_work); 51162306a36Sopenharmony_ci if (ret) { 51262306a36Sopenharmony_ci dev_err(dev, "Failed to init work\n"); 51362306a36Sopenharmony_ci return ret; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci schedule_delayed_work(&fg->work, 51762306a36Sopenharmony_ci msecs_to_jiffies(fg->interval)); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Register IRQ */ 52162306a36Sopenharmony_ci irq = platform_get_irq_byname(pdev, "FG"); 52262306a36Sopenharmony_ci if (irq < 0) 52362306a36Sopenharmony_ci return irq; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, irq, NULL, da9150_fg_irq, 52662306a36Sopenharmony_ci IRQF_ONESHOT, "FG", fg); 52762306a36Sopenharmony_ci if (ret) { 52862306a36Sopenharmony_ci dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret); 52962306a36Sopenharmony_ci return ret; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int da9150_fg_resume(struct platform_device *pdev) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct da9150_fg *fg = platform_get_drvdata(pdev); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* 54062306a36Sopenharmony_ci * Trigger SOC check to happen now so as to indicate any value change 54162306a36Sopenharmony_ci * since last check before suspend. 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_ci if (fg->interval) 54462306a36Sopenharmony_ci flush_delayed_work(&fg->work); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return 0; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic struct platform_driver da9150_fg_driver = { 55062306a36Sopenharmony_ci .driver = { 55162306a36Sopenharmony_ci .name = "da9150-fuel-gauge", 55262306a36Sopenharmony_ci }, 55362306a36Sopenharmony_ci .probe = da9150_fg_probe, 55462306a36Sopenharmony_ci .resume = da9150_fg_resume, 55562306a36Sopenharmony_ci}; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cimodule_platform_driver(da9150_fg_driver); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ciMODULE_DESCRIPTION("Fuel-Gauge Driver for DA9150"); 56062306a36Sopenharmony_ciMODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>"); 56162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 562