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

posted @ 2017-01-13 18:02  SuperTao1024  阅读(2643)  评论(0编辑  收藏  举报