本文记录阅读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 驱动解读的更多相关文章

  1. Linux ad7606 驱动

    Linux中已经移植好了ad7606,位于driver/staging/iio/adc/目录中.只要在板级文件中添加device中即可. 移植参考文档: https://wiki.analog.com ...

  2. Linux网络驱动--snull

    snull是<Linux Device Drivers>中的一个网络驱动的例子.这里引用这个例子学习Linux网络驱动. 因为snull的源码,网上已经更新到适合最新内核,而我自己用的还是 ...

  3. 浅谈Android系统移植、Linux设备驱动

    一.Android系统架构 第一层:Linux内核 包括驱动程序,管理内存.进程.电源等资源的程序 第二层:C/C++代码库 包括Linux的.so文件以及嵌入到APK程序中的NDK代码 第三层:An ...

  4. Linux设备驱动模型之I2C总线

    一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,提供了与具体硬件无关的I2C读写函数. (2)I2 ...

  5. linux设备驱动概述,王明学learn

    linux设备驱动学习-1 本章节主要学习有操作系统的设备驱动和无操作系统设备驱动的区别,以及对操作系统和设备驱动关系的认识. 一.设备驱动的作用 对设备驱动最通俗的解释就是“驱使硬件设备行动” .设 ...

  6. Smart210学习记录------linux串口驱动

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27025492&id=327609 一.核心数据结构 串口驱动有 ...

  7. linux网卡驱动移植

    这里重要的是物理层PHY receiver,MAC(media access control)层,这里与软件中的协议栈不同,在硬件上MAC是PHY的下一层.DM9000A将MAC和PHY做到一起,也可 ...

  8. Linux USB驱动

    linux usb 驱动详解 一 http://blog.163.com/cl2006ky@126/blog/static/87195173201131245557340/ USB设备驱动开发-USB ...

  9. 我就是认真:Linux SWAP 深度解读(必须收藏)

    我就是认真:Linux SWAP 深度解读(必须收藏) http://mp.weixin.qq.com/s?__biz=MzA4Nzg5Nzc5OA==&mid=2651660097& ...

随机推荐

  1. Java并发和多线程:序

      近期,和不少公司的"大牛"聊了聊,当中非常多是关于"并发和多线程"."系统架构"."分布式"等方面内容的.不少问题, ...

  2. /usr/local/ssl/lib/libssl.a: error adding symbols: Bad value

    一.背景 编译第三方库A的时候提示依赖openssl库. 二.编译openssl库 去官网下载最新版本的openssl-1.0.2l.tar.gztar -zxvf openssl-1.0.2l.ta ...

  3. node 服务器开发必备神器 —— nodemon

    nodemon 官方网站:http://nodemon.io/ github地址:https://github.com/remy/nodemon/ 简介:Nodemon 是一款非常实用的工具,用来监控 ...

  4. mysql (已解决p)MYSQL5.7启动不了,本地计算机上的 MySQL57 服务启动后停止。

    找到目录E:\AppServ\MySQL\data 备份data中的数据,然后删除掉data中所有的东西(如果删除不掉请在进程中找到mysqld.exe并且关闭) 打开CMD cd E:\AppSer ...

  5. session和cookie的联系

    前提: 一.cookie机制 正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie.然而纯粹的客户端脚本如J ...

  6. &lt;图形图像,动画,多媒体&gt; 读书笔记 --- AirPlay

    AirPlay技术是之前一直没有接触过的技术,正好这次做一个笔记 共用: 1.能够通过AirPlay将iOS和MAC设备上的视频或音频输出到高清电视上或高保真音响 2.能够通过AirPlay将iOS和 ...

  7. nginx location URI匹配规则

    当nginx收到一个请求后,会截取请求的url部份,去搜索所有location指令中定义的URI匹配模式.在server模块中可以定义多个location指令来匹配不同的url请求,多个不同的loca ...

  8. eclipse中maven项目部署到tomcat [转]

    其实maven项目部署到tomcat的方式很多,我从一开始的打war包到tomcat/webapps目录,到使用tomcat-maven插件,到直接使用servers部署,一路来走过很多弯路. 下面就 ...

  9. 基于jquery的邮箱输入联想插件开发

    js代码: /*create by code_bunny 20140701 973295131@qq.com https://github.com/OOP-Code-Bunny */ (functio ...

  10. c# 封装的7zip压缩 (全源码,不含任何类库)

    1,从soureforge下载sdk(包括汇编,c,c++,c#,java) 下载地址http://nchc.dl.sourceforge.net/project/sevenzip/LZMA%20SD ...