cregit-Linux how code gets into the kernel

Release 4.11 drivers/iio/adc/ti-tlc4541.c

Directory: drivers/iio/adc
/*
 * TI tlc4541 ADC Driver
 *
 * Copyright (C) 2017 Phil Reid
 *
 * Datasheets can be found here:
 * http://www.ti.com/lit/gpn/tlc3541
 * http://www.ti.com/lit/gpn/tlc4541
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * The tlc4541 requires 24 clock cycles to start a transfer.
 * Conversion then takes 2.94us to complete before data is ready
 * Data is returned MSB first.
 */

#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/interrupt.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/kernel.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/sysfs.h>


struct tlc4541_state {
	
struct spi_device               *spi;
	
struct regulator                *reg;
	
struct spi_transfer             scan_single_xfer[3];
	
struct spi_message              scan_single_msg;

	/*
         * DMA (thus cache coherency maintenance) requires the
         * transfer buffers to live in their own cache lines.
         * 2 bytes data + 6 bytes padding + 8 bytes timestamp when
         * call iio_push_to_buffers_with_timestamp.
         */
	
__be16                          rx_buf[8] ____cacheline_aligned;
};


struct tlc4541_chip_info {
	
const struct iio_chan_spec *channels;
	
unsigned int num_channels;
};


enum tlc4541_id {
	
TLC3541,
	
TLC4541,
};


#define TLC4541_V_CHAN(bits, bitshift) {                              \
                .type = IIO_VOLTAGE,                                  \
                .info_mask_separate       = BIT(IIO_CHAN_INFO_RAW),   \
                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
                .scan_type = {                                        \
                        .sign = 'u',                                  \
                        .realbits = (bits),                           \
                        .storagebits = 16,                            \
                        .shift = (bitshift),                          \
                        .endianness = IIO_BE,                         \
                },                                                    \
        }


#define DECLARE_TLC4541_CHANNELS(name, bits, bitshift) \
const struct iio_chan_spec name ## _channels[] = { \
        TLC4541_V_CHAN(bits, bitshift), \
        IIO_CHAN_SOFT_TIMESTAMP(1), \
}

static DECLARE_TLC4541_CHANNELS(tlc3541, 14, 2);
static DECLARE_TLC4541_CHANNELS(tlc4541, 16, 0);


static const struct tlc4541_chip_info tlc4541_chip_info[] = {
	[TLC3541] = {
		.channels = tlc3541_channels,
		.num_channels = ARRAY_SIZE(tlc3541_channels),
        },
	[TLC4541] = {
		.channels = tlc4541_channels,
		.num_channels = ARRAY_SIZE(tlc4541_channels),
        },
};


static irqreturn_t tlc4541_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct tlc4541_state *st = iio_priv(indio_dev); int ret; ret = spi_sync(st->spi, &st->scan_single_msg); if (ret < 0) 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
Phil Reid91100.00%1100.00%
Total91100.00%1100.00%


static int tlc4541_get_range(struct tlc4541_state *st) { int vref; vref = regulator_get_voltage(st->reg); if (vref < 0) return vref; vref /= 1000; return vref; }

Contributors

PersonTokensPropCommitsCommitProp
Phil Reid39100.00%1100.00%
Total39100.00%1100.00%


static int tlc4541_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long m) { int ret = 0; struct tlc4541_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; ret = spi_sync(st->spi, &st->scan_single_msg); iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; *val = be16_to_cpu(st->rx_buf[0]); *val = *val >> chan->scan_type.shift; *val &= GENMASK(chan->scan_type.realbits - 1, 0); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: ret = tlc4541_get_range(st); if (ret < 0) return ret; *val = ret; *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Phil Reid178100.00%1100.00%
Total178100.00%1100.00%

static const struct iio_info tlc4541_info = { .read_raw = &tlc4541_read_raw, .driver_module = THIS_MODULE, };
static int tlc4541_probe(struct spi_device *spi) { struct tlc4541_state *st; struct iio_dev *indio_dev; const struct tlc4541_chip_info *info; int ret; int8_t device_init = 0; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; st = iio_priv(indio_dev); spi_set_drvdata(spi, indio_dev); st->spi = spi; info = &tlc4541_chip_info[spi_get_device_id(spi)->driver_data]; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->dev.parent = &spi->dev; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = info->channels; indio_dev->num_channels = info->num_channels; indio_dev->info = &tlc4541_info; /* perform reset */ spi_write(spi, &device_init, 1); /* Setup default message */ st->scan_single_xfer[0].rx_buf = &st->rx_buf[0]; st->scan_single_xfer[0].len = 3; st->scan_single_xfer[1].delay_usecs = 3; st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; st->scan_single_xfer[2].len = 2; spi_message_init_with_transfers(&st->scan_single_msg, st->scan_single_xfer, 3); 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; ret = iio_triggered_buffer_setup(indio_dev, NULL, &tlc4541_trigger_handler, NULL); if (ret) goto error_disable_reg; ret = iio_device_register(indio_dev); if (ret) goto error_cleanup_buffer; return 0; error_cleanup_buffer: iio_triggered_buffer_cleanup(indio_dev); error_disable_reg: regulator_disable(st->reg); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Phil Reid342100.00%1100.00%
Total342100.00%1100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
Phil Reid51100.00%1100.00%
Total51100.00%1100.00%

#ifdef CONFIG_OF static const struct of_device_id tlc4541_dt_ids[] = { { .compatible = "ti,tlc3541", }, { .compatible = "ti,tlc4541", }, {} }; MODULE_DEVICE_TABLE(of, tlc4541_dt_ids); #endif static const struct spi_device_id tlc4541_id[] = { {"tlc3541", TLC3541}, {"tlc4541", TLC4541}, {} }; MODULE_DEVICE_TABLE(spi, tlc4541_id); static struct spi_driver tlc4541_driver = { .driver = { .name = "tlc4541", .of_match_table = of_match_ptr(tlc4541_dt_ids), }, .probe = tlc4541_probe, .remove = tlc4541_remove, .id_table = tlc4541_id, }; module_spi_driver(tlc4541_driver); MODULE_AUTHOR("Phil Reid <preid@electromag.com.au>"); MODULE_DESCRIPTION("Texas Instruments TLC4541 ADC"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Phil Reid1038100.00%1100.00%
Total1038100.00%1100.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.