Browse Source
Add a driver for the TI BQ25180. Implement enable/disable and current set/get. Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>pull/64476/head
6 changed files with 267 additions and 0 deletions
@ -0,0 +1,11 @@ |
|||||||
|
# Copyright 2024 Google LLC |
||||||
|
# |
||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
config CHARGER_BQ25180 |
||||||
|
bool "BQ25180 Battery Charger" |
||||||
|
default y |
||||||
|
depends on DT_HAS_TI_BQ25180_ENABLED |
||||||
|
select I2C |
||||||
|
help |
||||||
|
Enable BQ25180 battery charger driver. |
@ -0,0 +1,218 @@ |
|||||||
|
/*
|
||||||
|
* Copyright 2024 Google LLC |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
* |
||||||
|
* BQ25180 Datasheet: https://www.ti.com/lit/gpn/bq25180
|
||||||
|
*/ |
||||||
|
|
||||||
|
#define DT_DRV_COMPAT ti_bq25180 |
||||||
|
|
||||||
|
#include <zephyr/device.h> |
||||||
|
#include <zephyr/drivers/charger.h> |
||||||
|
#include <zephyr/drivers/i2c.h> |
||||||
|
#include <zephyr/kernel.h> |
||||||
|
#include <zephyr/logging/log.h> |
||||||
|
#include <zephyr/sys/util.h> |
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(bq25180, CONFIG_CHARGER_LOG_LEVEL); |
||||||
|
|
||||||
|
#define BQ25180_STAT0 0x00 |
||||||
|
#define BQ25180_STAT1 0x01 |
||||||
|
#define BQ25180_FLAG0 0x02 |
||||||
|
#define BQ25180_VBAT_CTRL 0x03 |
||||||
|
#define BQ25180_ICHG_CTRL 0x04 |
||||||
|
#define BQ25180_IC_CTRL 0x07 |
||||||
|
#define BQ25180_SHIP_RST 0x09 |
||||||
|
#define BQ25180_MASK_ID 0x0c |
||||||
|
|
||||||
|
#define BQ25180_ICHG_CHG_DIS BIT(7) |
||||||
|
#define BQ25180_ICHG_MSK GENMASK(6, 0) |
||||||
|
#define BQ25180_WATCHDOG_SEL_1_MSK GENMASK(1, 0) |
||||||
|
#define BQ25180_WATCHDOG_DISABLE 0x03 |
||||||
|
#define BQ25180_DEVICE_ID_MSK GENMASK(3, 0) |
||||||
|
#define BQ25180_DEVICE_ID 0x00 |
||||||
|
#define BQ25180_SHIP_RST_EN_RST_SHIP_MSK GENMASK(6, 5) |
||||||
|
#define BQ25180_SHIP_RST_EN_RST_SHIP_ADAPTER 0x20 |
||||||
|
#define BQ25180_SHIP_RST_EN_RST_SHIP_BUTTON 0x40 |
||||||
|
|
||||||
|
/* Charging current limits */ |
||||||
|
#define BQ25180_CURRENT_MIN_MA 5 |
||||||
|
#define BQ25180_CURRENT_MAX_MA 1000 |
||||||
|
|
||||||
|
struct bq25180_config { |
||||||
|
struct i2c_dt_spec i2c; |
||||||
|
uint32_t initial_current_microamp; |
||||||
|
}; |
||||||
|
|
||||||
|
/*
|
||||||
|
* For ICHG <= 35mA = ICHGCODE + 5mA |
||||||
|
* For ICHG > 35mA = 40 + ((ICHGCODE-31)*10)mA. |
||||||
|
* Maximum programmable current = 1000mA |
||||||
|
* |
||||||
|
* Return: value between 0 and 127, negative on error. |
||||||
|
*/ |
||||||
|
static int bq25180_ma_to_ichg(uint32_t current_ma, uint8_t *ichg) |
||||||
|
{ |
||||||
|
if (!IN_RANGE(current_ma, BQ25180_CURRENT_MIN_MA, BQ25180_CURRENT_MAX_MA)) { |
||||||
|
LOG_WRN("charging current out of range: %dmA, " |
||||||
|
"clamping to the nearest limit", current_ma); |
||||||
|
} |
||||||
|
current_ma = CLAMP(current_ma, BQ25180_CURRENT_MIN_MA, BQ25180_CURRENT_MAX_MA); |
||||||
|
|
||||||
|
if (current_ma <= 35) { |
||||||
|
*ichg = current_ma - 5; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
*ichg = (current_ma - 40) / 10 + 31; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static uint32_t bq25180_ichg_to_ma(uint8_t ichg) |
||||||
|
{ |
||||||
|
ichg &= BQ25180_ICHG_MSK; |
||||||
|
|
||||||
|
if (ichg <= 30) { |
||||||
|
return (ichg + 5); |
||||||
|
} |
||||||
|
|
||||||
|
return (ichg - 31) * 10 + 40; |
||||||
|
} |
||||||
|
|
||||||
|
static int bq25183_charge_enable(const struct device *dev, const bool enable) |
||||||
|
{ |
||||||
|
const struct bq25180_config *cfg = dev->config; |
||||||
|
uint8_t value = enable ? 0 : BQ25180_ICHG_CHG_DIS; |
||||||
|
int ret; |
||||||
|
|
||||||
|
ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_ICHG_CTRL, |
||||||
|
BQ25180_ICHG_CHG_DIS, value); |
||||||
|
if (ret < 0) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int bq25180_set_charge_current(const struct device *dev, |
||||||
|
uint32_t const_charge_current_ua) |
||||||
|
{ |
||||||
|
const struct bq25180_config *cfg = dev->config; |
||||||
|
uint8_t val; |
||||||
|
int ret; |
||||||
|
|
||||||
|
ret = bq25180_ma_to_ichg(const_charge_current_ua / 1000, &val); |
||||||
|
if (ret < 0) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_ICHG_CTRL, |
||||||
|
BQ25180_ICHG_MSK, val); |
||||||
|
if (ret < 0) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int bq25180_get_charge_current(const struct device *dev, |
||||||
|
uint32_t *const_charge_current_ua) |
||||||
|
{ |
||||||
|
const struct bq25180_config *cfg = dev->config; |
||||||
|
uint8_t val; |
||||||
|
int ret; |
||||||
|
|
||||||
|
ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_ICHG_CTRL, &val); |
||||||
|
if (ret < 0) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
*const_charge_current_ua = bq25180_ichg_to_ma(val) * 1000; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int bq25180_get_prop(const struct device *dev, charger_prop_t prop, |
||||||
|
union charger_propval *val) |
||||||
|
{ |
||||||
|
switch (prop) { |
||||||
|
case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA: |
||||||
|
return bq25180_get_charge_current(dev, &val->const_charge_current_ua); |
||||||
|
default: |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static int bq25180_set_prop(const struct device *dev, charger_prop_t prop, |
||||||
|
const union charger_propval *val) |
||||||
|
{ |
||||||
|
switch (prop) { |
||||||
|
case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA: |
||||||
|
return bq25180_set_charge_current(dev, val->const_charge_current_ua); |
||||||
|
default: |
||||||
|
return -ENOTSUP; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static const struct charger_driver_api bq25180_api = { |
||||||
|
.get_property = bq25180_get_prop, |
||||||
|
.set_property = bq25180_set_prop, |
||||||
|
.charge_enable = bq25183_charge_enable, |
||||||
|
}; |
||||||
|
|
||||||
|
static int bq25180_init(const struct device *dev) |
||||||
|
{ |
||||||
|
const struct bq25180_config *cfg = dev->config; |
||||||
|
uint8_t val; |
||||||
|
int ret; |
||||||
|
|
||||||
|
ret = i2c_reg_read_byte_dt(&cfg->i2c, BQ25180_MASK_ID, &val); |
||||||
|
if (ret < 0) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
val &= BQ25180_DEVICE_ID_MSK; |
||||||
|
if (val != BQ25180_DEVICE_ID) { |
||||||
|
LOG_ERR("Invalid device id: %02x", val); |
||||||
|
return -EINVAL; |
||||||
|
} |
||||||
|
|
||||||
|
/* Disable the watchdog */ |
||||||
|
ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_IC_CTRL, |
||||||
|
BQ25180_WATCHDOG_SEL_1_MSK, |
||||||
|
BQ25180_WATCHDOG_DISABLE); |
||||||
|
if (ret < 0) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
if (cfg->initial_current_microamp > 0) { |
||||||
|
ret = bq25180_ma_to_ichg(cfg->initial_current_microamp / 1000, &val); |
||||||
|
if (ret < 0) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
ret = i2c_reg_update_byte_dt(&cfg->i2c, BQ25180_ICHG_CTRL, |
||||||
|
BQ25180_ICHG_MSK, val); |
||||||
|
if (ret < 0) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
#define CHARGER_BQ25180_INIT(inst) \ |
||||||
|
static const struct bq25180_config bq25180_config_##inst = { \ |
||||||
|
.i2c = I2C_DT_SPEC_INST_GET(inst), \ |
||||||
|
.initial_current_microamp = DT_INST_PROP( \ |
||||||
|
inst, constant_charge_current_max_microamp), \ |
||||||
|
}; \ |
||||||
|
\ |
||||||
|
DEVICE_DT_INST_DEFINE(inst, bq25180_init, NULL, NULL, \ |
||||||
|
&bq25180_config_##inst, POST_KERNEL, \ |
||||||
|
CONFIG_CHARGER_INIT_PRIORITY, \ |
||||||
|
&bq25180_api); |
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(CHARGER_BQ25180_INIT) |
@ -0,0 +1,30 @@ |
|||||||
|
# Copyright 2024 Google LLC |
||||||
|
# SPDX-License-Identifier: Apache-2.0 |
||||||
|
|
||||||
|
description: | |
||||||
|
BQ25180 I2C Controlled, 1-Cell, 1-A Linear Battery Charger with Power Path |
||||||
|
and Ship Mode. |
||||||
|
|
||||||
|
The device has a single child node for the charger. For example: |
||||||
|
|
||||||
|
bq25180@6a { |
||||||
|
compatible = "ti,bq25180"; |
||||||
|
reg = <0x6a>; |
||||||
|
|
||||||
|
constant-charge-current-max-microamp = <500000>; |
||||||
|
}; |
||||||
|
|
||||||
|
compatible: "ti,bq25180" |
||||||
|
|
||||||
|
include: [battery.yaml, i2c-device.yaml] |
||||||
|
|
||||||
|
|
||||||
|
properties: |
||||||
|
constant-charge-current-max-microamp: |
||||||
|
type: int |
||||||
|
default: 0 |
||||||
|
description: | |
||||||
|
Charge current set at init time in uA, available range is 5 mA to 800 mA. |
||||||
|
The value specified will be rounded down to the closest implemented |
||||||
|
value. If set to 0 (default) skip setting the charge current value at |
||||||
|
driver initialization. |
Loading…
Reference in new issue