1 /* $OpenBSD: owtemp.c,v 1.18 2022/04/06 18:59:29 naddy 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/rwlock.h>
28 #include <sys/sensors.h>
29
30 #include <dev/onewire/onewiredevs.h>
31 #include <dev/onewire/onewirereg.h>
32 #include <dev/onewire/onewirevar.h>
33
34 /* Commands */
35 #define DS1920_CMD_CONVERT 0x44
36 #define DS1920_CMD_READ_SCRATCHPAD 0xbe
37
38 /* Scratchpad layout */
39 #define DS1920_SP_TEMP_LSB 0
40 #define DS1920_SP_TEMP_MSB 1
41 #define DS1920_SP_TH 2
42 #define DS1920_SP_TL 3
43 #define DS18B20_SP_CONFIG 4
44 #define DS1920_SP_COUNT_REMAIN 6
45 #define DS1920_SP_COUNT_PERC 7
46 #define DS1920_SP_CRC 8
47
48 struct owtemp_softc {
49 struct device sc_dev;
50
51 void * sc_onewire;
52 u_int64_t sc_rom;
53
54 struct ksensor sc_sensor;
55 struct ksensordev sc_sensordev;
56 struct sensor_task *sc_sensortask;
57 struct rwlock sc_lock;
58 };
59
60 int owtemp_match(struct device *, void *, void *);
61 void owtemp_attach(struct device *, struct device *, void *);
62 int owtemp_detach(struct device *, int);
63 int owtemp_activate(struct device *, int);
64
65 void owtemp_update(void *);
66
67 const struct cfattach owtemp_ca = {
68 sizeof(struct owtemp_softc),
69 owtemp_match,
70 owtemp_attach,
71 owtemp_detach,
72 owtemp_activate
73 };
74
75 struct cfdriver owtemp_cd = {
76 NULL, "owtemp", DV_DULL
77 };
78
79 static const struct onewire_matchfam owtemp_fams[] = {
80 { ONEWIRE_FAMILY_DS1920 },
81 { ONEWIRE_FAMILY_DS18B20 },
82 { ONEWIRE_FAMILY_DS1822 }
83 };
84
85 int
owtemp_match(struct device * parent,void * match,void * aux)86 owtemp_match(struct device *parent, void *match, void *aux)
87 {
88 return (onewire_matchbyfam(aux, owtemp_fams, nitems(owtemp_fams)));
89 }
90
91 void
owtemp_attach(struct device * parent,struct device * self,void * aux)92 owtemp_attach(struct device *parent, struct device *self, void *aux)
93 {
94 struct owtemp_softc *sc = (struct owtemp_softc *)self;
95 struct onewire_attach_args *oa = aux;
96
97 sc->sc_onewire = oa->oa_onewire;
98 sc->sc_rom = oa->oa_rom;
99
100 /* Initialize sensor */
101 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
102 sizeof(sc->sc_sensordev.xname));
103 sc->sc_sensor.type = SENSOR_TEMP;
104 snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), "sn %012llx",
105 ONEWIRE_ROM_SN(oa->oa_rom));
106
107 sc->sc_sensortask = sensor_task_register(sc, owtemp_update, 5);
108 if (sc->sc_sensortask == NULL) {
109 printf(": unable to register update task\n");
110 return;
111 }
112 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
113 sensordev_install(&sc->sc_sensordev);
114
115 rw_init(&sc->sc_lock, sc->sc_dev.dv_xname);
116 printf("\n");
117 }
118
119 int
owtemp_detach(struct device * self,int flags)120 owtemp_detach(struct device *self, int flags)
121 {
122 struct owtemp_softc *sc = (struct owtemp_softc *)self;
123
124 rw_enter_write(&sc->sc_lock);
125 sensordev_deinstall(&sc->sc_sensordev);
126 if (sc->sc_sensortask != NULL)
127 sensor_task_unregister(sc->sc_sensortask);
128 rw_exit_write(&sc->sc_lock);
129
130 return (0);
131 }
132
133 int
owtemp_activate(struct device * self,int act)134 owtemp_activate(struct device *self, int act)
135 {
136 return (0);
137 }
138
139 void
owtemp_update(void * arg)140 owtemp_update(void *arg)
141 {
142 struct owtemp_softc *sc = arg;
143 u_int8_t data[9];
144 int16_t temp;
145 int count_perc, count_remain, val;
146
147 rw_enter_write(&sc->sc_lock);
148 onewire_lock(sc->sc_onewire, 0);
149 if (onewire_reset(sc->sc_onewire) != 0)
150 goto done;
151 onewire_matchrom(sc->sc_onewire, sc->sc_rom);
152
153 /*
154 * Start temperature conversion. The conversion takes up to 750ms.
155 * After sending the command, the data line must be held high for
156 * at least 750ms to provide power during the conversion process.
157 * As such, no other activity may take place on the 1-Wire bus for
158 * at least this period.
159 */
160 onewire_write_byte(sc->sc_onewire, DS1920_CMD_CONVERT);
161 tsleep_nsec(sc, PRIBIO, "owtemp", SEC_TO_NSEC(1));
162
163 if (onewire_reset(sc->sc_onewire) != 0)
164 goto done;
165 onewire_matchrom(sc->sc_onewire, sc->sc_rom);
166
167 /*
168 * The result of the temperature measurement is placed in the
169 * first two bytes of the scratchpad.
170 */
171 onewire_write_byte(sc->sc_onewire, DS1920_CMD_READ_SCRATCHPAD);
172 onewire_read_block(sc->sc_onewire, data, 9);
173 if (onewire_crc(data, 8) == data[DS1920_SP_CRC]) {
174 temp = data[DS1920_SP_TEMP_MSB] << 8 |
175 data[DS1920_SP_TEMP_LSB];
176 if (ONEWIRE_ROM_FAMILY(sc->sc_rom) == ONEWIRE_FAMILY_DS18B20 ||
177 ONEWIRE_ROM_FAMILY(sc->sc_rom) == ONEWIRE_FAMILY_DS1822) {
178 /*
179 * DS18B20 decoding
180 * default 12 bit 0.0625 C resolution
181 */
182 val = temp * (1000000 / 16);
183 } else {
184 /* DS1920 decoding */
185 count_perc = data[DS1920_SP_COUNT_PERC];
186 count_remain = data[DS1920_SP_COUNT_REMAIN];
187
188 if (count_perc != 0) {
189 /* High resolution algorithm */
190 temp &= ~0x0001;
191 val = temp * 500000 - 250000 +
192 ((count_perc - count_remain) * 1000000) /
193 count_perc;
194 } else {
195 val = temp * 500000;
196 }
197 }
198 sc->sc_sensor.value = 273150000 + val;
199 }
200
201 done:
202 onewire_unlock(sc->sc_onewire);
203 rw_exit_write(&sc->sc_lock);
204 }
205