18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2012 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Main and Back-up battery management driver. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Note: Backup battery management is required in case of Li-Ion battery and not 88c2ecf20Sopenharmony_ci * for capacitive battery. HREF boards have capacitive battery and hence backup 98c2ecf20Sopenharmony_ci * battery management is not used and the supported code is available in this 108c2ecf20Sopenharmony_ci * driver. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Author: 138c2ecf20Sopenharmony_ci * Johan Palsson <johan.palsson@stericsson.com> 148c2ecf20Sopenharmony_ci * Karl Komierowski <karl.komierowski@stericsson.com> 158c2ecf20Sopenharmony_ci * Arun R Murthy <arun.murthy@stericsson.com> 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/device.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 248c2ecf20Sopenharmony_ci#include <linux/kobject.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/delay.h> 278c2ecf20Sopenharmony_ci#include <linux/time.h> 288c2ecf20Sopenharmony_ci#include <linux/time64.h> 298c2ecf20Sopenharmony_ci#include <linux/of.h> 308c2ecf20Sopenharmony_ci#include <linux/completion.h> 318c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 328c2ecf20Sopenharmony_ci#include <linux/mfd/abx500.h> 338c2ecf20Sopenharmony_ci#include <linux/mfd/abx500/ab8500.h> 348c2ecf20Sopenharmony_ci#include <linux/mfd/abx500/ab8500-bm.h> 358c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h> 368c2ecf20Sopenharmony_ci#include <linux/kernel.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define MILLI_TO_MICRO 1000 398c2ecf20Sopenharmony_ci#define FG_LSB_IN_MA 1627 408c2ecf20Sopenharmony_ci#define QLSB_NANO_AMP_HOURS_X10 1071 418c2ecf20Sopenharmony_ci#define INS_CURR_TIMEOUT (3 * HZ) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define SEC_TO_SAMPLE(S) (S * 4) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define NBR_AVG_SAMPLES 20 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define LOW_BAT_CHECK_INTERVAL (HZ / 16) /* 62.5 ms */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define VALID_CAPACITY_SEC (45 * 60) /* 45 minutes */ 508c2ecf20Sopenharmony_ci#define BATT_OK_MIN 2360 /* mV */ 518c2ecf20Sopenharmony_ci#define BATT_OK_INCREMENT 50 /* mV */ 528c2ecf20Sopenharmony_ci#define BATT_OK_MAX_NR_INCREMENTS 0xE 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* FG constants */ 558c2ecf20Sopenharmony_ci#define BATT_OVV 0x01 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define interpolate(x, x1, y1, x2, y2) \ 588c2ecf20Sopenharmony_ci ((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1)))); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/** 618c2ecf20Sopenharmony_ci * struct ab8500_fg_interrupts - ab8500 fg interupts 628c2ecf20Sopenharmony_ci * @name: name of the interrupt 638c2ecf20Sopenharmony_ci * @isr function pointer to the isr 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistruct ab8500_fg_interrupts { 668c2ecf20Sopenharmony_ci char *name; 678c2ecf20Sopenharmony_ci irqreturn_t (*isr)(int irq, void *data); 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cienum ab8500_fg_discharge_state { 718c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_INIT, 728c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_INITMEASURING, 738c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_INIT_RECOVERY, 748c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_RECOVERY, 758c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT_INIT, 768c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT, 778c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_WAKEUP, 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic char *discharge_state[] = { 818c2ecf20Sopenharmony_ci "DISCHARGE_INIT", 828c2ecf20Sopenharmony_ci "DISCHARGE_INITMEASURING", 838c2ecf20Sopenharmony_ci "DISCHARGE_INIT_RECOVERY", 848c2ecf20Sopenharmony_ci "DISCHARGE_RECOVERY", 858c2ecf20Sopenharmony_ci "DISCHARGE_READOUT_INIT", 868c2ecf20Sopenharmony_ci "DISCHARGE_READOUT", 878c2ecf20Sopenharmony_ci "DISCHARGE_WAKEUP", 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cienum ab8500_fg_charge_state { 918c2ecf20Sopenharmony_ci AB8500_FG_CHARGE_INIT, 928c2ecf20Sopenharmony_ci AB8500_FG_CHARGE_READOUT, 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic char *charge_state[] = { 968c2ecf20Sopenharmony_ci "CHARGE_INIT", 978c2ecf20Sopenharmony_ci "CHARGE_READOUT", 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cienum ab8500_fg_calibration_state { 1018c2ecf20Sopenharmony_ci AB8500_FG_CALIB_INIT, 1028c2ecf20Sopenharmony_ci AB8500_FG_CALIB_WAIT, 1038c2ecf20Sopenharmony_ci AB8500_FG_CALIB_END, 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistruct ab8500_fg_avg_cap { 1078c2ecf20Sopenharmony_ci int avg; 1088c2ecf20Sopenharmony_ci int samples[NBR_AVG_SAMPLES]; 1098c2ecf20Sopenharmony_ci time64_t time_stamps[NBR_AVG_SAMPLES]; 1108c2ecf20Sopenharmony_ci int pos; 1118c2ecf20Sopenharmony_ci int nbr_samples; 1128c2ecf20Sopenharmony_ci int sum; 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistruct ab8500_fg_cap_scaling { 1168c2ecf20Sopenharmony_ci bool enable; 1178c2ecf20Sopenharmony_ci int cap_to_scale[2]; 1188c2ecf20Sopenharmony_ci int disable_cap_level; 1198c2ecf20Sopenharmony_ci int scaled_cap; 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistruct ab8500_fg_battery_capacity { 1238c2ecf20Sopenharmony_ci int max_mah_design; 1248c2ecf20Sopenharmony_ci int max_mah; 1258c2ecf20Sopenharmony_ci int mah; 1268c2ecf20Sopenharmony_ci int permille; 1278c2ecf20Sopenharmony_ci int level; 1288c2ecf20Sopenharmony_ci int prev_mah; 1298c2ecf20Sopenharmony_ci int prev_percent; 1308c2ecf20Sopenharmony_ci int prev_level; 1318c2ecf20Sopenharmony_ci int user_mah; 1328c2ecf20Sopenharmony_ci struct ab8500_fg_cap_scaling cap_scale; 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistruct ab8500_fg_flags { 1368c2ecf20Sopenharmony_ci bool fg_enabled; 1378c2ecf20Sopenharmony_ci bool conv_done; 1388c2ecf20Sopenharmony_ci bool charging; 1398c2ecf20Sopenharmony_ci bool fully_charged; 1408c2ecf20Sopenharmony_ci bool force_full; 1418c2ecf20Sopenharmony_ci bool low_bat_delay; 1428c2ecf20Sopenharmony_ci bool low_bat; 1438c2ecf20Sopenharmony_ci bool bat_ovv; 1448c2ecf20Sopenharmony_ci bool batt_unknown; 1458c2ecf20Sopenharmony_ci bool calibrate; 1468c2ecf20Sopenharmony_ci bool user_cap; 1478c2ecf20Sopenharmony_ci bool batt_id_received; 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistruct inst_curr_result_list { 1518c2ecf20Sopenharmony_ci struct list_head list; 1528c2ecf20Sopenharmony_ci int *result; 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/** 1568c2ecf20Sopenharmony_ci * struct ab8500_fg - ab8500 FG device information 1578c2ecf20Sopenharmony_ci * @dev: Pointer to the structure device 1588c2ecf20Sopenharmony_ci * @node: a list of AB8500 FGs, hence prepared for reentrance 1598c2ecf20Sopenharmony_ci * @irq holds the CCEOC interrupt number 1608c2ecf20Sopenharmony_ci * @vbat: Battery voltage in mV 1618c2ecf20Sopenharmony_ci * @vbat_nom: Nominal battery voltage in mV 1628c2ecf20Sopenharmony_ci * @inst_curr: Instantenous battery current in mA 1638c2ecf20Sopenharmony_ci * @avg_curr: Average battery current in mA 1648c2ecf20Sopenharmony_ci * @bat_temp battery temperature 1658c2ecf20Sopenharmony_ci * @fg_samples: Number of samples used in the FG accumulation 1668c2ecf20Sopenharmony_ci * @accu_charge: Accumulated charge from the last conversion 1678c2ecf20Sopenharmony_ci * @recovery_cnt: Counter for recovery mode 1688c2ecf20Sopenharmony_ci * @high_curr_cnt: Counter for high current mode 1698c2ecf20Sopenharmony_ci * @init_cnt: Counter for init mode 1708c2ecf20Sopenharmony_ci * @low_bat_cnt Counter for number of consecutive low battery measures 1718c2ecf20Sopenharmony_ci * @nbr_cceoc_irq_cnt Counter for number of CCEOC irqs received since enabled 1728c2ecf20Sopenharmony_ci * @recovery_needed: Indicate if recovery is needed 1738c2ecf20Sopenharmony_ci * @high_curr_mode: Indicate if we're in high current mode 1748c2ecf20Sopenharmony_ci * @init_capacity: Indicate if initial capacity measuring should be done 1758c2ecf20Sopenharmony_ci * @turn_off_fg: True if fg was off before current measurement 1768c2ecf20Sopenharmony_ci * @calib_state State during offset calibration 1778c2ecf20Sopenharmony_ci * @discharge_state: Current discharge state 1788c2ecf20Sopenharmony_ci * @charge_state: Current charge state 1798c2ecf20Sopenharmony_ci * @ab8500_fg_started Completion struct used for the instant current start 1808c2ecf20Sopenharmony_ci * @ab8500_fg_complete Completion struct used for the instant current reading 1818c2ecf20Sopenharmony_ci * @flags: Structure for information about events triggered 1828c2ecf20Sopenharmony_ci * @bat_cap: Structure for battery capacity specific parameters 1838c2ecf20Sopenharmony_ci * @avg_cap: Average capacity filter 1848c2ecf20Sopenharmony_ci * @parent: Pointer to the struct ab8500 1858c2ecf20Sopenharmony_ci * @main_bat_v: ADC channel for the main battery voltage 1868c2ecf20Sopenharmony_ci * @bm: Platform specific battery management information 1878c2ecf20Sopenharmony_ci * @fg_psy: Structure that holds the FG specific battery properties 1888c2ecf20Sopenharmony_ci * @fg_wq: Work queue for running the FG algorithm 1898c2ecf20Sopenharmony_ci * @fg_periodic_work: Work to run the FG algorithm periodically 1908c2ecf20Sopenharmony_ci * @fg_low_bat_work: Work to check low bat condition 1918c2ecf20Sopenharmony_ci * @fg_reinit_work Work used to reset and reinitialise the FG algorithm 1928c2ecf20Sopenharmony_ci * @fg_work: Work to run the FG algorithm instantly 1938c2ecf20Sopenharmony_ci * @fg_acc_cur_work: Work to read the FG accumulator 1948c2ecf20Sopenharmony_ci * @fg_check_hw_failure_work: Work for checking HW state 1958c2ecf20Sopenharmony_ci * @cc_lock: Mutex for locking the CC 1968c2ecf20Sopenharmony_ci * @fg_kobject: Structure of type kobject 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_cistruct ab8500_fg { 1998c2ecf20Sopenharmony_ci struct device *dev; 2008c2ecf20Sopenharmony_ci struct list_head node; 2018c2ecf20Sopenharmony_ci int irq; 2028c2ecf20Sopenharmony_ci int vbat; 2038c2ecf20Sopenharmony_ci int vbat_nom; 2048c2ecf20Sopenharmony_ci int inst_curr; 2058c2ecf20Sopenharmony_ci int avg_curr; 2068c2ecf20Sopenharmony_ci int bat_temp; 2078c2ecf20Sopenharmony_ci int fg_samples; 2088c2ecf20Sopenharmony_ci int accu_charge; 2098c2ecf20Sopenharmony_ci int recovery_cnt; 2108c2ecf20Sopenharmony_ci int high_curr_cnt; 2118c2ecf20Sopenharmony_ci int init_cnt; 2128c2ecf20Sopenharmony_ci int low_bat_cnt; 2138c2ecf20Sopenharmony_ci int nbr_cceoc_irq_cnt; 2148c2ecf20Sopenharmony_ci bool recovery_needed; 2158c2ecf20Sopenharmony_ci bool high_curr_mode; 2168c2ecf20Sopenharmony_ci bool init_capacity; 2178c2ecf20Sopenharmony_ci bool turn_off_fg; 2188c2ecf20Sopenharmony_ci enum ab8500_fg_calibration_state calib_state; 2198c2ecf20Sopenharmony_ci enum ab8500_fg_discharge_state discharge_state; 2208c2ecf20Sopenharmony_ci enum ab8500_fg_charge_state charge_state; 2218c2ecf20Sopenharmony_ci struct completion ab8500_fg_started; 2228c2ecf20Sopenharmony_ci struct completion ab8500_fg_complete; 2238c2ecf20Sopenharmony_ci struct ab8500_fg_flags flags; 2248c2ecf20Sopenharmony_ci struct ab8500_fg_battery_capacity bat_cap; 2258c2ecf20Sopenharmony_ci struct ab8500_fg_avg_cap avg_cap; 2268c2ecf20Sopenharmony_ci struct ab8500 *parent; 2278c2ecf20Sopenharmony_ci struct iio_channel *main_bat_v; 2288c2ecf20Sopenharmony_ci struct abx500_bm_data *bm; 2298c2ecf20Sopenharmony_ci struct power_supply *fg_psy; 2308c2ecf20Sopenharmony_ci struct workqueue_struct *fg_wq; 2318c2ecf20Sopenharmony_ci struct delayed_work fg_periodic_work; 2328c2ecf20Sopenharmony_ci struct delayed_work fg_low_bat_work; 2338c2ecf20Sopenharmony_ci struct delayed_work fg_reinit_work; 2348c2ecf20Sopenharmony_ci struct work_struct fg_work; 2358c2ecf20Sopenharmony_ci struct work_struct fg_acc_cur_work; 2368c2ecf20Sopenharmony_ci struct delayed_work fg_check_hw_failure_work; 2378c2ecf20Sopenharmony_ci struct mutex cc_lock; 2388c2ecf20Sopenharmony_ci struct kobject fg_kobject; 2398c2ecf20Sopenharmony_ci}; 2408c2ecf20Sopenharmony_cistatic LIST_HEAD(ab8500_fg_list); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/** 2438c2ecf20Sopenharmony_ci * ab8500_fg_get() - returns a reference to the primary AB8500 fuel gauge 2448c2ecf20Sopenharmony_ci * (i.e. the first fuel gauge in the instance list) 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_cistruct ab8500_fg *ab8500_fg_get(void) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci return list_first_entry_or_null(&ab8500_fg_list, struct ab8500_fg, 2498c2ecf20Sopenharmony_ci node); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/* Main battery properties */ 2538c2ecf20Sopenharmony_cistatic enum power_supply_property ab8500_fg_props[] = { 2548c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 2558c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 2568c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_AVG, 2578c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 2588c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_FULL, 2598c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ENERGY_NOW, 2608c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 2618c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_FULL, 2628c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_NOW, 2638c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY, 2648c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CAPACITY_LEVEL, 2658c2ecf20Sopenharmony_ci}; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* 2688c2ecf20Sopenharmony_ci * This array maps the raw hex value to lowbat voltage used by the AB8500 2698c2ecf20Sopenharmony_ci * Values taken from the UM0836 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_cistatic int ab8500_fg_lowbat_voltage_map[] = { 2728c2ecf20Sopenharmony_ci 2300 , 2738c2ecf20Sopenharmony_ci 2325 , 2748c2ecf20Sopenharmony_ci 2350 , 2758c2ecf20Sopenharmony_ci 2375 , 2768c2ecf20Sopenharmony_ci 2400 , 2778c2ecf20Sopenharmony_ci 2425 , 2788c2ecf20Sopenharmony_ci 2450 , 2798c2ecf20Sopenharmony_ci 2475 , 2808c2ecf20Sopenharmony_ci 2500 , 2818c2ecf20Sopenharmony_ci 2525 , 2828c2ecf20Sopenharmony_ci 2550 , 2838c2ecf20Sopenharmony_ci 2575 , 2848c2ecf20Sopenharmony_ci 2600 , 2858c2ecf20Sopenharmony_ci 2625 , 2868c2ecf20Sopenharmony_ci 2650 , 2878c2ecf20Sopenharmony_ci 2675 , 2888c2ecf20Sopenharmony_ci 2700 , 2898c2ecf20Sopenharmony_ci 2725 , 2908c2ecf20Sopenharmony_ci 2750 , 2918c2ecf20Sopenharmony_ci 2775 , 2928c2ecf20Sopenharmony_ci 2800 , 2938c2ecf20Sopenharmony_ci 2825 , 2948c2ecf20Sopenharmony_ci 2850 , 2958c2ecf20Sopenharmony_ci 2875 , 2968c2ecf20Sopenharmony_ci 2900 , 2978c2ecf20Sopenharmony_ci 2925 , 2988c2ecf20Sopenharmony_ci 2950 , 2998c2ecf20Sopenharmony_ci 2975 , 3008c2ecf20Sopenharmony_ci 3000 , 3018c2ecf20Sopenharmony_ci 3025 , 3028c2ecf20Sopenharmony_ci 3050 , 3038c2ecf20Sopenharmony_ci 3075 , 3048c2ecf20Sopenharmony_ci 3100 , 3058c2ecf20Sopenharmony_ci 3125 , 3068c2ecf20Sopenharmony_ci 3150 , 3078c2ecf20Sopenharmony_ci 3175 , 3088c2ecf20Sopenharmony_ci 3200 , 3098c2ecf20Sopenharmony_ci 3225 , 3108c2ecf20Sopenharmony_ci 3250 , 3118c2ecf20Sopenharmony_ci 3275 , 3128c2ecf20Sopenharmony_ci 3300 , 3138c2ecf20Sopenharmony_ci 3325 , 3148c2ecf20Sopenharmony_ci 3350 , 3158c2ecf20Sopenharmony_ci 3375 , 3168c2ecf20Sopenharmony_ci 3400 , 3178c2ecf20Sopenharmony_ci 3425 , 3188c2ecf20Sopenharmony_ci 3450 , 3198c2ecf20Sopenharmony_ci 3475 , 3208c2ecf20Sopenharmony_ci 3500 , 3218c2ecf20Sopenharmony_ci 3525 , 3228c2ecf20Sopenharmony_ci 3550 , 3238c2ecf20Sopenharmony_ci 3575 , 3248c2ecf20Sopenharmony_ci 3600 , 3258c2ecf20Sopenharmony_ci 3625 , 3268c2ecf20Sopenharmony_ci 3650 , 3278c2ecf20Sopenharmony_ci 3675 , 3288c2ecf20Sopenharmony_ci 3700 , 3298c2ecf20Sopenharmony_ci 3725 , 3308c2ecf20Sopenharmony_ci 3750 , 3318c2ecf20Sopenharmony_ci 3775 , 3328c2ecf20Sopenharmony_ci 3800 , 3338c2ecf20Sopenharmony_ci 3825 , 3348c2ecf20Sopenharmony_ci 3850 , 3358c2ecf20Sopenharmony_ci 3850 , 3368c2ecf20Sopenharmony_ci}; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic u8 ab8500_volt_to_regval(int voltage) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci int i; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (voltage < ab8500_fg_lowbat_voltage_map[0]) 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ab8500_fg_lowbat_voltage_map); i++) { 3468c2ecf20Sopenharmony_ci if (voltage < ab8500_fg_lowbat_voltage_map[i]) 3478c2ecf20Sopenharmony_ci return (u8) i - 1; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* If not captured above, return index of last element */ 3518c2ecf20Sopenharmony_ci return (u8) ARRAY_SIZE(ab8500_fg_lowbat_voltage_map) - 1; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/** 3558c2ecf20Sopenharmony_ci * ab8500_fg_is_low_curr() - Low or high current mode 3568c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 3578c2ecf20Sopenharmony_ci * @curr: the current to base or our decision on 3588c2ecf20Sopenharmony_ci * 3598c2ecf20Sopenharmony_ci * Low current mode if the current consumption is below a certain threshold 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_cistatic int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci /* 3648c2ecf20Sopenharmony_ci * We want to know if we're in low current mode 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_ci if (curr > -di->bm->fg_params->high_curr_threshold) 3678c2ecf20Sopenharmony_ci return true; 3688c2ecf20Sopenharmony_ci else 3698c2ecf20Sopenharmony_ci return false; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci/** 3738c2ecf20Sopenharmony_ci * ab8500_fg_add_cap_sample() - Add capacity to average filter 3748c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 3758c2ecf20Sopenharmony_ci * @sample: the capacity in mAh to add to the filter 3768c2ecf20Sopenharmony_ci * 3778c2ecf20Sopenharmony_ci * A capacity is added to the filter and a new mean capacity is calculated and 3788c2ecf20Sopenharmony_ci * returned 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_cistatic int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci time64_t now = ktime_get_boottime_seconds(); 3838c2ecf20Sopenharmony_ci struct ab8500_fg_avg_cap *avg = &di->avg_cap; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci do { 3868c2ecf20Sopenharmony_ci avg->sum += sample - avg->samples[avg->pos]; 3878c2ecf20Sopenharmony_ci avg->samples[avg->pos] = sample; 3888c2ecf20Sopenharmony_ci avg->time_stamps[avg->pos] = now; 3898c2ecf20Sopenharmony_ci avg->pos++; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (avg->pos == NBR_AVG_SAMPLES) 3928c2ecf20Sopenharmony_ci avg->pos = 0; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (avg->nbr_samples < NBR_AVG_SAMPLES) 3958c2ecf20Sopenharmony_ci avg->nbr_samples++; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* 3988c2ecf20Sopenharmony_ci * Check the time stamp for each sample. If too old, 3998c2ecf20Sopenharmony_ci * replace with latest sample 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci } while (now - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci avg->avg = avg->sum / avg->nbr_samples; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return avg->avg; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci/** 4098c2ecf20Sopenharmony_ci * ab8500_fg_clear_cap_samples() - Clear average filter 4108c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 4118c2ecf20Sopenharmony_ci * 4128c2ecf20Sopenharmony_ci * The capacity filter is is reset to zero. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_cistatic void ab8500_fg_clear_cap_samples(struct ab8500_fg *di) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci int i; 4178c2ecf20Sopenharmony_ci struct ab8500_fg_avg_cap *avg = &di->avg_cap; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci avg->pos = 0; 4208c2ecf20Sopenharmony_ci avg->nbr_samples = 0; 4218c2ecf20Sopenharmony_ci avg->sum = 0; 4228c2ecf20Sopenharmony_ci avg->avg = 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci for (i = 0; i < NBR_AVG_SAMPLES; i++) { 4258c2ecf20Sopenharmony_ci avg->samples[i] = 0; 4268c2ecf20Sopenharmony_ci avg->time_stamps[i] = 0; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci/** 4318c2ecf20Sopenharmony_ci * ab8500_fg_fill_cap_sample() - Fill average filter 4328c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 4338c2ecf20Sopenharmony_ci * @sample: the capacity in mAh to fill the filter with 4348c2ecf20Sopenharmony_ci * 4358c2ecf20Sopenharmony_ci * The capacity filter is filled with a capacity in mAh 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_cistatic void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci int i; 4408c2ecf20Sopenharmony_ci time64_t now; 4418c2ecf20Sopenharmony_ci struct ab8500_fg_avg_cap *avg = &di->avg_cap; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci now = ktime_get_boottime_seconds(); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci for (i = 0; i < NBR_AVG_SAMPLES; i++) { 4468c2ecf20Sopenharmony_ci avg->samples[i] = sample; 4478c2ecf20Sopenharmony_ci avg->time_stamps[i] = now; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci avg->pos = 0; 4518c2ecf20Sopenharmony_ci avg->nbr_samples = NBR_AVG_SAMPLES; 4528c2ecf20Sopenharmony_ci avg->sum = sample * NBR_AVG_SAMPLES; 4538c2ecf20Sopenharmony_ci avg->avg = sample; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/** 4578c2ecf20Sopenharmony_ci * ab8500_fg_coulomb_counter() - enable coulomb counter 4588c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 4598c2ecf20Sopenharmony_ci * @enable: enable/disable 4608c2ecf20Sopenharmony_ci * 4618c2ecf20Sopenharmony_ci * Enable/Disable coulomb counter. 4628c2ecf20Sopenharmony_ci * On failure returns negative value. 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_cistatic int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci int ret = 0; 4678c2ecf20Sopenharmony_ci mutex_lock(&di->cc_lock); 4688c2ecf20Sopenharmony_ci if (enable) { 4698c2ecf20Sopenharmony_ci /* To be able to reprogram the number of samples, we have to 4708c2ecf20Sopenharmony_ci * first stop the CC and then enable it again */ 4718c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 4728c2ecf20Sopenharmony_ci AB8500_RTC_CC_CONF_REG, 0x00); 4738c2ecf20Sopenharmony_ci if (ret) 4748c2ecf20Sopenharmony_ci goto cc_err; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* Program the samples */ 4778c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, 4788c2ecf20Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU, 4798c2ecf20Sopenharmony_ci di->fg_samples); 4808c2ecf20Sopenharmony_ci if (ret) 4818c2ecf20Sopenharmony_ci goto cc_err; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* Start the CC */ 4848c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 4858c2ecf20Sopenharmony_ci AB8500_RTC_CC_CONF_REG, 4868c2ecf20Sopenharmony_ci (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA)); 4878c2ecf20Sopenharmony_ci if (ret) 4888c2ecf20Sopenharmony_ci goto cc_err; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci di->flags.fg_enabled = true; 4918c2ecf20Sopenharmony_ci } else { 4928c2ecf20Sopenharmony_ci /* Clear any pending read requests */ 4938c2ecf20Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 4948c2ecf20Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 4958c2ecf20Sopenharmony_ci (RESET_ACCU | READ_REQ), 0); 4968c2ecf20Sopenharmony_ci if (ret) 4978c2ecf20Sopenharmony_ci goto cc_err; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, 5008c2ecf20Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU_CTRL, 0); 5018c2ecf20Sopenharmony_ci if (ret) 5028c2ecf20Sopenharmony_ci goto cc_err; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* Stop the CC */ 5058c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 5068c2ecf20Sopenharmony_ci AB8500_RTC_CC_CONF_REG, 0); 5078c2ecf20Sopenharmony_ci if (ret) 5088c2ecf20Sopenharmony_ci goto cc_err; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci di->flags.fg_enabled = false; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci dev_dbg(di->dev, " CC enabled: %d Samples: %d\n", 5148c2ecf20Sopenharmony_ci enable, di->fg_samples); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci mutex_unlock(&di->cc_lock); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_cicc_err: 5208c2ecf20Sopenharmony_ci dev_err(di->dev, "%s Enabling coulomb counter failed\n", __func__); 5218c2ecf20Sopenharmony_ci mutex_unlock(&di->cc_lock); 5228c2ecf20Sopenharmony_ci return ret; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci/** 5268c2ecf20Sopenharmony_ci * ab8500_fg_inst_curr_start() - start battery instantaneous current 5278c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 5288c2ecf20Sopenharmony_ci * 5298c2ecf20Sopenharmony_ci * Returns 0 or error code 5308c2ecf20Sopenharmony_ci * Note: This is part "one" and has to be called before 5318c2ecf20Sopenharmony_ci * ab8500_fg_inst_curr_finalize() 5328c2ecf20Sopenharmony_ci */ 5338c2ecf20Sopenharmony_ciint ab8500_fg_inst_curr_start(struct ab8500_fg *di) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci u8 reg_val; 5368c2ecf20Sopenharmony_ci int ret; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci mutex_lock(&di->cc_lock); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci di->nbr_cceoc_irq_cnt = 0; 5418c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 5428c2ecf20Sopenharmony_ci AB8500_RTC_CC_CONF_REG, ®_val); 5438c2ecf20Sopenharmony_ci if (ret < 0) 5448c2ecf20Sopenharmony_ci goto fail; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (!(reg_val & CC_PWR_UP_ENA)) { 5478c2ecf20Sopenharmony_ci dev_dbg(di->dev, "%s Enable FG\n", __func__); 5488c2ecf20Sopenharmony_ci di->turn_off_fg = true; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Program the samples */ 5518c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, 5528c2ecf20Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_NCOV_ACCU, 5538c2ecf20Sopenharmony_ci SEC_TO_SAMPLE(10)); 5548c2ecf20Sopenharmony_ci if (ret) 5558c2ecf20Sopenharmony_ci goto fail; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* Start the CC */ 5588c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 5598c2ecf20Sopenharmony_ci AB8500_RTC_CC_CONF_REG, 5608c2ecf20Sopenharmony_ci (CC_DEEP_SLEEP_ENA | CC_PWR_UP_ENA)); 5618c2ecf20Sopenharmony_ci if (ret) 5628c2ecf20Sopenharmony_ci goto fail; 5638c2ecf20Sopenharmony_ci } else { 5648c2ecf20Sopenharmony_ci di->turn_off_fg = false; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Return and WFI */ 5688c2ecf20Sopenharmony_ci reinit_completion(&di->ab8500_fg_started); 5698c2ecf20Sopenharmony_ci reinit_completion(&di->ab8500_fg_complete); 5708c2ecf20Sopenharmony_ci enable_irq(di->irq); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* Note: cc_lock is still locked */ 5738c2ecf20Sopenharmony_ci return 0; 5748c2ecf20Sopenharmony_cifail: 5758c2ecf20Sopenharmony_ci mutex_unlock(&di->cc_lock); 5768c2ecf20Sopenharmony_ci return ret; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci/** 5808c2ecf20Sopenharmony_ci * ab8500_fg_inst_curr_started() - check if fg conversion has started 5818c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 5828c2ecf20Sopenharmony_ci * 5838c2ecf20Sopenharmony_ci * Returns 1 if conversion started, 0 if still waiting 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_ciint ab8500_fg_inst_curr_started(struct ab8500_fg *di) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci return completion_done(&di->ab8500_fg_started); 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/** 5918c2ecf20Sopenharmony_ci * ab8500_fg_inst_curr_done() - check if fg conversion is done 5928c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 5938c2ecf20Sopenharmony_ci * 5948c2ecf20Sopenharmony_ci * Returns 1 if conversion done, 0 if still waiting 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_ciint ab8500_fg_inst_curr_done(struct ab8500_fg *di) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci return completion_done(&di->ab8500_fg_complete); 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci/** 6028c2ecf20Sopenharmony_ci * ab8500_fg_inst_curr_finalize() - battery instantaneous current 6038c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 6048c2ecf20Sopenharmony_ci * @res: battery instantenous current(on success) 6058c2ecf20Sopenharmony_ci * 6068c2ecf20Sopenharmony_ci * Returns 0 or an error code 6078c2ecf20Sopenharmony_ci * Note: This is part "two" and has to be called at earliest 250 ms 6088c2ecf20Sopenharmony_ci * after ab8500_fg_inst_curr_start() 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_ciint ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci u8 low, high; 6138c2ecf20Sopenharmony_ci int val; 6148c2ecf20Sopenharmony_ci int ret; 6158c2ecf20Sopenharmony_ci unsigned long timeout; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (!completion_done(&di->ab8500_fg_complete)) { 6188c2ecf20Sopenharmony_ci timeout = wait_for_completion_timeout( 6198c2ecf20Sopenharmony_ci &di->ab8500_fg_complete, 6208c2ecf20Sopenharmony_ci INS_CURR_TIMEOUT); 6218c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Finalize time: %d ms\n", 6228c2ecf20Sopenharmony_ci jiffies_to_msecs(INS_CURR_TIMEOUT - timeout)); 6238c2ecf20Sopenharmony_ci if (!timeout) { 6248c2ecf20Sopenharmony_ci ret = -ETIME; 6258c2ecf20Sopenharmony_ci disable_irq(di->irq); 6268c2ecf20Sopenharmony_ci di->nbr_cceoc_irq_cnt = 0; 6278c2ecf20Sopenharmony_ci dev_err(di->dev, "completion timed out [%d]\n", 6288c2ecf20Sopenharmony_ci __LINE__); 6298c2ecf20Sopenharmony_ci goto fail; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci disable_irq(di->irq); 6348c2ecf20Sopenharmony_ci di->nbr_cceoc_irq_cnt = 0; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 6378c2ecf20Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 6388c2ecf20Sopenharmony_ci READ_REQ, READ_REQ); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* 100uS between read request and read is needed */ 6418c2ecf20Sopenharmony_ci usleep_range(100, 100); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* Read CC Sample conversion value Low and high */ 6448c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE, 6458c2ecf20Sopenharmony_ci AB8500_GASG_CC_SMPL_CNVL_REG, &low); 6468c2ecf20Sopenharmony_ci if (ret < 0) 6478c2ecf20Sopenharmony_ci goto fail; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE, 6508c2ecf20Sopenharmony_ci AB8500_GASG_CC_SMPL_CNVH_REG, &high); 6518c2ecf20Sopenharmony_ci if (ret < 0) 6528c2ecf20Sopenharmony_ci goto fail; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* 6558c2ecf20Sopenharmony_ci * negative value for Discharging 6568c2ecf20Sopenharmony_ci * convert 2's complement into decimal 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci if (high & 0x10) 6598c2ecf20Sopenharmony_ci val = (low | (high << 8) | 0xFFFFE000); 6608c2ecf20Sopenharmony_ci else 6618c2ecf20Sopenharmony_ci val = (low | (high << 8)); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* 6648c2ecf20Sopenharmony_ci * Convert to unit value in mA 6658c2ecf20Sopenharmony_ci * Full scale input voltage is 6668c2ecf20Sopenharmony_ci * 63.160mV => LSB = 63.160mV/(4096*res) = 1.542mA 6678c2ecf20Sopenharmony_ci * Given a 250ms conversion cycle time the LSB corresponds 6688c2ecf20Sopenharmony_ci * to 107.1 nAh. Convert to current by dividing by the conversion 6698c2ecf20Sopenharmony_ci * time in hours (250ms = 1 / (3600 * 4)h) 6708c2ecf20Sopenharmony_ci * 107.1nAh assumes 10mOhm, but fg_res is in 0.1mOhm 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_ci val = (val * QLSB_NANO_AMP_HOURS_X10 * 36 * 4) / 6738c2ecf20Sopenharmony_ci (1000 * di->bm->fg_res); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci if (di->turn_off_fg) { 6768c2ecf20Sopenharmony_ci dev_dbg(di->dev, "%s Disable FG\n", __func__); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* Clear any pending read requests */ 6798c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, 6808c2ecf20Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0); 6818c2ecf20Sopenharmony_ci if (ret) 6828c2ecf20Sopenharmony_ci goto fail; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* Stop the CC */ 6858c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 6868c2ecf20Sopenharmony_ci AB8500_RTC_CC_CONF_REG, 0); 6878c2ecf20Sopenharmony_ci if (ret) 6888c2ecf20Sopenharmony_ci goto fail; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci mutex_unlock(&di->cc_lock); 6918c2ecf20Sopenharmony_ci (*res) = val; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci return 0; 6948c2ecf20Sopenharmony_cifail: 6958c2ecf20Sopenharmony_ci mutex_unlock(&di->cc_lock); 6968c2ecf20Sopenharmony_ci return ret; 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci/** 7008c2ecf20Sopenharmony_ci * ab8500_fg_inst_curr_blocking() - battery instantaneous current 7018c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 7028c2ecf20Sopenharmony_ci * @res: battery instantenous current(on success) 7038c2ecf20Sopenharmony_ci * 7048c2ecf20Sopenharmony_ci * Returns 0 else error code 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ciint ab8500_fg_inst_curr_blocking(struct ab8500_fg *di) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci int ret; 7098c2ecf20Sopenharmony_ci unsigned long timeout; 7108c2ecf20Sopenharmony_ci int res = 0; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci ret = ab8500_fg_inst_curr_start(di); 7138c2ecf20Sopenharmony_ci if (ret) { 7148c2ecf20Sopenharmony_ci dev_err(di->dev, "Failed to initialize fg_inst\n"); 7158c2ecf20Sopenharmony_ci return 0; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* Wait for CC to actually start */ 7198c2ecf20Sopenharmony_ci if (!completion_done(&di->ab8500_fg_started)) { 7208c2ecf20Sopenharmony_ci timeout = wait_for_completion_timeout( 7218c2ecf20Sopenharmony_ci &di->ab8500_fg_started, 7228c2ecf20Sopenharmony_ci INS_CURR_TIMEOUT); 7238c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Start time: %d ms\n", 7248c2ecf20Sopenharmony_ci jiffies_to_msecs(INS_CURR_TIMEOUT - timeout)); 7258c2ecf20Sopenharmony_ci if (!timeout) { 7268c2ecf20Sopenharmony_ci ret = -ETIME; 7278c2ecf20Sopenharmony_ci dev_err(di->dev, "completion timed out [%d]\n", 7288c2ecf20Sopenharmony_ci __LINE__); 7298c2ecf20Sopenharmony_ci goto fail; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci ret = ab8500_fg_inst_curr_finalize(di, &res); 7348c2ecf20Sopenharmony_ci if (ret) { 7358c2ecf20Sopenharmony_ci dev_err(di->dev, "Failed to finalize fg_inst\n"); 7368c2ecf20Sopenharmony_ci return 0; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci dev_dbg(di->dev, "%s instant current: %d", __func__, res); 7408c2ecf20Sopenharmony_ci return res; 7418c2ecf20Sopenharmony_cifail: 7428c2ecf20Sopenharmony_ci disable_irq(di->irq); 7438c2ecf20Sopenharmony_ci mutex_unlock(&di->cc_lock); 7448c2ecf20Sopenharmony_ci return ret; 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci/** 7488c2ecf20Sopenharmony_ci * ab8500_fg_acc_cur_work() - average battery current 7498c2ecf20Sopenharmony_ci * @work: pointer to the work_struct structure 7508c2ecf20Sopenharmony_ci * 7518c2ecf20Sopenharmony_ci * Updated the average battery current obtained from the 7528c2ecf20Sopenharmony_ci * coulomb counter. 7538c2ecf20Sopenharmony_ci */ 7548c2ecf20Sopenharmony_cistatic void ab8500_fg_acc_cur_work(struct work_struct *work) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci int val; 7578c2ecf20Sopenharmony_ci int ret; 7588c2ecf20Sopenharmony_ci u8 low, med, high; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci struct ab8500_fg *di = container_of(work, 7618c2ecf20Sopenharmony_ci struct ab8500_fg, fg_acc_cur_work); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci mutex_lock(&di->cc_lock); 7648c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_GAS_GAUGE, 7658c2ecf20Sopenharmony_ci AB8500_GASG_CC_NCOV_ACCU_CTRL, RD_NCONV_ACCU_REQ); 7668c2ecf20Sopenharmony_ci if (ret) 7678c2ecf20Sopenharmony_ci goto exit; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE, 7708c2ecf20Sopenharmony_ci AB8500_GASG_CC_NCOV_ACCU_LOW, &low); 7718c2ecf20Sopenharmony_ci if (ret < 0) 7728c2ecf20Sopenharmony_ci goto exit; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE, 7758c2ecf20Sopenharmony_ci AB8500_GASG_CC_NCOV_ACCU_MED, &med); 7768c2ecf20Sopenharmony_ci if (ret < 0) 7778c2ecf20Sopenharmony_ci goto exit; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_GAS_GAUGE, 7808c2ecf20Sopenharmony_ci AB8500_GASG_CC_NCOV_ACCU_HIGH, &high); 7818c2ecf20Sopenharmony_ci if (ret < 0) 7828c2ecf20Sopenharmony_ci goto exit; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* Check for sign bit in case of negative value, 2's complement */ 7858c2ecf20Sopenharmony_ci if (high & 0x10) 7868c2ecf20Sopenharmony_ci val = (low | (med << 8) | (high << 16) | 0xFFE00000); 7878c2ecf20Sopenharmony_ci else 7888c2ecf20Sopenharmony_ci val = (low | (med << 8) | (high << 16)); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* 7918c2ecf20Sopenharmony_ci * Convert to uAh 7928c2ecf20Sopenharmony_ci * Given a 250ms conversion cycle time the LSB corresponds 7938c2ecf20Sopenharmony_ci * to 112.9 nAh. 7948c2ecf20Sopenharmony_ci * 112.9nAh assumes 10mOhm, but fg_res is in 0.1mOhm 7958c2ecf20Sopenharmony_ci */ 7968c2ecf20Sopenharmony_ci di->accu_charge = (val * QLSB_NANO_AMP_HOURS_X10) / 7978c2ecf20Sopenharmony_ci (100 * di->bm->fg_res); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* 8008c2ecf20Sopenharmony_ci * Convert to unit value in mA 8018c2ecf20Sopenharmony_ci * by dividing by the conversion 8028c2ecf20Sopenharmony_ci * time in hours (= samples / (3600 * 4)h) 8038c2ecf20Sopenharmony_ci * and multiply with 1000 8048c2ecf20Sopenharmony_ci */ 8058c2ecf20Sopenharmony_ci di->avg_curr = (val * QLSB_NANO_AMP_HOURS_X10 * 36) / 8068c2ecf20Sopenharmony_ci (1000 * di->bm->fg_res * (di->fg_samples / 4)); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci di->flags.conv_done = true; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci mutex_unlock(&di->cc_lock); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci queue_work(di->fg_wq, &di->fg_work); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci dev_dbg(di->dev, "fg_res: %d, fg_samples: %d, gasg: %d, accu_charge: %d \n", 8158c2ecf20Sopenharmony_ci di->bm->fg_res, di->fg_samples, val, di->accu_charge); 8168c2ecf20Sopenharmony_ci return; 8178c2ecf20Sopenharmony_ciexit: 8188c2ecf20Sopenharmony_ci dev_err(di->dev, 8198c2ecf20Sopenharmony_ci "Failed to read or write gas gauge registers\n"); 8208c2ecf20Sopenharmony_ci mutex_unlock(&di->cc_lock); 8218c2ecf20Sopenharmony_ci queue_work(di->fg_wq, &di->fg_work); 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci/** 8258c2ecf20Sopenharmony_ci * ab8500_fg_bat_voltage() - get battery voltage 8268c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 8278c2ecf20Sopenharmony_ci * 8288c2ecf20Sopenharmony_ci * Returns battery voltage(on success) else error code 8298c2ecf20Sopenharmony_ci */ 8308c2ecf20Sopenharmony_cistatic int ab8500_fg_bat_voltage(struct ab8500_fg *di) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci int vbat, ret; 8338c2ecf20Sopenharmony_ci static int prev; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci ret = iio_read_channel_processed(di->main_bat_v, &vbat); 8368c2ecf20Sopenharmony_ci if (ret < 0) { 8378c2ecf20Sopenharmony_ci dev_err(di->dev, 8388c2ecf20Sopenharmony_ci "%s ADC conversion failed, using previous value\n", 8398c2ecf20Sopenharmony_ci __func__); 8408c2ecf20Sopenharmony_ci return prev; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci prev = vbat; 8448c2ecf20Sopenharmony_ci return vbat; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci/** 8488c2ecf20Sopenharmony_ci * ab8500_fg_volt_to_capacity() - Voltage based capacity 8498c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 8508c2ecf20Sopenharmony_ci * @voltage: The voltage to convert to a capacity 8518c2ecf20Sopenharmony_ci * 8528c2ecf20Sopenharmony_ci * Returns battery capacity in per mille based on voltage 8538c2ecf20Sopenharmony_ci */ 8548c2ecf20Sopenharmony_cistatic int ab8500_fg_volt_to_capacity(struct ab8500_fg *di, int voltage) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci int i, tbl_size; 8578c2ecf20Sopenharmony_ci const struct abx500_v_to_cap *tbl; 8588c2ecf20Sopenharmony_ci int cap = 0; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci tbl = di->bm->bat_type[di->bm->batt_id].v_to_cap_tbl, 8618c2ecf20Sopenharmony_ci tbl_size = di->bm->bat_type[di->bm->batt_id].n_v_cap_tbl_elements; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci for (i = 0; i < tbl_size; ++i) { 8648c2ecf20Sopenharmony_ci if (voltage > tbl[i].voltage) 8658c2ecf20Sopenharmony_ci break; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if ((i > 0) && (i < tbl_size)) { 8698c2ecf20Sopenharmony_ci cap = interpolate(voltage, 8708c2ecf20Sopenharmony_ci tbl[i].voltage, 8718c2ecf20Sopenharmony_ci tbl[i].capacity * 10, 8728c2ecf20Sopenharmony_ci tbl[i-1].voltage, 8738c2ecf20Sopenharmony_ci tbl[i-1].capacity * 10); 8748c2ecf20Sopenharmony_ci } else if (i == 0) { 8758c2ecf20Sopenharmony_ci cap = 1000; 8768c2ecf20Sopenharmony_ci } else { 8778c2ecf20Sopenharmony_ci cap = 0; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci dev_dbg(di->dev, "%s Vbat: %d, Cap: %d per mille", 8818c2ecf20Sopenharmony_ci __func__, voltage, cap); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci return cap; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci/** 8878c2ecf20Sopenharmony_ci * ab8500_fg_uncomp_volt_to_capacity() - Uncompensated voltage based capacity 8888c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 8898c2ecf20Sopenharmony_ci * 8908c2ecf20Sopenharmony_ci * Returns battery capacity based on battery voltage that is not compensated 8918c2ecf20Sopenharmony_ci * for the voltage drop due to the load 8928c2ecf20Sopenharmony_ci */ 8938c2ecf20Sopenharmony_cistatic int ab8500_fg_uncomp_volt_to_capacity(struct ab8500_fg *di) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci di->vbat = ab8500_fg_bat_voltage(di); 8968c2ecf20Sopenharmony_ci return ab8500_fg_volt_to_capacity(di, di->vbat); 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci/** 9008c2ecf20Sopenharmony_ci * ab8500_fg_battery_resistance() - Returns the battery inner resistance 9018c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 9028c2ecf20Sopenharmony_ci * 9038c2ecf20Sopenharmony_ci * Returns battery inner resistance added with the fuel gauge resistor value 9048c2ecf20Sopenharmony_ci * to get the total resistance in the whole link from gnd to bat+ node. 9058c2ecf20Sopenharmony_ci */ 9068c2ecf20Sopenharmony_cistatic int ab8500_fg_battery_resistance(struct ab8500_fg *di) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci int i, tbl_size; 9098c2ecf20Sopenharmony_ci const struct batres_vs_temp *tbl; 9108c2ecf20Sopenharmony_ci int resist = 0; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci tbl = di->bm->bat_type[di->bm->batt_id].batres_tbl; 9138c2ecf20Sopenharmony_ci tbl_size = di->bm->bat_type[di->bm->batt_id].n_batres_tbl_elements; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci for (i = 0; i < tbl_size; ++i) { 9168c2ecf20Sopenharmony_ci if (di->bat_temp / 10 > tbl[i].temp) 9178c2ecf20Sopenharmony_ci break; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci if ((i > 0) && (i < tbl_size)) { 9218c2ecf20Sopenharmony_ci resist = interpolate(di->bat_temp / 10, 9228c2ecf20Sopenharmony_ci tbl[i].temp, 9238c2ecf20Sopenharmony_ci tbl[i].resist, 9248c2ecf20Sopenharmony_ci tbl[i-1].temp, 9258c2ecf20Sopenharmony_ci tbl[i-1].resist); 9268c2ecf20Sopenharmony_ci } else if (i == 0) { 9278c2ecf20Sopenharmony_ci resist = tbl[0].resist; 9288c2ecf20Sopenharmony_ci } else { 9298c2ecf20Sopenharmony_ci resist = tbl[tbl_size - 1].resist; 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci dev_dbg(di->dev, "%s Temp: %d battery internal resistance: %d" 9338c2ecf20Sopenharmony_ci " fg resistance %d, total: %d (mOhm)\n", 9348c2ecf20Sopenharmony_ci __func__, di->bat_temp, resist, di->bm->fg_res / 10, 9358c2ecf20Sopenharmony_ci (di->bm->fg_res / 10) + resist); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci /* fg_res variable is in 0.1mOhm */ 9388c2ecf20Sopenharmony_ci resist += di->bm->fg_res / 10; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci return resist; 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci/** 9448c2ecf20Sopenharmony_ci * ab8500_fg_load_comp_volt_to_capacity() - Load compensated voltage based capacity 9458c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 9468c2ecf20Sopenharmony_ci * 9478c2ecf20Sopenharmony_ci * Returns battery capacity based on battery voltage that is load compensated 9488c2ecf20Sopenharmony_ci * for the voltage drop 9498c2ecf20Sopenharmony_ci */ 9508c2ecf20Sopenharmony_cistatic int ab8500_fg_load_comp_volt_to_capacity(struct ab8500_fg *di) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci int vbat_comp, res; 9538c2ecf20Sopenharmony_ci int i = 0; 9548c2ecf20Sopenharmony_ci int vbat = 0; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci ab8500_fg_inst_curr_start(di); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci do { 9598c2ecf20Sopenharmony_ci vbat += ab8500_fg_bat_voltage(di); 9608c2ecf20Sopenharmony_ci i++; 9618c2ecf20Sopenharmony_ci usleep_range(5000, 6000); 9628c2ecf20Sopenharmony_ci } while (!ab8500_fg_inst_curr_done(di)); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci ab8500_fg_inst_curr_finalize(di, &di->inst_curr); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci di->vbat = vbat / i; 9678c2ecf20Sopenharmony_ci res = ab8500_fg_battery_resistance(di); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci /* Use Ohms law to get the load compensated voltage */ 9708c2ecf20Sopenharmony_ci vbat_comp = di->vbat - (di->inst_curr * res) / 1000; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci dev_dbg(di->dev, "%s Measured Vbat: %dmV,Compensated Vbat %dmV, " 9738c2ecf20Sopenharmony_ci "R: %dmOhm, Current: %dmA Vbat Samples: %d\n", 9748c2ecf20Sopenharmony_ci __func__, di->vbat, vbat_comp, res, di->inst_curr, i); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci return ab8500_fg_volt_to_capacity(di, vbat_comp); 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci/** 9808c2ecf20Sopenharmony_ci * ab8500_fg_convert_mah_to_permille() - Capacity in mAh to permille 9818c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 9828c2ecf20Sopenharmony_ci * @cap_mah: capacity in mAh 9838c2ecf20Sopenharmony_ci * 9848c2ecf20Sopenharmony_ci * Converts capacity in mAh to capacity in permille 9858c2ecf20Sopenharmony_ci */ 9868c2ecf20Sopenharmony_cistatic int ab8500_fg_convert_mah_to_permille(struct ab8500_fg *di, int cap_mah) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci return (cap_mah * 1000) / di->bat_cap.max_mah_design; 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci/** 9928c2ecf20Sopenharmony_ci * ab8500_fg_convert_permille_to_mah() - Capacity in permille to mAh 9938c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 9948c2ecf20Sopenharmony_ci * @cap_pm: capacity in permille 9958c2ecf20Sopenharmony_ci * 9968c2ecf20Sopenharmony_ci * Converts capacity in permille to capacity in mAh 9978c2ecf20Sopenharmony_ci */ 9988c2ecf20Sopenharmony_cistatic int ab8500_fg_convert_permille_to_mah(struct ab8500_fg *di, int cap_pm) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci return cap_pm * di->bat_cap.max_mah_design / 1000; 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci/** 10048c2ecf20Sopenharmony_ci * ab8500_fg_convert_mah_to_uwh() - Capacity in mAh to uWh 10058c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 10068c2ecf20Sopenharmony_ci * @cap_mah: capacity in mAh 10078c2ecf20Sopenharmony_ci * 10088c2ecf20Sopenharmony_ci * Converts capacity in mAh to capacity in uWh 10098c2ecf20Sopenharmony_ci */ 10108c2ecf20Sopenharmony_cistatic int ab8500_fg_convert_mah_to_uwh(struct ab8500_fg *di, int cap_mah) 10118c2ecf20Sopenharmony_ci{ 10128c2ecf20Sopenharmony_ci u64 div_res; 10138c2ecf20Sopenharmony_ci u32 div_rem; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci div_res = ((u64) cap_mah) * ((u64) di->vbat_nom); 10168c2ecf20Sopenharmony_ci div_rem = do_div(div_res, 1000); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* Make sure to round upwards if necessary */ 10198c2ecf20Sopenharmony_ci if (div_rem >= 1000 / 2) 10208c2ecf20Sopenharmony_ci div_res++; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci return (int) div_res; 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci/** 10268c2ecf20Sopenharmony_ci * ab8500_fg_calc_cap_charging() - Calculate remaining capacity while charging 10278c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 10288c2ecf20Sopenharmony_ci * 10298c2ecf20Sopenharmony_ci * Return the capacity in mAh based on previous calculated capcity and the FG 10308c2ecf20Sopenharmony_ci * accumulator register value. The filter is filled with this capacity 10318c2ecf20Sopenharmony_ci */ 10328c2ecf20Sopenharmony_cistatic int ab8500_fg_calc_cap_charging(struct ab8500_fg *di) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n", 10358c2ecf20Sopenharmony_ci __func__, 10368c2ecf20Sopenharmony_ci di->bat_cap.mah, 10378c2ecf20Sopenharmony_ci di->accu_charge); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci /* Capacity should not be less than 0 */ 10408c2ecf20Sopenharmony_ci if (di->bat_cap.mah + di->accu_charge > 0) 10418c2ecf20Sopenharmony_ci di->bat_cap.mah += di->accu_charge; 10428c2ecf20Sopenharmony_ci else 10438c2ecf20Sopenharmony_ci di->bat_cap.mah = 0; 10448c2ecf20Sopenharmony_ci /* 10458c2ecf20Sopenharmony_ci * We force capacity to 100% once when the algorithm 10468c2ecf20Sopenharmony_ci * reports that it's full. 10478c2ecf20Sopenharmony_ci */ 10488c2ecf20Sopenharmony_ci if (di->bat_cap.mah >= di->bat_cap.max_mah_design || 10498c2ecf20Sopenharmony_ci di->flags.force_full) { 10508c2ecf20Sopenharmony_ci di->bat_cap.mah = di->bat_cap.max_mah_design; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci ab8500_fg_fill_cap_sample(di, di->bat_cap.mah); 10548c2ecf20Sopenharmony_ci di->bat_cap.permille = 10558c2ecf20Sopenharmony_ci ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* We need to update battery voltage and inst current when charging */ 10588c2ecf20Sopenharmony_ci di->vbat = ab8500_fg_bat_voltage(di); 10598c2ecf20Sopenharmony_ci di->inst_curr = ab8500_fg_inst_curr_blocking(di); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci return di->bat_cap.mah; 10628c2ecf20Sopenharmony_ci} 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci/** 10658c2ecf20Sopenharmony_ci * ab8500_fg_calc_cap_discharge_voltage() - Capacity in discharge with voltage 10668c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 10678c2ecf20Sopenharmony_ci * @comp: if voltage should be load compensated before capacity calc 10688c2ecf20Sopenharmony_ci * 10698c2ecf20Sopenharmony_ci * Return the capacity in mAh based on the battery voltage. The voltage can 10708c2ecf20Sopenharmony_ci * either be load compensated or not. This value is added to the filter and a 10718c2ecf20Sopenharmony_ci * new mean value is calculated and returned. 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_cistatic int ab8500_fg_calc_cap_discharge_voltage(struct ab8500_fg *di, bool comp) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci int permille, mah; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci if (comp) 10788c2ecf20Sopenharmony_ci permille = ab8500_fg_load_comp_volt_to_capacity(di); 10798c2ecf20Sopenharmony_ci else 10808c2ecf20Sopenharmony_ci permille = ab8500_fg_uncomp_volt_to_capacity(di); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci mah = ab8500_fg_convert_permille_to_mah(di, permille); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci di->bat_cap.mah = ab8500_fg_add_cap_sample(di, mah); 10858c2ecf20Sopenharmony_ci di->bat_cap.permille = 10868c2ecf20Sopenharmony_ci ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci return di->bat_cap.mah; 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci/** 10928c2ecf20Sopenharmony_ci * ab8500_fg_calc_cap_discharge_fg() - Capacity in discharge with FG 10938c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 10948c2ecf20Sopenharmony_ci * 10958c2ecf20Sopenharmony_ci * Return the capacity in mAh based on previous calculated capcity and the FG 10968c2ecf20Sopenharmony_ci * accumulator register value. This value is added to the filter and a 10978c2ecf20Sopenharmony_ci * new mean value is calculated and returned. 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_cistatic int ab8500_fg_calc_cap_discharge_fg(struct ab8500_fg *di) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci int permille_volt, permille; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n", 11048c2ecf20Sopenharmony_ci __func__, 11058c2ecf20Sopenharmony_ci di->bat_cap.mah, 11068c2ecf20Sopenharmony_ci di->accu_charge); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci /* Capacity should not be less than 0 */ 11098c2ecf20Sopenharmony_ci if (di->bat_cap.mah + di->accu_charge > 0) 11108c2ecf20Sopenharmony_ci di->bat_cap.mah += di->accu_charge; 11118c2ecf20Sopenharmony_ci else 11128c2ecf20Sopenharmony_ci di->bat_cap.mah = 0; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci if (di->bat_cap.mah >= di->bat_cap.max_mah_design) 11158c2ecf20Sopenharmony_ci di->bat_cap.mah = di->bat_cap.max_mah_design; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci /* 11188c2ecf20Sopenharmony_ci * Check against voltage based capacity. It can not be lower 11198c2ecf20Sopenharmony_ci * than what the uncompensated voltage says 11208c2ecf20Sopenharmony_ci */ 11218c2ecf20Sopenharmony_ci permille = ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah); 11228c2ecf20Sopenharmony_ci permille_volt = ab8500_fg_uncomp_volt_to_capacity(di); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci if (permille < permille_volt) { 11258c2ecf20Sopenharmony_ci di->bat_cap.permille = permille_volt; 11268c2ecf20Sopenharmony_ci di->bat_cap.mah = ab8500_fg_convert_permille_to_mah(di, 11278c2ecf20Sopenharmony_ci di->bat_cap.permille); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci dev_dbg(di->dev, "%s voltage based: perm %d perm_volt %d\n", 11308c2ecf20Sopenharmony_ci __func__, 11318c2ecf20Sopenharmony_ci permille, 11328c2ecf20Sopenharmony_ci permille_volt); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci ab8500_fg_fill_cap_sample(di, di->bat_cap.mah); 11358c2ecf20Sopenharmony_ci } else { 11368c2ecf20Sopenharmony_ci ab8500_fg_fill_cap_sample(di, di->bat_cap.mah); 11378c2ecf20Sopenharmony_ci di->bat_cap.permille = 11388c2ecf20Sopenharmony_ci ab8500_fg_convert_mah_to_permille(di, di->bat_cap.mah); 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci return di->bat_cap.mah; 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci/** 11458c2ecf20Sopenharmony_ci * ab8500_fg_capacity_level() - Get the battery capacity level 11468c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 11478c2ecf20Sopenharmony_ci * 11488c2ecf20Sopenharmony_ci * Get the battery capacity level based on the capacity in percent 11498c2ecf20Sopenharmony_ci */ 11508c2ecf20Sopenharmony_cistatic int ab8500_fg_capacity_level(struct ab8500_fg *di) 11518c2ecf20Sopenharmony_ci{ 11528c2ecf20Sopenharmony_ci int ret, percent; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci if (percent <= di->bm->cap_levels->critical || 11578c2ecf20Sopenharmony_ci di->flags.low_bat) 11588c2ecf20Sopenharmony_ci ret = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; 11598c2ecf20Sopenharmony_ci else if (percent <= di->bm->cap_levels->low) 11608c2ecf20Sopenharmony_ci ret = POWER_SUPPLY_CAPACITY_LEVEL_LOW; 11618c2ecf20Sopenharmony_ci else if (percent <= di->bm->cap_levels->normal) 11628c2ecf20Sopenharmony_ci ret = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; 11638c2ecf20Sopenharmony_ci else if (percent <= di->bm->cap_levels->high) 11648c2ecf20Sopenharmony_ci ret = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; 11658c2ecf20Sopenharmony_ci else 11668c2ecf20Sopenharmony_ci ret = POWER_SUPPLY_CAPACITY_LEVEL_FULL; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci return ret; 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci/** 11728c2ecf20Sopenharmony_ci * ab8500_fg_calculate_scaled_capacity() - Capacity scaling 11738c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 11748c2ecf20Sopenharmony_ci * 11758c2ecf20Sopenharmony_ci * Calculates the capacity to be shown to upper layers. Scales the capacity 11768c2ecf20Sopenharmony_ci * to have 100% as a reference from the actual capacity upon removal of charger 11778c2ecf20Sopenharmony_ci * when charging is in maintenance mode. 11788c2ecf20Sopenharmony_ci */ 11798c2ecf20Sopenharmony_cistatic int ab8500_fg_calculate_scaled_capacity(struct ab8500_fg *di) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale; 11828c2ecf20Sopenharmony_ci int capacity = di->bat_cap.prev_percent; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci if (!cs->enable) 11858c2ecf20Sopenharmony_ci return capacity; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci /* 11888c2ecf20Sopenharmony_ci * As long as we are in fully charge mode scale the capacity 11898c2ecf20Sopenharmony_ci * to show 100%. 11908c2ecf20Sopenharmony_ci */ 11918c2ecf20Sopenharmony_ci if (di->flags.fully_charged) { 11928c2ecf20Sopenharmony_ci cs->cap_to_scale[0] = 100; 11938c2ecf20Sopenharmony_ci cs->cap_to_scale[1] = 11948c2ecf20Sopenharmony_ci max(capacity, di->bm->fg_params->maint_thres); 11958c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Scale cap with %d/%d\n", 11968c2ecf20Sopenharmony_ci cs->cap_to_scale[0], cs->cap_to_scale[1]); 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci /* Calculates the scaled capacity. */ 12008c2ecf20Sopenharmony_ci if ((cs->cap_to_scale[0] != cs->cap_to_scale[1]) 12018c2ecf20Sopenharmony_ci && (cs->cap_to_scale[1] > 0)) 12028c2ecf20Sopenharmony_ci capacity = min(100, 12038c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST(di->bat_cap.prev_percent * 12048c2ecf20Sopenharmony_ci cs->cap_to_scale[0], 12058c2ecf20Sopenharmony_ci cs->cap_to_scale[1])); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci if (di->flags.charging) { 12088c2ecf20Sopenharmony_ci if (capacity < cs->disable_cap_level) { 12098c2ecf20Sopenharmony_ci cs->disable_cap_level = capacity; 12108c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Cap to stop scale lowered %d%%\n", 12118c2ecf20Sopenharmony_ci cs->disable_cap_level); 12128c2ecf20Sopenharmony_ci } else if (!di->flags.fully_charged) { 12138c2ecf20Sopenharmony_ci if (di->bat_cap.prev_percent >= 12148c2ecf20Sopenharmony_ci cs->disable_cap_level) { 12158c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Disabling scaled capacity\n"); 12168c2ecf20Sopenharmony_ci cs->enable = false; 12178c2ecf20Sopenharmony_ci capacity = di->bat_cap.prev_percent; 12188c2ecf20Sopenharmony_ci } else { 12198c2ecf20Sopenharmony_ci dev_dbg(di->dev, 12208c2ecf20Sopenharmony_ci "Waiting in cap to level %d%%\n", 12218c2ecf20Sopenharmony_ci cs->disable_cap_level); 12228c2ecf20Sopenharmony_ci capacity = cs->disable_cap_level; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci return capacity; 12288c2ecf20Sopenharmony_ci} 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci/** 12318c2ecf20Sopenharmony_ci * ab8500_fg_update_cap_scalers() - Capacity scaling 12328c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 12338c2ecf20Sopenharmony_ci * 12348c2ecf20Sopenharmony_ci * To be called when state change from charge<->discharge to update 12358c2ecf20Sopenharmony_ci * the capacity scalers. 12368c2ecf20Sopenharmony_ci */ 12378c2ecf20Sopenharmony_cistatic void ab8500_fg_update_cap_scalers(struct ab8500_fg *di) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci struct ab8500_fg_cap_scaling *cs = &di->bat_cap.cap_scale; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci if (!cs->enable) 12428c2ecf20Sopenharmony_ci return; 12438c2ecf20Sopenharmony_ci if (di->flags.charging) { 12448c2ecf20Sopenharmony_ci di->bat_cap.cap_scale.disable_cap_level = 12458c2ecf20Sopenharmony_ci di->bat_cap.cap_scale.scaled_cap; 12468c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Cap to stop scale at charge %d%%\n", 12478c2ecf20Sopenharmony_ci di->bat_cap.cap_scale.disable_cap_level); 12488c2ecf20Sopenharmony_ci } else { 12498c2ecf20Sopenharmony_ci if (cs->scaled_cap != 100) { 12508c2ecf20Sopenharmony_ci cs->cap_to_scale[0] = cs->scaled_cap; 12518c2ecf20Sopenharmony_ci cs->cap_to_scale[1] = di->bat_cap.prev_percent; 12528c2ecf20Sopenharmony_ci } else { 12538c2ecf20Sopenharmony_ci cs->cap_to_scale[0] = 100; 12548c2ecf20Sopenharmony_ci cs->cap_to_scale[1] = 12558c2ecf20Sopenharmony_ci max(di->bat_cap.prev_percent, 12568c2ecf20Sopenharmony_ci di->bm->fg_params->maint_thres); 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Cap to scale at discharge %d/%d\n", 12608c2ecf20Sopenharmony_ci cs->cap_to_scale[0], cs->cap_to_scale[1]); 12618c2ecf20Sopenharmony_ci } 12628c2ecf20Sopenharmony_ci} 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci/** 12658c2ecf20Sopenharmony_ci * ab8500_fg_check_capacity_limits() - Check if capacity has changed 12668c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 12678c2ecf20Sopenharmony_ci * @init: capacity is allowed to go up in init mode 12688c2ecf20Sopenharmony_ci * 12698c2ecf20Sopenharmony_ci * Check if capacity or capacity limit has changed and notify the system 12708c2ecf20Sopenharmony_ci * about it using the power_supply framework 12718c2ecf20Sopenharmony_ci */ 12728c2ecf20Sopenharmony_cistatic void ab8500_fg_check_capacity_limits(struct ab8500_fg *di, bool init) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci bool changed = false; 12758c2ecf20Sopenharmony_ci int percent = DIV_ROUND_CLOSEST(di->bat_cap.permille, 10); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci di->bat_cap.level = ab8500_fg_capacity_level(di); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci if (di->bat_cap.level != di->bat_cap.prev_level) { 12808c2ecf20Sopenharmony_ci /* 12818c2ecf20Sopenharmony_ci * We do not allow reported capacity level to go up 12828c2ecf20Sopenharmony_ci * unless we're charging or if we're in init 12838c2ecf20Sopenharmony_ci */ 12848c2ecf20Sopenharmony_ci if (!(!di->flags.charging && di->bat_cap.level > 12858c2ecf20Sopenharmony_ci di->bat_cap.prev_level) || init) { 12868c2ecf20Sopenharmony_ci dev_dbg(di->dev, "level changed from %d to %d\n", 12878c2ecf20Sopenharmony_ci di->bat_cap.prev_level, 12888c2ecf20Sopenharmony_ci di->bat_cap.level); 12898c2ecf20Sopenharmony_ci di->bat_cap.prev_level = di->bat_cap.level; 12908c2ecf20Sopenharmony_ci changed = true; 12918c2ecf20Sopenharmony_ci } else { 12928c2ecf20Sopenharmony_ci dev_dbg(di->dev, "level not allowed to go up " 12938c2ecf20Sopenharmony_ci "since no charger is connected: %d to %d\n", 12948c2ecf20Sopenharmony_ci di->bat_cap.prev_level, 12958c2ecf20Sopenharmony_ci di->bat_cap.level); 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci /* 13008c2ecf20Sopenharmony_ci * If we have received the LOW_BAT IRQ, set capacity to 0 to initiate 13018c2ecf20Sopenharmony_ci * shutdown 13028c2ecf20Sopenharmony_ci */ 13038c2ecf20Sopenharmony_ci if (di->flags.low_bat) { 13048c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Battery low, set capacity to 0\n"); 13058c2ecf20Sopenharmony_ci di->bat_cap.prev_percent = 0; 13068c2ecf20Sopenharmony_ci di->bat_cap.permille = 0; 13078c2ecf20Sopenharmony_ci percent = 0; 13088c2ecf20Sopenharmony_ci di->bat_cap.prev_mah = 0; 13098c2ecf20Sopenharmony_ci di->bat_cap.mah = 0; 13108c2ecf20Sopenharmony_ci changed = true; 13118c2ecf20Sopenharmony_ci } else if (di->flags.fully_charged) { 13128c2ecf20Sopenharmony_ci /* 13138c2ecf20Sopenharmony_ci * We report 100% if algorithm reported fully charged 13148c2ecf20Sopenharmony_ci * and show 100% during maintenance charging (scaling). 13158c2ecf20Sopenharmony_ci */ 13168c2ecf20Sopenharmony_ci if (di->flags.force_full) { 13178c2ecf20Sopenharmony_ci di->bat_cap.prev_percent = percent; 13188c2ecf20Sopenharmony_ci di->bat_cap.prev_mah = di->bat_cap.mah; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci changed = true; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (!di->bat_cap.cap_scale.enable && 13238c2ecf20Sopenharmony_ci di->bm->capacity_scaling) { 13248c2ecf20Sopenharmony_ci di->bat_cap.cap_scale.enable = true; 13258c2ecf20Sopenharmony_ci di->bat_cap.cap_scale.cap_to_scale[0] = 100; 13268c2ecf20Sopenharmony_ci di->bat_cap.cap_scale.cap_to_scale[1] = 13278c2ecf20Sopenharmony_ci di->bat_cap.prev_percent; 13288c2ecf20Sopenharmony_ci di->bat_cap.cap_scale.disable_cap_level = 100; 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci } else if (di->bat_cap.prev_percent != percent) { 13318c2ecf20Sopenharmony_ci dev_dbg(di->dev, 13328c2ecf20Sopenharmony_ci "battery reported full " 13338c2ecf20Sopenharmony_ci "but capacity dropping: %d\n", 13348c2ecf20Sopenharmony_ci percent); 13358c2ecf20Sopenharmony_ci di->bat_cap.prev_percent = percent; 13368c2ecf20Sopenharmony_ci di->bat_cap.prev_mah = di->bat_cap.mah; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci changed = true; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci } else if (di->bat_cap.prev_percent != percent) { 13418c2ecf20Sopenharmony_ci if (percent == 0) { 13428c2ecf20Sopenharmony_ci /* 13438c2ecf20Sopenharmony_ci * We will not report 0% unless we've got 13448c2ecf20Sopenharmony_ci * the LOW_BAT IRQ, no matter what the FG 13458c2ecf20Sopenharmony_ci * algorithm says. 13468c2ecf20Sopenharmony_ci */ 13478c2ecf20Sopenharmony_ci di->bat_cap.prev_percent = 1; 13488c2ecf20Sopenharmony_ci percent = 1; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci changed = true; 13518c2ecf20Sopenharmony_ci } else if (!(!di->flags.charging && 13528c2ecf20Sopenharmony_ci percent > di->bat_cap.prev_percent) || init) { 13538c2ecf20Sopenharmony_ci /* 13548c2ecf20Sopenharmony_ci * We do not allow reported capacity to go up 13558c2ecf20Sopenharmony_ci * unless we're charging or if we're in init 13568c2ecf20Sopenharmony_ci */ 13578c2ecf20Sopenharmony_ci dev_dbg(di->dev, 13588c2ecf20Sopenharmony_ci "capacity changed from %d to %d (%d)\n", 13598c2ecf20Sopenharmony_ci di->bat_cap.prev_percent, 13608c2ecf20Sopenharmony_ci percent, 13618c2ecf20Sopenharmony_ci di->bat_cap.permille); 13628c2ecf20Sopenharmony_ci di->bat_cap.prev_percent = percent; 13638c2ecf20Sopenharmony_ci di->bat_cap.prev_mah = di->bat_cap.mah; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci changed = true; 13668c2ecf20Sopenharmony_ci } else { 13678c2ecf20Sopenharmony_ci dev_dbg(di->dev, "capacity not allowed to go up since " 13688c2ecf20Sopenharmony_ci "no charger is connected: %d to %d (%d)\n", 13698c2ecf20Sopenharmony_ci di->bat_cap.prev_percent, 13708c2ecf20Sopenharmony_ci percent, 13718c2ecf20Sopenharmony_ci di->bat_cap.permille); 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci if (changed) { 13768c2ecf20Sopenharmony_ci if (di->bm->capacity_scaling) { 13778c2ecf20Sopenharmony_ci di->bat_cap.cap_scale.scaled_cap = 13788c2ecf20Sopenharmony_ci ab8500_fg_calculate_scaled_capacity(di); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci dev_info(di->dev, "capacity=%d (%d)\n", 13818c2ecf20Sopenharmony_ci di->bat_cap.prev_percent, 13828c2ecf20Sopenharmony_ci di->bat_cap.cap_scale.scaled_cap); 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci power_supply_changed(di->fg_psy); 13858c2ecf20Sopenharmony_ci if (di->flags.fully_charged && di->flags.force_full) { 13868c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Battery full, notifying.\n"); 13878c2ecf20Sopenharmony_ci di->flags.force_full = false; 13888c2ecf20Sopenharmony_ci sysfs_notify(&di->fg_kobject, NULL, "charge_full"); 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci sysfs_notify(&di->fg_kobject, NULL, "charge_now"); 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci} 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_cistatic void ab8500_fg_charge_state_to(struct ab8500_fg *di, 13958c2ecf20Sopenharmony_ci enum ab8500_fg_charge_state new_state) 13968c2ecf20Sopenharmony_ci{ 13978c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Charge state from %d [%s] to %d [%s]\n", 13988c2ecf20Sopenharmony_ci di->charge_state, 13998c2ecf20Sopenharmony_ci charge_state[di->charge_state], 14008c2ecf20Sopenharmony_ci new_state, 14018c2ecf20Sopenharmony_ci charge_state[new_state]); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci di->charge_state = new_state; 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_cistatic void ab8500_fg_discharge_state_to(struct ab8500_fg *di, 14078c2ecf20Sopenharmony_ci enum ab8500_fg_discharge_state new_state) 14088c2ecf20Sopenharmony_ci{ 14098c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Discharge state from %d [%s] to %d [%s]\n", 14108c2ecf20Sopenharmony_ci di->discharge_state, 14118c2ecf20Sopenharmony_ci discharge_state[di->discharge_state], 14128c2ecf20Sopenharmony_ci new_state, 14138c2ecf20Sopenharmony_ci discharge_state[new_state]); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci di->discharge_state = new_state; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci/** 14198c2ecf20Sopenharmony_ci * ab8500_fg_algorithm_charging() - FG algorithm for when charging 14208c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 14218c2ecf20Sopenharmony_ci * 14228c2ecf20Sopenharmony_ci * Battery capacity calculation state machine for when we're charging 14238c2ecf20Sopenharmony_ci */ 14248c2ecf20Sopenharmony_cistatic void ab8500_fg_algorithm_charging(struct ab8500_fg *di) 14258c2ecf20Sopenharmony_ci{ 14268c2ecf20Sopenharmony_ci /* 14278c2ecf20Sopenharmony_ci * If we change to discharge mode 14288c2ecf20Sopenharmony_ci * we should start with recovery 14298c2ecf20Sopenharmony_ci */ 14308c2ecf20Sopenharmony_ci if (di->discharge_state != AB8500_FG_DISCHARGE_INIT_RECOVERY) 14318c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, 14328c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_INIT_RECOVERY); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci switch (di->charge_state) { 14358c2ecf20Sopenharmony_ci case AB8500_FG_CHARGE_INIT: 14368c2ecf20Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE( 14378c2ecf20Sopenharmony_ci di->bm->fg_params->accu_charging); 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 14408c2ecf20Sopenharmony_ci ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_READOUT); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci break; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci case AB8500_FG_CHARGE_READOUT: 14458c2ecf20Sopenharmony_ci /* 14468c2ecf20Sopenharmony_ci * Read the FG and calculate the new capacity 14478c2ecf20Sopenharmony_ci */ 14488c2ecf20Sopenharmony_ci mutex_lock(&di->cc_lock); 14498c2ecf20Sopenharmony_ci if (!di->flags.conv_done && !di->flags.force_full) { 14508c2ecf20Sopenharmony_ci /* Wasn't the CC IRQ that got us here */ 14518c2ecf20Sopenharmony_ci mutex_unlock(&di->cc_lock); 14528c2ecf20Sopenharmony_ci dev_dbg(di->dev, "%s CC conv not done\n", 14538c2ecf20Sopenharmony_ci __func__); 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci break; 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci di->flags.conv_done = false; 14588c2ecf20Sopenharmony_ci mutex_unlock(&di->cc_lock); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci ab8500_fg_calc_cap_charging(di); 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci break; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci default: 14658c2ecf20Sopenharmony_ci break; 14668c2ecf20Sopenharmony_ci } 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci /* Check capacity limits */ 14698c2ecf20Sopenharmony_ci ab8500_fg_check_capacity_limits(di, false); 14708c2ecf20Sopenharmony_ci} 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_cistatic void force_capacity(struct ab8500_fg *di) 14738c2ecf20Sopenharmony_ci{ 14748c2ecf20Sopenharmony_ci int cap; 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci ab8500_fg_clear_cap_samples(di); 14778c2ecf20Sopenharmony_ci cap = di->bat_cap.user_mah; 14788c2ecf20Sopenharmony_ci if (cap > di->bat_cap.max_mah_design) { 14798c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Remaining cap %d can't be bigger than total" 14808c2ecf20Sopenharmony_ci " %d\n", cap, di->bat_cap.max_mah_design); 14818c2ecf20Sopenharmony_ci cap = di->bat_cap.max_mah_design; 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci ab8500_fg_fill_cap_sample(di, di->bat_cap.user_mah); 14848c2ecf20Sopenharmony_ci di->bat_cap.permille = ab8500_fg_convert_mah_to_permille(di, cap); 14858c2ecf20Sopenharmony_ci di->bat_cap.mah = cap; 14868c2ecf20Sopenharmony_ci ab8500_fg_check_capacity_limits(di, true); 14878c2ecf20Sopenharmony_ci} 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_cistatic bool check_sysfs_capacity(struct ab8500_fg *di) 14908c2ecf20Sopenharmony_ci{ 14918c2ecf20Sopenharmony_ci int cap, lower, upper; 14928c2ecf20Sopenharmony_ci int cap_permille; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci cap = di->bat_cap.user_mah; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci cap_permille = ab8500_fg_convert_mah_to_permille(di, 14978c2ecf20Sopenharmony_ci di->bat_cap.user_mah); 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci lower = di->bat_cap.permille - di->bm->fg_params->user_cap_limit * 10; 15008c2ecf20Sopenharmony_ci upper = di->bat_cap.permille + di->bm->fg_params->user_cap_limit * 10; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci if (lower < 0) 15038c2ecf20Sopenharmony_ci lower = 0; 15048c2ecf20Sopenharmony_ci /* 1000 is permille, -> 100 percent */ 15058c2ecf20Sopenharmony_ci if (upper > 1000) 15068c2ecf20Sopenharmony_ci upper = 1000; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Capacity limits:" 15098c2ecf20Sopenharmony_ci " (Lower: %d User: %d Upper: %d) [user: %d, was: %d]\n", 15108c2ecf20Sopenharmony_ci lower, cap_permille, upper, cap, di->bat_cap.mah); 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci /* If within limits, use the saved capacity and exit estimation...*/ 15138c2ecf20Sopenharmony_ci if (cap_permille > lower && cap_permille < upper) { 15148c2ecf20Sopenharmony_ci dev_dbg(di->dev, "OK! Using users cap %d uAh now\n", cap); 15158c2ecf20Sopenharmony_ci force_capacity(di); 15168c2ecf20Sopenharmony_ci return true; 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Capacity from user out of limits, ignoring"); 15198c2ecf20Sopenharmony_ci return false; 15208c2ecf20Sopenharmony_ci} 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci/** 15238c2ecf20Sopenharmony_ci * ab8500_fg_algorithm_discharging() - FG algorithm for when discharging 15248c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 15258c2ecf20Sopenharmony_ci * 15268c2ecf20Sopenharmony_ci * Battery capacity calculation state machine for when we're discharging 15278c2ecf20Sopenharmony_ci */ 15288c2ecf20Sopenharmony_cistatic void ab8500_fg_algorithm_discharging(struct ab8500_fg *di) 15298c2ecf20Sopenharmony_ci{ 15308c2ecf20Sopenharmony_ci int sleep_time; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci /* If we change to charge mode we should start with init */ 15338c2ecf20Sopenharmony_ci if (di->charge_state != AB8500_FG_CHARGE_INIT) 15348c2ecf20Sopenharmony_ci ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci switch (di->discharge_state) { 15378c2ecf20Sopenharmony_ci case AB8500_FG_DISCHARGE_INIT: 15388c2ecf20Sopenharmony_ci /* We use the FG IRQ to work on */ 15398c2ecf20Sopenharmony_ci di->init_cnt = 0; 15408c2ecf20Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer); 15418c2ecf20Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 15428c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, 15438c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_INITMEASURING); 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci fallthrough; 15468c2ecf20Sopenharmony_ci case AB8500_FG_DISCHARGE_INITMEASURING: 15478c2ecf20Sopenharmony_ci /* 15488c2ecf20Sopenharmony_ci * Discard a number of samples during startup. 15498c2ecf20Sopenharmony_ci * After that, use compensated voltage for a few 15508c2ecf20Sopenharmony_ci * samples to get an initial capacity. 15518c2ecf20Sopenharmony_ci * Then go to READOUT 15528c2ecf20Sopenharmony_ci */ 15538c2ecf20Sopenharmony_ci sleep_time = di->bm->fg_params->init_timer; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci /* Discard the first [x] seconds */ 15568c2ecf20Sopenharmony_ci if (di->init_cnt > di->bm->fg_params->init_discard_time) { 15578c2ecf20Sopenharmony_ci ab8500_fg_calc_cap_discharge_voltage(di, true); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci ab8500_fg_check_capacity_limits(di, true); 15608c2ecf20Sopenharmony_ci } 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci di->init_cnt += sleep_time; 15638c2ecf20Sopenharmony_ci if (di->init_cnt > di->bm->fg_params->init_total_time) 15648c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, 15658c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT_INIT); 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci break; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci case AB8500_FG_DISCHARGE_INIT_RECOVERY: 15708c2ecf20Sopenharmony_ci di->recovery_cnt = 0; 15718c2ecf20Sopenharmony_ci di->recovery_needed = true; 15728c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, 15738c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_RECOVERY); 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci fallthrough; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci case AB8500_FG_DISCHARGE_RECOVERY: 15788c2ecf20Sopenharmony_ci sleep_time = di->bm->fg_params->recovery_sleep_timer; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci /* 15818c2ecf20Sopenharmony_ci * We should check the power consumption 15828c2ecf20Sopenharmony_ci * If low, go to READOUT (after x min) or 15838c2ecf20Sopenharmony_ci * RECOVERY_SLEEP if time left. 15848c2ecf20Sopenharmony_ci * If high, go to READOUT 15858c2ecf20Sopenharmony_ci */ 15868c2ecf20Sopenharmony_ci di->inst_curr = ab8500_fg_inst_curr_blocking(di); 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci if (ab8500_fg_is_low_curr(di, di->inst_curr)) { 15898c2ecf20Sopenharmony_ci if (di->recovery_cnt > 15908c2ecf20Sopenharmony_ci di->bm->fg_params->recovery_total_time) { 15918c2ecf20Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE( 15928c2ecf20Sopenharmony_ci di->bm->fg_params->accu_high_curr); 15938c2ecf20Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 15948c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, 15958c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT); 15968c2ecf20Sopenharmony_ci di->recovery_needed = false; 15978c2ecf20Sopenharmony_ci } else { 15988c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, 15998c2ecf20Sopenharmony_ci &di->fg_periodic_work, 16008c2ecf20Sopenharmony_ci sleep_time * HZ); 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci di->recovery_cnt += sleep_time; 16038c2ecf20Sopenharmony_ci } else { 16048c2ecf20Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE( 16058c2ecf20Sopenharmony_ci di->bm->fg_params->accu_high_curr); 16068c2ecf20Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 16078c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, 16088c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT); 16098c2ecf20Sopenharmony_ci } 16108c2ecf20Sopenharmony_ci break; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci case AB8500_FG_DISCHARGE_READOUT_INIT: 16138c2ecf20Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE( 16148c2ecf20Sopenharmony_ci di->bm->fg_params->accu_high_curr); 16158c2ecf20Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 16168c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, 16178c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT); 16188c2ecf20Sopenharmony_ci break; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci case AB8500_FG_DISCHARGE_READOUT: 16218c2ecf20Sopenharmony_ci di->inst_curr = ab8500_fg_inst_curr_blocking(di); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci if (ab8500_fg_is_low_curr(di, di->inst_curr)) { 16248c2ecf20Sopenharmony_ci /* Detect mode change */ 16258c2ecf20Sopenharmony_ci if (di->high_curr_mode) { 16268c2ecf20Sopenharmony_ci di->high_curr_mode = false; 16278c2ecf20Sopenharmony_ci di->high_curr_cnt = 0; 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci if (di->recovery_needed) { 16318c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, 16328c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_INIT_RECOVERY); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, 16358c2ecf20Sopenharmony_ci &di->fg_periodic_work, 0); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci break; 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci ab8500_fg_calc_cap_discharge_voltage(di, true); 16418c2ecf20Sopenharmony_ci } else { 16428c2ecf20Sopenharmony_ci mutex_lock(&di->cc_lock); 16438c2ecf20Sopenharmony_ci if (!di->flags.conv_done) { 16448c2ecf20Sopenharmony_ci /* Wasn't the CC IRQ that got us here */ 16458c2ecf20Sopenharmony_ci mutex_unlock(&di->cc_lock); 16468c2ecf20Sopenharmony_ci dev_dbg(di->dev, "%s CC conv not done\n", 16478c2ecf20Sopenharmony_ci __func__); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci break; 16508c2ecf20Sopenharmony_ci } 16518c2ecf20Sopenharmony_ci di->flags.conv_done = false; 16528c2ecf20Sopenharmony_ci mutex_unlock(&di->cc_lock); 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci /* Detect mode change */ 16558c2ecf20Sopenharmony_ci if (!di->high_curr_mode) { 16568c2ecf20Sopenharmony_ci di->high_curr_mode = true; 16578c2ecf20Sopenharmony_ci di->high_curr_cnt = 0; 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci di->high_curr_cnt += 16618c2ecf20Sopenharmony_ci di->bm->fg_params->accu_high_curr; 16628c2ecf20Sopenharmony_ci if (di->high_curr_cnt > 16638c2ecf20Sopenharmony_ci di->bm->fg_params->high_curr_time) 16648c2ecf20Sopenharmony_ci di->recovery_needed = true; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci ab8500_fg_calc_cap_discharge_fg(di); 16678c2ecf20Sopenharmony_ci } 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci ab8500_fg_check_capacity_limits(di, false); 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci break; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci case AB8500_FG_DISCHARGE_WAKEUP: 16748c2ecf20Sopenharmony_ci ab8500_fg_calc_cap_discharge_voltage(di, true); 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE( 16778c2ecf20Sopenharmony_ci di->bm->fg_params->accu_high_curr); 16788c2ecf20Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 16798c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, 16808c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT); 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci ab8500_fg_check_capacity_limits(di, false); 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci break; 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci default: 16878c2ecf20Sopenharmony_ci break; 16888c2ecf20Sopenharmony_ci } 16898c2ecf20Sopenharmony_ci} 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci/** 16928c2ecf20Sopenharmony_ci * ab8500_fg_algorithm_calibrate() - Internal columb counter offset calibration 16938c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 16948c2ecf20Sopenharmony_ci * 16958c2ecf20Sopenharmony_ci */ 16968c2ecf20Sopenharmony_cistatic void ab8500_fg_algorithm_calibrate(struct ab8500_fg *di) 16978c2ecf20Sopenharmony_ci{ 16988c2ecf20Sopenharmony_ci int ret; 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci switch (di->calib_state) { 17018c2ecf20Sopenharmony_ci case AB8500_FG_CALIB_INIT: 17028c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Calibration ongoing...\n"); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 17058c2ecf20Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 17068c2ecf20Sopenharmony_ci CC_INT_CAL_N_AVG_MASK, CC_INT_CAL_SAMPLES_8); 17078c2ecf20Sopenharmony_ci if (ret < 0) 17088c2ecf20Sopenharmony_ci goto err; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 17118c2ecf20Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 17128c2ecf20Sopenharmony_ci CC_INTAVGOFFSET_ENA, CC_INTAVGOFFSET_ENA); 17138c2ecf20Sopenharmony_ci if (ret < 0) 17148c2ecf20Sopenharmony_ci goto err; 17158c2ecf20Sopenharmony_ci di->calib_state = AB8500_FG_CALIB_WAIT; 17168c2ecf20Sopenharmony_ci break; 17178c2ecf20Sopenharmony_ci case AB8500_FG_CALIB_END: 17188c2ecf20Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 17198c2ecf20Sopenharmony_ci AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 17208c2ecf20Sopenharmony_ci CC_MUXOFFSET, CC_MUXOFFSET); 17218c2ecf20Sopenharmony_ci if (ret < 0) 17228c2ecf20Sopenharmony_ci goto err; 17238c2ecf20Sopenharmony_ci di->flags.calibrate = false; 17248c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Calibration done...\n"); 17258c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 17268c2ecf20Sopenharmony_ci break; 17278c2ecf20Sopenharmony_ci case AB8500_FG_CALIB_WAIT: 17288c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Calibration WFI\n"); 17298c2ecf20Sopenharmony_ci default: 17308c2ecf20Sopenharmony_ci break; 17318c2ecf20Sopenharmony_ci } 17328c2ecf20Sopenharmony_ci return; 17338c2ecf20Sopenharmony_cierr: 17348c2ecf20Sopenharmony_ci /* Something went wrong, don't calibrate then */ 17358c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to calibrate the CC\n"); 17368c2ecf20Sopenharmony_ci di->flags.calibrate = false; 17378c2ecf20Sopenharmony_ci di->calib_state = AB8500_FG_CALIB_INIT; 17388c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 17398c2ecf20Sopenharmony_ci} 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci/** 17428c2ecf20Sopenharmony_ci * ab8500_fg_algorithm() - Entry point for the FG algorithm 17438c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 17448c2ecf20Sopenharmony_ci * 17458c2ecf20Sopenharmony_ci * Entry point for the battery capacity calculation state machine 17468c2ecf20Sopenharmony_ci */ 17478c2ecf20Sopenharmony_cistatic void ab8500_fg_algorithm(struct ab8500_fg *di) 17488c2ecf20Sopenharmony_ci{ 17498c2ecf20Sopenharmony_ci if (di->flags.calibrate) 17508c2ecf20Sopenharmony_ci ab8500_fg_algorithm_calibrate(di); 17518c2ecf20Sopenharmony_ci else { 17528c2ecf20Sopenharmony_ci if (di->flags.charging) 17538c2ecf20Sopenharmony_ci ab8500_fg_algorithm_charging(di); 17548c2ecf20Sopenharmony_ci else 17558c2ecf20Sopenharmony_ci ab8500_fg_algorithm_discharging(di); 17568c2ecf20Sopenharmony_ci } 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d %d " 17598c2ecf20Sopenharmony_ci "%d %d %d %d %d %d %d\n", 17608c2ecf20Sopenharmony_ci di->bat_cap.max_mah_design, 17618c2ecf20Sopenharmony_ci di->bat_cap.max_mah, 17628c2ecf20Sopenharmony_ci di->bat_cap.mah, 17638c2ecf20Sopenharmony_ci di->bat_cap.permille, 17648c2ecf20Sopenharmony_ci di->bat_cap.level, 17658c2ecf20Sopenharmony_ci di->bat_cap.prev_mah, 17668c2ecf20Sopenharmony_ci di->bat_cap.prev_percent, 17678c2ecf20Sopenharmony_ci di->bat_cap.prev_level, 17688c2ecf20Sopenharmony_ci di->vbat, 17698c2ecf20Sopenharmony_ci di->inst_curr, 17708c2ecf20Sopenharmony_ci di->avg_curr, 17718c2ecf20Sopenharmony_ci di->accu_charge, 17728c2ecf20Sopenharmony_ci di->flags.charging, 17738c2ecf20Sopenharmony_ci di->charge_state, 17748c2ecf20Sopenharmony_ci di->discharge_state, 17758c2ecf20Sopenharmony_ci di->high_curr_mode, 17768c2ecf20Sopenharmony_ci di->recovery_needed); 17778c2ecf20Sopenharmony_ci} 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci/** 17808c2ecf20Sopenharmony_ci * ab8500_fg_periodic_work() - Run the FG state machine periodically 17818c2ecf20Sopenharmony_ci * @work: pointer to the work_struct structure 17828c2ecf20Sopenharmony_ci * 17838c2ecf20Sopenharmony_ci * Work queue function for periodic work 17848c2ecf20Sopenharmony_ci */ 17858c2ecf20Sopenharmony_cistatic void ab8500_fg_periodic_work(struct work_struct *work) 17868c2ecf20Sopenharmony_ci{ 17878c2ecf20Sopenharmony_ci struct ab8500_fg *di = container_of(work, struct ab8500_fg, 17888c2ecf20Sopenharmony_ci fg_periodic_work.work); 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci if (di->init_capacity) { 17918c2ecf20Sopenharmony_ci /* Get an initial capacity calculation */ 17928c2ecf20Sopenharmony_ci ab8500_fg_calc_cap_discharge_voltage(di, true); 17938c2ecf20Sopenharmony_ci ab8500_fg_check_capacity_limits(di, true); 17948c2ecf20Sopenharmony_ci di->init_capacity = false; 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 17978c2ecf20Sopenharmony_ci } else if (di->flags.user_cap) { 17988c2ecf20Sopenharmony_ci if (check_sysfs_capacity(di)) { 17998c2ecf20Sopenharmony_ci ab8500_fg_check_capacity_limits(di, true); 18008c2ecf20Sopenharmony_ci if (di->flags.charging) 18018c2ecf20Sopenharmony_ci ab8500_fg_charge_state_to(di, 18028c2ecf20Sopenharmony_ci AB8500_FG_CHARGE_INIT); 18038c2ecf20Sopenharmony_ci else 18048c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, 18058c2ecf20Sopenharmony_ci AB8500_FG_DISCHARGE_READOUT_INIT); 18068c2ecf20Sopenharmony_ci } 18078c2ecf20Sopenharmony_ci di->flags.user_cap = false; 18088c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 18098c2ecf20Sopenharmony_ci } else 18108c2ecf20Sopenharmony_ci ab8500_fg_algorithm(di); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci} 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci/** 18158c2ecf20Sopenharmony_ci * ab8500_fg_check_hw_failure_work() - Check OVV_BAT condition 18168c2ecf20Sopenharmony_ci * @work: pointer to the work_struct structure 18178c2ecf20Sopenharmony_ci * 18188c2ecf20Sopenharmony_ci * Work queue function for checking the OVV_BAT condition 18198c2ecf20Sopenharmony_ci */ 18208c2ecf20Sopenharmony_cistatic void ab8500_fg_check_hw_failure_work(struct work_struct *work) 18218c2ecf20Sopenharmony_ci{ 18228c2ecf20Sopenharmony_ci int ret; 18238c2ecf20Sopenharmony_ci u8 reg_value; 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci struct ab8500_fg *di = container_of(work, struct ab8500_fg, 18268c2ecf20Sopenharmony_ci fg_check_hw_failure_work.work); 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci /* 18298c2ecf20Sopenharmony_ci * If we have had a battery over-voltage situation, 18308c2ecf20Sopenharmony_ci * check ovv-bit to see if it should be reset. 18318c2ecf20Sopenharmony_ci */ 18328c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, 18338c2ecf20Sopenharmony_ci AB8500_CHARGER, AB8500_CH_STAT_REG, 18348c2ecf20Sopenharmony_ci ®_value); 18358c2ecf20Sopenharmony_ci if (ret < 0) { 18368c2ecf20Sopenharmony_ci dev_err(di->dev, "%s ab8500 read failed\n", __func__); 18378c2ecf20Sopenharmony_ci return; 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci if ((reg_value & BATT_OVV) == BATT_OVV) { 18408c2ecf20Sopenharmony_ci if (!di->flags.bat_ovv) { 18418c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Battery OVV\n"); 18428c2ecf20Sopenharmony_ci di->flags.bat_ovv = true; 18438c2ecf20Sopenharmony_ci power_supply_changed(di->fg_psy); 18448c2ecf20Sopenharmony_ci } 18458c2ecf20Sopenharmony_ci /* Not yet recovered from ovv, reschedule this test */ 18468c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 18478c2ecf20Sopenharmony_ci HZ); 18488c2ecf20Sopenharmony_ci } else { 18498c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Battery recovered from OVV\n"); 18508c2ecf20Sopenharmony_ci di->flags.bat_ovv = false; 18518c2ecf20Sopenharmony_ci power_supply_changed(di->fg_psy); 18528c2ecf20Sopenharmony_ci } 18538c2ecf20Sopenharmony_ci} 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci/** 18568c2ecf20Sopenharmony_ci * ab8500_fg_low_bat_work() - Check LOW_BAT condition 18578c2ecf20Sopenharmony_ci * @work: pointer to the work_struct structure 18588c2ecf20Sopenharmony_ci * 18598c2ecf20Sopenharmony_ci * Work queue function for checking the LOW_BAT condition 18608c2ecf20Sopenharmony_ci */ 18618c2ecf20Sopenharmony_cistatic void ab8500_fg_low_bat_work(struct work_struct *work) 18628c2ecf20Sopenharmony_ci{ 18638c2ecf20Sopenharmony_ci int vbat; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci struct ab8500_fg *di = container_of(work, struct ab8500_fg, 18668c2ecf20Sopenharmony_ci fg_low_bat_work.work); 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci vbat = ab8500_fg_bat_voltage(di); 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci /* Check if LOW_BAT still fulfilled */ 18718c2ecf20Sopenharmony_ci if (vbat < di->bm->fg_params->lowbat_threshold) { 18728c2ecf20Sopenharmony_ci /* Is it time to shut down? */ 18738c2ecf20Sopenharmony_ci if (di->low_bat_cnt < 1) { 18748c2ecf20Sopenharmony_ci di->flags.low_bat = true; 18758c2ecf20Sopenharmony_ci dev_warn(di->dev, "Shut down pending...\n"); 18768c2ecf20Sopenharmony_ci } else { 18778c2ecf20Sopenharmony_ci /* 18788c2ecf20Sopenharmony_ci * Else we need to re-schedule this check to be able to detect 18798c2ecf20Sopenharmony_ci * if the voltage increases again during charging or 18808c2ecf20Sopenharmony_ci * due to decreasing load. 18818c2ecf20Sopenharmony_ci */ 18828c2ecf20Sopenharmony_ci di->low_bat_cnt--; 18838c2ecf20Sopenharmony_ci dev_warn(di->dev, "Battery voltage still LOW\n"); 18848c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_low_bat_work, 18858c2ecf20Sopenharmony_ci round_jiffies(LOW_BAT_CHECK_INTERVAL)); 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci } else { 18888c2ecf20Sopenharmony_ci di->flags.low_bat_delay = false; 18898c2ecf20Sopenharmony_ci di->low_bat_cnt = 10; 18908c2ecf20Sopenharmony_ci dev_warn(di->dev, "Battery voltage OK again\n"); 18918c2ecf20Sopenharmony_ci } 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci /* This is needed to dispatch LOW_BAT */ 18948c2ecf20Sopenharmony_ci ab8500_fg_check_capacity_limits(di, false); 18958c2ecf20Sopenharmony_ci} 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci/** 18988c2ecf20Sopenharmony_ci * ab8500_fg_battok_calc - calculate the bit pattern corresponding 18998c2ecf20Sopenharmony_ci * to the target voltage. 19008c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 19018c2ecf20Sopenharmony_ci * @target: target voltage 19028c2ecf20Sopenharmony_ci * 19038c2ecf20Sopenharmony_ci * Returns bit pattern closest to the target voltage 19048c2ecf20Sopenharmony_ci * valid return values are 0-14. (0-BATT_OK_MAX_NR_INCREMENTS) 19058c2ecf20Sopenharmony_ci */ 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_cistatic int ab8500_fg_battok_calc(struct ab8500_fg *di, int target) 19088c2ecf20Sopenharmony_ci{ 19098c2ecf20Sopenharmony_ci if (target > BATT_OK_MIN + 19108c2ecf20Sopenharmony_ci (BATT_OK_INCREMENT * BATT_OK_MAX_NR_INCREMENTS)) 19118c2ecf20Sopenharmony_ci return BATT_OK_MAX_NR_INCREMENTS; 19128c2ecf20Sopenharmony_ci if (target < BATT_OK_MIN) 19138c2ecf20Sopenharmony_ci return 0; 19148c2ecf20Sopenharmony_ci return (target - BATT_OK_MIN) / BATT_OK_INCREMENT; 19158c2ecf20Sopenharmony_ci} 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci/** 19188c2ecf20Sopenharmony_ci * ab8500_fg_battok_init_hw_register - init battok levels 19198c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 19208c2ecf20Sopenharmony_ci * 19218c2ecf20Sopenharmony_ci */ 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_cistatic int ab8500_fg_battok_init_hw_register(struct ab8500_fg *di) 19248c2ecf20Sopenharmony_ci{ 19258c2ecf20Sopenharmony_ci int selected; 19268c2ecf20Sopenharmony_ci int sel0; 19278c2ecf20Sopenharmony_ci int sel1; 19288c2ecf20Sopenharmony_ci int cbp_sel0; 19298c2ecf20Sopenharmony_ci int cbp_sel1; 19308c2ecf20Sopenharmony_ci int ret; 19318c2ecf20Sopenharmony_ci int new_val; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci sel0 = di->bm->fg_params->battok_falling_th_sel0; 19348c2ecf20Sopenharmony_ci sel1 = di->bm->fg_params->battok_raising_th_sel1; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci cbp_sel0 = ab8500_fg_battok_calc(di, sel0); 19378c2ecf20Sopenharmony_ci cbp_sel1 = ab8500_fg_battok_calc(di, sel1); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci selected = BATT_OK_MIN + cbp_sel0 * BATT_OK_INCREMENT; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci if (selected != sel0) 19428c2ecf20Sopenharmony_ci dev_warn(di->dev, "Invalid voltage step:%d, using %d %d\n", 19438c2ecf20Sopenharmony_ci sel0, selected, cbp_sel0); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci selected = BATT_OK_MIN + cbp_sel1 * BATT_OK_INCREMENT; 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci if (selected != sel1) 19488c2ecf20Sopenharmony_ci dev_warn(di->dev, "Invalid voltage step:%d, using %d %d\n", 19498c2ecf20Sopenharmony_ci sel1, selected, cbp_sel1); 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci new_val = cbp_sel0 | (cbp_sel1 << 4); 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci dev_dbg(di->dev, "using: %x %d %d\n", new_val, cbp_sel0, cbp_sel1); 19548c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_SYS_CTRL2_BLOCK, 19558c2ecf20Sopenharmony_ci AB8500_BATT_OK_REG, new_val); 19568c2ecf20Sopenharmony_ci return ret; 19578c2ecf20Sopenharmony_ci} 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci/** 19608c2ecf20Sopenharmony_ci * ab8500_fg_instant_work() - Run the FG state machine instantly 19618c2ecf20Sopenharmony_ci * @work: pointer to the work_struct structure 19628c2ecf20Sopenharmony_ci * 19638c2ecf20Sopenharmony_ci * Work queue function for instant work 19648c2ecf20Sopenharmony_ci */ 19658c2ecf20Sopenharmony_cistatic void ab8500_fg_instant_work(struct work_struct *work) 19668c2ecf20Sopenharmony_ci{ 19678c2ecf20Sopenharmony_ci struct ab8500_fg *di = container_of(work, struct ab8500_fg, fg_work); 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci ab8500_fg_algorithm(di); 19708c2ecf20Sopenharmony_ci} 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci/** 19738c2ecf20Sopenharmony_ci * ab8500_fg_cc_data_end_handler() - end of data conversion isr. 19748c2ecf20Sopenharmony_ci * @irq: interrupt number 19758c2ecf20Sopenharmony_ci * @_di: pointer to the ab8500_fg structure 19768c2ecf20Sopenharmony_ci * 19778c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 19788c2ecf20Sopenharmony_ci */ 19798c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_fg_cc_data_end_handler(int irq, void *_di) 19808c2ecf20Sopenharmony_ci{ 19818c2ecf20Sopenharmony_ci struct ab8500_fg *di = _di; 19828c2ecf20Sopenharmony_ci if (!di->nbr_cceoc_irq_cnt) { 19838c2ecf20Sopenharmony_ci di->nbr_cceoc_irq_cnt++; 19848c2ecf20Sopenharmony_ci complete(&di->ab8500_fg_started); 19858c2ecf20Sopenharmony_ci } else { 19868c2ecf20Sopenharmony_ci di->nbr_cceoc_irq_cnt = 0; 19878c2ecf20Sopenharmony_ci complete(&di->ab8500_fg_complete); 19888c2ecf20Sopenharmony_ci } 19898c2ecf20Sopenharmony_ci return IRQ_HANDLED; 19908c2ecf20Sopenharmony_ci} 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci/** 19938c2ecf20Sopenharmony_ci * ab8500_fg_cc_int_calib_handler () - end of calibration isr. 19948c2ecf20Sopenharmony_ci * @irq: interrupt number 19958c2ecf20Sopenharmony_ci * @_di: pointer to the ab8500_fg structure 19968c2ecf20Sopenharmony_ci * 19978c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 19988c2ecf20Sopenharmony_ci */ 19998c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_fg_cc_int_calib_handler(int irq, void *_di) 20008c2ecf20Sopenharmony_ci{ 20018c2ecf20Sopenharmony_ci struct ab8500_fg *di = _di; 20028c2ecf20Sopenharmony_ci di->calib_state = AB8500_FG_CALIB_END; 20038c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 20048c2ecf20Sopenharmony_ci return IRQ_HANDLED; 20058c2ecf20Sopenharmony_ci} 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci/** 20088c2ecf20Sopenharmony_ci * ab8500_fg_cc_convend_handler() - isr to get battery avg current. 20098c2ecf20Sopenharmony_ci * @irq: interrupt number 20108c2ecf20Sopenharmony_ci * @_di: pointer to the ab8500_fg structure 20118c2ecf20Sopenharmony_ci * 20128c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 20138c2ecf20Sopenharmony_ci */ 20148c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_fg_cc_convend_handler(int irq, void *_di) 20158c2ecf20Sopenharmony_ci{ 20168c2ecf20Sopenharmony_ci struct ab8500_fg *di = _di; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci queue_work(di->fg_wq, &di->fg_acc_cur_work); 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci return IRQ_HANDLED; 20218c2ecf20Sopenharmony_ci} 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci/** 20248c2ecf20Sopenharmony_ci * ab8500_fg_batt_ovv_handler() - Battery OVV occured 20258c2ecf20Sopenharmony_ci * @irq: interrupt number 20268c2ecf20Sopenharmony_ci * @_di: pointer to the ab8500_fg structure 20278c2ecf20Sopenharmony_ci * 20288c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 20298c2ecf20Sopenharmony_ci */ 20308c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_fg_batt_ovv_handler(int irq, void *_di) 20318c2ecf20Sopenharmony_ci{ 20328c2ecf20Sopenharmony_ci struct ab8500_fg *di = _di; 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Battery OVV\n"); 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci /* Schedule a new HW failure check */ 20378c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_check_hw_failure_work, 0); 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci return IRQ_HANDLED; 20408c2ecf20Sopenharmony_ci} 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci/** 20438c2ecf20Sopenharmony_ci * ab8500_fg_lowbatf_handler() - Battery voltage is below LOW threshold 20448c2ecf20Sopenharmony_ci * @irq: interrupt number 20458c2ecf20Sopenharmony_ci * @_di: pointer to the ab8500_fg structure 20468c2ecf20Sopenharmony_ci * 20478c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED) 20488c2ecf20Sopenharmony_ci */ 20498c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_fg_lowbatf_handler(int irq, void *_di) 20508c2ecf20Sopenharmony_ci{ 20518c2ecf20Sopenharmony_ci struct ab8500_fg *di = _di; 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci /* Initiate handling in ab8500_fg_low_bat_work() if not already initiated. */ 20548c2ecf20Sopenharmony_ci if (!di->flags.low_bat_delay) { 20558c2ecf20Sopenharmony_ci dev_warn(di->dev, "Battery voltage is below LOW threshold\n"); 20568c2ecf20Sopenharmony_ci di->flags.low_bat_delay = true; 20578c2ecf20Sopenharmony_ci /* 20588c2ecf20Sopenharmony_ci * Start a timer to check LOW_BAT again after some time 20598c2ecf20Sopenharmony_ci * This is done to avoid shutdown on single voltage dips 20608c2ecf20Sopenharmony_ci */ 20618c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_low_bat_work, 20628c2ecf20Sopenharmony_ci round_jiffies(LOW_BAT_CHECK_INTERVAL)); 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci return IRQ_HANDLED; 20658c2ecf20Sopenharmony_ci} 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci/** 20688c2ecf20Sopenharmony_ci * ab8500_fg_get_property() - get the fg properties 20698c2ecf20Sopenharmony_ci * @psy: pointer to the power_supply structure 20708c2ecf20Sopenharmony_ci * @psp: pointer to the power_supply_property structure 20718c2ecf20Sopenharmony_ci * @val: pointer to the power_supply_propval union 20728c2ecf20Sopenharmony_ci * 20738c2ecf20Sopenharmony_ci * This function gets called when an application tries to get the 20748c2ecf20Sopenharmony_ci * fg properties by reading the sysfs files. 20758c2ecf20Sopenharmony_ci * voltage_now: battery voltage 20768c2ecf20Sopenharmony_ci * current_now: battery instant current 20778c2ecf20Sopenharmony_ci * current_avg: battery average current 20788c2ecf20Sopenharmony_ci * charge_full_design: capacity where battery is considered full 20798c2ecf20Sopenharmony_ci * charge_now: battery capacity in nAh 20808c2ecf20Sopenharmony_ci * capacity: capacity in percent 20818c2ecf20Sopenharmony_ci * capacity_level: capacity level 20828c2ecf20Sopenharmony_ci * 20838c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0 on success 20848c2ecf20Sopenharmony_ci */ 20858c2ecf20Sopenharmony_cistatic int ab8500_fg_get_property(struct power_supply *psy, 20868c2ecf20Sopenharmony_ci enum power_supply_property psp, 20878c2ecf20Sopenharmony_ci union power_supply_propval *val) 20888c2ecf20Sopenharmony_ci{ 20898c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci /* 20928c2ecf20Sopenharmony_ci * If battery is identified as unknown and charging of unknown 20938c2ecf20Sopenharmony_ci * batteries is disabled, we always report 100% capacity and 20948c2ecf20Sopenharmony_ci * capacity level UNKNOWN, since we can't calculate 20958c2ecf20Sopenharmony_ci * remaining capacity 20968c2ecf20Sopenharmony_ci */ 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci switch (psp) { 20998c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 21008c2ecf20Sopenharmony_ci if (di->flags.bat_ovv) 21018c2ecf20Sopenharmony_ci val->intval = BATT_OVV_VALUE * 1000; 21028c2ecf20Sopenharmony_ci else 21038c2ecf20Sopenharmony_ci val->intval = di->vbat * 1000; 21048c2ecf20Sopenharmony_ci break; 21058c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 21068c2ecf20Sopenharmony_ci val->intval = di->inst_curr * 1000; 21078c2ecf20Sopenharmony_ci break; 21088c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 21098c2ecf20Sopenharmony_ci val->intval = di->avg_curr * 1000; 21108c2ecf20Sopenharmony_ci break; 21118c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: 21128c2ecf20Sopenharmony_ci val->intval = ab8500_fg_convert_mah_to_uwh(di, 21138c2ecf20Sopenharmony_ci di->bat_cap.max_mah_design); 21148c2ecf20Sopenharmony_ci break; 21158c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_FULL: 21168c2ecf20Sopenharmony_ci val->intval = ab8500_fg_convert_mah_to_uwh(di, 21178c2ecf20Sopenharmony_ci di->bat_cap.max_mah); 21188c2ecf20Sopenharmony_ci break; 21198c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ENERGY_NOW: 21208c2ecf20Sopenharmony_ci if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && 21218c2ecf20Sopenharmony_ci di->flags.batt_id_received) 21228c2ecf20Sopenharmony_ci val->intval = ab8500_fg_convert_mah_to_uwh(di, 21238c2ecf20Sopenharmony_ci di->bat_cap.max_mah); 21248c2ecf20Sopenharmony_ci else 21258c2ecf20Sopenharmony_ci val->intval = ab8500_fg_convert_mah_to_uwh(di, 21268c2ecf20Sopenharmony_ci di->bat_cap.prev_mah); 21278c2ecf20Sopenharmony_ci break; 21288c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 21298c2ecf20Sopenharmony_ci val->intval = di->bat_cap.max_mah_design; 21308c2ecf20Sopenharmony_ci break; 21318c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_FULL: 21328c2ecf20Sopenharmony_ci val->intval = di->bat_cap.max_mah; 21338c2ecf20Sopenharmony_ci break; 21348c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_NOW: 21358c2ecf20Sopenharmony_ci if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && 21368c2ecf20Sopenharmony_ci di->flags.batt_id_received) 21378c2ecf20Sopenharmony_ci val->intval = di->bat_cap.max_mah; 21388c2ecf20Sopenharmony_ci else 21398c2ecf20Sopenharmony_ci val->intval = di->bat_cap.prev_mah; 21408c2ecf20Sopenharmony_ci break; 21418c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 21428c2ecf20Sopenharmony_ci if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && 21438c2ecf20Sopenharmony_ci di->flags.batt_id_received) 21448c2ecf20Sopenharmony_ci val->intval = 100; 21458c2ecf20Sopenharmony_ci else 21468c2ecf20Sopenharmony_ci val->intval = di->bat_cap.prev_percent; 21478c2ecf20Sopenharmony_ci break; 21488c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY_LEVEL: 21498c2ecf20Sopenharmony_ci if (di->flags.batt_unknown && !di->bm->chg_unknown_bat && 21508c2ecf20Sopenharmony_ci di->flags.batt_id_received) 21518c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; 21528c2ecf20Sopenharmony_ci else 21538c2ecf20Sopenharmony_ci val->intval = di->bat_cap.prev_level; 21548c2ecf20Sopenharmony_ci break; 21558c2ecf20Sopenharmony_ci default: 21568c2ecf20Sopenharmony_ci return -EINVAL; 21578c2ecf20Sopenharmony_ci } 21588c2ecf20Sopenharmony_ci return 0; 21598c2ecf20Sopenharmony_ci} 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_cistatic int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) 21628c2ecf20Sopenharmony_ci{ 21638c2ecf20Sopenharmony_ci struct power_supply *psy; 21648c2ecf20Sopenharmony_ci struct power_supply *ext = dev_get_drvdata(dev); 21658c2ecf20Sopenharmony_ci const char **supplicants = (const char **)ext->supplied_to; 21668c2ecf20Sopenharmony_ci struct ab8500_fg *di; 21678c2ecf20Sopenharmony_ci union power_supply_propval ret; 21688c2ecf20Sopenharmony_ci int j; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci psy = (struct power_supply *)data; 21718c2ecf20Sopenharmony_ci di = power_supply_get_drvdata(psy); 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci /* 21748c2ecf20Sopenharmony_ci * For all psy where the name of your driver 21758c2ecf20Sopenharmony_ci * appears in any supplied_to 21768c2ecf20Sopenharmony_ci */ 21778c2ecf20Sopenharmony_ci j = match_string(supplicants, ext->num_supplicants, psy->desc->name); 21788c2ecf20Sopenharmony_ci if (j < 0) 21798c2ecf20Sopenharmony_ci return 0; 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci /* Go through all properties for the psy */ 21828c2ecf20Sopenharmony_ci for (j = 0; j < ext->desc->num_properties; j++) { 21838c2ecf20Sopenharmony_ci enum power_supply_property prop; 21848c2ecf20Sopenharmony_ci prop = ext->desc->properties[j]; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci if (power_supply_get_property(ext, prop, &ret)) 21878c2ecf20Sopenharmony_ci continue; 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci switch (prop) { 21908c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 21918c2ecf20Sopenharmony_ci switch (ext->desc->type) { 21928c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 21938c2ecf20Sopenharmony_ci switch (ret.intval) { 21948c2ecf20Sopenharmony_ci case POWER_SUPPLY_STATUS_UNKNOWN: 21958c2ecf20Sopenharmony_ci case POWER_SUPPLY_STATUS_DISCHARGING: 21968c2ecf20Sopenharmony_ci case POWER_SUPPLY_STATUS_NOT_CHARGING: 21978c2ecf20Sopenharmony_ci if (!di->flags.charging) 21988c2ecf20Sopenharmony_ci break; 21998c2ecf20Sopenharmony_ci di->flags.charging = false; 22008c2ecf20Sopenharmony_ci di->flags.fully_charged = false; 22018c2ecf20Sopenharmony_ci if (di->bm->capacity_scaling) 22028c2ecf20Sopenharmony_ci ab8500_fg_update_cap_scalers(di); 22038c2ecf20Sopenharmony_ci queue_work(di->fg_wq, &di->fg_work); 22048c2ecf20Sopenharmony_ci break; 22058c2ecf20Sopenharmony_ci case POWER_SUPPLY_STATUS_FULL: 22068c2ecf20Sopenharmony_ci if (di->flags.fully_charged) 22078c2ecf20Sopenharmony_ci break; 22088c2ecf20Sopenharmony_ci di->flags.fully_charged = true; 22098c2ecf20Sopenharmony_ci di->flags.force_full = true; 22108c2ecf20Sopenharmony_ci /* Save current capacity as maximum */ 22118c2ecf20Sopenharmony_ci di->bat_cap.max_mah = di->bat_cap.mah; 22128c2ecf20Sopenharmony_ci queue_work(di->fg_wq, &di->fg_work); 22138c2ecf20Sopenharmony_ci break; 22148c2ecf20Sopenharmony_ci case POWER_SUPPLY_STATUS_CHARGING: 22158c2ecf20Sopenharmony_ci if (di->flags.charging && 22168c2ecf20Sopenharmony_ci !di->flags.fully_charged) 22178c2ecf20Sopenharmony_ci break; 22188c2ecf20Sopenharmony_ci di->flags.charging = true; 22198c2ecf20Sopenharmony_ci di->flags.fully_charged = false; 22208c2ecf20Sopenharmony_ci if (di->bm->capacity_scaling) 22218c2ecf20Sopenharmony_ci ab8500_fg_update_cap_scalers(di); 22228c2ecf20Sopenharmony_ci queue_work(di->fg_wq, &di->fg_work); 22238c2ecf20Sopenharmony_ci break; 22248c2ecf20Sopenharmony_ci } 22258c2ecf20Sopenharmony_ci default: 22268c2ecf20Sopenharmony_ci break; 22278c2ecf20Sopenharmony_ci } 22288c2ecf20Sopenharmony_ci break; 22298c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 22308c2ecf20Sopenharmony_ci switch (ext->desc->type) { 22318c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 22328c2ecf20Sopenharmony_ci if (!di->flags.batt_id_received && 22338c2ecf20Sopenharmony_ci di->bm->batt_id != BATTERY_UNKNOWN) { 22348c2ecf20Sopenharmony_ci const struct abx500_battery_type *b; 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci b = &(di->bm->bat_type[di->bm->batt_id]); 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci di->flags.batt_id_received = true; 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci di->bat_cap.max_mah_design = 22418c2ecf20Sopenharmony_ci MILLI_TO_MICRO * 22428c2ecf20Sopenharmony_ci b->charge_full_design; 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci di->bat_cap.max_mah = 22458c2ecf20Sopenharmony_ci di->bat_cap.max_mah_design; 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci di->vbat_nom = b->nominal_voltage; 22488c2ecf20Sopenharmony_ci } 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci if (ret.intval) 22518c2ecf20Sopenharmony_ci di->flags.batt_unknown = false; 22528c2ecf20Sopenharmony_ci else 22538c2ecf20Sopenharmony_ci di->flags.batt_unknown = true; 22548c2ecf20Sopenharmony_ci break; 22558c2ecf20Sopenharmony_ci default: 22568c2ecf20Sopenharmony_ci break; 22578c2ecf20Sopenharmony_ci } 22588c2ecf20Sopenharmony_ci break; 22598c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 22608c2ecf20Sopenharmony_ci switch (ext->desc->type) { 22618c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 22628c2ecf20Sopenharmony_ci if (di->flags.batt_id_received) 22638c2ecf20Sopenharmony_ci di->bat_temp = ret.intval; 22648c2ecf20Sopenharmony_ci break; 22658c2ecf20Sopenharmony_ci default: 22668c2ecf20Sopenharmony_ci break; 22678c2ecf20Sopenharmony_ci } 22688c2ecf20Sopenharmony_ci break; 22698c2ecf20Sopenharmony_ci default: 22708c2ecf20Sopenharmony_ci break; 22718c2ecf20Sopenharmony_ci } 22728c2ecf20Sopenharmony_ci } 22738c2ecf20Sopenharmony_ci return 0; 22748c2ecf20Sopenharmony_ci} 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci/** 22778c2ecf20Sopenharmony_ci * ab8500_fg_init_hw_registers() - Set up FG related registers 22788c2ecf20Sopenharmony_ci * @di: pointer to the ab8500_fg structure 22798c2ecf20Sopenharmony_ci * 22808c2ecf20Sopenharmony_ci * Set up battery OVV, low battery voltage registers 22818c2ecf20Sopenharmony_ci */ 22828c2ecf20Sopenharmony_cistatic int ab8500_fg_init_hw_registers(struct ab8500_fg *di) 22838c2ecf20Sopenharmony_ci{ 22848c2ecf20Sopenharmony_ci int ret; 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci /* Set VBAT OVV threshold */ 22878c2ecf20Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 22888c2ecf20Sopenharmony_ci AB8500_CHARGER, 22898c2ecf20Sopenharmony_ci AB8500_BATT_OVV, 22908c2ecf20Sopenharmony_ci BATT_OVV_TH_4P75, 22918c2ecf20Sopenharmony_ci BATT_OVV_TH_4P75); 22928c2ecf20Sopenharmony_ci if (ret) { 22938c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to set BATT_OVV\n"); 22948c2ecf20Sopenharmony_ci goto out; 22958c2ecf20Sopenharmony_ci } 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci /* Enable VBAT OVV detection */ 22988c2ecf20Sopenharmony_ci ret = abx500_mask_and_set_register_interruptible(di->dev, 22998c2ecf20Sopenharmony_ci AB8500_CHARGER, 23008c2ecf20Sopenharmony_ci AB8500_BATT_OVV, 23018c2ecf20Sopenharmony_ci BATT_OVV_ENA, 23028c2ecf20Sopenharmony_ci BATT_OVV_ENA); 23038c2ecf20Sopenharmony_ci if (ret) { 23048c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to enable BATT_OVV\n"); 23058c2ecf20Sopenharmony_ci goto out; 23068c2ecf20Sopenharmony_ci } 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci /* Low Battery Voltage */ 23098c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, 23108c2ecf20Sopenharmony_ci AB8500_SYS_CTRL2_BLOCK, 23118c2ecf20Sopenharmony_ci AB8500_LOW_BAT_REG, 23128c2ecf20Sopenharmony_ci ab8500_volt_to_regval( 23138c2ecf20Sopenharmony_ci di->bm->fg_params->lowbat_threshold) << 1 | 23148c2ecf20Sopenharmony_ci LOW_BAT_ENABLE); 23158c2ecf20Sopenharmony_ci if (ret) { 23168c2ecf20Sopenharmony_ci dev_err(di->dev, "%s write failed\n", __func__); 23178c2ecf20Sopenharmony_ci goto out; 23188c2ecf20Sopenharmony_ci } 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci /* Battery OK threshold */ 23218c2ecf20Sopenharmony_ci ret = ab8500_fg_battok_init_hw_register(di); 23228c2ecf20Sopenharmony_ci if (ret) { 23238c2ecf20Sopenharmony_ci dev_err(di->dev, "BattOk init write failed.\n"); 23248c2ecf20Sopenharmony_ci goto out; 23258c2ecf20Sopenharmony_ci } 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci if (is_ab8505(di->parent)) { 23288c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 23298c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_MAX_TIME_REG, di->bm->fg_params->pcut_max_time); 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci if (ret) { 23328c2ecf20Sopenharmony_ci dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__); 23338c2ecf20Sopenharmony_ci goto out; 23348c2ecf20Sopenharmony_ci } 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 23378c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time); 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci if (ret) { 23408c2ecf20Sopenharmony_ci dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__); 23418c2ecf20Sopenharmony_ci goto out; 23428c2ecf20Sopenharmony_ci } 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 23458c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart); 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_ci if (ret) { 23488c2ecf20Sopenharmony_ci dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__); 23498c2ecf20Sopenharmony_ci goto out; 23508c2ecf20Sopenharmony_ci } 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 23538c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time); 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci if (ret) { 23568c2ecf20Sopenharmony_ci dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__); 23578c2ecf20Sopenharmony_ci goto out; 23588c2ecf20Sopenharmony_ci } 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 23618c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable); 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci if (ret) { 23648c2ecf20Sopenharmony_ci dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__); 23658c2ecf20Sopenharmony_ci goto out; 23668c2ecf20Sopenharmony_ci } 23678c2ecf20Sopenharmony_ci } 23688c2ecf20Sopenharmony_ciout: 23698c2ecf20Sopenharmony_ci return ret; 23708c2ecf20Sopenharmony_ci} 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci/** 23738c2ecf20Sopenharmony_ci * ab8500_fg_external_power_changed() - callback for power supply changes 23748c2ecf20Sopenharmony_ci * @psy: pointer to the structure power_supply 23758c2ecf20Sopenharmony_ci * 23768c2ecf20Sopenharmony_ci * This function is the entry point of the pointer external_power_changed 23778c2ecf20Sopenharmony_ci * of the structure power_supply. 23788c2ecf20Sopenharmony_ci * This function gets executed when there is a change in any external power 23798c2ecf20Sopenharmony_ci * supply that this driver needs to be notified of. 23808c2ecf20Sopenharmony_ci */ 23818c2ecf20Sopenharmony_cistatic void ab8500_fg_external_power_changed(struct power_supply *psy) 23828c2ecf20Sopenharmony_ci{ 23838c2ecf20Sopenharmony_ci class_for_each_device(power_supply_class, NULL, psy, 23848c2ecf20Sopenharmony_ci ab8500_fg_get_ext_psy_data); 23858c2ecf20Sopenharmony_ci} 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_ci/** 23888c2ecf20Sopenharmony_ci * ab8500_fg_reinit_work() - work to reset the FG algorithm 23898c2ecf20Sopenharmony_ci * @work: pointer to the work_struct structure 23908c2ecf20Sopenharmony_ci * 23918c2ecf20Sopenharmony_ci * Used to reset the current battery capacity to be able to 23928c2ecf20Sopenharmony_ci * retrigger a new voltage base capacity calculation. For 23938c2ecf20Sopenharmony_ci * test and verification purpose. 23948c2ecf20Sopenharmony_ci */ 23958c2ecf20Sopenharmony_cistatic void ab8500_fg_reinit_work(struct work_struct *work) 23968c2ecf20Sopenharmony_ci{ 23978c2ecf20Sopenharmony_ci struct ab8500_fg *di = container_of(work, struct ab8500_fg, 23988c2ecf20Sopenharmony_ci fg_reinit_work.work); 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci if (!di->flags.calibrate) { 24018c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Resetting FG state machine to init.\n"); 24028c2ecf20Sopenharmony_ci ab8500_fg_clear_cap_samples(di); 24038c2ecf20Sopenharmony_ci ab8500_fg_calc_cap_discharge_voltage(di, true); 24048c2ecf20Sopenharmony_ci ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT); 24058c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT); 24068c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci } else { 24098c2ecf20Sopenharmony_ci dev_err(di->dev, "Residual offset calibration ongoing " 24108c2ecf20Sopenharmony_ci "retrying..\n"); 24118c2ecf20Sopenharmony_ci /* Wait one second until next try*/ 24128c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_reinit_work, 24138c2ecf20Sopenharmony_ci round_jiffies(1)); 24148c2ecf20Sopenharmony_ci } 24158c2ecf20Sopenharmony_ci} 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci/* Exposure to the sysfs interface */ 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_cistruct ab8500_fg_sysfs_entry { 24208c2ecf20Sopenharmony_ci struct attribute attr; 24218c2ecf20Sopenharmony_ci ssize_t (*show)(struct ab8500_fg *, char *); 24228c2ecf20Sopenharmony_ci ssize_t (*store)(struct ab8500_fg *, const char *, size_t); 24238c2ecf20Sopenharmony_ci}; 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_cistatic ssize_t charge_full_show(struct ab8500_fg *di, char *buf) 24268c2ecf20Sopenharmony_ci{ 24278c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", di->bat_cap.max_mah); 24288c2ecf20Sopenharmony_ci} 24298c2ecf20Sopenharmony_ci 24308c2ecf20Sopenharmony_cistatic ssize_t charge_full_store(struct ab8500_fg *di, const char *buf, 24318c2ecf20Sopenharmony_ci size_t count) 24328c2ecf20Sopenharmony_ci{ 24338c2ecf20Sopenharmony_ci unsigned long charge_full; 24348c2ecf20Sopenharmony_ci int ret; 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 10, &charge_full); 24378c2ecf20Sopenharmony_ci if (ret) 24388c2ecf20Sopenharmony_ci return ret; 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci di->bat_cap.max_mah = (int) charge_full; 24418c2ecf20Sopenharmony_ci return count; 24428c2ecf20Sopenharmony_ci} 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_cistatic ssize_t charge_now_show(struct ab8500_fg *di, char *buf) 24458c2ecf20Sopenharmony_ci{ 24468c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", di->bat_cap.prev_mah); 24478c2ecf20Sopenharmony_ci} 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_cistatic ssize_t charge_now_store(struct ab8500_fg *di, const char *buf, 24508c2ecf20Sopenharmony_ci size_t count) 24518c2ecf20Sopenharmony_ci{ 24528c2ecf20Sopenharmony_ci unsigned long charge_now; 24538c2ecf20Sopenharmony_ci int ret; 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 10, &charge_now); 24568c2ecf20Sopenharmony_ci if (ret) 24578c2ecf20Sopenharmony_ci return ret; 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci di->bat_cap.user_mah = (int) charge_now; 24608c2ecf20Sopenharmony_ci di->flags.user_cap = true; 24618c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 24628c2ecf20Sopenharmony_ci return count; 24638c2ecf20Sopenharmony_ci} 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_cistatic struct ab8500_fg_sysfs_entry charge_full_attr = 24668c2ecf20Sopenharmony_ci __ATTR(charge_full, 0644, charge_full_show, charge_full_store); 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_cistatic struct ab8500_fg_sysfs_entry charge_now_attr = 24698c2ecf20Sopenharmony_ci __ATTR(charge_now, 0644, charge_now_show, charge_now_store); 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_cistatic ssize_t 24728c2ecf20Sopenharmony_ciab8500_fg_show(struct kobject *kobj, struct attribute *attr, char *buf) 24738c2ecf20Sopenharmony_ci{ 24748c2ecf20Sopenharmony_ci struct ab8500_fg_sysfs_entry *entry; 24758c2ecf20Sopenharmony_ci struct ab8500_fg *di; 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr); 24788c2ecf20Sopenharmony_ci di = container_of(kobj, struct ab8500_fg, fg_kobject); 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci if (!entry->show) 24818c2ecf20Sopenharmony_ci return -EIO; 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci return entry->show(di, buf); 24848c2ecf20Sopenharmony_ci} 24858c2ecf20Sopenharmony_cistatic ssize_t 24868c2ecf20Sopenharmony_ciab8500_fg_store(struct kobject *kobj, struct attribute *attr, const char *buf, 24878c2ecf20Sopenharmony_ci size_t count) 24888c2ecf20Sopenharmony_ci{ 24898c2ecf20Sopenharmony_ci struct ab8500_fg_sysfs_entry *entry; 24908c2ecf20Sopenharmony_ci struct ab8500_fg *di; 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci entry = container_of(attr, struct ab8500_fg_sysfs_entry, attr); 24938c2ecf20Sopenharmony_ci di = container_of(kobj, struct ab8500_fg, fg_kobject); 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci if (!entry->store) 24968c2ecf20Sopenharmony_ci return -EIO; 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci return entry->store(di, buf, count); 24998c2ecf20Sopenharmony_ci} 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_cistatic const struct sysfs_ops ab8500_fg_sysfs_ops = { 25028c2ecf20Sopenharmony_ci .show = ab8500_fg_show, 25038c2ecf20Sopenharmony_ci .store = ab8500_fg_store, 25048c2ecf20Sopenharmony_ci}; 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_cistatic struct attribute *ab8500_fg_attrs[] = { 25078c2ecf20Sopenharmony_ci &charge_full_attr.attr, 25088c2ecf20Sopenharmony_ci &charge_now_attr.attr, 25098c2ecf20Sopenharmony_ci NULL, 25108c2ecf20Sopenharmony_ci}; 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_cistatic struct kobj_type ab8500_fg_ktype = { 25138c2ecf20Sopenharmony_ci .sysfs_ops = &ab8500_fg_sysfs_ops, 25148c2ecf20Sopenharmony_ci .default_attrs = ab8500_fg_attrs, 25158c2ecf20Sopenharmony_ci}; 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci/** 25188c2ecf20Sopenharmony_ci * ab8500_fg_sysfs_exit() - de-init of sysfs entry 25198c2ecf20Sopenharmony_ci * @di: pointer to the struct ab8500_chargalg 25208c2ecf20Sopenharmony_ci * 25218c2ecf20Sopenharmony_ci * This function removes the entry in sysfs. 25228c2ecf20Sopenharmony_ci */ 25238c2ecf20Sopenharmony_cistatic void ab8500_fg_sysfs_exit(struct ab8500_fg *di) 25248c2ecf20Sopenharmony_ci{ 25258c2ecf20Sopenharmony_ci kobject_del(&di->fg_kobject); 25268c2ecf20Sopenharmony_ci} 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci/** 25298c2ecf20Sopenharmony_ci * ab8500_fg_sysfs_init() - init of sysfs entry 25308c2ecf20Sopenharmony_ci * @di: pointer to the struct ab8500_chargalg 25318c2ecf20Sopenharmony_ci * 25328c2ecf20Sopenharmony_ci * This function adds an entry in sysfs. 25338c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0(on success) 25348c2ecf20Sopenharmony_ci */ 25358c2ecf20Sopenharmony_cistatic int ab8500_fg_sysfs_init(struct ab8500_fg *di) 25368c2ecf20Sopenharmony_ci{ 25378c2ecf20Sopenharmony_ci int ret = 0; 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci ret = kobject_init_and_add(&di->fg_kobject, 25408c2ecf20Sopenharmony_ci &ab8500_fg_ktype, 25418c2ecf20Sopenharmony_ci NULL, "battery"); 25428c2ecf20Sopenharmony_ci if (ret < 0) { 25438c2ecf20Sopenharmony_ci kobject_put(&di->fg_kobject); 25448c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to create sysfs entry\n"); 25458c2ecf20Sopenharmony_ci } 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci return ret; 25488c2ecf20Sopenharmony_ci} 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_flagtime_read(struct device *dev, 25518c2ecf20Sopenharmony_ci struct device_attribute *attr, 25528c2ecf20Sopenharmony_ci char *buf) 25538c2ecf20Sopenharmony_ci{ 25548c2ecf20Sopenharmony_ci int ret; 25558c2ecf20Sopenharmony_ci u8 reg_value; 25568c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 25578c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 25608c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_FLAG_TIME_REG, ®_value); 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci if (ret < 0) { 25638c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n"); 25648c2ecf20Sopenharmony_ci goto fail; 25658c2ecf20Sopenharmony_ci } 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F)); 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_cifail: 25708c2ecf20Sopenharmony_ci return ret; 25718c2ecf20Sopenharmony_ci} 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_flagtime_write(struct device *dev, 25748c2ecf20Sopenharmony_ci struct device_attribute *attr, 25758c2ecf20Sopenharmony_ci const char *buf, size_t count) 25768c2ecf20Sopenharmony_ci{ 25778c2ecf20Sopenharmony_ci int ret; 25788c2ecf20Sopenharmony_ci int reg_value; 25798c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 25808c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci if (kstrtoint(buf, 10, ®_value)) 25838c2ecf20Sopenharmony_ci goto fail; 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci if (reg_value > 0x7F) { 25868c2ecf20Sopenharmony_ci dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n"); 25878c2ecf20Sopenharmony_ci goto fail; 25888c2ecf20Sopenharmony_ci } 25898c2ecf20Sopenharmony_ci 25908c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 25918c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value); 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci if (ret < 0) 25948c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n"); 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_cifail: 25978c2ecf20Sopenharmony_ci return count; 25988c2ecf20Sopenharmony_ci} 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_maxtime_read(struct device *dev, 26018c2ecf20Sopenharmony_ci struct device_attribute *attr, 26028c2ecf20Sopenharmony_ci char *buf) 26038c2ecf20Sopenharmony_ci{ 26048c2ecf20Sopenharmony_ci int ret; 26058c2ecf20Sopenharmony_ci u8 reg_value; 26068c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 26078c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 26108c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_MAX_TIME_REG, ®_value); 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci if (ret < 0) { 26138c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_MAX_TIME_REG\n"); 26148c2ecf20Sopenharmony_ci goto fail; 26158c2ecf20Sopenharmony_ci } 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F)); 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_cifail: 26208c2ecf20Sopenharmony_ci return ret; 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci} 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_maxtime_write(struct device *dev, 26258c2ecf20Sopenharmony_ci struct device_attribute *attr, 26268c2ecf20Sopenharmony_ci const char *buf, size_t count) 26278c2ecf20Sopenharmony_ci{ 26288c2ecf20Sopenharmony_ci int ret; 26298c2ecf20Sopenharmony_ci int reg_value; 26308c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 26318c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_ci if (kstrtoint(buf, 10, ®_value)) 26348c2ecf20Sopenharmony_ci goto fail; 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci if (reg_value > 0x7F) { 26378c2ecf20Sopenharmony_ci dev_err(dev, "Incorrect parameter, echo 0 (0.0s) - 127 (1.98s) for maxtime\n"); 26388c2ecf20Sopenharmony_ci goto fail; 26398c2ecf20Sopenharmony_ci } 26408c2ecf20Sopenharmony_ci 26418c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 26428c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_MAX_TIME_REG, (u8)reg_value); 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci if (ret < 0) 26458c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set AB8505_RTC_PCUT_MAX_TIME_REG\n"); 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_cifail: 26488c2ecf20Sopenharmony_ci return count; 26498c2ecf20Sopenharmony_ci} 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_restart_read(struct device *dev, 26528c2ecf20Sopenharmony_ci struct device_attribute *attr, 26538c2ecf20Sopenharmony_ci char *buf) 26548c2ecf20Sopenharmony_ci{ 26558c2ecf20Sopenharmony_ci int ret; 26568c2ecf20Sopenharmony_ci u8 reg_value; 26578c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 26588c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 26618c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_RESTART_REG, ®_value); 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci if (ret < 0) { 26648c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n"); 26658c2ecf20Sopenharmony_ci goto fail; 26668c2ecf20Sopenharmony_ci } 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF)); 26698c2ecf20Sopenharmony_ci 26708c2ecf20Sopenharmony_cifail: 26718c2ecf20Sopenharmony_ci return ret; 26728c2ecf20Sopenharmony_ci} 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_restart_write(struct device *dev, 26758c2ecf20Sopenharmony_ci struct device_attribute *attr, 26768c2ecf20Sopenharmony_ci const char *buf, size_t count) 26778c2ecf20Sopenharmony_ci{ 26788c2ecf20Sopenharmony_ci int ret; 26798c2ecf20Sopenharmony_ci int reg_value; 26808c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 26818c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_ci if (kstrtoint(buf, 10, ®_value)) 26848c2ecf20Sopenharmony_ci goto fail; 26858c2ecf20Sopenharmony_ci 26868c2ecf20Sopenharmony_ci if (reg_value > 0xF) { 26878c2ecf20Sopenharmony_ci dev_err(dev, "Incorrect parameter, echo 0 - 15 for number of restart\n"); 26888c2ecf20Sopenharmony_ci goto fail; 26898c2ecf20Sopenharmony_ci } 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 26928c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_RESTART_REG, (u8)reg_value); 26938c2ecf20Sopenharmony_ci 26948c2ecf20Sopenharmony_ci if (ret < 0) 26958c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set AB8505_RTC_PCUT_RESTART_REG\n"); 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_cifail: 26988c2ecf20Sopenharmony_ci return count; 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci} 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_timer_read(struct device *dev, 27038c2ecf20Sopenharmony_ci struct device_attribute *attr, 27048c2ecf20Sopenharmony_ci char *buf) 27058c2ecf20Sopenharmony_ci{ 27068c2ecf20Sopenharmony_ci int ret; 27078c2ecf20Sopenharmony_ci u8 reg_value; 27088c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 27098c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 27128c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_TIME_REG, ®_value); 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci if (ret < 0) { 27158c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_TIME_REG\n"); 27168c2ecf20Sopenharmony_ci goto fail; 27178c2ecf20Sopenharmony_ci } 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F)); 27208c2ecf20Sopenharmony_ci 27218c2ecf20Sopenharmony_cifail: 27228c2ecf20Sopenharmony_ci return ret; 27238c2ecf20Sopenharmony_ci} 27248c2ecf20Sopenharmony_ci 27258c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_restart_counter_read(struct device *dev, 27268c2ecf20Sopenharmony_ci struct device_attribute *attr, 27278c2ecf20Sopenharmony_ci char *buf) 27288c2ecf20Sopenharmony_ci{ 27298c2ecf20Sopenharmony_ci int ret; 27308c2ecf20Sopenharmony_ci u8 reg_value; 27318c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 27328c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 27338c2ecf20Sopenharmony_ci 27348c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 27358c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_RESTART_REG, ®_value); 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci if (ret < 0) { 27388c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n"); 27398c2ecf20Sopenharmony_ci goto fail; 27408c2ecf20Sopenharmony_ci } 27418c2ecf20Sopenharmony_ci 27428c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF0) >> 4); 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_cifail: 27458c2ecf20Sopenharmony_ci return ret; 27468c2ecf20Sopenharmony_ci} 27478c2ecf20Sopenharmony_ci 27488c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_read(struct device *dev, 27498c2ecf20Sopenharmony_ci struct device_attribute *attr, 27508c2ecf20Sopenharmony_ci char *buf) 27518c2ecf20Sopenharmony_ci{ 27528c2ecf20Sopenharmony_ci int ret; 27538c2ecf20Sopenharmony_ci u8 reg_value; 27548c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 27558c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 27588c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci if (ret < 0) 27618c2ecf20Sopenharmony_ci goto fail; 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x1)); 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_cifail: 27668c2ecf20Sopenharmony_ci return ret; 27678c2ecf20Sopenharmony_ci} 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_write(struct device *dev, 27708c2ecf20Sopenharmony_ci struct device_attribute *attr, 27718c2ecf20Sopenharmony_ci const char *buf, size_t count) 27728c2ecf20Sopenharmony_ci{ 27738c2ecf20Sopenharmony_ci int ret; 27748c2ecf20Sopenharmony_ci int reg_value; 27758c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 27768c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci if (kstrtoint(buf, 10, ®_value)) 27798c2ecf20Sopenharmony_ci goto fail; 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci if (reg_value > 0x1) { 27828c2ecf20Sopenharmony_ci dev_err(dev, "Incorrect parameter, echo 0/1 to disable/enable Pcut feature\n"); 27838c2ecf20Sopenharmony_ci goto fail; 27848c2ecf20Sopenharmony_ci } 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 27878c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_CTL_STATUS_REG, (u8)reg_value); 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_ci if (ret < 0) 27908c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set AB8505_RTC_PCUT_CTL_STATUS_REG\n"); 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_cifail: 27938c2ecf20Sopenharmony_ci return count; 27948c2ecf20Sopenharmony_ci} 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_flag_read(struct device *dev, 27978c2ecf20Sopenharmony_ci struct device_attribute *attr, 27988c2ecf20Sopenharmony_ci char *buf) 27998c2ecf20Sopenharmony_ci{ 28008c2ecf20Sopenharmony_ci 28018c2ecf20Sopenharmony_ci int ret; 28028c2ecf20Sopenharmony_ci u8 reg_value; 28038c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 28048c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 28058c2ecf20Sopenharmony_ci 28068c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 28078c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_ci if (ret < 0) { 28108c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n"); 28118c2ecf20Sopenharmony_ci goto fail; 28128c2ecf20Sopenharmony_ci } 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x10) >> 4)); 28158c2ecf20Sopenharmony_ci 28168c2ecf20Sopenharmony_cifail: 28178c2ecf20Sopenharmony_ci return ret; 28188c2ecf20Sopenharmony_ci} 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_debounce_read(struct device *dev, 28218c2ecf20Sopenharmony_ci struct device_attribute *attr, 28228c2ecf20Sopenharmony_ci char *buf) 28238c2ecf20Sopenharmony_ci{ 28248c2ecf20Sopenharmony_ci int ret; 28258c2ecf20Sopenharmony_ci u8 reg_value; 28268c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 28278c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 28308c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_DEBOUNCE_REG, ®_value); 28318c2ecf20Sopenharmony_ci 28328c2ecf20Sopenharmony_ci if (ret < 0) { 28338c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_DEBOUNCE_REG\n"); 28348c2ecf20Sopenharmony_ci goto fail; 28358c2ecf20Sopenharmony_ci } 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7)); 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_cifail: 28408c2ecf20Sopenharmony_ci return ret; 28418c2ecf20Sopenharmony_ci} 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_debounce_write(struct device *dev, 28448c2ecf20Sopenharmony_ci struct device_attribute *attr, 28458c2ecf20Sopenharmony_ci const char *buf, size_t count) 28468c2ecf20Sopenharmony_ci{ 28478c2ecf20Sopenharmony_ci int ret; 28488c2ecf20Sopenharmony_ci int reg_value; 28498c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 28508c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci if (kstrtoint(buf, 10, ®_value)) 28538c2ecf20Sopenharmony_ci goto fail; 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci if (reg_value > 0x7) { 28568c2ecf20Sopenharmony_ci dev_err(dev, "Incorrect parameter, echo 0 to 7 for debounce setting\n"); 28578c2ecf20Sopenharmony_ci goto fail; 28588c2ecf20Sopenharmony_ci } 28598c2ecf20Sopenharmony_ci 28608c2ecf20Sopenharmony_ci ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, 28618c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_DEBOUNCE_REG, (u8)reg_value); 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_ci if (ret < 0) 28648c2ecf20Sopenharmony_ci dev_err(dev, "Failed to set AB8505_RTC_PCUT_DEBOUNCE_REG\n"); 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_cifail: 28678c2ecf20Sopenharmony_ci return count; 28688c2ecf20Sopenharmony_ci} 28698c2ecf20Sopenharmony_ci 28708c2ecf20Sopenharmony_cistatic ssize_t ab8505_powercut_enable_status_read(struct device *dev, 28718c2ecf20Sopenharmony_ci struct device_attribute *attr, 28728c2ecf20Sopenharmony_ci char *buf) 28738c2ecf20Sopenharmony_ci{ 28748c2ecf20Sopenharmony_ci int ret; 28758c2ecf20Sopenharmony_ci u8 reg_value; 28768c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 28778c2ecf20Sopenharmony_ci struct ab8500_fg *di = power_supply_get_drvdata(psy); 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, 28808c2ecf20Sopenharmony_ci AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); 28818c2ecf20Sopenharmony_ci 28828c2ecf20Sopenharmony_ci if (ret < 0) { 28838c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n"); 28848c2ecf20Sopenharmony_ci goto fail; 28858c2ecf20Sopenharmony_ci } 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x20) >> 5)); 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_cifail: 28908c2ecf20Sopenharmony_ci return ret; 28918c2ecf20Sopenharmony_ci} 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_cistatic struct device_attribute ab8505_fg_sysfs_psy_attrs[] = { 28948c2ecf20Sopenharmony_ci __ATTR(powercut_flagtime, (S_IRUGO | S_IWUSR | S_IWGRP), 28958c2ecf20Sopenharmony_ci ab8505_powercut_flagtime_read, ab8505_powercut_flagtime_write), 28968c2ecf20Sopenharmony_ci __ATTR(powercut_maxtime, (S_IRUGO | S_IWUSR | S_IWGRP), 28978c2ecf20Sopenharmony_ci ab8505_powercut_maxtime_read, ab8505_powercut_maxtime_write), 28988c2ecf20Sopenharmony_ci __ATTR(powercut_restart_max, (S_IRUGO | S_IWUSR | S_IWGRP), 28998c2ecf20Sopenharmony_ci ab8505_powercut_restart_read, ab8505_powercut_restart_write), 29008c2ecf20Sopenharmony_ci __ATTR(powercut_timer, S_IRUGO, ab8505_powercut_timer_read, NULL), 29018c2ecf20Sopenharmony_ci __ATTR(powercut_restart_counter, S_IRUGO, 29028c2ecf20Sopenharmony_ci ab8505_powercut_restart_counter_read, NULL), 29038c2ecf20Sopenharmony_ci __ATTR(powercut_enable, (S_IRUGO | S_IWUSR | S_IWGRP), 29048c2ecf20Sopenharmony_ci ab8505_powercut_read, ab8505_powercut_write), 29058c2ecf20Sopenharmony_ci __ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL), 29068c2ecf20Sopenharmony_ci __ATTR(powercut_debounce_time, (S_IRUGO | S_IWUSR | S_IWGRP), 29078c2ecf20Sopenharmony_ci ab8505_powercut_debounce_read, ab8505_powercut_debounce_write), 29088c2ecf20Sopenharmony_ci __ATTR(powercut_enable_status, S_IRUGO, 29098c2ecf20Sopenharmony_ci ab8505_powercut_enable_status_read, NULL), 29108c2ecf20Sopenharmony_ci}; 29118c2ecf20Sopenharmony_ci 29128c2ecf20Sopenharmony_cistatic int ab8500_fg_sysfs_psy_create_attrs(struct ab8500_fg *di) 29138c2ecf20Sopenharmony_ci{ 29148c2ecf20Sopenharmony_ci unsigned int i; 29158c2ecf20Sopenharmony_ci 29168c2ecf20Sopenharmony_ci if (is_ab8505(di->parent)) { 29178c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++) 29188c2ecf20Sopenharmony_ci if (device_create_file(&di->fg_psy->dev, 29198c2ecf20Sopenharmony_ci &ab8505_fg_sysfs_psy_attrs[i])) 29208c2ecf20Sopenharmony_ci goto sysfs_psy_create_attrs_failed_ab8505; 29218c2ecf20Sopenharmony_ci } 29228c2ecf20Sopenharmony_ci return 0; 29238c2ecf20Sopenharmony_cisysfs_psy_create_attrs_failed_ab8505: 29248c2ecf20Sopenharmony_ci dev_err(&di->fg_psy->dev, "Failed creating sysfs psy attrs for ab8505.\n"); 29258c2ecf20Sopenharmony_ci while (i--) 29268c2ecf20Sopenharmony_ci device_remove_file(&di->fg_psy->dev, 29278c2ecf20Sopenharmony_ci &ab8505_fg_sysfs_psy_attrs[i]); 29288c2ecf20Sopenharmony_ci 29298c2ecf20Sopenharmony_ci return -EIO; 29308c2ecf20Sopenharmony_ci} 29318c2ecf20Sopenharmony_ci 29328c2ecf20Sopenharmony_cistatic void ab8500_fg_sysfs_psy_remove_attrs(struct ab8500_fg *di) 29338c2ecf20Sopenharmony_ci{ 29348c2ecf20Sopenharmony_ci unsigned int i; 29358c2ecf20Sopenharmony_ci 29368c2ecf20Sopenharmony_ci if (is_ab8505(di->parent)) { 29378c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++) 29388c2ecf20Sopenharmony_ci (void)device_remove_file(&di->fg_psy->dev, 29398c2ecf20Sopenharmony_ci &ab8505_fg_sysfs_psy_attrs[i]); 29408c2ecf20Sopenharmony_ci } 29418c2ecf20Sopenharmony_ci} 29428c2ecf20Sopenharmony_ci 29438c2ecf20Sopenharmony_ci/* Exposure to the sysfs interface <<END>> */ 29448c2ecf20Sopenharmony_ci 29458c2ecf20Sopenharmony_ci#if defined(CONFIG_PM) 29468c2ecf20Sopenharmony_cistatic int ab8500_fg_resume(struct platform_device *pdev) 29478c2ecf20Sopenharmony_ci{ 29488c2ecf20Sopenharmony_ci struct ab8500_fg *di = platform_get_drvdata(pdev); 29498c2ecf20Sopenharmony_ci 29508c2ecf20Sopenharmony_ci /* 29518c2ecf20Sopenharmony_ci * Change state if we're not charging. If we're charging we will wake 29528c2ecf20Sopenharmony_ci * up on the FG IRQ 29538c2ecf20Sopenharmony_ci */ 29548c2ecf20Sopenharmony_ci if (!di->flags.charging) { 29558c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_WAKEUP); 29568c2ecf20Sopenharmony_ci queue_work(di->fg_wq, &di->fg_work); 29578c2ecf20Sopenharmony_ci } 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci return 0; 29608c2ecf20Sopenharmony_ci} 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_cistatic int ab8500_fg_suspend(struct platform_device *pdev, 29638c2ecf20Sopenharmony_ci pm_message_t state) 29648c2ecf20Sopenharmony_ci{ 29658c2ecf20Sopenharmony_ci struct ab8500_fg *di = platform_get_drvdata(pdev); 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_ci flush_delayed_work(&di->fg_periodic_work); 29688c2ecf20Sopenharmony_ci flush_work(&di->fg_work); 29698c2ecf20Sopenharmony_ci flush_work(&di->fg_acc_cur_work); 29708c2ecf20Sopenharmony_ci flush_delayed_work(&di->fg_reinit_work); 29718c2ecf20Sopenharmony_ci flush_delayed_work(&di->fg_low_bat_work); 29728c2ecf20Sopenharmony_ci flush_delayed_work(&di->fg_check_hw_failure_work); 29738c2ecf20Sopenharmony_ci 29748c2ecf20Sopenharmony_ci /* 29758c2ecf20Sopenharmony_ci * If the FG is enabled we will disable it before going to suspend 29768c2ecf20Sopenharmony_ci * only if we're not charging 29778c2ecf20Sopenharmony_ci */ 29788c2ecf20Sopenharmony_ci if (di->flags.fg_enabled && !di->flags.charging) 29798c2ecf20Sopenharmony_ci ab8500_fg_coulomb_counter(di, false); 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci return 0; 29828c2ecf20Sopenharmony_ci} 29838c2ecf20Sopenharmony_ci#else 29848c2ecf20Sopenharmony_ci#define ab8500_fg_suspend NULL 29858c2ecf20Sopenharmony_ci#define ab8500_fg_resume NULL 29868c2ecf20Sopenharmony_ci#endif 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_cistatic int ab8500_fg_remove(struct platform_device *pdev) 29898c2ecf20Sopenharmony_ci{ 29908c2ecf20Sopenharmony_ci int ret = 0; 29918c2ecf20Sopenharmony_ci struct ab8500_fg *di = platform_get_drvdata(pdev); 29928c2ecf20Sopenharmony_ci 29938c2ecf20Sopenharmony_ci list_del(&di->node); 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_ci /* Disable coulomb counter */ 29968c2ecf20Sopenharmony_ci ret = ab8500_fg_coulomb_counter(di, false); 29978c2ecf20Sopenharmony_ci if (ret) 29988c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to disable coulomb counter\n"); 29998c2ecf20Sopenharmony_ci 30008c2ecf20Sopenharmony_ci destroy_workqueue(di->fg_wq); 30018c2ecf20Sopenharmony_ci ab8500_fg_sysfs_exit(di); 30028c2ecf20Sopenharmony_ci 30038c2ecf20Sopenharmony_ci flush_scheduled_work(); 30048c2ecf20Sopenharmony_ci ab8500_fg_sysfs_psy_remove_attrs(di); 30058c2ecf20Sopenharmony_ci power_supply_unregister(di->fg_psy); 30068c2ecf20Sopenharmony_ci return ret; 30078c2ecf20Sopenharmony_ci} 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_ci/* ab8500 fg driver interrupts and their respective isr */ 30108c2ecf20Sopenharmony_cistatic struct ab8500_fg_interrupts ab8500_fg_irq_th[] = { 30118c2ecf20Sopenharmony_ci {"NCONV_ACCU", ab8500_fg_cc_convend_handler}, 30128c2ecf20Sopenharmony_ci {"BATT_OVV", ab8500_fg_batt_ovv_handler}, 30138c2ecf20Sopenharmony_ci {"LOW_BAT_F", ab8500_fg_lowbatf_handler}, 30148c2ecf20Sopenharmony_ci {"CC_INT_CALIB", ab8500_fg_cc_int_calib_handler}, 30158c2ecf20Sopenharmony_ci}; 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_cistatic struct ab8500_fg_interrupts ab8500_fg_irq_bh[] = { 30188c2ecf20Sopenharmony_ci {"CCEOC", ab8500_fg_cc_data_end_handler}, 30198c2ecf20Sopenharmony_ci}; 30208c2ecf20Sopenharmony_ci 30218c2ecf20Sopenharmony_cistatic char *supply_interface[] = { 30228c2ecf20Sopenharmony_ci "ab8500_chargalg", 30238c2ecf20Sopenharmony_ci "ab8500_usb", 30248c2ecf20Sopenharmony_ci}; 30258c2ecf20Sopenharmony_ci 30268c2ecf20Sopenharmony_cistatic const struct power_supply_desc ab8500_fg_desc = { 30278c2ecf20Sopenharmony_ci .name = "ab8500_fg", 30288c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 30298c2ecf20Sopenharmony_ci .properties = ab8500_fg_props, 30308c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(ab8500_fg_props), 30318c2ecf20Sopenharmony_ci .get_property = ab8500_fg_get_property, 30328c2ecf20Sopenharmony_ci .external_power_changed = ab8500_fg_external_power_changed, 30338c2ecf20Sopenharmony_ci}; 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_cistatic int ab8500_fg_probe(struct platform_device *pdev) 30368c2ecf20Sopenharmony_ci{ 30378c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 30388c2ecf20Sopenharmony_ci struct abx500_bm_data *plat = pdev->dev.platform_data; 30398c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; 30408c2ecf20Sopenharmony_ci struct ab8500_fg *di; 30418c2ecf20Sopenharmony_ci int i, irq; 30428c2ecf20Sopenharmony_ci int ret = 0; 30438c2ecf20Sopenharmony_ci 30448c2ecf20Sopenharmony_ci di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); 30458c2ecf20Sopenharmony_ci if (!di) { 30468c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s no mem for ab8500_fg\n", __func__); 30478c2ecf20Sopenharmony_ci return -ENOMEM; 30488c2ecf20Sopenharmony_ci } 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci if (!plat) { 30518c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no battery management data supplied\n"); 30528c2ecf20Sopenharmony_ci return -EINVAL; 30538c2ecf20Sopenharmony_ci } 30548c2ecf20Sopenharmony_ci di->bm = plat; 30558c2ecf20Sopenharmony_ci 30568c2ecf20Sopenharmony_ci if (np) { 30578c2ecf20Sopenharmony_ci ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm); 30588c2ecf20Sopenharmony_ci if (ret) { 30598c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get battery information\n"); 30608c2ecf20Sopenharmony_ci return ret; 30618c2ecf20Sopenharmony_ci } 30628c2ecf20Sopenharmony_ci } 30638c2ecf20Sopenharmony_ci 30648c2ecf20Sopenharmony_ci mutex_init(&di->cc_lock); 30658c2ecf20Sopenharmony_ci 30668c2ecf20Sopenharmony_ci /* get parent data */ 30678c2ecf20Sopenharmony_ci di->dev = &pdev->dev; 30688c2ecf20Sopenharmony_ci di->parent = dev_get_drvdata(pdev->dev.parent); 30698c2ecf20Sopenharmony_ci 30708c2ecf20Sopenharmony_ci di->main_bat_v = devm_iio_channel_get(&pdev->dev, "main_bat_v"); 30718c2ecf20Sopenharmony_ci if (IS_ERR(di->main_bat_v)) { 30728c2ecf20Sopenharmony_ci if (PTR_ERR(di->main_bat_v) == -ENODEV) 30738c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 30748c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get main battery ADC channel\n"); 30758c2ecf20Sopenharmony_ci return PTR_ERR(di->main_bat_v); 30768c2ecf20Sopenharmony_ci } 30778c2ecf20Sopenharmony_ci 30788c2ecf20Sopenharmony_ci psy_cfg.supplied_to = supply_interface; 30798c2ecf20Sopenharmony_ci psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); 30808c2ecf20Sopenharmony_ci psy_cfg.drv_data = di; 30818c2ecf20Sopenharmony_ci 30828c2ecf20Sopenharmony_ci di->bat_cap.max_mah_design = MILLI_TO_MICRO * 30838c2ecf20Sopenharmony_ci di->bm->bat_type[di->bm->batt_id].charge_full_design; 30848c2ecf20Sopenharmony_ci 30858c2ecf20Sopenharmony_ci di->bat_cap.max_mah = di->bat_cap.max_mah_design; 30868c2ecf20Sopenharmony_ci 30878c2ecf20Sopenharmony_ci di->vbat_nom = di->bm->bat_type[di->bm->batt_id].nominal_voltage; 30888c2ecf20Sopenharmony_ci 30898c2ecf20Sopenharmony_ci di->init_capacity = true; 30908c2ecf20Sopenharmony_ci 30918c2ecf20Sopenharmony_ci ab8500_fg_charge_state_to(di, AB8500_FG_CHARGE_INIT); 30928c2ecf20Sopenharmony_ci ab8500_fg_discharge_state_to(di, AB8500_FG_DISCHARGE_INIT); 30938c2ecf20Sopenharmony_ci 30948c2ecf20Sopenharmony_ci /* Create a work queue for running the FG algorithm */ 30958c2ecf20Sopenharmony_ci di->fg_wq = alloc_ordered_workqueue("ab8500_fg_wq", WQ_MEM_RECLAIM); 30968c2ecf20Sopenharmony_ci if (di->fg_wq == NULL) { 30978c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to create work queue\n"); 30988c2ecf20Sopenharmony_ci return -ENOMEM; 30998c2ecf20Sopenharmony_ci } 31008c2ecf20Sopenharmony_ci 31018c2ecf20Sopenharmony_ci /* Init work for running the fg algorithm instantly */ 31028c2ecf20Sopenharmony_ci INIT_WORK(&di->fg_work, ab8500_fg_instant_work); 31038c2ecf20Sopenharmony_ci 31048c2ecf20Sopenharmony_ci /* Init work for getting the battery accumulated current */ 31058c2ecf20Sopenharmony_ci INIT_WORK(&di->fg_acc_cur_work, ab8500_fg_acc_cur_work); 31068c2ecf20Sopenharmony_ci 31078c2ecf20Sopenharmony_ci /* Init work for reinitialising the fg algorithm */ 31088c2ecf20Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->fg_reinit_work, 31098c2ecf20Sopenharmony_ci ab8500_fg_reinit_work); 31108c2ecf20Sopenharmony_ci 31118c2ecf20Sopenharmony_ci /* Work delayed Queue to run the state machine */ 31128c2ecf20Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->fg_periodic_work, 31138c2ecf20Sopenharmony_ci ab8500_fg_periodic_work); 31148c2ecf20Sopenharmony_ci 31158c2ecf20Sopenharmony_ci /* Work to check low battery condition */ 31168c2ecf20Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->fg_low_bat_work, 31178c2ecf20Sopenharmony_ci ab8500_fg_low_bat_work); 31188c2ecf20Sopenharmony_ci 31198c2ecf20Sopenharmony_ci /* Init work for HW failure check */ 31208c2ecf20Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->fg_check_hw_failure_work, 31218c2ecf20Sopenharmony_ci ab8500_fg_check_hw_failure_work); 31228c2ecf20Sopenharmony_ci 31238c2ecf20Sopenharmony_ci /* Reset battery low voltage flag */ 31248c2ecf20Sopenharmony_ci di->flags.low_bat = false; 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_ci /* Initialize low battery counter */ 31278c2ecf20Sopenharmony_ci di->low_bat_cnt = 10; 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_ci /* Initialize OVV, and other registers */ 31308c2ecf20Sopenharmony_ci ret = ab8500_fg_init_hw_registers(di); 31318c2ecf20Sopenharmony_ci if (ret) { 31328c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to initialize registers\n"); 31338c2ecf20Sopenharmony_ci goto free_inst_curr_wq; 31348c2ecf20Sopenharmony_ci } 31358c2ecf20Sopenharmony_ci 31368c2ecf20Sopenharmony_ci /* Consider battery unknown until we're informed otherwise */ 31378c2ecf20Sopenharmony_ci di->flags.batt_unknown = true; 31388c2ecf20Sopenharmony_ci di->flags.batt_id_received = false; 31398c2ecf20Sopenharmony_ci 31408c2ecf20Sopenharmony_ci /* Register FG power supply class */ 31418c2ecf20Sopenharmony_ci di->fg_psy = power_supply_register(di->dev, &ab8500_fg_desc, &psy_cfg); 31428c2ecf20Sopenharmony_ci if (IS_ERR(di->fg_psy)) { 31438c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to register FG psy\n"); 31448c2ecf20Sopenharmony_ci ret = PTR_ERR(di->fg_psy); 31458c2ecf20Sopenharmony_ci goto free_inst_curr_wq; 31468c2ecf20Sopenharmony_ci } 31478c2ecf20Sopenharmony_ci 31488c2ecf20Sopenharmony_ci di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer); 31498c2ecf20Sopenharmony_ci ab8500_fg_coulomb_counter(di, true); 31508c2ecf20Sopenharmony_ci 31518c2ecf20Sopenharmony_ci /* 31528c2ecf20Sopenharmony_ci * Initialize completion used to notify completion and start 31538c2ecf20Sopenharmony_ci * of inst current 31548c2ecf20Sopenharmony_ci */ 31558c2ecf20Sopenharmony_ci init_completion(&di->ab8500_fg_started); 31568c2ecf20Sopenharmony_ci init_completion(&di->ab8500_fg_complete); 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_ci /* Register primary interrupt handlers */ 31598c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq_th); i++) { 31608c2ecf20Sopenharmony_ci irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name); 31618c2ecf20Sopenharmony_ci if (irq < 0) { 31628c2ecf20Sopenharmony_ci ret = irq; 31638c2ecf20Sopenharmony_ci goto free_irq_th; 31648c2ecf20Sopenharmony_ci } 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_ci ret = request_irq(irq, ab8500_fg_irq_th[i].isr, 31678c2ecf20Sopenharmony_ci IRQF_SHARED | IRQF_NO_SUSPEND, 31688c2ecf20Sopenharmony_ci ab8500_fg_irq_th[i].name, di); 31698c2ecf20Sopenharmony_ci 31708c2ecf20Sopenharmony_ci if (ret != 0) { 31718c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to request %s IRQ %d: %d\n", 31728c2ecf20Sopenharmony_ci ab8500_fg_irq_th[i].name, irq, ret); 31738c2ecf20Sopenharmony_ci goto free_irq_th; 31748c2ecf20Sopenharmony_ci } 31758c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Requested %s IRQ %d: %d\n", 31768c2ecf20Sopenharmony_ci ab8500_fg_irq_th[i].name, irq, ret); 31778c2ecf20Sopenharmony_ci } 31788c2ecf20Sopenharmony_ci 31798c2ecf20Sopenharmony_ci /* Register threaded interrupt handler */ 31808c2ecf20Sopenharmony_ci irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); 31818c2ecf20Sopenharmony_ci if (irq < 0) { 31828c2ecf20Sopenharmony_ci ret = irq; 31838c2ecf20Sopenharmony_ci goto free_irq_th; 31848c2ecf20Sopenharmony_ci } 31858c2ecf20Sopenharmony_ci 31868c2ecf20Sopenharmony_ci ret = request_threaded_irq(irq, NULL, ab8500_fg_irq_bh[0].isr, 31878c2ecf20Sopenharmony_ci IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, 31888c2ecf20Sopenharmony_ci ab8500_fg_irq_bh[0].name, di); 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci if (ret != 0) { 31918c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to request %s IRQ %d: %d\n", 31928c2ecf20Sopenharmony_ci ab8500_fg_irq_bh[0].name, irq, ret); 31938c2ecf20Sopenharmony_ci goto free_irq_th; 31948c2ecf20Sopenharmony_ci } 31958c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Requested %s IRQ %d: %d\n", 31968c2ecf20Sopenharmony_ci ab8500_fg_irq_bh[0].name, irq, ret); 31978c2ecf20Sopenharmony_ci 31988c2ecf20Sopenharmony_ci di->irq = platform_get_irq_byname(pdev, "CCEOC"); 31998c2ecf20Sopenharmony_ci disable_irq(di->irq); 32008c2ecf20Sopenharmony_ci di->nbr_cceoc_irq_cnt = 0; 32018c2ecf20Sopenharmony_ci 32028c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, di); 32038c2ecf20Sopenharmony_ci 32048c2ecf20Sopenharmony_ci ret = ab8500_fg_sysfs_init(di); 32058c2ecf20Sopenharmony_ci if (ret) { 32068c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to create sysfs entry\n"); 32078c2ecf20Sopenharmony_ci goto free_irq; 32088c2ecf20Sopenharmony_ci } 32098c2ecf20Sopenharmony_ci 32108c2ecf20Sopenharmony_ci ret = ab8500_fg_sysfs_psy_create_attrs(di); 32118c2ecf20Sopenharmony_ci if (ret) { 32128c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to create FG psy\n"); 32138c2ecf20Sopenharmony_ci ab8500_fg_sysfs_exit(di); 32148c2ecf20Sopenharmony_ci goto free_irq; 32158c2ecf20Sopenharmony_ci } 32168c2ecf20Sopenharmony_ci 32178c2ecf20Sopenharmony_ci /* Calibrate the fg first time */ 32188c2ecf20Sopenharmony_ci di->flags.calibrate = true; 32198c2ecf20Sopenharmony_ci di->calib_state = AB8500_FG_CALIB_INIT; 32208c2ecf20Sopenharmony_ci 32218c2ecf20Sopenharmony_ci /* Use room temp as default value until we get an update from driver. */ 32228c2ecf20Sopenharmony_ci di->bat_temp = 210; 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_ci /* Run the FG algorithm */ 32258c2ecf20Sopenharmony_ci queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0); 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_ci list_add_tail(&di->node, &ab8500_fg_list); 32288c2ecf20Sopenharmony_ci 32298c2ecf20Sopenharmony_ci return ret; 32308c2ecf20Sopenharmony_ci 32318c2ecf20Sopenharmony_cifree_irq: 32328c2ecf20Sopenharmony_ci /* We also have to free all registered irqs */ 32338c2ecf20Sopenharmony_ci irq = platform_get_irq_byname(pdev, ab8500_fg_irq_bh[0].name); 32348c2ecf20Sopenharmony_ci free_irq(irq, di); 32358c2ecf20Sopenharmony_cifree_irq_th: 32368c2ecf20Sopenharmony_ci while (--i >= 0) { 32378c2ecf20Sopenharmony_ci /* Last assignment of i from primary interrupt handlers */ 32388c2ecf20Sopenharmony_ci irq = platform_get_irq_byname(pdev, ab8500_fg_irq_th[i].name); 32398c2ecf20Sopenharmony_ci free_irq(irq, di); 32408c2ecf20Sopenharmony_ci } 32418c2ecf20Sopenharmony_ci 32428c2ecf20Sopenharmony_ci power_supply_unregister(di->fg_psy); 32438c2ecf20Sopenharmony_cifree_inst_curr_wq: 32448c2ecf20Sopenharmony_ci destroy_workqueue(di->fg_wq); 32458c2ecf20Sopenharmony_ci return ret; 32468c2ecf20Sopenharmony_ci} 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_cistatic const struct of_device_id ab8500_fg_match[] = { 32498c2ecf20Sopenharmony_ci { .compatible = "stericsson,ab8500-fg", }, 32508c2ecf20Sopenharmony_ci { }, 32518c2ecf20Sopenharmony_ci}; 32528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ab8500_fg_match); 32538c2ecf20Sopenharmony_ci 32548c2ecf20Sopenharmony_cistatic struct platform_driver ab8500_fg_driver = { 32558c2ecf20Sopenharmony_ci .probe = ab8500_fg_probe, 32568c2ecf20Sopenharmony_ci .remove = ab8500_fg_remove, 32578c2ecf20Sopenharmony_ci .suspend = ab8500_fg_suspend, 32588c2ecf20Sopenharmony_ci .resume = ab8500_fg_resume, 32598c2ecf20Sopenharmony_ci .driver = { 32608c2ecf20Sopenharmony_ci .name = "ab8500-fg", 32618c2ecf20Sopenharmony_ci .of_match_table = ab8500_fg_match, 32628c2ecf20Sopenharmony_ci }, 32638c2ecf20Sopenharmony_ci}; 32648c2ecf20Sopenharmony_ci 32658c2ecf20Sopenharmony_cistatic int __init ab8500_fg_init(void) 32668c2ecf20Sopenharmony_ci{ 32678c2ecf20Sopenharmony_ci return platform_driver_register(&ab8500_fg_driver); 32688c2ecf20Sopenharmony_ci} 32698c2ecf20Sopenharmony_ci 32708c2ecf20Sopenharmony_cistatic void __exit ab8500_fg_exit(void) 32718c2ecf20Sopenharmony_ci{ 32728c2ecf20Sopenharmony_ci platform_driver_unregister(&ab8500_fg_driver); 32738c2ecf20Sopenharmony_ci} 32748c2ecf20Sopenharmony_ci 32758c2ecf20Sopenharmony_cisubsys_initcall_sync(ab8500_fg_init); 32768c2ecf20Sopenharmony_cimodule_exit(ab8500_fg_exit); 32778c2ecf20Sopenharmony_ci 32788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 32798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johan Palsson, Karl Komierowski"); 32808c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ab8500-fg"); 32818c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AB8500 Fuel Gauge driver"); 3282