162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * axp288_charger.c - X-power AXP288 PMIC Charger driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com>
662306a36Sopenharmony_ci * Copyright (C) 2014 Intel Corporation
762306a36Sopenharmony_ci * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/acpi.h>
1162306a36Sopenharmony_ci#include <linux/bitops.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/device.h>
1462306a36Sopenharmony_ci#include <linux/regmap.h>
1562306a36Sopenharmony_ci#include <linux/workqueue.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/usb/otg.h>
1962306a36Sopenharmony_ci#include <linux/notifier.h>
2062306a36Sopenharmony_ci#include <linux/power_supply.h>
2162306a36Sopenharmony_ci#include <linux/property.h>
2262306a36Sopenharmony_ci#include <linux/mfd/axp20x.h>
2362306a36Sopenharmony_ci#include <linux/extcon.h>
2462306a36Sopenharmony_ci#include <linux/dmi.h>
2562306a36Sopenharmony_ci#include <asm/iosf_mbi.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define PS_STAT_VBUS_TRIGGER		BIT(0)
2862306a36Sopenharmony_ci#define PS_STAT_BAT_CHRG_DIR		BIT(2)
2962306a36Sopenharmony_ci#define PS_STAT_VBAT_ABOVE_VHOLD	BIT(3)
3062306a36Sopenharmony_ci#define PS_STAT_VBUS_VALID		BIT(4)
3162306a36Sopenharmony_ci#define PS_STAT_VBUS_PRESENT		BIT(5)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define CHRG_STAT_BAT_SAFE_MODE		BIT(3)
3462306a36Sopenharmony_ci#define CHRG_STAT_BAT_VALID		BIT(4)
3562306a36Sopenharmony_ci#define CHRG_STAT_BAT_PRESENT		BIT(5)
3662306a36Sopenharmony_ci#define CHRG_STAT_CHARGING		BIT(6)
3762306a36Sopenharmony_ci#define CHRG_STAT_PMIC_OTP		BIT(7)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_MASK	0x03
4062306a36Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_BIT_POS	0
4162306a36Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_900MA	0x0	/* 900mA */
4262306a36Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_1500MA	0x1	/* 1500mA */
4362306a36Sopenharmony_ci#define VBUS_ISPOUT_CUR_LIM_2000MA	0x2	/* 2000mA */
4462306a36Sopenharmony_ci#define VBUS_ISPOUT_CUR_NO_LIM		0x3	/* 2500mA */
4562306a36Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_MASK	0x38
4662306a36Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_BIT_POS	0x3
4762306a36Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_OFFSET	4000	/* 4000mV */
4862306a36Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_LSB_RES	100	/* 100mV */
4962306a36Sopenharmony_ci#define VBUS_ISPOUT_VHOLD_SET_4400MV	0x4	/* 4400mV */
5062306a36Sopenharmony_ci#define VBUS_ISPOUT_VBUS_PATH_DIS	BIT(7)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define CHRG_CCCV_CC_MASK		0xf		/* 4 bits */
5362306a36Sopenharmony_ci#define CHRG_CCCV_CC_BIT_POS		0
5462306a36Sopenharmony_ci#define CHRG_CCCV_CC_OFFSET		200		/* 200mA */
5562306a36Sopenharmony_ci#define CHRG_CCCV_CC_LSB_RES		200		/* 200mA */
5662306a36Sopenharmony_ci#define CHRG_CCCV_ITERM_20P		BIT(4)		/* 20% of CC */
5762306a36Sopenharmony_ci#define CHRG_CCCV_CV_MASK		0x60		/* 2 bits */
5862306a36Sopenharmony_ci#define CHRG_CCCV_CV_BIT_POS		5
5962306a36Sopenharmony_ci#define CHRG_CCCV_CV_4100MV		0x0		/* 4.10V */
6062306a36Sopenharmony_ci#define CHRG_CCCV_CV_4150MV		0x1		/* 4.15V */
6162306a36Sopenharmony_ci#define CHRG_CCCV_CV_4200MV		0x2		/* 4.20V */
6262306a36Sopenharmony_ci#define CHRG_CCCV_CV_4350MV		0x3		/* 4.35V */
6362306a36Sopenharmony_ci#define CHRG_CCCV_CHG_EN		BIT(7)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define CNTL2_CC_TIMEOUT_MASK		0x3	/* 2 bits */
6662306a36Sopenharmony_ci#define CNTL2_CC_TIMEOUT_OFFSET		6	/* 6 Hrs */
6762306a36Sopenharmony_ci#define CNTL2_CC_TIMEOUT_LSB_RES	2	/* 2 Hrs */
6862306a36Sopenharmony_ci#define CNTL2_CC_TIMEOUT_12HRS		0x3	/* 12 Hrs */
6962306a36Sopenharmony_ci#define CNTL2_CHGLED_TYPEB		BIT(4)
7062306a36Sopenharmony_ci#define CNTL2_CHG_OUT_TURNON		BIT(5)
7162306a36Sopenharmony_ci#define CNTL2_PC_TIMEOUT_MASK		0xC0
7262306a36Sopenharmony_ci#define CNTL2_PC_TIMEOUT_OFFSET		40	/* 40 mins */
7362306a36Sopenharmony_ci#define CNTL2_PC_TIMEOUT_LSB_RES	10	/* 10 mins */
7462306a36Sopenharmony_ci#define CNTL2_PC_TIMEOUT_70MINS		0x3
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define CHRG_ILIM_TEMP_LOOP_EN		BIT(3)
7762306a36Sopenharmony_ci#define CHRG_VBUS_ILIM_MASK		0xf0
7862306a36Sopenharmony_ci#define CHRG_VBUS_ILIM_BIT_POS		4
7962306a36Sopenharmony_ci#define CHRG_VBUS_ILIM_100MA		0x0	/* 100mA */
8062306a36Sopenharmony_ci#define CHRG_VBUS_ILIM_500MA		0x1	/* 500mA */
8162306a36Sopenharmony_ci#define CHRG_VBUS_ILIM_900MA		0x2	/* 900mA */
8262306a36Sopenharmony_ci#define CHRG_VBUS_ILIM_1500MA		0x3	/* 1500mA */
8362306a36Sopenharmony_ci#define CHRG_VBUS_ILIM_2000MA		0x4	/* 2000mA */
8462306a36Sopenharmony_ci#define CHRG_VBUS_ILIM_2500MA		0x5	/* 2500mA */
8562306a36Sopenharmony_ci#define CHRG_VBUS_ILIM_3000MA		0x6	/* 3000mA */
8662306a36Sopenharmony_ci#define CHRG_VBUS_ILIM_3500MA		0x7	/* 3500mA */
8762306a36Sopenharmony_ci#define CHRG_VBUS_ILIM_4000MA		0x8	/* 4000mA */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define CHRG_VLTFC_0C			0xA5	/* 0 DegC */
9062306a36Sopenharmony_ci#define CHRG_VHTFC_45C			0x1F	/* 45 DegC */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define FG_CNTL_OCV_ADJ_EN		BIT(3)
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define CV_4100MV			4100	/* 4100mV */
9562306a36Sopenharmony_ci#define CV_4150MV			4150	/* 4150mV */
9662306a36Sopenharmony_ci#define CV_4200MV			4200	/* 4200mV */
9762306a36Sopenharmony_ci#define CV_4350MV			4350	/* 4350mV */
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define AXP288_REG_UPDATE_INTERVAL	(60 * HZ)
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#define AXP288_EXTCON_DEV_NAME		"axp288_extcon"
10262306a36Sopenharmony_ci#define USB_HOST_EXTCON_HID		"INT3496"
10362306a36Sopenharmony_ci#define USB_HOST_EXTCON_NAME		"INT3496:00"
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cienum {
10662306a36Sopenharmony_ci	VBUS_OV_IRQ = 0,
10762306a36Sopenharmony_ci	CHARGE_DONE_IRQ,
10862306a36Sopenharmony_ci	CHARGE_CHARGING_IRQ,
10962306a36Sopenharmony_ci	BAT_SAFE_QUIT_IRQ,
11062306a36Sopenharmony_ci	BAT_SAFE_ENTER_IRQ,
11162306a36Sopenharmony_ci	QCBTU_IRQ,
11262306a36Sopenharmony_ci	CBTU_IRQ,
11362306a36Sopenharmony_ci	QCBTO_IRQ,
11462306a36Sopenharmony_ci	CBTO_IRQ,
11562306a36Sopenharmony_ci	CHRG_INTR_END,
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistruct axp288_chrg_info {
11962306a36Sopenharmony_ci	struct platform_device *pdev;
12062306a36Sopenharmony_ci	struct regmap *regmap;
12162306a36Sopenharmony_ci	struct regmap_irq_chip_data *regmap_irqc;
12262306a36Sopenharmony_ci	int irq[CHRG_INTR_END];
12362306a36Sopenharmony_ci	struct power_supply *psy_usb;
12462306a36Sopenharmony_ci	struct mutex lock;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* OTG/Host mode */
12762306a36Sopenharmony_ci	struct {
12862306a36Sopenharmony_ci		struct work_struct work;
12962306a36Sopenharmony_ci		struct extcon_dev *cable;
13062306a36Sopenharmony_ci		struct notifier_block id_nb;
13162306a36Sopenharmony_ci		bool id_short;
13262306a36Sopenharmony_ci	} otg;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* SDP/CDP/DCP USB charging cable notifications */
13562306a36Sopenharmony_ci	struct {
13662306a36Sopenharmony_ci		struct extcon_dev *edev;
13762306a36Sopenharmony_ci		struct notifier_block nb;
13862306a36Sopenharmony_ci		struct work_struct work;
13962306a36Sopenharmony_ci	} cable;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	int cc;
14262306a36Sopenharmony_ci	int cv;
14362306a36Sopenharmony_ci	int max_cc;
14462306a36Sopenharmony_ci	int max_cv;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	unsigned long last_updated;
14762306a36Sopenharmony_ci	unsigned int input_status;
14862306a36Sopenharmony_ci	unsigned int op_mode;
14962306a36Sopenharmony_ci	unsigned int backend_control;
15062306a36Sopenharmony_ci	bool valid;
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	u8 reg_val;
15662306a36Sopenharmony_ci	int ret;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (cc < CHRG_CCCV_CC_OFFSET)
15962306a36Sopenharmony_ci		cc = CHRG_CCCV_CC_OFFSET;
16062306a36Sopenharmony_ci	else if (cc > info->max_cc)
16162306a36Sopenharmony_ci		cc = info->max_cc;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	reg_val = (cc - CHRG_CCCV_CC_OFFSET) / CHRG_CCCV_CC_LSB_RES;
16462306a36Sopenharmony_ci	cc = (reg_val * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
16562306a36Sopenharmony_ci	reg_val = reg_val << CHRG_CCCV_CC_BIT_POS;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap,
16862306a36Sopenharmony_ci				AXP20X_CHRG_CTRL1,
16962306a36Sopenharmony_ci				CHRG_CCCV_CC_MASK, reg_val);
17062306a36Sopenharmony_ci	if (ret >= 0)
17162306a36Sopenharmony_ci		info->cc = cc;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return ret;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	u8 reg_val;
17962306a36Sopenharmony_ci	int ret;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (cv <= CV_4100MV) {
18262306a36Sopenharmony_ci		reg_val = CHRG_CCCV_CV_4100MV;
18362306a36Sopenharmony_ci		cv = CV_4100MV;
18462306a36Sopenharmony_ci	} else if (cv <= CV_4150MV) {
18562306a36Sopenharmony_ci		reg_val = CHRG_CCCV_CV_4150MV;
18662306a36Sopenharmony_ci		cv = CV_4150MV;
18762306a36Sopenharmony_ci	} else if (cv <= CV_4200MV) {
18862306a36Sopenharmony_ci		reg_val = CHRG_CCCV_CV_4200MV;
18962306a36Sopenharmony_ci		cv = CV_4200MV;
19062306a36Sopenharmony_ci	} else {
19162306a36Sopenharmony_ci		reg_val = CHRG_CCCV_CV_4350MV;
19262306a36Sopenharmony_ci		cv = CV_4350MV;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	reg_val = reg_val << CHRG_CCCV_CV_BIT_POS;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap,
19862306a36Sopenharmony_ci				AXP20X_CHRG_CTRL1,
19962306a36Sopenharmony_ci				CHRG_CCCV_CV_MASK, reg_val);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (ret >= 0)
20262306a36Sopenharmony_ci		info->cv = cv;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return ret;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int axp288_charger_get_vbus_inlmt(struct axp288_chrg_info *info)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	unsigned int val;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	val = info->backend_control;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	val >>= CHRG_VBUS_ILIM_BIT_POS;
21462306a36Sopenharmony_ci	switch (val) {
21562306a36Sopenharmony_ci	case CHRG_VBUS_ILIM_100MA:
21662306a36Sopenharmony_ci		return 100000;
21762306a36Sopenharmony_ci	case CHRG_VBUS_ILIM_500MA:
21862306a36Sopenharmony_ci		return 500000;
21962306a36Sopenharmony_ci	case CHRG_VBUS_ILIM_900MA:
22062306a36Sopenharmony_ci		return 900000;
22162306a36Sopenharmony_ci	case CHRG_VBUS_ILIM_1500MA:
22262306a36Sopenharmony_ci		return 1500000;
22362306a36Sopenharmony_ci	case CHRG_VBUS_ILIM_2000MA:
22462306a36Sopenharmony_ci		return 2000000;
22562306a36Sopenharmony_ci	case CHRG_VBUS_ILIM_2500MA:
22662306a36Sopenharmony_ci		return 2500000;
22762306a36Sopenharmony_ci	case CHRG_VBUS_ILIM_3000MA:
22862306a36Sopenharmony_ci		return 3000000;
22962306a36Sopenharmony_ci	case CHRG_VBUS_ILIM_3500MA:
23062306a36Sopenharmony_ci		return 3500000;
23162306a36Sopenharmony_ci	default:
23262306a36Sopenharmony_ci		/* All b1xxx values map to 4000 mA */
23362306a36Sopenharmony_ci		return 4000000;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,
23862306a36Sopenharmony_ci					   int inlmt)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	int ret;
24162306a36Sopenharmony_ci	u8 reg_val;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (inlmt >= 4000000)
24462306a36Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_4000MA << CHRG_VBUS_ILIM_BIT_POS;
24562306a36Sopenharmony_ci	else if (inlmt >= 3500000)
24662306a36Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_3500MA << CHRG_VBUS_ILIM_BIT_POS;
24762306a36Sopenharmony_ci	else if (inlmt >= 3000000)
24862306a36Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_3000MA << CHRG_VBUS_ILIM_BIT_POS;
24962306a36Sopenharmony_ci	else if (inlmt >= 2500000)
25062306a36Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_2500MA << CHRG_VBUS_ILIM_BIT_POS;
25162306a36Sopenharmony_ci	else if (inlmt >= 2000000)
25262306a36Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_2000MA << CHRG_VBUS_ILIM_BIT_POS;
25362306a36Sopenharmony_ci	else if (inlmt >= 1500000)
25462306a36Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_1500MA << CHRG_VBUS_ILIM_BIT_POS;
25562306a36Sopenharmony_ci	else if (inlmt >= 900000)
25662306a36Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_900MA << CHRG_VBUS_ILIM_BIT_POS;
25762306a36Sopenharmony_ci	else if (inlmt >= 500000)
25862306a36Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_500MA << CHRG_VBUS_ILIM_BIT_POS;
25962306a36Sopenharmony_ci	else
26062306a36Sopenharmony_ci		reg_val = CHRG_VBUS_ILIM_100MA << CHRG_VBUS_ILIM_BIT_POS;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL,
26362306a36Sopenharmony_ci				 CHRG_VBUS_ILIM_MASK, reg_val);
26462306a36Sopenharmony_ci	if (ret < 0)
26562306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "charger BAK control %d\n", ret);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return ret;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic int axp288_charger_vbus_path_select(struct axp288_chrg_info *info,
27162306a36Sopenharmony_ci								bool enable)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	int ret;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (enable)
27662306a36Sopenharmony_ci		ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
27762306a36Sopenharmony_ci					VBUS_ISPOUT_VBUS_PATH_DIS, 0);
27862306a36Sopenharmony_ci	else
27962306a36Sopenharmony_ci		ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
28062306a36Sopenharmony_ci			VBUS_ISPOUT_VBUS_PATH_DIS, VBUS_ISPOUT_VBUS_PATH_DIS);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (ret < 0)
28362306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	return ret;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic int axp288_charger_enable_charger(struct axp288_chrg_info *info,
28962306a36Sopenharmony_ci								bool enable)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	int ret;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (enable)
29462306a36Sopenharmony_ci		ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
29562306a36Sopenharmony_ci				CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
29662306a36Sopenharmony_ci	else
29762306a36Sopenharmony_ci		ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
29862306a36Sopenharmony_ci				CHRG_CCCV_CHG_EN, 0);
29962306a36Sopenharmony_ci	if (ret < 0)
30062306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return ret;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int axp288_get_charger_health(struct axp288_chrg_info *info)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	if (!(info->input_status & PS_STAT_VBUS_PRESENT))
30862306a36Sopenharmony_ci		return POWER_SUPPLY_HEALTH_UNKNOWN;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (!(info->input_status & PS_STAT_VBUS_VALID))
31162306a36Sopenharmony_ci		return POWER_SUPPLY_HEALTH_DEAD;
31262306a36Sopenharmony_ci	else if (info->op_mode & CHRG_STAT_PMIC_OTP)
31362306a36Sopenharmony_ci		return POWER_SUPPLY_HEALTH_OVERHEAT;
31462306a36Sopenharmony_ci	else if (info->op_mode & CHRG_STAT_BAT_SAFE_MODE)
31562306a36Sopenharmony_ci		return POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
31662306a36Sopenharmony_ci	else
31762306a36Sopenharmony_ci		return POWER_SUPPLY_HEALTH_GOOD;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int axp288_charger_usb_set_property(struct power_supply *psy,
32162306a36Sopenharmony_ci				    enum power_supply_property psp,
32262306a36Sopenharmony_ci				    const union power_supply_propval *val)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
32562306a36Sopenharmony_ci	int ret = 0;
32662306a36Sopenharmony_ci	int scaled_val;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	mutex_lock(&info->lock);
32962306a36Sopenharmony_ci	switch (psp) {
33062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
33162306a36Sopenharmony_ci		scaled_val = min(val->intval, info->max_cc);
33262306a36Sopenharmony_ci		scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
33362306a36Sopenharmony_ci		ret = axp288_charger_set_cc(info, scaled_val);
33462306a36Sopenharmony_ci		if (ret < 0) {
33562306a36Sopenharmony_ci			dev_warn(&info->pdev->dev, "set charge current failed\n");
33662306a36Sopenharmony_ci			goto out;
33762306a36Sopenharmony_ci		}
33862306a36Sopenharmony_ci		break;
33962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
34062306a36Sopenharmony_ci		scaled_val = min(val->intval, info->max_cv);
34162306a36Sopenharmony_ci		scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
34262306a36Sopenharmony_ci		ret = axp288_charger_set_cv(info, scaled_val);
34362306a36Sopenharmony_ci		if (ret < 0) {
34462306a36Sopenharmony_ci			dev_warn(&info->pdev->dev, "set charge voltage failed\n");
34562306a36Sopenharmony_ci			goto out;
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci		break;
34862306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
34962306a36Sopenharmony_ci		ret = axp288_charger_set_vbus_inlmt(info, val->intval);
35062306a36Sopenharmony_ci		if (ret < 0) {
35162306a36Sopenharmony_ci			dev_warn(&info->pdev->dev, "set input current limit failed\n");
35262306a36Sopenharmony_ci			goto out;
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci		info->valid = false;
35562306a36Sopenharmony_ci		break;
35662306a36Sopenharmony_ci	default:
35762306a36Sopenharmony_ci		ret = -EINVAL;
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ciout:
36162306a36Sopenharmony_ci	mutex_unlock(&info->lock);
36262306a36Sopenharmony_ci	return ret;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic int axp288_charger_reg_readb(struct axp288_chrg_info *info, int reg, unsigned int *ret_val)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	int ret;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	ret = regmap_read(info->regmap, reg, ret_val);
37062306a36Sopenharmony_ci	if (ret < 0) {
37162306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "Error %d on reading value from register 0x%04x\n",
37262306a36Sopenharmony_ci			ret,
37362306a36Sopenharmony_ci			reg);
37462306a36Sopenharmony_ci		return ret;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci	return 0;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int axp288_charger_usb_update_property(struct axp288_chrg_info *info)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	int ret = 0;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL))
38462306a36Sopenharmony_ci		return 0;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	dev_dbg(&info->pdev->dev, "Charger updating register values...\n");
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	ret = iosf_mbi_block_punit_i2c_access();
38962306a36Sopenharmony_ci	if (ret < 0)
39062306a36Sopenharmony_ci		return ret;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	ret = axp288_charger_reg_readb(info, AXP20X_PWR_INPUT_STATUS, &info->input_status);
39362306a36Sopenharmony_ci	if (ret < 0)
39462306a36Sopenharmony_ci		goto out;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	ret = axp288_charger_reg_readb(info, AXP20X_PWR_OP_MODE, &info->op_mode);
39762306a36Sopenharmony_ci	if (ret < 0)
39862306a36Sopenharmony_ci		goto out;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	ret = axp288_charger_reg_readb(info, AXP20X_CHRG_BAK_CTRL, &info->backend_control);
40162306a36Sopenharmony_ci	if (ret < 0)
40262306a36Sopenharmony_ci		goto out;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	info->last_updated = jiffies;
40562306a36Sopenharmony_ci	info->valid = true;
40662306a36Sopenharmony_ciout:
40762306a36Sopenharmony_ci	iosf_mbi_unblock_punit_i2c_access();
40862306a36Sopenharmony_ci	return ret;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic int axp288_charger_usb_get_property(struct power_supply *psy,
41262306a36Sopenharmony_ci				    enum power_supply_property psp,
41362306a36Sopenharmony_ci				    union power_supply_propval *val)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
41662306a36Sopenharmony_ci	int ret;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	mutex_lock(&info->lock);
41962306a36Sopenharmony_ci	ret = axp288_charger_usb_update_property(info);
42062306a36Sopenharmony_ci	if (ret < 0)
42162306a36Sopenharmony_ci		goto out;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	switch (psp) {
42462306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
42562306a36Sopenharmony_ci		/* Check for OTG case first */
42662306a36Sopenharmony_ci		if (info->otg.id_short) {
42762306a36Sopenharmony_ci			val->intval = 0;
42862306a36Sopenharmony_ci			break;
42962306a36Sopenharmony_ci		}
43062306a36Sopenharmony_ci		val->intval = (info->input_status & PS_STAT_VBUS_PRESENT) ? 1 : 0;
43162306a36Sopenharmony_ci		break;
43262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
43362306a36Sopenharmony_ci		/* Check for OTG case first */
43462306a36Sopenharmony_ci		if (info->otg.id_short) {
43562306a36Sopenharmony_ci			val->intval = 0;
43662306a36Sopenharmony_ci			break;
43762306a36Sopenharmony_ci		}
43862306a36Sopenharmony_ci		val->intval = (info->input_status & PS_STAT_VBUS_VALID) ? 1 : 0;
43962306a36Sopenharmony_ci		break;
44062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
44162306a36Sopenharmony_ci		val->intval = axp288_get_charger_health(info);
44262306a36Sopenharmony_ci		break;
44362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
44462306a36Sopenharmony_ci		val->intval = info->cc * 1000;
44562306a36Sopenharmony_ci		break;
44662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
44762306a36Sopenharmony_ci		val->intval = info->max_cc * 1000;
44862306a36Sopenharmony_ci		break;
44962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
45062306a36Sopenharmony_ci		val->intval = info->cv * 1000;
45162306a36Sopenharmony_ci		break;
45262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
45362306a36Sopenharmony_ci		val->intval = info->max_cv * 1000;
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
45662306a36Sopenharmony_ci		val->intval = axp288_charger_get_vbus_inlmt(info);
45762306a36Sopenharmony_ci		break;
45862306a36Sopenharmony_ci	default:
45962306a36Sopenharmony_ci		ret = -EINVAL;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ciout:
46362306a36Sopenharmony_ci	mutex_unlock(&info->lock);
46462306a36Sopenharmony_ci	return ret;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic int axp288_charger_property_is_writeable(struct power_supply *psy,
46862306a36Sopenharmony_ci		enum power_supply_property psp)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	int ret;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	switch (psp) {
47362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
47462306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
47562306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
47662306a36Sopenharmony_ci		ret = 1;
47762306a36Sopenharmony_ci		break;
47862306a36Sopenharmony_ci	default:
47962306a36Sopenharmony_ci		ret = 0;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return ret;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic enum power_supply_property axp288_usb_props[] = {
48662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
48762306a36Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
48862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_TYPE,
48962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
49062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
49162306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
49262306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
49362306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
49462306a36Sopenharmony_ci	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
49562306a36Sopenharmony_ci};
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic const struct power_supply_desc axp288_charger_desc = {
49862306a36Sopenharmony_ci	.name			= "axp288_charger",
49962306a36Sopenharmony_ci	.type			= POWER_SUPPLY_TYPE_USB,
50062306a36Sopenharmony_ci	.properties		= axp288_usb_props,
50162306a36Sopenharmony_ci	.num_properties		= ARRAY_SIZE(axp288_usb_props),
50262306a36Sopenharmony_ci	.get_property		= axp288_charger_usb_get_property,
50362306a36Sopenharmony_ci	.set_property		= axp288_charger_usb_set_property,
50462306a36Sopenharmony_ci	.property_is_writeable	= axp288_charger_property_is_writeable,
50562306a36Sopenharmony_ci};
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_cistatic irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct axp288_chrg_info *info = dev;
51062306a36Sopenharmony_ci	int i;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	for (i = 0; i < CHRG_INTR_END; i++) {
51362306a36Sopenharmony_ci		if (info->irq[i] == irq)
51462306a36Sopenharmony_ci			break;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (i >= CHRG_INTR_END) {
51862306a36Sopenharmony_ci		dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
51962306a36Sopenharmony_ci		return IRQ_NONE;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	switch (i) {
52362306a36Sopenharmony_ci	case VBUS_OV_IRQ:
52462306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev, "VBUS Over Voltage INTR\n");
52562306a36Sopenharmony_ci		break;
52662306a36Sopenharmony_ci	case CHARGE_DONE_IRQ:
52762306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev, "Charging Done INTR\n");
52862306a36Sopenharmony_ci		break;
52962306a36Sopenharmony_ci	case CHARGE_CHARGING_IRQ:
53062306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev, "Start Charging IRQ\n");
53162306a36Sopenharmony_ci		break;
53262306a36Sopenharmony_ci	case BAT_SAFE_QUIT_IRQ:
53362306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev,
53462306a36Sopenharmony_ci			"Quit Safe Mode(restart timer) Charging IRQ\n");
53562306a36Sopenharmony_ci		break;
53662306a36Sopenharmony_ci	case BAT_SAFE_ENTER_IRQ:
53762306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev,
53862306a36Sopenharmony_ci			"Enter Safe Mode(timer expire) Charging IRQ\n");
53962306a36Sopenharmony_ci		break;
54062306a36Sopenharmony_ci	case QCBTU_IRQ:
54162306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev,
54262306a36Sopenharmony_ci			"Quit Battery Under Temperature(CHRG) INTR\n");
54362306a36Sopenharmony_ci		break;
54462306a36Sopenharmony_ci	case CBTU_IRQ:
54562306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev,
54662306a36Sopenharmony_ci			"Hit Battery Under Temperature(CHRG) INTR\n");
54762306a36Sopenharmony_ci		break;
54862306a36Sopenharmony_ci	case QCBTO_IRQ:
54962306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev,
55062306a36Sopenharmony_ci			"Quit Battery Over Temperature(CHRG) INTR\n");
55162306a36Sopenharmony_ci		break;
55262306a36Sopenharmony_ci	case CBTO_IRQ:
55362306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev,
55462306a36Sopenharmony_ci			"Hit Battery Over Temperature(CHRG) INTR\n");
55562306a36Sopenharmony_ci		break;
55662306a36Sopenharmony_ci	default:
55762306a36Sopenharmony_ci		dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
55862306a36Sopenharmony_ci		goto out;
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci	mutex_lock(&info->lock);
56162306a36Sopenharmony_ci	info->valid = false;
56262306a36Sopenharmony_ci	mutex_unlock(&info->lock);
56362306a36Sopenharmony_ci	power_supply_changed(info->psy_usb);
56462306a36Sopenharmony_ciout:
56562306a36Sopenharmony_ci	return IRQ_HANDLED;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci/*
56962306a36Sopenharmony_ci * The HP Pavilion x2 10 series comes in a number of variants:
57062306a36Sopenharmony_ci * Bay Trail SoC    + AXP288 PMIC, Micro-USB, DMI_BOARD_NAME: "8021"
57162306a36Sopenharmony_ci * Bay Trail SoC    + AXP288 PMIC, Type-C,    DMI_BOARD_NAME: "815D"
57262306a36Sopenharmony_ci * Cherry Trail SoC + AXP288 PMIC, Type-C,    DMI_BOARD_NAME: "813E"
57362306a36Sopenharmony_ci * Cherry Trail SoC + TI PMIC,     Type-C,    DMI_BOARD_NAME: "827C" or "82F4"
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * The variants with the AXP288 + Type-C connector are all kinds of special:
57662306a36Sopenharmony_ci *
57762306a36Sopenharmony_ci * 1. They use a Type-C connector which the AXP288 does not support, so when
57862306a36Sopenharmony_ci * using a Type-C charger it is not recognized. Unlike most AXP288 devices,
57962306a36Sopenharmony_ci * this model actually has mostly working ACPI AC / Battery code, the ACPI code
58062306a36Sopenharmony_ci * "solves" this by simply setting the input_current_limit to 3A.
58162306a36Sopenharmony_ci * There are still some issues with the ACPI code, so we use this native driver,
58262306a36Sopenharmony_ci * and to solve the charging not working (500mA is not enough) issue we hardcode
58362306a36Sopenharmony_ci * the 3A input_current_limit like the ACPI code does.
58462306a36Sopenharmony_ci *
58562306a36Sopenharmony_ci * 2. If no charger is connected the machine boots with the vbus-path disabled.
58662306a36Sopenharmony_ci * Normally this is done when a 5V boost converter is active to avoid the PMIC
58762306a36Sopenharmony_ci * trying to charge from the 5V boost converter's output. This is done when
58862306a36Sopenharmony_ci * an OTG host cable is inserted and the ID pin on the micro-B receptacle is
58962306a36Sopenharmony_ci * pulled low and the ID pin has an ACPI event handler associated with it
59062306a36Sopenharmony_ci * which re-enables the vbus-path when the ID pin is pulled high when the
59162306a36Sopenharmony_ci * OTG host cable is removed. The Type-C connector has no ID pin, there is
59262306a36Sopenharmony_ci * no ID pin handler and there appears to be no 5V boost converter, so we
59362306a36Sopenharmony_ci * end up not charging because the vbus-path is disabled, until we unplug
59462306a36Sopenharmony_ci * the charger which automatically clears the vbus-path disable bit and then
59562306a36Sopenharmony_ci * on the second plug-in of the adapter we start charging. To solve the not
59662306a36Sopenharmony_ci * charging on first charger plugin we unconditionally enable the vbus-path at
59762306a36Sopenharmony_ci * probe on this model, which is safe since there is no 5V boost converter.
59862306a36Sopenharmony_ci */
59962306a36Sopenharmony_cistatic const struct dmi_system_id axp288_hp_x2_dmi_ids[] = {
60062306a36Sopenharmony_ci	{
60162306a36Sopenharmony_ci		.matches = {
60262306a36Sopenharmony_ci			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
60362306a36Sopenharmony_ci			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
60462306a36Sopenharmony_ci			DMI_EXACT_MATCH(DMI_BOARD_NAME, "815D"),
60562306a36Sopenharmony_ci		},
60662306a36Sopenharmony_ci	},
60762306a36Sopenharmony_ci	{
60862306a36Sopenharmony_ci		.matches = {
60962306a36Sopenharmony_ci			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "HP"),
61062306a36Sopenharmony_ci			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"),
61162306a36Sopenharmony_ci			DMI_EXACT_MATCH(DMI_BOARD_NAME, "813E"),
61262306a36Sopenharmony_ci		},
61362306a36Sopenharmony_ci	},
61462306a36Sopenharmony_ci	{} /* Terminating entry */
61562306a36Sopenharmony_ci};
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic void axp288_charger_extcon_evt_worker(struct work_struct *work)
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci	struct axp288_chrg_info *info =
62062306a36Sopenharmony_ci	    container_of(work, struct axp288_chrg_info, cable.work);
62162306a36Sopenharmony_ci	int ret, current_limit;
62262306a36Sopenharmony_ci	struct extcon_dev *edev = info->cable.edev;
62362306a36Sopenharmony_ci	unsigned int val;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
62662306a36Sopenharmony_ci	if (ret < 0) {
62762306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "Error reading status (%d)\n", ret);
62862306a36Sopenharmony_ci		return;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/* Offline? Disable charging and bail */
63262306a36Sopenharmony_ci	if (!(val & PS_STAT_VBUS_VALID)) {
63362306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev, "USB charger disconnected\n");
63462306a36Sopenharmony_ci		axp288_charger_enable_charger(info, false);
63562306a36Sopenharmony_ci		mutex_lock(&info->lock);
63662306a36Sopenharmony_ci		info->valid = false;
63762306a36Sopenharmony_ci		mutex_unlock(&info->lock);
63862306a36Sopenharmony_ci		power_supply_changed(info->psy_usb);
63962306a36Sopenharmony_ci		return;
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	/* Determine cable/charger type */
64362306a36Sopenharmony_ci	if (dmi_check_system(axp288_hp_x2_dmi_ids)) {
64462306a36Sopenharmony_ci		/* See comment above axp288_hp_x2_dmi_ids declaration */
64562306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev, "HP X2 with Type-C, setting inlmt to 3A\n");
64662306a36Sopenharmony_ci		current_limit = 3000000;
64762306a36Sopenharmony_ci	} else if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
64862306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n");
64962306a36Sopenharmony_ci		current_limit = 500000;
65062306a36Sopenharmony_ci	} else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
65162306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n");
65262306a36Sopenharmony_ci		current_limit = 1500000;
65362306a36Sopenharmony_ci	} else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {
65462306a36Sopenharmony_ci		dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n");
65562306a36Sopenharmony_ci		current_limit = 2000000;
65662306a36Sopenharmony_ci	} else {
65762306a36Sopenharmony_ci		/* Charger type detection still in progress, bail. */
65862306a36Sopenharmony_ci		return;
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	/* Set vbus current limit first, then enable charger */
66262306a36Sopenharmony_ci	ret = axp288_charger_set_vbus_inlmt(info, current_limit);
66362306a36Sopenharmony_ci	if (ret == 0)
66462306a36Sopenharmony_ci		axp288_charger_enable_charger(info, true);
66562306a36Sopenharmony_ci	else
66662306a36Sopenharmony_ci		dev_err(&info->pdev->dev,
66762306a36Sopenharmony_ci			"error setting current limit (%d)\n", ret);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	mutex_lock(&info->lock);
67062306a36Sopenharmony_ci	info->valid = false;
67162306a36Sopenharmony_ci	mutex_unlock(&info->lock);
67262306a36Sopenharmony_ci	power_supply_changed(info->psy_usb);
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic int axp288_charger_handle_cable_evt(struct notifier_block *nb,
67662306a36Sopenharmony_ci					   unsigned long event, void *param)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct axp288_chrg_info *info =
67962306a36Sopenharmony_ci		container_of(nb, struct axp288_chrg_info, cable.nb);
68062306a36Sopenharmony_ci	schedule_work(&info->cable.work);
68162306a36Sopenharmony_ci	return NOTIFY_OK;
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic void axp288_charger_otg_evt_worker(struct work_struct *work)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct axp288_chrg_info *info =
68762306a36Sopenharmony_ci	    container_of(work, struct axp288_chrg_info, otg.work);
68862306a36Sopenharmony_ci	struct extcon_dev *edev = info->otg.cable;
68962306a36Sopenharmony_ci	int ret, usb_host = extcon_get_state(edev, EXTCON_USB_HOST);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
69262306a36Sopenharmony_ci				usb_host ? "attached" : "detached");
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	/*
69562306a36Sopenharmony_ci	 * Set usb_id_short flag to avoid running charger detection logic
69662306a36Sopenharmony_ci	 * in case usb host.
69762306a36Sopenharmony_ci	 */
69862306a36Sopenharmony_ci	info->otg.id_short = usb_host;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	/* Disable VBUS path before enabling the 5V boost */
70162306a36Sopenharmony_ci	ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);
70262306a36Sopenharmony_ci	if (ret < 0)
70362306a36Sopenharmony_ci		dev_warn(&info->pdev->dev, "vbus path disable failed\n");
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic int axp288_charger_handle_otg_evt(struct notifier_block *nb,
70762306a36Sopenharmony_ci				   unsigned long event, void *param)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct axp288_chrg_info *info =
71062306a36Sopenharmony_ci	    container_of(nb, struct axp288_chrg_info, otg.id_nb);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	schedule_work(&info->otg.work);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	return NOTIFY_OK;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic int charger_init_hw_regs(struct axp288_chrg_info *info)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	int ret, cc, cv;
72062306a36Sopenharmony_ci	unsigned int val;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* Program temperature thresholds */
72362306a36Sopenharmony_ci	ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
72462306a36Sopenharmony_ci	if (ret < 0) {
72562306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
72662306a36Sopenharmony_ci							AXP20X_V_LTF_CHRG, ret);
72762306a36Sopenharmony_ci		return ret;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
73162306a36Sopenharmony_ci	if (ret < 0) {
73262306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
73362306a36Sopenharmony_ci							AXP20X_V_HTF_CHRG, ret);
73462306a36Sopenharmony_ci		return ret;
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	/* Do not turn-off charger o/p after charge cycle ends */
73862306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap,
73962306a36Sopenharmony_ci				AXP20X_CHRG_CTRL2,
74062306a36Sopenharmony_ci				CNTL2_CHG_OUT_TURNON, CNTL2_CHG_OUT_TURNON);
74162306a36Sopenharmony_ci	if (ret < 0) {
74262306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
74362306a36Sopenharmony_ci						AXP20X_CHRG_CTRL2, ret);
74462306a36Sopenharmony_ci		return ret;
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	/* Setup ending condition for charging to be 10% of I(chrg) */
74862306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap,
74962306a36Sopenharmony_ci				AXP20X_CHRG_CTRL1,
75062306a36Sopenharmony_ci				CHRG_CCCV_ITERM_20P, 0);
75162306a36Sopenharmony_ci	if (ret < 0) {
75262306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
75362306a36Sopenharmony_ci						AXP20X_CHRG_CTRL1, ret);
75462306a36Sopenharmony_ci		return ret;
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/* Disable OCV-SOC curve calibration */
75862306a36Sopenharmony_ci	ret = regmap_update_bits(info->regmap,
75962306a36Sopenharmony_ci				AXP20X_CC_CTRL,
76062306a36Sopenharmony_ci				FG_CNTL_OCV_ADJ_EN, 0);
76162306a36Sopenharmony_ci	if (ret < 0) {
76262306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
76362306a36Sopenharmony_ci						AXP20X_CC_CTRL, ret);
76462306a36Sopenharmony_ci		return ret;
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (dmi_check_system(axp288_hp_x2_dmi_ids)) {
76862306a36Sopenharmony_ci		/* See comment above axp288_hp_x2_dmi_ids declaration */
76962306a36Sopenharmony_ci		ret = axp288_charger_vbus_path_select(info, true);
77062306a36Sopenharmony_ci		if (ret < 0)
77162306a36Sopenharmony_ci			return ret;
77262306a36Sopenharmony_ci	} else {
77362306a36Sopenharmony_ci		/* Set Vhold to the factory default / recommended 4.4V */
77462306a36Sopenharmony_ci		val = VBUS_ISPOUT_VHOLD_SET_4400MV << VBUS_ISPOUT_VHOLD_SET_BIT_POS;
77562306a36Sopenharmony_ci		ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
77662306a36Sopenharmony_ci					 VBUS_ISPOUT_VHOLD_SET_MASK, val);
77762306a36Sopenharmony_ci		if (ret < 0) {
77862306a36Sopenharmony_ci			dev_err(&info->pdev->dev, "register(%x) write error(%d)\n",
77962306a36Sopenharmony_ci				AXP20X_VBUS_IPSOUT_MGMT, ret);
78062306a36Sopenharmony_ci			return ret;
78162306a36Sopenharmony_ci		}
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	/* Read current charge voltage and current limit */
78562306a36Sopenharmony_ci	ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
78662306a36Sopenharmony_ci	if (ret < 0) {
78762306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "register(%x) read error(%d)\n",
78862306a36Sopenharmony_ci			AXP20X_CHRG_CTRL1, ret);
78962306a36Sopenharmony_ci		return ret;
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	/* Determine charge voltage */
79362306a36Sopenharmony_ci	cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
79462306a36Sopenharmony_ci	switch (cv) {
79562306a36Sopenharmony_ci	case CHRG_CCCV_CV_4100MV:
79662306a36Sopenharmony_ci		info->cv = CV_4100MV;
79762306a36Sopenharmony_ci		break;
79862306a36Sopenharmony_ci	case CHRG_CCCV_CV_4150MV:
79962306a36Sopenharmony_ci		info->cv = CV_4150MV;
80062306a36Sopenharmony_ci		break;
80162306a36Sopenharmony_ci	case CHRG_CCCV_CV_4200MV:
80262306a36Sopenharmony_ci		info->cv = CV_4200MV;
80362306a36Sopenharmony_ci		break;
80462306a36Sopenharmony_ci	case CHRG_CCCV_CV_4350MV:
80562306a36Sopenharmony_ci		info->cv = CV_4350MV;
80662306a36Sopenharmony_ci		break;
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	/* Determine charge current limit */
81062306a36Sopenharmony_ci	cc = (val & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
81162306a36Sopenharmony_ci	cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
81262306a36Sopenharmony_ci	info->cc = cc;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	/*
81562306a36Sopenharmony_ci	 * Do not allow the user to configure higher settings then those
81662306a36Sopenharmony_ci	 * set by the firmware
81762306a36Sopenharmony_ci	 */
81862306a36Sopenharmony_ci	info->max_cv = info->cv;
81962306a36Sopenharmony_ci	info->max_cc = info->cc;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	return 0;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic void axp288_charger_cancel_work(void *data)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	struct axp288_chrg_info *info = data;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	cancel_work_sync(&info->otg.work);
82962306a36Sopenharmony_ci	cancel_work_sync(&info->cable.work);
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cistatic int axp288_charger_probe(struct platform_device *pdev)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	int ret, i, pirq;
83562306a36Sopenharmony_ci	struct axp288_chrg_info *info;
83662306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
83762306a36Sopenharmony_ci	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
83862306a36Sopenharmony_ci	struct power_supply_config charger_cfg = {};
83962306a36Sopenharmony_ci	const char *extcon_name = NULL;
84062306a36Sopenharmony_ci	unsigned int val;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	/*
84362306a36Sopenharmony_ci	 * Normally the native AXP288 fg/charger drivers are preferred but
84462306a36Sopenharmony_ci	 * on some devices the ACPI drivers should be used instead.
84562306a36Sopenharmony_ci	 */
84662306a36Sopenharmony_ci	if (!acpi_quirk_skip_acpi_ac_and_battery())
84762306a36Sopenharmony_ci		return -ENODEV;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	/*
85062306a36Sopenharmony_ci	 * On some devices the fuelgauge and charger parts of the axp288 are
85162306a36Sopenharmony_ci	 * not used, check that the fuelgauge is enabled (CC_CTRL != 0).
85262306a36Sopenharmony_ci	 */
85362306a36Sopenharmony_ci	ret = regmap_read(axp20x->regmap, AXP20X_CC_CTRL, &val);
85462306a36Sopenharmony_ci	if (ret < 0)
85562306a36Sopenharmony_ci		return ret;
85662306a36Sopenharmony_ci	if (val == 0)
85762306a36Sopenharmony_ci		return -ENODEV;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
86062306a36Sopenharmony_ci	if (!info)
86162306a36Sopenharmony_ci		return -ENOMEM;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	mutex_init(&info->lock);
86462306a36Sopenharmony_ci	info->pdev = pdev;
86562306a36Sopenharmony_ci	info->regmap = axp20x->regmap;
86662306a36Sopenharmony_ci	info->regmap_irqc = axp20x->regmap_irqc;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
86962306a36Sopenharmony_ci	if (IS_ERR(info->cable.edev)) {
87062306a36Sopenharmony_ci		dev_err_probe(dev, PTR_ERR(info->cable.edev),
87162306a36Sopenharmony_ci			      "extcon_get_extcon_dev(%s) failed\n",
87262306a36Sopenharmony_ci			      AXP288_EXTCON_DEV_NAME);
87362306a36Sopenharmony_ci		return PTR_ERR(info->cable.edev);
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	/*
87762306a36Sopenharmony_ci	 * On devices with broken ACPI GPIO event handlers there also is no ACPI
87862306a36Sopenharmony_ci	 * "INT3496" (USB_HOST_EXTCON_HID) device. x86-android-tablets.ko
87962306a36Sopenharmony_ci	 * instantiates an "intel-int3496" extcon on these devs as a workaround.
88062306a36Sopenharmony_ci	 */
88162306a36Sopenharmony_ci	if (acpi_quirk_skip_gpio_event_handlers())
88262306a36Sopenharmony_ci		extcon_name = "intel-int3496";
88362306a36Sopenharmony_ci	else if (acpi_dev_present(USB_HOST_EXTCON_HID, NULL, -1))
88462306a36Sopenharmony_ci		extcon_name = USB_HOST_EXTCON_NAME;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	if (extcon_name) {
88762306a36Sopenharmony_ci		info->otg.cable = extcon_get_extcon_dev(extcon_name);
88862306a36Sopenharmony_ci		if (IS_ERR(info->otg.cable)) {
88962306a36Sopenharmony_ci			dev_err_probe(dev, PTR_ERR(info->otg.cable),
89062306a36Sopenharmony_ci				      "extcon_get_extcon_dev(%s) failed\n",
89162306a36Sopenharmony_ci				      USB_HOST_EXTCON_NAME);
89262306a36Sopenharmony_ci			return PTR_ERR(info->otg.cable);
89362306a36Sopenharmony_ci		}
89462306a36Sopenharmony_ci		dev_info(dev, "Using " USB_HOST_EXTCON_HID " extcon for usb-id\n");
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	platform_set_drvdata(pdev, info);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	ret = charger_init_hw_regs(info);
90062306a36Sopenharmony_ci	if (ret)
90162306a36Sopenharmony_ci		return ret;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	/* Register with power supply class */
90462306a36Sopenharmony_ci	charger_cfg.drv_data = info;
90562306a36Sopenharmony_ci	info->psy_usb = devm_power_supply_register(dev, &axp288_charger_desc,
90662306a36Sopenharmony_ci						   &charger_cfg);
90762306a36Sopenharmony_ci	if (IS_ERR(info->psy_usb)) {
90862306a36Sopenharmony_ci		ret = PTR_ERR(info->psy_usb);
90962306a36Sopenharmony_ci		dev_err(dev, "failed to register power supply: %d\n", ret);
91062306a36Sopenharmony_ci		return ret;
91162306a36Sopenharmony_ci	}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	/* Cancel our work on cleanup, register this before the notifiers */
91462306a36Sopenharmony_ci	ret = devm_add_action(dev, axp288_charger_cancel_work, info);
91562306a36Sopenharmony_ci	if (ret)
91662306a36Sopenharmony_ci		return ret;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	/* Register for extcon notification */
91962306a36Sopenharmony_ci	INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
92062306a36Sopenharmony_ci	info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
92162306a36Sopenharmony_ci	ret = devm_extcon_register_notifier_all(dev, info->cable.edev,
92262306a36Sopenharmony_ci						&info->cable.nb);
92362306a36Sopenharmony_ci	if (ret) {
92462306a36Sopenharmony_ci		dev_err(dev, "failed to register cable extcon notifier\n");
92562306a36Sopenharmony_ci		return ret;
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci	schedule_work(&info->cable.work);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	/* Register for OTG notification */
93062306a36Sopenharmony_ci	INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
93162306a36Sopenharmony_ci	info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
93262306a36Sopenharmony_ci	if (info->otg.cable) {
93362306a36Sopenharmony_ci		ret = devm_extcon_register_notifier(dev, info->otg.cable,
93462306a36Sopenharmony_ci					EXTCON_USB_HOST, &info->otg.id_nb);
93562306a36Sopenharmony_ci		if (ret) {
93662306a36Sopenharmony_ci			dev_err(dev, "failed to register EXTCON_USB_HOST notifier\n");
93762306a36Sopenharmony_ci			return ret;
93862306a36Sopenharmony_ci		}
93962306a36Sopenharmony_ci		schedule_work(&info->otg.work);
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	/* Register charger interrupts */
94362306a36Sopenharmony_ci	for (i = 0; i < CHRG_INTR_END; i++) {
94462306a36Sopenharmony_ci		pirq = platform_get_irq(info->pdev, i);
94562306a36Sopenharmony_ci		if (pirq < 0)
94662306a36Sopenharmony_ci			return pirq;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci		info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
94962306a36Sopenharmony_ci		if (info->irq[i] < 0) {
95062306a36Sopenharmony_ci			dev_warn(&info->pdev->dev,
95162306a36Sopenharmony_ci				"failed to get virtual interrupt=%d\n", pirq);
95262306a36Sopenharmony_ci			return info->irq[i];
95362306a36Sopenharmony_ci		}
95462306a36Sopenharmony_ci		ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
95562306a36Sopenharmony_ci					NULL, axp288_charger_irq_thread_handler,
95662306a36Sopenharmony_ci					IRQF_ONESHOT, info->pdev->name, info);
95762306a36Sopenharmony_ci		if (ret) {
95862306a36Sopenharmony_ci			dev_err(dev, "failed to request interrupt=%d\n",
95962306a36Sopenharmony_ci								info->irq[i]);
96062306a36Sopenharmony_ci			return ret;
96162306a36Sopenharmony_ci		}
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	return 0;
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_cistatic const struct platform_device_id axp288_charger_id_table[] = {
96862306a36Sopenharmony_ci	{ .name = "axp288_charger" },
96962306a36Sopenharmony_ci	{},
97062306a36Sopenharmony_ci};
97162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, axp288_charger_id_table);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic struct platform_driver axp288_charger_driver = {
97462306a36Sopenharmony_ci	.probe = axp288_charger_probe,
97562306a36Sopenharmony_ci	.id_table = axp288_charger_id_table,
97662306a36Sopenharmony_ci	.driver = {
97762306a36Sopenharmony_ci		.name = "axp288_charger",
97862306a36Sopenharmony_ci	},
97962306a36Sopenharmony_ci};
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_cimodule_platform_driver(axp288_charger_driver);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ciMODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
98462306a36Sopenharmony_ciMODULE_DESCRIPTION("X-power AXP288 Charger Driver");
98562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
986