xref: /linux/drivers/power/supply/max77759_charger.c (revision 99ef60d119f3b2621067dd5fc1ea4a37360709e4)
1*70d7dd27SAmit Sunil Dhamne // SPDX-License-Identifier: GPL-2.0-only
2*70d7dd27SAmit Sunil Dhamne /*
3*70d7dd27SAmit Sunil Dhamne  * max77759_charger.c - Battery charger driver for MAX77759 charger device.
4*70d7dd27SAmit Sunil Dhamne  *
5*70d7dd27SAmit Sunil Dhamne  * Copyright 2025 Google LLC.
6*70d7dd27SAmit Sunil Dhamne  */
7*70d7dd27SAmit Sunil Dhamne 
8*70d7dd27SAmit Sunil Dhamne #include <linux/bitfield.h>
9*70d7dd27SAmit Sunil Dhamne #include <linux/cleanup.h>
10*70d7dd27SAmit Sunil Dhamne #include <linux/device.h>
11*70d7dd27SAmit Sunil Dhamne #include <linux/devm-helpers.h>
12*70d7dd27SAmit Sunil Dhamne #include <linux/interrupt.h>
13*70d7dd27SAmit Sunil Dhamne #include <linux/irq.h>
14*70d7dd27SAmit Sunil Dhamne #include <linux/linear_range.h>
15*70d7dd27SAmit Sunil Dhamne #include <linux/mfd/max77759.h>
16*70d7dd27SAmit Sunil Dhamne #include <linux/module.h>
17*70d7dd27SAmit Sunil Dhamne #include <linux/mod_devicetable.h>
18*70d7dd27SAmit Sunil Dhamne #include <linux/mutex.h>
19*70d7dd27SAmit Sunil Dhamne #include <linux/of.h>
20*70d7dd27SAmit Sunil Dhamne #include <linux/platform_device.h>
21*70d7dd27SAmit Sunil Dhamne #include <linux/power_supply.h>
22*70d7dd27SAmit Sunil Dhamne #include <linux/regmap.h>
23*70d7dd27SAmit Sunil Dhamne #include <linux/regulator/driver.h>
24*70d7dd27SAmit Sunil Dhamne #include <linux/string_choices.h>
25*70d7dd27SAmit Sunil Dhamne #include <linux/workqueue.h>
26*70d7dd27SAmit Sunil Dhamne 
27*70d7dd27SAmit Sunil Dhamne /* Default values for Fast Charge Current & Float Voltage */
28*70d7dd27SAmit Sunil Dhamne #define CHG_CC_DEFAULT_UA			2266770
29*70d7dd27SAmit Sunil Dhamne #define CHG_FV_DEFAULT_MV			4300
30*70d7dd27SAmit Sunil Dhamne 
31*70d7dd27SAmit Sunil Dhamne #define MAX_NUM_RETRIES				3
32*70d7dd27SAmit Sunil Dhamne #define PSY_WORK_RETRY_DELAY_MS			10
33*70d7dd27SAmit Sunil Dhamne 
34*70d7dd27SAmit Sunil Dhamne #define FOREACH_IRQ(S)			\
35*70d7dd27SAmit Sunil Dhamne 	S(AICL),			\
36*70d7dd27SAmit Sunil Dhamne 	S(CHGIN),			\
37*70d7dd27SAmit Sunil Dhamne 	S(CHG),				\
38*70d7dd27SAmit Sunil Dhamne 	S(INLIM),			\
39*70d7dd27SAmit Sunil Dhamne 	S(BAT_OILO),			\
40*70d7dd27SAmit Sunil Dhamne 	S(CHG_STA_CC),			\
41*70d7dd27SAmit Sunil Dhamne 	S(CHG_STA_CV),			\
42*70d7dd27SAmit Sunil Dhamne 	S(CHG_STA_TO),			\
43*70d7dd27SAmit Sunil Dhamne 	S(CHG_STA_DONE)
44*70d7dd27SAmit Sunil Dhamne 
45*70d7dd27SAmit Sunil Dhamne #define GENERATE_ENUM(e)		e
46*70d7dd27SAmit Sunil Dhamne #define GENERATE_STRING(s)		#s
47*70d7dd27SAmit Sunil Dhamne 
48*70d7dd27SAmit Sunil Dhamne enum {
49*70d7dd27SAmit Sunil Dhamne 	FOREACH_IRQ(GENERATE_ENUM)
50*70d7dd27SAmit Sunil Dhamne };
51*70d7dd27SAmit Sunil Dhamne 
52*70d7dd27SAmit Sunil Dhamne static const char *const chgr_irqs_str[] = {
53*70d7dd27SAmit Sunil Dhamne 	FOREACH_IRQ(GENERATE_STRING)
54*70d7dd27SAmit Sunil Dhamne };
55*70d7dd27SAmit Sunil Dhamne 
56*70d7dd27SAmit Sunil Dhamne #define NUM_IRQS			ARRAY_SIZE(chgr_irqs_str)
57*70d7dd27SAmit Sunil Dhamne 
58*70d7dd27SAmit Sunil Dhamne /* Fast charge current limits (in uA) */
59*70d7dd27SAmit Sunil Dhamne static const struct linear_range chgcc_limit_ranges[] = {
60*70d7dd27SAmit Sunil Dhamne 	LINEAR_RANGE(133330, 0x0, 0x2, 0),
61*70d7dd27SAmit Sunil Dhamne 	LINEAR_RANGE(200000, 0x3, 0x3C, 66670),
62*70d7dd27SAmit Sunil Dhamne };
63*70d7dd27SAmit Sunil Dhamne 
64*70d7dd27SAmit Sunil Dhamne /* Charge Termination Voltage Limits (in mV) */
65*70d7dd27SAmit Sunil Dhamne static const struct linear_range chg_cv_prm_ranges[] = {
66*70d7dd27SAmit Sunil Dhamne 	LINEAR_RANGE(3800, 0x38, 0x39, 100),
67*70d7dd27SAmit Sunil Dhamne 	LINEAR_RANGE(4000, 0x0, 0x32, 10),
68*70d7dd27SAmit Sunil Dhamne };
69*70d7dd27SAmit Sunil Dhamne 
70*70d7dd27SAmit Sunil Dhamne /* USB input current limits (in uA) */
71*70d7dd27SAmit Sunil Dhamne static const struct linear_range chgin_ilim_ranges[] = {
72*70d7dd27SAmit Sunil Dhamne 	LINEAR_RANGE(100000, 0x3, 0x7F, 25000),
73*70d7dd27SAmit Sunil Dhamne };
74*70d7dd27SAmit Sunil Dhamne 
75*70d7dd27SAmit Sunil Dhamne struct max77759_charger {
76*70d7dd27SAmit Sunil Dhamne 	struct device *dev;
77*70d7dd27SAmit Sunil Dhamne 	struct regmap *regmap;
78*70d7dd27SAmit Sunil Dhamne 	struct power_supply *psy;
79*70d7dd27SAmit Sunil Dhamne 	struct regulator_dev *chgin_otg_rdev;
80*70d7dd27SAmit Sunil Dhamne 	struct notifier_block nb;
81*70d7dd27SAmit Sunil Dhamne 	struct power_supply *tcpm_psy;
82*70d7dd27SAmit Sunil Dhamne 	struct delayed_work psy_work;
83*70d7dd27SAmit Sunil Dhamne 	struct mutex retry_lock; /* Protects psy_work_retry_cnt */
84*70d7dd27SAmit Sunil Dhamne 	u32 psy_work_retry_cnt;
85*70d7dd27SAmit Sunil Dhamne 	int irqs[NUM_IRQS];
86*70d7dd27SAmit Sunil Dhamne 	struct mutex lock; /* protects the state below */
87*70d7dd27SAmit Sunil Dhamne 	enum max77759_chgr_mode mode;
88*70d7dd27SAmit Sunil Dhamne };
89*70d7dd27SAmit Sunil Dhamne 
90*70d7dd27SAmit Sunil Dhamne static inline int unlock_prot_regs(struct max77759_charger *chg, bool unlock)
91*70d7dd27SAmit Sunil Dhamne {
92*70d7dd27SAmit Sunil Dhamne 	return regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_06,
93*70d7dd27SAmit Sunil Dhamne 				  MAX77759_CHGR_REG_CHG_CNFG_06_CHGPROT, unlock
94*70d7dd27SAmit Sunil Dhamne 				  ? MAX77759_CHGR_REG_CHG_CNFG_06_CHGPROT : 0);
95*70d7dd27SAmit Sunil Dhamne }
96*70d7dd27SAmit Sunil Dhamne 
97*70d7dd27SAmit Sunil Dhamne static int charger_input_valid(struct max77759_charger *chg)
98*70d7dd27SAmit Sunil Dhamne {
99*70d7dd27SAmit Sunil Dhamne 	u32 val;
100*70d7dd27SAmit Sunil Dhamne 	int ret;
101*70d7dd27SAmit Sunil Dhamne 
102*70d7dd27SAmit Sunil Dhamne 	ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_INT_OK, &val);
103*70d7dd27SAmit Sunil Dhamne 	if (ret)
104*70d7dd27SAmit Sunil Dhamne 		return ret;
105*70d7dd27SAmit Sunil Dhamne 
106*70d7dd27SAmit Sunil Dhamne 	return (val & MAX77759_CHGR_REG_CHG_INT_CHG) &&
107*70d7dd27SAmit Sunil Dhamne 		(val & MAX77759_CHGR_REG_CHG_INT_CHGIN);
108*70d7dd27SAmit Sunil Dhamne }
109*70d7dd27SAmit Sunil Dhamne 
110*70d7dd27SAmit Sunil Dhamne static int get_online(struct max77759_charger *chg)
111*70d7dd27SAmit Sunil Dhamne {
112*70d7dd27SAmit Sunil Dhamne 	u32 val;
113*70d7dd27SAmit Sunil Dhamne 	int ret;
114*70d7dd27SAmit Sunil Dhamne 
115*70d7dd27SAmit Sunil Dhamne 	ret = charger_input_valid(chg);
116*70d7dd27SAmit Sunil Dhamne 	if (ret <= 0)
117*70d7dd27SAmit Sunil Dhamne 		return ret;
118*70d7dd27SAmit Sunil Dhamne 
119*70d7dd27SAmit Sunil Dhamne 	ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_02, &val);
120*70d7dd27SAmit Sunil Dhamne 	if (ret)
121*70d7dd27SAmit Sunil Dhamne 		return ret;
122*70d7dd27SAmit Sunil Dhamne 
123*70d7dd27SAmit Sunil Dhamne 	guard(mutex)(&chg->lock);
124*70d7dd27SAmit Sunil Dhamne 
125*70d7dd27SAmit Sunil Dhamne 	return (val & MAX77759_CHGR_REG_CHG_DETAILS_02_CHGIN_STS) &&
126*70d7dd27SAmit Sunil Dhamne 		(chg->mode == MAX77759_CHGR_MODE_CHG_BUCK_ON);
127*70d7dd27SAmit Sunil Dhamne }
128*70d7dd27SAmit Sunil Dhamne 
129*70d7dd27SAmit Sunil Dhamne static int get_status(struct max77759_charger *chg)
130*70d7dd27SAmit Sunil Dhamne {
131*70d7dd27SAmit Sunil Dhamne 	u32 val;
132*70d7dd27SAmit Sunil Dhamne 	int ret;
133*70d7dd27SAmit Sunil Dhamne 
134*70d7dd27SAmit Sunil Dhamne 	ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_01, &val);
135*70d7dd27SAmit Sunil Dhamne 	if (ret)
136*70d7dd27SAmit Sunil Dhamne 		return ret;
137*70d7dd27SAmit Sunil Dhamne 
138*70d7dd27SAmit Sunil Dhamne 	switch (FIELD_GET(MAX77759_CHGR_REG_CHG_DETAILS_01_CHG_DTLS, val)) {
139*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_PREQUAL:
140*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_CC:
141*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_CV:
142*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_TO:
143*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_STATUS_CHARGING;
144*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_DONE:
145*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_STATUS_FULL;
146*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_TIMER_FAULT:
147*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_SUSP_BATT_THM:
148*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_OFF_WDOG_TIMER:
149*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_SUSP_JEITA:
150*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_STATUS_NOT_CHARGING;
151*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_OFF:
152*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_STATUS_DISCHARGING;
153*70d7dd27SAmit Sunil Dhamne 	default:
154*70d7dd27SAmit Sunil Dhamne 		break;
155*70d7dd27SAmit Sunil Dhamne 	}
156*70d7dd27SAmit Sunil Dhamne 
157*70d7dd27SAmit Sunil Dhamne 	return POWER_SUPPLY_STATUS_UNKNOWN;
158*70d7dd27SAmit Sunil Dhamne }
159*70d7dd27SAmit Sunil Dhamne 
160*70d7dd27SAmit Sunil Dhamne static int get_charge_type(struct max77759_charger *chg)
161*70d7dd27SAmit Sunil Dhamne {
162*70d7dd27SAmit Sunil Dhamne 	u32 val;
163*70d7dd27SAmit Sunil Dhamne 	int ret;
164*70d7dd27SAmit Sunil Dhamne 
165*70d7dd27SAmit Sunil Dhamne 	ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_01, &val);
166*70d7dd27SAmit Sunil Dhamne 	if (ret)
167*70d7dd27SAmit Sunil Dhamne 		return ret;
168*70d7dd27SAmit Sunil Dhamne 
169*70d7dd27SAmit Sunil Dhamne 	switch (FIELD_GET(MAX77759_CHGR_REG_CHG_DETAILS_01_CHG_DTLS, val)) {
170*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_PREQUAL:
171*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
172*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_CC:
173*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_CV:
174*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_CHARGE_TYPE_FAST;
175*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_TO:
176*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_CHARGE_TYPE_STANDARD;
177*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_DONE:
178*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_TIMER_FAULT:
179*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_SUSP_BATT_THM:
180*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_OFF_WDOG_TIMER:
181*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_SUSP_JEITA:
182*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHG_DTLS_OFF:
183*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_CHARGE_TYPE_NONE;
184*70d7dd27SAmit Sunil Dhamne 	default:
185*70d7dd27SAmit Sunil Dhamne 		break;
186*70d7dd27SAmit Sunil Dhamne 	}
187*70d7dd27SAmit Sunil Dhamne 
188*70d7dd27SAmit Sunil Dhamne 	return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
189*70d7dd27SAmit Sunil Dhamne }
190*70d7dd27SAmit Sunil Dhamne 
191*70d7dd27SAmit Sunil Dhamne static int get_chg_health(struct max77759_charger *chg)
192*70d7dd27SAmit Sunil Dhamne {
193*70d7dd27SAmit Sunil Dhamne 	u32 val;
194*70d7dd27SAmit Sunil Dhamne 	int ret;
195*70d7dd27SAmit Sunil Dhamne 
196*70d7dd27SAmit Sunil Dhamne 	ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_00, &val);
197*70d7dd27SAmit Sunil Dhamne 	if (ret)
198*70d7dd27SAmit Sunil Dhamne 		return ret;
199*70d7dd27SAmit Sunil Dhamne 
200*70d7dd27SAmit Sunil Dhamne 	switch (FIELD_GET(MAX77759_CHGR_REG_CHG_DETAILS_00_CHGIN_DTLS, val)) {
201*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHGIN_DTLS_VBUS_UNDERVOLTAGE:
202*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHGIN_DTLS_VBUS_MARGINAL_VOLTAGE:
203*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_HEALTH_UNDERVOLTAGE;
204*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHGIN_DTLS_VBUS_OVERVOLTAGE:
205*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_HEALTH_OVERVOLTAGE;
206*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_CHGIN_DTLS_VBUS_VALID:
207*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_HEALTH_GOOD;
208*70d7dd27SAmit Sunil Dhamne 	default:
209*70d7dd27SAmit Sunil Dhamne 		break;
210*70d7dd27SAmit Sunil Dhamne 	}
211*70d7dd27SAmit Sunil Dhamne 
212*70d7dd27SAmit Sunil Dhamne 	return POWER_SUPPLY_HEALTH_UNKNOWN;
213*70d7dd27SAmit Sunil Dhamne }
214*70d7dd27SAmit Sunil Dhamne 
215*70d7dd27SAmit Sunil Dhamne static int get_batt_health(struct max77759_charger *chg)
216*70d7dd27SAmit Sunil Dhamne {
217*70d7dd27SAmit Sunil Dhamne 	u32 val;
218*70d7dd27SAmit Sunil Dhamne 	int ret;
219*70d7dd27SAmit Sunil Dhamne 
220*70d7dd27SAmit Sunil Dhamne 	ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_DETAILS_01, &val);
221*70d7dd27SAmit Sunil Dhamne 	if (ret)
222*70d7dd27SAmit Sunil Dhamne 		return ret;
223*70d7dd27SAmit Sunil Dhamne 
224*70d7dd27SAmit Sunil Dhamne 	switch (FIELD_GET(MAX77759_CHGR_REG_CHG_DETAILS_01_BAT_DTLS, val)) {
225*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_BAT_DTLS_NO_BATT_CHG_SUSP:
226*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_HEALTH_NO_BATTERY;
227*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_BAT_DTLS_DEAD_BATTERY:
228*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_HEALTH_DEAD;
229*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_BAT_DTLS_BAT_CHG_TIMER_FAULT:
230*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
231*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_BAT_DTLS_BAT_OKAY:
232*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_BAT_DTLS_BAT_ONLY_MODE:
233*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_HEALTH_GOOD;
234*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_BAT_DTLS_BAT_UNDERVOLTAGE:
235*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_HEALTH_UNDERVOLTAGE;
236*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_BAT_DTLS_BAT_OVERVOLTAGE:
237*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_HEALTH_OVERVOLTAGE;
238*70d7dd27SAmit Sunil Dhamne 	case MAX77759_CHGR_BAT_DTLS_BAT_OVERCURRENT:
239*70d7dd27SAmit Sunil Dhamne 		return POWER_SUPPLY_HEALTH_OVERCURRENT;
240*70d7dd27SAmit Sunil Dhamne 	default:
241*70d7dd27SAmit Sunil Dhamne 		break;
242*70d7dd27SAmit Sunil Dhamne 	}
243*70d7dd27SAmit Sunil Dhamne 
244*70d7dd27SAmit Sunil Dhamne 	return POWER_SUPPLY_HEALTH_UNKNOWN;
245*70d7dd27SAmit Sunil Dhamne }
246*70d7dd27SAmit Sunil Dhamne 
247*70d7dd27SAmit Sunil Dhamne static int get_health(struct max77759_charger *chg)
248*70d7dd27SAmit Sunil Dhamne {
249*70d7dd27SAmit Sunil Dhamne 	int ret;
250*70d7dd27SAmit Sunil Dhamne 
251*70d7dd27SAmit Sunil Dhamne 	ret = get_online(chg);
252*70d7dd27SAmit Sunil Dhamne 	if (ret < 0)
253*70d7dd27SAmit Sunil Dhamne 		return ret;
254*70d7dd27SAmit Sunil Dhamne 
255*70d7dd27SAmit Sunil Dhamne 	if (ret) {
256*70d7dd27SAmit Sunil Dhamne 		ret = get_chg_health(chg);
257*70d7dd27SAmit Sunil Dhamne 		if (ret < 0 || ret != POWER_SUPPLY_HEALTH_GOOD)
258*70d7dd27SAmit Sunil Dhamne 			return ret;
259*70d7dd27SAmit Sunil Dhamne 	}
260*70d7dd27SAmit Sunil Dhamne 
261*70d7dd27SAmit Sunil Dhamne 	return get_batt_health(chg);
262*70d7dd27SAmit Sunil Dhamne }
263*70d7dd27SAmit Sunil Dhamne 
264*70d7dd27SAmit Sunil Dhamne static int get_fast_charge_current(struct max77759_charger *chg)
265*70d7dd27SAmit Sunil Dhamne {
266*70d7dd27SAmit Sunil Dhamne 	u32 regval, val;
267*70d7dd27SAmit Sunil Dhamne 	int ret;
268*70d7dd27SAmit Sunil Dhamne 
269*70d7dd27SAmit Sunil Dhamne 	ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_02, &regval);
270*70d7dd27SAmit Sunil Dhamne 	if (ret)
271*70d7dd27SAmit Sunil Dhamne 		return ret;
272*70d7dd27SAmit Sunil Dhamne 
273*70d7dd27SAmit Sunil Dhamne 	regval = FIELD_GET(MAX77759_CHGR_REG_CHG_CNFG_02_CHGCC, regval);
274*70d7dd27SAmit Sunil Dhamne 	ret = linear_range_get_value_array(chgcc_limit_ranges,
275*70d7dd27SAmit Sunil Dhamne 					   ARRAY_SIZE(chgcc_limit_ranges),
276*70d7dd27SAmit Sunil Dhamne 					   regval, &val);
277*70d7dd27SAmit Sunil Dhamne 	return ret ? ret : val;
278*70d7dd27SAmit Sunil Dhamne }
279*70d7dd27SAmit Sunil Dhamne 
280*70d7dd27SAmit Sunil Dhamne static int set_fast_charge_current_limit(struct max77759_charger *chg,
281*70d7dd27SAmit Sunil Dhamne 					 u32 cc_max_ua)
282*70d7dd27SAmit Sunil Dhamne {
283*70d7dd27SAmit Sunil Dhamne 	bool found;
284*70d7dd27SAmit Sunil Dhamne 	u32 regval;
285*70d7dd27SAmit Sunil Dhamne 
286*70d7dd27SAmit Sunil Dhamne 	linear_range_get_selector_high_array(chgcc_limit_ranges,
287*70d7dd27SAmit Sunil Dhamne 					     ARRAY_SIZE(chgcc_limit_ranges),
288*70d7dd27SAmit Sunil Dhamne 					     cc_max_ua, &regval, &found);
289*70d7dd27SAmit Sunil Dhamne 	if (!found)
290*70d7dd27SAmit Sunil Dhamne 		return -EINVAL;
291*70d7dd27SAmit Sunil Dhamne 
292*70d7dd27SAmit Sunil Dhamne 	return regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_02,
293*70d7dd27SAmit Sunil Dhamne 				  MAX77759_CHGR_REG_CHG_CNFG_02_CHGCC, regval);
294*70d7dd27SAmit Sunil Dhamne }
295*70d7dd27SAmit Sunil Dhamne 
296*70d7dd27SAmit Sunil Dhamne static int get_float_voltage(struct max77759_charger *chg)
297*70d7dd27SAmit Sunil Dhamne {
298*70d7dd27SAmit Sunil Dhamne 	u32 regval, val;
299*70d7dd27SAmit Sunil Dhamne 	int ret;
300*70d7dd27SAmit Sunil Dhamne 
301*70d7dd27SAmit Sunil Dhamne 	ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_04, &regval);
302*70d7dd27SAmit Sunil Dhamne 	if (ret)
303*70d7dd27SAmit Sunil Dhamne 		return ret;
304*70d7dd27SAmit Sunil Dhamne 
305*70d7dd27SAmit Sunil Dhamne 	regval = FIELD_GET(MAX77759_CHGR_REG_CHG_CNFG_04_CHG_CV_PRM, regval);
306*70d7dd27SAmit Sunil Dhamne 	ret = linear_range_get_value_array(chg_cv_prm_ranges,
307*70d7dd27SAmit Sunil Dhamne 					   ARRAY_SIZE(chg_cv_prm_ranges),
308*70d7dd27SAmit Sunil Dhamne 					   regval, &val);
309*70d7dd27SAmit Sunil Dhamne 
310*70d7dd27SAmit Sunil Dhamne 	return ret ? ret : val;
311*70d7dd27SAmit Sunil Dhamne }
312*70d7dd27SAmit Sunil Dhamne 
313*70d7dd27SAmit Sunil Dhamne static int set_float_voltage_limit(struct max77759_charger *chg, u32 fv_mv)
314*70d7dd27SAmit Sunil Dhamne {
315*70d7dd27SAmit Sunil Dhamne 	u32 regval;
316*70d7dd27SAmit Sunil Dhamne 	bool found;
317*70d7dd27SAmit Sunil Dhamne 
318*70d7dd27SAmit Sunil Dhamne 	linear_range_get_selector_high_array(chg_cv_prm_ranges,
319*70d7dd27SAmit Sunil Dhamne 					     ARRAY_SIZE(chg_cv_prm_ranges),
320*70d7dd27SAmit Sunil Dhamne 					     fv_mv, &regval, &found);
321*70d7dd27SAmit Sunil Dhamne 	if (!found)
322*70d7dd27SAmit Sunil Dhamne 		return -EINVAL;
323*70d7dd27SAmit Sunil Dhamne 
324*70d7dd27SAmit Sunil Dhamne 	return regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_04,
325*70d7dd27SAmit Sunil Dhamne 				  MAX77759_CHGR_REG_CHG_CNFG_04_CHG_CV_PRM,
326*70d7dd27SAmit Sunil Dhamne 				  regval);
327*70d7dd27SAmit Sunil Dhamne }
328*70d7dd27SAmit Sunil Dhamne 
329*70d7dd27SAmit Sunil Dhamne static int get_input_current_limit(struct max77759_charger *chg)
330*70d7dd27SAmit Sunil Dhamne {
331*70d7dd27SAmit Sunil Dhamne 	u32 regval, val;
332*70d7dd27SAmit Sunil Dhamne 	int ret;
333*70d7dd27SAmit Sunil Dhamne 
334*70d7dd27SAmit Sunil Dhamne 	ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_09, &regval);
335*70d7dd27SAmit Sunil Dhamne 	if (ret)
336*70d7dd27SAmit Sunil Dhamne 		return ret;
337*70d7dd27SAmit Sunil Dhamne 
338*70d7dd27SAmit Sunil Dhamne 	regval = FIELD_GET(MAX77759_CHGR_REG_CHG_CNFG_09_CHGIN_ILIM, regval);
339*70d7dd27SAmit Sunil Dhamne 	regval = umax(regval, chgin_ilim_ranges[0].min_sel);
340*70d7dd27SAmit Sunil Dhamne 
341*70d7dd27SAmit Sunil Dhamne 	ret = linear_range_get_value_array(chgin_ilim_ranges,
342*70d7dd27SAmit Sunil Dhamne 					   ARRAY_SIZE(chgin_ilim_ranges),
343*70d7dd27SAmit Sunil Dhamne 					   regval, &val);
344*70d7dd27SAmit Sunil Dhamne 
345*70d7dd27SAmit Sunil Dhamne 	return ret ? ret : val;
346*70d7dd27SAmit Sunil Dhamne }
347*70d7dd27SAmit Sunil Dhamne 
348*70d7dd27SAmit Sunil Dhamne static int set_input_current_limit(struct max77759_charger *chg, int ilim_ua)
349*70d7dd27SAmit Sunil Dhamne {
350*70d7dd27SAmit Sunil Dhamne 	u32 regval;
351*70d7dd27SAmit Sunil Dhamne 
352*70d7dd27SAmit Sunil Dhamne 	if (ilim_ua < 0)
353*70d7dd27SAmit Sunil Dhamne 		return -EINVAL;
354*70d7dd27SAmit Sunil Dhamne 
355*70d7dd27SAmit Sunil Dhamne 	linear_range_get_selector_within(chgin_ilim_ranges, ilim_ua, &regval);
356*70d7dd27SAmit Sunil Dhamne 
357*70d7dd27SAmit Sunil Dhamne 	return regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_09,
358*70d7dd27SAmit Sunil Dhamne 				  MAX77759_CHGR_REG_CHG_CNFG_09_CHGIN_ILIM,
359*70d7dd27SAmit Sunil Dhamne 				  regval);
360*70d7dd27SAmit Sunil Dhamne }
361*70d7dd27SAmit Sunil Dhamne 
362*70d7dd27SAmit Sunil Dhamne static const enum power_supply_property max77759_charger_props[] = {
363*70d7dd27SAmit Sunil Dhamne 	POWER_SUPPLY_PROP_ONLINE,
364*70d7dd27SAmit Sunil Dhamne 	POWER_SUPPLY_PROP_PRESENT,
365*70d7dd27SAmit Sunil Dhamne 	POWER_SUPPLY_PROP_STATUS,
366*70d7dd27SAmit Sunil Dhamne 	POWER_SUPPLY_PROP_CHARGE_TYPE,
367*70d7dd27SAmit Sunil Dhamne 	POWER_SUPPLY_PROP_HEALTH,
368*70d7dd27SAmit Sunil Dhamne 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
369*70d7dd27SAmit Sunil Dhamne 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
370*70d7dd27SAmit Sunil Dhamne 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
371*70d7dd27SAmit Sunil Dhamne };
372*70d7dd27SAmit Sunil Dhamne 
373*70d7dd27SAmit Sunil Dhamne static int max77759_charger_get_property(struct power_supply *psy,
374*70d7dd27SAmit Sunil Dhamne 					 enum power_supply_property psp,
375*70d7dd27SAmit Sunil Dhamne 					 union power_supply_propval *pval)
376*70d7dd27SAmit Sunil Dhamne {
377*70d7dd27SAmit Sunil Dhamne 	struct max77759_charger *chg = power_supply_get_drvdata(psy);
378*70d7dd27SAmit Sunil Dhamne 	int ret;
379*70d7dd27SAmit Sunil Dhamne 
380*70d7dd27SAmit Sunil Dhamne 	switch (psp) {
381*70d7dd27SAmit Sunil Dhamne 	case POWER_SUPPLY_PROP_ONLINE:
382*70d7dd27SAmit Sunil Dhamne 		ret = get_online(chg);
383*70d7dd27SAmit Sunil Dhamne 		break;
384*70d7dd27SAmit Sunil Dhamne 	case POWER_SUPPLY_PROP_PRESENT:
385*70d7dd27SAmit Sunil Dhamne 		ret = charger_input_valid(chg);
386*70d7dd27SAmit Sunil Dhamne 		break;
387*70d7dd27SAmit Sunil Dhamne 	case POWER_SUPPLY_PROP_STATUS:
388*70d7dd27SAmit Sunil Dhamne 		ret = get_status(chg);
389*70d7dd27SAmit Sunil Dhamne 		break;
390*70d7dd27SAmit Sunil Dhamne 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
391*70d7dd27SAmit Sunil Dhamne 		ret = get_charge_type(chg);
392*70d7dd27SAmit Sunil Dhamne 		break;
393*70d7dd27SAmit Sunil Dhamne 	case POWER_SUPPLY_PROP_HEALTH:
394*70d7dd27SAmit Sunil Dhamne 		ret = get_health(chg);
395*70d7dd27SAmit Sunil Dhamne 		break;
396*70d7dd27SAmit Sunil Dhamne 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
397*70d7dd27SAmit Sunil Dhamne 		ret = get_fast_charge_current(chg);
398*70d7dd27SAmit Sunil Dhamne 		break;
399*70d7dd27SAmit Sunil Dhamne 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
400*70d7dd27SAmit Sunil Dhamne 		ret = get_float_voltage(chg);
401*70d7dd27SAmit Sunil Dhamne 		break;
402*70d7dd27SAmit Sunil Dhamne 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
403*70d7dd27SAmit Sunil Dhamne 		ret = get_input_current_limit(chg);
404*70d7dd27SAmit Sunil Dhamne 		break;
405*70d7dd27SAmit Sunil Dhamne 	default:
406*70d7dd27SAmit Sunil Dhamne 		ret = -EINVAL;
407*70d7dd27SAmit Sunil Dhamne 	}
408*70d7dd27SAmit Sunil Dhamne 
409*70d7dd27SAmit Sunil Dhamne 	pval->intval = ret;
410*70d7dd27SAmit Sunil Dhamne 	return ret < 0 ? ret : 0;
411*70d7dd27SAmit Sunil Dhamne }
412*70d7dd27SAmit Sunil Dhamne 
413*70d7dd27SAmit Sunil Dhamne static const struct power_supply_desc max77759_charger_desc = {
414*70d7dd27SAmit Sunil Dhamne 	.name = "max77759-charger",
415*70d7dd27SAmit Sunil Dhamne 	.type = POWER_SUPPLY_TYPE_USB,
416*70d7dd27SAmit Sunil Dhamne 	.properties = max77759_charger_props,
417*70d7dd27SAmit Sunil Dhamne 	.num_properties = ARRAY_SIZE(max77759_charger_props),
418*70d7dd27SAmit Sunil Dhamne 	.get_property = max77759_charger_get_property,
419*70d7dd27SAmit Sunil Dhamne };
420*70d7dd27SAmit Sunil Dhamne 
421*70d7dd27SAmit Sunil Dhamne static int charger_set_mode(struct max77759_charger *chg,
422*70d7dd27SAmit Sunil Dhamne 			    enum max77759_chgr_mode mode)
423*70d7dd27SAmit Sunil Dhamne {
424*70d7dd27SAmit Sunil Dhamne 	int ret;
425*70d7dd27SAmit Sunil Dhamne 
426*70d7dd27SAmit Sunil Dhamne 	guard(mutex)(&chg->lock);
427*70d7dd27SAmit Sunil Dhamne 
428*70d7dd27SAmit Sunil Dhamne 	if (chg->mode == mode)
429*70d7dd27SAmit Sunil Dhamne 		return 0;
430*70d7dd27SAmit Sunil Dhamne 
431*70d7dd27SAmit Sunil Dhamne 	if ((mode == MAX77759_CHGR_MODE_CHG_BUCK_ON ||
432*70d7dd27SAmit Sunil Dhamne 	     mode == MAX77759_CHGR_MODE_OTG_BOOST_ON) &&
433*70d7dd27SAmit Sunil Dhamne 	    chg->mode != MAX77759_CHGR_MODE_OFF) {
434*70d7dd27SAmit Sunil Dhamne 		dev_err(chg->dev, "Invalid mode transition from %d to %d\n",
435*70d7dd27SAmit Sunil Dhamne 			chg->mode, mode);
436*70d7dd27SAmit Sunil Dhamne 		return -EINVAL;
437*70d7dd27SAmit Sunil Dhamne 	}
438*70d7dd27SAmit Sunil Dhamne 
439*70d7dd27SAmit Sunil Dhamne 	ret = regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_00,
440*70d7dd27SAmit Sunil Dhamne 				 MAX77759_CHGR_REG_CHG_CNFG_00_MODE, mode);
441*70d7dd27SAmit Sunil Dhamne 	if (ret)
442*70d7dd27SAmit Sunil Dhamne 		return ret;
443*70d7dd27SAmit Sunil Dhamne 
444*70d7dd27SAmit Sunil Dhamne 	chg->mode = mode;
445*70d7dd27SAmit Sunil Dhamne 	return 0;
446*70d7dd27SAmit Sunil Dhamne }
447*70d7dd27SAmit Sunil Dhamne 
448*70d7dd27SAmit Sunil Dhamne static int enable_chgin_otg(struct regulator_dev *rdev)
449*70d7dd27SAmit Sunil Dhamne {
450*70d7dd27SAmit Sunil Dhamne 	struct max77759_charger *chg = rdev_get_drvdata(rdev);
451*70d7dd27SAmit Sunil Dhamne 
452*70d7dd27SAmit Sunil Dhamne 	return charger_set_mode(chg, MAX77759_CHGR_MODE_OTG_BOOST_ON);
453*70d7dd27SAmit Sunil Dhamne }
454*70d7dd27SAmit Sunil Dhamne 
455*70d7dd27SAmit Sunil Dhamne static int disable_chgin_otg(struct regulator_dev *rdev)
456*70d7dd27SAmit Sunil Dhamne {
457*70d7dd27SAmit Sunil Dhamne 	struct max77759_charger *chg = rdev_get_drvdata(rdev);
458*70d7dd27SAmit Sunil Dhamne 
459*70d7dd27SAmit Sunil Dhamne 	return charger_set_mode(chg, MAX77759_CHGR_MODE_OFF);
460*70d7dd27SAmit Sunil Dhamne }
461*70d7dd27SAmit Sunil Dhamne 
462*70d7dd27SAmit Sunil Dhamne static int chgin_otg_status(struct regulator_dev *rdev)
463*70d7dd27SAmit Sunil Dhamne {
464*70d7dd27SAmit Sunil Dhamne 	struct max77759_charger *chg = rdev_get_drvdata(rdev);
465*70d7dd27SAmit Sunil Dhamne 
466*70d7dd27SAmit Sunil Dhamne 	guard(mutex)(&chg->lock);
467*70d7dd27SAmit Sunil Dhamne 
468*70d7dd27SAmit Sunil Dhamne 	return chg->mode == MAX77759_CHGR_MODE_OTG_BOOST_ON;
469*70d7dd27SAmit Sunil Dhamne }
470*70d7dd27SAmit Sunil Dhamne 
471*70d7dd27SAmit Sunil Dhamne static const struct regulator_ops chgin_otg_reg_ops = {
472*70d7dd27SAmit Sunil Dhamne 	.enable = enable_chgin_otg,
473*70d7dd27SAmit Sunil Dhamne 	.disable = disable_chgin_otg,
474*70d7dd27SAmit Sunil Dhamne 	.is_enabled = chgin_otg_status,
475*70d7dd27SAmit Sunil Dhamne };
476*70d7dd27SAmit Sunil Dhamne 
477*70d7dd27SAmit Sunil Dhamne static const struct regulator_desc chgin_otg_reg_desc = {
478*70d7dd27SAmit Sunil Dhamne 	.name = "chgin-otg",
479*70d7dd27SAmit Sunil Dhamne 	.of_match = of_match_ptr("chgin-otg-regulator"),
480*70d7dd27SAmit Sunil Dhamne 	.owner = THIS_MODULE,
481*70d7dd27SAmit Sunil Dhamne 	.ops = &chgin_otg_reg_ops,
482*70d7dd27SAmit Sunil Dhamne 	.fixed_uV = 5000000,
483*70d7dd27SAmit Sunil Dhamne 	.n_voltages = 1,
484*70d7dd27SAmit Sunil Dhamne };
485*70d7dd27SAmit Sunil Dhamne 
486*70d7dd27SAmit Sunil Dhamne static irqreturn_t irq_handler(int irq, void *data)
487*70d7dd27SAmit Sunil Dhamne {
488*70d7dd27SAmit Sunil Dhamne 	struct max77759_charger *chg = data;
489*70d7dd27SAmit Sunil Dhamne 
490*70d7dd27SAmit Sunil Dhamne 	power_supply_changed(chg->psy);
491*70d7dd27SAmit Sunil Dhamne 
492*70d7dd27SAmit Sunil Dhamne 	return IRQ_HANDLED;
493*70d7dd27SAmit Sunil Dhamne }
494*70d7dd27SAmit Sunil Dhamne 
495*70d7dd27SAmit Sunil Dhamne static irqreturn_t bat_oilo_irq_handler(int irq, void *data)
496*70d7dd27SAmit Sunil Dhamne {
497*70d7dd27SAmit Sunil Dhamne 	struct max77759_charger *chg = data;
498*70d7dd27SAmit Sunil Dhamne 
499*70d7dd27SAmit Sunil Dhamne 	dev_warn_ratelimited(chg->dev,
500*70d7dd27SAmit Sunil Dhamne 			     "Battery over-current threshold crossed\n");
501*70d7dd27SAmit Sunil Dhamne 
502*70d7dd27SAmit Sunil Dhamne 	return irq_handler(irq, data);
503*70d7dd27SAmit Sunil Dhamne }
504*70d7dd27SAmit Sunil Dhamne 
505*70d7dd27SAmit Sunil Dhamne static int max77759_init_irqhandler(struct max77759_charger *chg)
506*70d7dd27SAmit Sunil Dhamne {
507*70d7dd27SAmit Sunil Dhamne 	struct device *dev = chg->dev;
508*70d7dd27SAmit Sunil Dhamne 	irq_handler_t thread_fn;
509*70d7dd27SAmit Sunil Dhamne 	char *name;
510*70d7dd27SAmit Sunil Dhamne 	int i, ret;
511*70d7dd27SAmit Sunil Dhamne 
512*70d7dd27SAmit Sunil Dhamne 	for (i = 0; i < ARRAY_SIZE(chgr_irqs_str); i++) {
513*70d7dd27SAmit Sunil Dhamne 		ret = platform_get_irq_byname(to_platform_device(dev),
514*70d7dd27SAmit Sunil Dhamne 					      chgr_irqs_str[i]);
515*70d7dd27SAmit Sunil Dhamne 		if (ret < 0)
516*70d7dd27SAmit Sunil Dhamne 			return dev_err_probe(dev, ret,
517*70d7dd27SAmit Sunil Dhamne 					     "Failed to get irq resource for %s\n",
518*70d7dd27SAmit Sunil Dhamne 					     chgr_irqs_str[i]);
519*70d7dd27SAmit Sunil Dhamne 
520*70d7dd27SAmit Sunil Dhamne 		chg->irqs[i] = ret;
521*70d7dd27SAmit Sunil Dhamne 		name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", dev_name(dev),
522*70d7dd27SAmit Sunil Dhamne 				      chgr_irqs_str[i]);
523*70d7dd27SAmit Sunil Dhamne 		if (!name)
524*70d7dd27SAmit Sunil Dhamne 			return dev_err_probe(dev, -ENOMEM,
525*70d7dd27SAmit Sunil Dhamne 					     "Failed to allocate space for irqname: %s\n",
526*70d7dd27SAmit Sunil Dhamne 					     chgr_irqs_str[i]);
527*70d7dd27SAmit Sunil Dhamne 
528*70d7dd27SAmit Sunil Dhamne 		if (i == BAT_OILO)
529*70d7dd27SAmit Sunil Dhamne 			thread_fn = bat_oilo_irq_handler;
530*70d7dd27SAmit Sunil Dhamne 		else
531*70d7dd27SAmit Sunil Dhamne 			thread_fn = irq_handler;
532*70d7dd27SAmit Sunil Dhamne 
533*70d7dd27SAmit Sunil Dhamne 		ret = devm_request_threaded_irq(dev, chg->irqs[i], NULL,
534*70d7dd27SAmit Sunil Dhamne 						thread_fn, 0, name, chg);
535*70d7dd27SAmit Sunil Dhamne 		if (ret)
536*70d7dd27SAmit Sunil Dhamne 			return dev_err_probe(dev, ret,
537*70d7dd27SAmit Sunil Dhamne 					     "Unable to register irq handler for %s\n",
538*70d7dd27SAmit Sunil Dhamne 					     chgr_irqs_str[i]);
539*70d7dd27SAmit Sunil Dhamne 	}
540*70d7dd27SAmit Sunil Dhamne 
541*70d7dd27SAmit Sunil Dhamne 	return 0;
542*70d7dd27SAmit Sunil Dhamne }
543*70d7dd27SAmit Sunil Dhamne 
544*70d7dd27SAmit Sunil Dhamne static int max77759_charger_init(struct max77759_charger *chg)
545*70d7dd27SAmit Sunil Dhamne {
546*70d7dd27SAmit Sunil Dhamne 	struct power_supply_battery_info *info;
547*70d7dd27SAmit Sunil Dhamne 	u32 regval, fast_chg_curr, fv;
548*70d7dd27SAmit Sunil Dhamne 	int ret;
549*70d7dd27SAmit Sunil Dhamne 
550*70d7dd27SAmit Sunil Dhamne 	ret = regmap_read(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_00, &regval);
551*70d7dd27SAmit Sunil Dhamne 	if (ret)
552*70d7dd27SAmit Sunil Dhamne 		return ret;
553*70d7dd27SAmit Sunil Dhamne 
554*70d7dd27SAmit Sunil Dhamne 	chg->mode = FIELD_GET(MAX77759_CHGR_REG_CHG_CNFG_00_MODE, regval);
555*70d7dd27SAmit Sunil Dhamne 	ret = charger_set_mode(chg, MAX77759_CHGR_MODE_OFF);
556*70d7dd27SAmit Sunil Dhamne 	if (ret)
557*70d7dd27SAmit Sunil Dhamne 		return ret;
558*70d7dd27SAmit Sunil Dhamne 
559*70d7dd27SAmit Sunil Dhamne 	if (power_supply_get_battery_info(chg->psy, &info)) {
560*70d7dd27SAmit Sunil Dhamne 		fv = CHG_FV_DEFAULT_MV;
561*70d7dd27SAmit Sunil Dhamne 		fast_chg_curr = CHG_CC_DEFAULT_UA;
562*70d7dd27SAmit Sunil Dhamne 	} else {
563*70d7dd27SAmit Sunil Dhamne 		fv = info->constant_charge_voltage_max_uv / 1000;
564*70d7dd27SAmit Sunil Dhamne 		fast_chg_curr = info->constant_charge_current_max_ua;
565*70d7dd27SAmit Sunil Dhamne 	}
566*70d7dd27SAmit Sunil Dhamne 
567*70d7dd27SAmit Sunil Dhamne 	ret = set_fast_charge_current_limit(chg, fast_chg_curr);
568*70d7dd27SAmit Sunil Dhamne 	if (ret)
569*70d7dd27SAmit Sunil Dhamne 		return ret;
570*70d7dd27SAmit Sunil Dhamne 
571*70d7dd27SAmit Sunil Dhamne 	ret = set_float_voltage_limit(chg, fv);
572*70d7dd27SAmit Sunil Dhamne 	if (ret)
573*70d7dd27SAmit Sunil Dhamne 		return ret;
574*70d7dd27SAmit Sunil Dhamne 
575*70d7dd27SAmit Sunil Dhamne 	ret = unlock_prot_regs(chg, true);
576*70d7dd27SAmit Sunil Dhamne 	if (ret)
577*70d7dd27SAmit Sunil Dhamne 		return ret;
578*70d7dd27SAmit Sunil Dhamne 
579*70d7dd27SAmit Sunil Dhamne 	/* Disable wireless charging input */
580*70d7dd27SAmit Sunil Dhamne 	ret = regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_12,
581*70d7dd27SAmit Sunil Dhamne 				 MAX77759_CHGR_REG_CHG_CNFG_12_WCINSEL, 0);
582*70d7dd27SAmit Sunil Dhamne 	if (ret)
583*70d7dd27SAmit Sunil Dhamne 		goto relock;
584*70d7dd27SAmit Sunil Dhamne 
585*70d7dd27SAmit Sunil Dhamne 	ret = regmap_update_bits(chg->regmap, MAX77759_CHGR_REG_CHG_CNFG_18,
586*70d7dd27SAmit Sunil Dhamne 				 MAX77759_CHGR_REG_CHG_CNFG_18_WDTEN, 0);
587*70d7dd27SAmit Sunil Dhamne 	if (ret)
588*70d7dd27SAmit Sunil Dhamne 		goto relock;
589*70d7dd27SAmit Sunil Dhamne 
590*70d7dd27SAmit Sunil Dhamne 	return unlock_prot_regs(chg, false);
591*70d7dd27SAmit Sunil Dhamne 
592*70d7dd27SAmit Sunil Dhamne relock:
593*70d7dd27SAmit Sunil Dhamne 	(void)unlock_prot_regs(chg, false);
594*70d7dd27SAmit Sunil Dhamne 	return ret;
595*70d7dd27SAmit Sunil Dhamne }
596*70d7dd27SAmit Sunil Dhamne 
597*70d7dd27SAmit Sunil Dhamne static void psy_work_item(struct work_struct *work)
598*70d7dd27SAmit Sunil Dhamne {
599*70d7dd27SAmit Sunil Dhamne 	struct max77759_charger *chg =
600*70d7dd27SAmit Sunil Dhamne 		container_of(work, struct max77759_charger, psy_work.work);
601*70d7dd27SAmit Sunil Dhamne 	union power_supply_propval current_limit, online;
602*70d7dd27SAmit Sunil Dhamne 	int ret;
603*70d7dd27SAmit Sunil Dhamne 
604*70d7dd27SAmit Sunil Dhamne 	ret = power_supply_get_property(chg->tcpm_psy,
605*70d7dd27SAmit Sunil Dhamne 					POWER_SUPPLY_PROP_CURRENT_MAX,
606*70d7dd27SAmit Sunil Dhamne 					&current_limit);
607*70d7dd27SAmit Sunil Dhamne 	if (ret) {
608*70d7dd27SAmit Sunil Dhamne 		dev_err(chg->dev,
609*70d7dd27SAmit Sunil Dhamne 			"Failed to get CURRENT_MAX psy property, ret=%d\n",
610*70d7dd27SAmit Sunil Dhamne 			ret);
611*70d7dd27SAmit Sunil Dhamne 		goto err;
612*70d7dd27SAmit Sunil Dhamne 	}
613*70d7dd27SAmit Sunil Dhamne 
614*70d7dd27SAmit Sunil Dhamne 	ret = power_supply_get_property(chg->tcpm_psy, POWER_SUPPLY_PROP_ONLINE,
615*70d7dd27SAmit Sunil Dhamne 					&online);
616*70d7dd27SAmit Sunil Dhamne 	if (ret) {
617*70d7dd27SAmit Sunil Dhamne 		dev_err(chg->dev,
618*70d7dd27SAmit Sunil Dhamne 			"Failed to get ONLINE psy property, ret=%d\n",
619*70d7dd27SAmit Sunil Dhamne 			ret);
620*70d7dd27SAmit Sunil Dhamne 		goto err;
621*70d7dd27SAmit Sunil Dhamne 	}
622*70d7dd27SAmit Sunil Dhamne 
623*70d7dd27SAmit Sunil Dhamne 	if (online.intval && current_limit.intval) {
624*70d7dd27SAmit Sunil Dhamne 		ret = set_input_current_limit(chg, current_limit.intval);
625*70d7dd27SAmit Sunil Dhamne 		if (ret) {
626*70d7dd27SAmit Sunil Dhamne 			dev_err(chg->dev,
627*70d7dd27SAmit Sunil Dhamne 				"Unable to set current limit, ret=%d\n", ret);
628*70d7dd27SAmit Sunil Dhamne 			goto err;
629*70d7dd27SAmit Sunil Dhamne 		}
630*70d7dd27SAmit Sunil Dhamne 
631*70d7dd27SAmit Sunil Dhamne 		charger_set_mode(chg, MAX77759_CHGR_MODE_CHG_BUCK_ON);
632*70d7dd27SAmit Sunil Dhamne 	} else {
633*70d7dd27SAmit Sunil Dhamne 		charger_set_mode(chg, MAX77759_CHGR_MODE_OFF);
634*70d7dd27SAmit Sunil Dhamne 	}
635*70d7dd27SAmit Sunil Dhamne 
636*70d7dd27SAmit Sunil Dhamne 	scoped_guard(mutex, &chg->retry_lock) {
637*70d7dd27SAmit Sunil Dhamne 		if (chg->psy_work_retry_cnt)
638*70d7dd27SAmit Sunil Dhamne 			dev_dbg(chg->dev,
639*70d7dd27SAmit Sunil Dhamne 				"chg psy_work succeeded after %u tries\n",
640*70d7dd27SAmit Sunil Dhamne 				chg->psy_work_retry_cnt);
641*70d7dd27SAmit Sunil Dhamne 		chg->psy_work_retry_cnt = 0;
642*70d7dd27SAmit Sunil Dhamne 	}
643*70d7dd27SAmit Sunil Dhamne 
644*70d7dd27SAmit Sunil Dhamne 	return;
645*70d7dd27SAmit Sunil Dhamne 
646*70d7dd27SAmit Sunil Dhamne err:
647*70d7dd27SAmit Sunil Dhamne 	charger_set_mode(chg, MAX77759_CHGR_MODE_OFF);
648*70d7dd27SAmit Sunil Dhamne 	scoped_guard(mutex, &chg->retry_lock) {
649*70d7dd27SAmit Sunil Dhamne 		if (chg->psy_work_retry_cnt >= MAX_NUM_RETRIES) {
650*70d7dd27SAmit Sunil Dhamne 			dev_err(chg->dev, "chg psy work failed, giving up\n");
651*70d7dd27SAmit Sunil Dhamne 			return;
652*70d7dd27SAmit Sunil Dhamne 		}
653*70d7dd27SAmit Sunil Dhamne 
654*70d7dd27SAmit Sunil Dhamne 		++chg->psy_work_retry_cnt;
655*70d7dd27SAmit Sunil Dhamne 		dev_dbg(chg->dev, "Retrying %u/%u chg psy_work\n",
656*70d7dd27SAmit Sunil Dhamne 			chg->psy_work_retry_cnt, MAX_NUM_RETRIES);
657*70d7dd27SAmit Sunil Dhamne 		schedule_delayed_work(&chg->psy_work,
658*70d7dd27SAmit Sunil Dhamne 				      msecs_to_jiffies(PSY_WORK_RETRY_DELAY_MS));
659*70d7dd27SAmit Sunil Dhamne 	}
660*70d7dd27SAmit Sunil Dhamne }
661*70d7dd27SAmit Sunil Dhamne 
662*70d7dd27SAmit Sunil Dhamne static int psy_changed(struct notifier_block *nb, unsigned long evt, void *data)
663*70d7dd27SAmit Sunil Dhamne {
664*70d7dd27SAmit Sunil Dhamne 	struct max77759_charger *chg = container_of(nb, struct max77759_charger,
665*70d7dd27SAmit Sunil Dhamne 						    nb);
666*70d7dd27SAmit Sunil Dhamne 	static const char *psy_name = "tcpm-source";
667*70d7dd27SAmit Sunil Dhamne 	struct power_supply *psy = data;
668*70d7dd27SAmit Sunil Dhamne 
669*70d7dd27SAmit Sunil Dhamne 	if (!strnstr(psy->desc->name, psy_name, strlen(psy_name)) ||
670*70d7dd27SAmit Sunil Dhamne 	    evt != PSY_EVENT_PROP_CHANGED)
671*70d7dd27SAmit Sunil Dhamne 		return NOTIFY_OK;
672*70d7dd27SAmit Sunil Dhamne 
673*70d7dd27SAmit Sunil Dhamne 	chg->tcpm_psy = psy;
674*70d7dd27SAmit Sunil Dhamne 	scoped_guard(mutex, &chg->retry_lock)
675*70d7dd27SAmit Sunil Dhamne 		chg->psy_work_retry_cnt = 0;
676*70d7dd27SAmit Sunil Dhamne 
677*70d7dd27SAmit Sunil Dhamne 	schedule_delayed_work(&chg->psy_work, 0);
678*70d7dd27SAmit Sunil Dhamne 
679*70d7dd27SAmit Sunil Dhamne 	return NOTIFY_OK;
680*70d7dd27SAmit Sunil Dhamne }
681*70d7dd27SAmit Sunil Dhamne 
682*70d7dd27SAmit Sunil Dhamne static void max_tcpci_unregister_psy_notifier(void *nb)
683*70d7dd27SAmit Sunil Dhamne {
684*70d7dd27SAmit Sunil Dhamne 	power_supply_unreg_notifier(nb);
685*70d7dd27SAmit Sunil Dhamne }
686*70d7dd27SAmit Sunil Dhamne 
687*70d7dd27SAmit Sunil Dhamne static int max77759_charger_probe(struct platform_device *pdev)
688*70d7dd27SAmit Sunil Dhamne {
689*70d7dd27SAmit Sunil Dhamne 	struct regulator_config chgin_otg_reg_cfg;
690*70d7dd27SAmit Sunil Dhamne 	struct power_supply_config psy_cfg;
691*70d7dd27SAmit Sunil Dhamne 	struct device *dev = &pdev->dev;
692*70d7dd27SAmit Sunil Dhamne 	struct max77759_charger *chg;
693*70d7dd27SAmit Sunil Dhamne 	int ret;
694*70d7dd27SAmit Sunil Dhamne 
695*70d7dd27SAmit Sunil Dhamne 	device_set_of_node_from_dev(dev, dev->parent);
696*70d7dd27SAmit Sunil Dhamne 	chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL);
697*70d7dd27SAmit Sunil Dhamne 	if (!chg)
698*70d7dd27SAmit Sunil Dhamne 		return -ENOMEM;
699*70d7dd27SAmit Sunil Dhamne 
700*70d7dd27SAmit Sunil Dhamne 	platform_set_drvdata(pdev, chg);
701*70d7dd27SAmit Sunil Dhamne 	chg->dev = dev;
702*70d7dd27SAmit Sunil Dhamne 	chg->regmap = dev_get_regmap(dev->parent, "charger");
703*70d7dd27SAmit Sunil Dhamne 	if (!chg->regmap)
704*70d7dd27SAmit Sunil Dhamne 		return dev_err_probe(dev, -ENODEV, "Missing regmap\n");
705*70d7dd27SAmit Sunil Dhamne 
706*70d7dd27SAmit Sunil Dhamne 	ret = devm_mutex_init(dev, &chg->lock);
707*70d7dd27SAmit Sunil Dhamne 	if (ret)
708*70d7dd27SAmit Sunil Dhamne 		return dev_err_probe(dev, ret, "Failed to initialize lock\n");
709*70d7dd27SAmit Sunil Dhamne 
710*70d7dd27SAmit Sunil Dhamne 	ret = devm_mutex_init(dev, &chg->retry_lock);
711*70d7dd27SAmit Sunil Dhamne 	if (ret)
712*70d7dd27SAmit Sunil Dhamne 		return dev_err_probe(dev, ret,
713*70d7dd27SAmit Sunil Dhamne 				     "Failed to initialize retry_lock\n");
714*70d7dd27SAmit Sunil Dhamne 
715*70d7dd27SAmit Sunil Dhamne 	psy_cfg.fwnode = dev_fwnode(dev);
716*70d7dd27SAmit Sunil Dhamne 	psy_cfg.drv_data = chg;
717*70d7dd27SAmit Sunil Dhamne 	chg->psy = devm_power_supply_register(dev, &max77759_charger_desc,
718*70d7dd27SAmit Sunil Dhamne 					      &psy_cfg);
719*70d7dd27SAmit Sunil Dhamne 	if (IS_ERR(chg->psy))
720*70d7dd27SAmit Sunil Dhamne 		return dev_err_probe(dev, PTR_ERR(chg->psy),
721*70d7dd27SAmit Sunil Dhamne 				     "Failed to register psy\n");
722*70d7dd27SAmit Sunil Dhamne 
723*70d7dd27SAmit Sunil Dhamne 	ret = max77759_charger_init(chg);
724*70d7dd27SAmit Sunil Dhamne 	if (ret)
725*70d7dd27SAmit Sunil Dhamne 		return dev_err_probe(dev, ret,
726*70d7dd27SAmit Sunil Dhamne 				     "Failed to initialize max77759 charger\n");
727*70d7dd27SAmit Sunil Dhamne 
728*70d7dd27SAmit Sunil Dhamne 	chgin_otg_reg_cfg.dev = dev;
729*70d7dd27SAmit Sunil Dhamne 	chgin_otg_reg_cfg.driver_data = chg;
730*70d7dd27SAmit Sunil Dhamne 	chgin_otg_reg_cfg.of_node = dev_of_node(dev);
731*70d7dd27SAmit Sunil Dhamne 	chg->chgin_otg_rdev = devm_regulator_register(dev, &chgin_otg_reg_desc,
732*70d7dd27SAmit Sunil Dhamne 						      &chgin_otg_reg_cfg);
733*70d7dd27SAmit Sunil Dhamne 	if (IS_ERR(chg->chgin_otg_rdev))
734*70d7dd27SAmit Sunil Dhamne 		return dev_err_probe(dev, PTR_ERR(chg->chgin_otg_rdev),
735*70d7dd27SAmit Sunil Dhamne 				     "Failed to register chgin otg regulator\n");
736*70d7dd27SAmit Sunil Dhamne 
737*70d7dd27SAmit Sunil Dhamne 	ret = devm_delayed_work_autocancel(dev, &chg->psy_work, psy_work_item);
738*70d7dd27SAmit Sunil Dhamne 	if (ret)
739*70d7dd27SAmit Sunil Dhamne 		return dev_err_probe(dev, ret, "Failed to initialize psy work\n");
740*70d7dd27SAmit Sunil Dhamne 
741*70d7dd27SAmit Sunil Dhamne 	chg->nb.notifier_call = psy_changed;
742*70d7dd27SAmit Sunil Dhamne 	ret = power_supply_reg_notifier(&chg->nb);
743*70d7dd27SAmit Sunil Dhamne 	if (ret)
744*70d7dd27SAmit Sunil Dhamne 		return dev_err_probe(dev, ret,
745*70d7dd27SAmit Sunil Dhamne 				     "Unable to register psy notifier\n");
746*70d7dd27SAmit Sunil Dhamne 
747*70d7dd27SAmit Sunil Dhamne 	ret = devm_add_action_or_reset(dev, max_tcpci_unregister_psy_notifier,
748*70d7dd27SAmit Sunil Dhamne 				       &chg->nb);
749*70d7dd27SAmit Sunil Dhamne 	if (ret)
750*70d7dd27SAmit Sunil Dhamne 		return dev_err_probe(dev, ret,
751*70d7dd27SAmit Sunil Dhamne 				     "Failed to add devm action to unregister psy notifier\n");
752*70d7dd27SAmit Sunil Dhamne 
753*70d7dd27SAmit Sunil Dhamne 	return max77759_init_irqhandler(chg);
754*70d7dd27SAmit Sunil Dhamne }
755*70d7dd27SAmit Sunil Dhamne 
756*70d7dd27SAmit Sunil Dhamne static const struct platform_device_id max77759_charger_id[] = {
757*70d7dd27SAmit Sunil Dhamne 	{ .name = "max77759-charger", },
758*70d7dd27SAmit Sunil Dhamne 	{ }
759*70d7dd27SAmit Sunil Dhamne };
760*70d7dd27SAmit Sunil Dhamne MODULE_DEVICE_TABLE(platform, max77759_charger_id);
761*70d7dd27SAmit Sunil Dhamne 
762*70d7dd27SAmit Sunil Dhamne static struct platform_driver max77759_charger_driver = {
763*70d7dd27SAmit Sunil Dhamne 	.driver = {
764*70d7dd27SAmit Sunil Dhamne 		.name = "max77759-charger",
765*70d7dd27SAmit Sunil Dhamne 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
766*70d7dd27SAmit Sunil Dhamne 	},
767*70d7dd27SAmit Sunil Dhamne 	.probe = max77759_charger_probe,
768*70d7dd27SAmit Sunil Dhamne 	.id_table = max77759_charger_id,
769*70d7dd27SAmit Sunil Dhamne };
770*70d7dd27SAmit Sunil Dhamne module_platform_driver(max77759_charger_driver);
771*70d7dd27SAmit Sunil Dhamne 
772*70d7dd27SAmit Sunil Dhamne MODULE_AUTHOR("Amit Sunil Dhamne <amitsd@google.com>");
773*70d7dd27SAmit Sunil Dhamne MODULE_DESCRIPTION("Maxim MAX77759 charger driver");
774*70d7dd27SAmit Sunil Dhamne MODULE_LICENSE("GPL");
775