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& ...
随机推荐
- Calendar 中getActualMaximumd 功能
String str = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS")) .format(new Date()); Calend ...
- DataGridView在Cell编辑状态响应回车键下的KeyPress/KeyDown/KeyUp事件
我们知道由于DataGridView的单元格DataGridCell处于编辑的时候,当你按Enter键,那么DataGridView是不会激发KewPress/KeyDown/KeyUp这些事件的,因 ...
- mysql 5.6 grant授权的时候出现问题
mysql> grant select on huamu_licai.* to 'read'@'%' identified by password 'Abcd1234';ERROR 1827 ( ...
- android获取对话框文本注意事项
1.View注意设置成final类型如final View layout=.. . 2.获取文本框对象时候格式EditText e = (EditText)layout.findViewById(R. ...
- Disable Oracle Automatic Jobs
By default, Oracle will run some maintance jobs every night. If you don't want to run those jobs, yo ...
- C#特性Attribute学习
起初一直纠结于如何调用特性附着在下面那个成员的值,后来发现不需要调用,通过反射加载的时候是自动绑定上去的,即 获得成员对象之后,有一个方法可以获得特性标签. 其实从类库提供者,和类库使用者的角度,分开 ...
- [na]wireshark添加显示ip.id列
wireshark添加ip.id字段 为了在多个设备上追踪同一个数据包. 如果是同一个会话,则可以计算延迟, 如sta和应用服务器慢,这种问题,可以根据这个加上ip.id追踪数据到哪里慢了.
- 错误 1 error LNK2019: 无法解析的外部符号 "public: __thiscall Distance::Distance(int)" (??0Distance@@QAE@H@Z),该符号在函数 _main 中被引用
错误: 错误 1 error LNK2019: 无法解析的外部符号 "public: __thiscall Distance::Distance(int)" (??0Distanc ...
- 技术blog链接
http://www.cnblogs.com/anrainie/ 蔡羽 基础知识漫谈 http://blog.csdn.net/ioio_jy 姜晔的技术专栏 从苏宁电器到卡巴斯基
- 关于django模型里面的__str__和__unicode
简而言之,就是__str__和__unicode__都是为了再管理站点中加载这个表时想显示什么属性,当然一般都是显示一个name,大体来讲是通用的.下面是抄的csdn上面的一篇文章. str()是Py ...