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