Line data Source code
1 : /* $OpenBSD: asc7621.c,v 1.4 2007/10/31 20:46:17 cnst Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2007 Mike Belopuhov
5 : * Copyright (c) 2007 Theo de Raadt
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 : #include <sys/param.h>
21 : #include <sys/systm.h>
22 : #include <sys/device.h>
23 : #include <sys/sensors.h>
24 :
25 : #include <dev/i2c/i2cvar.h>
26 :
27 : /* ASC7621 registers */
28 :
29 : #define ASC7621_PECI 0x40 /* Check for PECI monitoring */
30 : #define ASC7621_PECI_MASK 0x10 /* 00010000 */
31 :
32 : #define ASC7621_LEGACY 0x36 /* Check for legacy mode */
33 : #define ASC7621_LEGACY_MASK 0x10 /* 00010000 */
34 :
35 : #define ASC7621_TEMP1H 0x25 /* Zone 1 Temperature (MS Byte) */
36 : #define ASC7621_TEMP1L 0x10 /* Zone 1 Temperature (LS Byte) */
37 : #define ASC7621_TEMP2H 0x26 /* Zone 2 Temperature (MS Byte) */
38 : #define ASC7621_TEMP2L 0x15 /* Zone 2 Temperature (LS Byte) */
39 : #define ASC7621_TEMP3H 0x27 /* Zone 3 Temperature (MS Byte) */
40 : #define ASC7621_TEMP3L 0x16 /* Zone 3 Temperature (LS Byte) */
41 : #define ASC7621_TEMP4H 0x33 /* Zone 4 Temperature (MS Byte) */
42 : #define ASC7621_TEMP4L 0x17 /* Zone 4 Temperature (LS Byte) */
43 : #define ASC7621_TEMP_NA 0x80 /* Not plugged */
44 :
45 : #define ASC7621_IN1_VH 0x20 /* 2.5V (MS Byte) */
46 : #define ASC7621_IN1_VL 0x13 /* 2.5V (LS Byte) */
47 : #define ASC7621_IN2_VH 0x21 /* VCCP (MS Byte) */
48 : #define ASC7621_IN2_VL 0x18 /* VCCP (LS Byte) */
49 : #define ASC7621_IN3_VH 0x22 /* 3.3V (MS Byte) */
50 : #define ASC7621_IN3_VL 0x11 /* 2.3V (LS Byte) */
51 : #define ASC7621_IN4_VH 0x23 /* 5V (MS Byte) */
52 : #define ASC7621_IN4_VL 0x12 /* 5V (LS Byte) */
53 : #define ASC7621_IN5_VH 0x24 /* 12V (MS Byte) */
54 : #define ASC7621_IN5_VL 0x14 /* 12V (LS Byte) */
55 :
56 : #define ASC7621_TACH1H 0x29 /* Tachometer 1 (MS Byte) */
57 : #define ASC7621_TACH1L 0x28 /* Tachometer 1 (LS Byte) */
58 : #define ASC7621_TACH2H 0x2b /* Tachometer 2 (MS Byte) */
59 : #define ASC7621_TACH2L 0x2a /* Tachometer 2 (LS Byte) */
60 : #define ASC7621_TACH3H 0x2d /* Tachometer 3 (MS Byte) */
61 : #define ASC7621_TACH3L 0x2c /* Tachometer 3 (LS Byte) */
62 : #define ASC7621_TACH4H 0x2f /* Tachometer 4 (MS Byte) */
63 : #define ASC7621_TACH4L 0x2e /* Tachometer 4 (LS Byte) */
64 :
65 : /* Sensors */
66 : #define ADL_TEMP1 0
67 : #define ADL_TEMP2 1
68 : #define ADL_TEMP3 2
69 : #define ADL_TEMP4 3
70 : #define ADL_IN1_V 4
71 : #define ADL_IN2_V 5
72 : #define ADL_IN3_V 6
73 : #define ADL_IN4_V 7
74 : #define ADL_IN5_V 8
75 : #define ADL_TACH1 9
76 : #define ADL_TACH2 10
77 : #define ADL_TACH3 11
78 : #define ADL_TACH4 12
79 : #define ADL_NUM_SENSORS 13
80 :
81 : struct {
82 : char sensor;
83 : u_int8_t hreg; /* MS-byte register */
84 : u_int8_t lreg; /* LS-byte register */
85 : char *name;
86 : u_short mVscale;
87 : u_short tempscale; /* else a fan */
88 : } adl_worklist[] = {
89 : { ADL_TEMP1, ASC7621_TEMP1H, ASC7621_TEMP1L, "CPU", 0, 1 },
90 : { ADL_TEMP2, ASC7621_TEMP2H, ASC7621_TEMP2L, "CPU", 0, 1 },
91 : { ADL_TEMP3, ASC7621_TEMP3H, ASC7621_TEMP3L, "Internal", 0, 1 },
92 : { ADL_TEMP4, ASC7621_TEMP4H, ASC7621_TEMP4L, "External", 0, 1 },
93 :
94 : { ADL_IN1_V, ASC7621_IN1_VH, ASC7621_IN1_VL, "+1.5V", 2500, 0 },
95 : { ADL_IN2_V, ASC7621_IN2_VH, ASC7621_IN2_VL, "Vccp", 2250, 0 },
96 : { ADL_IN3_V, ASC7621_IN3_VH, ASC7621_IN3_VL, "+3.3V", 3300, 0 },
97 : { ADL_IN4_V, ASC7621_IN4_VH, ASC7621_IN4_VL, "+5V", 5000, 0 },
98 : { ADL_IN5_V, ASC7621_IN5_VH, ASC7621_IN5_VL, "+12V", 12000, 0 },
99 :
100 : { ADL_TACH1, ASC7621_TACH1L, ASC7621_TACH1H, "", 0, 0 },
101 : { ADL_TACH2, ASC7621_TACH2L, ASC7621_TACH2H, "", 0, 0 },
102 : { ADL_TACH3, ASC7621_TACH3L, ASC7621_TACH3H, "", 0, 0 },
103 : { ADL_TACH4, ASC7621_TACH4L, ASC7621_TACH4H, "", 0, 0 }
104 : };
105 :
106 : struct adl_softc {
107 : struct device sc_dev;
108 : i2c_tag_t sc_tag;
109 : i2c_addr_t sc_addr;
110 : u_int8_t sc_conf;
111 :
112 : struct ksensor sc_sensor[ADL_NUM_SENSORS];
113 : struct ksensordev sc_sensordev;
114 : };
115 :
116 : #if 0
117 : static int peci_enabled;
118 : static int legacy_mode;
119 : #endif
120 :
121 : int adl_match(struct device *, void *, void *);
122 : void adl_attach(struct device *, struct device *, void *);
123 :
124 : void adl_refresh(void *);
125 :
126 : struct cfattach adl_ca = {
127 : sizeof(struct adl_softc), adl_match, adl_attach
128 : };
129 :
130 : struct cfdriver adl_cd = {
131 : NULL, "adl", DV_DULL
132 : };
133 :
134 : int
135 0 : adl_match(struct device *parent, void *match, void *aux)
136 : {
137 0 : struct i2c_attach_args *ia = aux;
138 :
139 0 : if (strcmp(ia->ia_name, "asc7621") == 0)
140 0 : return (1);
141 0 : return (0);
142 0 : }
143 :
144 : void
145 0 : adl_attach(struct device *parent, struct device *self, void *aux)
146 : {
147 0 : struct adl_softc *sc = (struct adl_softc *)self;
148 0 : struct i2c_attach_args *ia = aux;
149 0 : u_int8_t cmd, data;
150 : int i;
151 :
152 0 : sc->sc_tag = ia->ia_tag;
153 0 : sc->sc_addr = ia->ia_addr;
154 :
155 0 : printf(": %s", ia->ia_name);
156 :
157 : /* Initialize sensor data. */
158 0 : strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
159 : sizeof(sc->sc_sensordev.xname));
160 :
161 : /* Check for PECI mode */
162 0 : cmd = ASC7621_PECI;
163 0 : (void)iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
164 : &cmd, sizeof(cmd), &data, sizeof(data), 0);
165 0 : if (data & ASC7621_PECI_MASK)
166 0 : printf(", PECI enabled\n");
167 :
168 : #if 0
169 : /* Check for legacy mode */
170 : cmd = ASC7621_LEGACY;
171 : if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
172 : &cmd, sizeof(cmd), &data, sizeof(data), 0)) {
173 : printf(", unable to read PECI configuration register");
174 : }
175 : if (data & ASC7621_LEGACY_MASK)
176 : legacy_mode = 1;
177 : #endif
178 :
179 0 : if (sensor_task_register(sc, adl_refresh, 5) == NULL) {
180 0 : printf(", unable to register update task\n");
181 0 : return;
182 : }
183 :
184 0 : for (i = 0; i < ADL_NUM_SENSORS; i++) {
185 0 : if (adl_worklist[i].tempscale)
186 0 : sc->sc_sensor[i].type = SENSOR_TEMP;
187 0 : else if (adl_worklist[i].mVscale)
188 0 : sc->sc_sensor[i].type = SENSOR_VOLTS_DC;
189 : else
190 0 : sc->sc_sensor[i].type = SENSOR_FANRPM;
191 0 : strlcpy(sc->sc_sensor[i].desc, adl_worklist[i].name,
192 : sizeof(sc->sc_sensor[i].desc));
193 :
194 0 : sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
195 : }
196 0 : sensordev_install(&sc->sc_sensordev);
197 :
198 0 : printf("\n");
199 0 : }
200 :
201 : void
202 0 : adl_refresh(void *arg)
203 : {
204 0 : struct adl_softc *sc = arg;
205 : int64_t temp, volt;
206 0 : u_int8_t hdata, ldata, hreg, lreg;
207 : u_int16_t fan;
208 : int i;
209 :
210 0 : iic_acquire_bus(sc->sc_tag, 0);
211 :
212 0 : for (i = 0; i < sizeof adl_worklist / sizeof(adl_worklist[0]); i++) {
213 0 : hreg = adl_worklist[i].hreg;
214 0 : if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
215 0 : sc->sc_addr, &hreg, sizeof hreg, &hdata, sizeof hdata, 0)) {
216 0 : sc->sc_sensor[i].flags |= SENSOR_FINVALID;
217 0 : continue;
218 : }
219 0 : lreg = adl_worklist[i].lreg;
220 0 : if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
221 0 : sc->sc_addr, &lreg, sizeof lreg, &ldata, sizeof ldata, 0)) {
222 0 : sc->sc_sensor[i].flags |= SENSOR_FINVALID;
223 0 : continue;
224 : }
225 :
226 0 : sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
227 0 : if (adl_worklist[i].tempscale) {
228 0 : if (hdata == ASC7621_TEMP_NA)
229 0 : sc->sc_sensor[i].flags |= SENSOR_FINVALID;
230 : else {
231 : /*
232 : * 10-bit two's complement integer in
233 : * steps of 0.25
234 : */
235 0 : temp = ((hdata << 8 | ldata)) >> (16 - 10);
236 0 : temp = temp * 250000 + 273150000;
237 0 : sc->sc_sensor[i].value = temp;
238 : }
239 0 : } else if (adl_worklist[i].mVscale) {
240 0 : volt = ((hdata << 8 | ldata)) >> (16 - 10);
241 0 : volt = volt * adl_worklist[i].mVscale / (192 << 2);
242 0 : sc->sc_sensor[i].value = volt * 1000;
243 0 : } else {
244 : /*
245 : * Inversed to ensure that the LS byte will be read
246 : * before MS byte.
247 : */
248 0 : fan = hdata + (ldata << 8);
249 0 : if (fan == 0 || fan == 0xffff)
250 0 : sc->sc_sensor[i].flags |= SENSOR_FINVALID;
251 : else
252 0 : sc->sc_sensor[i].value = (90000 * 60) / fan;
253 : }
254 : }
255 :
256 0 : iic_release_bus(sc->sc_tag, 0);
257 0 : }
|