FILE
ti-adc161s626.c
File preview with syntax highlighting.
linux-6.19.14/drivers/iio/adc/ti-adc161s626.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.