162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2012 462306a36Sopenharmony_ci * Copyright (c) 2012 Sony Mobile Communications AB 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Charging algorithm driver for AB8500 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Authors: 962306a36Sopenharmony_ci * Johan Palsson <johan.palsson@stericsson.com> 1062306a36Sopenharmony_ci * Karl Komierowski <karl.komierowski@stericsson.com> 1162306a36Sopenharmony_ci * Arun R Murthy <arun.murthy@stericsson.com> 1262306a36Sopenharmony_ci * Author: Imre Sunyi <imre.sunyi@sonymobile.com> 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/device.h> 1862306a36Sopenharmony_ci#include <linux/component.h> 1962306a36Sopenharmony_ci#include <linux/hrtimer.h> 2062306a36Sopenharmony_ci#include <linux/interrupt.h> 2162306a36Sopenharmony_ci#include <linux/delay.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/platform_device.h> 2462306a36Sopenharmony_ci#include <linux/power_supply.h> 2562306a36Sopenharmony_ci#include <linux/completion.h> 2662306a36Sopenharmony_ci#include <linux/workqueue.h> 2762306a36Sopenharmony_ci#include <linux/kobject.h> 2862306a36Sopenharmony_ci#include <linux/of.h> 2962306a36Sopenharmony_ci#include <linux/mfd/core.h> 3062306a36Sopenharmony_ci#include <linux/mfd/abx500.h> 3162306a36Sopenharmony_ci#include <linux/mfd/abx500/ab8500.h> 3262306a36Sopenharmony_ci#include <linux/notifier.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "ab8500-bm.h" 3562306a36Sopenharmony_ci#include "ab8500-chargalg.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Watchdog kick interval */ 3862306a36Sopenharmony_ci#define CHG_WD_INTERVAL (6 * HZ) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* End-of-charge criteria counter */ 4162306a36Sopenharmony_ci#define EOC_COND_CNT 10 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* One hour expressed in seconds */ 4462306a36Sopenharmony_ci#define ONE_HOUR_IN_SECONDS 3600 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Five minutes expressed in seconds */ 4762306a36Sopenharmony_ci#define FIVE_MINUTES_IN_SECONDS 300 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * This is the battery capacity limit that will trigger a new 5162306a36Sopenharmony_ci * full charging cycle in the case where maintenance charging 5262306a36Sopenharmony_ci * has been disabled 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci#define AB8500_RECHARGE_CAP 95 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cienum ab8500_chargers { 5762306a36Sopenharmony_ci NO_CHG, 5862306a36Sopenharmony_ci AC_CHG, 5962306a36Sopenharmony_ci USB_CHG, 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct ab8500_chargalg_charger_info { 6362306a36Sopenharmony_ci enum ab8500_chargers conn_chg; 6462306a36Sopenharmony_ci enum ab8500_chargers prev_conn_chg; 6562306a36Sopenharmony_ci enum ab8500_chargers online_chg; 6662306a36Sopenharmony_ci enum ab8500_chargers prev_online_chg; 6762306a36Sopenharmony_ci enum ab8500_chargers charger_type; 6862306a36Sopenharmony_ci bool usb_chg_ok; 6962306a36Sopenharmony_ci bool ac_chg_ok; 7062306a36Sopenharmony_ci int usb_volt_uv; 7162306a36Sopenharmony_ci int usb_curr_ua; 7262306a36Sopenharmony_ci int ac_volt_uv; 7362306a36Sopenharmony_ci int ac_curr_ua; 7462306a36Sopenharmony_ci int usb_vset_uv; 7562306a36Sopenharmony_ci int usb_iset_ua; 7662306a36Sopenharmony_ci int ac_vset_uv; 7762306a36Sopenharmony_ci int ac_iset_ua; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct ab8500_chargalg_battery_data { 8162306a36Sopenharmony_ci int temp; 8262306a36Sopenharmony_ci int volt_uv; 8362306a36Sopenharmony_ci int avg_curr_ua; 8462306a36Sopenharmony_ci int inst_curr_ua; 8562306a36Sopenharmony_ci int percent; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cienum ab8500_chargalg_states { 8962306a36Sopenharmony_ci STATE_HANDHELD_INIT, 9062306a36Sopenharmony_ci STATE_HANDHELD, 9162306a36Sopenharmony_ci STATE_CHG_NOT_OK_INIT, 9262306a36Sopenharmony_ci STATE_CHG_NOT_OK, 9362306a36Sopenharmony_ci STATE_HW_TEMP_PROTECT_INIT, 9462306a36Sopenharmony_ci STATE_HW_TEMP_PROTECT, 9562306a36Sopenharmony_ci STATE_NORMAL_INIT, 9662306a36Sopenharmony_ci STATE_NORMAL, 9762306a36Sopenharmony_ci STATE_WAIT_FOR_RECHARGE_INIT, 9862306a36Sopenharmony_ci STATE_WAIT_FOR_RECHARGE, 9962306a36Sopenharmony_ci STATE_MAINTENANCE_A_INIT, 10062306a36Sopenharmony_ci STATE_MAINTENANCE_A, 10162306a36Sopenharmony_ci STATE_MAINTENANCE_B_INIT, 10262306a36Sopenharmony_ci STATE_MAINTENANCE_B, 10362306a36Sopenharmony_ci STATE_TEMP_UNDEROVER_INIT, 10462306a36Sopenharmony_ci STATE_TEMP_UNDEROVER, 10562306a36Sopenharmony_ci STATE_TEMP_LOWHIGH_INIT, 10662306a36Sopenharmony_ci STATE_TEMP_LOWHIGH, 10762306a36Sopenharmony_ci STATE_OVV_PROTECT_INIT, 10862306a36Sopenharmony_ci STATE_OVV_PROTECT, 10962306a36Sopenharmony_ci STATE_SAFETY_TIMER_EXPIRED_INIT, 11062306a36Sopenharmony_ci STATE_SAFETY_TIMER_EXPIRED, 11162306a36Sopenharmony_ci STATE_BATT_REMOVED_INIT, 11262306a36Sopenharmony_ci STATE_BATT_REMOVED, 11362306a36Sopenharmony_ci STATE_WD_EXPIRED_INIT, 11462306a36Sopenharmony_ci STATE_WD_EXPIRED, 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic const char * const states[] = { 11862306a36Sopenharmony_ci "HANDHELD_INIT", 11962306a36Sopenharmony_ci "HANDHELD", 12062306a36Sopenharmony_ci "CHG_NOT_OK_INIT", 12162306a36Sopenharmony_ci "CHG_NOT_OK", 12262306a36Sopenharmony_ci "HW_TEMP_PROTECT_INIT", 12362306a36Sopenharmony_ci "HW_TEMP_PROTECT", 12462306a36Sopenharmony_ci "NORMAL_INIT", 12562306a36Sopenharmony_ci "NORMAL", 12662306a36Sopenharmony_ci "WAIT_FOR_RECHARGE_INIT", 12762306a36Sopenharmony_ci "WAIT_FOR_RECHARGE", 12862306a36Sopenharmony_ci "MAINTENANCE_A_INIT", 12962306a36Sopenharmony_ci "MAINTENANCE_A", 13062306a36Sopenharmony_ci "MAINTENANCE_B_INIT", 13162306a36Sopenharmony_ci "MAINTENANCE_B", 13262306a36Sopenharmony_ci "TEMP_UNDEROVER_INIT", 13362306a36Sopenharmony_ci "TEMP_UNDEROVER", 13462306a36Sopenharmony_ci "TEMP_LOWHIGH_INIT", 13562306a36Sopenharmony_ci "TEMP_LOWHIGH", 13662306a36Sopenharmony_ci "OVV_PROTECT_INIT", 13762306a36Sopenharmony_ci "OVV_PROTECT", 13862306a36Sopenharmony_ci "SAFETY_TIMER_EXPIRED_INIT", 13962306a36Sopenharmony_ci "SAFETY_TIMER_EXPIRED", 14062306a36Sopenharmony_ci "BATT_REMOVED_INIT", 14162306a36Sopenharmony_ci "BATT_REMOVED", 14262306a36Sopenharmony_ci "WD_EXPIRED_INIT", 14362306a36Sopenharmony_ci "WD_EXPIRED", 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistruct ab8500_chargalg_events { 14762306a36Sopenharmony_ci bool batt_unknown; 14862306a36Sopenharmony_ci bool mainextchnotok; 14962306a36Sopenharmony_ci bool batt_ovv; 15062306a36Sopenharmony_ci bool batt_rem; 15162306a36Sopenharmony_ci bool btemp_underover; 15262306a36Sopenharmony_ci bool btemp_low; 15362306a36Sopenharmony_ci bool btemp_high; 15462306a36Sopenharmony_ci bool main_thermal_prot; 15562306a36Sopenharmony_ci bool usb_thermal_prot; 15662306a36Sopenharmony_ci bool main_ovv; 15762306a36Sopenharmony_ci bool vbus_ovv; 15862306a36Sopenharmony_ci bool usbchargernotok; 15962306a36Sopenharmony_ci bool safety_timer_expired; 16062306a36Sopenharmony_ci bool maintenance_timer_expired; 16162306a36Sopenharmony_ci bool ac_wd_expired; 16262306a36Sopenharmony_ci bool usb_wd_expired; 16362306a36Sopenharmony_ci bool ac_cv_active; 16462306a36Sopenharmony_ci bool usb_cv_active; 16562306a36Sopenharmony_ci bool vbus_collapsed; 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/** 16962306a36Sopenharmony_ci * struct ab8500_charge_curr_maximization - Charger maximization parameters 17062306a36Sopenharmony_ci * @original_iset_ua: the non optimized/maximised charger current 17162306a36Sopenharmony_ci * @current_iset_ua: the charging current used at this moment 17262306a36Sopenharmony_ci * @condition_cnt: number of iterations needed before a new charger current 17362306a36Sopenharmony_ci is set 17462306a36Sopenharmony_ci * @max_current_ua: maximum charger current 17562306a36Sopenharmony_ci * @wait_cnt: to avoid too fast current step down in case of charger 17662306a36Sopenharmony_ci * voltage collapse, we insert this delay between step 17762306a36Sopenharmony_ci * down 17862306a36Sopenharmony_ci * @level: tells in how many steps the charging current has been 17962306a36Sopenharmony_ci increased 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_cistruct ab8500_charge_curr_maximization { 18262306a36Sopenharmony_ci int original_iset_ua; 18362306a36Sopenharmony_ci int current_iset_ua; 18462306a36Sopenharmony_ci int condition_cnt; 18562306a36Sopenharmony_ci int max_current_ua; 18662306a36Sopenharmony_ci int wait_cnt; 18762306a36Sopenharmony_ci u8 level; 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cienum maxim_ret { 19162306a36Sopenharmony_ci MAXIM_RET_NOACTION, 19262306a36Sopenharmony_ci MAXIM_RET_CHANGE, 19362306a36Sopenharmony_ci MAXIM_RET_IBAT_TOO_HIGH, 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/** 19762306a36Sopenharmony_ci * struct ab8500_chargalg - ab8500 Charging algorithm device information 19862306a36Sopenharmony_ci * @dev: pointer to the structure device 19962306a36Sopenharmony_ci * @charge_status: battery operating status 20062306a36Sopenharmony_ci * @eoc_cnt: counter used to determine end-of_charge 20162306a36Sopenharmony_ci * @maintenance_chg: indicate if maintenance charge is active 20262306a36Sopenharmony_ci * @t_hyst_norm temperature hysteresis when the temperature has been 20362306a36Sopenharmony_ci * over or under normal limits 20462306a36Sopenharmony_ci * @t_hyst_lowhigh temperature hysteresis when the temperature has been 20562306a36Sopenharmony_ci * over or under the high or low limits 20662306a36Sopenharmony_ci * @charge_state: current state of the charging algorithm 20762306a36Sopenharmony_ci * @ccm charging current maximization parameters 20862306a36Sopenharmony_ci * @chg_info: information about connected charger types 20962306a36Sopenharmony_ci * @batt_data: data of the battery 21062306a36Sopenharmony_ci * @bm: Platform specific battery management information 21162306a36Sopenharmony_ci * @parent: pointer to the struct ab8500 21262306a36Sopenharmony_ci * @chargalg_psy: structure that holds the battery properties exposed by 21362306a36Sopenharmony_ci * the charging algorithm 21462306a36Sopenharmony_ci * @events: structure for information about events triggered 21562306a36Sopenharmony_ci * @chargalg_wq: work queue for running the charging algorithm 21662306a36Sopenharmony_ci * @chargalg_periodic_work: work to run the charging algorithm periodically 21762306a36Sopenharmony_ci * @chargalg_wd_work: work to kick the charger watchdog periodically 21862306a36Sopenharmony_ci * @chargalg_work: work to run the charging algorithm instantly 21962306a36Sopenharmony_ci * @safety_timer: charging safety timer 22062306a36Sopenharmony_ci * @maintenance_timer: maintenance charging timer 22162306a36Sopenharmony_ci * @chargalg_kobject: structure of type kobject 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_cistruct ab8500_chargalg { 22462306a36Sopenharmony_ci struct device *dev; 22562306a36Sopenharmony_ci int charge_status; 22662306a36Sopenharmony_ci int eoc_cnt; 22762306a36Sopenharmony_ci bool maintenance_chg; 22862306a36Sopenharmony_ci int t_hyst_norm; 22962306a36Sopenharmony_ci int t_hyst_lowhigh; 23062306a36Sopenharmony_ci enum ab8500_chargalg_states charge_state; 23162306a36Sopenharmony_ci struct ab8500_charge_curr_maximization ccm; 23262306a36Sopenharmony_ci struct ab8500_chargalg_charger_info chg_info; 23362306a36Sopenharmony_ci struct ab8500_chargalg_battery_data batt_data; 23462306a36Sopenharmony_ci struct ab8500 *parent; 23562306a36Sopenharmony_ci struct ab8500_bm_data *bm; 23662306a36Sopenharmony_ci struct power_supply *chargalg_psy; 23762306a36Sopenharmony_ci struct ux500_charger *ac_chg; 23862306a36Sopenharmony_ci struct ux500_charger *usb_chg; 23962306a36Sopenharmony_ci struct ab8500_chargalg_events events; 24062306a36Sopenharmony_ci struct workqueue_struct *chargalg_wq; 24162306a36Sopenharmony_ci struct delayed_work chargalg_periodic_work; 24262306a36Sopenharmony_ci struct delayed_work chargalg_wd_work; 24362306a36Sopenharmony_ci struct work_struct chargalg_work; 24462306a36Sopenharmony_ci struct hrtimer safety_timer; 24562306a36Sopenharmony_ci struct hrtimer maintenance_timer; 24662306a36Sopenharmony_ci struct kobject chargalg_kobject; 24762306a36Sopenharmony_ci}; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/* Main battery properties */ 25062306a36Sopenharmony_cistatic enum power_supply_property ab8500_chargalg_props[] = { 25162306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 25262306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 25362306a36Sopenharmony_ci}; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/** 25662306a36Sopenharmony_ci * ab8500_chargalg_safety_timer_expired() - Expiration of the safety timer 25762306a36Sopenharmony_ci * @timer: pointer to the hrtimer structure 25862306a36Sopenharmony_ci * 25962306a36Sopenharmony_ci * This function gets called when the safety timer for the charger 26062306a36Sopenharmony_ci * expires 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_cistatic enum hrtimer_restart 26362306a36Sopenharmony_ciab8500_chargalg_safety_timer_expired(struct hrtimer *timer) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct ab8500_chargalg *di = container_of(timer, struct ab8500_chargalg, 26662306a36Sopenharmony_ci safety_timer); 26762306a36Sopenharmony_ci dev_err(di->dev, "Safety timer expired\n"); 26862306a36Sopenharmony_ci di->events.safety_timer_expired = true; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Trigger execution of the algorithm instantly */ 27162306a36Sopenharmony_ci queue_work(di->chargalg_wq, &di->chargalg_work); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return HRTIMER_NORESTART; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/** 27762306a36Sopenharmony_ci * ab8500_chargalg_maintenance_timer_expired() - Expiration of 27862306a36Sopenharmony_ci * the maintenance timer 27962306a36Sopenharmony_ci * @timer: pointer to the timer structure 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * This function gets called when the maintenance timer 28262306a36Sopenharmony_ci * expires 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_cistatic enum hrtimer_restart 28562306a36Sopenharmony_ciab8500_chargalg_maintenance_timer_expired(struct hrtimer *timer) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci struct ab8500_chargalg *di = container_of(timer, struct ab8500_chargalg, 28962306a36Sopenharmony_ci maintenance_timer); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci dev_dbg(di->dev, "Maintenance timer expired\n"); 29262306a36Sopenharmony_ci di->events.maintenance_timer_expired = true; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* Trigger execution of the algorithm instantly */ 29562306a36Sopenharmony_ci queue_work(di->chargalg_wq, &di->chargalg_work); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return HRTIMER_NORESTART; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/** 30162306a36Sopenharmony_ci * ab8500_chargalg_state_to() - Change charge state 30262306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 30362306a36Sopenharmony_ci * 30462306a36Sopenharmony_ci * This function gets called when a charge state change should occur 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_cistatic void ab8500_chargalg_state_to(struct ab8500_chargalg *di, 30762306a36Sopenharmony_ci enum ab8500_chargalg_states state) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci dev_dbg(di->dev, 31062306a36Sopenharmony_ci "State changed: %s (From state: [%d] %s =to=> [%d] %s )\n", 31162306a36Sopenharmony_ci di->charge_state == state ? "NO" : "YES", 31262306a36Sopenharmony_ci di->charge_state, 31362306a36Sopenharmony_ci states[di->charge_state], 31462306a36Sopenharmony_ci state, 31562306a36Sopenharmony_ci states[state]); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci di->charge_state = state; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int ab8500_chargalg_check_charger_enable(struct ab8500_chargalg *di) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct power_supply_battery_info *bi = di->bm->bi; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci switch (di->charge_state) { 32562306a36Sopenharmony_ci case STATE_NORMAL: 32662306a36Sopenharmony_ci case STATE_MAINTENANCE_A: 32762306a36Sopenharmony_ci case STATE_MAINTENANCE_B: 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci default: 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (di->chg_info.charger_type & USB_CHG) { 33462306a36Sopenharmony_ci return di->usb_chg->ops.check_enable(di->usb_chg, 33562306a36Sopenharmony_ci bi->constant_charge_voltage_max_uv, 33662306a36Sopenharmony_ci bi->constant_charge_current_max_ua); 33762306a36Sopenharmony_ci } else if (di->chg_info.charger_type & AC_CHG) { 33862306a36Sopenharmony_ci return di->ac_chg->ops.check_enable(di->ac_chg, 33962306a36Sopenharmony_ci bi->constant_charge_voltage_max_uv, 34062306a36Sopenharmony_ci bi->constant_charge_current_max_ua); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/** 34662306a36Sopenharmony_ci * ab8500_chargalg_check_charger_connection() - Check charger connection change 34762306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 34862306a36Sopenharmony_ci * 34962306a36Sopenharmony_ci * This function will check if there is a change in the charger connection 35062306a36Sopenharmony_ci * and change charge state accordingly. AC has precedence over USB. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_cistatic int ab8500_chargalg_check_charger_connection(struct ab8500_chargalg *di) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci if (di->chg_info.conn_chg != di->chg_info.prev_conn_chg) { 35562306a36Sopenharmony_ci /* Charger state changed since last update */ 35662306a36Sopenharmony_ci if (di->chg_info.conn_chg & AC_CHG) { 35762306a36Sopenharmony_ci dev_info(di->dev, "Charging source is AC\n"); 35862306a36Sopenharmony_ci if (di->chg_info.charger_type != AC_CHG) { 35962306a36Sopenharmony_ci di->chg_info.charger_type = AC_CHG; 36062306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci } else if (di->chg_info.conn_chg & USB_CHG) { 36362306a36Sopenharmony_ci dev_info(di->dev, "Charging source is USB\n"); 36462306a36Sopenharmony_ci di->chg_info.charger_type = USB_CHG; 36562306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 36662306a36Sopenharmony_ci } else { 36762306a36Sopenharmony_ci dev_dbg(di->dev, "Charging source is OFF\n"); 36862306a36Sopenharmony_ci di->chg_info.charger_type = NO_CHG; 36962306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_HANDHELD_INIT); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci di->chg_info.prev_conn_chg = di->chg_info.conn_chg; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci return di->chg_info.conn_chg; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/** 37762306a36Sopenharmony_ci * ab8500_chargalg_start_safety_timer() - Start charging safety timer 37862306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 37962306a36Sopenharmony_ci * 38062306a36Sopenharmony_ci * The safety timer is used to avoid overcharging of old or bad batteries. 38162306a36Sopenharmony_ci * There are different timers for AC and USB 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_cistatic void ab8500_chargalg_start_safety_timer(struct ab8500_chargalg *di) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci /* Charger-dependent expiration time in hours*/ 38662306a36Sopenharmony_ci int timer_expiration = 0; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci switch (di->chg_info.charger_type) { 38962306a36Sopenharmony_ci case AC_CHG: 39062306a36Sopenharmony_ci timer_expiration = di->bm->main_safety_tmr_h; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci case USB_CHG: 39462306a36Sopenharmony_ci timer_expiration = di->bm->usb_safety_tmr_h; 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci default: 39862306a36Sopenharmony_ci dev_err(di->dev, "Unknown charger to charge from\n"); 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci di->events.safety_timer_expired = false; 40362306a36Sopenharmony_ci hrtimer_set_expires_range(&di->safety_timer, 40462306a36Sopenharmony_ci ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0), 40562306a36Sopenharmony_ci ktime_set(FIVE_MINUTES_IN_SECONDS, 0)); 40662306a36Sopenharmony_ci hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci/** 41062306a36Sopenharmony_ci * ab8500_chargalg_stop_safety_timer() - Stop charging safety timer 41162306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 41262306a36Sopenharmony_ci * 41362306a36Sopenharmony_ci * The safety timer is stopped whenever the NORMAL state is exited 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_cistatic void ab8500_chargalg_stop_safety_timer(struct ab8500_chargalg *di) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci if (hrtimer_try_to_cancel(&di->safety_timer) >= 0) 41862306a36Sopenharmony_ci di->events.safety_timer_expired = false; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/** 42262306a36Sopenharmony_ci * ab8500_chargalg_start_maintenance_timer() - Start charging maintenance timer 42362306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 42462306a36Sopenharmony_ci * @duration: duration of the maintenance timer in minutes 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * The maintenance timer is used to maintain the charge in the battery once 42762306a36Sopenharmony_ci * the battery is considered full. These timers are chosen to match the 42862306a36Sopenharmony_ci * discharge curve of the battery 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_cistatic void ab8500_chargalg_start_maintenance_timer(struct ab8500_chargalg *di, 43162306a36Sopenharmony_ci int duration) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci /* Set a timer in minutes with a 30 second range */ 43462306a36Sopenharmony_ci hrtimer_set_expires_range(&di->maintenance_timer, 43562306a36Sopenharmony_ci ktime_set(duration * 60, 0), 43662306a36Sopenharmony_ci ktime_set(30, 0)); 43762306a36Sopenharmony_ci di->events.maintenance_timer_expired = false; 43862306a36Sopenharmony_ci hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL); 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci/** 44262306a36Sopenharmony_ci * ab8500_chargalg_stop_maintenance_timer() - Stop maintenance timer 44362306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * The maintenance timer is stopped whenever maintenance ends or when another 44662306a36Sopenharmony_ci * state is entered 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_cistatic void ab8500_chargalg_stop_maintenance_timer(struct ab8500_chargalg *di) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0) 45162306a36Sopenharmony_ci di->events.maintenance_timer_expired = false; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/** 45562306a36Sopenharmony_ci * ab8500_chargalg_kick_watchdog() - Kick charger watchdog 45662306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 45762306a36Sopenharmony_ci * 45862306a36Sopenharmony_ci * The charger watchdog have to be kicked periodically whenever the charger is 45962306a36Sopenharmony_ci * on, else the ABB will reset the system 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_cistatic int ab8500_chargalg_kick_watchdog(struct ab8500_chargalg *di) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci /* Check if charger exists and kick watchdog if charging */ 46462306a36Sopenharmony_ci if (di->ac_chg && di->ac_chg->ops.kick_wd && 46562306a36Sopenharmony_ci di->chg_info.online_chg & AC_CHG) { 46662306a36Sopenharmony_ci return di->ac_chg->ops.kick_wd(di->ac_chg); 46762306a36Sopenharmony_ci } else if (di->usb_chg && di->usb_chg->ops.kick_wd && 46862306a36Sopenharmony_ci di->chg_info.online_chg & USB_CHG) 46962306a36Sopenharmony_ci return di->usb_chg->ops.kick_wd(di->usb_chg); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return -ENXIO; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci/** 47562306a36Sopenharmony_ci * ab8500_chargalg_ac_en() - Turn on/off the AC charger 47662306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 47762306a36Sopenharmony_ci * @enable: charger on/off 47862306a36Sopenharmony_ci * @vset_uv: requested charger output voltage in microvolt 47962306a36Sopenharmony_ci * @iset_ua: requested charger output current in microampere 48062306a36Sopenharmony_ci * 48162306a36Sopenharmony_ci * The AC charger will be turned on/off with the requested charge voltage and 48262306a36Sopenharmony_ci * current 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_cistatic int ab8500_chargalg_ac_en(struct ab8500_chargalg *di, int enable, 48562306a36Sopenharmony_ci int vset_uv, int iset_ua) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci if (!di->ac_chg || !di->ac_chg->ops.enable) 48862306a36Sopenharmony_ci return -ENXIO; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* Select maximum of what both the charger and the battery supports */ 49162306a36Sopenharmony_ci if (di->ac_chg->max_out_volt_uv) 49262306a36Sopenharmony_ci vset_uv = min(vset_uv, di->ac_chg->max_out_volt_uv); 49362306a36Sopenharmony_ci if (di->ac_chg->max_out_curr_ua) 49462306a36Sopenharmony_ci iset_ua = min(iset_ua, di->ac_chg->max_out_curr_ua); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci di->chg_info.ac_iset_ua = iset_ua; 49762306a36Sopenharmony_ci di->chg_info.ac_vset_uv = vset_uv; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return di->ac_chg->ops.enable(di->ac_chg, enable, vset_uv, iset_ua); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/** 50362306a36Sopenharmony_ci * ab8500_chargalg_usb_en() - Turn on/off the USB charger 50462306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 50562306a36Sopenharmony_ci * @enable: charger on/off 50662306a36Sopenharmony_ci * @vset_uv: requested charger output voltage in microvolt 50762306a36Sopenharmony_ci * @iset_ua: requested charger output current in microampere 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * The USB charger will be turned on/off with the requested charge voltage and 51062306a36Sopenharmony_ci * current 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_cistatic int ab8500_chargalg_usb_en(struct ab8500_chargalg *di, int enable, 51362306a36Sopenharmony_ci int vset_uv, int iset_ua) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci if (!di->usb_chg || !di->usb_chg->ops.enable) 51662306a36Sopenharmony_ci return -ENXIO; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* Select maximum of what both the charger and the battery supports */ 51962306a36Sopenharmony_ci if (di->usb_chg->max_out_volt_uv) 52062306a36Sopenharmony_ci vset_uv = min(vset_uv, di->usb_chg->max_out_volt_uv); 52162306a36Sopenharmony_ci if (di->usb_chg->max_out_curr_ua) 52262306a36Sopenharmony_ci iset_ua = min(iset_ua, di->usb_chg->max_out_curr_ua); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci di->chg_info.usb_iset_ua = iset_ua; 52562306a36Sopenharmony_ci di->chg_info.usb_vset_uv = vset_uv; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return di->usb_chg->ops.enable(di->usb_chg, enable, vset_uv, iset_ua); 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci/** 53162306a36Sopenharmony_ci * ab8500_chargalg_update_chg_curr() - Update charger current 53262306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 53362306a36Sopenharmony_ci * @iset_ua: requested charger output current in microampere 53462306a36Sopenharmony_ci * 53562306a36Sopenharmony_ci * The charger output current will be updated for the charger 53662306a36Sopenharmony_ci * that is currently in use 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_cistatic int ab8500_chargalg_update_chg_curr(struct ab8500_chargalg *di, 53962306a36Sopenharmony_ci int iset_ua) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci /* Check if charger exists and update current if charging */ 54262306a36Sopenharmony_ci if (di->ac_chg && di->ac_chg->ops.update_curr && 54362306a36Sopenharmony_ci di->chg_info.charger_type & AC_CHG) { 54462306a36Sopenharmony_ci /* 54562306a36Sopenharmony_ci * Select maximum of what both the charger 54662306a36Sopenharmony_ci * and the battery supports 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci if (di->ac_chg->max_out_curr_ua) 54962306a36Sopenharmony_ci iset_ua = min(iset_ua, di->ac_chg->max_out_curr_ua); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci di->chg_info.ac_iset_ua = iset_ua; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return di->ac_chg->ops.update_curr(di->ac_chg, iset_ua); 55462306a36Sopenharmony_ci } else if (di->usb_chg && di->usb_chg->ops.update_curr && 55562306a36Sopenharmony_ci di->chg_info.charger_type & USB_CHG) { 55662306a36Sopenharmony_ci /* 55762306a36Sopenharmony_ci * Select maximum of what both the charger 55862306a36Sopenharmony_ci * and the battery supports 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_ci if (di->usb_chg->max_out_curr_ua) 56162306a36Sopenharmony_ci iset_ua = min(iset_ua, di->usb_chg->max_out_curr_ua); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci di->chg_info.usb_iset_ua = iset_ua; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return di->usb_chg->ops.update_curr(di->usb_chg, iset_ua); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return -ENXIO; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci/** 57262306a36Sopenharmony_ci * ab8500_chargalg_stop_charging() - Stop charging 57362306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 57462306a36Sopenharmony_ci * 57562306a36Sopenharmony_ci * This function is called from any state where charging should be stopped. 57662306a36Sopenharmony_ci * All charging is disabled and all status parameters and timers are changed 57762306a36Sopenharmony_ci * accordingly 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_cistatic void ab8500_chargalg_stop_charging(struct ab8500_chargalg *di) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci ab8500_chargalg_ac_en(di, false, 0, 0); 58262306a36Sopenharmony_ci ab8500_chargalg_usb_en(di, false, 0, 0); 58362306a36Sopenharmony_ci ab8500_chargalg_stop_safety_timer(di); 58462306a36Sopenharmony_ci ab8500_chargalg_stop_maintenance_timer(di); 58562306a36Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; 58662306a36Sopenharmony_ci di->maintenance_chg = false; 58762306a36Sopenharmony_ci cancel_delayed_work(&di->chargalg_wd_work); 58862306a36Sopenharmony_ci power_supply_changed(di->chargalg_psy); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci/** 59262306a36Sopenharmony_ci * ab8500_chargalg_hold_charging() - Pauses charging 59362306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 59462306a36Sopenharmony_ci * 59562306a36Sopenharmony_ci * This function is called in the case where maintenance charging has been 59662306a36Sopenharmony_ci * disabled and instead a battery voltage mode is entered to check when the 59762306a36Sopenharmony_ci * battery voltage has reached a certain recharge voltage 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_cistatic void ab8500_chargalg_hold_charging(struct ab8500_chargalg *di) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci ab8500_chargalg_ac_en(di, false, 0, 0); 60262306a36Sopenharmony_ci ab8500_chargalg_usb_en(di, false, 0, 0); 60362306a36Sopenharmony_ci ab8500_chargalg_stop_safety_timer(di); 60462306a36Sopenharmony_ci ab8500_chargalg_stop_maintenance_timer(di); 60562306a36Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_CHARGING; 60662306a36Sopenharmony_ci di->maintenance_chg = false; 60762306a36Sopenharmony_ci cancel_delayed_work(&di->chargalg_wd_work); 60862306a36Sopenharmony_ci power_supply_changed(di->chargalg_psy); 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci/** 61262306a36Sopenharmony_ci * ab8500_chargalg_start_charging() - Start the charger 61362306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 61462306a36Sopenharmony_ci * @vset_uv: requested charger output voltage in microvolt 61562306a36Sopenharmony_ci * @iset_ua: requested charger output current in microampere 61662306a36Sopenharmony_ci * 61762306a36Sopenharmony_ci * A charger will be enabled depending on the requested charger type that was 61862306a36Sopenharmony_ci * detected previously. 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_cistatic void ab8500_chargalg_start_charging(struct ab8500_chargalg *di, 62162306a36Sopenharmony_ci int vset_uv, int iset_ua) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci switch (di->chg_info.charger_type) { 62462306a36Sopenharmony_ci case AC_CHG: 62562306a36Sopenharmony_ci dev_dbg(di->dev, 62662306a36Sopenharmony_ci "AC parameters: Vset %d, Ich %d\n", vset_uv, iset_ua); 62762306a36Sopenharmony_ci ab8500_chargalg_usb_en(di, false, 0, 0); 62862306a36Sopenharmony_ci ab8500_chargalg_ac_en(di, true, vset_uv, iset_ua); 62962306a36Sopenharmony_ci break; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci case USB_CHG: 63262306a36Sopenharmony_ci dev_dbg(di->dev, 63362306a36Sopenharmony_ci "USB parameters: Vset %d, Ich %d\n", vset_uv, iset_ua); 63462306a36Sopenharmony_ci ab8500_chargalg_ac_en(di, false, 0, 0); 63562306a36Sopenharmony_ci ab8500_chargalg_usb_en(di, true, vset_uv, iset_ua); 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci default: 63962306a36Sopenharmony_ci dev_err(di->dev, "Unknown charger to charge from\n"); 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci/** 64562306a36Sopenharmony_ci * ab8500_chargalg_check_temp() - Check battery temperature ranges 64662306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 64762306a36Sopenharmony_ci * 64862306a36Sopenharmony_ci * The battery temperature is checked against the predefined limits and the 64962306a36Sopenharmony_ci * charge state is changed accordingly 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_cistatic void ab8500_chargalg_check_temp(struct ab8500_chargalg *di) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct power_supply_battery_info *bi = di->bm->bi; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (di->batt_data.temp > (bi->temp_alert_min + di->t_hyst_norm) && 65662306a36Sopenharmony_ci di->batt_data.temp < (bi->temp_alert_max - di->t_hyst_norm)) { 65762306a36Sopenharmony_ci /* Temp OK! */ 65862306a36Sopenharmony_ci di->events.btemp_underover = false; 65962306a36Sopenharmony_ci di->events.btemp_low = false; 66062306a36Sopenharmony_ci di->events.btemp_high = false; 66162306a36Sopenharmony_ci di->t_hyst_norm = 0; 66262306a36Sopenharmony_ci di->t_hyst_lowhigh = 0; 66362306a36Sopenharmony_ci } else { 66462306a36Sopenharmony_ci if ((di->batt_data.temp >= bi->temp_alert_max) && 66562306a36Sopenharmony_ci (di->batt_data.temp < (bi->temp_max - di->t_hyst_lowhigh))) { 66662306a36Sopenharmony_ci /* Alert zone for high temperature */ 66762306a36Sopenharmony_ci di->events.btemp_underover = false; 66862306a36Sopenharmony_ci di->events.btemp_high = true; 66962306a36Sopenharmony_ci di->t_hyst_norm = di->bm->temp_hysteresis; 67062306a36Sopenharmony_ci di->t_hyst_lowhigh = 0; 67162306a36Sopenharmony_ci } else if ((di->batt_data.temp > (bi->temp_min + di->t_hyst_lowhigh)) && 67262306a36Sopenharmony_ci (di->batt_data.temp <= bi->temp_alert_min)) { 67362306a36Sopenharmony_ci /* Alert zone for low temperature */ 67462306a36Sopenharmony_ci di->events.btemp_underover = false; 67562306a36Sopenharmony_ci di->events.btemp_low = true; 67662306a36Sopenharmony_ci di->t_hyst_norm = di->bm->temp_hysteresis; 67762306a36Sopenharmony_ci di->t_hyst_lowhigh = 0; 67862306a36Sopenharmony_ci } else if (di->batt_data.temp <= bi->temp_min || 67962306a36Sopenharmony_ci di->batt_data.temp >= bi->temp_max) { 68062306a36Sopenharmony_ci /* TEMP major!!!!! */ 68162306a36Sopenharmony_ci di->events.btemp_underover = true; 68262306a36Sopenharmony_ci di->events.btemp_low = false; 68362306a36Sopenharmony_ci di->events.btemp_high = false; 68462306a36Sopenharmony_ci di->t_hyst_norm = 0; 68562306a36Sopenharmony_ci di->t_hyst_lowhigh = di->bm->temp_hysteresis; 68662306a36Sopenharmony_ci } else { 68762306a36Sopenharmony_ci /* Within hysteresis */ 68862306a36Sopenharmony_ci dev_dbg(di->dev, "Within hysteresis limit temp: %d " 68962306a36Sopenharmony_ci "hyst_lowhigh %d, hyst normal %d\n", 69062306a36Sopenharmony_ci di->batt_data.temp, di->t_hyst_lowhigh, 69162306a36Sopenharmony_ci di->t_hyst_norm); 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci/** 69762306a36Sopenharmony_ci * ab8500_chargalg_check_charger_voltage() - Check charger voltage 69862306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 69962306a36Sopenharmony_ci * 70062306a36Sopenharmony_ci * Charger voltage is checked against maximum limit 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_cistatic void ab8500_chargalg_check_charger_voltage(struct ab8500_chargalg *di) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci if (di->chg_info.usb_volt_uv > di->bm->chg_params->usb_volt_max_uv) 70562306a36Sopenharmony_ci di->chg_info.usb_chg_ok = false; 70662306a36Sopenharmony_ci else 70762306a36Sopenharmony_ci di->chg_info.usb_chg_ok = true; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (di->chg_info.ac_volt_uv > di->bm->chg_params->ac_volt_max_uv) 71062306a36Sopenharmony_ci di->chg_info.ac_chg_ok = false; 71162306a36Sopenharmony_ci else 71262306a36Sopenharmony_ci di->chg_info.ac_chg_ok = true; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci/** 71762306a36Sopenharmony_ci * ab8500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled 71862306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 71962306a36Sopenharmony_ci * 72062306a36Sopenharmony_ci * End-of-charge criteria is fulfilled when the battery voltage is above a 72162306a36Sopenharmony_ci * certain limit and the battery current is below a certain limit for a 72262306a36Sopenharmony_ci * predefined number of consecutive seconds. If true, the battery is full 72362306a36Sopenharmony_ci */ 72462306a36Sopenharmony_cistatic void ab8500_chargalg_end_of_charge(struct ab8500_chargalg *di) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING && 72762306a36Sopenharmony_ci di->charge_state == STATE_NORMAL && 72862306a36Sopenharmony_ci !di->maintenance_chg && (di->batt_data.volt_uv >= 72962306a36Sopenharmony_ci di->bm->bi->voltage_max_design_uv || 73062306a36Sopenharmony_ci di->events.usb_cv_active || di->events.ac_cv_active) && 73162306a36Sopenharmony_ci di->batt_data.avg_curr_ua < 73262306a36Sopenharmony_ci di->bm->bi->charge_term_current_ua && 73362306a36Sopenharmony_ci di->batt_data.avg_curr_ua > 0) { 73462306a36Sopenharmony_ci if (++di->eoc_cnt >= EOC_COND_CNT) { 73562306a36Sopenharmony_ci di->eoc_cnt = 0; 73662306a36Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_FULL; 73762306a36Sopenharmony_ci di->maintenance_chg = true; 73862306a36Sopenharmony_ci dev_dbg(di->dev, "EOC reached!\n"); 73962306a36Sopenharmony_ci power_supply_changed(di->chargalg_psy); 74062306a36Sopenharmony_ci } else { 74162306a36Sopenharmony_ci dev_dbg(di->dev, 74262306a36Sopenharmony_ci " EOC limit reached for the %d" 74362306a36Sopenharmony_ci " time, out of %d before EOC\n", 74462306a36Sopenharmony_ci di->eoc_cnt, 74562306a36Sopenharmony_ci EOC_COND_CNT); 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci } else { 74862306a36Sopenharmony_ci di->eoc_cnt = 0; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic void init_maxim_chg_curr(struct ab8500_chargalg *di) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci struct power_supply_battery_info *bi = di->bm->bi; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci di->ccm.original_iset_ua = bi->constant_charge_current_max_ua; 75762306a36Sopenharmony_ci di->ccm.current_iset_ua = bi->constant_charge_current_max_ua; 75862306a36Sopenharmony_ci di->ccm.max_current_ua = di->bm->maxi->chg_curr_ua; 75962306a36Sopenharmony_ci di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 76062306a36Sopenharmony_ci di->ccm.level = 0; 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci/** 76462306a36Sopenharmony_ci * ab8500_chargalg_chg_curr_maxim - increases the charger current to 76562306a36Sopenharmony_ci * compensate for the system load 76662306a36Sopenharmony_ci * @di pointer to the ab8500_chargalg structure 76762306a36Sopenharmony_ci * 76862306a36Sopenharmony_ci * This maximization function is used to raise the charger current to get the 76962306a36Sopenharmony_ci * battery current as close to the optimal value as possible. The battery 77062306a36Sopenharmony_ci * current during charging is affected by the system load 77162306a36Sopenharmony_ci */ 77262306a36Sopenharmony_cistatic enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (!di->bm->maxi->ena_maxi) 77662306a36Sopenharmony_ci return MAXIM_RET_NOACTION; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (di->events.vbus_collapsed) { 77962306a36Sopenharmony_ci dev_dbg(di->dev, "Charger voltage has collapsed %d\n", 78062306a36Sopenharmony_ci di->ccm.wait_cnt); 78162306a36Sopenharmony_ci if (di->ccm.wait_cnt == 0) { 78262306a36Sopenharmony_ci dev_dbg(di->dev, "lowering current\n"); 78362306a36Sopenharmony_ci di->ccm.wait_cnt++; 78462306a36Sopenharmony_ci di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 78562306a36Sopenharmony_ci di->ccm.max_current_ua = di->ccm.current_iset_ua; 78662306a36Sopenharmony_ci di->ccm.current_iset_ua = di->ccm.max_current_ua; 78762306a36Sopenharmony_ci di->ccm.level--; 78862306a36Sopenharmony_ci return MAXIM_RET_CHANGE; 78962306a36Sopenharmony_ci } else { 79062306a36Sopenharmony_ci dev_dbg(di->dev, "waiting\n"); 79162306a36Sopenharmony_ci /* Let's go in here twice before lowering curr again */ 79262306a36Sopenharmony_ci di->ccm.wait_cnt = (di->ccm.wait_cnt + 1) % 3; 79362306a36Sopenharmony_ci return MAXIM_RET_NOACTION; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci di->ccm.wait_cnt = 0; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (di->batt_data.inst_curr_ua > di->ccm.original_iset_ua) { 80062306a36Sopenharmony_ci dev_dbg(di->dev, " Maximization Ibat (%duA) too high" 80162306a36Sopenharmony_ci " (limit %duA) (current iset: %duA)!\n", 80262306a36Sopenharmony_ci di->batt_data.inst_curr_ua, di->ccm.original_iset_ua, 80362306a36Sopenharmony_ci di->ccm.current_iset_ua); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if (di->ccm.current_iset_ua == di->ccm.original_iset_ua) 80662306a36Sopenharmony_ci return MAXIM_RET_NOACTION; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 80962306a36Sopenharmony_ci di->ccm.current_iset_ua = di->ccm.original_iset_ua; 81062306a36Sopenharmony_ci di->ccm.level = 0; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci return MAXIM_RET_IBAT_TOO_HIGH; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 81662306a36Sopenharmony_ci return MAXIM_RET_NOACTION; 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic void handle_maxim_chg_curr(struct ab8500_chargalg *di) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct power_supply_battery_info *bi = di->bm->bi; 82262306a36Sopenharmony_ci enum maxim_ret ret; 82362306a36Sopenharmony_ci int result; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci ret = ab8500_chargalg_chg_curr_maxim(di); 82662306a36Sopenharmony_ci switch (ret) { 82762306a36Sopenharmony_ci case MAXIM_RET_CHANGE: 82862306a36Sopenharmony_ci result = ab8500_chargalg_update_chg_curr(di, 82962306a36Sopenharmony_ci di->ccm.current_iset_ua); 83062306a36Sopenharmony_ci if (result) 83162306a36Sopenharmony_ci dev_err(di->dev, "failed to set chg curr\n"); 83262306a36Sopenharmony_ci break; 83362306a36Sopenharmony_ci case MAXIM_RET_IBAT_TOO_HIGH: 83462306a36Sopenharmony_ci result = ab8500_chargalg_update_chg_curr(di, 83562306a36Sopenharmony_ci bi->constant_charge_current_max_ua); 83662306a36Sopenharmony_ci if (result) 83762306a36Sopenharmony_ci dev_err(di->dev, "failed to set chg curr\n"); 83862306a36Sopenharmony_ci break; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci case MAXIM_RET_NOACTION: 84162306a36Sopenharmony_ci default: 84262306a36Sopenharmony_ci /* Do nothing..*/ 84362306a36Sopenharmony_ci break; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic int ab8500_chargalg_get_ext_psy_data(struct device *dev, void *data) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci struct power_supply *psy; 85062306a36Sopenharmony_ci struct power_supply *ext = dev_get_drvdata(dev); 85162306a36Sopenharmony_ci const char **supplicants = (const char **)ext->supplied_to; 85262306a36Sopenharmony_ci struct ab8500_chargalg *di; 85362306a36Sopenharmony_ci union power_supply_propval ret; 85462306a36Sopenharmony_ci int j; 85562306a36Sopenharmony_ci bool capacity_updated = false; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci psy = (struct power_supply *)data; 85862306a36Sopenharmony_ci di = power_supply_get_drvdata(psy); 85962306a36Sopenharmony_ci /* For all psy where the driver name appears in any supplied_to */ 86062306a36Sopenharmony_ci j = match_string(supplicants, ext->num_supplicants, psy->desc->name); 86162306a36Sopenharmony_ci if (j < 0) 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci /* 86562306a36Sopenharmony_ci * If external is not registering 'POWER_SUPPLY_PROP_CAPACITY' to its 86662306a36Sopenharmony_ci * property because of handling that sysfs entry on its own, this is 86762306a36Sopenharmony_ci * the place to get the battery capacity. 86862306a36Sopenharmony_ci */ 86962306a36Sopenharmony_ci if (!power_supply_get_property(ext, POWER_SUPPLY_PROP_CAPACITY, &ret)) { 87062306a36Sopenharmony_ci di->batt_data.percent = ret.intval; 87162306a36Sopenharmony_ci capacity_updated = true; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* Go through all properties for the psy */ 87562306a36Sopenharmony_ci for (j = 0; j < ext->desc->num_properties; j++) { 87662306a36Sopenharmony_ci enum power_supply_property prop; 87762306a36Sopenharmony_ci prop = ext->desc->properties[j]; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci /* 88062306a36Sopenharmony_ci * Initialize chargers if not already done. 88162306a36Sopenharmony_ci * The ab8500_charger*/ 88262306a36Sopenharmony_ci if (!di->ac_chg && 88362306a36Sopenharmony_ci ext->desc->type == POWER_SUPPLY_TYPE_MAINS) 88462306a36Sopenharmony_ci di->ac_chg = psy_to_ux500_charger(ext); 88562306a36Sopenharmony_ci else if (!di->usb_chg && 88662306a36Sopenharmony_ci ext->desc->type == POWER_SUPPLY_TYPE_USB) 88762306a36Sopenharmony_ci di->usb_chg = psy_to_ux500_charger(ext); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (power_supply_get_property(ext, prop, &ret)) 89062306a36Sopenharmony_ci continue; 89162306a36Sopenharmony_ci switch (prop) { 89262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 89362306a36Sopenharmony_ci switch (ext->desc->type) { 89462306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 89562306a36Sopenharmony_ci /* Battery present */ 89662306a36Sopenharmony_ci if (ret.intval) 89762306a36Sopenharmony_ci di->events.batt_rem = false; 89862306a36Sopenharmony_ci /* Battery removed */ 89962306a36Sopenharmony_ci else 90062306a36Sopenharmony_ci di->events.batt_rem = true; 90162306a36Sopenharmony_ci break; 90262306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 90362306a36Sopenharmony_ci /* AC disconnected */ 90462306a36Sopenharmony_ci if (!ret.intval && 90562306a36Sopenharmony_ci (di->chg_info.conn_chg & AC_CHG)) { 90662306a36Sopenharmony_ci di->chg_info.prev_conn_chg = 90762306a36Sopenharmony_ci di->chg_info.conn_chg; 90862306a36Sopenharmony_ci di->chg_info.conn_chg &= ~AC_CHG; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci /* AC connected */ 91162306a36Sopenharmony_ci else if (ret.intval && 91262306a36Sopenharmony_ci !(di->chg_info.conn_chg & AC_CHG)) { 91362306a36Sopenharmony_ci di->chg_info.prev_conn_chg = 91462306a36Sopenharmony_ci di->chg_info.conn_chg; 91562306a36Sopenharmony_ci di->chg_info.conn_chg |= AC_CHG; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci break; 91862306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 91962306a36Sopenharmony_ci /* USB disconnected */ 92062306a36Sopenharmony_ci if (!ret.intval && 92162306a36Sopenharmony_ci (di->chg_info.conn_chg & USB_CHG)) { 92262306a36Sopenharmony_ci di->chg_info.prev_conn_chg = 92362306a36Sopenharmony_ci di->chg_info.conn_chg; 92462306a36Sopenharmony_ci di->chg_info.conn_chg &= ~USB_CHG; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci /* USB connected */ 92762306a36Sopenharmony_ci else if (ret.intval && 92862306a36Sopenharmony_ci !(di->chg_info.conn_chg & USB_CHG)) { 92962306a36Sopenharmony_ci di->chg_info.prev_conn_chg = 93062306a36Sopenharmony_ci di->chg_info.conn_chg; 93162306a36Sopenharmony_ci di->chg_info.conn_chg |= USB_CHG; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci break; 93462306a36Sopenharmony_ci default: 93562306a36Sopenharmony_ci break; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci break; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 94062306a36Sopenharmony_ci switch (ext->desc->type) { 94162306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 94262306a36Sopenharmony_ci break; 94362306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 94462306a36Sopenharmony_ci /* AC offline */ 94562306a36Sopenharmony_ci if (!ret.intval && 94662306a36Sopenharmony_ci (di->chg_info.online_chg & AC_CHG)) { 94762306a36Sopenharmony_ci di->chg_info.prev_online_chg = 94862306a36Sopenharmony_ci di->chg_info.online_chg; 94962306a36Sopenharmony_ci di->chg_info.online_chg &= ~AC_CHG; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci /* AC online */ 95262306a36Sopenharmony_ci else if (ret.intval && 95362306a36Sopenharmony_ci !(di->chg_info.online_chg & AC_CHG)) { 95462306a36Sopenharmony_ci di->chg_info.prev_online_chg = 95562306a36Sopenharmony_ci di->chg_info.online_chg; 95662306a36Sopenharmony_ci di->chg_info.online_chg |= AC_CHG; 95762306a36Sopenharmony_ci queue_delayed_work(di->chargalg_wq, 95862306a36Sopenharmony_ci &di->chargalg_wd_work, 0); 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci break; 96162306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 96262306a36Sopenharmony_ci /* USB offline */ 96362306a36Sopenharmony_ci if (!ret.intval && 96462306a36Sopenharmony_ci (di->chg_info.online_chg & USB_CHG)) { 96562306a36Sopenharmony_ci di->chg_info.prev_online_chg = 96662306a36Sopenharmony_ci di->chg_info.online_chg; 96762306a36Sopenharmony_ci di->chg_info.online_chg &= ~USB_CHG; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci /* USB online */ 97062306a36Sopenharmony_ci else if (ret.intval && 97162306a36Sopenharmony_ci !(di->chg_info.online_chg & USB_CHG)) { 97262306a36Sopenharmony_ci di->chg_info.prev_online_chg = 97362306a36Sopenharmony_ci di->chg_info.online_chg; 97462306a36Sopenharmony_ci di->chg_info.online_chg |= USB_CHG; 97562306a36Sopenharmony_ci queue_delayed_work(di->chargalg_wq, 97662306a36Sopenharmony_ci &di->chargalg_wd_work, 0); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci break; 97962306a36Sopenharmony_ci default: 98062306a36Sopenharmony_ci break; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci break; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 98562306a36Sopenharmony_ci switch (ext->desc->type) { 98662306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 98762306a36Sopenharmony_ci break; 98862306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 98962306a36Sopenharmony_ci switch (ret.intval) { 99062306a36Sopenharmony_ci case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: 99162306a36Sopenharmony_ci di->events.mainextchnotok = true; 99262306a36Sopenharmony_ci di->events.main_thermal_prot = false; 99362306a36Sopenharmony_ci di->events.main_ovv = false; 99462306a36Sopenharmony_ci di->events.ac_wd_expired = false; 99562306a36Sopenharmony_ci break; 99662306a36Sopenharmony_ci case POWER_SUPPLY_HEALTH_DEAD: 99762306a36Sopenharmony_ci di->events.ac_wd_expired = true; 99862306a36Sopenharmony_ci di->events.mainextchnotok = false; 99962306a36Sopenharmony_ci di->events.main_ovv = false; 100062306a36Sopenharmony_ci di->events.main_thermal_prot = false; 100162306a36Sopenharmony_ci break; 100262306a36Sopenharmony_ci case POWER_SUPPLY_HEALTH_COLD: 100362306a36Sopenharmony_ci case POWER_SUPPLY_HEALTH_OVERHEAT: 100462306a36Sopenharmony_ci di->events.main_thermal_prot = true; 100562306a36Sopenharmony_ci di->events.mainextchnotok = false; 100662306a36Sopenharmony_ci di->events.main_ovv = false; 100762306a36Sopenharmony_ci di->events.ac_wd_expired = false; 100862306a36Sopenharmony_ci break; 100962306a36Sopenharmony_ci case POWER_SUPPLY_HEALTH_OVERVOLTAGE: 101062306a36Sopenharmony_ci di->events.main_ovv = true; 101162306a36Sopenharmony_ci di->events.mainextchnotok = false; 101262306a36Sopenharmony_ci di->events.main_thermal_prot = false; 101362306a36Sopenharmony_ci di->events.ac_wd_expired = false; 101462306a36Sopenharmony_ci break; 101562306a36Sopenharmony_ci case POWER_SUPPLY_HEALTH_GOOD: 101662306a36Sopenharmony_ci di->events.main_thermal_prot = false; 101762306a36Sopenharmony_ci di->events.mainextchnotok = false; 101862306a36Sopenharmony_ci di->events.main_ovv = false; 101962306a36Sopenharmony_ci di->events.ac_wd_expired = false; 102062306a36Sopenharmony_ci break; 102162306a36Sopenharmony_ci default: 102262306a36Sopenharmony_ci break; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci break; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 102762306a36Sopenharmony_ci switch (ret.intval) { 102862306a36Sopenharmony_ci case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: 102962306a36Sopenharmony_ci di->events.usbchargernotok = true; 103062306a36Sopenharmony_ci di->events.usb_thermal_prot = false; 103162306a36Sopenharmony_ci di->events.vbus_ovv = false; 103262306a36Sopenharmony_ci di->events.usb_wd_expired = false; 103362306a36Sopenharmony_ci break; 103462306a36Sopenharmony_ci case POWER_SUPPLY_HEALTH_DEAD: 103562306a36Sopenharmony_ci di->events.usb_wd_expired = true; 103662306a36Sopenharmony_ci di->events.usbchargernotok = false; 103762306a36Sopenharmony_ci di->events.usb_thermal_prot = false; 103862306a36Sopenharmony_ci di->events.vbus_ovv = false; 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci case POWER_SUPPLY_HEALTH_COLD: 104162306a36Sopenharmony_ci case POWER_SUPPLY_HEALTH_OVERHEAT: 104262306a36Sopenharmony_ci di->events.usb_thermal_prot = true; 104362306a36Sopenharmony_ci di->events.usbchargernotok = false; 104462306a36Sopenharmony_ci di->events.vbus_ovv = false; 104562306a36Sopenharmony_ci di->events.usb_wd_expired = false; 104662306a36Sopenharmony_ci break; 104762306a36Sopenharmony_ci case POWER_SUPPLY_HEALTH_OVERVOLTAGE: 104862306a36Sopenharmony_ci di->events.vbus_ovv = true; 104962306a36Sopenharmony_ci di->events.usbchargernotok = false; 105062306a36Sopenharmony_ci di->events.usb_thermal_prot = false; 105162306a36Sopenharmony_ci di->events.usb_wd_expired = false; 105262306a36Sopenharmony_ci break; 105362306a36Sopenharmony_ci case POWER_SUPPLY_HEALTH_GOOD: 105462306a36Sopenharmony_ci di->events.usbchargernotok = false; 105562306a36Sopenharmony_ci di->events.usb_thermal_prot = false; 105662306a36Sopenharmony_ci di->events.vbus_ovv = false; 105762306a36Sopenharmony_ci di->events.usb_wd_expired = false; 105862306a36Sopenharmony_ci break; 105962306a36Sopenharmony_ci default: 106062306a36Sopenharmony_ci break; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci break; 106362306a36Sopenharmony_ci default: 106462306a36Sopenharmony_ci break; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci break; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 106962306a36Sopenharmony_ci switch (ext->desc->type) { 107062306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 107162306a36Sopenharmony_ci di->batt_data.volt_uv = ret.intval; 107262306a36Sopenharmony_ci break; 107362306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 107462306a36Sopenharmony_ci di->chg_info.ac_volt_uv = ret.intval; 107562306a36Sopenharmony_ci break; 107662306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 107762306a36Sopenharmony_ci di->chg_info.usb_volt_uv = ret.intval; 107862306a36Sopenharmony_ci break; 107962306a36Sopenharmony_ci default: 108062306a36Sopenharmony_ci break; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci break; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_AVG: 108562306a36Sopenharmony_ci switch (ext->desc->type) { 108662306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 108762306a36Sopenharmony_ci /* AVG is used to indicate when we are 108862306a36Sopenharmony_ci * in CV mode */ 108962306a36Sopenharmony_ci if (ret.intval) 109062306a36Sopenharmony_ci di->events.ac_cv_active = true; 109162306a36Sopenharmony_ci else 109262306a36Sopenharmony_ci di->events.ac_cv_active = false; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci break; 109562306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 109662306a36Sopenharmony_ci /* AVG is used to indicate when we are 109762306a36Sopenharmony_ci * in CV mode */ 109862306a36Sopenharmony_ci if (ret.intval) 109962306a36Sopenharmony_ci di->events.usb_cv_active = true; 110062306a36Sopenharmony_ci else 110162306a36Sopenharmony_ci di->events.usb_cv_active = false; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci break; 110462306a36Sopenharmony_ci default: 110562306a36Sopenharmony_ci break; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci break; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 111062306a36Sopenharmony_ci switch (ext->desc->type) { 111162306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 111262306a36Sopenharmony_ci if (ret.intval) 111362306a36Sopenharmony_ci di->events.batt_unknown = false; 111462306a36Sopenharmony_ci else 111562306a36Sopenharmony_ci di->events.batt_unknown = true; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci break; 111862306a36Sopenharmony_ci default: 111962306a36Sopenharmony_ci break; 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci break; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 112462306a36Sopenharmony_ci di->batt_data.temp = ret.intval / 10; 112562306a36Sopenharmony_ci break; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 112862306a36Sopenharmony_ci switch (ext->desc->type) { 112962306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_MAINS: 113062306a36Sopenharmony_ci di->chg_info.ac_curr_ua = ret.intval; 113162306a36Sopenharmony_ci break; 113262306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 113362306a36Sopenharmony_ci di->chg_info.usb_curr_ua = ret.intval; 113462306a36Sopenharmony_ci break; 113562306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 113662306a36Sopenharmony_ci di->batt_data.inst_curr_ua = ret.intval; 113762306a36Sopenharmony_ci break; 113862306a36Sopenharmony_ci default: 113962306a36Sopenharmony_ci break; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci break; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_AVG: 114462306a36Sopenharmony_ci switch (ext->desc->type) { 114562306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_BATTERY: 114662306a36Sopenharmony_ci di->batt_data.avg_curr_ua = ret.intval; 114762306a36Sopenharmony_ci break; 114862306a36Sopenharmony_ci case POWER_SUPPLY_TYPE_USB: 114962306a36Sopenharmony_ci if (ret.intval) 115062306a36Sopenharmony_ci di->events.vbus_collapsed = true; 115162306a36Sopenharmony_ci else 115262306a36Sopenharmony_ci di->events.vbus_collapsed = false; 115362306a36Sopenharmony_ci break; 115462306a36Sopenharmony_ci default: 115562306a36Sopenharmony_ci break; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci break; 115862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CAPACITY: 115962306a36Sopenharmony_ci if (!capacity_updated) 116062306a36Sopenharmony_ci di->batt_data.percent = ret.intval; 116162306a36Sopenharmony_ci break; 116262306a36Sopenharmony_ci default: 116362306a36Sopenharmony_ci break; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci return 0; 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci/** 117062306a36Sopenharmony_ci * ab8500_chargalg_external_power_changed() - callback for power supply changes 117162306a36Sopenharmony_ci * @psy: pointer to the structure power_supply 117262306a36Sopenharmony_ci * 117362306a36Sopenharmony_ci * This function is the entry point of the pointer external_power_changed 117462306a36Sopenharmony_ci * of the structure power_supply. 117562306a36Sopenharmony_ci * This function gets executed when there is a change in any external power 117662306a36Sopenharmony_ci * supply that this driver needs to be notified of. 117762306a36Sopenharmony_ci */ 117862306a36Sopenharmony_cistatic void ab8500_chargalg_external_power_changed(struct power_supply *psy) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci struct ab8500_chargalg *di = power_supply_get_drvdata(psy); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci /* 118362306a36Sopenharmony_ci * Trigger execution of the algorithm instantly and read 118462306a36Sopenharmony_ci * all power_supply properties there instead 118562306a36Sopenharmony_ci */ 118662306a36Sopenharmony_ci if (di->chargalg_wq) 118762306a36Sopenharmony_ci queue_work(di->chargalg_wq, &di->chargalg_work); 118862306a36Sopenharmony_ci} 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci/** 119162306a36Sopenharmony_ci * ab8500_chargalg_time_to_restart() - time to restart CC/CV charging? 119262306a36Sopenharmony_ci * @di: charging algorithm state 119362306a36Sopenharmony_ci * 119462306a36Sopenharmony_ci * This checks if the voltage or capacity of the battery has fallen so 119562306a36Sopenharmony_ci * low that we need to restart the CC/CV charge cycle. 119662306a36Sopenharmony_ci */ 119762306a36Sopenharmony_cistatic bool ab8500_chargalg_time_to_restart(struct ab8500_chargalg *di) 119862306a36Sopenharmony_ci{ 119962306a36Sopenharmony_ci struct power_supply_battery_info *bi = di->bm->bi; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci /* Sanity check - these need to have some reasonable values */ 120262306a36Sopenharmony_ci if (!di->batt_data.volt_uv || !di->batt_data.percent) 120362306a36Sopenharmony_ci return false; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* Some batteries tell us at which voltage we should restart charging */ 120662306a36Sopenharmony_ci if (bi->charge_restart_voltage_uv > 0) { 120762306a36Sopenharmony_ci if (di->batt_data.volt_uv <= bi->charge_restart_voltage_uv) 120862306a36Sopenharmony_ci return true; 120962306a36Sopenharmony_ci /* Else we restart as we reach a certain capacity */ 121062306a36Sopenharmony_ci } else { 121162306a36Sopenharmony_ci if (di->batt_data.percent <= AB8500_RECHARGE_CAP) 121262306a36Sopenharmony_ci return true; 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci return false; 121662306a36Sopenharmony_ci} 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci/** 121962306a36Sopenharmony_ci * ab8500_chargalg_algorithm() - Main function for the algorithm 122062306a36Sopenharmony_ci * @di: pointer to the ab8500_chargalg structure 122162306a36Sopenharmony_ci * 122262306a36Sopenharmony_ci * This is the main control function for the charging algorithm. 122362306a36Sopenharmony_ci * It is called periodically or when something happens that will 122462306a36Sopenharmony_ci * trigger a state change 122562306a36Sopenharmony_ci */ 122662306a36Sopenharmony_cistatic void ab8500_chargalg_algorithm(struct ab8500_chargalg *di) 122762306a36Sopenharmony_ci{ 122862306a36Sopenharmony_ci struct power_supply_battery_info *bi = di->bm->bi; 122962306a36Sopenharmony_ci struct power_supply_maintenance_charge_table *mt; 123062306a36Sopenharmony_ci int charger_status; 123162306a36Sopenharmony_ci int ret; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* Collect data from all power_supply class devices */ 123462306a36Sopenharmony_ci class_for_each_device(power_supply_class, NULL, 123562306a36Sopenharmony_ci di->chargalg_psy, ab8500_chargalg_get_ext_psy_data); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci ab8500_chargalg_end_of_charge(di); 123862306a36Sopenharmony_ci ab8500_chargalg_check_temp(di); 123962306a36Sopenharmony_ci ab8500_chargalg_check_charger_voltage(di); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci charger_status = ab8500_chargalg_check_charger_connection(di); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (is_ab8500(di->parent)) { 124462306a36Sopenharmony_ci ret = ab8500_chargalg_check_charger_enable(di); 124562306a36Sopenharmony_ci if (ret < 0) 124662306a36Sopenharmony_ci dev_err(di->dev, "Checking charger is enabled error" 124762306a36Sopenharmony_ci ": Returned Value %d\n", ret); 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci /* 125162306a36Sopenharmony_ci * First check if we have a charger connected. 125262306a36Sopenharmony_ci * Also we don't allow charging of unknown batteries if configured 125362306a36Sopenharmony_ci * this way 125462306a36Sopenharmony_ci */ 125562306a36Sopenharmony_ci if (!charger_status || 125662306a36Sopenharmony_ci (di->events.batt_unknown && !di->bm->chg_unknown_bat)) { 125762306a36Sopenharmony_ci if (di->charge_state != STATE_HANDHELD) { 125862306a36Sopenharmony_ci di->events.safety_timer_expired = false; 125962306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_HANDHELD_INIT); 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci /* Safety timer expiration */ 126462306a36Sopenharmony_ci else if (di->events.safety_timer_expired) { 126562306a36Sopenharmony_ci if (di->charge_state != STATE_SAFETY_TIMER_EXPIRED) 126662306a36Sopenharmony_ci ab8500_chargalg_state_to(di, 126762306a36Sopenharmony_ci STATE_SAFETY_TIMER_EXPIRED_INIT); 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci /* 127062306a36Sopenharmony_ci * Check if any interrupts has occurred 127162306a36Sopenharmony_ci * that will prevent us from charging 127262306a36Sopenharmony_ci */ 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci /* Battery removed */ 127562306a36Sopenharmony_ci else if (di->events.batt_rem) { 127662306a36Sopenharmony_ci if (di->charge_state != STATE_BATT_REMOVED) 127762306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT); 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci /* Main or USB charger not ok. */ 128062306a36Sopenharmony_ci else if (di->events.mainextchnotok || di->events.usbchargernotok) { 128162306a36Sopenharmony_ci /* 128262306a36Sopenharmony_ci * If vbus_collapsed is set, we have to lower the charger 128362306a36Sopenharmony_ci * current, which is done in the normal state below 128462306a36Sopenharmony_ci */ 128562306a36Sopenharmony_ci if (di->charge_state != STATE_CHG_NOT_OK && 128662306a36Sopenharmony_ci !di->events.vbus_collapsed) 128762306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT); 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci /* VBUS, Main or VBAT OVV. */ 129062306a36Sopenharmony_ci else if (di->events.vbus_ovv || 129162306a36Sopenharmony_ci di->events.main_ovv || 129262306a36Sopenharmony_ci di->events.batt_ovv || 129362306a36Sopenharmony_ci !di->chg_info.usb_chg_ok || 129462306a36Sopenharmony_ci !di->chg_info.ac_chg_ok) { 129562306a36Sopenharmony_ci if (di->charge_state != STATE_OVV_PROTECT) 129662306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT); 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci /* USB Thermal, stop charging */ 129962306a36Sopenharmony_ci else if (di->events.main_thermal_prot || 130062306a36Sopenharmony_ci di->events.usb_thermal_prot) { 130162306a36Sopenharmony_ci if (di->charge_state != STATE_HW_TEMP_PROTECT) 130262306a36Sopenharmony_ci ab8500_chargalg_state_to(di, 130362306a36Sopenharmony_ci STATE_HW_TEMP_PROTECT_INIT); 130462306a36Sopenharmony_ci } 130562306a36Sopenharmony_ci /* Battery temp over/under */ 130662306a36Sopenharmony_ci else if (di->events.btemp_underover) { 130762306a36Sopenharmony_ci if (di->charge_state != STATE_TEMP_UNDEROVER) 130862306a36Sopenharmony_ci ab8500_chargalg_state_to(di, 130962306a36Sopenharmony_ci STATE_TEMP_UNDEROVER_INIT); 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci /* Watchdog expired */ 131262306a36Sopenharmony_ci else if (di->events.ac_wd_expired || 131362306a36Sopenharmony_ci di->events.usb_wd_expired) { 131462306a36Sopenharmony_ci if (di->charge_state != STATE_WD_EXPIRED) 131562306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT); 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci /* Battery temp high/low */ 131862306a36Sopenharmony_ci else if (di->events.btemp_low || di->events.btemp_high) { 131962306a36Sopenharmony_ci if (di->charge_state != STATE_TEMP_LOWHIGH) 132062306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT); 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci dev_dbg(di->dev, 132462306a36Sopenharmony_ci "[CHARGALG] Vb %d Ib_avg %d Ib_inst %d Tb %d Cap %d Maint %d " 132562306a36Sopenharmony_ci "State %s Active_chg %d Chg_status %d AC %d USB %d " 132662306a36Sopenharmony_ci "AC_online %d USB_online %d AC_CV %d USB_CV %d AC_I %d " 132762306a36Sopenharmony_ci "USB_I %d AC_Vset %d AC_Iset %d USB_Vset %d USB_Iset %d\n", 132862306a36Sopenharmony_ci di->batt_data.volt_uv, 132962306a36Sopenharmony_ci di->batt_data.avg_curr_ua, 133062306a36Sopenharmony_ci di->batt_data.inst_curr_ua, 133162306a36Sopenharmony_ci di->batt_data.temp, 133262306a36Sopenharmony_ci di->batt_data.percent, 133362306a36Sopenharmony_ci di->maintenance_chg, 133462306a36Sopenharmony_ci states[di->charge_state], 133562306a36Sopenharmony_ci di->chg_info.charger_type, 133662306a36Sopenharmony_ci di->charge_status, 133762306a36Sopenharmony_ci di->chg_info.conn_chg & AC_CHG, 133862306a36Sopenharmony_ci di->chg_info.conn_chg & USB_CHG, 133962306a36Sopenharmony_ci di->chg_info.online_chg & AC_CHG, 134062306a36Sopenharmony_ci di->chg_info.online_chg & USB_CHG, 134162306a36Sopenharmony_ci di->events.ac_cv_active, 134262306a36Sopenharmony_ci di->events.usb_cv_active, 134362306a36Sopenharmony_ci di->chg_info.ac_curr_ua, 134462306a36Sopenharmony_ci di->chg_info.usb_curr_ua, 134562306a36Sopenharmony_ci di->chg_info.ac_vset_uv, 134662306a36Sopenharmony_ci di->chg_info.ac_iset_ua, 134762306a36Sopenharmony_ci di->chg_info.usb_vset_uv, 134862306a36Sopenharmony_ci di->chg_info.usb_iset_ua); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci switch (di->charge_state) { 135162306a36Sopenharmony_ci case STATE_HANDHELD_INIT: 135262306a36Sopenharmony_ci ab8500_chargalg_stop_charging(di); 135362306a36Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; 135462306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_HANDHELD); 135562306a36Sopenharmony_ci fallthrough; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci case STATE_HANDHELD: 135862306a36Sopenharmony_ci break; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci case STATE_BATT_REMOVED_INIT: 136162306a36Sopenharmony_ci ab8500_chargalg_stop_charging(di); 136262306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_BATT_REMOVED); 136362306a36Sopenharmony_ci fallthrough; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci case STATE_BATT_REMOVED: 136662306a36Sopenharmony_ci if (!di->events.batt_rem) 136762306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 136862306a36Sopenharmony_ci break; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci case STATE_HW_TEMP_PROTECT_INIT: 137162306a36Sopenharmony_ci ab8500_chargalg_stop_charging(di); 137262306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT); 137362306a36Sopenharmony_ci fallthrough; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci case STATE_HW_TEMP_PROTECT: 137662306a36Sopenharmony_ci if (!di->events.main_thermal_prot && 137762306a36Sopenharmony_ci !di->events.usb_thermal_prot) 137862306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 137962306a36Sopenharmony_ci break; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci case STATE_OVV_PROTECT_INIT: 138262306a36Sopenharmony_ci ab8500_chargalg_stop_charging(di); 138362306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_OVV_PROTECT); 138462306a36Sopenharmony_ci fallthrough; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci case STATE_OVV_PROTECT: 138762306a36Sopenharmony_ci if (!di->events.vbus_ovv && 138862306a36Sopenharmony_ci !di->events.main_ovv && 138962306a36Sopenharmony_ci !di->events.batt_ovv && 139062306a36Sopenharmony_ci di->chg_info.usb_chg_ok && 139162306a36Sopenharmony_ci di->chg_info.ac_chg_ok) 139262306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 139362306a36Sopenharmony_ci break; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci case STATE_CHG_NOT_OK_INIT: 139662306a36Sopenharmony_ci ab8500_chargalg_stop_charging(di); 139762306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK); 139862306a36Sopenharmony_ci fallthrough; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci case STATE_CHG_NOT_OK: 140162306a36Sopenharmony_ci if (!di->events.mainextchnotok && 140262306a36Sopenharmony_ci !di->events.usbchargernotok) 140362306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 140462306a36Sopenharmony_ci break; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci case STATE_SAFETY_TIMER_EXPIRED_INIT: 140762306a36Sopenharmony_ci ab8500_chargalg_stop_charging(di); 140862306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED); 140962306a36Sopenharmony_ci fallthrough; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci case STATE_SAFETY_TIMER_EXPIRED: 141262306a36Sopenharmony_ci /* We exit this state when charger is removed */ 141362306a36Sopenharmony_ci break; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci case STATE_NORMAL_INIT: 141662306a36Sopenharmony_ci if (bi->constant_charge_current_max_ua == 0) 141762306a36Sopenharmony_ci /* "charging" with 0 uA */ 141862306a36Sopenharmony_ci ab8500_chargalg_stop_charging(di); 141962306a36Sopenharmony_ci else { 142062306a36Sopenharmony_ci ab8500_chargalg_start_charging(di, 142162306a36Sopenharmony_ci bi->constant_charge_voltage_max_uv, 142262306a36Sopenharmony_ci bi->constant_charge_current_max_ua); 142362306a36Sopenharmony_ci } 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL); 142662306a36Sopenharmony_ci ab8500_chargalg_start_safety_timer(di); 142762306a36Sopenharmony_ci ab8500_chargalg_stop_maintenance_timer(di); 142862306a36Sopenharmony_ci init_maxim_chg_curr(di); 142962306a36Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_CHARGING; 143062306a36Sopenharmony_ci di->eoc_cnt = 0; 143162306a36Sopenharmony_ci di->maintenance_chg = false; 143262306a36Sopenharmony_ci power_supply_changed(di->chargalg_psy); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci break; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci case STATE_NORMAL: 143762306a36Sopenharmony_ci handle_maxim_chg_curr(di); 143862306a36Sopenharmony_ci if (di->charge_status == POWER_SUPPLY_STATUS_FULL && 143962306a36Sopenharmony_ci di->maintenance_chg) { 144062306a36Sopenharmony_ci /* 144162306a36Sopenharmony_ci * The battery is fully charged, check if we support 144262306a36Sopenharmony_ci * maintenance charging else go back to waiting for 144362306a36Sopenharmony_ci * the recharge voltage limit. 144462306a36Sopenharmony_ci */ 144562306a36Sopenharmony_ci if (!power_supply_supports_maintenance_charging(bi)) 144662306a36Sopenharmony_ci ab8500_chargalg_state_to(di, 144762306a36Sopenharmony_ci STATE_WAIT_FOR_RECHARGE_INIT); 144862306a36Sopenharmony_ci else 144962306a36Sopenharmony_ci ab8500_chargalg_state_to(di, 145062306a36Sopenharmony_ci STATE_MAINTENANCE_A_INIT); 145162306a36Sopenharmony_ci } 145262306a36Sopenharmony_ci break; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci /* This state will be used when the maintenance state is disabled */ 145562306a36Sopenharmony_ci case STATE_WAIT_FOR_RECHARGE_INIT: 145662306a36Sopenharmony_ci ab8500_chargalg_hold_charging(di); 145762306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE); 145862306a36Sopenharmony_ci fallthrough; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci case STATE_WAIT_FOR_RECHARGE: 146162306a36Sopenharmony_ci if (ab8500_chargalg_time_to_restart(di)) 146262306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 146362306a36Sopenharmony_ci break; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci case STATE_MAINTENANCE_A_INIT: 146662306a36Sopenharmony_ci mt = power_supply_get_maintenance_charging_setting(bi, 0); 146762306a36Sopenharmony_ci if (!mt) { 146862306a36Sopenharmony_ci /* No maintenance A state, go back to normal */ 146962306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 147062306a36Sopenharmony_ci power_supply_changed(di->chargalg_psy); 147162306a36Sopenharmony_ci break; 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ci ab8500_chargalg_stop_safety_timer(di); 147462306a36Sopenharmony_ci ab8500_chargalg_start_maintenance_timer(di, 147562306a36Sopenharmony_ci mt->charge_safety_timer_minutes); 147662306a36Sopenharmony_ci ab8500_chargalg_start_charging(di, 147762306a36Sopenharmony_ci mt->charge_voltage_max_uv, 147862306a36Sopenharmony_ci mt->charge_current_max_ua); 147962306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_MAINTENANCE_A); 148062306a36Sopenharmony_ci power_supply_changed(di->chargalg_psy); 148162306a36Sopenharmony_ci fallthrough; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci case STATE_MAINTENANCE_A: 148462306a36Sopenharmony_ci if (di->events.maintenance_timer_expired) { 148562306a36Sopenharmony_ci ab8500_chargalg_stop_maintenance_timer(di); 148662306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT); 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci /* 148962306a36Sopenharmony_ci * This happens if the voltage drops too quickly during 149062306a36Sopenharmony_ci * maintenance charging, especially in older batteries. 149162306a36Sopenharmony_ci */ 149262306a36Sopenharmony_ci if (ab8500_chargalg_time_to_restart(di)) { 149362306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 149462306a36Sopenharmony_ci dev_info(di->dev, "restarted charging from maintenance state A - battery getting old?\n"); 149562306a36Sopenharmony_ci } 149662306a36Sopenharmony_ci break; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci case STATE_MAINTENANCE_B_INIT: 149962306a36Sopenharmony_ci mt = power_supply_get_maintenance_charging_setting(bi, 1); 150062306a36Sopenharmony_ci if (!mt) { 150162306a36Sopenharmony_ci /* No maintenance B state, go back to normal */ 150262306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 150362306a36Sopenharmony_ci power_supply_changed(di->chargalg_psy); 150462306a36Sopenharmony_ci break; 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci ab8500_chargalg_start_maintenance_timer(di, 150762306a36Sopenharmony_ci mt->charge_safety_timer_minutes); 150862306a36Sopenharmony_ci ab8500_chargalg_start_charging(di, 150962306a36Sopenharmony_ci mt->charge_voltage_max_uv, 151062306a36Sopenharmony_ci mt->charge_current_max_ua); 151162306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B); 151262306a36Sopenharmony_ci power_supply_changed(di->chargalg_psy); 151362306a36Sopenharmony_ci fallthrough; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci case STATE_MAINTENANCE_B: 151662306a36Sopenharmony_ci if (di->events.maintenance_timer_expired) { 151762306a36Sopenharmony_ci ab8500_chargalg_stop_maintenance_timer(di); 151862306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci /* 152162306a36Sopenharmony_ci * This happens if the voltage drops too quickly during 152262306a36Sopenharmony_ci * maintenance charging, especially in older batteries. 152362306a36Sopenharmony_ci */ 152462306a36Sopenharmony_ci if (ab8500_chargalg_time_to_restart(di)) { 152562306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 152662306a36Sopenharmony_ci dev_info(di->dev, "restarted charging from maintenance state B - battery getting old?\n"); 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci break; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci case STATE_TEMP_LOWHIGH_INIT: 153162306a36Sopenharmony_ci if (di->events.btemp_low) { 153262306a36Sopenharmony_ci ab8500_chargalg_start_charging(di, 153362306a36Sopenharmony_ci bi->alert_low_temp_charge_voltage_uv, 153462306a36Sopenharmony_ci bi->alert_low_temp_charge_current_ua); 153562306a36Sopenharmony_ci } else if (di->events.btemp_high) { 153662306a36Sopenharmony_ci ab8500_chargalg_start_charging(di, 153762306a36Sopenharmony_ci bi->alert_high_temp_charge_voltage_uv, 153862306a36Sopenharmony_ci bi->alert_high_temp_charge_current_ua); 153962306a36Sopenharmony_ci } else { 154062306a36Sopenharmony_ci dev_err(di->dev, "neither low or high temp event occurred\n"); 154162306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 154262306a36Sopenharmony_ci break; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci ab8500_chargalg_stop_maintenance_timer(di); 154562306a36Sopenharmony_ci di->charge_status = POWER_SUPPLY_STATUS_CHARGING; 154662306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH); 154762306a36Sopenharmony_ci power_supply_changed(di->chargalg_psy); 154862306a36Sopenharmony_ci fallthrough; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci case STATE_TEMP_LOWHIGH: 155162306a36Sopenharmony_ci if (!di->events.btemp_low && !di->events.btemp_high) 155262306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 155362306a36Sopenharmony_ci break; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci case STATE_WD_EXPIRED_INIT: 155662306a36Sopenharmony_ci ab8500_chargalg_stop_charging(di); 155762306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_WD_EXPIRED); 155862306a36Sopenharmony_ci fallthrough; 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci case STATE_WD_EXPIRED: 156162306a36Sopenharmony_ci if (!di->events.ac_wd_expired && 156262306a36Sopenharmony_ci !di->events.usb_wd_expired) 156362306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 156462306a36Sopenharmony_ci break; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci case STATE_TEMP_UNDEROVER_INIT: 156762306a36Sopenharmony_ci ab8500_chargalg_stop_charging(di); 156862306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_TEMP_UNDEROVER); 156962306a36Sopenharmony_ci fallthrough; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci case STATE_TEMP_UNDEROVER: 157262306a36Sopenharmony_ci if (!di->events.btemp_underover) 157362306a36Sopenharmony_ci ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 157462306a36Sopenharmony_ci break; 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci /* Start charging directly if the new state is a charge state */ 157862306a36Sopenharmony_ci if (di->charge_state == STATE_NORMAL_INIT || 157962306a36Sopenharmony_ci di->charge_state == STATE_MAINTENANCE_A_INIT || 158062306a36Sopenharmony_ci di->charge_state == STATE_MAINTENANCE_B_INIT) 158162306a36Sopenharmony_ci queue_work(di->chargalg_wq, &di->chargalg_work); 158262306a36Sopenharmony_ci} 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci/** 158562306a36Sopenharmony_ci * ab8500_chargalg_periodic_work() - Periodic work for the algorithm 158662306a36Sopenharmony_ci * @work: pointer to the work_struct structure 158762306a36Sopenharmony_ci * 158862306a36Sopenharmony_ci * Work queue function for the charging algorithm 158962306a36Sopenharmony_ci */ 159062306a36Sopenharmony_cistatic void ab8500_chargalg_periodic_work(struct work_struct *work) 159162306a36Sopenharmony_ci{ 159262306a36Sopenharmony_ci struct ab8500_chargalg *di = container_of(work, 159362306a36Sopenharmony_ci struct ab8500_chargalg, chargalg_periodic_work.work); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci ab8500_chargalg_algorithm(di); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci /* 159862306a36Sopenharmony_ci * If a charger is connected then the battery has to be monitored 159962306a36Sopenharmony_ci * frequently, else the work can be delayed. 160062306a36Sopenharmony_ci */ 160162306a36Sopenharmony_ci if (di->chg_info.conn_chg) 160262306a36Sopenharmony_ci queue_delayed_work(di->chargalg_wq, 160362306a36Sopenharmony_ci &di->chargalg_periodic_work, 160462306a36Sopenharmony_ci di->bm->interval_charging * HZ); 160562306a36Sopenharmony_ci else 160662306a36Sopenharmony_ci queue_delayed_work(di->chargalg_wq, 160762306a36Sopenharmony_ci &di->chargalg_periodic_work, 160862306a36Sopenharmony_ci di->bm->interval_not_charging * HZ); 160962306a36Sopenharmony_ci} 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci/** 161262306a36Sopenharmony_ci * ab8500_chargalg_wd_work() - periodic work to kick the charger watchdog 161362306a36Sopenharmony_ci * @work: pointer to the work_struct structure 161462306a36Sopenharmony_ci * 161562306a36Sopenharmony_ci * Work queue function for kicking the charger watchdog 161662306a36Sopenharmony_ci */ 161762306a36Sopenharmony_cistatic void ab8500_chargalg_wd_work(struct work_struct *work) 161862306a36Sopenharmony_ci{ 161962306a36Sopenharmony_ci int ret; 162062306a36Sopenharmony_ci struct ab8500_chargalg *di = container_of(work, 162162306a36Sopenharmony_ci struct ab8500_chargalg, chargalg_wd_work.work); 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci ret = ab8500_chargalg_kick_watchdog(di); 162462306a36Sopenharmony_ci if (ret < 0) 162562306a36Sopenharmony_ci dev_err(di->dev, "failed to kick watchdog\n"); 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci queue_delayed_work(di->chargalg_wq, 162862306a36Sopenharmony_ci &di->chargalg_wd_work, CHG_WD_INTERVAL); 162962306a36Sopenharmony_ci} 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci/** 163262306a36Sopenharmony_ci * ab8500_chargalg_work() - Work to run the charging algorithm instantly 163362306a36Sopenharmony_ci * @work: pointer to the work_struct structure 163462306a36Sopenharmony_ci * 163562306a36Sopenharmony_ci * Work queue function for calling the charging algorithm 163662306a36Sopenharmony_ci */ 163762306a36Sopenharmony_cistatic void ab8500_chargalg_work(struct work_struct *work) 163862306a36Sopenharmony_ci{ 163962306a36Sopenharmony_ci struct ab8500_chargalg *di = container_of(work, 164062306a36Sopenharmony_ci struct ab8500_chargalg, chargalg_work); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci ab8500_chargalg_algorithm(di); 164362306a36Sopenharmony_ci} 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci/** 164662306a36Sopenharmony_ci * ab8500_chargalg_get_property() - get the chargalg properties 164762306a36Sopenharmony_ci * @psy: pointer to the power_supply structure 164862306a36Sopenharmony_ci * @psp: pointer to the power_supply_property structure 164962306a36Sopenharmony_ci * @val: pointer to the power_supply_propval union 165062306a36Sopenharmony_ci * 165162306a36Sopenharmony_ci * This function gets called when an application tries to get the 165262306a36Sopenharmony_ci * chargalg properties by reading the sysfs files. 165362306a36Sopenharmony_ci * status: charging/discharging/full/unknown 165462306a36Sopenharmony_ci * health: health of the battery 165562306a36Sopenharmony_ci * Returns error code in case of failure else 0 on success 165662306a36Sopenharmony_ci */ 165762306a36Sopenharmony_cistatic int ab8500_chargalg_get_property(struct power_supply *psy, 165862306a36Sopenharmony_ci enum power_supply_property psp, 165962306a36Sopenharmony_ci union power_supply_propval *val) 166062306a36Sopenharmony_ci{ 166162306a36Sopenharmony_ci struct ab8500_chargalg *di = power_supply_get_drvdata(psy); 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci switch (psp) { 166462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 166562306a36Sopenharmony_ci val->intval = di->charge_status; 166662306a36Sopenharmony_ci break; 166762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 166862306a36Sopenharmony_ci if (di->events.batt_ovv) { 166962306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 167062306a36Sopenharmony_ci } else if (di->events.btemp_underover) { 167162306a36Sopenharmony_ci if (di->batt_data.temp <= di->bm->bi->temp_min) 167262306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_COLD; 167362306a36Sopenharmony_ci else 167462306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 167562306a36Sopenharmony_ci } else if (di->charge_state == STATE_SAFETY_TIMER_EXPIRED || 167662306a36Sopenharmony_ci di->charge_state == STATE_SAFETY_TIMER_EXPIRED_INIT) { 167762306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 167862306a36Sopenharmony_ci } else { 167962306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_GOOD; 168062306a36Sopenharmony_ci } 168162306a36Sopenharmony_ci break; 168262306a36Sopenharmony_ci default: 168362306a36Sopenharmony_ci return -EINVAL; 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci return 0; 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_cistatic int __maybe_unused ab8500_chargalg_resume(struct device *dev) 168962306a36Sopenharmony_ci{ 169062306a36Sopenharmony_ci struct ab8500_chargalg *di = dev_get_drvdata(dev); 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci /* Kick charger watchdog if charging (any charger online) */ 169362306a36Sopenharmony_ci if (di->chg_info.online_chg) 169462306a36Sopenharmony_ci queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0); 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci /* 169762306a36Sopenharmony_ci * Run the charging algorithm directly to be sure we don't 169862306a36Sopenharmony_ci * do it too seldom 169962306a36Sopenharmony_ci */ 170062306a36Sopenharmony_ci queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0); 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci return 0; 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_cistatic int __maybe_unused ab8500_chargalg_suspend(struct device *dev) 170662306a36Sopenharmony_ci{ 170762306a36Sopenharmony_ci struct ab8500_chargalg *di = dev_get_drvdata(dev); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci if (di->chg_info.online_chg) 171062306a36Sopenharmony_ci cancel_delayed_work_sync(&di->chargalg_wd_work); 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci cancel_delayed_work_sync(&di->chargalg_periodic_work); 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci return 0; 171562306a36Sopenharmony_ci} 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_cistatic char *supply_interface[] = { 171862306a36Sopenharmony_ci "ab8500_fg", 171962306a36Sopenharmony_ci}; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_cistatic const struct power_supply_desc ab8500_chargalg_desc = { 172262306a36Sopenharmony_ci .name = "ab8500_chargalg", 172362306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_UNKNOWN, 172462306a36Sopenharmony_ci .properties = ab8500_chargalg_props, 172562306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(ab8500_chargalg_props), 172662306a36Sopenharmony_ci .get_property = ab8500_chargalg_get_property, 172762306a36Sopenharmony_ci .external_power_changed = ab8500_chargalg_external_power_changed, 172862306a36Sopenharmony_ci}; 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_cistatic int ab8500_chargalg_bind(struct device *dev, struct device *master, 173162306a36Sopenharmony_ci void *data) 173262306a36Sopenharmony_ci{ 173362306a36Sopenharmony_ci struct ab8500_chargalg *di = dev_get_drvdata(dev); 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci /* Create a work queue for the chargalg */ 173662306a36Sopenharmony_ci di->chargalg_wq = alloc_ordered_workqueue("ab8500_chargalg_wq", 173762306a36Sopenharmony_ci WQ_MEM_RECLAIM); 173862306a36Sopenharmony_ci if (di->chargalg_wq == NULL) { 173962306a36Sopenharmony_ci dev_err(di->dev, "failed to create work queue\n"); 174062306a36Sopenharmony_ci return -ENOMEM; 174162306a36Sopenharmony_ci } 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci /* Run the charging algorithm */ 174462306a36Sopenharmony_ci queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0); 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci return 0; 174762306a36Sopenharmony_ci} 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_cistatic void ab8500_chargalg_unbind(struct device *dev, struct device *master, 175062306a36Sopenharmony_ci void *data) 175162306a36Sopenharmony_ci{ 175262306a36Sopenharmony_ci struct ab8500_chargalg *di = dev_get_drvdata(dev); 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci /* Stop all timers and work */ 175562306a36Sopenharmony_ci hrtimer_cancel(&di->safety_timer); 175662306a36Sopenharmony_ci hrtimer_cancel(&di->maintenance_timer); 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci cancel_delayed_work_sync(&di->chargalg_periodic_work); 175962306a36Sopenharmony_ci cancel_delayed_work_sync(&di->chargalg_wd_work); 176062306a36Sopenharmony_ci cancel_work_sync(&di->chargalg_work); 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci /* Delete the work queue */ 176362306a36Sopenharmony_ci destroy_workqueue(di->chargalg_wq); 176462306a36Sopenharmony_ci} 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_cistatic const struct component_ops ab8500_chargalg_component_ops = { 176762306a36Sopenharmony_ci .bind = ab8500_chargalg_bind, 176862306a36Sopenharmony_ci .unbind = ab8500_chargalg_unbind, 176962306a36Sopenharmony_ci}; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_cistatic int ab8500_chargalg_probe(struct platform_device *pdev) 177262306a36Sopenharmony_ci{ 177362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 177462306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 177562306a36Sopenharmony_ci struct ab8500_chargalg *di; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); 177862306a36Sopenharmony_ci if (!di) 177962306a36Sopenharmony_ci return -ENOMEM; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci di->bm = &ab8500_bm_data; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci /* get device struct and parent */ 178462306a36Sopenharmony_ci di->dev = dev; 178562306a36Sopenharmony_ci di->parent = dev_get_drvdata(pdev->dev.parent); 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci psy_cfg.supplied_to = supply_interface; 178862306a36Sopenharmony_ci psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); 178962306a36Sopenharmony_ci psy_cfg.drv_data = di; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci /* Initilialize safety timer */ 179262306a36Sopenharmony_ci hrtimer_init(&di->safety_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 179362306a36Sopenharmony_ci di->safety_timer.function = ab8500_chargalg_safety_timer_expired; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci /* Initilialize maintenance timer */ 179662306a36Sopenharmony_ci hrtimer_init(&di->maintenance_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 179762306a36Sopenharmony_ci di->maintenance_timer.function = 179862306a36Sopenharmony_ci ab8500_chargalg_maintenance_timer_expired; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci /* Init work for chargalg */ 180162306a36Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->chargalg_periodic_work, 180262306a36Sopenharmony_ci ab8500_chargalg_periodic_work); 180362306a36Sopenharmony_ci INIT_DEFERRABLE_WORK(&di->chargalg_wd_work, 180462306a36Sopenharmony_ci ab8500_chargalg_wd_work); 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci /* Init work for chargalg */ 180762306a36Sopenharmony_ci INIT_WORK(&di->chargalg_work, ab8500_chargalg_work); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci /* To detect charger at startup */ 181062306a36Sopenharmony_ci di->chg_info.prev_conn_chg = -1; 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci /* Register chargalg power supply class */ 181362306a36Sopenharmony_ci di->chargalg_psy = devm_power_supply_register(di->dev, 181462306a36Sopenharmony_ci &ab8500_chargalg_desc, 181562306a36Sopenharmony_ci &psy_cfg); 181662306a36Sopenharmony_ci if (IS_ERR(di->chargalg_psy)) { 181762306a36Sopenharmony_ci dev_err(di->dev, "failed to register chargalg psy\n"); 181862306a36Sopenharmony_ci return PTR_ERR(di->chargalg_psy); 181962306a36Sopenharmony_ci } 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci platform_set_drvdata(pdev, di); 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci dev_info(di->dev, "probe success\n"); 182462306a36Sopenharmony_ci return component_add(dev, &ab8500_chargalg_component_ops); 182562306a36Sopenharmony_ci} 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_cistatic int ab8500_chargalg_remove(struct platform_device *pdev) 182862306a36Sopenharmony_ci{ 182962306a36Sopenharmony_ci component_del(&pdev->dev, &ab8500_chargalg_component_ops); 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci return 0; 183262306a36Sopenharmony_ci} 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ab8500_chargalg_pm_ops, ab8500_chargalg_suspend, ab8500_chargalg_resume); 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_cistatic const struct of_device_id ab8500_chargalg_match[] = { 183762306a36Sopenharmony_ci { .compatible = "stericsson,ab8500-chargalg", }, 183862306a36Sopenharmony_ci { }, 183962306a36Sopenharmony_ci}; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_cistruct platform_driver ab8500_chargalg_driver = { 184262306a36Sopenharmony_ci .probe = ab8500_chargalg_probe, 184362306a36Sopenharmony_ci .remove = ab8500_chargalg_remove, 184462306a36Sopenharmony_ci .driver = { 184562306a36Sopenharmony_ci .name = "ab8500_chargalg", 184662306a36Sopenharmony_ci .of_match_table = ab8500_chargalg_match, 184762306a36Sopenharmony_ci .pm = &ab8500_chargalg_pm_ops, 184862306a36Sopenharmony_ci }, 184962306a36Sopenharmony_ci}; 185062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 185162306a36Sopenharmony_ciMODULE_AUTHOR("Johan Palsson, Karl Komierowski"); 185262306a36Sopenharmony_ciMODULE_ALIAS("platform:ab8500-chargalg"); 185362306a36Sopenharmony_ciMODULE_DESCRIPTION("ab8500 battery charging algorithm"); 1854