xref: /netbsd-src/sys/dev/spi/mcp3k.c (revision f64c887e97cc4e0fca7184a6ff7742154fef673e)
1 /*	$NetBSD: mcp3k.c,v 1.4 2022/01/19 05:21:44 thorpej Exp $ */
2 
3 /*-
4  * Copyright (c) 2015 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Frank Wille.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * Microchip MCP3x0x SAR analog to digital converters.
34  * The driver supports various ADCs with different resolutions, operation
35  * modes and number of input channels.
36  * The reference voltage Vref defaults to the maximum output value in mV,
37  * but can be changed via sysctl(3).
38  *
39  * MCP3001: http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf
40  * MCP3002: http://ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf
41  * MCP3004/3008: http://ww1.microchip.com/downloads/en/DeviceDoc/21295C.pdf
42  * MCP3201: http://ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf
43  * MCP3204/3208: http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf
44  * MCP3301: http://ww1.microchip.com/downloads/en/DeviceDoc/21700E.pdf
45  * MPC3302/3304: http://ww1.microchip.com/downloads/en/DeviceDoc/21697F.pdf
46  */
47 
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/device.h>
51 #include <sys/kernel.h>
52 #include <sys/types.h>
53 #include <sys/sysctl.h>
54 
55 #include <dev/sysmon/sysmonvar.h>
56 #include <dev/spi/spivar.h>
57 
58 #define M3K_MAX_SENSORS		16		/* 8 single-ended & 8 diff. */
59 
60 /* mcp3x0x model description */
61 struct mcp3kadc_model {
62 	uint32_t		name;
63 	uint8_t			bits;
64 	uint8_t			channels;
65 	uint8_t			lead;		/* leading bits to ignore */
66 	uint8_t			flags;
67 #define M3K_SGLDIFF		0x01		/* single-ended/differential */
68 #define M3K_D2D1D0		0x02		/* 3 channel select bits */
69 #define M3K_MSBF		0x04		/* MSBF select bit */
70 #define M3K_SIGNED		0x80		/* result is signed */
71 #define M3K_CTRL_NEEDED		(M3K_SGLDIFF | M3K_D2D1D0 | M3K_MSBF)
72 };
73 
74 struct mcp3kadc_softc {
75 	device_t		sc_dev;
76 	struct spi_handle 	*sc_sh;
77 	int			sc_model;
78 	uint32_t		sc_adc_max;
79 	int32_t			sc_vref_mv;
80 
81 	struct sysmon_envsys 	*sc_sme;
82 	envsys_data_t 		sc_sensors[M3K_MAX_SENSORS];
83 };
84 
85 static int	mcp3kadc_match(device_t, cfdata_t, void *);
86 static void	mcp3kadc_attach(device_t, device_t, void *);
87 static void	mcp3kadc_envsys_refresh(struct sysmon_envsys *,
88 		    envsys_data_t *);
89 static int	sysctl_mcp3kadc_vref(SYSCTLFN_ARGS);
90 
91 CFATTACH_DECL_NEW(mcp3kadc, sizeof(struct mcp3kadc_softc),
92     mcp3kadc_match,  mcp3kadc_attach, NULL, NULL);
93 
94 static struct mcp3kadc_model mcp3k_models[] = {
95 	{
96 		.name = 3001,
97 		.bits = 10,
98 		.channels = 1,
99 		.lead = 3,
100 		.flags = 0
101 	},
102 	{
103 		.name = 3002,
104 		.bits = 10,
105 		.channels = 2,
106 		.lead = 2,
107 		.flags = M3K_SGLDIFF | M3K_MSBF
108 	},
109 	{
110 		.name = 3004,
111 		.bits = 10,
112 		.channels = 4,
113 		.lead = 2,
114 		.flags = M3K_SGLDIFF | M3K_D2D1D0
115 	},
116 	{
117 		.name = 3008,
118 		.bits = 10,
119 		.channels = 8,
120 		.lead = 2,
121 		.flags = M3K_SGLDIFF | M3K_D2D1D0
122 	},
123 	{
124 		.name = 3201,
125 		.bits = 12,
126 		.channels = 1,
127 		.lead = 3,
128 		.flags = 0
129 	},
130 	{
131 		.name = 3202,
132 		.bits = 12,
133 		.channels = 2,
134 		.lead = 2,
135 		.flags = M3K_SGLDIFF | M3K_MSBF
136 	},
137 	{
138 		.name = 3204,
139 		.bits = 12,
140 		.channels = 4,
141 		.lead = 2,
142 		.flags = M3K_SGLDIFF | M3K_D2D1D0
143 	},
144 	{
145 		.name = 3208,
146 		.bits = 12,
147 		.channels = 8,
148 		.lead = 2,
149 		.flags = M3K_SGLDIFF | M3K_D2D1D0
150 	},
151 	{
152 		.name = 3301,
153 		.bits = 13,
154 		.channels = 1,
155 		.lead = 3,
156 		.flags = M3K_SIGNED
157 	},
158 	{
159 		.name = 3302,
160 		.bits = 13,
161 		.channels = 4,
162 		.lead = 2,
163 		.flags = M3K_SIGNED | M3K_SGLDIFF | M3K_D2D1D0
164 	},
165 	{
166 		.name = 3304,
167 		.bits = 13,
168 		.channels = 8,
169 		.lead = 2,
170 		.flags = M3K_SIGNED | M3K_SGLDIFF | M3K_D2D1D0
171 	},
172 };
173 
174 static int
mcp3kadc_match(device_t parent,cfdata_t cf,void * aux)175 mcp3kadc_match(device_t parent, cfdata_t cf, void *aux)
176 {
177 
178 	if (strcmp(cf->cf_name, "mcp3kadc") != 0)
179 		return 0;
180 
181 	return 1;
182 }
183 
184 static void
mcp3kadc_attach(device_t parent,device_t self,void * aux)185 mcp3kadc_attach(device_t parent, device_t self, void *aux)
186 {
187 	const struct sysctlnode *rnode, *node;
188 	struct spi_attach_args *sa;
189 	struct mcp3kadc_softc *sc;
190 	struct mcp3kadc_model *model;
191 	int error, ch, i;
192 
193 	sa = aux;
194 	sc = device_private(self);
195 	sc->sc_dev = self;
196 	sc->sc_sh = sa->sa_handle;
197 
198 	/* device flags define the model */
199 	sc->sc_model = device_cfdata(sc->sc_dev)->cf_flags;
200 	model = &mcp3k_models[sc->sc_model];
201 
202 	aprint_naive(": Analog to Digital converter\n");
203 	aprint_normal(": MCP%u %u-channel %u-bit ADC\n",
204 	    (unsigned)model->name, (unsigned)model->channels,
205 	    (unsigned)model->bits);
206 
207 	/* configure for 1MHz */
208 	error = spi_configure(self, sa->sa_handle, SPI_MODE_0, 1000000);
209 	if (error) {
210 		return;
211 	}
212 
213 	/* set a default Vref in mV according to the chip's ADC resolution */
214 	sc->sc_vref_mv = 1 << ((model->flags & M3K_SIGNED) ?
215 	    model->bits - 1 : model->bits);
216 
217 	/* remember maximum value for this ADC - also used for masking */
218 	sc->sc_adc_max = (1 << model->bits) - 1;
219 
220 	/* attach voltage sensors to envsys */
221 	sc->sc_sme = sysmon_envsys_create();
222 
223 	/* adc difference from two neighbouring channels */
224 	for (ch = 0; ch < model->channels; ch++) {
225 		KASSERT(ch < M3K_MAX_SENSORS);
226 		sc->sc_sensors[ch].units = ENVSYS_SVOLTS_DC;
227 		sc->sc_sensors[ch].state = ENVSYS_SINVALID;
228 		if (model->channels == 1)
229 			strlcpy(sc->sc_sensors[ch].desc, "adc diff ch0",
230 			    sizeof(sc->sc_sensors[ch].desc));
231 		else
232 			snprintf(sc->sc_sensors[ch].desc,
233 			    sizeof(sc->sc_sensors[ch].desc),
234 			    "adc diff ch%d-ch%d", ch, ch ^ 1);
235 		sc->sc_sensors[ch].private = ch;
236 		sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensors[ch]);
237 	}
238 
239 	if (model->flags & M3K_SGLDIFF) {
240 		/* adc from single ended channels */
241 		for (i = 0; i < model->channels; i++, ch++) {
242 			KASSERT(ch < M3K_MAX_SENSORS);
243 			sc->sc_sensors[ch].units = ENVSYS_SVOLTS_DC;
244 			sc->sc_sensors[ch].state = ENVSYS_SINVALID;
245 			snprintf(sc->sc_sensors[ch].desc,
246 			    sizeof(sc->sc_sensors[ch].desc),
247 			    "adc single ch%d", i);
248 			sc->sc_sensors[ch].private = ch;
249 			sysmon_envsys_sensor_attach(sc->sc_sme,
250 			    &sc->sc_sensors[ch]);
251 		}
252 	}
253 
254 	sc->sc_sme->sme_name = device_xname(self);
255 	sc->sc_sme->sme_refresh = mcp3kadc_envsys_refresh;
256 	sc->sc_sme->sme_cookie = sc;
257 	if (sysmon_envsys_register(sc->sc_sme)) {
258 		aprint_error_dev(self, "unable to register with sysmon\n");
259 		sysmon_envsys_destroy(sc->sc_sme);
260 	}
261 
262 	/* create a sysctl node for adjusting the ADC's reference voltage */
263 	rnode = node = NULL;
264 	sysctl_createv(NULL, 0, NULL, &rnode,
265 	    CTLFLAG_READWRITE,
266 	    CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
267 	    NULL, 0, NULL, 0,
268 	    CTL_HW, CTL_CREATE, CTL_EOL);
269 
270 	if (rnode != NULL)
271 		sysctl_createv(NULL, 0, NULL, &node,
272 		    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
273 		    CTLTYPE_INT, "vref",
274 		    SYSCTL_DESCR("ADC reference voltage"),
275 		    sysctl_mcp3kadc_vref, 0, (void *)sc, 0,
276 		    CTL_HW, rnode->sysctl_num, CTL_CREATE, CTL_EOL);
277 }
278 
279 static void
mcp3kadc_envsys_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)280 mcp3kadc_envsys_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
281 {
282 	struct mcp3kadc_softc *sc;
283 	struct mcp3kadc_model *model;
284 	uint8_t buf[2], ctrl;
285 	int32_t val, scale;
286 
287 	sc = sme->sme_cookie;
288 	model = &mcp3k_models[sc->sc_model];
289 	scale = sc->sc_adc_max + 1;
290 
291 	if (model->flags & M3K_CTRL_NEEDED) {
292 		/* we need to send some control bits first */
293 		ctrl = 1;	/* start bit */
294 
295 		if (model->flags & M3K_SGLDIFF) {
296 			/* bit set to select single-ended mode */
297 			ctrl <<= 1;
298 			ctrl |= edata->private >= model->channels;
299 		}
300 
301 		if (model->flags & M3K_D2D1D0) {
302 			/* 3 bits select the channel */
303 			ctrl <<= 3;
304 			ctrl |= edata->private & (model->channels - 1);
305 		} else {
306 			/* 1 bit selects between two channels */
307 			ctrl <<= 1;
308 			ctrl |= edata->private & 1;
309 		}
310 
311 		if (model->flags & M3K_MSBF) {
312 			/* bit select MSB first format */
313 			ctrl <<= 1;
314 			ctrl |= 1;
315 		}
316 
317 		/* send control bits, receive ADC data */
318 		if (spi_send_recv(sc->sc_sh, 1, &ctrl, 2, buf) != 0) {
319 			edata->state = ENVSYS_SINVALID;
320 			return;
321 		}
322 	} else {
323 
324 		/* just read data from the ADC */
325 		if (spi_recv(sc->sc_sh, 2, buf) != 0) {
326 			edata->state = ENVSYS_SINVALID;
327 			return;
328 		}
329 	}
330 
331 	/* extract big-endian ADC data from buffer */
332 	val = (buf[0] << 8) | buf[1];
333 	val = (val >> (16 - (model->bits + model->lead))) & sc->sc_adc_max;
334 
335 	/* sign-extend the result, when needed */
336 	if (model->flags & M3K_SIGNED) {
337 		if (val & (1 << (model->bits - 1)))
338 			val -= sc->sc_adc_max + 1;
339 		scale >>= 1;	/* MSB is the sign */
340 	}
341 
342 	/* scale the value for Vref and convert to mV */
343 	edata->value_cur = (sc->sc_vref_mv * val / scale) * 1000;
344 	edata->state = ENVSYS_SVALID;
345 }
346 
347 static int
sysctl_mcp3kadc_vref(SYSCTLFN_ARGS)348 sysctl_mcp3kadc_vref(SYSCTLFN_ARGS)
349 {
350 	struct sysctlnode node;
351 	struct mcp3kadc_softc *sc;
352 	int32_t t;
353 	int error;
354 
355 	node = *rnode;
356 	sc = node.sysctl_data;
357 
358 	t = sc->sc_vref_mv;
359 	node.sysctl_data = &t;
360 
361 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
362 	if (error || newp == NULL)
363 		return error;
364 	if (t <= 0)
365 		return EINVAL;
366 
367 	sc->sc_vref_mv = t;
368 	return 0;
369 }
370