xref: /linux/drivers/iio/adc/ltc2309.c (revision 24168c5e6dfbdd5b414f048f47f75d64533296ca)
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");
247