1 /* $OpenBSD: owtemp.c,v 1.15 2010/07/08 07:19:54 jasper 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/proc.h> 28 #include <sys/rwlock.h> 29 #include <sys/sensors.h> 30 31 #include <dev/onewire/onewiredevs.h> 32 #include <dev/onewire/onewirereg.h> 33 #include <dev/onewire/onewirevar.h> 34 35 /* Commands */ 36 #define DS1920_CMD_CONVERT 0x44 37 #define DS1920_CMD_READ_SCRATCHPAD 0xbe 38 39 /* Scratchpad layout */ 40 #define DS1920_SP_TEMP_LSB 0 41 #define DS1920_SP_TEMP_MSB 1 42 #define DS1920_SP_TH 2 43 #define DS1920_SP_TL 3 44 #define DS18B20_SP_CONFIG 4 45 #define DS1920_SP_COUNT_REMAIN 6 46 #define DS1920_SP_COUNT_PERC 7 47 #define DS1920_SP_CRC 8 48 49 struct owtemp_softc { 50 struct device sc_dev; 51 52 void * sc_onewire; 53 u_int64_t sc_rom; 54 55 struct ksensor sc_sensor; 56 struct ksensordev sc_sensordev; 57 struct sensor_task *sc_sensortask; 58 struct rwlock sc_lock; 59 }; 60 61 int owtemp_match(struct device *, void *, void *); 62 void owtemp_attach(struct device *, struct device *, void *); 63 int owtemp_detach(struct device *, int); 64 int owtemp_activate(struct device *, int); 65 66 void owtemp_update(void *); 67 68 struct cfattach owtemp_ca = { 69 sizeof(struct owtemp_softc), 70 owtemp_match, 71 owtemp_attach, 72 owtemp_detach, 73 owtemp_activate 74 }; 75 76 struct cfdriver owtemp_cd = { 77 NULL, "owtemp", DV_DULL 78 }; 79 80 static const struct onewire_matchfam owtemp_fams[] = { 81 { ONEWIRE_FAMILY_DS1920 }, 82 { ONEWIRE_FAMILY_DS18B20 }, 83 { ONEWIRE_FAMILY_DS1822 } 84 }; 85 86 int 87 owtemp_match(struct device *parent, void *match, void *aux) 88 { 89 return (onewire_matchbyfam(aux, owtemp_fams, nitems(owtemp_fams))); 90 } 91 92 void 93 owtemp_attach(struct device *parent, struct device *self, void *aux) 94 { 95 struct owtemp_softc *sc = (struct owtemp_softc *)self; 96 struct onewire_attach_args *oa = aux; 97 98 sc->sc_onewire = oa->oa_onewire; 99 sc->sc_rom = oa->oa_rom; 100 101 /* Initialize sensor */ 102 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 103 sizeof(sc->sc_sensordev.xname)); 104 sc->sc_sensor.type = SENSOR_TEMP; 105 snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), "sn %012llx", 106 ONEWIRE_ROM_SN(oa->oa_rom)); 107 108 sc->sc_sensortask = sensor_task_register(sc, owtemp_update, 5); 109 if (sc->sc_sensortask == NULL) { 110 printf(": unable to register update task\n"); 111 return; 112 } 113 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); 114 sensordev_install(&sc->sc_sensordev); 115 116 rw_init(&sc->sc_lock, sc->sc_dev.dv_xname); 117 printf("\n"); 118 } 119 120 int 121 owtemp_detach(struct device *self, int flags) 122 { 123 struct owtemp_softc *sc = (struct owtemp_softc *)self; 124 125 rw_enter_write(&sc->sc_lock); 126 sensordev_deinstall(&sc->sc_sensordev); 127 if (sc->sc_sensortask != NULL) 128 sensor_task_unregister(sc->sc_sensortask); 129 rw_exit_write(&sc->sc_lock); 130 131 return (0); 132 } 133 134 int 135 owtemp_activate(struct device *self, int act) 136 { 137 return (0); 138 } 139 140 void 141 owtemp_update(void *arg) 142 { 143 struct owtemp_softc *sc = arg; 144 u_int8_t data[9]; 145 int16_t temp; 146 int count_perc, count_remain, val; 147 148 rw_enter_write(&sc->sc_lock); 149 onewire_lock(sc->sc_onewire, 0); 150 if (onewire_reset(sc->sc_onewire) != 0) 151 goto done; 152 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 153 154 /* 155 * Start temperature conversion. The conversion takes up to 750ms. 156 * After sending the command, the data line must be held high for 157 * at least 750ms to provide power during the conversion process. 158 * As such, no other activity may take place on the 1-Wire bus for 159 * at least this period. 160 */ 161 onewire_write_byte(sc->sc_onewire, DS1920_CMD_CONVERT); 162 tsleep(sc, PRIBIO, "owtemp", hz); 163 164 if (onewire_reset(sc->sc_onewire) != 0) 165 goto done; 166 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 167 168 /* 169 * The result of the temperature measurement is placed in the 170 * first two bytes of the scratchpad. 171 */ 172 onewire_write_byte(sc->sc_onewire, DS1920_CMD_READ_SCRATCHPAD); 173 onewire_read_block(sc->sc_onewire, data, 9); 174 if (onewire_crc(data, 8) == data[DS1920_SP_CRC]) { 175 temp = data[DS1920_SP_TEMP_MSB] << 8 | 176 data[DS1920_SP_TEMP_LSB]; 177 if (ONEWIRE_ROM_FAMILY(sc->sc_rom) == ONEWIRE_FAMILY_DS18B20 || 178 ONEWIRE_ROM_FAMILY(sc->sc_rom) == ONEWIRE_FAMILY_DS1822) { 179 /* 180 * DS18B20 decoding 181 * default 12 bit 0.0625 C resolution 182 */ 183 val = temp * (1000000 / 16); 184 } else { 185 /* DS1920 decoding */ 186 count_perc = data[DS1920_SP_COUNT_PERC]; 187 count_remain = data[DS1920_SP_COUNT_REMAIN]; 188 189 if (count_perc != 0) { 190 /* High resolution algorithm */ 191 temp &= ~0x0001; 192 val = temp * 500000 - 250000 + 193 ((count_perc - count_remain) * 1000000) / 194 count_perc; 195 } else { 196 val = temp * 500000; 197 } 198 } 199 sc->sc_sensor.value = 273150000 + val; 200 } 201 202 done: 203 onewire_unlock(sc->sc_onewire); 204 rw_exit_write(&sc->sc_lock); 205 } 206