xref: /openbsd-src/sys/dev/isa/it.c (revision d78b6a365c3ccf81acbe95adc60d56638a5b582b)
1 /*	$OpenBSD: it.c,v 1.42 2012/12/14 13:17:29 mikeb Exp $	*/
2 
3 /*
4  * Copyright (c) 2007-2008 Oleg Safiullin <form@pdp-11.org.ru>
5  * Copyright (c) 2006-2007 Juan Romero Pardines <juan@xtrarom.org>
6  * Copyright (c) 2003 Julien Bordet <zejames@greyhats.org>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/device.h>
33 #include <sys/sensors.h>
34 
35 #include <machine/bus.h>
36 
37 #include <dev/isa/isareg.h>
38 #include <dev/isa/isavar.h>
39 #include <dev/isa/itvar.h>
40 
41 
42 #if defined(ITDEBUG)
43 #define DPRINTF(x)		do { printf x; } while (0)
44 #else
45 #define DPRINTF(x)
46 #endif
47 
48 
49 int it_match(struct device *, void *, void *);
50 void it_attach(struct device *, struct device *, void *);
51 u_int8_t it_readreg(bus_space_tag_t, bus_space_handle_t, int);
52 void it_writereg(bus_space_tag_t, bus_space_handle_t, int, u_int8_t);
53 void it_enter(bus_space_tag_t, bus_space_handle_t, int);
54 void it_exit(bus_space_tag_t, bus_space_handle_t);
55 
56 u_int8_t it_ec_readreg(struct it_softc *, int);
57 void it_ec_writereg(struct it_softc *, int, u_int8_t);
58 void it_ec_refresh(void *arg);
59 
60 int it_wdog_cb(void *, int);
61 
62 /*
63  * IT87-compatible chips can typically measure voltages up to 4.096 V.
64  * To measure higher voltages the input is attenuated with (external)
65  * resistors.  Negative voltages are measured using a reference
66  * voltage.  So we have to convert the sensor values back to real
67  * voltages by applying the appropriate resistor factor.
68  */
69 #define RFACT_NONE		10000
70 #define RFACT(x, y)		(RFACT_NONE * ((x) + (y)) / (y))
71 
72 
73 struct {
74 	enum sensor_type	type;
75 	const char		*desc;
76 } it_sensors[IT_EC_NUMSENSORS] = {
77 #define IT_TEMP_BASE		0
78 #define IT_TEMP_COUNT		3
79 	{ SENSOR_TEMP,		NULL		},
80 	{ SENSOR_TEMP,		NULL		},
81 	{ SENSOR_TEMP,		NULL		},
82 
83 #define IT_FAN_BASE		3
84 #define IT_FAN_COUNT		5
85 	{ SENSOR_FANRPM,	NULL		},
86 	{ SENSOR_FANRPM,	NULL		},
87 	{ SENSOR_FANRPM,	NULL		},
88 	{ SENSOR_FANRPM,	NULL		},
89 	{ SENSOR_FANRPM,	NULL		},
90 
91 #define IT_VOLT_BASE		8
92 #define IT_VOLT_COUNT		9
93 	{ SENSOR_VOLTS_DC,	"VCORE_A"	},
94 	{ SENSOR_VOLTS_DC,	"VCORE_B"	},
95 	{ SENSOR_VOLTS_DC,	"+3.3V"		},
96 	{ SENSOR_VOLTS_DC,	"+5V"		},
97 	{ SENSOR_VOLTS_DC,	"+12V"		},
98 	{ SENSOR_VOLTS_DC,	"-12V"		},
99 	{ SENSOR_VOLTS_DC,	"-5V"		},
100 	{ SENSOR_VOLTS_DC,	"+5VSB"		},
101 	{ SENSOR_VOLTS_DC,	"VBAT"		}
102 };
103 
104 /* rfact values for voltage sensors */
105 int it_vrfact[IT_VOLT_COUNT] = {
106 	RFACT_NONE,		/* VCORE_A	*/
107 	RFACT_NONE,		/* VCORE_A	*/
108 	RFACT_NONE,		/* +3.3V	*/
109 	RFACT(68, 100),		/* +5V		*/
110 	RFACT(30, 10),		/* +12V		*/
111 	RFACT(83, 20),		/* -12V		*/
112 	RFACT(21, 10),		/* -5V		*/
113 	RFACT(68, 100),		/* +5VSB	*/
114 	RFACT_NONE		/* VBAT		*/
115 };
116 
117 int it_fan_regs[] = {
118 	IT_EC_FAN_TAC1, IT_EC_FAN_TAC2, IT_EC_FAN_TAC3,
119 	IT_EC_FAN_TAC4_LSB, IT_EC_FAN_TAC5_LSB
120 };
121 
122 int it_fan_ext_regs[] = {
123 	IT_EC_FAN_EXT_TAC1, IT_EC_FAN_EXT_TAC2, IT_EC_FAN_EXT_TAC3,
124 	IT_EC_FAN_TAC4_MSB, IT_EC_FAN_TAC5_MSB
125 };
126 
127 LIST_HEAD(, it_softc) it_softc_list = LIST_HEAD_INITIALIZER(&it_softc_list);
128 
129 
130 int
131 it_match(struct device *parent, void *match, void *aux)
132 {
133 	struct isa_attach_args *ia = aux;
134 	struct it_softc *sc;
135 	bus_space_handle_t ioh;
136 	int ec_iobase, found = 0;
137 	u_int16_t cr;
138 
139 	if (ia->ipa_io[0].base != IO_IT1 && ia->ipa_io[0].base != IO_IT2)
140 		return (0);
141 
142 	/* map i/o space */
143 	if (bus_space_map(ia->ia_iot, ia->ipa_io[0].base, 2, 0, &ioh) != 0) {
144 		DPRINTF(("it_match: can't map i/o space"));
145 		return (0);
146 	}
147 
148 	/* enter MB PnP mode */
149 	it_enter(ia->ia_iot, ioh, ia->ipa_io[0].base);
150 
151 	/*
152 	 * SMSC or similar SuperIO chips use 0x55 magic to enter PnP mode
153 	 * and 0xaa to exit. These chips also enter PnP mode via ITE
154 	 * `enter MB PnP mode' sequence, so force chip to exit PnP mode
155 	 * if this is the case.
156 	 */
157 	bus_space_write_1(ia->ia_iot, ioh, IT_IO_ADDR, 0xaa);
158 
159 	/* get chip id */
160 	cr = it_readreg(ia->ia_iot, ioh, IT_CHIPID1) << 8;
161 	cr |= it_readreg(ia->ia_iot, ioh, IT_CHIPID2);
162 
163 	switch (cr) {
164 	case IT_ID_8705:
165 	case IT_ID_8712:
166 	case IT_ID_8716:
167 	case IT_ID_8718:
168 	case IT_ID_8720:
169 	case IT_ID_8721:
170 	case IT_ID_8726:
171 	case IT_ID_8772:
172 		/* get environment controller base address */
173 		it_writereg(ia->ia_iot, ioh, IT_LDN, IT_EC_LDN);
174 		ec_iobase = it_readreg(ia->ia_iot, ioh, IT_EC_MSB) << 8;
175 		ec_iobase |= it_readreg(ia->ia_iot, ioh, IT_EC_LSB);
176 
177 		/* check if device already attached */
178 		LIST_FOREACH(sc, &it_softc_list, sc_list)
179 			if (sc->sc_ec_iobase == ec_iobase)
180 				break;
181 
182 		if (sc == NULL) {
183 			ia->ipa_nio = 1;
184 			ia->ipa_io[0].length = 2;
185 			ia->ipa_nmem = ia->ipa_nirq = ia->ipa_ndrq = 0;
186 			found++;
187 		}
188 
189 		break;
190 	}
191 
192 	/* exit MB PnP mode */
193 	it_exit(ia->ia_iot, ioh);
194 
195 	/* unmap i/o space */
196 	bus_space_unmap(ia->ia_iot, ioh, 2);
197 
198 	return (found);
199 }
200 
201 void
202 it_attach(struct device *parent, struct device *self, void *aux)
203 {
204 	struct it_softc *sc = (void *)self;
205 	struct isa_attach_args *ia = aux;
206 	int i;
207 
208 	sc->sc_iot = ia->ia_iot;
209 	sc->sc_iobase = ia->ipa_io[0].base;
210 	if (bus_space_map(sc->sc_iot, sc->sc_iobase, 2, 0, &sc->sc_ioh) != 0) {
211 		printf(": can't map i/o space\n");
212 		return;
213 	}
214 
215 	/* enter MB PnP mode */
216 	it_enter(sc->sc_iot, sc->sc_ioh, sc->sc_iobase);
217 
218 	/* get chip id and rev */
219 	sc->sc_chipid = it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPID1) << 8;
220 	sc->sc_chipid |= it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPID2);
221 	sc->sc_chiprev = it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPREV) & 0x0f;
222 
223 	/* get environment controller base address */
224 	it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_EC_LDN);
225 	sc->sc_ec_iobase = it_readreg(sc->sc_iot, sc->sc_ioh, IT_EC_MSB) << 8;
226 	sc->sc_ec_iobase |= it_readreg(sc->sc_iot, sc->sc_ioh, IT_EC_LSB);
227 
228 	/* initialize watchdog timer */
229 	if (sc->sc_chipid != IT_ID_8705) {
230 		it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_WDT_LDN);
231 		it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_CSR, 0x00);
232 		it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR, 0x00);
233 		wdog_register(it_wdog_cb, sc);
234 	}
235 
236 	/* exit MB PnP mode and unmap */
237 	it_exit(sc->sc_iot, sc->sc_ioh);
238 
239 	LIST_INSERT_HEAD(&it_softc_list, sc, sc_list);
240 	printf(": IT%xF rev %X", sc->sc_chipid, sc->sc_chiprev);
241 
242 	if (sc->sc_ec_iobase == 0) {
243 		printf(", EC disabled\n");
244 		return;
245 	}
246 
247 	printf(", EC port 0x%x\n", sc->sc_ec_iobase);
248 
249 	/* map environment controller i/o space */
250 	sc->sc_ec_iot = ia->ia_iot;
251 	if (bus_space_map(sc->sc_ec_iot, sc->sc_ec_iobase, 8, 0,
252 	    &sc->sc_ec_ioh) != 0) {
253 		printf("%s: can't map EC i/o space\n", sc->sc_dev.dv_xname);
254 		return;
255 	}
256 
257 	/* initialize sensor structures */
258 	for (i = 0; i < IT_EC_NUMSENSORS; i++) {
259 		sc->sc_sensors[i].type = it_sensors[i].type;
260 
261 		if (it_sensors[i].desc != NULL)
262 			strlcpy(sc->sc_sensors[i].desc, it_sensors[i].desc,
263 			    sizeof(sc->sc_sensors[i].desc));
264 	}
265 
266 	/* register sensor update task */
267 	if (sensor_task_register(sc, it_ec_refresh, IT_EC_INTERVAL) == NULL) {
268 		printf("%s: unable to register update task\n",
269 		    sc->sc_dev.dv_xname);
270 		bus_space_unmap(sc->sc_ec_iot, sc->sc_ec_ioh, 8);
271 		return;
272 	}
273 
274 	/* use 16-bit FAN tachometer registers for newer chips */
275 	if (sc->sc_chipid != IT_ID_8705 && sc->sc_chipid != IT_ID_8712)
276 		it_ec_writereg(sc, IT_EC_FAN_ECER,
277 		    it_ec_readreg(sc, IT_EC_FAN_ECER) | 0x07);
278 
279 	/* activate monitoring */
280 	it_ec_writereg(sc, IT_EC_CFG,
281 	    it_ec_readreg(sc, IT_EC_CFG) | IT_EC_CFG_START | IT_EC_CFG_INTCLR);
282 
283 	/* initialize sensors */
284 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
285 	    sizeof(sc->sc_sensordev.xname));
286 	for (i = 0; i < IT_EC_NUMSENSORS; i++)
287 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
288 	sensordev_install(&sc->sc_sensordev);
289 }
290 
291 u_int8_t
292 it_readreg(bus_space_tag_t iot, bus_space_handle_t ioh, int r)
293 {
294 	bus_space_write_1(iot, ioh, IT_IO_ADDR, r);
295 	return (bus_space_read_1(iot, ioh, IT_IO_DATA));
296 }
297 
298 void
299 it_writereg(bus_space_tag_t iot, bus_space_handle_t ioh, int r, u_int8_t v)
300 {
301 	bus_space_write_1(iot, ioh, IT_IO_ADDR, r);
302 	bus_space_write_1(iot, ioh, IT_IO_DATA, v);
303 }
304 
305 void
306 it_enter(bus_space_tag_t iot, bus_space_handle_t ioh, int iobase)
307 {
308 	bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x87);
309 	bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x01);
310 	bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x55);
311 	if (iobase == IO_IT1)
312 		bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x55);
313 	else
314 		bus_space_write_1(iot, ioh, IT_IO_ADDR, 0xaa);
315 }
316 
317 void
318 it_exit(bus_space_tag_t iot, bus_space_handle_t ioh)
319 {
320 	bus_space_write_1(iot, ioh, IT_IO_ADDR, IT_CCR);
321 	bus_space_write_1(iot, ioh, IT_IO_DATA, 0x02);
322 }
323 
324 u_int8_t
325 it_ec_readreg(struct it_softc *sc, int r)
326 {
327 	bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_ADDR, r);
328 	return (bus_space_read_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_DATA));
329 }
330 
331 void
332 it_ec_writereg(struct it_softc *sc, int r, u_int8_t v)
333 {
334 	bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_ADDR, r);
335 	bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_DATA, v);
336 }
337 
338 void
339 it_ec_refresh(void *arg)
340 {
341 	struct it_softc *sc = arg;
342 	int i, sdata, divisor, odivisor, ndivisor;
343 	u_int8_t cr, ecr;
344 
345 	/* refresh temp sensors */
346 	cr = it_ec_readreg(sc, IT_EC_ADC_TEMPER);
347 
348 	for (i = 0; i < IT_TEMP_COUNT; i++) {
349 		sc->sc_sensors[IT_TEMP_BASE + i].flags &=
350 		    SENSOR_FINVALID;
351 
352 		if (!(cr & (1 << i)) && !(cr & (1 << (i + 3)))) {
353 			sc->sc_sensors[IT_TEMP_BASE + i].flags |=
354 			    SENSOR_FINVALID;
355 			continue;
356 		}
357 
358 		sdata = it_ec_readreg(sc, IT_EC_TEMPBASE + i);
359 		/* convert to degF */
360 		sc->sc_sensors[IT_TEMP_BASE + i].value =
361 		    sdata * 1000000 + 273150000;
362 	}
363 
364 	/* refresh volt sensors */
365 	cr = it_ec_readreg(sc, IT_EC_ADC_VINER);
366 
367 	for (i = 0; i < IT_VOLT_COUNT; i++) {
368 		sc->sc_sensors[IT_VOLT_BASE + i].flags &=
369 		    SENSOR_FINVALID;
370 
371 		if ((i < 8) && !(cr & (1 << i))) {
372 			sc->sc_sensors[IT_VOLT_BASE + i].flags |=
373 			    SENSOR_FINVALID;
374 			continue;
375 		}
376 
377 		sdata = it_ec_readreg(sc, IT_EC_VOLTBASE + i);
378 		/* voltage returned as (mV >> 4) */
379 		sc->sc_sensors[IT_VOLT_BASE + i].value = sdata << 4;
380 		/* these two values are negative and formula is different */
381 		if (i == 5 || i == 6)
382 			sc->sc_sensors[IT_VOLT_BASE + i].value -= IT_EC_VREF;
383 		/* rfact is (factor * 10^4) */
384 		sc->sc_sensors[IT_VOLT_BASE + i].value *= it_vrfact[i];
385 		/* division by 10 gets us back to uVDC */
386 		sc->sc_sensors[IT_VOLT_BASE + i].value /= 10;
387 		if (i == 5 || i == 6)
388 			sc->sc_sensors[IT_VOLT_BASE + i].value +=
389 			    IT_EC_VREF * 1000;
390 	}
391 
392 	/* refresh fan sensors */
393 	cr = it_ec_readreg(sc, IT_EC_FAN_MCR);
394 
395 	if (sc->sc_chipid != IT_ID_8705 && sc->sc_chipid != IT_ID_8712) {
396 		/* use 16-bit FAN tachometer registers */
397 		ecr = it_ec_readreg(sc, IT_EC_FAN_ECER);
398 
399 		for (i = 0; i < IT_FAN_COUNT; i++) {
400 			sc->sc_sensors[IT_FAN_BASE + i].flags &=
401 			    ~SENSOR_FINVALID;
402 
403 			if (i < 3 && !(cr & (1 << (i + 4)))) {
404 				sc->sc_sensors[IT_FAN_BASE + i].flags |=
405 				    SENSOR_FINVALID;
406 				continue;
407 			} else if (i > 2 && !(ecr & (1 << (i + 1)))) {
408 				sc->sc_sensors[IT_FAN_BASE + i].flags |=
409 				    SENSOR_FINVALID;
410 				continue;
411 			}
412 
413 			sdata = it_ec_readreg(sc, it_fan_regs[i]);
414 			sdata |= it_ec_readreg(sc, it_fan_ext_regs[i]) << 8;
415 
416 			if (sdata == 0 || sdata == 0xffff)
417 				sc->sc_sensors[IT_FAN_BASE + i].value = 0;
418 			else
419 				sc->sc_sensors[IT_FAN_BASE + i].value =
420 				    675000 / sdata;
421 		}
422 	} else {
423 		/* use 8-bit FAN tachometer & FAN divisor registers */
424 		odivisor = ndivisor = divisor =
425 		    it_ec_readreg(sc, IT_EC_FAN_DIV);
426 
427 		for (i = 0; i < IT_FAN_COUNT; i++) {
428 			if (i > 2 || !(cr & (1 << (i + 4)))) {
429 				sc->sc_sensors[IT_FAN_BASE + i].flags |=
430 				    SENSOR_FINVALID;
431 				continue;
432 			}
433 
434 			sc->sc_sensors[IT_FAN_BASE + i].flags &=
435 			    ~SENSOR_FINVALID;
436 
437 			sdata = it_ec_readreg(sc, it_fan_regs[i]);
438 
439 			if (sdata == 0xff) {
440 				sc->sc_sensors[IT_FAN_BASE + i].value = 0;
441 
442 				if (i == 2)
443 					ndivisor ^= 0x40;
444 				else {
445 					ndivisor &= ~(7 << (i * 3));
446 					ndivisor |= ((divisor + 1) & 7) <<
447 					    (i * 3);
448 				}
449 			} else if (sdata != 0) {
450 				if (i == 2)
451 					divisor = divisor & 1 ? 3 : 1;
452 				sc->sc_sensors[IT_FAN_BASE + i].value =
453 				    1350000 / (sdata << (divisor & 7));
454 			} else
455 				sc->sc_sensors[IT_FAN_BASE + i].value = 0;
456 		}
457 
458 		if (ndivisor != odivisor)
459 			it_ec_writereg(sc, IT_EC_FAN_DIV, ndivisor);
460 	}
461 }
462 
463 int
464 it_wdog_cb(void *arg, int period)
465 {
466 	struct it_softc *sc = arg;
467 	int minutes = 0;
468 
469 	/* enter MB PnP mode and select WDT device */
470 	it_enter(sc->sc_iot, sc->sc_ioh, sc->sc_iobase);
471 	it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_WDT_LDN);
472 
473 	/* disable watchdog timer */
474 	it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR, 0x00);
475 
476 	/* 1000s should be enough for everyone */
477 	if (period > 1000)
478 		period = 1000;
479 	else if (period < 0)
480 		period = 0;
481 
482 	if (period > 0) {
483 		/*
484 		 * Older IT8712F chips have 8-bit timeout counter.
485 		 * Use minutes for 16-bit values for these chips.
486 		 */
487 		if (sc->sc_chipid == IT_ID_8712 && sc->sc_chiprev < 0x8 &&
488 		    period > 0xff) {
489 			if (period % 60 >= 30)
490 				period += 60;
491 			period /= 60;
492 			minutes++;
493 		}
494 
495 		/* set watchdog timeout (low byte) */
496 		it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TMO_LSB,
497 		    period & 0xff);
498 
499 		if (minutes) {
500 			/* enable watchdog timer */
501 			it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR,
502 			    IT_WDT_TCR_KRST | IT_WDT_TCR_PWROK);
503 
504 			period *= 60;
505 		} else {
506 			/* set watchdog timeout (high byte) */
507 			it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TMO_MSB,
508 			    period >> 8);
509 
510 			/* enable watchdog timer */
511 			it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR,
512 			    IT_WDT_TCR_SECS | IT_WDT_TCR_KRST |
513 			    IT_WDT_TCR_PWROK);
514 		}
515 	}
516 
517 	/* exit MB PnP mode */
518 	it_exit(sc->sc_iot, sc->sc_ioh);
519 
520 	return (period);
521 }
522 
523 
524 struct cfattach it_ca = {
525 	sizeof(struct it_softc),
526 	it_match,
527 	it_attach
528 };
529 
530 struct cfdriver it_cd = {
531 	NULL, "it", DV_DULL
532 };
533