1 /* $OpenBSD: asc7621.c,v 1.5 2022/04/06 18:59:28 naddy 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 const 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
adl_match(struct device * parent,void * match,void * aux)135 adl_match(struct device *parent, void *match, void *aux)
136 {
137 struct i2c_attach_args *ia = aux;
138
139 if (strcmp(ia->ia_name, "asc7621") == 0)
140 return (1);
141 return (0);
142 }
143
144 void
adl_attach(struct device * parent,struct device * self,void * aux)145 adl_attach(struct device *parent, struct device *self, void *aux)
146 {
147 struct adl_softc *sc = (struct adl_softc *)self;
148 struct i2c_attach_args *ia = aux;
149 u_int8_t cmd, data;
150 int i;
151
152 sc->sc_tag = ia->ia_tag;
153 sc->sc_addr = ia->ia_addr;
154
155 printf(": %s", ia->ia_name);
156
157 /* Initialize sensor data. */
158 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
159 sizeof(sc->sc_sensordev.xname));
160
161 /* Check for PECI mode */
162 cmd = ASC7621_PECI;
163 (void)iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
164 &cmd, sizeof(cmd), &data, sizeof(data), 0);
165 if (data & ASC7621_PECI_MASK)
166 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 if (sensor_task_register(sc, adl_refresh, 5) == NULL) {
180 printf(", unable to register update task\n");
181 return;
182 }
183
184 for (i = 0; i < ADL_NUM_SENSORS; i++) {
185 if (adl_worklist[i].tempscale)
186 sc->sc_sensor[i].type = SENSOR_TEMP;
187 else if (adl_worklist[i].mVscale)
188 sc->sc_sensor[i].type = SENSOR_VOLTS_DC;
189 else
190 sc->sc_sensor[i].type = SENSOR_FANRPM;
191 strlcpy(sc->sc_sensor[i].desc, adl_worklist[i].name,
192 sizeof(sc->sc_sensor[i].desc));
193
194 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
195 }
196 sensordev_install(&sc->sc_sensordev);
197
198 printf("\n");
199 }
200
201 void
adl_refresh(void * arg)202 adl_refresh(void *arg)
203 {
204 struct adl_softc *sc = arg;
205 int64_t temp, volt;
206 u_int8_t hdata, ldata, hreg, lreg;
207 u_int16_t fan;
208 int i;
209
210 iic_acquire_bus(sc->sc_tag, 0);
211
212 for (i = 0; i < sizeof adl_worklist / sizeof(adl_worklist[0]); i++) {
213 hreg = adl_worklist[i].hreg;
214 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
215 sc->sc_addr, &hreg, sizeof hreg, &hdata, sizeof hdata, 0)) {
216 sc->sc_sensor[i].flags |= SENSOR_FINVALID;
217 continue;
218 }
219 lreg = adl_worklist[i].lreg;
220 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
221 sc->sc_addr, &lreg, sizeof lreg, &ldata, sizeof ldata, 0)) {
222 sc->sc_sensor[i].flags |= SENSOR_FINVALID;
223 continue;
224 }
225
226 sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
227 if (adl_worklist[i].tempscale) {
228 if (hdata == ASC7621_TEMP_NA)
229 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 temp = ((hdata << 8 | ldata)) >> (16 - 10);
236 temp = temp * 250000 + 273150000;
237 sc->sc_sensor[i].value = temp;
238 }
239 } else if (adl_worklist[i].mVscale) {
240 volt = ((hdata << 8 | ldata)) >> (16 - 10);
241 volt = volt * adl_worklist[i].mVscale / (192 << 2);
242 sc->sc_sensor[i].value = volt * 1000;
243 } else {
244 /*
245 * Inversed to ensure that the LS byte will be read
246 * before MS byte.
247 */
248 fan = hdata + (ldata << 8);
249 if (fan == 0 || fan == 0xffff)
250 sc->sc_sensor[i].flags |= SENSOR_FINVALID;
251 else
252 sc->sc_sensor[i].value = (90000 * 60) / fan;
253 }
254 }
255
256 iic_release_bus(sc->sc_tag, 0);
257 }
258