1 /* $NetBSD: owtemp.c,v 1.22 2023/12/11 13:30:33 mlelstv Exp $ */ 2 /* $OpenBSD: owtemp.c,v 1.1 2006/03/04 16:27:03 grange Exp $ */ 3 4 /*- 5 * Copyright (c) 2019 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Andrew Doran. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Copyright (c) 2006 Alexander Yurchenko <grange@openbsd.org> 35 * 36 * Permission to use, copy, modify, and distribute this software for any 37 * purpose with or without fee is hereby granted, provided that the above 38 * copyright notice and this permission notice appear in all copies. 39 * 40 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 41 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 42 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 43 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 44 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 45 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 46 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 47 */ 48 49 /* 50 * 1-Wire temperature family type device driver. 51 */ 52 53 #include <sys/cdefs.h> 54 __KERNEL_RCSID(0, "$NetBSD: owtemp.c,v 1.22 2023/12/11 13:30:33 mlelstv Exp $"); 55 56 #include <sys/param.h> 57 #include <sys/systm.h> 58 #include <sys/device.h> 59 #include <sys/kernel.h> 60 #include <sys/proc.h> 61 #include <sys/module.h> 62 63 #include <dev/sysmon/sysmonvar.h> 64 65 #include <dev/onewire/onewiredevs.h> 66 #include <dev/onewire/onewirereg.h> 67 #include <dev/onewire/onewirevar.h> 68 69 #define DS_CMD_CONVERT 0x44 70 #define DS_CMD_READ_SCRATCHPAD 0xbe 71 72 struct owtemp_softc { 73 device_t sc_dv; 74 void *sc_onewire; 75 u_int64_t sc_rom; 76 const char *sc_chipname; 77 78 envsys_data_t sc_sensor; 79 struct sysmon_envsys *sc_sme; 80 81 uint32_t (*sc_owtemp_decode)(const uint8_t *); 82 83 int sc_dying; 84 85 struct evcnt sc_ev_update; 86 struct evcnt sc_ev_rsterr; 87 struct evcnt sc_ev_crcerr; 88 }; 89 90 static int owtemp_match(device_t, cfdata_t, void *); 91 static void owtemp_attach(device_t, device_t, void *); 92 static int owtemp_detach(device_t, int); 93 static int owtemp_activate(device_t, enum devact); 94 static bool owtemp_update(struct owtemp_softc *sc, uint32_t *temp); 95 static void owtemp_refresh(struct sysmon_envsys *, envsys_data_t *); 96 static uint32_t owtemp_decode_ds18b20(const uint8_t *); 97 static uint32_t owtemp_decode_ds1920(const uint8_t *); 98 99 CFATTACH_DECL_NEW(owtemp, sizeof(struct owtemp_softc), 100 owtemp_match, owtemp_attach, owtemp_detach, owtemp_activate); 101 102 extern struct cfdriver owtemp_cd; 103 104 static const struct onewire_matchfam owtemp_fams[] = { 105 { ONEWIRE_FAMILY_DS1920 }, /* also DS1820 */ 106 { ONEWIRE_FAMILY_DS18B20 }, 107 { ONEWIRE_FAMILY_DS1822 }, 108 }; 109 110 int owtemp_retries = 3; 111 112 static int 113 owtemp_match(device_t parent, cfdata_t match, void *aux) 114 { 115 return (onewire_matchbyfam(aux, owtemp_fams, 116 __arraycount(owtemp_fams))); 117 } 118 119 static void 120 owtemp_attach(device_t parent, device_t self, void *aux) 121 { 122 struct owtemp_softc *sc = device_private(self); 123 struct onewire_attach_args *oa = aux; 124 125 aprint_naive("\n"); 126 127 sc->sc_dv = self; 128 sc->sc_onewire = oa->oa_onewire; 129 sc->sc_rom = oa->oa_rom; 130 131 switch(ONEWIRE_ROM_FAMILY_TYPE(sc->sc_rom)) { 132 case ONEWIRE_FAMILY_DS18B20: 133 sc->sc_chipname = "DS18B20"; 134 sc->sc_owtemp_decode = owtemp_decode_ds18b20; 135 break; 136 case ONEWIRE_FAMILY_DS1822: 137 sc->sc_chipname = "DS1822"; 138 sc->sc_owtemp_decode = owtemp_decode_ds18b20; 139 break; 140 case ONEWIRE_FAMILY_DS1920: 141 sc->sc_chipname = "DS1920"; 142 sc->sc_owtemp_decode = owtemp_decode_ds1920; 143 break; 144 } 145 146 evcnt_attach_dynamic(&sc->sc_ev_update, EVCNT_TYPE_MISC, NULL, 147 device_xname(self), "update"); 148 evcnt_attach_dynamic(&sc->sc_ev_rsterr, EVCNT_TYPE_MISC, NULL, 149 device_xname(self), "reset error"); 150 evcnt_attach_dynamic(&sc->sc_ev_crcerr, EVCNT_TYPE_MISC, NULL, 151 device_xname(self), "crc error"); 152 153 sc->sc_sme = sysmon_envsys_create(); 154 155 /* Initialize sensor */ 156 sc->sc_sensor.units = ENVSYS_STEMP; 157 sc->sc_sensor.state = ENVSYS_SINVALID; 158 (void)strlcpy(sc->sc_sensor.desc, 159 device_xname(self), sizeof(sc->sc_sensor.desc)); 160 (void)snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), 161 "%s S/N %012" PRIx64, sc->sc_chipname, ONEWIRE_ROM_SN(sc->sc_rom)); 162 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 163 sysmon_envsys_destroy(sc->sc_sme); 164 sc->sc_sme = NULL; 165 return; 166 } 167 168 /* Hook into system monitor. */ 169 sc->sc_sme->sme_name = device_xname(self); 170 sc->sc_sme->sme_cookie = sc; 171 sc->sc_sme->sme_refresh = owtemp_refresh; 172 173 if (sysmon_envsys_register(sc->sc_sme)) { 174 aprint_error_dev(self, "unable to register with sysmon\n"); 175 sysmon_envsys_destroy(sc->sc_sme); 176 sc->sc_sme = NULL; 177 return; 178 } 179 180 aprint_normal("\n"); 181 } 182 183 static int 184 owtemp_detach(device_t self, int flags) 185 { 186 struct owtemp_softc *sc = device_private(self); 187 188 if (sc->sc_sme != NULL) 189 sysmon_envsys_unregister(sc->sc_sme); 190 evcnt_detach(&sc->sc_ev_rsterr); 191 evcnt_detach(&sc->sc_ev_update); 192 evcnt_detach(&sc->sc_ev_crcerr); 193 194 return 0; 195 } 196 197 static int 198 owtemp_activate(device_t self, enum devact act) 199 { 200 struct owtemp_softc *sc = device_private(self); 201 202 switch (act) { 203 case DVACT_DEACTIVATE: 204 sc->sc_dying = 1; 205 return 0; 206 default: 207 return EOPNOTSUPP; 208 } 209 } 210 211 static bool 212 owtemp_update(struct owtemp_softc *sc, uint32_t *temp) 213 { 214 u_int8_t data[9]; 215 216 sc->sc_ev_update.ev_count++; 217 218 /* 219 * Start temperature conversion. The conversion takes up to 750ms. 220 * After sending the command, the data line must be held high for 221 * at least 750ms to provide power during the conversion process. 222 * As such, no other activity may take place on the 1-Wire bus for 223 * at least this period. Keep the parent bus locked while waiting. 224 */ 225 if (onewire_reset(sc->sc_onewire) != 0) { 226 sc->sc_ev_rsterr.ev_count++; 227 return false; 228 } 229 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 230 onewire_write_byte(sc->sc_onewire, DS_CMD_CONVERT); 231 (void)kpause("owtemp", false, mstohz(750 + 10), NULL); 232 233 /* 234 * The result of the temperature measurement is placed in the 235 * first two bytes of the scratchpad. Perform the caculation 236 * only if the CRC is correct. 237 */ 238 if (onewire_reset(sc->sc_onewire) != 0) { 239 sc->sc_ev_rsterr.ev_count++; 240 return false; 241 } 242 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 243 onewire_write_byte(sc->sc_onewire, DS_CMD_READ_SCRATCHPAD); 244 onewire_read_block(sc->sc_onewire, data, 9); 245 if (onewire_crc(data, 8) != data[8]) { 246 sc->sc_ev_crcerr.ev_count++; 247 return false; 248 } 249 *temp = sc->sc_owtemp_decode(data); 250 return true; 251 } 252 253 static void 254 owtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 255 { 256 struct owtemp_softc *sc = sme->sme_cookie; 257 uint32_t reading; 258 int retry; 259 260 onewire_lock(sc->sc_onewire); 261 for (retry = 0; retry < owtemp_retries; retry++) { 262 if (owtemp_update(sc, &reading)) { 263 onewire_unlock(sc->sc_onewire); 264 sc->sc_sensor.value_cur = reading; 265 sc->sc_sensor.state = ENVSYS_SVALID; 266 return; 267 } 268 } 269 onewire_unlock(sc->sc_onewire); 270 aprint_error_dev(sc->sc_dv, 271 "update failed - use vmstat(8) to check event counters\n"); 272 sc->sc_sensor.state = ENVSYS_SINVALID; 273 } 274 275 static uint32_t 276 owtemp_decode_ds18b20(const uint8_t *buf) 277 { 278 int temp; 279 280 /* 281 * Sign-extend the MSB byte, and add in the fractions of a 282 * degree contained in the LSB (precision 1/16th DegC). 283 */ 284 temp = (int8_t)buf[1]; 285 temp = (temp << 8) | buf[0]; 286 287 /* 288 * Conversion to uK is simple. 289 */ 290 return (temp * 62500 + 273150000); 291 } 292 293 static uint32_t 294 owtemp_decode_ds1920(const uint8_t *buf) 295 { 296 int temp; 297 298 /* 299 * Sign-extend the MSB byte, and add in the fractions of a 300 * degree contained in the LSB (precision 1/2 DegC). 301 */ 302 temp = (int8_t)buf[1]; 303 temp = (temp << 8) | buf[0]; 304 305 if (buf[7] != 0) { 306 /* 307 * interpolate for higher precision using the count registers 308 * 309 * buf[7]: COUNT_PER_C(elsius) 310 * buf[6]: COUNT_REMAIN 311 * 312 * T = TEMP - 0.25 + (COUNT_PER_C - COUNT_REMAIN) / COUNT_PER_C 313 */ 314 temp &= ~1; 315 temp += 500000 * temp + (500000 * (buf[7] - buf[6])) / buf[7] - 250000; 316 } else { 317 temp *= 500000; 318 } 319 320 /* convert to uK */ 321 return (temp + 273150000); 322 } 323 324 MODULE(MODULE_CLASS_DRIVER, owtemp, "onewire"); 325 326 #ifdef _MODULE 327 #include "ioconf.c" 328 #endif 329 330 static int 331 owtemp_modcmd(modcmd_t cmd, void *opaque) 332 { 333 int error; 334 335 error = 0; 336 switch (cmd) { 337 case MODULE_CMD_INIT: 338 #ifdef _MODULE 339 error = config_init_component(cfdriver_ioconf_owtemp, 340 cfattach_ioconf_owtemp, cfdata_ioconf_owtemp); 341 if (error) 342 aprint_error("%s: unable to init component\n", 343 owtemp_cd.cd_name); 344 #endif 345 break; 346 case MODULE_CMD_FINI: 347 #ifdef _MODULE 348 config_fini_component(cfdriver_ioconf_owtemp, 349 cfattach_ioconf_owtemp, cfdata_ioconf_owtemp); 350 #endif 351 break; 352 default: 353 error = ENOTTY; 354 } 355 return error; 356 } 357