xref: /netbsd-src/sys/dev/i2c/lm75.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: lm75.c,v 1.2 2004/08/03 13:40:20 scw Exp $	*/
2 
3 /*
4  * Copyright (c) 2003 Wasabi Systems, Inc.
5  * All rights reserved.
6  *
7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed for the NetBSD Project by
20  *      Wasabi Systems, Inc.
21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22  *    or promote products derived from this software without specific prior
23  *    written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/device.h>
41 #include <sys/kernel.h>
42 
43 #include <dev/sysmon/sysmonvar.h>
44 
45 #include <dev/i2c/i2cvar.h>
46 #include <dev/i2c/lm75reg.h>
47 
48 struct lmtemp_softc {
49 	struct device sc_dev;
50 	i2c_tag_t sc_tag;
51 	int sc_address;
52 	int sc_is_ds75;
53 
54 	struct envsys_tre_data sc_sensor[1];
55 	struct envsys_basic_info sc_info[1];
56 
57 	struct sysmon_envsys sc_sysmon;
58 };
59 
60 static int  lmtemp_match(struct device *, struct cfdata *, void *);
61 static void lmtemp_attach(struct device *, struct device *, void *);
62 
63 CFATTACH_DECL(lmtemp, sizeof(struct lmtemp_softc),
64 	lmtemp_match, lmtemp_attach, NULL, NULL);
65 
66 static int	lmtemp_gtredata(struct sysmon_envsys *,
67 				struct envsys_tre_data *);
68 static int	lmtemp_streinfo(struct sysmon_envsys *,
69 				struct envsys_basic_info *);
70 
71 static const struct envsys_range lmtemp_ranges[] = {
72 	{ 0, 1,		ENVSYS_STEMP },
73 	{ 1, 0,		-1 },
74 };
75 
76 static int lmtemp_config_write(struct lmtemp_softc *, uint8_t);
77 static uint32_t lmtemp_decode_lm75(const uint8_t *);
78 static uint32_t lmtemp_decode_ds75(const uint8_t *);
79 
80 static int
81 lmtemp_match(struct device *parent, struct cfdata *cf, void *aux)
82 {
83 	struct i2c_attach_args *ia = aux;
84 
85 	if ((ia->ia_addr & LM75_ADDRMASK) == LM75_ADDR)
86 		return (1);
87 
88 	return (0);
89 }
90 
91 static void
92 lmtemp_attach(struct device *parent, struct device *self, void *aux)
93 {
94 	struct lmtemp_softc *sc = (struct lmtemp_softc *)self;
95 	struct i2c_attach_args *ia = aux;
96 	int ptype;
97 
98 	sc->sc_tag = ia->ia_tag;
99 	sc->sc_address = ia->ia_addr;
100 	sc->sc_is_ds75 = sc->sc_dev.dv_cfdata->cf_flags & 1;
101 
102 	aprint_naive(": Temperature Sensor\n");
103 	aprint_normal(": %s Temperature Sensor\n",
104 	    (sc->sc_is_ds75) ? "DS75" : "LM75");
105 
106 	/* Set the configuration of the LM75 to defaults. */
107 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
108 	if (lmtemp_config_write(sc, 0) != 0) {
109 		aprint_error("%s: unable to write config register\n",
110 		    sc->sc_dev.dv_xname);
111 		iic_release_bus(sc->sc_tag, I2C_F_POLL);
112 		return;
113 	}
114 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
115 
116 	/* Initialize sensor data. */
117 	sc->sc_sensor[0].sensor = sc->sc_info[0].sensor = 0;
118 	sc->sc_sensor[0].validflags = ENVSYS_FVALID;
119 	sc->sc_info[0].validflags = ENVSYS_FVALID;
120 	sc->sc_sensor[0].warnflags = ENVSYS_WARN_OK;
121 
122 	sc->sc_sensor[0].units = sc->sc_info[0].units = ENVSYS_STEMP;
123 	if (prop_get(dev_propdb, &sc->sc_dev, "description",
124 		     sc->sc_info[0].desc, sizeof(sc->sc_info[0].desc),
125 		     &ptype) < 1 ||
126 	    ptype != PROP_STRING)
127 		strcpy(sc->sc_info[0].desc, sc->sc_dev.dv_xname);
128 
129 	/* Hook info system monitor. */
130 	sc->sc_sysmon.sme_ranges = lmtemp_ranges;
131 	sc->sc_sysmon.sme_sensor_info = sc->sc_info;
132 	sc->sc_sysmon.sme_sensor_data = sc->sc_sensor;
133 	sc->sc_sysmon.sme_cookie = sc;
134 
135 	sc->sc_sysmon.sme_gtredata = lmtemp_gtredata;
136 	sc->sc_sysmon.sme_streinfo = lmtemp_streinfo;
137 
138 	sc->sc_sysmon.sme_nsensors = 1;
139 	sc->sc_sysmon.sme_envsys_version = 1000;
140 
141 	if (sysmon_envsys_register(&sc->sc_sysmon))
142 		aprint_error("%s: unable to register with sysmon\n",
143 		    sc->sc_dev.dv_xname);
144 }
145 
146 static int
147 lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val)
148 {
149 	uint8_t cmdbuf[2];
150 
151 	cmdbuf[0] = LM75_REG_CONFIG;
152 	cmdbuf[1] = val;
153 
154 	return (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
155 	    sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL));
156 }
157 
158 static int
159 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp)
160 {
161 	int error;
162 	uint8_t cmdbuf[1];
163 	uint8_t buf[LM75_TEMP_LEN];
164 
165 	cmdbuf[0] = which;
166 
167 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
168 	    sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0);
169 	if (error)
170 		return (error);
171 
172 	if (sc->sc_is_ds75)
173 		*valp = lmtemp_decode_ds75(buf);
174 	else
175 		*valp = lmtemp_decode_lm75(buf);
176 
177 	return (0);
178 }
179 
180 static void
181 lmtemp_refresh_sensor_data(struct lmtemp_softc *sc)
182 {
183 	uint32_t val;
184 	int error;
185 
186 	error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val);
187 	if (error) {
188 #if 0
189 		printf("%s: unable to read temperature, error = %d\n",
190 		    sc->sc_dev.dv_xname, error);
191 #endif
192 		sc->sc_sensor[0].validflags &= ~ENVSYS_FCURVALID;
193 		return;
194 	}
195 
196 	sc->sc_sensor[0].cur.data_us = val;
197 	sc->sc_sensor[0].validflags |= ENVSYS_FCURVALID;
198 }
199 
200 static int
201 lmtemp_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred)
202 {
203 	struct lmtemp_softc *sc = sme->sme_cookie;
204 
205 	iic_acquire_bus(sc->sc_tag, 0);	/* also locks our instance */
206 
207 	lmtemp_refresh_sensor_data(sc);
208 	*tred = sc->sc_sensor[tred->sensor];
209 
210 	iic_release_bus(sc->sc_tag, 0);	/* also unlocks our instance */
211 
212 	return (0);
213 }
214 
215 static int
216 lmtemp_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo)
217 {
218 	struct lmtemp_softc *sc = sme->sme_cookie;
219 
220 	iic_acquire_bus(sc->sc_tag, 0);	/* also locks our instance */
221 
222 	memcpy(sc->sc_info[binfo->sensor].desc, binfo->desc,
223 	    sizeof(sc->sc_info[binfo->sensor].desc));
224 	sc->sc_info[binfo->sensor].desc[
225 	    sizeof(sc->sc_info[binfo->sensor].desc) - 1] = '\0';
226 
227 	iic_release_bus(sc->sc_tag, 0);	/* also unlocks our instance */
228 
229 	binfo->validflags = ENVSYS_FVALID;
230 
231 	return (0);
232 }
233 
234 static uint32_t
235 lmtemp_decode_lm75(const uint8_t *buf)
236 {
237 	int neg, temp;
238 	uint32_t val;
239 
240 	if (buf[0] & 1) {
241 		/* Below 0C */
242 		temp = ~buf[1] + 1;
243 		neg = 1;
244 	} else {
245 		temp = buf[1];
246 		neg = 0;
247 	}
248 
249 	/* Temp is given in 1/2 deg. C, we convert to uK. */
250 	val = ((neg ? -temp : temp) / 2) * 1000000 + 273150000;
251 	if (temp & 1) {
252 		if (neg)
253 			val -= 500000;
254 		else
255 			val += 500000;
256 	}
257 
258 	return (val);
259 }
260 
261 static uint32_t
262 lmtemp_decode_ds75(const uint8_t *buf)
263 {
264 	int temp;
265 
266 	/*
267 	 * Sign-extend the MSB byte, and add in the fractions of a
268 	 * degree contained in the LSB (prescision 1/16th DegC).
269 	 */
270 	temp = (int8_t)buf[0];
271 	temp = (temp << 4) | ((buf[1] >> 4) & 0xf);
272 
273 	/*
274 	 * Conversion to uK is simple.
275 	 */
276 	return (temp * 62500 + 273150000);
277 }
278 
279