xref: /openbmc/linux/drivers/iio/adc/mcp320x.c (revision cd5d5810)
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");
250