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