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