Line data Source code
1 : /* $OpenBSD: adt7460.c,v 1.21 2007/12/12 16:56:59 deraadt Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2005 Mark Kettenis
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 : #include <sys/param.h>
20 : #include <sys/systm.h>
21 : #include <sys/device.h>
22 : #include <sys/sensors.h>
23 :
24 : #include <dev/i2c/i2cvar.h>
25 :
26 : /* ADT7460 registers */
27 : #define ADT7460_2_5V 0x20
28 : #define ADT7460_VCCP 0x21
29 : #define ADT7460_VCC 0x22
30 : #define ADT7460_V5 0x23
31 : #define ADT7460_V12 0x24
32 : #define ADT7460_VTR 0x99
33 : #define ADT7460_VBAT 0x9a
34 : #define ADT7460_REM1_TEMP 0x25
35 : #define ADT7460_LOCAL_TEMP 0x26
36 : #define ADT7460_REM2_TEMP 0x27
37 : #define ADT7460_TACH1L 0x28
38 : #define ADT7460_TACH1H 0x29
39 : #define ADT7460_TACH2L 0x2a
40 : #define ADT7460_TACH2H 0x2b
41 : #define ADT7460_TACH3L 0x2c
42 : #define ADT7460_TACH3H 0x2d
43 : #define ADT7460_TACH4L 0x2e
44 : #define ADT7460_TACH4H 0x2f
45 : #define ADT7460_TACH5L 0xa9
46 : #define ADT7460_TACH5H 0xaa
47 : #define ADT7460_TACH6L 0xab
48 : #define ADT7460_TACH6H 0xac
49 : #define ADT7460_REVISION 0x3f
50 : #define ADT7460_CONFIG 0x40
51 : #define ADT7460_CONFIG_Vcc 0x80
52 :
53 : /* Sensors */
54 : #define ADT_2_5V 0
55 : #define ADT_VCCP 1
56 : #define ADT_VCC 2
57 : #define ADT_V5 3
58 : #define ADT_V12 4
59 : #define ADT_VTR 5
60 : #define ADT_VBAT 6
61 : #define ADT_REM1_TEMP 7
62 : #define ADT_LOCAL_TEMP 8
63 : #define ADT_REM2_TEMP 9
64 : #define ADT_TACH1 10
65 : #define ADT_TACH2 11
66 : #define ADT_TACH3 12
67 : #define ADT_TACH4 13
68 : #define ADT_TACH5 14
69 : #define ADT_TACH6 15
70 : #define ADT_NUM_SENSORS 16
71 :
72 : struct adt_chip {
73 : const char *name;
74 : short ratio[7];
75 : int type;
76 : short vcc;
77 : } adt_chips[] = {
78 : /* register 0x20 0x21 0x22 0x23 0x24 0xa8 0xaa type */
79 : /* 2.5v vccp vcc 5v 12v vtr vbat */
80 :
81 : { "adt7460", { 2500, 0, 3300, 0, 0, 0, 0 }, 7460, 5000 },
82 : { "adt7467", { 2500, 2250, 3300, 5000, 12000, 0, 0 }, 7467, 5000 },
83 : { "adt7475", { 0, 2250, 3300, 0, 0, 0, 0 }, 7475, 0 },
84 : { "adt7476", { 2500, 2250, 3300, 5000, 12000, 0, 0 }, 7476, 0 },
85 : { "adm1027", { 2500, 2250, 3300, 5000, 12000, 0, 0 }, 1027, 5000 },
86 : { "lm85", { 2500, 2250, 3300, 5000, 12000, 0, 0 }, 7467, 0 },
87 : { "emc6d100", { 2500, 2250, 3300, 5000, 12000, 0, 0 }, 6100, 0 },
88 : { "emc6w201", { 2500, 2250, 3300, 5000, 12000, 0, 0 }, 6201, 0 },
89 : { "lm96000", { 2500, 2250, 3300, 5000, 12000, 0, 0 }, 96000, 0 },
90 : { "sch5017", { 5000, 2250, 3300, 5000, 12000, 0, 0 }, 5017, 0 },
91 : { "sch5027", { 5000, 2250, 3300, 5000, 12000, 3300, 3300 }, 5027, 0 }
92 : };
93 :
94 : struct {
95 : char sensor;
96 : u_int8_t cmd;
97 : u_short index;
98 : } worklist[] = {
99 : { ADT_2_5V, ADT7460_2_5V, 32768 + 0 },
100 : { ADT_VCCP, ADT7460_VCCP, 32768 + 1 },
101 : { ADT_VCC, ADT7460_VCC, 32768 + 2 },
102 : { ADT_V5, ADT7460_V5, 32768 + 3 },
103 : { ADT_V12, ADT7460_V12, 32768 + 4 },
104 : { ADT_VTR, ADT7460_VTR, 32768 + 5 },
105 : { ADT_VBAT, ADT7460_VBAT, 32768 + 6 },
106 : { ADT_REM1_TEMP, ADT7460_REM1_TEMP },
107 : { ADT_LOCAL_TEMP, ADT7460_LOCAL_TEMP },
108 : { ADT_REM2_TEMP, ADT7460_REM2_TEMP },
109 : { ADT_TACH1, ADT7460_TACH1L },
110 : { ADT_TACH2, ADT7460_TACH2L },
111 : { ADT_TACH3, ADT7460_TACH3L },
112 : { ADT_TACH4, ADT7460_TACH4L },
113 : { ADT_TACH5, ADT7460_TACH5L },
114 : { ADT_TACH6, ADT7460_TACH6L },
115 : };
116 :
117 : struct adt_softc {
118 : struct device sc_dev;
119 : i2c_tag_t sc_tag;
120 : i2c_addr_t sc_addr;
121 : u_int8_t sc_conf;
122 : struct adt_chip *chip;
123 :
124 : struct ksensor sc_sensor[ADT_NUM_SENSORS];
125 : struct ksensordev sc_sensordev;
126 : };
127 :
128 : int adt_match(struct device *, void *, void *);
129 : void adt_attach(struct device *, struct device *, void *);
130 :
131 : void adt_refresh(void *);
132 :
133 : struct cfattach adt_ca = {
134 : sizeof(struct adt_softc), adt_match, adt_attach
135 : };
136 :
137 : struct cfdriver adt_cd = {
138 : NULL, "adt", DV_DULL
139 : };
140 :
141 : int
142 0 : adt_match(struct device *parent, void *match, void *aux)
143 : {
144 0 : struct i2c_attach_args *ia = aux;
145 : int i;
146 :
147 0 : for (i = 0; i < sizeof(adt_chips) / sizeof(adt_chips[0]); i++)
148 0 : if (strcmp(ia->ia_name, adt_chips[i].name) == 0)
149 0 : return (1);
150 0 : return (0);
151 0 : }
152 :
153 : void
154 0 : adt_attach(struct device *parent, struct device *self, void *aux)
155 : {
156 0 : struct adt_softc *sc = (struct adt_softc *)self;
157 0 : struct i2c_attach_args *ia = aux;
158 0 : u_int8_t cmd, rev, data;
159 : int i;
160 :
161 0 : sc->sc_tag = ia->ia_tag;
162 0 : sc->sc_addr = ia->ia_addr;
163 :
164 0 : iic_acquire_bus(sc->sc_tag, 0);
165 :
166 0 : for (i = 0; i < sizeof(adt_chips) / sizeof(adt_chips[0]); i++) {
167 0 : if (strcmp(ia->ia_name, adt_chips[i].name) == 0) {
168 0 : sc->chip = &adt_chips[i];
169 0 : break;
170 : }
171 : }
172 :
173 0 : cmd = ADT7460_REVISION;
174 0 : if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
175 0 : sc->sc_addr, &cmd, sizeof cmd, &rev, sizeof rev, 0)) {
176 0 : iic_release_bus(sc->sc_tag, 0);
177 0 : printf(": cannot read REV register\n");
178 0 : return;
179 : }
180 :
181 0 : cmd = ADT7460_CONFIG;
182 0 : if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
183 0 : sc->sc_addr, &cmd, sizeof cmd, &sc->sc_conf, sizeof sc->sc_conf, 0)) {
184 0 : iic_release_bus(sc->sc_tag, 0);
185 0 : printf(": cannot read config register\n");
186 0 : return;
187 : }
188 :
189 0 : if (sc->chip->type == 7460) {
190 0 : data = 1;
191 0 : cmd = ADT7460_CONFIG;
192 0 : if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
193 0 : sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
194 0 : iic_release_bus(sc->sc_tag, 0);
195 0 : printf(": cannot set control register\n");
196 0 : return;
197 : }
198 : }
199 :
200 0 : iic_release_bus(sc->sc_tag, 0);
201 :
202 0 : printf(": %s rev 0x%02x", ia->ia_name, rev);
203 :
204 : /* Initialize sensor data. */
205 0 : strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
206 : sizeof(sc->sc_sensordev.xname));
207 :
208 0 : sc->sc_sensor[ADT_2_5V].type = SENSOR_VOLTS_DC;
209 0 : strlcpy(sc->sc_sensor[ADT_2_5V].desc, "+2.5Vin",
210 : sizeof(sc->sc_sensor[ADT_2_5V].desc));
211 :
212 0 : if (sc->chip->type == 5017)
213 0 : strlcpy(sc->sc_sensor[ADT_2_5V].desc, "+5VTR",
214 : sizeof(sc->sc_sensor[ADT_2_5V].desc));
215 0 : if (sc->chip->type == 5027)
216 0 : strlcpy(sc->sc_sensor[ADT_2_5V].desc, "+5V",
217 : sizeof(sc->sc_sensor[ADT_2_5V].desc));
218 :
219 0 : sc->sc_sensor[ADT_VCCP].type = SENSOR_VOLTS_DC;
220 0 : strlcpy(sc->sc_sensor[ADT_VCCP].desc, "Vccp",
221 : sizeof(sc->sc_sensor[ADT_VCCP].desc));
222 :
223 0 : sc->sc_sensor[ADT_VCC].type = SENSOR_VOLTS_DC;
224 0 : strlcpy(sc->sc_sensor[ADT_VCC].desc, "Vcc",
225 : sizeof(sc->sc_sensor[ADT_VCC].desc));
226 :
227 0 : sc->sc_sensor[ADT_V5].type = SENSOR_VOLTS_DC;
228 0 : strlcpy(sc->sc_sensor[ADT_V5].desc, "+5V",
229 : sizeof(sc->sc_sensor[ADT_V5].desc));
230 :
231 0 : sc->sc_sensor[ADT_V12].type = SENSOR_VOLTS_DC;
232 0 : strlcpy(sc->sc_sensor[ADT_V12].desc, "+12V",
233 : sizeof(sc->sc_sensor[ADT_V12].desc));
234 :
235 0 : sc->sc_sensor[ADT_VTR].type = SENSOR_VOLTS_DC;
236 0 : strlcpy(sc->sc_sensor[ADT_VTR].desc, "+Vtr",
237 : sizeof(sc->sc_sensor[ADT_VTR].desc));
238 :
239 0 : sc->sc_sensor[ADT_VBAT].type = SENSOR_VOLTS_DC;
240 0 : strlcpy(sc->sc_sensor[ADT_VBAT].desc, "+Vbat",
241 : sizeof(sc->sc_sensor[ADT_VBAT].desc));
242 :
243 0 : sc->sc_sensor[ADT_REM1_TEMP].type = SENSOR_TEMP;
244 0 : strlcpy(sc->sc_sensor[ADT_REM1_TEMP].desc, "Remote",
245 : sizeof(sc->sc_sensor[ADT_REM1_TEMP].desc));
246 :
247 0 : sc->sc_sensor[ADT_LOCAL_TEMP].type = SENSOR_TEMP;
248 0 : strlcpy(sc->sc_sensor[ADT_LOCAL_TEMP].desc, "Internal",
249 : sizeof(sc->sc_sensor[ADT_LOCAL_TEMP].desc));
250 :
251 0 : sc->sc_sensor[ADT_REM2_TEMP].type = SENSOR_TEMP;
252 0 : strlcpy(sc->sc_sensor[ADT_REM2_TEMP].desc, "Remote",
253 : sizeof(sc->sc_sensor[ADT_REM2_TEMP].desc));
254 :
255 0 : sc->sc_sensor[ADT_TACH1].type = SENSOR_FANRPM;
256 0 : sc->sc_sensor[ADT_TACH2].type = SENSOR_FANRPM;
257 0 : sc->sc_sensor[ADT_TACH3].type = SENSOR_FANRPM;
258 0 : sc->sc_sensor[ADT_TACH4].type = SENSOR_FANRPM;
259 0 : sc->sc_sensor[ADT_TACH5].type = SENSOR_FANRPM;
260 0 : sc->sc_sensor[ADT_TACH6].type = SENSOR_FANRPM;
261 :
262 0 : if (sensor_task_register(sc, adt_refresh, 5) == NULL) {
263 0 : printf(", unable to register update task\n");
264 0 : return;
265 : }
266 :
267 0 : for (i = 0; i < ADT_NUM_SENSORS; i++) {
268 0 : if (worklist[i].index >= 32768 &&
269 0 : sc->chip->ratio[worklist[i].index - 32768] == 0)
270 : continue;
271 0 : sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
272 0 : }
273 0 : sensordev_install(&sc->sc_sensordev);
274 :
275 :
276 0 : printf("\n");
277 0 : }
278 :
279 : void
280 0 : adt_refresh(void *arg)
281 : {
282 0 : struct adt_softc *sc = arg;
283 0 : u_int8_t cmd, data, data2;
284 : u_int16_t fan;
285 : int i, ratio;
286 :
287 0 : iic_acquire_bus(sc->sc_tag, 0);
288 :
289 0 : for (i = 0; i < sizeof worklist / sizeof(worklist[0]); i++) {
290 :
291 0 : if (worklist[i].index >= 32768) {
292 0 : ratio = sc->chip->ratio[worklist[i].index - 32768];
293 0 : if (ratio == 0) /* do not read a dead register */
294 : continue;
295 : }
296 0 : cmd = worklist[i].cmd;
297 0 : if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
298 0 : sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
299 0 : sc->sc_sensor[i].flags |= SENSOR_FINVALID;
300 0 : continue;
301 : }
302 :
303 0 : sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
304 0 : switch (worklist[i].sensor) {
305 : case ADT_VCC:
306 0 : if (sc->chip->vcc && (sc->sc_conf & ADT7460_CONFIG_Vcc))
307 0 : ratio = sc->chip->vcc;
308 : /* FALLTHROUGH */
309 : case ADT_2_5V:
310 : case ADT_VCCP:
311 : case ADT_V5:
312 : case ADT_V12:
313 : case ADT_VTR:
314 : case ADT_VBAT:
315 0 : sc->sc_sensor[i].value = ratio * 1000 * (u_int)data / 192;
316 0 : break;
317 : case ADT_LOCAL_TEMP:
318 : case ADT_REM1_TEMP:
319 : case ADT_REM2_TEMP:
320 0 : if (data == 0x80)
321 0 : sc->sc_sensor[i].flags |= SENSOR_FINVALID;
322 : else
323 0 : sc->sc_sensor[i].value =
324 0 : (int8_t)data * 1000000 + 273150000;
325 : break;
326 : case ADT_TACH1:
327 : case ADT_TACH2:
328 : case ADT_TACH3:
329 : case ADT_TACH4:
330 0 : cmd = worklist[i].cmd + 1; /* TACHnH follows TACHnL */
331 0 : if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
332 0 : sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
333 0 : sc->sc_sensor[i].flags |= SENSOR_FINVALID;
334 0 : continue;
335 : }
336 :
337 0 : fan = data + (data2 << 8);
338 0 : if (fan == 0 || fan == 0xffff)
339 0 : sc->sc_sensor[i].flags |= SENSOR_FINVALID;
340 : else
341 0 : sc->sc_sensor[i].value = (90000 * 60) / fan;
342 : break;
343 : case ADT_TACH5:
344 : case ADT_TACH6:
345 0 : if (sc->chip->type != 5027) {
346 0 : sc->sc_sensor[i].flags |= SENSOR_FINVALID;
347 0 : break; /* only 5027 has these fans? */
348 : }
349 0 : cmd = worklist[i].cmd + 1; /* TACHnH follows TACHnL */
350 0 : if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
351 0 : sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
352 0 : sc->sc_sensor[i].flags |= SENSOR_FINVALID;
353 0 : continue;
354 : }
355 :
356 0 : fan = data + (data2 << 8);
357 0 : if (fan == 0 || fan == 0xffff)
358 0 : sc->sc_sensor[i].flags |= SENSOR_FINVALID;
359 : else
360 0 : sc->sc_sensor[i].value = fan * 60;
361 : break;
362 : default:
363 0 : sc->sc_sensor[i].flags |= SENSOR_FINVALID;
364 0 : break;
365 : }
366 : }
367 :
368 0 : iic_release_bus(sc->sc_tag, 0);
369 0 : }
|