1733e0fedSLiam Beguin // SPDX-License-Identifier: GPL-2.0 2733e0fedSLiam Beguin /* 3733e0fedSLiam Beguin * The LTC2309 is an 8-Channel, 12-Bit SAR ADC with an I2C Interface. 4733e0fedSLiam Beguin * 5733e0fedSLiam Beguin * Datasheet: 6733e0fedSLiam Beguin * https://www.analog.com/media/en/technical-documentation/data-sheets/2309fd.pdf 7733e0fedSLiam Beguin * 8733e0fedSLiam Beguin * Copyright (c) 2023, Liam Beguin <liambeguin@gmail.com> 9733e0fedSLiam Beguin */ 10733e0fedSLiam Beguin #include <linux/bitfield.h> 11733e0fedSLiam Beguin #include <linux/i2c.h> 12733e0fedSLiam Beguin #include <linux/iio/iio.h> 13733e0fedSLiam Beguin #include <linux/kernel.h> 14733e0fedSLiam Beguin #include <linux/module.h> 15733e0fedSLiam Beguin #include <linux/mutex.h> 16733e0fedSLiam Beguin #include <linux/regulator/consumer.h> 17733e0fedSLiam Beguin 18733e0fedSLiam Beguin #define LTC2309_ADC_RESOLUTION 12 19*890582c2SDavid Lechner #define LTC2309_INTERNAL_REF_MV 4096 20733e0fedSLiam Beguin 21733e0fedSLiam Beguin #define LTC2309_DIN_CH_MASK GENMASK(7, 4) 22733e0fedSLiam Beguin #define LTC2309_DIN_SDN BIT(7) 23733e0fedSLiam Beguin #define LTC2309_DIN_OSN BIT(6) 24733e0fedSLiam Beguin #define LTC2309_DIN_S1 BIT(5) 25733e0fedSLiam Beguin #define LTC2309_DIN_S0 BIT(4) 26733e0fedSLiam Beguin #define LTC2309_DIN_UNI BIT(3) 27733e0fedSLiam Beguin #define LTC2309_DIN_SLEEP BIT(2) 28733e0fedSLiam Beguin 29733e0fedSLiam Beguin /** 30733e0fedSLiam Beguin * struct ltc2309 - internal device data structure 31733e0fedSLiam Beguin * @dev: Device reference 32733e0fedSLiam Beguin * @client: I2C reference 33733e0fedSLiam Beguin * @lock: Lock to serialize data access 34733e0fedSLiam Beguin * @vref_mv: Internal voltage reference 35733e0fedSLiam Beguin */ 36733e0fedSLiam Beguin struct ltc2309 { 37733e0fedSLiam Beguin struct device *dev; 38733e0fedSLiam Beguin struct i2c_client *client; 39733e0fedSLiam Beguin struct mutex lock; /* serialize data access */ 40733e0fedSLiam Beguin int vref_mv; 41733e0fedSLiam Beguin }; 42733e0fedSLiam Beguin 43733e0fedSLiam Beguin /* Order matches expected channel address, See datasheet Table 1. */ 44733e0fedSLiam Beguin enum ltc2309_channels { 45733e0fedSLiam Beguin LTC2309_CH0_CH1 = 0, 46733e0fedSLiam Beguin LTC2309_CH2_CH3, 47733e0fedSLiam Beguin LTC2309_CH4_CH5, 48733e0fedSLiam Beguin LTC2309_CH6_CH7, 49733e0fedSLiam Beguin LTC2309_CH1_CH0, 50733e0fedSLiam Beguin LTC2309_CH3_CH2, 51733e0fedSLiam Beguin LTC2309_CH5_CH4, 52733e0fedSLiam Beguin LTC2309_CH7_CH6, 53733e0fedSLiam Beguin LTC2309_CH0, 54733e0fedSLiam Beguin LTC2309_CH2, 55733e0fedSLiam Beguin LTC2309_CH4, 56733e0fedSLiam Beguin LTC2309_CH6, 57733e0fedSLiam Beguin LTC2309_CH1, 58733e0fedSLiam Beguin LTC2309_CH3, 59733e0fedSLiam Beguin LTC2309_CH5, 60733e0fedSLiam Beguin LTC2309_CH7, 61733e0fedSLiam Beguin }; 62733e0fedSLiam Beguin 63733e0fedSLiam Beguin #define LTC2309_CHAN(_chan, _addr) { \ 64733e0fedSLiam Beguin .type = IIO_VOLTAGE, \ 65733e0fedSLiam Beguin .indexed = 1, \ 66733e0fedSLiam Beguin .address = _addr, \ 67733e0fedSLiam Beguin .channel = _chan, \ 68733e0fedSLiam Beguin .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 69733e0fedSLiam Beguin .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 70733e0fedSLiam Beguin } 71733e0fedSLiam Beguin 72733e0fedSLiam Beguin #define LTC2309_DIFF_CHAN(_chan, _chan2, _addr) { \ 73733e0fedSLiam Beguin .type = IIO_VOLTAGE, \ 74733e0fedSLiam Beguin .differential = 1, \ 75733e0fedSLiam Beguin .indexed = 1, \ 76733e0fedSLiam Beguin .address = _addr, \ 77733e0fedSLiam Beguin .channel = _chan, \ 78733e0fedSLiam Beguin .channel2 = _chan2, \ 79733e0fedSLiam Beguin .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 80733e0fedSLiam Beguin .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 81733e0fedSLiam Beguin } 82733e0fedSLiam Beguin 83733e0fedSLiam Beguin static const struct iio_chan_spec ltc2309_channels[] = { 84733e0fedSLiam Beguin LTC2309_CHAN(0, LTC2309_CH0), 85733e0fedSLiam Beguin LTC2309_CHAN(1, LTC2309_CH1), 86733e0fedSLiam Beguin LTC2309_CHAN(2, LTC2309_CH2), 87733e0fedSLiam Beguin LTC2309_CHAN(3, LTC2309_CH3), 88733e0fedSLiam Beguin LTC2309_CHAN(4, LTC2309_CH4), 89733e0fedSLiam Beguin LTC2309_CHAN(5, LTC2309_CH5), 90733e0fedSLiam Beguin LTC2309_CHAN(6, LTC2309_CH6), 91733e0fedSLiam Beguin LTC2309_CHAN(7, LTC2309_CH7), 92733e0fedSLiam Beguin LTC2309_DIFF_CHAN(0, 1, LTC2309_CH0_CH1), 93733e0fedSLiam Beguin LTC2309_DIFF_CHAN(2, 3, LTC2309_CH2_CH3), 94733e0fedSLiam Beguin LTC2309_DIFF_CHAN(4, 5, LTC2309_CH4_CH5), 95733e0fedSLiam Beguin LTC2309_DIFF_CHAN(6, 7, LTC2309_CH6_CH7), 96733e0fedSLiam Beguin LTC2309_DIFF_CHAN(1, 0, LTC2309_CH1_CH0), 97733e0fedSLiam Beguin LTC2309_DIFF_CHAN(3, 2, LTC2309_CH3_CH2), 98733e0fedSLiam Beguin LTC2309_DIFF_CHAN(5, 4, LTC2309_CH5_CH4), 99733e0fedSLiam Beguin LTC2309_DIFF_CHAN(7, 6, LTC2309_CH7_CH6), 100733e0fedSLiam Beguin }; 101733e0fedSLiam Beguin 102733e0fedSLiam Beguin static int ltc2309_read_raw_channel(struct ltc2309 *ltc2309, 103733e0fedSLiam Beguin unsigned long address, int *val) 104733e0fedSLiam Beguin { 105733e0fedSLiam Beguin int ret; 106733e0fedSLiam Beguin u16 buf; 107733e0fedSLiam Beguin u8 din; 108733e0fedSLiam Beguin 109733e0fedSLiam Beguin din = FIELD_PREP(LTC2309_DIN_CH_MASK, address & 0x0f) | 110733e0fedSLiam Beguin FIELD_PREP(LTC2309_DIN_UNI, 1) | 111733e0fedSLiam Beguin FIELD_PREP(LTC2309_DIN_SLEEP, 0); 112733e0fedSLiam Beguin 113733e0fedSLiam Beguin ret = i2c_smbus_write_byte(ltc2309->client, din); 114733e0fedSLiam Beguin if (ret < 0) { 115733e0fedSLiam Beguin dev_err(ltc2309->dev, "i2c command failed: %pe\n", 116733e0fedSLiam Beguin ERR_PTR(ret)); 117733e0fedSLiam Beguin return ret; 118733e0fedSLiam Beguin } 119733e0fedSLiam Beguin 120733e0fedSLiam Beguin ret = i2c_master_recv(ltc2309->client, (char *)&buf, 2); 121733e0fedSLiam Beguin if (ret < 0) { 122733e0fedSLiam Beguin dev_err(ltc2309->dev, "i2c read failed: %pe\n", ERR_PTR(ret)); 123733e0fedSLiam Beguin return ret; 124733e0fedSLiam Beguin } 125733e0fedSLiam Beguin 126733e0fedSLiam Beguin *val = be16_to_cpu(buf) >> 4; 127733e0fedSLiam Beguin 128733e0fedSLiam Beguin return ret; 129733e0fedSLiam Beguin } 130733e0fedSLiam Beguin 131733e0fedSLiam Beguin static int ltc2309_read_raw(struct iio_dev *indio_dev, 132733e0fedSLiam Beguin struct iio_chan_spec const *chan, int *val, 133733e0fedSLiam Beguin int *val2, long mask) 134733e0fedSLiam Beguin { 135733e0fedSLiam Beguin struct ltc2309 *ltc2309 = iio_priv(indio_dev); 136733e0fedSLiam Beguin int ret; 137733e0fedSLiam Beguin 138733e0fedSLiam Beguin switch (mask) { 139733e0fedSLiam Beguin case IIO_CHAN_INFO_RAW: 140733e0fedSLiam Beguin mutex_lock(<c2309->lock); 141733e0fedSLiam Beguin ret = ltc2309_read_raw_channel(ltc2309, chan->address, val); 142733e0fedSLiam Beguin mutex_unlock(<c2309->lock); 143733e0fedSLiam Beguin if (ret < 0) 144733e0fedSLiam Beguin return -EINVAL; 145733e0fedSLiam Beguin return IIO_VAL_INT; 146733e0fedSLiam Beguin case IIO_CHAN_INFO_SCALE: 147733e0fedSLiam Beguin *val = ltc2309->vref_mv; 148733e0fedSLiam Beguin *val2 = LTC2309_ADC_RESOLUTION; 149733e0fedSLiam Beguin return IIO_VAL_FRACTIONAL_LOG2; 150733e0fedSLiam Beguin default: 151733e0fedSLiam Beguin return -EINVAL; 152733e0fedSLiam Beguin } 153733e0fedSLiam Beguin } 154733e0fedSLiam Beguin 155733e0fedSLiam Beguin static const struct iio_info ltc2309_info = { 156733e0fedSLiam Beguin .read_raw = ltc2309_read_raw, 157733e0fedSLiam Beguin }; 158733e0fedSLiam Beguin 159733e0fedSLiam Beguin static int ltc2309_probe(struct i2c_client *client) 160733e0fedSLiam Beguin { 161733e0fedSLiam Beguin struct iio_dev *indio_dev; 162733e0fedSLiam Beguin struct ltc2309 *ltc2309; 163733e0fedSLiam Beguin int ret; 164733e0fedSLiam Beguin 165733e0fedSLiam Beguin indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*ltc2309)); 166733e0fedSLiam Beguin if (!indio_dev) 167733e0fedSLiam Beguin return -ENOMEM; 168733e0fedSLiam Beguin 169733e0fedSLiam Beguin ltc2309 = iio_priv(indio_dev); 170733e0fedSLiam Beguin ltc2309->dev = &indio_dev->dev; 171733e0fedSLiam Beguin ltc2309->client = client; 172733e0fedSLiam Beguin 173733e0fedSLiam Beguin indio_dev->name = "ltc2309"; 174733e0fedSLiam Beguin indio_dev->modes = INDIO_DIRECT_MODE; 175733e0fedSLiam Beguin indio_dev->channels = ltc2309_channels; 176733e0fedSLiam Beguin indio_dev->num_channels = ARRAY_SIZE(ltc2309_channels); 177733e0fedSLiam Beguin indio_dev->info = <c2309_info; 178733e0fedSLiam Beguin 179*890582c2SDavid Lechner ret = devm_regulator_get_enable_read_voltage(&client->dev, "vref"); 180*890582c2SDavid Lechner if (ret < 0 && ret != -ENODEV) 181733e0fedSLiam Beguin return dev_err_probe(ltc2309->dev, ret, 182*890582c2SDavid Lechner "failed to get vref voltage\n"); 183733e0fedSLiam Beguin 184*890582c2SDavid Lechner ltc2309->vref_mv = ret == -ENODEV ? LTC2309_INTERNAL_REF_MV : ret / 1000; 185733e0fedSLiam Beguin 186733e0fedSLiam Beguin mutex_init(<c2309->lock); 187733e0fedSLiam Beguin 188733e0fedSLiam Beguin return devm_iio_device_register(&client->dev, indio_dev); 189733e0fedSLiam Beguin } 190733e0fedSLiam Beguin 191733e0fedSLiam Beguin static const struct of_device_id ltc2309_of_match[] = { 192733e0fedSLiam Beguin { .compatible = "lltc,ltc2309" }, 193733e0fedSLiam Beguin { } 194733e0fedSLiam Beguin }; 195733e0fedSLiam Beguin MODULE_DEVICE_TABLE(of, ltc2309_of_match); 196733e0fedSLiam Beguin 197733e0fedSLiam Beguin static const struct i2c_device_id ltc2309_id[] = { 198733e0fedSLiam Beguin { "ltc2309" }, 199733e0fedSLiam Beguin { } 200733e0fedSLiam Beguin }; 201733e0fedSLiam Beguin MODULE_DEVICE_TABLE(i2c, ltc2309_id); 202733e0fedSLiam Beguin 203733e0fedSLiam Beguin static struct i2c_driver ltc2309_driver = { 204733e0fedSLiam Beguin .driver = { 205733e0fedSLiam Beguin .name = "ltc2309", 206733e0fedSLiam Beguin .of_match_table = ltc2309_of_match, 207733e0fedSLiam Beguin }, 208733e0fedSLiam Beguin .probe = ltc2309_probe, 209733e0fedSLiam Beguin .id_table = ltc2309_id, 210733e0fedSLiam Beguin }; 211733e0fedSLiam Beguin module_i2c_driver(ltc2309_driver); 212733e0fedSLiam Beguin 213733e0fedSLiam Beguin MODULE_AUTHOR("Liam Beguin <liambeguin@gmail.com>"); 214733e0fedSLiam Beguin MODULE_DESCRIPTION("Linear Technology LTC2309 ADC"); 215733e0fedSLiam Beguin MODULE_LICENSE("GPL v2"); 216