1 /* $OpenBSD: viasio.c,v 1.15 2022/04/06 18:59:29 naddy Exp $ */
2 /*
3 * Copyright (c) 2005 Alexander Yurchenko <grange@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 /*
19 * VIA VT1211 LPC Super I/O driver.
20 */
21
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/device.h>
25 #include <sys/kernel.h>
26 #include <sys/sensors.h>
27 #include <sys/timeout.h>
28
29 #include <machine/bus.h>
30
31 #include <dev/isa/isareg.h>
32 #include <dev/isa/isavar.h>
33
34 #include <dev/isa/viasioreg.h>
35
36 #ifdef VIASIO_DEBUG
37 #define DPRINTF(x) printf x
38 #else
39 #define DPRINTF(x)
40 #endif
41
42 /* autoconf flags */
43 #define VIASIO_CFFLAGS_HM_ENABLE 0x0001 /* enable HM if disabled */
44 #define VIASIO_CFFLAGS_WDG_ENABLE 0x0002 /* enable WDG if disabled */
45
46 struct viasio_softc {
47 struct device sc_dev;
48
49 bus_space_tag_t sc_iot;
50 bus_space_handle_t sc_ioh;
51
52 /* Hardware monitor */
53 bus_space_handle_t sc_hm_ioh;
54 int sc_hm_clock;
55 struct ksensor sc_hm_sensors[VT1211_HM_NSENSORS];
56 struct ksensordev sc_sensordev;
57 struct timeout sc_hm_timo;
58
59 /* Watchdog timer */
60 bus_space_handle_t sc_wdg_ioh;
61 };
62
63 int viasio_probe(struct device *, void *, void *);
64 void viasio_attach(struct device *, struct device *, void *);
65 int viasio_activate(struct device *, int);
66
67 void viasio_hm_init(struct viasio_softc *);
68 void viasio_hm_refresh(void *);
69
70 void viasio_wdg_init(struct viasio_softc *);
71 int viasio_wdg_cb(void *, int);
72
73 const struct cfattach viasio_ca = {
74 sizeof(struct viasio_softc),
75 viasio_probe,
76 viasio_attach,
77 NULL,
78 viasio_activate
79 };
80
81 struct cfdriver viasio_cd = {
82 NULL, "viasio", DV_DULL
83 };
84
85 static __inline void
viasio_conf_enable(bus_space_tag_t iot,bus_space_handle_t ioh)86 viasio_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh)
87 {
88 bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_EN_MAGIC);
89 bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_EN_MAGIC);
90 }
91
92 static __inline void
viasio_conf_disable(bus_space_tag_t iot,bus_space_handle_t ioh)93 viasio_conf_disable(bus_space_tag_t iot, bus_space_handle_t ioh)
94 {
95 bus_space_write_1(iot, ioh, VT1211_INDEX, VT1211_CONF_DS_MAGIC);
96 }
97
98 static __inline u_int8_t
viasio_conf_read(bus_space_tag_t iot,bus_space_handle_t ioh,u_int8_t index)99 viasio_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index)
100 {
101 bus_space_write_1(iot, ioh, VT1211_INDEX, index);
102 return (bus_space_read_1(iot, ioh, VT1211_DATA));
103 }
104
105 static __inline void
viasio_conf_write(bus_space_tag_t iot,bus_space_handle_t ioh,u_int8_t index,u_int8_t data)106 viasio_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t index,
107 u_int8_t data)
108 {
109 bus_space_write_1(iot, ioh, VT1211_INDEX, index);
110 bus_space_write_1(iot, ioh, VT1211_DATA, data);
111 }
112
113 static __inline int64_t
viasio_raw2temp(int raw)114 viasio_raw2temp(int raw)
115 {
116 int tblsize = sizeof(vt1211_hm_temptbl) / sizeof(vt1211_hm_temptbl[0]);
117 int i;
118 int raw1, raw2;
119 int64_t temp = -1, temp1, temp2;
120
121 if (raw < vt1211_hm_temptbl[0].raw ||
122 raw > vt1211_hm_temptbl[tblsize - 1].raw)
123 return (-1);
124
125 for (i = 0; i < tblsize - 1; i++) {
126 raw1 = vt1211_hm_temptbl[i].raw;
127 temp1 = vt1211_hm_temptbl[i].temp;
128 raw2 = vt1211_hm_temptbl[i + 1].raw;
129 temp2 = vt1211_hm_temptbl[i + 1].temp;
130
131 if (raw >= raw1 && raw <= raw2) {
132 /* linear interpolation */
133 temp = temp1 + ((raw - raw1) * (temp2 - temp1)) /
134 (raw2 - raw1);
135 break;
136 }
137 }
138
139 return (temp);
140 }
141
142 int
viasio_probe(struct device * parent,void * match,void * aux)143 viasio_probe(struct device *parent, void *match, void *aux)
144 {
145 struct isa_attach_args *ia = aux;
146 bus_space_tag_t iot;
147 bus_space_handle_t ioh;
148 u_int8_t reg;
149
150 /* Match by device ID */
151 iot = ia->ia_iot;
152 if (bus_space_map(iot, ia->ipa_io[0].base, VT1211_IOSIZE, 0, &ioh))
153 return (0);
154 viasio_conf_enable(iot, ioh);
155 reg = viasio_conf_read(iot, ioh, VT1211_ID);
156 DPRINTF(("viasio_probe: id 0x%02x\n", reg));
157 viasio_conf_disable(iot, ioh);
158 bus_space_unmap(iot, ioh, VT1211_IOSIZE);
159 if (reg == VT1211_ID_VT1211) {
160 ia->ipa_nio = 1;
161 ia->ipa_io[0].length = VT1211_IOSIZE;
162 ia->ipa_nmem = 0;
163 ia->ipa_nirq = 0;
164 ia->ipa_ndrq = 0;
165 return (1);
166 }
167
168 return (0);
169 }
170
171 void
viasio_attach(struct device * parent,struct device * self,void * aux)172 viasio_attach(struct device *parent, struct device *self, void *aux)
173 {
174 struct viasio_softc *sc = (void *)self;
175 struct isa_attach_args *ia = aux;
176 u_int8_t reg;
177
178 /* Map ISA I/O space */
179 sc->sc_iot = ia->ia_iot;
180 if (bus_space_map(sc->sc_iot, ia->ipa_io[0].base,
181 VT1211_IOSIZE, 0, &sc->sc_ioh)) {
182 printf(": can't map i/o space\n");
183 return;
184 }
185
186 /* Enter configuration mode */
187 viasio_conf_enable(sc->sc_iot, sc->sc_ioh);
188
189 /* Read device revision */
190 reg = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_REV);
191 printf(": VT1211 rev 0x%02x", reg);
192
193 /* Initialize logical devices */
194 viasio_hm_init(sc);
195 viasio_wdg_init(sc);
196 printf("\n");
197
198 /* Escape from configuration mode */
199 viasio_conf_disable(sc->sc_iot, sc->sc_ioh);
200 }
201
202 int
viasio_activate(struct device * self,int act)203 viasio_activate(struct device *self, int act)
204 {
205 switch (act) {
206 case DVACT_POWERDOWN:
207 wdog_shutdown(self);
208 break;
209 }
210
211 return (0);
212 }
213
214 void
viasio_hm_init(struct viasio_softc * sc)215 viasio_hm_init(struct viasio_softc *sc)
216 {
217 u_int8_t reg0, reg1;
218 u_int16_t iobase;
219 int i;
220
221 printf(", HM");
222
223 /* Select HM logical device */
224 viasio_conf_write(sc->sc_iot, sc->sc_ioh, VT1211_LDN, VT1211_LDN_HM);
225
226 /*
227 * Check if logical device is activated by firmware. If not
228 * try to activate it only if requested.
229 */
230 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ACT);
231 DPRINTF((": ACT 0x%02x", reg0));
232 if ((reg0 & VT1211_HM_ACT_EN) == 0) {
233 if ((sc->sc_dev.dv_cfdata->cf_flags &
234 VIASIO_CFFLAGS_HM_ENABLE) != 0) {
235 reg0 |= VT1211_HM_ACT_EN;
236 viasio_conf_write(sc->sc_iot, sc->sc_ioh,
237 VT1211_HM_ACT, reg0);
238 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh,
239 VT1211_HM_ACT);
240 DPRINTF((", new ACT 0x%02x", reg0));
241 if ((reg0 & VT1211_HM_ACT_EN) == 0) {
242 printf(" failed to activate");
243 return;
244 }
245 } else {
246 printf(" not activated");
247 return;
248 }
249 }
250
251 /* Read HM I/O space address */
252 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ADDR_LSB);
253 reg1 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_HM_ADDR_MSB);
254 iobase = (reg1 << 8) | reg0;
255 DPRINTF((", addr 0x%04x", iobase));
256
257 /* Map HM I/O space */
258 if (bus_space_map(sc->sc_iot, iobase, VT1211_HM_IOSIZE, 0,
259 &sc->sc_hm_ioh)) {
260 printf(" can't map i/o space");
261 return;
262 }
263
264 /*
265 * Check if hardware monitoring is enabled by firmware. If not
266 * try to enable it only if requested.
267 */
268 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_CONF);
269 DPRINTF((", CONF 0x%02x", reg0));
270 if ((reg0 & VT1211_HM_CONF_START) == 0) {
271 if ((sc->sc_dev.dv_cfdata->cf_flags &
272 VIASIO_CFFLAGS_HM_ENABLE) != 0) {
273 reg0 |= VT1211_HM_CONF_START;
274 bus_space_write_1(sc->sc_iot, sc->sc_hm_ioh,
275 VT1211_HM_CONF, reg0);
276 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh,
277 VT1211_HM_CONF);
278 DPRINTF((", new CONF 0x%02x", reg0));
279 if ((reg0 & VT1211_HM_CONF_START) == 0) {
280 printf(" failed to enable monitoring");
281 return;
282 }
283 } else {
284 printf(" monitoring not enabled");
285 return;
286 }
287 }
288
289 /* Read PWM clock frequency */
290 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_PWMCS);
291 sc->sc_hm_clock = vt1211_hm_clock[reg0 & 0x07];
292 DPRINTF((", PWMCS 0x%02x, %dHz", reg0, sc->sc_hm_clock));
293
294 /* Temperature reading 1 */
295 sc->sc_hm_sensors[VT1211_HMS_TEMP1].type = SENSOR_TEMP;
296
297 /* Universal channels (UCH) 1-5 */
298 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_UCHCONF);
299 DPRINTF((", UCHCONF 0x%02x", reg0));
300 for (i = 1; i <= 5; i++) {
301 /* UCH can be configured either as thermal or voltage input */
302 if (VT1211_HM_UCHCONF_ISTEMP(reg0, i)) {
303 sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type =
304 SENSOR_TEMP;
305 } else {
306 sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type =
307 SENSOR_VOLTS_DC;
308 }
309 snprintf(sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].desc,
310 sizeof(sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].desc),
311 "UCH%d", i);
312 }
313
314 /* Internal +3.3V */
315 sc->sc_hm_sensors[VT1211_HMS_33V].type = SENSOR_VOLTS_DC;
316 strlcpy(sc->sc_hm_sensors[VT1211_HMS_33V].desc, "+3.3V",
317 sizeof(sc->sc_hm_sensors[VT1211_HMS_33V].desc));
318
319 /* FAN reading 1, 2 */
320 sc->sc_hm_sensors[VT1211_HMS_FAN1].type = SENSOR_FANRPM;
321 sc->sc_hm_sensors[VT1211_HMS_FAN2].type = SENSOR_FANRPM;
322
323 /* Start sensors */
324 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
325 sizeof(sc->sc_sensordev.xname));
326 for (i = 0; i < VT1211_HM_NSENSORS; i++)
327 sensor_attach(&sc->sc_sensordev, &sc->sc_hm_sensors[i]);
328 sensordev_install(&sc->sc_sensordev);
329 timeout_set(&sc->sc_hm_timo, viasio_hm_refresh, sc);
330 timeout_add_sec(&sc->sc_hm_timo, 1);
331 }
332
333 void
viasio_hm_refresh(void * arg)334 viasio_hm_refresh(void *arg)
335 {
336 struct viasio_softc *sc = arg;
337 u_int8_t reg0, reg1;
338 int64_t val, rfact;
339 int i;
340
341 /* TEMP1 is a 10-bit thermal input */
342 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_TEMP1);
343 reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_TCONF1);
344 reg1 = VT1211_HM_TCONF1_TEMP1(reg1);
345 val = (reg0 << 2) | reg1;
346
347 /* Convert to uK */
348 /* XXX: conversion function is guessed */
349 val = viasio_raw2temp(val);
350 if (val == -1) {
351 sc->sc_hm_sensors[VT1211_HMS_TEMP1].flags |= SENSOR_FINVALID;
352 } else {
353 sc->sc_hm_sensors[VT1211_HMS_TEMP1].flags &= ~SENSOR_FINVALID;
354 sc->sc_hm_sensors[VT1211_HMS_TEMP1].value = val;
355 }
356
357 /* Universal channels 1-5 */
358 for (i = 1; i <= 5; i++) {
359 if (sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].type ==
360 SENSOR_TEMP) {
361 /* UCH is a 10-bit thermal input */
362 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh,
363 VT1211_HM_UCH1 + i - 1);
364 if (i == 1) {
365 reg1 = bus_space_read_1(sc->sc_iot,
366 sc->sc_hm_ioh, VT1211_HM_VID4);
367 reg1 = VT1211_HM_VID4_UCH1(reg1);
368 } else {
369 reg1 = bus_space_read_1(sc->sc_iot,
370 sc->sc_hm_ioh, VT1211_HM_ETR);
371 reg1 = VT1211_HM_ETR_UCH(reg1, i);
372 }
373 val = (reg0 << 2) | reg1;
374
375 /* Convert to uK */
376 /* XXX: conversion function is guessed */
377 val = viasio_raw2temp(val);
378 if (val == -1) {
379 sc->sc_hm_sensors[VT1211_HMS_UCH1 +
380 i - 1].flags |= SENSOR_FINVALID;
381 } else {
382 sc->sc_hm_sensors[VT1211_HMS_UCH1 +
383 i - 1].flags &= ~SENSOR_FINVALID;
384 sc->sc_hm_sensors[VT1211_HMS_UCH1 +
385 i - 1].value = val;
386 }
387 } else {
388 /* UCH is a voltage input */
389 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh,
390 VT1211_HM_UCH1 + i - 1);
391 val = reg0;
392
393 /* Convert to uV */
394 /* XXX: conversion function is guessed */
395 rfact = vt1211_hm_vrfact[i - 1];
396 sc->sc_hm_sensors[VT1211_HMS_UCH1 + i - 1].value =
397 ((val * 100000000000ULL) / (rfact * 958));
398 }
399 }
400
401 /* Read internal +3.3V */
402 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_33V);
403 val = reg0;
404
405 /* Convert to uV */
406 /* XXX: conversion function is guessed */
407 rfact = vt1211_hm_vrfact[5];
408 sc->sc_hm_sensors[VT1211_HMS_33V].value = ((val * 100000000000ULL) /
409 (rfact * 958));
410
411 /* Read FAN1 clock counter and divisor */
412 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FAN1);
413 reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FSCTL);
414 reg1 = VT1211_HM_FSCTL_DIV1(reg1);
415 val = reg0 << reg1;
416
417 /* Convert to RPM */
418 /* XXX: conversion function is guessed */
419 if (val != 0) {
420 sc->sc_hm_sensors[VT1211_HMS_FAN1].value =
421 (sc->sc_hm_clock * 60 / 2) / val;
422 sc->sc_hm_sensors[VT1211_HMS_FAN1].flags &= ~SENSOR_FINVALID;
423 } else {
424 sc->sc_hm_sensors[VT1211_HMS_FAN1].flags |= SENSOR_FINVALID;
425 }
426
427 /* Read FAN2 clock counter and divisor */
428 reg0 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FAN2);
429 reg1 = bus_space_read_1(sc->sc_iot, sc->sc_hm_ioh, VT1211_HM_FSCTL);
430 reg1 = VT1211_HM_FSCTL_DIV2(reg1);
431 val = reg0 << reg1;
432
433 /* Convert to RPM */
434 /* XXX: conversion function is guessed */
435 if (val != 0) {
436 sc->sc_hm_sensors[VT1211_HMS_FAN2].value =
437 (sc->sc_hm_clock * 60 / 2) / val;
438 sc->sc_hm_sensors[VT1211_HMS_FAN2].flags &= ~SENSOR_FINVALID;
439 } else {
440 sc->sc_hm_sensors[VT1211_HMS_FAN2].flags |= SENSOR_FINVALID;
441 }
442
443 timeout_add_sec(&sc->sc_hm_timo, 1);
444 }
445
446 void
viasio_wdg_init(struct viasio_softc * sc)447 viasio_wdg_init(struct viasio_softc *sc)
448 {
449 u_int8_t reg0, reg1;
450 u_int16_t iobase;
451
452 printf(", WDG");
453
454 /* Select WDG logical device */
455 viasio_conf_write(sc->sc_iot, sc->sc_ioh, VT1211_LDN, VT1211_LDN_WDG);
456
457 /*
458 * Check if logical device is activated by firmware. If not
459 * try to activate it only if requested.
460 */
461 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ACT);
462 DPRINTF((": ACT 0x%02x", reg0));
463 if ((reg0 & VT1211_WDG_ACT_EN) == 0) {
464 if ((sc->sc_dev.dv_cfdata->cf_flags &
465 VIASIO_CFFLAGS_WDG_ENABLE) != 0) {
466 reg0 |= VT1211_WDG_ACT_EN;
467 viasio_conf_write(sc->sc_iot, sc->sc_ioh,
468 VT1211_WDG_ACT, reg0);
469 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh,
470 VT1211_WDG_ACT);
471 DPRINTF((", new ACT 0x%02x", reg0));
472 if ((reg0 & VT1211_WDG_ACT_EN) == 0) {
473 printf(" failed to activate");
474 return;
475 }
476 } else {
477 printf(" not activated");
478 return;
479 }
480 }
481
482 /* Read WDG I/O space address */
483 reg0 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ADDR_LSB);
484 reg1 = viasio_conf_read(sc->sc_iot, sc->sc_ioh, VT1211_WDG_ADDR_MSB);
485 iobase = (reg1 << 8) | reg0;
486 DPRINTF((", addr 0x%04x", iobase));
487
488 /* Map WDG I/O space */
489 if (bus_space_map(sc->sc_iot, iobase, VT1211_WDG_IOSIZE, 0,
490 &sc->sc_wdg_ioh)) {
491 printf(" can't map i/o space");
492 return;
493 }
494
495 /* Register new watchdog */
496 wdog_register(viasio_wdg_cb, sc);
497 }
498
499 int
viasio_wdg_cb(void * arg,int period)500 viasio_wdg_cb(void *arg, int period)
501 {
502 struct viasio_softc *sc = arg;
503 int mins;
504
505 mins = (period + 59) / 60;
506 if (mins > 255)
507 mins = 255;
508
509 bus_space_write_1(sc->sc_iot, sc->sc_wdg_ioh, VT1211_WDG_TIMEOUT, mins);
510 DPRINTF(("viasio_wdg_cb: %d mins\n", mins));
511
512 return (mins * 60);
513 }
514