]> Git Repo - J-linux.git/blob - drivers/iio/adc/ltc2309.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / iio / adc / ltc2309.c
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 <[email protected]>
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 <[email protected]>");
214 MODULE_DESCRIPTION("Linear Technology LTC2309 ADC");
215 MODULE_LICENSE("GPL v2");
This page took 0.036644 seconds and 4 git commands to generate.