1 /* $OpenBSD: owtemp.c,v 1.16 2014/09/14 14:17:25 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2006, 2009 Alexander Yurchenko <grange@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * 1-Wire temperature family type device driver. 21 */ 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/device.h> 26 #include <sys/kernel.h> 27 #include <sys/rwlock.h> 28 #include <sys/sensors.h> 29 30 #include <dev/onewire/onewiredevs.h> 31 #include <dev/onewire/onewirereg.h> 32 #include <dev/onewire/onewirevar.h> 33 34 /* Commands */ 35 #define DS1920_CMD_CONVERT 0x44 36 #define DS1920_CMD_READ_SCRATCHPAD 0xbe 37 38 /* Scratchpad layout */ 39 #define DS1920_SP_TEMP_LSB 0 40 #define DS1920_SP_TEMP_MSB 1 41 #define DS1920_SP_TH 2 42 #define DS1920_SP_TL 3 43 #define DS18B20_SP_CONFIG 4 44 #define DS1920_SP_COUNT_REMAIN 6 45 #define DS1920_SP_COUNT_PERC 7 46 #define DS1920_SP_CRC 8 47 48 struct owtemp_softc { 49 struct device sc_dev; 50 51 void * sc_onewire; 52 u_int64_t sc_rom; 53 54 struct ksensor sc_sensor; 55 struct ksensordev sc_sensordev; 56 struct sensor_task *sc_sensortask; 57 struct rwlock sc_lock; 58 }; 59 60 int owtemp_match(struct device *, void *, void *); 61 void owtemp_attach(struct device *, struct device *, void *); 62 int owtemp_detach(struct device *, int); 63 int owtemp_activate(struct device *, int); 64 65 void owtemp_update(void *); 66 67 struct cfattach owtemp_ca = { 68 sizeof(struct owtemp_softc), 69 owtemp_match, 70 owtemp_attach, 71 owtemp_detach, 72 owtemp_activate 73 }; 74 75 struct cfdriver owtemp_cd = { 76 NULL, "owtemp", DV_DULL 77 }; 78 79 static const struct onewire_matchfam owtemp_fams[] = { 80 { ONEWIRE_FAMILY_DS1920 }, 81 { ONEWIRE_FAMILY_DS18B20 }, 82 { ONEWIRE_FAMILY_DS1822 } 83 }; 84 85 int 86 owtemp_match(struct device *parent, void *match, void *aux) 87 { 88 return (onewire_matchbyfam(aux, owtemp_fams, nitems(owtemp_fams))); 89 } 90 91 void 92 owtemp_attach(struct device *parent, struct device *self, void *aux) 93 { 94 struct owtemp_softc *sc = (struct owtemp_softc *)self; 95 struct onewire_attach_args *oa = aux; 96 97 sc->sc_onewire = oa->oa_onewire; 98 sc->sc_rom = oa->oa_rom; 99 100 /* Initialize sensor */ 101 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 102 sizeof(sc->sc_sensordev.xname)); 103 sc->sc_sensor.type = SENSOR_TEMP; 104 snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), "sn %012llx", 105 ONEWIRE_ROM_SN(oa->oa_rom)); 106 107 sc->sc_sensortask = sensor_task_register(sc, owtemp_update, 5); 108 if (sc->sc_sensortask == NULL) { 109 printf(": unable to register update task\n"); 110 return; 111 } 112 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); 113 sensordev_install(&sc->sc_sensordev); 114 115 rw_init(&sc->sc_lock, sc->sc_dev.dv_xname); 116 printf("\n"); 117 } 118 119 int 120 owtemp_detach(struct device *self, int flags) 121 { 122 struct owtemp_softc *sc = (struct owtemp_softc *)self; 123 124 rw_enter_write(&sc->sc_lock); 125 sensordev_deinstall(&sc->sc_sensordev); 126 if (sc->sc_sensortask != NULL) 127 sensor_task_unregister(sc->sc_sensortask); 128 rw_exit_write(&sc->sc_lock); 129 130 return (0); 131 } 132 133 int 134 owtemp_activate(struct device *self, int act) 135 { 136 return (0); 137 } 138 139 void 140 owtemp_update(void *arg) 141 { 142 struct owtemp_softc *sc = arg; 143 u_int8_t data[9]; 144 int16_t temp; 145 int count_perc, count_remain, val; 146 147 rw_enter_write(&sc->sc_lock); 148 onewire_lock(sc->sc_onewire, 0); 149 if (onewire_reset(sc->sc_onewire) != 0) 150 goto done; 151 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 152 153 /* 154 * Start temperature conversion. The conversion takes up to 750ms. 155 * After sending the command, the data line must be held high for 156 * at least 750ms to provide power during the conversion process. 157 * As such, no other activity may take place on the 1-Wire bus for 158 * at least this period. 159 */ 160 onewire_write_byte(sc->sc_onewire, DS1920_CMD_CONVERT); 161 tsleep(sc, PRIBIO, "owtemp", hz); 162 163 if (onewire_reset(sc->sc_onewire) != 0) 164 goto done; 165 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 166 167 /* 168 * The result of the temperature measurement is placed in the 169 * first two bytes of the scratchpad. 170 */ 171 onewire_write_byte(sc->sc_onewire, DS1920_CMD_READ_SCRATCHPAD); 172 onewire_read_block(sc->sc_onewire, data, 9); 173 if (onewire_crc(data, 8) == data[DS1920_SP_CRC]) { 174 temp = data[DS1920_SP_TEMP_MSB] << 8 | 175 data[DS1920_SP_TEMP_LSB]; 176 if (ONEWIRE_ROM_FAMILY(sc->sc_rom) == ONEWIRE_FAMILY_DS18B20 || 177 ONEWIRE_ROM_FAMILY(sc->sc_rom) == ONEWIRE_FAMILY_DS1822) { 178 /* 179 * DS18B20 decoding 180 * default 12 bit 0.0625 C resolution 181 */ 182 val = temp * (1000000 / 16); 183 } else { 184 /* DS1920 decoding */ 185 count_perc = data[DS1920_SP_COUNT_PERC]; 186 count_remain = data[DS1920_SP_COUNT_REMAIN]; 187 188 if (count_perc != 0) { 189 /* High resolution algorithm */ 190 temp &= ~0x0001; 191 val = temp * 500000 - 250000 + 192 ((count_perc - count_remain) * 1000000) / 193 count_perc; 194 } else { 195 val = temp * 500000; 196 } 197 } 198 sc->sc_sensor.value = 273150000 + val; 199 } 200 201 done: 202 onewire_unlock(sc->sc_onewire); 203 rw_exit_write(&sc->sc_lock); 204 } 205