drivers: charger: charger_max20335: introduce int utilization
Utilization of chip interrupt line is mandatory to assure proper charger state control. Handle interrupt to prepare the driver for implementation of such functionality. Modify charger status update so the current state is fetched in the interrupt handler. Use level based interrupts combined with interrupt disabling for a period of time after interrupt handling to reduce number of interrupts triggered by the charger. There may be a case where the charger produces burst of interrupts for a several seconds and if the code attempts to handle every single interrupt separatery then the system might be significantly overloaded. Co-authored-by: Bartosz Bilas <b.bilas@grinn-global.com> Signed-off-by: Lukasz Madej <l.madej@grinn-global.com>
This commit is contained in:
parent
e9eb1d841e
commit
90c32e99d6
5 changed files with 202 additions and 2 deletions
|
|
@ -5,6 +5,7 @@ config CHARGER_MAX20335
|
|||
bool "MAX20335 battery charger driver"
|
||||
default y
|
||||
depends on DT_HAS_MAXIM_MAX20335_CHARGER_ENABLED
|
||||
select GPIO
|
||||
select I2C
|
||||
select MFD
|
||||
help
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/charger.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
|
@ -17,6 +18,11 @@
|
|||
LOG_MODULE_REGISTER(max20335_charger);
|
||||
|
||||
#define MAX20335_REG_STATUS_A 0x02
|
||||
#define MAX20335_REG_INT_A 0x05
|
||||
#define MAX20335_REG_INT_B 0x06
|
||||
#define MAX20335_INT_A_CHG_STAT_MASK BIT(6)
|
||||
#define MAX20335_REG_INT_MASK_A 0x07
|
||||
#define MAX20335_REG_INT_MASK_B 0x08
|
||||
#define MAX20335_REG_ILIMCNTL 0x09
|
||||
#define MAX20335_REG_CHG_CNTL_A 0x0A
|
||||
#define MAX20335_CHGCNTLA_BAT_REG_CFG_MASK GENMASK(4, 1)
|
||||
|
|
@ -29,12 +35,23 @@ LOG_MODULE_REGISTER(max20335_charger);
|
|||
#define MAX20335_REG_CVC_VREG_MIN_IDX 0x0U
|
||||
#define MAX20335_REG_CVC_VREG_MAX_IDX 0x0CU
|
||||
|
||||
#define INT_ENABLE_DELAY K_MSEC(500)
|
||||
|
||||
struct charger_max20335_config {
|
||||
struct i2c_dt_spec bus;
|
||||
struct gpio_dt_spec int_gpio;
|
||||
uint32_t max_ichg_ua;
|
||||
uint32_t max_vreg_uv;
|
||||
};
|
||||
|
||||
struct charger_max20335_data {
|
||||
const struct device *dev;
|
||||
struct gpio_callback gpio_cb;
|
||||
struct k_work int_routine_work;
|
||||
struct k_work_delayable int_enable_work;
|
||||
enum charger_status charger_status;
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX20335_CHARGER_OFF,
|
||||
MAX20335_CHARGING_SUSPENDED_DUE_TO_TEMPERATURE,
|
||||
|
|
@ -214,12 +231,55 @@ static int max20335_set_enabled(const struct device *dev, bool enable)
|
|||
enable ? MAX20335_CHRG_EN : 0);
|
||||
}
|
||||
|
||||
static int max20335_get_interrupt_source(const struct device *dev, uint8_t *int_a, uint8_t *int_b)
|
||||
{
|
||||
const struct charger_max20335_config *config = dev->config;
|
||||
uint8_t dummy;
|
||||
uint8_t *int_src;
|
||||
int ret;
|
||||
|
||||
/* Both INT_A and INT_B registers need to be read to clear all int flags */
|
||||
|
||||
int_src = (int_a != NULL) ? int_a : &dummy;
|
||||
ret = i2c_reg_read_byte_dt(&config->bus, MAX20335_REG_INT_A, int_src);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int_src = (int_b != NULL) ? int_b : &dummy;
|
||||
|
||||
return i2c_reg_read_byte_dt(&config->bus, MAX20335_REG_INT_B, int_src);
|
||||
}
|
||||
|
||||
static int max20335_enable_interrupts(const struct device *dev)
|
||||
{
|
||||
enum {MASK_A_VAL_ENABLE = 0xFF};
|
||||
const struct charger_max20335_config *config = dev->config;
|
||||
int ret;
|
||||
|
||||
ret = max20335_get_interrupt_source(dev, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
LOG_WRN("Failed to clear pending interrupts: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_reg_write_byte_dt(&config->bus, MAX20335_REG_INT_MASK_A, MASK_A_VAL_ENABLE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return i2c_reg_write_byte_dt(&config->bus, MAX20335_REG_INT_MASK_B, 0);
|
||||
}
|
||||
|
||||
static int max20335_get_prop(const struct device *dev, charger_prop_t prop,
|
||||
union charger_propval *val)
|
||||
{
|
||||
struct charger_max20335_data *data = dev->data;
|
||||
|
||||
switch (prop) {
|
||||
case CHARGER_PROP_STATUS:
|
||||
return max20335_get_charger_status(dev, &val->status);
|
||||
val->status = data->charger_status;
|
||||
return 0;
|
||||
case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA:
|
||||
return max20335_get_constant_charge_current(dev,
|
||||
&val->const_charge_current_ua);
|
||||
|
|
@ -246,14 +306,137 @@ static int max20335_set_prop(const struct device *dev, charger_prop_t prop,
|
|||
}
|
||||
}
|
||||
|
||||
static int max20335_enable_interrupt_pin(const struct device *dev, bool enabled)
|
||||
{
|
||||
const struct charger_max20335_config *const config = dev->config;
|
||||
gpio_flags_t flags;
|
||||
int ret;
|
||||
|
||||
flags = enabled ? GPIO_INT_LEVEL_ACTIVE : GPIO_INT_DISABLE;
|
||||
|
||||
ret = gpio_pin_interrupt_configure_dt(&config->int_gpio, flags);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Could not %s interrupt GPIO callback: %d", enabled ? "enable" : "disable",
|
||||
ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void max20335_gpio_callback(const struct device *dev, struct gpio_callback *cb,
|
||||
uint32_t pins)
|
||||
{
|
||||
struct charger_max20335_data *data = CONTAINER_OF(cb, struct charger_max20335_data,
|
||||
gpio_cb);
|
||||
int ret;
|
||||
|
||||
(void) max20335_enable_interrupt_pin(data->dev, false);
|
||||
|
||||
ret = k_work_submit(&data->int_routine_work);
|
||||
if (ret < 0) {
|
||||
LOG_WRN("Could not submit int work: %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void max20335_int_routine_work_handler(struct k_work *work)
|
||||
{
|
||||
struct charger_max20335_data *data = CONTAINER_OF(work, struct charger_max20335_data,
|
||||
int_routine_work);
|
||||
uint8_t int_src_a;
|
||||
int ret;
|
||||
|
||||
ret = max20335_get_interrupt_source(data->dev, &int_src_a, NULL);
|
||||
if (ret < 0) {
|
||||
LOG_WRN("Failed to read interrupt source");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((int_src_a & MAX20335_INT_A_CHG_STAT_MASK) != 0) {
|
||||
ret = max20335_get_charger_status(data->dev, &data->charger_status);
|
||||
if (ret < 0) {
|
||||
LOG_WRN("Failed to read charger status: %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
ret = k_work_reschedule(&data->int_enable_work, INT_ENABLE_DELAY);
|
||||
if (ret < 0) {
|
||||
LOG_WRN("Could not reschedule int_enable_work: %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void max20335_int_enable_work_handler(struct k_work *work)
|
||||
{
|
||||
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
|
||||
struct charger_max20335_data *data = CONTAINER_OF(dwork, struct charger_max20335_data,
|
||||
int_enable_work);
|
||||
|
||||
(void) max20335_enable_interrupt_pin(data->dev, true);
|
||||
}
|
||||
|
||||
static int max20335_configure_interrupt_pin(const struct device *dev)
|
||||
{
|
||||
struct charger_max20335_data *data = dev->data;
|
||||
const struct charger_max20335_config *config = dev->config;
|
||||
int ret;
|
||||
|
||||
if (!gpio_is_ready_dt(&config->int_gpio)) {
|
||||
LOG_ERR("Interrupt GPIO device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Could not configure interrupt GPIO");
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpio_init_callback(&data->gpio_cb, max20335_gpio_callback, BIT(config->int_gpio.pin));
|
||||
ret = gpio_add_callback_dt(&config->int_gpio, &data->gpio_cb);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Could not add interrupt GPIO callback");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max20335_init(const struct device *dev)
|
||||
{
|
||||
struct charger_max20335_data *data = dev->data;
|
||||
const struct charger_max20335_config *config = dev->config;
|
||||
int ret;
|
||||
|
||||
if (!i2c_is_ready_dt(&config->bus)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->dev = dev;
|
||||
|
||||
ret = max20335_get_charger_status(dev, &data->charger_status);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to read charger status: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_work_init(&data->int_routine_work, max20335_int_routine_work_handler);
|
||||
k_work_init_delayable(&data->int_enable_work, max20335_int_enable_work_handler);
|
||||
|
||||
ret = max20335_configure_interrupt_pin(dev);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = max20335_enable_interrupt_pin(dev, true);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = max20335_enable_interrupts(dev);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to enable interrupts");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -264,13 +447,15 @@ static const struct charger_driver_api max20335_driver_api = {
|
|||
};
|
||||
|
||||
#define MAX20335_DEFINE(inst) \
|
||||
static struct charger_max20335_data charger_max20335_data_##inst; \
|
||||
static const struct charger_max20335_config charger_max20335_config_##inst = { \
|
||||
.bus = I2C_DT_SPEC_GET(DT_INST_PARENT(inst)), \
|
||||
.int_gpio = GPIO_DT_SPEC_INST_GET(inst, int_gpios), \
|
||||
.max_ichg_ua = DT_INST_PROP(inst, constant_charge_current_max_microamp), \
|
||||
.max_vreg_uv = DT_INST_PROP(inst, constant_charge_voltage_max_microvolt), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, &max20335_init, NULL, NULL, \
|
||||
DEVICE_DT_INST_DEFINE(inst, &max20335_init, NULL, &charger_max20335_data_##inst, \
|
||||
&charger_max20335_config_##inst, \
|
||||
POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, \
|
||||
&max20335_driver_api);
|
||||
|
|
|
|||
|
|
@ -13,3 +13,8 @@ properties:
|
|||
|
||||
constant-charge-voltage-max-microvolt:
|
||||
required: true
|
||||
|
||||
int-gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: Interrupt pin
|
||||
|
|
|
|||
|
|
@ -10,6 +10,14 @@
|
|||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
test_gpio: gpio@deadbeef {
|
||||
compatible = "vnd,gpio";
|
||||
gpio-controller;
|
||||
reg = <0xdeadbeef 0x1000>;
|
||||
#gpio-cells = <0x2>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
test_i2c: i2c@11112222 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ max20335@1 {
|
|||
|
||||
constant-charge-current-max-microamp = <100000>;
|
||||
constant-charge-voltage-max-microvolt = <4050000>;
|
||||
int-gpios = <&test_gpio 0 0>;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue