xref: /openbsd/sys/dev/i2c/lm75.c (revision 404b540a)
1 /*	$OpenBSD: lm75.c,v 1.18 2008/04/17 19:01:48 deraadt Exp $	*/
2 /*	$NetBSD: lm75.c,v 1.1 2003/09/30 00:35:31 thorpej Exp $	*/
3 /*
4  * Copyright (c) 2006 Theo de Raadt <deraadt@openbsd.org>
5  * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /*
21  * National Semiconductor LM75/LM76/LM77 temperature sensor.
22  */
23 
24 #include <sys/param.h>
25 #include <sys/systm.h>
26 #include <sys/device.h>
27 #include <sys/kernel.h>
28 #include <sys/sensors.h>
29 
30 #include <dev/i2c/i2cvar.h>
31 
32 #define	LM_MODEL_LM75	1
33 #define	LM_MODEL_LM77	2
34 #define	LM_MODEL_DS1775	3
35 #define	LM_MODEL_LM75A	4
36 #define	LM_MODEL_LM76	5
37 
38 #define LM_POLLTIME	3	/* 3s */
39 
40 #define	LM75_REG_TEMP			0x00
41 #define	LM75_REG_CONFIG			0x01
42 #define  LM75_CONFIG_SHUTDOWN		0x01
43 #define  LM75_CONFIG_CMPINT		0x02
44 #define  LM75_CONFIG_OSPOLARITY		0x04
45 #define  LM75_CONFIG_FAULT_QUEUE_MASK	0x18
46 #define  LM75_CONFIG_FAULT_QUEUE_1	(0 << 3)
47 #define  LM75_CONFIG_FAULT_QUEUE_2	(1 << 3)
48 #define  LM75_CONFIG_FAULT_QUEUE_4	(2 << 3)
49 #define  LM75_CONFIG_FAULT_QUEUE_6	(3 << 3)
50 #define  LM77_CONFIG_INTPOLARITY	0x08
51 #define  LM77_CONFIG_FAULT_QUEUE_4	0x10
52 #define  DS1755_CONFIG_RESOLUTION(i)	(9 + (((i) >> 5) & 3))
53 #define	LM75_REG_THYST_SET_POINT	0x02
54 #define	LM75_REG_TOS_SET_POINT		0x03
55 #define	LM77_REG_TLOW			0x04
56 #define	LM77_REG_THIGH			0x05
57 
58 struct lmtemp_softc {
59 	struct device sc_dev;
60 	i2c_tag_t sc_tag;
61 	int	sc_addr;
62 	int	sc_model;
63 	int	sc_bits;
64 	int	sc_ratio;
65 
66 	struct ksensor sc_sensor;
67 	struct ksensordev sc_sensordev;
68 };
69 
70 int  lmtemp_match(struct device *, void *, void *);
71 void lmtemp_attach(struct device *, struct device *, void *);
72 
73 struct cfattach lmtemp_ca = {
74 	sizeof(struct lmtemp_softc),
75 	lmtemp_match,
76 	lmtemp_attach
77 };
78 
79 struct cfdriver lmtemp_cd = {
80 	NULL, "lmtemp", DV_DULL
81 };
82 
83 /*
84  * Temperature on the LM75 is represented by a 9-bit two's complement
85  * integer in steps of 0.5C.  The following examples are taken from
86  * the LM75 data sheet:
87  *
88  *	+125C	0 1111 1010	0x0fa
89  *	+25C	0 0011 0010	0x032
90  *	+0.5C	0 0000 0001	0x001
91  *	0C	0 0000 0000	0x000
92  *	-0.5C	1 1111 1111	0x1ff
93  *	-25C	1 1100 1110	0x1ce
94  *	-55C	1 1001 0010	0x192
95  *
96  * Temperature on the LM75A is represented by an 11-bit two's complement
97  * integer in steps of 0.125C.  The LM75A can be treated like an LM75 if
98  * the extra precision is not required.  The following examples are
99  * taken from the LM75A data sheet:
100  *
101  *	+127.000C	011 1111 1000	0x3f8
102  *	+126.875C	011 1111 0111	0x3f7
103  *	+126.125C	011 1111 0001	0x3f1
104  *	+125.000C	011 1110 1000	0x3e8
105  *	+25.000C	000 1100 1000	0x0c8
106  *	+0.125C		000 0000 0001	0x001
107  *	0C		000 0000 0000	0x000
108  *	-0.125C		111 1111 1111	0x7ff
109  *	-25.000C	111 0011 1000	0x738
110  *	-54.875C	110 0100 1001	0x649
111  *	-55.000C	110 0100 1000	0x648
112  *
113  * Temperature on the LM77 is represented by a 13-bit two's complement
114  * integer in steps of 0.5C.  The LM76 is similar, but the integer is
115  * in steps of 0.065C
116  *
117  * LM75 temperature word:
118  *
119  * MSB Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 X X X X X X X
120  * 15  14   13   12   11   10   9    8    7    6 5 4 3 2 1 0
121  *
122  *
123  * LM75A temperature word:
124  *
125  * MSB Bit9 Bit8 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 X X X X X
126  * 15  14   13   12   11   10   9    8    7    6    5    4 3 2 1 0
127  *
128  *
129  * LM77 temperature word:
130  *
131  * Sign Sign Sign Sign MSB Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 Status bits
132  * 15   14   13   12   11  10   9    8    7    6    5    4    3    2 1 0
133  */
134 
135 int  lmtemp_temp_read(struct lmtemp_softc *, uint8_t, int *);
136 void lmtemp_refresh_sensor_data(void *);
137 
138 int
139 lmtemp_match(struct device *parent, void *match, void *aux)
140 {
141 	struct i2c_attach_args *ia = aux;
142 
143 	if (strcmp(ia->ia_name, "lm75") == 0 ||
144 	    strcmp(ia->ia_name, "lm76") == 0 ||
145 	    strcmp(ia->ia_name, "lm77") == 0 ||
146 	    strcmp(ia->ia_name, "ds1775") == 0 ||
147 	    strcmp(ia->ia_name, "lm75a") == 0)
148 		return (1);
149 	return (0);
150 }
151 
152 void
153 lmtemp_attach(struct device *parent, struct device *self, void *aux)
154 {
155 	struct lmtemp_softc *sc = (struct lmtemp_softc *)self;
156 	struct i2c_attach_args *ia = aux;
157 	u_int8_t cmd, data;
158 
159 	sc->sc_tag = ia->ia_tag;
160 	sc->sc_addr = ia->ia_addr;
161 
162 	printf(": %s", ia->ia_name);
163 
164 	/* If in SHUTDOWN mode, wake it up */
165 	iic_acquire_bus(sc->sc_tag, 0);
166 	cmd = LM75_REG_CONFIG;
167 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
168 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
169 		iic_release_bus(sc->sc_tag, 0);
170 		printf(", fails to respond\n");
171 		return;
172 	}
173 	if (data & LM75_CONFIG_SHUTDOWN) {
174 		data &= ~LM75_CONFIG_SHUTDOWN;
175 		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
176 		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
177 			printf(", cannot wake up\n");
178 			iic_release_bus(sc->sc_tag, 0);
179 			return;
180 		}
181 		printf(", woken up");
182 	}
183 	iic_release_bus(sc->sc_tag, 0);
184 
185 	sc->sc_model = LM_MODEL_LM75;
186 	sc->sc_bits = 9;
187 	sc->sc_ratio = 500000;		/* 0.5 degC for LSB */
188 	if (strcmp(ia->ia_name, "lm77") == 0) {
189 		sc->sc_model = LM_MODEL_LM77;
190 		sc->sc_bits = 13;
191 	} else if (strcmp(ia->ia_name, "lm76") == 0) {
192 		sc->sc_model = LM_MODEL_LM76;
193 		sc->sc_bits = 13;
194 		sc->sc_ratio = 62500;	/* 0.0625 degC for LSB */
195 	} else if (strcmp(ia->ia_name, "ds1775") == 0) {
196 		sc->sc_model = LM_MODEL_DS1775;
197 		//sc->sc_bits = DS1755_CONFIG_RESOLUTION(data);
198 	} else if (strcmp(ia->ia_name, "lm75a") == 0) {
199 		/* For simplicity's sake, treat the LM75A as an LM75 */
200 		sc->sc_model = LM_MODEL_LM75A;
201 	}
202 
203 	printf("\n");
204 
205 	/* Initialize sensor data */
206 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
207 	    sizeof(sc->sc_sensordev.xname));
208 	sc->sc_sensor.type = SENSOR_TEMP;
209 
210 	/* Hook into the hw.sensors sysctl */
211 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
212 	sensordev_install(&sc->sc_sensordev);
213 
214 	sensor_task_register(sc, lmtemp_refresh_sensor_data, LM_POLLTIME);
215 }
216 
217 int
218 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, int *valp)
219 {
220 	u_int8_t cmd;
221 	u_int16_t data = 0x0000;
222 	int error;
223 
224 	cmd = which;
225 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
226 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0);
227 	if (error)
228 		return (error);
229 
230 	/* Some chips return transient 0's.. we try next time */
231 	if (data == 0x0000)
232 		return (1);
233 
234 	/* convert to half-degrees C */
235 	*valp = betoh16(data) / (1 << (16 - sc->sc_bits));
236 	return (0);
237 }
238 
239 void
240 lmtemp_refresh_sensor_data(void *aux)
241 {
242 	struct lmtemp_softc *sc = aux;
243 	int val;
244 	int error;
245 
246 	error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val);
247 	if (error) {
248 #if 0
249 		printf("%s: unable to read temperature, error = %d\n",
250 		    sc->sc_dev.dv_xname, error);
251 #endif
252 		sc->sc_sensor.flags |= SENSOR_FINVALID;
253 		return;
254 	}
255 
256 	sc->sc_sensor.value = val * sc->sc_ratio + 273150000;
257 	sc->sc_sensor.flags &= ~SENSOR_FINVALID;
258 }
259