11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel  * axp288_charger.c - X-power AXP288 PMIC Charger driver
48c0984e5SSebastian Reichel  *
5d8e65195SHans de Goede  * Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com>
68c0984e5SSebastian Reichel  * Copyright (C) 2014 Intel Corporation
78c0984e5SSebastian Reichel  * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
88c0984e5SSebastian Reichel  */
98c0984e5SSebastian Reichel 
106c4c9a9aSHans de Goede #include <linux/acpi.h>
11a95761d6SOlliver Schinagl #include <linux/bitops.h>
128c0984e5SSebastian Reichel #include <linux/module.h>
138c0984e5SSebastian Reichel #include <linux/device.h>
148c0984e5SSebastian Reichel #include <linux/regmap.h>
158c0984e5SSebastian Reichel #include <linux/workqueue.h>
168c0984e5SSebastian Reichel #include <linux/delay.h>
178c0984e5SSebastian Reichel #include <linux/platform_device.h>
188c0984e5SSebastian Reichel #include <linux/usb/otg.h>
198c0984e5SSebastian Reichel #include <linux/notifier.h>
208c0984e5SSebastian Reichel #include <linux/power_supply.h>
218c0984e5SSebastian Reichel #include <linux/property.h>
228c0984e5SSebastian Reichel #include <linux/mfd/axp20x.h>
238c0984e5SSebastian Reichel #include <linux/extcon.h>
249c80662aSHans de Goede #include <linux/dmi.h>
25ed229454SKate Hsuan #include <asm/iosf_mbi.h>
268c0984e5SSebastian Reichel 
27a95761d6SOlliver Schinagl #define PS_STAT_VBUS_TRIGGER		BIT(0)
28a95761d6SOlliver Schinagl #define PS_STAT_BAT_CHRG_DIR		BIT(2)
29a95761d6SOlliver Schinagl #define PS_STAT_VBAT_ABOVE_VHOLD	BIT(3)
30a95761d6SOlliver Schinagl #define PS_STAT_VBUS_VALID		BIT(4)
31a95761d6SOlliver Schinagl #define PS_STAT_VBUS_PRESENT		BIT(5)
328c0984e5SSebastian Reichel 
33a95761d6SOlliver Schinagl #define CHRG_STAT_BAT_SAFE_MODE		BIT(3)
34a95761d6SOlliver Schinagl #define CHRG_STAT_BAT_VALID		BIT(4)
35a95761d6SOlliver Schinagl #define CHRG_STAT_BAT_PRESENT		BIT(5)
36a95761d6SOlliver Schinagl #define CHRG_STAT_CHARGING		BIT(6)
37a95761d6SOlliver Schinagl #define CHRG_STAT_PMIC_OTP		BIT(7)
388c0984e5SSebastian Reichel 
398c0984e5SSebastian Reichel #define VBUS_ISPOUT_CUR_LIM_MASK	0x03
408c0984e5SSebastian Reichel #define VBUS_ISPOUT_CUR_LIM_BIT_POS	0
418c0984e5SSebastian Reichel #define VBUS_ISPOUT_CUR_LIM_900MA	0x0	/* 900mA */
428c0984e5SSebastian Reichel #define VBUS_ISPOUT_CUR_LIM_1500MA	0x1	/* 1500mA */
438c0984e5SSebastian Reichel #define VBUS_ISPOUT_CUR_LIM_2000MA	0x2	/* 2000mA */
448c0984e5SSebastian Reichel #define VBUS_ISPOUT_CUR_NO_LIM		0x3	/* 2500mA */
455ac121b8SHans de Goede #define VBUS_ISPOUT_VHOLD_SET_MASK	0x38
468c0984e5SSebastian Reichel #define VBUS_ISPOUT_VHOLD_SET_BIT_POS	0x3
478c0984e5SSebastian Reichel #define VBUS_ISPOUT_VHOLD_SET_OFFSET	4000	/* 4000mV */
488c0984e5SSebastian Reichel #define VBUS_ISPOUT_VHOLD_SET_LSB_RES	100	/* 100mV */
495ac121b8SHans de Goede #define VBUS_ISPOUT_VHOLD_SET_4400MV	0x4	/* 4400mV */
50a95761d6SOlliver Schinagl #define VBUS_ISPOUT_VBUS_PATH_DIS	BIT(7)
518c0984e5SSebastian Reichel 
528c0984e5SSebastian Reichel #define CHRG_CCCV_CC_MASK		0xf		/* 4 bits */
538c0984e5SSebastian Reichel #define CHRG_CCCV_CC_BIT_POS		0
548c0984e5SSebastian Reichel #define CHRG_CCCV_CC_OFFSET		200		/* 200mA */
558c0984e5SSebastian Reichel #define CHRG_CCCV_CC_LSB_RES		200		/* 200mA */
56a95761d6SOlliver Schinagl #define CHRG_CCCV_ITERM_20P		BIT(4)		/* 20% of CC */
578c0984e5SSebastian Reichel #define CHRG_CCCV_CV_MASK		0x60		/* 2 bits */
588c0984e5SSebastian Reichel #define CHRG_CCCV_CV_BIT_POS		5
598c0984e5SSebastian Reichel #define CHRG_CCCV_CV_4100MV		0x0		/* 4.10V */
608c0984e5SSebastian Reichel #define CHRG_CCCV_CV_4150MV		0x1		/* 4.15V */
618c0984e5SSebastian Reichel #define CHRG_CCCV_CV_4200MV		0x2		/* 4.20V */
628c0984e5SSebastian Reichel #define CHRG_CCCV_CV_4350MV		0x3		/* 4.35V */
63a95761d6SOlliver Schinagl #define CHRG_CCCV_CHG_EN		BIT(7)
648c0984e5SSebastian Reichel 
658c0984e5SSebastian Reichel #define CNTL2_CC_TIMEOUT_MASK		0x3	/* 2 bits */
668c0984e5SSebastian Reichel #define CNTL2_CC_TIMEOUT_OFFSET		6	/* 6 Hrs */
678c0984e5SSebastian Reichel #define CNTL2_CC_TIMEOUT_LSB_RES	2	/* 2 Hrs */
688c0984e5SSebastian Reichel #define CNTL2_CC_TIMEOUT_12HRS		0x3	/* 12 Hrs */
69a95761d6SOlliver Schinagl #define CNTL2_CHGLED_TYPEB		BIT(4)
70a95761d6SOlliver Schinagl #define CNTL2_CHG_OUT_TURNON		BIT(5)
718c0984e5SSebastian Reichel #define CNTL2_PC_TIMEOUT_MASK		0xC0
728c0984e5SSebastian Reichel #define CNTL2_PC_TIMEOUT_OFFSET		40	/* 40 mins */
738c0984e5SSebastian Reichel #define CNTL2_PC_TIMEOUT_LSB_RES	10	/* 10 mins */
748c0984e5SSebastian Reichel #define CNTL2_PC_TIMEOUT_70MINS		0x3
758c0984e5SSebastian Reichel 
76a95761d6SOlliver Schinagl #define CHRG_ILIM_TEMP_LOOP_EN		BIT(3)
778c0984e5SSebastian Reichel #define CHRG_VBUS_ILIM_MASK		0xf0
788c0984e5SSebastian Reichel #define CHRG_VBUS_ILIM_BIT_POS		4
798c0984e5SSebastian Reichel #define CHRG_VBUS_ILIM_100MA		0x0	/* 100mA */
808c0984e5SSebastian Reichel #define CHRG_VBUS_ILIM_500MA		0x1	/* 500mA */
818c0984e5SSebastian Reichel #define CHRG_VBUS_ILIM_900MA		0x2	/* 900mA */
828c0984e5SSebastian Reichel #define CHRG_VBUS_ILIM_1500MA		0x3	/* 1500mA */
838c0984e5SSebastian Reichel #define CHRG_VBUS_ILIM_2000MA		0x4	/* 2000mA */
848c0984e5SSebastian Reichel #define CHRG_VBUS_ILIM_2500MA		0x5	/* 2500mA */
858c0984e5SSebastian Reichel #define CHRG_VBUS_ILIM_3000MA		0x6	/* 3000mA */
865b76ad50SHans de Goede #define CHRG_VBUS_ILIM_3500MA		0x7	/* 3500mA */
875b76ad50SHans de Goede #define CHRG_VBUS_ILIM_4000MA		0x8	/* 4000mA */
888c0984e5SSebastian Reichel 
898c0984e5SSebastian Reichel #define CHRG_VLTFC_0C			0xA5	/* 0 DegC */
908c0984e5SSebastian Reichel #define CHRG_VHTFC_45C			0x1F	/* 45 DegC */
918c0984e5SSebastian Reichel 
92a95761d6SOlliver Schinagl #define FG_CNTL_OCV_ADJ_EN		BIT(3)
938c0984e5SSebastian Reichel 
948c0984e5SSebastian Reichel #define CV_4100MV			4100	/* 4100mV */
958c0984e5SSebastian Reichel #define CV_4150MV			4150	/* 4150mV */
968c0984e5SSebastian Reichel #define CV_4200MV			4200	/* 4200mV */
978c0984e5SSebastian Reichel #define CV_4350MV			4350	/* 4350mV */
988c0984e5SSebastian Reichel 
99ed229454SKate Hsuan #define AXP288_REG_UPDATE_INTERVAL	(60 * HZ)
100ed229454SKate Hsuan 
1018c0984e5SSebastian Reichel #define AXP288_EXTCON_DEV_NAME		"axp288_extcon"
1026c4c9a9aSHans de Goede #define USB_HOST_EXTCON_HID		"INT3496"
1036c4c9a9aSHans de Goede #define USB_HOST_EXTCON_NAME		"INT3496:00"
1048c0984e5SSebastian Reichel 
1058c0984e5SSebastian Reichel enum {
1068c0984e5SSebastian Reichel 	VBUS_OV_IRQ = 0,
1078c0984e5SSebastian Reichel 	CHARGE_DONE_IRQ,
1088c0984e5SSebastian Reichel 	CHARGE_CHARGING_IRQ,
1098c0984e5SSebastian Reichel 	BAT_SAFE_QUIT_IRQ,
1108c0984e5SSebastian Reichel 	BAT_SAFE_ENTER_IRQ,
1118c0984e5SSebastian Reichel 	QCBTU_IRQ,
1128c0984e5SSebastian Reichel 	CBTU_IRQ,
1138c0984e5SSebastian Reichel 	QCBTO_IRQ,
1148c0984e5SSebastian Reichel 	CBTO_IRQ,
1158c0984e5SSebastian Reichel 	CHRG_INTR_END,
1168c0984e5SSebastian Reichel };
1178c0984e5SSebastian Reichel 
1188c0984e5SSebastian Reichel struct axp288_chrg_info {
1198c0984e5SSebastian Reichel 	struct platform_device *pdev;
1208c0984e5SSebastian Reichel 	struct regmap *regmap;
1218c0984e5SSebastian Reichel 	struct regmap_irq_chip_data *regmap_irqc;
1228c0984e5SSebastian Reichel 	int irq[CHRG_INTR_END];
1238c0984e5SSebastian Reichel 	struct power_supply *psy_usb;
124ed229454SKate Hsuan 	struct mutex lock;
1258c0984e5SSebastian Reichel 
1268c0984e5SSebastian Reichel 	/* OTG/Host mode */
1278c0984e5SSebastian Reichel 	struct {
1288c0984e5SSebastian Reichel 		struct work_struct work;
1298c0984e5SSebastian Reichel 		struct extcon_dev *cable;
1308c0984e5SSebastian Reichel 		struct notifier_block id_nb;
1318c0984e5SSebastian Reichel 		bool id_short;
1328c0984e5SSebastian Reichel 	} otg;
1338c0984e5SSebastian Reichel 
1348c0984e5SSebastian Reichel 	/* SDP/CDP/DCP USB charging cable notifications */
1358c0984e5SSebastian Reichel 	struct {
1368c0984e5SSebastian Reichel 		struct extcon_dev *edev;
1378c0a0a29SHans de Goede 		struct notifier_block nb;
1388c0984e5SSebastian Reichel 		struct work_struct work;
1398c0984e5SSebastian Reichel 	} cable;
1408c0984e5SSebastian Reichel 
1418c0984e5SSebastian Reichel 	int cc;
1428c0984e5SSebastian Reichel 	int cv;
1438c0984e5SSebastian Reichel 	int max_cc;
1448c0984e5SSebastian Reichel 	int max_cv;
145ed229454SKate Hsuan 
146ed229454SKate Hsuan 	unsigned long last_updated;
147ed229454SKate Hsuan 	unsigned int input_status;
148ed229454SKate Hsuan 	unsigned int op_mode;
149ed229454SKate Hsuan 	unsigned int backend_control;
150ed229454SKate Hsuan 	bool valid;
1518c0984e5SSebastian Reichel };
1528c0984e5SSebastian Reichel 
axp288_charger_set_cc(struct axp288_chrg_info * info,int cc)1538c0984e5SSebastian Reichel static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
1548c0984e5SSebastian Reichel {
1558c0984e5SSebastian Reichel 	u8 reg_val;
1568c0984e5SSebastian Reichel 	int ret;
1578c0984e5SSebastian Reichel 
1588c0984e5SSebastian Reichel 	if (cc < CHRG_CCCV_CC_OFFSET)
1598c0984e5SSebastian Reichel 		cc = CHRG_CCCV_CC_OFFSET;
1608c0984e5SSebastian Reichel 	else if (cc > info->max_cc)
1618c0984e5SSebastian Reichel 		cc = info->max_cc;
1628c0984e5SSebastian Reichel 
1638c0984e5SSebastian Reichel 	reg_val = (cc - CHRG_CCCV_CC_OFFSET) / CHRG_CCCV_CC_LSB_RES;
1648c0984e5SSebastian Reichel 	cc = (reg_val * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
1658c0984e5SSebastian Reichel 	reg_val = reg_val << CHRG_CCCV_CC_BIT_POS;
1668c0984e5SSebastian Reichel 
1678c0984e5SSebastian Reichel 	ret = regmap_update_bits(info->regmap,
1688c0984e5SSebastian Reichel 				AXP20X_CHRG_CTRL1,
1698c0984e5SSebastian Reichel 				CHRG_CCCV_CC_MASK, reg_val);
1708c0984e5SSebastian Reichel 	if (ret >= 0)
1718c0984e5SSebastian Reichel 		info->cc = cc;
1728c0984e5SSebastian Reichel 
1738c0984e5SSebastian Reichel 	return ret;
1748c0984e5SSebastian Reichel }
1758c0984e5SSebastian Reichel 
axp288_charger_set_cv(struct axp288_chrg_info * info,int cv)1768c0984e5SSebastian Reichel static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv)
1778c0984e5SSebastian Reichel {
1788c0984e5SSebastian Reichel 	u8 reg_val;
1798c0984e5SSebastian Reichel 	int ret;
1808c0984e5SSebastian Reichel 
1818c0984e5SSebastian Reichel 	if (cv <= CV_4100MV) {
1828c0984e5SSebastian Reichel 		reg_val = CHRG_CCCV_CV_4100MV;
1838c0984e5SSebastian Reichel 		cv = CV_4100MV;
1848c0984e5SSebastian Reichel 	} else if (cv <= CV_4150MV) {
1858c0984e5SSebastian Reichel 		reg_val = CHRG_CCCV_CV_4150MV;
1868c0984e5SSebastian Reichel 		cv = CV_4150MV;
1878c0984e5SSebastian Reichel 	} else if (cv <= CV_4200MV) {
1888c0984e5SSebastian Reichel 		reg_val = CHRG_CCCV_CV_4200MV;
1898c0984e5SSebastian Reichel 		cv = CV_4200MV;
1908c0984e5SSebastian Reichel 	} else {
1918c0984e5SSebastian Reichel 		reg_val = CHRG_CCCV_CV_4350MV;
1928c0984e5SSebastian Reichel 		cv = CV_4350MV;
1938c0984e5SSebastian Reichel 	}
1948c0984e5SSebastian Reichel 
1958c0984e5SSebastian Reichel 	reg_val = reg_val << CHRG_CCCV_CV_BIT_POS;
1968c0984e5SSebastian Reichel 
1978c0984e5SSebastian Reichel 	ret = regmap_update_bits(info->regmap,
1988c0984e5SSebastian Reichel 				AXP20X_CHRG_CTRL1,
1998c0984e5SSebastian Reichel 				CHRG_CCCV_CV_MASK, reg_val);
2008c0984e5SSebastian Reichel 
2018c0984e5SSebastian Reichel 	if (ret >= 0)
2028c0984e5SSebastian Reichel 		info->cv = cv;
2038c0984e5SSebastian Reichel 
2048c0984e5SSebastian Reichel 	return ret;
2058c0984e5SSebastian Reichel }
2068c0984e5SSebastian Reichel 
axp288_charger_get_vbus_inlmt(struct axp288_chrg_info * info)207d1ce7e58SHans de Goede static int axp288_charger_get_vbus_inlmt(struct axp288_chrg_info *info)
208d1ce7e58SHans de Goede {
209d1ce7e58SHans de Goede 	unsigned int val;
210d1ce7e58SHans de Goede 
211ed229454SKate Hsuan 	val = info->backend_control;
212d1ce7e58SHans de Goede 
213d1ce7e58SHans de Goede 	val >>= CHRG_VBUS_ILIM_BIT_POS;
214d1ce7e58SHans de Goede 	switch (val) {
215d1ce7e58SHans de Goede 	case CHRG_VBUS_ILIM_100MA:
216d1ce7e58SHans de Goede 		return 100000;
217d1ce7e58SHans de Goede 	case CHRG_VBUS_ILIM_500MA:
218d1ce7e58SHans de Goede 		return 500000;
219d1ce7e58SHans de Goede 	case CHRG_VBUS_ILIM_900MA:
220d1ce7e58SHans de Goede 		return 900000;
221d1ce7e58SHans de Goede 	case CHRG_VBUS_ILIM_1500MA:
222d1ce7e58SHans de Goede 		return 1500000;
223d1ce7e58SHans de Goede 	case CHRG_VBUS_ILIM_2000MA:
224d1ce7e58SHans de Goede 		return 2000000;
225d1ce7e58SHans de Goede 	case CHRG_VBUS_ILIM_2500MA:
226d1ce7e58SHans de Goede 		return 2500000;
227d1ce7e58SHans de Goede 	case CHRG_VBUS_ILIM_3000MA:
228d1ce7e58SHans de Goede 		return 3000000;
2295b76ad50SHans de Goede 	case CHRG_VBUS_ILIM_3500MA:
2305b76ad50SHans de Goede 		return 3500000;
231d1ce7e58SHans de Goede 	default:
2325b76ad50SHans de Goede 		/* All b1xxx values map to 4000 mA */
2335b76ad50SHans de Goede 		return 4000000;
234d1ce7e58SHans de Goede 	}
235d1ce7e58SHans de Goede }
236d1ce7e58SHans de Goede 
axp288_charger_set_vbus_inlmt(struct axp288_chrg_info * info,int inlmt)2378c0984e5SSebastian Reichel static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,
2388c0984e5SSebastian Reichel 					   int inlmt)
2398c0984e5SSebastian Reichel {
2408c0984e5SSebastian Reichel 	int ret;
2418c0984e5SSebastian Reichel 	u8 reg_val;
2428c0984e5SSebastian Reichel 
2435b76ad50SHans de Goede 	if (inlmt >= 4000000)
2445b76ad50SHans de Goede 		reg_val = CHRG_VBUS_ILIM_4000MA << CHRG_VBUS_ILIM_BIT_POS;
2455b76ad50SHans de Goede 	else if (inlmt >= 3500000)
2465b76ad50SHans de Goede 		reg_val = CHRG_VBUS_ILIM_3500MA << CHRG_VBUS_ILIM_BIT_POS;
2475b76ad50SHans de Goede 	else if (inlmt >= 3000000)
2489563d054SHans de Goede 		reg_val = CHRG_VBUS_ILIM_3000MA << CHRG_VBUS_ILIM_BIT_POS;
2499563d054SHans de Goede 	else if (inlmt >= 2500000)
2509563d054SHans de Goede 		reg_val = CHRG_VBUS_ILIM_2500MA << CHRG_VBUS_ILIM_BIT_POS;
2519563d054SHans de Goede 	else if (inlmt >= 2000000)
2529563d054SHans de Goede 		reg_val = CHRG_VBUS_ILIM_2000MA << CHRG_VBUS_ILIM_BIT_POS;
2539563d054SHans de Goede 	else if (inlmt >= 1500000)
2549563d054SHans de Goede 		reg_val = CHRG_VBUS_ILIM_1500MA << CHRG_VBUS_ILIM_BIT_POS;
2559563d054SHans de Goede 	else if (inlmt >= 900000)
2569563d054SHans de Goede 		reg_val = CHRG_VBUS_ILIM_900MA << CHRG_VBUS_ILIM_BIT_POS;
2579563d054SHans de Goede 	else if (inlmt >= 500000)
2589563d054SHans de Goede 		reg_val = CHRG_VBUS_ILIM_500MA << CHRG_VBUS_ILIM_BIT_POS;
2599563d054SHans de Goede 	else
2609563d054SHans de Goede 		reg_val = CHRG_VBUS_ILIM_100MA << CHRG_VBUS_ILIM_BIT_POS;
261bbafa111SHans de Goede 
262bbafa111SHans de Goede 	ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL,
263bbafa111SHans de Goede 				 CHRG_VBUS_ILIM_MASK, reg_val);
264d1ce7e58SHans de Goede 	if (ret < 0)
2658c0984e5SSebastian Reichel 		dev_err(&info->pdev->dev, "charger BAK control %d\n", ret);
2668c0984e5SSebastian Reichel 
2678c0984e5SSebastian Reichel 	return ret;
2688c0984e5SSebastian Reichel }
2698c0984e5SSebastian Reichel 
axp288_charger_vbus_path_select(struct axp288_chrg_info * info,bool enable)2708c0984e5SSebastian Reichel static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info,
2718c0984e5SSebastian Reichel 								bool enable)
2728c0984e5SSebastian Reichel {
2738c0984e5SSebastian Reichel 	int ret;
2748c0984e5SSebastian Reichel 
2758c0984e5SSebastian Reichel 	if (enable)
2768c0984e5SSebastian Reichel 		ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
2778c0984e5SSebastian Reichel 					VBUS_ISPOUT_VBUS_PATH_DIS, 0);
2788c0984e5SSebastian Reichel 	else
2798c0984e5SSebastian Reichel 		ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
2808c0984e5SSebastian Reichel 			VBUS_ISPOUT_VBUS_PATH_DIS, VBUS_ISPOUT_VBUS_PATH_DIS);
2818c0984e5SSebastian Reichel 
2828c0984e5SSebastian Reichel 	if (ret < 0)
2838c0984e5SSebastian Reichel 		dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);
2848c0984e5SSebastian Reichel 
2858c0984e5SSebastian Reichel 	return ret;
2868c0984e5SSebastian Reichel }
2878c0984e5SSebastian Reichel 
axp288_charger_enable_charger(struct axp288_chrg_info * info,bool enable)2888c0984e5SSebastian Reichel static int axp288_charger_enable_charger(struct axp288_chrg_info *info,
2898c0984e5SSebastian Reichel 								bool enable)
2908c0984e5SSebastian Reichel {
2918c0984e5SSebastian Reichel 	int ret;
2928c0984e5SSebastian Reichel 
2938c0984e5SSebastian Reichel 	if (enable)
2948c0984e5SSebastian Reichel 		ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
2958c0984e5SSebastian Reichel 				CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
2968c0984e5SSebastian Reichel 	else
2978c0984e5SSebastian Reichel 		ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
2988c0984e5SSebastian Reichel 				CHRG_CCCV_CHG_EN, 0);
2998c0984e5SSebastian Reichel 	if (ret < 0)
3008c0984e5SSebastian Reichel 		dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret);
3018c0984e5SSebastian Reichel 
3028c0984e5SSebastian Reichel 	return ret;
3038c0984e5SSebastian Reichel }
3048c0984e5SSebastian Reichel 
axp288_get_charger_health(struct axp288_chrg_info * info)3058c0984e5SSebastian Reichel static int axp288_get_charger_health(struct axp288_chrg_info *info)
3068c0984e5SSebastian Reichel {
307ed229454SKate Hsuan 	if (!(info->input_status & PS_STAT_VBUS_PRESENT))
3085b5100c5SHans de Goede 		return POWER_SUPPLY_HEALTH_UNKNOWN;
3098c0984e5SSebastian Reichel 
310ed229454SKate Hsuan 	if (!(info->input_status & PS_STAT_VBUS_VALID))
3115b5100c5SHans de Goede 		return POWER_SUPPLY_HEALTH_DEAD;
312ed229454SKate Hsuan 	else if (info->op_mode & CHRG_STAT_PMIC_OTP)
3135b5100c5SHans de Goede 		return POWER_SUPPLY_HEALTH_OVERHEAT;
314ed229454SKate Hsuan 	else if (info->op_mode & CHRG_STAT_BAT_SAFE_MODE)
3155b5100c5SHans de Goede 		return POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
3168c0984e5SSebastian Reichel 	else
3175b5100c5SHans de Goede 		return POWER_SUPPLY_HEALTH_GOOD;
3188c0984e5SSebastian Reichel }
3198c0984e5SSebastian Reichel 
axp288_charger_usb_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)3208c0984e5SSebastian Reichel static int axp288_charger_usb_set_property(struct power_supply *psy,
3218c0984e5SSebastian Reichel 				    enum power_supply_property psp,
3228c0984e5SSebastian Reichel 				    const union power_supply_propval *val)
3238c0984e5SSebastian Reichel {
3248c0984e5SSebastian Reichel 	struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
3258c0984e5SSebastian Reichel 	int ret = 0;
3268c0984e5SSebastian Reichel 	int scaled_val;
3278c0984e5SSebastian Reichel 
328ed229454SKate Hsuan 	mutex_lock(&info->lock);
3298c0984e5SSebastian Reichel 	switch (psp) {
3308c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
3318c0984e5SSebastian Reichel 		scaled_val = min(val->intval, info->max_cc);
3328c0984e5SSebastian Reichel 		scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
3338c0984e5SSebastian Reichel 		ret = axp288_charger_set_cc(info, scaled_val);
334ed229454SKate Hsuan 		if (ret < 0) {
3358c0984e5SSebastian Reichel 			dev_warn(&info->pdev->dev, "set charge current failed\n");
336ed229454SKate Hsuan 			goto out;
337ed229454SKate Hsuan 		}
3388c0984e5SSebastian Reichel 		break;
3398c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
3408c0984e5SSebastian Reichel 		scaled_val = min(val->intval, info->max_cv);
3418c0984e5SSebastian Reichel 		scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
3428c0984e5SSebastian Reichel 		ret = axp288_charger_set_cv(info, scaled_val);
343ed229454SKate Hsuan 		if (ret < 0) {
3448c0984e5SSebastian Reichel 			dev_warn(&info->pdev->dev, "set charge voltage failed\n");
345ed229454SKate Hsuan 			goto out;
346ed229454SKate Hsuan 		}
3478c0984e5SSebastian Reichel 		break;
34881d56dd3SHans de Goede 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
34981d56dd3SHans de Goede 		ret = axp288_charger_set_vbus_inlmt(info, val->intval);
350ed229454SKate Hsuan 		if (ret < 0) {
35181d56dd3SHans de Goede 			dev_warn(&info->pdev->dev, "set input current limit failed\n");
352ed229454SKate Hsuan 			goto out;
353ed229454SKate Hsuan 		}
354ed229454SKate Hsuan 		info->valid = false;
35581d56dd3SHans de Goede 		break;
3568c0984e5SSebastian Reichel 	default:
3578c0984e5SSebastian Reichel 		ret = -EINVAL;
3588c0984e5SSebastian Reichel 	}
3598c0984e5SSebastian Reichel 
360ed229454SKate Hsuan out:
361ed229454SKate Hsuan 	mutex_unlock(&info->lock);
362ed229454SKate Hsuan 	return ret;
363ed229454SKate Hsuan }
364ed229454SKate Hsuan 
axp288_charger_reg_readb(struct axp288_chrg_info * info,int reg,unsigned int * ret_val)365ed229454SKate Hsuan static int axp288_charger_reg_readb(struct axp288_chrg_info *info, int reg, unsigned int *ret_val)
366ed229454SKate Hsuan {
367ed229454SKate Hsuan 	int ret;
368ed229454SKate Hsuan 
369ed229454SKate Hsuan 	ret = regmap_read(info->regmap, reg, ret_val);
370ed229454SKate Hsuan 	if (ret < 0) {
371ed229454SKate Hsuan 		dev_err(&info->pdev->dev, "Error %d on reading value from register 0x%04x\n",
372ed229454SKate Hsuan 			ret,
373ed229454SKate Hsuan 			reg);
374ed229454SKate Hsuan 		return ret;
375ed229454SKate Hsuan 	}
376ed229454SKate Hsuan 	return 0;
377ed229454SKate Hsuan }
378ed229454SKate Hsuan 
axp288_charger_usb_update_property(struct axp288_chrg_info * info)379ed229454SKate Hsuan static int axp288_charger_usb_update_property(struct axp288_chrg_info *info)
380ed229454SKate Hsuan {
381ed229454SKate Hsuan 	int ret = 0;
382ed229454SKate Hsuan 
383ed229454SKate Hsuan 	if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL))
384ed229454SKate Hsuan 		return 0;
385ed229454SKate Hsuan 
386ed229454SKate Hsuan 	dev_dbg(&info->pdev->dev, "Charger updating register values...\n");
387ed229454SKate Hsuan 
388ed229454SKate Hsuan 	ret = iosf_mbi_block_punit_i2c_access();
389ed229454SKate Hsuan 	if (ret < 0)
390ed229454SKate Hsuan 		return ret;
391ed229454SKate Hsuan 
392ed229454SKate Hsuan 	ret = axp288_charger_reg_readb(info, AXP20X_PWR_INPUT_STATUS, &info->input_status);
393ed229454SKate Hsuan 	if (ret < 0)
394ed229454SKate Hsuan 		goto out;
395ed229454SKate Hsuan 
396ed229454SKate Hsuan 	ret = axp288_charger_reg_readb(info, AXP20X_PWR_OP_MODE, &info->op_mode);
397ed229454SKate Hsuan 	if (ret < 0)
398ed229454SKate Hsuan 		goto out;
399ed229454SKate Hsuan 
400ed229454SKate Hsuan 	ret = axp288_charger_reg_readb(info, AXP20X_CHRG_BAK_CTRL, &info->backend_control);
401ed229454SKate Hsuan 	if (ret < 0)
402ed229454SKate Hsuan 		goto out;
403ed229454SKate Hsuan 
404ed229454SKate Hsuan 	info->last_updated = jiffies;
405ed229454SKate Hsuan 	info->valid = true;
406ed229454SKate Hsuan out:
407ed229454SKate Hsuan 	iosf_mbi_unblock_punit_i2c_access();
4088c0984e5SSebastian Reichel 	return ret;
4098c0984e5SSebastian Reichel }
4108c0984e5SSebastian Reichel 
axp288_charger_usb_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)4118c0984e5SSebastian Reichel static int axp288_charger_usb_get_property(struct power_supply *psy,
4128c0984e5SSebastian Reichel 				    enum power_supply_property psp,
4138c0984e5SSebastian Reichel 				    union power_supply_propval *val)
4148c0984e5SSebastian Reichel {
4158c0984e5SSebastian Reichel 	struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
416c28185bdSHans de Goede 	int ret;
4178c0984e5SSebastian Reichel 
418ed229454SKate Hsuan 	mutex_lock(&info->lock);
419ed229454SKate Hsuan 	ret = axp288_charger_usb_update_property(info);
420ed229454SKate Hsuan 	if (ret < 0)
421ed229454SKate Hsuan 		goto out;
422ed229454SKate Hsuan 
4238c0984e5SSebastian Reichel 	switch (psp) {
4248c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_PRESENT:
4258c0984e5SSebastian Reichel 		/* Check for OTG case first */
4268c0984e5SSebastian Reichel 		if (info->otg.id_short) {
4278c0984e5SSebastian Reichel 			val->intval = 0;
4288c0984e5SSebastian Reichel 			break;
4298c0984e5SSebastian Reichel 		}
4309052ff9bSHans de Goede 		val->intval = (info->input_status & PS_STAT_VBUS_PRESENT) ? 1 : 0;
4318c0984e5SSebastian Reichel 		break;
4328c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_ONLINE:
4338c0984e5SSebastian Reichel 		/* Check for OTG case first */
4348c0984e5SSebastian Reichel 		if (info->otg.id_short) {
4358c0984e5SSebastian Reichel 			val->intval = 0;
4368c0984e5SSebastian Reichel 			break;
4378c0984e5SSebastian Reichel 		}
4389052ff9bSHans de Goede 		val->intval = (info->input_status & PS_STAT_VBUS_VALID) ? 1 : 0;
4398c0984e5SSebastian Reichel 		break;
4408c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_HEALTH:
4418c0984e5SSebastian Reichel 		val->intval = axp288_get_charger_health(info);
4428c0984e5SSebastian Reichel 		break;
4438c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
4448c0984e5SSebastian Reichel 		val->intval = info->cc * 1000;
4458c0984e5SSebastian Reichel 		break;
4468c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
4478c0984e5SSebastian Reichel 		val->intval = info->max_cc * 1000;
4488c0984e5SSebastian Reichel 		break;
4498c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
4508c0984e5SSebastian Reichel 		val->intval = info->cv * 1000;
4518c0984e5SSebastian Reichel 		break;
4528c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
4538c0984e5SSebastian Reichel 		val->intval = info->max_cv * 1000;
4548c0984e5SSebastian Reichel 		break;
45581d56dd3SHans de Goede 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
456ed229454SKate Hsuan 		val->intval = axp288_charger_get_vbus_inlmt(info);
4578c0984e5SSebastian Reichel 		break;
4588c0984e5SSebastian Reichel 	default:
459ed229454SKate Hsuan 		ret = -EINVAL;
4608c0984e5SSebastian Reichel 	}
4618c0984e5SSebastian Reichel 
462ed229454SKate Hsuan out:
463ed229454SKate Hsuan 	mutex_unlock(&info->lock);
464ed229454SKate Hsuan 	return ret;
4658c0984e5SSebastian Reichel }
4668c0984e5SSebastian Reichel 
axp288_charger_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)4678c0984e5SSebastian Reichel static int axp288_charger_property_is_writeable(struct power_supply *psy,
4688c0984e5SSebastian Reichel 		enum power_supply_property psp)
4698c0984e5SSebastian Reichel {
4708c0984e5SSebastian Reichel 	int ret;
4718c0984e5SSebastian Reichel 
4728c0984e5SSebastian Reichel 	switch (psp) {
4738c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
4748c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
47581d56dd3SHans de Goede 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
4768c0984e5SSebastian Reichel 		ret = 1;
4778c0984e5SSebastian Reichel 		break;
4788c0984e5SSebastian Reichel 	default:
4798c0984e5SSebastian Reichel 		ret = 0;
4808c0984e5SSebastian Reichel 	}
4818c0984e5SSebastian Reichel 
4828c0984e5SSebastian Reichel 	return ret;
4838c0984e5SSebastian Reichel }
4848c0984e5SSebastian Reichel 
4858c0984e5SSebastian Reichel static enum power_supply_property axp288_usb_props[] = {
4868c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_PRESENT,
4878c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_ONLINE,
4888c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TYPE,
4898c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_HEALTH,
4908c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
4918c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
4928c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
4938c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
49481d56dd3SHans de Goede 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
4958c0984e5SSebastian Reichel };
4968c0984e5SSebastian Reichel 
4978c0984e5SSebastian Reichel static const struct power_supply_desc axp288_charger_desc = {
4988c0984e5SSebastian Reichel 	.name			= "axp288_charger",
4998c0984e5SSebastian Reichel 	.type			= POWER_SUPPLY_TYPE_USB,
5008c0984e5SSebastian Reichel 	.properties		= axp288_usb_props,
5018c0984e5SSebastian Reichel 	.num_properties		= ARRAY_SIZE(axp288_usb_props),
5028c0984e5SSebastian Reichel 	.get_property		= axp288_charger_usb_get_property,
5038c0984e5SSebastian Reichel 	.set_property		= axp288_charger_usb_set_property,
5048c0984e5SSebastian Reichel 	.property_is_writeable	= axp288_charger_property_is_writeable,
5058c0984e5SSebastian Reichel };
5068c0984e5SSebastian Reichel 
axp288_charger_irq_thread_handler(int irq,void * dev)5078c0984e5SSebastian Reichel static irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev)
5088c0984e5SSebastian Reichel {
5098c0984e5SSebastian Reichel 	struct axp288_chrg_info *info = dev;
5108c0984e5SSebastian Reichel 	int i;
5118c0984e5SSebastian Reichel 
5128c0984e5SSebastian Reichel 	for (i = 0; i < CHRG_INTR_END; i++) {
5138c0984e5SSebastian Reichel 		if (info->irq[i] == irq)
5148c0984e5SSebastian Reichel 			break;
5158c0984e5SSebastian Reichel 	}
5168c0984e5SSebastian Reichel 
5178c0984e5SSebastian Reichel 	if (i >= CHRG_INTR_END) {
5188c0984e5SSebastian Reichel 		dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
5198c0984e5SSebastian Reichel 		return IRQ_NONE;
5208c0984e5SSebastian Reichel 	}
5218c0984e5SSebastian Reichel 
5228c0984e5SSebastian Reichel 	switch (i) {
5238c0984e5SSebastian Reichel 	case VBUS_OV_IRQ:
5248c0984e5SSebastian Reichel 		dev_dbg(&info->pdev->dev, "VBUS Over Voltage INTR\n");
5258c0984e5SSebastian Reichel 		break;
5268c0984e5SSebastian Reichel 	case CHARGE_DONE_IRQ:
5278c0984e5SSebastian Reichel 		dev_dbg(&info->pdev->dev, "Charging Done INTR\n");
5288c0984e5SSebastian Reichel 		break;
5298c0984e5SSebastian Reichel 	case CHARGE_CHARGING_IRQ:
5308c0984e5SSebastian Reichel 		dev_dbg(&info->pdev->dev, "Start Charging IRQ\n");
5318c0984e5SSebastian Reichel 		break;
5328c0984e5SSebastian Reichel 	case BAT_SAFE_QUIT_IRQ:
5338c0984e5SSebastian Reichel 		dev_dbg(&info->pdev->dev,
5348c0984e5SSebastian Reichel 			"Quit Safe Mode(restart timer) Charging IRQ\n");
5358c0984e5SSebastian Reichel 		break;
5368c0984e5SSebastian Reichel 	case BAT_SAFE_ENTER_IRQ:
5378c0984e5SSebastian Reichel 		dev_dbg(&info->pdev->dev,
5388c0984e5SSebastian Reichel 			"Enter Safe Mode(timer expire) Charging IRQ\n");
5398c0984e5SSebastian Reichel 		break;
5408c0984e5SSebastian Reichel 	case QCBTU_IRQ:
5418c0984e5SSebastian Reichel 		dev_dbg(&info->pdev->dev,
5428c0984e5SSebastian Reichel 			"Quit Battery Under Temperature(CHRG) INTR\n");
5438c0984e5SSebastian Reichel 		break;
5448c0984e5SSebastian Reichel 	case CBTU_IRQ:
5458c0984e5SSebastian Reichel 		dev_dbg(&info->pdev->dev,
5468c0984e5SSebastian Reichel 			"Hit Battery Under Temperature(CHRG) INTR\n");
5478c0984e5SSebastian Reichel 		break;
5488c0984e5SSebastian Reichel 	case QCBTO_IRQ:
5498c0984e5SSebastian Reichel 		dev_dbg(&info->pdev->dev,
5508c0984e5SSebastian Reichel 			"Quit Battery Over Temperature(CHRG) INTR\n");
5518c0984e5SSebastian Reichel 		break;
5528c0984e5SSebastian Reichel 	case CBTO_IRQ:
5538c0984e5SSebastian Reichel 		dev_dbg(&info->pdev->dev,
5548c0984e5SSebastian Reichel 			"Hit Battery Over Temperature(CHRG) INTR\n");
5558c0984e5SSebastian Reichel 		break;
5568c0984e5SSebastian Reichel 	default:
5578c0984e5SSebastian Reichel 		dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
5588c0984e5SSebastian Reichel 		goto out;
5598c0984e5SSebastian Reichel 	}
560ed229454SKate Hsuan 	mutex_lock(&info->lock);
561ed229454SKate Hsuan 	info->valid = false;
562ed229454SKate Hsuan 	mutex_unlock(&info->lock);
5638c0984e5SSebastian Reichel 	power_supply_changed(info->psy_usb);
5648c0984e5SSebastian Reichel out:
5658c0984e5SSebastian Reichel 	return IRQ_HANDLED;
5668c0984e5SSebastian Reichel }
5678c0984e5SSebastian Reichel 
5689c80662aSHans de Goede /*
5699c80662aSHans de Goede  * The HP Pavilion x2 10 series comes in a number of variants:
570a0f1ccd9SHans de Goede  * Bay Trail SoC    + AXP288 PMIC, Micro-USB, DMI_BOARD_NAME: "8021"
571a0f1ccd9SHans de Goede  * Bay Trail SoC    + AXP288 PMIC, Type-C,    DMI_BOARD_NAME: "815D"
572a0f1ccd9SHans de Goede  * Cherry Trail SoC + AXP288 PMIC, Type-C,    DMI_BOARD_NAME: "813E"
573a0f1ccd9SHans de Goede  * Cherry Trail SoC + TI PMIC,     Type-C,    DMI_BOARD_NAME: "827C" or "82F4"
5749c80662aSHans de Goede  *
575a0f1ccd9SHans de Goede  * The variants with the AXP288 + Type-C connector are all kinds of special:
5769c80662aSHans de Goede  *
577a0f1ccd9SHans de Goede  * 1. They use a Type-C connector which the AXP288 does not support, so when
578a0f1ccd9SHans de Goede  * using a Type-C charger it is not recognized. Unlike most AXP288 devices,
5799c80662aSHans de Goede  * this model actually has mostly working ACPI AC / Battery code, the ACPI code
5809c80662aSHans de Goede  * "solves" this by simply setting the input_current_limit to 3A.
5819c80662aSHans de Goede  * There are still some issues with the ACPI code, so we use this native driver,
5829c80662aSHans de Goede  * and to solve the charging not working (500mA is not enough) issue we hardcode
5839c80662aSHans de Goede  * the 3A input_current_limit like the ACPI code does.
5849c80662aSHans de Goede  *
5859c80662aSHans de Goede  * 2. If no charger is connected the machine boots with the vbus-path disabled.
5869c80662aSHans de Goede  * Normally this is done when a 5V boost converter is active to avoid the PMIC
5879c80662aSHans de Goede  * trying to charge from the 5V boost converter's output. This is done when
5889c80662aSHans de Goede  * an OTG host cable is inserted and the ID pin on the micro-B receptacle is
5899c80662aSHans de Goede  * pulled low and the ID pin has an ACPI event handler associated with it
5909c80662aSHans de Goede  * which re-enables the vbus-path when the ID pin is pulled high when the
5919c80662aSHans de Goede  * OTG host cable is removed. The Type-C connector has no ID pin, there is
5929c80662aSHans de Goede  * no ID pin handler and there appears to be no 5V boost converter, so we
5939c80662aSHans de Goede  * end up not charging because the vbus-path is disabled, until we unplug
5949c80662aSHans de Goede  * the charger which automatically clears the vbus-path disable bit and then
5959c80662aSHans de Goede  * on the second plug-in of the adapter we start charging. To solve the not
5969c80662aSHans de Goede  * charging on first charger plugin we unconditionally enable the vbus-path at
5979c80662aSHans de Goede  * probe on this model, which is safe since there is no 5V boost converter.
5989c80662aSHans de Goede  */
5999c80662aSHans de Goede static const struct dmi_system_id axp288_hp_x2_dmi_ids[] = {
6009c80662aSHans de Goede 	{
6019c80662aSHans de Goede 		.matches = {
602a0f1ccd9SHans de Goede 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
603a0f1ccd9SHans de Goede 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
604a0f1ccd9SHans de Goede 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "815D"),
605a0f1ccd9SHans de Goede 		},
606a0f1ccd9SHans de Goede 	},
607a0f1ccd9SHans de Goede 	{
608a0f1ccd9SHans de Goede 		.matches = {
609a0f1ccd9SHans de Goede 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "HP"),
610a0f1ccd9SHans de Goede 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
611a0f1ccd9SHans de Goede 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "813E"),
6129c80662aSHans de Goede 		},
6139c80662aSHans de Goede 	},
6149c80662aSHans de Goede 	{} /* Terminating entry */
6159c80662aSHans de Goede };
6169c80662aSHans de Goede 
axp288_charger_extcon_evt_worker(struct work_struct * work)6178c0984e5SSebastian Reichel static void axp288_charger_extcon_evt_worker(struct work_struct *work)
6188c0984e5SSebastian Reichel {
6198c0984e5SSebastian Reichel 	struct axp288_chrg_info *info =
6208c0984e5SSebastian Reichel 	    container_of(work, struct axp288_chrg_info, cable.work);
6218c0984e5SSebastian Reichel 	int ret, current_limit;
6228c0984e5SSebastian Reichel 	struct extcon_dev *edev = info->cable.edev;
623d8e65195SHans de Goede 	unsigned int val;
624d8e65195SHans de Goede 
625d8e65195SHans de Goede 	ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
626d8e65195SHans de Goede 	if (ret < 0) {
627d8e65195SHans de Goede 		dev_err(&info->pdev->dev, "Error reading status (%d)\n", ret);
628d8e65195SHans de Goede 		return;
629d8e65195SHans de Goede 	}
630d8e65195SHans de Goede 
631d8e65195SHans de Goede 	/* Offline? Disable charging and bail */
632d8e65195SHans de Goede 	if (!(val & PS_STAT_VBUS_VALID)) {
633d8e65195SHans de Goede 		dev_dbg(&info->pdev->dev, "USB charger disconnected\n");
634d8e65195SHans de Goede 		axp288_charger_enable_charger(info, false);
635ed229454SKate Hsuan 		mutex_lock(&info->lock);
636ed229454SKate Hsuan 		info->valid = false;
637ed229454SKate Hsuan 		mutex_unlock(&info->lock);
638d8e65195SHans de Goede 		power_supply_changed(info->psy_usb);
639d8e65195SHans de Goede 		return;
640d8e65195SHans de Goede 	}
6418c0984e5SSebastian Reichel 
6428c0984e5SSebastian Reichel 	/* Determine cable/charger type */
6439c80662aSHans de Goede 	if (dmi_check_system(axp288_hp_x2_dmi_ids)) {
6449c80662aSHans de Goede 		/* See comment above axp288_hp_x2_dmi_ids declaration */
6459c80662aSHans de Goede 		dev_dbg(&info->pdev->dev, "HP X2 with Type-C, setting inlmt to 3A\n");
6469c80662aSHans de Goede 		current_limit = 3000000;
6479c80662aSHans de Goede 	} else if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
648a59943f8SHans de Goede 		dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n");
6499563d054SHans de Goede 		current_limit = 500000;
650c3148034SChanwoo Choi 	} else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
651a59943f8SHans de Goede 		dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n");
6529563d054SHans de Goede 		current_limit = 1500000;
653c3148034SChanwoo Choi 	} else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {
654a59943f8SHans de Goede 		dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n");
6559563d054SHans de Goede 		current_limit = 2000000;
6568c0984e5SSebastian Reichel 	} else {
657d8e65195SHans de Goede 		/* Charger type detection still in progress, bail. */
6588c0984e5SSebastian Reichel 		return;
659d8e65195SHans de Goede 	}
6608c0984e5SSebastian Reichel 
6618c0984e5SSebastian Reichel 	/* Set vbus current limit first, then enable charger */
6628c0984e5SSebastian Reichel 	ret = axp288_charger_set_vbus_inlmt(info, current_limit);
66371851a63SHans de Goede 	if (ret == 0)
66471851a63SHans de Goede 		axp288_charger_enable_charger(info, true);
66571851a63SHans de Goede 	else
6668c0984e5SSebastian Reichel 		dev_err(&info->pdev->dev,
667d8e65195SHans de Goede 			"error setting current limit (%d)\n", ret);
6688c0984e5SSebastian Reichel 
669ed229454SKate Hsuan 	mutex_lock(&info->lock);
670ed229454SKate Hsuan 	info->valid = false;
671ed229454SKate Hsuan 	mutex_unlock(&info->lock);
6728c0984e5SSebastian Reichel 	power_supply_changed(info->psy_usb);
6738c0984e5SSebastian Reichel }
6748c0984e5SSebastian Reichel 
axp288_charger_handle_cable_evt(struct notifier_block * nb,unsigned long event,void * param)6758c0a0a29SHans de Goede static int axp288_charger_handle_cable_evt(struct notifier_block *nb,
6768c0984e5SSebastian Reichel 					   unsigned long event, void *param)
6778c0984e5SSebastian Reichel {
6788c0984e5SSebastian Reichel 	struct axp288_chrg_info *info =
6798c0a0a29SHans de Goede 		container_of(nb, struct axp288_chrg_info, cable.nb);
680577b1f06SHans de Goede 	schedule_work(&info->cable.work);
6818c0984e5SSebastian Reichel 	return NOTIFY_OK;
6828c0984e5SSebastian Reichel }
6838c0984e5SSebastian Reichel 
axp288_charger_otg_evt_worker(struct work_struct * work)6848c0984e5SSebastian Reichel static void axp288_charger_otg_evt_worker(struct work_struct *work)
6858c0984e5SSebastian Reichel {
6868c0984e5SSebastian Reichel 	struct axp288_chrg_info *info =
6878c0984e5SSebastian Reichel 	    container_of(work, struct axp288_chrg_info, otg.work);
6885c5bcb8cSHans de Goede 	struct extcon_dev *edev = info->otg.cable;
6895c5bcb8cSHans de Goede 	int ret, usb_host = extcon_get_state(edev, EXTCON_USB_HOST);
6905c5bcb8cSHans de Goede 
6915c5bcb8cSHans de Goede 	dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
6925c5bcb8cSHans de Goede 				usb_host ? "attached" : "detached");
6935c5bcb8cSHans de Goede 
6945c5bcb8cSHans de Goede 	/*
6955c5bcb8cSHans de Goede 	 * Set usb_id_short flag to avoid running charger detection logic
6965c5bcb8cSHans de Goede 	 * in case usb host.
6975c5bcb8cSHans de Goede 	 */
6985c5bcb8cSHans de Goede 	info->otg.id_short = usb_host;
6998c0984e5SSebastian Reichel 
7008c0984e5SSebastian Reichel 	/* Disable VBUS path before enabling the 5V boost */
7018c0984e5SSebastian Reichel 	ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);
7028c0984e5SSebastian Reichel 	if (ret < 0)
7038c0984e5SSebastian Reichel 		dev_warn(&info->pdev->dev, "vbus path disable failed\n");
7048c0984e5SSebastian Reichel }
7058c0984e5SSebastian Reichel 
axp288_charger_handle_otg_evt(struct notifier_block * nb,unsigned long event,void * param)7068c0984e5SSebastian Reichel static int axp288_charger_handle_otg_evt(struct notifier_block *nb,
7078c0984e5SSebastian Reichel 				   unsigned long event, void *param)
7088c0984e5SSebastian Reichel {
7098c0984e5SSebastian Reichel 	struct axp288_chrg_info *info =
7108c0984e5SSebastian Reichel 	    container_of(nb, struct axp288_chrg_info, otg.id_nb);
7118c0984e5SSebastian Reichel 
7128c0984e5SSebastian Reichel 	schedule_work(&info->otg.work);
7138c0984e5SSebastian Reichel 
7148c0984e5SSebastian Reichel 	return NOTIFY_OK;
7158c0984e5SSebastian Reichel }
7168c0984e5SSebastian Reichel 
charger_init_hw_regs(struct axp288_chrg_info * info)717d556f21cSHans de Goede static int charger_init_hw_regs(struct axp288_chrg_info *info)
7188c0984e5SSebastian Reichel {
7198c0984e5SSebastian Reichel 	int ret, cc, cv;
7208c0984e5SSebastian Reichel 	unsigned int val;
7218c0984e5SSebastian Reichel 
7228c0984e5SSebastian Reichel 	/* Program temperature thresholds */
7238c0984e5SSebastian Reichel 	ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
724d556f21cSHans de Goede 	if (ret < 0) {
725d556f21cSHans de Goede 		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
7268c0984e5SSebastian Reichel 							AXP20X_V_LTF_CHRG, ret);
727d556f21cSHans de Goede 		return ret;
728d556f21cSHans de Goede 	}
7298c0984e5SSebastian Reichel 
7308c0984e5SSebastian Reichel 	ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
731d556f21cSHans de Goede 	if (ret < 0) {
732d556f21cSHans de Goede 		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
7338c0984e5SSebastian Reichel 							AXP20X_V_HTF_CHRG, ret);
734d556f21cSHans de Goede 		return ret;
735d556f21cSHans de Goede 	}
7368c0984e5SSebastian Reichel 
7378c0984e5SSebastian Reichel 	/* Do not turn-off charger o/p after charge cycle ends */
7388c0984e5SSebastian Reichel 	ret = regmap_update_bits(info->regmap,
7398c0984e5SSebastian Reichel 				AXP20X_CHRG_CTRL2,
7408cffbe47SHans de Goede 				CNTL2_CHG_OUT_TURNON, CNTL2_CHG_OUT_TURNON);
741d556f21cSHans de Goede 	if (ret < 0) {
742d556f21cSHans de Goede 		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
7438c0984e5SSebastian Reichel 						AXP20X_CHRG_CTRL2, ret);
744d556f21cSHans de Goede 		return ret;
745d556f21cSHans de Goede 	}
7468c0984e5SSebastian Reichel 
7478c0984e5SSebastian Reichel 	/* Setup ending condition for charging to be 10% of I(chrg) */
7488c0984e5SSebastian Reichel 	ret = regmap_update_bits(info->regmap,
7498c0984e5SSebastian Reichel 				AXP20X_CHRG_CTRL1,
7508c0984e5SSebastian Reichel 				CHRG_CCCV_ITERM_20P, 0);
751d556f21cSHans de Goede 	if (ret < 0) {
752d556f21cSHans de Goede 		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
7538c0984e5SSebastian Reichel 						AXP20X_CHRG_CTRL1, ret);
754d556f21cSHans de Goede 		return ret;
755d556f21cSHans de Goede 	}
7568c0984e5SSebastian Reichel 
7578c0984e5SSebastian Reichel 	/* Disable OCV-SOC curve calibration */
7588c0984e5SSebastian Reichel 	ret = regmap_update_bits(info->regmap,
7598c0984e5SSebastian Reichel 				AXP20X_CC_CTRL,
7608c0984e5SSebastian Reichel 				FG_CNTL_OCV_ADJ_EN, 0);
761d556f21cSHans de Goede 	if (ret < 0) {
762d556f21cSHans de Goede 		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
7638c0984e5SSebastian Reichel 						AXP20X_CC_CTRL, ret);
764d556f21cSHans de Goede 		return ret;
765d556f21cSHans de Goede 	}
7668c0984e5SSebastian Reichel 
7679c80662aSHans de Goede 	if (dmi_check_system(axp288_hp_x2_dmi_ids)) {
7689c80662aSHans de Goede 		/* See comment above axp288_hp_x2_dmi_ids declaration */
7699c80662aSHans de Goede 		ret = axp288_charger_vbus_path_select(info, true);
7709c80662aSHans de Goede 		if (ret < 0)
7719c80662aSHans de Goede 			return ret;
7725ac121b8SHans de Goede 	} else {
7735ac121b8SHans de Goede 		/* Set Vhold to the factory default / recommended 4.4V */
7745ac121b8SHans de Goede 		val = VBUS_ISPOUT_VHOLD_SET_4400MV << VBUS_ISPOUT_VHOLD_SET_BIT_POS;
7755ac121b8SHans de Goede 		ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
7765ac121b8SHans de Goede 					 VBUS_ISPOUT_VHOLD_SET_MASK, val);
7775ac121b8SHans de Goede 		if (ret < 0) {
7785ac121b8SHans de Goede 			dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
7795ac121b8SHans de Goede 				AXP20X_VBUS_IPSOUT_MGMT, ret);
7805ac121b8SHans de Goede 			return ret;
7815ac121b8SHans de Goede 		}
7829c80662aSHans de Goede 	}
7839c80662aSHans de Goede 
7848c0984e5SSebastian Reichel 	/* Read current charge voltage and current limit */
7858c0984e5SSebastian Reichel 	ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
7868c0984e5SSebastian Reichel 	if (ret < 0) {
787eac53b36SHans de Goede 		dev_err(&info->pdev->dev, "register(%x) read error(%d)\n",
788eac53b36SHans de Goede 			AXP20X_CHRG_CTRL1, ret);
789eac53b36SHans de Goede 		return ret;
790eac53b36SHans de Goede 	}
791eac53b36SHans de Goede 
7928c0984e5SSebastian Reichel 	/* Determine charge voltage */
7938c0984e5SSebastian Reichel 	cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
7948c0984e5SSebastian Reichel 	switch (cv) {
7958c0984e5SSebastian Reichel 	case CHRG_CCCV_CV_4100MV:
7968c0984e5SSebastian Reichel 		info->cv = CV_4100MV;
7978c0984e5SSebastian Reichel 		break;
7988c0984e5SSebastian Reichel 	case CHRG_CCCV_CV_4150MV:
7998c0984e5SSebastian Reichel 		info->cv = CV_4150MV;
8008c0984e5SSebastian Reichel 		break;
8018c0984e5SSebastian Reichel 	case CHRG_CCCV_CV_4200MV:
8028c0984e5SSebastian Reichel 		info->cv = CV_4200MV;
8038c0984e5SSebastian Reichel 		break;
8048c0984e5SSebastian Reichel 	case CHRG_CCCV_CV_4350MV:
8058c0984e5SSebastian Reichel 		info->cv = CV_4350MV;
8068c0984e5SSebastian Reichel 		break;
8078c0984e5SSebastian Reichel 	}
8088c0984e5SSebastian Reichel 
8098c0984e5SSebastian Reichel 	/* Determine charge current limit */
810f2a42595SHans de Goede 	cc = (val & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
8118c0984e5SSebastian Reichel 	cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
8128c0984e5SSebastian Reichel 	info->cc = cc;
8138c0984e5SSebastian Reichel 
814eac53b36SHans de Goede 	/*
815eac53b36SHans de Goede 	 * Do not allow the user to configure higher settings then those
816eac53b36SHans de Goede 	 * set by the firmware
817eac53b36SHans de Goede 	 */
818eac53b36SHans de Goede 	info->max_cv = info->cv;
819eac53b36SHans de Goede 	info->max_cc = info->cc;
8208c0984e5SSebastian Reichel 
821d556f21cSHans de Goede 	return 0;
8228c0984e5SSebastian Reichel }
8238c0984e5SSebastian Reichel 
axp288_charger_cancel_work(void * data)824165c2357SHans de Goede static void axp288_charger_cancel_work(void *data)
825165c2357SHans de Goede {
826165c2357SHans de Goede 	struct axp288_chrg_info *info = data;
827165c2357SHans de Goede 
828165c2357SHans de Goede 	cancel_work_sync(&info->otg.work);
829165c2357SHans de Goede 	cancel_work_sync(&info->cable.work);
830165c2357SHans de Goede }
831165c2357SHans de Goede 
axp288_charger_probe(struct platform_device * pdev)8328c0984e5SSebastian Reichel static int axp288_charger_probe(struct platform_device *pdev)
8338c0984e5SSebastian Reichel {
8348c0984e5SSebastian Reichel 	int ret, i, pirq;
8358c0984e5SSebastian Reichel 	struct axp288_chrg_info *info;
836e3668e37SHans de Goede 	struct device *dev = &pdev->dev;
8378c0984e5SSebastian Reichel 	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
8388c0984e5SSebastian Reichel 	struct power_supply_config charger_cfg = {};
839*ce38f3fcSHans de Goede 	const char *extcon_name = NULL;
840efb440ecSHans de Goede 	unsigned int val;
841efb440ecSHans de Goede 
842efb440ecSHans de Goede 	/*
84300d05666SHans de Goede 	 * Normally the native AXP288 fg/charger drivers are preferred but
84400d05666SHans de Goede 	 * on some devices the ACPI drivers should be used instead.
84500d05666SHans de Goede 	 */
84600d05666SHans de Goede 	if (!acpi_quirk_skip_acpi_ac_and_battery())
84700d05666SHans de Goede 		return -ENODEV;
84800d05666SHans de Goede 
84900d05666SHans de Goede 	/*
850efb440ecSHans de Goede 	 * On some devices the fuelgauge and charger parts of the axp288 are
851efb440ecSHans de Goede 	 * not used, check that the fuelgauge is enabled (CC_CTRL != 0).
852efb440ecSHans de Goede 	 */
853efb440ecSHans de Goede 	ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val);
854efb440ecSHans de Goede 	if (ret < 0)
855efb440ecSHans de Goede 		return ret;
856efb440ecSHans de Goede 	if (val == 0)
857efb440ecSHans de Goede 		return -ENODEV;
858efb440ecSHans de Goede 
859c02aa89bSTang Bin 	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
8608c0984e5SSebastian Reichel 	if (!info)
8618c0984e5SSebastian Reichel 		return -ENOMEM;
8628c0984e5SSebastian Reichel 
863db6e4362SWei Yongjun 	mutex_init(&info->lock);
8648c0984e5SSebastian Reichel 	info->pdev = pdev;
8658c0984e5SSebastian Reichel 	info->regmap = axp20x->regmap;
8668c0984e5SSebastian Reichel 	info->regmap_irqc = axp20x->regmap_irqc;
8678c0984e5SSebastian Reichel 
8688c0984e5SSebastian Reichel 	info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
86958e4a2d2SDan Carpenter 	if (IS_ERR(info->cable.edev)) {
87058e4a2d2SDan Carpenter 		dev_err_probe(dev, PTR_ERR(info->cable.edev),
87158e4a2d2SDan Carpenter 			      "extcon_get_extcon_dev(%s) failed\n",
8728c0984e5SSebastian Reichel 			      AXP288_EXTCON_DEV_NAME);
87358e4a2d2SDan Carpenter 		return PTR_ERR(info->cable.edev);
8748c0984e5SSebastian Reichel 	}
8758c0984e5SSebastian Reichel 
876*ce38f3fcSHans de Goede 	/*
877*ce38f3fcSHans de Goede 	 * On devices with broken ACPI GPIO event handlers there also is no ACPI
878*ce38f3fcSHans de Goede 	 * "INT3496" (USB_HOST_EXTCON_HID) device. x86-android-tablets.ko
879*ce38f3fcSHans de Goede 	 * instantiates an "intel-int3496" extcon on these devs as a workaround.
880*ce38f3fcSHans de Goede 	 */
881*ce38f3fcSHans de Goede 	if (acpi_quirk_skip_gpio_event_handlers())
882*ce38f3fcSHans de Goede 		extcon_name = "intel-int3496";
883*ce38f3fcSHans de Goede 	else if (acpi_dev_present(USB_HOST_EXTCON_HID, NULL, -1))
884*ce38f3fcSHans de Goede 		extcon_name = USB_HOST_EXTCON_NAME;
885*ce38f3fcSHans de Goede 
886*ce38f3fcSHans de Goede 	if (extcon_name) {
887*ce38f3fcSHans de Goede 		info->otg.cable = extcon_get_extcon_dev(extcon_name);
88858e4a2d2SDan Carpenter 		if (IS_ERR(info->otg.cable)) {
88958e4a2d2SDan Carpenter 			dev_err_probe(dev, PTR_ERR(info->otg.cable),
89058e4a2d2SDan Carpenter 				      "extcon_get_extcon_dev(%s) failed\n",
89158e4a2d2SDan Carpenter 				      USB_HOST_EXTCON_NAME);
89258e4a2d2SDan Carpenter 			return PTR_ERR(info->otg.cable);
8937508f441SHans de Goede 		}
894c02aa89bSTang Bin 		dev_info(dev, "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n");
8956c4c9a9aSHans de Goede 	}
8967508f441SHans de Goede 
8978c0984e5SSebastian Reichel 	platform_set_drvdata(pdev, info);
8988c0984e5SSebastian Reichel 
899d96e0735SHans de Goede 	ret = charger_init_hw_regs(info);
900d96e0735SHans de Goede 	if (ret)
901d96e0735SHans de Goede 		return ret;
902d96e0735SHans de Goede 
9038c0984e5SSebastian Reichel 	/* Register with power supply class */
9048c0984e5SSebastian Reichel 	charger_cfg.drv_data = info;
905e3668e37SHans de Goede 	info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc,
9068c0984e5SSebastian Reichel 						   &charger_cfg);
9078c0984e5SSebastian Reichel 	if (IS_ERR(info->psy_usb)) {
9088c0984e5SSebastian Reichel 		ret = PTR_ERR(info->psy_usb);
909e3668e37SHans de Goede 		dev_err(dev, "failed to register power supply: %d\n", ret);
910e3668e37SHans de Goede 		return ret;
9118c0984e5SSebastian Reichel 	}
9128c0984e5SSebastian Reichel 
913165c2357SHans de Goede 	/* Cancel our work on cleanup, register this before the notifiers */
914165c2357SHans de Goede 	ret = devm_add_action(dev, axp288_charger_cancel_work, info);
915165c2357SHans de Goede 	if (ret)
916165c2357SHans de Goede 		return ret;
917165c2357SHans de Goede 
91842e2008aSHans de Goede 	/* Register for extcon notification */
91942e2008aSHans de Goede 	INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
9208c0a0a29SHans de Goede 	info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
9218c0a0a29SHans de Goede 	ret = devm_extcon_register_notifier_all(dev, info->cable.edev,
9228c0a0a29SHans de Goede 						&info->cable.nb);
92342e2008aSHans de Goede 	if (ret) {
9248c0a0a29SHans de Goede 		dev_err(dev, "failed to register cable extcon notifier\n");
92542e2008aSHans de Goede 		return ret;
92642e2008aSHans de Goede 	}
9275c5bcb8cSHans de Goede 	schedule_work(&info->cable.work);
92842e2008aSHans de Goede 
9298c0984e5SSebastian Reichel 	/* Register for OTG notification */
9308c0984e5SSebastian Reichel 	INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
9318c0984e5SSebastian Reichel 	info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
9326c4c9a9aSHans de Goede 	if (info->otg.cable) {
933c02aa89bSTang Bin 		ret = devm_extcon_register_notifier(dev, info->otg.cable,
934c3148034SChanwoo Choi 					EXTCON_USB_HOST, &info->otg.id_nb);
9357508f441SHans de Goede 		if (ret) {
9367508f441SHans de Goede 			dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n");
9377508f441SHans de Goede 			return ret;
9387508f441SHans de Goede 		}
9395c5bcb8cSHans de Goede 		schedule_work(&info->otg.work);
9406c4c9a9aSHans de Goede 	}
9418c0984e5SSebastian Reichel 
9428c0984e5SSebastian Reichel 	/* Register charger interrupts */
9438c0984e5SSebastian Reichel 	for (i = 0; i < CHRG_INTR_END; i++) {
9448c0984e5SSebastian Reichel 		pirq = platform_get_irq(info->pdev, i);
945aa86e907STang Bin 		if (pirq < 0)
946c3422ad5SGustavo A. R. Silva 			return pirq;
947aa86e907STang Bin 
9488c0984e5SSebastian Reichel 		info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
9498c0984e5SSebastian Reichel 		if (info->irq[i] < 0) {
9508c0984e5SSebastian Reichel 			dev_warn(&info->pdev->dev,
9518c0984e5SSebastian Reichel 				"failed to get virtual interrupt=%d\n", pirq);
952e3668e37SHans de Goede 			return info->irq[i];
9538c0984e5SSebastian Reichel 		}
9548c0984e5SSebastian Reichel 		ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
9558c0984e5SSebastian Reichel 					NULL, axp288_charger_irq_thread_handler,
9568c0984e5SSebastian Reichel 					IRQF_ONESHOT, info->pdev->name, info);
9578c0984e5SSebastian Reichel 		if (ret) {
958c02aa89bSTang Bin 			dev_err(dev, "failed to request interrupt=%d\n",
9598c0984e5SSebastian Reichel 								info->irq[i]);
960e3668e37SHans de Goede 			return ret;
9618c0984e5SSebastian Reichel 		}
9628c0984e5SSebastian Reichel 	}
9638c0984e5SSebastian Reichel 
9648c0984e5SSebastian Reichel 	return 0;
9658c0984e5SSebastian Reichel }
9668c0984e5SSebastian Reichel 
9677def63caSHans de Goede static const struct platform_device_id axp288_charger_id_table[] = {
9687def63caSHans de Goede 	{ .name = "axp288_charger" },
9697def63caSHans de Goede 	{},
9707def63caSHans de Goede };
9717def63caSHans de Goede MODULE_DEVICE_TABLE(platform, axp288_charger_id_table);
9727def63caSHans de Goede 
9738c0984e5SSebastian Reichel static struct platform_driver axp288_charger_driver = {
9748c0984e5SSebastian Reichel 	.probe = axp288_charger_probe,
9757def63caSHans de Goede 	.id_table = axp288_charger_id_table,
9768c0984e5SSebastian Reichel 	.driver = {
9778c0984e5SSebastian Reichel 		.name = "axp288_charger",
9788c0984e5SSebastian Reichel 	},
9798c0984e5SSebastian Reichel };
9808c0984e5SSebastian Reichel 
9818c0984e5SSebastian Reichel module_platform_driver(axp288_charger_driver);
9828c0984e5SSebastian Reichel 
9838c0984e5SSebastian Reichel MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
9848c0984e5SSebastian Reichel MODULE_DESCRIPTION("X-power AXP288 Charger Driver");
9858c0984e5SSebastian Reichel MODULE_LICENSE("GPL v2");
986