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