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