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