cregit-Linux how code gets into the kernel

Release 4.11 drivers/iio/adc/ad7298.c

Directory: drivers/iio/adc
/*
 * AD7298 SPI ADC driver
 *
 * Copyright 2011 Analog Devices Inc.
 *
 * Licensed under the GPL-2.
 */

#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>

#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>

#include <linux/platform_data/ad7298.h>


#define AD7298_WRITE	BIT(15) 
/* write to the control register */

#define AD7298_REPEAT	BIT(14) 
/* repeated conversion enable */

#define AD7298_CH(x)	BIT(13 - (x)) 
/* channel select */

#define AD7298_TSENSE	BIT(5) 
/* temperature conversion enable */

#define AD7298_EXTREF	BIT(2) 
/* external reference enable */

#define AD7298_TAVG	BIT(1) 
/* temperature sensor averaging enable */

#define AD7298_PDD	BIT(0) 
/* partial power down enable */


#define AD7298_MAX_CHAN		8

#define AD7298_INTREF_mV	2500


#define AD7298_CH_TEMP		9


struct ad7298_state {
	
struct spi_device		*spi;
	
struct regulator		*reg;
	
unsigned			ext_ref;
	
struct spi_transfer		ring_xfer[10];
	
struct spi_transfer		scan_single_xfer[3];
	
struct spi_message		ring_msg;
	
struct spi_message		scan_single_msg;
	/*
         * DMA (thus cache coherency maintenance) requires the
         * transfer buffers to live in their own cache lines.
         */
	
__be16				rx_buf[12] ____cacheline_aligned;
	
__be16				tx_buf[2];
};


#define AD7298_V_CHAN(index)						\
	{                                                               \
                .type = IIO_VOLTAGE,                                    \
                .indexed = 1,                                           \
                .channel = index,                                       \
                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
                .address = index,                                       \
                .scan_index = index,                                    \
                .scan_type = {                                          \
                        .sign = 'u',                                    \
                        .realbits = 12,                                 \
                        .storagebits = 16,                              \
                        .endianness = IIO_BE,                           \
                },                                                      \
        }


static const struct iio_chan_spec ad7298_channels[] = {
	{
		.type = IIO_TEMP,
		.indexed = 1,
		.channel = 0,
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
			BIT(IIO_CHAN_INFO_SCALE) |
			BIT(IIO_CHAN_INFO_OFFSET),
		.address = AD7298_CH_TEMP,
		.scan_index = -1,
		.scan_type = {
			.sign = 's',
			.realbits = 32,
			.storagebits = 32,
                },
        },
	AD7298_V_CHAN(0),
	AD7298_V_CHAN(1),
	AD7298_V_CHAN(2),
	AD7298_V_CHAN(3),
	AD7298_V_CHAN(4),
	AD7298_V_CHAN(5),
	AD7298_V_CHAN(6),
	AD7298_V_CHAN(7),
	IIO_CHAN_SOFT_TIMESTAMP(8),
};

/**
 * ad7298_update_scan_mode() setup the spi transfer buffer for the new scan mask
 **/

static int ad7298_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *active_scan_mask) { struct ad7298_state *st = iio_priv(indio_dev); int i, m; unsigned short command; int scan_count; /* Now compute overall size */ scan_count = bitmap_weight(active_scan_mask, indio_dev->masklength); command = AD7298_WRITE | st->ext_ref; for (i = 0, m = AD7298_CH(0); i < AD7298_MAX_CHAN; i++, m >>= 1) if (test_bit(i, active_scan_mask)) command |= m; st->tx_buf[0] = cpu_to_be16(command); /* build spi ring message */ st->ring_xfer[0].tx_buf = &st->tx_buf[0]; st->ring_xfer[0].len = 2; st->ring_xfer[0].cs_change = 1; st->ring_xfer[1].tx_buf = &st->tx_buf[1]; st->ring_xfer[1].len = 2; st->ring_xfer[1].cs_change = 1; spi_message_init(&st->ring_msg); spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg); spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg); for (i = 0; i < scan_count; i++) { st->ring_xfer[i + 2].rx_buf = &st->rx_buf[i]; st->ring_xfer[i + 2].len = 2; st->ring_xfer[i + 2].cs_change = 1; spi_message_add_tail(&st->ring_xfer[i + 2], &st->ring_msg); } /* make sure last transfer cs_change is not set */ st->ring_xfer[i + 1].cs_change = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lars-Peter Clausen322100.00%1100.00%
Total322100.00%1100.00%

/** * ad7298_trigger_handler() bh of trigger launched polling to ring buffer * * Currently there is no option in this driver to disable the saving of * timestamps within the ring. **/
static irqreturn_t ad7298_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct ad7298_state *st = iio_priv(indio_dev); int b_sent; b_sent = spi_sync(st->spi, &st->ring_msg); if (b_sent) goto done; iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Lars-Peter Clausen8696.63%266.67%
Grégor Boirie33.37%133.33%
Total89100.00%3100.00%


static int ad7298_scan_direct(struct ad7298_state *st, unsigned ch) { int ret; st->tx_buf[0] = cpu_to_be16(AD7298_WRITE | st->ext_ref | (AD7298_CH(0) >> ch)); ret = spi_sync(st->spi, &st->scan_single_msg); if (ret) return ret; return be16_to_cpu(st->rx_buf[0]); }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich74100.00%2100.00%
Total74100.00%2100.00%


static int ad7298_scan_temp(struct ad7298_state *st, int *val) { int ret; __be16 buf; buf = cpu_to_be16(AD7298_WRITE | AD7298_TSENSE | AD7298_TAVG | st->ext_ref); ret = spi_write(st->spi, (u8 *)&buf, 2); if (ret) return ret; buf = cpu_to_be16(0); ret = spi_write(st->spi, (u8 *)&buf, 2); if (ret) return ret; usleep_range(101, 1000); /* sleep > 100us */ ret = spi_read(st->spi, (u8 *)&buf, 2); if (ret) return ret; *val = sign_extend32(be16_to_cpu(buf), 11); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich12084.51%250.00%
Dan Carpenter128.45%125.00%
Lars-Peter Clausen107.04%125.00%
Total142100.00%4100.00%


static int ad7298_get_ref_voltage(struct ad7298_state *st) { int vref; if (st->ext_ref) { vref = regulator_get_voltage(st->reg); if (vref < 0) return vref; return vref / 1000; } else { return AD7298_INTREF_mV; } }

Contributors

PersonTokensPropCommitsCommitProp
Lars-Peter Clausen3976.47%133.33%
Michael Hennerich1223.53%266.67%
Total51100.00%3100.00%


static int ad7298_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long m) { int ret; struct ad7298_state *st = iio_priv(indio_dev); switch (m) { case IIO_CHAN_INFO_RAW: ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; if (chan->address == AD7298_CH_TEMP) ret = ad7298_scan_temp(st, val); else ret = ad7298_scan_direct(st, chan->address); iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; if (chan->address != AD7298_CH_TEMP) *val = ret & GENMASK(chan->scan_type.realbits - 1, 0); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_VOLTAGE: *val = ad7298_get_ref_voltage(st); *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; case IIO_TEMP: *val = ad7298_get_ref_voltage(st); *val2 = 10; return IIO_VAL_FRACTIONAL; default: return -EINVAL; } case IIO_CHAN_INFO_OFFSET: *val = 1093 - 2732500 / ad7298_get_ref_voltage(st); return IIO_VAL_INT; } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich14064.81%330.00%
Lars-Peter Clausen3616.67%220.00%
Jonathan Cameron2310.65%330.00%
Peter Meerwald-Stadler104.63%110.00%
Alison Schofield73.24%110.00%
Total216100.00%10100.00%

static const struct iio_info ad7298_info = { .read_raw = &ad7298_read_raw, .update_scan_mode = ad7298_update_scan_mode, .driver_module = THIS_MODULE, };
static int ad7298_probe(struct spi_device *spi) { struct ad7298_platform_data *pdata = spi->dev.platform_data; struct ad7298_state *st; struct iio_dev *indio_dev; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; st = iio_priv(indio_dev); if (pdata && pdata->ext_ref) st->ext_ref = AD7298_EXTREF; if (st->ext_ref) { st->reg = devm_regulator_get(&spi->dev, "vref"); if (IS_ERR(st->reg)) return PTR_ERR(st->reg); ret = regulator_enable(st->reg); if (ret) return ret; } spi_set_drvdata(spi, indio_dev); st->spi = spi; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->dev.parent = &spi->dev; indio_dev->dev.of_node = spi->dev.of_node; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ad7298_channels; indio_dev->num_channels = ARRAY_SIZE(ad7298_channels); indio_dev->info = &ad7298_info; /* Setup default message */ st->scan_single_xfer[0].tx_buf = &st->tx_buf[0]; st->scan_single_xfer[0].len = 2; st->scan_single_xfer[0].cs_change = 1; st->scan_single_xfer[1].tx_buf = &st->tx_buf[1]; st->scan_single_xfer[1].len = 2; st->scan_single_xfer[1].cs_change = 1; st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; st->scan_single_xfer[2].len = 2; spi_message_init(&st->scan_single_msg); spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg); spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg); spi_message_add_tail(&st->scan_single_xfer[2], &st->scan_single_msg); ret = iio_triggered_buffer_setup(indio_dev, NULL, &ad7298_trigger_handler, NULL); if (ret) goto error_disable_reg; ret = iio_device_register(indio_dev); if (ret) goto error_cleanup_ring; return 0; error_cleanup_ring: iio_triggered_buffer_cleanup(indio_dev); error_disable_reg: if (st->ext_ref) regulator_disable(st->reg); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich34278.08%327.27%
Lars-Peter Clausen419.36%327.27%
Jonathan Cameron245.48%327.27%
Sachin Kamat194.34%19.09%
Matthew Ranostay122.74%19.09%
Total438100.00%11100.00%


static int ad7298_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); struct ad7298_state *st = iio_priv(indio_dev); iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); if (st->ext_ref) regulator_disable(st->reg); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich5494.74%240.00%
Lars-Peter Clausen23.51%240.00%
Jonathan Cameron11.75%120.00%
Total57100.00%5100.00%

static const struct spi_device_id ad7298_id[] = { {"ad7298", 0}, {} }; MODULE_DEVICE_TABLE(spi, ad7298_id); static struct spi_driver ad7298_driver = { .driver = { .name = "ad7298", }, .probe = ad7298_probe, .remove = ad7298_remove, .id_table = ad7298_id, }; module_spi_driver(ad7298_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("Analog Devices AD7298 ADC"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Michael Hennerich89550.42%310.00%
Lars-Peter Clausen66137.24%930.00%
Jonathan Cameron1438.06%1136.67%
Peter Meerwald-Stadler201.13%13.33%
Sachin Kamat191.07%13.33%
Dan Carpenter120.68%13.33%
Matthew Ranostay120.68%13.33%
Alison Schofield70.39%13.33%
Paul Gortmaker30.17%13.33%
Grégor Boirie30.17%13.33%
Total1775100.00%30100.00%
Directory: drivers/iio/adc
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.