1 /*- 2 * Copyright (c) 2015 M. Warner Losh <imp@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/kernel.h> 33 34 #include <sys/bus.h> 35 #include <sys/errno.h> 36 #include <sys/libkern.h> 37 #include <sys/kthread.h> 38 #include <sys/malloc.h> 39 #include <sys/module.h> 40 #include <sys/mutex.h> 41 #include <sys/proc.h> 42 #include <sys/sysctl.h> 43 44 #include <dev/ow/ow.h> 45 #include <dev/ow/own.h> 46 47 #define OWT_DS1820 0x10 /* Also 18S20 */ 48 #define OWT_DS1822 0x22 /* Very close to 18B20 */ 49 #define OWT_DS18B20 0x28 /* Also MAX31820 */ 50 #define OWT_DS1825 0x3B /* Just like 18B20 with address bits */ 51 52 #define CONVERT_T 0x44 53 #define COPY_SCRATCHPAD 0x48 54 #define WRITE_SCRATCHPAD 0x4e 55 #define READ_POWER_SUPPLY 0xb4 56 #define RECALL_EE 0xb8 57 #define READ_SCRATCHPAD 0xbe 58 59 60 #define OW_TEMP_DONE 0x01 61 #define OW_TEMP_RUNNING 0x02 62 63 struct ow_temp_softc 64 { 65 device_t dev; 66 int type; 67 int temp; 68 int flags; 69 int bad_crc; 70 int bad_reads; 71 int reading_interval; 72 int parasite; 73 struct mtx temp_lock; 74 struct proc *event_thread; 75 }; 76 77 static int 78 ow_temp_probe(device_t dev) 79 { 80 81 switch (ow_get_family(dev)) { 82 case OWT_DS1820: 83 device_set_desc(dev, "One Wire Temperature"); 84 return BUS_PROBE_DEFAULT; 85 case OWT_DS1822: 86 case OWT_DS1825: 87 case OWT_DS18B20: 88 device_set_desc(dev, "Advanced One Wire Temperature"); 89 return BUS_PROBE_DEFAULT; 90 default: 91 return ENXIO; 92 } 93 } 94 95 static int 96 ow_temp_read_scratchpad(device_t dev, uint8_t *scratch, int len) 97 { 98 struct ow_cmd cmd; 99 100 own_self_command(dev, &cmd, READ_SCRATCHPAD); 101 cmd.xpt_read_len = len; 102 own_command_wait(dev, &cmd); 103 memcpy(scratch, cmd.xpt_read, len); 104 105 return 0; 106 } 107 108 static int 109 ow_temp_convert_t(device_t dev) 110 { 111 struct ow_cmd cmd; 112 113 own_self_command(dev, &cmd, CONVERT_T); 114 own_command_wait(dev, &cmd); 115 116 return 0; 117 } 118 119 120 static int 121 ow_temp_read_power_supply(device_t dev, int *parasite) 122 { 123 struct ow_cmd cmd; 124 125 own_self_command(dev, &cmd, READ_POWER_SUPPLY); 126 cmd.flags |= OW_FLAG_READ_BIT; 127 cmd.xpt_read_len = 1; 128 own_command_wait(dev, &cmd); 129 *parasite = !cmd.xpt_read[0]; /* parasites pull bus low */ 130 131 return 0; 132 } 133 134 static void 135 ow_temp_event_thread(void *arg) 136 { 137 struct ow_temp_softc *sc; 138 uint8_t scratch[8 + 1]; 139 uint8_t crc; 140 int retries, rv, tmp; 141 142 sc = arg; 143 pause("owtstart", device_get_unit(sc->dev) * hz / 100); // 10ms stagger 144 mtx_lock(&sc->temp_lock); 145 sc->flags |= OW_TEMP_RUNNING; 146 ow_temp_read_power_supply(sc->dev, &sc->parasite); 147 if (sc->parasite) 148 device_printf(sc->dev, "Running in parasitic mode unsupported\n"); 149 while ((sc->flags & OW_TEMP_DONE) == 0) { 150 mtx_unlock(&sc->temp_lock); 151 ow_temp_convert_t(sc->dev); 152 mtx_lock(&sc->temp_lock); 153 msleep(sc, &sc->temp_lock, 0, "owtcvt", hz); 154 if (sc->flags & OW_TEMP_DONE) 155 break; 156 for (retries = 5; retries > 0; retries--) { 157 mtx_unlock(&sc->temp_lock); 158 rv = ow_temp_read_scratchpad(sc->dev, scratch, sizeof(scratch)); 159 mtx_lock(&sc->temp_lock); 160 if (rv == 0) { 161 crc = own_crc(sc->dev, scratch, sizeof(scratch) - 1); 162 if (crc == scratch[8]) { 163 if (sc->type == OWT_DS1820) { 164 if (scratch[7]) { 165 /* 166 * Formula from DS18S20 datasheet, page 6 167 * DS18S20 datasheet says count_per_c is 16, DS1820 does not 168 */ 169 tmp = (int16_t)((scratch[0] & 0xfe) | 170 (scratch[1] << 8)) << 3; 171 tmp += 16 - scratch[6] - 4; /* count_per_c == 16 */ 172 } else 173 tmp = (int16_t)(scratch[0] | (scratch[1] << 8)) << 3; 174 } else 175 tmp = (int16_t)(scratch[0] | (scratch[1] << 8)); 176 sc->temp = tmp * 1000 / 16 + 273150; 177 break; 178 } 179 sc->bad_crc++; 180 } else 181 sc->bad_reads++; 182 } 183 msleep(sc, &sc->temp_lock, 0, "owtcvt", sc->reading_interval); 184 } 185 sc->flags &= ~OW_TEMP_RUNNING; 186 mtx_unlock(&sc->temp_lock); 187 kproc_exit(0); 188 } 189 190 static int 191 ow_temp_attach(device_t dev) 192 { 193 struct ow_temp_softc *sc; 194 195 sc = device_get_softc(dev); 196 sc->dev = dev; 197 sc->type = ow_get_family(dev); 198 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 199 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 200 OID_AUTO, "temperature", CTLFLAG_RD | CTLTYPE_INT, 201 &sc->temp, 0, sysctl_handle_int, 202 "IK3", "Current Temperature"); 203 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 204 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 205 OID_AUTO, "badcrc", CTLFLAG_RD, 206 &sc->bad_crc, 0, 207 "Number of Bad CRC on reading scratchpad"); 208 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 209 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 210 OID_AUTO, "badread", CTLFLAG_RD, 211 &sc->bad_reads, 0, 212 "Number of errors on reading scratchpad"); 213 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 214 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 215 OID_AUTO, "reading_interval", CTLFLAG_RW, 216 &sc->reading_interval, 0, 217 "ticks between reads"); 218 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 219 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 220 OID_AUTO, "parasite", CTLFLAG_RW, 221 &sc->parasite, 0, 222 "In Parasite mode"); 223 /* 224 * Just do this for unit 0 to avoid locking 225 * the ow bus until that code can be put 226 * into place. 227 */ 228 sc->temp = -1; 229 sc->reading_interval = 10 * hz; 230 mtx_init(&sc->temp_lock, "lock for doing temperature", NULL, MTX_DEF); 231 /* Start the thread */ 232 if (kproc_create(ow_temp_event_thread, sc, &sc->event_thread, 0, 0, 233 "%s event thread", device_get_nameunit(dev))) { 234 device_printf(dev, "unable to create event thread.\n"); 235 panic("ow_temp_attach, can't create thread"); 236 } 237 238 return 0; 239 } 240 241 static int 242 ow_temp_detach(device_t dev) 243 { 244 struct ow_temp_softc *sc; 245 246 sc = device_get_softc(dev); 247 248 /* 249 * Wait for the thread to die. kproc_exit will do a wakeup 250 * on the event thread's struct thread * so that we know it is 251 * safe to proceed. IF the thread is running, set the please 252 * die flag and wait for it to comply. Since the wakeup on 253 * the event thread happens only in kproc_exit, we don't 254 * need to loop here. 255 */ 256 mtx_lock(&sc->temp_lock); 257 sc->flags |= OW_TEMP_DONE; 258 while (sc->flags & OW_TEMP_RUNNING) { 259 wakeup(sc); 260 msleep(sc->event_thread, &sc->temp_lock, PWAIT, "owtun", 0); 261 } 262 mtx_destroy(&sc->temp_lock); 263 264 return 0; 265 } 266 267 devclass_t ow_temp_devclass; 268 269 static device_method_t ow_temp_methods[] = { 270 /* Device interface */ 271 DEVMETHOD(device_probe, ow_temp_probe), 272 DEVMETHOD(device_attach, ow_temp_attach), 273 DEVMETHOD(device_detach, ow_temp_detach), 274 275 { 0, 0 } 276 }; 277 278 static driver_t ow_temp_driver = { 279 "ow_temp", 280 ow_temp_methods, 281 sizeof(struct ow_temp_softc), 282 }; 283 284 DRIVER_MODULE(ow_temp, ow, ow_temp_driver, ow_temp_devclass, 0, 0); 285 MODULE_DEPEND(ow_temp, ow, 1, 1, 1); 286