File Index
Files and directories
Sign in
FILE

ti-adc161s626.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/adc/ti-adc161s626.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC
  4 *
  5 * ADC Devices Supported:
  6 *  adc141s626 - 14-bit ADC
  7 *  adc161s626 - 16-bit ADC
  8 *
  9 * Copyright (C) 2016-2018
 10 * Author: Matt Ranostay <matt.ranostay@konsulko.com>
 11 */
 12
 13#include <linux/module.h>
 14#include <linux/mod_devicetable.h>
 15#include <linux/init.h>
 16#include <linux/err.h>
 17#include <linux/spi/spi.h>
 18#include <linux/unaligned.h>
 19#include <linux/iio/iio.h>
 20#include <linux/iio/trigger.h>
 21#include <linux/iio/buffer.h>
 22#include <linux/iio/trigger_consumer.h>
 23#include <linux/iio/triggered_buffer.h>
 24#include <linux/regulator/consumer.h>
 25
 26#define TI_ADC_DRV_NAME	"ti-adc161s626"
 27
 28enum {
 29	TI_ADC141S626,
 30	TI_ADC161S626,
 31};
 32
 33static const struct iio_chan_spec ti_adc141s626_channels[] = {
 34	{
 35		.type = IIO_VOLTAGE,
 36		.channel = 0,
 37		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 38				      BIT(IIO_CHAN_INFO_SCALE) |
 39				      BIT(IIO_CHAN_INFO_OFFSET),
 40		.scan_index = 0,
 41		.scan_type = {
 42			.sign = 's',
 43			.realbits = 14,
 44			.storagebits = 16,
 45		},
 46	},
 47	IIO_CHAN_SOFT_TIMESTAMP(1),
 48};
 49
 50static const struct iio_chan_spec ti_adc161s626_channels[] = {
 51	{
 52		.type = IIO_VOLTAGE,
 53		.channel = 0,
 54		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 55				      BIT(IIO_CHAN_INFO_SCALE) |
 56				      BIT(IIO_CHAN_INFO_OFFSET),
 57		.scan_index = 0,
 58		.scan_type = {
 59			.sign = 's',
 60			.realbits = 16,
 61			.storagebits = 16,
 62		},
 63	},
 64	IIO_CHAN_SOFT_TIMESTAMP(1),
 65};
 66
 67struct ti_adc_data {
 68	struct iio_dev *indio_dev;
 69	struct spi_device *spi;
 70	struct regulator *ref;
 71
 72	u8 read_size;
 73	u8 shift;
 74	u8 buf[3] __aligned(IIO_DMA_MINALIGN);
 75};
 76
 77static int ti_adc_read_measurement(struct ti_adc_data *data,
 78				   struct iio_chan_spec const *chan, int *val)
 79{
 80	int ret;
 81
 82	switch (data->read_size) {
 83	case 2:
 84		ret = spi_read(data->spi, data->buf, 2);
 85		if (ret)
 86			return ret;
 87
 88		*val = get_unaligned_be16(data->buf);
 89		break;
 90	case 3:
 91		ret = spi_read(data->spi, data->buf, 3);
 92		if (ret)
 93			return ret;
 94
 95		*val = get_unaligned_be24(data->buf);
 96		break;
 97	default:
 98		return -EINVAL;
 99	}
100
101	*val = sign_extend32(*val >> data->shift, chan->scan_type.realbits - 1);
102
103	return 0;
104}
105
106static irqreturn_t ti_adc_trigger_handler(int irq, void *private)
107{
108	struct iio_poll_func *pf = private;
109	struct iio_dev *indio_dev = pf->indio_dev;
110	struct ti_adc_data *data = iio_priv(indio_dev);
111	struct {
112		s16 data;
113		aligned_s64 timestamp;
114	} scan = { };
115	int ret, val;
116
117	ret = ti_adc_read_measurement(data, &indio_dev->channels[0], &val);
118	if (ret)
119		goto exit_notify_done;
120
121	scan.data = val;
122	iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev));
123
124 exit_notify_done:
125	iio_trigger_notify_done(indio_dev->trig);
126
127	return IRQ_HANDLED;
128}
129
130static int ti_adc_read_raw(struct iio_dev *indio_dev,
131			   struct iio_chan_spec const *chan,
132			   int *val, int *val2, long mask)
133{
134	struct ti_adc_data *data = iio_priv(indio_dev);
135	int ret;
136
137	switch (mask) {
138	case IIO_CHAN_INFO_RAW:
139		if (!iio_device_claim_direct(indio_dev))
140			return -EBUSY;
141		ret = ti_adc_read_measurement(data, chan, val);
142		iio_device_release_direct(indio_dev);
143		if (ret)
144			return ret;
145		return IIO_VAL_INT;
146	case IIO_CHAN_INFO_SCALE:
147		ret = regulator_get_voltage(data->ref);
148		if (ret < 0)
149			return ret;
150
151		*val = ret / 1000;
152		*val2 = chan->scan_type.realbits;
153
154		return IIO_VAL_FRACTIONAL_LOG2;
155	case IIO_CHAN_INFO_OFFSET:
156		*val = 1 << (chan->scan_type.realbits - 1);
157		return IIO_VAL_INT;
158	}
159
160	return 0;
161}
162
163static const struct iio_info ti_adc_info = {
164	.read_raw = ti_adc_read_raw,
165};
166
167static void ti_adc_reg_disable(void *reg)
168{
169	regulator_disable(reg);
170}
171
172static int ti_adc_probe(struct spi_device *spi)
173{
174	struct iio_dev *indio_dev;
175	struct ti_adc_data *data;
176	int ret;
177
178	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
179	if (!indio_dev)
180		return -ENOMEM;
181
182	indio_dev->info = &ti_adc_info;
183	indio_dev->name = TI_ADC_DRV_NAME;
184	indio_dev->modes = INDIO_DIRECT_MODE;
185
186	data = iio_priv(indio_dev);
187	data->spi = spi;
188
189	switch (spi_get_device_id(spi)->driver_data) {
190	case TI_ADC141S626:
191		indio_dev->channels = ti_adc141s626_channels;
192		indio_dev->num_channels = ARRAY_SIZE(ti_adc141s626_channels);
193		data->shift = 0;
194		data->read_size = 2;
195		break;
196	case TI_ADC161S626:
197		indio_dev->channels = ti_adc161s626_channels;
198		indio_dev->num_channels = ARRAY_SIZE(ti_adc161s626_channels);
199		data->shift = 6;
200		data->read_size = 3;
201		break;
202	}
203
204	data->ref = devm_regulator_get(&spi->dev, "vdda");
205	if (IS_ERR(data->ref))
206		return PTR_ERR(data->ref);
207
208	ret = regulator_enable(data->ref);
209	if (ret < 0)
210		return ret;
211
212	ret = devm_add_action_or_reset(&spi->dev, ti_adc_reg_disable,
213				       data->ref);
214	if (ret)
215		return ret;
216
217	ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
218					      ti_adc_trigger_handler, NULL);
219	if (ret)
220		return ret;
221
222	return devm_iio_device_register(&spi->dev, indio_dev);
223}
224
225static const struct of_device_id ti_adc_dt_ids[] = {
226	{ .compatible = "ti,adc141s626", },
227	{ .compatible = "ti,adc161s626", },
228	{ }
229};
230MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
231
232static const struct spi_device_id ti_adc_id[] = {
233	{ "adc141s626", TI_ADC141S626 },
234	{ "adc161s626", TI_ADC161S626 },
235	{ }
236};
237MODULE_DEVICE_TABLE(spi, ti_adc_id);
238
239static struct spi_driver ti_adc_driver = {
240	.driver = {
241		.name	= TI_ADC_DRV_NAME,
242		.of_match_table = ti_adc_dt_ids,
243	},
244	.probe		= ti_adc_probe,
245	.id_table	= ti_adc_id,
246};
247module_spi_driver(ti_adc_driver);
248
249MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
250MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC");
251MODULE_LICENSE("GPL");
~/ Read-only file preview.