xref: /linux/drivers/power/supply/max8971_charger.c (revision c7c18635363f06c1943514c2f4c8170b325302e8)
1*60cd40eeSSvyatoslav Ryhel // SPDX-License-Identifier: GPL-2.0-or-later
2*60cd40eeSSvyatoslav Ryhel 
3*60cd40eeSSvyatoslav Ryhel #include <linux/devm-helpers.h>
4*60cd40eeSSvyatoslav Ryhel #include <linux/delay.h>
5*60cd40eeSSvyatoslav Ryhel #include <linux/device.h>
6*60cd40eeSSvyatoslav Ryhel #include <linux/err.h>
7*60cd40eeSSvyatoslav Ryhel #include <linux/extcon.h>
8*60cd40eeSSvyatoslav Ryhel #include <linux/i2c.h>
9*60cd40eeSSvyatoslav Ryhel #include <linux/mod_devicetable.h>
10*60cd40eeSSvyatoslav Ryhel #include <linux/of_graph.h>
11*60cd40eeSSvyatoslav Ryhel #include <linux/property.h>
12*60cd40eeSSvyatoslav Ryhel #include <linux/interrupt.h>
13*60cd40eeSSvyatoslav Ryhel #include <linux/module.h>
14*60cd40eeSSvyatoslav Ryhel #include <linux/pm.h>
15*60cd40eeSSvyatoslav Ryhel #include <linux/power_supply.h>
16*60cd40eeSSvyatoslav Ryhel #include <linux/regmap.h>
17*60cd40eeSSvyatoslav Ryhel #include <linux/sysfs.h>
18*60cd40eeSSvyatoslav Ryhel #include <linux/types.h>
19*60cd40eeSSvyatoslav Ryhel 
20*60cd40eeSSvyatoslav Ryhel #define MAX8971_REG_CHGINT		0x0f
21*60cd40eeSSvyatoslav Ryhel #define   MAX8971_REG_CHG_RST		BIT(0)
22*60cd40eeSSvyatoslav Ryhel #define MAX8971_REG_CHGINT_MASK		0x01
23*60cd40eeSSvyatoslav Ryhel #define   MAX8971_AICL_MASK		BIT(7)
24*60cd40eeSSvyatoslav Ryhel #define MAX8971_REG_CHG_STAT		0x02
25*60cd40eeSSvyatoslav Ryhel #define   MAX8971_CHG_MASK		BIT(3)
26*60cd40eeSSvyatoslav Ryhel #define MAX8971_REG_DETAILS1		0x03
27*60cd40eeSSvyatoslav Ryhel #define MAX8971_REG_DETAILS2		0x04
28*60cd40eeSSvyatoslav Ryhel #define MAX8971_REG_CHGCNTL1		0x05
29*60cd40eeSSvyatoslav Ryhel #define MAX8971_REG_FCHGCRNT		0x06
30*60cd40eeSSvyatoslav Ryhel #define MAX8971_REG_DCCRNT		0x07
31*60cd40eeSSvyatoslav Ryhel #define   MAX8971_CHGRSTRT_MASK		BIT(6)
32*60cd40eeSSvyatoslav Ryhel #define MAX8971_REG_TOPOFF		0x08
33*60cd40eeSSvyatoslav Ryhel #define MAX8971_REG_TEMPREG		0x09
34*60cd40eeSSvyatoslav Ryhel #define MAX8971_REG_PROTCMD		0x0a
35*60cd40eeSSvyatoslav Ryhel #define   MAX8971_CHGPROT_LOCKED	0x00
36*60cd40eeSSvyatoslav Ryhel #define   MAX8971_CHGPROT_UNLOCKED	0x03
37*60cd40eeSSvyatoslav Ryhel 
38*60cd40eeSSvyatoslav Ryhel #define MAX8971_FCHGT_DEFAULT		2
39*60cd40eeSSvyatoslav Ryhel #define MAX8971_TOPOFFT_DEFAULT		3
40*60cd40eeSSvyatoslav Ryhel 
41*60cd40eeSSvyatoslav Ryhel static const char *max8971_manufacturer	= "Maxim Integrated";
42*60cd40eeSSvyatoslav Ryhel static const char *max8971_model	= "MAX8971";
43*60cd40eeSSvyatoslav Ryhel 
44*60cd40eeSSvyatoslav Ryhel enum max8971_charging_state {
45*60cd40eeSSvyatoslav Ryhel 	MAX8971_CHARGING_DEAD_BATTERY,
46*60cd40eeSSvyatoslav Ryhel 	MAX8971_CHARGING_PREQUALIFICATION,
47*60cd40eeSSvyatoslav Ryhel 	MAX8971_CHARGING_FAST_CONST_CURRENT,
48*60cd40eeSSvyatoslav Ryhel 	MAX8971_CHARGING_FAST_CONST_VOLTAGE,
49*60cd40eeSSvyatoslav Ryhel 	MAX8971_CHARGING_TOP_OFF,
50*60cd40eeSSvyatoslav Ryhel 	MAX8971_CHARGING_DONE,
51*60cd40eeSSvyatoslav Ryhel 	MAX8971_CHARGING_TIMER_FAULT,
52*60cd40eeSSvyatoslav Ryhel 	MAX8971_CHARGING_SUSPENDED_THERMAL,
53*60cd40eeSSvyatoslav Ryhel 	MAX8971_CHARGING_OFF,
54*60cd40eeSSvyatoslav Ryhel 	MAX8971_CHARGING_THERMAL_LOOP,
55*60cd40eeSSvyatoslav Ryhel };
56*60cd40eeSSvyatoslav Ryhel 
57*60cd40eeSSvyatoslav Ryhel enum max8971_health_state {
58*60cd40eeSSvyatoslav Ryhel 	MAX8971_HEALTH_UNKNOWN,
59*60cd40eeSSvyatoslav Ryhel 	MAX8971_HEALTH_COLD,
60*60cd40eeSSvyatoslav Ryhel 	MAX8971_HEALTH_COOL,
61*60cd40eeSSvyatoslav Ryhel 	MAX8971_HEALTH_WARM,
62*60cd40eeSSvyatoslav Ryhel 	MAX8971_HEALTH_HOT,
63*60cd40eeSSvyatoslav Ryhel 	MAX8971_HEALTH_OVERHEAT,
64*60cd40eeSSvyatoslav Ryhel };
65*60cd40eeSSvyatoslav Ryhel 
66*60cd40eeSSvyatoslav Ryhel /* Fast-Charge current limit, 250..1550 mA, 50 mA steps */
67*60cd40eeSSvyatoslav Ryhel #define MAX8971_CHG_CC_STEP			  50000U
68*60cd40eeSSvyatoslav Ryhel #define MAX8971_CHG_CC_MIN			 250000U
69*60cd40eeSSvyatoslav Ryhel #define MAX8971_CHG_CC_MAX			1550000U
70*60cd40eeSSvyatoslav Ryhel 
71*60cd40eeSSvyatoslav Ryhel /* Input current limit, 250..1500 mA, 25 mA steps */
72*60cd40eeSSvyatoslav Ryhel #define MAX8971_DCILMT_STEP			  25000U
73*60cd40eeSSvyatoslav Ryhel #define MAX8971_DCILMT_MIN			 250000U
74*60cd40eeSSvyatoslav Ryhel #define MAX8971_DCILMT_MAX			1500000U
75*60cd40eeSSvyatoslav Ryhel 
76*60cd40eeSSvyatoslav Ryhel enum max8971_field_idx {
77*60cd40eeSSvyatoslav Ryhel 	THM_DTLS,		/* DETAILS1 */
78*60cd40eeSSvyatoslav Ryhel 	BAT_DTLS, CHG_DTLS,	/* DETAILS2 */
79*60cd40eeSSvyatoslav Ryhel 	CHG_CC, FCHG_T,		/* FCHGCRNT */
80*60cd40eeSSvyatoslav Ryhel 	DCI_LMT,		/* DCCRNT */
81*60cd40eeSSvyatoslav Ryhel 	TOPOFF_T, TOPOFF_S,	/* TOPOFF */
82*60cd40eeSSvyatoslav Ryhel 	CPROT,			/* PROTCMD */
83*60cd40eeSSvyatoslav Ryhel 	MAX8971_N_REGMAP_FIELDS
84*60cd40eeSSvyatoslav Ryhel };
85*60cd40eeSSvyatoslav Ryhel 
86*60cd40eeSSvyatoslav Ryhel static const struct reg_field max8971_reg_field[MAX8971_N_REGMAP_FIELDS] = {
87*60cd40eeSSvyatoslav Ryhel 	[THM_DTLS] = REG_FIELD(MAX8971_REG_DETAILS1, 0, 2),
88*60cd40eeSSvyatoslav Ryhel 	[BAT_DTLS] = REG_FIELD(MAX8971_REG_DETAILS2, 4, 5),
89*60cd40eeSSvyatoslav Ryhel 	[CHG_DTLS] = REG_FIELD(MAX8971_REG_DETAILS2, 0, 3),
90*60cd40eeSSvyatoslav Ryhel 	[CHG_CC]   = REG_FIELD(MAX8971_REG_FCHGCRNT, 0, 4),
91*60cd40eeSSvyatoslav Ryhel 	[FCHG_T]   = REG_FIELD(MAX8971_REG_FCHGCRNT, 5, 7),
92*60cd40eeSSvyatoslav Ryhel 	[DCI_LMT]  = REG_FIELD(MAX8971_REG_DCCRNT,   0, 5),
93*60cd40eeSSvyatoslav Ryhel 	[TOPOFF_T] = REG_FIELD(MAX8971_REG_TOPOFF,   5, 7),
94*60cd40eeSSvyatoslav Ryhel 	[TOPOFF_S] = REG_FIELD(MAX8971_REG_TOPOFF,   2, 3),
95*60cd40eeSSvyatoslav Ryhel 	[CPROT]    = REG_FIELD(MAX8971_REG_PROTCMD,  2, 3),
96*60cd40eeSSvyatoslav Ryhel };
97*60cd40eeSSvyatoslav Ryhel 
98*60cd40eeSSvyatoslav Ryhel static const struct regmap_config max8971_regmap_config = {
99*60cd40eeSSvyatoslav Ryhel 	.reg_bits = 8,
100*60cd40eeSSvyatoslav Ryhel 	.val_bits = 8,
101*60cd40eeSSvyatoslav Ryhel 	.max_register = MAX8971_REG_CHGINT,
102*60cd40eeSSvyatoslav Ryhel };
103*60cd40eeSSvyatoslav Ryhel 
104*60cd40eeSSvyatoslav Ryhel struct max8971_data {
105*60cd40eeSSvyatoslav Ryhel 	struct device *dev;
106*60cd40eeSSvyatoslav Ryhel 	struct power_supply *psy_mains;
107*60cd40eeSSvyatoslav Ryhel 
108*60cd40eeSSvyatoslav Ryhel 	struct extcon_dev *edev;
109*60cd40eeSSvyatoslav Ryhel 	struct notifier_block extcon_nb;
110*60cd40eeSSvyatoslav Ryhel 	struct delayed_work extcon_work;
111*60cd40eeSSvyatoslav Ryhel 
112*60cd40eeSSvyatoslav Ryhel 	struct regmap *regmap;
113*60cd40eeSSvyatoslav Ryhel 	struct regmap_field *rfield[MAX8971_N_REGMAP_FIELDS];
114*60cd40eeSSvyatoslav Ryhel 
115*60cd40eeSSvyatoslav Ryhel 	enum power_supply_usb_type usb_type;
116*60cd40eeSSvyatoslav Ryhel 
117*60cd40eeSSvyatoslav Ryhel 	u32 fchgt;
118*60cd40eeSSvyatoslav Ryhel 	u32 tofft;
119*60cd40eeSSvyatoslav Ryhel 	u32 toffs;
120*60cd40eeSSvyatoslav Ryhel 
121*60cd40eeSSvyatoslav Ryhel 	bool present;
122*60cd40eeSSvyatoslav Ryhel };
123*60cd40eeSSvyatoslav Ryhel 
max8971_get_status(struct max8971_data * priv,int * val)124*60cd40eeSSvyatoslav Ryhel static int max8971_get_status(struct max8971_data *priv, int *val)
125*60cd40eeSSvyatoslav Ryhel {
126*60cd40eeSSvyatoslav Ryhel 	u32 regval;
127*60cd40eeSSvyatoslav Ryhel 	int err;
128*60cd40eeSSvyatoslav Ryhel 
129*60cd40eeSSvyatoslav Ryhel 	err = regmap_field_read(priv->rfield[CHG_DTLS], &regval);
130*60cd40eeSSvyatoslav Ryhel 	if (err)
131*60cd40eeSSvyatoslav Ryhel 		return err;
132*60cd40eeSSvyatoslav Ryhel 
133*60cd40eeSSvyatoslav Ryhel 	switch (regval) {
134*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_DEAD_BATTERY:
135*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_PREQUALIFICATION:
136*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_FAST_CONST_CURRENT:
137*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_FAST_CONST_VOLTAGE:
138*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_TOP_OFF:
139*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_THERMAL_LOOP:
140*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_STATUS_CHARGING;
141*60cd40eeSSvyatoslav Ryhel 		break;
142*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_DONE:
143*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_STATUS_FULL;
144*60cd40eeSSvyatoslav Ryhel 		break;
145*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_TIMER_FAULT:
146*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
147*60cd40eeSSvyatoslav Ryhel 		break;
148*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_OFF:
149*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_SUSPENDED_THERMAL:
150*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_STATUS_DISCHARGING;
151*60cd40eeSSvyatoslav Ryhel 		break;
152*60cd40eeSSvyatoslav Ryhel 	default:
153*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_STATUS_UNKNOWN;
154*60cd40eeSSvyatoslav Ryhel 	}
155*60cd40eeSSvyatoslav Ryhel 
156*60cd40eeSSvyatoslav Ryhel 	return 0;
157*60cd40eeSSvyatoslav Ryhel }
158*60cd40eeSSvyatoslav Ryhel 
max8971_get_charge_type(struct max8971_data * priv,int * val)159*60cd40eeSSvyatoslav Ryhel static int max8971_get_charge_type(struct max8971_data *priv, int *val)
160*60cd40eeSSvyatoslav Ryhel {
161*60cd40eeSSvyatoslav Ryhel 	u32 regval;
162*60cd40eeSSvyatoslav Ryhel 	int err;
163*60cd40eeSSvyatoslav Ryhel 
164*60cd40eeSSvyatoslav Ryhel 	err = regmap_field_read(priv->rfield[CHG_DTLS], &regval);
165*60cd40eeSSvyatoslav Ryhel 	if (err)
166*60cd40eeSSvyatoslav Ryhel 		return err;
167*60cd40eeSSvyatoslav Ryhel 
168*60cd40eeSSvyatoslav Ryhel 	switch (regval) {
169*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_DEAD_BATTERY:
170*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_PREQUALIFICATION:
171*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
172*60cd40eeSSvyatoslav Ryhel 		break;
173*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_FAST_CONST_CURRENT:
174*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_FAST_CONST_VOLTAGE:
175*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
176*60cd40eeSSvyatoslav Ryhel 		break;
177*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_TOP_OFF:
178*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_THERMAL_LOOP:
179*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
180*60cd40eeSSvyatoslav Ryhel 		break;
181*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_DONE:
182*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_TIMER_FAULT:
183*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_SUSPENDED_THERMAL:
184*60cd40eeSSvyatoslav Ryhel 	case MAX8971_CHARGING_OFF:
185*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
186*60cd40eeSSvyatoslav Ryhel 		break;
187*60cd40eeSSvyatoslav Ryhel 	default:
188*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
189*60cd40eeSSvyatoslav Ryhel 	}
190*60cd40eeSSvyatoslav Ryhel 
191*60cd40eeSSvyatoslav Ryhel 	return 0;
192*60cd40eeSSvyatoslav Ryhel }
193*60cd40eeSSvyatoslav Ryhel 
max8971_get_health(struct max8971_data * priv,int * val)194*60cd40eeSSvyatoslav Ryhel static int max8971_get_health(struct max8971_data *priv, int *val)
195*60cd40eeSSvyatoslav Ryhel {
196*60cd40eeSSvyatoslav Ryhel 	u32 regval;
197*60cd40eeSSvyatoslav Ryhel 	int err;
198*60cd40eeSSvyatoslav Ryhel 
199*60cd40eeSSvyatoslav Ryhel 	err = regmap_field_read(priv->rfield[THM_DTLS], &regval);
200*60cd40eeSSvyatoslav Ryhel 	if (err)
201*60cd40eeSSvyatoslav Ryhel 		return err;
202*60cd40eeSSvyatoslav Ryhel 
203*60cd40eeSSvyatoslav Ryhel 	switch (regval) {
204*60cd40eeSSvyatoslav Ryhel 	case MAX8971_HEALTH_COLD:
205*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_HEALTH_COLD;
206*60cd40eeSSvyatoslav Ryhel 		break;
207*60cd40eeSSvyatoslav Ryhel 	case MAX8971_HEALTH_COOL:
208*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_HEALTH_COOL;
209*60cd40eeSSvyatoslav Ryhel 		break;
210*60cd40eeSSvyatoslav Ryhel 	case MAX8971_HEALTH_WARM:
211*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_HEALTH_GOOD;
212*60cd40eeSSvyatoslav Ryhel 		break;
213*60cd40eeSSvyatoslav Ryhel 	case MAX8971_HEALTH_HOT:
214*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_HEALTH_HOT;
215*60cd40eeSSvyatoslav Ryhel 		break;
216*60cd40eeSSvyatoslav Ryhel 	case MAX8971_HEALTH_OVERHEAT:
217*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_HEALTH_OVERHEAT;
218*60cd40eeSSvyatoslav Ryhel 		break;
219*60cd40eeSSvyatoslav Ryhel 	case MAX8971_HEALTH_UNKNOWN:
220*60cd40eeSSvyatoslav Ryhel 	default:
221*60cd40eeSSvyatoslav Ryhel 		*val = POWER_SUPPLY_HEALTH_UNKNOWN;
222*60cd40eeSSvyatoslav Ryhel 	}
223*60cd40eeSSvyatoslav Ryhel 
224*60cd40eeSSvyatoslav Ryhel 	return 0;
225*60cd40eeSSvyatoslav Ryhel }
226*60cd40eeSSvyatoslav Ryhel 
max8971_get_online(struct max8971_data * priv,int * val)227*60cd40eeSSvyatoslav Ryhel static int max8971_get_online(struct max8971_data *priv, int *val)
228*60cd40eeSSvyatoslav Ryhel {
229*60cd40eeSSvyatoslav Ryhel 	u32 regval;
230*60cd40eeSSvyatoslav Ryhel 	int err;
231*60cd40eeSSvyatoslav Ryhel 
232*60cd40eeSSvyatoslav Ryhel 	err = regmap_read(priv->regmap, MAX8971_REG_CHG_STAT, &regval);
233*60cd40eeSSvyatoslav Ryhel 	if (err)
234*60cd40eeSSvyatoslav Ryhel 		return err;
235*60cd40eeSSvyatoslav Ryhel 
236*60cd40eeSSvyatoslav Ryhel 	if (priv->present)
237*60cd40eeSSvyatoslav Ryhel 		/* CHG_OK bit is 0 when charger is online */
238*60cd40eeSSvyatoslav Ryhel 		*val = !(regval & MAX8971_CHG_MASK);
239*60cd40eeSSvyatoslav Ryhel 	else
240*60cd40eeSSvyatoslav Ryhel 		*val = priv->present;
241*60cd40eeSSvyatoslav Ryhel 
242*60cd40eeSSvyatoslav Ryhel 	return 0;
243*60cd40eeSSvyatoslav Ryhel }
244*60cd40eeSSvyatoslav Ryhel 
max8971_get_integer(struct max8971_data * priv,enum max8971_field_idx fidx,u32 clamp_min,u32 clamp_max,u32 mult,int * val)245*60cd40eeSSvyatoslav Ryhel static int max8971_get_integer(struct max8971_data *priv, enum max8971_field_idx fidx,
246*60cd40eeSSvyatoslav Ryhel 			       u32 clamp_min, u32 clamp_max, u32 mult, int *val)
247*60cd40eeSSvyatoslav Ryhel {
248*60cd40eeSSvyatoslav Ryhel 	u32 regval;
249*60cd40eeSSvyatoslav Ryhel 	int err;
250*60cd40eeSSvyatoslav Ryhel 
251*60cd40eeSSvyatoslav Ryhel 	err = regmap_field_read(priv->rfield[fidx], &regval);
252*60cd40eeSSvyatoslav Ryhel 	if (err)
253*60cd40eeSSvyatoslav Ryhel 		return err;
254*60cd40eeSSvyatoslav Ryhel 
255*60cd40eeSSvyatoslav Ryhel 	*val = clamp_val(regval * mult, clamp_min, clamp_max);
256*60cd40eeSSvyatoslav Ryhel 
257*60cd40eeSSvyatoslav Ryhel 	return 0;
258*60cd40eeSSvyatoslav Ryhel }
259*60cd40eeSSvyatoslav Ryhel 
max8971_set_integer(struct max8971_data * priv,enum max8971_field_idx fidx,u32 clamp_min,u32 clamp_max,u32 div,int val)260*60cd40eeSSvyatoslav Ryhel static int max8971_set_integer(struct max8971_data *priv, enum max8971_field_idx fidx,
261*60cd40eeSSvyatoslav Ryhel 			       u32 clamp_min, u32 clamp_max, u32 div, int val)
262*60cd40eeSSvyatoslav Ryhel {
263*60cd40eeSSvyatoslav Ryhel 	u32 regval;
264*60cd40eeSSvyatoslav Ryhel 
265*60cd40eeSSvyatoslav Ryhel 	regval = clamp_val(val, clamp_min, clamp_max) / div;
266*60cd40eeSSvyatoslav Ryhel 
267*60cd40eeSSvyatoslav Ryhel 	return regmap_field_write(priv->rfield[fidx], regval);
268*60cd40eeSSvyatoslav Ryhel }
269*60cd40eeSSvyatoslav Ryhel 
max8971_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)270*60cd40eeSSvyatoslav Ryhel static int max8971_get_property(struct power_supply *psy, enum power_supply_property psp,
271*60cd40eeSSvyatoslav Ryhel 				union power_supply_propval *val)
272*60cd40eeSSvyatoslav Ryhel {
273*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv = power_supply_get_drvdata(psy);
274*60cd40eeSSvyatoslav Ryhel 	int err = 0;
275*60cd40eeSSvyatoslav Ryhel 
276*60cd40eeSSvyatoslav Ryhel 	switch (psp) {
277*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_STATUS:
278*60cd40eeSSvyatoslav Ryhel 		err = max8971_get_status(priv, &val->intval);
279*60cd40eeSSvyatoslav Ryhel 		break;
280*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
281*60cd40eeSSvyatoslav Ryhel 		err = max8971_get_charge_type(priv, &val->intval);
282*60cd40eeSSvyatoslav Ryhel 		break;
283*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_USB_TYPE:
284*60cd40eeSSvyatoslav Ryhel 		val->intval = priv->usb_type;
285*60cd40eeSSvyatoslav Ryhel 		break;
286*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_HEALTH:
287*60cd40eeSSvyatoslav Ryhel 		err = max8971_get_health(priv, &val->intval);
288*60cd40eeSSvyatoslav Ryhel 		break;
289*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_ONLINE:
290*60cd40eeSSvyatoslav Ryhel 		err = max8971_get_online(priv, &val->intval);
291*60cd40eeSSvyatoslav Ryhel 		break;
292*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_PRESENT:
293*60cd40eeSSvyatoslav Ryhel 		val->intval = priv->present;
294*60cd40eeSSvyatoslav Ryhel 		break;
295*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
296*60cd40eeSSvyatoslav Ryhel 		val->intval = MAX8971_CHG_CC_MAX;
297*60cd40eeSSvyatoslav Ryhel 		break;
298*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
299*60cd40eeSSvyatoslav Ryhel 		err = max8971_get_integer(priv, CHG_CC, MAX8971_CHG_CC_MIN, MAX8971_CHG_CC_MAX,
300*60cd40eeSSvyatoslav Ryhel 					  MAX8971_CHG_CC_STEP, &val->intval);
301*60cd40eeSSvyatoslav Ryhel 		break;
302*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
303*60cd40eeSSvyatoslav Ryhel 		err = max8971_get_integer(priv, DCI_LMT, MAX8971_DCILMT_MIN, MAX8971_DCILMT_MAX,
304*60cd40eeSSvyatoslav Ryhel 					  MAX8971_DCILMT_STEP, &val->intval);
305*60cd40eeSSvyatoslav Ryhel 		break;
306*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_MODEL_NAME:
307*60cd40eeSSvyatoslav Ryhel 		val->strval = max8971_model;
308*60cd40eeSSvyatoslav Ryhel 		break;
309*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_MANUFACTURER:
310*60cd40eeSSvyatoslav Ryhel 		val->strval = max8971_manufacturer;
311*60cd40eeSSvyatoslav Ryhel 		break;
312*60cd40eeSSvyatoslav Ryhel 	default:
313*60cd40eeSSvyatoslav Ryhel 		err = -EINVAL;
314*60cd40eeSSvyatoslav Ryhel 	}
315*60cd40eeSSvyatoslav Ryhel 
316*60cd40eeSSvyatoslav Ryhel 	return err;
317*60cd40eeSSvyatoslav Ryhel }
318*60cd40eeSSvyatoslav Ryhel 
max8971_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)319*60cd40eeSSvyatoslav Ryhel static int max8971_set_property(struct power_supply *psy, enum power_supply_property psp,
320*60cd40eeSSvyatoslav Ryhel 				const union power_supply_propval *val)
321*60cd40eeSSvyatoslav Ryhel {
322*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv = power_supply_get_drvdata(psy);
323*60cd40eeSSvyatoslav Ryhel 	int err = 0;
324*60cd40eeSSvyatoslav Ryhel 
325*60cd40eeSSvyatoslav Ryhel 	switch (psp) {
326*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
327*60cd40eeSSvyatoslav Ryhel 		err = max8971_set_integer(priv, CHG_CC, MAX8971_CHG_CC_MIN, MAX8971_CHG_CC_MAX,
328*60cd40eeSSvyatoslav Ryhel 					  MAX8971_CHG_CC_STEP, val->intval);
329*60cd40eeSSvyatoslav Ryhel 		break;
330*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
331*60cd40eeSSvyatoslav Ryhel 		err = max8971_set_integer(priv, DCI_LMT, MAX8971_DCILMT_MIN, MAX8971_DCILMT_MAX,
332*60cd40eeSSvyatoslav Ryhel 					  MAX8971_DCILMT_STEP, val->intval);
333*60cd40eeSSvyatoslav Ryhel 		break;
334*60cd40eeSSvyatoslav Ryhel 	default:
335*60cd40eeSSvyatoslav Ryhel 		err = -EINVAL;
336*60cd40eeSSvyatoslav Ryhel 	}
337*60cd40eeSSvyatoslav Ryhel 
338*60cd40eeSSvyatoslav Ryhel 	return err;
339*60cd40eeSSvyatoslav Ryhel };
340*60cd40eeSSvyatoslav Ryhel 
max8971_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)341*60cd40eeSSvyatoslav Ryhel static int max8971_property_is_writeable(struct power_supply *psy,
342*60cd40eeSSvyatoslav Ryhel 					 enum power_supply_property psp)
343*60cd40eeSSvyatoslav Ryhel {
344*60cd40eeSSvyatoslav Ryhel 	switch (psp) {
345*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
346*60cd40eeSSvyatoslav Ryhel 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
347*60cd40eeSSvyatoslav Ryhel 		return true;
348*60cd40eeSSvyatoslav Ryhel 	default:
349*60cd40eeSSvyatoslav Ryhel 		return false;
350*60cd40eeSSvyatoslav Ryhel 	}
351*60cd40eeSSvyatoslav Ryhel }
352*60cd40eeSSvyatoslav Ryhel 
353*60cd40eeSSvyatoslav Ryhel static enum power_supply_property max8971_properties[] = {
354*60cd40eeSSvyatoslav Ryhel 	POWER_SUPPLY_PROP_STATUS,
355*60cd40eeSSvyatoslav Ryhel 	POWER_SUPPLY_PROP_CHARGE_TYPE,
356*60cd40eeSSvyatoslav Ryhel 	POWER_SUPPLY_PROP_USB_TYPE,
357*60cd40eeSSvyatoslav Ryhel 	POWER_SUPPLY_PROP_HEALTH,
358*60cd40eeSSvyatoslav Ryhel 	POWER_SUPPLY_PROP_ONLINE,
359*60cd40eeSSvyatoslav Ryhel 	POWER_SUPPLY_PROP_PRESENT,
360*60cd40eeSSvyatoslav Ryhel 	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
361*60cd40eeSSvyatoslav Ryhel 	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
362*60cd40eeSSvyatoslav Ryhel 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
363*60cd40eeSSvyatoslav Ryhel 	POWER_SUPPLY_PROP_MODEL_NAME,
364*60cd40eeSSvyatoslav Ryhel 	POWER_SUPPLY_PROP_MANUFACTURER,
365*60cd40eeSSvyatoslav Ryhel };
366*60cd40eeSSvyatoslav Ryhel 
367*60cd40eeSSvyatoslav Ryhel static const struct power_supply_desc max8971_charger_desc = {
368*60cd40eeSSvyatoslav Ryhel 	.name = "max8971-charger",
369*60cd40eeSSvyatoslav Ryhel 	.type = POWER_SUPPLY_TYPE_USB,
370*60cd40eeSSvyatoslav Ryhel 	.usb_types = BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN) |
371*60cd40eeSSvyatoslav Ryhel 		     BIT(POWER_SUPPLY_USB_TYPE_SDP) |
372*60cd40eeSSvyatoslav Ryhel 		     BIT(POWER_SUPPLY_USB_TYPE_DCP) |
373*60cd40eeSSvyatoslav Ryhel 		     BIT(POWER_SUPPLY_USB_TYPE_CDP) |
374*60cd40eeSSvyatoslav Ryhel 		     BIT(POWER_SUPPLY_USB_TYPE_ACA),
375*60cd40eeSSvyatoslav Ryhel 	.properties = max8971_properties,
376*60cd40eeSSvyatoslav Ryhel 	.num_properties = ARRAY_SIZE(max8971_properties),
377*60cd40eeSSvyatoslav Ryhel 	.get_property = max8971_get_property,
378*60cd40eeSSvyatoslav Ryhel 	.set_property = max8971_set_property,
379*60cd40eeSSvyatoslav Ryhel 	.property_is_writeable = max8971_property_is_writeable,
380*60cd40eeSSvyatoslav Ryhel };
381*60cd40eeSSvyatoslav Ryhel 
max8971_update_config(struct max8971_data * priv)382*60cd40eeSSvyatoslav Ryhel static void max8971_update_config(struct max8971_data *priv)
383*60cd40eeSSvyatoslav Ryhel {
384*60cd40eeSSvyatoslav Ryhel 	regmap_field_write(priv->rfield[CPROT], MAX8971_CHGPROT_UNLOCKED);
385*60cd40eeSSvyatoslav Ryhel 
386*60cd40eeSSvyatoslav Ryhel 	if (priv->fchgt != MAX8971_FCHGT_DEFAULT)
387*60cd40eeSSvyatoslav Ryhel 		regmap_field_write(priv->rfield[FCHG_T], priv->fchgt);
388*60cd40eeSSvyatoslav Ryhel 
389*60cd40eeSSvyatoslav Ryhel 	regmap_write_bits(priv->regmap, MAX8971_REG_DCCRNT, MAX8971_CHGRSTRT_MASK,
390*60cd40eeSSvyatoslav Ryhel 			  MAX8971_CHGRSTRT_MASK);
391*60cd40eeSSvyatoslav Ryhel 
392*60cd40eeSSvyatoslav Ryhel 	if (priv->tofft != MAX8971_TOPOFFT_DEFAULT)
393*60cd40eeSSvyatoslav Ryhel 		regmap_field_write(priv->rfield[TOPOFF_T], priv->tofft);
394*60cd40eeSSvyatoslav Ryhel 
395*60cd40eeSSvyatoslav Ryhel 	if (priv->toffs)
396*60cd40eeSSvyatoslav Ryhel 		regmap_field_write(priv->rfield[TOPOFF_S], priv->toffs);
397*60cd40eeSSvyatoslav Ryhel 
398*60cd40eeSSvyatoslav Ryhel 	regmap_field_write(priv->rfield[CPROT], MAX8971_CHGPROT_LOCKED);
399*60cd40eeSSvyatoslav Ryhel }
400*60cd40eeSSvyatoslav Ryhel 
fast_charge_timer_show(struct device * dev,struct device_attribute * attr,char * buf)401*60cd40eeSSvyatoslav Ryhel static ssize_t fast_charge_timer_show(struct device *dev, struct device_attribute *attr,
402*60cd40eeSSvyatoslav Ryhel 				      char *buf)
403*60cd40eeSSvyatoslav Ryhel {
404*60cd40eeSSvyatoslav Ryhel 	struct power_supply *psy = to_power_supply(dev);
405*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv = power_supply_get_drvdata(psy);
406*60cd40eeSSvyatoslav Ryhel 	u32 regval;
407*60cd40eeSSvyatoslav Ryhel 	int err;
408*60cd40eeSSvyatoslav Ryhel 
409*60cd40eeSSvyatoslav Ryhel 	err = regmap_field_read(priv->rfield[FCHG_T], &regval);
410*60cd40eeSSvyatoslav Ryhel 	if (err)
411*60cd40eeSSvyatoslav Ryhel 		return err;
412*60cd40eeSSvyatoslav Ryhel 
413*60cd40eeSSvyatoslav Ryhel 	switch (regval) {
414*60cd40eeSSvyatoslav Ryhel 	case 0x1 ... 0x7:
415*60cd40eeSSvyatoslav Ryhel 		/* Time is off by 3 hours comparing to value */
416*60cd40eeSSvyatoslav Ryhel 		regval += 3;
417*60cd40eeSSvyatoslav Ryhel 		break;
418*60cd40eeSSvyatoslav Ryhel 	case 0x0:
419*60cd40eeSSvyatoslav Ryhel 	default:
420*60cd40eeSSvyatoslav Ryhel 		regval = 0;
421*60cd40eeSSvyatoslav Ryhel 		break;
422*60cd40eeSSvyatoslav Ryhel 	}
423*60cd40eeSSvyatoslav Ryhel 
424*60cd40eeSSvyatoslav Ryhel 	return sysfs_emit(buf, "%u\n", regval);
425*60cd40eeSSvyatoslav Ryhel }
426*60cd40eeSSvyatoslav Ryhel 
fast_charge_timer_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)427*60cd40eeSSvyatoslav Ryhel static ssize_t fast_charge_timer_store(struct device *dev, struct device_attribute *attr,
428*60cd40eeSSvyatoslav Ryhel 				       const char *buf, size_t count)
429*60cd40eeSSvyatoslav Ryhel {
430*60cd40eeSSvyatoslav Ryhel 	struct power_supply *psy = to_power_supply(dev);
431*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv = power_supply_get_drvdata(psy);
432*60cd40eeSSvyatoslav Ryhel 	unsigned long hours;
433*60cd40eeSSvyatoslav Ryhel 	int val, err;
434*60cd40eeSSvyatoslav Ryhel 
435*60cd40eeSSvyatoslav Ryhel 	err = kstrtoul(buf, 10, &hours);
436*60cd40eeSSvyatoslav Ryhel 	if (err)
437*60cd40eeSSvyatoslav Ryhel 		return err;
438*60cd40eeSSvyatoslav Ryhel 
439*60cd40eeSSvyatoslav Ryhel 	val = hours - 3;
440*60cd40eeSSvyatoslav Ryhel 	if (val <= 0 || val > 7)
441*60cd40eeSSvyatoslav Ryhel 		priv->fchgt = 0;
442*60cd40eeSSvyatoslav Ryhel 	else
443*60cd40eeSSvyatoslav Ryhel 		priv->fchgt = val;
444*60cd40eeSSvyatoslav Ryhel 
445*60cd40eeSSvyatoslav Ryhel 	max8971_update_config(priv);
446*60cd40eeSSvyatoslav Ryhel 
447*60cd40eeSSvyatoslav Ryhel 	return count;
448*60cd40eeSSvyatoslav Ryhel }
449*60cd40eeSSvyatoslav Ryhel 
top_off_threshold_current_show(struct device * dev,struct device_attribute * attr,char * buf)450*60cd40eeSSvyatoslav Ryhel static ssize_t top_off_threshold_current_show(struct device *dev,
451*60cd40eeSSvyatoslav Ryhel 					      struct device_attribute *attr,
452*60cd40eeSSvyatoslav Ryhel 					      char *buf)
453*60cd40eeSSvyatoslav Ryhel {
454*60cd40eeSSvyatoslav Ryhel 	struct power_supply *psy = to_power_supply(dev);
455*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv = power_supply_get_drvdata(psy);
456*60cd40eeSSvyatoslav Ryhel 	u32 regval, val;
457*60cd40eeSSvyatoslav Ryhel 	int err;
458*60cd40eeSSvyatoslav Ryhel 
459*60cd40eeSSvyatoslav Ryhel 	err = regmap_field_read(priv->rfield[TOPOFF_S], &regval);
460*60cd40eeSSvyatoslav Ryhel 	if (err)
461*60cd40eeSSvyatoslav Ryhel 		return err;
462*60cd40eeSSvyatoslav Ryhel 
463*60cd40eeSSvyatoslav Ryhel 	/* 50uA start with 50uA step */
464*60cd40eeSSvyatoslav Ryhel 	val = regval * 50 + 50;
465*60cd40eeSSvyatoslav Ryhel 	val *= 1000;
466*60cd40eeSSvyatoslav Ryhel 
467*60cd40eeSSvyatoslav Ryhel 	return sysfs_emit(buf, "%u\n", val);
468*60cd40eeSSvyatoslav Ryhel }
469*60cd40eeSSvyatoslav Ryhel 
top_off_threshold_current_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)470*60cd40eeSSvyatoslav Ryhel static ssize_t top_off_threshold_current_store(struct device *dev,
471*60cd40eeSSvyatoslav Ryhel 					       struct device_attribute *attr,
472*60cd40eeSSvyatoslav Ryhel 					       const char *buf, size_t count)
473*60cd40eeSSvyatoslav Ryhel {
474*60cd40eeSSvyatoslav Ryhel 	struct power_supply *psy = to_power_supply(dev);
475*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv = power_supply_get_drvdata(psy);
476*60cd40eeSSvyatoslav Ryhel 	unsigned long uamp;
477*60cd40eeSSvyatoslav Ryhel 	int err;
478*60cd40eeSSvyatoslav Ryhel 
479*60cd40eeSSvyatoslav Ryhel 	err = kstrtoul(buf, 10, &uamp);
480*60cd40eeSSvyatoslav Ryhel 	if (err)
481*60cd40eeSSvyatoslav Ryhel 		return err;
482*60cd40eeSSvyatoslav Ryhel 
483*60cd40eeSSvyatoslav Ryhel 	if (uamp < 50000 || uamp > 200000)
484*60cd40eeSSvyatoslav Ryhel 		return -EINVAL;
485*60cd40eeSSvyatoslav Ryhel 
486*60cd40eeSSvyatoslav Ryhel 	priv->toffs = uamp / 50000 - 1;
487*60cd40eeSSvyatoslav Ryhel 
488*60cd40eeSSvyatoslav Ryhel 	max8971_update_config(priv);
489*60cd40eeSSvyatoslav Ryhel 
490*60cd40eeSSvyatoslav Ryhel 	return count;
491*60cd40eeSSvyatoslav Ryhel }
492*60cd40eeSSvyatoslav Ryhel 
top_off_timer_show(struct device * dev,struct device_attribute * attr,char * buf)493*60cd40eeSSvyatoslav Ryhel static ssize_t top_off_timer_show(struct device *dev, struct device_attribute *attr,
494*60cd40eeSSvyatoslav Ryhel 				  char *buf)
495*60cd40eeSSvyatoslav Ryhel {
496*60cd40eeSSvyatoslav Ryhel 	struct power_supply *psy = to_power_supply(dev);
497*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv = power_supply_get_drvdata(psy);
498*60cd40eeSSvyatoslav Ryhel 	u32 regval;
499*60cd40eeSSvyatoslav Ryhel 	int err;
500*60cd40eeSSvyatoslav Ryhel 
501*60cd40eeSSvyatoslav Ryhel 	err = regmap_field_read(priv->rfield[TOPOFF_T], &regval);
502*60cd40eeSSvyatoslav Ryhel 	if (err)
503*60cd40eeSSvyatoslav Ryhel 		return err;
504*60cd40eeSSvyatoslav Ryhel 
505*60cd40eeSSvyatoslav Ryhel 	/* 10 min intervals */
506*60cd40eeSSvyatoslav Ryhel 	regval *= 10;
507*60cd40eeSSvyatoslav Ryhel 
508*60cd40eeSSvyatoslav Ryhel 	return sysfs_emit(buf, "%u\n", regval);
509*60cd40eeSSvyatoslav Ryhel }
510*60cd40eeSSvyatoslav Ryhel 
top_off_timer_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)511*60cd40eeSSvyatoslav Ryhel static ssize_t top_off_timer_store(struct device *dev, struct device_attribute *attr,
512*60cd40eeSSvyatoslav Ryhel 				   const char *buf, size_t count)
513*60cd40eeSSvyatoslav Ryhel {
514*60cd40eeSSvyatoslav Ryhel 	struct power_supply *psy = to_power_supply(dev);
515*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv = power_supply_get_drvdata(psy);
516*60cd40eeSSvyatoslav Ryhel 	unsigned long minutes;
517*60cd40eeSSvyatoslav Ryhel 	int err;
518*60cd40eeSSvyatoslav Ryhel 
519*60cd40eeSSvyatoslav Ryhel 	err = kstrtoul(buf, 10, &minutes);
520*60cd40eeSSvyatoslav Ryhel 	if (err)
521*60cd40eeSSvyatoslav Ryhel 		return err;
522*60cd40eeSSvyatoslav Ryhel 
523*60cd40eeSSvyatoslav Ryhel 	if (minutes > 70)
524*60cd40eeSSvyatoslav Ryhel 		return -EINVAL;
525*60cd40eeSSvyatoslav Ryhel 
526*60cd40eeSSvyatoslav Ryhel 	priv->tofft = minutes / 10;
527*60cd40eeSSvyatoslav Ryhel 
528*60cd40eeSSvyatoslav Ryhel 	max8971_update_config(priv);
529*60cd40eeSSvyatoslav Ryhel 
530*60cd40eeSSvyatoslav Ryhel 	return count;
531*60cd40eeSSvyatoslav Ryhel }
532*60cd40eeSSvyatoslav Ryhel 
533*60cd40eeSSvyatoslav Ryhel static DEVICE_ATTR_RW(fast_charge_timer);
534*60cd40eeSSvyatoslav Ryhel static DEVICE_ATTR_RW(top_off_threshold_current);
535*60cd40eeSSvyatoslav Ryhel static DEVICE_ATTR_RW(top_off_timer);
536*60cd40eeSSvyatoslav Ryhel 
537*60cd40eeSSvyatoslav Ryhel static struct attribute *max8971_attrs[] = {
538*60cd40eeSSvyatoslav Ryhel 	&dev_attr_fast_charge_timer.attr,
539*60cd40eeSSvyatoslav Ryhel 	&dev_attr_top_off_threshold_current.attr,
540*60cd40eeSSvyatoslav Ryhel 	&dev_attr_top_off_timer.attr,
541*60cd40eeSSvyatoslav Ryhel 	NULL
542*60cd40eeSSvyatoslav Ryhel };
543*60cd40eeSSvyatoslav Ryhel ATTRIBUTE_GROUPS(max8971);
544*60cd40eeSSvyatoslav Ryhel 
max8971_extcon_evt_worker(struct work_struct * work)545*60cd40eeSSvyatoslav Ryhel static void max8971_extcon_evt_worker(struct work_struct *work)
546*60cd40eeSSvyatoslav Ryhel {
547*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv =
548*60cd40eeSSvyatoslav Ryhel 		container_of(work, struct max8971_data, extcon_work.work);
549*60cd40eeSSvyatoslav Ryhel 	struct device *dev = priv->dev;
550*60cd40eeSSvyatoslav Ryhel 	struct extcon_dev *edev = priv->edev;
551*60cd40eeSSvyatoslav Ryhel 	u32 chgcc, dcilmt;
552*60cd40eeSSvyatoslav Ryhel 
553*60cd40eeSSvyatoslav Ryhel 	if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
554*60cd40eeSSvyatoslav Ryhel 		dev_dbg(dev, "USB SDP charger is connected\n");
555*60cd40eeSSvyatoslav Ryhel 		priv->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
556*60cd40eeSSvyatoslav Ryhel 		chgcc = 500000;
557*60cd40eeSSvyatoslav Ryhel 		dcilmt = 500000;
558*60cd40eeSSvyatoslav Ryhel 	} else if (extcon_get_state(edev, EXTCON_USB) > 0) {
559*60cd40eeSSvyatoslav Ryhel 		dev_dbg(dev, "USB charger is connected\n");
560*60cd40eeSSvyatoslav Ryhel 		priv->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
561*60cd40eeSSvyatoslav Ryhel 		chgcc = 500000;
562*60cd40eeSSvyatoslav Ryhel 		dcilmt = 500000;
563*60cd40eeSSvyatoslav Ryhel 	} else if (extcon_get_state(edev, EXTCON_DISP_MHL) > 0) {
564*60cd40eeSSvyatoslav Ryhel 		dev_dbg(dev, "MHL plug is connected\n");
565*60cd40eeSSvyatoslav Ryhel 		priv->usb_type = POWER_SUPPLY_USB_TYPE_SDP;
566*60cd40eeSSvyatoslav Ryhel 		chgcc = 500000;
567*60cd40eeSSvyatoslav Ryhel 		dcilmt = 500000;
568*60cd40eeSSvyatoslav Ryhel 	} else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {
569*60cd40eeSSvyatoslav Ryhel 		dev_dbg(dev, "USB DCP charger is connected\n");
570*60cd40eeSSvyatoslav Ryhel 		priv->usb_type = POWER_SUPPLY_USB_TYPE_DCP;
571*60cd40eeSSvyatoslav Ryhel 		chgcc = 900000;
572*60cd40eeSSvyatoslav Ryhel 		dcilmt = 1200000;
573*60cd40eeSSvyatoslav Ryhel 	} else if (extcon_get_state(edev, EXTCON_CHG_USB_FAST) > 0) {
574*60cd40eeSSvyatoslav Ryhel 		dev_dbg(dev, "USB FAST charger is connected\n");
575*60cd40eeSSvyatoslav Ryhel 		priv->usb_type = POWER_SUPPLY_USB_TYPE_ACA;
576*60cd40eeSSvyatoslav Ryhel 		chgcc = 900000;
577*60cd40eeSSvyatoslav Ryhel 		dcilmt = 1200000;
578*60cd40eeSSvyatoslav Ryhel 	} else if (extcon_get_state(edev, EXTCON_CHG_USB_SLOW) > 0) {
579*60cd40eeSSvyatoslav Ryhel 		dev_dbg(dev, "USB SLOW charger is connected\n");
580*60cd40eeSSvyatoslav Ryhel 		priv->usb_type = POWER_SUPPLY_USB_TYPE_ACA;
581*60cd40eeSSvyatoslav Ryhel 		chgcc = 900000;
582*60cd40eeSSvyatoslav Ryhel 		dcilmt = 1200000;
583*60cd40eeSSvyatoslav Ryhel 	} else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
584*60cd40eeSSvyatoslav Ryhel 		dev_dbg(dev, "USB CDP charger is connected\n");
585*60cd40eeSSvyatoslav Ryhel 		priv->usb_type = POWER_SUPPLY_USB_TYPE_CDP;
586*60cd40eeSSvyatoslav Ryhel 		chgcc = 900000;
587*60cd40eeSSvyatoslav Ryhel 		dcilmt = 1200000;
588*60cd40eeSSvyatoslav Ryhel 	} else {
589*60cd40eeSSvyatoslav Ryhel 		dev_dbg(dev, "USB state is unknown\n");
590*60cd40eeSSvyatoslav Ryhel 		priv->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
591*60cd40eeSSvyatoslav Ryhel 		return;
592*60cd40eeSSvyatoslav Ryhel 	}
593*60cd40eeSSvyatoslav Ryhel 
594*60cd40eeSSvyatoslav Ryhel 	regmap_field_write(priv->rfield[CPROT], MAX8971_CHGPROT_UNLOCKED);
595*60cd40eeSSvyatoslav Ryhel 
596*60cd40eeSSvyatoslav Ryhel 	max8971_set_integer(priv, CHG_CC, MAX8971_CHG_CC_MIN, MAX8971_CHG_CC_MAX,
597*60cd40eeSSvyatoslav Ryhel 			    MAX8971_CHG_CC_STEP, chgcc);
598*60cd40eeSSvyatoslav Ryhel 	max8971_set_integer(priv, DCI_LMT, MAX8971_DCILMT_MIN, MAX8971_DCILMT_MAX,
599*60cd40eeSSvyatoslav Ryhel 			    MAX8971_DCILMT_STEP, dcilmt);
600*60cd40eeSSvyatoslav Ryhel 
601*60cd40eeSSvyatoslav Ryhel 	regmap_field_write(priv->rfield[CPROT], MAX8971_CHGPROT_LOCKED);
602*60cd40eeSSvyatoslav Ryhel }
603*60cd40eeSSvyatoslav Ryhel 
extcon_get_charger_type(struct notifier_block * nb,unsigned long state,void * data)604*60cd40eeSSvyatoslav Ryhel static int extcon_get_charger_type(struct notifier_block *nb,
605*60cd40eeSSvyatoslav Ryhel 				   unsigned long state, void *data)
606*60cd40eeSSvyatoslav Ryhel {
607*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv =
608*60cd40eeSSvyatoslav Ryhel 		container_of(nb, struct max8971_data, extcon_nb);
609*60cd40eeSSvyatoslav Ryhel 	schedule_delayed_work(&priv->extcon_work, 0);
610*60cd40eeSSvyatoslav Ryhel 
611*60cd40eeSSvyatoslav Ryhel 	return NOTIFY_OK;
612*60cd40eeSSvyatoslav Ryhel }
613*60cd40eeSSvyatoslav Ryhel 
max8971_interrupt(int irq,void * dev_id)614*60cd40eeSSvyatoslav Ryhel static irqreturn_t max8971_interrupt(int irq, void *dev_id)
615*60cd40eeSSvyatoslav Ryhel {
616*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv = dev_id;
617*60cd40eeSSvyatoslav Ryhel 	struct device *dev = priv->dev;
618*60cd40eeSSvyatoslav Ryhel 	int err, state;
619*60cd40eeSSvyatoslav Ryhel 
620*60cd40eeSSvyatoslav Ryhel 	err = regmap_read(priv->regmap, MAX8971_REG_CHGINT, &state);
621*60cd40eeSSvyatoslav Ryhel 	if (err)
622*60cd40eeSSvyatoslav Ryhel 		dev_err(dev, "interrupt reg read failed %d\n", err);
623*60cd40eeSSvyatoslav Ryhel 
624*60cd40eeSSvyatoslav Ryhel 	err = regmap_write_bits(priv->regmap, MAX8971_REG_CHGINT_MASK,
625*60cd40eeSSvyatoslav Ryhel 				MAX8971_AICL_MASK, MAX8971_AICL_MASK);
626*60cd40eeSSvyatoslav Ryhel 	if (err)
627*60cd40eeSSvyatoslav Ryhel 		dev_err(dev, "failed to mask IRQ\n");
628*60cd40eeSSvyatoslav Ryhel 
629*60cd40eeSSvyatoslav Ryhel 	/* set presence prop */
630*60cd40eeSSvyatoslav Ryhel 	priv->present = state & MAX8971_REG_CHG_RST;
631*60cd40eeSSvyatoslav Ryhel 
632*60cd40eeSSvyatoslav Ryhel 	/* on every plug chip resets to default */
633*60cd40eeSSvyatoslav Ryhel 	if (priv->present)
634*60cd40eeSSvyatoslav Ryhel 		max8971_update_config(priv);
635*60cd40eeSSvyatoslav Ryhel 
636*60cd40eeSSvyatoslav Ryhel 	/* update supply status */
637*60cd40eeSSvyatoslav Ryhel 	power_supply_changed(priv->psy_mains);
638*60cd40eeSSvyatoslav Ryhel 
639*60cd40eeSSvyatoslav Ryhel 	return IRQ_HANDLED;
640*60cd40eeSSvyatoslav Ryhel }
641*60cd40eeSSvyatoslav Ryhel 
max8971_probe(struct i2c_client * client)642*60cd40eeSSvyatoslav Ryhel static int max8971_probe(struct i2c_client *client)
643*60cd40eeSSvyatoslav Ryhel {
644*60cd40eeSSvyatoslav Ryhel 	struct device *dev = &client->dev;
645*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv;
646*60cd40eeSSvyatoslav Ryhel 	struct device_node *extcon;
647*60cd40eeSSvyatoslav Ryhel 	struct power_supply_config cfg = { };
648*60cd40eeSSvyatoslav Ryhel 	int err, i;
649*60cd40eeSSvyatoslav Ryhel 
650*60cd40eeSSvyatoslav Ryhel 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
651*60cd40eeSSvyatoslav Ryhel 	if (!priv)
652*60cd40eeSSvyatoslav Ryhel 		return -ENOMEM;
653*60cd40eeSSvyatoslav Ryhel 
654*60cd40eeSSvyatoslav Ryhel 	priv->dev = dev;
655*60cd40eeSSvyatoslav Ryhel 	priv->usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN;
656*60cd40eeSSvyatoslav Ryhel 
657*60cd40eeSSvyatoslav Ryhel 	i2c_set_clientdata(client, priv);
658*60cd40eeSSvyatoslav Ryhel 
659*60cd40eeSSvyatoslav Ryhel 	priv->regmap = devm_regmap_init_i2c(client, &max8971_regmap_config);
660*60cd40eeSSvyatoslav Ryhel 	if (IS_ERR(priv->regmap))
661*60cd40eeSSvyatoslav Ryhel 		return dev_err_probe(dev, PTR_ERR(priv->regmap), "cannot allocate regmap\n");
662*60cd40eeSSvyatoslav Ryhel 
663*60cd40eeSSvyatoslav Ryhel 	for (i = 0; i < MAX8971_N_REGMAP_FIELDS; i++) {
664*60cd40eeSSvyatoslav Ryhel 		priv->rfield[i] = devm_regmap_field_alloc(dev, priv->regmap, max8971_reg_field[i]);
665*60cd40eeSSvyatoslav Ryhel 		if (IS_ERR(priv->rfield[i]))
666*60cd40eeSSvyatoslav Ryhel 			return dev_err_probe(dev, PTR_ERR(priv->rfield[i]),
667*60cd40eeSSvyatoslav Ryhel 					     "cannot allocate regmap field\n");
668*60cd40eeSSvyatoslav Ryhel 	}
669*60cd40eeSSvyatoslav Ryhel 
670*60cd40eeSSvyatoslav Ryhel 	cfg.attr_grp = max8971_groups;
671*60cd40eeSSvyatoslav Ryhel 	cfg.drv_data = priv;
672*60cd40eeSSvyatoslav Ryhel 	cfg.fwnode = dev_fwnode(dev);
673*60cd40eeSSvyatoslav Ryhel 
674*60cd40eeSSvyatoslav Ryhel 	priv->psy_mains = devm_power_supply_register(dev, &max8971_charger_desc, &cfg);
675*60cd40eeSSvyatoslav Ryhel 	if (IS_ERR(priv->psy_mains))
676*60cd40eeSSvyatoslav Ryhel 		return dev_err_probe(dev, PTR_ERR(priv->psy_mains),
677*60cd40eeSSvyatoslav Ryhel 				     "failed to register mains supply\n");
678*60cd40eeSSvyatoslav Ryhel 
679*60cd40eeSSvyatoslav Ryhel 	err = regmap_write_bits(priv->regmap, MAX8971_REG_CHGINT_MASK, MAX8971_AICL_MASK,
680*60cd40eeSSvyatoslav Ryhel 				MAX8971_AICL_MASK);
681*60cd40eeSSvyatoslav Ryhel 	if (err)
682*60cd40eeSSvyatoslav Ryhel 		return dev_err_probe(dev, err, "failed to mask IRQ\n");
683*60cd40eeSSvyatoslav Ryhel 
684*60cd40eeSSvyatoslav Ryhel 	err = devm_request_threaded_irq(dev, client->irq, NULL, &max8971_interrupt,
685*60cd40eeSSvyatoslav Ryhel 					IRQF_ONESHOT | IRQF_SHARED, client->name, priv);
686*60cd40eeSSvyatoslav Ryhel 	if (err)
687*60cd40eeSSvyatoslav Ryhel 		return dev_err_probe(dev, err, "failed to register IRQ %d\n", client->irq);
688*60cd40eeSSvyatoslav Ryhel 
689*60cd40eeSSvyatoslav Ryhel 	extcon = of_graph_get_remote_node(dev->of_node, -1, -1);
690*60cd40eeSSvyatoslav Ryhel 	if (!extcon)
691*60cd40eeSSvyatoslav Ryhel 		return 0;
692*60cd40eeSSvyatoslav Ryhel 
693*60cd40eeSSvyatoslav Ryhel 	priv->edev = extcon_find_edev_by_node(extcon);
694*60cd40eeSSvyatoslav Ryhel 	of_node_put(extcon);
695*60cd40eeSSvyatoslav Ryhel 	if (IS_ERR(priv->edev))
696*60cd40eeSSvyatoslav Ryhel 		return dev_err_probe(dev, PTR_ERR(priv->edev), "failed to find extcon\n");
697*60cd40eeSSvyatoslav Ryhel 
698*60cd40eeSSvyatoslav Ryhel 	err = devm_delayed_work_autocancel(dev, &priv->extcon_work,
699*60cd40eeSSvyatoslav Ryhel 					   max8971_extcon_evt_worker);
700*60cd40eeSSvyatoslav Ryhel 	if (err)
701*60cd40eeSSvyatoslav Ryhel 		return dev_err_probe(dev, err, "failed to add extcon evt stop action\n");
702*60cd40eeSSvyatoslav Ryhel 
703*60cd40eeSSvyatoslav Ryhel 	priv->extcon_nb.notifier_call = extcon_get_charger_type;
704*60cd40eeSSvyatoslav Ryhel 
705*60cd40eeSSvyatoslav Ryhel 	err = devm_extcon_register_notifier_all(dev, priv->edev, &priv->extcon_nb);
706*60cd40eeSSvyatoslav Ryhel 	if (err)
707*60cd40eeSSvyatoslav Ryhel 		return dev_err_probe(dev, err, "failed to register notifier\n");
708*60cd40eeSSvyatoslav Ryhel 
709*60cd40eeSSvyatoslav Ryhel 	/* Initial configuration work with 1 sec delay */
710*60cd40eeSSvyatoslav Ryhel 	schedule_delayed_work(&priv->extcon_work, msecs_to_jiffies(1000));
711*60cd40eeSSvyatoslav Ryhel 
712*60cd40eeSSvyatoslav Ryhel 	return 0;
713*60cd40eeSSvyatoslav Ryhel }
714*60cd40eeSSvyatoslav Ryhel 
max8971_resume(struct device * dev)715*60cd40eeSSvyatoslav Ryhel static int __maybe_unused max8971_resume(struct device *dev)
716*60cd40eeSSvyatoslav Ryhel {
717*60cd40eeSSvyatoslav Ryhel 	struct i2c_client *client = to_i2c_client(dev);
718*60cd40eeSSvyatoslav Ryhel 	struct max8971_data *priv = i2c_get_clientdata(client);
719*60cd40eeSSvyatoslav Ryhel 
720*60cd40eeSSvyatoslav Ryhel 	irq_wake_thread(client->irq, priv);
721*60cd40eeSSvyatoslav Ryhel 
722*60cd40eeSSvyatoslav Ryhel 	return 0;
723*60cd40eeSSvyatoslav Ryhel }
724*60cd40eeSSvyatoslav Ryhel 
725*60cd40eeSSvyatoslav Ryhel static SIMPLE_DEV_PM_OPS(max8971_pm_ops, NULL, max8971_resume);
726*60cd40eeSSvyatoslav Ryhel 
727*60cd40eeSSvyatoslav Ryhel static const struct of_device_id max8971_match_ids[] = {
728*60cd40eeSSvyatoslav Ryhel 	{ .compatible = "maxim,max8971" },
729*60cd40eeSSvyatoslav Ryhel 	{ /* sentinel */ }
730*60cd40eeSSvyatoslav Ryhel };
731*60cd40eeSSvyatoslav Ryhel MODULE_DEVICE_TABLE(of, max8971_match_ids);
732*60cd40eeSSvyatoslav Ryhel 
733*60cd40eeSSvyatoslav Ryhel static const struct i2c_device_id max8971_i2c_id[] = {
734*60cd40eeSSvyatoslav Ryhel 	{ "max8971" },
735*60cd40eeSSvyatoslav Ryhel 	{ }
736*60cd40eeSSvyatoslav Ryhel };
737*60cd40eeSSvyatoslav Ryhel MODULE_DEVICE_TABLE(i2c, max8971_i2c_id);
738*60cd40eeSSvyatoslav Ryhel 
739*60cd40eeSSvyatoslav Ryhel static struct i2c_driver max8971_driver = {
740*60cd40eeSSvyatoslav Ryhel 	.driver = {
741*60cd40eeSSvyatoslav Ryhel 		.name = "max8971-charger",
742*60cd40eeSSvyatoslav Ryhel 		.of_match_table = max8971_match_ids,
743*60cd40eeSSvyatoslav Ryhel 		.pm = &max8971_pm_ops,
744*60cd40eeSSvyatoslav Ryhel 	},
745*60cd40eeSSvyatoslav Ryhel 	.probe = max8971_probe,
746*60cd40eeSSvyatoslav Ryhel 	.id_table = max8971_i2c_id,
747*60cd40eeSSvyatoslav Ryhel };
748*60cd40eeSSvyatoslav Ryhel module_i2c_driver(max8971_driver);
749*60cd40eeSSvyatoslav Ryhel 
750*60cd40eeSSvyatoslav Ryhel MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
751*60cd40eeSSvyatoslav Ryhel MODULE_DESCRIPTION("MAX8971 Charger Driver");
752*60cd40eeSSvyatoslav Ryhel MODULE_LICENSE("GPL");
753