From 273721efc0b1d7fbd1e886491b2766f79cdf481e Mon Sep 17 00:00:00 2001 From: Jean Pierre Dudey Date: Wed, 25 Nov 2020 11:32:36 +0100 Subject: [PATCH] drivers: add bq2429x power management IC driver Signed-off-by: Jean Pierre Dudey --- CODEOWNERS | 2 + drivers/Kconfig | 1 + drivers/Makefile.dep | 4 + drivers/bq2429x/Kconfig | 38 ++ drivers/bq2429x/Makefile | 1 + drivers/bq2429x/Makefile.dep | 5 + drivers/bq2429x/Makefile.include | 2 + drivers/bq2429x/bq2429x.c | 338 ++++++++++++++ drivers/bq2429x/include/bq2429x_internal.h | 108 +++++ drivers/bq2429x/include/bq2429x_params.h | 103 +++++ drivers/include/bq2429x.h | 503 +++++++++++++++++++++ makefiles/pseudomodules.inc.mk | 1 + tests/driver_bq2429x/Makefile | 10 + tests/driver_bq2429x/Makefile.ci | 6 + tests/driver_bq2429x/README.md | 11 + tests/driver_bq2429x/_util.h | 212 +++++++++ tests/driver_bq2429x/app.config.test | 4 + tests/driver_bq2429x/main.c | 404 +++++++++++++++++ tests/driver_bq2429x/tests/01-run.py | 38 ++ 19 files changed, 1791 insertions(+) create mode 100644 drivers/bq2429x/Kconfig create mode 100644 drivers/bq2429x/Makefile create mode 100644 drivers/bq2429x/Makefile.dep create mode 100644 drivers/bq2429x/Makefile.include create mode 100644 drivers/bq2429x/bq2429x.c create mode 100644 drivers/bq2429x/include/bq2429x_internal.h create mode 100644 drivers/bq2429x/include/bq2429x_params.h create mode 100644 drivers/include/bq2429x.h create mode 100644 tests/driver_bq2429x/Makefile create mode 100644 tests/driver_bq2429x/Makefile.ci create mode 100644 tests/driver_bq2429x/README.md create mode 100644 tests/driver_bq2429x/_util.h create mode 100644 tests/driver_bq2429x/app.config.test create mode 100644 tests/driver_bq2429x/main.c create mode 100755 tests/driver_bq2429x/tests/01-run.py diff --git a/CODEOWNERS b/CODEOWNERS index 04202273b39c..04d3a87c6c43 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -71,6 +71,7 @@ /drivers/at86rf215/ @benpicco /drivers/at86rf2xx/ @daniel-k @Hyungsin @jia200x @smlng @miri64 /drivers/bh1900nux/ @wosym +/drivers/bq2429x/ @jeandudey /drivers/cc110x/ @maribu /drivers/cc1xxx_common/ @maribu /drivers/ccs811/ @gschorcht @@ -132,6 +133,7 @@ /tests/ @smlng @leandrolanzieri @aabadie @MichelRottleuthner @fjmolinas /tests/candev/ @wosym +/tests/driver_bq2429x/ @jeandudey /tests/gnrc* @miri64 /tests/lwip* @miri64 /tests/slip/ @miri64 diff --git a/drivers/Kconfig b/drivers/Kconfig index 8027e6eef425..c9fba8bd03f6 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -33,6 +33,7 @@ endmenu # Display Device Drivers menu "Miscellaneous Device Drivers" rsource "at/Kconfig" rsource "at24mac/Kconfig" +rsource "bq2429x/Kconfig" rsource "ds1307/Kconfig" rsource "ds3231/Kconfig" rsource "ds3234/Kconfig" diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 37b89bc6d00e..b0d4c66af1b8 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -36,6 +36,10 @@ ifneq (,$(filter bm%280_spi,$(USEMODULE))) USEMODULE += bmx280 endif +ifneq (,$(filter bq2429x_int,$(USEMODULE))) + USEMODULE += bq2429x +endif + ifneq (,$(filter cc110%,$(USEMODULE))) USEMODULE += cc110x endif diff --git a/drivers/bq2429x/Kconfig b/drivers/bq2429x/Kconfig new file mode 100644 index 000000000000..c56b2ddead38 --- /dev/null +++ b/drivers/bq2429x/Kconfig @@ -0,0 +1,38 @@ +# Copyright (C) 2020 Locha Inc +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +menuconfig MODULE_BQ2429X + bool "BQ2429x single-cell USB charger" + depends on HAS_PERIPH_I2C + depends on HAS_PERIPH_GPIO + depends on TEST_KCONFIG + select MODULE_PERIPH_I2C + select MODULE_PERIPH_GPIO + default y if HAVE_BQ2429X + help + The driver can be used with Texas Instruments USB charger ICs: BQ24295, + BQ24296, BQ24297, BQ24298, BQ24292I, BQ24296M. + +config MODULE_BQ2429X_INT + bool "Interrupt handling" + depends on HAS_PERIPH_GPIO_IRQ + depends on MODULE_BQ2429X + select MODULE_PERIPH_GPIO_IRQ + default y if HAVE_BQ2429X_INT + help + Say y to include interrupt handling functionality. + +config HAVE_BQ2429X + bool + help + Indicates that a BQ2429x charger and power management IC is present. + +config HAVE_BQ2429X_INT + bool + help + Indicates that a BQ2429x charger and power management IC is present, + with interrupt functionality (the INT pin is connected). diff --git a/drivers/bq2429x/Makefile b/drivers/bq2429x/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/drivers/bq2429x/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/bq2429x/Makefile.dep b/drivers/bq2429x/Makefile.dep new file mode 100644 index 000000000000..5b8db4e90a07 --- /dev/null +++ b/drivers/bq2429x/Makefile.dep @@ -0,0 +1,5 @@ +FEATURES_REQUIRED += periph_i2c +FEATURES_REQUIRED += periph_gpio +ifneq (,$(filter $(USEMODULE),bq2429x_int)) + FEATURES_REQUIRED += periph_gpio_irq +endif diff --git a/drivers/bq2429x/Makefile.include b/drivers/bq2429x/Makefile.include new file mode 100644 index 000000000000..75e48f3c7bf5 --- /dev/null +++ b/drivers/bq2429x/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE_INCLUDES_bq2429x := $(LAST_MAKEFILEDIR)/include +USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_bq2429x) diff --git a/drivers/bq2429x/bq2429x.c b/drivers/bq2429x/bq2429x.c new file mode 100644 index 000000000000..88c3c5c890e4 --- /dev/null +++ b/drivers/bq2429x/bq2429x.c @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2020 Locha Inc + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_bq2429x + * @{ + * + * @file + * @brief Device driver implementation for BQ2429x power ICs + * + * @author Jean Pierre Dudey + * + * @} + */ + +#include "bq2429x.h" +#include "bq2429x_internal.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#define DEBUG_DEV(f, d, ...) \ + DEBUG("[bq2429x] %s i2c dev=%d : " f "\n", \ + __func__, d->params.i2c, ## __VA_ARGS__); + +#define EXEC_RET(func) \ + do { \ + int _ret; \ + if ((_ret = (func)) != BQ2429X_OK) { \ + return _ret; \ + } \ + } while (0) + +static inline int _read_reg(const bq2429x_t *dev, uint8_t reg, uint8_t *value) +{ + int ret; + i2c_acquire(dev->params.i2c); + if ((ret = i2c_read_reg(dev->params.i2c, BQ2429X_I2C_ADDR, reg, value, + 0)) < 0) { + DEBUG_DEV("couldn't read reg=%02x ret=%d", dev, (unsigned)reg, ret); + i2c_release(dev->params.i2c); + return BQ2429X_ERR_I2C; + } + i2c_release(dev->params.i2c); + + return BQ2429X_OK; +} + +static inline int _write_reg(const bq2429x_t *dev, uint8_t reg, uint8_t value) +{ + int ret; + i2c_acquire(dev->params.i2c); + if ((ret = i2c_write_reg(dev->params.i2c, BQ2429X_I2C_ADDR, reg, value, + 0)) < 0) { + DEBUG_DEV("couldn't write reg=%02x: ret=%d", dev, (unsigned)reg, ret); + i2c_release(dev->params.i2c); + return BQ2429X_ERR_I2C; + } + i2c_release(dev->params.i2c); + + return BQ2429X_OK; +} + +static int _update_bits(const bq2429x_t *dev, uint8_t reg, uint8_t mask, + uint8_t value) +{ + uint8_t tmp; + + EXEC_RET(_read_reg(dev, reg, &tmp)); + + tmp &= ~mask; + tmp |= value & mask; + + return _write_reg(dev, reg, tmp); +} + +static int _update_charge_params(const bq2429x_t *dev) +{ + assert(dev); + + DEBUG_DEV("", dev); + EXEC_RET(_update_bits(dev, BQ2429X_REG00, BQ2429X_REG00_VINDPM_m, + dev->params.vlim << BQ2429X_REG00_VINDPM_s)); + EXEC_RET(_update_bits(dev, BQ2429X_REG00, BQ2429X_REG00_IINLIM_m, + dev->params.ilim << BQ2429X_REG00_IINLIM_s)); + EXEC_RET(_update_bits(dev, BQ2429X_REG02, BQ2429X_REG02_ICHG_m, + dev->params.ichg << BQ2429X_REG02_ICHG_s)); + EXEC_RET(_update_bits(dev, BQ2429X_REG04, BQ2429X_REG04_VREG_m, + dev->params.vreg << BQ2429X_REG04_VREG_s)); + return BQ2429X_OK; +} + +int bq2429x_init(bq2429x_t *dev, const bq2429x_params_t *params) +{ + assert(dev && params); + + DEBUG_DEV("", dev); + dev->params = *params; + + if (!gpio_is_equal(dev->params.ce_pin, GPIO_UNDEF)) { + if (gpio_init(dev->params.ce_pin, GPIO_OUT) < 0) { + DEBUG_DEV("couldn't initialize CE pin", dev); + return BQ2429X_ERR_GPIO; + } + /* enable charge by default */ + gpio_clear(dev->params.ce_pin); + } + if (!gpio_is_equal(dev->params.otg_pin, GPIO_UNDEF)) { + if (gpio_init(dev->params.otg_pin, GPIO_OUT) < 0) { + DEBUG_DEV("couldn't initialize OTG pin", dev); + return BQ2429X_ERR_GPIO; + } + /* disable OTG by default */ + gpio_clear(dev->params.otg_pin); + } + + /* update parameters on the device */ + EXEC_RET(_update_charge_params(dev)); + + return BQ2429X_OK; +} + +#if IS_USED(MODULE_BQ2429X_INT) +int bq2429x_init_int(bq2429x_t *dev, bq2429x_int_cb_t cb, void *arg) +{ + assert(dev && !gpio_is_equal(dev->params.int_pin, GPIO_UNDEF) && cb); + DEBUG_DEV("cb=%p arg=%p", dev, cb, arg); + + /* the device always emits an active-low 256-us pulse */ + if (gpio_init_int(dev->params.int_pin, GPIO_IN, GPIO_FALLING, cb, + arg) < 0) { + return BQ2429X_ERR_GPIO; + } + + return BQ2429X_OK; +} +#endif + +int bq2429x_get_status(const bq2429x_t *dev, bq2429x_status_t *status) +{ + assert(dev && status); + + DEBUG_DEV("", dev); + + uint8_t val; + EXEC_RET(_read_reg(dev, BQ2429X_REG08, &val)); + + status->vbus = (val & BQ2429X_REG08_VBUS_STATUS_m) + >> BQ2429X_REG08_VBUS_STATUS_s; + status->chrg = (val & BQ2429X_REG08_CHG_STAT_m) + >> BQ2429X_REG08_CHG_STAT_s; + status->dpm = (val & BQ2429X_REG08_DPM_STAT_m) + >> BQ2429X_REG08_DPM_STAT_s; + status->pg = (val & BQ2429X_REG08_PG_STAT_m) + >> BQ2429X_REG08_PG_STAT_s; + status->therm = (val & BQ2429X_REG08_THERM_STAT_m) + >> BQ2429X_REG08_THERM_STAT_s; + status->vsys = (val & BQ2429X_REG08_VSYS_STAT_m) + >> BQ2429X_REG08_VSYS_STAT_s; + + return BQ2429X_OK; +} + +int bq2429x_get_fault(const bq2429x_t *dev, bq2429x_fault_t *fault) +{ + assert(dev && fault); + + DEBUG_DEV("", dev); + + uint8_t val; + EXEC_RET(_read_reg(dev, BQ2429X_REG09, &val)); + + fault->watchdog = (val & BQ2429X_REG09_WD_FAULT_m) + >> BQ2429X_REG09_WD_FAULT_s; + fault->otg = (val & BQ2429X_REG09_OTG_FAULT_m) + >> BQ2429X_REG09_OTG_FAULT_s; + fault->chrg = (val & BQ2429X_REG09_CHRG_FAULT_m) + >> BQ2429X_REG09_CHRG_FAULT_s; + fault->bat = (val & BQ2429X_REG09_BAT_FAULT_m) + >> BQ2429X_REG09_BAT_FAULT_s; + fault->ntc_fault_1 = (val & BQ2429X_REG09_NTC_FAULT_1_m) + >> BQ2429X_REG09_NTC_FAULT_1_s; + fault->ntc_fault_0 = (val & BQ2429X_REG09_NTC_FAULT_0_m) + >> BQ2429X_REG09_NTC_FAULT_0_s; + + return BQ2429X_OK; +} + +int bq2429x_enable_otg(const bq2429x_t *dev) +{ + assert(dev); + + DEBUG_DEV("", dev); + if (!gpio_is_equal(dev->params.otg_pin, GPIO_UNDEF)) { + gpio_set(dev->params.otg_pin); + } + uint8_t val = BQ2429X_REG01_OTG_ENABLE << BQ2429X_REG01_OTG_s; + return _update_bits(dev, BQ2429X_REG01, BQ2429X_REG01_OTG_m, val); +} + +int bq2429x_disable_otg(const bq2429x_t *dev) +{ + assert(dev); + + DEBUG_DEV("", dev); + if (!gpio_is_equal(dev->params.otg_pin, GPIO_UNDEF)) { + gpio_clear(dev->params.otg_pin); + } + uint8_t val = BQ2429X_REG01_OTG_DISABLE << BQ2429X_REG01_OTG_s; + return _update_bits(dev, BQ2429X_REG01, BQ2429X_REG01_OTG_m, val); +} + +int bq2429x_enable_charge(const bq2429x_t *dev) +{ + assert(dev); + + DEBUG_DEV("", dev); + if (!gpio_is_equal(dev->params.ce_pin, GPIO_UNDEF)) { + gpio_clear(dev->params.ce_pin); + } + uint8_t val = BQ2429X_REG01_CHG_ENABLE << BQ2429X_REG01_CHG_s; + return _update_bits(dev, BQ2429X_REG01, BQ2429X_REG01_CHG_m, val); +} + +int bq2429x_disable_charge(const bq2429x_t *dev) +{ + assert(dev); + + DEBUG_DEV("", dev); + if (!gpio_is_equal(dev->params.ce_pin, GPIO_UNDEF)) { + /* set CE pin to high to disable charge */ + gpio_set(dev->params.ce_pin); + } + uint8_t val = BQ2429X_REG01_CHG_DISABLE << BQ2429X_REG01_CHG_s; + return _update_bits(dev, BQ2429X_REG01, BQ2429X_REG01_CHG_m, val); +} + +int bq2429x_set_vlim(bq2429x_t *dev, bq2429x_input_voltage_limit_t vlim) +{ + assert(dev); + + DEBUG_DEV("vlim=%u", dev, (unsigned)vlim); + EXEC_RET(_update_bits(dev, BQ2429X_REG00, BQ2429X_REG00_VINDPM_m, + vlim << BQ2429X_REG00_VINDPM_s)); + dev->params.vlim = vlim; + + return BQ2429X_OK; +} + +int bq2429x_get_vlim(const bq2429x_t *dev, bq2429x_input_voltage_limit_t *vlim) +{ + assert(dev && vlim); + + DEBUG_DEV("", dev); + + uint8_t val; + EXEC_RET(_read_reg(dev, BQ2429X_REG00, &val)); + *vlim = (val & BQ2429X_REG00_VINDPM_m) >> BQ2429X_REG00_VINDPM_s; + + return BQ2429X_OK; +} + +int bq2429x_set_ilim(bq2429x_t *dev, bq2429x_input_current_limit_t ilim) +{ + assert(dev); + + DEBUG_DEV("ilim=%u", dev, (unsigned)ilim); + EXEC_RET(_update_bits(dev, BQ2429X_REG00, BQ2429X_REG00_IINLIM_m, + ilim << BQ2429X_REG00_IINLIM_s)); + dev->params.ilim = ilim; + return BQ2429X_OK; +} + +int bq2429x_get_ilim(const bq2429x_t *dev, bq2429x_input_current_limit_t *ilim) +{ + assert(dev && ilim); + + DEBUG_DEV("", dev); + + uint8_t val; + EXEC_RET(_read_reg(dev, BQ2429X_REG00, &val)); + *ilim = (val & BQ2429X_REG00_IINLIM_m) >> BQ2429X_REG00_IINLIM_s; + + return BQ2429X_OK; +} + +int bq2429x_set_ichg(bq2429x_t *dev, bq2429x_charge_current_t ichg) +{ + assert(dev); + + DEBUG_DEV("ichg=%u", dev, (unsigned)ichg); + EXEC_RET(_update_bits(dev, BQ2429X_REG02, BQ2429X_REG02_ICHG_m, + ichg << BQ2429X_REG02_ICHG_s)); + dev->params.ichg = ichg; + return BQ2429X_OK; +} + +int bq2429x_get_ichg(const bq2429x_t *dev, bq2429x_charge_current_t *ichg) +{ + assert(dev && ichg); + + DEBUG_DEV("", dev); + + uint8_t val; + EXEC_RET(_read_reg(dev, BQ2429X_REG02, &val)); + *ichg = (val & BQ2429X_REG02_ICHG_m) >> BQ2429X_REG02_ICHG_s; + + return BQ2429X_OK; +} + +int bq2429x_set_vreg(bq2429x_t *dev, bq2429x_charge_voltage_limit_t vreg) +{ + assert(dev); + + DEBUG_DEV("vreg=%u", dev, (unsigned)vreg); + EXEC_RET(_update_bits(dev, BQ2429X_REG04, BQ2429X_REG04_VREG_m, + vreg << BQ2429X_REG04_VREG_s)); + dev->params.vreg = vreg; + return BQ2429X_OK; +} + +int bq2429x_get_vreg(const bq2429x_t *dev, bq2429x_charge_voltage_limit_t *vreg) +{ + assert(dev && vreg); + + DEBUG_DEV("", dev); + + uint8_t val; + EXEC_RET(_read_reg(dev, BQ2429X_REG04, &val)); + *vreg = (val & BQ2429X_REG04_VREG_m) >> BQ2429X_REG04_VREG_s; + + return BQ2429X_OK; +} diff --git a/drivers/bq2429x/include/bq2429x_internal.h b/drivers/bq2429x/include/bq2429x_internal.h new file mode 100644 index 000000000000..df3a947185bc --- /dev/null +++ b/drivers/bq2429x/include/bq2429x_internal.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2020 Locha Inc + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_bq2429x + * + * @{ + * @file + * @brief Internal address, registers, constatns for the BQ2429x family + * power ICs. + * + * @author Jean Pierre Dudey + */ + +#ifndef BQ2429X_INTERNAL_H +#define BQ2429X_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BQ2429x I2C device slave address + */ +#define BQ2429X_I2C_ADDR (0x6B) + +/** + * @brief Registers + * @{ + */ +#define BQ2429X_REG00 (0x00) /**< Input Source Control */ +#define BQ2429X_REG01 (0x01) /**< Power-On Configuration */ +#define BQ2429X_REG02 (0x02) /**< Charge Current Control */ +#define BQ2429X_REG04 (0x04) /**< Charge Voltage Control */ +#define BQ2429X_REG05 (0x05) /**< Charge Termination/Timer + Control */ +#define BQ2429X_REG08 (0x08) /**< System Status */ +#define BQ2429X_REG09 (0x09) /**< New Fault Register */ +#define BQ2429X_REG0A (0x0A) /**< Vendor / Part / Revision + Status */ +/** @} */ + +/** + * @brief Register values + * @{ + */ +#define BQ2429X_REG00_VINDPM_m (0x78) +#define BQ2429X_REG00_VINDPM_s (3) +#define BQ2429X_REG00_IINLIM_m (0x07) +#define BQ2429X_REG00_IINLIM_s (0) + +#define BQ2429X_REG01_OTG_m (0x20) +#define BQ2429X_REG01_OTG_s (5) +#define BQ2429X_REG01_OTG_ENABLE (1) +#define BQ2429X_REG01_OTG_DISABLE (0) +#define BQ2429X_REG01_CHG_m (0x10) +#define BQ2429X_REG01_CHG_s (4) +#define BQ2429X_REG01_CHG_ENABLE (1) +#define BQ2429X_REG01_CHG_DISABLE (0) + +#define BQ2429X_REG02_ICHG_m (0xFC) +#define BQ2429X_REG02_ICHG_s (2) + +#define BQ2429X_REG04_VREG_m (0xFC) +#define BQ2429X_REG04_VREG_s (2) + +#define BQ2429X_REG05_WD_m (0x30) +#define BQ2429X_REG05_WD_s (4) +#define BQ2429X_REG05_WD_DISABLE (0x00) + +#define BQ2429X_REG08_VBUS_STATUS_m (0xC0) +#define BQ2429X_REG08_VBUS_STATUS_s (6) +#define BQ2429X_REG08_CHG_STAT_m (0x30) +#define BQ2429X_REG08_CHG_STAT_s (4) +#define BQ2429X_REG08_DPM_STAT_m (0x08) +#define BQ2429X_REG08_DPM_STAT_s (3) +#define BQ2429X_REG08_PG_STAT_m (0x04) +#define BQ2429X_REG08_PG_STAT_s (2) +#define BQ2429X_REG08_THERM_STAT_m (0x02) +#define BQ2429X_REG08_THERM_STAT_s (1) +#define BQ2429X_REG08_VSYS_STAT_m (0x01) +#define BQ2429X_REG08_VSYS_STAT_s (0) + +#define BQ2429X_REG09_WD_FAULT_m (0x80) +#define BQ2429X_REG09_WD_FAULT_s (7) +#define BQ2429X_REG09_OTG_FAULT_m (0x40) +#define BQ2429X_REG09_OTG_FAULT_s (6) +#define BQ2429X_REG09_CHRG_FAULT_m (0x30) +#define BQ2429X_REG09_CHRG_FAULT_s (4) +#define BQ2429X_REG09_BAT_FAULT_m (0x08) +#define BQ2429X_REG09_BAT_FAULT_s (3) +#define BQ2429X_REG09_NTC_FAULT_1_m (0x02) +#define BQ2429X_REG09_NTC_FAULT_1_s (1) +#define BQ2429X_REG09_NTC_FAULT_0_m (0x01) +#define BQ2429X_REG09_NTC_FAULT_0_s (0) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* BQ2429X_INTERNAL_H */ +/** @} */ diff --git a/drivers/bq2429x/include/bq2429x_params.h b/drivers/bq2429x/include/bq2429x_params.h new file mode 100644 index 000000000000..96019ab677a5 --- /dev/null +++ b/drivers/bq2429x/include/bq2429x_params.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 Locha Inc + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_bq2429x + * + * @{ + * @file + * @brief Default configuration for BQ2429x power ICs. + * + * @author Jean Pierre Dudey + */ + +#ifndef BQ2429X_PARAMS_H +#define BQ2429X_PARAMS_H + +#include "board.h" +#include "bq2429x.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* I2C configuration */ +#ifndef BQ2429X_PARAM_I2C +#define BQ2429X_PARAM_I2C I2C_DEV(0) +#endif + +/* Interrupt pin configuration */ +#ifndef BQ2429X_PARAM_INT_PIN +#define BQ2429X_PARAM_INT_PIN GPIO_UNDEF +#endif + +/* Control pins */ +#ifndef BQ2429X_PARAM_CE_PIN +#define BQ2429X_PARMA_CE_PIN GPIO_UNDEF +#endif +#ifndef BQ2429X_PARAM_OTG_PIN +#define BQ2429X_PARMA_OTG_PIN GPIO_UNDEF +#endif + +/* Input current/voltage */ +#ifndef BQ2429X_PARAM_VLIM +#define BQ2429X_PARAM_VLIM BQ2429X_VLIM_4360 +#endif +#ifndef BQ2429X_PARAM_ILIM +#define BQ2429X_PARAM_ILIM BQ2429X_ILIM_500 +#endif + +/* Battery charge current/voltage */ +#ifndef BQ2429X_PARAM_ICHG +#define BQ2429X_PARAM_ICHG BQ2429X_ICHG_512 +#endif +#ifndef BQ2429X_PARAM_VREG +#define BQ2429X_PARAM_VREG BQ2429X_VREG_4208 +#endif + +#if !IS_USED(MODULE_BQ2429X_INT) + +#ifndef BQ2429X_PARAMS +#define BQ2429X_PARAMS { \ + .i2c = BQ2429X_PARAM_I2C, \ + .ce_pin = BQ2429X_PARMA_CE_PIN, \ + .otg_pin = BQ2429X_PARMA_OTG_PIN, \ + .vlim = BQ2429X_PARAM_VLIM, \ + .ilim = BQ2429X_PARAM_ILIM, \ + .ichg = BQ2429X_PARAM_ICHG, \ + .vreg = BQ2429X_PARAM_VREG, \ + } +#endif + +#else /* !IS_USED(MODULE_BQ2429X_INT) */ + +#ifndef BQ2429X_PARAMS +#define BQ2429X_PARAMS { \ + .i2c = BQ2429X_PARAM_I2C, \ + .int_pin = BQ2429X_PARAM_INT_PIN, \ + .ce_pin = BQ2429X_PARMA_CE_PIN, \ + .otg_pin = BQ2429X_PARMA_OTG_PIN, \ + .vlim = BQ2429X_PARAM_VLIM, \ + .ilim = BQ2429X_PARAM_ILIM, \ + .ichg = BQ2429X_PARAM_ICHG, \ + .vreg = BQ2429X_PARAM_VREG, \ + } +#endif + +#endif /* !IS_USED(MODULE_BQ2429X_INT) */ + +static bq2429x_params_t bq2429x_params[] = { + BQ2429X_PARAMS, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* BQ2429X_PARAMS_H */ +/** @} */ diff --git a/drivers/include/bq2429x.h b/drivers/include/bq2429x.h new file mode 100644 index 000000000000..dab2bf9d7873 --- /dev/null +++ b/drivers/include/bq2429x.h @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2020 Locha Inc + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_bq2429x BQ2429x + * @ingroup drivers_power + * @brief Device driver interface for the BQ2429x Single-Cell USB Charger + * + * BQ2429x series power management ICs by Texas Instruments are controllable by + * I2C, allowing to change charge parameters in the runtime. + * + * For more information, see the datasheets: + * * [BQ24295](https://www.ti.com/lit/ds/symlink/bq24295.pdf) + * * [BQ24296](https://www.ti.com/lit/ds/symlink/bq24296.pdf) + * * [BQ24297](https://www.ti.com/lit/ds/symlink/bq24297.pdf) + * * [BQ24298](https://www.ti.com/lit/ds/symlink/bq24298.pdf) + * * [BQ24292I](https://www.ti.com/lit/ds/symlink/bq24292i.pdf) + * * [BQ24296M](https://www.ti.com/lit/ds/symlink/bq24296m.pdf) + * + * It can be used for USB OTG to power other devices, see the + * @ref bq2429x_enable_otg and @ref bq2429x_disable_otg functions, + * an additional pin @ref bq2429x_params_t::otg_pin can be set to + * control it also by hardware and these functions will take care of + * setting it. + * + * To enable/disable charge the functions @ref bq2429x_enable_charge + * and @ref bq2429x_disable_charge can be used, and as with the OTG + * an additional pin @ref bq2429x_params_t::ce_pin can be set to + * control it also by hardware and these functions will take care of + * setting it. + * + * When a change happens on the FAULT registers of the device the + * BQ2429x device generates an interrupt to inform when that happens, this + * functionality has to be enabled with the `bq2429x_int` module and can be used + * with the @ref bq2429x_init_int functions. + * + * @{ + * + * @file + * @brief Device driver interface for the BQ2429x Single-Cell USB Charger + * + * @author Jean Pierre Dudey + */ + +#ifndef BQ2429X_H +#define BQ2429X_H + +#include +#include + +#include "kernel_defines.h" +#include "periph/i2c.h" +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Driver return values + */ +enum { + BQ2429X_OK = 0, /**< Everything is ok */ + BQ2429X_ERR_I2C = -1, /**< I2C error */ + BQ2429X_ERR_GPIO = -2, /**< GPIO initialization error */ +}; + +/** + * @brief Used VBUS type. + */ +typedef enum { + BQ2429X_VBUS_NONE = 0, /**< No input source detected */ + BQ2429X_VBUS_USB_CHARGER, /**< USB charger input */ + BQ2429X_VBUS_AP_CHARGER, /**< Adapter port charge input */ + BQ2429X_VBUS_OTG, /**< VBUS is used for OTG */ +} bq2429x_vbus_stat_t; + +/** + * @brief Battery charging status + */ +typedef enum { + BQ2429X_CHARGE_NONE = 0, /**< The device is not charging a + battery */ + BQ2429X_CHARGE_PRE, /**< Pre-charge state */ + BQ2429X_CHARGE_FAST, /**< Fast charging */ + BQ2429X_CHARGE_DONE, /**< Charging done */ +} bq2429x_chrg_stat_t; + +/** + * @brief Device status + */ +typedef struct { + bq2429x_vbus_stat_t vbus; /**< VBUS status */ + bq2429x_chrg_stat_t chrg; /**< Charge status */ + /** + * @brief DPM status + * @details false = Not in DPM. + * true = VINDPM or IINDMP. + */ + bool dpm; + /** + * @brief Power good status + * @details false = Not Power Good + * true = Power Good + */ + bool pg; + /** + * @brief Thermal regulation + * @details false = Normal + * true = In thermal regulation + */ + bool therm; + /** + * @brief VSYSMIN regulation + * @details false = Not in VSYSMIN regulation (BAT voltage > VSYSMIN) + * true = In VSYSMIN regulation (BAT voltage < VSYSMIN) + */ + bool vsys; +} bq2429x_status_t; + +/** + * @brief Charge fault values + */ +typedef enum { + BQ2429x_CHRG_FAULT_NORMAL = 0, /**< No fault, normal */ + BQ2429x_CHRG_FAULT_INPUT, /**< Input fault (OVP or bad + source) */ + BQ2429x_CHRG_FAULT_THERMAL_SHUTDOWN, /**< Thermal shutdown */ + BQ2429x_CHRG_FAULT_CHARGE_TIMER_EXPIRATION, /**< Charge timer expiration */ +} bq2429x_chrg_fault_t; + +/** + * @brief Device faults + */ +typedef struct { + /** + * @brief Watchdog fault. + * @details false = Normal. + * true = Watchdog timer expiration. + */ + bool watchdog; + /** + * @brief OTG fault. + * @details false = Normal. + * true = VBUS overloaded in OTG, or VBUS OVP, or battery is too + * low. + */ + bool otg; + /** + * @brief Charge fault. + */ + bq2429x_chrg_fault_t chrg; + /** + * @brief Battery fault. + */ + bool bat; + /** + * @brief NTC fault (1). + * @details false = Normal. + * true = Hot note. + */ + bool ntc_fault_1; + /** + * @brief NTC fault (0). + * @details false = Normal. + * true = Cold note. + */ + bool ntc_fault_0; +} bq2429x_fault_t; + +/** + * @brief Input Voltage Limit (VLIM). + */ +typedef enum { + BQ2429X_VLIM_3880 = 0, /**< 3880 mV */ + BQ2429X_VLIM_3960, /**< 3960 mV */ + BQ2429X_VLIM_4040, /**< 4040 mV */ + BQ2429X_VLIM_4120, /**< 4120 mV */ + BQ2429X_VLIM_4200, /**< 4200 mV */ + BQ2429X_VLIM_4280, /**< 4280 mV */ + BQ2429X_VLIM_4360, /**< 4360 mV */ + BQ2429X_VLIM_4440, /**< 4440 mV */ + BQ2429X_VLIM_4520, /**< 4520 mV */ + BQ2429X_VLIM_4600, /**< 4600 mV */ + BQ2429X_VLIM_4680, /**< 4680 mV */ + BQ2429X_VLIM_4760, /**< 4760 mV */ + BQ2429X_VLIM_4840, /**< 4840 mV */ + BQ2429X_VLIM_4920, /**< 4920 mV */ + BQ2429X_VLIM_5000, /**< 5000 mV */ + BQ2429X_VLIM_5080, /**< 5080 mV */ +} bq2429x_input_voltage_limit_t; + +/** + * @brief Input Current Limit (ILIM). + */ +typedef enum { + BQ2429X_ILIM_100 = 0, /**< 100 mA */ + BQ2429X_ILIM_150, /**< 150 mA */ + BQ2429X_ILIM_500, /**< 500 mA */ + BQ2429X_ILIM_900, /**< 900 mA */ + BQ2429X_ILIM_1000, /**< 1000 mA */ + BQ2429X_ILIM_1500, /**< 1500 mA */ + BQ2429X_ILIM_2000, /**< 2000 mA */ + BQ2429X_ILIM_3000, /**< 3000 mA */ +} bq2429x_input_current_limit_t; + +/** + * @brief Charge Current (ICHG) + */ +typedef enum { + BQ2429X_ICHG_512 = 0, /**< 512 mA */ + BQ2429X_ICHG_1024 = 8, /**< 1024 mA */ + BQ2429X_ICHG_2048 = 24, /**< 2048 mA */ + BQ2429X_ICHG_3008 = 39, /**< 4032 mA */ + BQ2429X_ICHG_4032 = 55, /**< 4544 mA */ + BQ2429X_ICHG_4544 = 63, +} bq2429x_charge_current_t; + +/** + * @brief Charge Voltage Limit (VREG). + */ +typedef enum { + BQ2429X_VREG_3504 = 0, /**< 3504 mV */ + BQ2429X_VREG_3600 = 6, /**< 3600 mV */ + BQ2429X_VREG_3808 = 19, /**< 3808 mV */ + BQ2429X_VREG_3904 = 25, /**< 3904 mV */ + BQ2429X_VREG_4000 = 31, /**< 4000 mV */ + BQ2429X_VREG_4112 = 38, /**< 4112 mV */ + BQ2429X_VREG_4208 = 44, /**< 4208 mV */ + BQ2429X_VREG_4304 = 50, /**< 4304 mV */ + BQ2429X_VREG_4352 = 53, /**< 4352 mV */ + BQ2429X_VREG_4400 = 56, /**< 4400 mV */ +} bq2429x_charge_voltage_limit_t; + +/** + * @brief BQ2429x device parameters + */ +typedef struct { + i2c_t i2c; /**< I2C device */ +#if IS_USED(MODULE_BQ2429X_INT) + gpio_t int_pin; /**< Interrupt pin */ +#endif + gpio_t ce_pin; /**< Charge Enable pin (optional) */ + gpio_t otg_pin; /**< OTG Enable pin (optional) */ + bq2429x_input_voltage_limit_t vlim; /**< Voltage limit */ + bq2429x_input_current_limit_t ilim; /**< Current limit */ + bq2429x_charge_current_t ichg; /**< Charge current limit */ + bq2429x_charge_voltage_limit_t vreg; /**< Charge voltage limit */ +} bq2429x_params_t; + +/** + * @brief BQ2429x device descriptor + */ +typedef struct { + bq2429x_params_t params; /**< Device parameters */ +} bq2429x_t; + +/** + * @brief Initialize device + * + * @pre @p dev != NULL && @p params != NULL + * + * @param[out] dev Device descriptor. + * @param[in] params Device parameters. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + * @return BQ2429X_ERR_GPIO on GPIO initialization failure (CE or OTG pin + * failed). + */ +int bq2429x_init(bq2429x_t *dev, const bq2429x_params_t *params); + +#if IS_USED(MODULE_BQ2429X_INT) || DOXYGEN +/** + * @brief Callback function for BQ2429x interrupts. + */ +typedef void (* bq2429x_int_cb_t)(void *); + +/** + * @brief Initialize interrupt support for the device. + * + * @pre @p dev != NULL && @p cb != NULL + * + * The callback @p cb is called in an ISR context, so keep in mind that heavy + * work shouldn't be done there. + * + * @note @ref bq2429x_init MUST have been called before! + * + * @param[in] dev Device descriptor. + * @param[in] cb Callback called on interrupt. + * @param[in] arg Argument to be passed when the callback is called. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_GPIO on GPIO initialization failure. + */ +int bq2429x_init_int(bq2429x_t *dev, bq2429x_int_cb_t cb, void *arg); +#endif /* IS_USED(MODULE_BQ2429X_INT) || DOXYGEN */ + +/** + * @brief Get device status. + * + * @pre @p dev != NULL && @p status != NULL + * + * @param[in] dev Device descriptor. + * @param[out] status Pointer where device status will be stored. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C error. + */ +int bq2429x_get_status(const bq2429x_t *dev, bq2429x_status_t *status); + +/** + * @brief Get device faults. + * + * @pre @p dev != NULL && @p fault != NULL + * + * @param[in] dev Device descriptor. + * @param[out] fault Pointer where device faults will be stored. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C error. + */ +int bq2429x_get_fault(const bq2429x_t *dev, bq2429x_fault_t *fault); + +/** + * @brief Enable OTG. + * + * This allows powering USB devices from the same port, i.e. to provide power + * or charge other devices. + * + * @note This will disable charging of the battery, if previously + * @ref bq2429x_enable_charge was called, to enable charge + * again only disable OTG mode with @ref bq2429x_disable_otg. + * + * @pre @p dev != NULL + * + * @param[in] dev Device descriptor. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + */ +int bq2429x_enable_otg(const bq2429x_t *dev); + +/** + * @brief Disable OTG. + * + * @pre @p dev != NULL + * + * @param[in] dev Device descriptor. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + */ +int bq2429x_disable_otg(const bq2429x_t *dev); + +/** + * @brief Enable battery charging. + * + * @pre @p dev != NULL + * + * @param[in] dev Device descriptor. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + */ +int bq2429x_enable_charge(const bq2429x_t *dev); + +/** + * @brief Disable battery charging. + * + * @pre @p dev != NULL + * + * @param[in] dev Device descriptor. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + */ +int bq2429x_disable_charge(const bq2429x_t *dev); + +/** + * @brief Set Input Voltage Limit. + * + * @note This is a limit on the lower bound of the voltage, for example, + * if the VLIM is set to @ref BQ2429X_VLIM_5000 (5.0V) and the supply + * voltage is lower than it (4.8V), it will result in a fault and no + * power will be delivered through the output pin (SYS pin). + * + * @pre @p dev != NULL + * + * @param[in] dev Device descriptor. + * @param[in] vlim Voltage limit. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + */ +int bq2429x_set_vlim(bq2429x_t *dev, bq2429x_input_voltage_limit_t vlim); + +/** + * @brief Get Input Voltage Limit. + * + * @pre @p dev != NULL && @p vlim != NULL + * + * @param[in] dev Device descriptor. + * @param[out] vlim Voltage limit. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + */ +int bq2429x_get_vlim(const bq2429x_t *dev, bq2429x_input_voltage_limit_t *vlim); + +/** + * @brief Set Input Current Limit. + * + * Sets the maximum current limit, this limit is also limited by hardware by + * setting a resistor to the VLIM pin which sets (by hardware) the upper limit. + * + * @pre @p dev != NULL + * + * @param[in] dev Device descriptor. + * @param[in] ilim Current limit. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + */ +int bq2429x_set_ilim(bq2429x_t *dev, bq2429x_input_current_limit_t ilim); + +/** + * @brief Get Input Current Limit. + * + * @pre @p dev != NULL && @p ilim != NULL + * + * @param[in] dev Device descriptor. + * @param[out] ilim Current limit. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + */ +int bq2429x_get_ilim(const bq2429x_t *dev, bq2429x_input_current_limit_t *ilim); + +/** + * @brief Set Charge Current. + * + * @pre @p dev != NULL + * + * @param[in] dev Device descriptor. + * @param[in] ichg Charge current. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + */ +int bq2429x_set_ichg(bq2429x_t *dev, bq2429x_charge_current_t ichg); + +/** + * @brief Get Charge Current. + * + * @pre @p dev != NULL && @p ichg != NULL + * + * @param[in] dev Device descriptor. + * @param[out] ichg Charge current. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + */ +int bq2429x_get_ichg(const bq2429x_t *dev, bq2429x_charge_current_t *ichg); + +/** + * @brief Set Charge Voltage Limit. + * + * @pre @p dev != NULL + * + * @param[in] dev Device descriptor. + * @param[in] vreg Voltage limit. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + */ +int bq2429x_set_vreg(bq2429x_t *dev, bq2429x_charge_voltage_limit_t vreg); + +/** + * @brief Get Charge Voltage Limit. + * + * @pre @p dev != NULL && @p vreg != NULL + * + * @param[in] dev Device descriptor. + * @param[out] vreg Voltage limit. + * + * @return BQ2429X_OK on success. + * @return BQ2429X_ERR_I2C on I2C failure. + */ +int bq2429x_get_vreg(const bq2429x_t *dev, + bq2429x_charge_voltage_limit_t *vreg); + +#ifdef __cplusplus +} +#endif + +#endif /* BQ2429X_H */ +/** @} */ diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 4e35fc58592b..381672f858a0 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -7,6 +7,7 @@ PSEUDOMODULES += at24c% PSEUDOMODULES += atomic_utils PSEUDOMODULES += base64url PSEUDOMODULES += board_software_reset +PSEUDOMODULES += bq2429x_int PSEUDOMODULES += can_mbox PSEUDOMODULES += can_pm PSEUDOMODULES += can_raw diff --git a/tests/driver_bq2429x/Makefile b/tests/driver_bq2429x/Makefile new file mode 100644 index 000000000000..a98e73baeaab --- /dev/null +++ b/tests/driver_bq2429x/Makefile @@ -0,0 +1,10 @@ +include ../Makefile.tests_common + +USEMODULE += bq2429x +USEMODULE += shell +USEMODULE += shell_commands + +# The driver is not present on any BOARD on the ci +TEST_ON_CI_BLACKLIST = all + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_bq2429x/Makefile.ci b/tests/driver_bq2429x/Makefile.ci new file mode 100644 index 000000000000..c93187b3835b --- /dev/null +++ b/tests/driver_bq2429x/Makefile.ci @@ -0,0 +1,6 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-nano \ + arduino-uno \ + atmega328p diff --git a/tests/driver_bq2429x/README.md b/tests/driver_bq2429x/README.md new file mode 100644 index 000000000000..c57a926ea9a2 --- /dev/null +++ b/tests/driver_bq2429x/README.md @@ -0,0 +1,11 @@ +# BQ2429x driver test application + +This is the test application for the BQ2429x device driver, it provides various +commands to set and get the battery charge parameters and to check the device +status. Type `help` on the serial console to see available commands. + +It can be flashed normally with: + +``` +make flash -C tests/driver/bq2429x BOARD=... +``` diff --git a/tests/driver_bq2429x/_util.h b/tests/driver_bq2429x/_util.h new file mode 100644 index 000000000000..50df8d8be742 --- /dev/null +++ b/tests/driver_bq2429x/_util.h @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2020 Locha Inc + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @brief Util functions for test application for the BQ24298x device + * driver + * @author Jean Pierre Dudey + * @file + */ + +#ifndef PRIV_UTIL_H +#define PRIV_UTIL_H + +#include +#include "bq2429x.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static inline const char *_util_vlim_to_str(bq2429x_input_voltage_limit_t vlim) +{ + switch (vlim) { + case BQ2429X_VLIM_3880: + return "3880 mV"; + case BQ2429X_VLIM_3960: + return "3960 mV"; + case BQ2429X_VLIM_4040: + return "4040 mV"; + case BQ2429X_VLIM_4120: + return "4120 mV"; + case BQ2429X_VLIM_4200: + return "4200 mV"; + case BQ2429X_VLIM_4280: + return "4280 mV"; + case BQ2429X_VLIM_4360: + return "4360 mV"; + case BQ2429X_VLIM_4440: + return "4440 mV"; + case BQ2429X_VLIM_4520: + return "4520 mV"; + case BQ2429X_VLIM_4600: + return "4600 mV"; + case BQ2429X_VLIM_4680: + return "4680 mV"; + case BQ2429X_VLIM_4760: + return "4760 mV"; + case BQ2429X_VLIM_4840: + return "4840 mV"; + case BQ2429X_VLIM_4920: + return "4920 mV"; + case BQ2429X_VLIM_5000: + return "5000 mV"; + case BQ2429X_VLIM_5080: + return "5080 mV"; + default: + assert(0); + break; + } + + return ""; +} + +static inline const char *_util_ilim_to_str(bq2429x_input_current_limit_t vlim) +{ + switch (vlim) { + case BQ2429X_ILIM_100: + return "100 mA"; + case BQ2429X_ILIM_150: + return "150 mA"; + case BQ2429X_ILIM_500: + return "500 mA"; + case BQ2429X_ILIM_900: + return "900 mA"; + case BQ2429X_ILIM_1000: + return "1000 mA"; + case BQ2429X_ILIM_1500: + return "1500 mA"; + case BQ2429X_ILIM_2000: + return "2000 mA"; + case BQ2429X_ILIM_3000: + return "3000 mA"; + default: + assert(0); + break; + } + + return ""; +} + +static inline const char *_util_ichg_to_str(bq2429x_charge_current_t vlim) +{ + switch (vlim) { + case BQ2429X_ICHG_512: + return "512 mA"; + case BQ2429X_ICHG_1024: + return "1024 mA"; + case BQ2429X_ICHG_2048: + return "2048 mA"; + case BQ2429X_ICHG_3008: + return "3008 mA"; + case BQ2429X_ICHG_4032: + return "4032 mA"; + case BQ2429X_ICHG_4544: + return "4544 mA"; + default: + assert(0); + break; + } + + return ""; +} + +static inline const char *_util_vreg_to_str(bq2429x_charge_voltage_limit_t vreg) +{ + switch (vreg) { + case BQ2429X_VREG_3504: + return "3504 mV"; + case BQ2429X_VREG_3600: + return "3600 mV"; + case BQ2429X_VREG_3808: + return "3808 mV"; + case BQ2429X_VREG_3904: + return "3904 mV"; + case BQ2429X_VREG_4000: + return "4000 mV"; + case BQ2429X_VREG_4112: + return "4112 mV"; + case BQ2429X_VREG_4208: + return "4208 mV"; + case BQ2429X_VREG_4304: + return "4304 mV"; + case BQ2429X_VREG_4352: + return "4352 mV"; + case BQ2429X_VREG_4400: + return "4400 mV"; + default: + assert(0); + break; + } + + return ""; +} + +static inline const char *_util_vbus_stat_to_str(bq2429x_vbus_stat_t stat) +{ + switch (stat) { + case BQ2429X_VBUS_NONE: + return "None"; + case BQ2429X_VBUS_USB_CHARGER: + return "USB"; + case BQ2429X_VBUS_AP_CHARGER: + return "Adapter Port"; + case BQ2429X_VBUS_OTG: + return "OTG"; + default: + assert(0); + break; + } + + return ""; +} + +static inline const char *_util_chrg_stat_to_str(bq2429x_chrg_stat_t stat) +{ + switch (stat) { + case BQ2429X_CHARGE_NONE: + return "None"; + case BQ2429X_CHARGE_PRE: + return "Pre-charge"; + case BQ2429X_CHARGE_FAST: + return "Fast Charging"; + case BQ2429X_CHARGE_DONE: + return "Charge Done"; + default: + assert(0); + break; + } + + return ""; +} + +static inline const char *_util_chrg_fault_to_str(bq2429x_chrg_stat_t stat) +{ + switch (stat) { + case BQ2429x_CHRG_FAULT_NORMAL: + return "Normal"; + case BQ2429x_CHRG_FAULT_INPUT: + return "Input Fault (OVP or bad source)"; + case BQ2429x_CHRG_FAULT_THERMAL_SHUTDOWN: + return "Thermal Shutdown"; + case BQ2429x_CHRG_FAULT_CHARGE_TIMER_EXPIRATION: + return "Charge Timer Expiration"; + default: + assert(0); + break; + } + + return ""; +} + +#ifdef __cplusplus +} +#endif + +#endif /* PRIV_UTIL_H */ diff --git a/tests/driver_bq2429x/app.config.test b/tests/driver_bq2429x/app.config.test new file mode 100644 index 000000000000..18a1cc39e9a1 --- /dev/null +++ b/tests/driver_bq2429x/app.config.test @@ -0,0 +1,4 @@ +CONFIG_MODULE_SHELL=y +CONFIG_MODULE_SHELL_COMMANDS=y +# include driver for bq2429x +CONFIG_MODULE_BQ2429X=y diff --git a/tests/driver_bq2429x/main.c b/tests/driver_bq2429x/main.c new file mode 100644 index 000000000000..7cf889918cf2 --- /dev/null +++ b/tests/driver_bq2429x/main.c @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2020 Locha Inc + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @brief Test application for the BQ24298x device driver + * @author Jean Pierre Dudey + * @file + */ + +#include +#include + +#include "bq2429x_params.h" +#include "shell.h" +#include "_util.h" + +static bq2429x_t _dev; + +/* Status and faults */ +static int _bq2429x_get_status_cmd(int argc, char **argv); +static int _bq2429x_get_fault_cmd(int argc, char **argv); +/* OTG */ +static int _bq2429x_enable_otg_cmd(int argc, char **argv); +static int _bq2429x_disable_otg_cmd(int argc, char **argv); +/* Charge */ +static int _bq2429x_enable_chg_cmd(int argc, char **argv); +static int _bq2429x_disable_chg_cmd(int argc, char **argv); +/* Set charge parameters */ +static int _bq2429x_set_vlim_cmd(int argc, char **argv); +static int _bq2429x_set_ilim_cmd(int argc, char **argv); +static int _bq2429x_set_ichg_cmd(int argc, char **argv); +static int _bq2429x_set_vreg_cmd(int argc, char **argv); +/* Get charge parameters */ +static int _bq2429x_get_vlim_cmd(int argc, char **argv); +static int _bq2429x_get_ilim_cmd(int argc, char **argv); +static int _bq2429x_get_ichg_cmd(int argc, char **argv); +static int _bq2429x_get_vreg_cmd(int argc, char **argv); + +static const shell_command_t shell_commands[] = { + /* Status and faults */ + { "bq2429x_get_status", "Get device status", _bq2429x_get_status_cmd }, + { "bq2429x_get_fault", "Get device faults", _bq2429x_get_fault_cmd }, + /* OTG */ + { "bq2429x_enable_otg", "Enable OTG mode", _bq2429x_enable_otg_cmd }, + { "bq2429x_disable_otg", "Disable OTG mode", _bq2429x_disable_otg_cmd }, + /* Charge */ + { "bq2429x_enable_charge", "Enable battery charge", + _bq2429x_enable_chg_cmd }, + { "bq2429x_disable_charge", "Disable battery charge", + _bq2429x_disable_chg_cmd }, + /* Set charge parameters */ + { "bq2429x_set_vlim", "Set input voltage limit (0=3880 mV .. 15=5080 mV)", + _bq2429x_set_vlim_cmd }, + { "bq2429x_set_ilim", "Set input current limit (0=100 mA .. 7=3000 mA)", + _bq2429x_set_ilim_cmd }, + { "bq2429x_set_ichg", "Set charge current (0=512 mA .. 5=4544 mA)", + _bq2429x_set_ichg_cmd }, + { "bq2429x_set_vreg", "Set charge voltage limit (0=3504 mV .. 9=4400 mV)", + _bq2429x_set_vreg_cmd }, + /* Get charge parameters */ + { "bq2429x_get_vlim", "Get input voltage limit", _bq2429x_get_vlim_cmd }, + { "bq2429x_get_ilim", "Get input current limit", _bq2429x_get_ilim_cmd }, + { "bq2429x_get_ichg", "Get charge current", _bq2429x_get_ichg_cmd }, + { "bq2429x_get_vreg", "Get charge voltage limit", _bq2429x_get_vreg_cmd }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + + puts("BQ2429x driver test application\n"); + + if (bq2429x_init(&_dev, &bq2429x_params[0]) != BQ2429X_OK) { + puts("[Failed]"); + return 1; + } + + puts("[OK]\n"); + + puts("All up, running the shell now."); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + /* should never be reached */ + return 0; +} + +static int _bq2429x_get_status_cmd(int argc, char **argv) +{ + (void)argc; + (void)argv; + + int ret; + bq2429x_status_t status; + if ((ret = bq2429x_get_status(&_dev, &status)) != BQ2429X_OK) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + + printf("Device status:\n"); + printf("==============\n\n"); + + printf("VBUS: %s\n", _util_vbus_stat_to_str(status.vbus)); + printf("Charge: %s\n", _util_chrg_stat_to_str(status.chrg)); + printf("DPM: %s\n", status.dpm ? "VINDPM or IINDPM" : "Not in DPM"); + printf("Power Good: %s\n", status.pg ? "Yes" : "No"); + printf("Thermal Regulation: %s\n", status.therm ? "In thermal retulation" + : "Normal"); + printf("In VSYSMIN Regulation (BAT < VSYSMIN): %s\n", status.vsys ? "Yes" + : "No"); + + return 0; +} + +static int _bq2429x_get_fault_cmd(int argc, char **argv) +{ + (void)argc; + (void)argv; + + int ret; + bq2429x_fault_t fault; + if ((ret = bq2429x_get_fault(&_dev, &fault)) != BQ2429X_OK) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + + printf("Device faults:\n"); + printf("==============\n\n"); + + printf("Watchdog: %s\n", fault.watchdog ? "Yes" : "No"); + printf("OTG: %s\n", fault.otg ? "Yes" : "No"); + printf("Charge: %s\n", _util_chrg_fault_to_str(fault.chrg)); + printf("Battery: %s\n", fault.bat ? "Yes" : "No"); + printf("NTC Fault 0: %s\n", fault.ntc_fault_0 ? "Yes" : "No"); + printf("NTC Fault 1: %s\n", fault.ntc_fault_1 ? "Yes" : "No"); + + return 0; +} + +static int _bq2429x_enable_otg_cmd(int argc, char **argv) +{ + (void)argc; + (void)argv; + + int ret; + if ((ret = bq2429x_enable_otg(&_dev))) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + puts("OTG enabled"); + return 0; +} + +static int _bq2429x_disable_otg_cmd(int argc, char **argv) +{ + (void)argc; + (void)argv; + + int ret; + if ((ret = bq2429x_disable_otg(&_dev))) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + puts("OTG disabled"); + return 0; +} + +static int _bq2429x_enable_chg_cmd(int argc, char **argv) +{ + (void)argc; + (void)argv; + + int ret; + if ((ret = bq2429x_enable_charge(&_dev))) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + puts("Battery charge enabled"); + return 0; +} + +static int _bq2429x_disable_chg_cmd(int argc, char **argv) +{ + (void)argc; + (void)argv; + + int ret; + if ((ret = bq2429x_disable_charge(&_dev))) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + puts("Battery charge disabled"); + return 0; +} + +static int _bq2429x_set_vlim_cmd(int argc, char **argv) +{ + if (argc < 2 || argc > 2) { + printf("usage: %s [vlim]\n", argv[0]); + return 1; + } + + int vlim = atoi(argv[1]); + if (vlim < 0) { + puts("error: only positive values allowed"); + return 1; + } + if (vlim > BQ2429X_VLIM_5080) { + puts("error: maximum value is 5.08 V (15)"); + return 1; + } + + int ret; + if ((ret = bq2429x_set_vlim(&_dev, vlim)) != BQ2429X_OK) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + printf("VLIM set to %s\n", _util_vlim_to_str(vlim)); + + return 0; +} + +static int _bq2429x_set_ilim_cmd(int argc, char **argv) +{ + if (argc < 2 || argc > 2) { + printf("usage: %s [ilim]\n", argv[0]); + return 1; + } + + int ilim = atoi(argv[1]); + if (ilim < 0) { + puts("error: only positive values allowed"); + return 1; + } + if (ilim > BQ2429X_ILIM_3000) { + puts("error: maximum value is 3.0 A (7)"); + return 1; + } + + int ret; + if ((ret = bq2429x_set_ilim(&_dev, ilim)) != BQ2429X_OK) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + printf("ILIM set to %s\n", _util_ilim_to_str(ilim)); + + return 0; +} + +static int _bq2429x_set_ichg_cmd(int argc, char **argv) +{ + const bq2429x_charge_current_t ichg_lookup_table[] = { + BQ2429X_ICHG_512, + BQ2429X_ICHG_1024, + BQ2429X_ICHG_2048, + BQ2429X_ICHG_3008, + BQ2429X_ICHG_4032, + BQ2429X_ICHG_4544, + }; + + if (argc < 2 || argc > 2) { + printf("usage: %s [ichg]\n", argv[0]); + return 1; + } + + int i = atoi(argv[1]); + if (i < 0) { + puts("error: only positive values allowed"); + return 1; + } + if (i >= (int)ARRAY_SIZE(ichg_lookup_table)) { + int last = ARRAY_SIZE(ichg_lookup_table) - 1; + printf("error: maximum value is %s (%d)\n", + _util_ichg_to_str(ichg_lookup_table[last]), last); + return 1; + } + + bq2429x_charge_current_t ichg = ichg_lookup_table[i]; + int ret; + if ((ret = bq2429x_set_ichg(&_dev, ichg)) != BQ2429X_OK) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + printf("ICHG set to %s\n", _util_ichg_to_str(ichg)); + + return 0; +} + +static int _bq2429x_set_vreg_cmd(int argc, char **argv) +{ + const bq2429x_charge_voltage_limit_t vreg_lookup_table[] = { + BQ2429X_VREG_3504, + BQ2429X_VREG_3600, + BQ2429X_VREG_3808, + BQ2429X_VREG_3904, + BQ2429X_VREG_4000, + BQ2429X_VREG_4112, + BQ2429X_VREG_4208, + BQ2429X_VREG_4304, + BQ2429X_VREG_4352, + BQ2429X_VREG_4400, + }; + + if (argc < 2 || argc > 2) { + printf("usage: %s [vreg]\n", argv[0]); + return 1; + } + + int i = atoi(argv[1]); + if (i < 0) { + puts("error: only positive values allowed"); + return 1; + } + if (i >= (int)ARRAY_SIZE(vreg_lookup_table)) { + int last = ARRAY_SIZE(vreg_lookup_table) - 1; + printf("error: maximum value is %s (%d)\n", + _util_vreg_to_str(vreg_lookup_table[last]), last); + return 1; + } + + bq2429x_charge_voltage_limit_t vreg = vreg_lookup_table[i]; + int ret; + if ((ret = bq2429x_set_vreg(&_dev, vreg)) != BQ2429X_OK) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + printf("VREG set to %s\n", _util_vreg_to_str(vreg)); + + return 0; +} + +static int _bq2429x_get_vlim_cmd(int argc, char **argv) +{ + (void)argc; + (void)argv; + + bq2429x_input_voltage_limit_t vlim; + int ret; + + if ((ret = bq2429x_get_vlim(&_dev, &vlim)) != BQ2429X_OK) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + printf("VLIM: %s\n", _util_vlim_to_str(vlim)); + + return 0; +} + +static int _bq2429x_get_ilim_cmd(int argc, char **argv) +{ + (void)argc; + (void)argv; + + bq2429x_input_current_limit_t ilim; + int ret; + + if ((ret = bq2429x_get_ilim(&_dev, &ilim)) != BQ2429X_OK) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + printf("ILIM: %s\n", _util_ilim_to_str(ilim)); + + return 0; +} + +static int _bq2429x_get_ichg_cmd(int argc, char **argv) +{ + (void)argc; + (void)argv; + + bq2429x_charge_current_t ichg; + int ret; + + if ((ret = bq2429x_get_ichg(&_dev, &ichg)) != BQ2429X_OK) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + printf("ICHG: %s\n", _util_ichg_to_str(ichg)); + + return 0; +} + +static int _bq2429x_get_vreg_cmd(int argc, char **argv) +{ + (void)argc; + (void)argv; + + bq2429x_charge_voltage_limit_t vreg; + int ret; + + if ((ret = bq2429x_get_vreg(&_dev, &vreg)) != BQ2429X_OK) { + printf("[Failed] ret=%d\n", ret); + return 1; + } + printf("VREG: %s\n", _util_vreg_to_str(vreg)); + + return 0; +} diff --git a/tests/driver_bq2429x/tests/01-run.py b/tests/driver_bq2429x/tests/01-run.py new file mode 100755 index 000000000000..959d49ef2229 --- /dev/null +++ b/tests/driver_bq2429x/tests/01-run.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2020 Locha Inc +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys + +from testrunner import run + + +def testfunc(child): + # VLIM + child.sendline("bq2429x_set_vlim 15") + child.expect(r"VLIM set to 5080 mV") + child.sendline("bq2429x_get_vlim") + child.expect(r"VLIM: 5080 mV") + # ILIM + child.sendline("bq2429x_set_ilim 2") + child.expect(r"ILIM set to 500 mA") + child.sendline("bq2429x_get_ilim") + child.expect(r"ILIM: 500 mA") + # ICHG + child.sendline("bq2429x_set_ichg 0") + child.expect(r"ICHG set to 512 mA") + child.sendline("bq2429x_get_ichg") + child.expect(r"ICHG: 512 mA") + # VREG + child.sendline("bq2429x_set_vreg 6") + child.expect(r"VREG set to 4208 mV") + child.sendline("bq2429x_get_vreg") + child.expect(r"VREG: 4208 mV") + + +if __name__ == "__main__": + sys.exit(run(testfunc, timeout=5, echo=True))