#include #include #include #include #include #include #include #include "ip5306_priv.h" #if IS_ENABLED(CONFIG_IP5306_KEEPALIVE_HW_NRF) #include #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)