feat(drivers): 添加IP5306 PMIC驱动支持
- 添加IP5306 PMIC驱动实现,包括I2C通信和GPIO唤醒功能 - 实现电源管理芯片的状态读取接口(充电状态、满电状态) - 集成Wakeup保持脉冲功能,支持可配置的脉冲宽度和间隔时间 - 添加设备树绑定文件和Kconfig配置选项 refactor(blinky): 集成IP5306电源管理芯片到电池模块 - 在电池模块中集成IP5306 PMIC状态监控功能 - 修改日志输出格式,显示电池电压及充电/满电状态 - 增加设备初始化检查和错误处理机制 - 配置电源管理限制级别为暂停模式 build: 配置CMakeLists.txt以包含驱动子目录 - 更新主CMakeLists.txt文件添加drivers子目录 - 配置驱动程序的构建层次结构(pmic -> ip5306) - 设置条件编译目标源文件 docs: 添加设备树和板级配置支持 - 添加mini_keyboard板的I2C引脚控制配置 - 配置IP5306设备节点和相关GPIO引脚定义 - 启用I2C配置选项以支持PMIC通信
This commit is contained in:
1
drivers/CMakeLists.txt
Normal file
1
drivers/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
add_subdirectory(pmic)
|
||||
1
drivers/Kconfig
Normal file
1
drivers/Kconfig
Normal file
@@ -0,0 +1 @@
|
||||
rsource "pmic/Kconfig"
|
||||
1
drivers/pmic/CMakeLists.txt
Normal file
1
drivers/pmic/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
add_subdirectory(ip5306)
|
||||
1
drivers/pmic/Kconfig
Normal file
1
drivers/pmic/Kconfig
Normal file
@@ -0,0 +1 @@
|
||||
rsource "ip5306/Kconfig"
|
||||
1
drivers/pmic/ip5306/CMakeLists.txt
Normal file
1
drivers/pmic/ip5306/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
target_sources_ifdef(CONFIG_IP5306 app PRIVATE ip5306.c)
|
||||
11
drivers/pmic/ip5306/Kconfig
Normal file
11
drivers/pmic/ip5306/Kconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
menu "PMIC Drivers"
|
||||
|
||||
config IP5306
|
||||
bool "IP5306 PMIC driver"
|
||||
default y
|
||||
depends on I2C
|
||||
depends on DT_HAS_INJOINIC_IP5306_ENABLED
|
||||
help
|
||||
Enable the out-of-tree IP5306 PMIC driver.
|
||||
|
||||
endmenu
|
||||
196
drivers/pmic/ip5306/ip5306.c
Normal file
196
drivers/pmic/ip5306/ip5306.c
Normal file
@@ -0,0 +1,196 @@
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#include <drivers/pmic/ip5306.h>
|
||||
|
||||
#define DT_DRV_COMPAT injoinic_ip5306
|
||||
|
||||
LOG_MODULE_REGISTER(ip5306, LOG_LEVEL_INF);
|
||||
|
||||
#define IP5306_REG_READ0 0x70
|
||||
#define IP5306_REG_READ1 0x71
|
||||
#define IP5306_CHARGING_BIT BIT(3)
|
||||
#define IP5306_FULL_BIT BIT(3)
|
||||
|
||||
struct ip5306_config {
|
||||
struct i2c_dt_spec bus;
|
||||
struct gpio_dt_spec wakeup_gpio;
|
||||
uint32_t keepalive_interval_ms;
|
||||
uint32_t keepalive_pulse_width_ms;
|
||||
};
|
||||
|
||||
struct ip5306_data {
|
||||
const struct device *dev;
|
||||
struct k_work_delayable keepalive_work;
|
||||
bool started;
|
||||
bool pulse_active;
|
||||
};
|
||||
|
||||
static uint32_t keepalive_low_delay_ms(const struct ip5306_config *config)
|
||||
{
|
||||
if (config->keepalive_interval_ms > config->keepalive_pulse_width_ms) {
|
||||
return config->keepalive_interval_ms -
|
||||
config->keepalive_pulse_width_ms;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void keepalive_work_handler(struct k_work *work)
|
||||
{
|
||||
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
|
||||
struct ip5306_data *data =
|
||||
CONTAINER_OF(dwork, struct ip5306_data, keepalive_work);
|
||||
const struct device *dev = data->dev;
|
||||
const struct ip5306_config *config = dev->config;
|
||||
int err;
|
||||
|
||||
if (!data->started) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data->pulse_active) {
|
||||
err = gpio_pin_set_dt(&config->wakeup_gpio, 1);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to assert wakeup pulse (%d)", err);
|
||||
}
|
||||
|
||||
data->pulse_active = true;
|
||||
k_work_reschedule(&data->keepalive_work,
|
||||
K_MSEC(config->keepalive_pulse_width_ms));
|
||||
return;
|
||||
}
|
||||
|
||||
err = gpio_pin_set_dt(&config->wakeup_gpio, 0);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to deassert wakeup pulse (%d)", err);
|
||||
}
|
||||
|
||||
data->pulse_active = false;
|
||||
k_work_reschedule(&data->keepalive_work,
|
||||
K_MSEC(keepalive_low_delay_ms(config)));
|
||||
}
|
||||
|
||||
static int ip5306_init_api(const struct device *dev)
|
||||
{
|
||||
const struct ip5306_config *config = dev->config;
|
||||
struct ip5306_data *data = dev->data;
|
||||
uint8_t reg_val;
|
||||
int err;
|
||||
|
||||
if (data->started) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = i2c_reg_read_byte_dt(&config->bus, IP5306_REG_READ0, ®_val);
|
||||
if (err) {
|
||||
LOG_ERR("IP5306 probe failed (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
data->started = true;
|
||||
data->pulse_active = false;
|
||||
k_work_reschedule(&data->keepalive_work,
|
||||
K_MSEC(keepalive_low_delay_ms(config)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ip5306_get_status_api(const struct device *dev,
|
||||
struct ip5306_status *status)
|
||||
{
|
||||
const struct ip5306_config *config = dev->config;
|
||||
uint8_t read0;
|
||||
uint8_t read1;
|
||||
int err;
|
||||
|
||||
if (status == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = i2c_reg_read_byte_dt(&config->bus, IP5306_REG_READ0, &read0);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = i2c_reg_read_byte_dt(&config->bus, IP5306_REG_READ1, &read1);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
status->charging = (read0 & IP5306_CHARGING_BIT) != 0U;
|
||||
status->full = (read1 & IP5306_FULL_BIT) != 0U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ip5306_dev_init(const struct device *dev)
|
||||
{
|
||||
const struct ip5306_config *config = dev->config;
|
||||
struct ip5306_data *data = dev->data;
|
||||
|
||||
if (!i2c_is_ready_dt(&config->bus)) {
|
||||
LOG_ERR("I2C bus not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!gpio_is_ready_dt(&config->wakeup_gpio)) {
|
||||
LOG_ERR("Wakeup GPIO not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (config->keepalive_pulse_width_ms == 0U) {
|
||||
LOG_ERR("Invalid keepalive pulse width");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (config->keepalive_interval_ms == 0U) {
|
||||
LOG_ERR("Invalid keepalive interval");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (config->keepalive_pulse_width_ms > config->keepalive_interval_ms) {
|
||||
LOG_ERR("Pulse width cannot exceed interval");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->dev = dev;
|
||||
data->started = false;
|
||||
data->pulse_active = false;
|
||||
|
||||
k_work_init_delayable(&data->keepalive_work, keepalive_work_handler);
|
||||
|
||||
return gpio_pin_configure_dt(&config->wakeup_gpio, GPIO_OUTPUT_INACTIVE);
|
||||
}
|
||||
|
||||
static const struct ip5306_driver_api ip5306_driver_api = {
|
||||
.init = ip5306_init_api,
|
||||
.get_status = ip5306_get_status_api,
|
||||
};
|
||||
|
||||
#define IP5306_DEFINE(inst) \
|
||||
static struct ip5306_data ip5306_data_##inst; \
|
||||
\
|
||||
static const struct ip5306_config ip5306_config_##inst = { \
|
||||
.bus = I2C_DT_SPEC_INST_GET(inst), \
|
||||
.wakeup_gpio = GPIO_DT_SPEC_INST_GET(inst, wakeup_gpios), \
|
||||
.keepalive_interval_ms = \
|
||||
DT_INST_PROP(inst, keepalive_interval_ms), \
|
||||
.keepalive_pulse_width_ms = \
|
||||
DT_INST_PROP(inst, keepalive_pulse_width_ms), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, ip5306_dev_init, NULL, \
|
||||
&ip5306_data_##inst, &ip5306_config_##inst, \
|
||||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
|
||||
&ip5306_driver_api)
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(IP5306_DEFINE)
|
||||
Reference in New Issue
Block a user