Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iio: adc: add ltc2309 support

The LTC2309 is an 8-Channel, 12-Bit SAR ADC with an I2C Interface.

This implements support for all single-ended and differential channels,
in unipolar mode only.

Signed-off-by: Liam Beguin <liambeguin@gmail.com>
Link: https://lore.kernel.org/r/20230828-ltc2309-v3-2-338b3a8fab8b@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Liam Beguin and committed by
Jonathan Cameron
733e0fed 0679ea0b

+257
+10
drivers/iio/adc/Kconfig
··· 607 607 activate only one via device tree selection. Provides direct access 608 608 via sysfs. 609 609 610 + config LTC2309 611 + tristate "Linear Technology LTC2309 ADC driver" 612 + depends on I2C 613 + help 614 + Say yes here to build support for Linear Technology LTC2309, a low 615 + noise, low power, 8-channel, 12-bit SAR ADC 616 + 617 + This driver can also be built as a module. If so, the module will 618 + be called ltc2309. 619 + 610 620 config LTC2471 611 621 tristate "Linear Technology LTC2471 and LTC2473 ADC driver" 612 622 depends on I2C
+1
drivers/iio/adc/Makefile
··· 56 56 obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o 57 57 obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o 58 58 obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o 59 + obj-$(CONFIG_LTC2309) += ltc2309.o 59 60 obj-$(CONFIG_LTC2471) += ltc2471.o 60 61 obj-$(CONFIG_LTC2485) += ltc2485.o 61 62 obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o
+246
drivers/iio/adc/ltc2309.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * The LTC2309 is an 8-Channel, 12-Bit SAR ADC with an I2C Interface. 4 + * 5 + * Datasheet: 6 + * https://www.analog.com/media/en/technical-documentation/data-sheets/2309fd.pdf 7 + * 8 + * Copyright (c) 2023, Liam Beguin <liambeguin@gmail.com> 9 + */ 10 + #include <linux/bitfield.h> 11 + #include <linux/i2c.h> 12 + #include <linux/iio/iio.h> 13 + #include <linux/kernel.h> 14 + #include <linux/module.h> 15 + #include <linux/mutex.h> 16 + #include <linux/regulator/consumer.h> 17 + 18 + #define LTC2309_ADC_RESOLUTION 12 19 + 20 + #define LTC2309_DIN_CH_MASK GENMASK(7, 4) 21 + #define LTC2309_DIN_SDN BIT(7) 22 + #define LTC2309_DIN_OSN BIT(6) 23 + #define LTC2309_DIN_S1 BIT(5) 24 + #define LTC2309_DIN_S0 BIT(4) 25 + #define LTC2309_DIN_UNI BIT(3) 26 + #define LTC2309_DIN_SLEEP BIT(2) 27 + 28 + /** 29 + * struct ltc2309 - internal device data structure 30 + * @dev: Device reference 31 + * @client: I2C reference 32 + * @vref: External reference source 33 + * @lock: Lock to serialize data access 34 + * @vref_mv: Internal voltage reference 35 + */ 36 + struct ltc2309 { 37 + struct device *dev; 38 + struct i2c_client *client; 39 + struct regulator *vref; 40 + struct mutex lock; /* serialize data access */ 41 + int vref_mv; 42 + }; 43 + 44 + /* Order matches expected channel address, See datasheet Table 1. */ 45 + enum ltc2309_channels { 46 + LTC2309_CH0_CH1 = 0, 47 + LTC2309_CH2_CH3, 48 + LTC2309_CH4_CH5, 49 + LTC2309_CH6_CH7, 50 + LTC2309_CH1_CH0, 51 + LTC2309_CH3_CH2, 52 + LTC2309_CH5_CH4, 53 + LTC2309_CH7_CH6, 54 + LTC2309_CH0, 55 + LTC2309_CH2, 56 + LTC2309_CH4, 57 + LTC2309_CH6, 58 + LTC2309_CH1, 59 + LTC2309_CH3, 60 + LTC2309_CH5, 61 + LTC2309_CH7, 62 + }; 63 + 64 + #define LTC2309_CHAN(_chan, _addr) { \ 65 + .type = IIO_VOLTAGE, \ 66 + .indexed = 1, \ 67 + .address = _addr, \ 68 + .channel = _chan, \ 69 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 70 + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 71 + } 72 + 73 + #define LTC2309_DIFF_CHAN(_chan, _chan2, _addr) { \ 74 + .type = IIO_VOLTAGE, \ 75 + .differential = 1, \ 76 + .indexed = 1, \ 77 + .address = _addr, \ 78 + .channel = _chan, \ 79 + .channel2 = _chan2, \ 80 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 81 + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 82 + } 83 + 84 + static const struct iio_chan_spec ltc2309_channels[] = { 85 + LTC2309_CHAN(0, LTC2309_CH0), 86 + LTC2309_CHAN(1, LTC2309_CH1), 87 + LTC2309_CHAN(2, LTC2309_CH2), 88 + LTC2309_CHAN(3, LTC2309_CH3), 89 + LTC2309_CHAN(4, LTC2309_CH4), 90 + LTC2309_CHAN(5, LTC2309_CH5), 91 + LTC2309_CHAN(6, LTC2309_CH6), 92 + LTC2309_CHAN(7, LTC2309_CH7), 93 + LTC2309_DIFF_CHAN(0, 1, LTC2309_CH0_CH1), 94 + LTC2309_DIFF_CHAN(2, 3, LTC2309_CH2_CH3), 95 + LTC2309_DIFF_CHAN(4, 5, LTC2309_CH4_CH5), 96 + LTC2309_DIFF_CHAN(6, 7, LTC2309_CH6_CH7), 97 + LTC2309_DIFF_CHAN(1, 0, LTC2309_CH1_CH0), 98 + LTC2309_DIFF_CHAN(3, 2, LTC2309_CH3_CH2), 99 + LTC2309_DIFF_CHAN(5, 4, LTC2309_CH5_CH4), 100 + LTC2309_DIFF_CHAN(7, 6, LTC2309_CH7_CH6), 101 + }; 102 + 103 + static int ltc2309_read_raw_channel(struct ltc2309 *ltc2309, 104 + unsigned long address, int *val) 105 + { 106 + int ret; 107 + u16 buf; 108 + u8 din; 109 + 110 + din = FIELD_PREP(LTC2309_DIN_CH_MASK, address & 0x0f) | 111 + FIELD_PREP(LTC2309_DIN_UNI, 1) | 112 + FIELD_PREP(LTC2309_DIN_SLEEP, 0); 113 + 114 + ret = i2c_smbus_write_byte(ltc2309->client, din); 115 + if (ret < 0) { 116 + dev_err(ltc2309->dev, "i2c command failed: %pe\n", 117 + ERR_PTR(ret)); 118 + return ret; 119 + } 120 + 121 + ret = i2c_master_recv(ltc2309->client, (char *)&buf, 2); 122 + if (ret < 0) { 123 + dev_err(ltc2309->dev, "i2c read failed: %pe\n", ERR_PTR(ret)); 124 + return ret; 125 + } 126 + 127 + *val = be16_to_cpu(buf) >> 4; 128 + 129 + return ret; 130 + } 131 + 132 + static int ltc2309_read_raw(struct iio_dev *indio_dev, 133 + struct iio_chan_spec const *chan, int *val, 134 + int *val2, long mask) 135 + { 136 + struct ltc2309 *ltc2309 = iio_priv(indio_dev); 137 + int ret; 138 + 139 + switch (mask) { 140 + case IIO_CHAN_INFO_RAW: 141 + mutex_lock(&ltc2309->lock); 142 + ret = ltc2309_read_raw_channel(ltc2309, chan->address, val); 143 + mutex_unlock(&ltc2309->lock); 144 + if (ret < 0) 145 + return -EINVAL; 146 + return IIO_VAL_INT; 147 + case IIO_CHAN_INFO_SCALE: 148 + *val = ltc2309->vref_mv; 149 + *val2 = LTC2309_ADC_RESOLUTION; 150 + return IIO_VAL_FRACTIONAL_LOG2; 151 + default: 152 + return -EINVAL; 153 + } 154 + } 155 + 156 + static const struct iio_info ltc2309_info = { 157 + .read_raw = ltc2309_read_raw, 158 + }; 159 + 160 + static void ltc2309_regulator_disable(void *regulator) 161 + { 162 + regulator_disable(regulator); 163 + } 164 + 165 + static int ltc2309_probe(struct i2c_client *client) 166 + { 167 + struct iio_dev *indio_dev; 168 + struct ltc2309 *ltc2309; 169 + int ret; 170 + 171 + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*ltc2309)); 172 + if (!indio_dev) 173 + return -ENOMEM; 174 + 175 + ltc2309 = iio_priv(indio_dev); 176 + ltc2309->dev = &indio_dev->dev; 177 + ltc2309->client = client; 178 + ltc2309->vref_mv = 4096; /* Default to the internal ref */ 179 + 180 + indio_dev->name = "ltc2309"; 181 + indio_dev->modes = INDIO_DIRECT_MODE; 182 + indio_dev->channels = ltc2309_channels; 183 + indio_dev->num_channels = ARRAY_SIZE(ltc2309_channels); 184 + indio_dev->info = &ltc2309_info; 185 + 186 + ltc2309->vref = devm_regulator_get_optional(&client->dev, "vref"); 187 + if (IS_ERR(ltc2309->vref)) { 188 + ret = PTR_ERR(ltc2309->vref); 189 + if (ret == -ENODEV) 190 + ltc2309->vref = NULL; 191 + else 192 + return ret; 193 + } 194 + 195 + if (ltc2309->vref) { 196 + ret = regulator_enable(ltc2309->vref); 197 + if (ret) 198 + return dev_err_probe(ltc2309->dev, ret, 199 + "failed to enable vref\n"); 200 + 201 + ret = devm_add_action_or_reset(ltc2309->dev, 202 + ltc2309_regulator_disable, 203 + ltc2309->vref); 204 + if (ret) { 205 + return dev_err_probe(ltc2309->dev, ret, 206 + "failed to add regulator_disable action: %d\n", 207 + ret); 208 + } 209 + 210 + ret = regulator_get_voltage(ltc2309->vref); 211 + if (ret < 0) 212 + return ret; 213 + 214 + ltc2309->vref_mv = ret / 1000; 215 + } 216 + 217 + mutex_init(&ltc2309->lock); 218 + 219 + return devm_iio_device_register(&client->dev, indio_dev); 220 + } 221 + 222 + static const struct of_device_id ltc2309_of_match[] = { 223 + { .compatible = "lltc,ltc2309" }, 224 + { } 225 + }; 226 + MODULE_DEVICE_TABLE(of, ltc2309_of_match); 227 + 228 + static const struct i2c_device_id ltc2309_id[] = { 229 + { "ltc2309" }, 230 + { } 231 + }; 232 + MODULE_DEVICE_TABLE(i2c, ltc2309_id); 233 + 234 + static struct i2c_driver ltc2309_driver = { 235 + .driver = { 236 + .name = "ltc2309", 237 + .of_match_table = ltc2309_of_match, 238 + }, 239 + .probe = ltc2309_probe, 240 + .id_table = ltc2309_id, 241 + }; 242 + module_i2c_driver(ltc2309_driver); 243 + 244 + MODULE_AUTHOR("Liam Beguin <liambeguin@gmail.com>"); 245 + MODULE_DESCRIPTION("Linear Technology LTC2309 ADC"); 246 + MODULE_LICENSE("GPL v2");