1 /*
2  * Copyright 2024 Google LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * BQ25180 Datasheet: https://www.ti.com/lit/gpn/bq25180
7  */
8 
9 #define DT_DRV_COMPAT ti_bq25180
10 
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/charger.h>
13 #include <zephyr/drivers/i2c.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/logging/log.h>
16 #include <zephyr/sys/util.h>
17 
18 LOG_MODULE_REGISTER(bq25180, CONFIG_CHARGER_LOG_LEVEL);
19 
20 #define BQ25180_STAT0 0x00
21 #define BQ25180_STAT1 0x01
22 #define BQ25180_FLAG0 0x02
23 #define BQ25180_VBAT_CTRL 0x03
24 #define BQ25180_ICHG_CTRL 0x04
25 #define BQ25180_IC_CTRL 0x07
26 #define BQ25180_SHIP_RST 0x09
27 #define BQ25180_MASK_ID 0x0c
28 
29 #define BQ25180_STAT0_CHG_STAT_MASK GENMASK(6, 5)
30 #define BQ25180_STAT0_CHG_STAT_NOT_CHARGING 0x00
31 #define BQ25180_STAT0_CHG_STAT_CONSTANT_CURRENT 0x01
32 #define BQ25180_STAT0_CHG_STAT_CONSTANT_VOLTAGE 0x02
33 #define BQ25180_STAT0_CHG_STAT_DONE 0x03
34 #define BQ25180_STAT0_VIN_PGOOD_STAT BIT(0)
35 #define BQ25180_VBAT_MSK                        GENMASK(6, 0)
36 #define BQ25180_ICHG_CHG_DIS BIT(7)
37 #define BQ25180_ICHG_MSK GENMASK(6, 0)
38 #define BQ25180_IC_CTRL_VRCH_100                0x00
39 #define BQ25180_IC_CTRL_VRCH_200                BIT(5)
40 #define BQ25180_IC_CTRL_VRCH_MSK                BIT(5)
41 #define BQ25180_VLOWV_SEL_2_8                   BIT(6)
42 #define BQ25180_VLOWV_SEL_3_0                   0x00
43 #define BQ25180_VLOWV_SEL_MSK                   BIT(6)
44 #define BQ25180_WATCHDOG_SEL_1_MSK GENMASK(1, 0)
45 #define BQ25180_WATCHDOG_DISABLE 0x03
46 #define BQ25180_DEVICE_ID_MSK GENMASK(3, 0)
47 #define BQ25180_DEVICE_ID 0x00
48 #define BQ25180_SHIP_RST_EN_RST_SHIP_MSK GENMASK(6, 5)
49 #define BQ25180_SHIP_RST_EN_RST_SHIP_ADAPTER 0x20
50 #define BQ25180_SHIP_RST_EN_RST_SHIP_BUTTON 0x40
51 
52 /* Charging current limits */
53 #define BQ25180_CURRENT_MIN_MA 5
54 #define BQ25180_CURRENT_MAX_MA 1000
55 #define BQ25180_VOLTAGE_MIN_MV 3500
56 #define BQ25180_VOLTAGE_MAX_MV 4650
57 
58 #define BQ25180_FACTOR_VBAT_TO_MV 10
59 
60 struct bq25180_config {
61 	struct i2c_dt_spec i2c;
62 	uint32_t initial_current_microamp;
63 	uint32_t max_voltage_microvolt;
64 	uint32_t recharge_voltage_microvolt;
65 	uint32_t precharge_threshold_voltage_microvolt;
66 };
67 
68 /*
69  * For ICHG <= 35mA = ICHGCODE + 5mA
70  * For ICHG > 35mA = 40 + ((ICHGCODE-31)*10)mA.
71  * Maximum programmable current = 1000mA
72  *
73  * Return: value between 0 and 127, negative on error.
74  */
bq25180_ma_to_ichg(uint32_t current_ma,uint8_t * ichg)75 static int bq25180_ma_to_ichg(uint32_t current_ma, uint8_t *ichg)
76 {
77 	if (!IN_RANGE(current_ma, BQ25180_CURRENT_MIN_MA, BQ25180_CURRENT_MAX_MA)) {
78 		LOG_WRN("charging current out of range: %dmA, "
79 			"clamping to the nearest limit", current_ma);
80 	}
81 	current_ma = CLAMP(current_ma, BQ25180_CURRENT_MIN_MA, BQ25180_CURRENT_MAX_MA);
82 
83 	if (current_ma <= 35) {
84 		*ichg = current_ma - 5;
85 		return 0;
86 	}
87 
88 	*ichg = (current_ma - 40) / 10 + 31;
89 
90 	return 0;
91 }
92 
bq25180_ichg_to_ma(uint8_t ichg)93 static uint32_t bq25180_ichg_to_ma(uint8_t ichg)
94 {
95 	ichg &= BQ25180_ICHG_MSK;
96 
97 	if (ichg <= 30) {
98 		return (ichg + 5);
99 	}
100 
101 	return (ichg - 31) * 10 + 40;
102 }
103 
bq25180_mv_to_vbatreg(const struct bq25180_config * cfg,uint32_t voltage_mv,uint8_t * vbat)104 static int bq25180_mv_to_vbatreg(const struct bq25180_config *cfg, uint32_t voltage_mv,
105 				 uint8_t *vbat)
106 {
107 	if (!IN_RANGE(voltage_mv, BQ25180_VOLTAGE_MIN_MV, cfg->max_voltage_microvolt)) {
108 		LOG_WRN("charging voltage out of range: %dmV, "
109 			"clamping to the nearest limit",
110 			voltage_mv);
111 	}
112 	voltage_mv = CLAMP(voltage_mv, BQ25180_VOLTAGE_MIN_MV, cfg->max_voltage_microvolt);
113 
114 	*vbat = (voltage_mv - BQ25180_VOLTAGE_MIN_MV) / BQ25180_FACTOR_VBAT_TO_MV;
115 
116 	return 0;
117 }
118 
bq25180_vbatreg_to_mv(uint8_t vbat)119 static uint32_t bq25180_vbatreg_to_mv(uint8_t vbat)
120 {
121 	vbat &= BQ25180_VBAT_MSK;
122 
123 	return (vbat * BQ25180_FACTOR_VBAT_TO_MV) + BQ25180_VOLTAGE_MIN_MV;
124 }
125 
bq25183_charge_enable(const struct device * dev,const bool enable)126 static int bq25183_charge_enable(const struct device *dev, const bool enable)
127 {
128 	const struct bq25180_config *cfg = dev->config;
129 	uint8_t value = enable ? 0 : BQ25180_ICHG_CHG_DIS;
130 	int ret;
131 
132 	ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_ICHG_CTRL,
133 				     BQ25180_ICHG_CHG_DIS, value);
134 	if (ret < 0) {
135 		return ret;
136 	}
137 
138 	return 0;
139 }
140 
bq25180_set_charge_current(const struct device * dev,uint32_t const_charge_current_ua)141 static int bq25180_set_charge_current(const struct device *dev,
142 				      uint32_t const_charge_current_ua)
143 {
144 	const struct bq25180_config *cfg = dev->config;
145 	uint8_t val;
146 	int ret;
147 
148 	ret = bq25180_ma_to_ichg(const_charge_current_ua / 1000, &val);
149 	if (ret < 0) {
150 		return ret;
151 	}
152 
153 	ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_ICHG_CTRL,
154 				     BQ25180_ICHG_MSK, val);
155 	if (ret < 0) {
156 		return ret;
157 	}
158 
159 	return 0;
160 }
161 
bq25180_get_charge_current(const struct device * dev,uint32_t * const_charge_current_ua)162 static int bq25180_get_charge_current(const struct device *dev,
163 				      uint32_t *const_charge_current_ua)
164 {
165 	const struct bq25180_config *cfg = dev->config;
166 	uint8_t val;
167 	int ret;
168 
169 	ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_ICHG_CTRL, &val);
170 	if (ret < 0) {
171 		return ret;
172 	}
173 
174 	*const_charge_current_ua = bq25180_ichg_to_ma(val) * 1000;
175 
176 	return 0;
177 }
178 
bq25180_set_charge_voltage(const struct device * dev,uint32_t const_charge_voltage_uv)179 static int bq25180_set_charge_voltage(const struct device *dev, uint32_t const_charge_voltage_uv)
180 {
181 	const struct bq25180_config *cfg = dev->config;
182 	uint8_t val;
183 	int ret;
184 
185 	ret = bq25180_mv_to_vbatreg(cfg, const_charge_voltage_uv / 1000, &val);
186 	if (ret < 0) {
187 		return ret;
188 	}
189 
190 	ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_VBAT_CTRL, BQ25180_VBAT_MSK, val);
191 	if (ret < 0) {
192 		return ret;
193 	}
194 
195 	return 0;
196 }
197 
bq25180_get_charge_voltage(const struct device * dev,uint32_t * const_charge_voltage_uv)198 static int bq25180_get_charge_voltage(const struct device *dev, uint32_t *const_charge_voltage_uv)
199 {
200 	const struct bq25180_config *cfg = dev->config;
201 	uint8_t val;
202 	int ret;
203 
204 	ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_VBAT_CTRL, &val);
205 	if (ret < 0) {
206 		return ret;
207 	}
208 
209 	*const_charge_voltage_uv = bq25180_vbatreg_to_mv(val) * 1000;
210 
211 	return 0;
212 }
213 
bq25180_get_online(const struct device * dev,enum charger_online * online)214 static int bq25180_get_online(const struct device *dev,
215 			      enum charger_online *online)
216 {
217 	const struct bq25180_config *cfg = dev->config;
218 	uint8_t val;
219 	int ret;
220 
221 	ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_STAT0, &val);
222 	if (ret < 0) {
223 		return ret;
224 	}
225 
226 	if ((val & BQ25180_STAT0_VIN_PGOOD_STAT) != 0x00) {
227 		*online = CHARGER_ONLINE_FIXED;
228 	} else {
229 		*online = CHARGER_ONLINE_OFFLINE;
230 	}
231 
232 	return 0;
233 }
234 
bq25180_get_status(const struct device * dev,enum charger_status * status)235 static int bq25180_get_status(const struct device *dev,
236 			      enum charger_status *status)
237 {
238 	const struct bq25180_config *cfg = dev->config;
239 	uint8_t stat0;
240 	uint8_t ichg_ctrl;
241 	int ret;
242 
243 	ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_STAT0, &stat0);
244 	if (ret < 0) {
245 		return ret;
246 	}
247 
248 	if ((stat0 & BQ25180_STAT0_VIN_PGOOD_STAT) == 0x00) {
249 		*status = CHARGER_STATUS_DISCHARGING;
250 		return 0;
251 	}
252 
253 	ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_ICHG_CTRL, &ichg_ctrl);
254 	if (ret < 0) {
255 		return ret;
256 	}
257 
258 	if ((ichg_ctrl & BQ25180_ICHG_CHG_DIS) != 0x00) {
259 		*status = CHARGER_STATUS_NOT_CHARGING;
260 		return 0;
261 	}
262 
263 	switch (FIELD_GET(BQ25180_STAT0_CHG_STAT_MASK, stat0)) {
264 	case BQ25180_STAT0_CHG_STAT_NOT_CHARGING:
265 		*status = CHARGER_STATUS_NOT_CHARGING;
266 		break;
267 	case BQ25180_STAT0_CHG_STAT_CONSTANT_CURRENT:
268 	case BQ25180_STAT0_CHG_STAT_CONSTANT_VOLTAGE:
269 		*status = CHARGER_STATUS_CHARGING;
270 		break;
271 	case BQ25180_STAT0_CHG_STAT_DONE:
272 		*status = CHARGER_STATUS_FULL;
273 		break;
274 	}
275 
276 	return 0;
277 }
278 
bq25180_get_prop(const struct device * dev,charger_prop_t prop,union charger_propval * val)279 static int bq25180_get_prop(const struct device *dev, charger_prop_t prop,
280 			    union charger_propval *val)
281 {
282 	switch (prop) {
283 	case CHARGER_PROP_ONLINE:
284 		return bq25180_get_online(dev, &val->online);
285 	case CHARGER_PROP_STATUS:
286 		return bq25180_get_status(dev, &val->status);
287 	case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA:
288 		return bq25180_get_charge_current(dev, &val->const_charge_current_ua);
289 	case CHARGER_PROP_CONSTANT_CHARGE_VOLTAGE_UV:
290 		return bq25180_get_charge_voltage(dev, &val->const_charge_voltage_uv);
291 	default:
292 		return -ENOTSUP;
293 	}
294 }
295 
bq25180_set_prop(const struct device * dev,charger_prop_t prop,const union charger_propval * val)296 static int bq25180_set_prop(const struct device *dev, charger_prop_t prop,
297 			    const union charger_propval *val)
298 {
299 	switch (prop) {
300 	case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA:
301 		return bq25180_set_charge_current(dev, val->const_charge_current_ua);
302 	case CHARGER_PROP_CONSTANT_CHARGE_VOLTAGE_UV:
303 		return bq25180_set_charge_voltage(dev, val->const_charge_voltage_uv);
304 	default:
305 		return -ENOTSUP;
306 	}
307 }
308 
309 static DEVICE_API(charger, bq25180_api) = {
310 	.get_property = bq25180_get_prop,
311 	.set_property = bq25180_set_prop,
312 	.charge_enable = bq25183_charge_enable,
313 };
314 
bq25180_init(const struct device * dev)315 static int bq25180_init(const struct device *dev)
316 {
317 	const struct bq25180_config *cfg = dev->config;
318 	uint8_t val;
319 	int ret;
320 
321 	ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_MASK_ID, &val);
322 	if (ret < 0) {
323 		return ret;
324 	}
325 
326 	val &= BQ25180_DEVICE_ID_MSK;
327 	if (val != BQ25180_DEVICE_ID) {
328 		LOG_ERR("Invalid device id: %02x", val);
329 		return -EINVAL;
330 	}
331 
332 	/* Disable the watchdog */
333 	ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_IC_CTRL,
334 				     BQ25180_WATCHDOG_SEL_1_MSK,
335 				     BQ25180_WATCHDOG_DISABLE);
336 	if (ret < 0) {
337 		return ret;
338 	}
339 
340 	ret = bq25180_set_charge_voltage(dev, cfg->max_voltage_microvolt);
341 	if (ret < 0) {
342 		LOG_ERR("Could not set the target voltage. (rc: %d)", ret);
343 		return ret;
344 	}
345 
346 	if (cfg->recharge_voltage_microvolt > 0) {
347 		if ((cfg->max_voltage_microvolt - cfg->recharge_voltage_microvolt) > 100000) {
348 			val = BQ25180_IC_CTRL_VRCH_200;
349 		} else {
350 			val = BQ25180_IC_CTRL_VRCH_100;
351 		}
352 
353 		ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_IC_CTRL, BQ25180_IC_CTRL_VRCH_MSK,
354 					     val);
355 		if (ret < 0) {
356 			return ret;
357 		}
358 	}
359 
360 	/* Precharge threshold voltage */
361 	if (cfg->precharge_threshold_voltage_microvolt <= 2800000) {
362 		val = BQ25180_VLOWV_SEL_2_8;
363 	} else {
364 		val = BQ25180_VLOWV_SEL_3_0;
365 	}
366 
367 	ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_IC_CTRL, BQ25180_VLOWV_SEL_MSK, val);
368 	if (ret < 0) {
369 		return ret;
370 	}
371 
372 	if (cfg->initial_current_microamp > 0) {
373 		ret = bq25180_set_charge_current(dev, cfg->initial_current_microamp);
374 		if (ret < 0) {
375 			return ret;
376 		}
377 	}
378 
379 	return 0;
380 }
381 
382 #define CHARGER_BQ25180_INIT(inst)                                                                 \
383 	static const struct bq25180_config bq25180_config_##inst = {                               \
384 		.i2c = I2C_DT_SPEC_INST_GET(inst),                                                 \
385 		.initial_current_microamp =                                                        \
386 			DT_INST_PROP(inst, constant_charge_current_max_microamp),                  \
387 		.max_voltage_microvolt =                                                           \
388 			DT_INST_PROP(inst, constant_charge_voltage_max_microvolt),                 \
389 		.recharge_voltage_microvolt =                                                      \
390 			DT_INST_PROP_OR(inst, re_charge_voltage_microvolt, 0),                     \
391 		.precharge_threshold_voltage_microvolt =                                           \
392 			DT_INST_PROP(inst, precharge_voltage_threshold_microvolt),                 \
393 	};                                                                                         \
394                                                                                                    \
395 	DEVICE_DT_INST_DEFINE(inst, bq25180_init, NULL, NULL, &bq25180_config_##inst, POST_KERNEL, \
396 			      CONFIG_CHARGER_INIT_PRIORITY, &bq25180_api);
397 
398 DT_INST_FOREACH_STATUS_OKAY(CHARGER_BQ25180_INIT)
399