xref: /openbsd/sys/dev/i2c/lm75.c (revision 76d0caae)
1 /*	$OpenBSD: lm75.c,v 1.20 2015/05/30 08:39:05 kettenis 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/sensors.h>
28 
29 #include <dev/i2c/i2cvar.h>
30 
31 #define	LM_MODEL_LM75	1
32 #define	LM_MODEL_LM77	2
33 #define	LM_MODEL_DS1775	3
34 #define	LM_MODEL_LM75A	4
35 #define	LM_MODEL_LM76	5
36 
37 #define LM_POLLTIME	3	/* 3s */
38 
39 #define	LM75_REG_TEMP			0x00
40 #define	LM75_REG_CONFIG			0x01
41 #define  LM75_CONFIG_SHUTDOWN		0x01
42 #define  LM75_CONFIG_CMPINT		0x02
43 #define  LM75_CONFIG_OSPOLARITY		0x04
44 #define  LM75_CONFIG_FAULT_QUEUE_MASK	0x18
45 #define  LM75_CONFIG_FAULT_QUEUE_1	(0 << 3)
46 #define  LM75_CONFIG_FAULT_QUEUE_2	(1 << 3)
47 #define  LM75_CONFIG_FAULT_QUEUE_4	(2 << 3)
48 #define  LM75_CONFIG_FAULT_QUEUE_6	(3 << 3)
49 #define  LM77_CONFIG_INTPOLARITY	0x08
50 #define  LM77_CONFIG_FAULT_QUEUE_4	0x10
51 #define  DS1755_CONFIG_RESOLUTION(i)	(9 + (((i) >> 5) & 3))
52 #define	LM75_REG_THYST_SET_POINT	0x02
53 #define	LM75_REG_TOS_SET_POINT		0x03
54 #define	LM77_REG_TLOW			0x04
55 #define	LM77_REG_THIGH			0x05
56 
57 struct lmtemp_softc {
58 	struct device sc_dev;
59 	i2c_tag_t sc_tag;
60 	int	sc_addr;
61 	int	sc_model;
62 	int	sc_bits;
63 	int	sc_ratio;
64 
65 	struct ksensor sc_sensor;
66 	struct ksensordev sc_sensordev;
67 };
68 
69 int  lmtemp_match(struct device *, void *, void *);
70 void lmtemp_attach(struct device *, struct device *, void *);
71 
72 struct cfattach lmtemp_ca = {
73 	sizeof(struct lmtemp_softc),
74 	lmtemp_match,
75 	lmtemp_attach
76 };
77 
78 struct cfdriver lmtemp_cd = {
79 	NULL, "lmtemp", DV_DULL
80 };
81 
82 /*
83  * Temperature on the LM75 is represented by a 9-bit two's complement
84  * integer in steps of 0.5C.  The following examples are taken from
85  * the LM75 data sheet:
86  *
87  *	+125C	0 1111 1010	0x0fa
88  *	+25C	0 0011 0010	0x032
89  *	+0.5C	0 0000 0001	0x001
90  *	0C	0 0000 0000	0x000
91  *	-0.5C	1 1111 1111	0x1ff
92  *	-25C	1 1100 1110	0x1ce
93  *	-55C	1 1001 0010	0x192
94  *
95  * Temperature on the LM75A is represented by an 11-bit two's complement
96  * integer in steps of 0.125C.  The LM75A can be treated like an LM75 if
97  * the extra precision is not required.  The following examples are
98  * taken from the LM75A data sheet:
99  *
100  *	+127.000C	011 1111 1000	0x3f8
101  *	+126.875C	011 1111 0111	0x3f7
102  *	+126.125C	011 1111 0001	0x3f1
103  *	+125.000C	011 1110 1000	0x3e8
104  *	+25.000C	000 1100 1000	0x0c8
105  *	+0.125C		000 0000 0001	0x001
106  *	0C		000 0000 0000	0x000
107  *	-0.125C		111 1111 1111	0x7ff
108  *	-25.000C	111 0011 1000	0x738
109  *	-54.875C	110 0100 1001	0x649
110  *	-55.000C	110 0100 1000	0x648
111  *
112  * Temperature on the LM77 is represented by a 13-bit two's complement
113  * integer in steps of 0.5C.  The LM76 is similar, but the integer is
114  * in steps of 0.065C
115  *
116  * LM75 temperature word:
117  *
118  * MSB Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 X X X X X X X
119  * 15  14   13   12   11   10   9    8    7    6 5 4 3 2 1 0
120  *
121  *
122  * LM75A temperature word:
123  *
124  * MSB Bit9 Bit8 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 X X X X X
125  * 15  14   13   12   11   10   9    8    7    6    5    4 3 2 1 0
126  *
127  *
128  * LM77 temperature word:
129  *
130  * Sign Sign Sign Sign MSB Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 Status bits
131  * 15   14   13   12   11  10   9    8    7    6    5    4    3    2 1 0
132  */
133 
134 int  lmtemp_temp_read(struct lmtemp_softc *, uint8_t, int *);
135 void lmtemp_refresh_sensor_data(void *);
136 
137 int
138 lmtemp_match(struct device *parent, void *match, void *aux)
139 {
140 	struct i2c_attach_args *ia = aux;
141 
142 	if (strcmp(ia->ia_name, "lm75") == 0 ||
143 	    strcmp(ia->ia_name, "lm76") == 0 ||
144 	    strcmp(ia->ia_name, "lm77") == 0 ||
145 	    strcmp(ia->ia_name, "ds1775") == 0 ||
146 	    strcmp(ia->ia_name, "lm75a") == 0)
147 		return (1);
148 	return (0);
149 }
150 
151 void
152 lmtemp_attach(struct device *parent, struct device *self, void *aux)
153 {
154 	struct lmtemp_softc *sc = (struct lmtemp_softc *)self;
155 	struct i2c_attach_args *ia = aux;
156 	u_int8_t cmd, data;
157 
158 	sc->sc_tag = ia->ia_tag;
159 	sc->sc_addr = ia->ia_addr;
160 
161 	printf(": %s", ia->ia_name);
162 
163 	/* If in SHUTDOWN mode, wake it up */
164 	iic_acquire_bus(sc->sc_tag, 0);
165 	cmd = LM75_REG_CONFIG;
166 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
167 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
168 		iic_release_bus(sc->sc_tag, 0);
169 		printf(", fails to respond\n");
170 		return;
171 	}
172 	if (data & LM75_CONFIG_SHUTDOWN) {
173 		data &= ~LM75_CONFIG_SHUTDOWN;
174 		if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
175 		    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
176 			printf(", cannot wake up\n");
177 			iic_release_bus(sc->sc_tag, 0);
178 			return;
179 		}
180 		printf(", woken up");
181 	}
182 	iic_release_bus(sc->sc_tag, 0);
183 
184 	sc->sc_model = LM_MODEL_LM75;
185 	sc->sc_bits = 9;
186 	sc->sc_ratio = 500000;		/* 0.5 degC for LSB */
187 	if (strcmp(ia->ia_name, "lm77") == 0) {
188 		sc->sc_model = LM_MODEL_LM77;
189 		sc->sc_bits = 13;
190 	} else if (strcmp(ia->ia_name, "lm76") == 0) {
191 		sc->sc_model = LM_MODEL_LM76;
192 		sc->sc_bits = 13;
193 		sc->sc_ratio = 62500;	/* 0.0625 degC for LSB */
194 	} else if (strcmp(ia->ia_name, "ds1775") == 0) {
195 		sc->sc_model = LM_MODEL_DS1775;
196 		//sc->sc_bits = DS1755_CONFIG_RESOLUTION(data);
197 	} else if (strcmp(ia->ia_name, "lm75a") == 0) {
198 		/* For simplicity's sake, treat the LM75A as an LM75 */
199 		sc->sc_model = LM_MODEL_LM75A;
200 	}
201 
202 	printf("\n");
203 
204 	/* Initialize sensor data */
205 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
206 	    sizeof(sc->sc_sensordev.xname));
207 	sc->sc_sensor.type = SENSOR_TEMP;
208 
209 	/* Hook into the hw.sensors sysctl */
210 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
211 	sensordev_install(&sc->sc_sensordev);
212 
213 	sensor_task_register(sc, lmtemp_refresh_sensor_data, LM_POLLTIME);
214 }
215 
216 int
217 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, int *valp)
218 {
219 	u_int8_t cmd = which;
220 	u_int16_t data = 0x0000;
221 	int error;
222 
223 	iic_acquire_bus(sc->sc_tag, 0);
224 	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
225 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0);
226 	iic_release_bus(sc->sc_tag, 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