18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2012
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Charger driver for AB8500
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author:
88c2ecf20Sopenharmony_ci *	Johan Palsson <johan.palsson@stericsson.com>
98c2ecf20Sopenharmony_ci *	Karl Komierowski <karl.komierowski@stericsson.com>
108c2ecf20Sopenharmony_ci *	Arun R Murthy <arun.murthy@stericsson.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/device.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/delay.h>
188c2ecf20Sopenharmony_ci#include <linux/notifier.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
228c2ecf20Sopenharmony_ci#include <linux/completion.h>
238c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
248c2ecf20Sopenharmony_ci#include <linux/err.h>
258c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
268c2ecf20Sopenharmony_ci#include <linux/kobject.h>
278c2ecf20Sopenharmony_ci#include <linux/of.h>
288c2ecf20Sopenharmony_ci#include <linux/mfd/core.h>
298c2ecf20Sopenharmony_ci#include <linux/mfd/abx500/ab8500.h>
308c2ecf20Sopenharmony_ci#include <linux/mfd/abx500.h>
318c2ecf20Sopenharmony_ci#include <linux/mfd/abx500/ab8500-bm.h>
328c2ecf20Sopenharmony_ci#include <linux/mfd/abx500/ux500_chargalg.h>
338c2ecf20Sopenharmony_ci#include <linux/usb/otg.h>
348c2ecf20Sopenharmony_ci#include <linux/mutex.h>
358c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* Charger constants */
388c2ecf20Sopenharmony_ci#define NO_PW_CONN			0
398c2ecf20Sopenharmony_ci#define AC_PW_CONN			1
408c2ecf20Sopenharmony_ci#define USB_PW_CONN			2
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define MAIN_WDOG_ENA			0x01
438c2ecf20Sopenharmony_ci#define MAIN_WDOG_KICK			0x02
448c2ecf20Sopenharmony_ci#define MAIN_WDOG_DIS			0x00
458c2ecf20Sopenharmony_ci#define CHARG_WD_KICK			0x01
468c2ecf20Sopenharmony_ci#define MAIN_CH_ENA			0x01
478c2ecf20Sopenharmony_ci#define MAIN_CH_NO_OVERSHOOT_ENA_N	0x02
488c2ecf20Sopenharmony_ci#define USB_CH_ENA			0x01
498c2ecf20Sopenharmony_ci#define USB_CHG_NO_OVERSHOOT_ENA_N	0x02
508c2ecf20Sopenharmony_ci#define MAIN_CH_DET			0x01
518c2ecf20Sopenharmony_ci#define MAIN_CH_CV_ON			0x04
528c2ecf20Sopenharmony_ci#define USB_CH_CV_ON			0x08
538c2ecf20Sopenharmony_ci#define VBUS_DET_DBNC100		0x02
548c2ecf20Sopenharmony_ci#define VBUS_DET_DBNC1			0x01
558c2ecf20Sopenharmony_ci#define OTP_ENABLE_WD			0x01
568c2ecf20Sopenharmony_ci#define DROP_COUNT_RESET		0x01
578c2ecf20Sopenharmony_ci#define USB_CH_DET			0x01
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define MAIN_CH_INPUT_CURR_SHIFT	4
608c2ecf20Sopenharmony_ci#define VBUS_IN_CURR_LIM_SHIFT		4
618c2ecf20Sopenharmony_ci#define AUTO_VBUS_IN_CURR_LIM_SHIFT	4
628c2ecf20Sopenharmony_ci#define VBUS_IN_CURR_LIM_RETRY_SET_TIME	30 /* seconds */
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define LED_INDICATOR_PWM_ENA		0x01
658c2ecf20Sopenharmony_ci#define LED_INDICATOR_PWM_DIS		0x00
668c2ecf20Sopenharmony_ci#define LED_IND_CUR_5MA			0x04
678c2ecf20Sopenharmony_ci#define LED_INDICATOR_PWM_DUTY_252_256	0xBF
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/* HW failure constants */
708c2ecf20Sopenharmony_ci#define MAIN_CH_TH_PROT			0x02
718c2ecf20Sopenharmony_ci#define VBUS_CH_NOK			0x08
728c2ecf20Sopenharmony_ci#define USB_CH_TH_PROT			0x02
738c2ecf20Sopenharmony_ci#define VBUS_OVV_TH			0x01
748c2ecf20Sopenharmony_ci#define MAIN_CH_NOK			0x01
758c2ecf20Sopenharmony_ci#define VBUS_DET			0x80
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci#define MAIN_CH_STATUS2_MAINCHGDROP		0x80
788c2ecf20Sopenharmony_ci#define MAIN_CH_STATUS2_MAINCHARGERDETDBNC	0x40
798c2ecf20Sopenharmony_ci#define USB_CH_VBUSDROP				0x40
808c2ecf20Sopenharmony_ci#define USB_CH_VBUSDETDBNC			0x01
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/* UsbLineStatus register bit masks */
838c2ecf20Sopenharmony_ci#define AB8500_USB_LINK_STATUS		0x78
848c2ecf20Sopenharmony_ci#define AB8505_USB_LINK_STATUS		0xF8
858c2ecf20Sopenharmony_ci#define AB8500_STD_HOST_SUSP		0x18
868c2ecf20Sopenharmony_ci#define USB_LINK_STATUS_SHIFT		3
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/* Watchdog timeout constant */
898c2ecf20Sopenharmony_ci#define WD_TIMER			0x30 /* 4min */
908c2ecf20Sopenharmony_ci#define WD_KICK_INTERVAL		(60 * HZ)
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* Lowest charger voltage is 3.39V -> 0x4E */
938c2ecf20Sopenharmony_ci#define LOW_VOLT_REG			0x4E
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/* Step up/down delay in us */
968c2ecf20Sopenharmony_ci#define STEP_UDELAY			1000
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define CHARGER_STATUS_POLL 10 /* in ms */
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#define CHG_WD_INTERVAL			(60 * HZ)
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#define AB8500_SW_CONTROL_FALLBACK	0x03
1038c2ecf20Sopenharmony_ci/* Wait for enumeration before charing in us */
1048c2ecf20Sopenharmony_ci#define WAIT_ACA_RID_ENUMERATION	(5 * 1000)
1058c2ecf20Sopenharmony_ci/*External charger control*/
1068c2ecf20Sopenharmony_ci#define AB8500_SYS_CHARGER_CONTROL_REG		0x52
1078c2ecf20Sopenharmony_ci#define EXTERNAL_CHARGER_DISABLE_REG_VAL	0x03
1088c2ecf20Sopenharmony_ci#define EXTERNAL_CHARGER_ENABLE_REG_VAL		0x07
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/* UsbLineStatus register - usb types */
1118c2ecf20Sopenharmony_cienum ab8500_charger_link_status {
1128c2ecf20Sopenharmony_ci	USB_STAT_NOT_CONFIGURED,
1138c2ecf20Sopenharmony_ci	USB_STAT_STD_HOST_NC,
1148c2ecf20Sopenharmony_ci	USB_STAT_STD_HOST_C_NS,
1158c2ecf20Sopenharmony_ci	USB_STAT_STD_HOST_C_S,
1168c2ecf20Sopenharmony_ci	USB_STAT_HOST_CHG_NM,
1178c2ecf20Sopenharmony_ci	USB_STAT_HOST_CHG_HS,
1188c2ecf20Sopenharmony_ci	USB_STAT_HOST_CHG_HS_CHIRP,
1198c2ecf20Sopenharmony_ci	USB_STAT_DEDICATED_CHG,
1208c2ecf20Sopenharmony_ci	USB_STAT_ACA_RID_A,
1218c2ecf20Sopenharmony_ci	USB_STAT_ACA_RID_B,
1228c2ecf20Sopenharmony_ci	USB_STAT_ACA_RID_C_NM,
1238c2ecf20Sopenharmony_ci	USB_STAT_ACA_RID_C_HS,
1248c2ecf20Sopenharmony_ci	USB_STAT_ACA_RID_C_HS_CHIRP,
1258c2ecf20Sopenharmony_ci	USB_STAT_HM_IDGND,
1268c2ecf20Sopenharmony_ci	USB_STAT_RESERVED,
1278c2ecf20Sopenharmony_ci	USB_STAT_NOT_VALID_LINK,
1288c2ecf20Sopenharmony_ci	USB_STAT_PHY_EN,
1298c2ecf20Sopenharmony_ci	USB_STAT_SUP_NO_IDGND_VBUS,
1308c2ecf20Sopenharmony_ci	USB_STAT_SUP_IDGND_VBUS,
1318c2ecf20Sopenharmony_ci	USB_STAT_CHARGER_LINE_1,
1328c2ecf20Sopenharmony_ci	USB_STAT_CARKIT_1,
1338c2ecf20Sopenharmony_ci	USB_STAT_CARKIT_2,
1348c2ecf20Sopenharmony_ci	USB_STAT_ACA_DOCK_CHARGER,
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cienum ab8500_usb_state {
1388c2ecf20Sopenharmony_ci	AB8500_BM_USB_STATE_RESET_HS,	/* HighSpeed Reset */
1398c2ecf20Sopenharmony_ci	AB8500_BM_USB_STATE_RESET_FS,	/* FullSpeed/LowSpeed Reset */
1408c2ecf20Sopenharmony_ci	AB8500_BM_USB_STATE_CONFIGURED,
1418c2ecf20Sopenharmony_ci	AB8500_BM_USB_STATE_SUSPEND,
1428c2ecf20Sopenharmony_ci	AB8500_BM_USB_STATE_RESUME,
1438c2ecf20Sopenharmony_ci	AB8500_BM_USB_STATE_MAX,
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/* VBUS input current limits supported in AB8500 in mA */
1478c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_0P05		50
1488c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_0P09		98
1498c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_0P19		193
1508c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_0P29		290
1518c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_0P38		380
1528c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_0P45		450
1538c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_0P5		500
1548c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_0P6		600
1558c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_0P7		700
1568c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_0P8		800
1578c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_0P9		900
1588c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_1P0		1000
1598c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_1P1		1100
1608c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_1P3		1300
1618c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_1P4		1400
1628c2ecf20Sopenharmony_ci#define USB_CH_IP_CUR_LVL_1P5		1500
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci#define VBAT_TRESH_IP_CUR_RED		3800
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci#define to_ab8500_charger_usb_device_info(x) container_of((x), \
1678c2ecf20Sopenharmony_ci	struct ab8500_charger, usb_chg)
1688c2ecf20Sopenharmony_ci#define to_ab8500_charger_ac_device_info(x) container_of((x), \
1698c2ecf20Sopenharmony_ci	struct ab8500_charger, ac_chg)
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/**
1728c2ecf20Sopenharmony_ci * struct ab8500_charger_interrupts - ab8500 interupts
1738c2ecf20Sopenharmony_ci * @name:	name of the interrupt
1748c2ecf20Sopenharmony_ci * @isr		function pointer to the isr
1758c2ecf20Sopenharmony_ci */
1768c2ecf20Sopenharmony_cistruct ab8500_charger_interrupts {
1778c2ecf20Sopenharmony_ci	char *name;
1788c2ecf20Sopenharmony_ci	irqreturn_t (*isr)(int irq, void *data);
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistruct ab8500_charger_info {
1828c2ecf20Sopenharmony_ci	int charger_connected;
1838c2ecf20Sopenharmony_ci	int charger_online;
1848c2ecf20Sopenharmony_ci	int charger_voltage;
1858c2ecf20Sopenharmony_ci	int cv_active;
1868c2ecf20Sopenharmony_ci	bool wd_expired;
1878c2ecf20Sopenharmony_ci	int charger_current;
1888c2ecf20Sopenharmony_ci};
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistruct ab8500_charger_event_flags {
1918c2ecf20Sopenharmony_ci	bool mainextchnotok;
1928c2ecf20Sopenharmony_ci	bool main_thermal_prot;
1938c2ecf20Sopenharmony_ci	bool usb_thermal_prot;
1948c2ecf20Sopenharmony_ci	bool vbus_ovv;
1958c2ecf20Sopenharmony_ci	bool usbchargernotok;
1968c2ecf20Sopenharmony_ci	bool chgwdexp;
1978c2ecf20Sopenharmony_ci	bool vbus_collapse;
1988c2ecf20Sopenharmony_ci	bool vbus_drop_end;
1998c2ecf20Sopenharmony_ci};
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistruct ab8500_charger_usb_state {
2028c2ecf20Sopenharmony_ci	int usb_current;
2038c2ecf20Sopenharmony_ci	int usb_current_tmp;
2048c2ecf20Sopenharmony_ci	enum ab8500_usb_state state;
2058c2ecf20Sopenharmony_ci	enum ab8500_usb_state state_tmp;
2068c2ecf20Sopenharmony_ci	spinlock_t usb_lock;
2078c2ecf20Sopenharmony_ci};
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistruct ab8500_charger_max_usb_in_curr {
2108c2ecf20Sopenharmony_ci	int usb_type_max;
2118c2ecf20Sopenharmony_ci	int set_max;
2128c2ecf20Sopenharmony_ci	int calculated_max;
2138c2ecf20Sopenharmony_ci};
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci/**
2168c2ecf20Sopenharmony_ci * struct ab8500_charger - ab8500 Charger device information
2178c2ecf20Sopenharmony_ci * @dev:		Pointer to the structure device
2188c2ecf20Sopenharmony_ci * @vbus_detected:	VBUS detected
2198c2ecf20Sopenharmony_ci * @vbus_detected_start:
2208c2ecf20Sopenharmony_ci *			VBUS detected during startup
2218c2ecf20Sopenharmony_ci * @ac_conn:		This will be true when the AC charger has been plugged
2228c2ecf20Sopenharmony_ci * @vddadc_en_ac:	Indicate if VDD ADC supply is enabled because AC
2238c2ecf20Sopenharmony_ci *			charger is enabled
2248c2ecf20Sopenharmony_ci * @vddadc_en_usb:	Indicate if VDD ADC supply is enabled because USB
2258c2ecf20Sopenharmony_ci *			charger is enabled
2268c2ecf20Sopenharmony_ci * @vbat		Battery voltage
2278c2ecf20Sopenharmony_ci * @old_vbat		Previously measured battery voltage
2288c2ecf20Sopenharmony_ci * @usb_device_is_unrecognised	USB device is unrecognised by the hardware
2298c2ecf20Sopenharmony_ci * @autopower		Indicate if we should have automatic pwron after pwrloss
2308c2ecf20Sopenharmony_ci * @autopower_cfg	platform specific power config support for "pwron after pwrloss"
2318c2ecf20Sopenharmony_ci * @invalid_charger_detect_state State when forcing AB to use invalid charger
2328c2ecf20Sopenharmony_ci * @is_aca_rid:		Incicate if accessory is ACA type
2338c2ecf20Sopenharmony_ci * @current_stepping_sessions:
2348c2ecf20Sopenharmony_ci *			Counter for current stepping sessions
2358c2ecf20Sopenharmony_ci * @parent:		Pointer to the struct ab8500
2368c2ecf20Sopenharmony_ci * @adc_main_charger_v	ADC channel for main charger voltage
2378c2ecf20Sopenharmony_ci * @adc_main_charger_c	ADC channel for main charger current
2388c2ecf20Sopenharmony_ci * @adc_vbus_v		ADC channel for USB charger voltage
2398c2ecf20Sopenharmony_ci * @adc_usb_charger_c	ADC channel for USB charger current
2408c2ecf20Sopenharmony_ci * @bm:           	Platform specific battery management information
2418c2ecf20Sopenharmony_ci * @flags:		Structure for information about events triggered
2428c2ecf20Sopenharmony_ci * @usb_state:		Structure for usb stack information
2438c2ecf20Sopenharmony_ci * @max_usb_in_curr:	Max USB charger input current
2448c2ecf20Sopenharmony_ci * @ac_chg:		AC charger power supply
2458c2ecf20Sopenharmony_ci * @usb_chg:		USB charger power supply
2468c2ecf20Sopenharmony_ci * @ac:			Structure that holds the AC charger properties
2478c2ecf20Sopenharmony_ci * @usb:		Structure that holds the USB charger properties
2488c2ecf20Sopenharmony_ci * @regu:		Pointer to the struct regulator
2498c2ecf20Sopenharmony_ci * @charger_wq:		Work queue for the IRQs and checking HW state
2508c2ecf20Sopenharmony_ci * @usb_ipt_crnt_lock:	Lock to protect VBUS input current setting from mutuals
2518c2ecf20Sopenharmony_ci * @pm_lock:		Lock to prevent system to suspend
2528c2ecf20Sopenharmony_ci * @check_vbat_work	Work for checking vbat threshold to adjust vbus current
2538c2ecf20Sopenharmony_ci * @check_hw_failure_work:	Work for checking HW state
2548c2ecf20Sopenharmony_ci * @check_usbchgnotok_work:	Work for checking USB charger not ok status
2558c2ecf20Sopenharmony_ci * @kick_wd_work:		Work for kicking the charger watchdog in case
2568c2ecf20Sopenharmony_ci *				of ABB rev 1.* due to the watchog logic bug
2578c2ecf20Sopenharmony_ci * @ac_charger_attached_work:	Work for checking if AC charger is still
2588c2ecf20Sopenharmony_ci *				connected
2598c2ecf20Sopenharmony_ci * @usb_charger_attached_work:	Work for checking if USB charger is still
2608c2ecf20Sopenharmony_ci *				connected
2618c2ecf20Sopenharmony_ci * @ac_work:			Work for checking AC charger connection
2628c2ecf20Sopenharmony_ci * @detect_usb_type_work:	Work for detecting the USB type connected
2638c2ecf20Sopenharmony_ci * @usb_link_status_work:	Work for checking the new USB link status
2648c2ecf20Sopenharmony_ci * @usb_state_changed_work:	Work for checking USB state
2658c2ecf20Sopenharmony_ci * @attach_work:		Work for detecting USB type
2668c2ecf20Sopenharmony_ci * @vbus_drop_end_work:		Work for detecting VBUS drop end
2678c2ecf20Sopenharmony_ci * @check_main_thermal_prot_work:
2688c2ecf20Sopenharmony_ci *				Work for checking Main thermal status
2698c2ecf20Sopenharmony_ci * @check_usb_thermal_prot_work:
2708c2ecf20Sopenharmony_ci *				Work for checking USB thermal status
2718c2ecf20Sopenharmony_ci * @charger_attached_mutex:	For controlling the wakelock
2728c2ecf20Sopenharmony_ci */
2738c2ecf20Sopenharmony_cistruct ab8500_charger {
2748c2ecf20Sopenharmony_ci	struct device *dev;
2758c2ecf20Sopenharmony_ci	bool vbus_detected;
2768c2ecf20Sopenharmony_ci	bool vbus_detected_start;
2778c2ecf20Sopenharmony_ci	bool ac_conn;
2788c2ecf20Sopenharmony_ci	bool vddadc_en_ac;
2798c2ecf20Sopenharmony_ci	bool vddadc_en_usb;
2808c2ecf20Sopenharmony_ci	int vbat;
2818c2ecf20Sopenharmony_ci	int old_vbat;
2828c2ecf20Sopenharmony_ci	bool usb_device_is_unrecognised;
2838c2ecf20Sopenharmony_ci	bool autopower;
2848c2ecf20Sopenharmony_ci	bool autopower_cfg;
2858c2ecf20Sopenharmony_ci	int invalid_charger_detect_state;
2868c2ecf20Sopenharmony_ci	int is_aca_rid;
2878c2ecf20Sopenharmony_ci	atomic_t current_stepping_sessions;
2888c2ecf20Sopenharmony_ci	struct ab8500 *parent;
2898c2ecf20Sopenharmony_ci	struct iio_channel *adc_main_charger_v;
2908c2ecf20Sopenharmony_ci	struct iio_channel *adc_main_charger_c;
2918c2ecf20Sopenharmony_ci	struct iio_channel *adc_vbus_v;
2928c2ecf20Sopenharmony_ci	struct iio_channel *adc_usb_charger_c;
2938c2ecf20Sopenharmony_ci	struct abx500_bm_data *bm;
2948c2ecf20Sopenharmony_ci	struct ab8500_charger_event_flags flags;
2958c2ecf20Sopenharmony_ci	struct ab8500_charger_usb_state usb_state;
2968c2ecf20Sopenharmony_ci	struct ab8500_charger_max_usb_in_curr max_usb_in_curr;
2978c2ecf20Sopenharmony_ci	struct ux500_charger ac_chg;
2988c2ecf20Sopenharmony_ci	struct ux500_charger usb_chg;
2998c2ecf20Sopenharmony_ci	struct ab8500_charger_info ac;
3008c2ecf20Sopenharmony_ci	struct ab8500_charger_info usb;
3018c2ecf20Sopenharmony_ci	struct regulator *regu;
3028c2ecf20Sopenharmony_ci	struct workqueue_struct *charger_wq;
3038c2ecf20Sopenharmony_ci	struct mutex usb_ipt_crnt_lock;
3048c2ecf20Sopenharmony_ci	struct delayed_work check_vbat_work;
3058c2ecf20Sopenharmony_ci	struct delayed_work check_hw_failure_work;
3068c2ecf20Sopenharmony_ci	struct delayed_work check_usbchgnotok_work;
3078c2ecf20Sopenharmony_ci	struct delayed_work kick_wd_work;
3088c2ecf20Sopenharmony_ci	struct delayed_work usb_state_changed_work;
3098c2ecf20Sopenharmony_ci	struct delayed_work attach_work;
3108c2ecf20Sopenharmony_ci	struct delayed_work ac_charger_attached_work;
3118c2ecf20Sopenharmony_ci	struct delayed_work usb_charger_attached_work;
3128c2ecf20Sopenharmony_ci	struct delayed_work vbus_drop_end_work;
3138c2ecf20Sopenharmony_ci	struct work_struct ac_work;
3148c2ecf20Sopenharmony_ci	struct work_struct detect_usb_type_work;
3158c2ecf20Sopenharmony_ci	struct work_struct usb_link_status_work;
3168c2ecf20Sopenharmony_ci	struct work_struct check_main_thermal_prot_work;
3178c2ecf20Sopenharmony_ci	struct work_struct check_usb_thermal_prot_work;
3188c2ecf20Sopenharmony_ci	struct usb_phy *usb_phy;
3198c2ecf20Sopenharmony_ci	struct notifier_block nb;
3208c2ecf20Sopenharmony_ci	struct mutex charger_attached_mutex;
3218c2ecf20Sopenharmony_ci};
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci/* AC properties */
3248c2ecf20Sopenharmony_cistatic enum power_supply_property ab8500_charger_ac_props[] = {
3258c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
3268c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
3278c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
3288c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
3298c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_AVG,
3308c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
3318c2ecf20Sopenharmony_ci};
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci/* USB properties */
3348c2ecf20Sopenharmony_cistatic enum power_supply_property ab8500_charger_usb_props[] = {
3358c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
3368c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_AVG,
3378c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
3388c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
3398c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
3408c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_AVG,
3418c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
3428c2ecf20Sopenharmony_ci};
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci/*
3458c2ecf20Sopenharmony_ci * Function for enabling and disabling sw fallback mode
3468c2ecf20Sopenharmony_ci * should always be disabled when no charger is connected.
3478c2ecf20Sopenharmony_ci */
3488c2ecf20Sopenharmony_cistatic void ab8500_enable_disable_sw_fallback(struct ab8500_charger *di,
3498c2ecf20Sopenharmony_ci		bool fallback)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	u8 val;
3528c2ecf20Sopenharmony_ci	u8 reg;
3538c2ecf20Sopenharmony_ci	u8 bank;
3548c2ecf20Sopenharmony_ci	u8 bit;
3558c2ecf20Sopenharmony_ci	int ret;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "SW Fallback: %d\n", fallback);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (is_ab8500(di->parent)) {
3608c2ecf20Sopenharmony_ci		bank = 0x15;
3618c2ecf20Sopenharmony_ci		reg = 0x0;
3628c2ecf20Sopenharmony_ci		bit = 3;
3638c2ecf20Sopenharmony_ci	} else {
3648c2ecf20Sopenharmony_ci		bank = AB8500_SYS_CTRL1_BLOCK;
3658c2ecf20Sopenharmony_ci		reg = AB8500_SW_CONTROL_FALLBACK;
3668c2ecf20Sopenharmony_ci		bit = 0;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* read the register containing fallback bit */
3708c2ecf20Sopenharmony_ci	ret = abx500_get_register_interruptible(di->dev, bank, reg, &val);
3718c2ecf20Sopenharmony_ci	if (ret < 0) {
3728c2ecf20Sopenharmony_ci		dev_err(di->dev, "%d read failed\n", __LINE__);
3738c2ecf20Sopenharmony_ci		return;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (is_ab8500(di->parent)) {
3778c2ecf20Sopenharmony_ci		/* enable the OPT emulation registers */
3788c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x2);
3798c2ecf20Sopenharmony_ci		if (ret) {
3808c2ecf20Sopenharmony_ci			dev_err(di->dev, "%d write failed\n", __LINE__);
3818c2ecf20Sopenharmony_ci			goto disable_otp;
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (fallback)
3868c2ecf20Sopenharmony_ci		val |= (1 << bit);
3878c2ecf20Sopenharmony_ci	else
3888c2ecf20Sopenharmony_ci		val &= ~(1 << bit);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* write back the changed fallback bit value to register */
3918c2ecf20Sopenharmony_ci	ret = abx500_set_register_interruptible(di->dev, bank, reg, val);
3928c2ecf20Sopenharmony_ci	if (ret) {
3938c2ecf20Sopenharmony_ci		dev_err(di->dev, "%d write failed\n", __LINE__);
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cidisable_otp:
3978c2ecf20Sopenharmony_ci	if (is_ab8500(di->parent)) {
3988c2ecf20Sopenharmony_ci		/* disable the set OTP registers again */
3998c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev, 0x11, 0x00, 0x0);
4008c2ecf20Sopenharmony_ci		if (ret) {
4018c2ecf20Sopenharmony_ci			dev_err(di->dev, "%d write failed\n", __LINE__);
4028c2ecf20Sopenharmony_ci		}
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci/**
4078c2ecf20Sopenharmony_ci * ab8500_power_supply_changed - a wrapper with local extensions for
4088c2ecf20Sopenharmony_ci * power_supply_changed
4098c2ecf20Sopenharmony_ci * @di:	  pointer to the ab8500_charger structure
4108c2ecf20Sopenharmony_ci * @psy:  pointer to power_supply_that have changed.
4118c2ecf20Sopenharmony_ci *
4128c2ecf20Sopenharmony_ci */
4138c2ecf20Sopenharmony_cistatic void ab8500_power_supply_changed(struct ab8500_charger *di,
4148c2ecf20Sopenharmony_ci					struct power_supply *psy)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	/*
4178c2ecf20Sopenharmony_ci	 * This happens if we get notifications or interrupts and
4188c2ecf20Sopenharmony_ci	 * the platform has been configured not to support one or
4198c2ecf20Sopenharmony_ci	 * other type of charging.
4208c2ecf20Sopenharmony_ci	 */
4218c2ecf20Sopenharmony_ci	if (!psy)
4228c2ecf20Sopenharmony_ci		return;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (di->autopower_cfg) {
4258c2ecf20Sopenharmony_ci		if (!di->usb.charger_connected &&
4268c2ecf20Sopenharmony_ci		    !di->ac.charger_connected &&
4278c2ecf20Sopenharmony_ci		    di->autopower) {
4288c2ecf20Sopenharmony_ci			di->autopower = false;
4298c2ecf20Sopenharmony_ci			ab8500_enable_disable_sw_fallback(di, false);
4308c2ecf20Sopenharmony_ci		} else if (!di->autopower &&
4318c2ecf20Sopenharmony_ci			   (di->ac.charger_connected ||
4328c2ecf20Sopenharmony_ci			    di->usb.charger_connected)) {
4338c2ecf20Sopenharmony_ci			di->autopower = true;
4348c2ecf20Sopenharmony_ci			ab8500_enable_disable_sw_fallback(di, true);
4358c2ecf20Sopenharmony_ci		}
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci	power_supply_changed(psy);
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic void ab8500_charger_set_usb_connected(struct ab8500_charger *di,
4418c2ecf20Sopenharmony_ci	bool connected)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	if (connected != di->usb.charger_connected) {
4448c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "USB connected:%i\n", connected);
4458c2ecf20Sopenharmony_ci		di->usb.charger_connected = connected;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci		if (!connected)
4488c2ecf20Sopenharmony_ci			di->flags.vbus_drop_end = false;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		/*
4518c2ecf20Sopenharmony_ci		 * Sometimes the platform is configured not to support
4528c2ecf20Sopenharmony_ci		 * USB charging and no psy has been created, but we still
4538c2ecf20Sopenharmony_ci		 * will get these notifications.
4548c2ecf20Sopenharmony_ci		 */
4558c2ecf20Sopenharmony_ci		if (di->usb_chg.psy) {
4568c2ecf20Sopenharmony_ci			sysfs_notify(&di->usb_chg.psy->dev.kobj, NULL,
4578c2ecf20Sopenharmony_ci				     "present");
4588c2ecf20Sopenharmony_ci		}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		if (connected) {
4618c2ecf20Sopenharmony_ci			mutex_lock(&di->charger_attached_mutex);
4628c2ecf20Sopenharmony_ci			mutex_unlock(&di->charger_attached_mutex);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci			if (is_ab8500(di->parent))
4658c2ecf20Sopenharmony_ci				queue_delayed_work(di->charger_wq,
4668c2ecf20Sopenharmony_ci					   &di->usb_charger_attached_work,
4678c2ecf20Sopenharmony_ci					   HZ);
4688c2ecf20Sopenharmony_ci		} else {
4698c2ecf20Sopenharmony_ci			cancel_delayed_work_sync(&di->usb_charger_attached_work);
4708c2ecf20Sopenharmony_ci			mutex_lock(&di->charger_attached_mutex);
4718c2ecf20Sopenharmony_ci			mutex_unlock(&di->charger_attached_mutex);
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci/**
4778c2ecf20Sopenharmony_ci * ab8500_charger_get_ac_voltage() - get ac charger voltage
4788c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
4798c2ecf20Sopenharmony_ci *
4808c2ecf20Sopenharmony_ci * Returns ac charger voltage (on success)
4818c2ecf20Sopenharmony_ci */
4828c2ecf20Sopenharmony_cistatic int ab8500_charger_get_ac_voltage(struct ab8500_charger *di)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	int vch, ret;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	/* Only measure voltage if the charger is connected */
4878c2ecf20Sopenharmony_ci	if (di->ac.charger_connected) {
4888c2ecf20Sopenharmony_ci		ret = iio_read_channel_processed(di->adc_main_charger_v, &vch);
4898c2ecf20Sopenharmony_ci		if (ret < 0)
4908c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s ADC conv failed,\n", __func__);
4918c2ecf20Sopenharmony_ci	} else {
4928c2ecf20Sopenharmony_ci		vch = 0;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci	return vch;
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci/**
4988c2ecf20Sopenharmony_ci * ab8500_charger_ac_cv() - check if the main charger is in CV mode
4998c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
5008c2ecf20Sopenharmony_ci *
5018c2ecf20Sopenharmony_ci * Returns ac charger CV mode (on success) else error code
5028c2ecf20Sopenharmony_ci */
5038c2ecf20Sopenharmony_cistatic int ab8500_charger_ac_cv(struct ab8500_charger *di)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	u8 val;
5068c2ecf20Sopenharmony_ci	int ret = 0;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	/* Only check CV mode if the charger is online */
5098c2ecf20Sopenharmony_ci	if (di->ac.charger_online) {
5108c2ecf20Sopenharmony_ci		ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
5118c2ecf20Sopenharmony_ci			AB8500_CH_STATUS1_REG, &val);
5128c2ecf20Sopenharmony_ci		if (ret < 0) {
5138c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
5148c2ecf20Sopenharmony_ci			return 0;
5158c2ecf20Sopenharmony_ci		}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		if (val & MAIN_CH_CV_ON)
5188c2ecf20Sopenharmony_ci			ret = 1;
5198c2ecf20Sopenharmony_ci		else
5208c2ecf20Sopenharmony_ci			ret = 0;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	return ret;
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci/**
5278c2ecf20Sopenharmony_ci * ab8500_charger_get_vbus_voltage() - get vbus voltage
5288c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
5298c2ecf20Sopenharmony_ci *
5308c2ecf20Sopenharmony_ci * This function returns the vbus voltage.
5318c2ecf20Sopenharmony_ci * Returns vbus voltage (on success)
5328c2ecf20Sopenharmony_ci */
5338c2ecf20Sopenharmony_cistatic int ab8500_charger_get_vbus_voltage(struct ab8500_charger *di)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	int vch, ret;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	/* Only measure voltage if the charger is connected */
5388c2ecf20Sopenharmony_ci	if (di->usb.charger_connected) {
5398c2ecf20Sopenharmony_ci		ret = iio_read_channel_processed(di->adc_vbus_v, &vch);
5408c2ecf20Sopenharmony_ci		if (ret < 0)
5418c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s ADC conv failed,\n", __func__);
5428c2ecf20Sopenharmony_ci	} else {
5438c2ecf20Sopenharmony_ci		vch = 0;
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci	return vch;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci/**
5498c2ecf20Sopenharmony_ci * ab8500_charger_get_usb_current() - get usb charger current
5508c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
5518c2ecf20Sopenharmony_ci *
5528c2ecf20Sopenharmony_ci * This function returns the usb charger current.
5538c2ecf20Sopenharmony_ci * Returns usb current (on success) and error code on failure
5548c2ecf20Sopenharmony_ci */
5558c2ecf20Sopenharmony_cistatic int ab8500_charger_get_usb_current(struct ab8500_charger *di)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	int ich, ret;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	/* Only measure current if the charger is online */
5608c2ecf20Sopenharmony_ci	if (di->usb.charger_online) {
5618c2ecf20Sopenharmony_ci		ret = iio_read_channel_processed(di->adc_usb_charger_c, &ich);
5628c2ecf20Sopenharmony_ci		if (ret < 0)
5638c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s ADC conv failed,\n", __func__);
5648c2ecf20Sopenharmony_ci	} else {
5658c2ecf20Sopenharmony_ci		ich = 0;
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci	return ich;
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci/**
5718c2ecf20Sopenharmony_ci * ab8500_charger_get_ac_current() - get ac charger current
5728c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
5738c2ecf20Sopenharmony_ci *
5748c2ecf20Sopenharmony_ci * This function returns the ac charger current.
5758c2ecf20Sopenharmony_ci * Returns ac current (on success) and error code on failure.
5768c2ecf20Sopenharmony_ci */
5778c2ecf20Sopenharmony_cistatic int ab8500_charger_get_ac_current(struct ab8500_charger *di)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	int ich, ret;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	/* Only measure current if the charger is online */
5828c2ecf20Sopenharmony_ci	if (di->ac.charger_online) {
5838c2ecf20Sopenharmony_ci		ret = iio_read_channel_processed(di->adc_main_charger_c, &ich);
5848c2ecf20Sopenharmony_ci		if (ret < 0)
5858c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s ADC conv failed,\n", __func__);
5868c2ecf20Sopenharmony_ci	} else {
5878c2ecf20Sopenharmony_ci		ich = 0;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci	return ich;
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci/**
5938c2ecf20Sopenharmony_ci * ab8500_charger_usb_cv() - check if the usb charger is in CV mode
5948c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
5958c2ecf20Sopenharmony_ci *
5968c2ecf20Sopenharmony_ci * Returns ac charger CV mode (on success) else error code
5978c2ecf20Sopenharmony_ci */
5988c2ecf20Sopenharmony_cistatic int ab8500_charger_usb_cv(struct ab8500_charger *di)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	int ret;
6018c2ecf20Sopenharmony_ci	u8 val;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	/* Only check CV mode if the charger is online */
6048c2ecf20Sopenharmony_ci	if (di->usb.charger_online) {
6058c2ecf20Sopenharmony_ci		ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
6068c2ecf20Sopenharmony_ci			AB8500_CH_USBCH_STAT1_REG, &val);
6078c2ecf20Sopenharmony_ci		if (ret < 0) {
6088c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
6098c2ecf20Sopenharmony_ci			return 0;
6108c2ecf20Sopenharmony_ci		}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci		if (val & USB_CH_CV_ON)
6138c2ecf20Sopenharmony_ci			ret = 1;
6148c2ecf20Sopenharmony_ci		else
6158c2ecf20Sopenharmony_ci			ret = 0;
6168c2ecf20Sopenharmony_ci	} else {
6178c2ecf20Sopenharmony_ci		ret = 0;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	return ret;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci/**
6248c2ecf20Sopenharmony_ci * ab8500_charger_detect_chargers() - Detect the connected chargers
6258c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
6268c2ecf20Sopenharmony_ci * @probe:	if probe, don't delay and wait for HW
6278c2ecf20Sopenharmony_ci *
6288c2ecf20Sopenharmony_ci * Returns the type of charger connected.
6298c2ecf20Sopenharmony_ci * For USB it will not mean we can actually charge from it
6308c2ecf20Sopenharmony_ci * but that there is a USB cable connected that we have to
6318c2ecf20Sopenharmony_ci * identify. This is used during startup when we don't get
6328c2ecf20Sopenharmony_ci * interrupts of the charger detection
6338c2ecf20Sopenharmony_ci *
6348c2ecf20Sopenharmony_ci * Returns an integer value, that means,
6358c2ecf20Sopenharmony_ci * NO_PW_CONN  no power supply is connected
6368c2ecf20Sopenharmony_ci * AC_PW_CONN  if the AC power supply is connected
6378c2ecf20Sopenharmony_ci * USB_PW_CONN  if the USB power supply is connected
6388c2ecf20Sopenharmony_ci * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
6398c2ecf20Sopenharmony_ci */
6408c2ecf20Sopenharmony_cistatic int ab8500_charger_detect_chargers(struct ab8500_charger *di, bool probe)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	int result = NO_PW_CONN;
6438c2ecf20Sopenharmony_ci	int ret;
6448c2ecf20Sopenharmony_ci	u8 val;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/* Check for AC charger */
6478c2ecf20Sopenharmony_ci	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
6488c2ecf20Sopenharmony_ci		AB8500_CH_STATUS1_REG, &val);
6498c2ecf20Sopenharmony_ci	if (ret < 0) {
6508c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
6518c2ecf20Sopenharmony_ci		return ret;
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	if (val & MAIN_CH_DET)
6558c2ecf20Sopenharmony_ci		result = AC_PW_CONN;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	/* Check for USB charger */
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	if (!probe) {
6608c2ecf20Sopenharmony_ci		/*
6618c2ecf20Sopenharmony_ci		 * AB8500 says VBUS_DET_DBNC1 & VBUS_DET_DBNC100
6628c2ecf20Sopenharmony_ci		 * when disconnecting ACA even though no
6638c2ecf20Sopenharmony_ci		 * charger was connected. Try waiting a little
6648c2ecf20Sopenharmony_ci		 * longer than the 100 ms of VBUS_DET_DBNC100...
6658c2ecf20Sopenharmony_ci		 */
6668c2ecf20Sopenharmony_ci		msleep(110);
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
6698c2ecf20Sopenharmony_ci		AB8500_CH_USBCH_STAT1_REG, &val);
6708c2ecf20Sopenharmony_ci	if (ret < 0) {
6718c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
6728c2ecf20Sopenharmony_ci		return ret;
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci	dev_dbg(di->dev,
6758c2ecf20Sopenharmony_ci		"%s AB8500_CH_USBCH_STAT1_REG %x\n", __func__,
6768c2ecf20Sopenharmony_ci		val);
6778c2ecf20Sopenharmony_ci	if ((val & VBUS_DET_DBNC1) && (val & VBUS_DET_DBNC100))
6788c2ecf20Sopenharmony_ci		result |= USB_PW_CONN;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	return result;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci/**
6848c2ecf20Sopenharmony_ci * ab8500_charger_max_usb_curr() - get the max curr for the USB type
6858c2ecf20Sopenharmony_ci * @di:			pointer to the ab8500_charger structure
6868c2ecf20Sopenharmony_ci * @link_status:	the identified USB type
6878c2ecf20Sopenharmony_ci *
6888c2ecf20Sopenharmony_ci * Get the maximum current that is allowed to be drawn from the host
6898c2ecf20Sopenharmony_ci * based on the USB type.
6908c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0 on success
6918c2ecf20Sopenharmony_ci */
6928c2ecf20Sopenharmony_cistatic int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
6938c2ecf20Sopenharmony_ci		enum ab8500_charger_link_status link_status)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	int ret = 0;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	di->usb_device_is_unrecognised = false;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	/*
7008c2ecf20Sopenharmony_ci	 * Platform only supports USB 2.0.
7018c2ecf20Sopenharmony_ci	 * This means that charging current from USB source
7028c2ecf20Sopenharmony_ci	 * is maximum 500 mA. Every occurrence of USB_STAT_*_HOST_*
7038c2ecf20Sopenharmony_ci	 * should set USB_CH_IP_CUR_LVL_0P5.
7048c2ecf20Sopenharmony_ci	 */
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	switch (link_status) {
7078c2ecf20Sopenharmony_ci	case USB_STAT_STD_HOST_NC:
7088c2ecf20Sopenharmony_ci	case USB_STAT_STD_HOST_C_NS:
7098c2ecf20Sopenharmony_ci	case USB_STAT_STD_HOST_C_S:
7108c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "USB Type - Standard host is "
7118c2ecf20Sopenharmony_ci			"detected through USB driver\n");
7128c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
7138c2ecf20Sopenharmony_ci		di->is_aca_rid = 0;
7148c2ecf20Sopenharmony_ci		break;
7158c2ecf20Sopenharmony_ci	case USB_STAT_HOST_CHG_HS_CHIRP:
7168c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
7178c2ecf20Sopenharmony_ci		di->is_aca_rid = 0;
7188c2ecf20Sopenharmony_ci		break;
7198c2ecf20Sopenharmony_ci	case USB_STAT_HOST_CHG_HS:
7208c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
7218c2ecf20Sopenharmony_ci		di->is_aca_rid = 0;
7228c2ecf20Sopenharmony_ci		break;
7238c2ecf20Sopenharmony_ci	case USB_STAT_ACA_RID_C_HS:
7248c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P9;
7258c2ecf20Sopenharmony_ci		di->is_aca_rid = 0;
7268c2ecf20Sopenharmony_ci		break;
7278c2ecf20Sopenharmony_ci	case USB_STAT_ACA_RID_A:
7288c2ecf20Sopenharmony_ci		/*
7298c2ecf20Sopenharmony_ci		 * Dedicated charger level minus maximum current accessory
7308c2ecf20Sopenharmony_ci		 * can consume (900mA). Closest level is 500mA
7318c2ecf20Sopenharmony_ci		 */
7328c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "USB_STAT_ACA_RID_A detected\n");
7338c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
7348c2ecf20Sopenharmony_ci		di->is_aca_rid = 1;
7358c2ecf20Sopenharmony_ci		break;
7368c2ecf20Sopenharmony_ci	case USB_STAT_ACA_RID_B:
7378c2ecf20Sopenharmony_ci		/*
7388c2ecf20Sopenharmony_ci		 * Dedicated charger level minus 120mA (20mA for ACA and
7398c2ecf20Sopenharmony_ci		 * 100mA for potential accessory). Closest level is 1300mA
7408c2ecf20Sopenharmony_ci		 */
7418c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P3;
7428c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
7438c2ecf20Sopenharmony_ci				di->max_usb_in_curr.usb_type_max);
7448c2ecf20Sopenharmony_ci		di->is_aca_rid = 1;
7458c2ecf20Sopenharmony_ci		break;
7468c2ecf20Sopenharmony_ci	case USB_STAT_HOST_CHG_NM:
7478c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
7488c2ecf20Sopenharmony_ci		di->is_aca_rid = 0;
7498c2ecf20Sopenharmony_ci		break;
7508c2ecf20Sopenharmony_ci	case USB_STAT_DEDICATED_CHG:
7518c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5;
7528c2ecf20Sopenharmony_ci		di->is_aca_rid = 0;
7538c2ecf20Sopenharmony_ci		break;
7548c2ecf20Sopenharmony_ci	case USB_STAT_ACA_RID_C_HS_CHIRP:
7558c2ecf20Sopenharmony_ci	case USB_STAT_ACA_RID_C_NM:
7568c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_1P5;
7578c2ecf20Sopenharmony_ci		di->is_aca_rid = 1;
7588c2ecf20Sopenharmony_ci		break;
7598c2ecf20Sopenharmony_ci	case USB_STAT_NOT_CONFIGURED:
7608c2ecf20Sopenharmony_ci		if (di->vbus_detected) {
7618c2ecf20Sopenharmony_ci			di->usb_device_is_unrecognised = true;
7628c2ecf20Sopenharmony_ci			dev_dbg(di->dev, "USB Type - Legacy charger.\n");
7638c2ecf20Sopenharmony_ci			di->max_usb_in_curr.usb_type_max =
7648c2ecf20Sopenharmony_ci						USB_CH_IP_CUR_LVL_1P5;
7658c2ecf20Sopenharmony_ci			break;
7668c2ecf20Sopenharmony_ci		}
7678c2ecf20Sopenharmony_ci		fallthrough;
7688c2ecf20Sopenharmony_ci	case USB_STAT_HM_IDGND:
7698c2ecf20Sopenharmony_ci		dev_err(di->dev, "USB Type - Charging not allowed\n");
7708c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
7718c2ecf20Sopenharmony_ci		ret = -ENXIO;
7728c2ecf20Sopenharmony_ci		break;
7738c2ecf20Sopenharmony_ci	case USB_STAT_RESERVED:
7748c2ecf20Sopenharmony_ci		if (is_ab8500(di->parent)) {
7758c2ecf20Sopenharmony_ci			di->flags.vbus_collapse = true;
7768c2ecf20Sopenharmony_ci			dev_err(di->dev, "USB Type - USB_STAT_RESERVED "
7778c2ecf20Sopenharmony_ci						"VBUS has collapsed\n");
7788c2ecf20Sopenharmony_ci			ret = -ENXIO;
7798c2ecf20Sopenharmony_ci			break;
7808c2ecf20Sopenharmony_ci		} else {
7818c2ecf20Sopenharmony_ci			dev_dbg(di->dev, "USB Type - Charging not allowed\n");
7828c2ecf20Sopenharmony_ci			di->max_usb_in_curr.usb_type_max =
7838c2ecf20Sopenharmony_ci						USB_CH_IP_CUR_LVL_0P05;
7848c2ecf20Sopenharmony_ci			dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
7858c2ecf20Sopenharmony_ci				link_status,
7868c2ecf20Sopenharmony_ci				di->max_usb_in_curr.usb_type_max);
7878c2ecf20Sopenharmony_ci			ret = -ENXIO;
7888c2ecf20Sopenharmony_ci			break;
7898c2ecf20Sopenharmony_ci		}
7908c2ecf20Sopenharmony_ci	case USB_STAT_CARKIT_1:
7918c2ecf20Sopenharmony_ci	case USB_STAT_CARKIT_2:
7928c2ecf20Sopenharmony_ci	case USB_STAT_ACA_DOCK_CHARGER:
7938c2ecf20Sopenharmony_ci	case USB_STAT_CHARGER_LINE_1:
7948c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
7958c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", link_status,
7968c2ecf20Sopenharmony_ci				di->max_usb_in_curr.usb_type_max);
7978c2ecf20Sopenharmony_ci		break;
7988c2ecf20Sopenharmony_ci	case USB_STAT_NOT_VALID_LINK:
7998c2ecf20Sopenharmony_ci		dev_err(di->dev, "USB Type invalid - try charging anyway\n");
8008c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
8018c2ecf20Sopenharmony_ci		break;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	default:
8048c2ecf20Sopenharmony_ci		dev_err(di->dev, "USB Type - Unknown\n");
8058c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
8068c2ecf20Sopenharmony_ci		ret = -ENXIO;
8078c2ecf20Sopenharmony_ci		break;
8088c2ecf20Sopenharmony_ci	}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
8118c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
8128c2ecf20Sopenharmony_ci		link_status, di->max_usb_in_curr.set_max);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	return ret;
8158c2ecf20Sopenharmony_ci}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci/**
8188c2ecf20Sopenharmony_ci * ab8500_charger_read_usb_type() - read the type of usb connected
8198c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
8208c2ecf20Sopenharmony_ci *
8218c2ecf20Sopenharmony_ci * Detect the type of the plugged USB
8228c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0 on success
8238c2ecf20Sopenharmony_ci */
8248c2ecf20Sopenharmony_cistatic int ab8500_charger_read_usb_type(struct ab8500_charger *di)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	int ret;
8278c2ecf20Sopenharmony_ci	u8 val;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	ret = abx500_get_register_interruptible(di->dev,
8308c2ecf20Sopenharmony_ci		AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG, &val);
8318c2ecf20Sopenharmony_ci	if (ret < 0) {
8328c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
8338c2ecf20Sopenharmony_ci		return ret;
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci	if (is_ab8500(di->parent))
8368c2ecf20Sopenharmony_ci		ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
8378c2ecf20Sopenharmony_ci			AB8500_USB_LINE_STAT_REG, &val);
8388c2ecf20Sopenharmony_ci	else
8398c2ecf20Sopenharmony_ci		ret = abx500_get_register_interruptible(di->dev,
8408c2ecf20Sopenharmony_ci			AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
8418c2ecf20Sopenharmony_ci	if (ret < 0) {
8428c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
8438c2ecf20Sopenharmony_ci		return ret;
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	/* get the USB type */
8478c2ecf20Sopenharmony_ci	if (is_ab8500(di->parent))
8488c2ecf20Sopenharmony_ci		val = (val & AB8500_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT;
8498c2ecf20Sopenharmony_ci	else
8508c2ecf20Sopenharmony_ci		val = (val & AB8505_USB_LINK_STATUS) >> USB_LINK_STATUS_SHIFT;
8518c2ecf20Sopenharmony_ci	ret = ab8500_charger_max_usb_curr(di,
8528c2ecf20Sopenharmony_ci		(enum ab8500_charger_link_status) val);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	return ret;
8558c2ecf20Sopenharmony_ci}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci/**
8588c2ecf20Sopenharmony_ci * ab8500_charger_detect_usb_type() - get the type of usb connected
8598c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
8608c2ecf20Sopenharmony_ci *
8618c2ecf20Sopenharmony_ci * Detect the type of the plugged USB
8628c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0 on success
8638c2ecf20Sopenharmony_ci */
8648c2ecf20Sopenharmony_cistatic int ab8500_charger_detect_usb_type(struct ab8500_charger *di)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	int i, ret;
8678c2ecf20Sopenharmony_ci	u8 val;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	/*
8708c2ecf20Sopenharmony_ci	 * On getting the VBUS rising edge detect interrupt there
8718c2ecf20Sopenharmony_ci	 * is a 250ms delay after which the register UsbLineStatus
8728c2ecf20Sopenharmony_ci	 * is filled with valid data.
8738c2ecf20Sopenharmony_ci	 */
8748c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++) {
8758c2ecf20Sopenharmony_ci		msleep(250);
8768c2ecf20Sopenharmony_ci		ret = abx500_get_register_interruptible(di->dev,
8778c2ecf20Sopenharmony_ci			AB8500_INTERRUPT, AB8500_IT_SOURCE21_REG,
8788c2ecf20Sopenharmony_ci			&val);
8798c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "%s AB8500_IT_SOURCE21_REG %x\n",
8808c2ecf20Sopenharmony_ci			__func__, val);
8818c2ecf20Sopenharmony_ci		if (ret < 0) {
8828c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
8838c2ecf20Sopenharmony_ci			return ret;
8848c2ecf20Sopenharmony_ci		}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		if (is_ab8500(di->parent))
8878c2ecf20Sopenharmony_ci			ret = abx500_get_register_interruptible(di->dev,
8888c2ecf20Sopenharmony_ci				AB8500_USB, AB8500_USB_LINE_STAT_REG, &val);
8898c2ecf20Sopenharmony_ci		else
8908c2ecf20Sopenharmony_ci			ret = abx500_get_register_interruptible(di->dev,
8918c2ecf20Sopenharmony_ci				AB8500_USB, AB8500_USB_LINK1_STAT_REG, &val);
8928c2ecf20Sopenharmony_ci		if (ret < 0) {
8938c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
8948c2ecf20Sopenharmony_ci			return ret;
8958c2ecf20Sopenharmony_ci		}
8968c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "%s AB8500_USB_LINE_STAT_REG %x\n", __func__,
8978c2ecf20Sopenharmony_ci			val);
8988c2ecf20Sopenharmony_ci		/*
8998c2ecf20Sopenharmony_ci		 * Until the IT source register is read the UsbLineStatus
9008c2ecf20Sopenharmony_ci		 * register is not updated, hence doing the same
9018c2ecf20Sopenharmony_ci		 * Revisit this:
9028c2ecf20Sopenharmony_ci		 */
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci		/* get the USB type */
9058c2ecf20Sopenharmony_ci		if (is_ab8500(di->parent))
9068c2ecf20Sopenharmony_ci			val = (val & AB8500_USB_LINK_STATUS) >>
9078c2ecf20Sopenharmony_ci							USB_LINK_STATUS_SHIFT;
9088c2ecf20Sopenharmony_ci		else
9098c2ecf20Sopenharmony_ci			val = (val & AB8505_USB_LINK_STATUS) >>
9108c2ecf20Sopenharmony_ci							USB_LINK_STATUS_SHIFT;
9118c2ecf20Sopenharmony_ci		if (val)
9128c2ecf20Sopenharmony_ci			break;
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci	ret = ab8500_charger_max_usb_curr(di,
9158c2ecf20Sopenharmony_ci		(enum ab8500_charger_link_status) val);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	return ret;
9188c2ecf20Sopenharmony_ci}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci/*
9218c2ecf20Sopenharmony_ci * This array maps the raw hex value to charger voltage used by the AB8500
9228c2ecf20Sopenharmony_ci * Values taken from the UM0836
9238c2ecf20Sopenharmony_ci */
9248c2ecf20Sopenharmony_cistatic int ab8500_charger_voltage_map[] = {
9258c2ecf20Sopenharmony_ci	3500 ,
9268c2ecf20Sopenharmony_ci	3525 ,
9278c2ecf20Sopenharmony_ci	3550 ,
9288c2ecf20Sopenharmony_ci	3575 ,
9298c2ecf20Sopenharmony_ci	3600 ,
9308c2ecf20Sopenharmony_ci	3625 ,
9318c2ecf20Sopenharmony_ci	3650 ,
9328c2ecf20Sopenharmony_ci	3675 ,
9338c2ecf20Sopenharmony_ci	3700 ,
9348c2ecf20Sopenharmony_ci	3725 ,
9358c2ecf20Sopenharmony_ci	3750 ,
9368c2ecf20Sopenharmony_ci	3775 ,
9378c2ecf20Sopenharmony_ci	3800 ,
9388c2ecf20Sopenharmony_ci	3825 ,
9398c2ecf20Sopenharmony_ci	3850 ,
9408c2ecf20Sopenharmony_ci	3875 ,
9418c2ecf20Sopenharmony_ci	3900 ,
9428c2ecf20Sopenharmony_ci	3925 ,
9438c2ecf20Sopenharmony_ci	3950 ,
9448c2ecf20Sopenharmony_ci	3975 ,
9458c2ecf20Sopenharmony_ci	4000 ,
9468c2ecf20Sopenharmony_ci	4025 ,
9478c2ecf20Sopenharmony_ci	4050 ,
9488c2ecf20Sopenharmony_ci	4060 ,
9498c2ecf20Sopenharmony_ci	4070 ,
9508c2ecf20Sopenharmony_ci	4080 ,
9518c2ecf20Sopenharmony_ci	4090 ,
9528c2ecf20Sopenharmony_ci	4100 ,
9538c2ecf20Sopenharmony_ci	4110 ,
9548c2ecf20Sopenharmony_ci	4120 ,
9558c2ecf20Sopenharmony_ci	4130 ,
9568c2ecf20Sopenharmony_ci	4140 ,
9578c2ecf20Sopenharmony_ci	4150 ,
9588c2ecf20Sopenharmony_ci	4160 ,
9598c2ecf20Sopenharmony_ci	4170 ,
9608c2ecf20Sopenharmony_ci	4180 ,
9618c2ecf20Sopenharmony_ci	4190 ,
9628c2ecf20Sopenharmony_ci	4200 ,
9638c2ecf20Sopenharmony_ci	4210 ,
9648c2ecf20Sopenharmony_ci	4220 ,
9658c2ecf20Sopenharmony_ci	4230 ,
9668c2ecf20Sopenharmony_ci	4240 ,
9678c2ecf20Sopenharmony_ci	4250 ,
9688c2ecf20Sopenharmony_ci	4260 ,
9698c2ecf20Sopenharmony_ci	4270 ,
9708c2ecf20Sopenharmony_ci	4280 ,
9718c2ecf20Sopenharmony_ci	4290 ,
9728c2ecf20Sopenharmony_ci	4300 ,
9738c2ecf20Sopenharmony_ci	4310 ,
9748c2ecf20Sopenharmony_ci	4320 ,
9758c2ecf20Sopenharmony_ci	4330 ,
9768c2ecf20Sopenharmony_ci	4340 ,
9778c2ecf20Sopenharmony_ci	4350 ,
9788c2ecf20Sopenharmony_ci	4360 ,
9798c2ecf20Sopenharmony_ci	4370 ,
9808c2ecf20Sopenharmony_ci	4380 ,
9818c2ecf20Sopenharmony_ci	4390 ,
9828c2ecf20Sopenharmony_ci	4400 ,
9838c2ecf20Sopenharmony_ci	4410 ,
9848c2ecf20Sopenharmony_ci	4420 ,
9858c2ecf20Sopenharmony_ci	4430 ,
9868c2ecf20Sopenharmony_ci	4440 ,
9878c2ecf20Sopenharmony_ci	4450 ,
9888c2ecf20Sopenharmony_ci	4460 ,
9898c2ecf20Sopenharmony_ci	4470 ,
9908c2ecf20Sopenharmony_ci	4480 ,
9918c2ecf20Sopenharmony_ci	4490 ,
9928c2ecf20Sopenharmony_ci	4500 ,
9938c2ecf20Sopenharmony_ci	4510 ,
9948c2ecf20Sopenharmony_ci	4520 ,
9958c2ecf20Sopenharmony_ci	4530 ,
9968c2ecf20Sopenharmony_ci	4540 ,
9978c2ecf20Sopenharmony_ci	4550 ,
9988c2ecf20Sopenharmony_ci	4560 ,
9998c2ecf20Sopenharmony_ci	4570 ,
10008c2ecf20Sopenharmony_ci	4580 ,
10018c2ecf20Sopenharmony_ci	4590 ,
10028c2ecf20Sopenharmony_ci	4600 ,
10038c2ecf20Sopenharmony_ci};
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_cistatic int ab8500_voltage_to_regval(int voltage)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	int i;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	/* Special case for voltage below 3.5V */
10108c2ecf20Sopenharmony_ci	if (voltage < ab8500_charger_voltage_map[0])
10118c2ecf20Sopenharmony_ci		return LOW_VOLT_REG;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	for (i = 1; i < ARRAY_SIZE(ab8500_charger_voltage_map); i++) {
10148c2ecf20Sopenharmony_ci		if (voltage < ab8500_charger_voltage_map[i])
10158c2ecf20Sopenharmony_ci			return i - 1;
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	/* If not last element, return error */
10198c2ecf20Sopenharmony_ci	i = ARRAY_SIZE(ab8500_charger_voltage_map) - 1;
10208c2ecf20Sopenharmony_ci	if (voltage == ab8500_charger_voltage_map[i])
10218c2ecf20Sopenharmony_ci		return i;
10228c2ecf20Sopenharmony_ci	else
10238c2ecf20Sopenharmony_ci		return -1;
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_cistatic int ab8500_current_to_regval(struct ab8500_charger *di, int curr)
10278c2ecf20Sopenharmony_ci{
10288c2ecf20Sopenharmony_ci	int i;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	if (curr < di->bm->chg_output_curr[0])
10318c2ecf20Sopenharmony_ci		return 0;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	for (i = 0; i < di->bm->n_chg_out_curr; i++) {
10348c2ecf20Sopenharmony_ci		if (curr < di->bm->chg_output_curr[i])
10358c2ecf20Sopenharmony_ci			return i - 1;
10368c2ecf20Sopenharmony_ci	}
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	/* If not last element, return error */
10398c2ecf20Sopenharmony_ci	i = di->bm->n_chg_out_curr - 1;
10408c2ecf20Sopenharmony_ci	if (curr == di->bm->chg_output_curr[i])
10418c2ecf20Sopenharmony_ci		return i;
10428c2ecf20Sopenharmony_ci	else
10438c2ecf20Sopenharmony_ci		return -1;
10448c2ecf20Sopenharmony_ci}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_cistatic int ab8500_vbus_in_curr_to_regval(struct ab8500_charger *di, int curr)
10478c2ecf20Sopenharmony_ci{
10488c2ecf20Sopenharmony_ci	int i;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	if (curr < di->bm->chg_input_curr[0])
10518c2ecf20Sopenharmony_ci		return 0;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	for (i = 0; i < di->bm->n_chg_in_curr; i++) {
10548c2ecf20Sopenharmony_ci		if (curr < di->bm->chg_input_curr[i])
10558c2ecf20Sopenharmony_ci			return i - 1;
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	/* If not last element, return error */
10598c2ecf20Sopenharmony_ci	i = di->bm->n_chg_in_curr - 1;
10608c2ecf20Sopenharmony_ci	if (curr == di->bm->chg_input_curr[i])
10618c2ecf20Sopenharmony_ci		return i;
10628c2ecf20Sopenharmony_ci	else
10638c2ecf20Sopenharmony_ci		return -1;
10648c2ecf20Sopenharmony_ci}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci/**
10678c2ecf20Sopenharmony_ci * ab8500_charger_get_usb_cur() - get usb current
10688c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structre
10698c2ecf20Sopenharmony_ci *
10708c2ecf20Sopenharmony_ci * The usb stack provides the maximum current that can be drawn from
10718c2ecf20Sopenharmony_ci * the standard usb host. This will be in mA.
10728c2ecf20Sopenharmony_ci * This function converts current in mA to a value that can be written
10738c2ecf20Sopenharmony_ci * to the register. Returns -1 if charging is not allowed
10748c2ecf20Sopenharmony_ci */
10758c2ecf20Sopenharmony_cistatic int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
10768c2ecf20Sopenharmony_ci{
10778c2ecf20Sopenharmony_ci	int ret = 0;
10788c2ecf20Sopenharmony_ci	switch (di->usb_state.usb_current) {
10798c2ecf20Sopenharmony_ci	case 100:
10808c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P09;
10818c2ecf20Sopenharmony_ci		break;
10828c2ecf20Sopenharmony_ci	case 200:
10838c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P19;
10848c2ecf20Sopenharmony_ci		break;
10858c2ecf20Sopenharmony_ci	case 300:
10868c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P29;
10878c2ecf20Sopenharmony_ci		break;
10888c2ecf20Sopenharmony_ci	case 400:
10898c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P38;
10908c2ecf20Sopenharmony_ci		break;
10918c2ecf20Sopenharmony_ci	case 500:
10928c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P5;
10938c2ecf20Sopenharmony_ci		break;
10948c2ecf20Sopenharmony_ci	default:
10958c2ecf20Sopenharmony_ci		di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05;
10968c2ecf20Sopenharmony_ci		ret = -EPERM;
10978c2ecf20Sopenharmony_ci		break;
10988c2ecf20Sopenharmony_ci	}
10998c2ecf20Sopenharmony_ci	di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max;
11008c2ecf20Sopenharmony_ci	return ret;
11018c2ecf20Sopenharmony_ci}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci/**
11048c2ecf20Sopenharmony_ci * ab8500_charger_check_continue_stepping() - Check to allow stepping
11058c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
11068c2ecf20Sopenharmony_ci * @reg:	select what charger register to check
11078c2ecf20Sopenharmony_ci *
11088c2ecf20Sopenharmony_ci * Check if current stepping should be allowed to continue.
11098c2ecf20Sopenharmony_ci * Checks if charger source has not collapsed. If it has, further stepping
11108c2ecf20Sopenharmony_ci * is not allowed.
11118c2ecf20Sopenharmony_ci */
11128c2ecf20Sopenharmony_cistatic bool ab8500_charger_check_continue_stepping(struct ab8500_charger *di,
11138c2ecf20Sopenharmony_ci						   int reg)
11148c2ecf20Sopenharmony_ci{
11158c2ecf20Sopenharmony_ci	if (reg == AB8500_USBCH_IPT_CRNTLVL_REG)
11168c2ecf20Sopenharmony_ci		return !di->flags.vbus_drop_end;
11178c2ecf20Sopenharmony_ci	else
11188c2ecf20Sopenharmony_ci		return true;
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci/**
11228c2ecf20Sopenharmony_ci * ab8500_charger_set_current() - set charger current
11238c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
11248c2ecf20Sopenharmony_ci * @ich:	charger current, in mA
11258c2ecf20Sopenharmony_ci * @reg:	select what charger register to set
11268c2ecf20Sopenharmony_ci *
11278c2ecf20Sopenharmony_ci * Set charger current.
11288c2ecf20Sopenharmony_ci * There is no state machine in the AB to step up/down the charger
11298c2ecf20Sopenharmony_ci * current to avoid dips and spikes on MAIN, VBUS and VBAT when
11308c2ecf20Sopenharmony_ci * charging is started. Instead we need to implement
11318c2ecf20Sopenharmony_ci * this charger current step-up/down here.
11328c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0(on success)
11338c2ecf20Sopenharmony_ci */
11348c2ecf20Sopenharmony_cistatic int ab8500_charger_set_current(struct ab8500_charger *di,
11358c2ecf20Sopenharmony_ci	int ich, int reg)
11368c2ecf20Sopenharmony_ci{
11378c2ecf20Sopenharmony_ci	int ret = 0;
11388c2ecf20Sopenharmony_ci	int curr_index, prev_curr_index, shift_value, i;
11398c2ecf20Sopenharmony_ci	u8 reg_value;
11408c2ecf20Sopenharmony_ci	u32 step_udelay;
11418c2ecf20Sopenharmony_ci	bool no_stepping = false;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	atomic_inc(&di->current_stepping_sessions);
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
11468c2ecf20Sopenharmony_ci		reg, &reg_value);
11478c2ecf20Sopenharmony_ci	if (ret < 0) {
11488c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s read failed\n", __func__);
11498c2ecf20Sopenharmony_ci		goto exit_set_current;
11508c2ecf20Sopenharmony_ci	}
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	switch (reg) {
11538c2ecf20Sopenharmony_ci	case AB8500_MCH_IPT_CURLVL_REG:
11548c2ecf20Sopenharmony_ci		shift_value = MAIN_CH_INPUT_CURR_SHIFT;
11558c2ecf20Sopenharmony_ci		prev_curr_index = (reg_value >> shift_value);
11568c2ecf20Sopenharmony_ci		curr_index = ab8500_current_to_regval(di, ich);
11578c2ecf20Sopenharmony_ci		step_udelay = STEP_UDELAY;
11588c2ecf20Sopenharmony_ci		if (!di->ac.charger_connected)
11598c2ecf20Sopenharmony_ci			no_stepping = true;
11608c2ecf20Sopenharmony_ci		break;
11618c2ecf20Sopenharmony_ci	case AB8500_USBCH_IPT_CRNTLVL_REG:
11628c2ecf20Sopenharmony_ci		shift_value = VBUS_IN_CURR_LIM_SHIFT;
11638c2ecf20Sopenharmony_ci		prev_curr_index = (reg_value >> shift_value);
11648c2ecf20Sopenharmony_ci		curr_index = ab8500_vbus_in_curr_to_regval(di, ich);
11658c2ecf20Sopenharmony_ci		step_udelay = STEP_UDELAY * 100;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci		if (!di->usb.charger_connected)
11688c2ecf20Sopenharmony_ci			no_stepping = true;
11698c2ecf20Sopenharmony_ci		break;
11708c2ecf20Sopenharmony_ci	case AB8500_CH_OPT_CRNTLVL_REG:
11718c2ecf20Sopenharmony_ci		shift_value = 0;
11728c2ecf20Sopenharmony_ci		prev_curr_index = (reg_value >> shift_value);
11738c2ecf20Sopenharmony_ci		curr_index = ab8500_current_to_regval(di, ich);
11748c2ecf20Sopenharmony_ci		step_udelay = STEP_UDELAY;
11758c2ecf20Sopenharmony_ci		if (curr_index && (curr_index - prev_curr_index) > 1)
11768c2ecf20Sopenharmony_ci			step_udelay *= 100;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci		if (!di->usb.charger_connected && !di->ac.charger_connected)
11798c2ecf20Sopenharmony_ci			no_stepping = true;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci		break;
11828c2ecf20Sopenharmony_ci	default:
11838c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s current register not valid\n", __func__);
11848c2ecf20Sopenharmony_ci		ret = -ENXIO;
11858c2ecf20Sopenharmony_ci		goto exit_set_current;
11868c2ecf20Sopenharmony_ci	}
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	if (curr_index < 0) {
11898c2ecf20Sopenharmony_ci		dev_err(di->dev, "requested current limit out-of-range\n");
11908c2ecf20Sopenharmony_ci		ret = -ENXIO;
11918c2ecf20Sopenharmony_ci		goto exit_set_current;
11928c2ecf20Sopenharmony_ci	}
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	/* only update current if it's been changed */
11958c2ecf20Sopenharmony_ci	if (prev_curr_index == curr_index) {
11968c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "%s current not changed for reg: 0x%02x\n",
11978c2ecf20Sopenharmony_ci			__func__, reg);
11988c2ecf20Sopenharmony_ci		ret = 0;
11998c2ecf20Sopenharmony_ci		goto exit_set_current;
12008c2ecf20Sopenharmony_ci	}
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n",
12038c2ecf20Sopenharmony_ci		__func__, ich, reg);
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	if (no_stepping) {
12068c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
12078c2ecf20Sopenharmony_ci					reg, (u8)curr_index << shift_value);
12088c2ecf20Sopenharmony_ci		if (ret)
12098c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s write failed\n", __func__);
12108c2ecf20Sopenharmony_ci	} else if (prev_curr_index > curr_index) {
12118c2ecf20Sopenharmony_ci		for (i = prev_curr_index - 1; i >= curr_index; i--) {
12128c2ecf20Sopenharmony_ci			dev_dbg(di->dev, "curr change_1 to: %x for 0x%02x\n",
12138c2ecf20Sopenharmony_ci				(u8) i << shift_value, reg);
12148c2ecf20Sopenharmony_ci			ret = abx500_set_register_interruptible(di->dev,
12158c2ecf20Sopenharmony_ci				AB8500_CHARGER, reg, (u8)i << shift_value);
12168c2ecf20Sopenharmony_ci			if (ret) {
12178c2ecf20Sopenharmony_ci				dev_err(di->dev, "%s write failed\n", __func__);
12188c2ecf20Sopenharmony_ci				goto exit_set_current;
12198c2ecf20Sopenharmony_ci			}
12208c2ecf20Sopenharmony_ci			if (i != curr_index)
12218c2ecf20Sopenharmony_ci				usleep_range(step_udelay, step_udelay * 2);
12228c2ecf20Sopenharmony_ci		}
12238c2ecf20Sopenharmony_ci	} else {
12248c2ecf20Sopenharmony_ci		bool allow = true;
12258c2ecf20Sopenharmony_ci		for (i = prev_curr_index + 1; i <= curr_index && allow; i++) {
12268c2ecf20Sopenharmony_ci			dev_dbg(di->dev, "curr change_2 to: %x for 0x%02x\n",
12278c2ecf20Sopenharmony_ci				(u8)i << shift_value, reg);
12288c2ecf20Sopenharmony_ci			ret = abx500_set_register_interruptible(di->dev,
12298c2ecf20Sopenharmony_ci				AB8500_CHARGER, reg, (u8)i << shift_value);
12308c2ecf20Sopenharmony_ci			if (ret) {
12318c2ecf20Sopenharmony_ci				dev_err(di->dev, "%s write failed\n", __func__);
12328c2ecf20Sopenharmony_ci				goto exit_set_current;
12338c2ecf20Sopenharmony_ci			}
12348c2ecf20Sopenharmony_ci			if (i != curr_index)
12358c2ecf20Sopenharmony_ci				usleep_range(step_udelay, step_udelay * 2);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci			allow = ab8500_charger_check_continue_stepping(di, reg);
12388c2ecf20Sopenharmony_ci		}
12398c2ecf20Sopenharmony_ci	}
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ciexit_set_current:
12428c2ecf20Sopenharmony_ci	atomic_dec(&di->current_stepping_sessions);
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	return ret;
12458c2ecf20Sopenharmony_ci}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci/**
12488c2ecf20Sopenharmony_ci * ab8500_charger_set_vbus_in_curr() - set VBUS input current limit
12498c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
12508c2ecf20Sopenharmony_ci * @ich_in:	charger input current limit
12518c2ecf20Sopenharmony_ci *
12528c2ecf20Sopenharmony_ci * Sets the current that can be drawn from the USB host
12538c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0(on success)
12548c2ecf20Sopenharmony_ci */
12558c2ecf20Sopenharmony_cistatic int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
12568c2ecf20Sopenharmony_ci		int ich_in)
12578c2ecf20Sopenharmony_ci{
12588c2ecf20Sopenharmony_ci	int min_value;
12598c2ecf20Sopenharmony_ci	int ret;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	/* We should always use to lowest current limit */
12628c2ecf20Sopenharmony_ci	min_value = min(di->bm->chg_params->usb_curr_max, ich_in);
12638c2ecf20Sopenharmony_ci	if (di->max_usb_in_curr.set_max > 0)
12648c2ecf20Sopenharmony_ci		min_value = min(di->max_usb_in_curr.set_max, min_value);
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	if (di->usb_state.usb_current >= 0)
12678c2ecf20Sopenharmony_ci		min_value = min(di->usb_state.usb_current, min_value);
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	switch (min_value) {
12708c2ecf20Sopenharmony_ci	case 100:
12718c2ecf20Sopenharmony_ci		if (di->vbat < VBAT_TRESH_IP_CUR_RED)
12728c2ecf20Sopenharmony_ci			min_value = USB_CH_IP_CUR_LVL_0P05;
12738c2ecf20Sopenharmony_ci		break;
12748c2ecf20Sopenharmony_ci	case 500:
12758c2ecf20Sopenharmony_ci		if (di->vbat < VBAT_TRESH_IP_CUR_RED)
12768c2ecf20Sopenharmony_ci			min_value = USB_CH_IP_CUR_LVL_0P45;
12778c2ecf20Sopenharmony_ci		break;
12788c2ecf20Sopenharmony_ci	default:
12798c2ecf20Sopenharmony_ci		break;
12808c2ecf20Sopenharmony_ci	}
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	dev_info(di->dev, "VBUS input current limit set to %d mA\n", min_value);
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	mutex_lock(&di->usb_ipt_crnt_lock);
12858c2ecf20Sopenharmony_ci	ret = ab8500_charger_set_current(di, min_value,
12868c2ecf20Sopenharmony_ci		AB8500_USBCH_IPT_CRNTLVL_REG);
12878c2ecf20Sopenharmony_ci	mutex_unlock(&di->usb_ipt_crnt_lock);
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	return ret;
12908c2ecf20Sopenharmony_ci}
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci/**
12938c2ecf20Sopenharmony_ci * ab8500_charger_set_main_in_curr() - set main charger input current
12948c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
12958c2ecf20Sopenharmony_ci * @ich_in:	input charger current, in mA
12968c2ecf20Sopenharmony_ci *
12978c2ecf20Sopenharmony_ci * Set main charger input current.
12988c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0(on success)
12998c2ecf20Sopenharmony_ci */
13008c2ecf20Sopenharmony_cistatic int ab8500_charger_set_main_in_curr(struct ab8500_charger *di,
13018c2ecf20Sopenharmony_ci	int ich_in)
13028c2ecf20Sopenharmony_ci{
13038c2ecf20Sopenharmony_ci	return ab8500_charger_set_current(di, ich_in,
13048c2ecf20Sopenharmony_ci		AB8500_MCH_IPT_CURLVL_REG);
13058c2ecf20Sopenharmony_ci}
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci/**
13088c2ecf20Sopenharmony_ci * ab8500_charger_set_output_curr() - set charger output current
13098c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
13108c2ecf20Sopenharmony_ci * @ich_out:	output charger current, in mA
13118c2ecf20Sopenharmony_ci *
13128c2ecf20Sopenharmony_ci * Set charger output current.
13138c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0(on success)
13148c2ecf20Sopenharmony_ci */
13158c2ecf20Sopenharmony_cistatic int ab8500_charger_set_output_curr(struct ab8500_charger *di,
13168c2ecf20Sopenharmony_ci	int ich_out)
13178c2ecf20Sopenharmony_ci{
13188c2ecf20Sopenharmony_ci	return ab8500_charger_set_current(di, ich_out,
13198c2ecf20Sopenharmony_ci		AB8500_CH_OPT_CRNTLVL_REG);
13208c2ecf20Sopenharmony_ci}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci/**
13238c2ecf20Sopenharmony_ci * ab8500_charger_led_en() - turn on/off chargign led
13248c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
13258c2ecf20Sopenharmony_ci * @on:		flag to turn on/off the chargign led
13268c2ecf20Sopenharmony_ci *
13278c2ecf20Sopenharmony_ci * Power ON/OFF charging LED indication
13288c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0(on success)
13298c2ecf20Sopenharmony_ci */
13308c2ecf20Sopenharmony_cistatic int ab8500_charger_led_en(struct ab8500_charger *di, int on)
13318c2ecf20Sopenharmony_ci{
13328c2ecf20Sopenharmony_ci	int ret;
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci	if (on) {
13358c2ecf20Sopenharmony_ci		/* Power ON charging LED indicator, set LED current to 5mA */
13368c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
13378c2ecf20Sopenharmony_ci			AB8500_LED_INDICATOR_PWM_CTRL,
13388c2ecf20Sopenharmony_ci			(LED_IND_CUR_5MA | LED_INDICATOR_PWM_ENA));
13398c2ecf20Sopenharmony_ci		if (ret) {
13408c2ecf20Sopenharmony_ci			dev_err(di->dev, "Power ON LED failed\n");
13418c2ecf20Sopenharmony_ci			return ret;
13428c2ecf20Sopenharmony_ci		}
13438c2ecf20Sopenharmony_ci		/* LED indicator PWM duty cycle 252/256 */
13448c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
13458c2ecf20Sopenharmony_ci			AB8500_LED_INDICATOR_PWM_DUTY,
13468c2ecf20Sopenharmony_ci			LED_INDICATOR_PWM_DUTY_252_256);
13478c2ecf20Sopenharmony_ci		if (ret) {
13488c2ecf20Sopenharmony_ci			dev_err(di->dev, "Set LED PWM duty cycle failed\n");
13498c2ecf20Sopenharmony_ci			return ret;
13508c2ecf20Sopenharmony_ci		}
13518c2ecf20Sopenharmony_ci	} else {
13528c2ecf20Sopenharmony_ci		/* Power off charging LED indicator */
13538c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
13548c2ecf20Sopenharmony_ci			AB8500_LED_INDICATOR_PWM_CTRL,
13558c2ecf20Sopenharmony_ci			LED_INDICATOR_PWM_DIS);
13568c2ecf20Sopenharmony_ci		if (ret) {
13578c2ecf20Sopenharmony_ci			dev_err(di->dev, "Power-off LED failed\n");
13588c2ecf20Sopenharmony_ci			return ret;
13598c2ecf20Sopenharmony_ci		}
13608c2ecf20Sopenharmony_ci	}
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	return ret;
13638c2ecf20Sopenharmony_ci}
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci/**
13668c2ecf20Sopenharmony_ci * ab8500_charger_ac_en() - enable or disable ac charging
13678c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
13688c2ecf20Sopenharmony_ci * @enable:	enable/disable flag
13698c2ecf20Sopenharmony_ci * @vset:	charging voltage
13708c2ecf20Sopenharmony_ci * @iset:	charging current
13718c2ecf20Sopenharmony_ci *
13728c2ecf20Sopenharmony_ci * Enable/Disable AC/Mains charging and turns on/off the charging led
13738c2ecf20Sopenharmony_ci * respectively.
13748c2ecf20Sopenharmony_ci **/
13758c2ecf20Sopenharmony_cistatic int ab8500_charger_ac_en(struct ux500_charger *charger,
13768c2ecf20Sopenharmony_ci	int enable, int vset, int iset)
13778c2ecf20Sopenharmony_ci{
13788c2ecf20Sopenharmony_ci	int ret;
13798c2ecf20Sopenharmony_ci	int volt_index;
13808c2ecf20Sopenharmony_ci	int curr_index;
13818c2ecf20Sopenharmony_ci	int input_curr_index;
13828c2ecf20Sopenharmony_ci	u8 overshoot = 0;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger);
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	if (enable) {
13878c2ecf20Sopenharmony_ci		/* Check if AC is connected */
13888c2ecf20Sopenharmony_ci		if (!di->ac.charger_connected) {
13898c2ecf20Sopenharmony_ci			dev_err(di->dev, "AC charger not connected\n");
13908c2ecf20Sopenharmony_ci			return -ENXIO;
13918c2ecf20Sopenharmony_ci		}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci		/* Enable AC charging */
13948c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "Enable AC: %dmV %dmA\n", vset, iset);
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci		/*
13978c2ecf20Sopenharmony_ci		 * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
13988c2ecf20Sopenharmony_ci		 * will be triggered every time we enable the VDD ADC supply.
13998c2ecf20Sopenharmony_ci		 * This will turn off charging for a short while.
14008c2ecf20Sopenharmony_ci		 * It can be avoided by having the supply on when
14018c2ecf20Sopenharmony_ci		 * there is a charger enabled. Normally the VDD ADC supply
14028c2ecf20Sopenharmony_ci		 * is enabled every time a GPADC conversion is triggered.
14038c2ecf20Sopenharmony_ci		 * We will force it to be enabled from this driver to have
14048c2ecf20Sopenharmony_ci		 * the GPADC module independent of the AB8500 chargers
14058c2ecf20Sopenharmony_ci		 */
14068c2ecf20Sopenharmony_ci		if (!di->vddadc_en_ac) {
14078c2ecf20Sopenharmony_ci			ret = regulator_enable(di->regu);
14088c2ecf20Sopenharmony_ci			if (ret)
14098c2ecf20Sopenharmony_ci				dev_warn(di->dev,
14108c2ecf20Sopenharmony_ci					"Failed to enable regulator\n");
14118c2ecf20Sopenharmony_ci			else
14128c2ecf20Sopenharmony_ci				di->vddadc_en_ac = true;
14138c2ecf20Sopenharmony_ci		}
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci		/* Check if the requested voltage or current is valid */
14168c2ecf20Sopenharmony_ci		volt_index = ab8500_voltage_to_regval(vset);
14178c2ecf20Sopenharmony_ci		curr_index = ab8500_current_to_regval(di, iset);
14188c2ecf20Sopenharmony_ci		input_curr_index = ab8500_current_to_regval(di,
14198c2ecf20Sopenharmony_ci			di->bm->chg_params->ac_curr_max);
14208c2ecf20Sopenharmony_ci		if (volt_index < 0 || curr_index < 0 || input_curr_index < 0) {
14218c2ecf20Sopenharmony_ci			dev_err(di->dev,
14228c2ecf20Sopenharmony_ci				"Charger voltage or current too high, "
14238c2ecf20Sopenharmony_ci				"charging not started\n");
14248c2ecf20Sopenharmony_ci			return -ENXIO;
14258c2ecf20Sopenharmony_ci		}
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci		/* ChVoltLevel: maximum battery charging voltage */
14288c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
14298c2ecf20Sopenharmony_ci			AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
14308c2ecf20Sopenharmony_ci		if (ret) {
14318c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s write failed\n", __func__);
14328c2ecf20Sopenharmony_ci			return ret;
14338c2ecf20Sopenharmony_ci		}
14348c2ecf20Sopenharmony_ci		/* MainChInputCurr: current that can be drawn from the charger*/
14358c2ecf20Sopenharmony_ci		ret = ab8500_charger_set_main_in_curr(di,
14368c2ecf20Sopenharmony_ci			di->bm->chg_params->ac_curr_max);
14378c2ecf20Sopenharmony_ci		if (ret) {
14388c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s Failed to set MainChInputCurr\n",
14398c2ecf20Sopenharmony_ci				__func__);
14408c2ecf20Sopenharmony_ci			return ret;
14418c2ecf20Sopenharmony_ci		}
14428c2ecf20Sopenharmony_ci		/* ChOutputCurentLevel: protected output current */
14438c2ecf20Sopenharmony_ci		ret = ab8500_charger_set_output_curr(di, iset);
14448c2ecf20Sopenharmony_ci		if (ret) {
14458c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s "
14468c2ecf20Sopenharmony_ci				"Failed to set ChOutputCurentLevel\n",
14478c2ecf20Sopenharmony_ci				__func__);
14488c2ecf20Sopenharmony_ci			return ret;
14498c2ecf20Sopenharmony_ci		}
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci		/* Check if VBAT overshoot control should be enabled */
14528c2ecf20Sopenharmony_ci		if (!di->bm->enable_overshoot)
14538c2ecf20Sopenharmony_ci			overshoot = MAIN_CH_NO_OVERSHOOT_ENA_N;
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci		/* Enable Main Charger */
14568c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
14578c2ecf20Sopenharmony_ci			AB8500_MCH_CTRL1, MAIN_CH_ENA | overshoot);
14588c2ecf20Sopenharmony_ci		if (ret) {
14598c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s write failed\n", __func__);
14608c2ecf20Sopenharmony_ci			return ret;
14618c2ecf20Sopenharmony_ci		}
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci		/* Power on charging LED indication */
14648c2ecf20Sopenharmony_ci		ret = ab8500_charger_led_en(di, true);
14658c2ecf20Sopenharmony_ci		if (ret < 0)
14668c2ecf20Sopenharmony_ci			dev_err(di->dev, "failed to enable LED\n");
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci		di->ac.charger_online = 1;
14698c2ecf20Sopenharmony_ci	} else {
14708c2ecf20Sopenharmony_ci		/* Disable AC charging */
14718c2ecf20Sopenharmony_ci		if (is_ab8500_1p1_or_earlier(di->parent)) {
14728c2ecf20Sopenharmony_ci			/*
14738c2ecf20Sopenharmony_ci			 * For ABB revision 1.0 and 1.1 there is a bug in the
14748c2ecf20Sopenharmony_ci			 * watchdog logic. That means we have to continuously
14758c2ecf20Sopenharmony_ci			 * kick the charger watchdog even when no charger is
14768c2ecf20Sopenharmony_ci			 * connected. This is only valid once the AC charger
14778c2ecf20Sopenharmony_ci			 * has been enabled. This is a bug that is not handled
14788c2ecf20Sopenharmony_ci			 * by the algorithm and the watchdog have to be kicked
14798c2ecf20Sopenharmony_ci			 * by the charger driver when the AC charger
14808c2ecf20Sopenharmony_ci			 * is disabled
14818c2ecf20Sopenharmony_ci			 */
14828c2ecf20Sopenharmony_ci			if (di->ac_conn) {
14838c2ecf20Sopenharmony_ci				queue_delayed_work(di->charger_wq,
14848c2ecf20Sopenharmony_ci					&di->kick_wd_work,
14858c2ecf20Sopenharmony_ci					round_jiffies(WD_KICK_INTERVAL));
14868c2ecf20Sopenharmony_ci			}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci			/*
14898c2ecf20Sopenharmony_ci			 * We can't turn off charging completely
14908c2ecf20Sopenharmony_ci			 * due to a bug in AB8500 cut1.
14918c2ecf20Sopenharmony_ci			 * If we do, charging will not start again.
14928c2ecf20Sopenharmony_ci			 * That is why we set the lowest voltage
14938c2ecf20Sopenharmony_ci			 * and current possible
14948c2ecf20Sopenharmony_ci			 */
14958c2ecf20Sopenharmony_ci			ret = abx500_set_register_interruptible(di->dev,
14968c2ecf20Sopenharmony_ci				AB8500_CHARGER,
14978c2ecf20Sopenharmony_ci				AB8500_CH_VOLT_LVL_REG, CH_VOL_LVL_3P5);
14988c2ecf20Sopenharmony_ci			if (ret) {
14998c2ecf20Sopenharmony_ci				dev_err(di->dev,
15008c2ecf20Sopenharmony_ci					"%s write failed\n", __func__);
15018c2ecf20Sopenharmony_ci				return ret;
15028c2ecf20Sopenharmony_ci			}
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci			ret = ab8500_charger_set_output_curr(di, 0);
15058c2ecf20Sopenharmony_ci			if (ret) {
15068c2ecf20Sopenharmony_ci				dev_err(di->dev, "%s "
15078c2ecf20Sopenharmony_ci					"Failed to set ChOutputCurentLevel\n",
15088c2ecf20Sopenharmony_ci					__func__);
15098c2ecf20Sopenharmony_ci				return ret;
15108c2ecf20Sopenharmony_ci			}
15118c2ecf20Sopenharmony_ci		} else {
15128c2ecf20Sopenharmony_ci			ret = abx500_set_register_interruptible(di->dev,
15138c2ecf20Sopenharmony_ci				AB8500_CHARGER,
15148c2ecf20Sopenharmony_ci				AB8500_MCH_CTRL1, 0);
15158c2ecf20Sopenharmony_ci			if (ret) {
15168c2ecf20Sopenharmony_ci				dev_err(di->dev,
15178c2ecf20Sopenharmony_ci					"%s write failed\n", __func__);
15188c2ecf20Sopenharmony_ci				return ret;
15198c2ecf20Sopenharmony_ci			}
15208c2ecf20Sopenharmony_ci		}
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci		ret = ab8500_charger_led_en(di, false);
15238c2ecf20Sopenharmony_ci		if (ret < 0)
15248c2ecf20Sopenharmony_ci			dev_err(di->dev, "failed to disable LED\n");
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci		di->ac.charger_online = 0;
15278c2ecf20Sopenharmony_ci		di->ac.wd_expired = false;
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci		/* Disable regulator if enabled */
15308c2ecf20Sopenharmony_ci		if (di->vddadc_en_ac) {
15318c2ecf20Sopenharmony_ci			regulator_disable(di->regu);
15328c2ecf20Sopenharmony_ci			di->vddadc_en_ac = false;
15338c2ecf20Sopenharmony_ci		}
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "%s Disabled AC charging\n", __func__);
15368c2ecf20Sopenharmony_ci	}
15378c2ecf20Sopenharmony_ci	ab8500_power_supply_changed(di, di->ac_chg.psy);
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	return ret;
15408c2ecf20Sopenharmony_ci}
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci/**
15438c2ecf20Sopenharmony_ci * ab8500_charger_usb_en() - enable usb charging
15448c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
15458c2ecf20Sopenharmony_ci * @enable:	enable/disable flag
15468c2ecf20Sopenharmony_ci * @vset:	charging voltage
15478c2ecf20Sopenharmony_ci * @ich_out:	charger output current
15488c2ecf20Sopenharmony_ci *
15498c2ecf20Sopenharmony_ci * Enable/Disable USB charging and turns on/off the charging led respectively.
15508c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0(on success)
15518c2ecf20Sopenharmony_ci */
15528c2ecf20Sopenharmony_cistatic int ab8500_charger_usb_en(struct ux500_charger *charger,
15538c2ecf20Sopenharmony_ci	int enable, int vset, int ich_out)
15548c2ecf20Sopenharmony_ci{
15558c2ecf20Sopenharmony_ci	int ret;
15568c2ecf20Sopenharmony_ci	int volt_index;
15578c2ecf20Sopenharmony_ci	int curr_index;
15588c2ecf20Sopenharmony_ci	u8 overshoot = 0;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	if (enable) {
15638c2ecf20Sopenharmony_ci		/* Check if USB is connected */
15648c2ecf20Sopenharmony_ci		if (!di->usb.charger_connected) {
15658c2ecf20Sopenharmony_ci			dev_err(di->dev, "USB charger not connected\n");
15668c2ecf20Sopenharmony_ci			return -ENXIO;
15678c2ecf20Sopenharmony_ci		}
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci		/*
15708c2ecf20Sopenharmony_ci		 * Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
15718c2ecf20Sopenharmony_ci		 * will be triggered every time we enable the VDD ADC supply.
15728c2ecf20Sopenharmony_ci		 * This will turn off charging for a short while.
15738c2ecf20Sopenharmony_ci		 * It can be avoided by having the supply on when
15748c2ecf20Sopenharmony_ci		 * there is a charger enabled. Normally the VDD ADC supply
15758c2ecf20Sopenharmony_ci		 * is enabled every time a GPADC conversion is triggered.
15768c2ecf20Sopenharmony_ci		 * We will force it to be enabled from this driver to have
15778c2ecf20Sopenharmony_ci		 * the GPADC module independent of the AB8500 chargers
15788c2ecf20Sopenharmony_ci		 */
15798c2ecf20Sopenharmony_ci		if (!di->vddadc_en_usb) {
15808c2ecf20Sopenharmony_ci			ret = regulator_enable(di->regu);
15818c2ecf20Sopenharmony_ci			if (ret)
15828c2ecf20Sopenharmony_ci				dev_warn(di->dev,
15838c2ecf20Sopenharmony_ci					"Failed to enable regulator\n");
15848c2ecf20Sopenharmony_ci			else
15858c2ecf20Sopenharmony_ci				di->vddadc_en_usb = true;
15868c2ecf20Sopenharmony_ci		}
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci		/* Enable USB charging */
15898c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "Enable USB: %dmV %dmA\n", vset, ich_out);
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci		/* Check if the requested voltage or current is valid */
15928c2ecf20Sopenharmony_ci		volt_index = ab8500_voltage_to_regval(vset);
15938c2ecf20Sopenharmony_ci		curr_index = ab8500_current_to_regval(di, ich_out);
15948c2ecf20Sopenharmony_ci		if (volt_index < 0 || curr_index < 0) {
15958c2ecf20Sopenharmony_ci			dev_err(di->dev,
15968c2ecf20Sopenharmony_ci				"Charger voltage or current too high, "
15978c2ecf20Sopenharmony_ci				"charging not started\n");
15988c2ecf20Sopenharmony_ci			return -ENXIO;
15998c2ecf20Sopenharmony_ci		}
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ci		/*
16028c2ecf20Sopenharmony_ci		 * ChVoltLevel: max voltage up to which battery can be
16038c2ecf20Sopenharmony_ci		 * charged
16048c2ecf20Sopenharmony_ci		 */
16058c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
16068c2ecf20Sopenharmony_ci			AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
16078c2ecf20Sopenharmony_ci		if (ret) {
16088c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s write failed\n", __func__);
16098c2ecf20Sopenharmony_ci			return ret;
16108c2ecf20Sopenharmony_ci		}
16118c2ecf20Sopenharmony_ci		/* Check if VBAT overshoot control should be enabled */
16128c2ecf20Sopenharmony_ci		if (!di->bm->enable_overshoot)
16138c2ecf20Sopenharmony_ci			overshoot = USB_CHG_NO_OVERSHOOT_ENA_N;
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci		/* Enable USB Charger */
16168c2ecf20Sopenharmony_ci		dev_dbg(di->dev,
16178c2ecf20Sopenharmony_ci			"Enabling USB with write to AB8500_USBCH_CTRL1_REG\n");
16188c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
16198c2ecf20Sopenharmony_ci			AB8500_USBCH_CTRL1_REG, USB_CH_ENA | overshoot);
16208c2ecf20Sopenharmony_ci		if (ret) {
16218c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s write failed\n", __func__);
16228c2ecf20Sopenharmony_ci			return ret;
16238c2ecf20Sopenharmony_ci		}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci		/* If success power on charging LED indication */
16268c2ecf20Sopenharmony_ci		ret = ab8500_charger_led_en(di, true);
16278c2ecf20Sopenharmony_ci		if (ret < 0)
16288c2ecf20Sopenharmony_ci			dev_err(di->dev, "failed to enable LED\n");
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci		di->usb.charger_online = 1;
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci		/* USBChInputCurr: current that can be drawn from the usb */
16338c2ecf20Sopenharmony_ci		ret = ab8500_charger_set_vbus_in_curr(di,
16348c2ecf20Sopenharmony_ci					di->max_usb_in_curr.usb_type_max);
16358c2ecf20Sopenharmony_ci		if (ret) {
16368c2ecf20Sopenharmony_ci			dev_err(di->dev, "setting USBChInputCurr failed\n");
16378c2ecf20Sopenharmony_ci			return ret;
16388c2ecf20Sopenharmony_ci		}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci		/* ChOutputCurentLevel: protected output current */
16418c2ecf20Sopenharmony_ci		ret = ab8500_charger_set_output_curr(di, ich_out);
16428c2ecf20Sopenharmony_ci		if (ret) {
16438c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s "
16448c2ecf20Sopenharmony_ci				"Failed to set ChOutputCurentLevel\n",
16458c2ecf20Sopenharmony_ci				__func__);
16468c2ecf20Sopenharmony_ci			return ret;
16478c2ecf20Sopenharmony_ci		}
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci		queue_delayed_work(di->charger_wq, &di->check_vbat_work, HZ);
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	} else {
16528c2ecf20Sopenharmony_ci		/* Disable USB charging */
16538c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
16548c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev,
16558c2ecf20Sopenharmony_ci			AB8500_CHARGER,
16568c2ecf20Sopenharmony_ci			AB8500_USBCH_CTRL1_REG, 0);
16578c2ecf20Sopenharmony_ci		if (ret) {
16588c2ecf20Sopenharmony_ci			dev_err(di->dev,
16598c2ecf20Sopenharmony_ci				"%s write failed\n", __func__);
16608c2ecf20Sopenharmony_ci			return ret;
16618c2ecf20Sopenharmony_ci		}
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci		ret = ab8500_charger_led_en(di, false);
16648c2ecf20Sopenharmony_ci		if (ret < 0)
16658c2ecf20Sopenharmony_ci			dev_err(di->dev, "failed to disable LED\n");
16668c2ecf20Sopenharmony_ci		/* USBChInputCurr: current that can be drawn from the usb */
16678c2ecf20Sopenharmony_ci		ret = ab8500_charger_set_vbus_in_curr(di, 0);
16688c2ecf20Sopenharmony_ci		if (ret) {
16698c2ecf20Sopenharmony_ci			dev_err(di->dev, "setting USBChInputCurr failed\n");
16708c2ecf20Sopenharmony_ci			return ret;
16718c2ecf20Sopenharmony_ci		}
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci		/* ChOutputCurentLevel: protected output current */
16748c2ecf20Sopenharmony_ci		ret = ab8500_charger_set_output_curr(di, 0);
16758c2ecf20Sopenharmony_ci		if (ret) {
16768c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s "
16778c2ecf20Sopenharmony_ci				"Failed to reset ChOutputCurentLevel\n",
16788c2ecf20Sopenharmony_ci				__func__);
16798c2ecf20Sopenharmony_ci			return ret;
16808c2ecf20Sopenharmony_ci		}
16818c2ecf20Sopenharmony_ci		di->usb.charger_online = 0;
16828c2ecf20Sopenharmony_ci		di->usb.wd_expired = false;
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci		/* Disable regulator if enabled */
16858c2ecf20Sopenharmony_ci		if (di->vddadc_en_usb) {
16868c2ecf20Sopenharmony_ci			regulator_disable(di->regu);
16878c2ecf20Sopenharmony_ci			di->vddadc_en_usb = false;
16888c2ecf20Sopenharmony_ci		}
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci		/* Cancel any pending Vbat check work */
16938c2ecf20Sopenharmony_ci		cancel_delayed_work(&di->check_vbat_work);
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	}
16968c2ecf20Sopenharmony_ci	ab8500_power_supply_changed(di, di->usb_chg.psy);
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	return ret;
16998c2ecf20Sopenharmony_ci}
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_cistatic int ab8500_external_charger_prepare(struct notifier_block *charger_nb,
17028c2ecf20Sopenharmony_ci				unsigned long event, void *data)
17038c2ecf20Sopenharmony_ci{
17048c2ecf20Sopenharmony_ci	int ret;
17058c2ecf20Sopenharmony_ci	struct device *dev = data;
17068c2ecf20Sopenharmony_ci	/*Toggle External charger control pin*/
17078c2ecf20Sopenharmony_ci	ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
17088c2ecf20Sopenharmony_ci				  AB8500_SYS_CHARGER_CONTROL_REG,
17098c2ecf20Sopenharmony_ci				  EXTERNAL_CHARGER_DISABLE_REG_VAL);
17108c2ecf20Sopenharmony_ci	if (ret < 0) {
17118c2ecf20Sopenharmony_ci		dev_err(dev, "write reg failed %d\n", ret);
17128c2ecf20Sopenharmony_ci		goto out;
17138c2ecf20Sopenharmony_ci	}
17148c2ecf20Sopenharmony_ci	ret = abx500_set_register_interruptible(dev, AB8500_SYS_CTRL1_BLOCK,
17158c2ecf20Sopenharmony_ci				  AB8500_SYS_CHARGER_CONTROL_REG,
17168c2ecf20Sopenharmony_ci				  EXTERNAL_CHARGER_ENABLE_REG_VAL);
17178c2ecf20Sopenharmony_ci	if (ret < 0)
17188c2ecf20Sopenharmony_ci		dev_err(dev, "Write reg failed %d\n", ret);
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ciout:
17218c2ecf20Sopenharmony_ci	return ret;
17228c2ecf20Sopenharmony_ci}
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci/**
17258c2ecf20Sopenharmony_ci * ab8500_charger_usb_check_enable() - enable usb charging
17268c2ecf20Sopenharmony_ci * @charger:	pointer to the ux500_charger structure
17278c2ecf20Sopenharmony_ci * @vset:	charging voltage
17288c2ecf20Sopenharmony_ci * @iset:	charger output current
17298c2ecf20Sopenharmony_ci *
17308c2ecf20Sopenharmony_ci * Check if the VBUS charger has been disconnected and reconnected without
17318c2ecf20Sopenharmony_ci * AB8500 rising an interrupt. Returns 0 on success.
17328c2ecf20Sopenharmony_ci */
17338c2ecf20Sopenharmony_cistatic int ab8500_charger_usb_check_enable(struct ux500_charger *charger,
17348c2ecf20Sopenharmony_ci	int vset, int iset)
17358c2ecf20Sopenharmony_ci{
17368c2ecf20Sopenharmony_ci	u8 usbch_ctrl1 = 0;
17378c2ecf20Sopenharmony_ci	int ret = 0;
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci	struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci	if (!di->usb.charger_connected)
17428c2ecf20Sopenharmony_ci		return ret;
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
17458c2ecf20Sopenharmony_ci				AB8500_USBCH_CTRL1_REG, &usbch_ctrl1);
17468c2ecf20Sopenharmony_ci	if (ret < 0) {
17478c2ecf20Sopenharmony_ci		dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
17488c2ecf20Sopenharmony_ci		return ret;
17498c2ecf20Sopenharmony_ci	}
17508c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "USB charger ctrl: 0x%02x\n", usbch_ctrl1);
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	if (!(usbch_ctrl1 & USB_CH_ENA)) {
17538c2ecf20Sopenharmony_ci		dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n");
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci		ret = abx500_mask_and_set_register_interruptible(di->dev,
17568c2ecf20Sopenharmony_ci					AB8500_CHARGER, AB8500_CHARGER_CTRL,
17578c2ecf20Sopenharmony_ci					DROP_COUNT_RESET, DROP_COUNT_RESET);
17588c2ecf20Sopenharmony_ci		if (ret < 0) {
17598c2ecf20Sopenharmony_ci			dev_err(di->dev, "ab8500 write failed %d\n", __LINE__);
17608c2ecf20Sopenharmony_ci			return ret;
17618c2ecf20Sopenharmony_ci		}
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci		ret = ab8500_charger_usb_en(&di->usb_chg, true, vset, iset);
17648c2ecf20Sopenharmony_ci		if (ret < 0) {
17658c2ecf20Sopenharmony_ci			dev_err(di->dev, "Failed to enable VBUS charger %d\n",
17668c2ecf20Sopenharmony_ci					__LINE__);
17678c2ecf20Sopenharmony_ci			return ret;
17688c2ecf20Sopenharmony_ci		}
17698c2ecf20Sopenharmony_ci	}
17708c2ecf20Sopenharmony_ci	return ret;
17718c2ecf20Sopenharmony_ci}
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci/**
17748c2ecf20Sopenharmony_ci * ab8500_charger_ac_check_enable() - enable usb charging
17758c2ecf20Sopenharmony_ci * @charger:	pointer to the ux500_charger structure
17768c2ecf20Sopenharmony_ci * @vset:	charging voltage
17778c2ecf20Sopenharmony_ci * @iset:	charger output current
17788c2ecf20Sopenharmony_ci *
17798c2ecf20Sopenharmony_ci * Check if the AC charger has been disconnected and reconnected without
17808c2ecf20Sopenharmony_ci * AB8500 rising an interrupt. Returns 0 on success.
17818c2ecf20Sopenharmony_ci */
17828c2ecf20Sopenharmony_cistatic int ab8500_charger_ac_check_enable(struct ux500_charger *charger,
17838c2ecf20Sopenharmony_ci	int vset, int iset)
17848c2ecf20Sopenharmony_ci{
17858c2ecf20Sopenharmony_ci	u8 mainch_ctrl1 = 0;
17868c2ecf20Sopenharmony_ci	int ret = 0;
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger);
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	if (!di->ac.charger_connected)
17918c2ecf20Sopenharmony_ci		return ret;
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_ci	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
17948c2ecf20Sopenharmony_ci				AB8500_MCH_CTRL1, &mainch_ctrl1);
17958c2ecf20Sopenharmony_ci	if (ret < 0) {
17968c2ecf20Sopenharmony_ci		dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
17978c2ecf20Sopenharmony_ci		return ret;
17988c2ecf20Sopenharmony_ci	}
17998c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "AC charger ctrl: 0x%02x\n", mainch_ctrl1);
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	if (!(mainch_ctrl1 & MAIN_CH_ENA)) {
18028c2ecf20Sopenharmony_ci		dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n");
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci		ret = abx500_mask_and_set_register_interruptible(di->dev,
18058c2ecf20Sopenharmony_ci					AB8500_CHARGER, AB8500_CHARGER_CTRL,
18068c2ecf20Sopenharmony_ci					DROP_COUNT_RESET, DROP_COUNT_RESET);
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_ci		if (ret < 0) {
18098c2ecf20Sopenharmony_ci			dev_err(di->dev, "ab8500 write failed %d\n", __LINE__);
18108c2ecf20Sopenharmony_ci			return ret;
18118c2ecf20Sopenharmony_ci		}
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci		ret = ab8500_charger_ac_en(&di->usb_chg, true, vset, iset);
18148c2ecf20Sopenharmony_ci		if (ret < 0) {
18158c2ecf20Sopenharmony_ci			dev_err(di->dev, "failed to enable AC charger %d\n",
18168c2ecf20Sopenharmony_ci				__LINE__);
18178c2ecf20Sopenharmony_ci			return ret;
18188c2ecf20Sopenharmony_ci		}
18198c2ecf20Sopenharmony_ci	}
18208c2ecf20Sopenharmony_ci	return ret;
18218c2ecf20Sopenharmony_ci}
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci/**
18248c2ecf20Sopenharmony_ci * ab8500_charger_watchdog_kick() - kick charger watchdog
18258c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
18268c2ecf20Sopenharmony_ci *
18278c2ecf20Sopenharmony_ci * Kick charger watchdog
18288c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0(on success)
18298c2ecf20Sopenharmony_ci */
18308c2ecf20Sopenharmony_cistatic int ab8500_charger_watchdog_kick(struct ux500_charger *charger)
18318c2ecf20Sopenharmony_ci{
18328c2ecf20Sopenharmony_ci	int ret;
18338c2ecf20Sopenharmony_ci	struct ab8500_charger *di;
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
18368c2ecf20Sopenharmony_ci		di = to_ab8500_charger_ac_device_info(charger);
18378c2ecf20Sopenharmony_ci	else if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB)
18388c2ecf20Sopenharmony_ci		di = to_ab8500_charger_usb_device_info(charger);
18398c2ecf20Sopenharmony_ci	else
18408c2ecf20Sopenharmony_ci		return -ENXIO;
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
18438c2ecf20Sopenharmony_ci		AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
18448c2ecf20Sopenharmony_ci	if (ret)
18458c2ecf20Sopenharmony_ci		dev_err(di->dev, "Failed to kick WD!\n");
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	return ret;
18488c2ecf20Sopenharmony_ci}
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci/**
18518c2ecf20Sopenharmony_ci * ab8500_charger_update_charger_current() - update charger current
18528c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
18538c2ecf20Sopenharmony_ci *
18548c2ecf20Sopenharmony_ci * Update the charger output current for the specified charger
18558c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0(on success)
18568c2ecf20Sopenharmony_ci */
18578c2ecf20Sopenharmony_cistatic int ab8500_charger_update_charger_current(struct ux500_charger *charger,
18588c2ecf20Sopenharmony_ci		int ich_out)
18598c2ecf20Sopenharmony_ci{
18608c2ecf20Sopenharmony_ci	int ret;
18618c2ecf20Sopenharmony_ci	struct ab8500_charger *di;
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci	if (charger->psy->desc->type == POWER_SUPPLY_TYPE_MAINS)
18648c2ecf20Sopenharmony_ci		di = to_ab8500_charger_ac_device_info(charger);
18658c2ecf20Sopenharmony_ci	else if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB)
18668c2ecf20Sopenharmony_ci		di = to_ab8500_charger_usb_device_info(charger);
18678c2ecf20Sopenharmony_ci	else
18688c2ecf20Sopenharmony_ci		return -ENXIO;
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci	ret = ab8500_charger_set_output_curr(di, ich_out);
18718c2ecf20Sopenharmony_ci	if (ret) {
18728c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s "
18738c2ecf20Sopenharmony_ci			"Failed to set ChOutputCurentLevel\n",
18748c2ecf20Sopenharmony_ci			__func__);
18758c2ecf20Sopenharmony_ci		return ret;
18768c2ecf20Sopenharmony_ci	}
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	/* Reset the main and usb drop input current measurement counter */
18798c2ecf20Sopenharmony_ci	ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
18808c2ecf20Sopenharmony_ci				AB8500_CHARGER_CTRL, DROP_COUNT_RESET);
18818c2ecf20Sopenharmony_ci	if (ret) {
18828c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s write failed\n", __func__);
18838c2ecf20Sopenharmony_ci		return ret;
18848c2ecf20Sopenharmony_ci	}
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci	return ret;
18878c2ecf20Sopenharmony_ci}
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_cistatic int ab8500_charger_get_ext_psy_data(struct device *dev, void *data)
18908c2ecf20Sopenharmony_ci{
18918c2ecf20Sopenharmony_ci	struct power_supply *psy;
18928c2ecf20Sopenharmony_ci	struct power_supply *ext = dev_get_drvdata(dev);
18938c2ecf20Sopenharmony_ci	const char **supplicants = (const char **)ext->supplied_to;
18948c2ecf20Sopenharmony_ci	struct ab8500_charger *di;
18958c2ecf20Sopenharmony_ci	union power_supply_propval ret;
18968c2ecf20Sopenharmony_ci	int j;
18978c2ecf20Sopenharmony_ci	struct ux500_charger *usb_chg;
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci	usb_chg = (struct ux500_charger *)data;
19008c2ecf20Sopenharmony_ci	psy = usb_chg->psy;
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci	di = to_ab8500_charger_usb_device_info(usb_chg);
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci	/* For all psy where the driver name appears in any supplied_to */
19058c2ecf20Sopenharmony_ci	j = match_string(supplicants, ext->num_supplicants, psy->desc->name);
19068c2ecf20Sopenharmony_ci	if (j < 0)
19078c2ecf20Sopenharmony_ci		return 0;
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci	/* Go through all properties for the psy */
19108c2ecf20Sopenharmony_ci	for (j = 0; j < ext->desc->num_properties; j++) {
19118c2ecf20Sopenharmony_ci		enum power_supply_property prop;
19128c2ecf20Sopenharmony_ci		prop = ext->desc->properties[j];
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci		if (power_supply_get_property(ext, prop, &ret))
19158c2ecf20Sopenharmony_ci			continue;
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci		switch (prop) {
19188c2ecf20Sopenharmony_ci		case POWER_SUPPLY_PROP_VOLTAGE_NOW:
19198c2ecf20Sopenharmony_ci			switch (ext->desc->type) {
19208c2ecf20Sopenharmony_ci			case POWER_SUPPLY_TYPE_BATTERY:
19218c2ecf20Sopenharmony_ci				di->vbat = ret.intval / 1000;
19228c2ecf20Sopenharmony_ci				break;
19238c2ecf20Sopenharmony_ci			default:
19248c2ecf20Sopenharmony_ci				break;
19258c2ecf20Sopenharmony_ci			}
19268c2ecf20Sopenharmony_ci			break;
19278c2ecf20Sopenharmony_ci		default:
19288c2ecf20Sopenharmony_ci			break;
19298c2ecf20Sopenharmony_ci		}
19308c2ecf20Sopenharmony_ci	}
19318c2ecf20Sopenharmony_ci	return 0;
19328c2ecf20Sopenharmony_ci}
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci/**
19358c2ecf20Sopenharmony_ci * ab8500_charger_check_vbat_work() - keep vbus current within spec
19368c2ecf20Sopenharmony_ci * @work	pointer to the work_struct structure
19378c2ecf20Sopenharmony_ci *
19388c2ecf20Sopenharmony_ci * Due to a asic bug it is necessary to lower the input current to the vbus
19398c2ecf20Sopenharmony_ci * charger when charging with at some specific levels. This issue is only valid
19408c2ecf20Sopenharmony_ci * for below a certain battery voltage. This function makes sure that the
19418c2ecf20Sopenharmony_ci * the allowed current limit isn't exceeded.
19428c2ecf20Sopenharmony_ci */
19438c2ecf20Sopenharmony_cistatic void ab8500_charger_check_vbat_work(struct work_struct *work)
19448c2ecf20Sopenharmony_ci{
19458c2ecf20Sopenharmony_ci	int t = 10;
19468c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
19478c2ecf20Sopenharmony_ci		struct ab8500_charger, check_vbat_work.work);
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci	class_for_each_device(power_supply_class, NULL,
19508c2ecf20Sopenharmony_ci		di->usb_chg.psy, ab8500_charger_get_ext_psy_data);
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	/* First run old_vbat is 0. */
19538c2ecf20Sopenharmony_ci	if (di->old_vbat == 0)
19548c2ecf20Sopenharmony_ci		di->old_vbat = di->vbat;
19558c2ecf20Sopenharmony_ci
19568c2ecf20Sopenharmony_ci	if (!((di->old_vbat <= VBAT_TRESH_IP_CUR_RED &&
19578c2ecf20Sopenharmony_ci		di->vbat <= VBAT_TRESH_IP_CUR_RED) ||
19588c2ecf20Sopenharmony_ci		(di->old_vbat > VBAT_TRESH_IP_CUR_RED &&
19598c2ecf20Sopenharmony_ci		di->vbat > VBAT_TRESH_IP_CUR_RED))) {
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "Vbat did cross threshold, curr: %d, new: %d,"
19628c2ecf20Sopenharmony_ci			" old: %d\n", di->max_usb_in_curr.usb_type_max,
19638c2ecf20Sopenharmony_ci			di->vbat, di->old_vbat);
19648c2ecf20Sopenharmony_ci		ab8500_charger_set_vbus_in_curr(di,
19658c2ecf20Sopenharmony_ci					di->max_usb_in_curr.usb_type_max);
19668c2ecf20Sopenharmony_ci		power_supply_changed(di->usb_chg.psy);
19678c2ecf20Sopenharmony_ci	}
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci	di->old_vbat = di->vbat;
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	/*
19728c2ecf20Sopenharmony_ci	 * No need to check the battery voltage every second when not close to
19738c2ecf20Sopenharmony_ci	 * the threshold.
19748c2ecf20Sopenharmony_ci	 */
19758c2ecf20Sopenharmony_ci	if (di->vbat < (VBAT_TRESH_IP_CUR_RED + 100) &&
19768c2ecf20Sopenharmony_ci		(di->vbat > (VBAT_TRESH_IP_CUR_RED - 100)))
19778c2ecf20Sopenharmony_ci			t = 1;
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	queue_delayed_work(di->charger_wq, &di->check_vbat_work, t * HZ);
19808c2ecf20Sopenharmony_ci}
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci/**
19838c2ecf20Sopenharmony_ci * ab8500_charger_check_hw_failure_work() - check main charger failure
19848c2ecf20Sopenharmony_ci * @work:	pointer to the work_struct structure
19858c2ecf20Sopenharmony_ci *
19868c2ecf20Sopenharmony_ci * Work queue function for checking the main charger status
19878c2ecf20Sopenharmony_ci */
19888c2ecf20Sopenharmony_cistatic void ab8500_charger_check_hw_failure_work(struct work_struct *work)
19898c2ecf20Sopenharmony_ci{
19908c2ecf20Sopenharmony_ci	int ret;
19918c2ecf20Sopenharmony_ci	u8 reg_value;
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
19948c2ecf20Sopenharmony_ci		struct ab8500_charger, check_hw_failure_work.work);
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci	/* Check if the status bits for HW failure is still active */
19978c2ecf20Sopenharmony_ci	if (di->flags.mainextchnotok) {
19988c2ecf20Sopenharmony_ci		ret = abx500_get_register_interruptible(di->dev,
19998c2ecf20Sopenharmony_ci			AB8500_CHARGER, AB8500_CH_STATUS2_REG, &reg_value);
20008c2ecf20Sopenharmony_ci		if (ret < 0) {
20018c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
20028c2ecf20Sopenharmony_ci			return;
20038c2ecf20Sopenharmony_ci		}
20048c2ecf20Sopenharmony_ci		if (!(reg_value & MAIN_CH_NOK)) {
20058c2ecf20Sopenharmony_ci			di->flags.mainextchnotok = false;
20068c2ecf20Sopenharmony_ci			ab8500_power_supply_changed(di, di->ac_chg.psy);
20078c2ecf20Sopenharmony_ci		}
20088c2ecf20Sopenharmony_ci	}
20098c2ecf20Sopenharmony_ci	if (di->flags.vbus_ovv) {
20108c2ecf20Sopenharmony_ci		ret = abx500_get_register_interruptible(di->dev,
20118c2ecf20Sopenharmony_ci			AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG,
20128c2ecf20Sopenharmony_ci			&reg_value);
20138c2ecf20Sopenharmony_ci		if (ret < 0) {
20148c2ecf20Sopenharmony_ci			dev_err(di->dev, "%s ab8500 read failed\n", __func__);
20158c2ecf20Sopenharmony_ci			return;
20168c2ecf20Sopenharmony_ci		}
20178c2ecf20Sopenharmony_ci		if (!(reg_value & VBUS_OVV_TH)) {
20188c2ecf20Sopenharmony_ci			di->flags.vbus_ovv = false;
20198c2ecf20Sopenharmony_ci			ab8500_power_supply_changed(di, di->usb_chg.psy);
20208c2ecf20Sopenharmony_ci		}
20218c2ecf20Sopenharmony_ci	}
20228c2ecf20Sopenharmony_ci	/* If we still have a failure, schedule a new check */
20238c2ecf20Sopenharmony_ci	if (di->flags.mainextchnotok || di->flags.vbus_ovv) {
20248c2ecf20Sopenharmony_ci		queue_delayed_work(di->charger_wq,
20258c2ecf20Sopenharmony_ci			&di->check_hw_failure_work, round_jiffies(HZ));
20268c2ecf20Sopenharmony_ci	}
20278c2ecf20Sopenharmony_ci}
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci/**
20308c2ecf20Sopenharmony_ci * ab8500_charger_kick_watchdog_work() - kick the watchdog
20318c2ecf20Sopenharmony_ci * @work:	pointer to the work_struct structure
20328c2ecf20Sopenharmony_ci *
20338c2ecf20Sopenharmony_ci * Work queue function for kicking the charger watchdog.
20348c2ecf20Sopenharmony_ci *
20358c2ecf20Sopenharmony_ci * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
20368c2ecf20Sopenharmony_ci * logic. That means we have to continuously kick the charger
20378c2ecf20Sopenharmony_ci * watchdog even when no charger is connected. This is only
20388c2ecf20Sopenharmony_ci * valid once the AC charger has been enabled. This is
20398c2ecf20Sopenharmony_ci * a bug that is not handled by the algorithm and the
20408c2ecf20Sopenharmony_ci * watchdog have to be kicked by the charger driver
20418c2ecf20Sopenharmony_ci * when the AC charger is disabled
20428c2ecf20Sopenharmony_ci */
20438c2ecf20Sopenharmony_cistatic void ab8500_charger_kick_watchdog_work(struct work_struct *work)
20448c2ecf20Sopenharmony_ci{
20458c2ecf20Sopenharmony_ci	int ret;
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
20488c2ecf20Sopenharmony_ci		struct ab8500_charger, kick_wd_work.work);
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci	ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
20518c2ecf20Sopenharmony_ci		AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
20528c2ecf20Sopenharmony_ci	if (ret)
20538c2ecf20Sopenharmony_ci		dev_err(di->dev, "Failed to kick WD!\n");
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci	/* Schedule a new watchdog kick */
20568c2ecf20Sopenharmony_ci	queue_delayed_work(di->charger_wq,
20578c2ecf20Sopenharmony_ci		&di->kick_wd_work, round_jiffies(WD_KICK_INTERVAL));
20588c2ecf20Sopenharmony_ci}
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_ci/**
20618c2ecf20Sopenharmony_ci * ab8500_charger_ac_work() - work to get and set main charger status
20628c2ecf20Sopenharmony_ci * @work:	pointer to the work_struct structure
20638c2ecf20Sopenharmony_ci *
20648c2ecf20Sopenharmony_ci * Work queue function for checking the main charger status
20658c2ecf20Sopenharmony_ci */
20668c2ecf20Sopenharmony_cistatic void ab8500_charger_ac_work(struct work_struct *work)
20678c2ecf20Sopenharmony_ci{
20688c2ecf20Sopenharmony_ci	int ret;
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
20718c2ecf20Sopenharmony_ci		struct ab8500_charger, ac_work);
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ci	/*
20748c2ecf20Sopenharmony_ci	 * Since we can't be sure that the events are received
20758c2ecf20Sopenharmony_ci	 * synchronously, we have the check if the main charger is
20768c2ecf20Sopenharmony_ci	 * connected by reading the status register
20778c2ecf20Sopenharmony_ci	 */
20788c2ecf20Sopenharmony_ci	ret = ab8500_charger_detect_chargers(di, false);
20798c2ecf20Sopenharmony_ci	if (ret < 0)
20808c2ecf20Sopenharmony_ci		return;
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci	if (ret & AC_PW_CONN) {
20838c2ecf20Sopenharmony_ci		di->ac.charger_connected = 1;
20848c2ecf20Sopenharmony_ci		di->ac_conn = true;
20858c2ecf20Sopenharmony_ci	} else {
20868c2ecf20Sopenharmony_ci		di->ac.charger_connected = 0;
20878c2ecf20Sopenharmony_ci	}
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_ci	ab8500_power_supply_changed(di, di->ac_chg.psy);
20908c2ecf20Sopenharmony_ci	sysfs_notify(&di->ac_chg.psy->dev.kobj, NULL, "present");
20918c2ecf20Sopenharmony_ci}
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_cistatic void ab8500_charger_usb_attached_work(struct work_struct *work)
20948c2ecf20Sopenharmony_ci{
20958c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
20968c2ecf20Sopenharmony_ci						 struct ab8500_charger,
20978c2ecf20Sopenharmony_ci						 usb_charger_attached_work.work);
20988c2ecf20Sopenharmony_ci	int usbch = (USB_CH_VBUSDROP | USB_CH_VBUSDETDBNC);
20998c2ecf20Sopenharmony_ci	int ret, i;
21008c2ecf20Sopenharmony_ci	u8 statval;
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++) {
21038c2ecf20Sopenharmony_ci		ret = abx500_get_register_interruptible(di->dev,
21048c2ecf20Sopenharmony_ci							AB8500_CHARGER,
21058c2ecf20Sopenharmony_ci							AB8500_CH_USBCH_STAT1_REG,
21068c2ecf20Sopenharmony_ci							&statval);
21078c2ecf20Sopenharmony_ci		if (ret < 0) {
21088c2ecf20Sopenharmony_ci			dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
21098c2ecf20Sopenharmony_ci			goto reschedule;
21108c2ecf20Sopenharmony_ci		}
21118c2ecf20Sopenharmony_ci		if ((statval & usbch) != usbch)
21128c2ecf20Sopenharmony_ci			goto reschedule;
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci		msleep(CHARGER_STATUS_POLL);
21158c2ecf20Sopenharmony_ci	}
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci	ab8500_charger_usb_en(&di->usb_chg, 0, 0, 0);
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci	mutex_lock(&di->charger_attached_mutex);
21208c2ecf20Sopenharmony_ci	mutex_unlock(&di->charger_attached_mutex);
21218c2ecf20Sopenharmony_ci
21228c2ecf20Sopenharmony_ci	return;
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_cireschedule:
21258c2ecf20Sopenharmony_ci	queue_delayed_work(di->charger_wq,
21268c2ecf20Sopenharmony_ci			   &di->usb_charger_attached_work,
21278c2ecf20Sopenharmony_ci			   HZ);
21288c2ecf20Sopenharmony_ci}
21298c2ecf20Sopenharmony_ci
21308c2ecf20Sopenharmony_cistatic void ab8500_charger_ac_attached_work(struct work_struct *work)
21318c2ecf20Sopenharmony_ci{
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
21348c2ecf20Sopenharmony_ci						 struct ab8500_charger,
21358c2ecf20Sopenharmony_ci						 ac_charger_attached_work.work);
21368c2ecf20Sopenharmony_ci	int mainch = (MAIN_CH_STATUS2_MAINCHGDROP |
21378c2ecf20Sopenharmony_ci		      MAIN_CH_STATUS2_MAINCHARGERDETDBNC);
21388c2ecf20Sopenharmony_ci	int ret, i;
21398c2ecf20Sopenharmony_ci	u8 statval;
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++) {
21428c2ecf20Sopenharmony_ci		ret = abx500_get_register_interruptible(di->dev,
21438c2ecf20Sopenharmony_ci							AB8500_CHARGER,
21448c2ecf20Sopenharmony_ci							AB8500_CH_STATUS2_REG,
21458c2ecf20Sopenharmony_ci							&statval);
21468c2ecf20Sopenharmony_ci		if (ret < 0) {
21478c2ecf20Sopenharmony_ci			dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
21488c2ecf20Sopenharmony_ci			goto reschedule;
21498c2ecf20Sopenharmony_ci		}
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_ci		if ((statval & mainch) != mainch)
21528c2ecf20Sopenharmony_ci			goto reschedule;
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci		msleep(CHARGER_STATUS_POLL);
21558c2ecf20Sopenharmony_ci	}
21568c2ecf20Sopenharmony_ci
21578c2ecf20Sopenharmony_ci	ab8500_charger_ac_en(&di->ac_chg, 0, 0, 0);
21588c2ecf20Sopenharmony_ci	queue_work(di->charger_wq, &di->ac_work);
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	mutex_lock(&di->charger_attached_mutex);
21618c2ecf20Sopenharmony_ci	mutex_unlock(&di->charger_attached_mutex);
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	return;
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_cireschedule:
21668c2ecf20Sopenharmony_ci	queue_delayed_work(di->charger_wq,
21678c2ecf20Sopenharmony_ci			   &di->ac_charger_attached_work,
21688c2ecf20Sopenharmony_ci			   HZ);
21698c2ecf20Sopenharmony_ci}
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci/**
21728c2ecf20Sopenharmony_ci * ab8500_charger_detect_usb_type_work() - work to detect USB type
21738c2ecf20Sopenharmony_ci * @work:	Pointer to the work_struct structure
21748c2ecf20Sopenharmony_ci *
21758c2ecf20Sopenharmony_ci * Detect the type of USB plugged
21768c2ecf20Sopenharmony_ci */
21778c2ecf20Sopenharmony_cistatic void ab8500_charger_detect_usb_type_work(struct work_struct *work)
21788c2ecf20Sopenharmony_ci{
21798c2ecf20Sopenharmony_ci	int ret;
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
21828c2ecf20Sopenharmony_ci		struct ab8500_charger, detect_usb_type_work);
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	/*
21858c2ecf20Sopenharmony_ci	 * Since we can't be sure that the events are received
21868c2ecf20Sopenharmony_ci	 * synchronously, we have the check if is
21878c2ecf20Sopenharmony_ci	 * connected by reading the status register
21888c2ecf20Sopenharmony_ci	 */
21898c2ecf20Sopenharmony_ci	ret = ab8500_charger_detect_chargers(di, false);
21908c2ecf20Sopenharmony_ci	if (ret < 0)
21918c2ecf20Sopenharmony_ci		return;
21928c2ecf20Sopenharmony_ci
21938c2ecf20Sopenharmony_ci	if (!(ret & USB_PW_CONN)) {
21948c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "%s di->vbus_detected = false\n", __func__);
21958c2ecf20Sopenharmony_ci		di->vbus_detected = false;
21968c2ecf20Sopenharmony_ci		ab8500_charger_set_usb_connected(di, false);
21978c2ecf20Sopenharmony_ci		ab8500_power_supply_changed(di, di->usb_chg.psy);
21988c2ecf20Sopenharmony_ci	} else {
21998c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "%s di->vbus_detected = true\n", __func__);
22008c2ecf20Sopenharmony_ci		di->vbus_detected = true;
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_ci		if (is_ab8500_1p1_or_earlier(di->parent)) {
22038c2ecf20Sopenharmony_ci			ret = ab8500_charger_detect_usb_type(di);
22048c2ecf20Sopenharmony_ci			if (!ret) {
22058c2ecf20Sopenharmony_ci				ab8500_charger_set_usb_connected(di, true);
22068c2ecf20Sopenharmony_ci				ab8500_power_supply_changed(di,
22078c2ecf20Sopenharmony_ci							    di->usb_chg.psy);
22088c2ecf20Sopenharmony_ci			}
22098c2ecf20Sopenharmony_ci		} else {
22108c2ecf20Sopenharmony_ci			/*
22118c2ecf20Sopenharmony_ci			 * For ABB cut2.0 and onwards we have an IRQ,
22128c2ecf20Sopenharmony_ci			 * USB_LINK_STATUS that will be triggered when the USB
22138c2ecf20Sopenharmony_ci			 * link status changes. The exception is USB connected
22148c2ecf20Sopenharmony_ci			 * during startup. Then we don't get a
22158c2ecf20Sopenharmony_ci			 * USB_LINK_STATUS IRQ
22168c2ecf20Sopenharmony_ci			 */
22178c2ecf20Sopenharmony_ci			if (di->vbus_detected_start) {
22188c2ecf20Sopenharmony_ci				di->vbus_detected_start = false;
22198c2ecf20Sopenharmony_ci				ret = ab8500_charger_detect_usb_type(di);
22208c2ecf20Sopenharmony_ci				if (!ret) {
22218c2ecf20Sopenharmony_ci					ab8500_charger_set_usb_connected(di,
22228c2ecf20Sopenharmony_ci						true);
22238c2ecf20Sopenharmony_ci					ab8500_power_supply_changed(di,
22248c2ecf20Sopenharmony_ci						di->usb_chg.psy);
22258c2ecf20Sopenharmony_ci				}
22268c2ecf20Sopenharmony_ci			}
22278c2ecf20Sopenharmony_ci		}
22288c2ecf20Sopenharmony_ci	}
22298c2ecf20Sopenharmony_ci}
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci/**
22328c2ecf20Sopenharmony_ci * ab8500_charger_usb_link_attach_work() - work to detect USB type
22338c2ecf20Sopenharmony_ci * @work:	pointer to the work_struct structure
22348c2ecf20Sopenharmony_ci *
22358c2ecf20Sopenharmony_ci * Detect the type of USB plugged
22368c2ecf20Sopenharmony_ci */
22378c2ecf20Sopenharmony_cistatic void ab8500_charger_usb_link_attach_work(struct work_struct *work)
22388c2ecf20Sopenharmony_ci{
22398c2ecf20Sopenharmony_ci	struct ab8500_charger *di =
22408c2ecf20Sopenharmony_ci		container_of(work, struct ab8500_charger, attach_work.work);
22418c2ecf20Sopenharmony_ci	int ret;
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_ci	/* Update maximum input current if USB enumeration is not detected */
22448c2ecf20Sopenharmony_ci	if (!di->usb.charger_online) {
22458c2ecf20Sopenharmony_ci		ret = ab8500_charger_set_vbus_in_curr(di,
22468c2ecf20Sopenharmony_ci					di->max_usb_in_curr.usb_type_max);
22478c2ecf20Sopenharmony_ci		if (ret)
22488c2ecf20Sopenharmony_ci			return;
22498c2ecf20Sopenharmony_ci	}
22508c2ecf20Sopenharmony_ci
22518c2ecf20Sopenharmony_ci	ab8500_charger_set_usb_connected(di, true);
22528c2ecf20Sopenharmony_ci	ab8500_power_supply_changed(di, di->usb_chg.psy);
22538c2ecf20Sopenharmony_ci}
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci/**
22568c2ecf20Sopenharmony_ci * ab8500_charger_usb_link_status_work() - work to detect USB type
22578c2ecf20Sopenharmony_ci * @work:	pointer to the work_struct structure
22588c2ecf20Sopenharmony_ci *
22598c2ecf20Sopenharmony_ci * Detect the type of USB plugged
22608c2ecf20Sopenharmony_ci */
22618c2ecf20Sopenharmony_cistatic void ab8500_charger_usb_link_status_work(struct work_struct *work)
22628c2ecf20Sopenharmony_ci{
22638c2ecf20Sopenharmony_ci	int detected_chargers;
22648c2ecf20Sopenharmony_ci	int ret;
22658c2ecf20Sopenharmony_ci	u8 val;
22668c2ecf20Sopenharmony_ci	u8 link_status;
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
22698c2ecf20Sopenharmony_ci		struct ab8500_charger, usb_link_status_work);
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci	/*
22728c2ecf20Sopenharmony_ci	 * Since we can't be sure that the events are received
22738c2ecf20Sopenharmony_ci	 * synchronously, we have the check if  is
22748c2ecf20Sopenharmony_ci	 * connected by reading the status register
22758c2ecf20Sopenharmony_ci	 */
22768c2ecf20Sopenharmony_ci	detected_chargers = ab8500_charger_detect_chargers(di, false);
22778c2ecf20Sopenharmony_ci	if (detected_chargers < 0)
22788c2ecf20Sopenharmony_ci		return;
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci	/*
22818c2ecf20Sopenharmony_ci	 * Some chargers that breaks the USB spec is
22828c2ecf20Sopenharmony_ci	 * identified as invalid by AB8500 and it refuse
22838c2ecf20Sopenharmony_ci	 * to start the charging process. but by jumping
22848c2ecf20Sopenharmony_ci	 * through a few hoops it can be forced to start.
22858c2ecf20Sopenharmony_ci	 */
22868c2ecf20Sopenharmony_ci	if (is_ab8500(di->parent))
22878c2ecf20Sopenharmony_ci		ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
22888c2ecf20Sopenharmony_ci					AB8500_USB_LINE_STAT_REG, &val);
22898c2ecf20Sopenharmony_ci	else
22908c2ecf20Sopenharmony_ci		ret = abx500_get_register_interruptible(di->dev, AB8500_USB,
22918c2ecf20Sopenharmony_ci					AB8500_USB_LINK1_STAT_REG, &val);
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_ci	if (ret >= 0)
22948c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "UsbLineStatus register = 0x%02x\n", val);
22958c2ecf20Sopenharmony_ci	else
22968c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "Error reading USB link status\n");
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_ci	if (is_ab8500(di->parent))
22998c2ecf20Sopenharmony_ci		link_status = AB8500_USB_LINK_STATUS;
23008c2ecf20Sopenharmony_ci	else
23018c2ecf20Sopenharmony_ci		link_status = AB8505_USB_LINK_STATUS;
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ci	if (detected_chargers & USB_PW_CONN) {
23048c2ecf20Sopenharmony_ci		if (((val & link_status) >> USB_LINK_STATUS_SHIFT) ==
23058c2ecf20Sopenharmony_ci				USB_STAT_NOT_VALID_LINK &&
23068c2ecf20Sopenharmony_ci				di->invalid_charger_detect_state == 0) {
23078c2ecf20Sopenharmony_ci			dev_dbg(di->dev,
23088c2ecf20Sopenharmony_ci					"Invalid charger detected, state= 0\n");
23098c2ecf20Sopenharmony_ci			/*Enable charger*/
23108c2ecf20Sopenharmony_ci			abx500_mask_and_set_register_interruptible(di->dev,
23118c2ecf20Sopenharmony_ci					AB8500_CHARGER, AB8500_USBCH_CTRL1_REG,
23128c2ecf20Sopenharmony_ci					USB_CH_ENA, USB_CH_ENA);
23138c2ecf20Sopenharmony_ci			/*Enable charger detection*/
23148c2ecf20Sopenharmony_ci			abx500_mask_and_set_register_interruptible(di->dev,
23158c2ecf20Sopenharmony_ci					AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
23168c2ecf20Sopenharmony_ci					USB_CH_DET, USB_CH_DET);
23178c2ecf20Sopenharmony_ci			di->invalid_charger_detect_state = 1;
23188c2ecf20Sopenharmony_ci			/*exit and wait for new link status interrupt.*/
23198c2ecf20Sopenharmony_ci			return;
23208c2ecf20Sopenharmony_ci
23218c2ecf20Sopenharmony_ci		}
23228c2ecf20Sopenharmony_ci		if (di->invalid_charger_detect_state == 1) {
23238c2ecf20Sopenharmony_ci			dev_dbg(di->dev,
23248c2ecf20Sopenharmony_ci					"Invalid charger detected, state= 1\n");
23258c2ecf20Sopenharmony_ci			/*Stop charger detection*/
23268c2ecf20Sopenharmony_ci			abx500_mask_and_set_register_interruptible(di->dev,
23278c2ecf20Sopenharmony_ci					AB8500_USB, AB8500_USB_LINE_CTRL2_REG,
23288c2ecf20Sopenharmony_ci					USB_CH_DET, 0x00);
23298c2ecf20Sopenharmony_ci			/*Check link status*/
23308c2ecf20Sopenharmony_ci			if (is_ab8500(di->parent))
23318c2ecf20Sopenharmony_ci				ret = abx500_get_register_interruptible(di->dev,
23328c2ecf20Sopenharmony_ci					AB8500_USB, AB8500_USB_LINE_STAT_REG,
23338c2ecf20Sopenharmony_ci					&val);
23348c2ecf20Sopenharmony_ci			else
23358c2ecf20Sopenharmony_ci				ret = abx500_get_register_interruptible(di->dev,
23368c2ecf20Sopenharmony_ci					AB8500_USB, AB8500_USB_LINK1_STAT_REG,
23378c2ecf20Sopenharmony_ci					&val);
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ci			dev_dbg(di->dev, "USB link status= 0x%02x\n",
23408c2ecf20Sopenharmony_ci				(val & link_status) >> USB_LINK_STATUS_SHIFT);
23418c2ecf20Sopenharmony_ci			di->invalid_charger_detect_state = 2;
23428c2ecf20Sopenharmony_ci		}
23438c2ecf20Sopenharmony_ci	} else {
23448c2ecf20Sopenharmony_ci		di->invalid_charger_detect_state = 0;
23458c2ecf20Sopenharmony_ci	}
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	if (!(detected_chargers & USB_PW_CONN)) {
23488c2ecf20Sopenharmony_ci		di->vbus_detected = false;
23498c2ecf20Sopenharmony_ci		ab8500_charger_set_usb_connected(di, false);
23508c2ecf20Sopenharmony_ci		ab8500_power_supply_changed(di, di->usb_chg.psy);
23518c2ecf20Sopenharmony_ci		return;
23528c2ecf20Sopenharmony_ci	}
23538c2ecf20Sopenharmony_ci
23548c2ecf20Sopenharmony_ci	dev_dbg(di->dev,"%s di->vbus_detected = true\n",__func__);
23558c2ecf20Sopenharmony_ci	di->vbus_detected = true;
23568c2ecf20Sopenharmony_ci	ret = ab8500_charger_read_usb_type(di);
23578c2ecf20Sopenharmony_ci	if (ret) {
23588c2ecf20Sopenharmony_ci		if (ret == -ENXIO) {
23598c2ecf20Sopenharmony_ci			/* No valid charger type detected */
23608c2ecf20Sopenharmony_ci			ab8500_charger_set_usb_connected(di, false);
23618c2ecf20Sopenharmony_ci			ab8500_power_supply_changed(di, di->usb_chg.psy);
23628c2ecf20Sopenharmony_ci		}
23638c2ecf20Sopenharmony_ci		return;
23648c2ecf20Sopenharmony_ci	}
23658c2ecf20Sopenharmony_ci
23668c2ecf20Sopenharmony_ci	if (di->usb_device_is_unrecognised) {
23678c2ecf20Sopenharmony_ci		dev_dbg(di->dev,
23688c2ecf20Sopenharmony_ci			"Potential Legacy Charger device. "
23698c2ecf20Sopenharmony_ci			"Delay work for %d msec for USB enum "
23708c2ecf20Sopenharmony_ci			"to finish",
23718c2ecf20Sopenharmony_ci			WAIT_ACA_RID_ENUMERATION);
23728c2ecf20Sopenharmony_ci		queue_delayed_work(di->charger_wq,
23738c2ecf20Sopenharmony_ci				   &di->attach_work,
23748c2ecf20Sopenharmony_ci				   msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
23758c2ecf20Sopenharmony_ci	} else if (di->is_aca_rid == 1) {
23768c2ecf20Sopenharmony_ci		/* Only wait once */
23778c2ecf20Sopenharmony_ci		di->is_aca_rid++;
23788c2ecf20Sopenharmony_ci		dev_dbg(di->dev,
23798c2ecf20Sopenharmony_ci			"%s Wait %d msec for USB enum to finish",
23808c2ecf20Sopenharmony_ci			__func__, WAIT_ACA_RID_ENUMERATION);
23818c2ecf20Sopenharmony_ci		queue_delayed_work(di->charger_wq,
23828c2ecf20Sopenharmony_ci				   &di->attach_work,
23838c2ecf20Sopenharmony_ci				   msecs_to_jiffies(WAIT_ACA_RID_ENUMERATION));
23848c2ecf20Sopenharmony_ci	} else {
23858c2ecf20Sopenharmony_ci		queue_delayed_work(di->charger_wq,
23868c2ecf20Sopenharmony_ci				   &di->attach_work,
23878c2ecf20Sopenharmony_ci				   0);
23888c2ecf20Sopenharmony_ci	}
23898c2ecf20Sopenharmony_ci}
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_cistatic void ab8500_charger_usb_state_changed_work(struct work_struct *work)
23928c2ecf20Sopenharmony_ci{
23938c2ecf20Sopenharmony_ci	int ret;
23948c2ecf20Sopenharmony_ci	unsigned long flags;
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
23978c2ecf20Sopenharmony_ci		struct ab8500_charger, usb_state_changed_work.work);
23988c2ecf20Sopenharmony_ci
23998c2ecf20Sopenharmony_ci	if (!di->vbus_detected)	{
24008c2ecf20Sopenharmony_ci		dev_dbg(di->dev,
24018c2ecf20Sopenharmony_ci			"%s !di->vbus_detected\n",
24028c2ecf20Sopenharmony_ci			__func__);
24038c2ecf20Sopenharmony_ci		return;
24048c2ecf20Sopenharmony_ci	}
24058c2ecf20Sopenharmony_ci
24068c2ecf20Sopenharmony_ci	spin_lock_irqsave(&di->usb_state.usb_lock, flags);
24078c2ecf20Sopenharmony_ci	di->usb_state.state = di->usb_state.state_tmp;
24088c2ecf20Sopenharmony_ci	di->usb_state.usb_current = di->usb_state.usb_current_tmp;
24098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&di->usb_state.usb_lock, flags);
24108c2ecf20Sopenharmony_ci
24118c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
24128c2ecf20Sopenharmony_ci		__func__, di->usb_state.state, di->usb_state.usb_current);
24138c2ecf20Sopenharmony_ci
24148c2ecf20Sopenharmony_ci	switch (di->usb_state.state) {
24158c2ecf20Sopenharmony_ci	case AB8500_BM_USB_STATE_RESET_HS:
24168c2ecf20Sopenharmony_ci	case AB8500_BM_USB_STATE_RESET_FS:
24178c2ecf20Sopenharmony_ci	case AB8500_BM_USB_STATE_SUSPEND:
24188c2ecf20Sopenharmony_ci	case AB8500_BM_USB_STATE_MAX:
24198c2ecf20Sopenharmony_ci		ab8500_charger_set_usb_connected(di, false);
24208c2ecf20Sopenharmony_ci		ab8500_power_supply_changed(di, di->usb_chg.psy);
24218c2ecf20Sopenharmony_ci		break;
24228c2ecf20Sopenharmony_ci
24238c2ecf20Sopenharmony_ci	case AB8500_BM_USB_STATE_RESUME:
24248c2ecf20Sopenharmony_ci		/*
24258c2ecf20Sopenharmony_ci		 * when suspend->resume there should be delay
24268c2ecf20Sopenharmony_ci		 * of 1sec for enabling charging
24278c2ecf20Sopenharmony_ci		 */
24288c2ecf20Sopenharmony_ci		msleep(1000);
24298c2ecf20Sopenharmony_ci		fallthrough;
24308c2ecf20Sopenharmony_ci	case AB8500_BM_USB_STATE_CONFIGURED:
24318c2ecf20Sopenharmony_ci		/*
24328c2ecf20Sopenharmony_ci		 * USB is configured, enable charging with the charging
24338c2ecf20Sopenharmony_ci		 * input current obtained from USB driver
24348c2ecf20Sopenharmony_ci		 */
24358c2ecf20Sopenharmony_ci		if (!ab8500_charger_get_usb_cur(di)) {
24368c2ecf20Sopenharmony_ci			/* Update maximum input current */
24378c2ecf20Sopenharmony_ci			ret = ab8500_charger_set_vbus_in_curr(di,
24388c2ecf20Sopenharmony_ci					di->max_usb_in_curr.usb_type_max);
24398c2ecf20Sopenharmony_ci			if (ret)
24408c2ecf20Sopenharmony_ci				return;
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_ci			ab8500_charger_set_usb_connected(di, true);
24438c2ecf20Sopenharmony_ci			ab8500_power_supply_changed(di, di->usb_chg.psy);
24448c2ecf20Sopenharmony_ci		}
24458c2ecf20Sopenharmony_ci		break;
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	default:
24488c2ecf20Sopenharmony_ci		break;
24498c2ecf20Sopenharmony_ci	}
24508c2ecf20Sopenharmony_ci}
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_ci/**
24538c2ecf20Sopenharmony_ci * ab8500_charger_check_usbchargernotok_work() - check USB chg not ok status
24548c2ecf20Sopenharmony_ci * @work:	pointer to the work_struct structure
24558c2ecf20Sopenharmony_ci *
24568c2ecf20Sopenharmony_ci * Work queue function for checking the USB charger Not OK status
24578c2ecf20Sopenharmony_ci */
24588c2ecf20Sopenharmony_cistatic void ab8500_charger_check_usbchargernotok_work(struct work_struct *work)
24598c2ecf20Sopenharmony_ci{
24608c2ecf20Sopenharmony_ci	int ret;
24618c2ecf20Sopenharmony_ci	u8 reg_value;
24628c2ecf20Sopenharmony_ci	bool prev_status;
24638c2ecf20Sopenharmony_ci
24648c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
24658c2ecf20Sopenharmony_ci		struct ab8500_charger, check_usbchgnotok_work.work);
24668c2ecf20Sopenharmony_ci
24678c2ecf20Sopenharmony_ci	/* Check if the status bit for usbchargernotok is still active */
24688c2ecf20Sopenharmony_ci	ret = abx500_get_register_interruptible(di->dev,
24698c2ecf20Sopenharmony_ci		AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG, &reg_value);
24708c2ecf20Sopenharmony_ci	if (ret < 0) {
24718c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
24728c2ecf20Sopenharmony_ci		return;
24738c2ecf20Sopenharmony_ci	}
24748c2ecf20Sopenharmony_ci	prev_status = di->flags.usbchargernotok;
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_ci	if (reg_value & VBUS_CH_NOK) {
24778c2ecf20Sopenharmony_ci		di->flags.usbchargernotok = true;
24788c2ecf20Sopenharmony_ci		/* Check again in 1sec */
24798c2ecf20Sopenharmony_ci		queue_delayed_work(di->charger_wq,
24808c2ecf20Sopenharmony_ci			&di->check_usbchgnotok_work, HZ);
24818c2ecf20Sopenharmony_ci	} else {
24828c2ecf20Sopenharmony_ci		di->flags.usbchargernotok = false;
24838c2ecf20Sopenharmony_ci		di->flags.vbus_collapse = false;
24848c2ecf20Sopenharmony_ci	}
24858c2ecf20Sopenharmony_ci
24868c2ecf20Sopenharmony_ci	if (prev_status != di->flags.usbchargernotok)
24878c2ecf20Sopenharmony_ci		ab8500_power_supply_changed(di, di->usb_chg.psy);
24888c2ecf20Sopenharmony_ci}
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ci/**
24918c2ecf20Sopenharmony_ci * ab8500_charger_check_main_thermal_prot_work() - check main thermal status
24928c2ecf20Sopenharmony_ci * @work:	pointer to the work_struct structure
24938c2ecf20Sopenharmony_ci *
24948c2ecf20Sopenharmony_ci * Work queue function for checking the Main thermal prot status
24958c2ecf20Sopenharmony_ci */
24968c2ecf20Sopenharmony_cistatic void ab8500_charger_check_main_thermal_prot_work(
24978c2ecf20Sopenharmony_ci	struct work_struct *work)
24988c2ecf20Sopenharmony_ci{
24998c2ecf20Sopenharmony_ci	int ret;
25008c2ecf20Sopenharmony_ci	u8 reg_value;
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
25038c2ecf20Sopenharmony_ci		struct ab8500_charger, check_main_thermal_prot_work);
25048c2ecf20Sopenharmony_ci
25058c2ecf20Sopenharmony_ci	/* Check if the status bit for main_thermal_prot is still active */
25068c2ecf20Sopenharmony_ci	ret = abx500_get_register_interruptible(di->dev,
25078c2ecf20Sopenharmony_ci		AB8500_CHARGER, AB8500_CH_STATUS2_REG, &reg_value);
25088c2ecf20Sopenharmony_ci	if (ret < 0) {
25098c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
25108c2ecf20Sopenharmony_ci		return;
25118c2ecf20Sopenharmony_ci	}
25128c2ecf20Sopenharmony_ci	if (reg_value & MAIN_CH_TH_PROT)
25138c2ecf20Sopenharmony_ci		di->flags.main_thermal_prot = true;
25148c2ecf20Sopenharmony_ci	else
25158c2ecf20Sopenharmony_ci		di->flags.main_thermal_prot = false;
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci	ab8500_power_supply_changed(di, di->ac_chg.psy);
25188c2ecf20Sopenharmony_ci}
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci/**
25218c2ecf20Sopenharmony_ci * ab8500_charger_check_usb_thermal_prot_work() - check usb thermal status
25228c2ecf20Sopenharmony_ci * @work:	pointer to the work_struct structure
25238c2ecf20Sopenharmony_ci *
25248c2ecf20Sopenharmony_ci * Work queue function for checking the USB thermal prot status
25258c2ecf20Sopenharmony_ci */
25268c2ecf20Sopenharmony_cistatic void ab8500_charger_check_usb_thermal_prot_work(
25278c2ecf20Sopenharmony_ci	struct work_struct *work)
25288c2ecf20Sopenharmony_ci{
25298c2ecf20Sopenharmony_ci	int ret;
25308c2ecf20Sopenharmony_ci	u8 reg_value;
25318c2ecf20Sopenharmony_ci
25328c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
25338c2ecf20Sopenharmony_ci		struct ab8500_charger, check_usb_thermal_prot_work);
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_ci	/* Check if the status bit for usb_thermal_prot is still active */
25368c2ecf20Sopenharmony_ci	ret = abx500_get_register_interruptible(di->dev,
25378c2ecf20Sopenharmony_ci		AB8500_CHARGER, AB8500_CH_USBCH_STAT2_REG, &reg_value);
25388c2ecf20Sopenharmony_ci	if (ret < 0) {
25398c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s ab8500 read failed\n", __func__);
25408c2ecf20Sopenharmony_ci		return;
25418c2ecf20Sopenharmony_ci	}
25428c2ecf20Sopenharmony_ci	if (reg_value & USB_CH_TH_PROT)
25438c2ecf20Sopenharmony_ci		di->flags.usb_thermal_prot = true;
25448c2ecf20Sopenharmony_ci	else
25458c2ecf20Sopenharmony_ci		di->flags.usb_thermal_prot = false;
25468c2ecf20Sopenharmony_ci
25478c2ecf20Sopenharmony_ci	ab8500_power_supply_changed(di, di->usb_chg.psy);
25488c2ecf20Sopenharmony_ci}
25498c2ecf20Sopenharmony_ci
25508c2ecf20Sopenharmony_ci/**
25518c2ecf20Sopenharmony_ci * ab8500_charger_mainchunplugdet_handler() - main charger unplugged
25528c2ecf20Sopenharmony_ci * @irq:       interrupt number
25538c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
25548c2ecf20Sopenharmony_ci *
25558c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
25568c2ecf20Sopenharmony_ci */
25578c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_mainchunplugdet_handler(int irq, void *_di)
25588c2ecf20Sopenharmony_ci{
25598c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "Main charger unplugged\n");
25628c2ecf20Sopenharmony_ci	queue_work(di->charger_wq, &di->ac_work);
25638c2ecf20Sopenharmony_ci
25648c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&di->ac_charger_attached_work);
25658c2ecf20Sopenharmony_ci	mutex_lock(&di->charger_attached_mutex);
25668c2ecf20Sopenharmony_ci	mutex_unlock(&di->charger_attached_mutex);
25678c2ecf20Sopenharmony_ci
25688c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
25698c2ecf20Sopenharmony_ci}
25708c2ecf20Sopenharmony_ci
25718c2ecf20Sopenharmony_ci/**
25728c2ecf20Sopenharmony_ci * ab8500_charger_mainchplugdet_handler() - main charger plugged
25738c2ecf20Sopenharmony_ci * @irq:       interrupt number
25748c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
25758c2ecf20Sopenharmony_ci *
25768c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
25778c2ecf20Sopenharmony_ci */
25788c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_mainchplugdet_handler(int irq, void *_di)
25798c2ecf20Sopenharmony_ci{
25808c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
25818c2ecf20Sopenharmony_ci
25828c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "Main charger plugged\n");
25838c2ecf20Sopenharmony_ci	queue_work(di->charger_wq, &di->ac_work);
25848c2ecf20Sopenharmony_ci
25858c2ecf20Sopenharmony_ci	mutex_lock(&di->charger_attached_mutex);
25868c2ecf20Sopenharmony_ci	mutex_unlock(&di->charger_attached_mutex);
25878c2ecf20Sopenharmony_ci
25888c2ecf20Sopenharmony_ci	if (is_ab8500(di->parent))
25898c2ecf20Sopenharmony_ci		queue_delayed_work(di->charger_wq,
25908c2ecf20Sopenharmony_ci			   &di->ac_charger_attached_work,
25918c2ecf20Sopenharmony_ci			   HZ);
25928c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
25938c2ecf20Sopenharmony_ci}
25948c2ecf20Sopenharmony_ci
25958c2ecf20Sopenharmony_ci/**
25968c2ecf20Sopenharmony_ci * ab8500_charger_mainextchnotok_handler() - main charger not ok
25978c2ecf20Sopenharmony_ci * @irq:       interrupt number
25988c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
25998c2ecf20Sopenharmony_ci *
26008c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
26018c2ecf20Sopenharmony_ci */
26028c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_mainextchnotok_handler(int irq, void *_di)
26038c2ecf20Sopenharmony_ci{
26048c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
26058c2ecf20Sopenharmony_ci
26068c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "Main charger not ok\n");
26078c2ecf20Sopenharmony_ci	di->flags.mainextchnotok = true;
26088c2ecf20Sopenharmony_ci	ab8500_power_supply_changed(di, di->ac_chg.psy);
26098c2ecf20Sopenharmony_ci
26108c2ecf20Sopenharmony_ci	/* Schedule a new HW failure check */
26118c2ecf20Sopenharmony_ci	queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
26128c2ecf20Sopenharmony_ci
26138c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
26148c2ecf20Sopenharmony_ci}
26158c2ecf20Sopenharmony_ci
26168c2ecf20Sopenharmony_ci/**
26178c2ecf20Sopenharmony_ci * ab8500_charger_mainchthprotr_handler() - Die temp is above main charger
26188c2ecf20Sopenharmony_ci * thermal protection threshold
26198c2ecf20Sopenharmony_ci * @irq:       interrupt number
26208c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
26218c2ecf20Sopenharmony_ci *
26228c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
26238c2ecf20Sopenharmony_ci */
26248c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_mainchthprotr_handler(int irq, void *_di)
26258c2ecf20Sopenharmony_ci{
26268c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_ci	dev_dbg(di->dev,
26298c2ecf20Sopenharmony_ci		"Die temp above Main charger thermal protection threshold\n");
26308c2ecf20Sopenharmony_ci	queue_work(di->charger_wq, &di->check_main_thermal_prot_work);
26318c2ecf20Sopenharmony_ci
26328c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
26338c2ecf20Sopenharmony_ci}
26348c2ecf20Sopenharmony_ci
26358c2ecf20Sopenharmony_ci/**
26368c2ecf20Sopenharmony_ci * ab8500_charger_mainchthprotf_handler() - Die temp is below main charger
26378c2ecf20Sopenharmony_ci * thermal protection threshold
26388c2ecf20Sopenharmony_ci * @irq:       interrupt number
26398c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
26408c2ecf20Sopenharmony_ci *
26418c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
26428c2ecf20Sopenharmony_ci */
26438c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_mainchthprotf_handler(int irq, void *_di)
26448c2ecf20Sopenharmony_ci{
26458c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
26468c2ecf20Sopenharmony_ci
26478c2ecf20Sopenharmony_ci	dev_dbg(di->dev,
26488c2ecf20Sopenharmony_ci		"Die temp ok for Main charger thermal protection threshold\n");
26498c2ecf20Sopenharmony_ci	queue_work(di->charger_wq, &di->check_main_thermal_prot_work);
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
26528c2ecf20Sopenharmony_ci}
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_cistatic void ab8500_charger_vbus_drop_end_work(struct work_struct *work)
26558c2ecf20Sopenharmony_ci{
26568c2ecf20Sopenharmony_ci	struct ab8500_charger *di = container_of(work,
26578c2ecf20Sopenharmony_ci		struct ab8500_charger, vbus_drop_end_work.work);
26588c2ecf20Sopenharmony_ci	int ret, curr;
26598c2ecf20Sopenharmony_ci	u8 reg_value;
26608c2ecf20Sopenharmony_ci
26618c2ecf20Sopenharmony_ci	di->flags.vbus_drop_end = false;
26628c2ecf20Sopenharmony_ci
26638c2ecf20Sopenharmony_ci	/* Reset the drop counter */
26648c2ecf20Sopenharmony_ci	abx500_set_register_interruptible(di->dev,
26658c2ecf20Sopenharmony_ci				  AB8500_CHARGER, AB8500_CHARGER_CTRL, 0x01);
26668c2ecf20Sopenharmony_ci
26678c2ecf20Sopenharmony_ci	ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
26688c2ecf20Sopenharmony_ci			AB8500_CH_USBCH_STAT2_REG, &reg_value);
26698c2ecf20Sopenharmony_ci	if (ret < 0) {
26708c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s read failed\n", __func__);
26718c2ecf20Sopenharmony_ci		return;
26728c2ecf20Sopenharmony_ci	}
26738c2ecf20Sopenharmony_ci
26748c2ecf20Sopenharmony_ci	curr = di->bm->chg_input_curr[
26758c2ecf20Sopenharmony_ci		reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT];
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_ci	if (di->max_usb_in_curr.calculated_max != curr) {
26788c2ecf20Sopenharmony_ci		/* USB source is collapsing */
26798c2ecf20Sopenharmony_ci		di->max_usb_in_curr.calculated_max = curr;
26808c2ecf20Sopenharmony_ci		dev_dbg(di->dev,
26818c2ecf20Sopenharmony_ci			 "VBUS input current limiting to %d mA\n",
26828c2ecf20Sopenharmony_ci			 di->max_usb_in_curr.calculated_max);
26838c2ecf20Sopenharmony_ci	} else {
26848c2ecf20Sopenharmony_ci		/*
26858c2ecf20Sopenharmony_ci		 * USB source can not give more than this amount.
26868c2ecf20Sopenharmony_ci		 * Taking more will collapse the source.
26878c2ecf20Sopenharmony_ci		 */
26888c2ecf20Sopenharmony_ci		di->max_usb_in_curr.set_max =
26898c2ecf20Sopenharmony_ci			di->max_usb_in_curr.calculated_max;
26908c2ecf20Sopenharmony_ci		dev_dbg(di->dev,
26918c2ecf20Sopenharmony_ci			 "VBUS input current limited to %d mA\n",
26928c2ecf20Sopenharmony_ci			 di->max_usb_in_curr.set_max);
26938c2ecf20Sopenharmony_ci	}
26948c2ecf20Sopenharmony_ci
26958c2ecf20Sopenharmony_ci	if (di->usb.charger_connected)
26968c2ecf20Sopenharmony_ci		ab8500_charger_set_vbus_in_curr(di,
26978c2ecf20Sopenharmony_ci					di->max_usb_in_curr.usb_type_max);
26988c2ecf20Sopenharmony_ci}
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_ci/**
27018c2ecf20Sopenharmony_ci * ab8500_charger_vbusdetf_handler() - VBUS falling detected
27028c2ecf20Sopenharmony_ci * @irq:       interrupt number
27038c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
27048c2ecf20Sopenharmony_ci *
27058c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
27068c2ecf20Sopenharmony_ci */
27078c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_vbusdetf_handler(int irq, void *_di)
27088c2ecf20Sopenharmony_ci{
27098c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
27108c2ecf20Sopenharmony_ci
27118c2ecf20Sopenharmony_ci	di->vbus_detected = false;
27128c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "VBUS falling detected\n");
27138c2ecf20Sopenharmony_ci	queue_work(di->charger_wq, &di->detect_usb_type_work);
27148c2ecf20Sopenharmony_ci
27158c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
27168c2ecf20Sopenharmony_ci}
27178c2ecf20Sopenharmony_ci
27188c2ecf20Sopenharmony_ci/**
27198c2ecf20Sopenharmony_ci * ab8500_charger_vbusdetr_handler() - VBUS rising detected
27208c2ecf20Sopenharmony_ci * @irq:       interrupt number
27218c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
27228c2ecf20Sopenharmony_ci *
27238c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
27248c2ecf20Sopenharmony_ci */
27258c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_vbusdetr_handler(int irq, void *_di)
27268c2ecf20Sopenharmony_ci{
27278c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
27288c2ecf20Sopenharmony_ci
27298c2ecf20Sopenharmony_ci	di->vbus_detected = true;
27308c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "VBUS rising detected\n");
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_ci	queue_work(di->charger_wq, &di->detect_usb_type_work);
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
27358c2ecf20Sopenharmony_ci}
27368c2ecf20Sopenharmony_ci
27378c2ecf20Sopenharmony_ci/**
27388c2ecf20Sopenharmony_ci * ab8500_charger_usblinkstatus_handler() - USB link status has changed
27398c2ecf20Sopenharmony_ci * @irq:       interrupt number
27408c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
27418c2ecf20Sopenharmony_ci *
27428c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
27438c2ecf20Sopenharmony_ci */
27448c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_usblinkstatus_handler(int irq, void *_di)
27458c2ecf20Sopenharmony_ci{
27468c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
27478c2ecf20Sopenharmony_ci
27488c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "USB link status changed\n");
27498c2ecf20Sopenharmony_ci
27508c2ecf20Sopenharmony_ci	queue_work(di->charger_wq, &di->usb_link_status_work);
27518c2ecf20Sopenharmony_ci
27528c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
27538c2ecf20Sopenharmony_ci}
27548c2ecf20Sopenharmony_ci
27558c2ecf20Sopenharmony_ci/**
27568c2ecf20Sopenharmony_ci * ab8500_charger_usbchthprotr_handler() - Die temp is above usb charger
27578c2ecf20Sopenharmony_ci * thermal protection threshold
27588c2ecf20Sopenharmony_ci * @irq:       interrupt number
27598c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
27608c2ecf20Sopenharmony_ci *
27618c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
27628c2ecf20Sopenharmony_ci */
27638c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_usbchthprotr_handler(int irq, void *_di)
27648c2ecf20Sopenharmony_ci{
27658c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
27668c2ecf20Sopenharmony_ci
27678c2ecf20Sopenharmony_ci	dev_dbg(di->dev,
27688c2ecf20Sopenharmony_ci		"Die temp above USB charger thermal protection threshold\n");
27698c2ecf20Sopenharmony_ci	queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
27708c2ecf20Sopenharmony_ci
27718c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
27728c2ecf20Sopenharmony_ci}
27738c2ecf20Sopenharmony_ci
27748c2ecf20Sopenharmony_ci/**
27758c2ecf20Sopenharmony_ci * ab8500_charger_usbchthprotf_handler() - Die temp is below usb charger
27768c2ecf20Sopenharmony_ci * thermal protection threshold
27778c2ecf20Sopenharmony_ci * @irq:       interrupt number
27788c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
27798c2ecf20Sopenharmony_ci *
27808c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
27818c2ecf20Sopenharmony_ci */
27828c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_usbchthprotf_handler(int irq, void *_di)
27838c2ecf20Sopenharmony_ci{
27848c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
27858c2ecf20Sopenharmony_ci
27868c2ecf20Sopenharmony_ci	dev_dbg(di->dev,
27878c2ecf20Sopenharmony_ci		"Die temp ok for USB charger thermal protection threshold\n");
27888c2ecf20Sopenharmony_ci	queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
27898c2ecf20Sopenharmony_ci
27908c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
27918c2ecf20Sopenharmony_ci}
27928c2ecf20Sopenharmony_ci
27938c2ecf20Sopenharmony_ci/**
27948c2ecf20Sopenharmony_ci * ab8500_charger_usbchargernotokr_handler() - USB charger not ok detected
27958c2ecf20Sopenharmony_ci * @irq:       interrupt number
27968c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
27978c2ecf20Sopenharmony_ci *
27988c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
27998c2ecf20Sopenharmony_ci */
28008c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_usbchargernotokr_handler(int irq, void *_di)
28018c2ecf20Sopenharmony_ci{
28028c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
28038c2ecf20Sopenharmony_ci
28048c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "Not allowed USB charger detected\n");
28058c2ecf20Sopenharmony_ci	queue_delayed_work(di->charger_wq, &di->check_usbchgnotok_work, 0);
28068c2ecf20Sopenharmony_ci
28078c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
28088c2ecf20Sopenharmony_ci}
28098c2ecf20Sopenharmony_ci
28108c2ecf20Sopenharmony_ci/**
28118c2ecf20Sopenharmony_ci * ab8500_charger_chwdexp_handler() - Charger watchdog expired
28128c2ecf20Sopenharmony_ci * @irq:       interrupt number
28138c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
28148c2ecf20Sopenharmony_ci *
28158c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
28168c2ecf20Sopenharmony_ci */
28178c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_chwdexp_handler(int irq, void *_di)
28188c2ecf20Sopenharmony_ci{
28198c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "Charger watchdog expired\n");
28228c2ecf20Sopenharmony_ci
28238c2ecf20Sopenharmony_ci	/*
28248c2ecf20Sopenharmony_ci	 * The charger that was online when the watchdog expired
28258c2ecf20Sopenharmony_ci	 * needs to be restarted for charging to start again
28268c2ecf20Sopenharmony_ci	 */
28278c2ecf20Sopenharmony_ci	if (di->ac.charger_online) {
28288c2ecf20Sopenharmony_ci		di->ac.wd_expired = true;
28298c2ecf20Sopenharmony_ci		ab8500_power_supply_changed(di, di->ac_chg.psy);
28308c2ecf20Sopenharmony_ci	}
28318c2ecf20Sopenharmony_ci	if (di->usb.charger_online) {
28328c2ecf20Sopenharmony_ci		di->usb.wd_expired = true;
28338c2ecf20Sopenharmony_ci		ab8500_power_supply_changed(di, di->usb_chg.psy);
28348c2ecf20Sopenharmony_ci	}
28358c2ecf20Sopenharmony_ci
28368c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
28378c2ecf20Sopenharmony_ci}
28388c2ecf20Sopenharmony_ci
28398c2ecf20Sopenharmony_ci/**
28408c2ecf20Sopenharmony_ci * ab8500_charger_vbuschdropend_handler() - VBUS drop removed
28418c2ecf20Sopenharmony_ci * @irq:       interrupt number
28428c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
28438c2ecf20Sopenharmony_ci *
28448c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
28458c2ecf20Sopenharmony_ci */
28468c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_vbuschdropend_handler(int irq, void *_di)
28478c2ecf20Sopenharmony_ci{
28488c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
28498c2ecf20Sopenharmony_ci
28508c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "VBUS charger drop ended\n");
28518c2ecf20Sopenharmony_ci	di->flags.vbus_drop_end = true;
28528c2ecf20Sopenharmony_ci
28538c2ecf20Sopenharmony_ci	/*
28548c2ecf20Sopenharmony_ci	 * VBUS might have dropped due to bad connection.
28558c2ecf20Sopenharmony_ci	 * Schedule a new input limit set to the value SW requests.
28568c2ecf20Sopenharmony_ci	 */
28578c2ecf20Sopenharmony_ci	queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work,
28588c2ecf20Sopenharmony_ci			   round_jiffies(VBUS_IN_CURR_LIM_RETRY_SET_TIME * HZ));
28598c2ecf20Sopenharmony_ci
28608c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
28618c2ecf20Sopenharmony_ci}
28628c2ecf20Sopenharmony_ci
28638c2ecf20Sopenharmony_ci/**
28648c2ecf20Sopenharmony_ci * ab8500_charger_vbusovv_handler() - VBUS overvoltage detected
28658c2ecf20Sopenharmony_ci * @irq:       interrupt number
28668c2ecf20Sopenharmony_ci * @_di:       pointer to the ab8500_charger structure
28678c2ecf20Sopenharmony_ci *
28688c2ecf20Sopenharmony_ci * Returns IRQ status(IRQ_HANDLED)
28698c2ecf20Sopenharmony_ci */
28708c2ecf20Sopenharmony_cistatic irqreturn_t ab8500_charger_vbusovv_handler(int irq, void *_di)
28718c2ecf20Sopenharmony_ci{
28728c2ecf20Sopenharmony_ci	struct ab8500_charger *di = _di;
28738c2ecf20Sopenharmony_ci
28748c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "VBUS overvoltage detected\n");
28758c2ecf20Sopenharmony_ci	di->flags.vbus_ovv = true;
28768c2ecf20Sopenharmony_ci	ab8500_power_supply_changed(di, di->usb_chg.psy);
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_ci	/* Schedule a new HW failure check */
28798c2ecf20Sopenharmony_ci	queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
28808c2ecf20Sopenharmony_ci
28818c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
28828c2ecf20Sopenharmony_ci}
28838c2ecf20Sopenharmony_ci
28848c2ecf20Sopenharmony_ci/**
28858c2ecf20Sopenharmony_ci * ab8500_charger_ac_get_property() - get the ac/mains properties
28868c2ecf20Sopenharmony_ci * @psy:       pointer to the power_supply structure
28878c2ecf20Sopenharmony_ci * @psp:       pointer to the power_supply_property structure
28888c2ecf20Sopenharmony_ci * @val:       pointer to the power_supply_propval union
28898c2ecf20Sopenharmony_ci *
28908c2ecf20Sopenharmony_ci * This function gets called when an application tries to get the ac/mains
28918c2ecf20Sopenharmony_ci * properties by reading the sysfs files.
28928c2ecf20Sopenharmony_ci * AC/Mains properties are online, present and voltage.
28938c2ecf20Sopenharmony_ci * online:     ac/mains charging is in progress or not
28948c2ecf20Sopenharmony_ci * present:    presence of the ac/mains
28958c2ecf20Sopenharmony_ci * voltage:    AC/Mains voltage
28968c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0(on success)
28978c2ecf20Sopenharmony_ci */
28988c2ecf20Sopenharmony_cistatic int ab8500_charger_ac_get_property(struct power_supply *psy,
28998c2ecf20Sopenharmony_ci	enum power_supply_property psp,
29008c2ecf20Sopenharmony_ci	union power_supply_propval *val)
29018c2ecf20Sopenharmony_ci{
29028c2ecf20Sopenharmony_ci	struct ab8500_charger *di;
29038c2ecf20Sopenharmony_ci	int ret;
29048c2ecf20Sopenharmony_ci
29058c2ecf20Sopenharmony_ci	di = to_ab8500_charger_ac_device_info(psy_to_ux500_charger(psy));
29068c2ecf20Sopenharmony_ci
29078c2ecf20Sopenharmony_ci	switch (psp) {
29088c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
29098c2ecf20Sopenharmony_ci		if (di->flags.mainextchnotok)
29108c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
29118c2ecf20Sopenharmony_ci		else if (di->ac.wd_expired || di->usb.wd_expired)
29128c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_DEAD;
29138c2ecf20Sopenharmony_ci		else if (di->flags.main_thermal_prot)
29148c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
29158c2ecf20Sopenharmony_ci		else
29168c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_GOOD;
29178c2ecf20Sopenharmony_ci		break;
29188c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
29198c2ecf20Sopenharmony_ci		val->intval = di->ac.charger_online;
29208c2ecf20Sopenharmony_ci		break;
29218c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
29228c2ecf20Sopenharmony_ci		val->intval = di->ac.charger_connected;
29238c2ecf20Sopenharmony_ci		break;
29248c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
29258c2ecf20Sopenharmony_ci		ret = ab8500_charger_get_ac_voltage(di);
29268c2ecf20Sopenharmony_ci		if (ret >= 0)
29278c2ecf20Sopenharmony_ci			di->ac.charger_voltage = ret;
29288c2ecf20Sopenharmony_ci		/* On error, use previous value */
29298c2ecf20Sopenharmony_ci		val->intval = di->ac.charger_voltage * 1000;
29308c2ecf20Sopenharmony_ci		break;
29318c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
29328c2ecf20Sopenharmony_ci		/*
29338c2ecf20Sopenharmony_ci		 * This property is used to indicate when CV mode is entered
29348c2ecf20Sopenharmony_ci		 * for the AC charger
29358c2ecf20Sopenharmony_ci		 */
29368c2ecf20Sopenharmony_ci		di->ac.cv_active = ab8500_charger_ac_cv(di);
29378c2ecf20Sopenharmony_ci		val->intval = di->ac.cv_active;
29388c2ecf20Sopenharmony_ci		break;
29398c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
29408c2ecf20Sopenharmony_ci		ret = ab8500_charger_get_ac_current(di);
29418c2ecf20Sopenharmony_ci		if (ret >= 0)
29428c2ecf20Sopenharmony_ci			di->ac.charger_current = ret;
29438c2ecf20Sopenharmony_ci		val->intval = di->ac.charger_current * 1000;
29448c2ecf20Sopenharmony_ci		break;
29458c2ecf20Sopenharmony_ci	default:
29468c2ecf20Sopenharmony_ci		return -EINVAL;
29478c2ecf20Sopenharmony_ci	}
29488c2ecf20Sopenharmony_ci	return 0;
29498c2ecf20Sopenharmony_ci}
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_ci/**
29528c2ecf20Sopenharmony_ci * ab8500_charger_usb_get_property() - get the usb properties
29538c2ecf20Sopenharmony_ci * @psy:        pointer to the power_supply structure
29548c2ecf20Sopenharmony_ci * @psp:        pointer to the power_supply_property structure
29558c2ecf20Sopenharmony_ci * @val:        pointer to the power_supply_propval union
29568c2ecf20Sopenharmony_ci *
29578c2ecf20Sopenharmony_ci * This function gets called when an application tries to get the usb
29588c2ecf20Sopenharmony_ci * properties by reading the sysfs files.
29598c2ecf20Sopenharmony_ci * USB properties are online, present and voltage.
29608c2ecf20Sopenharmony_ci * online:     usb charging is in progress or not
29618c2ecf20Sopenharmony_ci * present:    presence of the usb
29628c2ecf20Sopenharmony_ci * voltage:    vbus voltage
29638c2ecf20Sopenharmony_ci * Returns error code in case of failure else 0(on success)
29648c2ecf20Sopenharmony_ci */
29658c2ecf20Sopenharmony_cistatic int ab8500_charger_usb_get_property(struct power_supply *psy,
29668c2ecf20Sopenharmony_ci	enum power_supply_property psp,
29678c2ecf20Sopenharmony_ci	union power_supply_propval *val)
29688c2ecf20Sopenharmony_ci{
29698c2ecf20Sopenharmony_ci	struct ab8500_charger *di;
29708c2ecf20Sopenharmony_ci	int ret;
29718c2ecf20Sopenharmony_ci
29728c2ecf20Sopenharmony_ci	di = to_ab8500_charger_usb_device_info(psy_to_ux500_charger(psy));
29738c2ecf20Sopenharmony_ci
29748c2ecf20Sopenharmony_ci	switch (psp) {
29758c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
29768c2ecf20Sopenharmony_ci		if (di->flags.usbchargernotok)
29778c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
29788c2ecf20Sopenharmony_ci		else if (di->ac.wd_expired || di->usb.wd_expired)
29798c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_DEAD;
29808c2ecf20Sopenharmony_ci		else if (di->flags.usb_thermal_prot)
29818c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
29828c2ecf20Sopenharmony_ci		else if (di->flags.vbus_ovv)
29838c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
29848c2ecf20Sopenharmony_ci		else
29858c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_GOOD;
29868c2ecf20Sopenharmony_ci		break;
29878c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
29888c2ecf20Sopenharmony_ci		val->intval = di->usb.charger_online;
29898c2ecf20Sopenharmony_ci		break;
29908c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
29918c2ecf20Sopenharmony_ci		val->intval = di->usb.charger_connected;
29928c2ecf20Sopenharmony_ci		break;
29938c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
29948c2ecf20Sopenharmony_ci		ret = ab8500_charger_get_vbus_voltage(di);
29958c2ecf20Sopenharmony_ci		if (ret >= 0)
29968c2ecf20Sopenharmony_ci			di->usb.charger_voltage = ret;
29978c2ecf20Sopenharmony_ci		val->intval = di->usb.charger_voltage * 1000;
29988c2ecf20Sopenharmony_ci		break;
29998c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
30008c2ecf20Sopenharmony_ci		/*
30018c2ecf20Sopenharmony_ci		 * This property is used to indicate when CV mode is entered
30028c2ecf20Sopenharmony_ci		 * for the USB charger
30038c2ecf20Sopenharmony_ci		 */
30048c2ecf20Sopenharmony_ci		di->usb.cv_active = ab8500_charger_usb_cv(di);
30058c2ecf20Sopenharmony_ci		val->intval = di->usb.cv_active;
30068c2ecf20Sopenharmony_ci		break;
30078c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
30088c2ecf20Sopenharmony_ci		ret = ab8500_charger_get_usb_current(di);
30098c2ecf20Sopenharmony_ci		if (ret >= 0)
30108c2ecf20Sopenharmony_ci			di->usb.charger_current = ret;
30118c2ecf20Sopenharmony_ci		val->intval = di->usb.charger_current * 1000;
30128c2ecf20Sopenharmony_ci		break;
30138c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_AVG:
30148c2ecf20Sopenharmony_ci		/*
30158c2ecf20Sopenharmony_ci		 * This property is used to indicate when VBUS has collapsed
30168c2ecf20Sopenharmony_ci		 * due to too high output current from the USB charger
30178c2ecf20Sopenharmony_ci		 */
30188c2ecf20Sopenharmony_ci		if (di->flags.vbus_collapse)
30198c2ecf20Sopenharmony_ci			val->intval = 1;
30208c2ecf20Sopenharmony_ci		else
30218c2ecf20Sopenharmony_ci			val->intval = 0;
30228c2ecf20Sopenharmony_ci		break;
30238c2ecf20Sopenharmony_ci	default:
30248c2ecf20Sopenharmony_ci		return -EINVAL;
30258c2ecf20Sopenharmony_ci	}
30268c2ecf20Sopenharmony_ci	return 0;
30278c2ecf20Sopenharmony_ci}
30288c2ecf20Sopenharmony_ci
30298c2ecf20Sopenharmony_ci/**
30308c2ecf20Sopenharmony_ci * ab8500_charger_init_hw_registers() - Set up charger related registers
30318c2ecf20Sopenharmony_ci * @di:		pointer to the ab8500_charger structure
30328c2ecf20Sopenharmony_ci *
30338c2ecf20Sopenharmony_ci * Set up charger OVV, watchdog and maximum voltage registers as well as
30348c2ecf20Sopenharmony_ci * charging of the backup battery
30358c2ecf20Sopenharmony_ci */
30368c2ecf20Sopenharmony_cistatic int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
30378c2ecf20Sopenharmony_ci{
30388c2ecf20Sopenharmony_ci	int ret = 0;
30398c2ecf20Sopenharmony_ci
30408c2ecf20Sopenharmony_ci	/* Setup maximum charger current and voltage for ABB cut2.0 */
30418c2ecf20Sopenharmony_ci	if (!is_ab8500_1p1_or_earlier(di->parent)) {
30428c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev,
30438c2ecf20Sopenharmony_ci			AB8500_CHARGER,
30448c2ecf20Sopenharmony_ci			AB8500_CH_VOLT_LVL_MAX_REG, CH_VOL_LVL_4P6);
30458c2ecf20Sopenharmony_ci		if (ret) {
30468c2ecf20Sopenharmony_ci			dev_err(di->dev,
30478c2ecf20Sopenharmony_ci				"failed to set CH_VOLT_LVL_MAX_REG\n");
30488c2ecf20Sopenharmony_ci			goto out;
30498c2ecf20Sopenharmony_ci		}
30508c2ecf20Sopenharmony_ci
30518c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev,
30528c2ecf20Sopenharmony_ci			AB8500_CHARGER, AB8500_CH_OPT_CRNTLVL_MAX_REG,
30538c2ecf20Sopenharmony_ci			CH_OP_CUR_LVL_1P6);
30548c2ecf20Sopenharmony_ci		if (ret) {
30558c2ecf20Sopenharmony_ci			dev_err(di->dev,
30568c2ecf20Sopenharmony_ci				"failed to set CH_OPT_CRNTLVL_MAX_REG\n");
30578c2ecf20Sopenharmony_ci			goto out;
30588c2ecf20Sopenharmony_ci		}
30598c2ecf20Sopenharmony_ci	}
30608c2ecf20Sopenharmony_ci
30618c2ecf20Sopenharmony_ci	if (is_ab8505_2p0(di->parent))
30628c2ecf20Sopenharmony_ci		ret = abx500_mask_and_set_register_interruptible(di->dev,
30638c2ecf20Sopenharmony_ci			AB8500_CHARGER,
30648c2ecf20Sopenharmony_ci			AB8500_USBCH_CTRL2_REG,
30658c2ecf20Sopenharmony_ci			VBUS_AUTO_IN_CURR_LIM_ENA,
30668c2ecf20Sopenharmony_ci			VBUS_AUTO_IN_CURR_LIM_ENA);
30678c2ecf20Sopenharmony_ci	else
30688c2ecf20Sopenharmony_ci		/*
30698c2ecf20Sopenharmony_ci		 * VBUS OVV set to 6.3V and enable automatic current limitation
30708c2ecf20Sopenharmony_ci		 */
30718c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev,
30728c2ecf20Sopenharmony_ci			AB8500_CHARGER,
30738c2ecf20Sopenharmony_ci			AB8500_USBCH_CTRL2_REG,
30748c2ecf20Sopenharmony_ci			VBUS_OVV_SELECT_6P3V | VBUS_AUTO_IN_CURR_LIM_ENA);
30758c2ecf20Sopenharmony_ci	if (ret) {
30768c2ecf20Sopenharmony_ci		dev_err(di->dev,
30778c2ecf20Sopenharmony_ci			"failed to set automatic current limitation\n");
30788c2ecf20Sopenharmony_ci		goto out;
30798c2ecf20Sopenharmony_ci	}
30808c2ecf20Sopenharmony_ci
30818c2ecf20Sopenharmony_ci	/* Enable main watchdog in OTP */
30828c2ecf20Sopenharmony_ci	ret = abx500_set_register_interruptible(di->dev,
30838c2ecf20Sopenharmony_ci		AB8500_OTP_EMUL, AB8500_OTP_CONF_15, OTP_ENABLE_WD);
30848c2ecf20Sopenharmony_ci	if (ret) {
30858c2ecf20Sopenharmony_ci		dev_err(di->dev, "failed to enable main WD in OTP\n");
30868c2ecf20Sopenharmony_ci		goto out;
30878c2ecf20Sopenharmony_ci	}
30888c2ecf20Sopenharmony_ci
30898c2ecf20Sopenharmony_ci	/* Enable main watchdog */
30908c2ecf20Sopenharmony_ci	ret = abx500_set_register_interruptible(di->dev,
30918c2ecf20Sopenharmony_ci		AB8500_SYS_CTRL2_BLOCK,
30928c2ecf20Sopenharmony_ci		AB8500_MAIN_WDOG_CTRL_REG, MAIN_WDOG_ENA);
30938c2ecf20Sopenharmony_ci	if (ret) {
30948c2ecf20Sopenharmony_ci		dev_err(di->dev, "failed to enable main watchdog\n");
30958c2ecf20Sopenharmony_ci		goto out;
30968c2ecf20Sopenharmony_ci	}
30978c2ecf20Sopenharmony_ci
30988c2ecf20Sopenharmony_ci	/*
30998c2ecf20Sopenharmony_ci	 * Due to internal synchronisation, Enable and Kick watchdog bits
31008c2ecf20Sopenharmony_ci	 * cannot be enabled in a single write.
31018c2ecf20Sopenharmony_ci	 * A minimum delay of 2*32 kHz period (62.5µs) must be inserted
31028c2ecf20Sopenharmony_ci	 * between writing Enable then Kick bits.
31038c2ecf20Sopenharmony_ci	 */
31048c2ecf20Sopenharmony_ci	udelay(63);
31058c2ecf20Sopenharmony_ci
31068c2ecf20Sopenharmony_ci	/* Kick main watchdog */
31078c2ecf20Sopenharmony_ci	ret = abx500_set_register_interruptible(di->dev,
31088c2ecf20Sopenharmony_ci		AB8500_SYS_CTRL2_BLOCK,
31098c2ecf20Sopenharmony_ci		AB8500_MAIN_WDOG_CTRL_REG,
31108c2ecf20Sopenharmony_ci		(MAIN_WDOG_ENA | MAIN_WDOG_KICK));
31118c2ecf20Sopenharmony_ci	if (ret) {
31128c2ecf20Sopenharmony_ci		dev_err(di->dev, "failed to kick main watchdog\n");
31138c2ecf20Sopenharmony_ci		goto out;
31148c2ecf20Sopenharmony_ci	}
31158c2ecf20Sopenharmony_ci
31168c2ecf20Sopenharmony_ci	/* Disable main watchdog */
31178c2ecf20Sopenharmony_ci	ret = abx500_set_register_interruptible(di->dev,
31188c2ecf20Sopenharmony_ci		AB8500_SYS_CTRL2_BLOCK,
31198c2ecf20Sopenharmony_ci		AB8500_MAIN_WDOG_CTRL_REG, MAIN_WDOG_DIS);
31208c2ecf20Sopenharmony_ci	if (ret) {
31218c2ecf20Sopenharmony_ci		dev_err(di->dev, "failed to disable main watchdog\n");
31228c2ecf20Sopenharmony_ci		goto out;
31238c2ecf20Sopenharmony_ci	}
31248c2ecf20Sopenharmony_ci
31258c2ecf20Sopenharmony_ci	/* Set watchdog timeout */
31268c2ecf20Sopenharmony_ci	ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
31278c2ecf20Sopenharmony_ci		AB8500_CH_WD_TIMER_REG, WD_TIMER);
31288c2ecf20Sopenharmony_ci	if (ret) {
31298c2ecf20Sopenharmony_ci		dev_err(di->dev, "failed to set charger watchdog timeout\n");
31308c2ecf20Sopenharmony_ci		goto out;
31318c2ecf20Sopenharmony_ci	}
31328c2ecf20Sopenharmony_ci
31338c2ecf20Sopenharmony_ci	ret = ab8500_charger_led_en(di, false);
31348c2ecf20Sopenharmony_ci	if (ret < 0) {
31358c2ecf20Sopenharmony_ci		dev_err(di->dev, "failed to disable LED\n");
31368c2ecf20Sopenharmony_ci		goto out;
31378c2ecf20Sopenharmony_ci	}
31388c2ecf20Sopenharmony_ci
31398c2ecf20Sopenharmony_ci	ret = abx500_set_register_interruptible(di->dev,
31408c2ecf20Sopenharmony_ci		AB8500_RTC,
31418c2ecf20Sopenharmony_ci		AB8500_RTC_BACKUP_CHG_REG,
31428c2ecf20Sopenharmony_ci		(di->bm->bkup_bat_v & 0x3) | di->bm->bkup_bat_i);
31438c2ecf20Sopenharmony_ci	if (ret) {
31448c2ecf20Sopenharmony_ci		dev_err(di->dev, "failed to setup backup battery charging\n");
31458c2ecf20Sopenharmony_ci		goto out;
31468c2ecf20Sopenharmony_ci	}
31478c2ecf20Sopenharmony_ci
31488c2ecf20Sopenharmony_ci	/* Enable backup battery charging */
31498c2ecf20Sopenharmony_ci	ret = abx500_mask_and_set_register_interruptible(di->dev,
31508c2ecf20Sopenharmony_ci		AB8500_RTC, AB8500_RTC_CTRL_REG,
31518c2ecf20Sopenharmony_ci		RTC_BUP_CH_ENA, RTC_BUP_CH_ENA);
31528c2ecf20Sopenharmony_ci	if (ret < 0) {
31538c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s mask and set failed\n", __func__);
31548c2ecf20Sopenharmony_ci		goto out;
31558c2ecf20Sopenharmony_ci	}
31568c2ecf20Sopenharmony_ci
31578c2ecf20Sopenharmony_ciout:
31588c2ecf20Sopenharmony_ci	return ret;
31598c2ecf20Sopenharmony_ci}
31608c2ecf20Sopenharmony_ci
31618c2ecf20Sopenharmony_ci/*
31628c2ecf20Sopenharmony_ci * ab8500 charger driver interrupts and their respective isr
31638c2ecf20Sopenharmony_ci */
31648c2ecf20Sopenharmony_cistatic struct ab8500_charger_interrupts ab8500_charger_irq[] = {
31658c2ecf20Sopenharmony_ci	{"MAIN_CH_UNPLUG_DET", ab8500_charger_mainchunplugdet_handler},
31668c2ecf20Sopenharmony_ci	{"MAIN_CHARGE_PLUG_DET", ab8500_charger_mainchplugdet_handler},
31678c2ecf20Sopenharmony_ci	{"MAIN_EXT_CH_NOT_OK", ab8500_charger_mainextchnotok_handler},
31688c2ecf20Sopenharmony_ci	{"MAIN_CH_TH_PROT_R", ab8500_charger_mainchthprotr_handler},
31698c2ecf20Sopenharmony_ci	{"MAIN_CH_TH_PROT_F", ab8500_charger_mainchthprotf_handler},
31708c2ecf20Sopenharmony_ci	{"VBUS_DET_F", ab8500_charger_vbusdetf_handler},
31718c2ecf20Sopenharmony_ci	{"VBUS_DET_R", ab8500_charger_vbusdetr_handler},
31728c2ecf20Sopenharmony_ci	{"USB_LINK_STATUS", ab8500_charger_usblinkstatus_handler},
31738c2ecf20Sopenharmony_ci	{"USB_CH_TH_PROT_R", ab8500_charger_usbchthprotr_handler},
31748c2ecf20Sopenharmony_ci	{"USB_CH_TH_PROT_F", ab8500_charger_usbchthprotf_handler},
31758c2ecf20Sopenharmony_ci	{"USB_CHARGER_NOT_OKR", ab8500_charger_usbchargernotokr_handler},
31768c2ecf20Sopenharmony_ci	{"VBUS_OVV", ab8500_charger_vbusovv_handler},
31778c2ecf20Sopenharmony_ci	{"CH_WD_EXP", ab8500_charger_chwdexp_handler},
31788c2ecf20Sopenharmony_ci	{"VBUS_CH_DROP_END", ab8500_charger_vbuschdropend_handler},
31798c2ecf20Sopenharmony_ci};
31808c2ecf20Sopenharmony_ci
31818c2ecf20Sopenharmony_cistatic int ab8500_charger_usb_notifier_call(struct notifier_block *nb,
31828c2ecf20Sopenharmony_ci		unsigned long event, void *power)
31838c2ecf20Sopenharmony_ci{
31848c2ecf20Sopenharmony_ci	struct ab8500_charger *di =
31858c2ecf20Sopenharmony_ci		container_of(nb, struct ab8500_charger, nb);
31868c2ecf20Sopenharmony_ci	enum ab8500_usb_state bm_usb_state;
31878c2ecf20Sopenharmony_ci	unsigned mA = *((unsigned *)power);
31888c2ecf20Sopenharmony_ci
31898c2ecf20Sopenharmony_ci	if (!di)
31908c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
31918c2ecf20Sopenharmony_ci
31928c2ecf20Sopenharmony_ci	if (event != USB_EVENT_VBUS) {
31938c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "not a standard host, returning\n");
31948c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
31958c2ecf20Sopenharmony_ci	}
31968c2ecf20Sopenharmony_ci
31978c2ecf20Sopenharmony_ci	/* TODO: State is fabricate  here. See if charger really needs USB
31988c2ecf20Sopenharmony_ci	 * state or if mA is enough
31998c2ecf20Sopenharmony_ci	 */
32008c2ecf20Sopenharmony_ci	if ((di->usb_state.usb_current == 2) && (mA > 2))
32018c2ecf20Sopenharmony_ci		bm_usb_state = AB8500_BM_USB_STATE_RESUME;
32028c2ecf20Sopenharmony_ci	else if (mA == 0)
32038c2ecf20Sopenharmony_ci		bm_usb_state = AB8500_BM_USB_STATE_RESET_HS;
32048c2ecf20Sopenharmony_ci	else if (mA == 2)
32058c2ecf20Sopenharmony_ci		bm_usb_state = AB8500_BM_USB_STATE_SUSPEND;
32068c2ecf20Sopenharmony_ci	else if (mA >= 8) /* 8, 100, 500 */
32078c2ecf20Sopenharmony_ci		bm_usb_state = AB8500_BM_USB_STATE_CONFIGURED;
32088c2ecf20Sopenharmony_ci	else /* Should never occur */
32098c2ecf20Sopenharmony_ci		bm_usb_state = AB8500_BM_USB_STATE_RESET_FS;
32108c2ecf20Sopenharmony_ci
32118c2ecf20Sopenharmony_ci	dev_dbg(di->dev, "%s usb_state: 0x%02x mA: %d\n",
32128c2ecf20Sopenharmony_ci		__func__, bm_usb_state, mA);
32138c2ecf20Sopenharmony_ci
32148c2ecf20Sopenharmony_ci	spin_lock(&di->usb_state.usb_lock);
32158c2ecf20Sopenharmony_ci	di->usb_state.state_tmp = bm_usb_state;
32168c2ecf20Sopenharmony_ci	di->usb_state.usb_current_tmp = mA;
32178c2ecf20Sopenharmony_ci	spin_unlock(&di->usb_state.usb_lock);
32188c2ecf20Sopenharmony_ci
32198c2ecf20Sopenharmony_ci	/*
32208c2ecf20Sopenharmony_ci	 * wait for some time until you get updates from the usb stack
32218c2ecf20Sopenharmony_ci	 * and negotiations are completed
32228c2ecf20Sopenharmony_ci	 */
32238c2ecf20Sopenharmony_ci	queue_delayed_work(di->charger_wq, &di->usb_state_changed_work, HZ/2);
32248c2ecf20Sopenharmony_ci
32258c2ecf20Sopenharmony_ci	return NOTIFY_OK;
32268c2ecf20Sopenharmony_ci}
32278c2ecf20Sopenharmony_ci
32288c2ecf20Sopenharmony_ci#if defined(CONFIG_PM)
32298c2ecf20Sopenharmony_cistatic int ab8500_charger_resume(struct platform_device *pdev)
32308c2ecf20Sopenharmony_ci{
32318c2ecf20Sopenharmony_ci	int ret;
32328c2ecf20Sopenharmony_ci	struct ab8500_charger *di = platform_get_drvdata(pdev);
32338c2ecf20Sopenharmony_ci
32348c2ecf20Sopenharmony_ci	/*
32358c2ecf20Sopenharmony_ci	 * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
32368c2ecf20Sopenharmony_ci	 * logic. That means we have to continuously kick the charger
32378c2ecf20Sopenharmony_ci	 * watchdog even when no charger is connected. This is only
32388c2ecf20Sopenharmony_ci	 * valid once the AC charger has been enabled. This is
32398c2ecf20Sopenharmony_ci	 * a bug that is not handled by the algorithm and the
32408c2ecf20Sopenharmony_ci	 * watchdog have to be kicked by the charger driver
32418c2ecf20Sopenharmony_ci	 * when the AC charger is disabled
32428c2ecf20Sopenharmony_ci	 */
32438c2ecf20Sopenharmony_ci	if (di->ac_conn && is_ab8500_1p1_or_earlier(di->parent)) {
32448c2ecf20Sopenharmony_ci		ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
32458c2ecf20Sopenharmony_ci			AB8500_CHARG_WD_CTRL, CHARG_WD_KICK);
32468c2ecf20Sopenharmony_ci		if (ret)
32478c2ecf20Sopenharmony_ci			dev_err(di->dev, "Failed to kick WD!\n");
32488c2ecf20Sopenharmony_ci
32498c2ecf20Sopenharmony_ci		/* If not already pending start a new timer */
32508c2ecf20Sopenharmony_ci		queue_delayed_work(di->charger_wq, &di->kick_wd_work,
32518c2ecf20Sopenharmony_ci				   round_jiffies(WD_KICK_INTERVAL));
32528c2ecf20Sopenharmony_ci	}
32538c2ecf20Sopenharmony_ci
32548c2ecf20Sopenharmony_ci	/* If we still have a HW failure, schedule a new check */
32558c2ecf20Sopenharmony_ci	if (di->flags.mainextchnotok || di->flags.vbus_ovv) {
32568c2ecf20Sopenharmony_ci		queue_delayed_work(di->charger_wq,
32578c2ecf20Sopenharmony_ci			&di->check_hw_failure_work, 0);
32588c2ecf20Sopenharmony_ci	}
32598c2ecf20Sopenharmony_ci
32608c2ecf20Sopenharmony_ci	if (di->flags.vbus_drop_end)
32618c2ecf20Sopenharmony_ci		queue_delayed_work(di->charger_wq, &di->vbus_drop_end_work, 0);
32628c2ecf20Sopenharmony_ci
32638c2ecf20Sopenharmony_ci	return 0;
32648c2ecf20Sopenharmony_ci}
32658c2ecf20Sopenharmony_ci
32668c2ecf20Sopenharmony_cistatic int ab8500_charger_suspend(struct platform_device *pdev,
32678c2ecf20Sopenharmony_ci	pm_message_t state)
32688c2ecf20Sopenharmony_ci{
32698c2ecf20Sopenharmony_ci	struct ab8500_charger *di = platform_get_drvdata(pdev);
32708c2ecf20Sopenharmony_ci
32718c2ecf20Sopenharmony_ci	/* Cancel any pending jobs */
32728c2ecf20Sopenharmony_ci	cancel_delayed_work(&di->check_hw_failure_work);
32738c2ecf20Sopenharmony_ci	cancel_delayed_work(&di->vbus_drop_end_work);
32748c2ecf20Sopenharmony_ci
32758c2ecf20Sopenharmony_ci	flush_delayed_work(&di->attach_work);
32768c2ecf20Sopenharmony_ci	flush_delayed_work(&di->usb_charger_attached_work);
32778c2ecf20Sopenharmony_ci	flush_delayed_work(&di->ac_charger_attached_work);
32788c2ecf20Sopenharmony_ci	flush_delayed_work(&di->check_usbchgnotok_work);
32798c2ecf20Sopenharmony_ci	flush_delayed_work(&di->check_vbat_work);
32808c2ecf20Sopenharmony_ci	flush_delayed_work(&di->kick_wd_work);
32818c2ecf20Sopenharmony_ci
32828c2ecf20Sopenharmony_ci	flush_work(&di->usb_link_status_work);
32838c2ecf20Sopenharmony_ci	flush_work(&di->ac_work);
32848c2ecf20Sopenharmony_ci	flush_work(&di->detect_usb_type_work);
32858c2ecf20Sopenharmony_ci
32868c2ecf20Sopenharmony_ci	if (atomic_read(&di->current_stepping_sessions))
32878c2ecf20Sopenharmony_ci		return -EAGAIN;
32888c2ecf20Sopenharmony_ci
32898c2ecf20Sopenharmony_ci	return 0;
32908c2ecf20Sopenharmony_ci}
32918c2ecf20Sopenharmony_ci#else
32928c2ecf20Sopenharmony_ci#define ab8500_charger_suspend      NULL
32938c2ecf20Sopenharmony_ci#define ab8500_charger_resume       NULL
32948c2ecf20Sopenharmony_ci#endif
32958c2ecf20Sopenharmony_ci
32968c2ecf20Sopenharmony_cistatic struct notifier_block charger_nb = {
32978c2ecf20Sopenharmony_ci	.notifier_call = ab8500_external_charger_prepare,
32988c2ecf20Sopenharmony_ci};
32998c2ecf20Sopenharmony_ci
33008c2ecf20Sopenharmony_cistatic int ab8500_charger_remove(struct platform_device *pdev)
33018c2ecf20Sopenharmony_ci{
33028c2ecf20Sopenharmony_ci	struct ab8500_charger *di = platform_get_drvdata(pdev);
33038c2ecf20Sopenharmony_ci	int i, irq, ret;
33048c2ecf20Sopenharmony_ci
33058c2ecf20Sopenharmony_ci	/* Disable AC charging */
33068c2ecf20Sopenharmony_ci	ab8500_charger_ac_en(&di->ac_chg, false, 0, 0);
33078c2ecf20Sopenharmony_ci
33088c2ecf20Sopenharmony_ci	/* Disable USB charging */
33098c2ecf20Sopenharmony_ci	ab8500_charger_usb_en(&di->usb_chg, false, 0, 0);
33108c2ecf20Sopenharmony_ci
33118c2ecf20Sopenharmony_ci	/* Disable interrupts */
33128c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
33138c2ecf20Sopenharmony_ci		irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
33148c2ecf20Sopenharmony_ci		free_irq(irq, di);
33158c2ecf20Sopenharmony_ci	}
33168c2ecf20Sopenharmony_ci
33178c2ecf20Sopenharmony_ci	/* Backup battery voltage and current disable */
33188c2ecf20Sopenharmony_ci	ret = abx500_mask_and_set_register_interruptible(di->dev,
33198c2ecf20Sopenharmony_ci		AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, 0);
33208c2ecf20Sopenharmony_ci	if (ret < 0)
33218c2ecf20Sopenharmony_ci		dev_err(di->dev, "%s mask and set failed\n", __func__);
33228c2ecf20Sopenharmony_ci
33238c2ecf20Sopenharmony_ci	usb_unregister_notifier(di->usb_phy, &di->nb);
33248c2ecf20Sopenharmony_ci	usb_put_phy(di->usb_phy);
33258c2ecf20Sopenharmony_ci
33268c2ecf20Sopenharmony_ci	/* Delete the work queue */
33278c2ecf20Sopenharmony_ci	destroy_workqueue(di->charger_wq);
33288c2ecf20Sopenharmony_ci
33298c2ecf20Sopenharmony_ci	/* Unregister external charger enable notifier */
33308c2ecf20Sopenharmony_ci	if (!di->ac_chg.enabled)
33318c2ecf20Sopenharmony_ci		blocking_notifier_chain_unregister(
33328c2ecf20Sopenharmony_ci			&charger_notifier_list, &charger_nb);
33338c2ecf20Sopenharmony_ci
33348c2ecf20Sopenharmony_ci	flush_scheduled_work();
33358c2ecf20Sopenharmony_ci	if (di->usb_chg.enabled)
33368c2ecf20Sopenharmony_ci		power_supply_unregister(di->usb_chg.psy);
33378c2ecf20Sopenharmony_ci
33388c2ecf20Sopenharmony_ci	if (di->ac_chg.enabled && !di->ac_chg.external)
33398c2ecf20Sopenharmony_ci		power_supply_unregister(di->ac_chg.psy);
33408c2ecf20Sopenharmony_ci
33418c2ecf20Sopenharmony_ci	return 0;
33428c2ecf20Sopenharmony_ci}
33438c2ecf20Sopenharmony_ci
33448c2ecf20Sopenharmony_cistatic char *supply_interface[] = {
33458c2ecf20Sopenharmony_ci	"ab8500_chargalg",
33468c2ecf20Sopenharmony_ci	"ab8500_fg",
33478c2ecf20Sopenharmony_ci	"ab8500_btemp",
33488c2ecf20Sopenharmony_ci};
33498c2ecf20Sopenharmony_ci
33508c2ecf20Sopenharmony_cistatic const struct power_supply_desc ab8500_ac_chg_desc = {
33518c2ecf20Sopenharmony_ci	.name		= "ab8500_ac",
33528c2ecf20Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_MAINS,
33538c2ecf20Sopenharmony_ci	.properties	= ab8500_charger_ac_props,
33548c2ecf20Sopenharmony_ci	.num_properties	= ARRAY_SIZE(ab8500_charger_ac_props),
33558c2ecf20Sopenharmony_ci	.get_property	= ab8500_charger_ac_get_property,
33568c2ecf20Sopenharmony_ci};
33578c2ecf20Sopenharmony_ci
33588c2ecf20Sopenharmony_cistatic const struct power_supply_desc ab8500_usb_chg_desc = {
33598c2ecf20Sopenharmony_ci	.name		= "ab8500_usb",
33608c2ecf20Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_USB,
33618c2ecf20Sopenharmony_ci	.properties	= ab8500_charger_usb_props,
33628c2ecf20Sopenharmony_ci	.num_properties	= ARRAY_SIZE(ab8500_charger_usb_props),
33638c2ecf20Sopenharmony_ci	.get_property	= ab8500_charger_usb_get_property,
33648c2ecf20Sopenharmony_ci};
33658c2ecf20Sopenharmony_ci
33668c2ecf20Sopenharmony_cistatic int ab8500_charger_probe(struct platform_device *pdev)
33678c2ecf20Sopenharmony_ci{
33688c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
33698c2ecf20Sopenharmony_ci	struct abx500_bm_data *plat = pdev->dev.platform_data;
33708c2ecf20Sopenharmony_ci	struct power_supply_config ac_psy_cfg = {}, usb_psy_cfg = {};
33718c2ecf20Sopenharmony_ci	struct ab8500_charger *di;
33728c2ecf20Sopenharmony_ci	int irq, i, charger_status, ret = 0, ch_stat;
33738c2ecf20Sopenharmony_ci
33748c2ecf20Sopenharmony_ci	di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
33758c2ecf20Sopenharmony_ci	if (!di) {
33768c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "%s no mem for ab8500_charger\n", __func__);
33778c2ecf20Sopenharmony_ci		return -ENOMEM;
33788c2ecf20Sopenharmony_ci	}
33798c2ecf20Sopenharmony_ci
33808c2ecf20Sopenharmony_ci	if (!plat) {
33818c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no battery management data supplied\n");
33828c2ecf20Sopenharmony_ci		return -EINVAL;
33838c2ecf20Sopenharmony_ci	}
33848c2ecf20Sopenharmony_ci	di->bm = plat;
33858c2ecf20Sopenharmony_ci
33868c2ecf20Sopenharmony_ci	if (np) {
33878c2ecf20Sopenharmony_ci		ret = ab8500_bm_of_probe(&pdev->dev, np, di->bm);
33888c2ecf20Sopenharmony_ci		if (ret) {
33898c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "failed to get battery information\n");
33908c2ecf20Sopenharmony_ci			return ret;
33918c2ecf20Sopenharmony_ci		}
33928c2ecf20Sopenharmony_ci		di->autopower_cfg = of_property_read_bool(np, "autopower_cfg");
33938c2ecf20Sopenharmony_ci	} else
33948c2ecf20Sopenharmony_ci		di->autopower_cfg = false;
33958c2ecf20Sopenharmony_ci
33968c2ecf20Sopenharmony_ci	/* get parent data */
33978c2ecf20Sopenharmony_ci	di->dev = &pdev->dev;
33988c2ecf20Sopenharmony_ci	di->parent = dev_get_drvdata(pdev->dev.parent);
33998c2ecf20Sopenharmony_ci
34008c2ecf20Sopenharmony_ci	/* Get ADC channels */
34018c2ecf20Sopenharmony_ci	di->adc_main_charger_v = devm_iio_channel_get(&pdev->dev,
34028c2ecf20Sopenharmony_ci						      "main_charger_v");
34038c2ecf20Sopenharmony_ci	if (IS_ERR(di->adc_main_charger_v)) {
34048c2ecf20Sopenharmony_ci		if (PTR_ERR(di->adc_main_charger_v) == -ENODEV)
34058c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
34068c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get ADC main charger voltage\n");
34078c2ecf20Sopenharmony_ci		return PTR_ERR(di->adc_main_charger_v);
34088c2ecf20Sopenharmony_ci	}
34098c2ecf20Sopenharmony_ci	di->adc_main_charger_c = devm_iio_channel_get(&pdev->dev,
34108c2ecf20Sopenharmony_ci						      "main_charger_c");
34118c2ecf20Sopenharmony_ci	if (IS_ERR(di->adc_main_charger_c)) {
34128c2ecf20Sopenharmony_ci		if (PTR_ERR(di->adc_main_charger_c) == -ENODEV)
34138c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
34148c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get ADC main charger current\n");
34158c2ecf20Sopenharmony_ci		return PTR_ERR(di->adc_main_charger_c);
34168c2ecf20Sopenharmony_ci	}
34178c2ecf20Sopenharmony_ci	di->adc_vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v");
34188c2ecf20Sopenharmony_ci	if (IS_ERR(di->adc_vbus_v)) {
34198c2ecf20Sopenharmony_ci		if (PTR_ERR(di->adc_vbus_v) == -ENODEV)
34208c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
34218c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get ADC USB charger voltage\n");
34228c2ecf20Sopenharmony_ci		return PTR_ERR(di->adc_vbus_v);
34238c2ecf20Sopenharmony_ci	}
34248c2ecf20Sopenharmony_ci	di->adc_usb_charger_c = devm_iio_channel_get(&pdev->dev,
34258c2ecf20Sopenharmony_ci						     "usb_charger_c");
34268c2ecf20Sopenharmony_ci	if (IS_ERR(di->adc_usb_charger_c)) {
34278c2ecf20Sopenharmony_ci		if (PTR_ERR(di->adc_usb_charger_c) == -ENODEV)
34288c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
34298c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get ADC USB charger current\n");
34308c2ecf20Sopenharmony_ci		return PTR_ERR(di->adc_usb_charger_c);
34318c2ecf20Sopenharmony_ci	}
34328c2ecf20Sopenharmony_ci
34338c2ecf20Sopenharmony_ci	/* initialize lock */
34348c2ecf20Sopenharmony_ci	spin_lock_init(&di->usb_state.usb_lock);
34358c2ecf20Sopenharmony_ci	mutex_init(&di->usb_ipt_crnt_lock);
34368c2ecf20Sopenharmony_ci
34378c2ecf20Sopenharmony_ci	di->autopower = false;
34388c2ecf20Sopenharmony_ci	di->invalid_charger_detect_state = 0;
34398c2ecf20Sopenharmony_ci
34408c2ecf20Sopenharmony_ci	/* AC and USB supply config */
34418c2ecf20Sopenharmony_ci	ac_psy_cfg.supplied_to = supply_interface;
34428c2ecf20Sopenharmony_ci	ac_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
34438c2ecf20Sopenharmony_ci	ac_psy_cfg.drv_data = &di->ac_chg;
34448c2ecf20Sopenharmony_ci	usb_psy_cfg.supplied_to = supply_interface;
34458c2ecf20Sopenharmony_ci	usb_psy_cfg.num_supplicants = ARRAY_SIZE(supply_interface);
34468c2ecf20Sopenharmony_ci	usb_psy_cfg.drv_data = &di->usb_chg;
34478c2ecf20Sopenharmony_ci
34488c2ecf20Sopenharmony_ci	/* AC supply */
34498c2ecf20Sopenharmony_ci	/* ux500_charger sub-class */
34508c2ecf20Sopenharmony_ci	di->ac_chg.ops.enable = &ab8500_charger_ac_en;
34518c2ecf20Sopenharmony_ci	di->ac_chg.ops.check_enable = &ab8500_charger_ac_check_enable;
34528c2ecf20Sopenharmony_ci	di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
34538c2ecf20Sopenharmony_ci	di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current;
34548c2ecf20Sopenharmony_ci	di->ac_chg.max_out_volt = ab8500_charger_voltage_map[
34558c2ecf20Sopenharmony_ci		ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
34568c2ecf20Sopenharmony_ci	di->ac_chg.max_out_curr =
34578c2ecf20Sopenharmony_ci		di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1];
34588c2ecf20Sopenharmony_ci	di->ac_chg.wdt_refresh = CHG_WD_INTERVAL;
34598c2ecf20Sopenharmony_ci	di->ac_chg.enabled = di->bm->ac_enabled;
34608c2ecf20Sopenharmony_ci	di->ac_chg.external = false;
34618c2ecf20Sopenharmony_ci
34628c2ecf20Sopenharmony_ci	/*notifier for external charger enabling*/
34638c2ecf20Sopenharmony_ci	if (!di->ac_chg.enabled)
34648c2ecf20Sopenharmony_ci		blocking_notifier_chain_register(
34658c2ecf20Sopenharmony_ci			&charger_notifier_list, &charger_nb);
34668c2ecf20Sopenharmony_ci
34678c2ecf20Sopenharmony_ci	/* USB supply */
34688c2ecf20Sopenharmony_ci	/* ux500_charger sub-class */
34698c2ecf20Sopenharmony_ci	di->usb_chg.ops.enable = &ab8500_charger_usb_en;
34708c2ecf20Sopenharmony_ci	di->usb_chg.ops.check_enable = &ab8500_charger_usb_check_enable;
34718c2ecf20Sopenharmony_ci	di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
34728c2ecf20Sopenharmony_ci	di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current;
34738c2ecf20Sopenharmony_ci	di->usb_chg.max_out_volt = ab8500_charger_voltage_map[
34748c2ecf20Sopenharmony_ci		ARRAY_SIZE(ab8500_charger_voltage_map) - 1];
34758c2ecf20Sopenharmony_ci	di->usb_chg.max_out_curr =
34768c2ecf20Sopenharmony_ci		di->bm->chg_output_curr[di->bm->n_chg_out_curr - 1];
34778c2ecf20Sopenharmony_ci	di->usb_chg.wdt_refresh = CHG_WD_INTERVAL;
34788c2ecf20Sopenharmony_ci	di->usb_chg.enabled = di->bm->usb_enabled;
34798c2ecf20Sopenharmony_ci	di->usb_chg.external = false;
34808c2ecf20Sopenharmony_ci	di->usb_state.usb_current = -1;
34818c2ecf20Sopenharmony_ci
34828c2ecf20Sopenharmony_ci	/* Create a work queue for the charger */
34838c2ecf20Sopenharmony_ci	di->charger_wq = alloc_ordered_workqueue("ab8500_charger_wq",
34848c2ecf20Sopenharmony_ci						 WQ_MEM_RECLAIM);
34858c2ecf20Sopenharmony_ci	if (di->charger_wq == NULL) {
34868c2ecf20Sopenharmony_ci		dev_err(di->dev, "failed to create work queue\n");
34878c2ecf20Sopenharmony_ci		return -ENOMEM;
34888c2ecf20Sopenharmony_ci	}
34898c2ecf20Sopenharmony_ci
34908c2ecf20Sopenharmony_ci	mutex_init(&di->charger_attached_mutex);
34918c2ecf20Sopenharmony_ci
34928c2ecf20Sopenharmony_ci	/* Init work for HW failure check */
34938c2ecf20Sopenharmony_ci	INIT_DEFERRABLE_WORK(&di->check_hw_failure_work,
34948c2ecf20Sopenharmony_ci		ab8500_charger_check_hw_failure_work);
34958c2ecf20Sopenharmony_ci	INIT_DEFERRABLE_WORK(&di->check_usbchgnotok_work,
34968c2ecf20Sopenharmony_ci		ab8500_charger_check_usbchargernotok_work);
34978c2ecf20Sopenharmony_ci
34988c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&di->ac_charger_attached_work,
34998c2ecf20Sopenharmony_ci			  ab8500_charger_ac_attached_work);
35008c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&di->usb_charger_attached_work,
35018c2ecf20Sopenharmony_ci			  ab8500_charger_usb_attached_work);
35028c2ecf20Sopenharmony_ci
35038c2ecf20Sopenharmony_ci	/*
35048c2ecf20Sopenharmony_ci	 * For ABB revision 1.0 and 1.1 there is a bug in the watchdog
35058c2ecf20Sopenharmony_ci	 * logic. That means we have to continuously kick the charger
35068c2ecf20Sopenharmony_ci	 * watchdog even when no charger is connected. This is only
35078c2ecf20Sopenharmony_ci	 * valid once the AC charger has been enabled. This is
35088c2ecf20Sopenharmony_ci	 * a bug that is not handled by the algorithm and the
35098c2ecf20Sopenharmony_ci	 * watchdog have to be kicked by the charger driver
35108c2ecf20Sopenharmony_ci	 * when the AC charger is disabled
35118c2ecf20Sopenharmony_ci	 */
35128c2ecf20Sopenharmony_ci	INIT_DEFERRABLE_WORK(&di->kick_wd_work,
35138c2ecf20Sopenharmony_ci		ab8500_charger_kick_watchdog_work);
35148c2ecf20Sopenharmony_ci
35158c2ecf20Sopenharmony_ci	INIT_DEFERRABLE_WORK(&di->check_vbat_work,
35168c2ecf20Sopenharmony_ci		ab8500_charger_check_vbat_work);
35178c2ecf20Sopenharmony_ci
35188c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&di->attach_work,
35198c2ecf20Sopenharmony_ci		ab8500_charger_usb_link_attach_work);
35208c2ecf20Sopenharmony_ci
35218c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&di->usb_state_changed_work,
35228c2ecf20Sopenharmony_ci		ab8500_charger_usb_state_changed_work);
35238c2ecf20Sopenharmony_ci
35248c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&di->vbus_drop_end_work,
35258c2ecf20Sopenharmony_ci		ab8500_charger_vbus_drop_end_work);
35268c2ecf20Sopenharmony_ci
35278c2ecf20Sopenharmony_ci	/* Init work for charger detection */
35288c2ecf20Sopenharmony_ci	INIT_WORK(&di->usb_link_status_work,
35298c2ecf20Sopenharmony_ci		ab8500_charger_usb_link_status_work);
35308c2ecf20Sopenharmony_ci	INIT_WORK(&di->ac_work, ab8500_charger_ac_work);
35318c2ecf20Sopenharmony_ci	INIT_WORK(&di->detect_usb_type_work,
35328c2ecf20Sopenharmony_ci		ab8500_charger_detect_usb_type_work);
35338c2ecf20Sopenharmony_ci
35348c2ecf20Sopenharmony_ci	/* Init work for checking HW status */
35358c2ecf20Sopenharmony_ci	INIT_WORK(&di->check_main_thermal_prot_work,
35368c2ecf20Sopenharmony_ci		ab8500_charger_check_main_thermal_prot_work);
35378c2ecf20Sopenharmony_ci	INIT_WORK(&di->check_usb_thermal_prot_work,
35388c2ecf20Sopenharmony_ci		ab8500_charger_check_usb_thermal_prot_work);
35398c2ecf20Sopenharmony_ci
35408c2ecf20Sopenharmony_ci	/*
35418c2ecf20Sopenharmony_ci	 * VDD ADC supply needs to be enabled from this driver when there
35428c2ecf20Sopenharmony_ci	 * is a charger connected to avoid erroneous BTEMP_HIGH/LOW
35438c2ecf20Sopenharmony_ci	 * interrupts during charging
35448c2ecf20Sopenharmony_ci	 */
35458c2ecf20Sopenharmony_ci	di->regu = devm_regulator_get(di->dev, "vddadc");
35468c2ecf20Sopenharmony_ci	if (IS_ERR(di->regu)) {
35478c2ecf20Sopenharmony_ci		ret = PTR_ERR(di->regu);
35488c2ecf20Sopenharmony_ci		dev_err(di->dev, "failed to get vddadc regulator\n");
35498c2ecf20Sopenharmony_ci		goto free_charger_wq;
35508c2ecf20Sopenharmony_ci	}
35518c2ecf20Sopenharmony_ci
35528c2ecf20Sopenharmony_ci
35538c2ecf20Sopenharmony_ci	/* Initialize OVV, and other registers */
35548c2ecf20Sopenharmony_ci	ret = ab8500_charger_init_hw_registers(di);
35558c2ecf20Sopenharmony_ci	if (ret) {
35568c2ecf20Sopenharmony_ci		dev_err(di->dev, "failed to initialize ABB registers\n");
35578c2ecf20Sopenharmony_ci		goto free_charger_wq;
35588c2ecf20Sopenharmony_ci	}
35598c2ecf20Sopenharmony_ci
35608c2ecf20Sopenharmony_ci	/* Register AC charger class */
35618c2ecf20Sopenharmony_ci	if (di->ac_chg.enabled) {
35628c2ecf20Sopenharmony_ci		di->ac_chg.psy = power_supply_register(di->dev,
35638c2ecf20Sopenharmony_ci						       &ab8500_ac_chg_desc,
35648c2ecf20Sopenharmony_ci						       &ac_psy_cfg);
35658c2ecf20Sopenharmony_ci		if (IS_ERR(di->ac_chg.psy)) {
35668c2ecf20Sopenharmony_ci			dev_err(di->dev, "failed to register AC charger\n");
35678c2ecf20Sopenharmony_ci			ret = PTR_ERR(di->ac_chg.psy);
35688c2ecf20Sopenharmony_ci			goto free_charger_wq;
35698c2ecf20Sopenharmony_ci		}
35708c2ecf20Sopenharmony_ci	}
35718c2ecf20Sopenharmony_ci
35728c2ecf20Sopenharmony_ci	/* Register USB charger class */
35738c2ecf20Sopenharmony_ci	if (di->usb_chg.enabled) {
35748c2ecf20Sopenharmony_ci		di->usb_chg.psy = power_supply_register(di->dev,
35758c2ecf20Sopenharmony_ci							&ab8500_usb_chg_desc,
35768c2ecf20Sopenharmony_ci							&usb_psy_cfg);
35778c2ecf20Sopenharmony_ci		if (IS_ERR(di->usb_chg.psy)) {
35788c2ecf20Sopenharmony_ci			dev_err(di->dev, "failed to register USB charger\n");
35798c2ecf20Sopenharmony_ci			ret = PTR_ERR(di->usb_chg.psy);
35808c2ecf20Sopenharmony_ci			goto free_ac;
35818c2ecf20Sopenharmony_ci		}
35828c2ecf20Sopenharmony_ci	}
35838c2ecf20Sopenharmony_ci
35848c2ecf20Sopenharmony_ci	di->usb_phy = usb_get_phy(USB_PHY_TYPE_USB2);
35858c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(di->usb_phy)) {
35868c2ecf20Sopenharmony_ci		dev_err(di->dev, "failed to get usb transceiver\n");
35878c2ecf20Sopenharmony_ci		ret = -EINVAL;
35888c2ecf20Sopenharmony_ci		goto free_usb;
35898c2ecf20Sopenharmony_ci	}
35908c2ecf20Sopenharmony_ci	di->nb.notifier_call = ab8500_charger_usb_notifier_call;
35918c2ecf20Sopenharmony_ci	ret = usb_register_notifier(di->usb_phy, &di->nb);
35928c2ecf20Sopenharmony_ci	if (ret) {
35938c2ecf20Sopenharmony_ci		dev_err(di->dev, "failed to register usb notifier\n");
35948c2ecf20Sopenharmony_ci		goto put_usb_phy;
35958c2ecf20Sopenharmony_ci	}
35968c2ecf20Sopenharmony_ci
35978c2ecf20Sopenharmony_ci	/* Identify the connected charger types during startup */
35988c2ecf20Sopenharmony_ci	charger_status = ab8500_charger_detect_chargers(di, true);
35998c2ecf20Sopenharmony_ci	if (charger_status & AC_PW_CONN) {
36008c2ecf20Sopenharmony_ci		di->ac.charger_connected = 1;
36018c2ecf20Sopenharmony_ci		di->ac_conn = true;
36028c2ecf20Sopenharmony_ci		ab8500_power_supply_changed(di, di->ac_chg.psy);
36038c2ecf20Sopenharmony_ci		sysfs_notify(&di->ac_chg.psy->dev.kobj, NULL, "present");
36048c2ecf20Sopenharmony_ci	}
36058c2ecf20Sopenharmony_ci
36068c2ecf20Sopenharmony_ci	if (charger_status & USB_PW_CONN) {
36078c2ecf20Sopenharmony_ci		di->vbus_detected = true;
36088c2ecf20Sopenharmony_ci		di->vbus_detected_start = true;
36098c2ecf20Sopenharmony_ci		queue_work(di->charger_wq,
36108c2ecf20Sopenharmony_ci			&di->detect_usb_type_work);
36118c2ecf20Sopenharmony_ci	}
36128c2ecf20Sopenharmony_ci
36138c2ecf20Sopenharmony_ci	/* Register interrupts */
36148c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ab8500_charger_irq); i++) {
36158c2ecf20Sopenharmony_ci		irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
36168c2ecf20Sopenharmony_ci		if (irq < 0) {
36178c2ecf20Sopenharmony_ci			ret = irq;
36188c2ecf20Sopenharmony_ci			goto free_irq;
36198c2ecf20Sopenharmony_ci		}
36208c2ecf20Sopenharmony_ci
36218c2ecf20Sopenharmony_ci		ret = request_threaded_irq(irq, NULL, ab8500_charger_irq[i].isr,
36228c2ecf20Sopenharmony_ci			IRQF_SHARED | IRQF_NO_SUSPEND,
36238c2ecf20Sopenharmony_ci			ab8500_charger_irq[i].name, di);
36248c2ecf20Sopenharmony_ci
36258c2ecf20Sopenharmony_ci		if (ret != 0) {
36268c2ecf20Sopenharmony_ci			dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
36278c2ecf20Sopenharmony_ci				, ab8500_charger_irq[i].name, irq, ret);
36288c2ecf20Sopenharmony_ci			goto free_irq;
36298c2ecf20Sopenharmony_ci		}
36308c2ecf20Sopenharmony_ci		dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
36318c2ecf20Sopenharmony_ci			ab8500_charger_irq[i].name, irq, ret);
36328c2ecf20Sopenharmony_ci	}
36338c2ecf20Sopenharmony_ci
36348c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, di);
36358c2ecf20Sopenharmony_ci
36368c2ecf20Sopenharmony_ci	mutex_lock(&di->charger_attached_mutex);
36378c2ecf20Sopenharmony_ci
36388c2ecf20Sopenharmony_ci	ch_stat = ab8500_charger_detect_chargers(di, false);
36398c2ecf20Sopenharmony_ci
36408c2ecf20Sopenharmony_ci	if ((ch_stat & AC_PW_CONN) == AC_PW_CONN) {
36418c2ecf20Sopenharmony_ci		if (is_ab8500(di->parent))
36428c2ecf20Sopenharmony_ci			queue_delayed_work(di->charger_wq,
36438c2ecf20Sopenharmony_ci					   &di->ac_charger_attached_work,
36448c2ecf20Sopenharmony_ci					   HZ);
36458c2ecf20Sopenharmony_ci	}
36468c2ecf20Sopenharmony_ci	if ((ch_stat & USB_PW_CONN) == USB_PW_CONN) {
36478c2ecf20Sopenharmony_ci		if (is_ab8500(di->parent))
36488c2ecf20Sopenharmony_ci			queue_delayed_work(di->charger_wq,
36498c2ecf20Sopenharmony_ci					   &di->usb_charger_attached_work,
36508c2ecf20Sopenharmony_ci					   HZ);
36518c2ecf20Sopenharmony_ci	}
36528c2ecf20Sopenharmony_ci
36538c2ecf20Sopenharmony_ci	mutex_unlock(&di->charger_attached_mutex);
36548c2ecf20Sopenharmony_ci
36558c2ecf20Sopenharmony_ci	return ret;
36568c2ecf20Sopenharmony_ci
36578c2ecf20Sopenharmony_cifree_irq:
36588c2ecf20Sopenharmony_ci	usb_unregister_notifier(di->usb_phy, &di->nb);
36598c2ecf20Sopenharmony_ci
36608c2ecf20Sopenharmony_ci	/* We also have to free all successfully registered irqs */
36618c2ecf20Sopenharmony_ci	for (i = i - 1; i >= 0; i--) {
36628c2ecf20Sopenharmony_ci		irq = platform_get_irq_byname(pdev, ab8500_charger_irq[i].name);
36638c2ecf20Sopenharmony_ci		free_irq(irq, di);
36648c2ecf20Sopenharmony_ci	}
36658c2ecf20Sopenharmony_ciput_usb_phy:
36668c2ecf20Sopenharmony_ci	usb_put_phy(di->usb_phy);
36678c2ecf20Sopenharmony_cifree_usb:
36688c2ecf20Sopenharmony_ci	if (di->usb_chg.enabled)
36698c2ecf20Sopenharmony_ci		power_supply_unregister(di->usb_chg.psy);
36708c2ecf20Sopenharmony_cifree_ac:
36718c2ecf20Sopenharmony_ci	if (di->ac_chg.enabled)
36728c2ecf20Sopenharmony_ci		power_supply_unregister(di->ac_chg.psy);
36738c2ecf20Sopenharmony_cifree_charger_wq:
36748c2ecf20Sopenharmony_ci	destroy_workqueue(di->charger_wq);
36758c2ecf20Sopenharmony_ci	return ret;
36768c2ecf20Sopenharmony_ci}
36778c2ecf20Sopenharmony_ci
36788c2ecf20Sopenharmony_cistatic const struct of_device_id ab8500_charger_match[] = {
36798c2ecf20Sopenharmony_ci	{ .compatible = "stericsson,ab8500-charger", },
36808c2ecf20Sopenharmony_ci	{ },
36818c2ecf20Sopenharmony_ci};
36828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ab8500_charger_match);
36838c2ecf20Sopenharmony_ci
36848c2ecf20Sopenharmony_cistatic struct platform_driver ab8500_charger_driver = {
36858c2ecf20Sopenharmony_ci	.probe = ab8500_charger_probe,
36868c2ecf20Sopenharmony_ci	.remove = ab8500_charger_remove,
36878c2ecf20Sopenharmony_ci	.suspend = ab8500_charger_suspend,
36888c2ecf20Sopenharmony_ci	.resume = ab8500_charger_resume,
36898c2ecf20Sopenharmony_ci	.driver = {
36908c2ecf20Sopenharmony_ci		.name = "ab8500-charger",
36918c2ecf20Sopenharmony_ci		.of_match_table = ab8500_charger_match,
36928c2ecf20Sopenharmony_ci	},
36938c2ecf20Sopenharmony_ci};
36948c2ecf20Sopenharmony_ci
36958c2ecf20Sopenharmony_cistatic int __init ab8500_charger_init(void)
36968c2ecf20Sopenharmony_ci{
36978c2ecf20Sopenharmony_ci	return platform_driver_register(&ab8500_charger_driver);
36988c2ecf20Sopenharmony_ci}
36998c2ecf20Sopenharmony_ci
37008c2ecf20Sopenharmony_cistatic void __exit ab8500_charger_exit(void)
37018c2ecf20Sopenharmony_ci{
37028c2ecf20Sopenharmony_ci	platform_driver_unregister(&ab8500_charger_driver);
37038c2ecf20Sopenharmony_ci}
37048c2ecf20Sopenharmony_ci
37058c2ecf20Sopenharmony_cisubsys_initcall_sync(ab8500_charger_init);
37068c2ecf20Sopenharmony_cimodule_exit(ab8500_charger_exit);
37078c2ecf20Sopenharmony_ci
37088c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
37098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johan Palsson, Karl Komierowski, Arun R Murthy");
37108c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ab8500-charger");
37118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AB8500 charger management driver");
3712