xref: /linux/drivers/iio/adc/ltc2309.c (revision 55d0969c451159cff86949b38c39171cab962069)
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 #define LTC2309_INTERNAL_REF_MV 4096
20 
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 structure
31  * @dev:	Device reference
32  * @client:	I2C reference
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 mutex		lock; /* serialize data access */
40 	int			vref_mv;
41 };
42 
43 /* Order matches expected channel address, See datasheet Table 1. */
44 enum 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 
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),
100 };
101 
102 static 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 
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)
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(&ltc2309->lock);
141 		ret = ltc2309_read_raw_channel(ltc2309, chan->address, val);
142 		mutex_unlock(&ltc2309->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 
155 static const struct iio_info ltc2309_info = {
156 	.read_raw = ltc2309_read_raw,
157 };
158 
159 static 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 = &ltc2309_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(&ltc2309->lock);
187 
188 	return devm_iio_device_register(&client->dev, indio_dev);
189 }
190 
191 static const struct of_device_id ltc2309_of_match[] = {
192 	{ .compatible = "lltc,ltc2309" },
193 	{ }
194 };
195 MODULE_DEVICE_TABLE(of, ltc2309_of_match);
196 
197 static const struct i2c_device_id ltc2309_id[] = {
198 	{ "ltc2309" },
199 	{ }
200 };
201 MODULE_DEVICE_TABLE(i2c, ltc2309_id);
202 
203 static 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 };
211 module_i2c_driver(ltc2309_driver);
212 
213 MODULE_AUTHOR("Liam Beguin <liambeguin@gmail.com>");
214 MODULE_DESCRIPTION("Linear Technology LTC2309 ADC");
215 MODULE_LICENSE("GPL v2");
216