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