xref: /netbsd-src/sys/dev/i2c/lm75.c (revision a6f3f22f245acb8ee3bbf6871d7dce989204fa97)
1 /*	$NetBSD: lm75.c,v 1.26 2015/09/27 13:02:21 phx 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/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: lm75.c,v 1.26 2015/09/27 13:02:21 phx Exp $");
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/device.h>
44 #include <sys/kernel.h>
45 #include <sys/sysctl.h>
46 
47 #include <dev/sysmon/sysmonvar.h>
48 
49 #include <dev/i2c/i2cvar.h>
50 #include <dev/i2c/lm75reg.h>
51 
52 struct lmtemp_softc {
53 	device_t sc_dev;
54 	i2c_tag_t sc_tag;
55 	int sc_address;
56 
57 	struct sysmon_envsys *sc_sme;
58 	envsys_data_t sc_sensor;
59 	int sc_tmax;
60 
61 	uint32_t (*sc_lmtemp_decode)(const uint8_t *);
62 };
63 
64 static int  lmtemp_match(device_t, cfdata_t, void *);
65 static void lmtemp_attach(device_t, device_t, void *);
66 
67 CFATTACH_DECL_NEW(lmtemp, sizeof(struct lmtemp_softc),
68 	lmtemp_match, lmtemp_attach, NULL, NULL);
69 
70 static void	lmtemp_refresh(struct sysmon_envsys *, envsys_data_t *);
71 
72 static int	lmtemp_config_write(struct lmtemp_softc *, uint8_t);
73 static int	lmtemp_temp_write(struct lmtemp_softc *, int, uint16_t);
74 static uint32_t lmtemp_decode_lm75(const uint8_t *);
75 static uint32_t lmtemp_decode_ds75(const uint8_t *);
76 static uint32_t lmtemp_decode_lm77(const uint8_t *);
77 
78 static void	lmtemp_setup_sysctl(struct lmtemp_softc *);
79 static int	sysctl_lm75_temp(SYSCTLFN_ARGS);
80 
81 static const char * lmtemp_compats[] = {
82 	"i2c-lm75",
83 	/*
84 	 * see XXX in _attach() below: add code once non-lm75 matches are
85 	 * added here!
86 	 */
87 	NULL
88 };
89 
90 enum {
91 	lmtemp_lm75 = 0,
92 	lmtemp_ds75,
93 	lmtemp_lm77,
94 };
95 static const struct {
96 	int lmtemp_type;
97 	const char *lmtemp_name;
98 	int lmtemp_addrmask;
99 	int lmtemp_addr;
100 	uint32_t (*lmtemp_decode)(const uint8_t *);
101 } lmtemptbl[] = {
102 	{ lmtemp_lm75,	"LM75",
103 	    LM75_ADDRMASK,	LM75_ADDR,	lmtemp_decode_lm75 },
104 	{ lmtemp_ds75,	"DS75",
105 	    LM75_ADDRMASK,	LM75_ADDR,	lmtemp_decode_ds75 },
106 	{ lmtemp_lm77,	"LM77",
107 	    LM77_ADDRMASK,	LM77_ADDR,	lmtemp_decode_lm77 },
108 
109 	{ -1,		NULL,
110 	    0,			0,		NULL }
111 };
112 
113 static int
114 lmtemp_match(device_t parent, cfdata_t cf, void *aux)
115 {
116 	struct i2c_attach_args *ia = aux;
117 	int i;
118 
119 	if (ia->ia_name == NULL) {
120 		/*
121 		 * Indirect config - not much we can do!
122 		 */
123 		for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++)
124 			if (lmtemptbl[i].lmtemp_type == cf->cf_flags)
125 				break;
126 		if (lmtemptbl[i].lmtemp_type == -1)
127 			return 0;
128 
129 		if ((ia->ia_addr & lmtemptbl[i].lmtemp_addrmask) ==
130 		    lmtemptbl[i].lmtemp_addr)
131 			return 1;
132 	} else {
133 		/*
134 		 * Direct config - match via the list of compatible
135 		 * hardware or simply match the device name.
136 		 */
137 		if (ia->ia_ncompat > 0) {
138 			if (iic_compat_match(ia, lmtemp_compats))
139 				return 1;
140 		} else {
141 			if (strcmp(ia->ia_name, "lmtemp") == 0)
142 				return 1;
143 		}
144 	}
145 
146 
147 	return 0;
148 }
149 
150 static void
151 lmtemp_attach(device_t parent, device_t self, void *aux)
152 {
153 	struct lmtemp_softc *sc = device_private(self);
154 	struct i2c_attach_args *ia = aux;
155 	int i;
156 
157 	sc->sc_dev = self;
158 	if (ia->ia_name == NULL) {
159 		for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++)
160 			if (lmtemptbl[i].lmtemp_type ==
161 			    device_cfdata(self)->cf_flags)
162 				break;
163 	} else {
164 		/* XXX - add code when adding other direct matches! */
165 		i = 0;
166 	}
167 
168 	sc->sc_tag = ia->ia_tag;
169 	sc->sc_address = ia->ia_addr;
170 
171 	aprint_naive(": Temperature Sensor\n");
172 	if (ia->ia_name) {
173 		aprint_normal(": %s %s Temperature Sensor\n", ia->ia_name,
174 			lmtemptbl[i].lmtemp_name);
175 	} else {
176 		aprint_normal(": %s Temperature Sensor\n",
177 			lmtemptbl[i].lmtemp_name);
178 	}
179 
180 	/*
181 	 * according to the LM75 data sheet 80C is the default, so leave it
182 	 * there to avoid unexpected behaviour
183 	 */
184 	sc->sc_tmax = 80;
185 	if (i == lmtemp_lm75)
186 		lmtemp_setup_sysctl(sc);
187 
188 	/* Set the configuration of the LM75 to defaults. */
189 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
190 	if (lmtemp_config_write(sc, LM75_CONFIG_FAULT_QUEUE_4) != 0) {
191 		aprint_error_dev(self, "unable to write config register\n");
192 		iic_release_bus(sc->sc_tag, I2C_F_POLL);
193 		return;
194 	}
195 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
196 
197 	sc->sc_sme = sysmon_envsys_create();
198 	/* Initialize sensor data. */
199 	sc->sc_sensor.units =  ENVSYS_STEMP;
200 	sc->sc_sensor.state =  ENVSYS_SINVALID;
201 	(void)strlcpy(sc->sc_sensor.desc,
202 	    ia->ia_name? ia->ia_name : device_xname(self),
203 	    sizeof(sc->sc_sensor.desc));
204 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) {
205 		sysmon_envsys_destroy(sc->sc_sme);
206 		return;
207 	}
208 
209 	sc->sc_lmtemp_decode = lmtemptbl[i].lmtemp_decode;
210 
211 	/* Hook into system monitor. */
212 	sc->sc_sme->sme_name = device_xname(self);
213 	sc->sc_sme->sme_cookie = sc;
214 	sc->sc_sme->sme_refresh = lmtemp_refresh;
215 
216 	if (sysmon_envsys_register(sc->sc_sme)) {
217 		aprint_error_dev(self, "unable to register with sysmon\n");
218 		sysmon_envsys_destroy(sc->sc_sme);
219 	}
220 }
221 
222 static int
223 lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val)
224 {
225 	uint8_t cmdbuf[2];
226 
227 	cmdbuf[0] = LM75_REG_CONFIG;
228 	cmdbuf[1] = val;
229 
230 	return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
231 	    sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL);
232 }
233 
234 static int
235 lmtemp_temp_write(struct lmtemp_softc *sc, int reg, uint16_t val)
236 {
237 	uint8_t cmdbuf[3];
238 
239 	cmdbuf[0] = reg;
240 	cmdbuf[1] = (val >> 1) & 0xff;
241 	cmdbuf[2] = (val & 1) << 7;
242 
243 	return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
244 	    sc->sc_address, cmdbuf, 1, &cmdbuf[1], 2, I2C_F_POLL);
245 }
246 
247 static int
248 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp)
249 {
250 	int error;
251 	uint8_t cmdbuf[1];
252 	uint8_t buf[LM75_TEMP_LEN];
253 
254 	cmdbuf[0] = which;
255 
256 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
257 	    sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0);
258 	if (error)
259 		return error;
260 
261 	*valp = sc->sc_lmtemp_decode(buf);
262 	return 0;
263 }
264 
265 static void
266 lmtemp_refresh_sensor_data(struct lmtemp_softc *sc)
267 {
268 	uint32_t val;
269 	int error;
270 
271 	error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val);
272 	if (error) {
273 #if 0
274 		aprint_error_dev(sc->sc_dev, "unable to read temperature, error = %d\n",
275 		    error);
276 #endif
277 		sc->sc_sensor.state = ENVSYS_SINVALID;
278 		return;
279 	}
280 
281 	sc->sc_sensor.value_cur = val;
282 	sc->sc_sensor.state = ENVSYS_SVALID;
283 }
284 
285 static void
286 lmtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
287 {
288 	struct lmtemp_softc *sc = sme->sme_cookie;
289 
290 	iic_acquire_bus(sc->sc_tag, 0);	/* also locks our instance */
291 	lmtemp_refresh_sensor_data(sc);
292 	iic_release_bus(sc->sc_tag, 0);	/* also unlocks our instance */
293 }
294 
295 static uint32_t
296 lmtemp_decode_lm75(const uint8_t *buf)
297 {
298 	int temp;
299 	uint32_t val;
300 
301 	/*
302 	 * LM75 temps are the most-significant 9 bits of a 16-bit reg.
303 	 * sign-extend the MSB and add in the 0.5 from the LSB
304 	 */
305 	temp = (int8_t) buf[0];
306 	temp = (temp << 1) + ((buf[1] >> 7) & 0x1);
307 
308 	/* Temp is given in 1/2 deg. C, we convert to uK. */
309 	val = temp * 500000 + 273150000;
310 
311 	return val;
312 }
313 
314 static uint32_t
315 lmtemp_decode_ds75(const uint8_t *buf)
316 {
317 	int temp;
318 
319 	/*
320 	 * Sign-extend the MSB byte, and add in the fractions of a
321 	 * degree contained in the LSB (precision 1/16th DegC).
322 	 */
323 	temp = (int8_t)buf[0];
324 	temp = (temp << 4) | ((buf[1] >> 4) & 0xf);
325 
326 	/*
327 	 * Conversion to uK is simple.
328 	 */
329 	return (temp * 62500 + 273150000);
330 }
331 
332 static uint32_t
333 lmtemp_decode_lm77(const uint8_t *buf)
334 {
335 	int temp;
336 	uint32_t val;
337 
338 	/*
339 	 * Describe each bits of temperature registers on LM77.
340 	 *   D15 - D12:	Sign
341 	 *   D11 - D3 :	Bit8(MSB) - Bit0
342 	 */
343 	temp = (int8_t)buf[0];
344 	temp = (temp << 5) | ((buf[1] >> 3) & 0x1f);
345 
346 	/* Temp is given in 1/2 deg. C, we convert to uK. */
347 	val = temp * 500000 + 273150000;
348 
349 	return val;
350 }
351 
352 static void
353 lmtemp_setup_sysctl(struct lmtemp_softc *sc)
354 {
355 	const struct sysctlnode *me = NULL, *node = NULL;
356 
357 	iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
358 	lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT, (sc->sc_tmax - 5) * 2);
359 	lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, sc->sc_tmax * 2);
360 	iic_release_bus(sc->sc_tag, I2C_F_POLL);
361 
362 	sysctl_createv(NULL, 0, NULL, &me,
363 	    CTLFLAG_READWRITE,
364 	    CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
365 	    NULL, 0, NULL, 0,
366 	    CTL_MACHDEP, CTL_CREATE, CTL_EOL);
367 
368 	sysctl_createv(NULL, 0, NULL, &node,
369 	    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
370 	    CTLTYPE_INT, "temp", "Threshold temperature",
371 	    sysctl_lm75_temp, 1, (void *)sc, 0,
372 	    CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
373 }
374 
375 static int
376 sysctl_lm75_temp(SYSCTLFN_ARGS)
377 {
378 	struct sysctlnode node = *rnode;
379 	struct lmtemp_softc *sc = node.sysctl_data;
380 	int temp;
381 
382 	if (newp) {
383 
384 		/* we're asked to write */
385 		node.sysctl_data = &sc->sc_tmax;
386 		if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
387 
388 			temp = *(int *)node.sysctl_data;
389 			sc->sc_tmax = temp;
390 			iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
391 			lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT,
392 			    (sc->sc_tmax - 5) * 2);
393 			lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT,
394 			    sc->sc_tmax * 2);
395 			iic_release_bus(sc->sc_tag, I2C_F_POLL);
396 			return 0;
397 		}
398 		return EINVAL;
399 	} else {
400 
401 		node.sysctl_data = &sc->sc_tmax;
402 		node.sysctl_size = 4;
403 		return (sysctl_lookup(SYSCTLFN_CALL(&node)));
404 	}
405 
406 	return 0;
407 }
408 
409 SYSCTL_SETUP(sysctl_lmtemp_setup, "sysctl lmtemp subtree setup")
410 {
411 
412 	sysctl_createv(NULL, 0, NULL, NULL,
413 		       CTLFLAG_PERMANENT,
414 		       CTLTYPE_NODE, "machdep", NULL,
415 		       NULL, 0, NULL, 0,
416 		       CTL_MACHDEP, CTL_EOL);
417 }
418 
419 
420