xref: /OK3568_Linux_fs/u-boot/drivers/power/charge/bq25890_charger.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * (C) Copyright 2022 Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <dm.h>
9 #include <dm/device.h>
10 #include <dm/uclass.h>
11 #include <power/fuel_gauge.h>
12 #include <power/pmic.h>
13 #include <power/power_delivery/power_delivery.h>
14 #include <linux/usb/phy-rockchip-usb2.h>
15 
16 DECLARE_GLOBAL_DATA_PTR;
17 
18 #define BQ25890_CHARGE_CURRENT_1500MA		1500
19 #define BQ25890_SDP_INPUT_CURRENT_500MA		0x45
20 #define BQ25890_DCP_INPUT_CURRENT_1500MA	0x4f
21 #define BQ25890_DCP_INPUT_CURRENT_2000MA	0x54
22 #define BQ25890_DCP_INPUT_CURRENT_3000MA	0x5e
23 
24 #define WATCHDOG_ENSABLE			(0x03 << 4)
25 
26 #define BQ25890_CHARGEOPTION0_REG		0x07
27 #define BQ25890_CHARGECURREN_REG		0x04
28 #define BQ25890_CHARGERSTAUS_REG		0x0B
29 #define BQ25890_INPUTVOLTAGE_REG		0x0D
30 #define BQ25890_INPUTCURREN_REG			0x00
31 #define BQ25890_AUTO_DPDM_REG			0x02
32 
33 /*
34  * Most of the val -> idx conversions can be computed, given the minimum,
35  * maximum and the step between values. For the rest of conversions, we use
36  * lookup tables.
37  */
38 enum bq25890_table_ids {
39 	/* range tables */
40 	TBL_ICHG,
41 	TBL_ITERM,
42 	TBL_IPRECHG,
43 	TBL_VREG,
44 	TBL_BATCMP,
45 	TBL_VCLAMP,
46 	TBL_BOOSTV,
47 	TBL_SYSVMIN,
48 	TBL_VINDPM,
49 	TBL_IINLIM,
50 
51 	/* lookup tables */
52 	TBL_TREG,
53 	TBL_BOOSTI,
54 };
55 
56 struct bq25890 {
57 	struct udevice *dev;
58 	u32 ichg;
59 	u32 chip_id;
60 	struct udevice *pd;
61 	bool pd_online;
62 };
63 
64 /* Thermal Regulation Threshold lookup table, in degrees Celsius */
65 static const u32 bq25890_treg_tbl[] = { 60, 80, 100, 120 };
66 
67 #define BQ25890_TREG_TBL_SIZE		ARRAY_SIZE(bq25890_treg_tbl)
68 
69 /* Boost mode current limit lookup table, in uA */
70 static const u32 bq25890_boosti_tbl[] = {
71 	500000, 700000, 1100000, 1300000, 1600000, 1800000, 2100000, 2400000
72 };
73 
74 #define BQ25890_BOOSTI_TBL_SIZE		ARRAY_SIZE(bq25890_boosti_tbl)
75 
76 struct bq25890_range {
77 	u32 min;
78 	u32 max;
79 	u32 step;
80 };
81 
82 struct bq25890_lookup {
83 	const u32 *tbl;
84 	u32 size;
85 };
86 
87 static const union {
88 	struct bq25890_range  rt;
89 	struct bq25890_lookup lt;
90 } bq25890_tables[] = {
91 	/* range tables */
92 	[TBL_ICHG] =	{ .rt = {0,	  5056000, 64000} },	 /* uA */
93 	[TBL_ITERM] =	{ .rt = {64000,   1024000, 64000} },	 /* uA */
94 	[TBL_VREG] =	{ .rt = {3840000, 4608000, 16000} },	 /* uV */
95 	[TBL_BATCMP] =	{ .rt = {0,	  140,     20} },	 /* mOhm */
96 	[TBL_VCLAMP] =	{ .rt = {0,	  224000,  32000} },	 /* uV */
97 	[TBL_BOOSTV] =	{ .rt = {4550000, 5510000, 64000} },	 /* uV */
98 	[TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} },	 /* uV */
99 	[TBL_VINDPM] = { .rt = {2600000, 15300000, 100000} }, /* uV */
100 	[TBL_IINLIM] = { .rt = {100, 3250000, 100000} }, /* uA */
101 
102 	/* lookup tables */
103 	[TBL_TREG] =	{ .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} },
104 	[TBL_BOOSTI] =	{ .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} }
105 };
106 
bq25890_read(struct bq25890 * charger,uint reg)107 static int bq25890_read(struct bq25890 *charger, uint reg)
108 {
109 	u16 val;
110 	int ret;
111 
112 	ret = dm_i2c_read(charger->dev, reg, (u8 *)&val, 1);
113 	if (ret) {
114 		printf("bq25890: read %#x error, ret=%d", reg, ret);
115 		return ret;
116 	}
117 
118 	return val;
119 }
120 
bq25890_write(struct bq25890 * charger,uint reg,u16 val)121 static int bq25890_write(struct bq25890 *charger, uint reg, u16 val)
122 {
123 	int ret;
124 
125 	ret = dm_i2c_write(charger->dev, reg, (u8 *)&val, 1);
126 	if (ret) {
127 		printf("bq25890: write %#x error, ret=%d", reg, ret);
128 		return ret;
129 	}
130 
131 	return 0;
132 }
133 
bq25890_find_idx(u32 value,enum bq25890_table_ids id)134 static u8 bq25890_find_idx(u32 value, enum bq25890_table_ids id)
135 {
136 	u8 idx;
137 
138 	if (id >= TBL_TREG) {
139 		const u32 *tbl = bq25890_tables[id].lt.tbl;
140 		u32 tbl_size = bq25890_tables[id].lt.size;
141 
142 		for (idx = 1; idx < tbl_size && tbl[idx] <= value; idx++)
143 			;
144 	} else {
145 		const struct bq25890_range *rtbl = &bq25890_tables[id].rt;
146 		u8 rtbl_size;
147 
148 		rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1;
149 
150 		for (idx = 1;
151 		     idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value);
152 		     idx++)
153 			;
154 	}
155 
156 	return idx - 1;
157 }
158 
bq25890_charger_status(struct bq25890 * charger)159 static bool bq25890_charger_status(struct bq25890 *charger)
160 {
161 	int state_of_charger;
162 	u16 value;
163 	int i = 0;
164 
165 __retry:
166 	value = bq25890_read(charger, BQ25890_CHARGERSTAUS_REG);
167 	state_of_charger = value & 0x04;
168 	if (!state_of_charger && charger->pd_online) {
169 		if (i < 3) {
170 			i++;
171 			mdelay(20);
172 			goto __retry;
173 		}
174 	}
175 
176 	return state_of_charger;
177 }
178 
bq257xx_charger_status(struct udevice * dev)179 static bool bq257xx_charger_status(struct udevice *dev)
180 {
181 	struct bq25890 *charger = dev_get_priv(dev);
182 
183 	return bq25890_charger_status(charger);
184 }
185 
bq25890_charger_capability(struct udevice * dev)186 static int bq25890_charger_capability(struct udevice *dev)
187 {
188 	return FG_CAP_CHARGER;
189 }
190 
bq25890_get_usb_type(void)191 static int bq25890_get_usb_type(void)
192 {
193 #ifdef CONFIG_PHY_ROCKCHIP_INNO_USB2
194 	return rockchip_chg_get_type();
195 #else
196 	return 0;
197 #endif
198 }
199 
bq25890_get_pd_output_val(struct bq25890 * charger,int * vol,int * cur)200 static int bq25890_get_pd_output_val(struct bq25890 *charger,
201 				     int *vol, int *cur)
202 {
203 	struct power_delivery_data pd_data;
204 	int ret;
205 
206 	if (!charger->pd)
207 		return -EINVAL;
208 
209 	memset(&pd_data, 0, sizeof(pd_data));
210 	ret = power_delivery_get_data(charger->pd, &pd_data);
211 	if (ret)
212 		return ret;
213 	if (!pd_data.online || !pd_data.voltage || !pd_data.current)
214 		return -EINVAL;
215 
216 	*vol = pd_data.voltage;
217 	*cur = pd_data.current;
218 	charger->pd_online = pd_data.online;
219 
220 	return 0;
221 }
222 
bq25890_set_auto_dpdm_detect(struct bq25890 * charger,bool enable)223 static void bq25890_set_auto_dpdm_detect(struct bq25890 *charger, bool enable)
224 {
225 	u8 value;
226 
227 	value = bq25890_read(charger, BQ25890_AUTO_DPDM_REG);
228 	value &= 0xfe;
229 	value |= enable;
230 	bq25890_write(charger, BQ25890_AUTO_DPDM_REG, value);
231 }
232 
bq25890_charger_current_init(struct bq25890 * charger)233 static void bq25890_charger_current_init(struct bq25890 *charger)
234 {
235 	u8 charge_current =  bq25890_find_idx(BQ25890_CHARGE_CURRENT_1500MA * 1000, TBL_ICHG);
236 	u8 sdp_inputcurrent = BQ25890_SDP_INPUT_CURRENT_500MA;
237 	u8 dcp_inputcurrent = BQ25890_DCP_INPUT_CURRENT_1500MA;
238 	int pd_inputvol,  pd_inputcurrent;
239 	u16 vol_idx = 0, cur_idx;
240 	u8 temp;
241 
242 	temp = bq25890_read(charger, BQ25890_CHARGEOPTION0_REG);
243 	temp &= (~WATCHDOG_ENSABLE);
244 	bq25890_write(charger, BQ25890_CHARGEOPTION0_REG, temp);
245 
246 	if (!bq25890_get_pd_output_val(charger, &pd_inputvol,
247 				       &pd_inputcurrent)) {
248 		printf("bq25890: pd charge %duV, %duA\n", pd_inputvol, pd_inputcurrent);
249 		if (pd_inputvol > 5000000) {
250 			vol_idx = bq25890_find_idx((pd_inputvol - 1280000 - 3200000),
251 						   TBL_VINDPM);
252 			vol_idx = vol_idx << 6;
253 		}
254 		cur_idx = bq25890_find_idx(pd_inputcurrent,
255 					   TBL_IINLIM);
256 		cur_idx  = cur_idx << 8;
257 		if (pd_inputcurrent != 0) {
258 			bq25890_set_auto_dpdm_detect(charger, false);
259 			bq25890_write(charger, BQ25890_INPUTCURREN_REG,
260 				      cur_idx);
261 			if (vol_idx)
262 				bq25890_write(charger, BQ25890_INPUTVOLTAGE_REG,
263 					      vol_idx);
264 			charge_current = bq25890_find_idx(charger->ichg,
265 							  TBL_ICHG);
266 		}
267 	} else {
268 		if (bq25890_get_usb_type() > 1)
269 			bq25890_write(charger, BQ25890_INPUTCURREN_REG,
270 				      dcp_inputcurrent);
271 		else
272 			bq25890_write(charger, BQ25890_INPUTCURREN_REG,
273 				      sdp_inputcurrent);
274 	}
275 
276 	if (bq25890_charger_status(charger))
277 		bq25890_write(charger, BQ25890_CHARGECURREN_REG,
278 			      charge_current);
279 }
280 
bq25890_ofdata_to_platdata(struct udevice * dev)281 static int bq25890_ofdata_to_platdata(struct udevice *dev)
282 {
283 	struct bq25890 *charger = dev_get_priv(dev);
284 
285 	charger->dev = dev;
286 	charger->ichg = dev_read_u32_default(dev, "ti,charge-current", 0);
287 
288 	return 0;
289 }
290 
bq25890_probe(struct udevice * dev)291 static int bq25890_probe(struct udevice *dev)
292 {
293 	struct bq25890 *charger = dev_get_priv(dev);
294 	int ret;
295 
296 	ret = uclass_get_device(UCLASS_PD, 0, &charger->pd);
297 	if (ret) {
298 		if (ret == -ENODEV)
299 			printf("Can't find PD\n");
300 		else
301 			printf("Get UCLASS PD failed: %d\n", ret);
302 
303 		charger->pd = NULL;
304 	}
305 
306 	bq25890_charger_current_init(charger);
307 
308 	return 0;
309 }
310 
311 static const struct udevice_id charger_ids[] = {
312 	{ .compatible = "ti,bq25890" },
313 	{ .compatible = "sy,sy6970" },
314 	{ },
315 };
316 
317 static struct dm_fuel_gauge_ops charger_ops = {
318 	.get_chrg_online = bq257xx_charger_status,
319 	.capability = bq25890_charger_capability,
320 };
321 
322 U_BOOT_DRIVER(bq25890_charger) = {
323 	.name = "bq25890_charger",
324 	.id = UCLASS_FG,
325 	.probe = bq25890_probe,
326 	.of_match = charger_ids,
327 	.ops = &charger_ops,
328 	.ofdata_to_platdata = bq25890_ofdata_to_platdata,
329 	.priv_auto_alloc_size = sizeof(struct bq25890),
330 };
331