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& ...
 
随机推荐
- 敏捷转型中why与how的总结
			
敏捷转型參考框架: 为了成功顺畅地推行敏捷开发.下面将对整个敏捷转型參考框架作个整体说明.为企业进行敏捷转型提供基本方法參考.整个敏捷转型參考框架主要包括5个步骤,前两个步骤主要是回答 Wh y的问题 ...
 - Python degrees() 函数
			
描述 degrees() 将弧度转换为角度. 语法 以下是 degrees() 方法的语法: import math math.degrees(x) 注意:degrees()是不能直接访问的,需要导入 ...
 - tomcat支持https的历程
			
tomcat真是业界良心啊,文档写的详细无比. 一.https是什么? 简单的说,就是http+SSL/TLS 协议还是http,但是在传输层过程中使用了加密(涉及握手.秘钥分发.加密.解密等过程). ...
 - 转 docker 部署 kafka
			
原文链接 http://blog.csdn.net/snowcity1231/article/details/54946857 -e KAFKA_BROKER_ID=1 -e ZK=zk -p 909 ...
 - ubuntu为python处理图片安装图片数据增强库imgaug
			
1 依赖Required dependencies: six numpy scipy scikit-image (pip install -U scikit-image) OpenCV (i.e. c ...
 - Linux常用命令的解释
			
作者博客已转移,http://www.daniubiji.cn/archives/25 Linux简介及Ubuntu安装 常见指令 系统管理命令 打包压缩相关命令 关机/重启机器 Linux管道 Li ...
 - cocos2dx 3.3 getParentToNodeTransform bug
			
cocos2dx 3.3中getParentToNodeTransform实现如下: const Mat4& Node::getParentToNodeTransform() const { ...
 - Netty(六):Netty中的连接管理(心跳机制和定时断线重连)
			
何为心跳 顾名思义, 所谓心跳, 即在TCP长连接中, 客户端和服务器之间定期发送的一种特殊的数据包, 通知对方自己还在线, 以确保 TCP 连接的有效性. 为什么需要心跳 因为网络的不可靠性, 有可 ...
 - jQuery $.extend()使用方法
			
$.extend()使用方法总结. jQuery为开发插件提拱了两个方法,各自是: jQuery.fn.extend(object); jQuery.extend(object); jQuery.ex ...
 - [na]vrrp两用(网关冗余+服务器热备)
			
VRRP的两种用途 早上想了想vrrp的使用,1,网关冗余 2,服务器热备 思想稍微有点不一样.主要在于监控口 服务器的话有心跳线,用户同步一些配置和迁移一些服务.达到热备的目的.:牵涉到四个优先级: ...