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