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