1 /* $OpenBSD: owtemp.c,v 1.14 2009/10/13 19:33:16 pirofti 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, 90 sizeof(owtemp_fams) /sizeof(owtemp_fams[0]))); 91 } 92 93 void 94 owtemp_attach(struct device *parent, struct device *self, void *aux) 95 { 96 struct owtemp_softc *sc = (struct owtemp_softc *)self; 97 struct onewire_attach_args *oa = aux; 98 99 sc->sc_onewire = oa->oa_onewire; 100 sc->sc_rom = oa->oa_rom; 101 102 /* Initialize sensor */ 103 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 104 sizeof(sc->sc_sensordev.xname)); 105 sc->sc_sensor.type = SENSOR_TEMP; 106 snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), "sn %012llx", 107 ONEWIRE_ROM_SN(oa->oa_rom)); 108 109 sc->sc_sensortask = sensor_task_register(sc, owtemp_update, 5); 110 if (sc->sc_sensortask == NULL) { 111 printf(": unable to register update task\n"); 112 return; 113 } 114 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); 115 sensordev_install(&sc->sc_sensordev); 116 117 rw_init(&sc->sc_lock, sc->sc_dev.dv_xname); 118 printf("\n"); 119 } 120 121 int 122 owtemp_detach(struct device *self, int flags) 123 { 124 struct owtemp_softc *sc = (struct owtemp_softc *)self; 125 126 rw_enter_write(&sc->sc_lock); 127 sensordev_deinstall(&sc->sc_sensordev); 128 if (sc->sc_sensortask != NULL) 129 sensor_task_unregister(sc->sc_sensortask); 130 rw_exit_write(&sc->sc_lock); 131 132 return (0); 133 } 134 135 int 136 owtemp_activate(struct device *self, int act) 137 { 138 return (0); 139 } 140 141 void 142 owtemp_update(void *arg) 143 { 144 struct owtemp_softc *sc = arg; 145 u_int8_t data[9]; 146 int16_t temp; 147 int count_perc, count_remain, val; 148 149 rw_enter_write(&sc->sc_lock); 150 onewire_lock(sc->sc_onewire, 0); 151 if (onewire_reset(sc->sc_onewire) != 0) 152 goto done; 153 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 154 155 /* 156 * Start temperature conversion. The conversion takes up to 750ms. 157 * After sending the command, the data line must be held high for 158 * at least 750ms to provide power during the conversion process. 159 * As such, no other activity may take place on the 1-Wire bus for 160 * at least this period. 161 */ 162 onewire_write_byte(sc->sc_onewire, DS1920_CMD_CONVERT); 163 tsleep(sc, PRIBIO, "owtemp", hz); 164 165 if (onewire_reset(sc->sc_onewire) != 0) 166 goto done; 167 onewire_matchrom(sc->sc_onewire, sc->sc_rom); 168 169 /* 170 * The result of the temperature measurement is placed in the 171 * first two bytes of the scratchpad. 172 */ 173 onewire_write_byte(sc->sc_onewire, DS1920_CMD_READ_SCRATCHPAD); 174 onewire_read_block(sc->sc_onewire, data, 9); 175 if (onewire_crc(data, 8) == data[DS1920_SP_CRC]) { 176 temp = data[DS1920_SP_TEMP_MSB] << 8 | 177 data[DS1920_SP_TEMP_LSB]; 178 if (ONEWIRE_ROM_FAMILY(sc->sc_rom) == ONEWIRE_FAMILY_DS18B20 || 179 ONEWIRE_ROM_FAMILY(sc->sc_rom) == ONEWIRE_FAMILY_DS1822) { 180 /* 181 * DS18B20 decoding 182 * default 12 bit 0.0625 C resolution 183 */ 184 val = temp * (1000000 / 16); 185 } else { 186 /* DS1920 decoding */ 187 count_perc = data[DS1920_SP_COUNT_PERC]; 188 count_remain = data[DS1920_SP_COUNT_REMAIN]; 189 190 if (count_perc != 0) { 191 /* High resolution algorithm */ 192 temp &= ~0x0001; 193 val = temp * 500000 - 250000 + 194 ((count_perc - count_remain) * 1000000) / 195 count_perc; 196 } else { 197 val = temp * 500000; 198 } 199 } 200 sc->sc_sensor.value = 273150000 + val; 201 } 202 203 done: 204 onewire_unlock(sc->sc_onewire); 205 rw_exit_write(&sc->sc_lock); 206 } 207