1 /* $NetBSD: owtemp.c,v 1.19 2019/11/30 23:06:52 ad 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.19 2019/11/30 23:06:52 ad 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 62 #include <dev/sysmon/sysmonvar.h> 63 64 #include <dev/onewire/onewiredevs.h> 65 #include <dev/onewire/onewirereg.h> 66 #include <dev/onewire/onewirevar.h> 67 68 #define DS_CMD_CONVERT 0x44 69 #define DS_CMD_READ_SCRATCHPAD 0xbe 70 71 struct owtemp_softc { 72 device_t sc_dv; 73 void *sc_onewire; 74 u_int64_t sc_rom; 75 const char *sc_chipname; 76 77 envsys_data_t sc_sensor; 78 struct sysmon_envsys *sc_sme; 79 80 uint32_t (*sc_owtemp_decode)(const uint8_t *); 81 82 int sc_dying; 83 84 struct evcnt sc_ev_update; 85 struct evcnt sc_ev_rsterr; 86 struct evcnt sc_ev_crcerr; 87 }; 88 89 static int owtemp_match(device_t, cfdata_t, void *); 90 static void owtemp_attach(device_t, device_t, void *); 91 static int owtemp_detach(device_t, int); 92 static int owtemp_activate(device_t, enum devact); 93 static bool owtemp_update(struct owtemp_softc *sc, uint32_t *temp); 94 static void owtemp_refresh(struct sysmon_envsys *, envsys_data_t *); 95 static uint32_t owtemp_decode_ds18b20(const uint8_t *); 96 static uint32_t owtemp_decode_ds1920(const uint8_t *); 97 98 CFATTACH_DECL_NEW(owtemp, sizeof(struct owtemp_softc), 99 owtemp_match, owtemp_attach, owtemp_detach, owtemp_activate); 100 101 extern struct cfdriver owtemp_cd; 102 103 static const struct onewire_matchfam owtemp_fams[] = { 104 { ONEWIRE_FAMILY_DS1920 }, /* also DS1820 */ 105 { ONEWIRE_FAMILY_DS18B20 }, 106 { ONEWIRE_FAMILY_DS1822 }, 107 }; 108 109 int owtemp_retries = 3; 110 111 static int 112 owtemp_match(device_t parent, cfdata_t match, void *aux) 113 { 114 return (onewire_matchbyfam(aux, owtemp_fams, 115 __arraycount(owtemp_fams))); 116 } 117 118 static void 119 owtemp_attach(device_t parent, device_t self, void *aux) 120 { 121 struct owtemp_softc *sc = device_private(self); 122 struct onewire_attach_args *oa = aux; 123 124 aprint_naive("\n"); 125 126 sc->sc_dv = self; 127 sc->sc_onewire = oa->oa_onewire; 128 sc->sc_rom = oa->oa_rom; 129 130 switch(ONEWIRE_ROM_FAMILY_TYPE(sc->sc_rom)) { 131 case ONEWIRE_FAMILY_DS18B20: 132 sc->sc_chipname = "DS18B20"; 133 sc->sc_owtemp_decode = owtemp_decode_ds18b20; 134 break; 135 case ONEWIRE_FAMILY_DS1822: 136 sc->sc_chipname = "DS1822"; 137 sc->sc_owtemp_decode = owtemp_decode_ds18b20; 138 break; 139 case ONEWIRE_FAMILY_DS1920: 140 sc->sc_chipname = "DS1920"; 141 sc->sc_owtemp_decode = owtemp_decode_ds1920; 142 break; 143 } 144 145 evcnt_attach_dynamic(&sc->sc_ev_update, EVCNT_TYPE_MISC, NULL, 146 device_xname(self), "update"); 147 evcnt_attach_dynamic(&sc->sc_ev_rsterr, EVCNT_TYPE_MISC, NULL, 148 device_xname(self), "reset error"); 149 evcnt_attach_dynamic(&sc->sc_ev_crcerr, EVCNT_TYPE_MISC, NULL, 150 device_xname(self), "crc error"); 151 152 sc->sc_sme = sysmon_envsys_create(); 153 154 /* Initialize sensor */ 155 sc->sc_sensor.units = ENVSYS_STEMP; 156 sc->sc_sensor.state = ENVSYS_SINVALID; 157 (void)strlcpy(sc->sc_sensor.desc, 158 device_xname(self), sizeof(sc->sc_sensor.desc)); 159 (void)snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), 160 "%s S/N %012" PRIx64, sc->sc_chipname, ONEWIRE_ROM_SN(sc->sc_rom)); 161 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 162 sysmon_envsys_destroy(sc->sc_sme); 163 return; 164 } 165 166 /* Hook into system monitor. */ 167 sc->sc_sme->sme_name = device_xname(self); 168 sc->sc_sme->sme_cookie = sc; 169 sc->sc_sme->sme_refresh = owtemp_refresh; 170 171 if (sysmon_envsys_register(sc->sc_sme)) { 172 aprint_error_dev(self, "unable to register with sysmon\n"); 173 sysmon_envsys_destroy(sc->sc_sme); 174 return; 175 } 176 177 aprint_normal("\n"); 178 } 179 180 static int 181 owtemp_detach(device_t self, int flags) 182 { 183 struct owtemp_softc *sc = device_private(self); 184 185 sysmon_envsys_unregister(sc->sc_sme); 186 evcnt_detach(&sc->sc_ev_rsterr); 187 evcnt_detach(&sc->sc_ev_update); 188 evcnt_detach(&sc->sc_ev_crcerr); 189 190 return 0; 191 } 192 193 static int 194 owtemp_activate(device_t self, enum devact act) 195 { 196 struct owtemp_softc *sc = device_private(self); 197 198 switch (act) { 199 case DVACT_DEACTIVATE: 200 sc->sc_dying = 1; 201 return 0; 202 default: 203 return EOPNOTSUPP; 204 } 205 } 206 207 static bool 208 owtemp_update(struct owtemp_softc *sc, uint32_t *temp) 209 { 210 u_int8_t data[9]; 211 212 sc->sc_ev_update.ev_count++; 213 214 /* 215 * Start temperature conversion. The conversion takes up to 750ms. 216 * After sending the command, the data line must be held high for 217 * at least 750ms to provide power during the conversion process. 218 * As such, no other activity may take place on the 1-Wire bus for 219 * at least this period. Keep the parent bus locked while waiting. 220 */ 221 if (onewire_reset(sc->sc_onewire) != 0) { 222 sc->sc_ev_rsterr.ev_count++; 223 return false; 224 } 225 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 226 onewire_write_byte(sc->sc_onewire, DS_CMD_CONVERT); 227 (void)kpause("owtemp", false, mstohz(750 + 10), NULL); 228 229 /* 230 * The result of the temperature measurement is placed in the 231 * first two bytes of the scratchpad. Perform the caculation 232 * only if the CRC is correct. 233 */ 234 if (onewire_reset(sc->sc_onewire) != 0) { 235 sc->sc_ev_rsterr.ev_count++; 236 return false; 237 } 238 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 239 onewire_write_byte(sc->sc_onewire, DS_CMD_READ_SCRATCHPAD); 240 onewire_read_block(sc->sc_onewire, data, 9); 241 if (onewire_crc(data, 8) != data[8]) { 242 sc->sc_ev_crcerr.ev_count++; 243 return false; 244 } 245 *temp = sc->sc_owtemp_decode(data); 246 return true; 247 } 248 249 static void 250 owtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 251 { 252 struct owtemp_softc *sc = sme->sme_cookie; 253 uint32_t reading; 254 int retry; 255 256 onewire_lock(sc->sc_onewire); 257 for (retry = 0; retry < owtemp_retries; retry++) { 258 if (owtemp_update(sc, &reading)) { 259 onewire_unlock(sc->sc_onewire); 260 sc->sc_sensor.value_cur = reading; 261 sc->sc_sensor.state = ENVSYS_SVALID; 262 return; 263 } 264 } 265 onewire_unlock(sc->sc_onewire); 266 aprint_error_dev(sc->sc_dv, 267 "update failed - use vmstat(8) to check event counters\n"); 268 sc->sc_sensor.state = ENVSYS_SINVALID; 269 } 270 271 static uint32_t 272 owtemp_decode_ds18b20(const uint8_t *buf) 273 { 274 int temp; 275 276 /* 277 * Sign-extend the MSB byte, and add in the fractions of a 278 * degree contained in the LSB (precision 1/16th DegC). 279 */ 280 temp = (int8_t)buf[1]; 281 temp = (temp << 8) | buf[0]; 282 283 /* 284 * Conversion to uK is simple. 285 */ 286 return (temp * 62500 + 273150000); 287 } 288 289 static uint32_t 290 owtemp_decode_ds1920(const uint8_t *buf) 291 { 292 int temp; 293 294 /* 295 * Sign-extend the MSB byte, and add in the fractions of a 296 * degree contained in the LSB (precision 1/2 DegC). 297 */ 298 temp = (int8_t)buf[1]; 299 temp = (temp << 8) | buf[0]; 300 301 if (buf[7] != 0) { 302 /* 303 * interpolate for higher precision using the count registers 304 * 305 * buf[7]: COUNT_PER_C(elsius) 306 * buf[6]: COUNT_REMAIN 307 * 308 * T = TEMP - 0.25 + (COUNT_PER_C - COUNT_REMAIN) / COUNT_PER_C 309 */ 310 temp &= ~1; 311 temp += 500000 * temp + (500000 * (buf[7] - buf[6])) / buf[7] - 250000; 312 } else { 313 temp *= 500000; 314 } 315 316 /* convert to uK */ 317 return (temp + 273150000); 318 } 319