18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * axp288_charger.c - X-power AXP288 PMIC Charger driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com>
68c2ecf20Sopenharmony_ci * Copyright (C) 2014 Intel Corporation
78c2ecf20Sopenharmony_ci * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/acpi.h>
118c2ecf20Sopenharmony_ci#include <linux/bitops.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/device.h>
148c2ecf20Sopenharmony_ci#include <linux/regmap.h>
158c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/usb/otg.h>
198c2ecf20Sopenharmony_ci#include <linux/notifier.h>
208c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
218c2ecf20Sopenharmony_ci#include <linux/property.h>
228c2ecf20Sopenharmony_ci#include <linux/mfd/axp20x.h>
238c2ecf20Sopenharmony_ci#include <linux/extcon.h>
248c2ecf20Sopenharmony_ci#include <linux/dmi.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define PS_STAT_VBUS_TRIGGER		BIT(0)
278c2ecf20Sopenharmony_ci#define PS_STAT_BAT_CHRG_DIR		BIT(2)
288c2ecf20Sopenharmony_ci#define PS_STAT_VBAT_ABOVE_VHOLD	BIT(3)
298c2ecf20Sopenharmony_ci#define PS_STAT_VBUS_VALID		BIT(4)
308c2ecf20Sopenharmony_ci#define PS_STAT_VBUS_PRESENT		BIT(5)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define CHRG_STAT_BAT_SAFE_MODE		BIT(3)
338c2ecf20Sopenharmony_ci#define CHRG_STAT_BAT_VALID		BIT(4)
348c2ecf20Sopenharmony_ci#define CHRG_STAT_BAT_PRESENT		BIT(5)
358c2ecf20Sopenharmony_ci#define CHRG_STAT_CHARGING		BIT(6)
368c2ecf20Sopenharmony_ci#define CHRG_STAT_PMIC_OTP		BIT(7)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_MASK	0x03
398c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_BIT_POS	0
408c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_900MA	0x0	/* 900mA */
418c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_1500MA	0x1	/* 1500mA */
428c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_2000MA	0x2	/* 2000mA */
438c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_CUR_NO_LIM		0x3	/* 2500mA */
448c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_MASK	0x38
458c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_BIT_POS	0x3
468c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_OFFSET	4000	/* 4000mV */
478c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_LSB_RES	100	/* 100mV */
488c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_4400MV	0x4	/* 4400mV */
498c2ecf20Sopenharmony_ci#define VBUS_ISPOUT_VBUS_PATH_DIS	BIT(7)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define CHRG_CCCV_CC_MASK		0xf		/* 4 bits */
528c2ecf20Sopenharmony_ci#define CHRG_CCCV_CC_BIT_POS		0
538c2ecf20Sopenharmony_ci#define CHRG_CCCV_CC_OFFSET		200		/* 200mA */
548c2ecf20Sopenharmony_ci#define CHRG_CCCV_CC_LSB_RES		200		/* 200mA */
558c2ecf20Sopenharmony_ci#define CHRG_CCCV_ITERM_20P		BIT(4)		/* 20% of CC */
568c2ecf20Sopenharmony_ci#define CHRG_CCCV_CV_MASK		0x60		/* 2 bits */
578c2ecf20Sopenharmony_ci#define CHRG_CCCV_CV_BIT_POS		5
588c2ecf20Sopenharmony_ci#define CHRG_CCCV_CV_4100MV		0x0		/* 4.10V */
598c2ecf20Sopenharmony_ci#define CHRG_CCCV_CV_4150MV		0x1		/* 4.15V */
608c2ecf20Sopenharmony_ci#define CHRG_CCCV_CV_4200MV		0x2		/* 4.20V */
618c2ecf20Sopenharmony_ci#define CHRG_CCCV_CV_4350MV		0x3		/* 4.35V */
628c2ecf20Sopenharmony_ci#define CHRG_CCCV_CHG_EN		BIT(7)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define CNTL2_CC_TIMEOUT_MASK		0x3	/* 2 bits */
658c2ecf20Sopenharmony_ci#define CNTL2_CC_TIMEOUT_OFFSET		6	/* 6 Hrs */
668c2ecf20Sopenharmony_ci#define CNTL2_CC_TIMEOUT_LSB_RES	2	/* 2 Hrs */
678c2ecf20Sopenharmony_ci#define CNTL2_CC_TIMEOUT_12HRS		0x3	/* 12 Hrs */
688c2ecf20Sopenharmony_ci#define CNTL2_CHGLED_TYPEB		BIT(4)
698c2ecf20Sopenharmony_ci#define CNTL2_CHG_OUT_TURNON		BIT(5)
708c2ecf20Sopenharmony_ci#define CNTL2_PC_TIMEOUT_MASK		0xC0
718c2ecf20Sopenharmony_ci#define CNTL2_PC_TIMEOUT_OFFSET		40	/* 40 mins */
728c2ecf20Sopenharmony_ci#define CNTL2_PC_TIMEOUT_LSB_RES	10	/* 10 mins */
738c2ecf20Sopenharmony_ci#define CNTL2_PC_TIMEOUT_70MINS		0x3
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define CHRG_ILIM_TEMP_LOOP_EN		BIT(3)
768c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_MASK		0xf0
778c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_BIT_POS		4
788c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_100MA		0x0	/* 100mA */
798c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_500MA		0x1	/* 500mA */
808c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_900MA		0x2	/* 900mA */
818c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_1500MA		0x3	/* 1500mA */
828c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_2000MA		0x4	/* 2000mA */
838c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_2500MA		0x5	/* 2500mA */
848c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_3000MA		0x6	/* 3000mA */
858c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_3500MA		0x7	/* 3500mA */
868c2ecf20Sopenharmony_ci#define CHRG_VBUS_ILIM_4000MA		0x8	/* 4000mA */
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci#define CHRG_VLTFC_0C			0xA5	/* 0 DegC */
898c2ecf20Sopenharmony_ci#define CHRG_VHTFC_45C			0x1F	/* 45 DegC */
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#define FG_CNTL_OCV_ADJ_EN		BIT(3)
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define CV_4100MV			4100	/* 4100mV */
948c2ecf20Sopenharmony_ci#define CV_4150MV			4150	/* 4150mV */
958c2ecf20Sopenharmony_ci#define CV_4200MV			4200	/* 4200mV */
968c2ecf20Sopenharmony_ci#define CV_4350MV			4350	/* 4350mV */
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define AXP288_EXTCON_DEV_NAME		"axp288_extcon"
998c2ecf20Sopenharmony_ci#define USB_HOST_EXTCON_HID		"INT3496"
1008c2ecf20Sopenharmony_ci#define USB_HOST_EXTCON_NAME		"INT3496:00"
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cienum {
1038c2ecf20Sopenharmony_ci	VBUS_OV_IRQ = 0,
1048c2ecf20Sopenharmony_ci	CHARGE_DONE_IRQ,
1058c2ecf20Sopenharmony_ci	CHARGE_CHARGING_IRQ,
1068c2ecf20Sopenharmony_ci	BAT_SAFE_QUIT_IRQ,
1078c2ecf20Sopenharmony_ci	BAT_SAFE_ENTER_IRQ,
1088c2ecf20Sopenharmony_ci	QCBTU_IRQ,
1098c2ecf20Sopenharmony_ci	CBTU_IRQ,
1108c2ecf20Sopenharmony_ci	QCBTO_IRQ,
1118c2ecf20Sopenharmony_ci	CBTO_IRQ,
1128c2ecf20Sopenharmony_ci	CHRG_INTR_END,
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistruct axp288_chrg_info {
1168c2ecf20Sopenharmony_ci	struct platform_device *pdev;
1178c2ecf20Sopenharmony_ci	struct regmap *regmap;
1188c2ecf20Sopenharmony_ci	struct regmap_irq_chip_data *regmap_irqc;
1198c2ecf20Sopenharmony_ci	int irq[CHRG_INTR_END];
1208c2ecf20Sopenharmony_ci	struct power_supply *psy_usb;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/* OTG/Host mode */
1238c2ecf20Sopenharmony_ci	struct {
1248c2ecf20Sopenharmony_ci		struct work_struct work;
1258c2ecf20Sopenharmony_ci		struct extcon_dev *cable;
1268c2ecf20Sopenharmony_ci		struct notifier_block id_nb;
1278c2ecf20Sopenharmony_ci		bool id_short;
1288c2ecf20Sopenharmony_ci	} otg;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* SDP/CDP/DCP USB charging cable notifications */
1318c2ecf20Sopenharmony_ci	struct {
1328c2ecf20Sopenharmony_ci		struct extcon_dev *edev;
1338c2ecf20Sopenharmony_ci		struct notifier_block nb;
1348c2ecf20Sopenharmony_ci		struct work_struct work;
1358c2ecf20Sopenharmony_ci	} cable;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	int cc;
1388c2ecf20Sopenharmony_ci	int cv;
1398c2ecf20Sopenharmony_ci	int max_cc;
1408c2ecf20Sopenharmony_ci	int max_cv;
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	u8 reg_val;
1468c2ecf20Sopenharmony_ci	int ret;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (cc < CHRG_CCCV_CC_OFFSET)
1498c2ecf20Sopenharmony_ci		cc = CHRG_CCCV_CC_OFFSET;
1508c2ecf20Sopenharmony_ci	else if (cc > info->max_cc)
1518c2ecf20Sopenharmony_ci		cc = info->max_cc;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	reg_val = (cc - CHRG_CCCV_CC_OFFSET) / CHRG_CCCV_CC_LSB_RES;
1548c2ecf20Sopenharmony_ci	cc = (reg_val * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
1558c2ecf20Sopenharmony_ci	reg_val = reg_val << CHRG_CCCV_CC_BIT_POS;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	ret = regmap_update_bits(info->regmap,
1588c2ecf20Sopenharmony_ci				AXP20X_CHRG_CTRL1,
1598c2ecf20Sopenharmony_ci				CHRG_CCCV_CC_MASK, reg_val);
1608c2ecf20Sopenharmony_ci	if (ret >= 0)
1618c2ecf20Sopenharmony_ci		info->cc = cc;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return ret;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	u8 reg_val;
1698c2ecf20Sopenharmony_ci	int ret;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (cv <= CV_4100MV) {
1728c2ecf20Sopenharmony_ci		reg_val = CHRG_CCCV_CV_4100MV;
1738c2ecf20Sopenharmony_ci		cv = CV_4100MV;
1748c2ecf20Sopenharmony_ci	} else if (cv <= CV_4150MV) {
1758c2ecf20Sopenharmony_ci		reg_val = CHRG_CCCV_CV_4150MV;
1768c2ecf20Sopenharmony_ci		cv = CV_4150MV;
1778c2ecf20Sopenharmony_ci	} else if (cv <= CV_4200MV) {
1788c2ecf20Sopenharmony_ci		reg_val = CHRG_CCCV_CV_4200MV;
1798c2ecf20Sopenharmony_ci		cv = CV_4200MV;
1808c2ecf20Sopenharmony_ci	} else {
1818c2ecf20Sopenharmony_ci		reg_val = CHRG_CCCV_CV_4350MV;
1828c2ecf20Sopenharmony_ci		cv = CV_4350MV;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	reg_val = reg_val << CHRG_CCCV_CV_BIT_POS;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	ret = regmap_update_bits(info->regmap,
1888c2ecf20Sopenharmony_ci				AXP20X_CHRG_CTRL1,
1898c2ecf20Sopenharmony_ci				CHRG_CCCV_CV_MASK, reg_val);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (ret >= 0)
1928c2ecf20Sopenharmony_ci		info->cv = cv;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return ret;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int axp288_charger_get_vbus_inlmt(struct axp288_chrg_info *info)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	unsigned int val;
2008c2ecf20Sopenharmony_ci	int ret;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val);
2038c2ecf20Sopenharmony_ci	if (ret < 0)
2048c2ecf20Sopenharmony_ci		return ret;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	val >>= CHRG_VBUS_ILIM_BIT_POS;
2078c2ecf20Sopenharmony_ci	switch (val) {
2088c2ecf20Sopenharmony_ci	case CHRG_VBUS_ILIM_100MA:
2098c2ecf20Sopenharmony_ci		return 100000;
2108c2ecf20Sopenharmony_ci	case CHRG_VBUS_ILIM_500MA:
2118c2ecf20Sopenharmony_ci		return 500000;
2128c2ecf20Sopenharmony_ci	case CHRG_VBUS_ILIM_900MA:
2138c2ecf20Sopenharmony_ci		return 900000;
2148c2ecf20Sopenharmony_ci	case CHRG_VBUS_ILIM_1500MA:
2158c2ecf20Sopenharmony_ci		return 1500000;
2168c2ecf20Sopenharmony_ci	case CHRG_VBUS_ILIM_2000MA:
2178c2ecf20Sopenharmony_ci		return 2000000;
2188c2ecf20Sopenharmony_ci	case CHRG_VBUS_ILIM_2500MA:
2198c2ecf20Sopenharmony_ci		return 2500000;
2208c2ecf20Sopenharmony_ci	case CHRG_VBUS_ILIM_3000MA:
2218c2ecf20Sopenharmony_ci		return 3000000;
2228c2ecf20Sopenharmony_ci	case CHRG_VBUS_ILIM_3500MA:
2238c2ecf20Sopenharmony_ci		return 3500000;
2248c2ecf20Sopenharmony_ci	default:
2258c2ecf20Sopenharmony_ci		/* All b1xxx values map to 4000 mA */
2268c2ecf20Sopenharmony_ci		return 4000000;
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,
2318c2ecf20Sopenharmony_ci					   int inlmt)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	int ret;
2348c2ecf20Sopenharmony_ci	u8 reg_val;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (inlmt >= 4000000)
2378c2ecf20Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_4000MA << CHRG_VBUS_ILIM_BIT_POS;
2388c2ecf20Sopenharmony_ci	else if (inlmt >= 3500000)
2398c2ecf20Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_3500MA << CHRG_VBUS_ILIM_BIT_POS;
2408c2ecf20Sopenharmony_ci	else if (inlmt >= 3000000)
2418c2ecf20Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_3000MA << CHRG_VBUS_ILIM_BIT_POS;
2428c2ecf20Sopenharmony_ci	else if (inlmt >= 2500000)
2438c2ecf20Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_2500MA << CHRG_VBUS_ILIM_BIT_POS;
2448c2ecf20Sopenharmony_ci	else if (inlmt >= 2000000)
2458c2ecf20Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_2000MA << CHRG_VBUS_ILIM_BIT_POS;
2468c2ecf20Sopenharmony_ci	else if (inlmt >= 1500000)
2478c2ecf20Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_1500MA << CHRG_VBUS_ILIM_BIT_POS;
2488c2ecf20Sopenharmony_ci	else if (inlmt >= 900000)
2498c2ecf20Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_900MA << CHRG_VBUS_ILIM_BIT_POS;
2508c2ecf20Sopenharmony_ci	else if (inlmt >= 500000)
2518c2ecf20Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_500MA << CHRG_VBUS_ILIM_BIT_POS;
2528c2ecf20Sopenharmony_ci	else
2538c2ecf20Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_100MA << CHRG_VBUS_ILIM_BIT_POS;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL,
2568c2ecf20Sopenharmony_ci				 CHRG_VBUS_ILIM_MASK, reg_val);
2578c2ecf20Sopenharmony_ci	if (ret < 0)
2588c2ecf20Sopenharmony_ci		dev_err(&info->pdev->dev, "charger BAK control %d\n", ret);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return ret;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic int axp288_charger_vbus_path_select(struct axp288_chrg_info *info,
2648c2ecf20Sopenharmony_ci								bool enable)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	int ret;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (enable)
2698c2ecf20Sopenharmony_ci		ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
2708c2ecf20Sopenharmony_ci					VBUS_ISPOUT_VBUS_PATH_DIS, 0);
2718c2ecf20Sopenharmony_ci	else
2728c2ecf20Sopenharmony_ci		ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
2738c2ecf20Sopenharmony_ci			VBUS_ISPOUT_VBUS_PATH_DIS, VBUS_ISPOUT_VBUS_PATH_DIS);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (ret < 0)
2768c2ecf20Sopenharmony_ci		dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	return ret;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic int axp288_charger_enable_charger(struct axp288_chrg_info *info,
2828c2ecf20Sopenharmony_ci								bool enable)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	int ret;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (enable)
2878c2ecf20Sopenharmony_ci		ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
2888c2ecf20Sopenharmony_ci				CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
2898c2ecf20Sopenharmony_ci	else
2908c2ecf20Sopenharmony_ci		ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
2918c2ecf20Sopenharmony_ci				CHRG_CCCV_CHG_EN, 0);
2928c2ecf20Sopenharmony_ci	if (ret < 0)
2938c2ecf20Sopenharmony_ci		dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return ret;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int axp288_charger_is_present(struct axp288_chrg_info *info)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	int ret, present = 0;
3018c2ecf20Sopenharmony_ci	unsigned int val;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
3048c2ecf20Sopenharmony_ci	if (ret < 0)
3058c2ecf20Sopenharmony_ci		return ret;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (val & PS_STAT_VBUS_PRESENT)
3088c2ecf20Sopenharmony_ci		present = 1;
3098c2ecf20Sopenharmony_ci	return present;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic int axp288_charger_is_online(struct axp288_chrg_info *info)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	int ret, online = 0;
3158c2ecf20Sopenharmony_ci	unsigned int val;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
3188c2ecf20Sopenharmony_ci	if (ret < 0)
3198c2ecf20Sopenharmony_ci		return ret;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (val & PS_STAT_VBUS_VALID)
3228c2ecf20Sopenharmony_ci		online = 1;
3238c2ecf20Sopenharmony_ci	return online;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic int axp288_get_charger_health(struct axp288_chrg_info *info)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	int ret, pwr_stat, chrg_stat;
3298c2ecf20Sopenharmony_ci	int health = POWER_SUPPLY_HEALTH_UNKNOWN;
3308c2ecf20Sopenharmony_ci	unsigned int val;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
3338c2ecf20Sopenharmony_ci	if ((ret < 0) || !(val & PS_STAT_VBUS_PRESENT))
3348c2ecf20Sopenharmony_ci		goto health_read_fail;
3358c2ecf20Sopenharmony_ci	else
3368c2ecf20Sopenharmony_ci		pwr_stat = val;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, AXP20X_PWR_OP_MODE, &val);
3398c2ecf20Sopenharmony_ci	if (ret < 0)
3408c2ecf20Sopenharmony_ci		goto health_read_fail;
3418c2ecf20Sopenharmony_ci	else
3428c2ecf20Sopenharmony_ci		chrg_stat = val;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (!(pwr_stat & PS_STAT_VBUS_VALID))
3458c2ecf20Sopenharmony_ci		health = POWER_SUPPLY_HEALTH_DEAD;
3468c2ecf20Sopenharmony_ci	else if (chrg_stat & CHRG_STAT_PMIC_OTP)
3478c2ecf20Sopenharmony_ci		health = POWER_SUPPLY_HEALTH_OVERHEAT;
3488c2ecf20Sopenharmony_ci	else if (chrg_stat & CHRG_STAT_BAT_SAFE_MODE)
3498c2ecf20Sopenharmony_ci		health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
3508c2ecf20Sopenharmony_ci	else
3518c2ecf20Sopenharmony_ci		health = POWER_SUPPLY_HEALTH_GOOD;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cihealth_read_fail:
3548c2ecf20Sopenharmony_ci	return health;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic int axp288_charger_usb_set_property(struct power_supply *psy,
3588c2ecf20Sopenharmony_ci				    enum power_supply_property psp,
3598c2ecf20Sopenharmony_ci				    const union power_supply_propval *val)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
3628c2ecf20Sopenharmony_ci	int ret = 0;
3638c2ecf20Sopenharmony_ci	int scaled_val;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	switch (psp) {
3668c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
3678c2ecf20Sopenharmony_ci		scaled_val = min(val->intval, info->max_cc);
3688c2ecf20Sopenharmony_ci		scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
3698c2ecf20Sopenharmony_ci		ret = axp288_charger_set_cc(info, scaled_val);
3708c2ecf20Sopenharmony_ci		if (ret < 0)
3718c2ecf20Sopenharmony_ci			dev_warn(&info->pdev->dev, "set charge current failed\n");
3728c2ecf20Sopenharmony_ci		break;
3738c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
3748c2ecf20Sopenharmony_ci		scaled_val = min(val->intval, info->max_cv);
3758c2ecf20Sopenharmony_ci		scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
3768c2ecf20Sopenharmony_ci		ret = axp288_charger_set_cv(info, scaled_val);
3778c2ecf20Sopenharmony_ci		if (ret < 0)
3788c2ecf20Sopenharmony_ci			dev_warn(&info->pdev->dev, "set charge voltage failed\n");
3798c2ecf20Sopenharmony_ci		break;
3808c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
3818c2ecf20Sopenharmony_ci		ret = axp288_charger_set_vbus_inlmt(info, val->intval);
3828c2ecf20Sopenharmony_ci		if (ret < 0)
3838c2ecf20Sopenharmony_ci			dev_warn(&info->pdev->dev, "set input current limit failed\n");
3848c2ecf20Sopenharmony_ci		break;
3858c2ecf20Sopenharmony_ci	default:
3868c2ecf20Sopenharmony_ci		ret = -EINVAL;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	return ret;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int axp288_charger_usb_get_property(struct power_supply *psy,
3938c2ecf20Sopenharmony_ci				    enum power_supply_property psp,
3948c2ecf20Sopenharmony_ci				    union power_supply_propval *val)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
3978c2ecf20Sopenharmony_ci	int ret;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	switch (psp) {
4008c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
4018c2ecf20Sopenharmony_ci		/* Check for OTG case first */
4028c2ecf20Sopenharmony_ci		if (info->otg.id_short) {
4038c2ecf20Sopenharmony_ci			val->intval = 0;
4048c2ecf20Sopenharmony_ci			break;
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci		ret = axp288_charger_is_present(info);
4078c2ecf20Sopenharmony_ci		if (ret < 0)
4088c2ecf20Sopenharmony_ci			return ret;
4098c2ecf20Sopenharmony_ci		val->intval = ret;
4108c2ecf20Sopenharmony_ci		break;
4118c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
4128c2ecf20Sopenharmony_ci		/* Check for OTG case first */
4138c2ecf20Sopenharmony_ci		if (info->otg.id_short) {
4148c2ecf20Sopenharmony_ci			val->intval = 0;
4158c2ecf20Sopenharmony_ci			break;
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci		ret = axp288_charger_is_online(info);
4188c2ecf20Sopenharmony_ci		if (ret < 0)
4198c2ecf20Sopenharmony_ci			return ret;
4208c2ecf20Sopenharmony_ci		val->intval = ret;
4218c2ecf20Sopenharmony_ci		break;
4228c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
4238c2ecf20Sopenharmony_ci		val->intval = axp288_get_charger_health(info);
4248c2ecf20Sopenharmony_ci		break;
4258c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
4268c2ecf20Sopenharmony_ci		val->intval = info->cc * 1000;
4278c2ecf20Sopenharmony_ci		break;
4288c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
4298c2ecf20Sopenharmony_ci		val->intval = info->max_cc * 1000;
4308c2ecf20Sopenharmony_ci		break;
4318c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
4328c2ecf20Sopenharmony_ci		val->intval = info->cv * 1000;
4338c2ecf20Sopenharmony_ci		break;
4348c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
4358c2ecf20Sopenharmony_ci		val->intval = info->max_cv * 1000;
4368c2ecf20Sopenharmony_ci		break;
4378c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
4388c2ecf20Sopenharmony_ci		ret = axp288_charger_get_vbus_inlmt(info);
4398c2ecf20Sopenharmony_ci		if (ret < 0)
4408c2ecf20Sopenharmony_ci			return ret;
4418c2ecf20Sopenharmony_ci		val->intval = ret;
4428c2ecf20Sopenharmony_ci		break;
4438c2ecf20Sopenharmony_ci	default:
4448c2ecf20Sopenharmony_ci		return -EINVAL;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	return 0;
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic int axp288_charger_property_is_writeable(struct power_supply *psy,
4518c2ecf20Sopenharmony_ci		enum power_supply_property psp)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	int ret;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	switch (psp) {
4568c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
4578c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
4588c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
4598c2ecf20Sopenharmony_ci		ret = 1;
4608c2ecf20Sopenharmony_ci		break;
4618c2ecf20Sopenharmony_ci	default:
4628c2ecf20Sopenharmony_ci		ret = 0;
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	return ret;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic enum power_supply_property axp288_usb_props[] = {
4698c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
4708c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
4718c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_TYPE,
4728c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
4738c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
4748c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
4758c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
4768c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
4778c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
4788c2ecf20Sopenharmony_ci};
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic const struct power_supply_desc axp288_charger_desc = {
4818c2ecf20Sopenharmony_ci	.name			= "axp288_charger",
4828c2ecf20Sopenharmony_ci	.type			= POWER_SUPPLY_TYPE_USB,
4838c2ecf20Sopenharmony_ci	.properties		= axp288_usb_props,
4848c2ecf20Sopenharmony_ci	.num_properties		= ARRAY_SIZE(axp288_usb_props),
4858c2ecf20Sopenharmony_ci	.get_property		= axp288_charger_usb_get_property,
4868c2ecf20Sopenharmony_ci	.set_property		= axp288_charger_usb_set_property,
4878c2ecf20Sopenharmony_ci	.property_is_writeable	= axp288_charger_property_is_writeable,
4888c2ecf20Sopenharmony_ci};
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_cistatic irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	struct axp288_chrg_info *info = dev;
4938c2ecf20Sopenharmony_ci	int i;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	for (i = 0; i < CHRG_INTR_END; i++) {
4968c2ecf20Sopenharmony_ci		if (info->irq[i] == irq)
4978c2ecf20Sopenharmony_ci			break;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if (i >= CHRG_INTR_END) {
5018c2ecf20Sopenharmony_ci		dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
5028c2ecf20Sopenharmony_ci		return IRQ_NONE;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	switch (i) {
5068c2ecf20Sopenharmony_ci	case VBUS_OV_IRQ:
5078c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev, "VBUS Over Voltage INTR\n");
5088c2ecf20Sopenharmony_ci		break;
5098c2ecf20Sopenharmony_ci	case CHARGE_DONE_IRQ:
5108c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev, "Charging Done INTR\n");
5118c2ecf20Sopenharmony_ci		break;
5128c2ecf20Sopenharmony_ci	case CHARGE_CHARGING_IRQ:
5138c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev, "Start Charging IRQ\n");
5148c2ecf20Sopenharmony_ci		break;
5158c2ecf20Sopenharmony_ci	case BAT_SAFE_QUIT_IRQ:
5168c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev,
5178c2ecf20Sopenharmony_ci			"Quit Safe Mode(restart timer) Charging IRQ\n");
5188c2ecf20Sopenharmony_ci		break;
5198c2ecf20Sopenharmony_ci	case BAT_SAFE_ENTER_IRQ:
5208c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev,
5218c2ecf20Sopenharmony_ci			"Enter Safe Mode(timer expire) Charging IRQ\n");
5228c2ecf20Sopenharmony_ci		break;
5238c2ecf20Sopenharmony_ci	case QCBTU_IRQ:
5248c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev,
5258c2ecf20Sopenharmony_ci			"Quit Battery Under Temperature(CHRG) INTR\n");
5268c2ecf20Sopenharmony_ci		break;
5278c2ecf20Sopenharmony_ci	case CBTU_IRQ:
5288c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev,
5298c2ecf20Sopenharmony_ci			"Hit Battery Under Temperature(CHRG) INTR\n");
5308c2ecf20Sopenharmony_ci		break;
5318c2ecf20Sopenharmony_ci	case QCBTO_IRQ:
5328c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev,
5338c2ecf20Sopenharmony_ci			"Quit Battery Over Temperature(CHRG) INTR\n");
5348c2ecf20Sopenharmony_ci		break;
5358c2ecf20Sopenharmony_ci	case CBTO_IRQ:
5368c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev,
5378c2ecf20Sopenharmony_ci			"Hit Battery Over Temperature(CHRG) INTR\n");
5388c2ecf20Sopenharmony_ci		break;
5398c2ecf20Sopenharmony_ci	default:
5408c2ecf20Sopenharmony_ci		dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
5418c2ecf20Sopenharmony_ci		goto out;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	power_supply_changed(info->psy_usb);
5458c2ecf20Sopenharmony_ciout:
5468c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci/*
5508c2ecf20Sopenharmony_ci * The HP Pavilion x2 10 series comes in a number of variants:
5518c2ecf20Sopenharmony_ci * Bay Trail SoC    + AXP288 PMIC, Micro-USB, DMI_BOARD_NAME: "8021"
5528c2ecf20Sopenharmony_ci * Bay Trail SoC    + AXP288 PMIC, Type-C,    DMI_BOARD_NAME: "815D"
5538c2ecf20Sopenharmony_ci * Cherry Trail SoC + AXP288 PMIC, Type-C,    DMI_BOARD_NAME: "813E"
5548c2ecf20Sopenharmony_ci * Cherry Trail SoC + TI PMIC,     Type-C,    DMI_BOARD_NAME: "827C" or "82F4"
5558c2ecf20Sopenharmony_ci *
5568c2ecf20Sopenharmony_ci * The variants with the AXP288 + Type-C connector are all kinds of special:
5578c2ecf20Sopenharmony_ci *
5588c2ecf20Sopenharmony_ci * 1. They use a Type-C connector which the AXP288 does not support, so when
5598c2ecf20Sopenharmony_ci * using a Type-C charger it is not recognized. Unlike most AXP288 devices,
5608c2ecf20Sopenharmony_ci * this model actually has mostly working ACPI AC / Battery code, the ACPI code
5618c2ecf20Sopenharmony_ci * "solves" this by simply setting the input_current_limit to 3A.
5628c2ecf20Sopenharmony_ci * There are still some issues with the ACPI code, so we use this native driver,
5638c2ecf20Sopenharmony_ci * and to solve the charging not working (500mA is not enough) issue we hardcode
5648c2ecf20Sopenharmony_ci * the 3A input_current_limit like the ACPI code does.
5658c2ecf20Sopenharmony_ci *
5668c2ecf20Sopenharmony_ci * 2. If no charger is connected the machine boots with the vbus-path disabled.
5678c2ecf20Sopenharmony_ci * Normally this is done when a 5V boost converter is active to avoid the PMIC
5688c2ecf20Sopenharmony_ci * trying to charge from the 5V boost converter's output. This is done when
5698c2ecf20Sopenharmony_ci * an OTG host cable is inserted and the ID pin on the micro-B receptacle is
5708c2ecf20Sopenharmony_ci * pulled low and the ID pin has an ACPI event handler associated with it
5718c2ecf20Sopenharmony_ci * which re-enables the vbus-path when the ID pin is pulled high when the
5728c2ecf20Sopenharmony_ci * OTG host cable is removed. The Type-C connector has no ID pin, there is
5738c2ecf20Sopenharmony_ci * no ID pin handler and there appears to be no 5V boost converter, so we
5748c2ecf20Sopenharmony_ci * end up not charging because the vbus-path is disabled, until we unplug
5758c2ecf20Sopenharmony_ci * the charger which automatically clears the vbus-path disable bit and then
5768c2ecf20Sopenharmony_ci * on the second plug-in of the adapter we start charging. To solve the not
5778c2ecf20Sopenharmony_ci * charging on first charger plugin we unconditionally enable the vbus-path at
5788c2ecf20Sopenharmony_ci * probe on this model, which is safe since there is no 5V boost converter.
5798c2ecf20Sopenharmony_ci */
5808c2ecf20Sopenharmony_cistatic const struct dmi_system_id axp288_hp_x2_dmi_ids[] = {
5818c2ecf20Sopenharmony_ci	{
5828c2ecf20Sopenharmony_ci		.matches = {
5838c2ecf20Sopenharmony_ci			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
5848c2ecf20Sopenharmony_ci			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
5858c2ecf20Sopenharmony_ci			DMI_EXACT_MATCH(DMI_BOARD_NAME, "815D"),
5868c2ecf20Sopenharmony_ci		},
5878c2ecf20Sopenharmony_ci	},
5888c2ecf20Sopenharmony_ci	{
5898c2ecf20Sopenharmony_ci		.matches = {
5908c2ecf20Sopenharmony_ci			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "HP"),
5918c2ecf20Sopenharmony_ci			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
5928c2ecf20Sopenharmony_ci			DMI_EXACT_MATCH(DMI_BOARD_NAME, "813E"),
5938c2ecf20Sopenharmony_ci		},
5948c2ecf20Sopenharmony_ci	},
5958c2ecf20Sopenharmony_ci	{} /* Terminating entry */
5968c2ecf20Sopenharmony_ci};
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic void axp288_charger_extcon_evt_worker(struct work_struct *work)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	struct axp288_chrg_info *info =
6018c2ecf20Sopenharmony_ci	    container_of(work, struct axp288_chrg_info, cable.work);
6028c2ecf20Sopenharmony_ci	int ret, current_limit;
6038c2ecf20Sopenharmony_ci	struct extcon_dev *edev = info->cable.edev;
6048c2ecf20Sopenharmony_ci	unsigned int val;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
6078c2ecf20Sopenharmony_ci	if (ret < 0) {
6088c2ecf20Sopenharmony_ci		dev_err(&info->pdev->dev, "Error reading status (%d)\n", ret);
6098c2ecf20Sopenharmony_ci		return;
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	/* Offline? Disable charging and bail */
6138c2ecf20Sopenharmony_ci	if (!(val & PS_STAT_VBUS_VALID)) {
6148c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev, "USB charger disconnected\n");
6158c2ecf20Sopenharmony_ci		axp288_charger_enable_charger(info, false);
6168c2ecf20Sopenharmony_ci		power_supply_changed(info->psy_usb);
6178c2ecf20Sopenharmony_ci		return;
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	/* Determine cable/charger type */
6218c2ecf20Sopenharmony_ci	if (dmi_check_system(axp288_hp_x2_dmi_ids)) {
6228c2ecf20Sopenharmony_ci		/* See comment above axp288_hp_x2_dmi_ids declaration */
6238c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev, "HP X2 with Type-C, setting inlmt to 3A\n");
6248c2ecf20Sopenharmony_ci		current_limit = 3000000;
6258c2ecf20Sopenharmony_ci	} else if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
6268c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n");
6278c2ecf20Sopenharmony_ci		current_limit = 500000;
6288c2ecf20Sopenharmony_ci	} else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
6298c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n");
6308c2ecf20Sopenharmony_ci		current_limit = 1500000;
6318c2ecf20Sopenharmony_ci	} else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {
6328c2ecf20Sopenharmony_ci		dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n");
6338c2ecf20Sopenharmony_ci		current_limit = 2000000;
6348c2ecf20Sopenharmony_ci	} else {
6358c2ecf20Sopenharmony_ci		/* Charger type detection still in progress, bail. */
6368c2ecf20Sopenharmony_ci		return;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	/* Set vbus current limit first, then enable charger */
6408c2ecf20Sopenharmony_ci	ret = axp288_charger_set_vbus_inlmt(info, current_limit);
6418c2ecf20Sopenharmony_ci	if (ret == 0)
6428c2ecf20Sopenharmony_ci		axp288_charger_enable_charger(info, true);
6438c2ecf20Sopenharmony_ci	else
6448c2ecf20Sopenharmony_ci		dev_err(&info->pdev->dev,
6458c2ecf20Sopenharmony_ci			"error setting current limit (%d)\n", ret);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	power_supply_changed(info->psy_usb);
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_cistatic int axp288_charger_handle_cable_evt(struct notifier_block *nb,
6518c2ecf20Sopenharmony_ci					   unsigned long event, void *param)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	struct axp288_chrg_info *info =
6548c2ecf20Sopenharmony_ci		container_of(nb, struct axp288_chrg_info, cable.nb);
6558c2ecf20Sopenharmony_ci	schedule_work(&info->cable.work);
6568c2ecf20Sopenharmony_ci	return NOTIFY_OK;
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic void axp288_charger_otg_evt_worker(struct work_struct *work)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct axp288_chrg_info *info =
6628c2ecf20Sopenharmony_ci	    container_of(work, struct axp288_chrg_info, otg.work);
6638c2ecf20Sopenharmony_ci	struct extcon_dev *edev = info->otg.cable;
6648c2ecf20Sopenharmony_ci	int ret, usb_host = extcon_get_state(edev, EXTCON_USB_HOST);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
6678c2ecf20Sopenharmony_ci				usb_host ? "attached" : "detached");
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	/*
6708c2ecf20Sopenharmony_ci	 * Set usb_id_short flag to avoid running charger detection logic
6718c2ecf20Sopenharmony_ci	 * in case usb host.
6728c2ecf20Sopenharmony_ci	 */
6738c2ecf20Sopenharmony_ci	info->otg.id_short = usb_host;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	/* Disable VBUS path before enabling the 5V boost */
6768c2ecf20Sopenharmony_ci	ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);
6778c2ecf20Sopenharmony_ci	if (ret < 0)
6788c2ecf20Sopenharmony_ci		dev_warn(&info->pdev->dev, "vbus path disable failed\n");
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_cistatic int axp288_charger_handle_otg_evt(struct notifier_block *nb,
6828c2ecf20Sopenharmony_ci				   unsigned long event, void *param)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	struct axp288_chrg_info *info =
6858c2ecf20Sopenharmony_ci	    container_of(nb, struct axp288_chrg_info, otg.id_nb);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	schedule_work(&info->otg.work);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	return NOTIFY_OK;
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic int charger_init_hw_regs(struct axp288_chrg_info *info)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	int ret, cc, cv;
6958c2ecf20Sopenharmony_ci	unsigned int val;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/* Program temperature thresholds */
6988c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
6998c2ecf20Sopenharmony_ci	if (ret < 0) {
7008c2ecf20Sopenharmony_ci		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
7018c2ecf20Sopenharmony_ci							AXP20X_V_LTF_CHRG, ret);
7028c2ecf20Sopenharmony_ci		return ret;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
7068c2ecf20Sopenharmony_ci	if (ret < 0) {
7078c2ecf20Sopenharmony_ci		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
7088c2ecf20Sopenharmony_ci							AXP20X_V_HTF_CHRG, ret);
7098c2ecf20Sopenharmony_ci		return ret;
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	/* Do not turn-off charger o/p after charge cycle ends */
7138c2ecf20Sopenharmony_ci	ret = regmap_update_bits(info->regmap,
7148c2ecf20Sopenharmony_ci				AXP20X_CHRG_CTRL2,
7158c2ecf20Sopenharmony_ci				CNTL2_CHG_OUT_TURNON, CNTL2_CHG_OUT_TURNON);
7168c2ecf20Sopenharmony_ci	if (ret < 0) {
7178c2ecf20Sopenharmony_ci		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
7188c2ecf20Sopenharmony_ci						AXP20X_CHRG_CTRL2, ret);
7198c2ecf20Sopenharmony_ci		return ret;
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* Setup ending condition for charging to be 10% of I(chrg) */
7238c2ecf20Sopenharmony_ci	ret = regmap_update_bits(info->regmap,
7248c2ecf20Sopenharmony_ci				AXP20X_CHRG_CTRL1,
7258c2ecf20Sopenharmony_ci				CHRG_CCCV_ITERM_20P, 0);
7268c2ecf20Sopenharmony_ci	if (ret < 0) {
7278c2ecf20Sopenharmony_ci		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
7288c2ecf20Sopenharmony_ci						AXP20X_CHRG_CTRL1, ret);
7298c2ecf20Sopenharmony_ci		return ret;
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	/* Disable OCV-SOC curve calibration */
7338c2ecf20Sopenharmony_ci	ret = regmap_update_bits(info->regmap,
7348c2ecf20Sopenharmony_ci				AXP20X_CC_CTRL,
7358c2ecf20Sopenharmony_ci				FG_CNTL_OCV_ADJ_EN, 0);
7368c2ecf20Sopenharmony_ci	if (ret < 0) {
7378c2ecf20Sopenharmony_ci		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
7388c2ecf20Sopenharmony_ci						AXP20X_CC_CTRL, ret);
7398c2ecf20Sopenharmony_ci		return ret;
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	if (dmi_check_system(axp288_hp_x2_dmi_ids)) {
7438c2ecf20Sopenharmony_ci		/* See comment above axp288_hp_x2_dmi_ids declaration */
7448c2ecf20Sopenharmony_ci		ret = axp288_charger_vbus_path_select(info, true);
7458c2ecf20Sopenharmony_ci		if (ret < 0)
7468c2ecf20Sopenharmony_ci			return ret;
7478c2ecf20Sopenharmony_ci	} else {
7488c2ecf20Sopenharmony_ci		/* Set Vhold to the factory default / recommended 4.4V */
7498c2ecf20Sopenharmony_ci		val = VBUS_ISPOUT_VHOLD_SET_4400MV << VBUS_ISPOUT_VHOLD_SET_BIT_POS;
7508c2ecf20Sopenharmony_ci		ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
7518c2ecf20Sopenharmony_ci					 VBUS_ISPOUT_VHOLD_SET_MASK, val);
7528c2ecf20Sopenharmony_ci		if (ret < 0) {
7538c2ecf20Sopenharmony_ci			dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
7548c2ecf20Sopenharmony_ci				AXP20X_VBUS_IPSOUT_MGMT, ret);
7558c2ecf20Sopenharmony_ci			return ret;
7568c2ecf20Sopenharmony_ci		}
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	/* Read current charge voltage and current limit */
7608c2ecf20Sopenharmony_ci	ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
7618c2ecf20Sopenharmony_ci	if (ret < 0) {
7628c2ecf20Sopenharmony_ci		dev_err(&info->pdev->dev, "register(%x) read error(%d)\n",
7638c2ecf20Sopenharmony_ci			AXP20X_CHRG_CTRL1, ret);
7648c2ecf20Sopenharmony_ci		return ret;
7658c2ecf20Sopenharmony_ci	}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	/* Determine charge voltage */
7688c2ecf20Sopenharmony_ci	cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
7698c2ecf20Sopenharmony_ci	switch (cv) {
7708c2ecf20Sopenharmony_ci	case CHRG_CCCV_CV_4100MV:
7718c2ecf20Sopenharmony_ci		info->cv = CV_4100MV;
7728c2ecf20Sopenharmony_ci		break;
7738c2ecf20Sopenharmony_ci	case CHRG_CCCV_CV_4150MV:
7748c2ecf20Sopenharmony_ci		info->cv = CV_4150MV;
7758c2ecf20Sopenharmony_ci		break;
7768c2ecf20Sopenharmony_ci	case CHRG_CCCV_CV_4200MV:
7778c2ecf20Sopenharmony_ci		info->cv = CV_4200MV;
7788c2ecf20Sopenharmony_ci		break;
7798c2ecf20Sopenharmony_ci	case CHRG_CCCV_CV_4350MV:
7808c2ecf20Sopenharmony_ci		info->cv = CV_4350MV;
7818c2ecf20Sopenharmony_ci		break;
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	/* Determine charge current limit */
7858c2ecf20Sopenharmony_ci	cc = (val & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
7868c2ecf20Sopenharmony_ci	cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
7878c2ecf20Sopenharmony_ci	info->cc = cc;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	/*
7908c2ecf20Sopenharmony_ci	 * Do not allow the user to configure higher settings then those
7918c2ecf20Sopenharmony_ci	 * set by the firmware
7928c2ecf20Sopenharmony_ci	 */
7938c2ecf20Sopenharmony_ci	info->max_cv = info->cv;
7948c2ecf20Sopenharmony_ci	info->max_cc = info->cc;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	return 0;
7978c2ecf20Sopenharmony_ci}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_cistatic void axp288_charger_cancel_work(void *data)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	struct axp288_chrg_info *info = data;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	cancel_work_sync(&info->otg.work);
8048c2ecf20Sopenharmony_ci	cancel_work_sync(&info->cable.work);
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistatic int axp288_charger_probe(struct platform_device *pdev)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	int ret, i, pirq;
8108c2ecf20Sopenharmony_ci	struct axp288_chrg_info *info;
8118c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
8128c2ecf20Sopenharmony_ci	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
8138c2ecf20Sopenharmony_ci	struct power_supply_config charger_cfg = {};
8148c2ecf20Sopenharmony_ci	unsigned int val;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	/*
8178c2ecf20Sopenharmony_ci	 * On some devices the fuelgauge and charger parts of the axp288 are
8188c2ecf20Sopenharmony_ci	 * not used, check that the fuelgauge is enabled (CC_CTRL != 0).
8198c2ecf20Sopenharmony_ci	 */
8208c2ecf20Sopenharmony_ci	ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val);
8218c2ecf20Sopenharmony_ci	if (ret < 0)
8228c2ecf20Sopenharmony_ci		return ret;
8238c2ecf20Sopenharmony_ci	if (val == 0)
8248c2ecf20Sopenharmony_ci		return -ENODEV;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
8278c2ecf20Sopenharmony_ci	if (!info)
8288c2ecf20Sopenharmony_ci		return -ENOMEM;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	info->pdev = pdev;
8318c2ecf20Sopenharmony_ci	info->regmap = axp20x->regmap;
8328c2ecf20Sopenharmony_ci	info->regmap_irqc = axp20x->regmap_irqc;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
8358c2ecf20Sopenharmony_ci	if (info->cable.edev == NULL) {
8368c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "%s is not ready, probe deferred\n",
8378c2ecf20Sopenharmony_ci			AXP288_EXTCON_DEV_NAME);
8388c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
8398c2ecf20Sopenharmony_ci	}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	if (acpi_dev_present(USB_HOST_EXTCON_HID, NULL, -1)) {
8428c2ecf20Sopenharmony_ci		info->otg.cable = extcon_get_extcon_dev(USB_HOST_EXTCON_NAME);
8438c2ecf20Sopenharmony_ci		if (info->otg.cable == NULL) {
8448c2ecf20Sopenharmony_ci			dev_dbg(dev, "EXTCON_USB_HOST is not ready, probe deferred\n");
8458c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
8468c2ecf20Sopenharmony_ci		}
8478c2ecf20Sopenharmony_ci		dev_info(&pdev->dev,
8488c2ecf20Sopenharmony_ci			 "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n");
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, info);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	ret = charger_init_hw_regs(info);
8548c2ecf20Sopenharmony_ci	if (ret)
8558c2ecf20Sopenharmony_ci		return ret;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	/* Register with power supply class */
8588c2ecf20Sopenharmony_ci	charger_cfg.drv_data = info;
8598c2ecf20Sopenharmony_ci	info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc,
8608c2ecf20Sopenharmony_ci						   &charger_cfg);
8618c2ecf20Sopenharmony_ci	if (IS_ERR(info->psy_usb)) {
8628c2ecf20Sopenharmony_ci		ret = PTR_ERR(info->psy_usb);
8638c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register power supply: %d\n", ret);
8648c2ecf20Sopenharmony_ci		return ret;
8658c2ecf20Sopenharmony_ci	}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	/* Cancel our work on cleanup, register this before the notifiers */
8688c2ecf20Sopenharmony_ci	ret = devm_add_action(dev, axp288_charger_cancel_work, info);
8698c2ecf20Sopenharmony_ci	if (ret)
8708c2ecf20Sopenharmony_ci		return ret;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	/* Register for extcon notification */
8738c2ecf20Sopenharmony_ci	INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
8748c2ecf20Sopenharmony_ci	info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
8758c2ecf20Sopenharmony_ci	ret = devm_extcon_register_notifier_all(dev, info->cable.edev,
8768c2ecf20Sopenharmony_ci						&info->cable.nb);
8778c2ecf20Sopenharmony_ci	if (ret) {
8788c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register cable extcon notifier\n");
8798c2ecf20Sopenharmony_ci		return ret;
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci	schedule_work(&info->cable.work);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	/* Register for OTG notification */
8848c2ecf20Sopenharmony_ci	INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
8858c2ecf20Sopenharmony_ci	info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
8868c2ecf20Sopenharmony_ci	if (info->otg.cable) {
8878c2ecf20Sopenharmony_ci		ret = devm_extcon_register_notifier(&pdev->dev, info->otg.cable,
8888c2ecf20Sopenharmony_ci					EXTCON_USB_HOST, &info->otg.id_nb);
8898c2ecf20Sopenharmony_ci		if (ret) {
8908c2ecf20Sopenharmony_ci			dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n");
8918c2ecf20Sopenharmony_ci			return ret;
8928c2ecf20Sopenharmony_ci		}
8938c2ecf20Sopenharmony_ci		schedule_work(&info->otg.work);
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	/* Register charger interrupts */
8978c2ecf20Sopenharmony_ci	for (i = 0; i < CHRG_INTR_END; i++) {
8988c2ecf20Sopenharmony_ci		pirq = platform_get_irq(info->pdev, i);
8998c2ecf20Sopenharmony_ci		if (pirq < 0)
9008c2ecf20Sopenharmony_ci			return pirq;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci		info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
9038c2ecf20Sopenharmony_ci		if (info->irq[i] < 0) {
9048c2ecf20Sopenharmony_ci			dev_warn(&info->pdev->dev,
9058c2ecf20Sopenharmony_ci				"failed to get virtual interrupt=%d\n", pirq);
9068c2ecf20Sopenharmony_ci			return info->irq[i];
9078c2ecf20Sopenharmony_ci		}
9088c2ecf20Sopenharmony_ci		ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
9098c2ecf20Sopenharmony_ci					NULL, axp288_charger_irq_thread_handler,
9108c2ecf20Sopenharmony_ci					IRQF_ONESHOT, info->pdev->name, info);
9118c2ecf20Sopenharmony_ci		if (ret) {
9128c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "failed to request interrupt=%d\n",
9138c2ecf20Sopenharmony_ci								info->irq[i]);
9148c2ecf20Sopenharmony_ci			return ret;
9158c2ecf20Sopenharmony_ci		}
9168c2ecf20Sopenharmony_ci	}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	return 0;
9198c2ecf20Sopenharmony_ci}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_cistatic const struct platform_device_id axp288_charger_id_table[] = {
9228c2ecf20Sopenharmony_ci	{ .name = "axp288_charger" },
9238c2ecf20Sopenharmony_ci	{},
9248c2ecf20Sopenharmony_ci};
9258c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, axp288_charger_id_table);
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_cistatic struct platform_driver axp288_charger_driver = {
9288c2ecf20Sopenharmony_ci	.probe = axp288_charger_probe,
9298c2ecf20Sopenharmony_ci	.id_table = axp288_charger_id_table,
9308c2ecf20Sopenharmony_ci	.driver = {
9318c2ecf20Sopenharmony_ci		.name = "axp288_charger",
9328c2ecf20Sopenharmony_ci	},
9338c2ecf20Sopenharmony_ci};
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cimodule_platform_driver(axp288_charger_driver);
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
9388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("X-power AXP288 Charger Driver");
9398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
940