blob: 3c1eb6aa6150057c363f6de64bb1a803cd61e087 [file] [log] [blame]
/*
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nxp_pca9422_charger
#include <zephyr/device.h>
#include <zephyr/drivers/mfd/pca9422.h>
#include <zephyr/drivers/charger.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/linear_range.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(pca9422_charger, CONFIG_CHARGER_LOG_LEVEL);
/** Register memory map. See datasheet for more details. */
/** Interrupt registers */
/** @brief Interrupt registers for device operation */
#define PCA9422_REG_INT_DEVICE_0 0x5CU
#define PCA9422_REG_INT_DEVICE_1 0x5DU
/** @brief Interrupt registers for charger operation */
#define PCA9422_REG_INT_CHARGER_0 0x5EU
#define PCA9422_REG_INT_CHARGER_1 0x5FU
#define PCA9422_REG_INT_CHARGER_2 0x60U
#define PCA9422_REG_INT_CHARGER_3 0x61U
/** @brief Interrupt mask registers for device operation */
#define PCA9422_REG_INT_DEVICE_0_MASK 0x62U
#define PCA9422_REG_INT_DEVICE_1_MASK 0x63U
/** @brief Interrupt mask registers for charger operation */
#define PCA9422_REG_INT_CHARGER_0_MASK 0x64U
#define PCA9422_REG_INT_CHARGER_1_MASK 0x65U
#define PCA9422_REG_INT_CHARGER_2_MASK 0x66U
#define PCA9422_REG_INT_CHARGER_3_MASK 0x67U
/** Status registers */
/** @brief Status registers for device operation */
#define PCA9422_REG_DEVICE_0_STS 0x68U
#define PCA9422_BIT_VIN_SAFE_0V BIT(2)
#define PCA9422_BIT_VIN_NOK BIT(1)
#define PCA9422_BIT_VIN_OK BIT(0)
#define PCA9422_REG_DEVICE_1_STS 0x69U
#define PCA9422_BIT_VIN_I_LIMIT_STS BIT(7)
#define PCA9422_BIT_VSYS_SUPPLEMENT_EXIT BIT(6)
#define PCA9422_BIT_VSYS_SUPPLEMENT BIT(5)
#define PCA9422_BIT_VSYS_OVER_LOAD BIT(4)
#define PCA9422_BIT_VIN_AICL_RELEASE BIT(3)
#define PCA9422_BIT_VIN_AICL BIT(2)
#define PCA9422_BIT_VIN_OVP_EXIT BIT(1)
#define PCA9422_BIT_VIN_OVP BIT(0)
/** @brief Status registers for charger operation */
#define PCA9422_REG_CHARGER_0_STS 0x6AU
#define PCA9422_BIT_TOP_OFF BIT(7)
#define PCA9422_BIT_CV_MODE BIT(6)
#define PCA9422_BIT_FAST_CHARGE BIT(5)
#define PCA9422_BIT_PRECHARGE BIT(4)
#define PCA9422_BIT_CHARGER_OFF BIT(3)
#define PCA9422_BIT_CHARGER_ON BIT(2)
#define PCA9422_BIT_CHG_QUAL_NOK BIT(1)
#define PCA9422_BIT_CHG_QUAL_OK BIT(0)
#define PCA9422_REG_CHARGER_1_STS 0x6BU
#define PCA9422_BIT_THERM_HOT BIT(7)
#define PCA9422_BIT_THERM_WARM_PLUS BIT(6)
#define PCA9422_BIT_THERM_WARM BIT(5)
#define PCA9422_BIT_THERM_COOL BIT(4)
#define PCA9422_BIT_THERM_COLD BIT(3)
#define PCA9422_BIT_VBAT_OVP_EXIT BIT(2)
#define PCA9422_BIT_VBAT_OVP BIT(1)
#define PCA9422_BIT_NO_BATTERY BIT(0)
#define PCA9422_REG_CHARGER_2_STS 0x6CU
#define PCA9422_BIT_RECHARGE BIT(7)
#define PCA9422_BIT_CHARGE_DONE BIT(6)
#define PCA9422_BIT_THERMAL_REGULATION BIT(5)
#define PCA9422_BIT_TOP_OFF_TIMER_OUT BIT(4)
#define PCA9422_BIT_FAST_CHG_TIMER_OUT BIT(3)
#define PCA9422_BIT_PRECHARGE_TIMER_OUT BIT(2)
#define PCA9422_BIT_THERM_DISABLE BIT(1)
#define PCA9422_BIT_THERM_OPEN BIT(0)
#define PCA9422_REG_CHARGER_3_STS 0x6DU
#define PCA9422_BIT_VBAT_OCP BIT(0)
/** Control registers */
/** @brief Device control registers */
#define PCA9422_REG_VIN_CNTL_0 0x6EU
#define PCA9422_BIT_VIN_PD_EN BIT(1)
#define PCA9422_REG_VIN_CNTL_1 0x6FU
#define PCA9422_BIT_FORCE_DISACHARGE_VSYS_EN BIT(3)
#define PCA9422_BIT_AICL_V GENMASK(2, 1)
#define PCA9422_BIT_AICL_EN BIT(0)
#define PCA9422_REG_VIN_CNTL_2 0x70U
#define PCA9422_BIT_VIN_I_LIMIT GENMASK(4, 0)
#define PCA9422_REG_VIN_CNTL_3 0x71U
#define PCA9422_BIT_VSYS_REG GENMASK(7, 4)
/** @brief Charger control registers */
#define PCA9422_REG_CHARGER_CNTL_0 0x72U
#define PCA9422_BIT_CHARGER_LOCK GENMASK(5, 4)
#define PCA9422_REG_CHARGER_CNTL_1 0x73U
#define PCA9422_BIT_BAT_PRESENCE_DET_DISABLE BIT(6)
#define PCA9422_BIT_AUTOSTOP_CHG_EN BIT(5)
#define PCA9422_BIT_CHARGER_EN BIT(4)
#define PCA9422_BIT_V_WARM_50C GENMASK(3, 2)
#define PCA9422_BIT_PRECHG_CURRENT BIT(1)
#define PCA9422_BIT_CHG_CURRENT_STEP BIT(0)
#define PCA9422_REG_CHARGER_CNTL_2 0x74U
#define PCA9422_BIT_VBAT_REG GENMASK(6, 0)
#define PCA9422_REG_CHARGER_CNTL_3 0x75U
#define PCA9422_BIT_I_FAST_CHG GENMASK(6, 0)
#define PCA9422_REG_CHARGER_CNTL_4 0x76U
#define PCA9422_BIT_VBAT_OVP_DEB GENMASK(7, 6)
#define PCA9422_BIT_RECHARGE_TH GENMASK(5, 4)
#define PCA9422_BIT_TOP_OFF_CURRENT GENMASK(3, 2)
#define PCA9422_BIT_PRE_CHG_TIMER GENMASK(1, 0)
#define PCA9422_REG_CHARGER_CNTL_5 0x77U
#define PCA9422_BIT_THERM_NTC_EN BIT(6)
#define PCA9422_BIT_OCP_DISCHARGE_DEB GENMASK(5, 4)
#define PCA9422_BIT_OCP_DISCHARGE GENMASK(1, 0)
#define PCA9422_REG_CHARGER_CNTL_6 0x78U
#define PCA9422_BIT_V_HOT_60C GENMASK(7, 6)
#define PCA9422_BIT_V_WARM_45C GENMASK(5, 4)
#define PCA9422_BIT_V_COOL_10C GENMASK(3, 2)
#define PCA9422_BIT_V_COLD_0C GENMASK(1, 0)
#define PCA9422_REG_CHARGER_CNTL_7 0x79U
#define PCA9422_BIT_FAST_CHG_TIMER GENMASK(7, 6)
#define PCA9422_BIT_2X_ALL_TIMERS_EN BIT(5)
#define PCA9422_BIT_CHG_DISABLE_AT_COLD_HOT_EN BIT(4)
#define PCA9422_BIT_NEW_I_VBAT_AT_10C GENMASK(3, 2)
#define PCA9422_BIT_NEW_VBAT_AT_45C GENMASK(1, 0)
#define PCA9422_REG_CHARGER_CNTL_8 0x7AU
#define PCA9422_BIT_THERMAL_REGULATION_TH GENMASK(5, 3)
#define PCA9422_BIT_TOP_OFF_TIMER_OUT_MIN GENMASK(2, 0)
#define PCA9422_REG_CHARGER_CNTL_9 0x7BU
#define PCA9422_BIT_NEW_VBAT_AT_50C GENMASK(7, 6)
#define PCA9422_BIT_NEW_I_VBAT_AT_50C GENMASK(5, 4)
#define PCA9422_BIT_NEW_I_VBAT_AT_45C GENMASK(3, 2)
#define PCA9422_BIT_FORCE_DISCHARGE_VBAT_EN BIT(1)
#define PCA9422_BIT_USB_SUSPEND BIT(0)
#define PCA9422_REG_CHARGER_CNTL_10 0x7CU
#define PCA9422_BIT_AMUX_AUTO_OFF_WAIT GENMASK(7, 6)
#define PCA9422_BIT_AMUX_MODE BIT(5)
#define PCA9422_BIT_AMUX_VBAT_VSYS_GAIN BIT(4)
#define PCA9422_BIT_AMUX_THERM_GAIN BIT(3)
#define PCA9422_BIT_AMUX_CHANNEL GENMASK(2, 0)
/** brief Charger unlock value */
#define PCA9422_CHARGER_UNLOCK 0x30U
#define PCA9422_CHARGER_LOCK 0x00U
enum {
RECHARGE_TH_100MV,
RECHARGE_TH_150MV,
RECHARGE_TH_200MV,
};
enum {
ITOPOFF_2P5PCT, /* 2.5% of Ifast_chg */
ITOPOFF_5P0PCT, /* 5.0% of Ifast_chg */
ITOPOFF_7P5PCT, /* 7.5% of Ifast_chg */
ITOPOFF_10P0PCT, /* 10.0% of Ifast_chg */
};
enum {
CHG_CURRENT_STEP_2P5MA,
CHG_CURRENT_STEP_5P0MA,
};
#define CURRENT_STEP_2P5MA_MIN_UA 2500 /* 2.5mA */
#define CURRENT_STEP_2P5MA_MAX_UA 320000 /* 320mA */
#define CURRENT_STEP_5P0MA_MAX_UA 640000 /* 640mA */
#define VBAT_REG_MIN_UV 3600000 /* 3.6V */
#define VBAT_REG_MAX_UV 4600000 /* 4.6V */
#define VIN_I_LIMIT_MIN_UA 45000 /* 45mA */
#define VIN_I_LIMIT_MAX_UA 1195000 /* 1195mA */
#define VSYS_REG_MIN_UV 4425000 /* 4.425V */
#define VSYS_REG_MAX_UV 4800000 /* 4.8V */
struct charger_pca9422_config {
const struct device *mfd;
uint32_t vin_i_limit_ua;
uint32_t vsys_reg_uv;
};
struct charger_pca9422_data {
const struct device *dev;
struct k_mutex mutex;
uint32_t i_fast_chg_ua;
uint8_t i_topoff_sel;
uint8_t i_prechg_sel;
uint32_t vbat_reg_uv;
uint32_t recharge_th_sel;
uint8_t chg_current_step;
enum charger_status status;
enum charger_online online;
bool charger_enabled;
};
static const struct linear_range vin_i_limit_ua_range[] = {
LINEAR_RANGE_INIT(45000, 25000, 0x0U, 0x1AU),
LINEAR_RANGE_INIT(795000, 100000, 0x1BU, 0x1FU),
};
static const struct linear_range vsys_reg_uv_range[] = {
LINEAR_RANGE_INIT(4425000, 25000, 0x0U, 0xFU),
};
static const struct linear_range vbat_reg_uv_range[] = {
LINEAR_RANGE_INIT(3600000, 10000, 0x0U, 0x64U),
LINEAR_RANGE_INIT(4600000, 0, 0x65U, 0x7FU),
};
static const struct linear_range i_fast_chg_ua_range[] = {
LINEAR_RANGE_INIT(2500, 2500, 0x0U, 0x7FU),
};
static int pca9422_charger_get_status(const struct device *dev, enum charger_status *status)
{
const struct charger_pca9422_config *const config = dev->config;
uint8_t val;
int ret;
ret = mfd_pca9422_reg_read_byte(config->mfd, PCA9422_REG_CHARGER_0_STS, &val);
if (ret < 0) {
return ret;
}
if (val & PCA9422_BIT_CHG_QUAL_OK) {
if (val & (PCA9422_BIT_PRECHARGE | PCA9422_BIT_FAST_CHARGE | PCA9422_BIT_CV_MODE |
PCA9422_BIT_TOP_OFF)) {
*status = CHARGER_STATUS_CHARGING;
} else {
*status = CHARGER_STATUS_NOT_CHARGING;
}
/* Check charging done status */
ret = mfd_pca9422_reg_read_byte(config->mfd, PCA9422_REG_CHARGER_2_STS, &val);
if (ret < 0) {
return ret;
}
if (val & PCA9422_BIT_CHARGE_DONE) {
*status = CHARGER_STATUS_FULL;
}
} else {
*status = CHARGER_STATUS_NOT_CHARGING;
}
return 0;
}
static int pca9422_charger_get_online(const struct device *dev, enum charger_online *online)
{
const struct charger_pca9422_config *const config = dev->config;
uint8_t val;
int ret;
ret = mfd_pca9422_reg_read_byte(config->mfd, PCA9422_REG_DEVICE_0_STS, &val);
if (ret < 0) {
return ret;
}
if (val & PCA9422_BIT_VIN_OK) {
*online = CHARGER_ONLINE_FIXED;
} else {
*online = CHARGER_ONLINE_OFFLINE;
}
return 0;
}
static int pca9422_charger_set_constant_charge_current(const struct device *dev,
uint32_t current_ua)
{
const struct charger_pca9422_config *const config = dev->config;
struct charger_pca9422_data *data = dev->data;
uint16_t idx;
uint8_t val;
uint8_t num_ranges;
int ret;
/* Lock mutex */
k_mutex_lock(&data->mutex, K_FOREVER);
/* Unlock charger control register */
val = PCA9422_CHARGER_UNLOCK;
ret = mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_0, val);
if (ret < 0) {
goto lock;
}
current_ua = CLAMP(current_ua, CURRENT_STEP_2P5MA_MIN_UA, CURRENT_STEP_5P0MA_MAX_UA);
if ((data->chg_current_step == CHG_CURRENT_STEP_2P5MA) &&
(current_ua > CURRENT_STEP_2P5MA_MAX_UA)) {
val = CHG_CURRENT_STEP_5P0MA;
val = FIELD_PREP(PCA9422_BIT_CHG_CURRENT_STEP, val);
ret = mfd_pca9422_reg_update_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_1,
PCA9422_BIT_CHG_CURRENT_STEP, val);
if (ret < 0) {
goto lock;
}
data->chg_current_step = CHG_CURRENT_STEP_5P0MA;
}
num_ranges = ARRAY_SIZE(i_fast_chg_ua_range);
ret = linear_range_group_get_win_index(i_fast_chg_ua_range, num_ranges, current_ua,
current_ua, &idx);
if (ret == -EINVAL) {
goto lock;
}
ret = mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_3, (uint8_t)idx);
if (ret == 0) {
data->i_fast_chg_ua = current_ua;
}
lock:
/* Lock charger control register */
val = PCA9422_CHARGER_LOCK;
(void)mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_0, val);
/* Unlock mutex */
k_mutex_unlock(&data->mutex);
return ret;
}
static int pca9422_charger_set_constant_charge_voltage(const struct device *dev,
uint32_t voltage_uv)
{
const struct charger_pca9422_config *const config = dev->config;
struct charger_pca9422_data *data = dev->data;
uint16_t idx;
uint8_t val;
uint8_t num_ranges;
int ret;
/* Lock mutex */
k_mutex_lock(&data->mutex, K_FOREVER);
/* Unlock charger control register */
val = PCA9422_CHARGER_UNLOCK;
ret = mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_0, val);
if (ret < 0) {
goto lock;
}
voltage_uv = CLAMP(voltage_uv, VBAT_REG_MIN_UV, VBAT_REG_MAX_UV);
num_ranges = ARRAY_SIZE(vbat_reg_uv_range);
ret = linear_range_group_get_win_index(vbat_reg_uv_range, num_ranges, voltage_uv,
voltage_uv, &idx);
if (ret == -EINVAL) {
goto lock;
}
ret = mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_2, (uint8_t)idx);
if (ret == 0) {
data->vbat_reg_uv = voltage_uv;
}
lock:
/* Lock charger control register */
val = PCA9422_CHARGER_LOCK;
(void)mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_0, val);
/* Unlock mutex */
k_mutex_unlock(&data->mutex);
return ret;
}
static int pca9422_charger_set_topoff_current(const struct device *dev, uint32_t current_ua)
{
const struct charger_pca9422_config *const config = dev->config;
struct charger_pca9422_data *data = dev->data;
uint8_t topoff_pct[4] = {25, 50, 75, 100}; /* 2.5%, 5.0%, 7.5%, 10.0% - permilliage */
uint16_t cur_pct;
uint8_t val;
int ret;
/* Lock mutex */
k_mutex_lock(&data->mutex, K_FOREVER);
/* Unlock charger control register */
val = PCA9422_CHARGER_UNLOCK;
ret = mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_0, val);
if (ret < 0) {
goto lock;
}
cur_pct = current_ua * 1000U / data->i_fast_chg_ua; /* 0.1% - permilliage */
data->i_topoff_sel = 4;
for (int i = 0; i < 4; i++) {
if (cur_pct <= topoff_pct[i]) {
data->i_topoff_sel = i;
break;
}
}
/* Topoff current */
val = FIELD_PREP(PCA9422_BIT_TOP_OFF_CURRENT, data->i_topoff_sel);
ret = mfd_pca9422_reg_update_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_4,
PCA9422_BIT_TOP_OFF_CURRENT, val);
lock:
/* Lock charger control register */
val = PCA9422_CHARGER_LOCK;
(void)mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_0, val);
/* Unlock mutex */
k_mutex_unlock(&data->mutex);
return ret;
}
static int pca9422_charger_set_vsys_regulation_voltage(const struct device *dev,
uint32_t voltage_uv)
{
const struct charger_pca9422_config *const config = dev->config;
uint16_t idx;
uint8_t num_ranges;
int ret;
voltage_uv = CLAMP(voltage_uv, VSYS_REG_MIN_UV, VSYS_REG_MAX_UV);
num_ranges = ARRAY_SIZE(vsys_reg_uv_range);
ret = linear_range_group_get_win_index(vsys_reg_uv_range, num_ranges, voltage_uv,
voltage_uv, &idx);
if (ret == -EINVAL) {
return ret;
}
idx = FIELD_PREP(PCA9422_BIT_VSYS_REG, idx);
return mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_VIN_CNTL_3, (uint8_t)idx);
}
static int pca9422_charger_set_input_regulation_current(const struct device *dev,
uint32_t current_ua)
{
const struct charger_pca9422_config *const config = dev->config;
uint16_t idx;
uint8_t num_ranges;
int ret;
current_ua = CLAMP(current_ua, VIN_I_LIMIT_MIN_UA, VIN_I_LIMIT_MAX_UA);
num_ranges = ARRAY_SIZE(vin_i_limit_ua_range);
ret = linear_range_group_get_win_index(vin_i_limit_ua_range, num_ranges, current_ua,
current_ua, &idx);
if (ret == -EINVAL) {
return ret;
}
return mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_VIN_CNTL_2, (uint8_t)idx);
}
static int pca9422_charger_set_enabled(const struct device *dev, bool enable)
{
const struct charger_pca9422_config *const config = dev->config;
struct charger_pca9422_data *data = dev->data;
uint8_t val;
int ret;
/* Lock mutex */
k_mutex_lock(&data->mutex, K_FOREVER);
/* Unlock charger control register */
val = PCA9422_CHARGER_UNLOCK;
ret = mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_0, val);
if (ret < 0) {
goto lock;
}
data->charger_enabled = enable;
val = FIELD_PREP(PCA9422_BIT_CHARGER_EN, enable);
ret = mfd_pca9422_reg_update_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_1,
PCA9422_BIT_CHARGER_EN, val);
lock:
/* Lock charger control register */
val = PCA9422_CHARGER_LOCK;
(void)mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_0, val);
/* Unlock mutex */
k_mutex_unlock(&data->mutex);
return ret;
}
static int pca9422_charger_set_config(const struct device *dev)
{
const struct charger_pca9422_config *const config = dev->config;
struct charger_pca9422_data *data = dev->data;
uint8_t val;
int ret;
/* Input limit current */
ret = pca9422_charger_set_input_regulation_current(dev, config->vin_i_limit_ua);
if (ret < 0) {
return ret;
}
/* System regulation voltage */
ret = pca9422_charger_set_vsys_regulation_voltage(dev, config->vsys_reg_uv);
if (ret < 0) {
return ret;
}
/* Battery regulation voltage */
ret = pca9422_charger_set_constant_charge_voltage(dev, data->vbat_reg_uv);
if (ret < 0) {
return ret;
}
/* Fast charge current */
ret = pca9422_charger_set_constant_charge_current(dev, data->i_fast_chg_ua);
if (ret < 0) {
return ret;
}
/* Unlock charger control register */
val = PCA9422_CHARGER_UNLOCK;
ret = mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_0, val);
if (ret < 0) {
goto lock;
}
/* Precharge current */
val = FIELD_PREP(PCA9422_BIT_PRECHG_CURRENT, data->i_prechg_sel);
ret = mfd_pca9422_reg_update_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_1,
PCA9422_BIT_PRECHG_CURRENT, val);
if (ret < 0) {
goto lock;
}
/* Topoff current */
val = FIELD_PREP(PCA9422_BIT_TOP_OFF_CURRENT, data->i_topoff_sel);
ret = mfd_pca9422_reg_update_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_4,
PCA9422_BIT_TOP_OFF_CURRENT, val);
if (ret < 0) {
goto lock;
}
/* Recharge threshold */
val = FIELD_PREP(PCA9422_BIT_RECHARGE_TH, data->recharge_th_sel);
ret = mfd_pca9422_reg_update_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_4,
PCA9422_BIT_RECHARGE_TH, val);
lock:
/* Lock charger control register */
val = PCA9422_CHARGER_LOCK;
(void)mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_CHARGER_CNTL_0, val);
return ret;
}
static int pca9422_charger_get_prop(const struct device *dev, charger_prop_t prop,
union charger_propval *val)
{
struct charger_pca9422_data *data = dev->data;
uint8_t precharge_pct[2] = {7, 16}; /* 7%, 16% */
uint8_t topoff_pct[4] = {25, 50, 75, 100}; /* 2.5%, 5.0%, 7.5%, 10.0% - permilliage */
int ret;
switch (prop) {
case CHARGER_PROP_ONLINE:
val->online = data->online;
return 0;
case CHARGER_PROP_STATUS:
ret = pca9422_charger_get_status(dev, &data->status);
if (ret < 0) {
LOG_ERR("Failed to read charger status %d", ret);
} else {
val->status = data->status;
}
return ret;
case CHARGER_PROP_CONSTANT_CHARGE_CURRENT_UA:
val->const_charge_current_ua = data->i_fast_chg_ua;
return 0;
case CHARGER_PROP_CONSTANT_CHARGE_VOLTAGE_UV:
val->const_charge_voltage_uv = data->vbat_reg_uv;
return 0;
case CHARGER_PROP_PRECHARGE_CURRENT_UA:
val->precharge_current_ua =
data->i_fast_chg_ua * precharge_pct[data->i_prechg_sel] / 100U;
return 0;
case CHARGER_PROP_CHARGE_TERM_CURRENT_UA:
val->charge_term_current_ua =
data->i_fast_chg_ua * topoff_pct[data->i_topoff_sel] / 1000U;
return 0;
default:
return -ENOTSUP;
}
return ret;
}
static int pca9422_charger_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 pca9422_charger_set_constant_charge_current(dev,
val->const_charge_current_ua);
case CHARGER_PROP_CONSTANT_CHARGE_VOLTAGE_UV:
return pca9422_charger_set_constant_charge_voltage(dev,
val->const_charge_voltage_uv);
case CHARGER_PROP_INPUT_REGULATION_CURRENT_UA:
return pca9422_charger_set_input_regulation_current(
dev, val->input_current_regulation_current_ua);
case CHARGER_PROP_CHARGE_TERM_CURRENT_UA:
return pca9422_charger_set_topoff_current(dev, val->charge_term_current_ua);
default:
return -ENOTSUP;
}
}
static void pca9422_charger_isr(const struct device *dev)
{
const struct charger_pca9422_config *const config = dev->config;
struct charger_pca9422_data *data = dev->data;
uint8_t int_val[6];
uint8_t mask_val[6];
int ret;
/* Read charger interrupt register */
/* Read int_device interrupt 0, 1, int_charger 0, 1, 2, and 3 */
ret = mfd_pca9422_reg_burst_read(config->mfd, PCA9422_REG_INT_DEVICE_0, int_val, 6);
if (ret == 0) {
/* Check interrupt event */
LOG_DBG("%s: int_device[0]=0x%x, [1]=0x%x\n", __func__, int_val[0], int_val[1]);
LOG_DBG("%s: int_charger[0]=0x%x, [1]=0x%x, [2]=0x%x, [3]=0x%x\n", __func__,
int_val[2], int_val[3], int_val[4], int_val[5]);
} else {
LOG_ERR("%s: INT_DEVICE_0 ~ INT_CHARGER_3 read fail(%d)\n", __func__, ret);
}
/* Read mask registers */
ret = mfd_pca9422_reg_burst_read(config->mfd, PCA9422_REG_INT_DEVICE_0_MASK, mask_val, 6);
if (ret == 0) {
/* Check interrupt event */
LOG_DBG("%s: int_device_mask[0]=0x%x, [1]=0x%x\n", __func__, mask_val[0],
mask_val[1]);
LOG_DBG("%s: int_charger_mask[0]=0x%x, [1]=0x%x, [2]=0x%x, [3]=0x%x\n", __func__,
mask_val[2], mask_val[3], mask_val[4], mask_val[5]);
} else {
LOG_ERR("%s: INT_DEVICE_0_MASK ~ INT_CHARGER_3_MASK read fail(%d)\n", __func__,
ret);
}
/* Set event */
if ((int_val[0] & PCA9422_BIT_VIN_OK) && (~mask_val[0] & PCA9422_BIT_VIN_OK)) {
/* Check status register */
(void)pca9422_charger_get_online(dev, &data->online);
LOG_DBG("%s: VIN_OK INT - online=%d\n", __func__, data->online);
}
if ((int_val[0] & PCA9422_BIT_VIN_NOK) && (~mask_val[0] & PCA9422_BIT_VIN_NOK)) {
/* Check status register */
(void)pca9422_charger_get_online(dev, &data->online);
LOG_DBG("%s: VIN_NOK INT - online=%d\n", __func__, data->online);
}
}
static int pca9422_charger_init(const struct device *dev)
{
const struct charger_pca9422_config *const config = dev->config;
struct charger_pca9422_data *data = dev->data;
int ret;
uint8_t val;
uint8_t int_val[6];
k_mutex_init(&data->mutex);
if (!device_is_ready(config->mfd)) {
return -ENODEV;
}
ret = pca9422_charger_set_config(dev);
if (ret < 0) {
return ret;
}
/* Get initial properties */
ret = pca9422_charger_get_status(dev, &data->status);
if (ret < 0) {
return ret;
}
ret = pca9422_charger_get_online(dev, &data->online);
if (ret < 0) {
return ret;
}
/* Set interrupt handler */
mfd_pca9422_set_irqhandler(config->mfd, dev, PCA9422_DEV_CHG, pca9422_charger_isr);
/* Clear pending interrupt */
ret = mfd_pca9422_reg_burst_read(config->mfd, PCA9422_REG_INT_DEVICE_0, int_val, 6);
if (ret < 0) {
return ret;
}
/* Set interrupt mask register */
val = (uint8_t)~(PCA9422_BIT_VIN_OK | PCA9422_BIT_VIN_NOK);
return mfd_pca9422_reg_write_byte(config->mfd, PCA9422_REG_INT_DEVICE_0_MASK, val);
}
static DEVICE_API(charger, pca9422_charger_driver_api) = {
.get_property = pca9422_charger_get_prop,
.set_property = pca9422_charger_set_prop,
.charge_enable = pca9422_charger_set_enabled,
};
#define CHARGER_PCA9422_DEFINE(inst) \
static struct charger_pca9422_data charger_pca9422_data_##inst = { \
.i_fast_chg_ua = DT_INST_PROP(inst, constant_charge_current_max_microamp), \
.vbat_reg_uv = DT_INST_PROP(inst, constant_charge_voltage_max_microvolt), \
.recharge_th_sel = DT_INST_ENUM_IDX(inst, re_charge_threshold_microvolt), \
.i_prechg_sel = DT_INST_ENUM_IDX(inst, precharge_current_percent), \
.i_topoff_sel = DT_INST_ENUM_IDX(inst, charge_termination_current_percent), \
}; \
static const struct charger_pca9422_config charger_pca9422_config_##inst = { \
.mfd = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
.vin_i_limit_ua = DT_INST_PROP(inst, input_current_limit_microamp), \
.vsys_reg_uv = DT_INST_PROP(inst, system_voltage_min_threshold_microvolt), \
}; \
\
DEVICE_DT_INST_DEFINE(inst, &pca9422_charger_init, NULL, &charger_pca9422_data_##inst, \
&charger_pca9422_config_##inst, POST_KERNEL, \
CONFIG_CHARGER_INIT_PRIORITY, &pca9422_charger_driver_api);
DT_INST_FOREACH_STATUS_OKAY(CHARGER_PCA9422_DEFINE)