1 1.10 thorpej /* $NetBSD: mcp3k.c,v 1.10 2025/09/13 14:10:44 thorpej Exp $ */ 2 1.1 phx 3 1.1 phx /*- 4 1.1 phx * Copyright (c) 2015 The NetBSD Foundation, Inc. 5 1.1 phx * All rights reserved. 6 1.1 phx * 7 1.1 phx * This code is derived from software contributed to The NetBSD Foundation 8 1.1 phx * by Frank Wille. 9 1.1 phx * 10 1.1 phx * Redistribution and use in source and binary forms, with or without 11 1.1 phx * modification, are permitted provided that the following conditions 12 1.1 phx * are met: 13 1.1 phx * 1. Redistributions of source code must retain the above copyright 14 1.1 phx * notice, this list of conditions and the following disclaimer. 15 1.1 phx * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 phx * notice, this list of conditions and the following disclaimer in the 17 1.1 phx * documentation and/or other materials provided with the distribution. 18 1.1 phx * 19 1.1 phx * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 phx * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 phx * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 phx * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 phx * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 phx * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 phx * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 phx * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 phx * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 phx * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 phx * POSSIBILITY OF SUCH DAMAGE. 30 1.1 phx */ 31 1.1 phx 32 1.1 phx /* 33 1.1 phx * Microchip MCP3x0x SAR analog to digital converters. 34 1.1 phx * The driver supports various ADCs with different resolutions, operation 35 1.1 phx * modes and number of input channels. 36 1.1 phx * The reference voltage Vref defaults to the maximum output value in mV, 37 1.1 phx * but can be changed via sysctl(3). 38 1.1 phx * 39 1.1 phx * MCP3001: http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf 40 1.1 phx * MCP3002: http://ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf 41 1.1 phx * MCP3004/3008: http://ww1.microchip.com/downloads/en/DeviceDoc/21295C.pdf 42 1.1 phx * MCP3201: http://ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf 43 1.1 phx * MCP3204/3208: http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf 44 1.1 phx * MCP3301: http://ww1.microchip.com/downloads/en/DeviceDoc/21700E.pdf 45 1.1 phx * MPC3302/3304: http://ww1.microchip.com/downloads/en/DeviceDoc/21697F.pdf 46 1.1 phx */ 47 1.1 phx 48 1.8 thorpej #include "opt_fdt.h" 49 1.8 thorpej 50 1.1 phx #include <sys/param.h> 51 1.1 phx #include <sys/systm.h> 52 1.1 phx #include <sys/device.h> 53 1.1 phx #include <sys/kernel.h> 54 1.1 phx #include <sys/types.h> 55 1.1 phx #include <sys/sysctl.h> 56 1.1 phx 57 1.1 phx #include <dev/sysmon/sysmonvar.h> 58 1.1 phx #include <dev/spi/spivar.h> 59 1.1 phx 60 1.8 thorpej #ifdef FDT 61 1.8 thorpej #include <dev/fdt/fdtvar.h> 62 1.8 thorpej #endif 63 1.8 thorpej 64 1.1 phx #define M3K_MAX_SENSORS 16 /* 8 single-ended & 8 diff. */ 65 1.1 phx 66 1.1 phx /* mcp3x0x model description */ 67 1.1 phx struct mcp3kadc_model { 68 1.1 phx uint32_t name; 69 1.1 phx uint8_t bits; 70 1.1 phx uint8_t channels; 71 1.1 phx uint8_t lead; /* leading bits to ignore */ 72 1.1 phx uint8_t flags; 73 1.1 phx #define M3K_SGLDIFF 0x01 /* single-ended/differential */ 74 1.1 phx #define M3K_D2D1D0 0x02 /* 3 channel select bits */ 75 1.1 phx #define M3K_MSBF 0x04 /* MSBF select bit */ 76 1.1 phx #define M3K_SIGNED 0x80 /* result is signed */ 77 1.1 phx #define M3K_CTRL_NEEDED (M3K_SGLDIFF | M3K_D2D1D0 | M3K_MSBF) 78 1.1 phx }; 79 1.1 phx 80 1.1 phx struct mcp3kadc_softc { 81 1.6 thorpej device_t sc_dev; 82 1.10 thorpej spi_handle_t sc_sh; 83 1.6 thorpej const struct mcp3kadc_model *sc_model; 84 1.6 thorpej uint32_t sc_adc_max; 85 1.6 thorpej int32_t sc_vref_mv; 86 1.8 thorpej #ifdef FDT 87 1.8 thorpej struct fdtbus_regulator *sc_vref_supply; 88 1.8 thorpej #endif 89 1.1 phx 90 1.6 thorpej struct sysmon_envsys *sc_sme; 91 1.6 thorpej envsys_data_t sc_sensors[M3K_MAX_SENSORS]; 92 1.1 phx }; 93 1.1 phx 94 1.1 phx static int mcp3kadc_match(device_t, cfdata_t, void *); 95 1.1 phx static void mcp3kadc_attach(device_t, device_t, void *); 96 1.1 phx static void mcp3kadc_envsys_refresh(struct sysmon_envsys *, 97 1.1 phx envsys_data_t *); 98 1.1 phx static int sysctl_mcp3kadc_vref(SYSCTLFN_ARGS); 99 1.1 phx 100 1.1 phx CFATTACH_DECL_NEW(mcp3kadc, sizeof(struct mcp3kadc_softc), 101 1.1 phx mcp3kadc_match, mcp3kadc_attach, NULL, NULL); 102 1.1 phx 103 1.7 thorpej static const struct mcp3kadc_model mcp3001 = { 104 1.7 thorpej .name = 3001, 105 1.7 thorpej .bits = 10, 106 1.7 thorpej .channels = 1, 107 1.7 thorpej .lead = 3, 108 1.7 thorpej .flags = 0 109 1.1 phx }; 110 1.7 thorpej 111 1.7 thorpej static const struct mcp3kadc_model mcp3002 = { 112 1.7 thorpej .name = 3002, 113 1.7 thorpej .bits = 10, 114 1.7 thorpej .channels = 2, 115 1.7 thorpej .lead = 2, 116 1.7 thorpej .flags = M3K_SGLDIFF | M3K_MSBF 117 1.7 thorpej }; 118 1.7 thorpej 119 1.7 thorpej static const struct mcp3kadc_model mcp3004 = { 120 1.7 thorpej .name = 3004, 121 1.7 thorpej .bits = 10, 122 1.7 thorpej .channels = 4, 123 1.7 thorpej .lead = 2, 124 1.7 thorpej .flags = M3K_SGLDIFF | M3K_D2D1D0 125 1.7 thorpej }; 126 1.7 thorpej 127 1.7 thorpej static const struct mcp3kadc_model mcp3008 = { 128 1.7 thorpej .name = 3008, 129 1.7 thorpej .bits = 10, 130 1.7 thorpej .channels = 8, 131 1.7 thorpej .lead = 2, 132 1.7 thorpej .flags = M3K_SGLDIFF | M3K_D2D1D0 133 1.7 thorpej }; 134 1.7 thorpej 135 1.7 thorpej static const struct mcp3kadc_model mcp3201 = { 136 1.7 thorpej .name = 3201, 137 1.7 thorpej .bits = 12, 138 1.7 thorpej .channels = 1, 139 1.7 thorpej .lead = 3, 140 1.7 thorpej .flags = 0 141 1.7 thorpej }; 142 1.7 thorpej 143 1.7 thorpej static const struct mcp3kadc_model mcp3202 = { 144 1.7 thorpej .name = 3202, 145 1.7 thorpej .bits = 12, 146 1.7 thorpej .channels = 2, 147 1.7 thorpej .lead = 2, 148 1.7 thorpej .flags = M3K_SGLDIFF | M3K_MSBF 149 1.7 thorpej }; 150 1.7 thorpej 151 1.7 thorpej static const struct mcp3kadc_model mcp3204 = { 152 1.7 thorpej .name = 3204, 153 1.7 thorpej .bits = 12, 154 1.7 thorpej .channels = 4, 155 1.7 thorpej .lead = 2, 156 1.7 thorpej .flags = M3K_SGLDIFF | M3K_D2D1D0 157 1.7 thorpej }; 158 1.7 thorpej 159 1.7 thorpej static const struct mcp3kadc_model mcp3208 = { 160 1.7 thorpej .name = 3208, 161 1.7 thorpej .bits = 12, 162 1.7 thorpej .channels = 8, 163 1.7 thorpej .lead = 2, 164 1.7 thorpej .flags = M3K_SGLDIFF | M3K_D2D1D0 165 1.7 thorpej }; 166 1.7 thorpej 167 1.7 thorpej static const struct mcp3kadc_model mcp3301 = { 168 1.7 thorpej .name = 3301, 169 1.7 thorpej .bits = 13, 170 1.7 thorpej .channels = 1, 171 1.7 thorpej .lead = 3, 172 1.7 thorpej .flags = M3K_SIGNED 173 1.7 thorpej }; 174 1.7 thorpej 175 1.7 thorpej static const struct mcp3kadc_model mcp3302 = { 176 1.7 thorpej .name = 3302, 177 1.7 thorpej .bits = 13, 178 1.7 thorpej .channels = 4, 179 1.7 thorpej .lead = 2, 180 1.7 thorpej .flags = M3K_SIGNED | M3K_SGLDIFF | M3K_D2D1D0 181 1.7 thorpej }; 182 1.7 thorpej 183 1.7 thorpej static const struct mcp3kadc_model mcp3304 = { 184 1.7 thorpej .name = 3304, 185 1.7 thorpej .bits = 13, 186 1.7 thorpej .channels = 8, 187 1.7 thorpej .lead = 2, 188 1.7 thorpej .flags = M3K_SIGNED | M3K_SGLDIFF | M3K_D2D1D0 189 1.7 thorpej }; 190 1.7 thorpej 191 1.7 thorpej /* 192 1.7 thorpej * N.B. The order of this table is important! It matches the order 193 1.7 thorpej * of the legacy mcp3k_models[] array, which is used to manually 194 1.7 thorpej * select the device type in the kernel configuration file when 195 1.7 thorpej * direct configuration is not available. 196 1.7 thorpej */ 197 1.7 thorpej static const struct device_compatible_entry compat_data[] = { 198 1.7 thorpej { .compat = "microchip,mcp3001", .data = &mcp3001 }, 199 1.7 thorpej { .compat = "microchip,mcp3002", .data = &mcp3002 }, 200 1.7 thorpej { .compat = "microchip,mcp3004", .data = &mcp3004 }, 201 1.7 thorpej { .compat = "microchip,mcp3008", .data = &mcp3008 }, 202 1.7 thorpej { .compat = "microchip,mcp3201", .data = &mcp3201 }, 203 1.7 thorpej { .compat = "microchip,mcp3202", .data = &mcp3202 }, 204 1.7 thorpej { .compat = "microchip,mcp3204", .data = &mcp3204 }, 205 1.7 thorpej { .compat = "microchip,mcp3208", .data = &mcp3208 }, 206 1.7 thorpej { .compat = "microchip,mcp3301", .data = &mcp3301 }, 207 1.7 thorpej { .compat = "microchip,mcp3302", .data = &mcp3302 }, 208 1.7 thorpej { .compat = "microchip,mcp3304", .data = &mcp3304 }, 209 1.7 thorpej 210 1.7 thorpej #if 0 /* We should also add support for these: */ 211 1.7 thorpej { .compat = "microchip,mcp3550-50" }, 212 1.7 thorpej { .compat = "microchip,mcp3550-60" }, 213 1.7 thorpej { .compat = "microchip,mcp3551" }, 214 1.7 thorpej { .compat = "microchip,mcp3553" }, 215 1.7 thorpej #endif 216 1.7 thorpej 217 1.7 thorpej DEVICE_COMPAT_EOL 218 1.7 thorpej }; 219 1.7 thorpej static const int mcp3k_nmodels = __arraycount(compat_data) - 1; 220 1.6 thorpej 221 1.6 thorpej static const struct mcp3kadc_model * 222 1.7 thorpej mcp3kadc_lookup(const struct spi_attach_args *sa, const cfdata_t cf) 223 1.6 thorpej { 224 1.9 thorpej const struct mcp3kadc_model *model = NULL; 225 1.9 thorpej const struct device_compatible_entry *dce = 226 1.9 thorpej spi_compatible_lookup(sa, compat_data); 227 1.9 thorpej 228 1.9 thorpej if (dce != NULL) { 229 1.9 thorpej model = dce->data; 230 1.9 thorpej } else if (cf->cf_flags >= 0 && cf->cf_flags < mcp3k_nmodels) { 231 1.9 thorpej model = compat_data[cf->cf_flags].data; 232 1.6 thorpej } 233 1.9 thorpej return model; 234 1.6 thorpej } 235 1.1 phx 236 1.1 phx static int 237 1.1 phx mcp3kadc_match(device_t parent, cfdata_t cf, void *aux) 238 1.1 phx { 239 1.7 thorpej struct spi_attach_args *sa = aux; 240 1.7 thorpej int match_result; 241 1.1 phx 242 1.7 thorpej if (spi_use_direct_match(sa, compat_data, &match_result)) { 243 1.7 thorpej return match_result; 244 1.7 thorpej } 245 1.1 phx 246 1.6 thorpej /* 247 1.6 thorpej * If we're doing indirect config, the user must 248 1.6 thorpej * have specified a valid model. 249 1.6 thorpej */ 250 1.9 thorpej if (mcp3kadc_lookup(sa, cf) == NULL) { 251 1.6 thorpej return 0; 252 1.6 thorpej } 253 1.6 thorpej 254 1.7 thorpej return SPI_MATCH_DEFAULT; 255 1.1 phx } 256 1.1 phx 257 1.8 thorpej #ifdef FDT 258 1.8 thorpej static bool 259 1.8 thorpej mcp3kadc_vref_fdt(struct mcp3kadc_softc *sc) 260 1.8 thorpej { 261 1.8 thorpej devhandle_t devhandle = device_handle(sc->sc_dev); 262 1.8 thorpej int phandle = devhandle_to_of(devhandle); 263 1.8 thorpej int error; 264 1.8 thorpej u_int uvolts; 265 1.8 thorpej 266 1.8 thorpej sc->sc_vref_supply = fdtbus_regulator_acquire(phandle, "vref-supply"); 267 1.8 thorpej if (sc->sc_vref_supply == NULL) { 268 1.8 thorpej aprint_error_dev(sc->sc_dev, 269 1.8 thorpej "unable to acquire \"vref-supply\"\n"); 270 1.8 thorpej return false; 271 1.8 thorpej } 272 1.8 thorpej 273 1.8 thorpej error = fdtbus_regulator_enable(sc->sc_vref_supply); 274 1.8 thorpej if (error) { 275 1.8 thorpej aprint_error_dev(sc->sc_dev, 276 1.8 thorpej "failed to enable \"vref-supply\" (error = %d)\n", 277 1.8 thorpej error); 278 1.8 thorpej return false; 279 1.8 thorpej } 280 1.8 thorpej 281 1.8 thorpej error = fdtbus_regulator_get_voltage(sc->sc_vref_supply, &uvolts); 282 1.8 thorpej if (error) { 283 1.8 thorpej aprint_error_dev(sc->sc_dev, 284 1.8 thorpej "unable to get \"vref-supply\" voltage (error = %d)\n", 285 1.8 thorpej error); 286 1.8 thorpej (void) fdtbus_regulator_disable(sc->sc_vref_supply); 287 1.8 thorpej return false; 288 1.8 thorpej } 289 1.8 thorpej 290 1.8 thorpej /* 291 1.8 thorpej * Device tree property is uV, convert to mV that we use 292 1.8 thorpej * internally. 293 1.8 thorpej */ 294 1.8 thorpej sc->sc_vref_mv = uvolts / 1000; 295 1.8 thorpej return true; 296 1.8 thorpej } 297 1.8 thorpej #endif /* FDT */ 298 1.8 thorpej 299 1.1 phx static void 300 1.1 phx mcp3kadc_attach(device_t parent, device_t self, void *aux) 301 1.1 phx { 302 1.1 phx const struct sysctlnode *rnode, *node; 303 1.6 thorpej struct spi_attach_args *sa = aux; 304 1.9 thorpej struct mcp3kadc_softc *sc = device_private(self); 305 1.8 thorpej devhandle_t devhandle = device_handle(self); 306 1.6 thorpej const struct mcp3kadc_model *model; 307 1.3 thorpej int error, ch, i; 308 1.8 thorpej bool vref_read_only; 309 1.1 phx 310 1.1 phx sc->sc_dev = self; 311 1.1 phx sc->sc_sh = sa->sa_handle; 312 1.1 phx 313 1.7 thorpej model = mcp3kadc_lookup(sa, device_cfdata(self)); 314 1.6 thorpej KASSERT(model != NULL); 315 1.6 thorpej 316 1.6 thorpej sc->sc_model = model; 317 1.1 phx 318 1.1 phx aprint_naive(": Analog to Digital converter\n"); 319 1.1 phx aprint_normal(": MCP%u %u-channel %u-bit ADC\n", 320 1.1 phx (unsigned)model->name, (unsigned)model->channels, 321 1.1 phx (unsigned)model->bits); 322 1.1 phx 323 1.3 thorpej /* configure for 1MHz */ 324 1.5 thorpej error = spi_configure(self, sa->sa_handle, SPI_MODE_0, SPI_FREQ_MHz(1)); 325 1.3 thorpej if (error) { 326 1.3 thorpej return; 327 1.3 thorpej } 328 1.3 thorpej 329 1.8 thorpej vref_read_only = false; 330 1.8 thorpej switch (devhandle_type(devhandle)) { 331 1.8 thorpej #ifdef FDT 332 1.8 thorpej case DEVHANDLE_TYPE_OF: 333 1.8 thorpej vref_read_only = mcp3kadc_vref_fdt(sc); 334 1.8 thorpej if (! vref_read_only) { 335 1.8 thorpej /* Error already displayed. */ 336 1.8 thorpej return; 337 1.8 thorpej } 338 1.8 thorpej break; 339 1.8 thorpej #endif /* FDT */ 340 1.8 thorpej default: 341 1.8 thorpej /* 342 1.8 thorpej * Set a default Vref in mV according to the chip's ADC 343 1.8 thorpej * resolution. 344 1.8 thorpej */ 345 1.8 thorpej sc->sc_vref_mv = 1 << ((model->flags & M3K_SIGNED) ? 346 1.8 thorpej model->bits - 1 : model->bits); 347 1.8 thorpej break; 348 1.8 thorpej } 349 1.1 phx 350 1.1 phx /* remember maximum value for this ADC - also used for masking */ 351 1.1 phx sc->sc_adc_max = (1 << model->bits) - 1; 352 1.1 phx 353 1.1 phx /* attach voltage sensors to envsys */ 354 1.1 phx sc->sc_sme = sysmon_envsys_create(); 355 1.1 phx 356 1.1 phx /* adc difference from two neighbouring channels */ 357 1.1 phx for (ch = 0; ch < model->channels; ch++) { 358 1.1 phx KASSERT(ch < M3K_MAX_SENSORS); 359 1.1 phx sc->sc_sensors[ch].units = ENVSYS_SVOLTS_DC; 360 1.1 phx sc->sc_sensors[ch].state = ENVSYS_SINVALID; 361 1.1 phx if (model->channels == 1) 362 1.1 phx strlcpy(sc->sc_sensors[ch].desc, "adc diff ch0", 363 1.1 phx sizeof(sc->sc_sensors[ch].desc)); 364 1.1 phx else 365 1.1 phx snprintf(sc->sc_sensors[ch].desc, 366 1.1 phx sizeof(sc->sc_sensors[ch].desc), 367 1.1 phx "adc diff ch%d-ch%d", ch, ch ^ 1); 368 1.1 phx sc->sc_sensors[ch].private = ch; 369 1.1 phx sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensors[ch]); 370 1.1 phx } 371 1.1 phx 372 1.1 phx if (model->flags & M3K_SGLDIFF) { 373 1.1 phx /* adc from single ended channels */ 374 1.1 phx for (i = 0; i < model->channels; i++, ch++) { 375 1.1 phx KASSERT(ch < M3K_MAX_SENSORS); 376 1.1 phx sc->sc_sensors[ch].units = ENVSYS_SVOLTS_DC; 377 1.1 phx sc->sc_sensors[ch].state = ENVSYS_SINVALID; 378 1.1 phx snprintf(sc->sc_sensors[ch].desc, 379 1.1 phx sizeof(sc->sc_sensors[ch].desc), 380 1.1 phx "adc single ch%d", i); 381 1.1 phx sc->sc_sensors[ch].private = ch; 382 1.1 phx sysmon_envsys_sensor_attach(sc->sc_sme, 383 1.1 phx &sc->sc_sensors[ch]); 384 1.1 phx } 385 1.1 phx } 386 1.1 phx 387 1.1 phx sc->sc_sme->sme_name = device_xname(self); 388 1.1 phx sc->sc_sme->sme_refresh = mcp3kadc_envsys_refresh; 389 1.1 phx sc->sc_sme->sme_cookie = sc; 390 1.1 phx if (sysmon_envsys_register(sc->sc_sme)) { 391 1.1 phx aprint_error_dev(self, "unable to register with sysmon\n"); 392 1.1 phx sysmon_envsys_destroy(sc->sc_sme); 393 1.1 phx } 394 1.1 phx 395 1.1 phx /* create a sysctl node for adjusting the ADC's reference voltage */ 396 1.1 phx rnode = node = NULL; 397 1.1 phx sysctl_createv(NULL, 0, NULL, &rnode, 398 1.1 phx CTLFLAG_READWRITE, 399 1.1 phx CTLTYPE_NODE, device_xname(sc->sc_dev), NULL, 400 1.1 phx NULL, 0, NULL, 0, 401 1.1 phx CTL_HW, CTL_CREATE, CTL_EOL); 402 1.1 phx 403 1.8 thorpej const int ctlflag = vref_read_only ? CTLFLAG_READONLY 404 1.8 thorpej : CTLFLAG_READWRITE; 405 1.8 thorpej 406 1.1 phx if (rnode != NULL) 407 1.1 phx sysctl_createv(NULL, 0, NULL, &node, 408 1.8 thorpej ctlflag | CTLFLAG_OWNDESC, 409 1.1 phx CTLTYPE_INT, "vref", 410 1.1 phx SYSCTL_DESCR("ADC reference voltage"), 411 1.1 phx sysctl_mcp3kadc_vref, 0, (void *)sc, 0, 412 1.1 phx CTL_HW, rnode->sysctl_num, CTL_CREATE, CTL_EOL); 413 1.1 phx } 414 1.1 phx 415 1.1 phx static void 416 1.1 phx mcp3kadc_envsys_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 417 1.1 phx { 418 1.1 phx struct mcp3kadc_softc *sc; 419 1.6 thorpej const struct mcp3kadc_model *model; 420 1.1 phx uint8_t buf[2], ctrl; 421 1.1 phx int32_t val, scale; 422 1.1 phx 423 1.1 phx sc = sme->sme_cookie; 424 1.6 thorpej model = sc->sc_model; 425 1.1 phx scale = sc->sc_adc_max + 1; 426 1.1 phx 427 1.1 phx if (model->flags & M3K_CTRL_NEEDED) { 428 1.1 phx /* we need to send some control bits first */ 429 1.1 phx ctrl = 1; /* start bit */ 430 1.1 phx 431 1.1 phx if (model->flags & M3K_SGLDIFF) { 432 1.1 phx /* bit set to select single-ended mode */ 433 1.1 phx ctrl <<= 1; 434 1.1 phx ctrl |= edata->private >= model->channels; 435 1.1 phx } 436 1.1 phx 437 1.1 phx if (model->flags & M3K_D2D1D0) { 438 1.1 phx /* 3 bits select the channel */ 439 1.1 phx ctrl <<= 3; 440 1.1 phx ctrl |= edata->private & (model->channels - 1); 441 1.1 phx } else { 442 1.1 phx /* 1 bit selects between two channels */ 443 1.1 phx ctrl <<= 1; 444 1.1 phx ctrl |= edata->private & 1; 445 1.1 phx } 446 1.1 phx 447 1.1 phx if (model->flags & M3K_MSBF) { 448 1.1 phx /* bit select MSB first format */ 449 1.1 phx ctrl <<= 1; 450 1.1 phx ctrl |= 1; 451 1.1 phx } 452 1.1 phx 453 1.1 phx /* send control bits, receive ADC data */ 454 1.1 phx if (spi_send_recv(sc->sc_sh, 1, &ctrl, 2, buf) != 0) { 455 1.1 phx edata->state = ENVSYS_SINVALID; 456 1.1 phx return; 457 1.1 phx } 458 1.1 phx } else { 459 1.1 phx 460 1.1 phx /* just read data from the ADC */ 461 1.1 phx if (spi_recv(sc->sc_sh, 2, buf) != 0) { 462 1.1 phx edata->state = ENVSYS_SINVALID; 463 1.1 phx return; 464 1.1 phx } 465 1.1 phx } 466 1.1 phx 467 1.1 phx /* extract big-endian ADC data from buffer */ 468 1.1 phx val = (buf[0] << 8) | buf[1]; 469 1.1 phx val = (val >> (16 - (model->bits + model->lead))) & sc->sc_adc_max; 470 1.1 phx 471 1.1 phx /* sign-extend the result, when needed */ 472 1.1 phx if (model->flags & M3K_SIGNED) { 473 1.1 phx if (val & (1 << (model->bits - 1))) 474 1.1 phx val -= sc->sc_adc_max + 1; 475 1.1 phx scale >>= 1; /* MSB is the sign */ 476 1.1 phx } 477 1.1 phx 478 1.1 phx /* scale the value for Vref and convert to mV */ 479 1.1 phx edata->value_cur = (sc->sc_vref_mv * val / scale) * 1000; 480 1.1 phx edata->state = ENVSYS_SVALID; 481 1.1 phx } 482 1.1 phx 483 1.1 phx static int 484 1.1 phx sysctl_mcp3kadc_vref(SYSCTLFN_ARGS) 485 1.1 phx { 486 1.1 phx struct sysctlnode node; 487 1.1 phx struct mcp3kadc_softc *sc; 488 1.1 phx int32_t t; 489 1.1 phx int error; 490 1.1 phx 491 1.1 phx node = *rnode; 492 1.1 phx sc = node.sysctl_data; 493 1.1 phx 494 1.1 phx t = sc->sc_vref_mv; 495 1.1 phx node.sysctl_data = &t; 496 1.1 phx 497 1.1 phx error = sysctl_lookup(SYSCTLFN_CALL(&node)); 498 1.1 phx if (error || newp == NULL) 499 1.1 phx return error; 500 1.1 phx if (t <= 0) 501 1.1 phx return EINVAL; 502 1.1 phx 503 1.1 phx sc->sc_vref_mv = t; 504 1.1 phx return 0; 505 1.1 phx } 506