1 // SPDX-License-Identifier: GPL-2.0
3 * The LTC2309 is an 8-Channel, 12-Bit SAR ADC with an I2C Interface.
6 * https://www.analog.com/media/en/technical-documentation/data-sheets/2309fd.pdf
8 * Copyright (c) 2023, Liam Beguin <liambeguin@gmail.com>
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>
18 #define LTC2309_ADC_RESOLUTION 12
19 #define LTC2309_INTERNAL_REF_MV 4096
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)
30 * struct ltc2309 - internal device data structure
31 * @dev: Device reference
32 * @client: I2C reference
33 * @lock: Lock to serialize data access
34 * @vref_mv: Internal voltage reference
38 struct i2c_client
*client
;
39 struct mutex lock
; /* serialize data access */
43 /* Order matches expected channel address, See datasheet Table 1. */
44 enum ltc2309_channels
{
63 #define LTC2309_CHAN(_chan, _addr) { \
64 .type = IIO_VOLTAGE, \
68 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
69 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
72 #define LTC2309_DIFF_CHAN(_chan, _chan2, _addr) { \
73 .type = IIO_VOLTAGE, \
79 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
80 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
83 static 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
),
102 static int ltc2309_read_raw_channel(struct ltc2309
*ltc2309
,
103 unsigned long address
, int *val
)
109 din
= FIELD_PREP(LTC2309_DIN_CH_MASK
, address
& 0x0f) |
110 FIELD_PREP(LTC2309_DIN_UNI
, 1) |
111 FIELD_PREP(LTC2309_DIN_SLEEP
, 0);
113 ret
= i2c_smbus_write_byte(ltc2309
->client
, din
);
115 dev_err(ltc2309
->dev
, "i2c command failed: %pe\n",
120 ret
= i2c_master_recv(ltc2309
->client
, (char *)&buf
, 2);
122 dev_err(ltc2309
->dev
, "i2c read failed: %pe\n", ERR_PTR(ret
));
126 *val
= be16_to_cpu(buf
) >> 4;
131 static int ltc2309_read_raw(struct iio_dev
*indio_dev
,
132 struct iio_chan_spec
const *chan
, int *val
,
133 int *val2
, long mask
)
135 struct ltc2309
*ltc2309
= iio_priv(indio_dev
);
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
);
146 case IIO_CHAN_INFO_SCALE
:
147 *val
= ltc2309
->vref_mv
;
148 *val2
= LTC2309_ADC_RESOLUTION
;
149 return IIO_VAL_FRACTIONAL_LOG2
;
155 static const struct iio_info ltc2309_info
= {
156 .read_raw
= ltc2309_read_raw
,
159 static int ltc2309_probe(struct i2c_client
*client
)
161 struct iio_dev
*indio_dev
;
162 struct ltc2309
*ltc2309
;
165 indio_dev
= devm_iio_device_alloc(&client
->dev
, sizeof(*ltc2309
));
169 ltc2309
= iio_priv(indio_dev
);
170 ltc2309
->dev
= &indio_dev
->dev
;
171 ltc2309
->client
= client
;
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
;
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");
184 ltc2309
->vref_mv
= ret
== -ENODEV
? LTC2309_INTERNAL_REF_MV
: ret
/ 1000;
186 mutex_init(<c2309
->lock
);
188 return devm_iio_device_register(&client
->dev
, indio_dev
);
191 static const struct of_device_id ltc2309_of_match
[] = {
192 { .compatible
= "lltc,ltc2309" },
195 MODULE_DEVICE_TABLE(of
, ltc2309_of_match
);
197 static const struct i2c_device_id ltc2309_id
[] = {
201 MODULE_DEVICE_TABLE(i2c
, ltc2309_id
);
203 static struct i2c_driver ltc2309_driver
= {
206 .of_match_table
= ltc2309_of_match
,
208 .probe
= ltc2309_probe
,
209 .id_table
= ltc2309_id
,
211 module_i2c_driver(ltc2309_driver
);
213 MODULE_AUTHOR("Liam Beguin <liambeguin@gmail.com>");
214 MODULE_DESCRIPTION("Linear Technology LTC2309 ADC");
215 MODULE_LICENSE("GPL v2");