文章目录
IIO设备驱动源码路径
drivers/iio/adc/mt635x-auxadc.c ,每个设备的路径都不一样,这里以mt635x-auxadc为例做介绍。pmic是MT6330芯片,使用spmi通信。
设备树
&spmi_bus {
pmic: mt6330 {
compatible = "mediatek,mt6330";
reg = <0x4 SPMI_USID 0x1 SPMI_GSID>;
interrupts = <4>;
#address-cells = <1>;
#size-cells = <0>;
interrupt-controller;
#interrupt-cells = <2>;
mt-pmic {
compatible = "mediatek,mt63xx-debug";
};
pmic_auxadc: pmic_auxadc {
compatible = "mediatek,pmic-auxadc",
"mediatek,mt6330-auxadc";
#io-channel-cells = <1>;
batadc {
channel = <AUXADC_BATADC>;
resistance-ratio = <3 1>;
avg-num = <128>;
};
chip_temp {
channel = <AUXADC_CHIP_TEMP>;
};
vcore_temp {
channel = <AUXADC_VCORE_TEMP>;
};
vproc_temp {
channel = <AUXADC_VPROC_TEMP>;
};
vgpu_temp {
channel = <AUXADC_VGPU_TEMP>;
};
tsx_temp {
channel = <AUXADC_TSX_TEMP>;
avg-num = <128>;
};
dcxo_temp {
channel = <AUXADC_DCXO_TEMP>;
avg-num = <16>;
};
dcxo_ext0 {
channel = <AUXADC_TYPEL>;
avg-num = <128>;
};
dcxo_ext1 {
channel = <AUXADC_DRDI>;
avg-num = <128>;
};
};
...... 后面的类容省略
};
设备驱动
源码路径 drivers/iio/adc/mt635x-auxadc.c
static const struct of_device_id mt635x_auxadc_of_match[] = {
{
.compatible = "mediatek,mt6330-auxadc",
.data = &mt6330_info,
}
};
MODULE_DEVICE_TABLE(of, mt635x_auxadc_of_match);
static struct platform_driver mt635x_auxadc_driver = {
.driver = {
.name = "mt635x-auxadc",
.of_match_table = mt635x_auxadc_of_match,
},
.probe = mt635x_auxadc_probe,
};
module_platform_driver(mt635x_auxadc_driver);
驱动代码里面的compatible的值为"mediatek,mt6330-auxadc",与设备树中的compatible值相等,compatible匹配后执行mt635x_auxadc_probe函数。
static int mt635x_auxadc_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct mt635x_auxadc_device *adc_dev;
struct iio_dev *indio_dev;
struct mt6397_chip *chip;
int ret, imp_irq;
chip = dev_get_drvdata(pdev->dev.parent);
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
if (!indio_dev)
return -ENOMEM;
adc_dev = iio_priv(indio_dev);
adc_dev->regmap = chip->regmap;
adc_dev->dev = &pdev->dev;
mutex_init(&adc_dev->lock);
init_completion(&adc_dev->imp_done);
device_init_wakeup(&pdev->dev, true);
adc_dev->info = of_device_get_match_data(&pdev->dev);
if (adc_dev->info->imp_conv) {
imp_irq = platform_get_irq_byname(pdev, "imp");
if (imp_irq < 0) {
dev_notice(&pdev->dev, "failed to get IMP irq, ret=%d\n",
imp_irq);
return imp_irq;
}
ret = devm_request_threaded_irq(&pdev->dev, imp_irq, NULL, imp_isr,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"auxadc_imp", adc_dev);
if (ret) {
dev_notice(&pdev->dev,
"failed to request IMP irq, ret=%d\n", ret);
return ret;
}
}
ret = auxadc_parse_dt(adc_dev, node);
if (ret < 0) {
dev_notice(&pdev->dev, "auxadc_parse_dt fail, ret=%d\n", ret);
return ret;
}
auxadc_reset(adc_dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(&pdev->dev);
indio_dev->info = &mt635x_auxadc_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = adc_dev->iio_chans;
indio_dev->num_channels = adc_dev->nchannels;
ret = devm_iio_device_register(&pdev->dev, indio_dev);
if (ret < 0) {
dev_notice(&pdev->dev, "failed to register iio device!\n");
return ret;
}
dev_info(&pdev->dev, "%s done\n", __func__);
return 0;
}
第9行, chip = dev_get_drvdata(pdev->dev.parent),获取父节点的数据;
第15行, adc_dev->regmap = chip->regmap,获取父节点的regmap,regmap在另外一个文件中初始化,即drivers/mfd/mt6330-core.c的mt6330_probe()函数中,如下:
regmap = devm_regmap_init(&sdev->dev, &mt6330_regmap_bus,
pmic, &spmi_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
pmic->regmap = regmap;
mt6330_probe()函数暂时不深入了解,继续回到mt635x_auxadc_probe()函数。
第10行, devm_iio_device_alloc为indio_dev和adc_dev申请空间,adc_dev的地址在indio_dev的后面;
第14行, 通过函数iio_priv获取adc_dev的地址;
第14~19行, 初始化互斥锁、完成量、休眠唤醒功能;
第20~36行, 因为imp_conv没有定义,这里不涉及;
第38行, ret = auxadc_parse_dt(adc_dev, node)设备树解析,下面再分析;
第43行, auxadc_reset(adc_dev),通过spmi通信,复位pmic的ADC;
第45~50行, indio_dev赋值;
第52行, 调用iio_device_register函数,注册到IIO驱动框架里面去,具体怎么实现可以看Linux驱动开发 - iio子系统(1)
auxadc_parse_dt
mt635x_auxadc_probe函数中第49行indio_dev->channels = adc_dev->iio_chans,其中adc_dev->iio_chans就是在auxadc_parse_dt中解析的,
static int auxadc_parse_dt(struct mt635x_auxadc_device *adc_dev,
struct device_node *node)
{
struct iio_chan_spec *iio_chan;
struct device_node *child;
unsigned int channel = 0, index = 0;
int ret;
adc_dev->nchannels = of_get_available_child_count(node);
if (!adc_dev->nchannels)
return -EINVAL;
adc_dev->iio_chans = devm_kcalloc(adc_dev->dev, adc_dev->nchannels,
sizeof(*adc_dev->iio_chans), GFP_KERNEL);
if (!adc_dev->iio_chans)
return -ENOMEM;
iio_chan = adc_dev->iio_chans;
for_each_available_child_of_node(node, child) {
ret = auxadc_get_data_from_dt(adc_dev, &channel, child);
if (ret) {
of_node_put(child);
return ret;
}
if (auxadc_chans[channel].has_regs) {
auxadc_chans[channel].regs =
&adc_dev->info->regs_tbl[channel];
}
iio_chan->channel = channel;
iio_chan->datasheet_name = auxadc_chans[channel].ch_name;
iio_chan->extend_name = auxadc_chans[channel].ch_name;
iio_chan->info_mask_separate = auxadc_chans[channel].info_mask;
iio_chan->type = auxadc_chans[channel].type;
iio_chan->indexed = 1;
iio_chan->address = index++;
iio_chan++;
}
return 0;
}
第10行, adc_dev->nchannels = of_get_available_child_count(node),获取设备树的子节点数量,可以看到有9个子节点,即对应9个channel;
第14行, 给iio_chans申请空间;上一篇是定义的全局变量,Linux驱动开发 - iio子系统(2) ADC设备驱动;
第20~40行, 给iio_chans赋值和给auxadc_chans[channel].regs赋值;
通过spmi读取ADC
mt635x_auxadc_probe函数中第X行indio_dev->channels = adc_dev->iio_chans,其中adc_dev->iio_chans就是在auxadc_parse_dt中解析的,
static int mt635x_auxadc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long mask)
{
struct mt635x_auxadc_device *adc_dev = iio_priv(indio_dev);
const struct auxadc_channels *auxadc_chan;
int auxadc_out = 0;
int ret;
mutex_lock(&adc_dev->lock);
pm_stay_awake(adc_dev->dev);
auxadc_chan = &auxadc_chans[chan->channel];
switch (chan->channel) {
case AUXADC_IMP:
if (adc_dev->info->imp_conv)
ret = adc_dev->info->imp_conv(adc_dev,
&auxadc_out, val2);
else
ret = -EINVAL;
break;
case AUXADC_IMIX_R:
auxadc_out = adc_dev->imix_r;
ret = 0;
break;
default:
if (auxadc_chan->regs)
ret = get_auxadc_out(adc_dev, auxadc_chan,
&auxadc_out);
else
ret = -EINVAL;
break;
}
pm_relax(adc_dev->dev);
mutex_unlock(&adc_dev->lock);
if (ret != -ETIMEDOUT && ret < 0)
goto err;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
*val = auxadc_out * auxadc_chan->r_ratio[0] * VOLT_FULL;
*val = (*val / auxadc_chan->r_ratio[1]) >> auxadc_chan->res;
ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_RAW:
*val = auxadc_out;
ret = IIO_VAL_INT;
break;
default:
return -EINVAL;
}
if (chan->channel == AUXADC_IMP)
ret = IIO_VAL_INT_MULTIPLE;
err:
return ret;
}
static int mt635x_auxadc_of_xlate(struct iio_dev *indio_dev,
const struct of_phandle_args *iiospec)
{
int i;
for (i = 0; i < indio_dev->num_channels; i++) {
if (indio_dev->channels[i].channel == iiospec->args[0])
return i;
}
return -EINVAL;
}
static const struct iio_info mt635x_auxadc_info = {
.read_raw = &mt635x_auxadc_read_raw,
.of_xlate = &mt635x_auxadc_of_xlate,
};
第76行,设置read_raw回调函数,当终端执行cat in_voltage0时,将调用read_raw(),即调用mt6577_auxadc_read_raw()函数
第17~36行,不同的通道,获取ADC的方法不一样,常用的是通过default中的get_auxadc_out函数获取ADC。
第31行,get_auxadc_out函数通过spmic通信获取ADC的值,源码如下,regmap_write和regmap_read涉及Regmap子系统,这里不做详细介绍;
第43~55行,ADC的转换,我们使用的是IIO_CHAN_INFO_RAW,所以不用转换,至此,终端执行cat in_voltage0就会返回adc的电压值。
static int get_auxadc_out(struct mt635x_auxadc_device *adc_dev,
const struct auxadc_channels *auxadc_chan, int *val)
{
int ret;
regmap_write(adc_dev->regmap,
auxadc_chan->regs->rqst_reg,
BIT(auxadc_chan->regs->rqst_shift));
usleep_range(auxadc_chan->avg_num * AUXADC_AVG_TIME_US,
(auxadc_chan->avg_num + 1) * AUXADC_AVG_TIME_US);
if (adc_dev->info->attr & AUXADC_ATTR_BURST16)
ret = regmap_bulk_read_poll_timeout(adc_dev->regmap,
auxadc_chan->regs->out_reg,
*val,
(*val & AUXADC_RDY_BIT),
AUXADC_POLL_DELAY_US,
AUXADC_TIMEOUT_US);
else
ret = regmap_read_poll_timeout(adc_dev->regmap,
auxadc_chan->regs->out_reg,
*val,
(*val & AUXADC_RDY_BIT),
AUXADC_POLL_DELAY_US,
AUXADC_TIMEOUT_US);
*val &= BIT(auxadc_chan->res) - 1;
if (ret == -ETIMEDOUT)
dev_err(adc_dev->dev, "(%d)Time out!\n", auxadc_chan->ch_num);
return ret;
}