Line data Source code
1 : /* $OpenBSD: owtemp.c,v 1.16 2014/09/14 14:17:25 jsg 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 : 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
86 0 : owtemp_match(struct device *parent, void *match, void *aux)
87 : {
88 0 : return (onewire_matchbyfam(aux, owtemp_fams, nitems(owtemp_fams)));
89 : }
90 :
91 : void
92 0 : owtemp_attach(struct device *parent, struct device *self, void *aux)
93 : {
94 0 : struct owtemp_softc *sc = (struct owtemp_softc *)self;
95 0 : struct onewire_attach_args *oa = aux;
96 :
97 0 : sc->sc_onewire = oa->oa_onewire;
98 0 : sc->sc_rom = oa->oa_rom;
99 :
100 : /* Initialize sensor */
101 0 : strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
102 : sizeof(sc->sc_sensordev.xname));
103 0 : sc->sc_sensor.type = SENSOR_TEMP;
104 0 : snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc), "sn %012llx",
105 0 : ONEWIRE_ROM_SN(oa->oa_rom));
106 :
107 0 : sc->sc_sensortask = sensor_task_register(sc, owtemp_update, 5);
108 0 : if (sc->sc_sensortask == NULL) {
109 0 : printf(": unable to register update task\n");
110 0 : return;
111 : }
112 0 : sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
113 0 : sensordev_install(&sc->sc_sensordev);
114 :
115 0 : rw_init(&sc->sc_lock, sc->sc_dev.dv_xname);
116 0 : printf("\n");
117 0 : }
118 :
119 : int
120 0 : owtemp_detach(struct device *self, int flags)
121 : {
122 0 : struct owtemp_softc *sc = (struct owtemp_softc *)self;
123 :
124 0 : rw_enter_write(&sc->sc_lock);
125 0 : sensordev_deinstall(&sc->sc_sensordev);
126 0 : if (sc->sc_sensortask != NULL)
127 0 : sensor_task_unregister(sc->sc_sensortask);
128 0 : rw_exit_write(&sc->sc_lock);
129 :
130 0 : return (0);
131 : }
132 :
133 : int
134 0 : owtemp_activate(struct device *self, int act)
135 : {
136 0 : return (0);
137 : }
138 :
139 : void
140 0 : owtemp_update(void *arg)
141 : {
142 0 : struct owtemp_softc *sc = arg;
143 0 : u_int8_t data[9];
144 : int16_t temp;
145 : int count_perc, count_remain, val;
146 :
147 0 : rw_enter_write(&sc->sc_lock);
148 0 : onewire_lock(sc->sc_onewire, 0);
149 0 : if (onewire_reset(sc->sc_onewire) != 0)
150 : goto done;
151 0 : 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 0 : onewire_write_byte(sc->sc_onewire, DS1920_CMD_CONVERT);
161 0 : tsleep(sc, PRIBIO, "owtemp", hz);
162 :
163 0 : if (onewire_reset(sc->sc_onewire) != 0)
164 : goto done;
165 0 : 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 0 : onewire_write_byte(sc->sc_onewire, DS1920_CMD_READ_SCRATCHPAD);
172 0 : onewire_read_block(sc->sc_onewire, data, 9);
173 0 : if (onewire_crc(data, 8) == data[DS1920_SP_CRC]) {
174 0 : temp = data[DS1920_SP_TEMP_MSB] << 8 |
175 0 : data[DS1920_SP_TEMP_LSB];
176 0 : if (ONEWIRE_ROM_FAMILY(sc->sc_rom) == ONEWIRE_FAMILY_DS18B20 ||
177 0 : ONEWIRE_ROM_FAMILY(sc->sc_rom) == ONEWIRE_FAMILY_DS1822) {
178 : /*
179 : * DS18B20 decoding
180 : * default 12 bit 0.0625 C resolution
181 : */
182 0 : val = temp * (1000000 / 16);
183 0 : } else {
184 : /* DS1920 decoding */
185 0 : count_perc = data[DS1920_SP_COUNT_PERC];
186 0 : count_remain = data[DS1920_SP_COUNT_REMAIN];
187 :
188 0 : if (count_perc != 0) {
189 : /* High resolution algorithm */
190 0 : temp &= ~0x0001;
191 0 : val = temp * 500000 - 250000 +
192 0 : ((count_perc - count_remain) * 1000000) /
193 : count_perc;
194 0 : } else {
195 0 : val = temp * 500000;
196 : }
197 : }
198 0 : sc->sc_sensor.value = 273150000 + val;
199 0 : }
200 :
201 : done:
202 0 : onewire_unlock(sc->sc_onewire);
203 0 : rw_exit_write(&sc->sc_lock);
204 0 : }
|