Commit | Line | Data |
---|---|---|
8dd2d7c0 GC |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * ADS8344 16-bit 8-Channel ADC driver | |
4 | * | |
5 | * Author: Gregory CLEMENT <gregory.clement@bootlin.com> | |
6 | * | |
3593cd53 | 7 | * Datasheet: https://www.ti.com/lit/ds/symlink/ads8344.pdf |
8dd2d7c0 GC |
8 | */ |
9 | ||
10 | #include <linux/delay.h> | |
11 | #include <linux/iio/buffer.h> | |
12 | #include <linux/iio/iio.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/regulator/consumer.h> | |
15 | #include <linux/spi/spi.h> | |
16 | ||
17 | #define ADS8344_START BIT(7) | |
18 | #define ADS8344_SINGLE_END BIT(2) | |
19 | #define ADS8344_CHANNEL(channel) ((channel) << 4) | |
20 | #define ADS8344_CLOCK_INTERNAL 0x2 /* PD1 = 1 and PD0 = 0 */ | |
21 | ||
22 | struct ads8344 { | |
23 | struct spi_device *spi; | |
24 | struct regulator *reg; | |
25 | /* | |
26 | * Lock protecting access to adc->tx_buff and rx_buff, | |
27 | * especially from concurrent read on sysfs file. | |
28 | */ | |
29 | struct mutex lock; | |
30 | ||
8966b11e | 31 | u8 tx_buf __aligned(IIO_DMA_MINALIGN); |
dd7de4c0 | 32 | u8 rx_buf[3]; |
8dd2d7c0 GC |
33 | }; |
34 | ||
bcfa1e25 | 35 | #define ADS8344_VOLTAGE_CHANNEL(chan, addr) \ |
8dd2d7c0 GC |
36 | { \ |
37 | .type = IIO_VOLTAGE, \ | |
38 | .indexed = 1, \ | |
39 | .channel = chan, \ | |
40 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
41 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
bcfa1e25 | 42 | .address = addr, \ |
8dd2d7c0 GC |
43 | } |
44 | ||
bcfa1e25 | 45 | #define ADS8344_VOLTAGE_CHANNEL_DIFF(chan1, chan2, addr) \ |
8dd2d7c0 GC |
46 | { \ |
47 | .type = IIO_VOLTAGE, \ | |
48 | .indexed = 1, \ | |
49 | .channel = (chan1), \ | |
50 | .channel2 = (chan2), \ | |
51 | .differential = 1, \ | |
52 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
53 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
bcfa1e25 | 54 | .address = addr, \ |
8dd2d7c0 GC |
55 | } |
56 | ||
57 | static const struct iio_chan_spec ads8344_channels[] = { | |
58 | ADS8344_VOLTAGE_CHANNEL(0, 0), | |
59 | ADS8344_VOLTAGE_CHANNEL(1, 4), | |
60 | ADS8344_VOLTAGE_CHANNEL(2, 1), | |
61 | ADS8344_VOLTAGE_CHANNEL(3, 5), | |
62 | ADS8344_VOLTAGE_CHANNEL(4, 2), | |
63 | ADS8344_VOLTAGE_CHANNEL(5, 6), | |
64 | ADS8344_VOLTAGE_CHANNEL(6, 3), | |
65 | ADS8344_VOLTAGE_CHANNEL(7, 7), | |
66 | ADS8344_VOLTAGE_CHANNEL_DIFF(0, 1, 8), | |
67 | ADS8344_VOLTAGE_CHANNEL_DIFF(2, 3, 9), | |
68 | ADS8344_VOLTAGE_CHANNEL_DIFF(4, 5, 10), | |
69 | ADS8344_VOLTAGE_CHANNEL_DIFF(6, 7, 11), | |
70 | ADS8344_VOLTAGE_CHANNEL_DIFF(1, 0, 12), | |
71 | ADS8344_VOLTAGE_CHANNEL_DIFF(3, 2, 13), | |
72 | ADS8344_VOLTAGE_CHANNEL_DIFF(5, 4, 14), | |
73 | ADS8344_VOLTAGE_CHANNEL_DIFF(7, 6, 15), | |
74 | }; | |
75 | ||
76 | static int ads8344_adc_conversion(struct ads8344 *adc, int channel, | |
77 | bool differential) | |
78 | { | |
79 | struct spi_device *spi = adc->spi; | |
80 | int ret; | |
81 | ||
82 | adc->tx_buf = ADS8344_START; | |
83 | if (!differential) | |
84 | adc->tx_buf |= ADS8344_SINGLE_END; | |
85 | adc->tx_buf |= ADS8344_CHANNEL(channel); | |
86 | adc->tx_buf |= ADS8344_CLOCK_INTERNAL; | |
87 | ||
88 | ret = spi_write(spi, &adc->tx_buf, 1); | |
89 | if (ret) | |
90 | return ret; | |
91 | ||
92 | udelay(9); | |
93 | ||
dd7de4c0 | 94 | ret = spi_read(spi, adc->rx_buf, sizeof(adc->rx_buf)); |
8dd2d7c0 GC |
95 | if (ret) |
96 | return ret; | |
97 | ||
dd7de4c0 | 98 | return adc->rx_buf[0] << 9 | adc->rx_buf[1] << 1 | adc->rx_buf[2] >> 7; |
8dd2d7c0 GC |
99 | } |
100 | ||
101 | static int ads8344_read_raw(struct iio_dev *iio, | |
102 | struct iio_chan_spec const *channel, int *value, | |
103 | int *shift, long mask) | |
104 | { | |
105 | struct ads8344 *adc = iio_priv(iio); | |
106 | ||
107 | switch (mask) { | |
108 | case IIO_CHAN_INFO_RAW: | |
109 | mutex_lock(&adc->lock); | |
bcfa1e25 | 110 | *value = ads8344_adc_conversion(adc, channel->address, |
8dd2d7c0 GC |
111 | channel->differential); |
112 | mutex_unlock(&adc->lock); | |
113 | if (*value < 0) | |
114 | return *value; | |
115 | ||
116 | return IIO_VAL_INT; | |
117 | case IIO_CHAN_INFO_SCALE: | |
118 | *value = regulator_get_voltage(adc->reg); | |
119 | if (*value < 0) | |
120 | return *value; | |
121 | ||
122 | /* convert regulator output voltage to mV */ | |
123 | *value /= 1000; | |
124 | *shift = 16; | |
125 | ||
126 | return IIO_VAL_FRACTIONAL_LOG2; | |
127 | default: | |
128 | return -EINVAL; | |
129 | } | |
130 | } | |
131 | ||
132 | static const struct iio_info ads8344_info = { | |
133 | .read_raw = ads8344_read_raw, | |
134 | }; | |
135 | ||
9cec9be7 AA |
136 | static void ads8344_reg_disable(void *data) |
137 | { | |
138 | regulator_disable(data); | |
139 | } | |
140 | ||
8dd2d7c0 GC |
141 | static int ads8344_probe(struct spi_device *spi) |
142 | { | |
143 | struct iio_dev *indio_dev; | |
144 | struct ads8344 *adc; | |
145 | int ret; | |
146 | ||
147 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); | |
148 | if (!indio_dev) | |
149 | return -ENOMEM; | |
150 | ||
151 | adc = iio_priv(indio_dev); | |
152 | adc->spi = spi; | |
153 | mutex_init(&adc->lock); | |
154 | ||
155 | indio_dev->name = dev_name(&spi->dev); | |
8dd2d7c0 GC |
156 | indio_dev->info = &ads8344_info; |
157 | indio_dev->modes = INDIO_DIRECT_MODE; | |
158 | indio_dev->channels = ads8344_channels; | |
159 | indio_dev->num_channels = ARRAY_SIZE(ads8344_channels); | |
160 | ||
161 | adc->reg = devm_regulator_get(&spi->dev, "vref"); | |
162 | if (IS_ERR(adc->reg)) | |
163 | return PTR_ERR(adc->reg); | |
164 | ||
165 | ret = regulator_enable(adc->reg); | |
166 | if (ret) | |
167 | return ret; | |
168 | ||
9cec9be7 AA |
169 | ret = devm_add_action_or_reset(&spi->dev, ads8344_reg_disable, adc->reg); |
170 | if (ret) | |
8dd2d7c0 | 171 | return ret; |
8dd2d7c0 | 172 | |
9cec9be7 | 173 | return devm_iio_device_register(&spi->dev, indio_dev); |
8dd2d7c0 GC |
174 | } |
175 | ||
176 | static const struct of_device_id ads8344_of_match[] = { | |
177 | { .compatible = "ti,ads8344", }, | |
178 | {} | |
179 | }; | |
180 | MODULE_DEVICE_TABLE(of, ads8344_of_match); | |
181 | ||
182 | static struct spi_driver ads8344_driver = { | |
183 | .driver = { | |
184 | .name = "ads8344", | |
185 | .of_match_table = ads8344_of_match, | |
186 | }, | |
187 | .probe = ads8344_probe, | |
8dd2d7c0 GC |
188 | }; |
189 | module_spi_driver(ads8344_driver); | |
190 | ||
191 | MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@bootlin.com>"); | |
192 | MODULE_DESCRIPTION("ADS8344 driver"); | |
193 | MODULE_LICENSE("GPL"); |