最近在做新项目的充电bringup,kernel 内核版本为5.4版本,使用到的充电电荷泵(charger pump)IC具备采样vbus vbat ibat ibus等功能,以往项目都是通过power_supply架构来实现采集adc数据,但是最新的kernel版本由于GKI的限制vendor无法在自行添加自定义的power_supply,所以需要修改原来项目采集adc数据的方法,所以采用linux正统的iio驱动来实现adc读取的功能,顺便学习梳理了一下iio驱动的使用方法。
iio子系统:Industrial I/O
一般用于具备ADC/DAC的器件提供驱动支持,使用范围比较广泛,比如温湿度传感器、陀螺仪、adc采样等等。软件驱动上提供sysfs(/sys/bus/iio/devices/)和dev(dev/iio:deviceX)节点供上层交互,内核空间提供接口给其他驱动使用。
iio子系统主要代码路径:
include/linux/iio/consumer.h
kernel/msm-4.19/drivers/iio/industrialio-core.c industrialio-buffer.c industrialio-trigger.c …
使用示例:
iio注册三部曲,申请、填充、注册
#include <linux/iio/iio.h>
#include <dt-bindings/iio/qti_power_supply_iio.h>
struct charger_pump_iio_channels {
const char *datasheet_name;
int channel_num;
enum iio_chan_type type;
long info_mask;
};
#define CP_IIO_CHAN(_name, _num, _type, _mask) \
{ .datasheet_name = _name, \
.channel_num = _num, \
.type = _type, \
.info_mask = _mask, },
#define CP_CHAN_VOLT(_name, _num) \
CP_IIO_CHAN(_name, _num, IIO_VOLTAGE, BIT(IIO_CHAN_INFO_PROCESSED))
#define CP_CHAN_CUR(_name, _num) \
CP_IIO_CHAN(_name, _num, IIO_CURRENT, BIT(IIO_CHAN_INFO_PROCESSED))
#define SC8551_CHAN_TEMP(_name, _num) \
CP_IIO_CHAN(_name, _num, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED))
static const struct charger_pump_iio_channels cp_iio_psy_channels[] = {
SC8551_CHAN_VOLT("sc_battery_voltage", PSY_IIO_SC_BATTERY_VOLTAGE)
SC8551_CHAN_CUR("sc_battery_current", PSY_IIO_SC_BATTERY_CURRENT)
SC8551_CHAN_TEMP("sc_battery_temperature", PSY_IIO_SC_BATTERY_TEMPERATURE)
......
};
提供读写的实际底层函数接口
static const struct iio_info sc_iio_info = {
.read_raw = sc_iio_read_raw,
.write_raw = sc_iio_write_raw,
.of_xlate = sc_iio_of_xlate,
};
struct iio_dev *indio_dev = chip->indio_dev;
struct iio_chan_spec *chan;
int num_iio_channels = ARRAY_SIZE(cp_iio_psy_channels);
1.分配内存
chip->iio_chan = devm_kcalloc(chip->dev, num_iio_channels, sizeof(*chip->iio_chan), GFP_KERNEL);
chip->int_iio_chans = devm_kcalloc(chip->dev, num_iio_channels, sizeof(*chip->int_iio_chans),GFP_KERNEL);
2.填充
indio_dev->name = "cp-standalone";
indio_dev->info = &sc_iio_info;
indio_dev->channels = chip->iio_chan;
indio_dev->num_channels = num_iio_channels;
for (i = 0; i < num_iio_channels; i++) {
chip->int_iio_chans[i].indio_dev = indio_dev;
chan = &chip->iio_chan[i];
chip->int_iio_chans[i].channel = chan;
chan->address = i;
chan->channel = sc8551_iio_psy_channels[i].channel_num;
chan->type = sc8551_iio_psy_channels[i].type;
chan->datasheet_name =sc8551_iio_psy_channels[i].datasheet_name;
chan->extend_name =sc8551_iio_psy_channels[i].datasheet_name;
chan->info_mask_separate =sc8551_iio_psy_channels[i].info_mask;
}
3.注册indio_dev
rc = devm_iio_device_register(chip->dev, indio_dev);
iio使用API:
ret = usbpd_get_iio_channel(pdpm, CP_MASTER, CHARGE_PUMP_SC_BATTERY_VOLTAGE, &val1);