]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/iio/adc/mcp320x.c
Merge remote-tracking branch 'staging/staging-next'
[karo-tx-linux.git] / drivers / iio / adc / mcp320x.c
1 /*
2  * Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
3  *
4  * Driver for Microchip Technology's MCP3204 and MCP3208 ADC chips.
5  * Datasheet can be found here:
6  * http://ww1.microchip.com/downloads/en/devicedoc/21298c.pdf
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12
13 #include <linux/err.h>
14 #include <linux/spi/spi.h>
15 #include <linux/module.h>
16 #include <linux/iio/iio.h>
17 #include <linux/regulator/consumer.h>
18
19 #define MCP_SINGLE_ENDED        (1 << 3)
20 #define MCP_START_BIT           (1 << 4)
21
22 enum {
23         mcp3204,
24         mcp3208,
25 };
26
27 struct mcp320x {
28         struct spi_device *spi;
29         struct spi_message msg;
30         struct spi_transfer transfer[2];
31
32         u8 tx_buf;
33         u8 rx_buf[2];
34
35         struct regulator *reg;
36         struct mutex lock;
37 };
38
39 static int mcp320x_adc_conversion(struct mcp320x *adc, u8 msg)
40 {
41         int ret;
42
43         adc->tx_buf = msg;
44         ret = spi_sync(adc->spi, &adc->msg);
45         if (ret < 0)
46                 return ret;
47
48         return ((adc->rx_buf[0] & 0x3f) << 6)  |
49                 (adc->rx_buf[1] >> 2);
50 }
51
52 static int mcp320x_read_raw(struct iio_dev *indio_dev,
53                             struct iio_chan_spec const *channel, int *val,
54                             int *val2, long mask)
55 {
56         struct mcp320x *adc = iio_priv(indio_dev);
57         int ret = -EINVAL;
58
59         mutex_lock(&adc->lock);
60
61         switch (mask) {
62         case IIO_CHAN_INFO_RAW:
63                 if (channel->differential)
64                         ret = mcp320x_adc_conversion(adc,
65                                 MCP_START_BIT | channel->address);
66                 else
67                         ret = mcp320x_adc_conversion(adc,
68                                 MCP_START_BIT | MCP_SINGLE_ENDED |
69                                 channel->address);
70                 if (ret < 0)
71                         goto out;
72
73                 *val = ret;
74                 ret = IIO_VAL_INT;
75                 break;
76
77         case IIO_CHAN_INFO_SCALE:
78                 /* Digital output code = (4096 * Vin) / Vref */
79                 ret = regulator_get_voltage(adc->reg);
80                 if (ret < 0)
81                         goto out;
82
83                 *val = ret / 1000;
84                 *val2 = 12;
85                 ret = IIO_VAL_FRACTIONAL_LOG2;
86                 break;
87
88         default:
89                 break;
90         }
91
92 out:
93         mutex_unlock(&adc->lock);
94
95         return ret;
96 }
97
98 #define MCP320X_VOLTAGE_CHANNEL(num)                            \
99         {                                                       \
100                 .type = IIO_VOLTAGE,                            \
101                 .indexed = 1,                                   \
102                 .channel = (num),                               \
103                 .address = (num),                               \
104                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
105                 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
106         }
107
108 #define MCP320X_VOLTAGE_CHANNEL_DIFF(num)                       \
109         {                                                       \
110                 .type = IIO_VOLTAGE,                            \
111                 .indexed = 1,                                   \
112                 .channel = (num * 2),                           \
113                 .channel2 = (num * 2 + 1),                      \
114                 .address = (num * 2),                           \
115                 .differential = 1,                              \
116                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
117                 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
118         }
119
120 static const struct iio_chan_spec mcp3204_channels[] = {
121         MCP320X_VOLTAGE_CHANNEL(0),
122         MCP320X_VOLTAGE_CHANNEL(1),
123         MCP320X_VOLTAGE_CHANNEL(2),
124         MCP320X_VOLTAGE_CHANNEL(3),
125         MCP320X_VOLTAGE_CHANNEL_DIFF(0),
126         MCP320X_VOLTAGE_CHANNEL_DIFF(1),
127 };
128
129 static const struct iio_chan_spec mcp3208_channels[] = {
130         MCP320X_VOLTAGE_CHANNEL(0),
131         MCP320X_VOLTAGE_CHANNEL(1),
132         MCP320X_VOLTAGE_CHANNEL(2),
133         MCP320X_VOLTAGE_CHANNEL(3),
134         MCP320X_VOLTAGE_CHANNEL(4),
135         MCP320X_VOLTAGE_CHANNEL(5),
136         MCP320X_VOLTAGE_CHANNEL(6),
137         MCP320X_VOLTAGE_CHANNEL(7),
138         MCP320X_VOLTAGE_CHANNEL_DIFF(0),
139         MCP320X_VOLTAGE_CHANNEL_DIFF(1),
140         MCP320X_VOLTAGE_CHANNEL_DIFF(2),
141         MCP320X_VOLTAGE_CHANNEL_DIFF(3),
142 };
143
144 static const struct iio_info mcp320x_info = {
145         .read_raw = mcp320x_read_raw,
146         .driver_module = THIS_MODULE,
147 };
148
149 struct mcp3208_chip_info {
150         const struct iio_chan_spec *channels;
151         unsigned int num_channels;
152 };
153
154 static const struct mcp3208_chip_info mcp3208_chip_infos[] = {
155         [mcp3204] = {
156                 .channels = mcp3204_channels,
157                 .num_channels = ARRAY_SIZE(mcp3204_channels)
158         },
159         [mcp3208] = {
160                 .channels = mcp3208_channels,
161                 .num_channels = ARRAY_SIZE(mcp3208_channels)
162         },
163 };
164
165 static int mcp320x_probe(struct spi_device *spi)
166 {
167         struct iio_dev *indio_dev;
168         struct mcp320x *adc;
169         const struct mcp3208_chip_info *chip_info;
170         int ret;
171
172         indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
173         if (!indio_dev)
174                 return -ENOMEM;
175
176         adc = iio_priv(indio_dev);
177         adc->spi = spi;
178
179         indio_dev->dev.parent = &spi->dev;
180         indio_dev->name = spi_get_device_id(spi)->name;
181         indio_dev->modes = INDIO_DIRECT_MODE;
182         indio_dev->info = &mcp320x_info;
183
184         chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data];
185         indio_dev->channels = chip_info->channels;
186         indio_dev->num_channels = chip_info->num_channels;
187
188         adc->transfer[0].tx_buf = &adc->tx_buf;
189         adc->transfer[0].len = sizeof(adc->tx_buf);
190         adc->transfer[1].rx_buf = adc->rx_buf;
191         adc->transfer[1].len = sizeof(adc->rx_buf);
192
193         spi_message_init_with_transfers(&adc->msg, adc->transfer,
194                                         ARRAY_SIZE(adc->transfer));
195
196         adc->reg = devm_regulator_get(&spi->dev, "vref");
197         if (IS_ERR(adc->reg))
198                 return PTR_ERR(adc->reg);
199
200         ret = regulator_enable(adc->reg);
201         if (ret < 0)
202                 return ret;
203
204         mutex_init(&adc->lock);
205
206         ret = iio_device_register(indio_dev);
207         if (ret < 0)
208                 goto reg_disable;
209
210         return 0;
211
212 reg_disable:
213         regulator_disable(adc->reg);
214
215         return ret;
216 }
217
218 static int mcp320x_remove(struct spi_device *spi)
219 {
220         struct iio_dev *indio_dev = spi_get_drvdata(spi);
221         struct mcp320x *adc = iio_priv(indio_dev);
222
223         iio_device_unregister(indio_dev);
224         regulator_disable(adc->reg);
225
226         return 0;
227 }
228
229 static const struct spi_device_id mcp320x_id[] = {
230         { "mcp3204", mcp3204 },
231         { "mcp3208", mcp3208 },
232         { }
233 };
234 MODULE_DEVICE_TABLE(spi, mcp320x_id);
235
236 static struct spi_driver mcp320x_driver = {
237         .driver = {
238                 .name = "mcp320x",
239                 .owner = THIS_MODULE,
240         },
241         .probe = mcp320x_probe,
242         .remove = mcp320x_remove,
243         .id_table = mcp320x_id,
244 };
245 module_spi_driver(mcp320x_driver);
246
247 MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>");
248 MODULE_DESCRIPTION("Microchip Technology MCP3204/08");
249 MODULE_LICENSE("GPL v2");