xref: /openbsd-src/sys/dev/isa/it.c (revision 421a338cb91e03eccd96aa5ef3a7a5f26b00f54a)
1 /*	$OpenBSD: it.c,v 1.36 2008/10/11 20:31:50 miod 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 	int		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		3
85 	{ SENSOR_FANRPM,	NULL		},
86 	{ SENSOR_FANRPM,	NULL		},
87 	{ SENSOR_FANRPM,	NULL		},
88 
89 #define IT_VOLT_BASE		6
90 #define IT_VOLT_COUNT		9
91 	{ SENSOR_VOLTS_DC,	"VCORE_A"	},
92 	{ SENSOR_VOLTS_DC,	"VCORE_B"	},
93 	{ SENSOR_VOLTS_DC,	"+3.3V"		},
94 	{ SENSOR_VOLTS_DC,	"+5V"		},
95 	{ SENSOR_VOLTS_DC,	"+12V"		},
96 	{ SENSOR_VOLTS_DC,	"-5V"		},
97 	{ SENSOR_VOLTS_DC,	"-12V"		},
98 	{ SENSOR_VOLTS_DC,	"+5VSB"		},
99 	{ SENSOR_VOLTS_DC,	"VBAT"		}
100 };
101 
102 /* rfact values for voltage sensors */
103 int it_vrfact[IT_VOLT_COUNT] = {
104 	RFACT_NONE,		/* VCORE_A	*/
105 	RFACT_NONE,		/* VCORE_A	*/
106 	RFACT_NONE,		/* +3.3V	*/
107 	RFACT(68, 100),		/* +5V		*/
108 	RFACT(30, 10),		/* +12V		*/
109 	RFACT(21, 10),		/* -5V		*/
110 	RFACT(83, 20),		/* -12V		*/
111 	RFACT(68, 100),		/* +5VSB	*/
112 	RFACT_NONE		/* VBAT		*/
113 };
114 
115 LIST_HEAD(, it_softc) it_softc_list = LIST_HEAD_INITIALIZER(&it_softc_list);
116 
117 
118 int
119 it_match(struct device *parent, void *match, void *aux)
120 {
121 	struct isa_attach_args *ia = aux;
122 	struct it_softc *sc;
123 	bus_space_handle_t ioh;
124 	int ec_iobase, found = 0;
125 	u_int16_t cr;
126 
127 	if (ia->ipa_io[0].base != IO_IT1 && ia->ipa_io[0].base != IO_IT2)
128 		return (0);
129 
130 	/* map i/o space */
131 	if (bus_space_map(ia->ia_iot, ia->ipa_io[0].base, 2, 0, &ioh) != 0) {
132 		DPRINTF(("it_match: can't map i/o space"));
133 		return (0);
134 	}
135 
136 	/* enter MB PnP mode */
137 	it_enter(ia->ia_iot, ioh, ia->ipa_io[0].base);
138 
139 	/*
140 	 * SMSC or similar SuperIO chips use 0x55 magic to enter PnP mode
141 	 * and 0xaa to exit. These chips also enter PnP mode via ITE
142 	 * `enter MB PnP mode' sequence, so force chip to exit PnP mode
143 	 * if this is the case.
144 	 */
145 	bus_space_write_1(ia->ia_iot, ioh, IT_IO_ADDR, 0xaa);
146 
147 	/* get chip id */
148 	cr = it_readreg(ia->ia_iot, ioh, IT_CHIPID1) << 8;
149 	cr |= it_readreg(ia->ia_iot, ioh, IT_CHIPID2);
150 
151 	switch (cr) {
152 	case IT_ID_8705:
153 	case IT_ID_8712:
154 	case IT_ID_8716:
155 	case IT_ID_8718:
156 	case IT_ID_8726:
157 		/* get environment controller base address */
158 		it_writereg(ia->ia_iot, ioh, IT_LDN, IT_EC_LDN);
159 		ec_iobase = it_readreg(ia->ia_iot, ioh, IT_EC_MSB) << 8;
160 		ec_iobase |= it_readreg(ia->ia_iot, ioh, IT_EC_LSB);
161 
162 		/* check if device already attached */
163 		LIST_FOREACH(sc, &it_softc_list, sc_list)
164 			if (sc->sc_ec_iobase == ec_iobase)
165 				break;
166 
167 		if (sc == NULL) {
168 			ia->ipa_nio = 1;
169 			ia->ipa_io[0].length = 2;
170 			ia->ipa_nmem = ia->ipa_nirq = ia->ipa_ndrq = 0;
171 			found++;
172 		}
173 
174 		break;
175 	}
176 
177 	/* exit MB PnP mode */
178 	it_exit(ia->ia_iot, ioh);
179 
180 	/* unmap i/o space */
181 	bus_space_unmap(ia->ia_iot, ioh, 2);
182 
183 	return (found);
184 }
185 
186 void
187 it_attach(struct device *parent, struct device *self, void *aux)
188 {
189 	struct it_softc *sc = (void *)self;
190 	struct isa_attach_args *ia = aux;
191 	int i;
192 
193 	sc->sc_iot = ia->ia_iot;
194 	sc->sc_iobase = ia->ipa_io[0].base;
195 	if (bus_space_map(sc->sc_iot, sc->sc_iobase, 2, 0, &sc->sc_ioh) != 0) {
196 		printf(": can't map i/o space\n");
197 		return;
198 	}
199 
200 	/* enter MB PnP mode */
201 	it_enter(sc->sc_iot, sc->sc_ioh, sc->sc_iobase);
202 
203 	/* get chip id and rev */
204 	sc->sc_chipid = it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPID1) << 8;
205 	sc->sc_chipid |= it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPID2);
206 	sc->sc_chiprev = it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPREV) & 0x0f;
207 
208 	/* get environment controller base address */
209 	it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_EC_LDN);
210 	sc->sc_ec_iobase = it_readreg(sc->sc_iot, sc->sc_ioh, IT_EC_MSB) << 8;
211 	sc->sc_ec_iobase |= it_readreg(sc->sc_iot, sc->sc_ioh, IT_EC_LSB);
212 
213 	/* initialize watchdog timer */
214 	if (sc->sc_chipid != IT_ID_8705) {
215 		it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_WDT_LDN);
216 		it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_CSR, 0x00);
217 		it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR, 0x00);
218 		wdog_register(sc, it_wdog_cb);
219 	}
220 
221 	/* exit MB PnP mode and unmap */
222 	it_exit(sc->sc_iot, sc->sc_ioh);
223 
224 	LIST_INSERT_HEAD(&it_softc_list, sc, sc_list);
225 	printf(": IT%xF rev 0x%x", sc->sc_chipid, sc->sc_chiprev);
226 
227 	if (sc->sc_ec_iobase == 0) {
228 		printf(", EC disabled\n");
229 		return;
230 	}
231 
232 	printf(", EC port 0x%x\n", sc->sc_ec_iobase);
233 
234 	/* map environment controller i/o space */
235 	sc->sc_ec_iot = ia->ia_iot;
236 	if (bus_space_map(sc->sc_ec_iot, sc->sc_ec_iobase, 8, 0,
237 	    &sc->sc_ec_ioh) != 0) {
238 		printf("%s: can't map EC i/o space\n", sc->sc_dev.dv_xname);
239 		return;
240 	}
241 
242 	/* initialize sensor structures */
243 	for (i = 0; i < IT_EC_NUMSENSORS; i++) {
244 		sc->sc_sensors[i].type = it_sensors[i].type;
245 
246 		if (it_sensors[i].desc != NULL)
247 			strlcpy(sc->sc_sensors[i].desc, it_sensors[i].desc,
248 			    sizeof(sc->sc_sensors[i].desc));
249 	}
250 
251 	/* register sensor update task */
252 	if (sensor_task_register(sc, it_ec_refresh, IT_EC_INTERVAL) == NULL) {
253 		printf("%s: unable to register update task\n",
254 		    sc->sc_dev.dv_xname);
255 		bus_space_unmap(sc->sc_ec_iot, sc->sc_ec_ioh, 8);
256 		return;
257 	}
258 
259 	/* activate monitoring */
260 	it_ec_writereg(sc, IT_EC_CFG,
261 	    it_ec_readreg(sc, IT_EC_CFG) | IT_EC_CFG_START | IT_EC_INT_CLEAR);
262 
263 	/* initialize sensors */
264 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
265 	    sizeof(sc->sc_sensordev.xname));
266 	for (i = 0; i < IT_EC_NUMSENSORS; i++)
267 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
268 	sensordev_install(&sc->sc_sensordev);
269 }
270 
271 u_int8_t
272 it_readreg(bus_space_tag_t iot, bus_space_handle_t ioh, int r)
273 {
274 	bus_space_write_1(iot, ioh, IT_IO_ADDR, r);
275 	return (bus_space_read_1(iot, ioh, IT_IO_DATA));
276 }
277 
278 void
279 it_writereg(bus_space_tag_t iot, bus_space_handle_t ioh, int r, u_int8_t v)
280 {
281 	bus_space_write_1(iot, ioh, IT_IO_ADDR, r);
282 	bus_space_write_1(iot, ioh, IT_IO_DATA, v);
283 }
284 
285 void
286 it_enter(bus_space_tag_t iot, bus_space_handle_t ioh, int iobase)
287 {
288 	bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x87);
289 	bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x01);
290 	bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x55);
291 	if (iobase == IO_IT1)
292 		bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x55);
293 	else
294 		bus_space_write_1(iot, ioh, IT_IO_ADDR, 0xaa);
295 }
296 
297 void
298 it_exit(bus_space_tag_t iot, bus_space_handle_t ioh)
299 {
300 	bus_space_write_1(iot, ioh, IT_IO_ADDR, IT_CCR);
301 	bus_space_write_1(iot, ioh, IT_IO_DATA, 0x02);
302 }
303 
304 u_int8_t
305 it_ec_readreg(struct it_softc *sc, int r)
306 {
307 	bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_ADDR, r);
308 	return (bus_space_read_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_DATA));
309 }
310 
311 void
312 it_ec_writereg(struct it_softc *sc, int r, u_int8_t v)
313 {
314 	bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_ADDR, r);
315 	bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_DATA, v);
316 }
317 
318 void
319 it_ec_refresh(void *arg)
320 {
321 	struct it_softc *sc = arg;
322 	int i, sdata, mode, divisor, odivisor, ndivisor;
323 
324 	/* refresh temp sensors */
325 	for (i = 0; i < IT_TEMP_COUNT; i++) {
326 		sdata = it_ec_readreg(sc, IT_EC_TEMPBASE + i);
327 		/* convert to degF */
328 		sc->sc_sensors[IT_TEMP_BASE + i].value =
329 		    sdata * 1000000 + 273150000;
330 	}
331 
332 	/* refresh volt sensors */
333 	for (i = 0; i < IT_VOLT_COUNT; i++) {
334 		sdata = it_ec_readreg(sc, IT_EC_VOLTBASE + i);
335 		/* voltage returned as (mV >> 4) */
336 		sc->sc_sensors[IT_VOLT_BASE + i].value = sdata << 4;
337 		/* these two values are negative and formula is different */
338 		if (i == 5 || i == 6)
339 			sc->sc_sensors[IT_VOLT_BASE + i].value -= IT_EC_VREF;
340 		/* rfact is (factor * 10^4) */
341 		sc->sc_sensors[IT_VOLT_BASE + i].value *= it_vrfact[i];
342 		/* division by 10 gets us back to uVDC */
343 		sc->sc_sensors[IT_VOLT_BASE + i].value /= 10;
344 		if (i == 5 || i == 6)
345 			sc->sc_sensors[IT_VOLT_BASE + i].value +=
346 			    IT_EC_VREF * 1000;
347 	}
348 
349 	/* refresh fan sensors */
350 	if (sc->sc_chipid == IT_ID_8705 || sc->sc_chipid == IT_ID_8712)
351 		odivisor = ndivisor = divisor =
352 		    it_ec_readreg(sc, IT_EC_FAN_DIV);
353 	else {
354 		mode = it_ec_readreg(sc, IT_EC_FAN_ECR);
355 		divisor = -1;
356 	}
357 
358 	for (i = 0; i < IT_FAN_COUNT; i++) {
359 		sc->sc_sensors[IT_FAN_BASE + i].flags &= ~SENSOR_FINVALID;
360 		sdata = it_ec_readreg(sc, IT_EC_FANBASE + i);
361 
362 		if (divisor != -1) {
363 			/*
364 			 * Use 8-bit FAN Tachometer & FAN Divisor registers
365 			 */
366 			if (sdata == 0xff) {
367 				sc->sc_sensors[IT_FAN_BASE + i].flags |=
368 				    SENSOR_FINVALID;
369 				if (i == 2)
370 					ndivisor ^= 0x40;
371 				else {
372 					ndivisor &= ~(7 << (i * 3));
373 					ndivisor |= ((divisor + 1) & 7) <<
374 					    (i * 3);
375 				}
376 			} else if (sdata != 0) {
377 				if (i == 2)
378 					divisor = divisor & 1 ? 3 : 1;
379 				sc->sc_sensors[IT_FAN_BASE + i].value =
380 				    1350000 / (sdata << (divisor & 7));
381 			} else
382 				sc->sc_sensors[IT_FAN_BASE + i].value = 0;
383 
384 			if (ndivisor != odivisor)
385 				it_ec_writereg(sc, IT_EC_FAN_DIV, ndivisor);
386 		} else {
387 			/*
388 			 * Use 16-bit FAN tachometer register
389 			 */
390 			if (mode & (1 << i))
391 				sdata |= it_ec_readreg(sc,
392 				    IT_EC_FANEXTBASE + i) << 8;
393 			if (sdata == ((mode & (1 << i)) ? 0xffff : 0xff))
394 				sc->sc_sensors[IT_FAN_BASE + i].flags |=
395 				    SENSOR_FINVALID;
396 			else if (sdata != 0)
397 				sc->sc_sensors[IT_FAN_BASE + i].value =
398 				    675000 / sdata;
399 			else
400 				sc->sc_sensors[IT_FAN_BASE + i].value = 0;
401 		}
402 	}
403 }
404 
405 int
406 it_wdog_cb(void *arg, int period)
407 {
408 	struct it_softc *sc = arg;
409 	int minutes = 0;
410 
411 	/* enter MB PnP mode and select WDT device */
412 	it_enter(sc->sc_iot, sc->sc_ioh, sc->sc_iobase);
413 	it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_WDT_LDN);
414 
415 	/* disable watchdog timer */
416 	it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR, 0x00);
417 
418 	/* 1000s should be enough for everyone */
419 	if (period > 1000)
420 		period = 1000;
421 	else if (period < 0)
422 		period = 0;
423 
424 	if (period > 0) {
425 		/*
426 		 * Older IT8712F chips have 8-bit timeout counter.
427 		 * Use minutes for 16-bit values for these chips.
428 		 */
429 		if (sc->sc_chipid == IT_ID_8712 && sc->sc_chiprev < 0x8 &&
430 		    period > 0xff) {
431 			period /= 60;
432 			minutes++;
433 		}
434 
435 		/* set watchdog timeout (low byte) */
436 		it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TMO_LSB,
437 		    period & 0xff);
438 
439 		if (minutes) {
440 			/* enable watchdog timer */
441 			it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR,
442 			    IT_WDT_TCR_KRST | IT_WDT_TCR_PWROK);
443 
444 			period *= 60;
445 		} else {
446 			/* set watchdog timeout (high byte) */
447 			it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TMO_MSB,
448 			    period >> 8);
449 
450 			/* enable watchdog timer */
451 			it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR,
452 			    IT_WDT_TCR_SECS | IT_WDT_TCR_KRST |
453 			    IT_WDT_TCR_PWROK);
454 		}
455 	}
456 
457 	/* exit MB PnP mode */
458 	it_exit(sc->sc_iot, sc->sc_ioh);
459 
460 	return (period);
461 }
462 
463 
464 struct cfattach it_ca = {
465 	sizeof(struct it_softc),
466 	it_match,
467 	it_attach
468 };
469 
470 struct cfdriver it_cd = {
471 	NULL, "it", DV_DULL
472 };
473