1c5b64a99SLinus Walleij // SPDX-License-Identifier: GPL-2.0-only 2c5b64a99SLinus Walleij /* 3c5b64a99SLinus Walleij * Copyright (C) ST-Ericsson SA 2012 4c5b64a99SLinus Walleij * Copyright (c) 2012 Sony Mobile Communications AB 5c5b64a99SLinus Walleij * 6c5b64a99SLinus Walleij * Charging algorithm driver for AB8500 7c5b64a99SLinus Walleij * 8c5b64a99SLinus Walleij * Authors: 9c5b64a99SLinus Walleij * Johan Palsson <johan.palsson@stericsson.com> 10c5b64a99SLinus Walleij * Karl Komierowski <karl.komierowski@stericsson.com> 11c5b64a99SLinus Walleij * Arun R Murthy <arun.murthy@stericsson.com> 12c5b64a99SLinus Walleij * Author: Imre Sunyi <imre.sunyi@sonymobile.com> 13c5b64a99SLinus Walleij */ 14c5b64a99SLinus Walleij 15c5b64a99SLinus Walleij #include <linux/init.h> 16c5b64a99SLinus Walleij #include <linux/module.h> 17c5b64a99SLinus Walleij #include <linux/device.h> 18c5b64a99SLinus Walleij #include <linux/component.h> 19c5b64a99SLinus Walleij #include <linux/hrtimer.h> 20c5b64a99SLinus Walleij #include <linux/interrupt.h> 21c5b64a99SLinus Walleij #include <linux/delay.h> 22c5b64a99SLinus Walleij #include <linux/slab.h> 23c5b64a99SLinus Walleij #include <linux/platform_device.h> 24c5b64a99SLinus Walleij #include <linux/power_supply.h> 25c5b64a99SLinus Walleij #include <linux/completion.h> 26c5b64a99SLinus Walleij #include <linux/workqueue.h> 27c5b64a99SLinus Walleij #include <linux/kobject.h> 28c5b64a99SLinus Walleij #include <linux/of.h> 29c5b64a99SLinus Walleij #include <linux/mfd/core.h> 30c5b64a99SLinus Walleij #include <linux/mfd/abx500.h> 31c5b64a99SLinus Walleij #include <linux/mfd/abx500/ab8500.h> 32c5b64a99SLinus Walleij #include <linux/notifier.h> 33c5b64a99SLinus Walleij 34c5b64a99SLinus Walleij #include "ab8500-bm.h" 35c5b64a99SLinus Walleij #include "ab8500-chargalg.h" 36c5b64a99SLinus Walleij 37c5b64a99SLinus Walleij /* Watchdog kick interval */ 38c5b64a99SLinus Walleij #define CHG_WD_INTERVAL (6 * HZ) 39c5b64a99SLinus Walleij 40c5b64a99SLinus Walleij /* End-of-charge criteria counter */ 41c5b64a99SLinus Walleij #define EOC_COND_CNT 10 42c5b64a99SLinus Walleij 43c5b64a99SLinus Walleij /* One hour expressed in seconds */ 44c5b64a99SLinus Walleij #define ONE_HOUR_IN_SECONDS 3600 45c5b64a99SLinus Walleij 46c5b64a99SLinus Walleij /* Five minutes expressed in seconds */ 47c5b64a99SLinus Walleij #define FIVE_MINUTES_IN_SECONDS 300 48c5b64a99SLinus Walleij 49c5b64a99SLinus Walleij #define CHARGALG_CURR_STEP_LOW 0 50c5b64a99SLinus Walleij #define CHARGALG_CURR_STEP_HIGH 100 51c5b64a99SLinus Walleij 52c5b64a99SLinus Walleij enum ab8500_chargers { 53c5b64a99SLinus Walleij NO_CHG, 54c5b64a99SLinus Walleij AC_CHG, 55c5b64a99SLinus Walleij USB_CHG, 56c5b64a99SLinus Walleij }; 57c5b64a99SLinus Walleij 58c5b64a99SLinus Walleij struct ab8500_chargalg_charger_info { 59c5b64a99SLinus Walleij enum ab8500_chargers conn_chg; 60c5b64a99SLinus Walleij enum ab8500_chargers prev_conn_chg; 61c5b64a99SLinus Walleij enum ab8500_chargers online_chg; 62c5b64a99SLinus Walleij enum ab8500_chargers prev_online_chg; 63c5b64a99SLinus Walleij enum ab8500_chargers charger_type; 64c5b64a99SLinus Walleij bool usb_chg_ok; 65c5b64a99SLinus Walleij bool ac_chg_ok; 66c5b64a99SLinus Walleij int usb_volt; 67c5b64a99SLinus Walleij int usb_curr; 68c5b64a99SLinus Walleij int ac_volt; 69c5b64a99SLinus Walleij int ac_curr; 70c5b64a99SLinus Walleij int usb_vset; 71c5b64a99SLinus Walleij int usb_iset; 72c5b64a99SLinus Walleij int ac_vset; 73c5b64a99SLinus Walleij int ac_iset; 74c5b64a99SLinus Walleij }; 75c5b64a99SLinus Walleij 76c5b64a99SLinus Walleij struct ab8500_chargalg_suspension_status { 77c5b64a99SLinus Walleij bool suspended_change; 78c5b64a99SLinus Walleij bool ac_suspended; 79c5b64a99SLinus Walleij bool usb_suspended; 80c5b64a99SLinus Walleij }; 81c5b64a99SLinus Walleij 82c5b64a99SLinus Walleij struct ab8500_chargalg_current_step_status { 83c5b64a99SLinus Walleij bool curr_step_change; 84c5b64a99SLinus Walleij int curr_step; 85c5b64a99SLinus Walleij }; 86c5b64a99SLinus Walleij 87c5b64a99SLinus Walleij struct ab8500_chargalg_battery_data { 88c5b64a99SLinus Walleij int temp; 89c5b64a99SLinus Walleij int volt; 90c5b64a99SLinus Walleij int avg_curr; 91c5b64a99SLinus Walleij int inst_curr; 92c5b64a99SLinus Walleij int percent; 93c5b64a99SLinus Walleij }; 94c5b64a99SLinus Walleij 95c5b64a99SLinus Walleij enum ab8500_chargalg_states { 96c5b64a99SLinus Walleij STATE_HANDHELD_INIT, 97c5b64a99SLinus Walleij STATE_HANDHELD, 98c5b64a99SLinus Walleij STATE_CHG_NOT_OK_INIT, 99c5b64a99SLinus Walleij STATE_CHG_NOT_OK, 100c5b64a99SLinus Walleij STATE_HW_TEMP_PROTECT_INIT, 101c5b64a99SLinus Walleij STATE_HW_TEMP_PROTECT, 102c5b64a99SLinus Walleij STATE_NORMAL_INIT, 103c5b64a99SLinus Walleij STATE_NORMAL, 104c5b64a99SLinus Walleij STATE_WAIT_FOR_RECHARGE_INIT, 105c5b64a99SLinus Walleij STATE_WAIT_FOR_RECHARGE, 106c5b64a99SLinus Walleij STATE_MAINTENANCE_A_INIT, 107c5b64a99SLinus Walleij STATE_MAINTENANCE_A, 108c5b64a99SLinus Walleij STATE_MAINTENANCE_B_INIT, 109c5b64a99SLinus Walleij STATE_MAINTENANCE_B, 110c5b64a99SLinus Walleij STATE_TEMP_UNDEROVER_INIT, 111c5b64a99SLinus Walleij STATE_TEMP_UNDEROVER, 112c5b64a99SLinus Walleij STATE_TEMP_LOWHIGH_INIT, 113c5b64a99SLinus Walleij STATE_TEMP_LOWHIGH, 114c5b64a99SLinus Walleij STATE_SUSPENDED_INIT, 115c5b64a99SLinus Walleij STATE_SUSPENDED, 116c5b64a99SLinus Walleij STATE_OVV_PROTECT_INIT, 117c5b64a99SLinus Walleij STATE_OVV_PROTECT, 118c5b64a99SLinus Walleij STATE_SAFETY_TIMER_EXPIRED_INIT, 119c5b64a99SLinus Walleij STATE_SAFETY_TIMER_EXPIRED, 120c5b64a99SLinus Walleij STATE_BATT_REMOVED_INIT, 121c5b64a99SLinus Walleij STATE_BATT_REMOVED, 122c5b64a99SLinus Walleij STATE_WD_EXPIRED_INIT, 123c5b64a99SLinus Walleij STATE_WD_EXPIRED, 124c5b64a99SLinus Walleij }; 125c5b64a99SLinus Walleij 126c5b64a99SLinus Walleij static const char *states[] = { 127c5b64a99SLinus Walleij "HANDHELD_INIT", 128c5b64a99SLinus Walleij "HANDHELD", 129c5b64a99SLinus Walleij "CHG_NOT_OK_INIT", 130c5b64a99SLinus Walleij "CHG_NOT_OK", 131c5b64a99SLinus Walleij "HW_TEMP_PROTECT_INIT", 132c5b64a99SLinus Walleij "HW_TEMP_PROTECT", 133c5b64a99SLinus Walleij "NORMAL_INIT", 134c5b64a99SLinus Walleij "NORMAL", 135c5b64a99SLinus Walleij "WAIT_FOR_RECHARGE_INIT", 136c5b64a99SLinus Walleij "WAIT_FOR_RECHARGE", 137c5b64a99SLinus Walleij "MAINTENANCE_A_INIT", 138c5b64a99SLinus Walleij "MAINTENANCE_A", 139c5b64a99SLinus Walleij "MAINTENANCE_B_INIT", 140c5b64a99SLinus Walleij "MAINTENANCE_B", 141c5b64a99SLinus Walleij "TEMP_UNDEROVER_INIT", 142c5b64a99SLinus Walleij "TEMP_UNDEROVER", 143c5b64a99SLinus Walleij "TEMP_LOWHIGH_INIT", 144c5b64a99SLinus Walleij "TEMP_LOWHIGH", 145c5b64a99SLinus Walleij "SUSPENDED_INIT", 146c5b64a99SLinus Walleij "SUSPENDED", 147c5b64a99SLinus Walleij "OVV_PROTECT_INIT", 148c5b64a99SLinus Walleij "OVV_PROTECT", 149c5b64a99SLinus Walleij "SAFETY_TIMER_EXPIRED_INIT", 150c5b64a99SLinus Walleij "SAFETY_TIMER_EXPIRED", 151c5b64a99SLinus Walleij "BATT_REMOVED_INIT", 152c5b64a99SLinus Walleij "BATT_REMOVED", 153c5b64a99SLinus Walleij "WD_EXPIRED_INIT", 154c5b64a99SLinus Walleij "WD_EXPIRED", 155c5b64a99SLinus Walleij }; 156c5b64a99SLinus Walleij 157c5b64a99SLinus Walleij struct ab8500_chargalg_events { 158c5b64a99SLinus Walleij bool batt_unknown; 159c5b64a99SLinus Walleij bool mainextchnotok; 160c5b64a99SLinus Walleij bool batt_ovv; 161c5b64a99SLinus Walleij bool batt_rem; 162c5b64a99SLinus Walleij bool btemp_underover; 163c5b64a99SLinus Walleij bool btemp_lowhigh; 164c5b64a99SLinus Walleij bool main_thermal_prot; 165c5b64a99SLinus Walleij bool usb_thermal_prot; 166c5b64a99SLinus Walleij bool main_ovv; 167c5b64a99SLinus Walleij bool vbus_ovv; 168c5b64a99SLinus Walleij bool usbchargernotok; 169c5b64a99SLinus Walleij bool safety_timer_expired; 170c5b64a99SLinus Walleij bool maintenance_timer_expired; 171c5b64a99SLinus Walleij bool ac_wd_expired; 172c5b64a99SLinus Walleij bool usb_wd_expired; 173c5b64a99SLinus Walleij bool ac_cv_active; 174c5b64a99SLinus Walleij bool usb_cv_active; 175c5b64a99SLinus Walleij bool vbus_collapsed; 176c5b64a99SLinus Walleij }; 177c5b64a99SLinus Walleij 178c5b64a99SLinus Walleij /** 179c5b64a99SLinus Walleij * struct ab8500_charge_curr_maximization - Charger maximization parameters 180c5b64a99SLinus Walleij * @original_iset: the non optimized/maximised charger current 181c5b64a99SLinus Walleij * @current_iset: the charging current used at this moment 182c5b64a99SLinus Walleij * @test_delta_i: the delta between the current we want to charge and the 183c5b64a99SLinus Walleij current that is really going into the battery 184c5b64a99SLinus Walleij * @condition_cnt: number of iterations needed before a new charger current 185c5b64a99SLinus Walleij is set 186c5b64a99SLinus Walleij * @max_current: maximum charger current 187c5b64a99SLinus Walleij * @wait_cnt: to avoid too fast current step down in case of charger 188c5b64a99SLinus Walleij * voltage collapse, we insert this delay between step 189c5b64a99SLinus Walleij * down 190c5b64a99SLinus Walleij * @level: tells in how many steps the charging current has been 191c5b64a99SLinus Walleij increased 192c5b64a99SLinus Walleij */ 193c5b64a99SLinus Walleij struct ab8500_charge_curr_maximization { 194c5b64a99SLinus Walleij int original_iset; 195c5b64a99SLinus Walleij int current_iset; 196c5b64a99SLinus Walleij int test_delta_i; 197c5b64a99SLinus Walleij int condition_cnt; 198c5b64a99SLinus Walleij int max_current; 199c5b64a99SLinus Walleij int wait_cnt; 200c5b64a99SLinus Walleij u8 level; 201c5b64a99SLinus Walleij }; 202c5b64a99SLinus Walleij 203c5b64a99SLinus Walleij enum maxim_ret { 204c5b64a99SLinus Walleij MAXIM_RET_NOACTION, 205c5b64a99SLinus Walleij MAXIM_RET_CHANGE, 206c5b64a99SLinus Walleij MAXIM_RET_IBAT_TOO_HIGH, 207c5b64a99SLinus Walleij }; 208c5b64a99SLinus Walleij 209c5b64a99SLinus Walleij /** 210c5b64a99SLinus Walleij * struct ab8500_chargalg - ab8500 Charging algorithm device information 211c5b64a99SLinus Walleij * @dev: pointer to the structure device 212c5b64a99SLinus Walleij * @charge_status: battery operating status 213c5b64a99SLinus Walleij * @eoc_cnt: counter used to determine end-of_charge 214c5b64a99SLinus Walleij * @maintenance_chg: indicate if maintenance charge is active 215c5b64a99SLinus Walleij * @t_hyst_norm temperature hysteresis when the temperature has been 216c5b64a99SLinus Walleij * over or under normal limits 217c5b64a99SLinus Walleij * @t_hyst_lowhigh temperature hysteresis when the temperature has been 218c5b64a99SLinus Walleij * over or under the high or low limits 219c5b64a99SLinus Walleij * @charge_state: current state of the charging algorithm 220c5b64a99SLinus Walleij * @ccm charging current maximization parameters 221c5b64a99SLinus Walleij * @chg_info: information about connected charger types 222c5b64a99SLinus Walleij * @batt_data: data of the battery 223c5b64a99SLinus Walleij * @susp_status: current charger suspension status 224c5b64a99SLinus Walleij * @bm: Platform specific battery management information 225c5b64a99SLinus Walleij * @curr_status: Current step status for over-current protection 226c5b64a99SLinus Walleij * @parent: pointer to the struct ab8500 227c5b64a99SLinus Walleij * @chargalg_psy: structure that holds the battery properties exposed by 228c5b64a99SLinus Walleij * the charging algorithm 229c5b64a99SLinus Walleij * @events: structure for information about events triggered 230c5b64a99SLinus Walleij * @chargalg_wq: work queue for running the charging algorithm 231c5b64a99SLinus Walleij * @chargalg_periodic_work: work to run the charging algorithm periodically 232c5b64a99SLinus Walleij * @chargalg_wd_work: work to kick the charger watchdog periodically 233c5b64a99SLinus Walleij * @chargalg_work: work to run the charging algorithm instantly 234c5b64a99SLinus Walleij * @safety_timer: charging safety timer 235c5b64a99SLinus Walleij * @maintenance_timer: maintenance charging timer 236c5b64a99SLinus Walleij * @chargalg_kobject: structure of type kobject 237c5b64a99SLinus Walleij */ 238c5b64a99SLinus Walleij struct ab8500_chargalg { 239c5b64a99SLinus Walleij struct device *dev; 240c5b64a99SLinus Walleij int charge_status; 241c5b64a99SLinus Walleij int eoc_cnt; 242c5b64a99SLinus Walleij bool maintenance_chg; 243c5b64a99SLinus Walleij int t_hyst_norm; 244c5b64a99SLinus Walleij int t_hyst_lowhigh; 245c5b64a99SLinus Walleij enum ab8500_chargalg_states charge_state; 246c5b64a99SLinus Walleij struct ab8500_charge_curr_maximization ccm; 247c5b64a99SLinus Walleij struct ab8500_chargalg_charger_info chg_info; 248c5b64a99SLinus Walleij struct ab8500_chargalg_battery_data batt_data; 249c5b64a99SLinus Walleij struct ab8500_chargalg_suspension_status susp_status; 250c5b64a99SLinus Walleij struct ab8500 *parent; 251c5b64a99SLinus Walleij struct ab8500_chargalg_current_step_status curr_status; 252*484a9cc3SLinus Walleij struct ab8500_bm_data *bm; 253c5b64a99SLinus Walleij struct power_supply *chargalg_psy; 254c5b64a99SLinus Walleij struct ux500_charger *ac_chg; 255c5b64a99SLinus Walleij struct ux500_charger *usb_chg; 256c5b64a99SLinus Walleij struct ab8500_chargalg_events events; 257c5b64a99SLinus Walleij struct workqueue_struct *chargalg_wq; 258c5b64a99SLinus Walleij struct delayed_work chargalg_periodic_work; 259c5b64a99SLinus Walleij struct delayed_work chargalg_wd_work; 260c5b64a99SLinus Walleij struct work_struct chargalg_work; 261c5b64a99SLinus Walleij struct hrtimer safety_timer; 262c5b64a99SLinus Walleij struct hrtimer maintenance_timer; 263c5b64a99SLinus Walleij struct kobject chargalg_kobject; 264c5b64a99SLinus Walleij }; 265c5b64a99SLinus Walleij 266c5b64a99SLinus Walleij /*External charger prepare notifier*/ 267c5b64a99SLinus Walleij BLOCKING_NOTIFIER_HEAD(charger_notifier_list); 268c5b64a99SLinus Walleij 269c5b64a99SLinus Walleij /* Main battery properties */ 270c5b64a99SLinus Walleij static enum power_supply_property ab8500_chargalg_props[] = { 271c5b64a99SLinus Walleij POWER_SUPPLY_PROP_STATUS, 272c5b64a99SLinus Walleij POWER_SUPPLY_PROP_HEALTH, 273c5b64a99SLinus Walleij }; 274c5b64a99SLinus Walleij 275c5b64a99SLinus Walleij struct ab8500_chargalg_sysfs_entry { 276c5b64a99SLinus Walleij struct attribute attr; 277c5b64a99SLinus Walleij ssize_t (*show)(struct ab8500_chargalg *, char *); 278c5b64a99SLinus Walleij ssize_t (*store)(struct ab8500_chargalg *, const char *, size_t); 279c5b64a99SLinus Walleij }; 280c5b64a99SLinus Walleij 281c5b64a99SLinus Walleij /** 282c5b64a99SLinus Walleij * ab8500_chargalg_safety_timer_expired() - Expiration of the safety timer 283c5b64a99SLinus Walleij * @timer: pointer to the hrtimer structure 284c5b64a99SLinus Walleij * 285c5b64a99SLinus Walleij * This function gets called when the safety timer for the charger 286c5b64a99SLinus Walleij * expires 287c5b64a99SLinus Walleij */ 288c5b64a99SLinus Walleij static enum hrtimer_restart 289c5b64a99SLinus Walleij ab8500_chargalg_safety_timer_expired(struct hrtimer *timer) 290c5b64a99SLinus Walleij { 291c5b64a99SLinus Walleij struct ab8500_chargalg *di = container_of(timer, struct ab8500_chargalg, 292c5b64a99SLinus Walleij safety_timer); 293c5b64a99SLinus Walleij dev_err(di->dev, "Safety timer expired\n"); 294c5b64a99SLinus Walleij di->events.safety_timer_expired = true; 295c5b64a99SLinus Walleij 296c5b64a99SLinus Walleij /* Trigger execution of the algorithm instantly */ 297c5b64a99SLinus Walleij queue_work(di->chargalg_wq, &di->chargalg_work); 298c5b64a99SLinus Walleij 299c5b64a99SLinus Walleij return HRTIMER_NORESTART; 300c5b64a99SLinus Walleij } 301c5b64a99SLinus Walleij 302c5b64a99SLinus Walleij /** 303c5b64a99SLinus Walleij * ab8500_chargalg_maintenance_timer_expired() - Expiration of 304c5b64a99SLinus Walleij * the maintenance timer 305c5b64a99SLinus Walleij * @timer: pointer to the timer structure 306c5b64a99SLinus Walleij * 307c5b64a99SLinus Walleij * This function gets called when the maintenence timer 308c5b64a99SLinus Walleij * expires 309c5b64a99SLinus Walleij */ 310c5b64a99SLinus Walleij static enum hrtimer_restart 311c5b64a99SLinus Walleij ab8500_chargalg_maintenance_timer_expired(struct hrtimer *timer) 312c5b64a99SLinus Walleij { 313c5b64a99SLinus Walleij 314c5b64a99SLinus Walleij struct ab8500_chargalg *di = container_of(timer, struct ab8500_chargalg, 315c5b64a99SLinus Walleij maintenance_timer); 316c5b64a99SLinus Walleij 317c5b64a99SLinus Walleij dev_dbg(di->dev, "Maintenance timer expired\n"); 318c5b64a99SLinus Walleij di->events.maintenance_timer_expired = true; 319c5b64a99SLinus Walleij 320c5b64a99SLinus Walleij /* Trigger execution of the algorithm instantly */ 321c5b64a99SLinus Walleij queue_work(di->chargalg_wq, &di->chargalg_work); 322c5b64a99SLinus Walleij 323c5b64a99SLinus Walleij return HRTIMER_NORESTART; 324c5b64a99SLinus Walleij } 325c5b64a99SLinus Walleij 326c5b64a99SLinus Walleij /** 327c5b64a99SLinus Walleij * ab8500_chargalg_state_to() - Change charge state 328c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 329c5b64a99SLinus Walleij * 330c5b64a99SLinus Walleij * This function gets called when a charge state change should occur 331c5b64a99SLinus Walleij */ 332c5b64a99SLinus Walleij static void ab8500_chargalg_state_to(struct ab8500_chargalg *di, 333c5b64a99SLinus Walleij enum ab8500_chargalg_states state) 334c5b64a99SLinus Walleij { 335c5b64a99SLinus Walleij dev_dbg(di->dev, 336c5b64a99SLinus Walleij "State changed: %s (From state: [%d] %s =to=> [%d] %s )\n", 337c5b64a99SLinus Walleij di->charge_state == state ? "NO" : "YES", 338c5b64a99SLinus Walleij di->charge_state, 339c5b64a99SLinus Walleij states[di->charge_state], 340c5b64a99SLinus Walleij state, 341c5b64a99SLinus Walleij states[state]); 342c5b64a99SLinus Walleij 343c5b64a99SLinus Walleij di->charge_state = state; 344c5b64a99SLinus Walleij } 345c5b64a99SLinus Walleij 346c5b64a99SLinus Walleij static int ab8500_chargalg_check_charger_enable(struct ab8500_chargalg *di) 347c5b64a99SLinus Walleij { 348c5b64a99SLinus Walleij switch (di->charge_state) { 349c5b64a99SLinus Walleij case STATE_NORMAL: 350c5b64a99SLinus Walleij case STATE_MAINTENANCE_A: 351c5b64a99SLinus Walleij case STATE_MAINTENANCE_B: 352c5b64a99SLinus Walleij break; 353c5b64a99SLinus Walleij default: 354c5b64a99SLinus Walleij return 0; 355c5b64a99SLinus Walleij } 356c5b64a99SLinus Walleij 357c5b64a99SLinus Walleij if (di->chg_info.charger_type & USB_CHG) { 358c5b64a99SLinus Walleij return di->usb_chg->ops.check_enable(di->usb_chg, 359c5b64a99SLinus Walleij di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, 360c5b64a99SLinus Walleij di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); 361c5b64a99SLinus Walleij } else if ((di->chg_info.charger_type & AC_CHG) && 362c5b64a99SLinus Walleij !(di->ac_chg->external)) { 363c5b64a99SLinus Walleij return di->ac_chg->ops.check_enable(di->ac_chg, 364c5b64a99SLinus Walleij di->bm->bat_type[di->bm->batt_id].normal_vol_lvl, 365c5b64a99SLinus Walleij di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); 366c5b64a99SLinus Walleij } 367c5b64a99SLinus Walleij return 0; 368c5b64a99SLinus Walleij } 369c5b64a99SLinus Walleij 370c5b64a99SLinus Walleij /** 371c5b64a99SLinus Walleij * ab8500_chargalg_check_charger_connection() - Check charger connection change 372c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 373c5b64a99SLinus Walleij * 374c5b64a99SLinus Walleij * This function will check if there is a change in the charger connection 375c5b64a99SLinus Walleij * and change charge state accordingly. AC has precedence over USB. 376c5b64a99SLinus Walleij */ 377c5b64a99SLinus Walleij static int ab8500_chargalg_check_charger_connection(struct ab8500_chargalg *di) 378c5b64a99SLinus Walleij { 379c5b64a99SLinus Walleij if (di->chg_info.conn_chg != di->chg_info.prev_conn_chg || 380c5b64a99SLinus Walleij di->susp_status.suspended_change) { 381c5b64a99SLinus Walleij /* 382c5b64a99SLinus Walleij * Charger state changed or suspension 383c5b64a99SLinus Walleij * has changed since last update 384c5b64a99SLinus Walleij */ 385c5b64a99SLinus Walleij if ((di->chg_info.conn_chg & AC_CHG) && 386c5b64a99SLinus Walleij !di->susp_status.ac_suspended) { 387c5b64a99SLinus Walleij dev_dbg(di->dev, "Charging source is AC\n"); 388c5b64a99SLinus Walleij if (di->chg_info.charger_type != AC_CHG) { 389c5b64a99SLinus Walleij di->chg_info.charger_type = AC_CHG; 390c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 391c5b64a99SLinus Walleij } 392c5b64a99SLinus Walleij } else if ((di->chg_info.conn_chg & USB_CHG) && 393c5b64a99SLinus Walleij !di->susp_status.usb_suspended) { 394c5b64a99SLinus Walleij dev_dbg(di->dev, "Charging source is USB\n"); 395c5b64a99SLinus Walleij di->chg_info.charger_type = USB_CHG; 396c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 397c5b64a99SLinus Walleij } else if (di->chg_info.conn_chg && 398c5b64a99SLinus Walleij (di->susp_status.ac_suspended || 399c5b64a99SLinus Walleij di->susp_status.usb_suspended)) { 400c5b64a99SLinus Walleij dev_dbg(di->dev, "Charging is suspended\n"); 401c5b64a99SLinus Walleij di->chg_info.charger_type = NO_CHG; 402c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_SUSPENDED_INIT); 403c5b64a99SLinus Walleij } else { 404c5b64a99SLinus Walleij dev_dbg(di->dev, "Charging source is OFF\n"); 405c5b64a99SLinus Walleij di->chg_info.charger_type = NO_CHG; 406c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_HANDHELD_INIT); 407c5b64a99SLinus Walleij } 408c5b64a99SLinus Walleij di->chg_info.prev_conn_chg = di->chg_info.conn_chg; 409c5b64a99SLinus Walleij di->susp_status.suspended_change = false; 410c5b64a99SLinus Walleij } 411c5b64a99SLinus Walleij return di->chg_info.conn_chg; 412c5b64a99SLinus Walleij } 413c5b64a99SLinus Walleij 414c5b64a99SLinus Walleij /** 415c5b64a99SLinus Walleij * ab8500_chargalg_check_current_step_status() - Check charging current 416c5b64a99SLinus Walleij * step status. 417c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 418c5b64a99SLinus Walleij * 419c5b64a99SLinus Walleij * This function will check if there is a change in the charging current step 420c5b64a99SLinus Walleij * and change charge state accordingly. 421c5b64a99SLinus Walleij */ 422c5b64a99SLinus Walleij static void ab8500_chargalg_check_current_step_status 423c5b64a99SLinus Walleij (struct ab8500_chargalg *di) 424c5b64a99SLinus Walleij { 425c5b64a99SLinus Walleij if (di->curr_status.curr_step_change) 426c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 427c5b64a99SLinus Walleij di->curr_status.curr_step_change = false; 428c5b64a99SLinus Walleij } 429c5b64a99SLinus Walleij 430c5b64a99SLinus Walleij /** 431c5b64a99SLinus Walleij * ab8500_chargalg_start_safety_timer() - Start charging safety timer 432c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 433c5b64a99SLinus Walleij * 434c5b64a99SLinus Walleij * The safety timer is used to avoid overcharging of old or bad batteries. 435c5b64a99SLinus Walleij * There are different timers for AC and USB 436c5b64a99SLinus Walleij */ 437c5b64a99SLinus Walleij static void ab8500_chargalg_start_safety_timer(struct ab8500_chargalg *di) 438c5b64a99SLinus Walleij { 439c5b64a99SLinus Walleij /* Charger-dependent expiration time in hours*/ 440c5b64a99SLinus Walleij int timer_expiration = 0; 441c5b64a99SLinus Walleij 442c5b64a99SLinus Walleij switch (di->chg_info.charger_type) { 443c5b64a99SLinus Walleij case AC_CHG: 444c5b64a99SLinus Walleij timer_expiration = di->bm->main_safety_tmr_h; 445c5b64a99SLinus Walleij break; 446c5b64a99SLinus Walleij 447c5b64a99SLinus Walleij case USB_CHG: 448c5b64a99SLinus Walleij timer_expiration = di->bm->usb_safety_tmr_h; 449c5b64a99SLinus Walleij break; 450c5b64a99SLinus Walleij 451c5b64a99SLinus Walleij default: 452c5b64a99SLinus Walleij dev_err(di->dev, "Unknown charger to charge from\n"); 453c5b64a99SLinus Walleij break; 454c5b64a99SLinus Walleij } 455c5b64a99SLinus Walleij 456c5b64a99SLinus Walleij di->events.safety_timer_expired = false; 457c5b64a99SLinus Walleij hrtimer_set_expires_range(&di->safety_timer, 458c5b64a99SLinus Walleij ktime_set(timer_expiration * ONE_HOUR_IN_SECONDS, 0), 459c5b64a99SLinus Walleij ktime_set(FIVE_MINUTES_IN_SECONDS, 0)); 460c5b64a99SLinus Walleij hrtimer_start_expires(&di->safety_timer, HRTIMER_MODE_REL); 461c5b64a99SLinus Walleij } 462c5b64a99SLinus Walleij 463c5b64a99SLinus Walleij /** 464c5b64a99SLinus Walleij * ab8500_chargalg_stop_safety_timer() - Stop charging safety timer 465c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 466c5b64a99SLinus Walleij * 467c5b64a99SLinus Walleij * The safety timer is stopped whenever the NORMAL state is exited 468c5b64a99SLinus Walleij */ 469c5b64a99SLinus Walleij static void ab8500_chargalg_stop_safety_timer(struct ab8500_chargalg *di) 470c5b64a99SLinus Walleij { 471c5b64a99SLinus Walleij if (hrtimer_try_to_cancel(&di->safety_timer) >= 0) 472c5b64a99SLinus Walleij di->events.safety_timer_expired = false; 473c5b64a99SLinus Walleij } 474c5b64a99SLinus Walleij 475c5b64a99SLinus Walleij /** 476c5b64a99SLinus Walleij * ab8500_chargalg_start_maintenance_timer() - Start charging maintenance timer 477c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 478c5b64a99SLinus Walleij * @duration: duration of ther maintenance timer in hours 479c5b64a99SLinus Walleij * 480c5b64a99SLinus Walleij * The maintenance timer is used to maintain the charge in the battery once 481c5b64a99SLinus Walleij * the battery is considered full. These timers are chosen to match the 482c5b64a99SLinus Walleij * discharge curve of the battery 483c5b64a99SLinus Walleij */ 484c5b64a99SLinus Walleij static void ab8500_chargalg_start_maintenance_timer(struct ab8500_chargalg *di, 485c5b64a99SLinus Walleij int duration) 486c5b64a99SLinus Walleij { 487c5b64a99SLinus Walleij hrtimer_set_expires_range(&di->maintenance_timer, 488c5b64a99SLinus Walleij ktime_set(duration * ONE_HOUR_IN_SECONDS, 0), 489c5b64a99SLinus Walleij ktime_set(FIVE_MINUTES_IN_SECONDS, 0)); 490c5b64a99SLinus Walleij di->events.maintenance_timer_expired = false; 491c5b64a99SLinus Walleij hrtimer_start_expires(&di->maintenance_timer, HRTIMER_MODE_REL); 492c5b64a99SLinus Walleij } 493c5b64a99SLinus Walleij 494c5b64a99SLinus Walleij /** 495c5b64a99SLinus Walleij * ab8500_chargalg_stop_maintenance_timer() - Stop maintenance timer 496c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 497c5b64a99SLinus Walleij * 498c5b64a99SLinus Walleij * The maintenance timer is stopped whenever maintenance ends or when another 499c5b64a99SLinus Walleij * state is entered 500c5b64a99SLinus Walleij */ 501c5b64a99SLinus Walleij static void ab8500_chargalg_stop_maintenance_timer(struct ab8500_chargalg *di) 502c5b64a99SLinus Walleij { 503c5b64a99SLinus Walleij if (hrtimer_try_to_cancel(&di->maintenance_timer) >= 0) 504c5b64a99SLinus Walleij di->events.maintenance_timer_expired = false; 505c5b64a99SLinus Walleij } 506c5b64a99SLinus Walleij 507c5b64a99SLinus Walleij /** 508c5b64a99SLinus Walleij * ab8500_chargalg_kick_watchdog() - Kick charger watchdog 509c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 510c5b64a99SLinus Walleij * 511c5b64a99SLinus Walleij * The charger watchdog have to be kicked periodically whenever the charger is 512c5b64a99SLinus Walleij * on, else the ABB will reset the system 513c5b64a99SLinus Walleij */ 514c5b64a99SLinus Walleij static int ab8500_chargalg_kick_watchdog(struct ab8500_chargalg *di) 515c5b64a99SLinus Walleij { 516c5b64a99SLinus Walleij /* Check if charger exists and kick watchdog if charging */ 517c5b64a99SLinus Walleij if (di->ac_chg && di->ac_chg->ops.kick_wd && 518c5b64a99SLinus Walleij di->chg_info.online_chg & AC_CHG) { 519c5b64a99SLinus Walleij /* 520c5b64a99SLinus Walleij * If AB charger watchdog expired, pm2xxx charging 521c5b64a99SLinus Walleij * gets disabled. To be safe, kick both AB charger watchdog 522c5b64a99SLinus Walleij * and pm2xxx watchdog. 523c5b64a99SLinus Walleij */ 524c5b64a99SLinus Walleij if (di->ac_chg->external && 525c5b64a99SLinus Walleij di->usb_chg && di->usb_chg->ops.kick_wd) 526c5b64a99SLinus Walleij di->usb_chg->ops.kick_wd(di->usb_chg); 527c5b64a99SLinus Walleij 528c5b64a99SLinus Walleij return di->ac_chg->ops.kick_wd(di->ac_chg); 529c5b64a99SLinus Walleij } 530c5b64a99SLinus Walleij else if (di->usb_chg && di->usb_chg->ops.kick_wd && 531c5b64a99SLinus Walleij di->chg_info.online_chg & USB_CHG) 532c5b64a99SLinus Walleij return di->usb_chg->ops.kick_wd(di->usb_chg); 533c5b64a99SLinus Walleij 534c5b64a99SLinus Walleij return -ENXIO; 535c5b64a99SLinus Walleij } 536c5b64a99SLinus Walleij 537c5b64a99SLinus Walleij /** 538c5b64a99SLinus Walleij * ab8500_chargalg_ac_en() - Turn on/off the AC charger 539c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 540c5b64a99SLinus Walleij * @enable: charger on/off 541c5b64a99SLinus Walleij * @vset: requested charger output voltage 542c5b64a99SLinus Walleij * @iset: requested charger output current 543c5b64a99SLinus Walleij * 544c5b64a99SLinus Walleij * The AC charger will be turned on/off with the requested charge voltage and 545c5b64a99SLinus Walleij * current 546c5b64a99SLinus Walleij */ 547c5b64a99SLinus Walleij static int ab8500_chargalg_ac_en(struct ab8500_chargalg *di, int enable, 548c5b64a99SLinus Walleij int vset, int iset) 549c5b64a99SLinus Walleij { 550c5b64a99SLinus Walleij static int ab8500_chargalg_ex_ac_enable_toggle; 551c5b64a99SLinus Walleij 552c5b64a99SLinus Walleij if (!di->ac_chg || !di->ac_chg->ops.enable) 553c5b64a99SLinus Walleij return -ENXIO; 554c5b64a99SLinus Walleij 555c5b64a99SLinus Walleij /* Select maximum of what both the charger and the battery supports */ 556c5b64a99SLinus Walleij if (di->ac_chg->max_out_volt) 557c5b64a99SLinus Walleij vset = min(vset, di->ac_chg->max_out_volt); 558c5b64a99SLinus Walleij if (di->ac_chg->max_out_curr) 559c5b64a99SLinus Walleij iset = min(iset, di->ac_chg->max_out_curr); 560c5b64a99SLinus Walleij 561c5b64a99SLinus Walleij di->chg_info.ac_iset = iset; 562c5b64a99SLinus Walleij di->chg_info.ac_vset = vset; 563c5b64a99SLinus Walleij 564c5b64a99SLinus Walleij /* Enable external charger */ 565c5b64a99SLinus Walleij if (enable && di->ac_chg->external && 566c5b64a99SLinus Walleij !ab8500_chargalg_ex_ac_enable_toggle) { 567c5b64a99SLinus Walleij blocking_notifier_call_chain(&charger_notifier_list, 568c5b64a99SLinus Walleij 0, di->dev); 569c5b64a99SLinus Walleij ab8500_chargalg_ex_ac_enable_toggle++; 570c5b64a99SLinus Walleij } 571c5b64a99SLinus Walleij 572c5b64a99SLinus Walleij return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset); 573c5b64a99SLinus Walleij } 574c5b64a99SLinus Walleij 575c5b64a99SLinus Walleij /** 576c5b64a99SLinus Walleij * ab8500_chargalg_usb_en() - Turn on/off the USB charger 577c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 578c5b64a99SLinus Walleij * @enable: charger on/off 579c5b64a99SLinus Walleij * @vset: requested charger output voltage 580c5b64a99SLinus Walleij * @iset: requested charger output current 581c5b64a99SLinus Walleij * 582c5b64a99SLinus Walleij * The USB charger will be turned on/off with the requested charge voltage and 583c5b64a99SLinus Walleij * current 584c5b64a99SLinus Walleij */ 585c5b64a99SLinus Walleij static int ab8500_chargalg_usb_en(struct ab8500_chargalg *di, int enable, 586c5b64a99SLinus Walleij int vset, int iset) 587c5b64a99SLinus Walleij { 588c5b64a99SLinus Walleij if (!di->usb_chg || !di->usb_chg->ops.enable) 589c5b64a99SLinus Walleij return -ENXIO; 590c5b64a99SLinus Walleij 591c5b64a99SLinus Walleij /* Select maximum of what both the charger and the battery supports */ 592c5b64a99SLinus Walleij if (di->usb_chg->max_out_volt) 593c5b64a99SLinus Walleij vset = min(vset, di->usb_chg->max_out_volt); 594c5b64a99SLinus Walleij if (di->usb_chg->max_out_curr) 595c5b64a99SLinus Walleij iset = min(iset, di->usb_chg->max_out_curr); 596c5b64a99SLinus Walleij 597c5b64a99SLinus Walleij di->chg_info.usb_iset = iset; 598c5b64a99SLinus Walleij di->chg_info.usb_vset = vset; 599c5b64a99SLinus Walleij 600c5b64a99SLinus Walleij return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset); 601c5b64a99SLinus Walleij } 602c5b64a99SLinus Walleij 603c5b64a99SLinus Walleij /** 604c5b64a99SLinus Walleij * ab8500_chargalg_update_chg_curr() - Update charger current 605c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 606c5b64a99SLinus Walleij * @iset: requested charger output current 607c5b64a99SLinus Walleij * 608c5b64a99SLinus Walleij * The charger output current will be updated for the charger 609c5b64a99SLinus Walleij * that is currently in use 610c5b64a99SLinus Walleij */ 611c5b64a99SLinus Walleij static int ab8500_chargalg_update_chg_curr(struct ab8500_chargalg *di, 612c5b64a99SLinus Walleij int iset) 613c5b64a99SLinus Walleij { 614c5b64a99SLinus Walleij /* Check if charger exists and update current if charging */ 615c5b64a99SLinus Walleij if (di->ac_chg && di->ac_chg->ops.update_curr && 616c5b64a99SLinus Walleij di->chg_info.charger_type & AC_CHG) { 617c5b64a99SLinus Walleij /* 618c5b64a99SLinus Walleij * Select maximum of what both the charger 619c5b64a99SLinus Walleij * and the battery supports 620c5b64a99SLinus Walleij */ 621c5b64a99SLinus Walleij if (di->ac_chg->max_out_curr) 622c5b64a99SLinus Walleij iset = min(iset, di->ac_chg->max_out_curr); 623c5b64a99SLinus Walleij 624c5b64a99SLinus Walleij di->chg_info.ac_iset = iset; 625c5b64a99SLinus Walleij 626c5b64a99SLinus Walleij return di->ac_chg->ops.update_curr(di->ac_chg, iset); 627c5b64a99SLinus Walleij } else if (di->usb_chg && di->usb_chg->ops.update_curr && 628c5b64a99SLinus Walleij di->chg_info.charger_type & USB_CHG) { 629c5b64a99SLinus Walleij /* 630c5b64a99SLinus Walleij * Select maximum of what both the charger 631c5b64a99SLinus Walleij * and the battery supports 632c5b64a99SLinus Walleij */ 633c5b64a99SLinus Walleij if (di->usb_chg->max_out_curr) 634c5b64a99SLinus Walleij iset = min(iset, di->usb_chg->max_out_curr); 635c5b64a99SLinus Walleij 636c5b64a99SLinus Walleij di->chg_info.usb_iset = iset; 637c5b64a99SLinus Walleij 638c5b64a99SLinus Walleij return di->usb_chg->ops.update_curr(di->usb_chg, iset); 639c5b64a99SLinus Walleij } 640c5b64a99SLinus Walleij 641c5b64a99SLinus Walleij return -ENXIO; 642c5b64a99SLinus Walleij } 643c5b64a99SLinus Walleij 644c5b64a99SLinus Walleij /** 645c5b64a99SLinus Walleij * ab8500_chargalg_stop_charging() - Stop charging 646c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 647c5b64a99SLinus Walleij * 648c5b64a99SLinus Walleij * This function is called from any state where charging should be stopped. 649c5b64a99SLinus Walleij * All charging is disabled and all status parameters and timers are changed 650c5b64a99SLinus Walleij * accordingly 651c5b64a99SLinus Walleij */ 652c5b64a99SLinus Walleij static void ab8500_chargalg_stop_charging(struct ab8500_chargalg *di) 653c5b64a99SLinus Walleij { 654c5b64a99SLinus Walleij ab8500_chargalg_ac_en(di, false, 0, 0); 655c5b64a99SLinus Walleij ab8500_chargalg_usb_en(di, false, 0, 0); 656c5b64a99SLinus Walleij ab8500_chargalg_stop_safety_timer(di); 657c5b64a99SLinus Walleij ab8500_chargalg_stop_maintenance_timer(di); 658c5b64a99SLinus Walleij di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; 659c5b64a99SLinus Walleij di->maintenance_chg = false; 660c5b64a99SLinus Walleij cancel_delayed_work(&di->chargalg_wd_work); 661c5b64a99SLinus Walleij power_supply_changed(di->chargalg_psy); 662c5b64a99SLinus Walleij } 663c5b64a99SLinus Walleij 664c5b64a99SLinus Walleij /** 665c5b64a99SLinus Walleij * ab8500_chargalg_hold_charging() - Pauses charging 666c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 667c5b64a99SLinus Walleij * 668c5b64a99SLinus Walleij * This function is called in the case where maintenance charging has been 669c5b64a99SLinus Walleij * disabled and instead a battery voltage mode is entered to check when the 670c5b64a99SLinus Walleij * battery voltage has reached a certain recharge voltage 671c5b64a99SLinus Walleij */ 672c5b64a99SLinus Walleij static void ab8500_chargalg_hold_charging(struct ab8500_chargalg *di) 673c5b64a99SLinus Walleij { 674c5b64a99SLinus Walleij ab8500_chargalg_ac_en(di, false, 0, 0); 675c5b64a99SLinus Walleij ab8500_chargalg_usb_en(di, false, 0, 0); 676c5b64a99SLinus Walleij ab8500_chargalg_stop_safety_timer(di); 677c5b64a99SLinus Walleij ab8500_chargalg_stop_maintenance_timer(di); 678c5b64a99SLinus Walleij di->charge_status = POWER_SUPPLY_STATUS_CHARGING; 679c5b64a99SLinus Walleij di->maintenance_chg = false; 680c5b64a99SLinus Walleij cancel_delayed_work(&di->chargalg_wd_work); 681c5b64a99SLinus Walleij power_supply_changed(di->chargalg_psy); 682c5b64a99SLinus Walleij } 683c5b64a99SLinus Walleij 684c5b64a99SLinus Walleij /** 685c5b64a99SLinus Walleij * ab8500_chargalg_start_charging() - Start the charger 686c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 687c5b64a99SLinus Walleij * @vset: requested charger output voltage 688c5b64a99SLinus Walleij * @iset: requested charger output current 689c5b64a99SLinus Walleij * 690c5b64a99SLinus Walleij * A charger will be enabled depending on the requested charger type that was 691c5b64a99SLinus Walleij * detected previously. 692c5b64a99SLinus Walleij */ 693c5b64a99SLinus Walleij static void ab8500_chargalg_start_charging(struct ab8500_chargalg *di, 694c5b64a99SLinus Walleij int vset, int iset) 695c5b64a99SLinus Walleij { 696c5b64a99SLinus Walleij switch (di->chg_info.charger_type) { 697c5b64a99SLinus Walleij case AC_CHG: 698c5b64a99SLinus Walleij dev_dbg(di->dev, 699c5b64a99SLinus Walleij "AC parameters: Vset %d, Ich %d\n", vset, iset); 700c5b64a99SLinus Walleij ab8500_chargalg_usb_en(di, false, 0, 0); 701c5b64a99SLinus Walleij ab8500_chargalg_ac_en(di, true, vset, iset); 702c5b64a99SLinus Walleij break; 703c5b64a99SLinus Walleij 704c5b64a99SLinus Walleij case USB_CHG: 705c5b64a99SLinus Walleij dev_dbg(di->dev, 706c5b64a99SLinus Walleij "USB parameters: Vset %d, Ich %d\n", vset, iset); 707c5b64a99SLinus Walleij ab8500_chargalg_ac_en(di, false, 0, 0); 708c5b64a99SLinus Walleij ab8500_chargalg_usb_en(di, true, vset, iset); 709c5b64a99SLinus Walleij break; 710c5b64a99SLinus Walleij 711c5b64a99SLinus Walleij default: 712c5b64a99SLinus Walleij dev_err(di->dev, "Unknown charger to charge from\n"); 713c5b64a99SLinus Walleij break; 714c5b64a99SLinus Walleij } 715c5b64a99SLinus Walleij } 716c5b64a99SLinus Walleij 717c5b64a99SLinus Walleij /** 718c5b64a99SLinus Walleij * ab8500_chargalg_check_temp() - Check battery temperature ranges 719c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 720c5b64a99SLinus Walleij * 721c5b64a99SLinus Walleij * The battery temperature is checked against the predefined limits and the 722c5b64a99SLinus Walleij * charge state is changed accordingly 723c5b64a99SLinus Walleij */ 724c5b64a99SLinus Walleij static void ab8500_chargalg_check_temp(struct ab8500_chargalg *di) 725c5b64a99SLinus Walleij { 726c5b64a99SLinus Walleij if (di->batt_data.temp > (di->bm->temp_low + di->t_hyst_norm) && 727c5b64a99SLinus Walleij di->batt_data.temp < (di->bm->temp_high - di->t_hyst_norm)) { 728c5b64a99SLinus Walleij /* Temp OK! */ 729c5b64a99SLinus Walleij di->events.btemp_underover = false; 730c5b64a99SLinus Walleij di->events.btemp_lowhigh = false; 731c5b64a99SLinus Walleij di->t_hyst_norm = 0; 732c5b64a99SLinus Walleij di->t_hyst_lowhigh = 0; 733c5b64a99SLinus Walleij } else { 734c5b64a99SLinus Walleij if (((di->batt_data.temp >= di->bm->temp_high) && 735c5b64a99SLinus Walleij (di->batt_data.temp < 736c5b64a99SLinus Walleij (di->bm->temp_over - di->t_hyst_lowhigh))) || 737c5b64a99SLinus Walleij ((di->batt_data.temp > 738c5b64a99SLinus Walleij (di->bm->temp_under + di->t_hyst_lowhigh)) && 739c5b64a99SLinus Walleij (di->batt_data.temp <= di->bm->temp_low))) { 740c5b64a99SLinus Walleij /* TEMP minor!!!!! */ 741c5b64a99SLinus Walleij di->events.btemp_underover = false; 742c5b64a99SLinus Walleij di->events.btemp_lowhigh = true; 743c5b64a99SLinus Walleij di->t_hyst_norm = di->bm->temp_hysteresis; 744c5b64a99SLinus Walleij di->t_hyst_lowhigh = 0; 745c5b64a99SLinus Walleij } else if (di->batt_data.temp <= di->bm->temp_under || 746c5b64a99SLinus Walleij di->batt_data.temp >= di->bm->temp_over) { 747c5b64a99SLinus Walleij /* TEMP major!!!!! */ 748c5b64a99SLinus Walleij di->events.btemp_underover = true; 749c5b64a99SLinus Walleij di->events.btemp_lowhigh = false; 750c5b64a99SLinus Walleij di->t_hyst_norm = 0; 751c5b64a99SLinus Walleij di->t_hyst_lowhigh = di->bm->temp_hysteresis; 752c5b64a99SLinus Walleij } else { 753c5b64a99SLinus Walleij /* Within hysteresis */ 754c5b64a99SLinus Walleij dev_dbg(di->dev, "Within hysteresis limit temp: %d " 755c5b64a99SLinus Walleij "hyst_lowhigh %d, hyst normal %d\n", 756c5b64a99SLinus Walleij di->batt_data.temp, di->t_hyst_lowhigh, 757c5b64a99SLinus Walleij di->t_hyst_norm); 758c5b64a99SLinus Walleij } 759c5b64a99SLinus Walleij } 760c5b64a99SLinus Walleij } 761c5b64a99SLinus Walleij 762c5b64a99SLinus Walleij /** 763c5b64a99SLinus Walleij * ab8500_chargalg_check_charger_voltage() - Check charger voltage 764c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 765c5b64a99SLinus Walleij * 766c5b64a99SLinus Walleij * Charger voltage is checked against maximum limit 767c5b64a99SLinus Walleij */ 768c5b64a99SLinus Walleij static void ab8500_chargalg_check_charger_voltage(struct ab8500_chargalg *di) 769c5b64a99SLinus Walleij { 770c5b64a99SLinus Walleij if (di->chg_info.usb_volt > di->bm->chg_params->usb_volt_max) 771c5b64a99SLinus Walleij di->chg_info.usb_chg_ok = false; 772c5b64a99SLinus Walleij else 773c5b64a99SLinus Walleij di->chg_info.usb_chg_ok = true; 774c5b64a99SLinus Walleij 775c5b64a99SLinus Walleij if (di->chg_info.ac_volt > di->bm->chg_params->ac_volt_max) 776c5b64a99SLinus Walleij di->chg_info.ac_chg_ok = false; 777c5b64a99SLinus Walleij else 778c5b64a99SLinus Walleij di->chg_info.ac_chg_ok = true; 779c5b64a99SLinus Walleij 780c5b64a99SLinus Walleij } 781c5b64a99SLinus Walleij 782c5b64a99SLinus Walleij /** 783c5b64a99SLinus Walleij * ab8500_chargalg_end_of_charge() - Check if end-of-charge criteria is fulfilled 784c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 785c5b64a99SLinus Walleij * 786c5b64a99SLinus Walleij * End-of-charge criteria is fulfilled when the battery voltage is above a 787c5b64a99SLinus Walleij * certain limit and the battery current is below a certain limit for a 788c5b64a99SLinus Walleij * predefined number of consecutive seconds. If true, the battery is full 789c5b64a99SLinus Walleij */ 790c5b64a99SLinus Walleij static void ab8500_chargalg_end_of_charge(struct ab8500_chargalg *di) 791c5b64a99SLinus Walleij { 792c5b64a99SLinus Walleij if (di->charge_status == POWER_SUPPLY_STATUS_CHARGING && 793c5b64a99SLinus Walleij di->charge_state == STATE_NORMAL && 794c5b64a99SLinus Walleij !di->maintenance_chg && (di->batt_data.volt >= 795c5b64a99SLinus Walleij di->bm->bat_type[di->bm->batt_id].termination_vol || 796c5b64a99SLinus Walleij di->events.usb_cv_active || di->events.ac_cv_active) && 797c5b64a99SLinus Walleij di->batt_data.avg_curr < 798c5b64a99SLinus Walleij di->bm->bat_type[di->bm->batt_id].termination_curr && 799c5b64a99SLinus Walleij di->batt_data.avg_curr > 0) { 800c5b64a99SLinus Walleij if (++di->eoc_cnt >= EOC_COND_CNT) { 801c5b64a99SLinus Walleij di->eoc_cnt = 0; 802c5b64a99SLinus Walleij di->charge_status = POWER_SUPPLY_STATUS_FULL; 803c5b64a99SLinus Walleij di->maintenance_chg = true; 804c5b64a99SLinus Walleij dev_dbg(di->dev, "EOC reached!\n"); 805c5b64a99SLinus Walleij power_supply_changed(di->chargalg_psy); 806c5b64a99SLinus Walleij } else { 807c5b64a99SLinus Walleij dev_dbg(di->dev, 808c5b64a99SLinus Walleij " EOC limit reached for the %d" 809c5b64a99SLinus Walleij " time, out of %d before EOC\n", 810c5b64a99SLinus Walleij di->eoc_cnt, 811c5b64a99SLinus Walleij EOC_COND_CNT); 812c5b64a99SLinus Walleij } 813c5b64a99SLinus Walleij } else { 814c5b64a99SLinus Walleij di->eoc_cnt = 0; 815c5b64a99SLinus Walleij } 816c5b64a99SLinus Walleij } 817c5b64a99SLinus Walleij 818c5b64a99SLinus Walleij static void init_maxim_chg_curr(struct ab8500_chargalg *di) 819c5b64a99SLinus Walleij { 820c5b64a99SLinus Walleij di->ccm.original_iset = 821c5b64a99SLinus Walleij di->bm->bat_type[di->bm->batt_id].normal_cur_lvl; 822c5b64a99SLinus Walleij di->ccm.current_iset = 823c5b64a99SLinus Walleij di->bm->bat_type[di->bm->batt_id].normal_cur_lvl; 824c5b64a99SLinus Walleij di->ccm.test_delta_i = di->bm->maxi->charger_curr_step; 825c5b64a99SLinus Walleij di->ccm.max_current = di->bm->maxi->chg_curr; 826c5b64a99SLinus Walleij di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 827c5b64a99SLinus Walleij di->ccm.level = 0; 828c5b64a99SLinus Walleij } 829c5b64a99SLinus Walleij 830c5b64a99SLinus Walleij /** 831c5b64a99SLinus Walleij * ab8500_chargalg_chg_curr_maxim - increases the charger current to 832c5b64a99SLinus Walleij * compensate for the system load 833c5b64a99SLinus Walleij * @di pointer to the ab8500_chargalg structure 834c5b64a99SLinus Walleij * 835c5b64a99SLinus Walleij * This maximization function is used to raise the charger current to get the 836c5b64a99SLinus Walleij * battery current as close to the optimal value as possible. The battery 837c5b64a99SLinus Walleij * current during charging is affected by the system load 838c5b64a99SLinus Walleij */ 839c5b64a99SLinus Walleij static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di) 840c5b64a99SLinus Walleij { 841c5b64a99SLinus Walleij int delta_i; 842c5b64a99SLinus Walleij 843c5b64a99SLinus Walleij if (!di->bm->maxi->ena_maxi) 844c5b64a99SLinus Walleij return MAXIM_RET_NOACTION; 845c5b64a99SLinus Walleij 846c5b64a99SLinus Walleij delta_i = di->ccm.original_iset - di->batt_data.inst_curr; 847c5b64a99SLinus Walleij 848c5b64a99SLinus Walleij if (di->events.vbus_collapsed) { 849c5b64a99SLinus Walleij dev_dbg(di->dev, "Charger voltage has collapsed %d\n", 850c5b64a99SLinus Walleij di->ccm.wait_cnt); 851c5b64a99SLinus Walleij if (di->ccm.wait_cnt == 0) { 852c5b64a99SLinus Walleij dev_dbg(di->dev, "lowering current\n"); 853c5b64a99SLinus Walleij di->ccm.wait_cnt++; 854c5b64a99SLinus Walleij di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 855c5b64a99SLinus Walleij di->ccm.max_current = 856c5b64a99SLinus Walleij di->ccm.current_iset - di->ccm.test_delta_i; 857c5b64a99SLinus Walleij di->ccm.current_iset = di->ccm.max_current; 858c5b64a99SLinus Walleij di->ccm.level--; 859c5b64a99SLinus Walleij return MAXIM_RET_CHANGE; 860c5b64a99SLinus Walleij } else { 861c5b64a99SLinus Walleij dev_dbg(di->dev, "waiting\n"); 862c5b64a99SLinus Walleij /* Let's go in here twice before lowering curr again */ 863c5b64a99SLinus Walleij di->ccm.wait_cnt = (di->ccm.wait_cnt + 1) % 3; 864c5b64a99SLinus Walleij return MAXIM_RET_NOACTION; 865c5b64a99SLinus Walleij } 866c5b64a99SLinus Walleij } 867c5b64a99SLinus Walleij 868c5b64a99SLinus Walleij di->ccm.wait_cnt = 0; 869c5b64a99SLinus Walleij 870c5b64a99SLinus Walleij if ((di->batt_data.inst_curr > di->ccm.original_iset)) { 871c5b64a99SLinus Walleij dev_dbg(di->dev, " Maximization Ibat (%dmA) too high" 872c5b64a99SLinus Walleij " (limit %dmA) (current iset: %dmA)!\n", 873c5b64a99SLinus Walleij di->batt_data.inst_curr, di->ccm.original_iset, 874c5b64a99SLinus Walleij di->ccm.current_iset); 875c5b64a99SLinus Walleij 876c5b64a99SLinus Walleij if (di->ccm.current_iset == di->ccm.original_iset) 877c5b64a99SLinus Walleij return MAXIM_RET_NOACTION; 878c5b64a99SLinus Walleij 879c5b64a99SLinus Walleij di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 880c5b64a99SLinus Walleij di->ccm.current_iset = di->ccm.original_iset; 881c5b64a99SLinus Walleij di->ccm.level = 0; 882c5b64a99SLinus Walleij 883c5b64a99SLinus Walleij return MAXIM_RET_IBAT_TOO_HIGH; 884c5b64a99SLinus Walleij } 885c5b64a99SLinus Walleij 886c5b64a99SLinus Walleij if (delta_i > di->ccm.test_delta_i && 887c5b64a99SLinus Walleij (di->ccm.current_iset + di->ccm.test_delta_i) < 888c5b64a99SLinus Walleij di->ccm.max_current) { 889c5b64a99SLinus Walleij if (di->ccm.condition_cnt-- == 0) { 890c5b64a99SLinus Walleij /* Increse the iset with cco.test_delta_i */ 891c5b64a99SLinus Walleij di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 892c5b64a99SLinus Walleij di->ccm.current_iset += di->ccm.test_delta_i; 893c5b64a99SLinus Walleij di->ccm.level++; 894c5b64a99SLinus Walleij dev_dbg(di->dev, " Maximization needed, increase" 895c5b64a99SLinus Walleij " with %d mA to %dmA (Optimal ibat: %d)" 896c5b64a99SLinus Walleij " Level %d\n", 897c5b64a99SLinus Walleij di->ccm.test_delta_i, 898c5b64a99SLinus Walleij di->ccm.current_iset, 899c5b64a99SLinus Walleij di->ccm.original_iset, 900c5b64a99SLinus Walleij di->ccm.level); 901c5b64a99SLinus Walleij return MAXIM_RET_CHANGE; 902c5b64a99SLinus Walleij } else { 903c5b64a99SLinus Walleij return MAXIM_RET_NOACTION; 904c5b64a99SLinus Walleij } 905c5b64a99SLinus Walleij } else { 906c5b64a99SLinus Walleij di->ccm.condition_cnt = di->bm->maxi->wait_cycles; 907c5b64a99SLinus Walleij return MAXIM_RET_NOACTION; 908c5b64a99SLinus Walleij } 909c5b64a99SLinus Walleij } 910c5b64a99SLinus Walleij 911c5b64a99SLinus Walleij static void handle_maxim_chg_curr(struct ab8500_chargalg *di) 912c5b64a99SLinus Walleij { 913c5b64a99SLinus Walleij enum maxim_ret ret; 914c5b64a99SLinus Walleij int result; 915c5b64a99SLinus Walleij 916c5b64a99SLinus Walleij ret = ab8500_chargalg_chg_curr_maxim(di); 917c5b64a99SLinus Walleij switch (ret) { 918c5b64a99SLinus Walleij case MAXIM_RET_CHANGE: 919c5b64a99SLinus Walleij result = ab8500_chargalg_update_chg_curr(di, 920c5b64a99SLinus Walleij di->ccm.current_iset); 921c5b64a99SLinus Walleij if (result) 922c5b64a99SLinus Walleij dev_err(di->dev, "failed to set chg curr\n"); 923c5b64a99SLinus Walleij break; 924c5b64a99SLinus Walleij case MAXIM_RET_IBAT_TOO_HIGH: 925c5b64a99SLinus Walleij result = ab8500_chargalg_update_chg_curr(di, 926c5b64a99SLinus Walleij di->bm->bat_type[di->bm->batt_id].normal_cur_lvl); 927c5b64a99SLinus Walleij if (result) 928c5b64a99SLinus Walleij dev_err(di->dev, "failed to set chg curr\n"); 929c5b64a99SLinus Walleij break; 930c5b64a99SLinus Walleij 931c5b64a99SLinus Walleij case MAXIM_RET_NOACTION: 932c5b64a99SLinus Walleij default: 933c5b64a99SLinus Walleij /* Do nothing..*/ 934c5b64a99SLinus Walleij break; 935c5b64a99SLinus Walleij } 936c5b64a99SLinus Walleij } 937c5b64a99SLinus Walleij 938c5b64a99SLinus Walleij static int ab8500_chargalg_get_ext_psy_data(struct device *dev, void *data) 939c5b64a99SLinus Walleij { 940c5b64a99SLinus Walleij struct power_supply *psy; 941c5b64a99SLinus Walleij struct power_supply *ext = dev_get_drvdata(dev); 942c5b64a99SLinus Walleij const char **supplicants = (const char **)ext->supplied_to; 943c5b64a99SLinus Walleij struct ab8500_chargalg *di; 944c5b64a99SLinus Walleij union power_supply_propval ret; 945c5b64a99SLinus Walleij int j; 946c5b64a99SLinus Walleij bool capacity_updated = false; 947c5b64a99SLinus Walleij 948c5b64a99SLinus Walleij psy = (struct power_supply *)data; 949c5b64a99SLinus Walleij di = power_supply_get_drvdata(psy); 950c5b64a99SLinus Walleij /* For all psy where the driver name appears in any supplied_to */ 951c5b64a99SLinus Walleij j = match_string(supplicants, ext->num_supplicants, psy->desc->name); 952c5b64a99SLinus Walleij if (j < 0) 953c5b64a99SLinus Walleij return 0; 954c5b64a99SLinus Walleij 955c5b64a99SLinus Walleij /* 956c5b64a99SLinus Walleij * If external is not registering 'POWER_SUPPLY_PROP_CAPACITY' to its 957c5b64a99SLinus Walleij * property because of handling that sysfs entry on its own, this is 958c5b64a99SLinus Walleij * the place to get the battery capacity. 959c5b64a99SLinus Walleij */ 960c5b64a99SLinus Walleij if (!power_supply_get_property(ext, POWER_SUPPLY_PROP_CAPACITY, &ret)) { 961c5b64a99SLinus Walleij di->batt_data.percent = ret.intval; 962c5b64a99SLinus Walleij capacity_updated = true; 963c5b64a99SLinus Walleij } 964c5b64a99SLinus Walleij 965c5b64a99SLinus Walleij /* Go through all properties for the psy */ 966c5b64a99SLinus Walleij for (j = 0; j < ext->desc->num_properties; j++) { 967c5b64a99SLinus Walleij enum power_supply_property prop; 968c5b64a99SLinus Walleij prop = ext->desc->properties[j]; 969c5b64a99SLinus Walleij 970c5b64a99SLinus Walleij /* 971c5b64a99SLinus Walleij * Initialize chargers if not already done. 972c5b64a99SLinus Walleij * The ab8500_charger*/ 973c5b64a99SLinus Walleij if (!di->ac_chg && 974c5b64a99SLinus Walleij ext->desc->type == POWER_SUPPLY_TYPE_MAINS) 975c5b64a99SLinus Walleij di->ac_chg = psy_to_ux500_charger(ext); 976c5b64a99SLinus Walleij else if (!di->usb_chg && 977c5b64a99SLinus Walleij ext->desc->type == POWER_SUPPLY_TYPE_USB) 978c5b64a99SLinus Walleij di->usb_chg = psy_to_ux500_charger(ext); 979c5b64a99SLinus Walleij 980c5b64a99SLinus Walleij if (power_supply_get_property(ext, prop, &ret)) 981c5b64a99SLinus Walleij continue; 982c5b64a99SLinus Walleij switch (prop) { 983c5b64a99SLinus Walleij case POWER_SUPPLY_PROP_PRESENT: 984c5b64a99SLinus Walleij switch (ext->desc->type) { 985c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_BATTERY: 986c5b64a99SLinus Walleij /* Battery present */ 987c5b64a99SLinus Walleij if (ret.intval) 988c5b64a99SLinus Walleij di->events.batt_rem = false; 989c5b64a99SLinus Walleij /* Battery removed */ 990c5b64a99SLinus Walleij else 991c5b64a99SLinus Walleij di->events.batt_rem = true; 992c5b64a99SLinus Walleij break; 993c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_MAINS: 994c5b64a99SLinus Walleij /* AC disconnected */ 995c5b64a99SLinus Walleij if (!ret.intval && 996c5b64a99SLinus Walleij (di->chg_info.conn_chg & AC_CHG)) { 997c5b64a99SLinus Walleij di->chg_info.prev_conn_chg = 998c5b64a99SLinus Walleij di->chg_info.conn_chg; 999c5b64a99SLinus Walleij di->chg_info.conn_chg &= ~AC_CHG; 1000c5b64a99SLinus Walleij } 1001c5b64a99SLinus Walleij /* AC connected */ 1002c5b64a99SLinus Walleij else if (ret.intval && 1003c5b64a99SLinus Walleij !(di->chg_info.conn_chg & AC_CHG)) { 1004c5b64a99SLinus Walleij di->chg_info.prev_conn_chg = 1005c5b64a99SLinus Walleij di->chg_info.conn_chg; 1006c5b64a99SLinus Walleij di->chg_info.conn_chg |= AC_CHG; 1007c5b64a99SLinus Walleij } 1008c5b64a99SLinus Walleij break; 1009c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_USB: 1010c5b64a99SLinus Walleij /* USB disconnected */ 1011c5b64a99SLinus Walleij if (!ret.intval && 1012c5b64a99SLinus Walleij (di->chg_info.conn_chg & USB_CHG)) { 1013c5b64a99SLinus Walleij di->chg_info.prev_conn_chg = 1014c5b64a99SLinus Walleij di->chg_info.conn_chg; 1015c5b64a99SLinus Walleij di->chg_info.conn_chg &= ~USB_CHG; 1016c5b64a99SLinus Walleij } 1017c5b64a99SLinus Walleij /* USB connected */ 1018c5b64a99SLinus Walleij else if (ret.intval && 1019c5b64a99SLinus Walleij !(di->chg_info.conn_chg & USB_CHG)) { 1020c5b64a99SLinus Walleij di->chg_info.prev_conn_chg = 1021c5b64a99SLinus Walleij di->chg_info.conn_chg; 1022c5b64a99SLinus Walleij di->chg_info.conn_chg |= USB_CHG; 1023c5b64a99SLinus Walleij } 1024c5b64a99SLinus Walleij break; 1025c5b64a99SLinus Walleij default: 1026c5b64a99SLinus Walleij break; 1027c5b64a99SLinus Walleij } 1028c5b64a99SLinus Walleij break; 1029c5b64a99SLinus Walleij 1030c5b64a99SLinus Walleij case POWER_SUPPLY_PROP_ONLINE: 1031c5b64a99SLinus Walleij switch (ext->desc->type) { 1032c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_BATTERY: 1033c5b64a99SLinus Walleij break; 1034c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_MAINS: 1035c5b64a99SLinus Walleij /* AC offline */ 1036c5b64a99SLinus Walleij if (!ret.intval && 1037c5b64a99SLinus Walleij (di->chg_info.online_chg & AC_CHG)) { 1038c5b64a99SLinus Walleij di->chg_info.prev_online_chg = 1039c5b64a99SLinus Walleij di->chg_info.online_chg; 1040c5b64a99SLinus Walleij di->chg_info.online_chg &= ~AC_CHG; 1041c5b64a99SLinus Walleij } 1042c5b64a99SLinus Walleij /* AC online */ 1043c5b64a99SLinus Walleij else if (ret.intval && 1044c5b64a99SLinus Walleij !(di->chg_info.online_chg & AC_CHG)) { 1045c5b64a99SLinus Walleij di->chg_info.prev_online_chg = 1046c5b64a99SLinus Walleij di->chg_info.online_chg; 1047c5b64a99SLinus Walleij di->chg_info.online_chg |= AC_CHG; 1048c5b64a99SLinus Walleij queue_delayed_work(di->chargalg_wq, 1049c5b64a99SLinus Walleij &di->chargalg_wd_work, 0); 1050c5b64a99SLinus Walleij } 1051c5b64a99SLinus Walleij break; 1052c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_USB: 1053c5b64a99SLinus Walleij /* USB offline */ 1054c5b64a99SLinus Walleij if (!ret.intval && 1055c5b64a99SLinus Walleij (di->chg_info.online_chg & USB_CHG)) { 1056c5b64a99SLinus Walleij di->chg_info.prev_online_chg = 1057c5b64a99SLinus Walleij di->chg_info.online_chg; 1058c5b64a99SLinus Walleij di->chg_info.online_chg &= ~USB_CHG; 1059c5b64a99SLinus Walleij } 1060c5b64a99SLinus Walleij /* USB online */ 1061c5b64a99SLinus Walleij else if (ret.intval && 1062c5b64a99SLinus Walleij !(di->chg_info.online_chg & USB_CHG)) { 1063c5b64a99SLinus Walleij di->chg_info.prev_online_chg = 1064c5b64a99SLinus Walleij di->chg_info.online_chg; 1065c5b64a99SLinus Walleij di->chg_info.online_chg |= USB_CHG; 1066c5b64a99SLinus Walleij queue_delayed_work(di->chargalg_wq, 1067c5b64a99SLinus Walleij &di->chargalg_wd_work, 0); 1068c5b64a99SLinus Walleij } 1069c5b64a99SLinus Walleij break; 1070c5b64a99SLinus Walleij default: 1071c5b64a99SLinus Walleij break; 1072c5b64a99SLinus Walleij } 1073c5b64a99SLinus Walleij break; 1074c5b64a99SLinus Walleij 1075c5b64a99SLinus Walleij case POWER_SUPPLY_PROP_HEALTH: 1076c5b64a99SLinus Walleij switch (ext->desc->type) { 1077c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_BATTERY: 1078c5b64a99SLinus Walleij break; 1079c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_MAINS: 1080c5b64a99SLinus Walleij switch (ret.intval) { 1081c5b64a99SLinus Walleij case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: 1082c5b64a99SLinus Walleij di->events.mainextchnotok = true; 1083c5b64a99SLinus Walleij di->events.main_thermal_prot = false; 1084c5b64a99SLinus Walleij di->events.main_ovv = false; 1085c5b64a99SLinus Walleij di->events.ac_wd_expired = false; 1086c5b64a99SLinus Walleij break; 1087c5b64a99SLinus Walleij case POWER_SUPPLY_HEALTH_DEAD: 1088c5b64a99SLinus Walleij di->events.ac_wd_expired = true; 1089c5b64a99SLinus Walleij di->events.mainextchnotok = false; 1090c5b64a99SLinus Walleij di->events.main_ovv = false; 1091c5b64a99SLinus Walleij di->events.main_thermal_prot = false; 1092c5b64a99SLinus Walleij break; 1093c5b64a99SLinus Walleij case POWER_SUPPLY_HEALTH_COLD: 1094c5b64a99SLinus Walleij case POWER_SUPPLY_HEALTH_OVERHEAT: 1095c5b64a99SLinus Walleij di->events.main_thermal_prot = true; 1096c5b64a99SLinus Walleij di->events.mainextchnotok = false; 1097c5b64a99SLinus Walleij di->events.main_ovv = false; 1098c5b64a99SLinus Walleij di->events.ac_wd_expired = false; 1099c5b64a99SLinus Walleij break; 1100c5b64a99SLinus Walleij case POWER_SUPPLY_HEALTH_OVERVOLTAGE: 1101c5b64a99SLinus Walleij di->events.main_ovv = true; 1102c5b64a99SLinus Walleij di->events.mainextchnotok = false; 1103c5b64a99SLinus Walleij di->events.main_thermal_prot = false; 1104c5b64a99SLinus Walleij di->events.ac_wd_expired = false; 1105c5b64a99SLinus Walleij break; 1106c5b64a99SLinus Walleij case POWER_SUPPLY_HEALTH_GOOD: 1107c5b64a99SLinus Walleij di->events.main_thermal_prot = false; 1108c5b64a99SLinus Walleij di->events.mainextchnotok = false; 1109c5b64a99SLinus Walleij di->events.main_ovv = false; 1110c5b64a99SLinus Walleij di->events.ac_wd_expired = false; 1111c5b64a99SLinus Walleij break; 1112c5b64a99SLinus Walleij default: 1113c5b64a99SLinus Walleij break; 1114c5b64a99SLinus Walleij } 1115c5b64a99SLinus Walleij break; 1116c5b64a99SLinus Walleij 1117c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_USB: 1118c5b64a99SLinus Walleij switch (ret.intval) { 1119c5b64a99SLinus Walleij case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE: 1120c5b64a99SLinus Walleij di->events.usbchargernotok = true; 1121c5b64a99SLinus Walleij di->events.usb_thermal_prot = false; 1122c5b64a99SLinus Walleij di->events.vbus_ovv = false; 1123c5b64a99SLinus Walleij di->events.usb_wd_expired = false; 1124c5b64a99SLinus Walleij break; 1125c5b64a99SLinus Walleij case POWER_SUPPLY_HEALTH_DEAD: 1126c5b64a99SLinus Walleij di->events.usb_wd_expired = true; 1127c5b64a99SLinus Walleij di->events.usbchargernotok = false; 1128c5b64a99SLinus Walleij di->events.usb_thermal_prot = false; 1129c5b64a99SLinus Walleij di->events.vbus_ovv = false; 1130c5b64a99SLinus Walleij break; 1131c5b64a99SLinus Walleij case POWER_SUPPLY_HEALTH_COLD: 1132c5b64a99SLinus Walleij case POWER_SUPPLY_HEALTH_OVERHEAT: 1133c5b64a99SLinus Walleij di->events.usb_thermal_prot = true; 1134c5b64a99SLinus Walleij di->events.usbchargernotok = false; 1135c5b64a99SLinus Walleij di->events.vbus_ovv = false; 1136c5b64a99SLinus Walleij di->events.usb_wd_expired = false; 1137c5b64a99SLinus Walleij break; 1138c5b64a99SLinus Walleij case POWER_SUPPLY_HEALTH_OVERVOLTAGE: 1139c5b64a99SLinus Walleij di->events.vbus_ovv = true; 1140c5b64a99SLinus Walleij di->events.usbchargernotok = false; 1141c5b64a99SLinus Walleij di->events.usb_thermal_prot = false; 1142c5b64a99SLinus Walleij di->events.usb_wd_expired = false; 1143c5b64a99SLinus Walleij break; 1144c5b64a99SLinus Walleij case POWER_SUPPLY_HEALTH_GOOD: 1145c5b64a99SLinus Walleij di->events.usbchargernotok = false; 1146c5b64a99SLinus Walleij di->events.usb_thermal_prot = false; 1147c5b64a99SLinus Walleij di->events.vbus_ovv = false; 1148c5b64a99SLinus Walleij di->events.usb_wd_expired = false; 1149c5b64a99SLinus Walleij break; 1150c5b64a99SLinus Walleij default: 1151c5b64a99SLinus Walleij break; 1152c5b64a99SLinus Walleij } 1153c5b64a99SLinus Walleij default: 1154c5b64a99SLinus Walleij break; 1155c5b64a99SLinus Walleij } 1156c5b64a99SLinus Walleij break; 1157c5b64a99SLinus Walleij 1158c5b64a99SLinus Walleij case POWER_SUPPLY_PROP_VOLTAGE_NOW: 1159c5b64a99SLinus Walleij switch (ext->desc->type) { 1160c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_BATTERY: 1161c5b64a99SLinus Walleij di->batt_data.volt = ret.intval / 1000; 1162c5b64a99SLinus Walleij break; 1163c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_MAINS: 1164c5b64a99SLinus Walleij di->chg_info.ac_volt = ret.intval / 1000; 1165c5b64a99SLinus Walleij break; 1166c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_USB: 1167c5b64a99SLinus Walleij di->chg_info.usb_volt = ret.intval / 1000; 1168c5b64a99SLinus Walleij break; 1169c5b64a99SLinus Walleij default: 1170c5b64a99SLinus Walleij break; 1171c5b64a99SLinus Walleij } 1172c5b64a99SLinus Walleij break; 1173c5b64a99SLinus Walleij 1174c5b64a99SLinus Walleij case POWER_SUPPLY_PROP_VOLTAGE_AVG: 1175c5b64a99SLinus Walleij switch (ext->desc->type) { 1176c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_MAINS: 1177c5b64a99SLinus Walleij /* AVG is used to indicate when we are 1178c5b64a99SLinus Walleij * in CV mode */ 1179c5b64a99SLinus Walleij if (ret.intval) 1180c5b64a99SLinus Walleij di->events.ac_cv_active = true; 1181c5b64a99SLinus Walleij else 1182c5b64a99SLinus Walleij di->events.ac_cv_active = false; 1183c5b64a99SLinus Walleij 1184c5b64a99SLinus Walleij break; 1185c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_USB: 1186c5b64a99SLinus Walleij /* AVG is used to indicate when we are 1187c5b64a99SLinus Walleij * in CV mode */ 1188c5b64a99SLinus Walleij if (ret.intval) 1189c5b64a99SLinus Walleij di->events.usb_cv_active = true; 1190c5b64a99SLinus Walleij else 1191c5b64a99SLinus Walleij di->events.usb_cv_active = false; 1192c5b64a99SLinus Walleij 1193c5b64a99SLinus Walleij break; 1194c5b64a99SLinus Walleij default: 1195c5b64a99SLinus Walleij break; 1196c5b64a99SLinus Walleij } 1197c5b64a99SLinus Walleij break; 1198c5b64a99SLinus Walleij 1199c5b64a99SLinus Walleij case POWER_SUPPLY_PROP_TECHNOLOGY: 1200c5b64a99SLinus Walleij switch (ext->desc->type) { 1201c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_BATTERY: 1202c5b64a99SLinus Walleij if (ret.intval) 1203c5b64a99SLinus Walleij di->events.batt_unknown = false; 1204c5b64a99SLinus Walleij else 1205c5b64a99SLinus Walleij di->events.batt_unknown = true; 1206c5b64a99SLinus Walleij 1207c5b64a99SLinus Walleij break; 1208c5b64a99SLinus Walleij default: 1209c5b64a99SLinus Walleij break; 1210c5b64a99SLinus Walleij } 1211c5b64a99SLinus Walleij break; 1212c5b64a99SLinus Walleij 1213c5b64a99SLinus Walleij case POWER_SUPPLY_PROP_TEMP: 1214c5b64a99SLinus Walleij di->batt_data.temp = ret.intval / 10; 1215c5b64a99SLinus Walleij break; 1216c5b64a99SLinus Walleij 1217c5b64a99SLinus Walleij case POWER_SUPPLY_PROP_CURRENT_NOW: 1218c5b64a99SLinus Walleij switch (ext->desc->type) { 1219c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_MAINS: 1220c5b64a99SLinus Walleij di->chg_info.ac_curr = 1221c5b64a99SLinus Walleij ret.intval / 1000; 1222c5b64a99SLinus Walleij break; 1223c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_USB: 1224c5b64a99SLinus Walleij di->chg_info.usb_curr = 1225c5b64a99SLinus Walleij ret.intval / 1000; 1226c5b64a99SLinus Walleij break; 1227c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_BATTERY: 1228c5b64a99SLinus Walleij di->batt_data.inst_curr = ret.intval / 1000; 1229c5b64a99SLinus Walleij break; 1230c5b64a99SLinus Walleij default: 1231c5b64a99SLinus Walleij break; 1232c5b64a99SLinus Walleij } 1233c5b64a99SLinus Walleij break; 1234c5b64a99SLinus Walleij 1235c5b64a99SLinus Walleij case POWER_SUPPLY_PROP_CURRENT_AVG: 1236c5b64a99SLinus Walleij switch (ext->desc->type) { 1237c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_BATTERY: 1238c5b64a99SLinus Walleij di->batt_data.avg_curr = ret.intval / 1000; 1239c5b64a99SLinus Walleij break; 1240c5b64a99SLinus Walleij case POWER_SUPPLY_TYPE_USB: 1241c5b64a99SLinus Walleij if (ret.intval) 1242c5b64a99SLinus Walleij di->events.vbus_collapsed = true; 1243c5b64a99SLinus Walleij else 1244c5b64a99SLinus Walleij di->events.vbus_collapsed = false; 1245c5b64a99SLinus Walleij break; 1246c5b64a99SLinus Walleij default: 1247c5b64a99SLinus Walleij break; 1248c5b64a99SLinus Walleij } 1249c5b64a99SLinus Walleij break; 1250c5b64a99SLinus Walleij case POWER_SUPPLY_PROP_CAPACITY: 1251c5b64a99SLinus Walleij if (!capacity_updated) 1252c5b64a99SLinus Walleij di->batt_data.percent = ret.intval; 1253c5b64a99SLinus Walleij break; 1254c5b64a99SLinus Walleij default: 1255c5b64a99SLinus Walleij break; 1256c5b64a99SLinus Walleij } 1257c5b64a99SLinus Walleij } 1258c5b64a99SLinus Walleij return 0; 1259c5b64a99SLinus Walleij } 1260c5b64a99SLinus Walleij 1261c5b64a99SLinus Walleij /** 1262c5b64a99SLinus Walleij * ab8500_chargalg_external_power_changed() - callback for power supply changes 1263c5b64a99SLinus Walleij * @psy: pointer to the structure power_supply 1264c5b64a99SLinus Walleij * 1265c5b64a99SLinus Walleij * This function is the entry point of the pointer external_power_changed 1266c5b64a99SLinus Walleij * of the structure power_supply. 1267c5b64a99SLinus Walleij * This function gets executed when there is a change in any external power 1268c5b64a99SLinus Walleij * supply that this driver needs to be notified of. 1269c5b64a99SLinus Walleij */ 1270c5b64a99SLinus Walleij static void ab8500_chargalg_external_power_changed(struct power_supply *psy) 1271c5b64a99SLinus Walleij { 1272c5b64a99SLinus Walleij struct ab8500_chargalg *di = power_supply_get_drvdata(psy); 1273c5b64a99SLinus Walleij 1274c5b64a99SLinus Walleij /* 1275c5b64a99SLinus Walleij * Trigger execution of the algorithm instantly and read 1276c5b64a99SLinus Walleij * all power_supply properties there instead 1277c5b64a99SLinus Walleij */ 1278c5b64a99SLinus Walleij queue_work(di->chargalg_wq, &di->chargalg_work); 1279c5b64a99SLinus Walleij } 1280c5b64a99SLinus Walleij 1281c5b64a99SLinus Walleij /** 1282c5b64a99SLinus Walleij * ab8500_chargalg_algorithm() - Main function for the algorithm 1283c5b64a99SLinus Walleij * @di: pointer to the ab8500_chargalg structure 1284c5b64a99SLinus Walleij * 1285c5b64a99SLinus Walleij * This is the main control function for the charging algorithm. 1286c5b64a99SLinus Walleij * It is called periodically or when something happens that will 1287c5b64a99SLinus Walleij * trigger a state change 1288c5b64a99SLinus Walleij */ 1289c5b64a99SLinus Walleij static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di) 1290c5b64a99SLinus Walleij { 1291c5b64a99SLinus Walleij int charger_status; 1292c5b64a99SLinus Walleij int ret; 1293c5b64a99SLinus Walleij int curr_step_lvl; 1294c5b64a99SLinus Walleij 1295c5b64a99SLinus Walleij /* Collect data from all power_supply class devices */ 1296c5b64a99SLinus Walleij class_for_each_device(power_supply_class, NULL, 1297c5b64a99SLinus Walleij di->chargalg_psy, ab8500_chargalg_get_ext_psy_data); 1298c5b64a99SLinus Walleij 1299c5b64a99SLinus Walleij ab8500_chargalg_end_of_charge(di); 1300c5b64a99SLinus Walleij ab8500_chargalg_check_temp(di); 1301c5b64a99SLinus Walleij ab8500_chargalg_check_charger_voltage(di); 1302c5b64a99SLinus Walleij 1303c5b64a99SLinus Walleij charger_status = ab8500_chargalg_check_charger_connection(di); 1304c5b64a99SLinus Walleij ab8500_chargalg_check_current_step_status(di); 1305c5b64a99SLinus Walleij 1306c5b64a99SLinus Walleij if (is_ab8500(di->parent)) { 1307c5b64a99SLinus Walleij ret = ab8500_chargalg_check_charger_enable(di); 1308c5b64a99SLinus Walleij if (ret < 0) 1309c5b64a99SLinus Walleij dev_err(di->dev, "Checking charger is enabled error" 1310c5b64a99SLinus Walleij ": Returned Value %d\n", ret); 1311c5b64a99SLinus Walleij } 1312c5b64a99SLinus Walleij 1313c5b64a99SLinus Walleij /* 1314c5b64a99SLinus Walleij * First check if we have a charger connected. 1315c5b64a99SLinus Walleij * Also we don't allow charging of unknown batteries if configured 1316c5b64a99SLinus Walleij * this way 1317c5b64a99SLinus Walleij */ 1318c5b64a99SLinus Walleij if (!charger_status || 1319c5b64a99SLinus Walleij (di->events.batt_unknown && !di->bm->chg_unknown_bat)) { 1320c5b64a99SLinus Walleij if (di->charge_state != STATE_HANDHELD) { 1321c5b64a99SLinus Walleij di->events.safety_timer_expired = false; 1322c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_HANDHELD_INIT); 1323c5b64a99SLinus Walleij } 1324c5b64a99SLinus Walleij } 1325c5b64a99SLinus Walleij 1326c5b64a99SLinus Walleij /* If suspended, we should not continue checking the flags */ 1327c5b64a99SLinus Walleij else if (di->charge_state == STATE_SUSPENDED_INIT || 1328c5b64a99SLinus Walleij di->charge_state == STATE_SUSPENDED) { 1329c5b64a99SLinus Walleij /* We don't do anything here, just don,t continue */ 1330c5b64a99SLinus Walleij } 1331c5b64a99SLinus Walleij 1332c5b64a99SLinus Walleij /* Safety timer expiration */ 1333c5b64a99SLinus Walleij else if (di->events.safety_timer_expired) { 1334c5b64a99SLinus Walleij if (di->charge_state != STATE_SAFETY_TIMER_EXPIRED) 1335c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, 1336c5b64a99SLinus Walleij STATE_SAFETY_TIMER_EXPIRED_INIT); 1337c5b64a99SLinus Walleij } 1338c5b64a99SLinus Walleij /* 1339c5b64a99SLinus Walleij * Check if any interrupts has occured 1340c5b64a99SLinus Walleij * that will prevent us from charging 1341c5b64a99SLinus Walleij */ 1342c5b64a99SLinus Walleij 1343c5b64a99SLinus Walleij /* Battery removed */ 1344c5b64a99SLinus Walleij else if (di->events.batt_rem) { 1345c5b64a99SLinus Walleij if (di->charge_state != STATE_BATT_REMOVED) 1346c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_BATT_REMOVED_INIT); 1347c5b64a99SLinus Walleij } 1348c5b64a99SLinus Walleij /* Main or USB charger not ok. */ 1349c5b64a99SLinus Walleij else if (di->events.mainextchnotok || di->events.usbchargernotok) { 1350c5b64a99SLinus Walleij /* 1351c5b64a99SLinus Walleij * If vbus_collapsed is set, we have to lower the charger 1352c5b64a99SLinus Walleij * current, which is done in the normal state below 1353c5b64a99SLinus Walleij */ 1354c5b64a99SLinus Walleij if (di->charge_state != STATE_CHG_NOT_OK && 1355c5b64a99SLinus Walleij !di->events.vbus_collapsed) 1356c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT); 1357c5b64a99SLinus Walleij } 1358c5b64a99SLinus Walleij /* VBUS, Main or VBAT OVV. */ 1359c5b64a99SLinus Walleij else if (di->events.vbus_ovv || 1360c5b64a99SLinus Walleij di->events.main_ovv || 1361c5b64a99SLinus Walleij di->events.batt_ovv || 1362c5b64a99SLinus Walleij !di->chg_info.usb_chg_ok || 1363c5b64a99SLinus Walleij !di->chg_info.ac_chg_ok) { 1364c5b64a99SLinus Walleij if (di->charge_state != STATE_OVV_PROTECT) 1365c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_OVV_PROTECT_INIT); 1366c5b64a99SLinus Walleij } 1367c5b64a99SLinus Walleij /* USB Thermal, stop charging */ 1368c5b64a99SLinus Walleij else if (di->events.main_thermal_prot || 1369c5b64a99SLinus Walleij di->events.usb_thermal_prot) { 1370c5b64a99SLinus Walleij if (di->charge_state != STATE_HW_TEMP_PROTECT) 1371c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, 1372c5b64a99SLinus Walleij STATE_HW_TEMP_PROTECT_INIT); 1373c5b64a99SLinus Walleij } 1374c5b64a99SLinus Walleij /* Battery temp over/under */ 1375c5b64a99SLinus Walleij else if (di->events.btemp_underover) { 1376c5b64a99SLinus Walleij if (di->charge_state != STATE_TEMP_UNDEROVER) 1377c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, 1378c5b64a99SLinus Walleij STATE_TEMP_UNDEROVER_INIT); 1379c5b64a99SLinus Walleij } 1380c5b64a99SLinus Walleij /* Watchdog expired */ 1381c5b64a99SLinus Walleij else if (di->events.ac_wd_expired || 1382c5b64a99SLinus Walleij di->events.usb_wd_expired) { 1383c5b64a99SLinus Walleij if (di->charge_state != STATE_WD_EXPIRED) 1384c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_WD_EXPIRED_INIT); 1385c5b64a99SLinus Walleij } 1386c5b64a99SLinus Walleij /* Battery temp high/low */ 1387c5b64a99SLinus Walleij else if (di->events.btemp_lowhigh) { 1388c5b64a99SLinus Walleij if (di->charge_state != STATE_TEMP_LOWHIGH) 1389c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH_INIT); 1390c5b64a99SLinus Walleij } 1391c5b64a99SLinus Walleij 1392c5b64a99SLinus Walleij dev_dbg(di->dev, 1393c5b64a99SLinus Walleij "[CHARGALG] Vb %d Ib_avg %d Ib_inst %d Tb %d Cap %d Maint %d " 1394c5b64a99SLinus Walleij "State %s Active_chg %d Chg_status %d AC %d USB %d " 1395c5b64a99SLinus Walleij "AC_online %d USB_online %d AC_CV %d USB_CV %d AC_I %d " 1396c5b64a99SLinus Walleij "USB_I %d AC_Vset %d AC_Iset %d USB_Vset %d USB_Iset %d\n", 1397c5b64a99SLinus Walleij di->batt_data.volt, 1398c5b64a99SLinus Walleij di->batt_data.avg_curr, 1399c5b64a99SLinus Walleij di->batt_data.inst_curr, 1400c5b64a99SLinus Walleij di->batt_data.temp, 1401c5b64a99SLinus Walleij di->batt_data.percent, 1402c5b64a99SLinus Walleij di->maintenance_chg, 1403c5b64a99SLinus Walleij states[di->charge_state], 1404c5b64a99SLinus Walleij di->chg_info.charger_type, 1405c5b64a99SLinus Walleij di->charge_status, 1406c5b64a99SLinus Walleij di->chg_info.conn_chg & AC_CHG, 1407c5b64a99SLinus Walleij di->chg_info.conn_chg & USB_CHG, 1408c5b64a99SLinus Walleij di->chg_info.online_chg & AC_CHG, 1409c5b64a99SLinus Walleij di->chg_info.online_chg & USB_CHG, 1410c5b64a99SLinus Walleij di->events.ac_cv_active, 1411c5b64a99SLinus Walleij di->events.usb_cv_active, 1412c5b64a99SLinus Walleij di->chg_info.ac_curr, 1413c5b64a99SLinus Walleij di->chg_info.usb_curr, 1414c5b64a99SLinus Walleij di->chg_info.ac_vset, 1415c5b64a99SLinus Walleij di->chg_info.ac_iset, 1416c5b64a99SLinus Walleij di->chg_info.usb_vset, 1417c5b64a99SLinus Walleij di->chg_info.usb_iset); 1418c5b64a99SLinus Walleij 1419c5b64a99SLinus Walleij switch (di->charge_state) { 1420c5b64a99SLinus Walleij case STATE_HANDHELD_INIT: 1421c5b64a99SLinus Walleij ab8500_chargalg_stop_charging(di); 1422c5b64a99SLinus Walleij di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; 1423c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_HANDHELD); 1424c5b64a99SLinus Walleij fallthrough; 1425c5b64a99SLinus Walleij 1426c5b64a99SLinus Walleij case STATE_HANDHELD: 1427c5b64a99SLinus Walleij break; 1428c5b64a99SLinus Walleij 1429c5b64a99SLinus Walleij case STATE_SUSPENDED_INIT: 1430c5b64a99SLinus Walleij if (di->susp_status.ac_suspended) 1431c5b64a99SLinus Walleij ab8500_chargalg_ac_en(di, false, 0, 0); 1432c5b64a99SLinus Walleij if (di->susp_status.usb_suspended) 1433c5b64a99SLinus Walleij ab8500_chargalg_usb_en(di, false, 0, 0); 1434c5b64a99SLinus Walleij ab8500_chargalg_stop_safety_timer(di); 1435c5b64a99SLinus Walleij ab8500_chargalg_stop_maintenance_timer(di); 1436c5b64a99SLinus Walleij di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; 1437c5b64a99SLinus Walleij di->maintenance_chg = false; 1438c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_SUSPENDED); 1439c5b64a99SLinus Walleij power_supply_changed(di->chargalg_psy); 1440c5b64a99SLinus Walleij fallthrough; 1441c5b64a99SLinus Walleij 1442c5b64a99SLinus Walleij case STATE_SUSPENDED: 1443c5b64a99SLinus Walleij /* CHARGING is suspended */ 1444c5b64a99SLinus Walleij break; 1445c5b64a99SLinus Walleij 1446c5b64a99SLinus Walleij case STATE_BATT_REMOVED_INIT: 1447c5b64a99SLinus Walleij ab8500_chargalg_stop_charging(di); 1448c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_BATT_REMOVED); 1449c5b64a99SLinus Walleij fallthrough; 1450c5b64a99SLinus Walleij 1451c5b64a99SLinus Walleij case STATE_BATT_REMOVED: 1452c5b64a99SLinus Walleij if (!di->events.batt_rem) 1453c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 1454c5b64a99SLinus Walleij break; 1455c5b64a99SLinus Walleij 1456c5b64a99SLinus Walleij case STATE_HW_TEMP_PROTECT_INIT: 1457c5b64a99SLinus Walleij ab8500_chargalg_stop_charging(di); 1458c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_HW_TEMP_PROTECT); 1459c5b64a99SLinus Walleij fallthrough; 1460c5b64a99SLinus Walleij 1461c5b64a99SLinus Walleij case STATE_HW_TEMP_PROTECT: 1462c5b64a99SLinus Walleij if (!di->events.main_thermal_prot && 1463c5b64a99SLinus Walleij !di->events.usb_thermal_prot) 1464c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 1465c5b64a99SLinus Walleij break; 1466c5b64a99SLinus Walleij 1467c5b64a99SLinus Walleij case STATE_OVV_PROTECT_INIT: 1468c5b64a99SLinus Walleij ab8500_chargalg_stop_charging(di); 1469c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_OVV_PROTECT); 1470c5b64a99SLinus Walleij fallthrough; 1471c5b64a99SLinus Walleij 1472c5b64a99SLinus Walleij case STATE_OVV_PROTECT: 1473c5b64a99SLinus Walleij if (!di->events.vbus_ovv && 1474c5b64a99SLinus Walleij !di->events.main_ovv && 1475c5b64a99SLinus Walleij !di->events.batt_ovv && 1476c5b64a99SLinus Walleij di->chg_info.usb_chg_ok && 1477c5b64a99SLinus Walleij di->chg_info.ac_chg_ok) 1478c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 1479c5b64a99SLinus Walleij break; 1480c5b64a99SLinus Walleij 1481c5b64a99SLinus Walleij case STATE_CHG_NOT_OK_INIT: 1482c5b64a99SLinus Walleij ab8500_chargalg_stop_charging(di); 1483c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK); 1484c5b64a99SLinus Walleij fallthrough; 1485c5b64a99SLinus Walleij 1486c5b64a99SLinus Walleij case STATE_CHG_NOT_OK: 1487c5b64a99SLinus Walleij if (!di->events.mainextchnotok && 1488c5b64a99SLinus Walleij !di->events.usbchargernotok) 1489c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 1490c5b64a99SLinus Walleij break; 1491c5b64a99SLinus Walleij 1492c5b64a99SLinus Walleij case STATE_SAFETY_TIMER_EXPIRED_INIT: 1493c5b64a99SLinus Walleij ab8500_chargalg_stop_charging(di); 1494c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_SAFETY_TIMER_EXPIRED); 1495c5b64a99SLinus Walleij fallthrough; 1496c5b64a99SLinus Walleij 1497c5b64a99SLinus Walleij case STATE_SAFETY_TIMER_EXPIRED: 1498c5b64a99SLinus Walleij /* We exit this state when charger is removed */ 1499c5b64a99SLinus Walleij break; 1500c5b64a99SLinus Walleij 1501c5b64a99SLinus Walleij case STATE_NORMAL_INIT: 1502c5b64a99SLinus Walleij if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW) 1503c5b64a99SLinus Walleij ab8500_chargalg_stop_charging(di); 1504c5b64a99SLinus Walleij else { 1505c5b64a99SLinus Walleij curr_step_lvl = di->bm->bat_type[ 1506c5b64a99SLinus Walleij di->bm->batt_id].normal_cur_lvl 1507c5b64a99SLinus Walleij * di->curr_status.curr_step 1508c5b64a99SLinus Walleij / CHARGALG_CURR_STEP_HIGH; 1509c5b64a99SLinus Walleij ab8500_chargalg_start_charging(di, 1510c5b64a99SLinus Walleij di->bm->bat_type[di->bm->batt_id] 1511c5b64a99SLinus Walleij .normal_vol_lvl, curr_step_lvl); 1512c5b64a99SLinus Walleij } 1513c5b64a99SLinus Walleij 1514c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL); 1515c5b64a99SLinus Walleij ab8500_chargalg_start_safety_timer(di); 1516c5b64a99SLinus Walleij ab8500_chargalg_stop_maintenance_timer(di); 1517c5b64a99SLinus Walleij init_maxim_chg_curr(di); 1518c5b64a99SLinus Walleij di->charge_status = POWER_SUPPLY_STATUS_CHARGING; 1519c5b64a99SLinus Walleij di->eoc_cnt = 0; 1520c5b64a99SLinus Walleij di->maintenance_chg = false; 1521c5b64a99SLinus Walleij power_supply_changed(di->chargalg_psy); 1522c5b64a99SLinus Walleij 1523c5b64a99SLinus Walleij break; 1524c5b64a99SLinus Walleij 1525c5b64a99SLinus Walleij case STATE_NORMAL: 1526c5b64a99SLinus Walleij handle_maxim_chg_curr(di); 1527c5b64a99SLinus Walleij if (di->charge_status == POWER_SUPPLY_STATUS_FULL && 1528c5b64a99SLinus Walleij di->maintenance_chg) { 1529c5b64a99SLinus Walleij if (di->bm->no_maintenance) 1530c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, 1531c5b64a99SLinus Walleij STATE_WAIT_FOR_RECHARGE_INIT); 1532c5b64a99SLinus Walleij else 1533c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, 1534c5b64a99SLinus Walleij STATE_MAINTENANCE_A_INIT); 1535c5b64a99SLinus Walleij } 1536c5b64a99SLinus Walleij break; 1537c5b64a99SLinus Walleij 1538c5b64a99SLinus Walleij /* This state will be used when the maintenance state is disabled */ 1539c5b64a99SLinus Walleij case STATE_WAIT_FOR_RECHARGE_INIT: 1540c5b64a99SLinus Walleij ab8500_chargalg_hold_charging(di); 1541c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_WAIT_FOR_RECHARGE); 1542c5b64a99SLinus Walleij fallthrough; 1543c5b64a99SLinus Walleij 1544c5b64a99SLinus Walleij case STATE_WAIT_FOR_RECHARGE: 1545c5b64a99SLinus Walleij if (di->batt_data.percent <= 1546c5b64a99SLinus Walleij di->bm->bat_type[di->bm->batt_id]. 1547c5b64a99SLinus Walleij recharge_cap) 1548c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 1549c5b64a99SLinus Walleij break; 1550c5b64a99SLinus Walleij 1551c5b64a99SLinus Walleij case STATE_MAINTENANCE_A_INIT: 1552c5b64a99SLinus Walleij ab8500_chargalg_stop_safety_timer(di); 1553c5b64a99SLinus Walleij ab8500_chargalg_start_maintenance_timer(di, 1554c5b64a99SLinus Walleij di->bm->bat_type[ 1555c5b64a99SLinus Walleij di->bm->batt_id].maint_a_chg_timer_h); 1556c5b64a99SLinus Walleij ab8500_chargalg_start_charging(di, 1557c5b64a99SLinus Walleij di->bm->bat_type[ 1558c5b64a99SLinus Walleij di->bm->batt_id].maint_a_vol_lvl, 1559c5b64a99SLinus Walleij di->bm->bat_type[ 1560c5b64a99SLinus Walleij di->bm->batt_id].maint_a_cur_lvl); 1561c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_MAINTENANCE_A); 1562c5b64a99SLinus Walleij power_supply_changed(di->chargalg_psy); 1563c5b64a99SLinus Walleij fallthrough; 1564c5b64a99SLinus Walleij 1565c5b64a99SLinus Walleij case STATE_MAINTENANCE_A: 1566c5b64a99SLinus Walleij if (di->events.maintenance_timer_expired) { 1567c5b64a99SLinus Walleij ab8500_chargalg_stop_maintenance_timer(di); 1568c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B_INIT); 1569c5b64a99SLinus Walleij } 1570c5b64a99SLinus Walleij break; 1571c5b64a99SLinus Walleij 1572c5b64a99SLinus Walleij case STATE_MAINTENANCE_B_INIT: 1573c5b64a99SLinus Walleij ab8500_chargalg_start_maintenance_timer(di, 1574c5b64a99SLinus Walleij di->bm->bat_type[ 1575c5b64a99SLinus Walleij di->bm->batt_id].maint_b_chg_timer_h); 1576c5b64a99SLinus Walleij ab8500_chargalg_start_charging(di, 1577c5b64a99SLinus Walleij di->bm->bat_type[ 1578c5b64a99SLinus Walleij di->bm->batt_id].maint_b_vol_lvl, 1579c5b64a99SLinus Walleij di->bm->bat_type[ 1580c5b64a99SLinus Walleij di->bm->batt_id].maint_b_cur_lvl); 1581c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_MAINTENANCE_B); 1582c5b64a99SLinus Walleij power_supply_changed(di->chargalg_psy); 1583c5b64a99SLinus Walleij fallthrough; 1584c5b64a99SLinus Walleij 1585c5b64a99SLinus Walleij case STATE_MAINTENANCE_B: 1586c5b64a99SLinus Walleij if (di->events.maintenance_timer_expired) { 1587c5b64a99SLinus Walleij ab8500_chargalg_stop_maintenance_timer(di); 1588c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 1589c5b64a99SLinus Walleij } 1590c5b64a99SLinus Walleij break; 1591c5b64a99SLinus Walleij 1592c5b64a99SLinus Walleij case STATE_TEMP_LOWHIGH_INIT: 1593c5b64a99SLinus Walleij ab8500_chargalg_start_charging(di, 1594c5b64a99SLinus Walleij di->bm->bat_type[ 1595c5b64a99SLinus Walleij di->bm->batt_id].low_high_vol_lvl, 1596c5b64a99SLinus Walleij di->bm->bat_type[ 1597c5b64a99SLinus Walleij di->bm->batt_id].low_high_cur_lvl); 1598c5b64a99SLinus Walleij ab8500_chargalg_stop_maintenance_timer(di); 1599c5b64a99SLinus Walleij di->charge_status = POWER_SUPPLY_STATUS_CHARGING; 1600c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_TEMP_LOWHIGH); 1601c5b64a99SLinus Walleij power_supply_changed(di->chargalg_psy); 1602c5b64a99SLinus Walleij fallthrough; 1603c5b64a99SLinus Walleij 1604c5b64a99SLinus Walleij case STATE_TEMP_LOWHIGH: 1605c5b64a99SLinus Walleij if (!di->events.btemp_lowhigh) 1606c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 1607c5b64a99SLinus Walleij break; 1608c5b64a99SLinus Walleij 1609c5b64a99SLinus Walleij case STATE_WD_EXPIRED_INIT: 1610c5b64a99SLinus Walleij ab8500_chargalg_stop_charging(di); 1611c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_WD_EXPIRED); 1612c5b64a99SLinus Walleij fallthrough; 1613c5b64a99SLinus Walleij 1614c5b64a99SLinus Walleij case STATE_WD_EXPIRED: 1615c5b64a99SLinus Walleij if (!di->events.ac_wd_expired && 1616c5b64a99SLinus Walleij !di->events.usb_wd_expired) 1617c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 1618c5b64a99SLinus Walleij break; 1619c5b64a99SLinus Walleij 1620c5b64a99SLinus Walleij case STATE_TEMP_UNDEROVER_INIT: 1621c5b64a99SLinus Walleij ab8500_chargalg_stop_charging(di); 1622c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_TEMP_UNDEROVER); 1623c5b64a99SLinus Walleij fallthrough; 1624c5b64a99SLinus Walleij 1625c5b64a99SLinus Walleij case STATE_TEMP_UNDEROVER: 1626c5b64a99SLinus Walleij if (!di->events.btemp_underover) 1627c5b64a99SLinus Walleij ab8500_chargalg_state_to(di, STATE_NORMAL_INIT); 1628c5b64a99SLinus Walleij break; 1629c5b64a99SLinus Walleij } 1630c5b64a99SLinus Walleij 1631c5b64a99SLinus Walleij /* Start charging directly if the new state is a charge state */ 1632c5b64a99SLinus Walleij if (di->charge_state == STATE_NORMAL_INIT || 1633c5b64a99SLinus Walleij di->charge_state == STATE_MAINTENANCE_A_INIT || 1634c5b64a99SLinus Walleij di->charge_state == STATE_MAINTENANCE_B_INIT) 1635c5b64a99SLinus Walleij queue_work(di->chargalg_wq, &di->chargalg_work); 1636c5b64a99SLinus Walleij } 1637c5b64a99SLinus Walleij 1638c5b64a99SLinus Walleij /** 1639c5b64a99SLinus Walleij * ab8500_chargalg_periodic_work() - Periodic work for the algorithm 1640c5b64a99SLinus Walleij * @work: pointer to the work_struct structure 1641c5b64a99SLinus Walleij * 1642c5b64a99SLinus Walleij * Work queue function for the charging algorithm 1643c5b64a99SLinus Walleij */ 1644c5b64a99SLinus Walleij static void ab8500_chargalg_periodic_work(struct work_struct *work) 1645c5b64a99SLinus Walleij { 1646c5b64a99SLinus Walleij struct ab8500_chargalg *di = container_of(work, 1647c5b64a99SLinus Walleij struct ab8500_chargalg, chargalg_periodic_work.work); 1648c5b64a99SLinus Walleij 1649c5b64a99SLinus Walleij ab8500_chargalg_algorithm(di); 1650c5b64a99SLinus Walleij 1651c5b64a99SLinus Walleij /* 1652c5b64a99SLinus Walleij * If a charger is connected then the battery has to be monitored 1653c5b64a99SLinus Walleij * frequently, else the work can be delayed. 1654c5b64a99SLinus Walleij */ 1655c5b64a99SLinus Walleij if (di->chg_info.conn_chg) 1656c5b64a99SLinus Walleij queue_delayed_work(di->chargalg_wq, 1657c5b64a99SLinus Walleij &di->chargalg_periodic_work, 1658c5b64a99SLinus Walleij di->bm->interval_charging * HZ); 1659c5b64a99SLinus Walleij else 1660c5b64a99SLinus Walleij queue_delayed_work(di->chargalg_wq, 1661c5b64a99SLinus Walleij &di->chargalg_periodic_work, 1662c5b64a99SLinus Walleij di->bm->interval_not_charging * HZ); 1663c5b64a99SLinus Walleij } 1664c5b64a99SLinus Walleij 1665c5b64a99SLinus Walleij /** 1666c5b64a99SLinus Walleij * ab8500_chargalg_wd_work() - periodic work to kick the charger watchdog 1667c5b64a99SLinus Walleij * @work: pointer to the work_struct structure 1668c5b64a99SLinus Walleij * 1669c5b64a99SLinus Walleij * Work queue function for kicking the charger watchdog 1670c5b64a99SLinus Walleij */ 1671c5b64a99SLinus Walleij static void ab8500_chargalg_wd_work(struct work_struct *work) 1672c5b64a99SLinus Walleij { 1673c5b64a99SLinus Walleij int ret; 1674c5b64a99SLinus Walleij struct ab8500_chargalg *di = container_of(work, 1675c5b64a99SLinus Walleij struct ab8500_chargalg, chargalg_wd_work.work); 1676c5b64a99SLinus Walleij 1677c5b64a99SLinus Walleij dev_dbg(di->dev, "ab8500_chargalg_wd_work\n"); 1678c5b64a99SLinus Walleij 1679c5b64a99SLinus Walleij ret = ab8500_chargalg_kick_watchdog(di); 1680c5b64a99SLinus Walleij if (ret < 0) 1681c5b64a99SLinus Walleij dev_err(di->dev, "failed to kick watchdog\n"); 1682c5b64a99SLinus Walleij 1683c5b64a99SLinus Walleij queue_delayed_work(di->chargalg_wq, 1684c5b64a99SLinus Walleij &di->chargalg_wd_work, CHG_WD_INTERVAL); 1685c5b64a99SLinus Walleij } 1686c5b64a99SLinus Walleij 1687c5b64a99SLinus Walleij /** 1688c5b64a99SLinus Walleij * ab8500_chargalg_work() - Work to run the charging algorithm instantly 1689c5b64a99SLinus Walleij * @work: pointer to the work_struct structure 1690c5b64a99SLinus Walleij * 1691c5b64a99SLinus Walleij * Work queue function for calling the charging algorithm 1692c5b64a99SLinus Walleij */ 1693c5b64a99SLinus Walleij static void ab8500_chargalg_work(struct work_struct *work) 1694c5b64a99SLinus Walleij { 1695c5b64a99SLinus Walleij struct ab8500_chargalg *di = container_of(work, 1696c5b64a99SLinus Walleij struct ab8500_chargalg, chargalg_work); 1697c5b64a99SLinus Walleij 1698c5b64a99SLinus Walleij ab8500_chargalg_algorithm(di); 1699c5b64a99SLinus Walleij } 1700c5b64a99SLinus Walleij 1701c5b64a99SLinus Walleij /** 1702c5b64a99SLinus Walleij * ab8500_chargalg_get_property() - get the chargalg properties 1703c5b64a99SLinus Walleij * @psy: pointer to the power_supply structure 1704c5b64a99SLinus Walleij * @psp: pointer to the power_supply_property structure 1705c5b64a99SLinus Walleij * @val: pointer to the power_supply_propval union 1706c5b64a99SLinus Walleij * 1707c5b64a99SLinus Walleij * This function gets called when an application tries to get the 1708c5b64a99SLinus Walleij * chargalg properties by reading the sysfs files. 1709c5b64a99SLinus Walleij * status: charging/discharging/full/unknown 1710c5b64a99SLinus Walleij * health: health of the battery 1711c5b64a99SLinus Walleij * Returns error code in case of failure else 0 on success 1712c5b64a99SLinus Walleij */ 1713c5b64a99SLinus Walleij static int ab8500_chargalg_get_property(struct power_supply *psy, 1714c5b64a99SLinus Walleij enum power_supply_property psp, 1715c5b64a99SLinus Walleij union power_supply_propval *val) 1716c5b64a99SLinus Walleij { 1717c5b64a99SLinus Walleij struct ab8500_chargalg *di = power_supply_get_drvdata(psy); 1718c5b64a99SLinus Walleij 1719c5b64a99SLinus Walleij switch (psp) { 1720c5b64a99SLinus Walleij case POWER_SUPPLY_PROP_STATUS: 1721c5b64a99SLinus Walleij val->intval = di->charge_status; 1722c5b64a99SLinus Walleij break; 1723c5b64a99SLinus Walleij case POWER_SUPPLY_PROP_HEALTH: 1724c5b64a99SLinus Walleij if (di->events.batt_ovv) { 1725c5b64a99SLinus Walleij val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 1726c5b64a99SLinus Walleij } else if (di->events.btemp_underover) { 1727c5b64a99SLinus Walleij if (di->batt_data.temp <= di->bm->temp_under) 1728c5b64a99SLinus Walleij val->intval = POWER_SUPPLY_HEALTH_COLD; 1729c5b64a99SLinus Walleij else 1730c5b64a99SLinus Walleij val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 1731c5b64a99SLinus Walleij } else if (di->charge_state == STATE_SAFETY_TIMER_EXPIRED || 1732c5b64a99SLinus Walleij di->charge_state == STATE_SAFETY_TIMER_EXPIRED_INIT) { 1733c5b64a99SLinus Walleij val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 1734c5b64a99SLinus Walleij } else { 1735c5b64a99SLinus Walleij val->intval = POWER_SUPPLY_HEALTH_GOOD; 1736c5b64a99SLinus Walleij } 1737c5b64a99SLinus Walleij break; 1738c5b64a99SLinus Walleij default: 1739c5b64a99SLinus Walleij return -EINVAL; 1740c5b64a99SLinus Walleij } 1741c5b64a99SLinus Walleij return 0; 1742c5b64a99SLinus Walleij } 1743c5b64a99SLinus Walleij 1744c5b64a99SLinus Walleij /* Exposure to the sysfs interface */ 1745c5b64a99SLinus Walleij 1746c5b64a99SLinus Walleij static ssize_t ab8500_chargalg_curr_step_show(struct ab8500_chargalg *di, 1747c5b64a99SLinus Walleij char *buf) 1748c5b64a99SLinus Walleij { 1749c5b64a99SLinus Walleij return sprintf(buf, "%d\n", di->curr_status.curr_step); 1750c5b64a99SLinus Walleij } 1751c5b64a99SLinus Walleij 1752c5b64a99SLinus Walleij static ssize_t ab8500_chargalg_curr_step_store(struct ab8500_chargalg *di, 1753c5b64a99SLinus Walleij const char *buf, size_t length) 1754c5b64a99SLinus Walleij { 1755c5b64a99SLinus Walleij long int param; 1756c5b64a99SLinus Walleij int ret; 1757c5b64a99SLinus Walleij 1758c5b64a99SLinus Walleij ret = kstrtol(buf, 10, ¶m); 1759c5b64a99SLinus Walleij if (ret < 0) 1760c5b64a99SLinus Walleij return ret; 1761c5b64a99SLinus Walleij 1762c5b64a99SLinus Walleij di->curr_status.curr_step = param; 1763c5b64a99SLinus Walleij if (di->curr_status.curr_step >= CHARGALG_CURR_STEP_LOW && 1764c5b64a99SLinus Walleij di->curr_status.curr_step <= CHARGALG_CURR_STEP_HIGH) { 1765c5b64a99SLinus Walleij di->curr_status.curr_step_change = true; 1766c5b64a99SLinus Walleij queue_work(di->chargalg_wq, &di->chargalg_work); 1767c5b64a99SLinus Walleij } else 1768c5b64a99SLinus Walleij dev_info(di->dev, "Wrong current step\n" 1769c5b64a99SLinus Walleij "Enter 0. Disable AC/USB Charging\n" 1770c5b64a99SLinus Walleij "1--100. Set AC/USB charging current step\n" 1771c5b64a99SLinus Walleij "100. Enable AC/USB Charging\n"); 1772c5b64a99SLinus Walleij 1773c5b64a99SLinus Walleij return strlen(buf); 1774c5b64a99SLinus Walleij } 1775c5b64a99SLinus Walleij 1776c5b64a99SLinus Walleij 1777c5b64a99SLinus Walleij static ssize_t ab8500_chargalg_en_show(struct ab8500_chargalg *di, 1778c5b64a99SLinus Walleij char *buf) 1779c5b64a99SLinus Walleij { 1780c5b64a99SLinus Walleij return sprintf(buf, "%d\n", 1781c5b64a99SLinus Walleij di->susp_status.ac_suspended && 1782c5b64a99SLinus Walleij di->susp_status.usb_suspended); 1783c5b64a99SLinus Walleij } 1784c5b64a99SLinus Walleij 1785c5b64a99SLinus Walleij static ssize_t ab8500_chargalg_en_store(struct ab8500_chargalg *di, 1786c5b64a99SLinus Walleij const char *buf, size_t length) 1787c5b64a99SLinus Walleij { 1788c5b64a99SLinus Walleij long int param; 1789c5b64a99SLinus Walleij int ac_usb; 1790c5b64a99SLinus Walleij int ret; 1791c5b64a99SLinus Walleij 1792c5b64a99SLinus Walleij ret = kstrtol(buf, 10, ¶m); 1793c5b64a99SLinus Walleij if (ret < 0) 1794c5b64a99SLinus Walleij return ret; 1795c5b64a99SLinus Walleij 1796c5b64a99SLinus Walleij ac_usb = param; 1797c5b64a99SLinus Walleij switch (ac_usb) { 1798c5b64a99SLinus Walleij case 0: 1799c5b64a99SLinus Walleij /* Disable charging */ 1800c5b64a99SLinus Walleij di->susp_status.ac_suspended = true; 1801c5b64a99SLinus Walleij di->susp_status.usb_suspended = true; 1802c5b64a99SLinus Walleij di->susp_status.suspended_change = true; 1803c5b64a99SLinus Walleij /* Trigger a state change */ 1804c5b64a99SLinus Walleij queue_work(di->chargalg_wq, 1805c5b64a99SLinus Walleij &di->chargalg_work); 1806c5b64a99SLinus Walleij break; 1807c5b64a99SLinus Walleij case 1: 1808c5b64a99SLinus Walleij /* Enable AC Charging */ 1809c5b64a99SLinus Walleij di->susp_status.ac_suspended = false; 1810c5b64a99SLinus Walleij di->susp_status.suspended_change = true; 1811c5b64a99SLinus Walleij /* Trigger a state change */ 1812c5b64a99SLinus Walleij queue_work(di->chargalg_wq, 1813c5b64a99SLinus Walleij &di->chargalg_work); 1814c5b64a99SLinus Walleij break; 1815c5b64a99SLinus Walleij case 2: 1816c5b64a99SLinus Walleij /* Enable USB charging */ 1817c5b64a99SLinus Walleij di->susp_status.usb_suspended = false; 1818c5b64a99SLinus Walleij di->susp_status.suspended_change = true; 1819c5b64a99SLinus Walleij /* Trigger a state change */ 1820c5b64a99SLinus Walleij queue_work(di->chargalg_wq, 1821c5b64a99SLinus Walleij &di->chargalg_work); 1822c5b64a99SLinus Walleij break; 1823c5b64a99SLinus Walleij default: 1824c5b64a99SLinus Walleij dev_info(di->dev, "Wrong input\n" 1825c5b64a99SLinus Walleij "Enter 0. Disable AC/USB Charging\n" 1826c5b64a99SLinus Walleij "1. Enable AC charging\n" 1827c5b64a99SLinus Walleij "2. Enable USB Charging\n"); 1828c5b64a99SLinus Walleij } 1829c5b64a99SLinus Walleij return strlen(buf); 1830c5b64a99SLinus Walleij } 1831c5b64a99SLinus Walleij 1832c5b64a99SLinus Walleij static struct ab8500_chargalg_sysfs_entry ab8500_chargalg_en_charger = 1833c5b64a99SLinus Walleij __ATTR(chargalg, 0644, ab8500_chargalg_en_show, 1834c5b64a99SLinus Walleij ab8500_chargalg_en_store); 1835c5b64a99SLinus Walleij 1836c5b64a99SLinus Walleij static struct ab8500_chargalg_sysfs_entry ab8500_chargalg_curr_step = 1837c5b64a99SLinus Walleij __ATTR(chargalg_curr_step, 0644, ab8500_chargalg_curr_step_show, 1838c5b64a99SLinus Walleij ab8500_chargalg_curr_step_store); 1839c5b64a99SLinus Walleij 1840c5b64a99SLinus Walleij static ssize_t ab8500_chargalg_sysfs_show(struct kobject *kobj, 1841c5b64a99SLinus Walleij struct attribute *attr, char *buf) 1842c5b64a99SLinus Walleij { 1843c5b64a99SLinus Walleij struct ab8500_chargalg_sysfs_entry *entry = container_of(attr, 1844c5b64a99SLinus Walleij struct ab8500_chargalg_sysfs_entry, attr); 1845c5b64a99SLinus Walleij 1846c5b64a99SLinus Walleij struct ab8500_chargalg *di = container_of(kobj, 1847c5b64a99SLinus Walleij struct ab8500_chargalg, chargalg_kobject); 1848c5b64a99SLinus Walleij 1849c5b64a99SLinus Walleij if (!entry->show) 1850c5b64a99SLinus Walleij return -EIO; 1851c5b64a99SLinus Walleij 1852c5b64a99SLinus Walleij return entry->show(di, buf); 1853c5b64a99SLinus Walleij } 1854c5b64a99SLinus Walleij 1855c5b64a99SLinus Walleij static ssize_t ab8500_chargalg_sysfs_charger(struct kobject *kobj, 1856c5b64a99SLinus Walleij struct attribute *attr, const char *buf, size_t length) 1857c5b64a99SLinus Walleij { 1858c5b64a99SLinus Walleij struct ab8500_chargalg_sysfs_entry *entry = container_of(attr, 1859c5b64a99SLinus Walleij struct ab8500_chargalg_sysfs_entry, attr); 1860c5b64a99SLinus Walleij 1861c5b64a99SLinus Walleij struct ab8500_chargalg *di = container_of(kobj, 1862c5b64a99SLinus Walleij struct ab8500_chargalg, chargalg_kobject); 1863c5b64a99SLinus Walleij 1864c5b64a99SLinus Walleij if (!entry->store) 1865c5b64a99SLinus Walleij return -EIO; 1866c5b64a99SLinus Walleij 1867c5b64a99SLinus Walleij return entry->store(di, buf, length); 1868c5b64a99SLinus Walleij } 1869c5b64a99SLinus Walleij 1870c5b64a99SLinus Walleij static struct attribute *ab8500_chargalg_chg[] = { 1871c5b64a99SLinus Walleij &ab8500_chargalg_en_charger.attr, 1872c5b64a99SLinus Walleij &ab8500_chargalg_curr_step.attr, 1873c5b64a99SLinus Walleij NULL, 1874c5b64a99SLinus Walleij }; 1875c5b64a99SLinus Walleij 1876c5b64a99SLinus Walleij static const struct sysfs_ops ab8500_chargalg_sysfs_ops = { 1877c5b64a99SLinus Walleij .show = ab8500_chargalg_sysfs_show, 1878c5b64a99SLinus Walleij .store = ab8500_chargalg_sysfs_charger, 1879c5b64a99SLinus Walleij }; 1880c5b64a99SLinus Walleij 1881c5b64a99SLinus Walleij static struct kobj_type ab8500_chargalg_ktype = { 1882c5b64a99SLinus Walleij .sysfs_ops = &ab8500_chargalg_sysfs_ops, 1883c5b64a99SLinus Walleij .default_attrs = ab8500_chargalg_chg, 1884c5b64a99SLinus Walleij }; 1885c5b64a99SLinus Walleij 1886c5b64a99SLinus Walleij /** 1887c5b64a99SLinus Walleij * ab8500_chargalg_sysfs_exit() - de-init of sysfs entry 1888c5b64a99SLinus Walleij * @di: pointer to the struct ab8500_chargalg 1889c5b64a99SLinus Walleij * 1890c5b64a99SLinus Walleij * This function removes the entry in sysfs. 1891c5b64a99SLinus Walleij */ 1892c5b64a99SLinus Walleij static void ab8500_chargalg_sysfs_exit(struct ab8500_chargalg *di) 1893c5b64a99SLinus Walleij { 1894c5b64a99SLinus Walleij kobject_del(&di->chargalg_kobject); 1895c5b64a99SLinus Walleij } 1896c5b64a99SLinus Walleij 1897c5b64a99SLinus Walleij /** 1898c5b64a99SLinus Walleij * ab8500_chargalg_sysfs_init() - init of sysfs entry 1899c5b64a99SLinus Walleij * @di: pointer to the struct ab8500_chargalg 1900c5b64a99SLinus Walleij * 1901c5b64a99SLinus Walleij * This function adds an entry in sysfs. 1902c5b64a99SLinus Walleij * Returns error code in case of failure else 0(on success) 1903c5b64a99SLinus Walleij */ 1904c5b64a99SLinus Walleij static int ab8500_chargalg_sysfs_init(struct ab8500_chargalg *di) 1905c5b64a99SLinus Walleij { 1906c5b64a99SLinus Walleij int ret = 0; 1907c5b64a99SLinus Walleij 1908c5b64a99SLinus Walleij ret = kobject_init_and_add(&di->chargalg_kobject, 1909c5b64a99SLinus Walleij &ab8500_chargalg_ktype, 1910c5b64a99SLinus Walleij NULL, "ab8500_chargalg"); 1911c5b64a99SLinus Walleij if (ret < 0) 1912c5b64a99SLinus Walleij dev_err(di->dev, "failed to create sysfs entry\n"); 1913c5b64a99SLinus Walleij 1914c5b64a99SLinus Walleij return ret; 1915c5b64a99SLinus Walleij } 1916c5b64a99SLinus Walleij /* Exposure to the sysfs interface <<END>> */ 1917c5b64a99SLinus Walleij 1918c5b64a99SLinus Walleij static int __maybe_unused ab8500_chargalg_resume(struct device *dev) 1919c5b64a99SLinus Walleij { 1920c5b64a99SLinus Walleij struct ab8500_chargalg *di = dev_get_drvdata(dev); 1921c5b64a99SLinus Walleij 1922c5b64a99SLinus Walleij /* Kick charger watchdog if charging (any charger online) */ 1923c5b64a99SLinus Walleij if (di->chg_info.online_chg) 1924c5b64a99SLinus Walleij queue_delayed_work(di->chargalg_wq, &di->chargalg_wd_work, 0); 1925c5b64a99SLinus Walleij 1926c5b64a99SLinus Walleij /* 1927c5b64a99SLinus Walleij * Run the charging algorithm directly to be sure we don't 1928c5b64a99SLinus Walleij * do it too seldom 1929c5b64a99SLinus Walleij */ 1930c5b64a99SLinus Walleij queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0); 1931c5b64a99SLinus Walleij 1932c5b64a99SLinus Walleij return 0; 1933c5b64a99SLinus Walleij } 1934c5b64a99SLinus Walleij 1935c5b64a99SLinus Walleij static int __maybe_unused ab8500_chargalg_suspend(struct device *dev) 1936c5b64a99SLinus Walleij { 1937c5b64a99SLinus Walleij struct ab8500_chargalg *di = dev_get_drvdata(dev); 1938c5b64a99SLinus Walleij 1939c5b64a99SLinus Walleij if (di->chg_info.online_chg) 1940c5b64a99SLinus Walleij cancel_delayed_work_sync(&di->chargalg_wd_work); 1941c5b64a99SLinus Walleij 1942c5b64a99SLinus Walleij cancel_delayed_work_sync(&di->chargalg_periodic_work); 1943c5b64a99SLinus Walleij 1944c5b64a99SLinus Walleij return 0; 1945c5b64a99SLinus Walleij } 1946c5b64a99SLinus Walleij 1947c5b64a99SLinus Walleij static char *supply_interface[] = { 1948c5b64a99SLinus Walleij "ab8500_fg", 1949c5b64a99SLinus Walleij }; 1950c5b64a99SLinus Walleij 1951c5b64a99SLinus Walleij static const struct power_supply_desc ab8500_chargalg_desc = { 1952c5b64a99SLinus Walleij .name = "abx500_chargalg", 1953c5b64a99SLinus Walleij .type = POWER_SUPPLY_TYPE_BATTERY, 1954c5b64a99SLinus Walleij .properties = ab8500_chargalg_props, 1955c5b64a99SLinus Walleij .num_properties = ARRAY_SIZE(ab8500_chargalg_props), 1956c5b64a99SLinus Walleij .get_property = ab8500_chargalg_get_property, 1957c5b64a99SLinus Walleij .external_power_changed = ab8500_chargalg_external_power_changed, 1958c5b64a99SLinus Walleij }; 1959c5b64a99SLinus Walleij 1960c5b64a99SLinus Walleij static int ab8500_chargalg_bind(struct device *dev, struct device *master, 1961c5b64a99SLinus Walleij void *data) 1962c5b64a99SLinus Walleij { 1963c5b64a99SLinus Walleij struct ab8500_chargalg *di = dev_get_drvdata(dev); 1964c5b64a99SLinus Walleij 1965c5b64a99SLinus Walleij /* Create a work queue for the chargalg */ 1966c5b64a99SLinus Walleij di->chargalg_wq = alloc_ordered_workqueue("ab8500_chargalg_wq", 1967c5b64a99SLinus Walleij WQ_MEM_RECLAIM); 1968c5b64a99SLinus Walleij if (di->chargalg_wq == NULL) { 1969c5b64a99SLinus Walleij dev_err(di->dev, "failed to create work queue\n"); 1970c5b64a99SLinus Walleij return -ENOMEM; 1971c5b64a99SLinus Walleij } 1972c5b64a99SLinus Walleij 1973c5b64a99SLinus Walleij /* Run the charging algorithm */ 1974c5b64a99SLinus Walleij queue_delayed_work(di->chargalg_wq, &di->chargalg_periodic_work, 0); 1975c5b64a99SLinus Walleij 1976c5b64a99SLinus Walleij return 0; 1977c5b64a99SLinus Walleij } 1978c5b64a99SLinus Walleij 1979c5b64a99SLinus Walleij static void ab8500_chargalg_unbind(struct device *dev, struct device *master, 1980c5b64a99SLinus Walleij void *data) 1981c5b64a99SLinus Walleij { 1982c5b64a99SLinus Walleij struct ab8500_chargalg *di = dev_get_drvdata(dev); 1983c5b64a99SLinus Walleij 1984c5b64a99SLinus Walleij /* Stop all timers and work */ 1985c5b64a99SLinus Walleij hrtimer_cancel(&di->safety_timer); 1986c5b64a99SLinus Walleij hrtimer_cancel(&di->maintenance_timer); 1987c5b64a99SLinus Walleij 1988c5b64a99SLinus Walleij cancel_delayed_work_sync(&di->chargalg_periodic_work); 1989c5b64a99SLinus Walleij cancel_delayed_work_sync(&di->chargalg_wd_work); 1990c5b64a99SLinus Walleij cancel_work_sync(&di->chargalg_work); 1991c5b64a99SLinus Walleij 1992c5b64a99SLinus Walleij /* Delete the work queue */ 1993c5b64a99SLinus Walleij destroy_workqueue(di->chargalg_wq); 1994c5b64a99SLinus Walleij flush_scheduled_work(); 1995c5b64a99SLinus Walleij } 1996c5b64a99SLinus Walleij 1997c5b64a99SLinus Walleij static const struct component_ops ab8500_chargalg_component_ops = { 1998c5b64a99SLinus Walleij .bind = ab8500_chargalg_bind, 1999c5b64a99SLinus Walleij .unbind = ab8500_chargalg_unbind, 2000c5b64a99SLinus Walleij }; 2001c5b64a99SLinus Walleij 2002c5b64a99SLinus Walleij static int ab8500_chargalg_probe(struct platform_device *pdev) 2003c5b64a99SLinus Walleij { 2004c5b64a99SLinus Walleij struct device *dev = &pdev->dev; 2005c5b64a99SLinus Walleij struct power_supply_config psy_cfg = {}; 2006c5b64a99SLinus Walleij struct ab8500_chargalg *di; 2007c5b64a99SLinus Walleij int ret = 0; 2008c5b64a99SLinus Walleij 2009c5b64a99SLinus Walleij di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL); 2010c5b64a99SLinus Walleij if (!di) 2011c5b64a99SLinus Walleij return -ENOMEM; 2012c5b64a99SLinus Walleij 2013c5b64a99SLinus Walleij di->bm = &ab8500_bm_data; 2014c5b64a99SLinus Walleij 2015c5b64a99SLinus Walleij /* get device struct and parent */ 2016c5b64a99SLinus Walleij di->dev = dev; 2017c5b64a99SLinus Walleij di->parent = dev_get_drvdata(pdev->dev.parent); 2018c5b64a99SLinus Walleij 2019c5b64a99SLinus Walleij psy_cfg.supplied_to = supply_interface; 2020c5b64a99SLinus Walleij psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface); 2021c5b64a99SLinus Walleij psy_cfg.drv_data = di; 2022c5b64a99SLinus Walleij 2023c5b64a99SLinus Walleij /* Initilialize safety timer */ 2024c5b64a99SLinus Walleij hrtimer_init(&di->safety_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); 2025c5b64a99SLinus Walleij di->safety_timer.function = ab8500_chargalg_safety_timer_expired; 2026c5b64a99SLinus Walleij 2027c5b64a99SLinus Walleij /* Initilialize maintenance timer */ 2028c5b64a99SLinus Walleij hrtimer_init(&di->maintenance_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); 2029c5b64a99SLinus Walleij di->maintenance_timer.function = 2030c5b64a99SLinus Walleij ab8500_chargalg_maintenance_timer_expired; 2031c5b64a99SLinus Walleij 2032c5b64a99SLinus Walleij /* Init work for chargalg */ 2033c5b64a99SLinus Walleij INIT_DEFERRABLE_WORK(&di->chargalg_periodic_work, 2034c5b64a99SLinus Walleij ab8500_chargalg_periodic_work); 2035c5b64a99SLinus Walleij INIT_DEFERRABLE_WORK(&di->chargalg_wd_work, 2036c5b64a99SLinus Walleij ab8500_chargalg_wd_work); 2037c5b64a99SLinus Walleij 2038c5b64a99SLinus Walleij /* Init work for chargalg */ 2039c5b64a99SLinus Walleij INIT_WORK(&di->chargalg_work, ab8500_chargalg_work); 2040c5b64a99SLinus Walleij 2041c5b64a99SLinus Walleij /* To detect charger at startup */ 2042c5b64a99SLinus Walleij di->chg_info.prev_conn_chg = -1; 2043c5b64a99SLinus Walleij 2044c5b64a99SLinus Walleij /* Register chargalg power supply class */ 2045c5b64a99SLinus Walleij di->chargalg_psy = devm_power_supply_register(di->dev, 2046c5b64a99SLinus Walleij &ab8500_chargalg_desc, 2047c5b64a99SLinus Walleij &psy_cfg); 2048c5b64a99SLinus Walleij if (IS_ERR(di->chargalg_psy)) { 2049c5b64a99SLinus Walleij dev_err(di->dev, "failed to register chargalg psy\n"); 2050c5b64a99SLinus Walleij return PTR_ERR(di->chargalg_psy); 2051c5b64a99SLinus Walleij } 2052c5b64a99SLinus Walleij 2053c5b64a99SLinus Walleij platform_set_drvdata(pdev, di); 2054c5b64a99SLinus Walleij 2055c5b64a99SLinus Walleij /* sysfs interface to enable/disable charging from user space */ 2056c5b64a99SLinus Walleij ret = ab8500_chargalg_sysfs_init(di); 2057c5b64a99SLinus Walleij if (ret) { 2058c5b64a99SLinus Walleij dev_err(di->dev, "failed to create sysfs entry\n"); 2059c5b64a99SLinus Walleij return ret; 2060c5b64a99SLinus Walleij } 2061c5b64a99SLinus Walleij di->curr_status.curr_step = CHARGALG_CURR_STEP_HIGH; 2062c5b64a99SLinus Walleij 2063c5b64a99SLinus Walleij dev_info(di->dev, "probe success\n"); 2064c5b64a99SLinus Walleij return component_add(dev, &ab8500_chargalg_component_ops); 2065c5b64a99SLinus Walleij } 2066c5b64a99SLinus Walleij 2067c5b64a99SLinus Walleij static int ab8500_chargalg_remove(struct platform_device *pdev) 2068c5b64a99SLinus Walleij { 2069c5b64a99SLinus Walleij struct ab8500_chargalg *di = platform_get_drvdata(pdev); 2070c5b64a99SLinus Walleij 2071c5b64a99SLinus Walleij component_del(&pdev->dev, &ab8500_chargalg_component_ops); 2072c5b64a99SLinus Walleij 2073c5b64a99SLinus Walleij /* sysfs interface to enable/disable charging from user space */ 2074c5b64a99SLinus Walleij ab8500_chargalg_sysfs_exit(di); 2075c5b64a99SLinus Walleij 2076c5b64a99SLinus Walleij return 0; 2077c5b64a99SLinus Walleij } 2078c5b64a99SLinus Walleij 2079c5b64a99SLinus Walleij static SIMPLE_DEV_PM_OPS(ab8500_chargalg_pm_ops, ab8500_chargalg_suspend, ab8500_chargalg_resume); 2080c5b64a99SLinus Walleij 2081c5b64a99SLinus Walleij static const struct of_device_id ab8500_chargalg_match[] = { 2082c5b64a99SLinus Walleij { .compatible = "stericsson,ab8500-chargalg", }, 2083c5b64a99SLinus Walleij { }, 2084c5b64a99SLinus Walleij }; 2085c5b64a99SLinus Walleij 2086c5b64a99SLinus Walleij struct platform_driver ab8500_chargalg_driver = { 2087c5b64a99SLinus Walleij .probe = ab8500_chargalg_probe, 2088c5b64a99SLinus Walleij .remove = ab8500_chargalg_remove, 2089c5b64a99SLinus Walleij .driver = { 2090c5b64a99SLinus Walleij .name = "ab8500_chargalg", 2091c5b64a99SLinus Walleij .of_match_table = ab8500_chargalg_match, 2092c5b64a99SLinus Walleij .pm = &ab8500_chargalg_pm_ops, 2093c5b64a99SLinus Walleij }, 2094c5b64a99SLinus Walleij }; 2095c5b64a99SLinus Walleij MODULE_LICENSE("GPL v2"); 2096c5b64a99SLinus Walleij MODULE_AUTHOR("Johan Palsson, Karl Komierowski"); 2097c5b64a99SLinus Walleij MODULE_ALIAS("platform:ab8500-chargalg"); 2098c5b64a99SLinus Walleij MODULE_DESCRIPTION("ab8500 battery charging algorithm"); 2099