xref: /openbsd/sys/dev/isa/viasio.c (revision 471aeecf)
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