18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2012 48c2ecf20Sopenharmony_ci * Copyright (c) 2012 Sony Mobile Communications AB 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Charging algorithm driver for abx500 variants 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Authors: 98c2ecf20Sopenharmony_ci * Johan Palsson <johan.palsson@stericsson.com> 108c2ecf20Sopenharmony_ci * Karl Komierowski <karl.komierowski@stericsson.com> 118c2ecf20Sopenharmony_ci * Arun R Murthy <arun.murthy@stericsson.com> 128c2ecf20Sopenharmony_ci * Author: Imre Sunyi <imre.sunyi@sonymobile.com> 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/device.h> 188c2ecf20Sopenharmony_ci#include <linux/hrtimer.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 248c2ecf20Sopenharmony_ci#include <linux/completion.h> 258c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 268c2ecf20Sopenharmony_ci#include <linux/kobject.h> 278c2ecf20Sopenharmony_ci#include <linux/of.h> 288c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 298c2ecf20Sopenharmony_ci#include <linux/mfd/abx500.h> 308c2ecf20Sopenharmony_ci#include <linux/mfd/abx500/ab8500.h> 318c2ecf20Sopenharmony_ci#include <linux/mfd/abx500/ux500_chargalg.h> 328c2ecf20Sopenharmony_ci#include <linux/mfd/abx500/ab8500-bm.h> 338c2ecf20Sopenharmony_ci#include <linux/notifier.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* Watchdog kick interval */ 368c2ecf20Sopenharmony_ci#define CHG_WD_INTERVAL (6 * HZ) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* End-of-charge criteria counter */ 398c2ecf20Sopenharmony_ci#define EOC_COND_CNT 10 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* One hour expressed in seconds */ 428c2ecf20Sopenharmony_ci#define ONE_HOUR_IN_SECONDS 3600 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Five minutes expressed in seconds */ 458c2ecf20Sopenharmony_ci#define FIVE_MINUTES_IN_SECONDS 300 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define CHARGALG_CURR_STEP_LOW 0 488c2ecf20Sopenharmony_ci#define CHARGALG_CURR_STEP_HIGH 100 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cienum abx500_chargers { 518c2ecf20Sopenharmony_ci NO_CHG, 528c2ecf20Sopenharmony_ci AC_CHG, 538c2ecf20Sopenharmony_ci USB_CHG, 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct abx500_chargalg_charger_info { 578c2ecf20Sopenharmony_ci enum abx500_chargers conn_chg; 588c2ecf20Sopenharmony_ci enum abx500_chargers prev_conn_chg; 598c2ecf20Sopenharmony_ci enum abx500_chargers online_chg; 608c2ecf20Sopenharmony_ci enum abx500_chargers prev_online_chg; 618c2ecf20Sopenharmony_ci enum abx500_chargers charger_type; 628c2ecf20Sopenharmony_ci bool usb_chg_ok; 638c2ecf20Sopenharmony_ci bool ac_chg_ok; 648c2ecf20Sopenharmony_ci int usb_volt; 658c2ecf20Sopenharmony_ci int usb_curr; 668c2ecf20Sopenharmony_ci int ac_volt; 678c2ecf20Sopenharmony_ci int ac_curr; 688c2ecf20Sopenharmony_ci int usb_vset; 698c2ecf20Sopenharmony_ci int usb_iset; 708c2ecf20Sopenharmony_ci int ac_vset; 718c2ecf20Sopenharmony_ci int ac_iset; 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistruct abx500_chargalg_suspension_status { 758c2ecf20Sopenharmony_ci bool suspended_change; 768c2ecf20Sopenharmony_ci bool ac_suspended; 778c2ecf20Sopenharmony_ci bool usb_suspended; 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistruct abx500_chargalg_current_step_status { 818c2ecf20Sopenharmony_ci bool curr_step_change; 828c2ecf20Sopenharmony_ci int curr_step; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct abx500_chargalg_battery_data { 868c2ecf20Sopenharmony_ci int temp; 878c2ecf20Sopenharmony_ci int volt; 888c2ecf20Sopenharmony_ci int avg_curr; 898c2ecf20Sopenharmony_ci int inst_curr; 908c2ecf20Sopenharmony_ci int percent; 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cienum abx500_chargalg_states { 948c2ecf20Sopenharmony_ci STATE_HANDHELD_INIT, 958c2ecf20Sopenharmony_ci STATE_HANDHELD, 968c2ecf20Sopenharmony_ci STATE_CHG_NOT_OK_INIT, 978c2ecf20Sopenharmony_ci STATE_CHG_NOT_OK, 988c2ecf20Sopenharmony_ci STATE_HW_TEMP_PROTECT_INIT, 998c2ecf20Sopenharmony_ci STATE_HW_TEMP_PROTECT, 1008c2ecf20Sopenharmony_ci STATE_NORMAL_INIT, 1018c2ecf20Sopenharmony_ci STATE_NORMAL, 1028c2ecf20Sopenharmony_ci STATE_WAIT_FOR_RECHARGE_INIT, 1038c2ecf20Sopenharmony_ci STATE_WAIT_FOR_RECHARGE, 1048c2ecf20Sopenharmony_ci STATE_MAINTENANCE_A_INIT, 1058c2ecf20Sopenharmony_ci STATE_MAINTENANCE_A, 1068c2ecf20Sopenharmony_ci STATE_MAINTENANCE_B_INIT, 1078c2ecf20Sopenharmony_ci STATE_MAINTENANCE_B, 1088c2ecf20Sopenharmony_ci STATE_TEMP_UNDEROVER_INIT, 1098c2ecf20Sopenharmony_ci STATE_TEMP_UNDEROVER, 1108c2ecf20Sopenharmony_ci STATE_TEMP_LOWHIGH_INIT, 1118c2ecf20Sopenharmony_ci STATE_TEMP_LOWHIGH, 1128c2ecf20Sopenharmony_ci STATE_SUSPENDED_INIT, 1138c2ecf20Sopenharmony_ci STATE_SUSPENDED, 1148c2ecf20Sopenharmony_ci STATE_OVV_PROTECT_INIT, 1158c2ecf20Sopenharmony_ci STATE_OVV_PROTECT, 1168c2ecf20Sopenharmony_ci STATE_SAFETY_TIMER_EXPIRED_INIT, 1178c2ecf20Sopenharmony_ci STATE_SAFETY_TIMER_EXPIRED, 1188c2ecf20Sopenharmony_ci STATE_BATT_REMOVED_INIT, 1198c2ecf20Sopenharmony_ci STATE_BATT_REMOVED, 1208c2ecf20Sopenharmony_ci STATE_WD_EXPIRED_INIT, 1218c2ecf20Sopenharmony_ci STATE_WD_EXPIRED, 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic const char *states[] = { 1258c2ecf20Sopenharmony_ci "HANDHELD_INIT", 1268c2ecf20Sopenharmony_ci "HANDHELD", 1278c2ecf20Sopenharmony_ci "CHG_NOT_OK_INIT", 1288c2ecf20Sopenharmony_ci "CHG_NOT_OK", 1298c2ecf20Sopenharmony_ci "HW_TEMP_PROTECT_INIT", 1308c2ecf20Sopenharmony_ci "HW_TEMP_PROTECT", 1318c2ecf20Sopenharmony_ci "NORMAL_INIT", 1328c2ecf20Sopenharmony_ci "NORMAL", 1338c2ecf20Sopenharmony_ci "WAIT_FOR_RECHARGE_INIT", 1348c2ecf20Sopenharmony_ci "WAIT_FOR_RECHARGE", 1358c2ecf20Sopenharmony_ci "MAINTENANCE_A_INIT", 1368c2ecf20Sopenharmony_ci "MAINTENANCE_A", 1378c2ecf20Sopenharmony_ci "MAINTENANCE_B_INIT", 1388c2ecf20Sopenharmony_ci "MAINTENANCE_B", 1398c2ecf20Sopenharmony_ci "TEMP_UNDEROVER_INIT", 1408c2ecf20Sopenharmony_ci "TEMP_UNDEROVER", 1418c2ecf20Sopenharmony_ci "TEMP_LOWHIGH_INIT", 1428c2ecf20Sopenharmony_ci "TEMP_LOWHIGH", 1438c2ecf20Sopenharmony_ci "SUSPENDED_INIT", 1448c2ecf20Sopenharmony_ci "SUSPENDED", 1458c2ecf20Sopenharmony_ci "OVV_PROTECT_INIT", 1468c2ecf20Sopenharmony_ci "OVV_PROTECT", 1478c2ecf20Sopenharmony_ci "SAFETY_TIMER_EXPIRED_INIT", 1488c2ecf20Sopenharmony_ci "SAFETY_TIMER_EXPIRED", 1498c2ecf20Sopenharmony_ci "BATT_REMOVED_INIT", 1508c2ecf20Sopenharmony_ci "BATT_REMOVED", 1518c2ecf20Sopenharmony_ci "WD_EXPIRED_INIT", 1528c2ecf20Sopenharmony_ci "WD_EXPIRED", 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistruct abx500_chargalg_events { 1568c2ecf20Sopenharmony_ci bool batt_unknown; 1578c2ecf20Sopenharmony_ci bool mainextchnotok; 1588c2ecf20Sopenharmony_ci bool batt_ovv; 1598c2ecf20Sopenharmony_ci bool batt_rem; 1608c2ecf20Sopenharmony_ci bool btemp_underover; 1618c2ecf20Sopenharmony_ci bool btemp_lowhigh; 1628c2ecf20Sopenharmony_ci bool main_thermal_prot; 1638c2ecf20Sopenharmony_ci bool usb_thermal_prot; 1648c2ecf20Sopenharmony_ci bool main_ovv; 1658c2ecf20Sopenharmony_ci bool vbus_ovv; 1668c2ecf20Sopenharmony_ci bool usbchargernotok; 1678c2ecf20Sopenharmony_ci bool safety_timer_expired; 1688c2ecf20Sopenharmony_ci bool maintenance_timer_expired; 1698c2ecf20Sopenharmony_ci bool ac_wd_expired; 1708c2ecf20Sopenharmony_ci bool usb_wd_expired; 1718c2ecf20Sopenharmony_ci bool ac_cv_active; 1728c2ecf20Sopenharmony_ci bool usb_cv_active; 1738c2ecf20Sopenharmony_ci bool vbus_collapsed; 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/** 1778c2ecf20Sopenharmony_ci * struct abx500_charge_curr_maximization - Charger maximization parameters 1788c2ecf20Sopenharmony_ci * @original_iset: the non optimized/maximised charger current 1798c2ecf20Sopenharmony_ci * @current_iset: the charging current used at this moment 1808c2ecf20Sopenharmony_ci * @test_delta_i: the delta between the current we want to charge and the 1818c2ecf20Sopenharmony_ci current that is really going into the battery 1828c2ecf20Sopenharmony_ci * @condition_cnt: number of iterations needed before a new charger current 1838c2ecf20Sopenharmony_ci is set 1848c2ecf20Sopenharmony_ci * @max_current: maximum charger current 1858c2ecf20Sopenharmony_ci * @wait_cnt: to avoid too fast current step down in case of charger 1868c2ecf20Sopenharmony_ci * voltage collapse, we insert this delay between step 1878c2ecf20Sopenharmony_ci * down 1888c2ecf20Sopenharmony_ci * @level: tells in how many steps the charging current has been 1898c2ecf20Sopenharmony_ci increased 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_cistruct abx500_charge_curr_maximization { 1928c2ecf20Sopenharmony_ci int original_iset; 1938c2ecf20Sopenharmony_ci int current_iset; 1948c2ecf20Sopenharmony_ci int test_delta_i; 1958c2ecf20Sopenharmony_ci int condition_cnt; 1968c2ecf20Sopenharmony_ci int max_current; 1978c2ecf20Sopenharmony_ci int wait_cnt; 1988c2ecf20Sopenharmony_ci u8 level; 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cienum maxim_ret { 2028c2ecf20Sopenharmony_ci MAXIM_RET_NOACTION, 2038c2ecf20Sopenharmony_ci MAXIM_RET_CHANGE, 2048c2ecf20Sopenharmony_ci MAXIM_RET_IBAT_TOO_HIGH, 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/** 2088c2ecf20Sopenharmony_ci * struct abx500_chargalg - abx500 Charging algorithm device information 2098c2ecf20Sopenharmony_ci * @dev: pointer to the structure device 2108c2ecf20Sopenharmony_ci * @charge_status: battery operating status 2118c2ecf20Sopenharmony_ci * @eoc_cnt: counter used to determine end-of_charge 2128c2ecf20Sopenharmony_ci * @maintenance_chg: indicate if maintenance charge is active 2138c2ecf20Sopenharmony_ci * @t_hyst_norm temperature hysteresis when the temperature has been 2148c2ecf20Sopenharmony_ci * over or under normal limits 2158c2ecf20Sopenharmony_ci * @t_hyst_lowhigh temperature hysteresis when the temperature has been 2168c2ecf20Sopenharmony_ci * over or under the high or low limits 2178c2ecf20Sopenharmony_ci * @charge_state: current state of the charging algorithm 2188c2ecf20Sopenharmony_ci * @ccm charging current maximization parameters 2198c2ecf20Sopenharmony_ci * @chg_info: information about connected charger types 2208c2ecf20Sopenharmony_ci * @batt_data: data of the battery 2218c2ecf20Sopenharmony_ci * @susp_status: current charger suspension status 2228c2ecf20Sopenharmony_ci * @bm: Platform specific battery management information 2238c2ecf20Sopenharmony_ci * @curr_status: Current step status for over-current protection 2248c2ecf20Sopenharmony_ci * @parent: pointer to the struct abx500 2258c2ecf20Sopenharmony_ci * @chargalg_psy: structure that holds the battery properties exposed by 2268c2ecf20Sopenharmony_ci * the charging algorithm 2278c2ecf20Sopenharmony_ci * @events: structure for information about events triggered 2288c2ecf20Sopenharmony_ci * @chargalg_wq: work queue for running the charging algorithm 2298c2ecf20Sopenharmony_ci * @chargalg_periodic_work: work to run the charging algorithm periodically 2308c2ecf20Sopenharmony_ci * @chargalg_wd_work: work to kick the charger watchdog periodically 2318c2ecf20Sopenharmony_ci * @chargalg_work: work to run the charging algorithm instantly 2328c2ecf20Sopenharmony_ci * @safety_timer: charging safety timer 2338c2ecf20Sopenharmony_ci * @maintenance_timer: maintenance charging timer 2348c2ecf20Sopenharmony_ci * @chargalg_kobject: structure of type kobject 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_cistruct abx500_chargalg { 2378c2ecf20Sopenharmony_ci struct device *dev; 2388c2ecf20Sopenharmony_ci int charge_status; 2398c2ecf20Sopenharmony_ci int eoc_cnt; 2408c2ecf20Sopenharmony_ci bool maintenance_chg; 2418c2ecf20Sopenharmony_ci int t_hyst_norm; 2428c2ecf20Sopenharmony_ci int t_hyst_lowhigh; 2438c2ecf20Sopenharmony_ci enum abx500_chargalg_states charge_state; 2448c2ecf20Sopenharmony_ci struct abx500_charge_curr_maximization ccm; 2458c2ecf20Sopenharmony_ci struct abx500_chargalg_charger_info chg_info; 2468c2ecf20Sopenharmony_ci struct abx500_chargalg_battery_data batt_data; 2478c2ecf20Sopenharmony_ci struct abx500_chargalg_suspension_status susp_status; 2488c2ecf20Sopenharmony_ci struct ab8500 *parent; 2498c2ecf20Sopenharmony_ci struct abx500_chargalg_current_step_status curr_status; 2508c2ecf20Sopenharmony_ci struct abx500_bm_data *bm; 2518c2ecf20Sopenharmony_ci struct power_supply *chargalg_psy; 2528c2ecf20Sopenharmony_ci struct ux500_charger *ac_chg; 2538c2ecf20Sopenharmony_ci struct ux500_charger *usb_chg; 2548c2ecf20Sopenharmony_ci struct abx500_chargalg_events events; 2558c2ecf20Sopenharmony_ci struct workqueue_struct *chargalg_wq; 2568c2ecf20Sopenharmony_ci struct delayed_work chargalg_periodic_work; 2578c2ecf20Sopenharmony_ci struct delayed_work chargalg_wd_work; 2588c2ecf20Sopenharmony_ci struct work_struct chargalg_work; 2598c2ecf20Sopenharmony_ci struct hrtimer safety_timer; 2608c2ecf20Sopenharmony_ci struct hrtimer maintenance_timer; 2618c2ecf20Sopenharmony_ci struct kobject chargalg_kobject; 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci/*External charger prepare notifier*/ 2658c2ecf20Sopenharmony_ciBLOCKING_NOTIFIER_HEAD(charger_notifier_list); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* Main battery properties */ 2688c2ecf20Sopenharmony_cistatic enum power_supply_property abx500_chargalg_props[] = { 2698c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 2708c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistruct abx500_chargalg_sysfs_entry { 2748c2ecf20Sopenharmony_ci struct attribute attr; 2758c2ecf20Sopenharmony_ci ssize_t (*show)(struct abx500_chargalg *, char *); 2768c2ecf20Sopenharmony_ci ssize_t (*store)(struct abx500_chargalg *, const char *, size_t); 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci/** 2808c2ecf20Sopenharmony_ci * abx500_chargalg_safety_timer_expired() - Expiration of the safety timer 2818c2ecf20Sopenharmony_ci * @timer: pointer to the hrtimer structure 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * This function gets called when the safety timer for the charger 2848c2ecf20Sopenharmony_ci * expires 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_cistatic enum hrtimer_restart 2878c2ecf20Sopenharmony_ciabx500_chargalg_safety_timer_expired(struct hrtimer *timer) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg, 2908c2ecf20Sopenharmony_ci safety_timer); 2918c2ecf20Sopenharmony_ci dev_err(di->dev, "Safety timer expired\n"); 2928c2ecf20Sopenharmony_ci di->events.safety_timer_expired = true; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Trigger execution of the algorithm instantly */ 2958c2ecf20Sopenharmony_ci queue_work(di->chargalg_wq, &di->chargalg_work); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return HRTIMER_NORESTART; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/** 3018c2ecf20Sopenharmony_ci * abx500_chargalg_maintenance_timer_expired() - Expiration of 3028c2ecf20Sopenharmony_ci * the maintenance timer 3038c2ecf20Sopenharmony_ci * @timer: pointer to the timer structure 3048c2ecf20Sopenharmony_ci * 3058c2ecf20Sopenharmony_ci * This function gets called when the maintenence timer 3068c2ecf20Sopenharmony_ci * expires 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_cistatic enum hrtimer_restart 3098c2ecf20Sopenharmony_ciabx500_chargalg_maintenance_timer_expired(struct hrtimer *timer) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci struct abx500_chargalg *di = container_of(timer, struct abx500_chargalg, 3138c2ecf20Sopenharmony_ci maintenance_timer); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Maintenance timer expired\n"); 3168c2ecf20Sopenharmony_ci di->events.maintenance_timer_expired = true; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Trigger execution of the algorithm instantly */ 3198c2ecf20Sopenharmony_ci queue_work(di->chargalg_wq, &di->chargalg_work); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return HRTIMER_NORESTART; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci/** 3258c2ecf20Sopenharmony_ci * abx500_chargalg_state_to() - Change charge state 3268c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 3278c2ecf20Sopenharmony_ci * 3288c2ecf20Sopenharmony_ci * This function gets called when a charge state change should occur 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_cistatic void abx500_chargalg_state_to(struct abx500_chargalg *di, 3318c2ecf20Sopenharmony_ci enum abx500_chargalg_states state) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci dev_dbg(di->dev, 3348c2ecf20Sopenharmony_ci "State changed: %s (From state: [%d] %s =to=> [%d] %s )\n", 3358c2ecf20Sopenharmony_ci di->charge_state == state ? "NO" : "YES", 3368c2ecf20Sopenharmony_ci di->charge_state, 3378c2ecf20Sopenharmony_ci states[di->charge_state], 3388c2ecf20Sopenharmony_ci state, 3398c2ecf20Sopenharmony_ci states[state]); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci di->charge_state = state; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci switch (di->charge_state) { 3478c2ecf20Sopenharmony_ci case STATE_NORMAL: 3488c2ecf20Sopenharmony_ci case STATE_MAINTENANCE_A: 3498c2ecf20Sopenharmony_ci case STATE_MAINTENANCE_B: 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci default: 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (di->chg_info.charger_type & USB_CHG) { 3568c2ecf20Sopenharmony_ci return di->usb_chg->ops.check_enable(di->usb_chg, 3578c2ecf20Sopenharmony_ci di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, 3588c2ecf20Sopenharmony_ci di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); 3598c2ecf20Sopenharmony_ci } else if ((di->chg_info.charger_type & AC_CHG) && 3608c2ecf20Sopenharmony_ci !(di->ac_chg->external)) { 3618c2ecf20Sopenharmony_ci return di->ac_chg->ops.check_enable(di->ac_chg, 3628c2ecf20Sopenharmony_ci di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, 3638c2ecf20Sopenharmony_ci di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/** 3698c2ecf20Sopenharmony_ci * abx500_chargalg_check_charger_connection() - Check charger connection change 3708c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 3718c2ecf20Sopenharmony_ci * 3728c2ecf20Sopenharmony_ci * This function will check if there is a change in the charger connection 3738c2ecf20Sopenharmony_ci * and change charge state accordingly. AC has precedence over USB. 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_cistatic int abx500_chargalg_check_charger_connection(struct abx500_chargalg *di) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci if (di->chg_info.conn_chg != di->chg_info.prev_conn_chg || 3788c2ecf20Sopenharmony_ci di->susp_status.suspended_change) { 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * Charger state changed or suspension 3818c2ecf20Sopenharmony_ci * has changed since last update 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci if ((di->chg_info.conn_chg & AC_CHG) && 3848c2ecf20Sopenharmony_ci !di->susp_status.ac_suspended) { 3858c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Charging source is AC\n"); 3868c2ecf20Sopenharmony_ci if (di->chg_info.charger_type != AC_CHG) { 3878c2ecf20Sopenharmony_ci di->chg_info.charger_type = AC_CHG; 3888c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL_INIT); 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci } else if ((di->chg_info.conn_chg & USB_CHG) && 3918c2ecf20Sopenharmony_ci !di->susp_status.usb_suspended) { 3928c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Charging source is USB\n"); 3938c2ecf20Sopenharmony_ci di->chg_info.charger_type = USB_CHG; 3948c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL_INIT); 3958c2ecf20Sopenharmony_ci } else if (di->chg_info.conn_chg && 3968c2ecf20Sopenharmony_ci (di->susp_status.ac_suspended || 3978c2ecf20Sopenharmony_ci di->susp_status.usb_suspended)) { 3988c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Charging is suspended\n"); 3998c2ecf20Sopenharmony_ci di->chg_info.charger_type = NO_CHG; 4008c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_SUSPENDED_INIT); 4018c2ecf20Sopenharmony_ci } else { 4028c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Charging source is OFF\n"); 4038c2ecf20Sopenharmony_ci di->chg_info.charger_type = NO_CHG; 4048c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_HANDHELD_INIT); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci di->chg_info.prev_conn_chg = di->chg_info.conn_chg; 4078c2ecf20Sopenharmony_ci di->susp_status.suspended_change = false; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci return di->chg_info.conn_chg; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci/** 4138c2ecf20Sopenharmony_ci * abx500_chargalg_check_current_step_status() - Check charging current 4148c2ecf20Sopenharmony_ci * step status. 4158c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 4168c2ecf20Sopenharmony_ci * 4178c2ecf20Sopenharmony_ci * This function will check if there is a change in the charging current step 4188c2ecf20Sopenharmony_ci * and change charge state accordingly. 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_cistatic void abx500_chargalg_check_current_step_status 4218c2ecf20Sopenharmony_ci (struct abx500_chargalg *di) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci if (di->curr_status.curr_step_change) 4248c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL_INIT); 4258c2ecf20Sopenharmony_ci di->curr_status.curr_step_change = false; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci/** 4298c2ecf20Sopenharmony_ci * abx500_chargalg_start_safety_timer() - Start charging safety timer 4308c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 4318c2ecf20Sopenharmony_ci * 4328c2ecf20Sopenharmony_ci * The safety timer is used to avoid overcharging of old or bad batteries. 4338c2ecf20Sopenharmony_ci * There are different timers for AC and USB 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_cistatic void abx500_chargalg_start_safety_timer(struct abx500_chargalg *di) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci /* Charger-dependent expiration time in hours*/ 4388c2ecf20Sopenharmony_ci int timer_expiration = 0; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci switch (di->chg_info.charger_type) { 4418c2ecf20Sopenharmony_ci case AC_CHG: 4428c2ecf20Sopenharmony_ci timer_expiration = di->bm->main_safety_tmr_h; 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci case USB_CHG: 4468c2ecf20Sopenharmony_ci timer_expiration = di->bm->usb_safety_tmr_h; 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci default: 4508c2ecf20Sopenharmony_ci dev_err(di->dev, "Unknown charger to charge from\n"); 4518c2ecf20Sopenharmony_ci break; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci di->events.safety_timer_expired = false; 4558c2ecf20Sopenharmony_ci hrtimer_set_expires_range(&di->safety_timer, 4568c2ecf20Sopenharmony_ci ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0), 4578c2ecf20Sopenharmony_ci ktime_set(FIVE_MINUTES_IN_SECONDS, 0)); 4588c2ecf20Sopenharmony_ci hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci/** 4628c2ecf20Sopenharmony_ci * abx500_chargalg_stop_safety_timer() - Stop charging safety timer 4638c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 4648c2ecf20Sopenharmony_ci * 4658c2ecf20Sopenharmony_ci * The safety timer is stopped whenever the NORMAL state is exited 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_cistatic void abx500_chargalg_stop_safety_timer(struct abx500_chargalg *di) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci if (hrtimer_try_to_cancel(&di->safety_timer) >= 0) 4708c2ecf20Sopenharmony_ci di->events.safety_timer_expired = false; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci/** 4748c2ecf20Sopenharmony_ci * abx500_chargalg_start_maintenance_timer() - Start charging maintenance timer 4758c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 4768c2ecf20Sopenharmony_ci * @duration: duration of ther maintenance timer in hours 4778c2ecf20Sopenharmony_ci * 4788c2ecf20Sopenharmony_ci * The maintenance timer is used to maintain the charge in the battery once 4798c2ecf20Sopenharmony_ci * the battery is considered full. These timers are chosen to match the 4808c2ecf20Sopenharmony_ci * discharge curve of the battery 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_cistatic void abx500_chargalg_start_maintenance_timer(struct abx500_chargalg *di, 4838c2ecf20Sopenharmony_ci int duration) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci hrtimer_set_expires_range(&di->maintenance_timer, 4868c2ecf20Sopenharmony_ci ktime_set(duration * ONE_HOUR_IN_SECONDS, 0), 4878c2ecf20Sopenharmony_ci ktime_set(FIVE_MINUTES_IN_SECONDS, 0)); 4888c2ecf20Sopenharmony_ci di->events.maintenance_timer_expired = false; 4898c2ecf20Sopenharmony_ci hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/** 4938c2ecf20Sopenharmony_ci * abx500_chargalg_stop_maintenance_timer() - Stop maintenance timer 4948c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 4958c2ecf20Sopenharmony_ci * 4968c2ecf20Sopenharmony_ci * The maintenance timer is stopped whenever maintenance ends or when another 4978c2ecf20Sopenharmony_ci * state is entered 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_cistatic void abx500_chargalg_stop_maintenance_timer(struct abx500_chargalg *di) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0) 5028c2ecf20Sopenharmony_ci di->events.maintenance_timer_expired = false; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/** 5068c2ecf20Sopenharmony_ci * abx500_chargalg_kick_watchdog() - Kick charger watchdog 5078c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 5088c2ecf20Sopenharmony_ci * 5098c2ecf20Sopenharmony_ci * The charger watchdog have to be kicked periodically whenever the charger is 5108c2ecf20Sopenharmony_ci * on, else the ABB will reset the system 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_cistatic int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci /* Check if charger exists and kick watchdog if charging */ 5158c2ecf20Sopenharmony_ci if (di->ac_chg && di->ac_chg->ops.kick_wd && 5168c2ecf20Sopenharmony_ci di->chg_info.online_chg & AC_CHG) { 5178c2ecf20Sopenharmony_ci /* 5188c2ecf20Sopenharmony_ci * If AB charger watchdog expired, pm2xxx charging 5198c2ecf20Sopenharmony_ci * gets disabled. To be safe, kick both AB charger watchdog 5208c2ecf20Sopenharmony_ci * and pm2xxx watchdog. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci if (di->ac_chg->external && 5238c2ecf20Sopenharmony_ci di->usb_chg && di->usb_chg->ops.kick_wd) 5248c2ecf20Sopenharmony_ci di->usb_chg->ops.kick_wd(di->usb_chg); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return di->ac_chg->ops.kick_wd(di->ac_chg); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci else if (di->usb_chg && di->usb_chg->ops.kick_wd && 5298c2ecf20Sopenharmony_ci di->chg_info.online_chg & USB_CHG) 5308c2ecf20Sopenharmony_ci return di->usb_chg->ops.kick_wd(di->usb_chg); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return -ENXIO; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci/** 5368c2ecf20Sopenharmony_ci * abx500_chargalg_ac_en() - Turn on/off the AC charger 5378c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 5388c2ecf20Sopenharmony_ci * @enable: charger on/off 5398c2ecf20Sopenharmony_ci * @vset: requested charger output voltage 5408c2ecf20Sopenharmony_ci * @iset: requested charger output current 5418c2ecf20Sopenharmony_ci * 5428c2ecf20Sopenharmony_ci * The AC charger will be turned on/off with the requested charge voltage and 5438c2ecf20Sopenharmony_ci * current 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_cistatic int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable, 5468c2ecf20Sopenharmony_ci int vset, int iset) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci static int abx500_chargalg_ex_ac_enable_toggle; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (!di->ac_chg || !di->ac_chg->ops.enable) 5518c2ecf20Sopenharmony_ci return -ENXIO; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* Select maximum of what both the charger and the battery supports */ 5548c2ecf20Sopenharmony_ci if (di->ac_chg->max_out_volt) 5558c2ecf20Sopenharmony_ci vset = min(vset, di->ac_chg->max_out_volt); 5568c2ecf20Sopenharmony_ci if (di->ac_chg->max_out_curr) 5578c2ecf20Sopenharmony_ci iset = min(iset, di->ac_chg->max_out_curr); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci di->chg_info.ac_iset = iset; 5608c2ecf20Sopenharmony_ci di->chg_info.ac_vset = vset; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* Enable external charger */ 5638c2ecf20Sopenharmony_ci if (enable && di->ac_chg->external && 5648c2ecf20Sopenharmony_ci !abx500_chargalg_ex_ac_enable_toggle) { 5658c2ecf20Sopenharmony_ci blocking_notifier_call_chain(&charger_notifier_list, 5668c2ecf20Sopenharmony_ci 0, di->dev); 5678c2ecf20Sopenharmony_ci abx500_chargalg_ex_ac_enable_toggle++; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset); 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci/** 5748c2ecf20Sopenharmony_ci * abx500_chargalg_usb_en() - Turn on/off the USB charger 5758c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 5768c2ecf20Sopenharmony_ci * @enable: charger on/off 5778c2ecf20Sopenharmony_ci * @vset: requested charger output voltage 5788c2ecf20Sopenharmony_ci * @iset: requested charger output current 5798c2ecf20Sopenharmony_ci * 5808c2ecf20Sopenharmony_ci * The USB charger will be turned on/off with the requested charge voltage and 5818c2ecf20Sopenharmony_ci * current 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_cistatic int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable, 5848c2ecf20Sopenharmony_ci int vset, int iset) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci if (!di->usb_chg || !di->usb_chg->ops.enable) 5878c2ecf20Sopenharmony_ci return -ENXIO; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* Select maximum of what both the charger and the battery supports */ 5908c2ecf20Sopenharmony_ci if (di->usb_chg->max_out_volt) 5918c2ecf20Sopenharmony_ci vset = min(vset, di->usb_chg->max_out_volt); 5928c2ecf20Sopenharmony_ci if (di->usb_chg->max_out_curr) 5938c2ecf20Sopenharmony_ci iset = min(iset, di->usb_chg->max_out_curr); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci di->chg_info.usb_iset = iset; 5968c2ecf20Sopenharmony_ci di->chg_info.usb_vset = vset; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset); 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci/** 6028c2ecf20Sopenharmony_ci * abx500_chargalg_update_chg_curr() - Update charger current 6038c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 6048c2ecf20Sopenharmony_ci * @iset: requested charger output current 6058c2ecf20Sopenharmony_ci * 6068c2ecf20Sopenharmony_ci * The charger output current will be updated for the charger 6078c2ecf20Sopenharmony_ci * that is currently in use 6088c2ecf20Sopenharmony_ci */ 6098c2ecf20Sopenharmony_cistatic int abx500_chargalg_update_chg_curr(struct abx500_chargalg *di, 6108c2ecf20Sopenharmony_ci int iset) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci /* Check if charger exists and update current if charging */ 6138c2ecf20Sopenharmony_ci if (di->ac_chg && di->ac_chg->ops.update_curr && 6148c2ecf20Sopenharmony_ci di->chg_info.charger_type & AC_CHG) { 6158c2ecf20Sopenharmony_ci /* 6168c2ecf20Sopenharmony_ci * Select maximum of what both the charger 6178c2ecf20Sopenharmony_ci * and the battery supports 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_ci if (di->ac_chg->max_out_curr) 6208c2ecf20Sopenharmony_ci iset = min(iset, di->ac_chg->max_out_curr); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci di->chg_info.ac_iset = iset; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return di->ac_chg->ops.update_curr(di->ac_chg, iset); 6258c2ecf20Sopenharmony_ci } else if (di->usb_chg && di->usb_chg->ops.update_curr && 6268c2ecf20Sopenharmony_ci di->chg_info.charger_type & USB_CHG) { 6278c2ecf20Sopenharmony_ci /* 6288c2ecf20Sopenharmony_ci * Select maximum of what both the charger 6298c2ecf20Sopenharmony_ci * and the battery supports 6308c2ecf20Sopenharmony_ci */ 6318c2ecf20Sopenharmony_ci if (di->usb_chg->max_out_curr) 6328c2ecf20Sopenharmony_ci iset = min(iset, di->usb_chg->max_out_curr); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci di->chg_info.usb_iset = iset; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci return di->usb_chg->ops.update_curr(di->usb_chg, iset); 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return -ENXIO; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/** 6438c2ecf20Sopenharmony_ci * abx500_chargalg_stop_charging() - Stop charging 6448c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 6458c2ecf20Sopenharmony_ci * 6468c2ecf20Sopenharmony_ci * This function is called from any state where charging should be stopped. 6478c2ecf20Sopenharmony_ci * All charging is disabled and all status parameters and timers are changed 6488c2ecf20Sopenharmony_ci * accordingly 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_cistatic void abx500_chargalg_stop_charging(struct abx500_chargalg *di) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci abx500_chargalg_ac_en(di, false, 0, 0); 6538c2ecf20Sopenharmony_ci abx500_chargalg_usb_en(di, false, 0, 0); 6548c2ecf20Sopenharmony_ci abx500_chargalg_stop_safety_timer(di); 6558c2ecf20Sopenharmony_ci abx500_chargalg_stop_maintenance_timer(di); 6568c2ecf20Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; 6578c2ecf20Sopenharmony_ci di->maintenance_chg = false; 6588c2ecf20Sopenharmony_ci cancel_delayed_work(&di->chargalg_wd_work); 6598c2ecf20Sopenharmony_ci power_supply_changed(di->chargalg_psy); 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci/** 6638c2ecf20Sopenharmony_ci * abx500_chargalg_hold_charging() - Pauses charging 6648c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 6658c2ecf20Sopenharmony_ci * 6668c2ecf20Sopenharmony_ci * This function is called in the case where maintenance charging has been 6678c2ecf20Sopenharmony_ci * disabled and instead a battery voltage mode is entered to check when the 6688c2ecf20Sopenharmony_ci * battery voltage has reached a certain recharge voltage 6698c2ecf20Sopenharmony_ci */ 6708c2ecf20Sopenharmony_cistatic void abx500_chargalg_hold_charging(struct abx500_chargalg *di) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci abx500_chargalg_ac_en(di, false, 0, 0); 6738c2ecf20Sopenharmony_ci abx500_chargalg_usb_en(di, false, 0, 0); 6748c2ecf20Sopenharmony_ci abx500_chargalg_stop_safety_timer(di); 6758c2ecf20Sopenharmony_ci abx500_chargalg_stop_maintenance_timer(di); 6768c2ecf20Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_CHARGING; 6778c2ecf20Sopenharmony_ci di->maintenance_chg = false; 6788c2ecf20Sopenharmony_ci cancel_delayed_work(&di->chargalg_wd_work); 6798c2ecf20Sopenharmony_ci power_supply_changed(di->chargalg_psy); 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci/** 6838c2ecf20Sopenharmony_ci * abx500_chargalg_start_charging() - Start the charger 6848c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 6858c2ecf20Sopenharmony_ci * @vset: requested charger output voltage 6868c2ecf20Sopenharmony_ci * @iset: requested charger output current 6878c2ecf20Sopenharmony_ci * 6888c2ecf20Sopenharmony_ci * A charger will be enabled depending on the requested charger type that was 6898c2ecf20Sopenharmony_ci * detected previously. 6908c2ecf20Sopenharmony_ci */ 6918c2ecf20Sopenharmony_cistatic void abx500_chargalg_start_charging(struct abx500_chargalg *di, 6928c2ecf20Sopenharmony_ci int vset, int iset) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci switch (di->chg_info.charger_type) { 6958c2ecf20Sopenharmony_ci case AC_CHG: 6968c2ecf20Sopenharmony_ci dev_dbg(di->dev, 6978c2ecf20Sopenharmony_ci "AC parameters: Vset %d, Ich %d\n", vset, iset); 6988c2ecf20Sopenharmony_ci abx500_chargalg_usb_en(di, false, 0, 0); 6998c2ecf20Sopenharmony_ci abx500_chargalg_ac_en(di, true, vset, iset); 7008c2ecf20Sopenharmony_ci break; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci case USB_CHG: 7038c2ecf20Sopenharmony_ci dev_dbg(di->dev, 7048c2ecf20Sopenharmony_ci "USB parameters: Vset %d, Ich %d\n", vset, iset); 7058c2ecf20Sopenharmony_ci abx500_chargalg_ac_en(di, false, 0, 0); 7068c2ecf20Sopenharmony_ci abx500_chargalg_usb_en(di, true, vset, iset); 7078c2ecf20Sopenharmony_ci break; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci default: 7108c2ecf20Sopenharmony_ci dev_err(di->dev, "Unknown charger to charge from\n"); 7118c2ecf20Sopenharmony_ci break; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/** 7168c2ecf20Sopenharmony_ci * abx500_chargalg_check_temp() - Check battery temperature ranges 7178c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 7188c2ecf20Sopenharmony_ci * 7198c2ecf20Sopenharmony_ci * The battery temperature is checked against the predefined limits and the 7208c2ecf20Sopenharmony_ci * charge state is changed accordingly 7218c2ecf20Sopenharmony_ci */ 7228c2ecf20Sopenharmony_cistatic void abx500_chargalg_check_temp(struct abx500_chargalg *di) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci if (di->batt_data.temp > (di->bm->temp_low + di->t_hyst_norm) && 7258c2ecf20Sopenharmony_ci di->batt_data.temp < (di->bm->temp_high - di->t_hyst_norm)) { 7268c2ecf20Sopenharmony_ci /* Temp OK! */ 7278c2ecf20Sopenharmony_ci di->events.btemp_underover = false; 7288c2ecf20Sopenharmony_ci di->events.btemp_lowhigh = false; 7298c2ecf20Sopenharmony_ci di->t_hyst_norm = 0; 7308c2ecf20Sopenharmony_ci di->t_hyst_lowhigh = 0; 7318c2ecf20Sopenharmony_ci } else { 7328c2ecf20Sopenharmony_ci if (((di->batt_data.temp >= di->bm->temp_high) && 7338c2ecf20Sopenharmony_ci (di->batt_data.temp < 7348c2ecf20Sopenharmony_ci (di->bm->temp_over - di->t_hyst_lowhigh))) || 7358c2ecf20Sopenharmony_ci ((di->batt_data.temp > 7368c2ecf20Sopenharmony_ci (di->bm->temp_under + di->t_hyst_lowhigh)) && 7378c2ecf20Sopenharmony_ci (di->batt_data.temp <= di->bm->temp_low))) { 7388c2ecf20Sopenharmony_ci /* TEMP minor!!!!! */ 7398c2ecf20Sopenharmony_ci di->events.btemp_underover = false; 7408c2ecf20Sopenharmony_ci di->events.btemp_lowhigh = true; 7418c2ecf20Sopenharmony_ci di->t_hyst_norm = di->bm->temp_hysteresis; 7428c2ecf20Sopenharmony_ci di->t_hyst_lowhigh = 0; 7438c2ecf20Sopenharmony_ci } else if (di->batt_data.temp <= di->bm->temp_under || 7448c2ecf20Sopenharmony_ci di->batt_data.temp >= di->bm->temp_over) { 7458c2ecf20Sopenharmony_ci /* TEMP major!!!!! */ 7468c2ecf20Sopenharmony_ci di->events.btemp_underover = true; 7478c2ecf20Sopenharmony_ci di->events.btemp_lowhigh = false; 7488c2ecf20Sopenharmony_ci di->t_hyst_norm = 0; 7498c2ecf20Sopenharmony_ci di->t_hyst_lowhigh = di->bm->temp_hysteresis; 7508c2ecf20Sopenharmony_ci } else { 7518c2ecf20Sopenharmony_ci /* Within hysteresis */ 7528c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Within hysteresis limit temp: %d " 7538c2ecf20Sopenharmony_ci "hyst_lowhigh %d, hyst normal %d\n", 7548c2ecf20Sopenharmony_ci di->batt_data.temp, di->t_hyst_lowhigh, 7558c2ecf20Sopenharmony_ci di->t_hyst_norm); 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci/** 7618c2ecf20Sopenharmony_ci * abx500_chargalg_check_charger_voltage() - Check charger voltage 7628c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 7638c2ecf20Sopenharmony_ci * 7648c2ecf20Sopenharmony_ci * Charger voltage is checked against maximum limit 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_cistatic void abx500_chargalg_check_charger_voltage(struct abx500_chargalg *di) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci if (di->chg_info.usb_volt > di->bm->chg_params->usb_volt_max) 7698c2ecf20Sopenharmony_ci di->chg_info.usb_chg_ok = false; 7708c2ecf20Sopenharmony_ci else 7718c2ecf20Sopenharmony_ci di->chg_info.usb_chg_ok = true; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (di->chg_info.ac_volt > di->bm->chg_params->ac_volt_max) 7748c2ecf20Sopenharmony_ci di->chg_info.ac_chg_ok = false; 7758c2ecf20Sopenharmony_ci else 7768c2ecf20Sopenharmony_ci di->chg_info.ac_chg_ok = true; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci/** 7818c2ecf20Sopenharmony_ci * abx500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled 7828c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 7838c2ecf20Sopenharmony_ci * 7848c2ecf20Sopenharmony_ci * End-of-charge criteria is fulfilled when the battery voltage is above a 7858c2ecf20Sopenharmony_ci * certain limit and the battery current is below a certain limit for a 7868c2ecf20Sopenharmony_ci * predefined number of consecutive seconds. If true, the battery is full 7878c2ecf20Sopenharmony_ci */ 7888c2ecf20Sopenharmony_cistatic void abx500_chargalg_end_of_charge(struct abx500_chargalg *di) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING && 7918c2ecf20Sopenharmony_ci di->charge_state == STATE_NORMAL && 7928c2ecf20Sopenharmony_ci !di->maintenance_chg && (di->batt_data.volt >= 7938c2ecf20Sopenharmony_ci di->bm->bat_type[di->bm->batt_id].termination_vol || 7948c2ecf20Sopenharmony_ci di->events.usb_cv_active || di->events.ac_cv_active) && 7958c2ecf20Sopenharmony_ci di->batt_data.avg_curr < 7968c2ecf20Sopenharmony_ci di->bm->bat_type[di->bm->batt_id].termination_curr && 7978c2ecf20Sopenharmony_ci di->batt_data.avg_curr > 0) { 7988c2ecf20Sopenharmony_ci if (++di->eoc_cnt >= EOC_COND_CNT) { 7998c2ecf20Sopenharmony_ci di->eoc_cnt = 0; 8008c2ecf20Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_FULL; 8018c2ecf20Sopenharmony_ci di->maintenance_chg = true; 8028c2ecf20Sopenharmony_ci dev_dbg(di->dev, "EOC reached!\n"); 8038c2ecf20Sopenharmony_ci power_supply_changed(di->chargalg_psy); 8048c2ecf20Sopenharmony_ci } else { 8058c2ecf20Sopenharmony_ci dev_dbg(di->dev, 8068c2ecf20Sopenharmony_ci " EOC limit reached for the %d" 8078c2ecf20Sopenharmony_ci " time, out of %d before EOC\n", 8088c2ecf20Sopenharmony_ci di->eoc_cnt, 8098c2ecf20Sopenharmony_ci EOC_COND_CNT); 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci } else { 8128c2ecf20Sopenharmony_ci di->eoc_cnt = 0; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic void init_maxim_chg_curr(struct abx500_chargalg *di) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci di->ccm.original_iset = 8198c2ecf20Sopenharmony_ci di->bm->bat_type[di->bm->batt_id].normal_cur_lvl; 8208c2ecf20Sopenharmony_ci di->ccm.current_iset = 8218c2ecf20Sopenharmony_ci di->bm->bat_type[di->bm->batt_id].normal_cur_lvl; 8228c2ecf20Sopenharmony_ci di->ccm.test_delta_i = di->bm->maxi->charger_curr_step; 8238c2ecf20Sopenharmony_ci di->ccm.max_current = di->bm->maxi->chg_curr; 8248c2ecf20Sopenharmony_ci di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 8258c2ecf20Sopenharmony_ci di->ccm.level = 0; 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci/** 8298c2ecf20Sopenharmony_ci * abx500_chargalg_chg_curr_maxim - increases the charger current to 8308c2ecf20Sopenharmony_ci * compensate for the system load 8318c2ecf20Sopenharmony_ci * @di pointer to the abx500_chargalg structure 8328c2ecf20Sopenharmony_ci * 8338c2ecf20Sopenharmony_ci * This maximization function is used to raise the charger current to get the 8348c2ecf20Sopenharmony_ci * battery current as close to the optimal value as possible. The battery 8358c2ecf20Sopenharmony_ci * current during charging is affected by the system load 8368c2ecf20Sopenharmony_ci */ 8378c2ecf20Sopenharmony_cistatic enum maxim_ret abx500_chargalg_chg_curr_maxim(struct abx500_chargalg *di) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci int delta_i; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (!di->bm->maxi->ena_maxi) 8428c2ecf20Sopenharmony_ci return MAXIM_RET_NOACTION; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci delta_i = di->ccm.original_iset - di->batt_data.inst_curr; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (di->events.vbus_collapsed) { 8478c2ecf20Sopenharmony_ci dev_dbg(di->dev, "Charger voltage has collapsed %d\n", 8488c2ecf20Sopenharmony_ci di->ccm.wait_cnt); 8498c2ecf20Sopenharmony_ci if (di->ccm.wait_cnt == 0) { 8508c2ecf20Sopenharmony_ci dev_dbg(di->dev, "lowering current\n"); 8518c2ecf20Sopenharmony_ci di->ccm.wait_cnt++; 8528c2ecf20Sopenharmony_ci di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 8538c2ecf20Sopenharmony_ci di->ccm.max_current = 8548c2ecf20Sopenharmony_ci di->ccm.current_iset - di->ccm.test_delta_i; 8558c2ecf20Sopenharmony_ci di->ccm.current_iset = di->ccm.max_current; 8568c2ecf20Sopenharmony_ci di->ccm.level--; 8578c2ecf20Sopenharmony_ci return MAXIM_RET_CHANGE; 8588c2ecf20Sopenharmony_ci } else { 8598c2ecf20Sopenharmony_ci dev_dbg(di->dev, "waiting\n"); 8608c2ecf20Sopenharmony_ci /* Let's go in here twice before lowering curr again */ 8618c2ecf20Sopenharmony_ci di->ccm.wait_cnt = (di->ccm.wait_cnt + 1) % 3; 8628c2ecf20Sopenharmony_ci return MAXIM_RET_NOACTION; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci di->ccm.wait_cnt = 0; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci if ((di->batt_data.inst_curr > di->ccm.original_iset)) { 8698c2ecf20Sopenharmony_ci dev_dbg(di->dev, " Maximization Ibat (%dmA) too high" 8708c2ecf20Sopenharmony_ci " (limit %dmA) (current iset: %dmA)!\n", 8718c2ecf20Sopenharmony_ci di->batt_data.inst_curr, di->ccm.original_iset, 8728c2ecf20Sopenharmony_ci di->ccm.current_iset); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (di->ccm.current_iset == di->ccm.original_iset) 8758c2ecf20Sopenharmony_ci return MAXIM_RET_NOACTION; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 8788c2ecf20Sopenharmony_ci di->ccm.current_iset = di->ccm.original_iset; 8798c2ecf20Sopenharmony_ci di->ccm.level = 0; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci return MAXIM_RET_IBAT_TOO_HIGH; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci if (delta_i > di->ccm.test_delta_i && 8858c2ecf20Sopenharmony_ci (di->ccm.current_iset + di->ccm.test_delta_i) < 8868c2ecf20Sopenharmony_ci di->ccm.max_current) { 8878c2ecf20Sopenharmony_ci if (di->ccm.condition_cnt-- == 0) { 8888c2ecf20Sopenharmony_ci /* Increse the iset with cco.test_delta_i */ 8898c2ecf20Sopenharmony_ci di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 8908c2ecf20Sopenharmony_ci di->ccm.current_iset += di->ccm.test_delta_i; 8918c2ecf20Sopenharmony_ci di->ccm.level++; 8928c2ecf20Sopenharmony_ci dev_dbg(di->dev, " Maximization needed, increase" 8938c2ecf20Sopenharmony_ci " with %d mA to %dmA (Optimal ibat: %d)" 8948c2ecf20Sopenharmony_ci " Level %d\n", 8958c2ecf20Sopenharmony_ci di->ccm.test_delta_i, 8968c2ecf20Sopenharmony_ci di->ccm.current_iset, 8978c2ecf20Sopenharmony_ci di->ccm.original_iset, 8988c2ecf20Sopenharmony_ci di->ccm.level); 8998c2ecf20Sopenharmony_ci return MAXIM_RET_CHANGE; 9008c2ecf20Sopenharmony_ci } else { 9018c2ecf20Sopenharmony_ci return MAXIM_RET_NOACTION; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci } else { 9048c2ecf20Sopenharmony_ci di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 9058c2ecf20Sopenharmony_ci return MAXIM_RET_NOACTION; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci} 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic void handle_maxim_chg_curr(struct abx500_chargalg *di) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci enum maxim_ret ret; 9128c2ecf20Sopenharmony_ci int result; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci ret = abx500_chargalg_chg_curr_maxim(di); 9158c2ecf20Sopenharmony_ci switch (ret) { 9168c2ecf20Sopenharmony_ci case MAXIM_RET_CHANGE: 9178c2ecf20Sopenharmony_ci result = abx500_chargalg_update_chg_curr(di, 9188c2ecf20Sopenharmony_ci di->ccm.current_iset); 9198c2ecf20Sopenharmony_ci if (result) 9208c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to set chg curr\n"); 9218c2ecf20Sopenharmony_ci break; 9228c2ecf20Sopenharmony_ci case MAXIM_RET_IBAT_TOO_HIGH: 9238c2ecf20Sopenharmony_ci result = abx500_chargalg_update_chg_curr(di, 9248c2ecf20Sopenharmony_ci di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); 9258c2ecf20Sopenharmony_ci if (result) 9268c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to set chg curr\n"); 9278c2ecf20Sopenharmony_ci break; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci case MAXIM_RET_NOACTION: 9308c2ecf20Sopenharmony_ci default: 9318c2ecf20Sopenharmony_ci /* Do nothing..*/ 9328c2ecf20Sopenharmony_ci break; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_cistatic int abx500_chargalg_get_ext_psy_data(struct device *dev, void *data) 9378c2ecf20Sopenharmony_ci{ 9388c2ecf20Sopenharmony_ci struct power_supply *psy; 9398c2ecf20Sopenharmony_ci struct power_supply *ext = dev_get_drvdata(dev); 9408c2ecf20Sopenharmony_ci const char **supplicants = (const char **)ext->supplied_to; 9418c2ecf20Sopenharmony_ci struct abx500_chargalg *di; 9428c2ecf20Sopenharmony_ci union power_supply_propval ret; 9438c2ecf20Sopenharmony_ci int j; 9448c2ecf20Sopenharmony_ci bool capacity_updated = false; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci psy = (struct power_supply *)data; 9478c2ecf20Sopenharmony_ci di = power_supply_get_drvdata(psy); 9488c2ecf20Sopenharmony_ci /* For all psy where the driver name appears in any supplied_to */ 9498c2ecf20Sopenharmony_ci j = match_string(supplicants, ext->num_supplicants, psy->desc->name); 9508c2ecf20Sopenharmony_ci if (j < 0) 9518c2ecf20Sopenharmony_ci return 0; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci /* 9548c2ecf20Sopenharmony_ci * If external is not registering 'POWER_SUPPLY_PROP_CAPACITY' to its 9558c2ecf20Sopenharmony_ci * property because of handling that sysfs entry on its own, this is 9568c2ecf20Sopenharmony_ci * the place to get the battery capacity. 9578c2ecf20Sopenharmony_ci */ 9588c2ecf20Sopenharmony_ci if (!power_supply_get_property(ext, POWER_SUPPLY_PROP_CAPACITY, &ret)) { 9598c2ecf20Sopenharmony_ci di->batt_data.percent = ret.intval; 9608c2ecf20Sopenharmony_ci capacity_updated = true; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci /* Go through all properties for the psy */ 9648c2ecf20Sopenharmony_ci for (j = 0; j < ext->desc->num_properties; j++) { 9658c2ecf20Sopenharmony_ci enum power_supply_property prop; 9668c2ecf20Sopenharmony_ci prop = ext->desc->properties[j]; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci /* 9698c2ecf20Sopenharmony_ci * Initialize chargers if not already done. 9708c2ecf20Sopenharmony_ci * The ab8500_charger*/ 9718c2ecf20Sopenharmony_ci if (!di->ac_chg && 9728c2ecf20Sopenharmony_ci ext->desc->type == POWER_SUPPLY_TYPE_MAINS) 9738c2ecf20Sopenharmony_ci di->ac_chg = psy_to_ux500_charger(ext); 9748c2ecf20Sopenharmony_ci else if (!di->usb_chg && 9758c2ecf20Sopenharmony_ci ext->desc->type == POWER_SUPPLY_TYPE_USB) 9768c2ecf20Sopenharmony_ci di->usb_chg = psy_to_ux500_charger(ext); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (power_supply_get_property(ext, prop, &ret)) 9798c2ecf20Sopenharmony_ci continue; 9808c2ecf20Sopenharmony_ci switch (prop) { 9818c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 9828c2ecf20Sopenharmony_ci switch (ext->desc->type) { 9838c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 9848c2ecf20Sopenharmony_ci /* Battery present */ 9858c2ecf20Sopenharmony_ci if (ret.intval) 9868c2ecf20Sopenharmony_ci di->events.batt_rem = false; 9878c2ecf20Sopenharmony_ci /* Battery removed */ 9888c2ecf20Sopenharmony_ci else 9898c2ecf20Sopenharmony_ci di->events.batt_rem = true; 9908c2ecf20Sopenharmony_ci break; 9918c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 9928c2ecf20Sopenharmony_ci /* AC disconnected */ 9938c2ecf20Sopenharmony_ci if (!ret.intval && 9948c2ecf20Sopenharmony_ci (di->chg_info.conn_chg & AC_CHG)) { 9958c2ecf20Sopenharmony_ci di->chg_info.prev_conn_chg = 9968c2ecf20Sopenharmony_ci di->chg_info.conn_chg; 9978c2ecf20Sopenharmony_ci di->chg_info.conn_chg &= ~AC_CHG; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci /* AC connected */ 10008c2ecf20Sopenharmony_ci else if (ret.intval && 10018c2ecf20Sopenharmony_ci !(di->chg_info.conn_chg & AC_CHG)) { 10028c2ecf20Sopenharmony_ci di->chg_info.prev_conn_chg = 10038c2ecf20Sopenharmony_ci di->chg_info.conn_chg; 10048c2ecf20Sopenharmony_ci di->chg_info.conn_chg |= AC_CHG; 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci break; 10078c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 10088c2ecf20Sopenharmony_ci /* USB disconnected */ 10098c2ecf20Sopenharmony_ci if (!ret.intval && 10108c2ecf20Sopenharmony_ci (di->chg_info.conn_chg & USB_CHG)) { 10118c2ecf20Sopenharmony_ci di->chg_info.prev_conn_chg = 10128c2ecf20Sopenharmony_ci di->chg_info.conn_chg; 10138c2ecf20Sopenharmony_ci di->chg_info.conn_chg &= ~USB_CHG; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci /* USB connected */ 10168c2ecf20Sopenharmony_ci else if (ret.intval && 10178c2ecf20Sopenharmony_ci !(di->chg_info.conn_chg & USB_CHG)) { 10188c2ecf20Sopenharmony_ci di->chg_info.prev_conn_chg = 10198c2ecf20Sopenharmony_ci di->chg_info.conn_chg; 10208c2ecf20Sopenharmony_ci di->chg_info.conn_chg |= USB_CHG; 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci break; 10238c2ecf20Sopenharmony_ci default: 10248c2ecf20Sopenharmony_ci break; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci break; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 10298c2ecf20Sopenharmony_ci switch (ext->desc->type) { 10308c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 10318c2ecf20Sopenharmony_ci break; 10328c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 10338c2ecf20Sopenharmony_ci /* AC offline */ 10348c2ecf20Sopenharmony_ci if (!ret.intval && 10358c2ecf20Sopenharmony_ci (di->chg_info.online_chg & AC_CHG)) { 10368c2ecf20Sopenharmony_ci di->chg_info.prev_online_chg = 10378c2ecf20Sopenharmony_ci di->chg_info.online_chg; 10388c2ecf20Sopenharmony_ci di->chg_info.online_chg &= ~AC_CHG; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci /* AC online */ 10418c2ecf20Sopenharmony_ci else if (ret.intval && 10428c2ecf20Sopenharmony_ci !(di->chg_info.online_chg & AC_CHG)) { 10438c2ecf20Sopenharmony_ci di->chg_info.prev_online_chg = 10448c2ecf20Sopenharmony_ci di->chg_info.online_chg; 10458c2ecf20Sopenharmony_ci di->chg_info.online_chg |= AC_CHG; 10468c2ecf20Sopenharmony_ci queue_delayed_work(di->chargalg_wq, 10478c2ecf20Sopenharmony_ci &di->chargalg_wd_work, 0); 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci break; 10508c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 10518c2ecf20Sopenharmony_ci /* USB offline */ 10528c2ecf20Sopenharmony_ci if (!ret.intval && 10538c2ecf20Sopenharmony_ci (di->chg_info.online_chg & USB_CHG)) { 10548c2ecf20Sopenharmony_ci di->chg_info.prev_online_chg = 10558c2ecf20Sopenharmony_ci di->chg_info.online_chg; 10568c2ecf20Sopenharmony_ci di->chg_info.online_chg &= ~USB_CHG; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci /* USB online */ 10598c2ecf20Sopenharmony_ci else if (ret.intval && 10608c2ecf20Sopenharmony_ci !(di->chg_info.online_chg & USB_CHG)) { 10618c2ecf20Sopenharmony_ci di->chg_info.prev_online_chg = 10628c2ecf20Sopenharmony_ci di->chg_info.online_chg; 10638c2ecf20Sopenharmony_ci di->chg_info.online_chg |= USB_CHG; 10648c2ecf20Sopenharmony_ci queue_delayed_work(di->chargalg_wq, 10658c2ecf20Sopenharmony_ci &di->chargalg_wd_work, 0); 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci break; 10688c2ecf20Sopenharmony_ci default: 10698c2ecf20Sopenharmony_ci break; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci break; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 10748c2ecf20Sopenharmony_ci switch (ext->desc->type) { 10758c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 10768c2ecf20Sopenharmony_ci break; 10778c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 10788c2ecf20Sopenharmony_ci switch (ret.intval) { 10798c2ecf20Sopenharmony_ci case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: 10808c2ecf20Sopenharmony_ci di->events.mainextchnotok = true; 10818c2ecf20Sopenharmony_ci di->events.main_thermal_prot = false; 10828c2ecf20Sopenharmony_ci di->events.main_ovv = false; 10838c2ecf20Sopenharmony_ci di->events.ac_wd_expired = false; 10848c2ecf20Sopenharmony_ci break; 10858c2ecf20Sopenharmony_ci case POWER_SUPPLY_HEALTH_DEAD: 10868c2ecf20Sopenharmony_ci di->events.ac_wd_expired = true; 10878c2ecf20Sopenharmony_ci di->events.mainextchnotok = false; 10888c2ecf20Sopenharmony_ci di->events.main_ovv = false; 10898c2ecf20Sopenharmony_ci di->events.main_thermal_prot = false; 10908c2ecf20Sopenharmony_ci break; 10918c2ecf20Sopenharmony_ci case POWER_SUPPLY_HEALTH_COLD: 10928c2ecf20Sopenharmony_ci case POWER_SUPPLY_HEALTH_OVERHEAT: 10938c2ecf20Sopenharmony_ci di->events.main_thermal_prot = true; 10948c2ecf20Sopenharmony_ci di->events.mainextchnotok = false; 10958c2ecf20Sopenharmony_ci di->events.main_ovv = false; 10968c2ecf20Sopenharmony_ci di->events.ac_wd_expired = false; 10978c2ecf20Sopenharmony_ci break; 10988c2ecf20Sopenharmony_ci case POWER_SUPPLY_HEALTH_OVERVOLTAGE: 10998c2ecf20Sopenharmony_ci di->events.main_ovv = true; 11008c2ecf20Sopenharmony_ci di->events.mainextchnotok = false; 11018c2ecf20Sopenharmony_ci di->events.main_thermal_prot = false; 11028c2ecf20Sopenharmony_ci di->events.ac_wd_expired = false; 11038c2ecf20Sopenharmony_ci break; 11048c2ecf20Sopenharmony_ci case POWER_SUPPLY_HEALTH_GOOD: 11058c2ecf20Sopenharmony_ci di->events.main_thermal_prot = false; 11068c2ecf20Sopenharmony_ci di->events.mainextchnotok = false; 11078c2ecf20Sopenharmony_ci di->events.main_ovv = false; 11088c2ecf20Sopenharmony_ci di->events.ac_wd_expired = false; 11098c2ecf20Sopenharmony_ci break; 11108c2ecf20Sopenharmony_ci default: 11118c2ecf20Sopenharmony_ci break; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci break; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 11168c2ecf20Sopenharmony_ci switch (ret.intval) { 11178c2ecf20Sopenharmony_ci case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: 11188c2ecf20Sopenharmony_ci di->events.usbchargernotok = true; 11198c2ecf20Sopenharmony_ci di->events.usb_thermal_prot = false; 11208c2ecf20Sopenharmony_ci di->events.vbus_ovv = false; 11218c2ecf20Sopenharmony_ci di->events.usb_wd_expired = false; 11228c2ecf20Sopenharmony_ci break; 11238c2ecf20Sopenharmony_ci case POWER_SUPPLY_HEALTH_DEAD: 11248c2ecf20Sopenharmony_ci di->events.usb_wd_expired = true; 11258c2ecf20Sopenharmony_ci di->events.usbchargernotok = false; 11268c2ecf20Sopenharmony_ci di->events.usb_thermal_prot = false; 11278c2ecf20Sopenharmony_ci di->events.vbus_ovv = false; 11288c2ecf20Sopenharmony_ci break; 11298c2ecf20Sopenharmony_ci case POWER_SUPPLY_HEALTH_COLD: 11308c2ecf20Sopenharmony_ci case POWER_SUPPLY_HEALTH_OVERHEAT: 11318c2ecf20Sopenharmony_ci di->events.usb_thermal_prot = true; 11328c2ecf20Sopenharmony_ci di->events.usbchargernotok = false; 11338c2ecf20Sopenharmony_ci di->events.vbus_ovv = false; 11348c2ecf20Sopenharmony_ci di->events.usb_wd_expired = false; 11358c2ecf20Sopenharmony_ci break; 11368c2ecf20Sopenharmony_ci case POWER_SUPPLY_HEALTH_OVERVOLTAGE: 11378c2ecf20Sopenharmony_ci di->events.vbus_ovv = true; 11388c2ecf20Sopenharmony_ci di->events.usbchargernotok = false; 11398c2ecf20Sopenharmony_ci di->events.usb_thermal_prot = false; 11408c2ecf20Sopenharmony_ci di->events.usb_wd_expired = false; 11418c2ecf20Sopenharmony_ci break; 11428c2ecf20Sopenharmony_ci case POWER_SUPPLY_HEALTH_GOOD: 11438c2ecf20Sopenharmony_ci di->events.usbchargernotok = false; 11448c2ecf20Sopenharmony_ci di->events.usb_thermal_prot = false; 11458c2ecf20Sopenharmony_ci di->events.vbus_ovv = false; 11468c2ecf20Sopenharmony_ci di->events.usb_wd_expired = false; 11478c2ecf20Sopenharmony_ci break; 11488c2ecf20Sopenharmony_ci default: 11498c2ecf20Sopenharmony_ci break; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci default: 11528c2ecf20Sopenharmony_ci break; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci break; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 11578c2ecf20Sopenharmony_ci switch (ext->desc->type) { 11588c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 11598c2ecf20Sopenharmony_ci di->batt_data.volt = ret.intval / 1000; 11608c2ecf20Sopenharmony_ci break; 11618c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 11628c2ecf20Sopenharmony_ci di->chg_info.ac_volt = ret.intval / 1000; 11638c2ecf20Sopenharmony_ci break; 11648c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 11658c2ecf20Sopenharmony_ci di->chg_info.usb_volt = ret.intval / 1000; 11668c2ecf20Sopenharmony_ci break; 11678c2ecf20Sopenharmony_ci default: 11688c2ecf20Sopenharmony_ci break; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci break; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_AVG: 11738c2ecf20Sopenharmony_ci switch (ext->desc->type) { 11748c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 11758c2ecf20Sopenharmony_ci /* AVG is used to indicate when we are 11768c2ecf20Sopenharmony_ci * in CV mode */ 11778c2ecf20Sopenharmony_ci if (ret.intval) 11788c2ecf20Sopenharmony_ci di->events.ac_cv_active = true; 11798c2ecf20Sopenharmony_ci else 11808c2ecf20Sopenharmony_ci di->events.ac_cv_active = false; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci break; 11838c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 11848c2ecf20Sopenharmony_ci /* AVG is used to indicate when we are 11858c2ecf20Sopenharmony_ci * in CV mode */ 11868c2ecf20Sopenharmony_ci if (ret.intval) 11878c2ecf20Sopenharmony_ci di->events.usb_cv_active = true; 11888c2ecf20Sopenharmony_ci else 11898c2ecf20Sopenharmony_ci di->events.usb_cv_active = false; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci break; 11928c2ecf20Sopenharmony_ci default: 11938c2ecf20Sopenharmony_ci break; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci break; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 11988c2ecf20Sopenharmony_ci switch (ext->desc->type) { 11998c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 12008c2ecf20Sopenharmony_ci if (ret.intval) 12018c2ecf20Sopenharmony_ci di->events.batt_unknown = false; 12028c2ecf20Sopenharmony_ci else 12038c2ecf20Sopenharmony_ci di->events.batt_unknown = true; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci break; 12068c2ecf20Sopenharmony_ci default: 12078c2ecf20Sopenharmony_ci break; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci break; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 12128c2ecf20Sopenharmony_ci di->batt_data.temp = ret.intval / 10; 12138c2ecf20Sopenharmony_ci break; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 12168c2ecf20Sopenharmony_ci switch (ext->desc->type) { 12178c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 12188c2ecf20Sopenharmony_ci di->chg_info.ac_curr = 12198c2ecf20Sopenharmony_ci ret.intval / 1000; 12208c2ecf20Sopenharmony_ci break; 12218c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 12228c2ecf20Sopenharmony_ci di->chg_info.usb_curr = 12238c2ecf20Sopenharmony_ci ret.intval / 1000; 12248c2ecf20Sopenharmony_ci break; 12258c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 12268c2ecf20Sopenharmony_ci di->batt_data.inst_curr = ret.intval / 1000; 12278c2ecf20Sopenharmony_ci break; 12288c2ecf20Sopenharmony_ci default: 12298c2ecf20Sopenharmony_ci break; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci break; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 12348c2ecf20Sopenharmony_ci switch (ext->desc->type) { 12358c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 12368c2ecf20Sopenharmony_ci di->batt_data.avg_curr = ret.intval / 1000; 12378c2ecf20Sopenharmony_ci break; 12388c2ecf20Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 12398c2ecf20Sopenharmony_ci if (ret.intval) 12408c2ecf20Sopenharmony_ci di->events.vbus_collapsed = true; 12418c2ecf20Sopenharmony_ci else 12428c2ecf20Sopenharmony_ci di->events.vbus_collapsed = false; 12438c2ecf20Sopenharmony_ci break; 12448c2ecf20Sopenharmony_ci default: 12458c2ecf20Sopenharmony_ci break; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci break; 12488c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 12498c2ecf20Sopenharmony_ci if (!capacity_updated) 12508c2ecf20Sopenharmony_ci di->batt_data.percent = ret.intval; 12518c2ecf20Sopenharmony_ci break; 12528c2ecf20Sopenharmony_ci default: 12538c2ecf20Sopenharmony_ci break; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci return 0; 12578c2ecf20Sopenharmony_ci} 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci/** 12608c2ecf20Sopenharmony_ci * abx500_chargalg_external_power_changed() - callback for power supply changes 12618c2ecf20Sopenharmony_ci * @psy: pointer to the structure power_supply 12628c2ecf20Sopenharmony_ci * 12638c2ecf20Sopenharmony_ci * This function is the entry point of the pointer external_power_changed 12648c2ecf20Sopenharmony_ci * of the structure power_supply. 12658c2ecf20Sopenharmony_ci * This function gets executed when there is a change in any external power 12668c2ecf20Sopenharmony_ci * supply that this driver needs to be notified of. 12678c2ecf20Sopenharmony_ci */ 12688c2ecf20Sopenharmony_cistatic void abx500_chargalg_external_power_changed(struct power_supply *psy) 12698c2ecf20Sopenharmony_ci{ 12708c2ecf20Sopenharmony_ci struct abx500_chargalg *di = power_supply_get_drvdata(psy); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci /* 12738c2ecf20Sopenharmony_ci * Trigger execution of the algorithm instantly and read 12748c2ecf20Sopenharmony_ci * all power_supply properties there instead 12758c2ecf20Sopenharmony_ci */ 12768c2ecf20Sopenharmony_ci queue_work(di->chargalg_wq, &di->chargalg_work); 12778c2ecf20Sopenharmony_ci} 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci/** 12808c2ecf20Sopenharmony_ci * abx500_chargalg_algorithm() - Main function for the algorithm 12818c2ecf20Sopenharmony_ci * @di: pointer to the abx500_chargalg structure 12828c2ecf20Sopenharmony_ci * 12838c2ecf20Sopenharmony_ci * This is the main control function for the charging algorithm. 12848c2ecf20Sopenharmony_ci * It is called periodically or when something happens that will 12858c2ecf20Sopenharmony_ci * trigger a state change 12868c2ecf20Sopenharmony_ci */ 12878c2ecf20Sopenharmony_cistatic void abx500_chargalg_algorithm(struct abx500_chargalg *di) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci int charger_status; 12908c2ecf20Sopenharmony_ci int ret; 12918c2ecf20Sopenharmony_ci int curr_step_lvl; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci /* Collect data from all power_supply class devices */ 12948c2ecf20Sopenharmony_ci class_for_each_device(power_supply_class, NULL, 12958c2ecf20Sopenharmony_ci di->chargalg_psy, abx500_chargalg_get_ext_psy_data); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci abx500_chargalg_end_of_charge(di); 12988c2ecf20Sopenharmony_ci abx500_chargalg_check_temp(di); 12998c2ecf20Sopenharmony_ci abx500_chargalg_check_charger_voltage(di); 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci charger_status = abx500_chargalg_check_charger_connection(di); 13028c2ecf20Sopenharmony_ci abx500_chargalg_check_current_step_status(di); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci if (is_ab8500(di->parent)) { 13058c2ecf20Sopenharmony_ci ret = abx500_chargalg_check_charger_enable(di); 13068c2ecf20Sopenharmony_ci if (ret < 0) 13078c2ecf20Sopenharmony_ci dev_err(di->dev, "Checking charger is enabled error" 13088c2ecf20Sopenharmony_ci ": Returned Value %d\n", ret); 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci /* 13128c2ecf20Sopenharmony_ci * First check if we have a charger connected. 13138c2ecf20Sopenharmony_ci * Also we don't allow charging of unknown batteries if configured 13148c2ecf20Sopenharmony_ci * this way 13158c2ecf20Sopenharmony_ci */ 13168c2ecf20Sopenharmony_ci if (!charger_status || 13178c2ecf20Sopenharmony_ci (di->events.batt_unknown && !di->bm->chg_unknown_bat)) { 13188c2ecf20Sopenharmony_ci if (di->charge_state != STATE_HANDHELD) { 13198c2ecf20Sopenharmony_ci di->events.safety_timer_expired = false; 13208c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_HANDHELD_INIT); 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci } 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* If suspended, we should not continue checking the flags */ 13258c2ecf20Sopenharmony_ci else if (di->charge_state == STATE_SUSPENDED_INIT || 13268c2ecf20Sopenharmony_ci di->charge_state == STATE_SUSPENDED) { 13278c2ecf20Sopenharmony_ci /* We don't do anything here, just don,t continue */ 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci /* Safety timer expiration */ 13318c2ecf20Sopenharmony_ci else if (di->events.safety_timer_expired) { 13328c2ecf20Sopenharmony_ci if (di->charge_state != STATE_SAFETY_TIMER_EXPIRED) 13338c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, 13348c2ecf20Sopenharmony_ci STATE_SAFETY_TIMER_EXPIRED_INIT); 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci /* 13378c2ecf20Sopenharmony_ci * Check if any interrupts has occured 13388c2ecf20Sopenharmony_ci * that will prevent us from charging 13398c2ecf20Sopenharmony_ci */ 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci /* Battery removed */ 13428c2ecf20Sopenharmony_ci else if (di->events.batt_rem) { 13438c2ecf20Sopenharmony_ci if (di->charge_state != STATE_BATT_REMOVED) 13448c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT); 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci /* Main or USB charger not ok. */ 13478c2ecf20Sopenharmony_ci else if (di->events.mainextchnotok || di->events.usbchargernotok) { 13488c2ecf20Sopenharmony_ci /* 13498c2ecf20Sopenharmony_ci * If vbus_collapsed is set, we have to lower the charger 13508c2ecf20Sopenharmony_ci * current, which is done in the normal state below 13518c2ecf20Sopenharmony_ci */ 13528c2ecf20Sopenharmony_ci if (di->charge_state != STATE_CHG_NOT_OK && 13538c2ecf20Sopenharmony_ci !di->events.vbus_collapsed) 13548c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT); 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci /* VBUS, Main or VBAT OVV. */ 13578c2ecf20Sopenharmony_ci else if (di->events.vbus_ovv || 13588c2ecf20Sopenharmony_ci di->events.main_ovv || 13598c2ecf20Sopenharmony_ci di->events.batt_ovv || 13608c2ecf20Sopenharmony_ci !di->chg_info.usb_chg_ok || 13618c2ecf20Sopenharmony_ci !di->chg_info.ac_chg_ok) { 13628c2ecf20Sopenharmony_ci if (di->charge_state != STATE_OVV_PROTECT) 13638c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT); 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci /* USB Thermal, stop charging */ 13668c2ecf20Sopenharmony_ci else if (di->events.main_thermal_prot || 13678c2ecf20Sopenharmony_ci di->events.usb_thermal_prot) { 13688c2ecf20Sopenharmony_ci if (di->charge_state != STATE_HW_TEMP_PROTECT) 13698c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, 13708c2ecf20Sopenharmony_ci STATE_HW_TEMP_PROTECT_INIT); 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci /* Battery temp over/under */ 13738c2ecf20Sopenharmony_ci else if (di->events.btemp_underover) { 13748c2ecf20Sopenharmony_ci if (di->charge_state != STATE_TEMP_UNDEROVER) 13758c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, 13768c2ecf20Sopenharmony_ci STATE_TEMP_UNDEROVER_INIT); 13778c2ecf20Sopenharmony_ci } 13788c2ecf20Sopenharmony_ci /* Watchdog expired */ 13798c2ecf20Sopenharmony_ci else if (di->events.ac_wd_expired || 13808c2ecf20Sopenharmony_ci di->events.usb_wd_expired) { 13818c2ecf20Sopenharmony_ci if (di->charge_state != STATE_WD_EXPIRED) 13828c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT); 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci /* Battery temp high/low */ 13858c2ecf20Sopenharmony_ci else if (di->events.btemp_lowhigh) { 13868c2ecf20Sopenharmony_ci if (di->charge_state != STATE_TEMP_LOWHIGH) 13878c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT); 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci dev_dbg(di->dev, 13918c2ecf20Sopenharmony_ci "[CHARGALG] Vb %d Ib_avg %d Ib_inst %d Tb %d Cap %d Maint %d " 13928c2ecf20Sopenharmony_ci "State %s Active_chg %d Chg_status %d AC %d USB %d " 13938c2ecf20Sopenharmony_ci "AC_online %d USB_online %d AC_CV %d USB_CV %d AC_I %d " 13948c2ecf20Sopenharmony_ci "USB_I %d AC_Vset %d AC_Iset %d USB_Vset %d USB_Iset %d\n", 13958c2ecf20Sopenharmony_ci di->batt_data.volt, 13968c2ecf20Sopenharmony_ci di->batt_data.avg_curr, 13978c2ecf20Sopenharmony_ci di->batt_data.inst_curr, 13988c2ecf20Sopenharmony_ci di->batt_data.temp, 13998c2ecf20Sopenharmony_ci di->batt_data.percent, 14008c2ecf20Sopenharmony_ci di->maintenance_chg, 14018c2ecf20Sopenharmony_ci states[di->charge_state], 14028c2ecf20Sopenharmony_ci di->chg_info.charger_type, 14038c2ecf20Sopenharmony_ci di->charge_status, 14048c2ecf20Sopenharmony_ci di->chg_info.conn_chg & AC_CHG, 14058c2ecf20Sopenharmony_ci di->chg_info.conn_chg & USB_CHG, 14068c2ecf20Sopenharmony_ci di->chg_info.online_chg & AC_CHG, 14078c2ecf20Sopenharmony_ci di->chg_info.online_chg & USB_CHG, 14088c2ecf20Sopenharmony_ci di->events.ac_cv_active, 14098c2ecf20Sopenharmony_ci di->events.usb_cv_active, 14108c2ecf20Sopenharmony_ci di->chg_info.ac_curr, 14118c2ecf20Sopenharmony_ci di->chg_info.usb_curr, 14128c2ecf20Sopenharmony_ci di->chg_info.ac_vset, 14138c2ecf20Sopenharmony_ci di->chg_info.ac_iset, 14148c2ecf20Sopenharmony_ci di->chg_info.usb_vset, 14158c2ecf20Sopenharmony_ci di->chg_info.usb_iset); 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci switch (di->charge_state) { 14188c2ecf20Sopenharmony_ci case STATE_HANDHELD_INIT: 14198c2ecf20Sopenharmony_ci abx500_chargalg_stop_charging(di); 14208c2ecf20Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; 14218c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_HANDHELD); 14228c2ecf20Sopenharmony_ci fallthrough; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci case STATE_HANDHELD: 14258c2ecf20Sopenharmony_ci break; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci case STATE_SUSPENDED_INIT: 14288c2ecf20Sopenharmony_ci if (di->susp_status.ac_suspended) 14298c2ecf20Sopenharmony_ci abx500_chargalg_ac_en(di, false, 0, 0); 14308c2ecf20Sopenharmony_ci if (di->susp_status.usb_suspended) 14318c2ecf20Sopenharmony_ci abx500_chargalg_usb_en(di, false, 0, 0); 14328c2ecf20Sopenharmony_ci abx500_chargalg_stop_safety_timer(di); 14338c2ecf20Sopenharmony_ci abx500_chargalg_stop_maintenance_timer(di); 14348c2ecf20Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; 14358c2ecf20Sopenharmony_ci di->maintenance_chg = false; 14368c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_SUSPENDED); 14378c2ecf20Sopenharmony_ci power_supply_changed(di->chargalg_psy); 14388c2ecf20Sopenharmony_ci fallthrough; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci case STATE_SUSPENDED: 14418c2ecf20Sopenharmony_ci /* CHARGING is suspended */ 14428c2ecf20Sopenharmony_ci break; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci case STATE_BATT_REMOVED_INIT: 14458c2ecf20Sopenharmony_ci abx500_chargalg_stop_charging(di); 14468c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_BATT_REMOVED); 14478c2ecf20Sopenharmony_ci fallthrough; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci case STATE_BATT_REMOVED: 14508c2ecf20Sopenharmony_ci if (!di->events.batt_rem) 14518c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL_INIT); 14528c2ecf20Sopenharmony_ci break; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci case STATE_HW_TEMP_PROTECT_INIT: 14558c2ecf20Sopenharmony_ci abx500_chargalg_stop_charging(di); 14568c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT); 14578c2ecf20Sopenharmony_ci fallthrough; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci case STATE_HW_TEMP_PROTECT: 14608c2ecf20Sopenharmony_ci if (!di->events.main_thermal_prot && 14618c2ecf20Sopenharmony_ci !di->events.usb_thermal_prot) 14628c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL_INIT); 14638c2ecf20Sopenharmony_ci break; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci case STATE_OVV_PROTECT_INIT: 14668c2ecf20Sopenharmony_ci abx500_chargalg_stop_charging(di); 14678c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_OVV_PROTECT); 14688c2ecf20Sopenharmony_ci fallthrough; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci case STATE_OVV_PROTECT: 14718c2ecf20Sopenharmony_ci if (!di->events.vbus_ovv && 14728c2ecf20Sopenharmony_ci !di->events.main_ovv && 14738c2ecf20Sopenharmony_ci !di->events.batt_ovv && 14748c2ecf20Sopenharmony_ci di->chg_info.usb_chg_ok && 14758c2ecf20Sopenharmony_ci di->chg_info.ac_chg_ok) 14768c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL_INIT); 14778c2ecf20Sopenharmony_ci break; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci case STATE_CHG_NOT_OK_INIT: 14808c2ecf20Sopenharmony_ci abx500_chargalg_stop_charging(di); 14818c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_CHG_NOT_OK); 14828c2ecf20Sopenharmony_ci fallthrough; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci case STATE_CHG_NOT_OK: 14858c2ecf20Sopenharmony_ci if (!di->events.mainextchnotok && 14868c2ecf20Sopenharmony_ci !di->events.usbchargernotok) 14878c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL_INIT); 14888c2ecf20Sopenharmony_ci break; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci case STATE_SAFETY_TIMER_EXPIRED_INIT: 14918c2ecf20Sopenharmony_ci abx500_chargalg_stop_charging(di); 14928c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED); 14938c2ecf20Sopenharmony_ci fallthrough; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci case STATE_SAFETY_TIMER_EXPIRED: 14968c2ecf20Sopenharmony_ci /* We exit this state when charger is removed */ 14978c2ecf20Sopenharmony_ci break; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci case STATE_NORMAL_INIT: 15008c2ecf20Sopenharmony_ci if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW) 15018c2ecf20Sopenharmony_ci abx500_chargalg_stop_charging(di); 15028c2ecf20Sopenharmony_ci else { 15038c2ecf20Sopenharmony_ci curr_step_lvl = di->bm->bat_type[ 15048c2ecf20Sopenharmony_ci di->bm->batt_id].normal_cur_lvl 15058c2ecf20Sopenharmony_ci * di->curr_status.curr_step 15068c2ecf20Sopenharmony_ci / CHARGALG_CURR_STEP_HIGH; 15078c2ecf20Sopenharmony_ci abx500_chargalg_start_charging(di, 15088c2ecf20Sopenharmony_ci di->bm->bat_type[di->bm->batt_id] 15098c2ecf20Sopenharmony_ci .normal_vol_lvl, curr_step_lvl); 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL); 15138c2ecf20Sopenharmony_ci abx500_chargalg_start_safety_timer(di); 15148c2ecf20Sopenharmony_ci abx500_chargalg_stop_maintenance_timer(di); 15158c2ecf20Sopenharmony_ci init_maxim_chg_curr(di); 15168c2ecf20Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_CHARGING; 15178c2ecf20Sopenharmony_ci di->eoc_cnt = 0; 15188c2ecf20Sopenharmony_ci di->maintenance_chg = false; 15198c2ecf20Sopenharmony_ci power_supply_changed(di->chargalg_psy); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci break; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci case STATE_NORMAL: 15248c2ecf20Sopenharmony_ci handle_maxim_chg_curr(di); 15258c2ecf20Sopenharmony_ci if (di->charge_status == POWER_SUPPLY_STATUS_FULL && 15268c2ecf20Sopenharmony_ci di->maintenance_chg) { 15278c2ecf20Sopenharmony_ci if (di->bm->no_maintenance) 15288c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, 15298c2ecf20Sopenharmony_ci STATE_WAIT_FOR_RECHARGE_INIT); 15308c2ecf20Sopenharmony_ci else 15318c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, 15328c2ecf20Sopenharmony_ci STATE_MAINTENANCE_A_INIT); 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci break; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* This state will be used when the maintenance state is disabled */ 15378c2ecf20Sopenharmony_ci case STATE_WAIT_FOR_RECHARGE_INIT: 15388c2ecf20Sopenharmony_ci abx500_chargalg_hold_charging(di); 15398c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE); 15408c2ecf20Sopenharmony_ci fallthrough; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci case STATE_WAIT_FOR_RECHARGE: 15438c2ecf20Sopenharmony_ci if (di->batt_data.percent <= 15448c2ecf20Sopenharmony_ci di->bm->bat_type[di->bm->batt_id]. 15458c2ecf20Sopenharmony_ci recharge_cap) 15468c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL_INIT); 15478c2ecf20Sopenharmony_ci break; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci case STATE_MAINTENANCE_A_INIT: 15508c2ecf20Sopenharmony_ci abx500_chargalg_stop_safety_timer(di); 15518c2ecf20Sopenharmony_ci abx500_chargalg_start_maintenance_timer(di, 15528c2ecf20Sopenharmony_ci di->bm->bat_type[ 15538c2ecf20Sopenharmony_ci di->bm->batt_id].maint_a_chg_timer_h); 15548c2ecf20Sopenharmony_ci abx500_chargalg_start_charging(di, 15558c2ecf20Sopenharmony_ci di->bm->bat_type[ 15568c2ecf20Sopenharmony_ci di->bm->batt_id].maint_a_vol_lvl, 15578c2ecf20Sopenharmony_ci di->bm->bat_type[ 15588c2ecf20Sopenharmony_ci di->bm->batt_id].maint_a_cur_lvl); 15598c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_MAINTENANCE_A); 15608c2ecf20Sopenharmony_ci power_supply_changed(di->chargalg_psy); 15618c2ecf20Sopenharmony_ci fallthrough; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci case STATE_MAINTENANCE_A: 15648c2ecf20Sopenharmony_ci if (di->events.maintenance_timer_expired) { 15658c2ecf20Sopenharmony_ci abx500_chargalg_stop_maintenance_timer(di); 15668c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT); 15678c2ecf20Sopenharmony_ci } 15688c2ecf20Sopenharmony_ci break; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci case STATE_MAINTENANCE_B_INIT: 15718c2ecf20Sopenharmony_ci abx500_chargalg_start_maintenance_timer(di, 15728c2ecf20Sopenharmony_ci di->bm->bat_type[ 15738c2ecf20Sopenharmony_ci di->bm->batt_id].maint_b_chg_timer_h); 15748c2ecf20Sopenharmony_ci abx500_chargalg_start_charging(di, 15758c2ecf20Sopenharmony_ci di->bm->bat_type[ 15768c2ecf20Sopenharmony_ci di->bm->batt_id].maint_b_vol_lvl, 15778c2ecf20Sopenharmony_ci di->bm->bat_type[ 15788c2ecf20Sopenharmony_ci di->bm->batt_id].maint_b_cur_lvl); 15798c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_MAINTENANCE_B); 15808c2ecf20Sopenharmony_ci power_supply_changed(di->chargalg_psy); 15818c2ecf20Sopenharmony_ci fallthrough; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci case STATE_MAINTENANCE_B: 15848c2ecf20Sopenharmony_ci if (di->events.maintenance_timer_expired) { 15858c2ecf20Sopenharmony_ci abx500_chargalg_stop_maintenance_timer(di); 15868c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL_INIT); 15878c2ecf20Sopenharmony_ci } 15888c2ecf20Sopenharmony_ci break; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci case STATE_TEMP_LOWHIGH_INIT: 15918c2ecf20Sopenharmony_ci abx500_chargalg_start_charging(di, 15928c2ecf20Sopenharmony_ci di->bm->bat_type[ 15938c2ecf20Sopenharmony_ci di->bm->batt_id].low_high_vol_lvl, 15948c2ecf20Sopenharmony_ci di->bm->bat_type[ 15958c2ecf20Sopenharmony_ci di->bm->batt_id].low_high_cur_lvl); 15968c2ecf20Sopenharmony_ci abx500_chargalg_stop_maintenance_timer(di); 15978c2ecf20Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_CHARGING; 15988c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_TEMP_LOWHIGH); 15998c2ecf20Sopenharmony_ci power_supply_changed(di->chargalg_psy); 16008c2ecf20Sopenharmony_ci fallthrough; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci case STATE_TEMP_LOWHIGH: 16038c2ecf20Sopenharmony_ci if (!di->events.btemp_lowhigh) 16048c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL_INIT); 16058c2ecf20Sopenharmony_ci break; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci case STATE_WD_EXPIRED_INIT: 16088c2ecf20Sopenharmony_ci abx500_chargalg_stop_charging(di); 16098c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_WD_EXPIRED); 16108c2ecf20Sopenharmony_ci fallthrough; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci case STATE_WD_EXPIRED: 16138c2ecf20Sopenharmony_ci if (!di->events.ac_wd_expired && 16148c2ecf20Sopenharmony_ci !di->events.usb_wd_expired) 16158c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL_INIT); 16168c2ecf20Sopenharmony_ci break; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci case STATE_TEMP_UNDEROVER_INIT: 16198c2ecf20Sopenharmony_ci abx500_chargalg_stop_charging(di); 16208c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_TEMP_UNDEROVER); 16218c2ecf20Sopenharmony_ci fallthrough; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci case STATE_TEMP_UNDEROVER: 16248c2ecf20Sopenharmony_ci if (!di->events.btemp_underover) 16258c2ecf20Sopenharmony_ci abx500_chargalg_state_to(di, STATE_NORMAL_INIT); 16268c2ecf20Sopenharmony_ci break; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* Start charging directly if the new state is a charge state */ 16308c2ecf20Sopenharmony_ci if (di->charge_state == STATE_NORMAL_INIT || 16318c2ecf20Sopenharmony_ci di->charge_state == STATE_MAINTENANCE_A_INIT || 16328c2ecf20Sopenharmony_ci di->charge_state == STATE_MAINTENANCE_B_INIT) 16338c2ecf20Sopenharmony_ci queue_work(di->chargalg_wq, &di->chargalg_work); 16348c2ecf20Sopenharmony_ci} 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci/** 16378c2ecf20Sopenharmony_ci * abx500_chargalg_periodic_work() - Periodic work for the algorithm 16388c2ecf20Sopenharmony_ci * @work: pointer to the work_struct structure 16398c2ecf20Sopenharmony_ci * 16408c2ecf20Sopenharmony_ci * Work queue function for the charging algorithm 16418c2ecf20Sopenharmony_ci */ 16428c2ecf20Sopenharmony_cistatic void abx500_chargalg_periodic_work(struct work_struct *work) 16438c2ecf20Sopenharmony_ci{ 16448c2ecf20Sopenharmony_ci struct abx500_chargalg *di = container_of(work, 16458c2ecf20Sopenharmony_ci struct abx500_chargalg, chargalg_periodic_work.work); 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci abx500_chargalg_algorithm(di); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci /* 16508c2ecf20Sopenharmony_ci * If a charger is connected then the battery has to be monitored 16518c2ecf20Sopenharmony_ci * frequently, else the work can be delayed. 16528c2ecf20Sopenharmony_ci */ 16538c2ecf20Sopenharmony_ci if (di->chg_info.conn_chg) 16548c2ecf20Sopenharmony_ci queue_delayed_work(di->chargalg_wq, 16558c2ecf20Sopenharmony_ci &di->chargalg_periodic_work, 16568c2ecf20Sopenharmony_ci di->bm->interval_charging * HZ); 16578c2ecf20Sopenharmony_ci else 16588c2ecf20Sopenharmony_ci queue_delayed_work(di->chargalg_wq, 16598c2ecf20Sopenharmony_ci &di->chargalg_periodic_work, 16608c2ecf20Sopenharmony_ci di->bm->interval_not_charging * HZ); 16618c2ecf20Sopenharmony_ci} 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci/** 16648c2ecf20Sopenharmony_ci * abx500_chargalg_wd_work() - periodic work to kick the charger watchdog 16658c2ecf20Sopenharmony_ci * @work: pointer to the work_struct structure 16668c2ecf20Sopenharmony_ci * 16678c2ecf20Sopenharmony_ci * Work queue function for kicking the charger watchdog 16688c2ecf20Sopenharmony_ci */ 16698c2ecf20Sopenharmony_cistatic void abx500_chargalg_wd_work(struct work_struct *work) 16708c2ecf20Sopenharmony_ci{ 16718c2ecf20Sopenharmony_ci int ret; 16728c2ecf20Sopenharmony_ci struct abx500_chargalg *di = container_of(work, 16738c2ecf20Sopenharmony_ci struct abx500_chargalg, chargalg_wd_work.work); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci dev_dbg(di->dev, "abx500_chargalg_wd_work\n"); 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci ret = abx500_chargalg_kick_watchdog(di); 16788c2ecf20Sopenharmony_ci if (ret < 0) 16798c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to kick watchdog\n"); 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci queue_delayed_work(di->chargalg_wq, 16828c2ecf20Sopenharmony_ci &di->chargalg_wd_work, CHG_WD_INTERVAL); 16838c2ecf20Sopenharmony_ci} 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci/** 16868c2ecf20Sopenharmony_ci * abx500_chargalg_work() - Work to run the charging algorithm instantly 16878c2ecf20Sopenharmony_ci * @work: pointer to the work_struct structure 16888c2ecf20Sopenharmony_ci * 16898c2ecf20Sopenharmony_ci * Work queue function for calling the charging algorithm 16908c2ecf20Sopenharmony_ci */ 16918c2ecf20Sopenharmony_cistatic void abx500_chargalg_work(struct work_struct *work) 16928c2ecf20Sopenharmony_ci{ 16938c2ecf20Sopenharmony_ci struct abx500_chargalg *di = container_of(work, 16948c2ecf20Sopenharmony_ci struct abx500_chargalg, chargalg_work); 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci abx500_chargalg_algorithm(di); 16978c2ecf20Sopenharmony_ci} 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci/** 17008c2ecf20Sopenharmony_ci * abx500_chargalg_get_property() - get the chargalg properties 17018c2ecf20Sopenharmony_ci * @psy: pointer to the power_supply structure 17028c2ecf20Sopenharmony_ci * @psp: pointer to the power_supply_property structure 17038c2ecf20Sopenharmony_ci * @val: pointer to the power_supply_propval union 17048c2ecf20Sopenharmony_ci * 17058c2ecf20Sopenharmony_ci * This function gets called when an application tries to get the 17068c2ecf20Sopenharmony_ci * chargalg properties by reading the sysfs files. 17078c2ecf20Sopenharmony_ci * status: charging/discharging/full/unknown 17088c2ecf20Sopenharmony_ci * health: health of the battery 17098c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0 on success 17108c2ecf20Sopenharmony_ci */ 17118c2ecf20Sopenharmony_cistatic int abx500_chargalg_get_property(struct power_supply *psy, 17128c2ecf20Sopenharmony_ci enum power_supply_property psp, 17138c2ecf20Sopenharmony_ci union power_supply_propval *val) 17148c2ecf20Sopenharmony_ci{ 17158c2ecf20Sopenharmony_ci struct abx500_chargalg *di = power_supply_get_drvdata(psy); 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci switch (psp) { 17188c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 17198c2ecf20Sopenharmony_ci val->intval = di->charge_status; 17208c2ecf20Sopenharmony_ci break; 17218c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 17228c2ecf20Sopenharmony_ci if (di->events.batt_ovv) { 17238c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 17248c2ecf20Sopenharmony_ci } else if (di->events.btemp_underover) { 17258c2ecf20Sopenharmony_ci if (di->batt_data.temp <= di->bm->temp_under) 17268c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_COLD; 17278c2ecf20Sopenharmony_ci else 17288c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 17298c2ecf20Sopenharmony_ci } else if (di->charge_state == STATE_SAFETY_TIMER_EXPIRED || 17308c2ecf20Sopenharmony_ci di->charge_state == STATE_SAFETY_TIMER_EXPIRED_INIT) { 17318c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 17328c2ecf20Sopenharmony_ci } else { 17338c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_GOOD; 17348c2ecf20Sopenharmony_ci } 17358c2ecf20Sopenharmony_ci break; 17368c2ecf20Sopenharmony_ci default: 17378c2ecf20Sopenharmony_ci return -EINVAL; 17388c2ecf20Sopenharmony_ci } 17398c2ecf20Sopenharmony_ci return 0; 17408c2ecf20Sopenharmony_ci} 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci/* Exposure to the sysfs interface */ 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_cistatic ssize_t abx500_chargalg_curr_step_show(struct abx500_chargalg *di, 17458c2ecf20Sopenharmony_ci char *buf) 17468c2ecf20Sopenharmony_ci{ 17478c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", di->curr_status.curr_step); 17488c2ecf20Sopenharmony_ci} 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_cistatic ssize_t abx500_chargalg_curr_step_store(struct abx500_chargalg *di, 17518c2ecf20Sopenharmony_ci const char *buf, size_t length) 17528c2ecf20Sopenharmony_ci{ 17538c2ecf20Sopenharmony_ci long int param; 17548c2ecf20Sopenharmony_ci int ret; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, ¶m); 17578c2ecf20Sopenharmony_ci if (ret < 0) 17588c2ecf20Sopenharmony_ci return ret; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci di->curr_status.curr_step = param; 17618c2ecf20Sopenharmony_ci if (di->curr_status.curr_step >= CHARGALG_CURR_STEP_LOW && 17628c2ecf20Sopenharmony_ci di->curr_status.curr_step <= CHARGALG_CURR_STEP_HIGH) { 17638c2ecf20Sopenharmony_ci di->curr_status.curr_step_change = true; 17648c2ecf20Sopenharmony_ci queue_work(di->chargalg_wq, &di->chargalg_work); 17658c2ecf20Sopenharmony_ci } else 17668c2ecf20Sopenharmony_ci dev_info(di->dev, "Wrong current step\n" 17678c2ecf20Sopenharmony_ci "Enter 0. Disable AC/USB Charging\n" 17688c2ecf20Sopenharmony_ci "1--100. Set AC/USB charging current step\n" 17698c2ecf20Sopenharmony_ci "100. Enable AC/USB Charging\n"); 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci return strlen(buf); 17728c2ecf20Sopenharmony_ci} 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_cistatic ssize_t abx500_chargalg_en_show(struct abx500_chargalg *di, 17768c2ecf20Sopenharmony_ci char *buf) 17778c2ecf20Sopenharmony_ci{ 17788c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", 17798c2ecf20Sopenharmony_ci di->susp_status.ac_suspended && 17808c2ecf20Sopenharmony_ci di->susp_status.usb_suspended); 17818c2ecf20Sopenharmony_ci} 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_cistatic ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di, 17848c2ecf20Sopenharmony_ci const char *buf, size_t length) 17858c2ecf20Sopenharmony_ci{ 17868c2ecf20Sopenharmony_ci long int param; 17878c2ecf20Sopenharmony_ci int ac_usb; 17888c2ecf20Sopenharmony_ci int ret; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, ¶m); 17918c2ecf20Sopenharmony_ci if (ret < 0) 17928c2ecf20Sopenharmony_ci return ret; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci ac_usb = param; 17958c2ecf20Sopenharmony_ci switch (ac_usb) { 17968c2ecf20Sopenharmony_ci case 0: 17978c2ecf20Sopenharmony_ci /* Disable charging */ 17988c2ecf20Sopenharmony_ci di->susp_status.ac_suspended = true; 17998c2ecf20Sopenharmony_ci di->susp_status.usb_suspended = true; 18008c2ecf20Sopenharmony_ci di->susp_status.suspended_change = true; 18018c2ecf20Sopenharmony_ci /* Trigger a state change */ 18028c2ecf20Sopenharmony_ci queue_work(di->chargalg_wq, 18038c2ecf20Sopenharmony_ci &di->chargalg_work); 18048c2ecf20Sopenharmony_ci break; 18058c2ecf20Sopenharmony_ci case 1: 18068c2ecf20Sopenharmony_ci /* Enable AC Charging */ 18078c2ecf20Sopenharmony_ci di->susp_status.ac_suspended = false; 18088c2ecf20Sopenharmony_ci di->susp_status.suspended_change = true; 18098c2ecf20Sopenharmony_ci /* Trigger a state change */ 18108c2ecf20Sopenharmony_ci queue_work(di->chargalg_wq, 18118c2ecf20Sopenharmony_ci &di->chargalg_work); 18128c2ecf20Sopenharmony_ci break; 18138c2ecf20Sopenharmony_ci case 2: 18148c2ecf20Sopenharmony_ci /* Enable USB charging */ 18158c2ecf20Sopenharmony_ci di->susp_status.usb_suspended = false; 18168c2ecf20Sopenharmony_ci di->susp_status.suspended_change = true; 18178c2ecf20Sopenharmony_ci /* Trigger a state change */ 18188c2ecf20Sopenharmony_ci queue_work(di->chargalg_wq, 18198c2ecf20Sopenharmony_ci &di->chargalg_work); 18208c2ecf20Sopenharmony_ci break; 18218c2ecf20Sopenharmony_ci default: 18228c2ecf20Sopenharmony_ci dev_info(di->dev, "Wrong input\n" 18238c2ecf20Sopenharmony_ci "Enter 0. Disable AC/USB Charging\n" 18248c2ecf20Sopenharmony_ci "1. Enable AC charging\n" 18258c2ecf20Sopenharmony_ci "2. Enable USB Charging\n"); 18268c2ecf20Sopenharmony_ci } 18278c2ecf20Sopenharmony_ci return strlen(buf); 18288c2ecf20Sopenharmony_ci} 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_cistatic struct abx500_chargalg_sysfs_entry abx500_chargalg_en_charger = 18318c2ecf20Sopenharmony_ci __ATTR(chargalg, 0644, abx500_chargalg_en_show, 18328c2ecf20Sopenharmony_ci abx500_chargalg_en_store); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_cistatic struct abx500_chargalg_sysfs_entry abx500_chargalg_curr_step = 18358c2ecf20Sopenharmony_ci __ATTR(chargalg_curr_step, 0644, abx500_chargalg_curr_step_show, 18368c2ecf20Sopenharmony_ci abx500_chargalg_curr_step_store); 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_cistatic ssize_t abx500_chargalg_sysfs_show(struct kobject *kobj, 18398c2ecf20Sopenharmony_ci struct attribute *attr, char *buf) 18408c2ecf20Sopenharmony_ci{ 18418c2ecf20Sopenharmony_ci struct abx500_chargalg_sysfs_entry *entry = container_of(attr, 18428c2ecf20Sopenharmony_ci struct abx500_chargalg_sysfs_entry, attr); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci struct abx500_chargalg *di = container_of(kobj, 18458c2ecf20Sopenharmony_ci struct abx500_chargalg, chargalg_kobject); 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci if (!entry->show) 18488c2ecf20Sopenharmony_ci return -EIO; 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci return entry->show(di, buf); 18518c2ecf20Sopenharmony_ci} 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_cistatic ssize_t abx500_chargalg_sysfs_charger(struct kobject *kobj, 18548c2ecf20Sopenharmony_ci struct attribute *attr, const char *buf, size_t length) 18558c2ecf20Sopenharmony_ci{ 18568c2ecf20Sopenharmony_ci struct abx500_chargalg_sysfs_entry *entry = container_of(attr, 18578c2ecf20Sopenharmony_ci struct abx500_chargalg_sysfs_entry, attr); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci struct abx500_chargalg *di = container_of(kobj, 18608c2ecf20Sopenharmony_ci struct abx500_chargalg, chargalg_kobject); 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci if (!entry->store) 18638c2ecf20Sopenharmony_ci return -EIO; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci return entry->store(di, buf, length); 18668c2ecf20Sopenharmony_ci} 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_cistatic struct attribute *abx500_chargalg_chg[] = { 18698c2ecf20Sopenharmony_ci &abx500_chargalg_en_charger.attr, 18708c2ecf20Sopenharmony_ci &abx500_chargalg_curr_step.attr, 18718c2ecf20Sopenharmony_ci NULL, 18728c2ecf20Sopenharmony_ci}; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_cistatic const struct sysfs_ops abx500_chargalg_sysfs_ops = { 18758c2ecf20Sopenharmony_ci .show = abx500_chargalg_sysfs_show, 18768c2ecf20Sopenharmony_ci .store = abx500_chargalg_sysfs_charger, 18778c2ecf20Sopenharmony_ci}; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_cistatic struct kobj_type abx500_chargalg_ktype = { 18808c2ecf20Sopenharmony_ci .sysfs_ops = &abx500_chargalg_sysfs_ops, 18818c2ecf20Sopenharmony_ci .default_attrs = abx500_chargalg_chg, 18828c2ecf20Sopenharmony_ci}; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci/** 18858c2ecf20Sopenharmony_ci * abx500_chargalg_sysfs_exit() - de-init of sysfs entry 18868c2ecf20Sopenharmony_ci * @di: pointer to the struct abx500_chargalg 18878c2ecf20Sopenharmony_ci * 18888c2ecf20Sopenharmony_ci * This function removes the entry in sysfs. 18898c2ecf20Sopenharmony_ci */ 18908c2ecf20Sopenharmony_cistatic void abx500_chargalg_sysfs_exit(struct abx500_chargalg *di) 18918c2ecf20Sopenharmony_ci{ 18928c2ecf20Sopenharmony_ci kobject_del(&di->chargalg_kobject); 18938c2ecf20Sopenharmony_ci} 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci/** 18968c2ecf20Sopenharmony_ci * abx500_chargalg_sysfs_init() - init of sysfs entry 18978c2ecf20Sopenharmony_ci * @di: pointer to the struct abx500_chargalg 18988c2ecf20Sopenharmony_ci * 18998c2ecf20Sopenharmony_ci * This function adds an entry in sysfs. 19008c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0(on success) 19018c2ecf20Sopenharmony_ci */ 19028c2ecf20Sopenharmony_cistatic int abx500_chargalg_sysfs_init(struct abx500_chargalg *di) 19038c2ecf20Sopenharmony_ci{ 19048c2ecf20Sopenharmony_ci int ret = 0; 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci ret = kobject_init_and_add(&di->chargalg_kobject, 19078c2ecf20Sopenharmony_ci &abx500_chargalg_ktype, 19088c2ecf20Sopenharmony_ci NULL, "abx500_chargalg"); 19098c2ecf20Sopenharmony_ci if (ret < 0) 19108c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to create sysfs entry\n"); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci return ret; 19138c2ecf20Sopenharmony_ci} 19148c2ecf20Sopenharmony_ci/* Exposure to the sysfs interface <<END>> */ 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci#if defined(CONFIG_PM) 19178c2ecf20Sopenharmony_cistatic int abx500_chargalg_resume(struct platform_device *pdev) 19188c2ecf20Sopenharmony_ci{ 19198c2ecf20Sopenharmony_ci struct abx500_chargalg *di = platform_get_drvdata(pdev); 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci /* Kick charger watchdog if charging (any charger online) */ 19228c2ecf20Sopenharmony_ci if (di->chg_info.online_chg) 19238c2ecf20Sopenharmony_ci queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0); 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci /* 19268c2ecf20Sopenharmony_ci * Run the charging algorithm directly to be sure we don't 19278c2ecf20Sopenharmony_ci * do it too seldom 19288c2ecf20Sopenharmony_ci */ 19298c2ecf20Sopenharmony_ci queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0); 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci return 0; 19328c2ecf20Sopenharmony_ci} 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_cistatic int abx500_chargalg_suspend(struct platform_device *pdev, 19358c2ecf20Sopenharmony_ci pm_message_t state) 19368c2ecf20Sopenharmony_ci{ 19378c2ecf20Sopenharmony_ci struct abx500_chargalg *di = platform_get_drvdata(pdev); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci if (di->chg_info.online_chg) 19408c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&di->chargalg_wd_work); 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&di->chargalg_periodic_work); 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci return 0; 19458c2ecf20Sopenharmony_ci} 19468c2ecf20Sopenharmony_ci#else 19478c2ecf20Sopenharmony_ci#define abx500_chargalg_suspend NULL 19488c2ecf20Sopenharmony_ci#define abx500_chargalg_resume NULL 19498c2ecf20Sopenharmony_ci#endif 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_cistatic int abx500_chargalg_remove(struct platform_device *pdev) 19528c2ecf20Sopenharmony_ci{ 19538c2ecf20Sopenharmony_ci struct abx500_chargalg *di = platform_get_drvdata(pdev); 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci /* sysfs interface to enable/disbale charging from user space */ 19568c2ecf20Sopenharmony_ci abx500_chargalg_sysfs_exit(di); 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci hrtimer_cancel(&di->safety_timer); 19598c2ecf20Sopenharmony_ci hrtimer_cancel(&di->maintenance_timer); 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&di->chargalg_periodic_work); 19628c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&di->chargalg_wd_work); 19638c2ecf20Sopenharmony_ci cancel_work_sync(&di->chargalg_work); 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci /* Delete the work queue */ 19668c2ecf20Sopenharmony_ci destroy_workqueue(di->chargalg_wq); 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci power_supply_unregister(di->chargalg_psy); 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci return 0; 19718c2ecf20Sopenharmony_ci} 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_cistatic char *supply_interface[] = { 19748c2ecf20Sopenharmony_ci "ab8500_fg", 19758c2ecf20Sopenharmony_ci}; 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_cistatic const struct power_supply_desc abx500_chargalg_desc = { 19788c2ecf20Sopenharmony_ci .name = "abx500_chargalg", 19798c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 19808c2ecf20Sopenharmony_ci .properties = abx500_chargalg_props, 19818c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(abx500_chargalg_props), 19828c2ecf20Sopenharmony_ci .get_property = abx500_chargalg_get_property, 19838c2ecf20Sopenharmony_ci .external_power_changed = abx500_chargalg_external_power_changed, 19848c2ecf20Sopenharmony_ci}; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_cistatic int abx500_chargalg_probe(struct platform_device *pdev) 19878c2ecf20Sopenharmony_ci{ 19888c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 19898c2ecf20Sopenharmony_ci struct abx500_bm_data *plat = pdev->dev.platform_data; 19908c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; 19918c2ecf20Sopenharmony_ci struct abx500_chargalg *di; 19928c2ecf20Sopenharmony_ci int ret = 0; 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL); 19958c2ecf20Sopenharmony_ci if (!di) { 19968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s no mem for ab8500_chargalg\n", __func__); 19978c2ecf20Sopenharmony_ci return -ENOMEM; 19988c2ecf20Sopenharmony_ci } 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci if (!plat) { 20018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no battery management data supplied\n"); 20028c2ecf20Sopenharmony_ci return -EINVAL; 20038c2ecf20Sopenharmony_ci } 20048c2ecf20Sopenharmony_ci di->bm = plat; 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci if (np) { 20078c2ecf20Sopenharmony_ci ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm); 20088c2ecf20Sopenharmony_ci if (ret) { 20098c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get battery information\n"); 20108c2ecf20Sopenharmony_ci return ret; 20118c2ecf20Sopenharmony_ci } 20128c2ecf20Sopenharmony_ci } 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci /* get device struct and parent */ 20158c2ecf20Sopenharmony_ci di->dev = &pdev->dev; 20168c2ecf20Sopenharmony_ci di->parent = dev_get_drvdata(pdev->dev.parent); 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci psy_cfg.supplied_to = supply_interface; 20198c2ecf20Sopenharmony_ci psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); 20208c2ecf20Sopenharmony_ci psy_cfg.drv_data = di; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci /* Initilialize safety timer */ 20238c2ecf20Sopenharmony_ci hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); 20248c2ecf20Sopenharmony_ci di->safety_timer.function = abx500_chargalg_safety_timer_expired; 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci /* Initilialize maintenance timer */ 20278c2ecf20Sopenharmony_ci hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); 20288c2ecf20Sopenharmony_ci di->maintenance_timer.function = 20298c2ecf20Sopenharmony_ci abx500_chargalg_maintenance_timer_expired; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci /* Create a work queue for the chargalg */ 20328c2ecf20Sopenharmony_ci di->chargalg_wq = alloc_ordered_workqueue("abx500_chargalg_wq", 20338c2ecf20Sopenharmony_ci WQ_MEM_RECLAIM); 20348c2ecf20Sopenharmony_ci if (di->chargalg_wq == NULL) { 20358c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to create work queue\n"); 20368c2ecf20Sopenharmony_ci return -ENOMEM; 20378c2ecf20Sopenharmony_ci } 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci /* Init work for chargalg */ 20408c2ecf20Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->chargalg_periodic_work, 20418c2ecf20Sopenharmony_ci abx500_chargalg_periodic_work); 20428c2ecf20Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->chargalg_wd_work, 20438c2ecf20Sopenharmony_ci abx500_chargalg_wd_work); 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci /* Init work for chargalg */ 20468c2ecf20Sopenharmony_ci INIT_WORK(&di->chargalg_work, abx500_chargalg_work); 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci /* To detect charger at startup */ 20498c2ecf20Sopenharmony_ci di->chg_info.prev_conn_chg = -1; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci /* Register chargalg power supply class */ 20528c2ecf20Sopenharmony_ci di->chargalg_psy = power_supply_register(di->dev, &abx500_chargalg_desc, 20538c2ecf20Sopenharmony_ci &psy_cfg); 20548c2ecf20Sopenharmony_ci if (IS_ERR(di->chargalg_psy)) { 20558c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to register chargalg psy\n"); 20568c2ecf20Sopenharmony_ci ret = PTR_ERR(di->chargalg_psy); 20578c2ecf20Sopenharmony_ci goto free_chargalg_wq; 20588c2ecf20Sopenharmony_ci } 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, di); 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci /* sysfs interface to enable/disable charging from user space */ 20638c2ecf20Sopenharmony_ci ret = abx500_chargalg_sysfs_init(di); 20648c2ecf20Sopenharmony_ci if (ret) { 20658c2ecf20Sopenharmony_ci dev_err(di->dev, "failed to create sysfs entry\n"); 20668c2ecf20Sopenharmony_ci goto free_psy; 20678c2ecf20Sopenharmony_ci } 20688c2ecf20Sopenharmony_ci di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH; 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci /* Run the charging algorithm */ 20718c2ecf20Sopenharmony_ci queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0); 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci dev_info(di->dev, "probe success\n"); 20748c2ecf20Sopenharmony_ci return ret; 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_cifree_psy: 20778c2ecf20Sopenharmony_ci power_supply_unregister(di->chargalg_psy); 20788c2ecf20Sopenharmony_cifree_chargalg_wq: 20798c2ecf20Sopenharmony_ci destroy_workqueue(di->chargalg_wq); 20808c2ecf20Sopenharmony_ci return ret; 20818c2ecf20Sopenharmony_ci} 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_cistatic const struct of_device_id ab8500_chargalg_match[] = { 20848c2ecf20Sopenharmony_ci { .compatible = "stericsson,ab8500-chargalg", }, 20858c2ecf20Sopenharmony_ci { }, 20868c2ecf20Sopenharmony_ci}; 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_cistatic struct platform_driver abx500_chargalg_driver = { 20898c2ecf20Sopenharmony_ci .probe = abx500_chargalg_probe, 20908c2ecf20Sopenharmony_ci .remove = abx500_chargalg_remove, 20918c2ecf20Sopenharmony_ci .suspend = abx500_chargalg_suspend, 20928c2ecf20Sopenharmony_ci .resume = abx500_chargalg_resume, 20938c2ecf20Sopenharmony_ci .driver = { 20948c2ecf20Sopenharmony_ci .name = "ab8500-chargalg", 20958c2ecf20Sopenharmony_ci .of_match_table = ab8500_chargalg_match, 20968c2ecf20Sopenharmony_ci }, 20978c2ecf20Sopenharmony_ci}; 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_cimodule_platform_driver(abx500_chargalg_driver); 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 21028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johan Palsson, Karl Komierowski"); 21038c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:abx500-chargalg"); 21048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("abx500 battery charging algorithm"); 2105