linux ad7606 驱动解读
本文记录阅读linux ad7606驱动的笔记。
主要文件
drivers/staging/iio/adc/ad7606_spi.c
drivers/staging/iio/adc/ad7606_core.c
drivers/staging/iio/adc/ad7606_ring.c
drivers/staging/iio/adc/ad7606_spi.c
static int __init ad7606_spi_init(void)
{
return spi_register_driver(&ad7606_driver);
}
module_init(ad7606_spi_init);
static struct spi_driver ad7606_driver = {
.driver = {
.name = "ad7606",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.pm = AD7606_SPI_PM_OPS,
},
.probe = ad7606_spi_probe,
.remove = __devexit_p(ad7606_spi_remove),
.id_table = ad7606_id,
};
static int __devinit ad7606_spi_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
indio_dev = ad7606_probe(&spi->dev, spi->irq, NULL, ------+
spi_get_device_id(spi)->driver_data, |
&ad7606_spi_bops); ---------+ |
| |
if (IS_ERR(indio_dev)) | |
return PTR_ERR(indio_dev); | |
| |
| |
spi_set_drvdata(spi, indio_dev); | |
| |
return 0; | |
} | |
V |
static const struct ad7606_bus_ops ad7606_spi_bops = { |
.read_block = ad7606_spi_read_block, |
}; |
|
drivers/staging/iio/adc/ad7606_core.c |
struct iio_dev *ad7606_probe(struct device *dev, int irq, <---+
void __iomem *base_address,
unsigned id,
const struct ad7606_bus_ops *bops)
{
struct ad7606_platform_data *pdata = dev->platform_data;
struct ad7606_state *st;
int ret, regdone = 0;
struct iio_dev *indio_dev = iio_allocate_device(sizeof(*st));
if (indio_dev == NULL) {
ret = -ENOMEM;
goto error_ret;
}
st = iio_priv(indio_dev);
st->dev = dev;
st->id = id;
st->irq = irq;
st->bops = bops;
st->base_address = base_address;
// 默认的模拟通道输入电压范围, 10V/5V
st->range = pdata->default_range == 10000 ? 10000 : 5000;
// 过采样率
ret = ad7606_oversampling_get_index(pdata->default_os);
if (ret < 0) {
dev_warn(dev, "oversampling %d is not supported\n",
pdata->default_os);
st->oversampling = 0;
} else {
st->oversampling = pdata->default_os;
}
st->reg = regulator_get(dev, "vcc");
if (!IS_ERR(st->reg)) {
ret = regulator_enable(st->reg);
if (ret)
goto error_put_reg;
}
st->pdata = pdata;
st->chip_info = &ad7606_chip_info_tbl[id]; -----------------------------+
|
indio_dev->dev.parent = dev; |
indio_dev->info = &ad7606_info; |
indio_dev->modes = INDIO_DIRECT_MODE; |
indio_dev->name = st->chip_info->name; |
indio_dev->channels = st->chip_info->channels; |
indio_dev->num_channels = st->chip_info->num_channels; |
|
init_waitqueue_head(&st->wq_data_avail); |
|
ret = ad7606_request_gpios(st); -------------------------------+ |
if (ret) | |
goto error_disable_reg; | |
| |
| |
ret = ad7606_reset(st); | |
if (ret) | |
dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n"); | |
// ad7606中断,我使用的是busy引脚作为中断输入。 | |
ret = request_irq(st->irq, ad7606_interrupt, -------+ | |
IRQF_TRIGGER_FALLING, st->chip_info->name, indio_dev); | | |
if (ret) | | |
goto error_free_gpios; | | |
| | |
ret = ad7606_register_ring_funcs_and_init(indio_dev); | | |
if (ret) | | |
goto error_free_irq; | | |
| | |
ret = iio_device_register(indio_dev); | | |
if (ret) | | |
goto error_free_irq; | | |
regdone = 1; | | |
| | |
ret = iio_ring_buffer_register_ex(indio_dev->ring, 0, | | |
indio_dev->channels, | | |
indio_dev->num_channels); | | |
if (ret) | | |
goto error_cleanup_ring; | | |
| | |
return indio_dev; | | |
| | |
error_cleanup_ring: | | |
ad7606_ring_cleanup(indio_dev); | | |
| | |
error_free_irq: | | |
free_irq(st->irq, indio_dev); | | |
| | |
error_free_gpios: | | |
ad7606_free_gpios(st); | | |
| | |
error_disable_reg: | | |
if (!IS_ERR(st->reg)) | | |
regulator_disable(st->reg); | | |
error_put_reg: | | |
if (!IS_ERR(st->reg)) | | |
regulator_put(st->reg); | | |
if (regdone) | | |
iio_device_unregister(indio_dev); | | |
else | | |
iio_free_device(indio_dev); | | |
error_ret: | | |
return ERR_PTR(ret); | | |
} | | |
| | |
// 中断处理函数 | | |
static irqreturn_t ad7606_interrupt(int irq, void *dev_id) <---+ | |
{ | |
struct iio_dev *indio_dev = dev_id; | |
struct ad7606_state *st = iio_priv(indio_dev); | |
| |
if (iio_ring_enabled(indio_dev)) { | |
if (!work_pending(&st->poll_work)) | |
schedule_work(&st->poll_work); | |
} else { | |
st->done = true; | |
// 唤醒中断 | |
wake_up_interruptible(&st->wq_data_avail); | |
} | |
| |
return IRQ_HANDLED; | |
}; | |
static int ad7606_request_gpios(struct ad7606_state *st) <----------+ |
{ |
struct gpio gpio_array[3] = { |
[0] = { |
.gpio = st->pdata->gpio_os0, |
.flags = GPIOF_DIR_OUT | ((st->oversampling & 1) ? |
GPIOF_INIT_HIGH : GPIOF_INIT_LOW), |
.label = "AD7606_OS0", |
}, |
[1] = { |
.gpio = st->pdata->gpio_os1, |
.flags = GPIOF_DIR_OUT | ((st->oversampling & 2) ? |
GPIOF_INIT_HIGH : GPIOF_INIT_LOW), |
.label = "AD7606_OS1", |
}, |
[2] = { |
.gpio = st->pdata->gpio_os2, |
.flags = GPIOF_DIR_OUT | ((st->oversampling & 4) ? |
GPIOF_INIT_HIGH : GPIOF_INIT_LOW), |
.label = "AD7606_OS2", |
}, |
}; |
int ret; |
|
ret = gpio_request_one(st->pdata->gpio_convst, GPIOF_OUT_INIT_LOW, |
"AD7606_CONVST"); |
if (ret) { |
dev_err(st->dev, "failed to request GPIO CONVST\n"); |
return ret; |
} |
|
ret = gpio_request_array(gpio_array, ARRAY_SIZE(gpio_array)); |
if (!ret) { |
st->have_os = true; |
} |
|
ret = gpio_request_one(st->pdata->gpio_reset, GPIOF_OUT_INIT_LOW, |
"AD7606_RESET"); |
if (!ret) |
st->have_reset = true; |
|
ret = gpio_request_one(st->pdata->gpio_range, GPIOF_DIR_OUT | |
((st->range == 10000) ? GPIOF_INIT_HIGH : |
GPIOF_INIT_LOW), "AD7606_RANGE"); |
if (!ret) |
st->have_range = true; |
|
ret = gpio_request_one(st->pdata->gpio_stby, GPIOF_OUT_INIT_HIGH, |
"AD7606_STBY"); |
if (!ret) |
st->have_stby = true; |
// 是否定义了gpio_frstdata,没有定义就是-1. |
// 我调试的时候这个信号有问题,所以就设置成-1.st->have_frstdata=false |
if (gpio_is_valid(st->pdata->gpio_frstdata)) { |
ret = gpio_request_one(st->pdata->gpio_frstdata, GPIOF_IN, |
"AD7606_FRSTDATA"); |
if (!ret) |
st->have_frstdata = true; |
} |
|
return 0; |
} |
|
static const struct ad7606_chip_info ad7606_chip_info_tbl[] = { <------+
/*
* More devices added in future
*/
[ID_AD7606_8] = {
.name = "ad7606",
.int_vref_mv = 2500,
.channels = ad7606_8_channels,
.num_channels = 8,
},
[ID_AD7606_6] = {
.name = "ad7606-6",
.int_vref_mv = 2500,
.channels = ad7606_6_channels,
.num_channels = 6,
},
[ID_AD7606_4] = {
.name = "ad7606-4",
.int_vref_mv = 2500,
.channels = ad7606_4_channels,
.num_channels = 4,
},
};
static int ad7606_oversampling_get_index(unsigned val)
{
unsigned char supported[] = {0, 2, 4, 8, 16, 32, 64};
int i;
for (i = 0; i < ARRAY_SIZE(supported); i++)
if (val == supported[i])
return i;
return -EINVAL;
}
//应用层读取的时候会调用这个函数。
static int ad7606_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long m)
{
int ret;
struct ad7606_state *st = iio_priv(indio_dev);
unsigned int scale_uv;
switch (m) {
case 0:
mutex_lock(&indio_dev->mlock);
if (iio_ring_enabled(indio_dev))
ret = ad7606_scan_from_ring(indio_dev, chan->address);
else
ret = ad7606_scan_direct(indio_dev, chan->address); ------+
mutex_unlock(&indio_dev->mlock); |
|
if (ret < 0) |
return ret; |
*val = (short) ret; |
return IIO_VAL_INT; |
case (1 << IIO_CHAN_INFO_SCALE_SHARED): |
scale_uv = (st->range * 1000 * 2) |
>> st->chip_info->channels[0].scan_type.realbits; |
*val = scale_uv / 1000; |
*val2 = (scale_uv % 1000) * 1000; |
return IIO_VAL_INT_PLUS_MICRO; |
} |
return -EINVAL; |
} |
|
static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned ch) <----+
{
struct ad7606_state *st = iio_priv(indio_dev);
int ret;
st->done = false;
gpio_set_value(st->pdata->gpio_convst, 1);
// 此处中断, 等待中断调用ad7606_interrupt函数唤醒中断。
ret = wait_event_interruptible(st->wq_data_avail, st->done);
if (ret)
goto error_ret;
// 判断have_frstdata是否为true
if (st->have_frstdata) {
ret = st->bops->read_block(st->dev, 1, st->data);
if (ret)
goto error_ret;
if (!gpio_get_value(st->pdata->gpio_frstdata)) {
/* This should never happen */
ad7606_reset(st);
ret = -EIO;
goto error_ret;
}
ret = st->bops->read_block(st->dev,
st->chip_info->num_channels - 1, &st->data[1]);
if (ret){
goto error_ret;
} else {
ret = st->bops->read_block(st->dev,
st->chip_info->num_channels, st->data);
if (ret)
goto error_ret;
}
ret = st->data[ch];
error_ret:
gpio_set_value(st->pdata->gpio_convst, 0);
return ret;
}
static int ad7606_spi_read_block(struct device *dev,
int count, void *buf)
{
struct spi_device *spi = to_spi_device(dev);
int i, ret;
unsigned short *data = buf;
ret = spi_read(spi, (u8 *)buf, count * 2);
if (ret < 0) {
dev_err(&spi->dev, "SPI read error\n");
return ret;
}
for (i = 0; i < count; i++) {
data[i] = be16_to_cpu(data[i]);
}
return 0;
}
include/linux/spi/spi.h
static inline int
spi_read(struct spi_device *spi, void *buf, size_t len)
{
struct spi_transfer t = {
.rx_buf = buf,
.len = len,
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spi_sync(spi, &m);
}
Tony Liu
2017-1-13, Shenzhen
linux ad7606 驱动解读的更多相关文章
- Linux ad7606 驱动
Linux中已经移植好了ad7606,位于driver/staging/iio/adc/目录中.只要在板级文件中添加device中即可. 移植参考文档: https://wiki.analog.com ...
- Linux网络驱动--snull
snull是<Linux Device Drivers>中的一个网络驱动的例子.这里引用这个例子学习Linux网络驱动. 因为snull的源码,网上已经更新到适合最新内核,而我自己用的还是 ...
- 浅谈Android系统移植、Linux设备驱动
一.Android系统架构 第一层:Linux内核 包括驱动程序,管理内存.进程.电源等资源的程序 第二层:C/C++代码库 包括Linux的.so文件以及嵌入到APK程序中的NDK代码 第三层:An ...
- Linux设备驱动模型之I2C总线
一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,提供了与具体硬件无关的I2C读写函数. (2)I2 ...
- linux设备驱动概述,王明学learn
linux设备驱动学习-1 本章节主要学习有操作系统的设备驱动和无操作系统设备驱动的区别,以及对操作系统和设备驱动关系的认识. 一.设备驱动的作用 对设备驱动最通俗的解释就是“驱使硬件设备行动” .设 ...
- Smart210学习记录------linux串口驱动
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27025492&id=327609 一.核心数据结构 串口驱动有 ...
- linux网卡驱动移植
这里重要的是物理层PHY receiver,MAC(media access control)层,这里与软件中的协议栈不同,在硬件上MAC是PHY的下一层.DM9000A将MAC和PHY做到一起,也可 ...
- Linux USB驱动
linux usb 驱动详解 一 http://blog.163.com/cl2006ky@126/blog/static/87195173201131245557340/ USB设备驱动开发-USB ...
- 我就是认真:Linux SWAP 深度解读(必须收藏)
我就是认真:Linux SWAP 深度解读(必须收藏) http://mp.weixin.qq.com/s?__biz=MzA4Nzg5Nzc5OA==&mid=2651660097& ...
随机推荐
- webpack-dev-server的自动更新配置
一.背景 测试发布一个项目,修改jsx中的内容,页面不自动更新. 每次必须执行npm run build:然后执行npm run start. 脚本如下: "scripts": { ...
- 修改 Input placeholder 的样式
::-webkit-input-placeholder { /* WebKit browsers */ color: #ccc; } :-moz-placeholder { /* Mozilla Fi ...
- activiti自己定义流程之Spring整合activiti-modeler实例(一):环境搭建
项目中须要整合activiti-modeler自己定义流程,找了非常多资料后,最终成功的跳转到activiti-modeler流程设计界面.下面是记录: 一.整合基础:eclipse4.4.1.tom ...
- Hypothesis Testing
Hypothesis Testing What's Hypothesis Testing(假设检验) Hypothesis testing is the statistical assessment ...
- 用TTTAttributedLabel创建变化丰富的UILabel
转自:http://blog.csdn.net/prevention/article/details/9998575 1. 不同颜色的字段混合在一个Label里怎么实现? 看TTTAttributed ...
- seajs加载jquery插件
假设有如下一个名为get-data的jq插件: (function($) { $.fn.getData= function() { console.log($(this).attr('data')) ...
- 临界区&Monitor
监视器(Monitor)的概念 可以在MSDN(http://msdn.microsoft.com/zh-cn/library/ms173179(VS.80).aspx)上找到下面一段话: 与lock ...
- js在IE8+兼容String没有trim方法,写一个兼容ie8一下的浏览器的trim()方法
方法一: String.prototype.trim = function(){ return Trim(this);}; function LTrim(str) { var i; fo ...
- LeetCode: Combination Sum II 解题报告
Combination Sum II Given a collection of candidate numbers (C) and a target number (T), find all uni ...
- Web攻防系列教程之文件上传攻防解析(转载)
Web攻防系列教程之文件上传攻防解析: 文件上传是WEB应用很常见的一种功能,本身是一项正常的业务需求,不存在什么问题.但如果在上传时没有对文件进行正确处理,则很可能会发生安全问题.本文将对文件上传的 ...