153 lines
4.6 KiB
C
153 lines
4.6 KiB
C
|
|
#include <errno.h>
|
|||
|
|
#include <stdbool.h>
|
|||
|
|
#include <stdint.h>
|
|||
|
|
|
|||
|
|
#include <zephyr/device.h>
|
|||
|
|
#include <zephyr/drivers/power/ip5306.h>
|
|||
|
|
#include <zephyr/logging/log.h>
|
|||
|
|
#include <zephyr/sys/util.h>
|
|||
|
|
|
|||
|
|
#include "ip5306_priv.h"
|
|||
|
|
|
|||
|
|
#if IS_ENABLED(CONFIG_IP5306_KEEPALIVE_HW_NRF)
|
|||
|
|
#include <soc_nrf_common.h>
|
|||
|
|
#define IP5306_CFG_KEEPALIVE_PSEL_INIT(inst) \
|
|||
|
|
.keepalive_psel = NRF_DT_GPIOS_TO_PSEL_OR(DT_DRV_INST(inst), keepalive_gpios, 0),
|
|||
|
|
#else
|
|||
|
|
#define IP5306_CFG_KEEPALIVE_PSEL_INIT(inst)
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
LOG_MODULE_REGISTER(ip5306, LOG_LEVEL_INF);
|
|||
|
|
|
|||
|
|
#define DT_DRV_COMPAT injoinic_ip5306
|
|||
|
|
|
|||
|
|
#define IP5306_REG_READ0 0x70
|
|||
|
|
#define IP5306_REG_READ1 0x71
|
|||
|
|
#define IP5306_STATUS_BIT BIT(3)
|
|||
|
|
|
|||
|
|
static int ip5306_read_reg(const struct device *dev, uint8_t reg, uint8_t *val)
|
|||
|
|
{
|
|||
|
|
const struct ip5306_config *cfg = dev->config;
|
|||
|
|
|
|||
|
|
return i2c_reg_read_byte_dt(&cfg->i2c, reg, val);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int ip5306_get_status_bit(const struct device *dev, uint8_t reg, bool *flag)
|
|||
|
|
{
|
|||
|
|
uint8_t value = 0U;
|
|||
|
|
int ret;
|
|||
|
|
|
|||
|
|
if (flag == NULL) {
|
|||
|
|
return -EINVAL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ret = ip5306_read_reg(dev, reg, &value);
|
|||
|
|
if (ret != 0) {
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
*flag = ((value & IP5306_STATUS_BIT) != 0U);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int ip5306_api_is_charging(const struct device *dev, bool *charging)
|
|||
|
|
{
|
|||
|
|
return ip5306_get_status_bit(dev, IP5306_REG_READ0, charging);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int ip5306_api_is_charge_full(const struct device *dev, bool *full)
|
|||
|
|
{
|
|||
|
|
return ip5306_get_status_bit(dev, IP5306_REG_READ1, full);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void ip5306_keepalive_start(const struct device *dev)
|
|||
|
|
{
|
|||
|
|
struct ip5306_data *data = dev->data;
|
|||
|
|
const struct ip5306_config *cfg = dev->config;
|
|||
|
|
|
|||
|
|
if (!cfg->has_keepalive_gpio || (data->keepalive_interval_ms == 0U)) {
|
|||
|
|
data->backend = IP5306_KEEPALIVE_BACKEND_NONE;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
* 选择策略:
|
|||
|
|
* 1) DTS 显式请求硬件 offload 时,先尝试硬件后端;
|
|||
|
|
* 2) 若硬件依赖不可用或资源被占用,则告警后回退软件后端。
|
|||
|
|
*/
|
|||
|
|
if (cfg->keepalive_offload) {
|
|||
|
|
#if IS_ENABLED(CONFIG_IP5306_KEEPALIVE_HW_NRF)
|
|||
|
|
int ret = ip5306_keepalive_hw_nrf_start(dev);
|
|||
|
|
|
|||
|
|
if (ret == 0) {
|
|||
|
|
LOG_INF("Keepalive backend=HW(nRF), pulse=%ums interval=%ums",
|
|||
|
|
data->keepalive_pulse_ms, data->keepalive_interval_ms);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
LOG_WRN("HW keepalive unavailable (%d), fallback to SW backend", ret);
|
|||
|
|
#else
|
|||
|
|
LOG_WRN("HW keepalive requested but HW backend is not built, fallback to SW backend");
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
ip5306_keepalive_sw_start(dev);
|
|||
|
|
LOG_INF("Keepalive backend=SW, pulse=%ums interval=%ums",
|
|||
|
|
data->keepalive_pulse_ms, data->keepalive_interval_ms);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static int ip5306_init(const struct device *dev)
|
|||
|
|
{
|
|||
|
|
const struct ip5306_config *cfg = dev->config;
|
|||
|
|
struct ip5306_data *data = dev->data;
|
|||
|
|
|
|||
|
|
if (!i2c_is_ready_dt(&cfg->i2c)) {
|
|||
|
|
return -ENODEV;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (cfg->has_keepalive_gpio) {
|
|||
|
|
if (!gpio_is_ready_dt(&cfg->keepalive_gpio)) {
|
|||
|
|
return -ENODEV;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (gpio_pin_configure_dt(&cfg->keepalive_gpio, GPIO_OUTPUT_INACTIVE) != 0) {
|
|||
|
|
return -EIO;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
data->keepalive_high = false;
|
|||
|
|
data->dev = dev;
|
|||
|
|
data->backend = IP5306_KEEPALIVE_BACKEND_NONE;
|
|||
|
|
data->keepalive_pulse_ms = (cfg->keepalive_pulse_ms != 0U) ?
|
|||
|
|
cfg->keepalive_pulse_ms : IP5306_KEEPALIVE_DEFAULT_PULSE_MS;
|
|||
|
|
data->keepalive_interval_ms = (cfg->keepalive_interval_ms != 0U) ?
|
|||
|
|
cfg->keepalive_interval_ms : IP5306_KEEPALIVE_DEFAULT_INTERVAL_MS;
|
|||
|
|
|
|||
|
|
ip5306_keepalive_start(dev);
|
|||
|
|
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static const struct ip5306_driver_api ip5306_api = {
|
|||
|
|
.is_charging = ip5306_api_is_charging,
|
|||
|
|
.is_charge_full = ip5306_api_is_charge_full,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
#define IP5306_DEFINE(inst) \
|
|||
|
|
static struct ip5306_data ip5306_data_##inst; \
|
|||
|
|
static const struct ip5306_config ip5306_cfg_##inst = { \
|
|||
|
|
.i2c = I2C_DT_SPEC_INST_GET(inst), \
|
|||
|
|
.keepalive_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, keepalive_gpios, {0}), \
|
|||
|
|
IP5306_CFG_KEEPALIVE_PSEL_INIT(inst) \
|
|||
|
|
.keepalive_pulse_ms = DT_INST_PROP(inst, keepalive_pulse_ms), \
|
|||
|
|
.keepalive_interval_ms = DT_INST_PROP(inst, keepalive_interval_ms), \
|
|||
|
|
.has_keepalive_gpio = DT_INST_NODE_HAS_PROP(inst, keepalive_gpios), \
|
|||
|
|
.keepalive_offload = DT_INST_PROP_OR(inst, keepalive_offload, false), \
|
|||
|
|
}; \
|
|||
|
|
DEVICE_DT_INST_DEFINE(inst, ip5306_init, NULL, &ip5306_data_##inst, \
|
|||
|
|
&ip5306_cfg_##inst, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
|||
|
|
&ip5306_api);
|
|||
|
|
|
|||
|
|
DT_INST_FOREACH_STATUS_OKAY(IP5306_DEFINE)
|