mm: use pgdat_end_pfn() to simplify the code in others
[deliverable/linux.git] / drivers / iio / adc / mcp320x.c
CommitLineData
f5ce4a7a
OA
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
22enum {
23 mcp3204,
24 mcp3208,
25};
26
27struct 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
39static 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
52static 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
92out:
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
120static 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
129static 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
144static const struct iio_info mcp320x_info = {
145 .read_raw = mcp320x_read_raw,
146 .driver_module = THIS_MODULE,
147};
148
149struct mcp3208_chip_info {
150 const struct iio_chan_spec *channels;
151 unsigned int num_channels;
152};
153
154static 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
165static 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
a726dea5 172 indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
f5ce4a7a
OA
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
a726dea5
SK
196 adc->reg = devm_regulator_get(&spi->dev, "vref");
197 if (IS_ERR(adc->reg))
198 return PTR_ERR(adc->reg);
f5ce4a7a
OA
199
200 ret = regulator_enable(adc->reg);
201 if (ret < 0)
a726dea5 202 return ret;
f5ce4a7a
OA
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
212reg_disable:
213 regulator_disable(adc->reg);
f5ce4a7a
OA
214
215 return ret;
216}
217
218static 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);
f5ce4a7a
OA
225
226 return 0;
227}
228
229static const struct spi_device_id mcp320x_id[] = {
230 { "mcp3204", mcp3204 },
231 { "mcp3208", mcp3208 },
232 { }
233};
234MODULE_DEVICE_TABLE(spi, mcp320x_id);
235
236static 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};
245module_spi_driver(mcp320x_driver);
246
247MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>");
248MODULE_DESCRIPTION("Microchip Technology MCP3204/08");
249MODULE_LICENSE("GPL v2");
This page took 0.106904 seconds and 5 git commands to generate.