162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2012 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Main and Back-up battery management driver. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Note: Backup battery management is required in case of Li-Ion battery and not 862306a36Sopenharmony_ci * for capacitive battery. HREF boards have capacitive battery and hence backup 962306a36Sopenharmony_ci * battery management is not used and the supported code is available in this 1062306a36Sopenharmony_ci * driver. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Author: 1362306a36Sopenharmony_ci * Johan Palsson <johan.palsson@stericsson.com> 1462306a36Sopenharmony_ci * Karl Komierowski <karl.komierowski@stericsson.com> 1562306a36Sopenharmony_ci * Arun R Murthy <arun.murthy@stericsson.com> 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/component.h> 2162306a36Sopenharmony_ci#include <linux/device.h> 2262306a36Sopenharmony_ci#include <linux/interrupt.h> 2362306a36Sopenharmony_ci#include <linux/platform_device.h> 2462306a36Sopenharmony_ci#include <linux/power_supply.h> 2562306a36Sopenharmony_ci#include <linux/kobject.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/delay.h> 2862306a36Sopenharmony_ci#include <linux/time.h> 2962306a36Sopenharmony_ci#include <linux/time64.h> 3062306a36Sopenharmony_ci#include <linux/of.h> 3162306a36Sopenharmony_ci#include <linux/completion.h> 3262306a36Sopenharmony_ci#include <linux/mfd/core.h> 3362306a36Sopenharmony_ci#include <linux/mfd/abx500.h> 3462306a36Sopenharmony_ci#include <linux/mfd/abx500/ab8500.h> 3562306a36Sopenharmony_ci#include <linux/iio/consumer.h> 3662306a36Sopenharmony_ci#include <linux/kernel.h> 3762306a36Sopenharmony_ci#include <linux/fixp-arith.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include "ab8500-bm.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define FG_LSB_IN_MA 1627 4262306a36Sopenharmony_ci#define QLSB_NANO_AMP_HOURS_X10 1071 4362306a36Sopenharmony_ci#define INS_CURR_TIMEOUT (3 * HZ) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define SEC_TO_SAMPLE(S) (S * 4) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define NBR_AVG_SAMPLES 20 4862306a36Sopenharmony_ci#define WAIT_FOR_INST_CURRENT_MAX 70 4962306a36Sopenharmony_ci/* Currents higher than -500mA (dissipating) will make compensation unstable */ 5062306a36Sopenharmony_ci#define IGNORE_VBAT_HIGHCUR -500000 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define LOW_BAT_CHECK_INTERVAL (HZ / 16) /* 62.5 ms */ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define VALID_CAPACITY_SEC (45 * 60) /* 45 minutes */ 5562306a36Sopenharmony_ci#define BATT_OK_MIN 2360 /* mV */ 5662306a36Sopenharmony_ci#define BATT_OK_INCREMENT 50 /* mV */ 5762306a36Sopenharmony_ci#define BATT_OK_MAX_NR_INCREMENTS 0xE 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* FG constants */ 6062306a36Sopenharmony_ci#define BATT_OVV 0x01 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/** 6362306a36Sopenharmony_ci * struct ab8500_fg_interrupts - ab8500 fg interrupts 6462306a36Sopenharmony_ci * @name: name of the interrupt 6562306a36Sopenharmony_ci * @isr function pointer to the isr 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_cistruct ab8500_fg_interrupts { 6862306a36Sopenharmony_ci char *name; 6962306a36Sopenharmony_ci irqreturn_t (*isr)(int irq, void *data); 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cienum ab8500_fg_discharge_state { 7362306a36Sopenharmony_ci AB8500_FG_DISCHARGE_INIT, 7462306a36Sopenharmony_ci AB8500_FG_DISCHARGE_INITMEASURING, 7562306a36Sopenharmony_ci AB8500_FG_DISCHARGE_INIT_RECOVERY, 7662306a36Sopenharmony_ci AB8500_FG_DISCHARGE_RECOVERY, 7762306a36Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT_INIT, 7862306a36Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT, 7962306a36Sopenharmony_ci AB8500_FG_DISCHARGE_WAKEUP, 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic char *discharge_state[] = { 8362306a36Sopenharmony_ci "DISCHARGE_INIT", 8462306a36Sopenharmony_ci "DISCHARGE_INITMEASURING", 8562306a36Sopenharmony_ci "DISCHARGE_INIT_RECOVERY", 8662306a36Sopenharmony_ci "DISCHARGE_RECOVERY", 8762306a36Sopenharmony_ci "DISCHARGE_READOUT_INIT", 8862306a36Sopenharmony_ci "DISCHARGE_READOUT", 8962306a36Sopenharmony_ci "DISCHARGE_WAKEUP", 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cienum ab8500_fg_charge_state { 9362306a36Sopenharmony_ci AB8500_FG_CHARGE_INIT, 9462306a36Sopenharmony_ci AB8500_FG_CHARGE_READOUT, 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic char *charge_state[] = { 9862306a36Sopenharmony_ci "CHARGE_INIT", 9962306a36Sopenharmony_ci "CHARGE_READOUT", 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cienum ab8500_fg_calibration_state { 10362306a36Sopenharmony_ci AB8500_FG_CALIB_INIT, 10462306a36Sopenharmony_ci AB8500_FG_CALIB_WAIT, 10562306a36Sopenharmony_ci AB8500_FG_CALIB_END, 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistruct ab8500_fg_avg_cap { 10962306a36Sopenharmony_ci int avg; 11062306a36Sopenharmony_ci int samples[NBR_AVG_SAMPLES]; 11162306a36Sopenharmony_ci time64_t time_stamps[NBR_AVG_SAMPLES]; 11262306a36Sopenharmony_ci int pos; 11362306a36Sopenharmony_ci int nbr_samples; 11462306a36Sopenharmony_ci int sum; 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistruct ab8500_fg_cap_scaling { 11862306a36Sopenharmony_ci bool enable; 11962306a36Sopenharmony_ci int cap_to_scale[2]; 12062306a36Sopenharmony_ci int disable_cap_level; 12162306a36Sopenharmony_ci int scaled_cap; 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct ab8500_fg_battery_capacity { 12562306a36Sopenharmony_ci int max_mah_design; 12662306a36Sopenharmony_ci int max_mah; 12762306a36Sopenharmony_ci int mah; 12862306a36Sopenharmony_ci int permille; 12962306a36Sopenharmony_ci int level; 13062306a36Sopenharmony_ci int prev_mah; 13162306a36Sopenharmony_ci int prev_percent; 13262306a36Sopenharmony_ci int prev_level; 13362306a36Sopenharmony_ci int user_mah; 13462306a36Sopenharmony_ci struct ab8500_fg_cap_scaling cap_scale; 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistruct ab8500_fg_flags { 13862306a36Sopenharmony_ci bool fg_enabled; 13962306a36Sopenharmony_ci bool conv_done; 14062306a36Sopenharmony_ci bool charging; 14162306a36Sopenharmony_ci bool fully_charged; 14262306a36Sopenharmony_ci bool force_full; 14362306a36Sopenharmony_ci bool low_bat_delay; 14462306a36Sopenharmony_ci bool low_bat; 14562306a36Sopenharmony_ci bool bat_ovv; 14662306a36Sopenharmony_ci bool batt_unknown; 14762306a36Sopenharmony_ci bool calibrate; 14862306a36Sopenharmony_ci bool user_cap; 14962306a36Sopenharmony_ci bool batt_id_received; 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistruct inst_curr_result_list { 15362306a36Sopenharmony_ci struct list_head list; 15462306a36Sopenharmony_ci int *result; 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/** 15862306a36Sopenharmony_ci * struct ab8500_fg - ab8500 FG device information 15962306a36Sopenharmony_ci * @dev: Pointer to the structure device 16062306a36Sopenharmony_ci * @node: a list of AB8500 FGs, hence prepared for reentrance 16162306a36Sopenharmony_ci * @irq holds the CCEOC interrupt number 16262306a36Sopenharmony_ci * @vbat_uv: Battery voltage in uV 16362306a36Sopenharmony_ci * @vbat_nom_uv: Nominal battery voltage in uV 16462306a36Sopenharmony_ci * @inst_curr_ua: Instantenous battery current in uA 16562306a36Sopenharmony_ci * @avg_curr_ua: Average battery current in uA 16662306a36Sopenharmony_ci * @bat_temp battery temperature 16762306a36Sopenharmony_ci * @fg_samples: Number of samples used in the FG accumulation 16862306a36Sopenharmony_ci * @accu_charge: Accumulated charge from the last conversion 16962306a36Sopenharmony_ci * @recovery_cnt: Counter for recovery mode 17062306a36Sopenharmony_ci * @high_curr_cnt: Counter for high current mode 17162306a36Sopenharmony_ci * @init_cnt: Counter for init mode 17262306a36Sopenharmony_ci * @low_bat_cnt Counter for number of consecutive low battery measures 17362306a36Sopenharmony_ci * @nbr_cceoc_irq_cnt Counter for number of CCEOC irqs received since enabled 17462306a36Sopenharmony_ci * @recovery_needed: Indicate if recovery is needed 17562306a36Sopenharmony_ci * @high_curr_mode: Indicate if we're in high current mode 17662306a36Sopenharmony_ci * @init_capacity: Indicate if initial capacity measuring should be done 17762306a36Sopenharmony_ci * @turn_off_fg: True if fg was off before current measurement 17862306a36Sopenharmony_ci * @calib_state State during offset calibration 17962306a36Sopenharmony_ci * @discharge_state: Current discharge state 18062306a36Sopenharmony_ci * @charge_state: Current charge state 18162306a36Sopenharmony_ci * @ab8500_fg_started Completion struct used for the instant current start 18262306a36Sopenharmony_ci * @ab8500_fg_complete Completion struct used for the instant current reading 18362306a36Sopenharmony_ci * @flags: Structure for information about events triggered 18462306a36Sopenharmony_ci * @bat_cap: Structure for battery capacity specific parameters 18562306a36Sopenharmony_ci * @avg_cap: Average capacity filter 18662306a36Sopenharmony_ci * @parent: Pointer to the struct ab8500 18762306a36Sopenharmony_ci * @main_bat_v: ADC channel for the main battery voltage 18862306a36Sopenharmony_ci * @bm: Platform specific battery management information 18962306a36Sopenharmony_ci * @fg_psy: Structure that holds the FG specific battery properties 19062306a36Sopenharmony_ci * @fg_wq: Work queue for running the FG algorithm 19162306a36Sopenharmony_ci * @fg_periodic_work: Work to run the FG algorithm periodically 19262306a36Sopenharmony_ci * @fg_low_bat_work: Work to check low bat condition 19362306a36Sopenharmony_ci * @fg_reinit_work Work used to reset and reinitialise the FG algorithm 19462306a36Sopenharmony_ci * @fg_work: Work to run the FG algorithm instantly 19562306a36Sopenharmony_ci * @fg_acc_cur_work: Work to read the FG accumulator 19662306a36Sopenharmony_ci * @fg_check_hw_failure_work: Work for checking HW state 19762306a36Sopenharmony_ci * @cc_lock: Mutex for locking the CC 19862306a36Sopenharmony_ci * @fg_kobject: Structure of type kobject 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistruct ab8500_fg { 20162306a36Sopenharmony_ci struct device *dev; 20262306a36Sopenharmony_ci struct list_head node; 20362306a36Sopenharmony_ci int irq; 20462306a36Sopenharmony_ci int vbat_uv; 20562306a36Sopenharmony_ci int vbat_nom_uv; 20662306a36Sopenharmony_ci int inst_curr_ua; 20762306a36Sopenharmony_ci int avg_curr_ua; 20862306a36Sopenharmony_ci int bat_temp; 20962306a36Sopenharmony_ci int fg_samples; 21062306a36Sopenharmony_ci int accu_charge; 21162306a36Sopenharmony_ci int recovery_cnt; 21262306a36Sopenharmony_ci int high_curr_cnt; 21362306a36Sopenharmony_ci int init_cnt; 21462306a36Sopenharmony_ci int low_bat_cnt; 21562306a36Sopenharmony_ci int nbr_cceoc_irq_cnt; 21662306a36Sopenharmony_ci u32 line_impedance_uohm; 21762306a36Sopenharmony_ci bool recovery_needed; 21862306a36Sopenharmony_ci bool high_curr_mode; 21962306a36Sopenharmony_ci bool init_capacity; 22062306a36Sopenharmony_ci bool turn_off_fg; 22162306a36Sopenharmony_ci enum ab8500_fg_calibration_state calib_state; 22262306a36Sopenharmony_ci enum ab8500_fg_discharge_state discharge_state; 22362306a36Sopenharmony_ci enum ab8500_fg_charge_state charge_state; 22462306a36Sopenharmony_ci struct completion ab8500_fg_started; 22562306a36Sopenharmony_ci struct completion ab8500_fg_complete; 22662306a36Sopenharmony_ci struct ab8500_fg_flags flags; 22762306a36Sopenharmony_ci struct ab8500_fg_battery_capacity bat_cap; 22862306a36Sopenharmony_ci struct ab8500_fg_avg_cap avg_cap; 22962306a36Sopenharmony_ci struct ab8500 *parent; 23062306a36Sopenharmony_ci struct iio_channel *main_bat_v; 23162306a36Sopenharmony_ci struct ab8500_bm_data *bm; 23262306a36Sopenharmony_ci struct power_supply *fg_psy; 23362306a36Sopenharmony_ci struct workqueue_struct *fg_wq; 23462306a36Sopenharmony_ci struct delayed_work fg_periodic_work; 23562306a36Sopenharmony_ci struct delayed_work fg_low_bat_work; 23662306a36Sopenharmony_ci struct delayed_work fg_reinit_work; 23762306a36Sopenharmony_ci struct work_struct fg_work; 23862306a36Sopenharmony_ci struct work_struct fg_acc_cur_work; 23962306a36Sopenharmony_ci struct delayed_work fg_check_hw_failure_work; 24062306a36Sopenharmony_ci struct mutex cc_lock; 24162306a36Sopenharmony_ci struct kobject fg_kobject; 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_cistatic LIST_HEAD(ab8500_fg_list); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/** 24662306a36Sopenharmony_ci * ab8500_fg_get() - returns a reference to the primary AB8500 fuel gauge 24762306a36Sopenharmony_ci * (i.e. the first fuel gauge in the instance list) 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_cistruct ab8500_fg *ab8500_fg_get(void) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci return list_first_entry_or_null(&ab8500_fg_list, struct ab8500_fg, 25262306a36Sopenharmony_ci node); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/* Main battery properties */ 25662306a36Sopenharmony_cistatic enum power_supply_property ab8500_fg_props[] = { 25762306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 25862306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 25962306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_AVG, 26062306a36Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 26162306a36Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_FULL, 26262306a36Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_NOW, 26362306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 26462306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL, 26562306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_NOW, 26662306a36Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 26762306a36Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY_LEVEL, 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* 27162306a36Sopenharmony_ci * This array maps the raw hex value to lowbat voltage used by the AB8500 27262306a36Sopenharmony_ci * Values taken from the UM0836, in microvolts. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_cistatic int ab8500_fg_lowbat_voltage_map[] = { 27562306a36Sopenharmony_ci 2300000, 27662306a36Sopenharmony_ci 2325000, 27762306a36Sopenharmony_ci 2350000, 27862306a36Sopenharmony_ci 2375000, 27962306a36Sopenharmony_ci 2400000, 28062306a36Sopenharmony_ci 2425000, 28162306a36Sopenharmony_ci 2450000, 28262306a36Sopenharmony_ci 2475000, 28362306a36Sopenharmony_ci 2500000, 28462306a36Sopenharmony_ci 2525000, 28562306a36Sopenharmony_ci 2550000, 28662306a36Sopenharmony_ci 2575000, 28762306a36Sopenharmony_ci 2600000, 28862306a36Sopenharmony_ci 2625000, 28962306a36Sopenharmony_ci 2650000, 29062306a36Sopenharmony_ci 2675000, 29162306a36Sopenharmony_ci 2700000, 29262306a36Sopenharmony_ci 2725000, 29362306a36Sopenharmony_ci 2750000, 29462306a36Sopenharmony_ci 2775000, 29562306a36Sopenharmony_ci 2800000, 29662306a36Sopenharmony_ci 2825000, 29762306a36Sopenharmony_ci 2850000, 29862306a36Sopenharmony_ci 2875000, 29962306a36Sopenharmony_ci 2900000, 30062306a36Sopenharmony_ci 2925000, 30162306a36Sopenharmony_ci 2950000, 30262306a36Sopenharmony_ci 2975000, 30362306a36Sopenharmony_ci 3000000, 30462306a36Sopenharmony_ci 3025000, 30562306a36Sopenharmony_ci 3050000, 30662306a36Sopenharmony_ci 3075000, 30762306a36Sopenharmony_ci 3100000, 30862306a36Sopenharmony_ci 3125000, 30962306a36Sopenharmony_ci 3150000, 31062306a36Sopenharmony_ci 3175000, 31162306a36Sopenharmony_ci 3200000, 31262306a36Sopenharmony_ci 3225000, 31362306a36Sopenharmony_ci 3250000, 31462306a36Sopenharmony_ci 3275000, 31562306a36Sopenharmony_ci 3300000, 31662306a36Sopenharmony_ci 3325000, 31762306a36Sopenharmony_ci 3350000, 31862306a36Sopenharmony_ci 3375000, 31962306a36Sopenharmony_ci 3400000, 32062306a36Sopenharmony_ci 3425000, 32162306a36Sopenharmony_ci 3450000, 32262306a36Sopenharmony_ci 3475000, 32362306a36Sopenharmony_ci 3500000, 32462306a36Sopenharmony_ci 3525000, 32562306a36Sopenharmony_ci 3550000, 32662306a36Sopenharmony_ci 3575000, 32762306a36Sopenharmony_ci 3600000, 32862306a36Sopenharmony_ci 3625000, 32962306a36Sopenharmony_ci 3650000, 33062306a36Sopenharmony_ci 3675000, 33162306a36Sopenharmony_ci 3700000, 33262306a36Sopenharmony_ci 3725000, 33362306a36Sopenharmony_ci 3750000, 33462306a36Sopenharmony_ci 3775000, 33562306a36Sopenharmony_ci 3800000, 33662306a36Sopenharmony_ci 3825000, 33762306a36Sopenharmony_ci 3850000, 33862306a36Sopenharmony_ci 3850000, 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic u8 ab8500_volt_to_regval(int voltage_uv) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci int i; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (voltage_uv < ab8500_fg_lowbat_voltage_map[0]) 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ab8500_fg_lowbat_voltage_map); i++) { 34962306a36Sopenharmony_ci if (voltage_uv < ab8500_fg_lowbat_voltage_map[i]) 35062306a36Sopenharmony_ci return (u8) i - 1; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* If not captured above, return index of last element */ 35462306a36Sopenharmony_ci return (u8) ARRAY_SIZE(ab8500_fg_lowbat_voltage_map) - 1; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/** 35862306a36Sopenharmony_ci * ab8500_fg_is_low_curr() - Low or high current mode 35962306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 36062306a36Sopenharmony_ci * @curr_ua: the current to base or our decision on in microampere 36162306a36Sopenharmony_ci * 36262306a36Sopenharmony_ci * Low current mode if the current consumption is below a certain threshold 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_cistatic int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr_ua) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci /* 36762306a36Sopenharmony_ci * We want to know if we're in low current mode 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ci if (curr_ua > -di->bm->fg_params->high_curr_threshold_ua) 37062306a36Sopenharmony_ci return true; 37162306a36Sopenharmony_ci else 37262306a36Sopenharmony_ci return false; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/** 37662306a36Sopenharmony_ci * ab8500_fg_add_cap_sample() - Add capacity to average filter 37762306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 37862306a36Sopenharmony_ci * @sample: the capacity in mAh to add to the filter 37962306a36Sopenharmony_ci * 38062306a36Sopenharmony_ci * A capacity is added to the filter and a new mean capacity is calculated and 38162306a36Sopenharmony_ci * returned 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_cistatic int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci time64_t now = ktime_get_boottime_seconds(); 38662306a36Sopenharmony_ci struct ab8500_fg_avg_cap *avg = &di->avg_cap; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci do { 38962306a36Sopenharmony_ci avg->sum += sample - avg->samples[avg->pos]; 39062306a36Sopenharmony_ci avg->samples[avg->pos] = sample; 39162306a36Sopenharmony_ci avg->time_stamps[avg->pos] = now; 39262306a36Sopenharmony_ci avg->pos++; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (avg->pos == NBR_AVG_SAMPLES) 39562306a36Sopenharmony_ci avg->pos = 0; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (avg->nbr_samples < NBR_AVG_SAMPLES) 39862306a36Sopenharmony_ci avg->nbr_samples++; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* 40162306a36Sopenharmony_ci * Check the time stamp for each sample. If too old, 40262306a36Sopenharmony_ci * replace with latest sample 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_ci } while (now - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci avg->avg = avg->sum / avg->nbr_samples; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return avg->avg; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci/** 41262306a36Sopenharmony_ci * ab8500_fg_clear_cap_samples() - Clear average filter 41362306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 41462306a36Sopenharmony_ci * 41562306a36Sopenharmony_ci * The capacity filter is reset to zero. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_cistatic void ab8500_fg_clear_cap_samples(struct ab8500_fg *di) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci int i; 42062306a36Sopenharmony_ci struct ab8500_fg_avg_cap *avg = &di->avg_cap; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci avg->pos = 0; 42362306a36Sopenharmony_ci avg->nbr_samples = 0; 42462306a36Sopenharmony_ci avg->sum = 0; 42562306a36Sopenharmony_ci avg->avg = 0; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci for (i = 0; i < NBR_AVG_SAMPLES; i++) { 42862306a36Sopenharmony_ci avg->samples[i] = 0; 42962306a36Sopenharmony_ci avg->time_stamps[i] = 0; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci/** 43462306a36Sopenharmony_ci * ab8500_fg_fill_cap_sample() - Fill average filter 43562306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 43662306a36Sopenharmony_ci * @sample: the capacity in mAh to fill the filter with 43762306a36Sopenharmony_ci * 43862306a36Sopenharmony_ci * The capacity filter is filled with a capacity in mAh 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_cistatic void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci int i; 44362306a36Sopenharmony_ci time64_t now; 44462306a36Sopenharmony_ci struct ab8500_fg_avg_cap *avg = &di->avg_cap; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci now = ktime_get_boottime_seconds(); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci for (i = 0; i < NBR_AVG_SAMPLES; i++) { 44962306a36Sopenharmony_ci avg->samples[i] = sample; 45062306a36Sopenharmony_ci avg->time_stamps[i] = now; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci avg->pos = 0; 45462306a36Sopenharmony_ci avg->nbr_samples = NBR_AVG_SAMPLES; 45562306a36Sopenharmony_ci avg->sum = sample * NBR_AVG_SAMPLES; 45662306a36Sopenharmony_ci avg->avg = sample; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci/** 46062306a36Sopenharmony_ci * ab8500_fg_coulomb_counter() - enable coulomb counter 46162306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 46262306a36Sopenharmony_ci * @enable: enable/disable 46362306a36Sopenharmony_ci * 46462306a36Sopenharmony_ci * Enable/Disable coulomb counter. 46562306a36Sopenharmony_ci * On failure returns negative value. 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_cistatic int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci int ret = 0; 47062306a36Sopenharmony_ci mutex_lock(&di->cc_lock); 47162306a36Sopenharmony_ci if (enable) { 47262306a36Sopenharmony_ci /* To be able to reprogram the number of samples, we have to 47362306a36Sopenharmony_ci * first stop the CC and then enable it again */ 47462306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 47562306a36Sopenharmony_ci AB8500_RTC_CC_CONF_REG, 0x00); 47662306a36Sopenharmony_ci if (ret) 47762306a36Sopenharmony_ci goto cc_err; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Program the samples */ 48062306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, 48162306a36Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU, 48262306a36Sopenharmony_ci di->fg_samples); 48362306a36Sopenharmony_ci if (ret) 48462306a36Sopenharmony_ci goto cc_err; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* Start the CC */ 48762306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 48862306a36Sopenharmony_ci AB8500_RTC_CC_CONF_REG, 48962306a36Sopenharmony_ci (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA)); 49062306a36Sopenharmony_ci if (ret) 49162306a36Sopenharmony_ci goto cc_err; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci di->flags.fg_enabled = true; 49462306a36Sopenharmony_ci } else { 49562306a36Sopenharmony_ci /* Clear any pending read requests */ 49662306a36Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 49762306a36Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 49862306a36Sopenharmony_ci (RESET_ACCU | READ_REQ), 0); 49962306a36Sopenharmony_ci if (ret) 50062306a36Sopenharmony_ci goto cc_err; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, 50362306a36Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU_CTRL, 0); 50462306a36Sopenharmony_ci if (ret) 50562306a36Sopenharmony_ci goto cc_err; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* Stop the CC */ 50862306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 50962306a36Sopenharmony_ci AB8500_RTC_CC_CONF_REG, 0); 51062306a36Sopenharmony_ci if (ret) 51162306a36Sopenharmony_ci goto cc_err; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci di->flags.fg_enabled = false; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci dev_dbg(di->dev, " CC enabled: %d Samples: %d\n", 51762306a36Sopenharmony_ci enable, di->fg_samples); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci mutex_unlock(&di->cc_lock); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return ret; 52262306a36Sopenharmony_cicc_err: 52362306a36Sopenharmony_ci dev_err(di->dev, "%s Enabling coulomb counter failed\n", __func__); 52462306a36Sopenharmony_ci mutex_unlock(&di->cc_lock); 52562306a36Sopenharmony_ci return ret; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci/** 52962306a36Sopenharmony_ci * ab8500_fg_inst_curr_start() - start battery instantaneous current 53062306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 53162306a36Sopenharmony_ci * 53262306a36Sopenharmony_ci * Returns 0 or error code 53362306a36Sopenharmony_ci * Note: This is part "one" and has to be called before 53462306a36Sopenharmony_ci * ab8500_fg_inst_curr_finalize() 53562306a36Sopenharmony_ci */ 53662306a36Sopenharmony_ciint ab8500_fg_inst_curr_start(struct ab8500_fg *di) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci u8 reg_val; 53962306a36Sopenharmony_ci int ret; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci mutex_lock(&di->cc_lock); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci di->nbr_cceoc_irq_cnt = 0; 54462306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 54562306a36Sopenharmony_ci AB8500_RTC_CC_CONF_REG, ®_val); 54662306a36Sopenharmony_ci if (ret < 0) 54762306a36Sopenharmony_ci goto fail; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (!(reg_val & CC_PWR_UP_ENA)) { 55062306a36Sopenharmony_ci dev_dbg(di->dev, "%s Enable FG\n", __func__); 55162306a36Sopenharmony_ci di->turn_off_fg = true; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* Program the samples */ 55462306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, 55562306a36Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU, 55662306a36Sopenharmony_ci SEC_TO_SAMPLE(10)); 55762306a36Sopenharmony_ci if (ret) 55862306a36Sopenharmony_ci goto fail; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* Start the CC */ 56162306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 56262306a36Sopenharmony_ci AB8500_RTC_CC_CONF_REG, 56362306a36Sopenharmony_ci (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA)); 56462306a36Sopenharmony_ci if (ret) 56562306a36Sopenharmony_ci goto fail; 56662306a36Sopenharmony_ci } else { 56762306a36Sopenharmony_ci di->turn_off_fg = false; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Return and WFI */ 57162306a36Sopenharmony_ci reinit_completion(&di->ab8500_fg_started); 57262306a36Sopenharmony_ci reinit_completion(&di->ab8500_fg_complete); 57362306a36Sopenharmony_ci enable_irq(di->irq); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* Note: cc_lock is still locked */ 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_cifail: 57862306a36Sopenharmony_ci mutex_unlock(&di->cc_lock); 57962306a36Sopenharmony_ci return ret; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci/** 58362306a36Sopenharmony_ci * ab8500_fg_inst_curr_started() - check if fg conversion has started 58462306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 58562306a36Sopenharmony_ci * 58662306a36Sopenharmony_ci * Returns 1 if conversion started, 0 if still waiting 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ciint ab8500_fg_inst_curr_started(struct ab8500_fg *di) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci return completion_done(&di->ab8500_fg_started); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci/** 59462306a36Sopenharmony_ci * ab8500_fg_inst_curr_done() - check if fg conversion is done 59562306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 59662306a36Sopenharmony_ci * 59762306a36Sopenharmony_ci * Returns 1 if conversion done, 0 if still waiting 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_ciint ab8500_fg_inst_curr_done(struct ab8500_fg *di) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci return completion_done(&di->ab8500_fg_complete); 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci/** 60562306a36Sopenharmony_ci * ab8500_fg_inst_curr_finalize() - battery instantaneous current 60662306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 60762306a36Sopenharmony_ci * @curr_ua: battery instantenous current in microampere (on success) 60862306a36Sopenharmony_ci * 60962306a36Sopenharmony_ci * Returns 0 or an error code 61062306a36Sopenharmony_ci * Note: This is part "two" and has to be called at earliest 250 ms 61162306a36Sopenharmony_ci * after ab8500_fg_inst_curr_start() 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_ciint ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *curr_ua) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci u8 low, high; 61662306a36Sopenharmony_ci int val; 61762306a36Sopenharmony_ci int ret; 61862306a36Sopenharmony_ci unsigned long timeout; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (!completion_done(&di->ab8500_fg_complete)) { 62162306a36Sopenharmony_ci timeout = wait_for_completion_timeout( 62262306a36Sopenharmony_ci &di->ab8500_fg_complete, 62362306a36Sopenharmony_ci INS_CURR_TIMEOUT); 62462306a36Sopenharmony_ci dev_dbg(di->dev, "Finalize time: %d ms\n", 62562306a36Sopenharmony_ci jiffies_to_msecs(INS_CURR_TIMEOUT - timeout)); 62662306a36Sopenharmony_ci if (!timeout) { 62762306a36Sopenharmony_ci ret = -ETIME; 62862306a36Sopenharmony_ci disable_irq(di->irq); 62962306a36Sopenharmony_ci di->nbr_cceoc_irq_cnt = 0; 63062306a36Sopenharmony_ci dev_err(di->dev, "completion timed out [%d]\n", 63162306a36Sopenharmony_ci __LINE__); 63262306a36Sopenharmony_ci goto fail; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci disable_irq(di->irq); 63762306a36Sopenharmony_ci di->nbr_cceoc_irq_cnt = 0; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 64062306a36Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 64162306a36Sopenharmony_ci READ_REQ, READ_REQ); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* 100uS between read request and read is needed */ 64462306a36Sopenharmony_ci usleep_range(100, 100); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* Read CC Sample conversion value Low and high */ 64762306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE, 64862306a36Sopenharmony_ci AB8500_GASG_CC_SMPL_CNVL_REG, &low); 64962306a36Sopenharmony_ci if (ret < 0) 65062306a36Sopenharmony_ci goto fail; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE, 65362306a36Sopenharmony_ci AB8500_GASG_CC_SMPL_CNVH_REG, &high); 65462306a36Sopenharmony_ci if (ret < 0) 65562306a36Sopenharmony_ci goto fail; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* 65862306a36Sopenharmony_ci * negative value for Discharging 65962306a36Sopenharmony_ci * convert 2's complement into decimal 66062306a36Sopenharmony_ci */ 66162306a36Sopenharmony_ci if (high & 0x10) 66262306a36Sopenharmony_ci val = (low | (high << 8) | 0xFFFFE000); 66362306a36Sopenharmony_ci else 66462306a36Sopenharmony_ci val = (low | (high << 8)); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* 66762306a36Sopenharmony_ci * Convert to unit value in mA 66862306a36Sopenharmony_ci * Full scale input voltage is 66962306a36Sopenharmony_ci * 63.160mV => LSB = 63.160mV/(4096*res) = 1.542.000 uA 67062306a36Sopenharmony_ci * Given a 250ms conversion cycle time the LSB corresponds 67162306a36Sopenharmony_ci * to 107.1 nAh. Convert to current by dividing by the conversion 67262306a36Sopenharmony_ci * time in hours (250ms = 1 / (3600 * 4)h) 67362306a36Sopenharmony_ci * 107.1nAh assumes 10mOhm, but fg_res is in 0.1mOhm 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci val = (val * QLSB_NANO_AMP_HOURS_X10 * 36 * 4) / di->bm->fg_res; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (di->turn_off_fg) { 67862306a36Sopenharmony_ci dev_dbg(di->dev, "%s Disable FG\n", __func__); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* Clear any pending read requests */ 68162306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, 68262306a36Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0); 68362306a36Sopenharmony_ci if (ret) 68462306a36Sopenharmony_ci goto fail; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Stop the CC */ 68762306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 68862306a36Sopenharmony_ci AB8500_RTC_CC_CONF_REG, 0); 68962306a36Sopenharmony_ci if (ret) 69062306a36Sopenharmony_ci goto fail; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci mutex_unlock(&di->cc_lock); 69362306a36Sopenharmony_ci *curr_ua = val; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return 0; 69662306a36Sopenharmony_cifail: 69762306a36Sopenharmony_ci mutex_unlock(&di->cc_lock); 69862306a36Sopenharmony_ci return ret; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci/** 70262306a36Sopenharmony_ci * ab8500_fg_inst_curr_blocking() - battery instantaneous current 70362306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 70462306a36Sopenharmony_ci * 70562306a36Sopenharmony_ci * Returns battery instantenous current in microampere (on success) 70662306a36Sopenharmony_ci * else error code 70762306a36Sopenharmony_ci */ 70862306a36Sopenharmony_ciint ab8500_fg_inst_curr_blocking(struct ab8500_fg *di) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci int ret; 71162306a36Sopenharmony_ci unsigned long timeout; 71262306a36Sopenharmony_ci int curr_ua = 0; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci ret = ab8500_fg_inst_curr_start(di); 71562306a36Sopenharmony_ci if (ret) { 71662306a36Sopenharmony_ci dev_err(di->dev, "Failed to initialize fg_inst\n"); 71762306a36Sopenharmony_ci return 0; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* Wait for CC to actually start */ 72162306a36Sopenharmony_ci if (!completion_done(&di->ab8500_fg_started)) { 72262306a36Sopenharmony_ci timeout = wait_for_completion_timeout( 72362306a36Sopenharmony_ci &di->ab8500_fg_started, 72462306a36Sopenharmony_ci INS_CURR_TIMEOUT); 72562306a36Sopenharmony_ci dev_dbg(di->dev, "Start time: %d ms\n", 72662306a36Sopenharmony_ci jiffies_to_msecs(INS_CURR_TIMEOUT - timeout)); 72762306a36Sopenharmony_ci if (!timeout) { 72862306a36Sopenharmony_ci ret = -ETIME; 72962306a36Sopenharmony_ci dev_err(di->dev, "completion timed out [%d]\n", 73062306a36Sopenharmony_ci __LINE__); 73162306a36Sopenharmony_ci goto fail; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci ret = ab8500_fg_inst_curr_finalize(di, &curr_ua); 73662306a36Sopenharmony_ci if (ret) { 73762306a36Sopenharmony_ci dev_err(di->dev, "Failed to finalize fg_inst\n"); 73862306a36Sopenharmony_ci return 0; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci dev_dbg(di->dev, "%s instant current: %d uA", __func__, curr_ua); 74262306a36Sopenharmony_ci return curr_ua; 74362306a36Sopenharmony_cifail: 74462306a36Sopenharmony_ci disable_irq(di->irq); 74562306a36Sopenharmony_ci mutex_unlock(&di->cc_lock); 74662306a36Sopenharmony_ci return ret; 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci/** 75062306a36Sopenharmony_ci * ab8500_fg_acc_cur_work() - average battery current 75162306a36Sopenharmony_ci * @work: pointer to the work_struct structure 75262306a36Sopenharmony_ci * 75362306a36Sopenharmony_ci * Updated the average battery current obtained from the 75462306a36Sopenharmony_ci * coulomb counter. 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_cistatic void ab8500_fg_acc_cur_work(struct work_struct *work) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci int val; 75962306a36Sopenharmony_ci int ret; 76062306a36Sopenharmony_ci u8 low, med, high; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci struct ab8500_fg *di = container_of(work, 76362306a36Sopenharmony_ci struct ab8500_fg, fg_acc_cur_work); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci mutex_lock(&di->cc_lock); 76662306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_GAS_GAUGE, 76762306a36Sopenharmony_ci AB8500_GASG_CC_NCOV_ACCU_CTRL, RD_NCONV_ACCU_REQ); 76862306a36Sopenharmony_ci if (ret) 76962306a36Sopenharmony_ci goto exit; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE, 77262306a36Sopenharmony_ci AB8500_GASG_CC_NCOV_ACCU_LOW, &low); 77362306a36Sopenharmony_ci if (ret < 0) 77462306a36Sopenharmony_ci goto exit; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE, 77762306a36Sopenharmony_ci AB8500_GASG_CC_NCOV_ACCU_MED, &med); 77862306a36Sopenharmony_ci if (ret < 0) 77962306a36Sopenharmony_ci goto exit; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE, 78262306a36Sopenharmony_ci AB8500_GASG_CC_NCOV_ACCU_HIGH, &high); 78362306a36Sopenharmony_ci if (ret < 0) 78462306a36Sopenharmony_ci goto exit; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* Check for sign bit in case of negative value, 2's complement */ 78762306a36Sopenharmony_ci if (high & 0x10) 78862306a36Sopenharmony_ci val = (low | (med << 8) | (high << 16) | 0xFFE00000); 78962306a36Sopenharmony_ci else 79062306a36Sopenharmony_ci val = (low | (med << 8) | (high << 16)); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* 79362306a36Sopenharmony_ci * Convert to uAh 79462306a36Sopenharmony_ci * Given a 250ms conversion cycle time the LSB corresponds 79562306a36Sopenharmony_ci * to 112.9 nAh. 79662306a36Sopenharmony_ci * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_ci di->accu_charge = (val * QLSB_NANO_AMP_HOURS_X10) / 79962306a36Sopenharmony_ci (100 * di->bm->fg_res); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* 80262306a36Sopenharmony_ci * Convert to unit value in uA 80362306a36Sopenharmony_ci * by dividing by the conversion 80462306a36Sopenharmony_ci * time in hours (= samples / (3600 * 4)h) 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_ci di->avg_curr_ua = (val * QLSB_NANO_AMP_HOURS_X10 * 36) / 80762306a36Sopenharmony_ci (di->bm->fg_res * (di->fg_samples / 4)); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci di->flags.conv_done = true; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci mutex_unlock(&di->cc_lock); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci queue_work(di->fg_wq, &di->fg_work); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci dev_dbg(di->dev, "fg_res: %d, fg_samples: %d, gasg: %d, accu_charge: %d \n", 81662306a36Sopenharmony_ci di->bm->fg_res, di->fg_samples, val, di->accu_charge); 81762306a36Sopenharmony_ci return; 81862306a36Sopenharmony_ciexit: 81962306a36Sopenharmony_ci dev_err(di->dev, 82062306a36Sopenharmony_ci "Failed to read or write gas gauge registers\n"); 82162306a36Sopenharmony_ci mutex_unlock(&di->cc_lock); 82262306a36Sopenharmony_ci queue_work(di->fg_wq, &di->fg_work); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci/** 82662306a36Sopenharmony_ci * ab8500_fg_bat_voltage() - get battery voltage 82762306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 82862306a36Sopenharmony_ci * 82962306a36Sopenharmony_ci * Returns battery voltage in microvolts (on success) else error code 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_cistatic int ab8500_fg_bat_voltage(struct ab8500_fg *di) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci int vbat, ret; 83462306a36Sopenharmony_ci static int prev; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci ret = iio_read_channel_processed(di->main_bat_v, &vbat); 83762306a36Sopenharmony_ci if (ret < 0) { 83862306a36Sopenharmony_ci dev_err(di->dev, 83962306a36Sopenharmony_ci "%s ADC conversion failed, using previous value\n", 84062306a36Sopenharmony_ci __func__); 84162306a36Sopenharmony_ci return prev; 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* IIO returns millivolts but we want microvolts */ 84562306a36Sopenharmony_ci vbat *= 1000; 84662306a36Sopenharmony_ci prev = vbat; 84762306a36Sopenharmony_ci return vbat; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci/** 85162306a36Sopenharmony_ci * ab8500_fg_volt_to_capacity() - Voltage based capacity 85262306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 85362306a36Sopenharmony_ci * @voltage_uv: The voltage to convert to a capacity in microvolt 85462306a36Sopenharmony_ci * 85562306a36Sopenharmony_ci * Returns battery capacity in per mille based on voltage 85662306a36Sopenharmony_ci */ 85762306a36Sopenharmony_cistatic int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage_uv) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci struct power_supply_battery_info *bi = di->bm->bi; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* Multiply by 10 because the capacity is tracked in per mille */ 86262306a36Sopenharmony_ci return power_supply_batinfo_ocv2cap(bi, voltage_uv, di->bat_temp) * 10; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci/** 86662306a36Sopenharmony_ci * ab8500_fg_uncomp_volt_to_capacity() - Uncompensated voltage based capacity 86762306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 86862306a36Sopenharmony_ci * 86962306a36Sopenharmony_ci * Returns battery capacity based on battery voltage that is not compensated 87062306a36Sopenharmony_ci * for the voltage drop due to the load 87162306a36Sopenharmony_ci */ 87262306a36Sopenharmony_cistatic int ab8500_fg_uncomp_volt_to_capacity(struct ab8500_fg *di) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci di->vbat_uv = ab8500_fg_bat_voltage(di); 87562306a36Sopenharmony_ci return ab8500_fg_volt_to_capacity(di, di->vbat_uv); 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci/** 87962306a36Sopenharmony_ci * ab8500_fg_battery_resistance() - Returns the battery inner resistance 88062306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 88162306a36Sopenharmony_ci * @vbat_uncomp_uv: Uncompensated VBAT voltage 88262306a36Sopenharmony_ci * 88362306a36Sopenharmony_ci * Returns battery inner resistance added with the fuel gauge resistor value 88462306a36Sopenharmony_ci * to get the total resistance in the whole link from gnd to bat+ node 88562306a36Sopenharmony_ci * in milliohm. 88662306a36Sopenharmony_ci */ 88762306a36Sopenharmony_cistatic int ab8500_fg_battery_resistance(struct ab8500_fg *di, int vbat_uncomp_uv) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct power_supply_battery_info *bi = di->bm->bi; 89062306a36Sopenharmony_ci int resistance_percent = 0; 89162306a36Sopenharmony_ci int resistance; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci /* 89462306a36Sopenharmony_ci * Determine the resistance at this voltage. First try VBAT-to-Ri else 89562306a36Sopenharmony_ci * just infer it from the surrounding temperature, if nothing works just 89662306a36Sopenharmony_ci * use the internal resistance. 89762306a36Sopenharmony_ci */ 89862306a36Sopenharmony_ci if (power_supply_supports_vbat2ri(bi)) { 89962306a36Sopenharmony_ci resistance = power_supply_vbat2ri(bi, vbat_uncomp_uv, di->flags.charging); 90062306a36Sopenharmony_ci /* Convert to milliohm */ 90162306a36Sopenharmony_ci resistance = resistance / 1000; 90262306a36Sopenharmony_ci } else if (power_supply_supports_temp2ri(bi)) { 90362306a36Sopenharmony_ci resistance_percent = power_supply_temp2resist_simple(bi->resist_table, 90462306a36Sopenharmony_ci bi->resist_table_size, 90562306a36Sopenharmony_ci di->bat_temp / 10); 90662306a36Sopenharmony_ci /* Convert to milliohm */ 90762306a36Sopenharmony_ci resistance = bi->factory_internal_resistance_uohm / 1000; 90862306a36Sopenharmony_ci resistance = resistance * resistance_percent / 100; 90962306a36Sopenharmony_ci } else { 91062306a36Sopenharmony_ci /* Last fallback */ 91162306a36Sopenharmony_ci resistance = bi->factory_internal_resistance_uohm / 1000; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* Compensate for line impedance */ 91562306a36Sopenharmony_ci resistance += (di->line_impedance_uohm / 1000); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci dev_dbg(di->dev, "%s Temp: %d battery internal resistance: %d" 91862306a36Sopenharmony_ci " fg resistance %d, total: %d (mOhm)\n", 91962306a36Sopenharmony_ci __func__, di->bat_temp, resistance, di->bm->fg_res / 10, 92062306a36Sopenharmony_ci (di->bm->fg_res / 10) + resistance); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci /* fg_res variable is in 0.1mOhm */ 92362306a36Sopenharmony_ci resistance += di->bm->fg_res / 10; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci return resistance; 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci/** 92962306a36Sopenharmony_ci * ab8500_load_comp_fg_bat_voltage() - get load compensated battery voltage 93062306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 93162306a36Sopenharmony_ci * @always: always return a voltage, also uncompensated 93262306a36Sopenharmony_ci * 93362306a36Sopenharmony_ci * Returns compensated battery voltage (on success) else error code. 93462306a36Sopenharmony_ci * If always is specified, we always return a voltage but it may be 93562306a36Sopenharmony_ci * uncompensated. 93662306a36Sopenharmony_ci */ 93762306a36Sopenharmony_cistatic int ab8500_load_comp_fg_bat_voltage(struct ab8500_fg *di, bool always) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci int i = 0; 94062306a36Sopenharmony_ci int vbat_uv = 0; 94162306a36Sopenharmony_ci int rcomp; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* Average the instant current to get a stable current measurement */ 94462306a36Sopenharmony_ci ab8500_fg_inst_curr_start(di); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci do { 94762306a36Sopenharmony_ci vbat_uv += ab8500_fg_bat_voltage(di); 94862306a36Sopenharmony_ci i++; 94962306a36Sopenharmony_ci usleep_range(5000, 6000); 95062306a36Sopenharmony_ci } while (!ab8500_fg_inst_curr_done(di) && 95162306a36Sopenharmony_ci i <= WAIT_FOR_INST_CURRENT_MAX); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci if (i > WAIT_FOR_INST_CURRENT_MAX) { 95462306a36Sopenharmony_ci dev_err(di->dev, 95562306a36Sopenharmony_ci "TIMEOUT: return uncompensated measurement of VBAT\n"); 95662306a36Sopenharmony_ci di->vbat_uv = vbat_uv / i; 95762306a36Sopenharmony_ci return di->vbat_uv; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci ab8500_fg_inst_curr_finalize(di, &di->inst_curr_ua); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* 96362306a36Sopenharmony_ci * If there is too high current dissipation, the compensation cannot be 96462306a36Sopenharmony_ci * trusted so return an error unless we must return something here, as 96562306a36Sopenharmony_ci * enforced by the "always" parameter. 96662306a36Sopenharmony_ci */ 96762306a36Sopenharmony_ci if (!always && di->inst_curr_ua < IGNORE_VBAT_HIGHCUR) 96862306a36Sopenharmony_ci return -EINVAL; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci vbat_uv = vbat_uv / i; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci /* Next we apply voltage compensation from internal resistance */ 97362306a36Sopenharmony_ci rcomp = ab8500_fg_battery_resistance(di, vbat_uv); 97462306a36Sopenharmony_ci vbat_uv = vbat_uv - (di->inst_curr_ua * rcomp) / 1000; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* Always keep this state at latest measurement */ 97762306a36Sopenharmony_ci di->vbat_uv = vbat_uv; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci return vbat_uv; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci/** 98362306a36Sopenharmony_ci * ab8500_fg_load_comp_volt_to_capacity() - Load compensated voltage based capacity 98462306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 98562306a36Sopenharmony_ci * 98662306a36Sopenharmony_ci * Returns battery capacity based on battery voltage that is load compensated 98762306a36Sopenharmony_ci * for the voltage drop 98862306a36Sopenharmony_ci */ 98962306a36Sopenharmony_cistatic int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di) 99062306a36Sopenharmony_ci{ 99162306a36Sopenharmony_ci int vbat_comp_uv; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci vbat_comp_uv = ab8500_load_comp_fg_bat_voltage(di, true); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return ab8500_fg_volt_to_capacity(di, vbat_comp_uv); 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci/** 99962306a36Sopenharmony_ci * ab8500_fg_convert_mah_to_permille() - Capacity in mAh to permille 100062306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 100162306a36Sopenharmony_ci * @cap_mah: capacity in mAh 100262306a36Sopenharmony_ci * 100362306a36Sopenharmony_ci * Converts capacity in mAh to capacity in permille 100462306a36Sopenharmony_ci */ 100562306a36Sopenharmony_cistatic int ab8500_fg_convert_mah_to_permille(struct ab8500_fg *di, int cap_mah) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci return (cap_mah * 1000) / di->bat_cap.max_mah_design; 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci/** 101162306a36Sopenharmony_ci * ab8500_fg_convert_permille_to_mah() - Capacity in permille to mAh 101262306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 101362306a36Sopenharmony_ci * @cap_pm: capacity in permille 101462306a36Sopenharmony_ci * 101562306a36Sopenharmony_ci * Converts capacity in permille to capacity in mAh 101662306a36Sopenharmony_ci */ 101762306a36Sopenharmony_cistatic int ab8500_fg_convert_permille_to_mah(struct ab8500_fg *di, int cap_pm) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci return cap_pm * di->bat_cap.max_mah_design / 1000; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci/** 102362306a36Sopenharmony_ci * ab8500_fg_convert_mah_to_uwh() - Capacity in mAh to uWh 102462306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 102562306a36Sopenharmony_ci * @cap_mah: capacity in mAh 102662306a36Sopenharmony_ci * 102762306a36Sopenharmony_ci * Converts capacity in mAh to capacity in uWh 102862306a36Sopenharmony_ci */ 102962306a36Sopenharmony_cistatic int ab8500_fg_convert_mah_to_uwh(struct ab8500_fg *di, int cap_mah) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci u64 div_res; 103262306a36Sopenharmony_ci u32 div_rem; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci /* 103562306a36Sopenharmony_ci * Capacity is in milli ampere hours (10^-3)Ah 103662306a36Sopenharmony_ci * Nominal voltage is in microvolts (10^-6)V 103762306a36Sopenharmony_ci * divide by 1000000 after multiplication to get to mWh 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_ci div_res = ((u64) cap_mah) * ((u64) di->vbat_nom_uv); 104062306a36Sopenharmony_ci div_rem = do_div(div_res, 1000000); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* Make sure to round upwards if necessary */ 104362306a36Sopenharmony_ci if (div_rem >= 1000000 / 2) 104462306a36Sopenharmony_ci div_res++; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci return (int) div_res; 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci/** 105062306a36Sopenharmony_ci * ab8500_fg_calc_cap_charging() - Calculate remaining capacity while charging 105162306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 105262306a36Sopenharmony_ci * 105362306a36Sopenharmony_ci * Return the capacity in mAh based on previous calculated capcity and the FG 105462306a36Sopenharmony_ci * accumulator register value. The filter is filled with this capacity 105562306a36Sopenharmony_ci */ 105662306a36Sopenharmony_cistatic int ab8500_fg_calc_cap_charging(struct ab8500_fg *di) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n", 105962306a36Sopenharmony_ci __func__, 106062306a36Sopenharmony_ci di->bat_cap.mah, 106162306a36Sopenharmony_ci di->accu_charge); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* Capacity should not be less than 0 */ 106462306a36Sopenharmony_ci if (di->bat_cap.mah + di->accu_charge > 0) 106562306a36Sopenharmony_ci di->bat_cap.mah += di->accu_charge; 106662306a36Sopenharmony_ci else 106762306a36Sopenharmony_ci di->bat_cap.mah = 0; 106862306a36Sopenharmony_ci /* 106962306a36Sopenharmony_ci * We force capacity to 100% once when the algorithm 107062306a36Sopenharmony_ci * reports that it's full. 107162306a36Sopenharmony_ci */ 107262306a36Sopenharmony_ci if (di->bat_cap.mah >= di->bat_cap.max_mah_design || 107362306a36Sopenharmony_ci di->flags.force_full) { 107462306a36Sopenharmony_ci di->bat_cap.mah = di->bat_cap.max_mah_design; 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci ab8500_fg_fill_cap_sample(di, di->bat_cap.mah); 107862306a36Sopenharmony_ci di->bat_cap.permille = 107962306a36Sopenharmony_ci ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* We need to update battery voltage and inst current when charging */ 108262306a36Sopenharmony_ci di->vbat_uv = ab8500_fg_bat_voltage(di); 108362306a36Sopenharmony_ci di->inst_curr_ua = ab8500_fg_inst_curr_blocking(di); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return di->bat_cap.mah; 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci/** 108962306a36Sopenharmony_ci * ab8500_fg_calc_cap_discharge_voltage() - Capacity in discharge with voltage 109062306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 109162306a36Sopenharmony_ci * 109262306a36Sopenharmony_ci * Return the capacity in mAh based on the load compensated battery voltage. 109362306a36Sopenharmony_ci * This value is added to the filter and a new mean value is calculated and 109462306a36Sopenharmony_ci * returned. 109562306a36Sopenharmony_ci */ 109662306a36Sopenharmony_cistatic int ab8500_fg_calc_cap_discharge_voltage(struct ab8500_fg *di) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci int permille, mah; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci permille = ab8500_fg_load_comp_volt_to_capacity(di); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci mah = ab8500_fg_convert_permille_to_mah(di, permille); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci di->bat_cap.mah = ab8500_fg_add_cap_sample(di, mah); 110562306a36Sopenharmony_ci di->bat_cap.permille = 110662306a36Sopenharmony_ci ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci return di->bat_cap.mah; 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci/** 111262306a36Sopenharmony_ci * ab8500_fg_calc_cap_discharge_fg() - Capacity in discharge with FG 111362306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 111462306a36Sopenharmony_ci * 111562306a36Sopenharmony_ci * Return the capacity in mAh based on previous calculated capcity and the FG 111662306a36Sopenharmony_ci * accumulator register value. This value is added to the filter and a 111762306a36Sopenharmony_ci * new mean value is calculated and returned. 111862306a36Sopenharmony_ci */ 111962306a36Sopenharmony_cistatic int ab8500_fg_calc_cap_discharge_fg(struct ab8500_fg *di) 112062306a36Sopenharmony_ci{ 112162306a36Sopenharmony_ci int permille_volt, permille; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n", 112462306a36Sopenharmony_ci __func__, 112562306a36Sopenharmony_ci di->bat_cap.mah, 112662306a36Sopenharmony_ci di->accu_charge); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci /* Capacity should not be less than 0 */ 112962306a36Sopenharmony_ci if (di->bat_cap.mah + di->accu_charge > 0) 113062306a36Sopenharmony_ci di->bat_cap.mah += di->accu_charge; 113162306a36Sopenharmony_ci else 113262306a36Sopenharmony_ci di->bat_cap.mah = 0; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (di->bat_cap.mah >= di->bat_cap.max_mah_design) 113562306a36Sopenharmony_ci di->bat_cap.mah = di->bat_cap.max_mah_design; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci /* 113862306a36Sopenharmony_ci * Check against voltage based capacity. It can not be lower 113962306a36Sopenharmony_ci * than what the uncompensated voltage says 114062306a36Sopenharmony_ci */ 114162306a36Sopenharmony_ci permille = ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah); 114262306a36Sopenharmony_ci permille_volt = ab8500_fg_uncomp_volt_to_capacity(di); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci if (permille < permille_volt) { 114562306a36Sopenharmony_ci di->bat_cap.permille = permille_volt; 114662306a36Sopenharmony_ci di->bat_cap.mah = ab8500_fg_convert_permille_to_mah(di, 114762306a36Sopenharmony_ci di->bat_cap.permille); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci dev_dbg(di->dev, "%s voltage based: perm %d perm_volt %d\n", 115062306a36Sopenharmony_ci __func__, 115162306a36Sopenharmony_ci permille, 115262306a36Sopenharmony_ci permille_volt); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci ab8500_fg_fill_cap_sample(di, di->bat_cap.mah); 115562306a36Sopenharmony_ci } else { 115662306a36Sopenharmony_ci ab8500_fg_fill_cap_sample(di, di->bat_cap.mah); 115762306a36Sopenharmony_ci di->bat_cap.permille = 115862306a36Sopenharmony_ci ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah); 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci return di->bat_cap.mah; 116262306a36Sopenharmony_ci} 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci/** 116562306a36Sopenharmony_ci * ab8500_fg_capacity_level() - Get the battery capacity level 116662306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 116762306a36Sopenharmony_ci * 116862306a36Sopenharmony_ci * Get the battery capacity level based on the capacity in percent 116962306a36Sopenharmony_ci */ 117062306a36Sopenharmony_cistatic int ab8500_fg_capacity_level(struct ab8500_fg *di) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci int ret, percent; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci if (percent <= di->bm->cap_levels->critical || 117762306a36Sopenharmony_ci di->flags.low_bat) 117862306a36Sopenharmony_ci ret = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 117962306a36Sopenharmony_ci else if (percent <= di->bm->cap_levels->low) 118062306a36Sopenharmony_ci ret = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 118162306a36Sopenharmony_ci else if (percent <= di->bm->cap_levels->normal) 118262306a36Sopenharmony_ci ret = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 118362306a36Sopenharmony_ci else if (percent <= di->bm->cap_levels->high) 118462306a36Sopenharmony_ci ret = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; 118562306a36Sopenharmony_ci else 118662306a36Sopenharmony_ci ret = POWER_SUPPLY_CAPACITY_LEVEL_FULL; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci return ret; 118962306a36Sopenharmony_ci} 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci/** 119262306a36Sopenharmony_ci * ab8500_fg_calculate_scaled_capacity() - Capacity scaling 119362306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 119462306a36Sopenharmony_ci * 119562306a36Sopenharmony_ci * Calculates the capacity to be shown to upper layers. Scales the capacity 119662306a36Sopenharmony_ci * to have 100% as a reference from the actual capacity upon removal of charger 119762306a36Sopenharmony_ci * when charging is in maintenance mode. 119862306a36Sopenharmony_ci */ 119962306a36Sopenharmony_cistatic int ab8500_fg_calculate_scaled_capacity(struct ab8500_fg *di) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale; 120262306a36Sopenharmony_ci int capacity = di->bat_cap.prev_percent; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (!cs->enable) 120562306a36Sopenharmony_ci return capacity; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci /* 120862306a36Sopenharmony_ci * As long as we are in fully charge mode scale the capacity 120962306a36Sopenharmony_ci * to show 100%. 121062306a36Sopenharmony_ci */ 121162306a36Sopenharmony_ci if (di->flags.fully_charged) { 121262306a36Sopenharmony_ci cs->cap_to_scale[0] = 100; 121362306a36Sopenharmony_ci cs->cap_to_scale[1] = 121462306a36Sopenharmony_ci max(capacity, di->bm->fg_params->maint_thres); 121562306a36Sopenharmony_ci dev_dbg(di->dev, "Scale cap with %d/%d\n", 121662306a36Sopenharmony_ci cs->cap_to_scale[0], cs->cap_to_scale[1]); 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* Calculates the scaled capacity. */ 122062306a36Sopenharmony_ci if ((cs->cap_to_scale[0] != cs->cap_to_scale[1]) 122162306a36Sopenharmony_ci && (cs->cap_to_scale[1] > 0)) 122262306a36Sopenharmony_ci capacity = min(100, 122362306a36Sopenharmony_ci DIV_ROUND_CLOSEST(di->bat_cap.prev_percent * 122462306a36Sopenharmony_ci cs->cap_to_scale[0], 122562306a36Sopenharmony_ci cs->cap_to_scale[1])); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci if (di->flags.charging) { 122862306a36Sopenharmony_ci if (capacity < cs->disable_cap_level) { 122962306a36Sopenharmony_ci cs->disable_cap_level = capacity; 123062306a36Sopenharmony_ci dev_dbg(di->dev, "Cap to stop scale lowered %d%%\n", 123162306a36Sopenharmony_ci cs->disable_cap_level); 123262306a36Sopenharmony_ci } else if (!di->flags.fully_charged) { 123362306a36Sopenharmony_ci if (di->bat_cap.prev_percent >= 123462306a36Sopenharmony_ci cs->disable_cap_level) { 123562306a36Sopenharmony_ci dev_dbg(di->dev, "Disabling scaled capacity\n"); 123662306a36Sopenharmony_ci cs->enable = false; 123762306a36Sopenharmony_ci capacity = di->bat_cap.prev_percent; 123862306a36Sopenharmony_ci } else { 123962306a36Sopenharmony_ci dev_dbg(di->dev, 124062306a36Sopenharmony_ci "Waiting in cap to level %d%%\n", 124162306a36Sopenharmony_ci cs->disable_cap_level); 124262306a36Sopenharmony_ci capacity = cs->disable_cap_level; 124362306a36Sopenharmony_ci } 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci } 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci return capacity; 124862306a36Sopenharmony_ci} 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci/** 125162306a36Sopenharmony_ci * ab8500_fg_update_cap_scalers() - Capacity scaling 125262306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 125362306a36Sopenharmony_ci * 125462306a36Sopenharmony_ci * To be called when state change from charge<->discharge to update 125562306a36Sopenharmony_ci * the capacity scalers. 125662306a36Sopenharmony_ci */ 125762306a36Sopenharmony_cistatic void ab8500_fg_update_cap_scalers(struct ab8500_fg *di) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci if (!cs->enable) 126262306a36Sopenharmony_ci return; 126362306a36Sopenharmony_ci if (di->flags.charging) { 126462306a36Sopenharmony_ci di->bat_cap.cap_scale.disable_cap_level = 126562306a36Sopenharmony_ci di->bat_cap.cap_scale.scaled_cap; 126662306a36Sopenharmony_ci dev_dbg(di->dev, "Cap to stop scale at charge %d%%\n", 126762306a36Sopenharmony_ci di->bat_cap.cap_scale.disable_cap_level); 126862306a36Sopenharmony_ci } else { 126962306a36Sopenharmony_ci if (cs->scaled_cap != 100) { 127062306a36Sopenharmony_ci cs->cap_to_scale[0] = cs->scaled_cap; 127162306a36Sopenharmony_ci cs->cap_to_scale[1] = di->bat_cap.prev_percent; 127262306a36Sopenharmony_ci } else { 127362306a36Sopenharmony_ci cs->cap_to_scale[0] = 100; 127462306a36Sopenharmony_ci cs->cap_to_scale[1] = 127562306a36Sopenharmony_ci max(di->bat_cap.prev_percent, 127662306a36Sopenharmony_ci di->bm->fg_params->maint_thres); 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci dev_dbg(di->dev, "Cap to scale at discharge %d/%d\n", 128062306a36Sopenharmony_ci cs->cap_to_scale[0], cs->cap_to_scale[1]); 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci} 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci/** 128562306a36Sopenharmony_ci * ab8500_fg_check_capacity_limits() - Check if capacity has changed 128662306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 128762306a36Sopenharmony_ci * @init: capacity is allowed to go up in init mode 128862306a36Sopenharmony_ci * 128962306a36Sopenharmony_ci * Check if capacity or capacity limit has changed and notify the system 129062306a36Sopenharmony_ci * about it using the power_supply framework 129162306a36Sopenharmony_ci */ 129262306a36Sopenharmony_cistatic void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) 129362306a36Sopenharmony_ci{ 129462306a36Sopenharmony_ci bool changed = false; 129562306a36Sopenharmony_ci int percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci di->bat_cap.level = ab8500_fg_capacity_level(di); 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (di->bat_cap.level != di->bat_cap.prev_level) { 130062306a36Sopenharmony_ci /* 130162306a36Sopenharmony_ci * We do not allow reported capacity level to go up 130262306a36Sopenharmony_ci * unless we're charging or if we're in init 130362306a36Sopenharmony_ci */ 130462306a36Sopenharmony_ci if (!(!di->flags.charging && di->bat_cap.level > 130562306a36Sopenharmony_ci di->bat_cap.prev_level) || init) { 130662306a36Sopenharmony_ci dev_dbg(di->dev, "level changed from %d to %d\n", 130762306a36Sopenharmony_ci di->bat_cap.prev_level, 130862306a36Sopenharmony_ci di->bat_cap.level); 130962306a36Sopenharmony_ci di->bat_cap.prev_level = di->bat_cap.level; 131062306a36Sopenharmony_ci changed = true; 131162306a36Sopenharmony_ci } else { 131262306a36Sopenharmony_ci dev_dbg(di->dev, "level not allowed to go up " 131362306a36Sopenharmony_ci "since no charger is connected: %d to %d\n", 131462306a36Sopenharmony_ci di->bat_cap.prev_level, 131562306a36Sopenharmony_ci di->bat_cap.level); 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* 132062306a36Sopenharmony_ci * If we have received the LOW_BAT IRQ, set capacity to 0 to initiate 132162306a36Sopenharmony_ci * shutdown 132262306a36Sopenharmony_ci */ 132362306a36Sopenharmony_ci if (di->flags.low_bat) { 132462306a36Sopenharmony_ci dev_dbg(di->dev, "Battery low, set capacity to 0\n"); 132562306a36Sopenharmony_ci di->bat_cap.prev_percent = 0; 132662306a36Sopenharmony_ci di->bat_cap.permille = 0; 132762306a36Sopenharmony_ci percent = 0; 132862306a36Sopenharmony_ci di->bat_cap.prev_mah = 0; 132962306a36Sopenharmony_ci di->bat_cap.mah = 0; 133062306a36Sopenharmony_ci changed = true; 133162306a36Sopenharmony_ci } else if (di->flags.fully_charged) { 133262306a36Sopenharmony_ci /* 133362306a36Sopenharmony_ci * We report 100% if algorithm reported fully charged 133462306a36Sopenharmony_ci * and show 100% during maintenance charging (scaling). 133562306a36Sopenharmony_ci */ 133662306a36Sopenharmony_ci if (di->flags.force_full) { 133762306a36Sopenharmony_ci di->bat_cap.prev_percent = percent; 133862306a36Sopenharmony_ci di->bat_cap.prev_mah = di->bat_cap.mah; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci changed = true; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci if (!di->bat_cap.cap_scale.enable && 134362306a36Sopenharmony_ci di->bm->capacity_scaling) { 134462306a36Sopenharmony_ci di->bat_cap.cap_scale.enable = true; 134562306a36Sopenharmony_ci di->bat_cap.cap_scale.cap_to_scale[0] = 100; 134662306a36Sopenharmony_ci di->bat_cap.cap_scale.cap_to_scale[1] = 134762306a36Sopenharmony_ci di->bat_cap.prev_percent; 134862306a36Sopenharmony_ci di->bat_cap.cap_scale.disable_cap_level = 100; 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci } else if (di->bat_cap.prev_percent != percent) { 135162306a36Sopenharmony_ci dev_dbg(di->dev, 135262306a36Sopenharmony_ci "battery reported full " 135362306a36Sopenharmony_ci "but capacity dropping: %d\n", 135462306a36Sopenharmony_ci percent); 135562306a36Sopenharmony_ci di->bat_cap.prev_percent = percent; 135662306a36Sopenharmony_ci di->bat_cap.prev_mah = di->bat_cap.mah; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci changed = true; 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci } else if (di->bat_cap.prev_percent != percent) { 136162306a36Sopenharmony_ci if (percent == 0) { 136262306a36Sopenharmony_ci /* 136362306a36Sopenharmony_ci * We will not report 0% unless we've got 136462306a36Sopenharmony_ci * the LOW_BAT IRQ, no matter what the FG 136562306a36Sopenharmony_ci * algorithm says. 136662306a36Sopenharmony_ci */ 136762306a36Sopenharmony_ci di->bat_cap.prev_percent = 1; 136862306a36Sopenharmony_ci percent = 1; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci changed = true; 137162306a36Sopenharmony_ci } else if (!(!di->flags.charging && 137262306a36Sopenharmony_ci percent > di->bat_cap.prev_percent) || init) { 137362306a36Sopenharmony_ci /* 137462306a36Sopenharmony_ci * We do not allow reported capacity to go up 137562306a36Sopenharmony_ci * unless we're charging or if we're in init 137662306a36Sopenharmony_ci */ 137762306a36Sopenharmony_ci dev_dbg(di->dev, 137862306a36Sopenharmony_ci "capacity changed from %d to %d (%d)\n", 137962306a36Sopenharmony_ci di->bat_cap.prev_percent, 138062306a36Sopenharmony_ci percent, 138162306a36Sopenharmony_ci di->bat_cap.permille); 138262306a36Sopenharmony_ci di->bat_cap.prev_percent = percent; 138362306a36Sopenharmony_ci di->bat_cap.prev_mah = di->bat_cap.mah; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci changed = true; 138662306a36Sopenharmony_ci } else { 138762306a36Sopenharmony_ci dev_dbg(di->dev, "capacity not allowed to go up since " 138862306a36Sopenharmony_ci "no charger is connected: %d to %d (%d)\n", 138962306a36Sopenharmony_ci di->bat_cap.prev_percent, 139062306a36Sopenharmony_ci percent, 139162306a36Sopenharmony_ci di->bat_cap.permille); 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci } 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (changed) { 139662306a36Sopenharmony_ci if (di->bm->capacity_scaling) { 139762306a36Sopenharmony_ci di->bat_cap.cap_scale.scaled_cap = 139862306a36Sopenharmony_ci ab8500_fg_calculate_scaled_capacity(di); 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci dev_info(di->dev, "capacity=%d (%d)\n", 140162306a36Sopenharmony_ci di->bat_cap.prev_percent, 140262306a36Sopenharmony_ci di->bat_cap.cap_scale.scaled_cap); 140362306a36Sopenharmony_ci } 140462306a36Sopenharmony_ci power_supply_changed(di->fg_psy); 140562306a36Sopenharmony_ci if (di->flags.fully_charged && di->flags.force_full) { 140662306a36Sopenharmony_ci dev_dbg(di->dev, "Battery full, notifying.\n"); 140762306a36Sopenharmony_ci di->flags.force_full = false; 140862306a36Sopenharmony_ci sysfs_notify(&di->fg_kobject, NULL, "charge_full"); 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci sysfs_notify(&di->fg_kobject, NULL, "charge_now"); 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci} 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_cistatic void ab8500_fg_charge_state_to(struct ab8500_fg *di, 141562306a36Sopenharmony_ci enum ab8500_fg_charge_state new_state) 141662306a36Sopenharmony_ci{ 141762306a36Sopenharmony_ci dev_dbg(di->dev, "Charge state from %d [%s] to %d [%s]\n", 141862306a36Sopenharmony_ci di->charge_state, 141962306a36Sopenharmony_ci charge_state[di->charge_state], 142062306a36Sopenharmony_ci new_state, 142162306a36Sopenharmony_ci charge_state[new_state]); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci di->charge_state = new_state; 142462306a36Sopenharmony_ci} 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_cistatic void ab8500_fg_discharge_state_to(struct ab8500_fg *di, 142762306a36Sopenharmony_ci enum ab8500_fg_discharge_state new_state) 142862306a36Sopenharmony_ci{ 142962306a36Sopenharmony_ci dev_dbg(di->dev, "Discharge state from %d [%s] to %d [%s]\n", 143062306a36Sopenharmony_ci di->discharge_state, 143162306a36Sopenharmony_ci discharge_state[di->discharge_state], 143262306a36Sopenharmony_ci new_state, 143362306a36Sopenharmony_ci discharge_state[new_state]); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci di->discharge_state = new_state; 143662306a36Sopenharmony_ci} 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci/** 143962306a36Sopenharmony_ci * ab8500_fg_algorithm_charging() - FG algorithm for when charging 144062306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 144162306a36Sopenharmony_ci * 144262306a36Sopenharmony_ci * Battery capacity calculation state machine for when we're charging 144362306a36Sopenharmony_ci */ 144462306a36Sopenharmony_cistatic void ab8500_fg_algorithm_charging(struct ab8500_fg *di) 144562306a36Sopenharmony_ci{ 144662306a36Sopenharmony_ci /* 144762306a36Sopenharmony_ci * If we change to discharge mode 144862306a36Sopenharmony_ci * we should start with recovery 144962306a36Sopenharmony_ci */ 145062306a36Sopenharmony_ci if (di->discharge_state != AB8500_FG_DISCHARGE_INIT_RECOVERY) 145162306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, 145262306a36Sopenharmony_ci AB8500_FG_DISCHARGE_INIT_RECOVERY); 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci switch (di->charge_state) { 145562306a36Sopenharmony_ci case AB8500_FG_CHARGE_INIT: 145662306a36Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE( 145762306a36Sopenharmony_ci di->bm->fg_params->accu_charging); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 146062306a36Sopenharmony_ci ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_READOUT); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci break; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci case AB8500_FG_CHARGE_READOUT: 146562306a36Sopenharmony_ci /* 146662306a36Sopenharmony_ci * Read the FG and calculate the new capacity 146762306a36Sopenharmony_ci */ 146862306a36Sopenharmony_ci mutex_lock(&di->cc_lock); 146962306a36Sopenharmony_ci if (!di->flags.conv_done && !di->flags.force_full) { 147062306a36Sopenharmony_ci /* Wasn't the CC IRQ that got us here */ 147162306a36Sopenharmony_ci mutex_unlock(&di->cc_lock); 147262306a36Sopenharmony_ci dev_dbg(di->dev, "%s CC conv not done\n", 147362306a36Sopenharmony_ci __func__); 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci break; 147662306a36Sopenharmony_ci } 147762306a36Sopenharmony_ci di->flags.conv_done = false; 147862306a36Sopenharmony_ci mutex_unlock(&di->cc_lock); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci ab8500_fg_calc_cap_charging(di); 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci break; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci default: 148562306a36Sopenharmony_ci break; 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci /* Check capacity limits */ 148962306a36Sopenharmony_ci ab8500_fg_check_capacity_limits(di, false); 149062306a36Sopenharmony_ci} 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_cistatic void force_capacity(struct ab8500_fg *di) 149362306a36Sopenharmony_ci{ 149462306a36Sopenharmony_ci int cap; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci ab8500_fg_clear_cap_samples(di); 149762306a36Sopenharmony_ci cap = di->bat_cap.user_mah; 149862306a36Sopenharmony_ci if (cap > di->bat_cap.max_mah_design) { 149962306a36Sopenharmony_ci dev_dbg(di->dev, "Remaining cap %d can't be bigger than total" 150062306a36Sopenharmony_ci " %d\n", cap, di->bat_cap.max_mah_design); 150162306a36Sopenharmony_ci cap = di->bat_cap.max_mah_design; 150262306a36Sopenharmony_ci } 150362306a36Sopenharmony_ci ab8500_fg_fill_cap_sample(di, di->bat_cap.user_mah); 150462306a36Sopenharmony_ci di->bat_cap.permille = ab8500_fg_convert_mah_to_permille(di, cap); 150562306a36Sopenharmony_ci di->bat_cap.mah = cap; 150662306a36Sopenharmony_ci ab8500_fg_check_capacity_limits(di, true); 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_cistatic bool check_sysfs_capacity(struct ab8500_fg *di) 151062306a36Sopenharmony_ci{ 151162306a36Sopenharmony_ci int cap, lower, upper; 151262306a36Sopenharmony_ci int cap_permille; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci cap = di->bat_cap.user_mah; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci cap_permille = ab8500_fg_convert_mah_to_permille(di, 151762306a36Sopenharmony_ci di->bat_cap.user_mah); 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci lower = di->bat_cap.permille - di->bm->fg_params->user_cap_limit * 10; 152062306a36Sopenharmony_ci upper = di->bat_cap.permille + di->bm->fg_params->user_cap_limit * 10; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci if (lower < 0) 152362306a36Sopenharmony_ci lower = 0; 152462306a36Sopenharmony_ci /* 1000 is permille, -> 100 percent */ 152562306a36Sopenharmony_ci if (upper > 1000) 152662306a36Sopenharmony_ci upper = 1000; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci dev_dbg(di->dev, "Capacity limits:" 152962306a36Sopenharmony_ci " (Lower: %d User: %d Upper: %d) [user: %d, was: %d]\n", 153062306a36Sopenharmony_ci lower, cap_permille, upper, cap, di->bat_cap.mah); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci /* If within limits, use the saved capacity and exit estimation...*/ 153362306a36Sopenharmony_ci if (cap_permille > lower && cap_permille < upper) { 153462306a36Sopenharmony_ci dev_dbg(di->dev, "OK! Using users cap %d uAh now\n", cap); 153562306a36Sopenharmony_ci force_capacity(di); 153662306a36Sopenharmony_ci return true; 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci dev_dbg(di->dev, "Capacity from user out of limits, ignoring"); 153962306a36Sopenharmony_ci return false; 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci/** 154362306a36Sopenharmony_ci * ab8500_fg_algorithm_discharging() - FG algorithm for when discharging 154462306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 154562306a36Sopenharmony_ci * 154662306a36Sopenharmony_ci * Battery capacity calculation state machine for when we're discharging 154762306a36Sopenharmony_ci */ 154862306a36Sopenharmony_cistatic void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) 154962306a36Sopenharmony_ci{ 155062306a36Sopenharmony_ci int sleep_time; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci /* If we change to charge mode we should start with init */ 155362306a36Sopenharmony_ci if (di->charge_state != AB8500_FG_CHARGE_INIT) 155462306a36Sopenharmony_ci ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT); 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci switch (di->discharge_state) { 155762306a36Sopenharmony_ci case AB8500_FG_DISCHARGE_INIT: 155862306a36Sopenharmony_ci /* We use the FG IRQ to work on */ 155962306a36Sopenharmony_ci di->init_cnt = 0; 156062306a36Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer); 156162306a36Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 156262306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, 156362306a36Sopenharmony_ci AB8500_FG_DISCHARGE_INITMEASURING); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci fallthrough; 156662306a36Sopenharmony_ci case AB8500_FG_DISCHARGE_INITMEASURING: 156762306a36Sopenharmony_ci /* 156862306a36Sopenharmony_ci * Discard a number of samples during startup. 156962306a36Sopenharmony_ci * After that, use compensated voltage for a few 157062306a36Sopenharmony_ci * samples to get an initial capacity. 157162306a36Sopenharmony_ci * Then go to READOUT 157262306a36Sopenharmony_ci */ 157362306a36Sopenharmony_ci sleep_time = di->bm->fg_params->init_timer; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci /* Discard the first [x] seconds */ 157662306a36Sopenharmony_ci if (di->init_cnt > di->bm->fg_params->init_discard_time) { 157762306a36Sopenharmony_ci ab8500_fg_calc_cap_discharge_voltage(di); 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci ab8500_fg_check_capacity_limits(di, true); 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci di->init_cnt += sleep_time; 158362306a36Sopenharmony_ci if (di->init_cnt > di->bm->fg_params->init_total_time) 158462306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, 158562306a36Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT_INIT); 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci break; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci case AB8500_FG_DISCHARGE_INIT_RECOVERY: 159062306a36Sopenharmony_ci di->recovery_cnt = 0; 159162306a36Sopenharmony_ci di->recovery_needed = true; 159262306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, 159362306a36Sopenharmony_ci AB8500_FG_DISCHARGE_RECOVERY); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci fallthrough; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci case AB8500_FG_DISCHARGE_RECOVERY: 159862306a36Sopenharmony_ci sleep_time = di->bm->fg_params->recovery_sleep_timer; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci /* 160162306a36Sopenharmony_ci * We should check the power consumption 160262306a36Sopenharmony_ci * If low, go to READOUT (after x min) or 160362306a36Sopenharmony_ci * RECOVERY_SLEEP if time left. 160462306a36Sopenharmony_ci * If high, go to READOUT 160562306a36Sopenharmony_ci */ 160662306a36Sopenharmony_ci di->inst_curr_ua = ab8500_fg_inst_curr_blocking(di); 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci if (ab8500_fg_is_low_curr(di, di->inst_curr_ua)) { 160962306a36Sopenharmony_ci if (di->recovery_cnt > 161062306a36Sopenharmony_ci di->bm->fg_params->recovery_total_time) { 161162306a36Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE( 161262306a36Sopenharmony_ci di->bm->fg_params->accu_high_curr); 161362306a36Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 161462306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, 161562306a36Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT); 161662306a36Sopenharmony_ci di->recovery_needed = false; 161762306a36Sopenharmony_ci } else { 161862306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, 161962306a36Sopenharmony_ci &di->fg_periodic_work, 162062306a36Sopenharmony_ci sleep_time * HZ); 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci di->recovery_cnt += sleep_time; 162362306a36Sopenharmony_ci } else { 162462306a36Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE( 162562306a36Sopenharmony_ci di->bm->fg_params->accu_high_curr); 162662306a36Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 162762306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, 162862306a36Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT); 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci break; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci case AB8500_FG_DISCHARGE_READOUT_INIT: 163362306a36Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE( 163462306a36Sopenharmony_ci di->bm->fg_params->accu_high_curr); 163562306a36Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 163662306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, 163762306a36Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT); 163862306a36Sopenharmony_ci break; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci case AB8500_FG_DISCHARGE_READOUT: 164162306a36Sopenharmony_ci di->inst_curr_ua = ab8500_fg_inst_curr_blocking(di); 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci if (ab8500_fg_is_low_curr(di, di->inst_curr_ua)) { 164462306a36Sopenharmony_ci /* Detect mode change */ 164562306a36Sopenharmony_ci if (di->high_curr_mode) { 164662306a36Sopenharmony_ci di->high_curr_mode = false; 164762306a36Sopenharmony_ci di->high_curr_cnt = 0; 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci if (di->recovery_needed) { 165162306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, 165262306a36Sopenharmony_ci AB8500_FG_DISCHARGE_INIT_RECOVERY); 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, 165562306a36Sopenharmony_ci &di->fg_periodic_work, 0); 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci break; 165862306a36Sopenharmony_ci } 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci ab8500_fg_calc_cap_discharge_voltage(di); 166162306a36Sopenharmony_ci } else { 166262306a36Sopenharmony_ci mutex_lock(&di->cc_lock); 166362306a36Sopenharmony_ci if (!di->flags.conv_done) { 166462306a36Sopenharmony_ci /* Wasn't the CC IRQ that got us here */ 166562306a36Sopenharmony_ci mutex_unlock(&di->cc_lock); 166662306a36Sopenharmony_ci dev_dbg(di->dev, "%s CC conv not done\n", 166762306a36Sopenharmony_ci __func__); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci break; 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci di->flags.conv_done = false; 167262306a36Sopenharmony_ci mutex_unlock(&di->cc_lock); 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci /* Detect mode change */ 167562306a36Sopenharmony_ci if (!di->high_curr_mode) { 167662306a36Sopenharmony_ci di->high_curr_mode = true; 167762306a36Sopenharmony_ci di->high_curr_cnt = 0; 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci di->high_curr_cnt += 168162306a36Sopenharmony_ci di->bm->fg_params->accu_high_curr; 168262306a36Sopenharmony_ci if (di->high_curr_cnt > 168362306a36Sopenharmony_ci di->bm->fg_params->high_curr_time) 168462306a36Sopenharmony_ci di->recovery_needed = true; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci ab8500_fg_calc_cap_discharge_fg(di); 168762306a36Sopenharmony_ci } 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci ab8500_fg_check_capacity_limits(di, false); 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci break; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci case AB8500_FG_DISCHARGE_WAKEUP: 169462306a36Sopenharmony_ci ab8500_fg_calc_cap_discharge_voltage(di); 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE( 169762306a36Sopenharmony_ci di->bm->fg_params->accu_high_curr); 169862306a36Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 169962306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, 170062306a36Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT); 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci ab8500_fg_check_capacity_limits(di, false); 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci break; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci default: 170762306a36Sopenharmony_ci break; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci} 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci/** 171262306a36Sopenharmony_ci * ab8500_fg_algorithm_calibrate() - Internal columb counter offset calibration 171362306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 171462306a36Sopenharmony_ci * 171562306a36Sopenharmony_ci */ 171662306a36Sopenharmony_cistatic void ab8500_fg_algorithm_calibrate(struct ab8500_fg *di) 171762306a36Sopenharmony_ci{ 171862306a36Sopenharmony_ci int ret; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci switch (di->calib_state) { 172162306a36Sopenharmony_ci case AB8500_FG_CALIB_INIT: 172262306a36Sopenharmony_ci dev_dbg(di->dev, "Calibration ongoing...\n"); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 172562306a36Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 172662306a36Sopenharmony_ci CC_INT_CAL_N_AVG_MASK, CC_INT_CAL_SAMPLES_8); 172762306a36Sopenharmony_ci if (ret < 0) 172862306a36Sopenharmony_ci goto err; 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 173162306a36Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 173262306a36Sopenharmony_ci CC_INTAVGOFFSET_ENA, CC_INTAVGOFFSET_ENA); 173362306a36Sopenharmony_ci if (ret < 0) 173462306a36Sopenharmony_ci goto err; 173562306a36Sopenharmony_ci di->calib_state = AB8500_FG_CALIB_WAIT; 173662306a36Sopenharmony_ci break; 173762306a36Sopenharmony_ci case AB8500_FG_CALIB_END: 173862306a36Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 173962306a36Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 174062306a36Sopenharmony_ci CC_MUXOFFSET, CC_MUXOFFSET); 174162306a36Sopenharmony_ci if (ret < 0) 174262306a36Sopenharmony_ci goto err; 174362306a36Sopenharmony_ci di->flags.calibrate = false; 174462306a36Sopenharmony_ci dev_dbg(di->dev, "Calibration done...\n"); 174562306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 174662306a36Sopenharmony_ci break; 174762306a36Sopenharmony_ci case AB8500_FG_CALIB_WAIT: 174862306a36Sopenharmony_ci dev_dbg(di->dev, "Calibration WFI\n"); 174962306a36Sopenharmony_ci break; 175062306a36Sopenharmony_ci default: 175162306a36Sopenharmony_ci break; 175262306a36Sopenharmony_ci } 175362306a36Sopenharmony_ci return; 175462306a36Sopenharmony_cierr: 175562306a36Sopenharmony_ci /* Something went wrong, don't calibrate then */ 175662306a36Sopenharmony_ci dev_err(di->dev, "failed to calibrate the CC\n"); 175762306a36Sopenharmony_ci di->flags.calibrate = false; 175862306a36Sopenharmony_ci di->calib_state = AB8500_FG_CALIB_INIT; 175962306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 176062306a36Sopenharmony_ci} 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci/** 176362306a36Sopenharmony_ci * ab8500_fg_algorithm() - Entry point for the FG algorithm 176462306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 176562306a36Sopenharmony_ci * 176662306a36Sopenharmony_ci * Entry point for the battery capacity calculation state machine 176762306a36Sopenharmony_ci */ 176862306a36Sopenharmony_cistatic void ab8500_fg_algorithm(struct ab8500_fg *di) 176962306a36Sopenharmony_ci{ 177062306a36Sopenharmony_ci if (di->flags.calibrate) 177162306a36Sopenharmony_ci ab8500_fg_algorithm_calibrate(di); 177262306a36Sopenharmony_ci else { 177362306a36Sopenharmony_ci if (di->flags.charging) 177462306a36Sopenharmony_ci ab8500_fg_algorithm_charging(di); 177562306a36Sopenharmony_ci else 177662306a36Sopenharmony_ci ab8500_fg_algorithm_discharging(di); 177762306a36Sopenharmony_ci } 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d %d " 178062306a36Sopenharmony_ci "%d %d %d %d %d %d %d\n", 178162306a36Sopenharmony_ci di->bat_cap.max_mah_design, 178262306a36Sopenharmony_ci di->bat_cap.max_mah, 178362306a36Sopenharmony_ci di->bat_cap.mah, 178462306a36Sopenharmony_ci di->bat_cap.permille, 178562306a36Sopenharmony_ci di->bat_cap.level, 178662306a36Sopenharmony_ci di->bat_cap.prev_mah, 178762306a36Sopenharmony_ci di->bat_cap.prev_percent, 178862306a36Sopenharmony_ci di->bat_cap.prev_level, 178962306a36Sopenharmony_ci di->vbat_uv, 179062306a36Sopenharmony_ci di->inst_curr_ua, 179162306a36Sopenharmony_ci di->avg_curr_ua, 179262306a36Sopenharmony_ci di->accu_charge, 179362306a36Sopenharmony_ci di->flags.charging, 179462306a36Sopenharmony_ci di->charge_state, 179562306a36Sopenharmony_ci di->discharge_state, 179662306a36Sopenharmony_ci di->high_curr_mode, 179762306a36Sopenharmony_ci di->recovery_needed); 179862306a36Sopenharmony_ci} 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci/** 180162306a36Sopenharmony_ci * ab8500_fg_periodic_work() - Run the FG state machine periodically 180262306a36Sopenharmony_ci * @work: pointer to the work_struct structure 180362306a36Sopenharmony_ci * 180462306a36Sopenharmony_ci * Work queue function for periodic work 180562306a36Sopenharmony_ci */ 180662306a36Sopenharmony_cistatic void ab8500_fg_periodic_work(struct work_struct *work) 180762306a36Sopenharmony_ci{ 180862306a36Sopenharmony_ci struct ab8500_fg *di = container_of(work, struct ab8500_fg, 180962306a36Sopenharmony_ci fg_periodic_work.work); 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci if (di->init_capacity) { 181262306a36Sopenharmony_ci /* Get an initial capacity calculation */ 181362306a36Sopenharmony_ci ab8500_fg_calc_cap_discharge_voltage(di); 181462306a36Sopenharmony_ci ab8500_fg_check_capacity_limits(di, true); 181562306a36Sopenharmony_ci di->init_capacity = false; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 181862306a36Sopenharmony_ci } else if (di->flags.user_cap) { 181962306a36Sopenharmony_ci if (check_sysfs_capacity(di)) { 182062306a36Sopenharmony_ci ab8500_fg_check_capacity_limits(di, true); 182162306a36Sopenharmony_ci if (di->flags.charging) 182262306a36Sopenharmony_ci ab8500_fg_charge_state_to(di, 182362306a36Sopenharmony_ci AB8500_FG_CHARGE_INIT); 182462306a36Sopenharmony_ci else 182562306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, 182662306a36Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT_INIT); 182762306a36Sopenharmony_ci } 182862306a36Sopenharmony_ci di->flags.user_cap = false; 182962306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 183062306a36Sopenharmony_ci } else 183162306a36Sopenharmony_ci ab8500_fg_algorithm(di); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci} 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci/** 183662306a36Sopenharmony_ci * ab8500_fg_check_hw_failure_work() - Check OVV_BAT condition 183762306a36Sopenharmony_ci * @work: pointer to the work_struct structure 183862306a36Sopenharmony_ci * 183962306a36Sopenharmony_ci * Work queue function for checking the OVV_BAT condition 184062306a36Sopenharmony_ci */ 184162306a36Sopenharmony_cistatic void ab8500_fg_check_hw_failure_work(struct work_struct *work) 184262306a36Sopenharmony_ci{ 184362306a36Sopenharmony_ci int ret; 184462306a36Sopenharmony_ci u8 reg_value; 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci struct ab8500_fg *di = container_of(work, struct ab8500_fg, 184762306a36Sopenharmony_ci fg_check_hw_failure_work.work); 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci /* 185062306a36Sopenharmony_ci * If we have had a battery over-voltage situation, 185162306a36Sopenharmony_ci * check ovv-bit to see if it should be reset. 185262306a36Sopenharmony_ci */ 185362306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, 185462306a36Sopenharmony_ci AB8500_CHARGER, AB8500_CH_STAT_REG, 185562306a36Sopenharmony_ci ®_value); 185662306a36Sopenharmony_ci if (ret < 0) { 185762306a36Sopenharmony_ci dev_err(di->dev, "%s ab8500 read failed\n", __func__); 185862306a36Sopenharmony_ci return; 185962306a36Sopenharmony_ci } 186062306a36Sopenharmony_ci if ((reg_value & BATT_OVV) == BATT_OVV) { 186162306a36Sopenharmony_ci if (!di->flags.bat_ovv) { 186262306a36Sopenharmony_ci dev_dbg(di->dev, "Battery OVV\n"); 186362306a36Sopenharmony_ci di->flags.bat_ovv = true; 186462306a36Sopenharmony_ci power_supply_changed(di->fg_psy); 186562306a36Sopenharmony_ci } 186662306a36Sopenharmony_ci /* Not yet recovered from ovv, reschedule this test */ 186762306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 186862306a36Sopenharmony_ci HZ); 186962306a36Sopenharmony_ci } else { 187062306a36Sopenharmony_ci dev_dbg(di->dev, "Battery recovered from OVV\n"); 187162306a36Sopenharmony_ci di->flags.bat_ovv = false; 187262306a36Sopenharmony_ci power_supply_changed(di->fg_psy); 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci} 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci/** 187762306a36Sopenharmony_ci * ab8500_fg_low_bat_work() - Check LOW_BAT condition 187862306a36Sopenharmony_ci * @work: pointer to the work_struct structure 187962306a36Sopenharmony_ci * 188062306a36Sopenharmony_ci * Work queue function for checking the LOW_BAT condition 188162306a36Sopenharmony_ci */ 188262306a36Sopenharmony_cistatic void ab8500_fg_low_bat_work(struct work_struct *work) 188362306a36Sopenharmony_ci{ 188462306a36Sopenharmony_ci int vbat_uv; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci struct ab8500_fg *di = container_of(work, struct ab8500_fg, 188762306a36Sopenharmony_ci fg_low_bat_work.work); 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci vbat_uv = ab8500_fg_bat_voltage(di); 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci /* Check if LOW_BAT still fulfilled */ 189262306a36Sopenharmony_ci if (vbat_uv < di->bm->fg_params->lowbat_threshold_uv) { 189362306a36Sopenharmony_ci /* Is it time to shut down? */ 189462306a36Sopenharmony_ci if (di->low_bat_cnt < 1) { 189562306a36Sopenharmony_ci di->flags.low_bat = true; 189662306a36Sopenharmony_ci dev_warn(di->dev, "Shut down pending...\n"); 189762306a36Sopenharmony_ci } else { 189862306a36Sopenharmony_ci /* 189962306a36Sopenharmony_ci * Else we need to re-schedule this check to be able to detect 190062306a36Sopenharmony_ci * if the voltage increases again during charging or 190162306a36Sopenharmony_ci * due to decreasing load. 190262306a36Sopenharmony_ci */ 190362306a36Sopenharmony_ci di->low_bat_cnt--; 190462306a36Sopenharmony_ci dev_warn(di->dev, "Battery voltage still LOW\n"); 190562306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_low_bat_work, 190662306a36Sopenharmony_ci round_jiffies(LOW_BAT_CHECK_INTERVAL)); 190762306a36Sopenharmony_ci } 190862306a36Sopenharmony_ci } else { 190962306a36Sopenharmony_ci di->flags.low_bat_delay = false; 191062306a36Sopenharmony_ci di->low_bat_cnt = 10; 191162306a36Sopenharmony_ci dev_warn(di->dev, "Battery voltage OK again\n"); 191262306a36Sopenharmony_ci } 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci /* This is needed to dispatch LOW_BAT */ 191562306a36Sopenharmony_ci ab8500_fg_check_capacity_limits(di, false); 191662306a36Sopenharmony_ci} 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci/** 191962306a36Sopenharmony_ci * ab8500_fg_battok_calc - calculate the bit pattern corresponding 192062306a36Sopenharmony_ci * to the target voltage. 192162306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 192262306a36Sopenharmony_ci * @target: target voltage 192362306a36Sopenharmony_ci * 192462306a36Sopenharmony_ci * Returns bit pattern closest to the target voltage 192562306a36Sopenharmony_ci * valid return values are 0-14. (0-BATT_OK_MAX_NR_INCREMENTS) 192662306a36Sopenharmony_ci */ 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_cistatic int ab8500_fg_battok_calc(struct ab8500_fg *di, int target) 192962306a36Sopenharmony_ci{ 193062306a36Sopenharmony_ci if (target > BATT_OK_MIN + 193162306a36Sopenharmony_ci (BATT_OK_INCREMENT * BATT_OK_MAX_NR_INCREMENTS)) 193262306a36Sopenharmony_ci return BATT_OK_MAX_NR_INCREMENTS; 193362306a36Sopenharmony_ci if (target < BATT_OK_MIN) 193462306a36Sopenharmony_ci return 0; 193562306a36Sopenharmony_ci return (target - BATT_OK_MIN) / BATT_OK_INCREMENT; 193662306a36Sopenharmony_ci} 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci/** 193962306a36Sopenharmony_ci * ab8500_fg_battok_init_hw_register - init battok levels 194062306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 194162306a36Sopenharmony_ci * 194262306a36Sopenharmony_ci */ 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_cistatic int ab8500_fg_battok_init_hw_register(struct ab8500_fg *di) 194562306a36Sopenharmony_ci{ 194662306a36Sopenharmony_ci int selected; 194762306a36Sopenharmony_ci int sel0; 194862306a36Sopenharmony_ci int sel1; 194962306a36Sopenharmony_ci int cbp_sel0; 195062306a36Sopenharmony_ci int cbp_sel1; 195162306a36Sopenharmony_ci int ret; 195262306a36Sopenharmony_ci int new_val; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci sel0 = di->bm->fg_params->battok_falling_th_sel0; 195562306a36Sopenharmony_ci sel1 = di->bm->fg_params->battok_raising_th_sel1; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci cbp_sel0 = ab8500_fg_battok_calc(di, sel0); 195862306a36Sopenharmony_ci cbp_sel1 = ab8500_fg_battok_calc(di, sel1); 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci selected = BATT_OK_MIN + cbp_sel0 * BATT_OK_INCREMENT; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci if (selected != sel0) 196362306a36Sopenharmony_ci dev_warn(di->dev, "Invalid voltage step:%d, using %d %d\n", 196462306a36Sopenharmony_ci sel0, selected, cbp_sel0); 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci selected = BATT_OK_MIN + cbp_sel1 * BATT_OK_INCREMENT; 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci if (selected != sel1) 196962306a36Sopenharmony_ci dev_warn(di->dev, "Invalid voltage step:%d, using %d %d\n", 197062306a36Sopenharmony_ci sel1, selected, cbp_sel1); 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci new_val = cbp_sel0 | (cbp_sel1 << 4); 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci dev_dbg(di->dev, "using: %x %d %d\n", new_val, cbp_sel0, cbp_sel1); 197562306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_SYS_CTRL2_BLOCK, 197662306a36Sopenharmony_ci AB8500_BATT_OK_REG, new_val); 197762306a36Sopenharmony_ci return ret; 197862306a36Sopenharmony_ci} 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci/** 198162306a36Sopenharmony_ci * ab8500_fg_instant_work() - Run the FG state machine instantly 198262306a36Sopenharmony_ci * @work: pointer to the work_struct structure 198362306a36Sopenharmony_ci * 198462306a36Sopenharmony_ci * Work queue function for instant work 198562306a36Sopenharmony_ci */ 198662306a36Sopenharmony_cistatic void ab8500_fg_instant_work(struct work_struct *work) 198762306a36Sopenharmony_ci{ 198862306a36Sopenharmony_ci struct ab8500_fg *di = container_of(work, struct ab8500_fg, fg_work); 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci ab8500_fg_algorithm(di); 199162306a36Sopenharmony_ci} 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci/** 199462306a36Sopenharmony_ci * ab8500_fg_cc_data_end_handler() - end of data conversion isr. 199562306a36Sopenharmony_ci * @irq: interrupt number 199662306a36Sopenharmony_ci * @_di: pointer to the ab8500_fg structure 199762306a36Sopenharmony_ci * 199862306a36Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 199962306a36Sopenharmony_ci */ 200062306a36Sopenharmony_cistatic irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di) 200162306a36Sopenharmony_ci{ 200262306a36Sopenharmony_ci struct ab8500_fg *di = _di; 200362306a36Sopenharmony_ci if (!di->nbr_cceoc_irq_cnt) { 200462306a36Sopenharmony_ci di->nbr_cceoc_irq_cnt++; 200562306a36Sopenharmony_ci complete(&di->ab8500_fg_started); 200662306a36Sopenharmony_ci } else { 200762306a36Sopenharmony_ci di->nbr_cceoc_irq_cnt = 0; 200862306a36Sopenharmony_ci complete(&di->ab8500_fg_complete); 200962306a36Sopenharmony_ci } 201062306a36Sopenharmony_ci return IRQ_HANDLED; 201162306a36Sopenharmony_ci} 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci/** 201462306a36Sopenharmony_ci * ab8500_fg_cc_int_calib_handler () - end of calibration isr. 201562306a36Sopenharmony_ci * @irq: interrupt number 201662306a36Sopenharmony_ci * @_di: pointer to the ab8500_fg structure 201762306a36Sopenharmony_ci * 201862306a36Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 201962306a36Sopenharmony_ci */ 202062306a36Sopenharmony_cistatic irqreturn_t ab8500_fg_cc_int_calib_handler(int irq, void *_di) 202162306a36Sopenharmony_ci{ 202262306a36Sopenharmony_ci struct ab8500_fg *di = _di; 202362306a36Sopenharmony_ci di->calib_state = AB8500_FG_CALIB_END; 202462306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 202562306a36Sopenharmony_ci return IRQ_HANDLED; 202662306a36Sopenharmony_ci} 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci/** 202962306a36Sopenharmony_ci * ab8500_fg_cc_convend_handler() - isr to get battery avg current. 203062306a36Sopenharmony_ci * @irq: interrupt number 203162306a36Sopenharmony_ci * @_di: pointer to the ab8500_fg structure 203262306a36Sopenharmony_ci * 203362306a36Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 203462306a36Sopenharmony_ci */ 203562306a36Sopenharmony_cistatic irqreturn_t ab8500_fg_cc_convend_handler(int irq, void *_di) 203662306a36Sopenharmony_ci{ 203762306a36Sopenharmony_ci struct ab8500_fg *di = _di; 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci queue_work(di->fg_wq, &di->fg_acc_cur_work); 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci return IRQ_HANDLED; 204262306a36Sopenharmony_ci} 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci/** 204562306a36Sopenharmony_ci * ab8500_fg_batt_ovv_handler() - Battery OVV occured 204662306a36Sopenharmony_ci * @irq: interrupt number 204762306a36Sopenharmony_ci * @_di: pointer to the ab8500_fg structure 204862306a36Sopenharmony_ci * 204962306a36Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 205062306a36Sopenharmony_ci */ 205162306a36Sopenharmony_cistatic irqreturn_t ab8500_fg_batt_ovv_handler(int irq, void *_di) 205262306a36Sopenharmony_ci{ 205362306a36Sopenharmony_ci struct ab8500_fg *di = _di; 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci dev_dbg(di->dev, "Battery OVV\n"); 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci /* Schedule a new HW failure check */ 205862306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 0); 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci return IRQ_HANDLED; 206162306a36Sopenharmony_ci} 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci/** 206462306a36Sopenharmony_ci * ab8500_fg_lowbatf_handler() - Battery voltage is below LOW threshold 206562306a36Sopenharmony_ci * @irq: interrupt number 206662306a36Sopenharmony_ci * @_di: pointer to the ab8500_fg structure 206762306a36Sopenharmony_ci * 206862306a36Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 206962306a36Sopenharmony_ci */ 207062306a36Sopenharmony_cistatic irqreturn_t ab8500_fg_lowbatf_handler(int irq, void *_di) 207162306a36Sopenharmony_ci{ 207262306a36Sopenharmony_ci struct ab8500_fg *di = _di; 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci /* Initiate handling in ab8500_fg_low_bat_work() if not already initiated. */ 207562306a36Sopenharmony_ci if (!di->flags.low_bat_delay) { 207662306a36Sopenharmony_ci dev_warn(di->dev, "Battery voltage is below LOW threshold\n"); 207762306a36Sopenharmony_ci di->flags.low_bat_delay = true; 207862306a36Sopenharmony_ci /* 207962306a36Sopenharmony_ci * Start a timer to check LOW_BAT again after some time 208062306a36Sopenharmony_ci * This is done to avoid shutdown on single voltage dips 208162306a36Sopenharmony_ci */ 208262306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_low_bat_work, 208362306a36Sopenharmony_ci round_jiffies(LOW_BAT_CHECK_INTERVAL)); 208462306a36Sopenharmony_ci } 208562306a36Sopenharmony_ci return IRQ_HANDLED; 208662306a36Sopenharmony_ci} 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci/** 208962306a36Sopenharmony_ci * ab8500_fg_get_property() - get the fg properties 209062306a36Sopenharmony_ci * @psy: pointer to the power_supply structure 209162306a36Sopenharmony_ci * @psp: pointer to the power_supply_property structure 209262306a36Sopenharmony_ci * @val: pointer to the power_supply_propval union 209362306a36Sopenharmony_ci * 209462306a36Sopenharmony_ci * This function gets called when an application tries to get the 209562306a36Sopenharmony_ci * fg properties by reading the sysfs files. 209662306a36Sopenharmony_ci * voltage_now: battery voltage 209762306a36Sopenharmony_ci * current_now: battery instant current 209862306a36Sopenharmony_ci * current_avg: battery average current 209962306a36Sopenharmony_ci * charge_full_design: capacity where battery is considered full 210062306a36Sopenharmony_ci * charge_now: battery capacity in nAh 210162306a36Sopenharmony_ci * capacity: capacity in percent 210262306a36Sopenharmony_ci * capacity_level: capacity level 210362306a36Sopenharmony_ci * 210462306a36Sopenharmony_ci * Returns error code in case of failure else 0 on success 210562306a36Sopenharmony_ci */ 210662306a36Sopenharmony_cistatic int ab8500_fg_get_property(struct power_supply *psy, 210762306a36Sopenharmony_ci enum power_supply_property psp, 210862306a36Sopenharmony_ci union power_supply_propval *val) 210962306a36Sopenharmony_ci{ 211062306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci /* 211362306a36Sopenharmony_ci * If battery is identified as unknown and charging of unknown 211462306a36Sopenharmony_ci * batteries is disabled, we always report 100% capacity and 211562306a36Sopenharmony_ci * capacity level UNKNOWN, since we can't calculate 211662306a36Sopenharmony_ci * remaining capacity 211762306a36Sopenharmony_ci */ 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci switch (psp) { 212062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 212162306a36Sopenharmony_ci if (di->flags.bat_ovv) 212262306a36Sopenharmony_ci val->intval = BATT_OVV_VALUE; 212362306a36Sopenharmony_ci else 212462306a36Sopenharmony_ci val->intval = di->vbat_uv; 212562306a36Sopenharmony_ci break; 212662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 212762306a36Sopenharmony_ci val->intval = di->inst_curr_ua; 212862306a36Sopenharmony_ci break; 212962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 213062306a36Sopenharmony_ci val->intval = di->avg_curr_ua; 213162306a36Sopenharmony_ci break; 213262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 213362306a36Sopenharmony_ci val->intval = ab8500_fg_convert_mah_to_uwh(di, 213462306a36Sopenharmony_ci di->bat_cap.max_mah_design); 213562306a36Sopenharmony_ci break; 213662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_FULL: 213762306a36Sopenharmony_ci val->intval = ab8500_fg_convert_mah_to_uwh(di, 213862306a36Sopenharmony_ci di->bat_cap.max_mah); 213962306a36Sopenharmony_ci break; 214062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_NOW: 214162306a36Sopenharmony_ci if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && 214262306a36Sopenharmony_ci di->flags.batt_id_received) 214362306a36Sopenharmony_ci val->intval = ab8500_fg_convert_mah_to_uwh(di, 214462306a36Sopenharmony_ci di->bat_cap.max_mah); 214562306a36Sopenharmony_ci else 214662306a36Sopenharmony_ci val->intval = ab8500_fg_convert_mah_to_uwh(di, 214762306a36Sopenharmony_ci di->bat_cap.prev_mah); 214862306a36Sopenharmony_ci break; 214962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 215062306a36Sopenharmony_ci val->intval = di->bat_cap.max_mah_design; 215162306a36Sopenharmony_ci break; 215262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL: 215362306a36Sopenharmony_ci val->intval = di->bat_cap.max_mah; 215462306a36Sopenharmony_ci break; 215562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_NOW: 215662306a36Sopenharmony_ci if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && 215762306a36Sopenharmony_ci di->flags.batt_id_received) 215862306a36Sopenharmony_ci val->intval = di->bat_cap.max_mah; 215962306a36Sopenharmony_ci else 216062306a36Sopenharmony_ci val->intval = di->bat_cap.prev_mah; 216162306a36Sopenharmony_ci break; 216262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 216362306a36Sopenharmony_ci if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && 216462306a36Sopenharmony_ci di->flags.batt_id_received) 216562306a36Sopenharmony_ci val->intval = 100; 216662306a36Sopenharmony_ci else 216762306a36Sopenharmony_ci val->intval = di->bat_cap.prev_percent; 216862306a36Sopenharmony_ci break; 216962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY_LEVEL: 217062306a36Sopenharmony_ci if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && 217162306a36Sopenharmony_ci di->flags.batt_id_received) 217262306a36Sopenharmony_ci val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; 217362306a36Sopenharmony_ci else 217462306a36Sopenharmony_ci val->intval = di->bat_cap.prev_level; 217562306a36Sopenharmony_ci break; 217662306a36Sopenharmony_ci default: 217762306a36Sopenharmony_ci return -EINVAL; 217862306a36Sopenharmony_ci } 217962306a36Sopenharmony_ci return 0; 218062306a36Sopenharmony_ci} 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_cistatic int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) 218362306a36Sopenharmony_ci{ 218462306a36Sopenharmony_ci struct power_supply *psy; 218562306a36Sopenharmony_ci struct power_supply *ext = dev_get_drvdata(dev); 218662306a36Sopenharmony_ci const char **supplicants = (const char **)ext->supplied_to; 218762306a36Sopenharmony_ci struct ab8500_fg *di; 218862306a36Sopenharmony_ci struct power_supply_battery_info *bi; 218962306a36Sopenharmony_ci union power_supply_propval ret; 219062306a36Sopenharmony_ci int j; 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci psy = (struct power_supply *)data; 219362306a36Sopenharmony_ci di = power_supply_get_drvdata(psy); 219462306a36Sopenharmony_ci bi = di->bm->bi; 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci /* 219762306a36Sopenharmony_ci * For all psy where the name of your driver 219862306a36Sopenharmony_ci * appears in any supplied_to 219962306a36Sopenharmony_ci */ 220062306a36Sopenharmony_ci j = match_string(supplicants, ext->num_supplicants, psy->desc->name); 220162306a36Sopenharmony_ci if (j < 0) 220262306a36Sopenharmony_ci return 0; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci /* Go through all properties for the psy */ 220562306a36Sopenharmony_ci for (j = 0; j < ext->desc->num_properties; j++) { 220662306a36Sopenharmony_ci enum power_supply_property prop; 220762306a36Sopenharmony_ci prop = ext->desc->properties[j]; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci if (power_supply_get_property(ext, prop, &ret)) 221062306a36Sopenharmony_ci continue; 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci switch (prop) { 221362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 221462306a36Sopenharmony_ci switch (ext->desc->type) { 221562306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 221662306a36Sopenharmony_ci switch (ret.intval) { 221762306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_UNKNOWN: 221862306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_DISCHARGING: 221962306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_NOT_CHARGING: 222062306a36Sopenharmony_ci if (!di->flags.charging) 222162306a36Sopenharmony_ci break; 222262306a36Sopenharmony_ci di->flags.charging = false; 222362306a36Sopenharmony_ci di->flags.fully_charged = false; 222462306a36Sopenharmony_ci if (di->bm->capacity_scaling) 222562306a36Sopenharmony_ci ab8500_fg_update_cap_scalers(di); 222662306a36Sopenharmony_ci queue_work(di->fg_wq, &di->fg_work); 222762306a36Sopenharmony_ci break; 222862306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_FULL: 222962306a36Sopenharmony_ci if (di->flags.fully_charged) 223062306a36Sopenharmony_ci break; 223162306a36Sopenharmony_ci di->flags.fully_charged = true; 223262306a36Sopenharmony_ci di->flags.force_full = true; 223362306a36Sopenharmony_ci /* Save current capacity as maximum */ 223462306a36Sopenharmony_ci di->bat_cap.max_mah = di->bat_cap.mah; 223562306a36Sopenharmony_ci queue_work(di->fg_wq, &di->fg_work); 223662306a36Sopenharmony_ci break; 223762306a36Sopenharmony_ci case POWER_SUPPLY_STATUS_CHARGING: 223862306a36Sopenharmony_ci if (di->flags.charging && 223962306a36Sopenharmony_ci !di->flags.fully_charged) 224062306a36Sopenharmony_ci break; 224162306a36Sopenharmony_ci di->flags.charging = true; 224262306a36Sopenharmony_ci di->flags.fully_charged = false; 224362306a36Sopenharmony_ci if (di->bm->capacity_scaling) 224462306a36Sopenharmony_ci ab8500_fg_update_cap_scalers(di); 224562306a36Sopenharmony_ci queue_work(di->fg_wq, &di->fg_work); 224662306a36Sopenharmony_ci break; 224762306a36Sopenharmony_ci } 224862306a36Sopenharmony_ci break; 224962306a36Sopenharmony_ci default: 225062306a36Sopenharmony_ci break; 225162306a36Sopenharmony_ci } 225262306a36Sopenharmony_ci break; 225362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 225462306a36Sopenharmony_ci switch (ext->desc->type) { 225562306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 225662306a36Sopenharmony_ci if (!di->flags.batt_id_received && 225762306a36Sopenharmony_ci (bi && (bi->technology != 225862306a36Sopenharmony_ci POWER_SUPPLY_TECHNOLOGY_UNKNOWN))) { 225962306a36Sopenharmony_ci di->flags.batt_id_received = true; 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci di->bat_cap.max_mah_design = 226262306a36Sopenharmony_ci di->bm->bi->charge_full_design_uah; 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci di->bat_cap.max_mah = 226562306a36Sopenharmony_ci di->bat_cap.max_mah_design; 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci di->vbat_nom_uv = 226862306a36Sopenharmony_ci di->bm->bi->voltage_max_design_uv; 226962306a36Sopenharmony_ci } 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci if (ret.intval) 227262306a36Sopenharmony_ci di->flags.batt_unknown = false; 227362306a36Sopenharmony_ci else 227462306a36Sopenharmony_ci di->flags.batt_unknown = true; 227562306a36Sopenharmony_ci break; 227662306a36Sopenharmony_ci default: 227762306a36Sopenharmony_ci break; 227862306a36Sopenharmony_ci } 227962306a36Sopenharmony_ci break; 228062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 228162306a36Sopenharmony_ci switch (ext->desc->type) { 228262306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 228362306a36Sopenharmony_ci if (di->flags.batt_id_received) 228462306a36Sopenharmony_ci di->bat_temp = ret.intval; 228562306a36Sopenharmony_ci break; 228662306a36Sopenharmony_ci default: 228762306a36Sopenharmony_ci break; 228862306a36Sopenharmony_ci } 228962306a36Sopenharmony_ci break; 229062306a36Sopenharmony_ci default: 229162306a36Sopenharmony_ci break; 229262306a36Sopenharmony_ci } 229362306a36Sopenharmony_ci } 229462306a36Sopenharmony_ci return 0; 229562306a36Sopenharmony_ci} 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci/** 229862306a36Sopenharmony_ci * ab8500_fg_init_hw_registers() - Set up FG related registers 229962306a36Sopenharmony_ci * @di: pointer to the ab8500_fg structure 230062306a36Sopenharmony_ci * 230162306a36Sopenharmony_ci * Set up battery OVV, low battery voltage registers 230262306a36Sopenharmony_ci */ 230362306a36Sopenharmony_cistatic int ab8500_fg_init_hw_registers(struct ab8500_fg *di) 230462306a36Sopenharmony_ci{ 230562306a36Sopenharmony_ci int ret; 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci /* 230862306a36Sopenharmony_ci * Set VBAT OVV (overvoltage) threshold to 4.75V (typ) this is what 230962306a36Sopenharmony_ci * the hardware supports, nothing else can be configured in hardware. 231062306a36Sopenharmony_ci * See this as an "outer limit" where the charger will certainly 231162306a36Sopenharmony_ci * shut down. Other (lower) overvoltage levels need to be implemented 231262306a36Sopenharmony_ci * in software. 231362306a36Sopenharmony_ci */ 231462306a36Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 231562306a36Sopenharmony_ci AB8500_CHARGER, 231662306a36Sopenharmony_ci AB8500_BATT_OVV, 231762306a36Sopenharmony_ci BATT_OVV_TH_4P75, 231862306a36Sopenharmony_ci BATT_OVV_TH_4P75); 231962306a36Sopenharmony_ci if (ret) { 232062306a36Sopenharmony_ci dev_err(di->dev, "failed to set BATT_OVV\n"); 232162306a36Sopenharmony_ci goto out; 232262306a36Sopenharmony_ci } 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci /* Enable VBAT OVV detection */ 232562306a36Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 232662306a36Sopenharmony_ci AB8500_CHARGER, 232762306a36Sopenharmony_ci AB8500_BATT_OVV, 232862306a36Sopenharmony_ci BATT_OVV_ENA, 232962306a36Sopenharmony_ci BATT_OVV_ENA); 233062306a36Sopenharmony_ci if (ret) { 233162306a36Sopenharmony_ci dev_err(di->dev, "failed to enable BATT_OVV\n"); 233262306a36Sopenharmony_ci goto out; 233362306a36Sopenharmony_ci } 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci /* Low Battery Voltage */ 233662306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, 233762306a36Sopenharmony_ci AB8500_SYS_CTRL2_BLOCK, 233862306a36Sopenharmony_ci AB8500_LOW_BAT_REG, 233962306a36Sopenharmony_ci ab8500_volt_to_regval( 234062306a36Sopenharmony_ci di->bm->fg_params->lowbat_threshold_uv) << 1 | 234162306a36Sopenharmony_ci LOW_BAT_ENABLE); 234262306a36Sopenharmony_ci if (ret) { 234362306a36Sopenharmony_ci dev_err(di->dev, "%s write failed\n", __func__); 234462306a36Sopenharmony_ci goto out; 234562306a36Sopenharmony_ci } 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci /* Battery OK threshold */ 234862306a36Sopenharmony_ci ret = ab8500_fg_battok_init_hw_register(di); 234962306a36Sopenharmony_ci if (ret) { 235062306a36Sopenharmony_ci dev_err(di->dev, "BattOk init write failed.\n"); 235162306a36Sopenharmony_ci goto out; 235262306a36Sopenharmony_ci } 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci if (is_ab8505(di->parent)) { 235562306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 235662306a36Sopenharmony_ci AB8505_RTC_PCUT_MAX_TIME_REG, di->bm->fg_params->pcut_max_time); 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci if (ret) { 235962306a36Sopenharmony_ci dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__); 236062306a36Sopenharmony_ci goto out; 236162306a36Sopenharmony_ci } 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 236462306a36Sopenharmony_ci AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time); 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci if (ret) { 236762306a36Sopenharmony_ci dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__); 236862306a36Sopenharmony_ci goto out; 236962306a36Sopenharmony_ci } 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 237262306a36Sopenharmony_ci AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart); 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci if (ret) { 237562306a36Sopenharmony_ci dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__); 237662306a36Sopenharmony_ci goto out; 237762306a36Sopenharmony_ci } 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 238062306a36Sopenharmony_ci AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci if (ret) { 238362306a36Sopenharmony_ci dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__); 238462306a36Sopenharmony_ci goto out; 238562306a36Sopenharmony_ci } 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 238862306a36Sopenharmony_ci AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable); 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci if (ret) { 239162306a36Sopenharmony_ci dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__); 239262306a36Sopenharmony_ci goto out; 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci } 239562306a36Sopenharmony_ciout: 239662306a36Sopenharmony_ci return ret; 239762306a36Sopenharmony_ci} 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci/** 240062306a36Sopenharmony_ci * ab8500_fg_external_power_changed() - callback for power supply changes 240162306a36Sopenharmony_ci * @psy: pointer to the structure power_supply 240262306a36Sopenharmony_ci * 240362306a36Sopenharmony_ci * This function is the entry point of the pointer external_power_changed 240462306a36Sopenharmony_ci * of the structure power_supply. 240562306a36Sopenharmony_ci * This function gets executed when there is a change in any external power 240662306a36Sopenharmony_ci * supply that this driver needs to be notified of. 240762306a36Sopenharmony_ci */ 240862306a36Sopenharmony_cistatic void ab8500_fg_external_power_changed(struct power_supply *psy) 240962306a36Sopenharmony_ci{ 241062306a36Sopenharmony_ci class_for_each_device(power_supply_class, NULL, psy, 241162306a36Sopenharmony_ci ab8500_fg_get_ext_psy_data); 241262306a36Sopenharmony_ci} 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci/** 241562306a36Sopenharmony_ci * ab8500_fg_reinit_work() - work to reset the FG algorithm 241662306a36Sopenharmony_ci * @work: pointer to the work_struct structure 241762306a36Sopenharmony_ci * 241862306a36Sopenharmony_ci * Used to reset the current battery capacity to be able to 241962306a36Sopenharmony_ci * retrigger a new voltage base capacity calculation. For 242062306a36Sopenharmony_ci * test and verification purpose. 242162306a36Sopenharmony_ci */ 242262306a36Sopenharmony_cistatic void ab8500_fg_reinit_work(struct work_struct *work) 242362306a36Sopenharmony_ci{ 242462306a36Sopenharmony_ci struct ab8500_fg *di = container_of(work, struct ab8500_fg, 242562306a36Sopenharmony_ci fg_reinit_work.work); 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci if (!di->flags.calibrate) { 242862306a36Sopenharmony_ci dev_dbg(di->dev, "Resetting FG state machine to init.\n"); 242962306a36Sopenharmony_ci ab8500_fg_clear_cap_samples(di); 243062306a36Sopenharmony_ci ab8500_fg_calc_cap_discharge_voltage(di); 243162306a36Sopenharmony_ci ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT); 243262306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT); 243362306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci } else { 243662306a36Sopenharmony_ci dev_err(di->dev, "Residual offset calibration ongoing " 243762306a36Sopenharmony_ci "retrying..\n"); 243862306a36Sopenharmony_ci /* Wait one second until next try*/ 243962306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_reinit_work, 244062306a36Sopenharmony_ci round_jiffies(1)); 244162306a36Sopenharmony_ci } 244262306a36Sopenharmony_ci} 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci/* Exposure to the sysfs interface */ 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_cistruct ab8500_fg_sysfs_entry { 244762306a36Sopenharmony_ci struct attribute attr; 244862306a36Sopenharmony_ci ssize_t (*show)(struct ab8500_fg *, char *); 244962306a36Sopenharmony_ci ssize_t (*store)(struct ab8500_fg *, const char *, size_t); 245062306a36Sopenharmony_ci}; 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_cistatic ssize_t charge_full_show(struct ab8500_fg *di, char *buf) 245362306a36Sopenharmony_ci{ 245462306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", di->bat_cap.max_mah); 245562306a36Sopenharmony_ci} 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_cistatic ssize_t charge_full_store(struct ab8500_fg *di, const char *buf, 245862306a36Sopenharmony_ci size_t count) 245962306a36Sopenharmony_ci{ 246062306a36Sopenharmony_ci unsigned long charge_full; 246162306a36Sopenharmony_ci int ret; 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci ret = kstrtoul(buf, 10, &charge_full); 246462306a36Sopenharmony_ci if (ret) 246562306a36Sopenharmony_ci return ret; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci di->bat_cap.max_mah = (int) charge_full; 246862306a36Sopenharmony_ci return count; 246962306a36Sopenharmony_ci} 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_cistatic ssize_t charge_now_show(struct ab8500_fg *di, char *buf) 247262306a36Sopenharmony_ci{ 247362306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", di->bat_cap.prev_mah); 247462306a36Sopenharmony_ci} 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_cistatic ssize_t charge_now_store(struct ab8500_fg *di, const char *buf, 247762306a36Sopenharmony_ci size_t count) 247862306a36Sopenharmony_ci{ 247962306a36Sopenharmony_ci unsigned long charge_now; 248062306a36Sopenharmony_ci int ret; 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci ret = kstrtoul(buf, 10, &charge_now); 248362306a36Sopenharmony_ci if (ret) 248462306a36Sopenharmony_ci return ret; 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci di->bat_cap.user_mah = (int) charge_now; 248762306a36Sopenharmony_ci di->flags.user_cap = true; 248862306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 248962306a36Sopenharmony_ci return count; 249062306a36Sopenharmony_ci} 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_cistatic struct ab8500_fg_sysfs_entry charge_full_attr = 249362306a36Sopenharmony_ci __ATTR(charge_full, 0644, charge_full_show, charge_full_store); 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_cistatic struct ab8500_fg_sysfs_entry charge_now_attr = 249662306a36Sopenharmony_ci __ATTR(charge_now, 0644, charge_now_show, charge_now_store); 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_cistatic ssize_t 249962306a36Sopenharmony_ciab8500_fg_show(struct kobject *kobj, struct attribute *attr, char *buf) 250062306a36Sopenharmony_ci{ 250162306a36Sopenharmony_ci struct ab8500_fg_sysfs_entry *entry; 250262306a36Sopenharmony_ci struct ab8500_fg *di; 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_ci entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr); 250562306a36Sopenharmony_ci di = container_of(kobj, struct ab8500_fg, fg_kobject); 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_ci if (!entry->show) 250862306a36Sopenharmony_ci return -EIO; 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci return entry->show(di, buf); 251162306a36Sopenharmony_ci} 251262306a36Sopenharmony_cistatic ssize_t 251362306a36Sopenharmony_ciab8500_fg_store(struct kobject *kobj, struct attribute *attr, const char *buf, 251462306a36Sopenharmony_ci size_t count) 251562306a36Sopenharmony_ci{ 251662306a36Sopenharmony_ci struct ab8500_fg_sysfs_entry *entry; 251762306a36Sopenharmony_ci struct ab8500_fg *di; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr); 252062306a36Sopenharmony_ci di = container_of(kobj, struct ab8500_fg, fg_kobject); 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci if (!entry->store) 252362306a36Sopenharmony_ci return -EIO; 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_ci return entry->store(di, buf, count); 252662306a36Sopenharmony_ci} 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_cistatic const struct sysfs_ops ab8500_fg_sysfs_ops = { 252962306a36Sopenharmony_ci .show = ab8500_fg_show, 253062306a36Sopenharmony_ci .store = ab8500_fg_store, 253162306a36Sopenharmony_ci}; 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_cistatic struct attribute *ab8500_fg_attrs[] = { 253462306a36Sopenharmony_ci &charge_full_attr.attr, 253562306a36Sopenharmony_ci &charge_now_attr.attr, 253662306a36Sopenharmony_ci NULL, 253762306a36Sopenharmony_ci}; 253862306a36Sopenharmony_ciATTRIBUTE_GROUPS(ab8500_fg); 253962306a36Sopenharmony_ci 254062306a36Sopenharmony_cistatic struct kobj_type ab8500_fg_ktype = { 254162306a36Sopenharmony_ci .sysfs_ops = &ab8500_fg_sysfs_ops, 254262306a36Sopenharmony_ci .default_groups = ab8500_fg_groups, 254362306a36Sopenharmony_ci}; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci/** 254662306a36Sopenharmony_ci * ab8500_fg_sysfs_exit() - de-init of sysfs entry 254762306a36Sopenharmony_ci * @di: pointer to the struct ab8500_chargalg 254862306a36Sopenharmony_ci * 254962306a36Sopenharmony_ci * This function removes the entry in sysfs. 255062306a36Sopenharmony_ci */ 255162306a36Sopenharmony_cistatic void ab8500_fg_sysfs_exit(struct ab8500_fg *di) 255262306a36Sopenharmony_ci{ 255362306a36Sopenharmony_ci kobject_del(&di->fg_kobject); 255462306a36Sopenharmony_ci} 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci/** 255762306a36Sopenharmony_ci * ab8500_fg_sysfs_init() - init of sysfs entry 255862306a36Sopenharmony_ci * @di: pointer to the struct ab8500_chargalg 255962306a36Sopenharmony_ci * 256062306a36Sopenharmony_ci * This function adds an entry in sysfs. 256162306a36Sopenharmony_ci * Returns error code in case of failure else 0(on success) 256262306a36Sopenharmony_ci */ 256362306a36Sopenharmony_cistatic int ab8500_fg_sysfs_init(struct ab8500_fg *di) 256462306a36Sopenharmony_ci{ 256562306a36Sopenharmony_ci int ret = 0; 256662306a36Sopenharmony_ci 256762306a36Sopenharmony_ci ret = kobject_init_and_add(&di->fg_kobject, 256862306a36Sopenharmony_ci &ab8500_fg_ktype, 256962306a36Sopenharmony_ci NULL, "battery"); 257062306a36Sopenharmony_ci if (ret < 0) { 257162306a36Sopenharmony_ci kobject_put(&di->fg_kobject); 257262306a36Sopenharmony_ci dev_err(di->dev, "failed to create sysfs entry\n"); 257362306a36Sopenharmony_ci } 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci return ret; 257662306a36Sopenharmony_ci} 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_cistatic ssize_t ab8505_powercut_flagtime_read(struct device *dev, 257962306a36Sopenharmony_ci struct device_attribute *attr, 258062306a36Sopenharmony_ci char *buf) 258162306a36Sopenharmony_ci{ 258262306a36Sopenharmony_ci int ret; 258362306a36Sopenharmony_ci u8 reg_value; 258462306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 258562306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 258862306a36Sopenharmony_ci AB8505_RTC_PCUT_FLAG_TIME_REG, ®_value); 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci if (ret < 0) { 259162306a36Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n"); 259262306a36Sopenharmony_ci goto fail; 259362306a36Sopenharmony_ci } 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", (reg_value & 0x7F)); 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_cifail: 259862306a36Sopenharmony_ci return ret; 259962306a36Sopenharmony_ci} 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_cistatic ssize_t ab8505_powercut_flagtime_write(struct device *dev, 260262306a36Sopenharmony_ci struct device_attribute *attr, 260362306a36Sopenharmony_ci const char *buf, size_t count) 260462306a36Sopenharmony_ci{ 260562306a36Sopenharmony_ci int ret; 260662306a36Sopenharmony_ci int reg_value; 260762306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 260862306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci if (kstrtoint(buf, 10, ®_value)) 261162306a36Sopenharmony_ci goto fail; 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci if (reg_value > 0x7F) { 261462306a36Sopenharmony_ci dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n"); 261562306a36Sopenharmony_ci goto fail; 261662306a36Sopenharmony_ci } 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 261962306a36Sopenharmony_ci AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value); 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci if (ret < 0) 262262306a36Sopenharmony_ci dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n"); 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_cifail: 262562306a36Sopenharmony_ci return count; 262662306a36Sopenharmony_ci} 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_cistatic ssize_t ab8505_powercut_maxtime_read(struct device *dev, 262962306a36Sopenharmony_ci struct device_attribute *attr, 263062306a36Sopenharmony_ci char *buf) 263162306a36Sopenharmony_ci{ 263262306a36Sopenharmony_ci int ret; 263362306a36Sopenharmony_ci u8 reg_value; 263462306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 263562306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 263862306a36Sopenharmony_ci AB8505_RTC_PCUT_MAX_TIME_REG, ®_value); 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci if (ret < 0) { 264162306a36Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_MAX_TIME_REG\n"); 264262306a36Sopenharmony_ci goto fail; 264362306a36Sopenharmony_ci } 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", (reg_value & 0x7F)); 264662306a36Sopenharmony_ci 264762306a36Sopenharmony_cifail: 264862306a36Sopenharmony_ci return ret; 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_ci} 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_cistatic ssize_t ab8505_powercut_maxtime_write(struct device *dev, 265362306a36Sopenharmony_ci struct device_attribute *attr, 265462306a36Sopenharmony_ci const char *buf, size_t count) 265562306a36Sopenharmony_ci{ 265662306a36Sopenharmony_ci int ret; 265762306a36Sopenharmony_ci int reg_value; 265862306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 265962306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci if (kstrtoint(buf, 10, ®_value)) 266262306a36Sopenharmony_ci goto fail; 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci if (reg_value > 0x7F) { 266562306a36Sopenharmony_ci dev_err(dev, "Incorrect parameter, echo 0 (0.0s) - 127 (1.98s) for maxtime\n"); 266662306a36Sopenharmony_ci goto fail; 266762306a36Sopenharmony_ci } 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 267062306a36Sopenharmony_ci AB8505_RTC_PCUT_MAX_TIME_REG, (u8)reg_value); 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci if (ret < 0) 267362306a36Sopenharmony_ci dev_err(dev, "Failed to set AB8505_RTC_PCUT_MAX_TIME_REG\n"); 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_cifail: 267662306a36Sopenharmony_ci return count; 267762306a36Sopenharmony_ci} 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_cistatic ssize_t ab8505_powercut_restart_read(struct device *dev, 268062306a36Sopenharmony_ci struct device_attribute *attr, 268162306a36Sopenharmony_ci char *buf) 268262306a36Sopenharmony_ci{ 268362306a36Sopenharmony_ci int ret; 268462306a36Sopenharmony_ci u8 reg_value; 268562306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 268662306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 268762306a36Sopenharmony_ci 268862306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 268962306a36Sopenharmony_ci AB8505_RTC_PCUT_RESTART_REG, ®_value); 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci if (ret < 0) { 269262306a36Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n"); 269362306a36Sopenharmony_ci goto fail; 269462306a36Sopenharmony_ci } 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", (reg_value & 0xF)); 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_cifail: 269962306a36Sopenharmony_ci return ret; 270062306a36Sopenharmony_ci} 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_cistatic ssize_t ab8505_powercut_restart_write(struct device *dev, 270362306a36Sopenharmony_ci struct device_attribute *attr, 270462306a36Sopenharmony_ci const char *buf, size_t count) 270562306a36Sopenharmony_ci{ 270662306a36Sopenharmony_ci int ret; 270762306a36Sopenharmony_ci int reg_value; 270862306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 270962306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_ci if (kstrtoint(buf, 10, ®_value)) 271262306a36Sopenharmony_ci goto fail; 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_ci if (reg_value > 0xF) { 271562306a36Sopenharmony_ci dev_err(dev, "Incorrect parameter, echo 0 - 15 for number of restart\n"); 271662306a36Sopenharmony_ci goto fail; 271762306a36Sopenharmony_ci } 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 272062306a36Sopenharmony_ci AB8505_RTC_PCUT_RESTART_REG, (u8)reg_value); 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_ci if (ret < 0) 272362306a36Sopenharmony_ci dev_err(dev, "Failed to set AB8505_RTC_PCUT_RESTART_REG\n"); 272462306a36Sopenharmony_ci 272562306a36Sopenharmony_cifail: 272662306a36Sopenharmony_ci return count; 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_ci} 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_cistatic ssize_t ab8505_powercut_timer_read(struct device *dev, 273162306a36Sopenharmony_ci struct device_attribute *attr, 273262306a36Sopenharmony_ci char *buf) 273362306a36Sopenharmony_ci{ 273462306a36Sopenharmony_ci int ret; 273562306a36Sopenharmony_ci u8 reg_value; 273662306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 273762306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 274062306a36Sopenharmony_ci AB8505_RTC_PCUT_TIME_REG, ®_value); 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci if (ret < 0) { 274362306a36Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_TIME_REG\n"); 274462306a36Sopenharmony_ci goto fail; 274562306a36Sopenharmony_ci } 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", (reg_value & 0x7F)); 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_cifail: 275062306a36Sopenharmony_ci return ret; 275162306a36Sopenharmony_ci} 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_cistatic ssize_t ab8505_powercut_restart_counter_read(struct device *dev, 275462306a36Sopenharmony_ci struct device_attribute *attr, 275562306a36Sopenharmony_ci char *buf) 275662306a36Sopenharmony_ci{ 275762306a36Sopenharmony_ci int ret; 275862306a36Sopenharmony_ci u8 reg_value; 275962306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 276062306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 276362306a36Sopenharmony_ci AB8505_RTC_PCUT_RESTART_REG, ®_value); 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_ci if (ret < 0) { 276662306a36Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n"); 276762306a36Sopenharmony_ci goto fail; 276862306a36Sopenharmony_ci } 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", (reg_value & 0xF0) >> 4); 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_cifail: 277362306a36Sopenharmony_ci return ret; 277462306a36Sopenharmony_ci} 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_cistatic ssize_t ab8505_powercut_read(struct device *dev, 277762306a36Sopenharmony_ci struct device_attribute *attr, 277862306a36Sopenharmony_ci char *buf) 277962306a36Sopenharmony_ci{ 278062306a36Sopenharmony_ci int ret; 278162306a36Sopenharmony_ci u8 reg_value; 278262306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 278362306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 278662306a36Sopenharmony_ci AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci if (ret < 0) 278962306a36Sopenharmony_ci goto fail; 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", (reg_value & 0x1)); 279262306a36Sopenharmony_ci 279362306a36Sopenharmony_cifail: 279462306a36Sopenharmony_ci return ret; 279562306a36Sopenharmony_ci} 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_cistatic ssize_t ab8505_powercut_write(struct device *dev, 279862306a36Sopenharmony_ci struct device_attribute *attr, 279962306a36Sopenharmony_ci const char *buf, size_t count) 280062306a36Sopenharmony_ci{ 280162306a36Sopenharmony_ci int ret; 280262306a36Sopenharmony_ci int reg_value; 280362306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 280462306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_ci if (kstrtoint(buf, 10, ®_value)) 280762306a36Sopenharmony_ci goto fail; 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci if (reg_value > 0x1) { 281062306a36Sopenharmony_ci dev_err(dev, "Incorrect parameter, echo 0/1 to disable/enable Pcut feature\n"); 281162306a36Sopenharmony_ci goto fail; 281262306a36Sopenharmony_ci } 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 281562306a36Sopenharmony_ci AB8505_RTC_PCUT_CTL_STATUS_REG, (u8)reg_value); 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci if (ret < 0) 281862306a36Sopenharmony_ci dev_err(dev, "Failed to set AB8505_RTC_PCUT_CTL_STATUS_REG\n"); 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_cifail: 282162306a36Sopenharmony_ci return count; 282262306a36Sopenharmony_ci} 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_cistatic ssize_t ab8505_powercut_flag_read(struct device *dev, 282562306a36Sopenharmony_ci struct device_attribute *attr, 282662306a36Sopenharmony_ci char *buf) 282762306a36Sopenharmony_ci{ 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci int ret; 283062306a36Sopenharmony_ci u8 reg_value; 283162306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 283262306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 283562306a36Sopenharmony_ci AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci if (ret < 0) { 283862306a36Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n"); 283962306a36Sopenharmony_ci goto fail; 284062306a36Sopenharmony_ci } 284162306a36Sopenharmony_ci 284262306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", ((reg_value & 0x10) >> 4)); 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_cifail: 284562306a36Sopenharmony_ci return ret; 284662306a36Sopenharmony_ci} 284762306a36Sopenharmony_ci 284862306a36Sopenharmony_cistatic ssize_t ab8505_powercut_debounce_read(struct device *dev, 284962306a36Sopenharmony_ci struct device_attribute *attr, 285062306a36Sopenharmony_ci char *buf) 285162306a36Sopenharmony_ci{ 285262306a36Sopenharmony_ci int ret; 285362306a36Sopenharmony_ci u8 reg_value; 285462306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 285562306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 285862306a36Sopenharmony_ci AB8505_RTC_PCUT_DEBOUNCE_REG, ®_value); 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci if (ret < 0) { 286162306a36Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_DEBOUNCE_REG\n"); 286262306a36Sopenharmony_ci goto fail; 286362306a36Sopenharmony_ci } 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", (reg_value & 0x7)); 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_cifail: 286862306a36Sopenharmony_ci return ret; 286962306a36Sopenharmony_ci} 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_cistatic ssize_t ab8505_powercut_debounce_write(struct device *dev, 287262306a36Sopenharmony_ci struct device_attribute *attr, 287362306a36Sopenharmony_ci const char *buf, size_t count) 287462306a36Sopenharmony_ci{ 287562306a36Sopenharmony_ci int ret; 287662306a36Sopenharmony_ci int reg_value; 287762306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 287862306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_ci if (kstrtoint(buf, 10, ®_value)) 288162306a36Sopenharmony_ci goto fail; 288262306a36Sopenharmony_ci 288362306a36Sopenharmony_ci if (reg_value > 0x7) { 288462306a36Sopenharmony_ci dev_err(dev, "Incorrect parameter, echo 0 to 7 for debounce setting\n"); 288562306a36Sopenharmony_ci goto fail; 288662306a36Sopenharmony_ci } 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 288962306a36Sopenharmony_ci AB8505_RTC_PCUT_DEBOUNCE_REG, (u8)reg_value); 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ci if (ret < 0) 289262306a36Sopenharmony_ci dev_err(dev, "Failed to set AB8505_RTC_PCUT_DEBOUNCE_REG\n"); 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_cifail: 289562306a36Sopenharmony_ci return count; 289662306a36Sopenharmony_ci} 289762306a36Sopenharmony_ci 289862306a36Sopenharmony_cistatic ssize_t ab8505_powercut_enable_status_read(struct device *dev, 289962306a36Sopenharmony_ci struct device_attribute *attr, 290062306a36Sopenharmony_ci char *buf) 290162306a36Sopenharmony_ci{ 290262306a36Sopenharmony_ci int ret; 290362306a36Sopenharmony_ci u8 reg_value; 290462306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 290562306a36Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 290862306a36Sopenharmony_ci AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci if (ret < 0) { 291162306a36Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n"); 291262306a36Sopenharmony_ci goto fail; 291362306a36Sopenharmony_ci } 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", ((reg_value & 0x20) >> 5)); 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_cifail: 291862306a36Sopenharmony_ci return ret; 291962306a36Sopenharmony_ci} 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_cistatic struct device_attribute ab8505_fg_sysfs_psy_attrs[] = { 292262306a36Sopenharmony_ci __ATTR(powercut_flagtime, (S_IRUGO | S_IWUSR | S_IWGRP), 292362306a36Sopenharmony_ci ab8505_powercut_flagtime_read, ab8505_powercut_flagtime_write), 292462306a36Sopenharmony_ci __ATTR(powercut_maxtime, (S_IRUGO | S_IWUSR | S_IWGRP), 292562306a36Sopenharmony_ci ab8505_powercut_maxtime_read, ab8505_powercut_maxtime_write), 292662306a36Sopenharmony_ci __ATTR(powercut_restart_max, (S_IRUGO | S_IWUSR | S_IWGRP), 292762306a36Sopenharmony_ci ab8505_powercut_restart_read, ab8505_powercut_restart_write), 292862306a36Sopenharmony_ci __ATTR(powercut_timer, S_IRUGO, ab8505_powercut_timer_read, NULL), 292962306a36Sopenharmony_ci __ATTR(powercut_restart_counter, S_IRUGO, 293062306a36Sopenharmony_ci ab8505_powercut_restart_counter_read, NULL), 293162306a36Sopenharmony_ci __ATTR(powercut_enable, (S_IRUGO | S_IWUSR | S_IWGRP), 293262306a36Sopenharmony_ci ab8505_powercut_read, ab8505_powercut_write), 293362306a36Sopenharmony_ci __ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL), 293462306a36Sopenharmony_ci __ATTR(powercut_debounce_time, (S_IRUGO | S_IWUSR | S_IWGRP), 293562306a36Sopenharmony_ci ab8505_powercut_debounce_read, ab8505_powercut_debounce_write), 293662306a36Sopenharmony_ci __ATTR(powercut_enable_status, S_IRUGO, 293762306a36Sopenharmony_ci ab8505_powercut_enable_status_read, NULL), 293862306a36Sopenharmony_ci}; 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_cistatic int ab8500_fg_sysfs_psy_create_attrs(struct ab8500_fg *di) 294162306a36Sopenharmony_ci{ 294262306a36Sopenharmony_ci unsigned int i; 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci if (is_ab8505(di->parent)) { 294562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++) 294662306a36Sopenharmony_ci if (device_create_file(&di->fg_psy->dev, 294762306a36Sopenharmony_ci &ab8505_fg_sysfs_psy_attrs[i])) 294862306a36Sopenharmony_ci goto sysfs_psy_create_attrs_failed_ab8505; 294962306a36Sopenharmony_ci } 295062306a36Sopenharmony_ci return 0; 295162306a36Sopenharmony_cisysfs_psy_create_attrs_failed_ab8505: 295262306a36Sopenharmony_ci dev_err(&di->fg_psy->dev, "Failed creating sysfs psy attrs for ab8505.\n"); 295362306a36Sopenharmony_ci while (i--) 295462306a36Sopenharmony_ci device_remove_file(&di->fg_psy->dev, 295562306a36Sopenharmony_ci &ab8505_fg_sysfs_psy_attrs[i]); 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci return -EIO; 295862306a36Sopenharmony_ci} 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_cistatic void ab8500_fg_sysfs_psy_remove_attrs(struct ab8500_fg *di) 296162306a36Sopenharmony_ci{ 296262306a36Sopenharmony_ci unsigned int i; 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci if (is_ab8505(di->parent)) { 296562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++) 296662306a36Sopenharmony_ci (void)device_remove_file(&di->fg_psy->dev, 296762306a36Sopenharmony_ci &ab8505_fg_sysfs_psy_attrs[i]); 296862306a36Sopenharmony_ci } 296962306a36Sopenharmony_ci} 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_ci/* Exposure to the sysfs interface <<END>> */ 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_cistatic int __maybe_unused ab8500_fg_resume(struct device *dev) 297462306a36Sopenharmony_ci{ 297562306a36Sopenharmony_ci struct ab8500_fg *di = dev_get_drvdata(dev); 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_ci /* 297862306a36Sopenharmony_ci * Change state if we're not charging. If we're charging we will wake 297962306a36Sopenharmony_ci * up on the FG IRQ 298062306a36Sopenharmony_ci */ 298162306a36Sopenharmony_ci if (!di->flags.charging) { 298262306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_WAKEUP); 298362306a36Sopenharmony_ci queue_work(di->fg_wq, &di->fg_work); 298462306a36Sopenharmony_ci } 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci return 0; 298762306a36Sopenharmony_ci} 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_cistatic int __maybe_unused ab8500_fg_suspend(struct device *dev) 299062306a36Sopenharmony_ci{ 299162306a36Sopenharmony_ci struct ab8500_fg *di = dev_get_drvdata(dev); 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci flush_delayed_work(&di->fg_periodic_work); 299462306a36Sopenharmony_ci flush_work(&di->fg_work); 299562306a36Sopenharmony_ci flush_work(&di->fg_acc_cur_work); 299662306a36Sopenharmony_ci flush_delayed_work(&di->fg_reinit_work); 299762306a36Sopenharmony_ci flush_delayed_work(&di->fg_low_bat_work); 299862306a36Sopenharmony_ci flush_delayed_work(&di->fg_check_hw_failure_work); 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_ci /* 300162306a36Sopenharmony_ci * If the FG is enabled we will disable it before going to suspend 300262306a36Sopenharmony_ci * only if we're not charging 300362306a36Sopenharmony_ci */ 300462306a36Sopenharmony_ci if (di->flags.fg_enabled && !di->flags.charging) 300562306a36Sopenharmony_ci ab8500_fg_coulomb_counter(di, false); 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_ci return 0; 300862306a36Sopenharmony_ci} 300962306a36Sopenharmony_ci 301062306a36Sopenharmony_ci/* ab8500 fg driver interrupts and their respective isr */ 301162306a36Sopenharmony_cistatic struct ab8500_fg_interrupts ab8500_fg_irq[] = { 301262306a36Sopenharmony_ci {"NCONV_ACCU", ab8500_fg_cc_convend_handler}, 301362306a36Sopenharmony_ci {"BATT_OVV", ab8500_fg_batt_ovv_handler}, 301462306a36Sopenharmony_ci {"LOW_BAT_F", ab8500_fg_lowbatf_handler}, 301562306a36Sopenharmony_ci {"CC_INT_CALIB", ab8500_fg_cc_int_calib_handler}, 301662306a36Sopenharmony_ci {"CCEOC", ab8500_fg_cc_data_end_handler}, 301762306a36Sopenharmony_ci}; 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_cistatic char *supply_interface[] = { 302062306a36Sopenharmony_ci "ab8500_chargalg", 302162306a36Sopenharmony_ci "ab8500_usb", 302262306a36Sopenharmony_ci}; 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_cistatic const struct power_supply_desc ab8500_fg_desc = { 302562306a36Sopenharmony_ci .name = "ab8500_fg", 302662306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 302762306a36Sopenharmony_ci .properties = ab8500_fg_props, 302862306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(ab8500_fg_props), 302962306a36Sopenharmony_ci .get_property = ab8500_fg_get_property, 303062306a36Sopenharmony_ci .external_power_changed = ab8500_fg_external_power_changed, 303162306a36Sopenharmony_ci}; 303262306a36Sopenharmony_ci 303362306a36Sopenharmony_cistatic int ab8500_fg_bind(struct device *dev, struct device *master, 303462306a36Sopenharmony_ci void *data) 303562306a36Sopenharmony_ci{ 303662306a36Sopenharmony_ci struct ab8500_fg *di = dev_get_drvdata(dev); 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ci di->bat_cap.max_mah_design = di->bm->bi->charge_full_design_uah; 303962306a36Sopenharmony_ci di->bat_cap.max_mah = di->bat_cap.max_mah_design; 304062306a36Sopenharmony_ci di->vbat_nom_uv = di->bm->bi->voltage_max_design_uv; 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci /* Start the coulomb counter */ 304362306a36Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 304462306a36Sopenharmony_ci /* Run the FG algorithm */ 304562306a36Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_ci return 0; 304862306a36Sopenharmony_ci} 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_cistatic void ab8500_fg_unbind(struct device *dev, struct device *master, 305162306a36Sopenharmony_ci void *data) 305262306a36Sopenharmony_ci{ 305362306a36Sopenharmony_ci struct ab8500_fg *di = dev_get_drvdata(dev); 305462306a36Sopenharmony_ci int ret; 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci /* Disable coulomb counter */ 305762306a36Sopenharmony_ci ret = ab8500_fg_coulomb_counter(di, false); 305862306a36Sopenharmony_ci if (ret) 305962306a36Sopenharmony_ci dev_err(dev, "failed to disable coulomb counter\n"); 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ci flush_workqueue(di->fg_wq); 306262306a36Sopenharmony_ci} 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_cistatic const struct component_ops ab8500_fg_component_ops = { 306562306a36Sopenharmony_ci .bind = ab8500_fg_bind, 306662306a36Sopenharmony_ci .unbind = ab8500_fg_unbind, 306762306a36Sopenharmony_ci}; 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_cistatic int ab8500_fg_probe(struct platform_device *pdev) 307062306a36Sopenharmony_ci{ 307162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 307262306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 307362306a36Sopenharmony_ci struct ab8500_fg *di; 307462306a36Sopenharmony_ci int i, irq; 307562306a36Sopenharmony_ci int ret = 0; 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); 307862306a36Sopenharmony_ci if (!di) 307962306a36Sopenharmony_ci return -ENOMEM; 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci di->bm = &ab8500_bm_data; 308262306a36Sopenharmony_ci 308362306a36Sopenharmony_ci mutex_init(&di->cc_lock); 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_ci /* get parent data */ 308662306a36Sopenharmony_ci di->dev = dev; 308762306a36Sopenharmony_ci di->parent = dev_get_drvdata(pdev->dev.parent); 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_ci di->main_bat_v = devm_iio_channel_get(dev, "main_bat_v"); 309062306a36Sopenharmony_ci if (IS_ERR(di->main_bat_v)) { 309162306a36Sopenharmony_ci ret = dev_err_probe(dev, PTR_ERR(di->main_bat_v), 309262306a36Sopenharmony_ci "failed to get main battery ADC channel\n"); 309362306a36Sopenharmony_ci return ret; 309462306a36Sopenharmony_ci } 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "line-impedance-micro-ohms", 309762306a36Sopenharmony_ci &di->line_impedance_uohm)) 309862306a36Sopenharmony_ci dev_info(dev, "line impedance: %u uOhm\n", 309962306a36Sopenharmony_ci di->line_impedance_uohm); 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci psy_cfg.supplied_to = supply_interface; 310262306a36Sopenharmony_ci psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); 310362306a36Sopenharmony_ci psy_cfg.drv_data = di; 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci di->init_capacity = true; 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_ci ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT); 310862306a36Sopenharmony_ci ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT); 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci /* Create a work queue for running the FG algorithm */ 311162306a36Sopenharmony_ci di->fg_wq = alloc_ordered_workqueue("ab8500_fg_wq", WQ_MEM_RECLAIM); 311262306a36Sopenharmony_ci if (di->fg_wq == NULL) { 311362306a36Sopenharmony_ci dev_err(dev, "failed to create work queue\n"); 311462306a36Sopenharmony_ci return -ENOMEM; 311562306a36Sopenharmony_ci } 311662306a36Sopenharmony_ci 311762306a36Sopenharmony_ci /* Init work for running the fg algorithm instantly */ 311862306a36Sopenharmony_ci INIT_WORK(&di->fg_work, ab8500_fg_instant_work); 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci /* Init work for getting the battery accumulated current */ 312162306a36Sopenharmony_ci INIT_WORK(&di->fg_acc_cur_work, ab8500_fg_acc_cur_work); 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci /* Init work for reinitialising the fg algorithm */ 312462306a36Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->fg_reinit_work, 312562306a36Sopenharmony_ci ab8500_fg_reinit_work); 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_ci /* Work delayed Queue to run the state machine */ 312862306a36Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->fg_periodic_work, 312962306a36Sopenharmony_ci ab8500_fg_periodic_work); 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci /* Work to check low battery condition */ 313262306a36Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->fg_low_bat_work, 313362306a36Sopenharmony_ci ab8500_fg_low_bat_work); 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci /* Init work for HW failure check */ 313662306a36Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->fg_check_hw_failure_work, 313762306a36Sopenharmony_ci ab8500_fg_check_hw_failure_work); 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_ci /* Reset battery low voltage flag */ 314062306a36Sopenharmony_ci di->flags.low_bat = false; 314162306a36Sopenharmony_ci 314262306a36Sopenharmony_ci /* Initialize low battery counter */ 314362306a36Sopenharmony_ci di->low_bat_cnt = 10; 314462306a36Sopenharmony_ci 314562306a36Sopenharmony_ci /* Initialize OVV, and other registers */ 314662306a36Sopenharmony_ci ret = ab8500_fg_init_hw_registers(di); 314762306a36Sopenharmony_ci if (ret) { 314862306a36Sopenharmony_ci dev_err(dev, "failed to initialize registers\n"); 314962306a36Sopenharmony_ci destroy_workqueue(di->fg_wq); 315062306a36Sopenharmony_ci return ret; 315162306a36Sopenharmony_ci } 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ci /* Consider battery unknown until we're informed otherwise */ 315462306a36Sopenharmony_ci di->flags.batt_unknown = true; 315562306a36Sopenharmony_ci di->flags.batt_id_received = false; 315662306a36Sopenharmony_ci 315762306a36Sopenharmony_ci /* Register FG power supply class */ 315862306a36Sopenharmony_ci di->fg_psy = devm_power_supply_register(dev, &ab8500_fg_desc, &psy_cfg); 315962306a36Sopenharmony_ci if (IS_ERR(di->fg_psy)) { 316062306a36Sopenharmony_ci dev_err(dev, "failed to register FG psy\n"); 316162306a36Sopenharmony_ci destroy_workqueue(di->fg_wq); 316262306a36Sopenharmony_ci return PTR_ERR(di->fg_psy); 316362306a36Sopenharmony_ci } 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer); 316662306a36Sopenharmony_ci 316762306a36Sopenharmony_ci /* 316862306a36Sopenharmony_ci * Initialize completion used to notify completion and start 316962306a36Sopenharmony_ci * of inst current 317062306a36Sopenharmony_ci */ 317162306a36Sopenharmony_ci init_completion(&di->ab8500_fg_started); 317262306a36Sopenharmony_ci init_completion(&di->ab8500_fg_complete); 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci /* Register primary interrupt handlers */ 317562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq); i++) { 317662306a36Sopenharmony_ci irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name); 317762306a36Sopenharmony_ci if (irq < 0) { 317862306a36Sopenharmony_ci destroy_workqueue(di->fg_wq); 317962306a36Sopenharmony_ci return irq; 318062306a36Sopenharmony_ci } 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, irq, NULL, 318362306a36Sopenharmony_ci ab8500_fg_irq[i].isr, 318462306a36Sopenharmony_ci IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, 318562306a36Sopenharmony_ci ab8500_fg_irq[i].name, di); 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci if (ret != 0) { 318862306a36Sopenharmony_ci dev_err(dev, "failed to request %s IRQ %d: %d\n", 318962306a36Sopenharmony_ci ab8500_fg_irq[i].name, irq, ret); 319062306a36Sopenharmony_ci destroy_workqueue(di->fg_wq); 319162306a36Sopenharmony_ci return ret; 319262306a36Sopenharmony_ci } 319362306a36Sopenharmony_ci dev_dbg(dev, "Requested %s IRQ %d: %d\n", 319462306a36Sopenharmony_ci ab8500_fg_irq[i].name, irq, ret); 319562306a36Sopenharmony_ci } 319662306a36Sopenharmony_ci 319762306a36Sopenharmony_ci di->irq = platform_get_irq_byname(pdev, "CCEOC"); 319862306a36Sopenharmony_ci disable_irq(di->irq); 319962306a36Sopenharmony_ci di->nbr_cceoc_irq_cnt = 0; 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci platform_set_drvdata(pdev, di); 320262306a36Sopenharmony_ci 320362306a36Sopenharmony_ci ret = ab8500_fg_sysfs_init(di); 320462306a36Sopenharmony_ci if (ret) { 320562306a36Sopenharmony_ci dev_err(dev, "failed to create sysfs entry\n"); 320662306a36Sopenharmony_ci destroy_workqueue(di->fg_wq); 320762306a36Sopenharmony_ci return ret; 320862306a36Sopenharmony_ci } 320962306a36Sopenharmony_ci 321062306a36Sopenharmony_ci ret = ab8500_fg_sysfs_psy_create_attrs(di); 321162306a36Sopenharmony_ci if (ret) { 321262306a36Sopenharmony_ci dev_err(dev, "failed to create FG psy\n"); 321362306a36Sopenharmony_ci ab8500_fg_sysfs_exit(di); 321462306a36Sopenharmony_ci destroy_workqueue(di->fg_wq); 321562306a36Sopenharmony_ci return ret; 321662306a36Sopenharmony_ci } 321762306a36Sopenharmony_ci 321862306a36Sopenharmony_ci /* Calibrate the fg first time */ 321962306a36Sopenharmony_ci di->flags.calibrate = true; 322062306a36Sopenharmony_ci di->calib_state = AB8500_FG_CALIB_INIT; 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ci /* Use room temp as default value until we get an update from driver. */ 322362306a36Sopenharmony_ci di->bat_temp = 210; 322462306a36Sopenharmony_ci 322562306a36Sopenharmony_ci list_add_tail(&di->node, &ab8500_fg_list); 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_ci return component_add(dev, &ab8500_fg_component_ops); 322862306a36Sopenharmony_ci} 322962306a36Sopenharmony_ci 323062306a36Sopenharmony_cistatic int ab8500_fg_remove(struct platform_device *pdev) 323162306a36Sopenharmony_ci{ 323262306a36Sopenharmony_ci struct ab8500_fg *di = platform_get_drvdata(pdev); 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci destroy_workqueue(di->fg_wq); 323562306a36Sopenharmony_ci component_del(&pdev->dev, &ab8500_fg_component_ops); 323662306a36Sopenharmony_ci list_del(&di->node); 323762306a36Sopenharmony_ci ab8500_fg_sysfs_exit(di); 323862306a36Sopenharmony_ci ab8500_fg_sysfs_psy_remove_attrs(di); 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci return 0; 324162306a36Sopenharmony_ci} 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ab8500_fg_pm_ops, ab8500_fg_suspend, ab8500_fg_resume); 324462306a36Sopenharmony_ci 324562306a36Sopenharmony_cistatic const struct of_device_id ab8500_fg_match[] = { 324662306a36Sopenharmony_ci { .compatible = "stericsson,ab8500-fg", }, 324762306a36Sopenharmony_ci { }, 324862306a36Sopenharmony_ci}; 324962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ab8500_fg_match); 325062306a36Sopenharmony_ci 325162306a36Sopenharmony_cistruct platform_driver ab8500_fg_driver = { 325262306a36Sopenharmony_ci .probe = ab8500_fg_probe, 325362306a36Sopenharmony_ci .remove = ab8500_fg_remove, 325462306a36Sopenharmony_ci .driver = { 325562306a36Sopenharmony_ci .name = "ab8500-fg", 325662306a36Sopenharmony_ci .of_match_table = ab8500_fg_match, 325762306a36Sopenharmony_ci .pm = &ab8500_fg_pm_ops, 325862306a36Sopenharmony_ci }, 325962306a36Sopenharmony_ci}; 326062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 326162306a36Sopenharmony_ciMODULE_AUTHOR("Johan Palsson, Karl Komierowski"); 326262306a36Sopenharmony_ciMODULE_ALIAS("platform:ab8500-fg"); 326362306a36Sopenharmony_ciMODULE_DESCRIPTION("AB8500 Fuel Gauge driver"); 3264