216 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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.pdf7 *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 1219#define LTC2309_INTERNAL_REF_MV 409620 21#define LTC2309_DIN_CH_MASK GENMASK(7, 4)22#define LTC2309_DIN_SDN BIT(7)23#define LTC2309_DIN_OSN BIT(6)24#define LTC2309_DIN_S1 BIT(5)25#define LTC2309_DIN_S0 BIT(4)26#define LTC2309_DIN_UNI BIT(3)27#define LTC2309_DIN_SLEEP BIT(2)28 29/**30 * struct ltc2309 - internal device data structure31 * @dev: Device reference32 * @client: I2C reference33 * @lock: Lock to serialize data access34 * @vref_mv: Internal voltage reference35 */36struct ltc2309 {37 struct device *dev;38 struct i2c_client *client;39 struct mutex lock; /* serialize data access */40 int vref_mv;41};42 43/* Order matches expected channel address, See datasheet Table 1. */44enum ltc2309_channels {45 LTC2309_CH0_CH1 = 0,46 LTC2309_CH2_CH3,47 LTC2309_CH4_CH5,48 LTC2309_CH6_CH7,49 LTC2309_CH1_CH0,50 LTC2309_CH3_CH2,51 LTC2309_CH5_CH4,52 LTC2309_CH7_CH6,53 LTC2309_CH0,54 LTC2309_CH2,55 LTC2309_CH4,56 LTC2309_CH6,57 LTC2309_CH1,58 LTC2309_CH3,59 LTC2309_CH5,60 LTC2309_CH7,61};62 63#define LTC2309_CHAN(_chan, _addr) { \64 .type = IIO_VOLTAGE, \65 .indexed = 1, \66 .address = _addr, \67 .channel = _chan, \68 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \69 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \70}71 72#define LTC2309_DIFF_CHAN(_chan, _chan2, _addr) { \73 .type = IIO_VOLTAGE, \74 .differential = 1, \75 .indexed = 1, \76 .address = _addr, \77 .channel = _chan, \78 .channel2 = _chan2, \79 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \80 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \81}82 83static const struct iio_chan_spec ltc2309_channels[] = {84 LTC2309_CHAN(0, LTC2309_CH0),85 LTC2309_CHAN(1, LTC2309_CH1),86 LTC2309_CHAN(2, LTC2309_CH2),87 LTC2309_CHAN(3, LTC2309_CH3),88 LTC2309_CHAN(4, LTC2309_CH4),89 LTC2309_CHAN(5, LTC2309_CH5),90 LTC2309_CHAN(6, LTC2309_CH6),91 LTC2309_CHAN(7, LTC2309_CH7),92 LTC2309_DIFF_CHAN(0, 1, LTC2309_CH0_CH1),93 LTC2309_DIFF_CHAN(2, 3, LTC2309_CH2_CH3),94 LTC2309_DIFF_CHAN(4, 5, LTC2309_CH4_CH5),95 LTC2309_DIFF_CHAN(6, 7, LTC2309_CH6_CH7),96 LTC2309_DIFF_CHAN(1, 0, LTC2309_CH1_CH0),97 LTC2309_DIFF_CHAN(3, 2, LTC2309_CH3_CH2),98 LTC2309_DIFF_CHAN(5, 4, LTC2309_CH5_CH4),99 LTC2309_DIFF_CHAN(7, 6, LTC2309_CH7_CH6),100};101 102static int ltc2309_read_raw_channel(struct ltc2309 *ltc2309,103 unsigned long address, int *val)104{105 int ret;106 __be16 buf;107 u8 din;108 109 din = FIELD_PREP(LTC2309_DIN_CH_MASK, address & 0x0f) |110 FIELD_PREP(LTC2309_DIN_UNI, 1) |111 FIELD_PREP(LTC2309_DIN_SLEEP, 0);112 113 ret = i2c_smbus_write_byte(ltc2309->client, din);114 if (ret < 0) {115 dev_err(ltc2309->dev, "i2c command failed: %pe\n",116 ERR_PTR(ret));117 return ret;118 }119 120 ret = i2c_master_recv(ltc2309->client, (char *)&buf, 2);121 if (ret < 0) {122 dev_err(ltc2309->dev, "i2c read failed: %pe\n", ERR_PTR(ret));123 return ret;124 }125 126 *val = be16_to_cpu(buf) >> 4;127 128 return ret;129}130 131static int ltc2309_read_raw(struct iio_dev *indio_dev,132 struct iio_chan_spec const *chan, int *val,133 int *val2, long mask)134{135 struct ltc2309 *ltc2309 = iio_priv(indio_dev);136 int ret;137 138 switch (mask) {139 case IIO_CHAN_INFO_RAW:140 mutex_lock(<c2309->lock);141 ret = ltc2309_read_raw_channel(ltc2309, chan->address, val);142 mutex_unlock(<c2309->lock);143 if (ret < 0)144 return -EINVAL;145 return IIO_VAL_INT;146 case IIO_CHAN_INFO_SCALE:147 *val = ltc2309->vref_mv;148 *val2 = LTC2309_ADC_RESOLUTION;149 return IIO_VAL_FRACTIONAL_LOG2;150 default:151 return -EINVAL;152 }153}154 155static const struct iio_info ltc2309_info = {156 .read_raw = ltc2309_read_raw,157};158 159static int ltc2309_probe(struct i2c_client *client)160{161 struct iio_dev *indio_dev;162 struct ltc2309 *ltc2309;163 int ret;164 165 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*ltc2309));166 if (!indio_dev)167 return -ENOMEM;168 169 ltc2309 = iio_priv(indio_dev);170 ltc2309->dev = &indio_dev->dev;171 ltc2309->client = client;172 173 indio_dev->name = "ltc2309";174 indio_dev->modes = INDIO_DIRECT_MODE;175 indio_dev->channels = ltc2309_channels;176 indio_dev->num_channels = ARRAY_SIZE(ltc2309_channels);177 indio_dev->info = <c2309_info;178 179 ret = devm_regulator_get_enable_read_voltage(&client->dev, "vref");180 if (ret < 0 && ret != -ENODEV)181 return dev_err_probe(ltc2309->dev, ret,182 "failed to get vref voltage\n");183 184 ltc2309->vref_mv = ret == -ENODEV ? LTC2309_INTERNAL_REF_MV : ret / 1000;185 186 mutex_init(<c2309->lock);187 188 return devm_iio_device_register(&client->dev, indio_dev);189}190 191static const struct of_device_id ltc2309_of_match[] = {192 { .compatible = "lltc,ltc2309" },193 { }194};195MODULE_DEVICE_TABLE(of, ltc2309_of_match);196 197static const struct i2c_device_id ltc2309_id[] = {198 { "ltc2309" },199 { }200};201MODULE_DEVICE_TABLE(i2c, ltc2309_id);202 203static struct i2c_driver ltc2309_driver = {204 .driver = {205 .name = "ltc2309",206 .of_match_table = ltc2309_of_match,207 },208 .probe = ltc2309_probe,209 .id_table = ltc2309_id,210};211module_i2c_driver(ltc2309_driver);212 213MODULE_AUTHOR("Liam Beguin <liambeguin@gmail.com>");214MODULE_DESCRIPTION("Linear Technology LTC2309 ADC");215MODULE_LICENSE("GPL v2");216