10402e8ebSGene Chen // SPDX-License-Identifier: GPL-2.0
20402e8ebSGene Chen /*
30402e8ebSGene Chen  * Copyright (c) 2021 MediaTek Inc.
40402e8ebSGene Chen  */
50402e8ebSGene Chen 
60402e8ebSGene Chen #include <linux/devm-helpers.h>
70402e8ebSGene Chen #include <linux/init.h>
80402e8ebSGene Chen #include <linux/interrupt.h>
90402e8ebSGene Chen #include <linux/kernel.h>
100402e8ebSGene Chen #include <linux/linear_range.h>
110402e8ebSGene Chen #include <linux/module.h>
120402e8ebSGene Chen #include <linux/of.h>
130402e8ebSGene Chen #include <linux/platform_device.h>
140402e8ebSGene Chen #include <linux/power_supply.h>
150402e8ebSGene Chen #include <linux/property.h>
160402e8ebSGene Chen #include <linux/regmap.h>
170402e8ebSGene Chen #include <linux/regulator/driver.h>
180402e8ebSGene Chen 
190402e8ebSGene Chen #define MT6360_PMU_CHG_CTRL1	0x311
200402e8ebSGene Chen #define MT6360_PMU_CHG_CTRL2	0x312
210402e8ebSGene Chen #define MT6360_PMU_CHG_CTRL3	0x313
220402e8ebSGene Chen #define MT6360_PMU_CHG_CTRL4	0x314
230402e8ebSGene Chen #define MT6360_PMU_CHG_CTRL5	0x315
240402e8ebSGene Chen #define MT6360_PMU_CHG_CTRL6	0x316
250402e8ebSGene Chen #define MT6360_PMU_CHG_CTRL7	0x317
260402e8ebSGene Chen #define MT6360_PMU_CHG_CTRL8	0x318
270402e8ebSGene Chen #define MT6360_PMU_CHG_CTRL9	0x319
280402e8ebSGene Chen #define MT6360_PMU_CHG_CTRL10	0x31A
290402e8ebSGene Chen #define MT6360_PMU_DEVICE_TYPE	0x322
300402e8ebSGene Chen #define MT6360_PMU_USB_STATUS1	0x327
310402e8ebSGene Chen #define MT6360_PMU_CHG_STAT	0x34A
320402e8ebSGene Chen #define MT6360_PMU_CHG_CTRL19	0x361
330402e8ebSGene Chen #define MT6360_PMU_FOD_STAT	0x3E7
340402e8ebSGene Chen 
350402e8ebSGene Chen /* MT6360_PMU_CHG_CTRL1 */
360402e8ebSGene Chen #define MT6360_FSLP_SHFT	(3)
370402e8ebSGene Chen #define MT6360_FSLP_MASK	BIT(MT6360_FSLP_SHFT)
380402e8ebSGene Chen #define MT6360_OPA_MODE_SHFT	(0)
390402e8ebSGene Chen #define MT6360_OPA_MODE_MASK	BIT(MT6360_OPA_MODE_SHFT)
400402e8ebSGene Chen /* MT6360_PMU_CHG_CTRL2 */
410402e8ebSGene Chen #define MT6360_IINLMTSEL_SHFT	(2)
420402e8ebSGene Chen #define MT6360_IINLMTSEL_MASK	GENMASK(3, 2)
430402e8ebSGene Chen /* MT6360_PMU_CHG_CTRL3 */
440402e8ebSGene Chen #define MT6360_IAICR_SHFT	(2)
450402e8ebSGene Chen #define MT6360_IAICR_MASK	GENMASK(7, 2)
460402e8ebSGene Chen #define MT6360_ILIM_EN_MASK	BIT(0)
470402e8ebSGene Chen /* MT6360_PMU_CHG_CTRL4 */
480402e8ebSGene Chen #define MT6360_VOREG_SHFT	(1)
490402e8ebSGene Chen #define MT6360_VOREG_MASK	GENMASK(7, 1)
500402e8ebSGene Chen /* MT6360_PMU_CHG_CTRL5 */
510402e8ebSGene Chen #define MT6360_VOBST_MASK	GENMASK(7, 2)
520402e8ebSGene Chen /* MT6360_PMU_CHG_CTRL6 */
530402e8ebSGene Chen #define MT6360_VMIVR_SHFT      (1)
540402e8ebSGene Chen #define MT6360_VMIVR_MASK      GENMASK(7, 1)
550402e8ebSGene Chen /* MT6360_PMU_CHG_CTRL7 */
560402e8ebSGene Chen #define MT6360_ICHG_SHFT	(2)
570402e8ebSGene Chen #define MT6360_ICHG_MASK	GENMASK(7, 2)
580402e8ebSGene Chen /* MT6360_PMU_CHG_CTRL8 */
590402e8ebSGene Chen #define MT6360_IPREC_SHFT	(0)
600402e8ebSGene Chen #define MT6360_IPREC_MASK	GENMASK(3, 0)
610402e8ebSGene Chen /* MT6360_PMU_CHG_CTRL9 */
620402e8ebSGene Chen #define MT6360_IEOC_SHFT	(4)
630402e8ebSGene Chen #define MT6360_IEOC_MASK	GENMASK(7, 4)
640402e8ebSGene Chen /* MT6360_PMU_CHG_CTRL10 */
650402e8ebSGene Chen #define MT6360_OTG_OC_MASK	GENMASK(3, 0)
660402e8ebSGene Chen /* MT6360_PMU_DEVICE_TYPE */
670402e8ebSGene Chen #define MT6360_USBCHGEN_MASK	BIT(7)
680402e8ebSGene Chen /* MT6360_PMU_USB_STATUS1 */
690402e8ebSGene Chen #define MT6360_USB_STATUS_SHFT	(4)
700402e8ebSGene Chen #define MT6360_USB_STATUS_MASK	GENMASK(6, 4)
710402e8ebSGene Chen /* MT6360_PMU_CHG_STAT */
720402e8ebSGene Chen #define MT6360_CHG_STAT_SHFT	(6)
730402e8ebSGene Chen #define MT6360_CHG_STAT_MASK	GENMASK(7, 6)
740402e8ebSGene Chen #define MT6360_VBAT_LVL_MASK	BIT(5)
750402e8ebSGene Chen /* MT6360_PMU_CHG_CTRL19 */
760402e8ebSGene Chen #define MT6360_VINOVP_SHFT	(5)
770402e8ebSGene Chen #define MT6360_VINOVP_MASK	GENMASK(6, 5)
780402e8ebSGene Chen /* MT6360_PMU_FOD_STAT */
790402e8ebSGene Chen #define MT6360_CHRDET_EXT_MASK	BIT(4)
800402e8ebSGene Chen 
810402e8ebSGene Chen /* uV */
820402e8ebSGene Chen #define MT6360_VMIVR_MIN	3900000
830402e8ebSGene Chen #define MT6360_VMIVR_MAX	13400000
840402e8ebSGene Chen #define MT6360_VMIVR_STEP	100000
850402e8ebSGene Chen /* uA */
860402e8ebSGene Chen #define MT6360_ICHG_MIN		100000
870402e8ebSGene Chen #define MT6360_ICHG_MAX		5000000
880402e8ebSGene Chen #define MT6360_ICHG_STEP	100000
890402e8ebSGene Chen /* uV */
900402e8ebSGene Chen #define MT6360_VOREG_MIN	3900000
910402e8ebSGene Chen #define MT6360_VOREG_MAX	4710000
920402e8ebSGene Chen #define MT6360_VOREG_STEP	10000
930402e8ebSGene Chen /* uA */
940402e8ebSGene Chen #define MT6360_AICR_MIN		100000
950402e8ebSGene Chen #define MT6360_AICR_MAX		3250000
960402e8ebSGene Chen #define MT6360_AICR_STEP	50000
970402e8ebSGene Chen /* uA */
980402e8ebSGene Chen #define MT6360_IPREC_MIN	100000
990402e8ebSGene Chen #define MT6360_IPREC_MAX	850000
1000402e8ebSGene Chen #define MT6360_IPREC_STEP	50000
1010402e8ebSGene Chen /* uA */
1020402e8ebSGene Chen #define MT6360_IEOC_MIN		100000
1030402e8ebSGene Chen #define MT6360_IEOC_MAX		850000
1040402e8ebSGene Chen #define MT6360_IEOC_STEP	50000
1050402e8ebSGene Chen 
1060402e8ebSGene Chen enum {
1070402e8ebSGene Chen 	MT6360_RANGE_VMIVR,
1080402e8ebSGene Chen 	MT6360_RANGE_ICHG,
1090402e8ebSGene Chen 	MT6360_RANGE_VOREG,
1100402e8ebSGene Chen 	MT6360_RANGE_AICR,
1110402e8ebSGene Chen 	MT6360_RANGE_IPREC,
1120402e8ebSGene Chen 	MT6360_RANGE_IEOC,
1130402e8ebSGene Chen 	MT6360_RANGE_MAX,
1140402e8ebSGene Chen };
1150402e8ebSGene Chen 
1160402e8ebSGene Chen static const struct linear_range mt6360_chg_range[MT6360_RANGE_MAX] = {
117eae063f6SMatti Vaittinen 	LINEAR_RANGE_IDX(MT6360_RANGE_VMIVR, 3900000, 0, 0x5F, 100000),
118eae063f6SMatti Vaittinen 	LINEAR_RANGE_IDX(MT6360_RANGE_ICHG, 100000, 0, 0x31, 100000),
119eae063f6SMatti Vaittinen 	LINEAR_RANGE_IDX(MT6360_RANGE_VOREG, 3900000, 0, 0x51, 10000),
120eae063f6SMatti Vaittinen 	LINEAR_RANGE_IDX(MT6360_RANGE_AICR, 100000, 0, 0x3F, 50000),
121eae063f6SMatti Vaittinen 	LINEAR_RANGE_IDX(MT6360_RANGE_IPREC, 100000, 0, 0x0F, 50000),
122eae063f6SMatti Vaittinen 	LINEAR_RANGE_IDX(MT6360_RANGE_IEOC, 100000, 0, 0x0F, 50000),
1230402e8ebSGene Chen };
1240402e8ebSGene Chen 
1250402e8ebSGene Chen struct mt6360_chg_info {
1260402e8ebSGene Chen 	struct device *dev;
1270402e8ebSGene Chen 	struct regmap *regmap;
1280402e8ebSGene Chen 	struct power_supply_desc psy_desc;
1290402e8ebSGene Chen 	struct power_supply *psy;
1300402e8ebSGene Chen 	struct regulator_dev *otg_rdev;
1310402e8ebSGene Chen 	struct mutex chgdet_lock;
1320402e8ebSGene Chen 	u32 vinovp;
1330402e8ebSGene Chen 	bool pwr_rdy;
1340402e8ebSGene Chen 	bool bc12_en;
1350402e8ebSGene Chen 	int psy_usb_type;
1360402e8ebSGene Chen 	struct work_struct chrdet_work;
1370402e8ebSGene Chen };
1380402e8ebSGene Chen 
1390402e8ebSGene Chen enum mt6360_iinlmtsel {
1400402e8ebSGene Chen 	MT6360_IINLMTSEL_AICR_3250 = 0,
1410402e8ebSGene Chen 	MT6360_IINLMTSEL_CHG_TYPE,
1420402e8ebSGene Chen 	MT6360_IINLMTSEL_AICR,
1430402e8ebSGene Chen 	MT6360_IINLMTSEL_LOWER_LEVEL,
1440402e8ebSGene Chen };
1450402e8ebSGene Chen 
1460402e8ebSGene Chen enum mt6360_pmu_chg_type {
1470402e8ebSGene Chen 	MT6360_CHG_TYPE_NOVBUS = 0,
1480402e8ebSGene Chen 	MT6360_CHG_TYPE_UNDER_GOING,
1490402e8ebSGene Chen 	MT6360_CHG_TYPE_SDP,
1500402e8ebSGene Chen 	MT6360_CHG_TYPE_SDPNSTD,
1510402e8ebSGene Chen 	MT6360_CHG_TYPE_DCP,
1520402e8ebSGene Chen 	MT6360_CHG_TYPE_CDP,
1530402e8ebSGene Chen 	MT6360_CHG_TYPE_DISABLE_BC12,
1540402e8ebSGene Chen 	MT6360_CHG_TYPE_MAX,
1550402e8ebSGene Chen };
1560402e8ebSGene Chen 
1570402e8ebSGene Chen static enum power_supply_usb_type mt6360_charger_usb_types[] = {
1580402e8ebSGene Chen 	POWER_SUPPLY_USB_TYPE_UNKNOWN,
1590402e8ebSGene Chen 	POWER_SUPPLY_USB_TYPE_SDP,
1600402e8ebSGene Chen 	POWER_SUPPLY_USB_TYPE_DCP,
1610402e8ebSGene Chen 	POWER_SUPPLY_USB_TYPE_CDP,
1620402e8ebSGene Chen };
1630402e8ebSGene Chen 
mt6360_get_chrdet_ext_stat(struct mt6360_chg_info * mci,bool * pwr_rdy)1640402e8ebSGene Chen static int mt6360_get_chrdet_ext_stat(struct mt6360_chg_info *mci,
1650402e8ebSGene Chen 					     bool *pwr_rdy)
1660402e8ebSGene Chen {
1670402e8ebSGene Chen 	int ret;
1680402e8ebSGene Chen 	unsigned int regval;
1690402e8ebSGene Chen 
1700402e8ebSGene Chen 	ret = regmap_read(mci->regmap, MT6360_PMU_FOD_STAT, &regval);
1710402e8ebSGene Chen 	if (ret < 0)
1720402e8ebSGene Chen 		return ret;
1730402e8ebSGene Chen 	*pwr_rdy = (regval & MT6360_CHRDET_EXT_MASK) ? true : false;
1740402e8ebSGene Chen 	return 0;
1750402e8ebSGene Chen }
1760402e8ebSGene Chen 
mt6360_charger_get_online(struct mt6360_chg_info * mci,union power_supply_propval * val)1770402e8ebSGene Chen static int mt6360_charger_get_online(struct mt6360_chg_info *mci,
1780402e8ebSGene Chen 				     union power_supply_propval *val)
1790402e8ebSGene Chen {
1800402e8ebSGene Chen 	int ret;
1810402e8ebSGene Chen 	bool pwr_rdy;
1820402e8ebSGene Chen 
1830402e8ebSGene Chen 	ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy);
1840402e8ebSGene Chen 	if (ret < 0)
1850402e8ebSGene Chen 		return ret;
1860402e8ebSGene Chen 	val->intval = pwr_rdy ? true : false;
1870402e8ebSGene Chen 	return 0;
1880402e8ebSGene Chen }
1890402e8ebSGene Chen 
mt6360_charger_get_status(struct mt6360_chg_info * mci,union power_supply_propval * val)1900402e8ebSGene Chen static int mt6360_charger_get_status(struct mt6360_chg_info *mci,
1910402e8ebSGene Chen 				     union power_supply_propval *val)
1920402e8ebSGene Chen {
1930402e8ebSGene Chen 	int status, ret;
1940402e8ebSGene Chen 	unsigned int regval;
1950402e8ebSGene Chen 	bool pwr_rdy;
1960402e8ebSGene Chen 
1970402e8ebSGene Chen 	ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy);
1980402e8ebSGene Chen 	if (ret < 0)
1990402e8ebSGene Chen 		return ret;
2000402e8ebSGene Chen 	if (!pwr_rdy) {
2010402e8ebSGene Chen 		status = POWER_SUPPLY_STATUS_DISCHARGING;
2020402e8ebSGene Chen 		goto out;
2030402e8ebSGene Chen 	}
2040402e8ebSGene Chen 
2050402e8ebSGene Chen 	ret = regmap_read(mci->regmap, MT6360_PMU_CHG_STAT, &regval);
2060402e8ebSGene Chen 	if (ret < 0)
2070402e8ebSGene Chen 		return ret;
2080402e8ebSGene Chen 	regval &= MT6360_CHG_STAT_MASK;
2090402e8ebSGene Chen 	regval >>= MT6360_CHG_STAT_SHFT;
2100402e8ebSGene Chen 	switch (regval) {
2110402e8ebSGene Chen 	case 0x0:
2120402e8ebSGene Chen 		status = POWER_SUPPLY_STATUS_NOT_CHARGING;
2130402e8ebSGene Chen 		break;
2140402e8ebSGene Chen 	case 0x1:
2150402e8ebSGene Chen 		status = POWER_SUPPLY_STATUS_CHARGING;
2160402e8ebSGene Chen 		break;
2170402e8ebSGene Chen 	case 0x2:
2180402e8ebSGene Chen 		status = POWER_SUPPLY_STATUS_FULL;
2190402e8ebSGene Chen 		break;
2200402e8ebSGene Chen 	default:
2210402e8ebSGene Chen 		ret = -EIO;
2220402e8ebSGene Chen 	}
2230402e8ebSGene Chen out:
2240402e8ebSGene Chen 	if (!ret)
2250402e8ebSGene Chen 		val->intval = status;
2260402e8ebSGene Chen 	return ret;
2270402e8ebSGene Chen }
2280402e8ebSGene Chen 
mt6360_charger_get_charge_type(struct mt6360_chg_info * mci,union power_supply_propval * val)2290402e8ebSGene Chen static int mt6360_charger_get_charge_type(struct mt6360_chg_info *mci,
2300402e8ebSGene Chen 					  union power_supply_propval *val)
2310402e8ebSGene Chen {
2320402e8ebSGene Chen 	int type, ret;
2330402e8ebSGene Chen 	unsigned int regval;
2340402e8ebSGene Chen 	u8 chg_stat;
2350402e8ebSGene Chen 
2360402e8ebSGene Chen 	ret = regmap_read(mci->regmap, MT6360_PMU_CHG_STAT, &regval);
2370402e8ebSGene Chen 	if (ret < 0)
2380402e8ebSGene Chen 		return ret;
2390402e8ebSGene Chen 
2400402e8ebSGene Chen 	chg_stat = (regval & MT6360_CHG_STAT_MASK) >> MT6360_CHG_STAT_SHFT;
2410402e8ebSGene Chen 	switch (chg_stat) {
2420402e8ebSGene Chen 	case 0x01: /* Charge in Progress */
2430402e8ebSGene Chen 		if (regval & MT6360_VBAT_LVL_MASK)
2440402e8ebSGene Chen 			type = POWER_SUPPLY_CHARGE_TYPE_FAST;
2450402e8ebSGene Chen 		else
2460402e8ebSGene Chen 			type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
2470402e8ebSGene Chen 		break;
2480402e8ebSGene Chen 	case 0x00: /* Not Charging */
2490402e8ebSGene Chen 	case 0x02: /* Charge Done */
2500402e8ebSGene Chen 	case 0x03: /* Charge Fault */
2510402e8ebSGene Chen 	default:
2520402e8ebSGene Chen 		type = POWER_SUPPLY_CHARGE_TYPE_NONE;
2530402e8ebSGene Chen 		break;
2540402e8ebSGene Chen 	}
2550402e8ebSGene Chen 
2560402e8ebSGene Chen 	val->intval = type;
2570402e8ebSGene Chen 	return 0;
2580402e8ebSGene Chen }
2590402e8ebSGene Chen 
mt6360_charger_get_ichg(struct mt6360_chg_info * mci,union power_supply_propval * val)2600402e8ebSGene Chen static int mt6360_charger_get_ichg(struct mt6360_chg_info *mci,
2610402e8ebSGene Chen 				   union power_supply_propval *val)
2620402e8ebSGene Chen {
2630402e8ebSGene Chen 	int ret;
2640402e8ebSGene Chen 	u32 sel, value;
2650402e8ebSGene Chen 
2660402e8ebSGene Chen 	ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL7, &sel);
2670402e8ebSGene Chen 	if (ret < 0)
2680402e8ebSGene Chen 		return ret;
2690402e8ebSGene Chen 	sel = (sel & MT6360_ICHG_MASK) >> MT6360_ICHG_SHFT;
2700402e8ebSGene Chen 	ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_ICHG], sel, &value);
2710402e8ebSGene Chen 	if (!ret)
2720402e8ebSGene Chen 		val->intval = value;
2730402e8ebSGene Chen 	return ret;
2740402e8ebSGene Chen }
2750402e8ebSGene Chen 
mt6360_charger_get_max_ichg(struct mt6360_chg_info * mci,union power_supply_propval * val)2760402e8ebSGene Chen static int mt6360_charger_get_max_ichg(struct mt6360_chg_info *mci,
2770402e8ebSGene Chen 				       union power_supply_propval *val)
2780402e8ebSGene Chen {
2790402e8ebSGene Chen 	val->intval = MT6360_ICHG_MAX;
2800402e8ebSGene Chen 	return 0;
2810402e8ebSGene Chen }
2820402e8ebSGene Chen 
mt6360_charger_get_cv(struct mt6360_chg_info * mci,union power_supply_propval * val)2830402e8ebSGene Chen static int mt6360_charger_get_cv(struct mt6360_chg_info *mci,
2840402e8ebSGene Chen 				 union power_supply_propval *val)
2850402e8ebSGene Chen {
2860402e8ebSGene Chen 	int ret;
2870402e8ebSGene Chen 	u32 sel, value;
2880402e8ebSGene Chen 
2890402e8ebSGene Chen 	ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL4, &sel);
2900402e8ebSGene Chen 	if (ret < 0)
2910402e8ebSGene Chen 		return ret;
2920402e8ebSGene Chen 	sel = (sel & MT6360_VOREG_MASK) >> MT6360_VOREG_SHFT;
2930402e8ebSGene Chen 	ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_VOREG], sel, &value);
2940402e8ebSGene Chen 	if (!ret)
2950402e8ebSGene Chen 		val->intval = value;
2960402e8ebSGene Chen 	return ret;
2970402e8ebSGene Chen }
2980402e8ebSGene Chen 
mt6360_charger_get_max_cv(struct mt6360_chg_info * mci,union power_supply_propval * val)2990402e8ebSGene Chen static int mt6360_charger_get_max_cv(struct mt6360_chg_info *mci,
3000402e8ebSGene Chen 				     union power_supply_propval *val)
3010402e8ebSGene Chen {
3020402e8ebSGene Chen 	val->intval = MT6360_VOREG_MAX;
3030402e8ebSGene Chen 	return 0;
3040402e8ebSGene Chen }
3050402e8ebSGene Chen 
mt6360_charger_get_aicr(struct mt6360_chg_info * mci,union power_supply_propval * val)3060402e8ebSGene Chen static int mt6360_charger_get_aicr(struct mt6360_chg_info *mci,
3070402e8ebSGene Chen 				   union power_supply_propval *val)
3080402e8ebSGene Chen {
3090402e8ebSGene Chen 	int ret;
3100402e8ebSGene Chen 	u32 sel, value;
3110402e8ebSGene Chen 
3120402e8ebSGene Chen 	ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL3, &sel);
3130402e8ebSGene Chen 	if (ret < 0)
3140402e8ebSGene Chen 		return ret;
3150402e8ebSGene Chen 	sel = (sel & MT6360_IAICR_MASK) >> MT6360_IAICR_SHFT;
3160402e8ebSGene Chen 	ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_AICR], sel, &value);
3170402e8ebSGene Chen 	if (!ret)
3180402e8ebSGene Chen 		val->intval = value;
3190402e8ebSGene Chen 	return ret;
3200402e8ebSGene Chen }
3210402e8ebSGene Chen 
mt6360_charger_get_mivr(struct mt6360_chg_info * mci,union power_supply_propval * val)3220402e8ebSGene Chen static int mt6360_charger_get_mivr(struct mt6360_chg_info *mci,
3230402e8ebSGene Chen 				   union power_supply_propval *val)
3240402e8ebSGene Chen {
3250402e8ebSGene Chen 	int ret;
3260402e8ebSGene Chen 	u32 sel, value;
3270402e8ebSGene Chen 
3280402e8ebSGene Chen 	ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL6, &sel);
3290402e8ebSGene Chen 	if (ret < 0)
3300402e8ebSGene Chen 		return ret;
3310402e8ebSGene Chen 	sel = (sel & MT6360_VMIVR_MASK) >> MT6360_VMIVR_SHFT;
3320402e8ebSGene Chen 	ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_VMIVR], sel, &value);
3330402e8ebSGene Chen 	if (!ret)
3340402e8ebSGene Chen 		val->intval = value;
3350402e8ebSGene Chen 	return ret;
3360402e8ebSGene Chen }
3370402e8ebSGene Chen 
mt6360_charger_get_iprechg(struct mt6360_chg_info * mci,union power_supply_propval * val)3380402e8ebSGene Chen static int mt6360_charger_get_iprechg(struct mt6360_chg_info *mci,
3390402e8ebSGene Chen 				      union power_supply_propval *val)
3400402e8ebSGene Chen {
3410402e8ebSGene Chen 	int ret;
3420402e8ebSGene Chen 	u32 sel, value;
3430402e8ebSGene Chen 
3440402e8ebSGene Chen 	ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL8, &sel);
3450402e8ebSGene Chen 	if (ret < 0)
3460402e8ebSGene Chen 		return ret;
3470402e8ebSGene Chen 	sel = (sel & MT6360_IPREC_MASK) >> MT6360_IPREC_SHFT;
3480402e8ebSGene Chen 	ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_IPREC], sel, &value);
3490402e8ebSGene Chen 	if (!ret)
3500402e8ebSGene Chen 		val->intval = value;
3510402e8ebSGene Chen 	return ret;
3520402e8ebSGene Chen }
3530402e8ebSGene Chen 
mt6360_charger_get_ieoc(struct mt6360_chg_info * mci,union power_supply_propval * val)3540402e8ebSGene Chen static int mt6360_charger_get_ieoc(struct mt6360_chg_info *mci,
3550402e8ebSGene Chen 				   union power_supply_propval *val)
3560402e8ebSGene Chen {
3570402e8ebSGene Chen 	int ret;
3580402e8ebSGene Chen 	u32 sel, value;
3590402e8ebSGene Chen 
3600402e8ebSGene Chen 	ret = regmap_read(mci->regmap, MT6360_PMU_CHG_CTRL9, &sel);
3610402e8ebSGene Chen 	if (ret < 0)
3620402e8ebSGene Chen 		return ret;
3630402e8ebSGene Chen 	sel = (sel & MT6360_IEOC_MASK) >> MT6360_IEOC_SHFT;
3640402e8ebSGene Chen 	ret = linear_range_get_value(&mt6360_chg_range[MT6360_RANGE_IEOC], sel, &value);
3650402e8ebSGene Chen 	if (!ret)
3660402e8ebSGene Chen 		val->intval = value;
3670402e8ebSGene Chen 	return ret;
3680402e8ebSGene Chen }
3690402e8ebSGene Chen 
mt6360_charger_set_online(struct mt6360_chg_info * mci,const union power_supply_propval * val)3700402e8ebSGene Chen static int mt6360_charger_set_online(struct mt6360_chg_info *mci,
3710402e8ebSGene Chen 				     const union power_supply_propval *val)
3720402e8ebSGene Chen {
3730402e8ebSGene Chen 	u8 force_sleep = val->intval ? 0 : 1;
3740402e8ebSGene Chen 
3750402e8ebSGene Chen 	return regmap_update_bits(mci->regmap,
3760402e8ebSGene Chen 				  MT6360_PMU_CHG_CTRL1,
3770402e8ebSGene Chen 				  MT6360_FSLP_MASK,
3780402e8ebSGene Chen 				  force_sleep << MT6360_FSLP_SHFT);
3790402e8ebSGene Chen }
3800402e8ebSGene Chen 
mt6360_charger_set_ichg(struct mt6360_chg_info * mci,const union power_supply_propval * val)3810402e8ebSGene Chen static int mt6360_charger_set_ichg(struct mt6360_chg_info *mci,
3820402e8ebSGene Chen 				   const union power_supply_propval *val)
3830402e8ebSGene Chen {
3840402e8ebSGene Chen 	u32 sel;
3850402e8ebSGene Chen 
3860402e8ebSGene Chen 	linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_ICHG], val->intval, &sel);
3870402e8ebSGene Chen 	return regmap_update_bits(mci->regmap,
3880402e8ebSGene Chen 				  MT6360_PMU_CHG_CTRL7,
3890402e8ebSGene Chen 				  MT6360_ICHG_MASK,
3900402e8ebSGene Chen 				  sel << MT6360_ICHG_SHFT);
3910402e8ebSGene Chen }
3920402e8ebSGene Chen 
mt6360_charger_set_cv(struct mt6360_chg_info * mci,const union power_supply_propval * val)3930402e8ebSGene Chen static int mt6360_charger_set_cv(struct mt6360_chg_info *mci,
3940402e8ebSGene Chen 				 const union power_supply_propval *val)
3950402e8ebSGene Chen {
3960402e8ebSGene Chen 	u32 sel;
3970402e8ebSGene Chen 
3980402e8ebSGene Chen 	linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_VOREG], val->intval, &sel);
3990402e8ebSGene Chen 	return regmap_update_bits(mci->regmap,
4000402e8ebSGene Chen 				  MT6360_PMU_CHG_CTRL4,
4010402e8ebSGene Chen 				  MT6360_VOREG_MASK,
4020402e8ebSGene Chen 				  sel << MT6360_VOREG_SHFT);
4030402e8ebSGene Chen }
4040402e8ebSGene Chen 
mt6360_charger_set_aicr(struct mt6360_chg_info * mci,const union power_supply_propval * val)4050402e8ebSGene Chen static int mt6360_charger_set_aicr(struct mt6360_chg_info *mci,
4060402e8ebSGene Chen 				   const union power_supply_propval *val)
4070402e8ebSGene Chen {
4080402e8ebSGene Chen 	u32 sel;
4090402e8ebSGene Chen 
4100402e8ebSGene Chen 	linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_AICR], val->intval, &sel);
4110402e8ebSGene Chen 	return regmap_update_bits(mci->regmap,
4120402e8ebSGene Chen 				  MT6360_PMU_CHG_CTRL3,
4130402e8ebSGene Chen 				  MT6360_IAICR_MASK,
4140402e8ebSGene Chen 				  sel << MT6360_IAICR_SHFT);
4150402e8ebSGene Chen }
4160402e8ebSGene Chen 
mt6360_charger_set_mivr(struct mt6360_chg_info * mci,const union power_supply_propval * val)4170402e8ebSGene Chen static int mt6360_charger_set_mivr(struct mt6360_chg_info *mci,
4180402e8ebSGene Chen 				   const union power_supply_propval *val)
4190402e8ebSGene Chen {
4200402e8ebSGene Chen 	u32 sel;
4210402e8ebSGene Chen 
4220402e8ebSGene Chen 	linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_VMIVR], val->intval, &sel);
4230402e8ebSGene Chen 	return regmap_update_bits(mci->regmap,
4240402e8ebSGene Chen 				  MT6360_PMU_CHG_CTRL3,
4250402e8ebSGene Chen 				  MT6360_VMIVR_MASK,
4260402e8ebSGene Chen 				  sel << MT6360_VMIVR_SHFT);
4270402e8ebSGene Chen }
4280402e8ebSGene Chen 
mt6360_charger_set_iprechg(struct mt6360_chg_info * mci,const union power_supply_propval * val)4290402e8ebSGene Chen static int mt6360_charger_set_iprechg(struct mt6360_chg_info *mci,
4300402e8ebSGene Chen 				      const union power_supply_propval *val)
4310402e8ebSGene Chen {
4320402e8ebSGene Chen 	u32 sel;
4330402e8ebSGene Chen 
4340402e8ebSGene Chen 	linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_IPREC], val->intval, &sel);
4350402e8ebSGene Chen 	return regmap_update_bits(mci->regmap,
4360402e8ebSGene Chen 				  MT6360_PMU_CHG_CTRL8,
4370402e8ebSGene Chen 				  MT6360_IPREC_MASK,
4380402e8ebSGene Chen 				  sel << MT6360_IPREC_SHFT);
4390402e8ebSGene Chen }
4400402e8ebSGene Chen 
mt6360_charger_set_ieoc(struct mt6360_chg_info * mci,const union power_supply_propval * val)4410402e8ebSGene Chen static int mt6360_charger_set_ieoc(struct mt6360_chg_info *mci,
4420402e8ebSGene Chen 				   const union power_supply_propval *val)
4430402e8ebSGene Chen {
4440402e8ebSGene Chen 	u32 sel;
4450402e8ebSGene Chen 
4460402e8ebSGene Chen 	linear_range_get_selector_within(&mt6360_chg_range[MT6360_RANGE_IEOC], val->intval, &sel);
4470402e8ebSGene Chen 	return regmap_update_bits(mci->regmap,
4480402e8ebSGene Chen 				  MT6360_PMU_CHG_CTRL9,
4490402e8ebSGene Chen 				  MT6360_IEOC_MASK,
4500402e8ebSGene Chen 				  sel << MT6360_IEOC_SHFT);
4510402e8ebSGene Chen }
4520402e8ebSGene Chen 
mt6360_charger_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)4530402e8ebSGene Chen static int mt6360_charger_get_property(struct power_supply *psy,
4540402e8ebSGene Chen 				       enum power_supply_property psp,
4550402e8ebSGene Chen 				       union power_supply_propval *val)
4560402e8ebSGene Chen {
4570402e8ebSGene Chen 	struct mt6360_chg_info *mci = power_supply_get_drvdata(psy);
4580402e8ebSGene Chen 	int ret = 0;
4590402e8ebSGene Chen 
4600402e8ebSGene Chen 	switch (psp) {
4610402e8ebSGene Chen 	case POWER_SUPPLY_PROP_ONLINE:
4620402e8ebSGene Chen 		ret = mt6360_charger_get_online(mci, val);
4630402e8ebSGene Chen 		break;
4640402e8ebSGene Chen 	case POWER_SUPPLY_PROP_STATUS:
4650402e8ebSGene Chen 		ret = mt6360_charger_get_status(mci, val);
4660402e8ebSGene Chen 		break;
4670402e8ebSGene Chen 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
4680402e8ebSGene Chen 		ret = mt6360_charger_get_charge_type(mci, val);
4690402e8ebSGene Chen 		break;
4700402e8ebSGene Chen 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
4710402e8ebSGene Chen 		ret = mt6360_charger_get_ichg(mci, val);
4720402e8ebSGene Chen 		break;
4730402e8ebSGene Chen 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
4740402e8ebSGene Chen 		ret = mt6360_charger_get_max_ichg(mci, val);
4750402e8ebSGene Chen 		break;
4760402e8ebSGene Chen 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
4770402e8ebSGene Chen 		ret = mt6360_charger_get_cv(mci, val);
4780402e8ebSGene Chen 		break;
4790402e8ebSGene Chen 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
4800402e8ebSGene Chen 		ret = mt6360_charger_get_max_cv(mci, val);
4810402e8ebSGene Chen 		break;
4820402e8ebSGene Chen 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
4830402e8ebSGene Chen 		ret = mt6360_charger_get_aicr(mci, val);
4840402e8ebSGene Chen 		break;
4850402e8ebSGene Chen 	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
4860402e8ebSGene Chen 		ret = mt6360_charger_get_mivr(mci, val);
4870402e8ebSGene Chen 		break;
4880402e8ebSGene Chen 	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
4890402e8ebSGene Chen 		ret = mt6360_charger_get_iprechg(mci, val);
4900402e8ebSGene Chen 		break;
4910402e8ebSGene Chen 	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
4920402e8ebSGene Chen 		ret = mt6360_charger_get_ieoc(mci, val);
4930402e8ebSGene Chen 		break;
4940402e8ebSGene Chen 	case POWER_SUPPLY_PROP_USB_TYPE:
4950402e8ebSGene Chen 		val->intval = mci->psy_usb_type;
4960402e8ebSGene Chen 		break;
4970402e8ebSGene Chen 	default:
4980402e8ebSGene Chen 		ret = -ENODATA;
4990402e8ebSGene Chen 	}
5000402e8ebSGene Chen 	return ret;
5010402e8ebSGene Chen }
5020402e8ebSGene Chen 
mt6360_charger_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)5030402e8ebSGene Chen static int mt6360_charger_set_property(struct power_supply *psy,
5040402e8ebSGene Chen 				       enum power_supply_property psp,
5050402e8ebSGene Chen 				       const union power_supply_propval *val)
5060402e8ebSGene Chen {
5070402e8ebSGene Chen 	struct mt6360_chg_info *mci = power_supply_get_drvdata(psy);
5080402e8ebSGene Chen 	int ret;
5090402e8ebSGene Chen 
5100402e8ebSGene Chen 	switch (psp) {
5110402e8ebSGene Chen 	case POWER_SUPPLY_PROP_ONLINE:
5120402e8ebSGene Chen 		ret = mt6360_charger_set_online(mci, val);
5130402e8ebSGene Chen 		break;
5140402e8ebSGene Chen 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
5150402e8ebSGene Chen 		ret = mt6360_charger_set_ichg(mci, val);
5160402e8ebSGene Chen 		break;
5170402e8ebSGene Chen 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
5180402e8ebSGene Chen 		ret = mt6360_charger_set_cv(mci, val);
5190402e8ebSGene Chen 		break;
5200402e8ebSGene Chen 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
5210402e8ebSGene Chen 		ret = mt6360_charger_set_aicr(mci, val);
5220402e8ebSGene Chen 		break;
5230402e8ebSGene Chen 	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
5240402e8ebSGene Chen 		ret = mt6360_charger_set_mivr(mci, val);
5250402e8ebSGene Chen 		break;
5260402e8ebSGene Chen 	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
5270402e8ebSGene Chen 		ret = mt6360_charger_set_iprechg(mci, val);
5280402e8ebSGene Chen 		break;
5290402e8ebSGene Chen 	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
5300402e8ebSGene Chen 		ret = mt6360_charger_set_ieoc(mci, val);
5310402e8ebSGene Chen 		break;
5320402e8ebSGene Chen 	default:
5330402e8ebSGene Chen 		ret = -EINVAL;
5340402e8ebSGene Chen 	}
5350402e8ebSGene Chen 	return ret;
5360402e8ebSGene Chen }
5370402e8ebSGene Chen 
mt6360_charger_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)5380402e8ebSGene Chen static int mt6360_charger_property_is_writeable(struct power_supply *psy,
5390402e8ebSGene Chen 					       enum power_supply_property psp)
5400402e8ebSGene Chen {
5410402e8ebSGene Chen 	switch (psp) {
5420402e8ebSGene Chen 	case POWER_SUPPLY_PROP_ONLINE:
5430402e8ebSGene Chen 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
5440402e8ebSGene Chen 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
5450402e8ebSGene Chen 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
5460402e8ebSGene Chen 	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
5470402e8ebSGene Chen 	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
5480402e8ebSGene Chen 	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
5490402e8ebSGene Chen 		return 1;
5500402e8ebSGene Chen 	default:
5510402e8ebSGene Chen 		return 0;
5520402e8ebSGene Chen 	}
5530402e8ebSGene Chen }
5540402e8ebSGene Chen 
5550402e8ebSGene Chen static enum power_supply_property mt6360_charger_properties[] = {
5560402e8ebSGene Chen 	POWER_SUPPLY_PROP_ONLINE,
5570402e8ebSGene Chen 	POWER_SUPPLY_PROP_STATUS,
5580402e8ebSGene Chen 	POWER_SUPPLY_PROP_CHARGE_TYPE,
5590402e8ebSGene Chen 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
5600402e8ebSGene Chen 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
5610402e8ebSGene Chen 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
5620402e8ebSGene Chen 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
5630402e8ebSGene Chen 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
5640402e8ebSGene Chen 	POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
5650402e8ebSGene Chen 	POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
5660402e8ebSGene Chen 	POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
5670402e8ebSGene Chen 	POWER_SUPPLY_PROP_USB_TYPE,
5680402e8ebSGene Chen };
5690402e8ebSGene Chen 
5700402e8ebSGene Chen static const struct power_supply_desc mt6360_charger_desc = {
5710402e8ebSGene Chen 	.type			= POWER_SUPPLY_TYPE_USB,
5720402e8ebSGene Chen 	.properties		= mt6360_charger_properties,
5730402e8ebSGene Chen 	.num_properties		= ARRAY_SIZE(mt6360_charger_properties),
5740402e8ebSGene Chen 	.get_property		= mt6360_charger_get_property,
5750402e8ebSGene Chen 	.set_property		= mt6360_charger_set_property,
5760402e8ebSGene Chen 	.property_is_writeable	= mt6360_charger_property_is_writeable,
5770402e8ebSGene Chen 	.usb_types		= mt6360_charger_usb_types,
5780402e8ebSGene Chen 	.num_usb_types		= ARRAY_SIZE(mt6360_charger_usb_types),
5790402e8ebSGene Chen };
5800402e8ebSGene Chen 
5810402e8ebSGene Chen static const struct regulator_ops mt6360_chg_otg_ops = {
5820402e8ebSGene Chen 	.list_voltage = regulator_list_voltage_linear,
5830402e8ebSGene Chen 	.enable = regulator_enable_regmap,
5840402e8ebSGene Chen 	.disable = regulator_disable_regmap,
5850402e8ebSGene Chen 	.is_enabled = regulator_is_enabled_regmap,
5860402e8ebSGene Chen 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
5870402e8ebSGene Chen 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
5880402e8ebSGene Chen };
5890402e8ebSGene Chen 
5900402e8ebSGene Chen static const struct regulator_desc mt6360_otg_rdesc = {
591109b9ecdSAngeloGioacchino Del Regno 	.of_match = "usb-otg-vbus-regulator",
5920402e8ebSGene Chen 	.name = "usb-otg-vbus",
5930402e8ebSGene Chen 	.ops = &mt6360_chg_otg_ops,
5940402e8ebSGene Chen 	.owner = THIS_MODULE,
5950402e8ebSGene Chen 	.type = REGULATOR_VOLTAGE,
5960402e8ebSGene Chen 	.min_uV = 4425000,
5970402e8ebSGene Chen 	.uV_step = 25000,
5980402e8ebSGene Chen 	.n_voltages = 57,
5990402e8ebSGene Chen 	.vsel_reg = MT6360_PMU_CHG_CTRL5,
6000402e8ebSGene Chen 	.vsel_mask = MT6360_VOBST_MASK,
6010402e8ebSGene Chen 	.enable_reg = MT6360_PMU_CHG_CTRL1,
6020402e8ebSGene Chen 	.enable_mask = MT6360_OPA_MODE_MASK,
6030402e8ebSGene Chen };
6040402e8ebSGene Chen 
mt6360_pmu_attach_i_handler(int irq,void * data)6050402e8ebSGene Chen static irqreturn_t mt6360_pmu_attach_i_handler(int irq, void *data)
6060402e8ebSGene Chen {
6070402e8ebSGene Chen 	struct mt6360_chg_info *mci = data;
6080402e8ebSGene Chen 	int ret;
6090402e8ebSGene Chen 	unsigned int usb_status;
6100402e8ebSGene Chen 	int last_usb_type;
6110402e8ebSGene Chen 
6120402e8ebSGene Chen 	mutex_lock(&mci->chgdet_lock);
6130402e8ebSGene Chen 	if (!mci->bc12_en) {
6140402e8ebSGene Chen 		dev_warn(mci->dev, "Received attach interrupt, bc12 disabled, ignore irq\n");
6150402e8ebSGene Chen 		goto out;
6160402e8ebSGene Chen 	}
6170402e8ebSGene Chen 	last_usb_type = mci->psy_usb_type;
6180402e8ebSGene Chen 	/* Plug in */
6190402e8ebSGene Chen 	ret = regmap_read(mci->regmap, MT6360_PMU_USB_STATUS1, &usb_status);
6200402e8ebSGene Chen 	if (ret < 0)
6210402e8ebSGene Chen 		goto out;
6220402e8ebSGene Chen 	usb_status &= MT6360_USB_STATUS_MASK;
6230402e8ebSGene Chen 	usb_status >>= MT6360_USB_STATUS_SHFT;
6240402e8ebSGene Chen 	switch (usb_status) {
6250402e8ebSGene Chen 	case MT6360_CHG_TYPE_NOVBUS:
6260402e8ebSGene Chen 		dev_dbg(mci->dev, "Received attach interrupt, no vbus\n");
6270402e8ebSGene Chen 		goto out;
6280402e8ebSGene Chen 	case MT6360_CHG_TYPE_UNDER_GOING:
6290402e8ebSGene Chen 		dev_dbg(mci->dev, "Received attach interrupt, under going...\n");
6300402e8ebSGene Chen 		goto out;
6310402e8ebSGene Chen 	case MT6360_CHG_TYPE_SDP:
6320402e8ebSGene Chen 		mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
6330402e8ebSGene Chen 		break;
6340402e8ebSGene Chen 	case MT6360_CHG_TYPE_SDPNSTD:
6350402e8ebSGene Chen 		mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
6360402e8ebSGene Chen 		break;
6370402e8ebSGene Chen 	case MT6360_CHG_TYPE_CDP:
6380402e8ebSGene Chen 		mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP;
6390402e8ebSGene Chen 		break;
6400402e8ebSGene Chen 	case MT6360_CHG_TYPE_DCP:
6410402e8ebSGene Chen 		mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP;
6420402e8ebSGene Chen 		break;
6430402e8ebSGene Chen 	case MT6360_CHG_TYPE_DISABLE_BC12:
6440402e8ebSGene Chen 		dev_dbg(mci->dev, "Received attach interrupt, bc12 detect not enable\n");
6450402e8ebSGene Chen 		goto out;
6460402e8ebSGene Chen 	default:
6470402e8ebSGene Chen 		mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
6480402e8ebSGene Chen 		dev_dbg(mci->dev, "Received attach interrupt, reserved address\n");
6490402e8ebSGene Chen 		goto out;
6500402e8ebSGene Chen 	}
6510402e8ebSGene Chen 
6520402e8ebSGene Chen 	dev_dbg(mci->dev, "Received attach interrupt, chg_type = %d\n", mci->psy_usb_type);
6530402e8ebSGene Chen 	if (last_usb_type != mci->psy_usb_type)
6540402e8ebSGene Chen 		power_supply_changed(mci->psy);
6550402e8ebSGene Chen out:
6560402e8ebSGene Chen 	mutex_unlock(&mci->chgdet_lock);
6570402e8ebSGene Chen 	return IRQ_HANDLED;
6580402e8ebSGene Chen }
6590402e8ebSGene Chen 
mt6360_handle_chrdet_ext_evt(struct mt6360_chg_info * mci)6600402e8ebSGene Chen static void mt6360_handle_chrdet_ext_evt(struct mt6360_chg_info *mci)
6610402e8ebSGene Chen {
6620402e8ebSGene Chen 	int ret;
6630402e8ebSGene Chen 	bool pwr_rdy;
6640402e8ebSGene Chen 
6650402e8ebSGene Chen 	mutex_lock(&mci->chgdet_lock);
6660402e8ebSGene Chen 	ret = mt6360_get_chrdet_ext_stat(mci, &pwr_rdy);
6670402e8ebSGene Chen 	if (ret < 0)
6680402e8ebSGene Chen 		goto out;
6690402e8ebSGene Chen 	if (mci->pwr_rdy == pwr_rdy) {
6700402e8ebSGene Chen 		dev_dbg(mci->dev, "Received vbus interrupt, pwr_rdy is same(%d)\n", pwr_rdy);
6710402e8ebSGene Chen 		goto out;
6720402e8ebSGene Chen 	}
6730402e8ebSGene Chen 	mci->pwr_rdy = pwr_rdy;
6740402e8ebSGene Chen 	dev_dbg(mci->dev, "Received vbus interrupt, pwr_rdy = %d\n", pwr_rdy);
6750402e8ebSGene Chen 	if (!pwr_rdy) {
6760402e8ebSGene Chen 		mci->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
6770402e8ebSGene Chen 		power_supply_changed(mci->psy);
6780402e8ebSGene Chen 
6790402e8ebSGene Chen 	}
6800402e8ebSGene Chen 	ret = regmap_update_bits(mci->regmap,
6810402e8ebSGene Chen 				 MT6360_PMU_DEVICE_TYPE,
6820402e8ebSGene Chen 				 MT6360_USBCHGEN_MASK,
6830402e8ebSGene Chen 				 pwr_rdy ? MT6360_USBCHGEN_MASK : 0);
6840402e8ebSGene Chen 	if (ret < 0)
6850402e8ebSGene Chen 		goto out;
6860402e8ebSGene Chen 	mci->bc12_en = pwr_rdy;
6870402e8ebSGene Chen out:
6880402e8ebSGene Chen 	mutex_unlock(&mci->chgdet_lock);
6890402e8ebSGene Chen }
6900402e8ebSGene Chen 
mt6360_chrdet_work(struct work_struct * work)6910402e8ebSGene Chen static void mt6360_chrdet_work(struct work_struct *work)
6920402e8ebSGene Chen {
6930402e8ebSGene Chen 	struct mt6360_chg_info *mci = (struct mt6360_chg_info *)container_of(
6940402e8ebSGene Chen 				     work, struct mt6360_chg_info, chrdet_work);
6950402e8ebSGene Chen 
6960402e8ebSGene Chen 	mt6360_handle_chrdet_ext_evt(mci);
6970402e8ebSGene Chen }
6980402e8ebSGene Chen 
mt6360_pmu_chrdet_ext_evt_handler(int irq,void * data)6990402e8ebSGene Chen static irqreturn_t mt6360_pmu_chrdet_ext_evt_handler(int irq, void *data)
7000402e8ebSGene Chen {
7010402e8ebSGene Chen 	struct mt6360_chg_info *mci = data;
7020402e8ebSGene Chen 
7030402e8ebSGene Chen 	mt6360_handle_chrdet_ext_evt(mci);
7040402e8ebSGene Chen 	return IRQ_HANDLED;
7050402e8ebSGene Chen }
7060402e8ebSGene Chen 
mt6360_chg_irq_register(struct platform_device * pdev)7070402e8ebSGene Chen static int mt6360_chg_irq_register(struct platform_device *pdev)
7080402e8ebSGene Chen {
7090402e8ebSGene Chen 	const struct {
7100402e8ebSGene Chen 		const char *name;
7110402e8ebSGene Chen 		irq_handler_t handler;
7120402e8ebSGene Chen 	} irq_descs[] = {
7130402e8ebSGene Chen 		{ "attach_i", mt6360_pmu_attach_i_handler },
7140402e8ebSGene Chen 		{ "chrdet_ext_evt", mt6360_pmu_chrdet_ext_evt_handler }
7150402e8ebSGene Chen 	};
7160402e8ebSGene Chen 	int i, ret;
7170402e8ebSGene Chen 
7180402e8ebSGene Chen 	for (i = 0; i < ARRAY_SIZE(irq_descs); i++) {
7190402e8ebSGene Chen 		ret = platform_get_irq_byname(pdev, irq_descs[i].name);
7200402e8ebSGene Chen 		if (ret < 0)
7210402e8ebSGene Chen 			return ret;
7220402e8ebSGene Chen 
7230402e8ebSGene Chen 		ret = devm_request_threaded_irq(&pdev->dev, ret, NULL,
7240402e8ebSGene Chen 						irq_descs[i].handler,
7250402e8ebSGene Chen 						IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
7260402e8ebSGene Chen 						irq_descs[i].name,
7270402e8ebSGene Chen 						platform_get_drvdata(pdev));
7280402e8ebSGene Chen 		if (ret < 0)
7290402e8ebSGene Chen 			return dev_err_probe(&pdev->dev, ret, "Failed to request %s irq\n",
7300402e8ebSGene Chen 					     irq_descs[i].name);
7310402e8ebSGene Chen 	}
7320402e8ebSGene Chen 
7330402e8ebSGene Chen 	return 0;
7340402e8ebSGene Chen }
7350402e8ebSGene Chen 
mt6360_vinovp_trans_to_sel(u32 val)7360402e8ebSGene Chen static u32 mt6360_vinovp_trans_to_sel(u32 val)
7370402e8ebSGene Chen {
7380402e8ebSGene Chen 	u32 vinovp_tbl[] = { 5500000, 6500000, 11000000, 14500000 };
7390402e8ebSGene Chen 	int i;
7400402e8ebSGene Chen 
7410402e8ebSGene Chen 	/* Select the smaller and equal supported value */
7420402e8ebSGene Chen 	for (i = 0; i < ARRAY_SIZE(vinovp_tbl)-1; i++) {
7430402e8ebSGene Chen 		if (val < vinovp_tbl[i+1])
7440402e8ebSGene Chen 			break;
7450402e8ebSGene Chen 	}
7460402e8ebSGene Chen 	return i;
7470402e8ebSGene Chen }
7480402e8ebSGene Chen 
mt6360_chg_init_setting(struct mt6360_chg_info * mci)7490402e8ebSGene Chen static int mt6360_chg_init_setting(struct mt6360_chg_info *mci)
7500402e8ebSGene Chen {
7510402e8ebSGene Chen 	int ret;
7520402e8ebSGene Chen 	u32 sel;
7530402e8ebSGene Chen 
7540402e8ebSGene Chen 	sel = mt6360_vinovp_trans_to_sel(mci->vinovp);
7550402e8ebSGene Chen 	ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL19,
7560402e8ebSGene Chen 				  MT6360_VINOVP_MASK, sel << MT6360_VINOVP_SHFT);
7570402e8ebSGene Chen 	if (ret)
7580402e8ebSGene Chen 		return dev_err_probe(mci->dev, ret, "%s: Failed to apply vinovp\n", __func__);
7590402e8ebSGene Chen 	ret = regmap_update_bits(mci->regmap, MT6360_PMU_DEVICE_TYPE,
7600402e8ebSGene Chen 				 MT6360_USBCHGEN_MASK, 0);
7610402e8ebSGene Chen 	if (ret)
7620402e8ebSGene Chen 		return dev_err_probe(mci->dev, ret, "%s: Failed to disable bc12\n", __func__);
7630402e8ebSGene Chen 	ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL2,
7640402e8ebSGene Chen 				 MT6360_IINLMTSEL_MASK,
7650402e8ebSGene Chen 				 MT6360_IINLMTSEL_AICR <<
7660402e8ebSGene Chen 					MT6360_IINLMTSEL_SHFT);
7670402e8ebSGene Chen 	if (ret)
7680402e8ebSGene Chen 		return dev_err_probe(mci->dev, ret,
7690402e8ebSGene Chen 				     "%s: Failed to switch iinlmtsel to aicr\n", __func__);
7700402e8ebSGene Chen 	usleep_range(5000, 6000);
7710402e8ebSGene Chen 	ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL3,
7720402e8ebSGene Chen 				 MT6360_ILIM_EN_MASK, 0);
7730402e8ebSGene Chen 	if (ret)
7740402e8ebSGene Chen 		return dev_err_probe(mci->dev, ret,
7750402e8ebSGene Chen 				     "%s: Failed to disable ilim\n", __func__);
7760402e8ebSGene Chen 	ret = regmap_update_bits(mci->regmap, MT6360_PMU_CHG_CTRL10,
7770402e8ebSGene Chen 				 MT6360_OTG_OC_MASK, MT6360_OTG_OC_MASK);
7780402e8ebSGene Chen 	if (ret)
7790402e8ebSGene Chen 		return dev_err_probe(mci->dev, ret,
7800402e8ebSGene Chen 				     "%s: Failed to config otg oc to 3A\n", __func__);
7810402e8ebSGene Chen 	return 0;
7820402e8ebSGene Chen }
7830402e8ebSGene Chen 
mt6360_charger_probe(struct platform_device * pdev)7840402e8ebSGene Chen static int mt6360_charger_probe(struct platform_device *pdev)
7850402e8ebSGene Chen {
7860402e8ebSGene Chen 	struct mt6360_chg_info *mci;
7870402e8ebSGene Chen 	struct power_supply_config charger_cfg = {};
7880402e8ebSGene Chen 	struct regulator_config config = { };
7890402e8ebSGene Chen 	int ret;
7900402e8ebSGene Chen 
7910402e8ebSGene Chen 	mci = devm_kzalloc(&pdev->dev, sizeof(*mci), GFP_KERNEL);
7920402e8ebSGene Chen 	if (!mci)
7930402e8ebSGene Chen 		return -ENOMEM;
7940402e8ebSGene Chen 
7950402e8ebSGene Chen 	mci->dev = &pdev->dev;
7960402e8ebSGene Chen 	mci->vinovp = 6500000;
7970402e8ebSGene Chen 	mutex_init(&mci->chgdet_lock);
7980402e8ebSGene Chen 	platform_set_drvdata(pdev, mci);
7994cbb0d35SKang Chen 	ret = devm_work_autocancel(&pdev->dev, &mci->chrdet_work, mt6360_chrdet_work);
8004cbb0d35SKang Chen 	if (ret)
8014cbb0d35SKang Chen 		return dev_err_probe(&pdev->dev, ret, "Failed to set delayed work\n");
8020402e8ebSGene Chen 
8030402e8ebSGene Chen 	ret = device_property_read_u32(&pdev->dev, "richtek,vinovp-microvolt", &mci->vinovp);
8040402e8ebSGene Chen 	if (ret)
8050402e8ebSGene Chen 		dev_warn(&pdev->dev, "Failed to parse vinovp in DT, keep default 6.5v\n");
8060402e8ebSGene Chen 
8070402e8ebSGene Chen 	mci->regmap = dev_get_regmap(pdev->dev.parent, NULL);
8080402e8ebSGene Chen 	if (!mci->regmap)
8090402e8ebSGene Chen 		return dev_err_probe(&pdev->dev, -ENODEV, "Failed to get parent regmap\n");
8100402e8ebSGene Chen 
8110402e8ebSGene Chen 	ret = mt6360_chg_init_setting(mci);
8120402e8ebSGene Chen 	if (ret)
8130402e8ebSGene Chen 		return dev_err_probe(&pdev->dev, ret, "Failed to initial setting\n");
8140402e8ebSGene Chen 
8150402e8ebSGene Chen 	memcpy(&mci->psy_desc, &mt6360_charger_desc, sizeof(mci->psy_desc));
8160402e8ebSGene Chen 	mci->psy_desc.name = dev_name(&pdev->dev);
8170402e8ebSGene Chen 	charger_cfg.drv_data = mci;
8180402e8ebSGene Chen 	charger_cfg.of_node = pdev->dev.of_node;
8190402e8ebSGene Chen 	mci->psy = devm_power_supply_register(&pdev->dev,
8200402e8ebSGene Chen 					      &mci->psy_desc, &charger_cfg);
8210402e8ebSGene Chen 	if (IS_ERR(mci->psy))
8220402e8ebSGene Chen 		return dev_err_probe(&pdev->dev, PTR_ERR(mci->psy),
8230402e8ebSGene Chen 				     "Failed to register power supply dev\n");
8240402e8ebSGene Chen 
8250402e8ebSGene Chen 
8260402e8ebSGene Chen 	ret = mt6360_chg_irq_register(pdev);
8270402e8ebSGene Chen 	if (ret)
8280402e8ebSGene Chen 		return dev_err_probe(&pdev->dev, ret, "Failed to register irqs\n");
8290402e8ebSGene Chen 
8300402e8ebSGene Chen 	config.dev = &pdev->dev;
8310402e8ebSGene Chen 	config.regmap = mci->regmap;
8320402e8ebSGene Chen 	mci->otg_rdev = devm_regulator_register(&pdev->dev, &mt6360_otg_rdesc,
8330402e8ebSGene Chen 						&config);
8340402e8ebSGene Chen 	if (IS_ERR(mci->otg_rdev))
8350402e8ebSGene Chen 		return PTR_ERR(mci->otg_rdev);
8360402e8ebSGene Chen 
8370402e8ebSGene Chen 	schedule_work(&mci->chrdet_work);
8380402e8ebSGene Chen 
8390402e8ebSGene Chen 	return 0;
8400402e8ebSGene Chen }
8410402e8ebSGene Chen 
8420402e8ebSGene Chen static const struct of_device_id __maybe_unused mt6360_charger_of_id[] = {
8430402e8ebSGene Chen 	{ .compatible = "mediatek,mt6360-chg", },
8440402e8ebSGene Chen 	{},
8450402e8ebSGene Chen };
8460402e8ebSGene Chen MODULE_DEVICE_TABLE(of, mt6360_charger_of_id);
8470402e8ebSGene Chen 
8480402e8ebSGene Chen static const struct platform_device_id mt6360_charger_id[] = {
8490402e8ebSGene Chen 	{ "mt6360-chg", 0 },
8500402e8ebSGene Chen 	{},
8510402e8ebSGene Chen };
8520402e8ebSGene Chen MODULE_DEVICE_TABLE(platform, mt6360_charger_id);
8530402e8ebSGene Chen 
8540402e8ebSGene Chen static struct platform_driver mt6360_charger_driver = {
8550402e8ebSGene Chen 	.driver = {
8560402e8ebSGene Chen 		.name = "mt6360-chg",
8570402e8ebSGene Chen 		.of_match_table = of_match_ptr(mt6360_charger_of_id),
8580402e8ebSGene Chen 	},
8590402e8ebSGene Chen 	.probe = mt6360_charger_probe,
8600402e8ebSGene Chen 	.id_table = mt6360_charger_id,
8610402e8ebSGene Chen };
8620402e8ebSGene Chen module_platform_driver(mt6360_charger_driver);
8630402e8ebSGene Chen 
8640402e8ebSGene Chen MODULE_AUTHOR("Gene Chen <gene_chen@richtek.com>");
8650402e8ebSGene Chen MODULE_DESCRIPTION("MT6360 Charger Driver");
8660402e8ebSGene Chen MODULE_LICENSE("GPL");
867