xref: /openbsd-src/sys/dev/isa/it.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /*	$OpenBSD: it.c,v 1.32 2008/04/07 17:50:37 form 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 	u_int8_t cr;
193 
194 	sc->sc_iot = ia->ia_iot;
195 	sc->sc_iobase = ia->ipa_io[0].base;
196 	if (bus_space_map(sc->sc_iot, sc->sc_iobase, 2, 0, &sc->sc_ioh) != 0) {
197 		printf(": can't map i/o space\n");
198 		return;
199 	}
200 
201 	/* enter MB PnP mode */
202 	it_enter(sc->sc_iot, sc->sc_ioh, sc->sc_iobase);
203 
204 	/* get chip id and rev */
205 	sc->sc_chipid = it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPID1) << 8;
206 	sc->sc_chipid |= it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPID2);
207 	sc->sc_chiprev = it_readreg(sc->sc_iot, sc->sc_ioh, IT_CHIPREV);
208 
209 	/* get environment controller base address */
210 	it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_EC_LDN);
211 	sc->sc_ec_iobase = it_readreg(sc->sc_iot, sc->sc_ioh, IT_EC_MSB) << 8;
212 	sc->sc_ec_iobase |= it_readreg(sc->sc_iot, sc->sc_ioh, IT_EC_LSB);
213 
214 	/* initialize watchdog */
215 	if (sc->sc_chipid != IT_ID_8705) {
216 		it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_WDT_LDN);
217 		it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_CSR, 0x00);
218 		it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR,
219 		    IT_WDT_TCR_SECS);
220 		wdog_register(sc, it_wdog_cb);
221 	}
222 
223 	/* exit MB PnP mode and unmap */
224 	it_exit(sc->sc_iot, sc->sc_ioh);
225 
226 	LIST_INSERT_HEAD(&it_softc_list, sc, sc_list);
227 
228 	printf(": IT%xF rev 0x%02x", sc->sc_chipid, sc->sc_chiprev);
229 
230 	if (sc->sc_ec_iobase == 0) {
231 		printf(", EC disabled\n");
232 		return;
233 	}
234 
235 	printf(", EC port 0x%x\n", sc->sc_ec_iobase);
236 
237 	/* map environment controller i/o space */
238 	sc->sc_ec_iot = ia->ia_iot;
239 	if (bus_space_map(sc->sc_ec_iot, sc->sc_ec_iobase, 8, 0,
240 	    &sc->sc_ec_ioh) != 0) {
241 		printf("%s: can't map EC i/o space\n", sc->sc_dev.dv_xname);
242 		return;
243 	}
244 
245 	/* initialize sensor structures */
246 	for (i = 0; i < IT_EC_NUMSENSORS; i++) {
247 		sc->sc_sensors[i].type = it_sensors[i].type;
248 
249 		if (it_sensors[i].desc != NULL)
250 			strlcpy(sc->sc_sensors[i].desc, it_sensors[i].desc,
251 			    sizeof(sc->sc_sensors[i].desc));
252 	}
253 
254 	/* register update task */
255 	if (sensor_task_register(sc, it_ec_refresh, IT_EC_INTERVAL) == NULL) {
256 		printf(": unable to register update task\n",
257 		    sc->sc_dev.dv_xname);
258 		bus_space_unmap(sc->sc_ec_iot, sc->sc_ec_ioh, 8);
259 		return;
260 	}
261 
262 	/* activate monitoring */
263 	cr = it_ec_readreg(sc, IT_EC_CFG);
264 	it_ec_writereg(sc, IT_EC_CFG, cr | 0x09);
265 
266 	/* initialize sensors */
267 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
268 	    sizeof(sc->sc_sensordev.xname));
269 	for (i = 0; i < IT_EC_NUMSENSORS; i++)
270 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
271 	sensordev_install(&sc->sc_sensordev);
272 }
273 
274 u_int8_t
275 it_readreg(bus_space_tag_t iot, bus_space_handle_t ioh, int r)
276 {
277 	bus_space_write_1(iot, ioh, IT_IO_ADDR, r);
278 	return (bus_space_read_1(iot, ioh, IT_IO_DATA));
279 }
280 
281 void
282 it_writereg(bus_space_tag_t iot, bus_space_handle_t ioh, int r, u_int8_t v)
283 {
284 	bus_space_write_1(iot, ioh, IT_IO_ADDR, r);
285 	bus_space_write_1(iot, ioh, IT_IO_DATA, v);
286 }
287 
288 void
289 it_enter(bus_space_tag_t iot, bus_space_handle_t ioh, int iobase)
290 {
291 	bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x87);
292 	bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x01);
293 	bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x55);
294 	if (iobase == IO_IT1)
295 		bus_space_write_1(iot, ioh, IT_IO_ADDR, 0x55);
296 	else
297 		bus_space_write_1(iot, ioh, IT_IO_ADDR, 0xaa);
298 }
299 
300 void
301 it_exit(bus_space_tag_t iot, bus_space_handle_t ioh)
302 {
303 	bus_space_write_1(iot, ioh, IT_IO_ADDR, IT_CCR);
304 	bus_space_write_1(iot, ioh, IT_IO_DATA, 0x02);
305 }
306 
307 u_int8_t
308 it_ec_readreg(struct it_softc *sc, int r)
309 {
310 	bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_ADDR, r);
311 	return (bus_space_read_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_DATA));
312 }
313 
314 void
315 it_ec_writereg(struct it_softc *sc, int r, u_int8_t v)
316 {
317 	bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_ADDR, r);
318 	bus_space_write_1(sc->sc_ec_iot, sc->sc_ec_ioh, IT_EC_DATA, v);
319 }
320 
321 void
322 it_ec_refresh(void *arg)
323 {
324 	struct it_softc *sc = arg;
325 	int i, sdata, mode, divisor, odivisor, ndivisor;
326 
327 	/* refresh temp sensors */
328 	for (i = 0; i < IT_TEMP_COUNT; i++) {
329 		sdata = it_ec_readreg(sc, IT_EC_TEMPBASE + i);
330 		/* convert to degF */
331 		sc->sc_sensors[IT_TEMP_BASE + i].value =
332 		    sdata * 1000000 + 273150000;
333 	}
334 
335 	/* refresh volt sensors */
336 	for (i = 0; i < IT_VOLT_COUNT; i++) {
337 		sdata = it_ec_readreg(sc, IT_EC_VOLTBASE + i);
338 		/* voltage returned as (mV >> 4) */
339 		sc->sc_sensors[IT_VOLT_BASE + i].value = sdata << 4;
340 		/* these two values are negative and formula is different */
341 		if (i == 5 || i == 6)
342 			sc->sc_sensors[IT_VOLT_BASE + i].value -= IT_EC_VREF;
343 		/* rfact is (factor * 10^4) */
344 		sc->sc_sensors[IT_VOLT_BASE + i].value *= it_vrfact[i];
345 		/* division by 10 gets us back to uVDC */
346 		sc->sc_sensors[IT_VOLT_BASE + i].value /= 10;
347 		if (i == 5 || i == 6)
348 			sc->sc_sensors[IT_VOLT_BASE + i].value +=
349 			    IT_EC_VREF * 1000;
350 	}
351 
352 	/* refresh fan sensors */
353 	if (sc->sc_chipid == IT_ID_8705 || sc->sc_chipid == IT_ID_8712)
354 		odivisor = ndivisor = divisor =
355 		    it_ec_readreg(sc, IT_EC_FAN_DIV);
356 	else {
357 		mode = it_ec_readreg(sc, IT_EC_FAN_ECR);
358 		divisor = -1;
359 	}
360 
361 	for (i = 0; i < IT_FAN_COUNT; i++) {
362 		sc->sc_sensors[IT_FAN_BASE + i].flags &= ~SENSOR_FINVALID;
363 		sdata = it_ec_readreg(sc, IT_EC_FANBASE + i);
364 
365 		if (divisor != -1) {
366 			/*
367 			 * Use 8-bit FAN Tachometer & FAN Divisor registers
368 			 */
369 			if (sdata == 0xff) {
370 				sc->sc_sensors[IT_FAN_BASE + i].flags |=
371 				    SENSOR_FINVALID;
372 				if (i == 2)
373 					ndivisor ^= 0x40;
374 				else {
375 					ndivisor &= ~(7 << (i * 3));
376 					ndivisor |= ((divisor + 1) & 7) <<
377 					    (i * 3);
378 				}
379 			} else if (sdata != 0) {
380 				if (i == 2)
381 					divisor = divisor & 1 ? 3 : 1;
382 				sc->sc_sensors[IT_FAN_BASE + i].value =
383 				    1350000 / (sdata << (divisor & 7));
384 			} else
385 				sc->sc_sensors[IT_FAN_BASE + i].value = 0;
386 
387 			if (ndivisor != odivisor)
388 				it_ec_writereg(sc, IT_EC_FAN_DIV, ndivisor);
389 		} else {
390 			/*
391 			 * Use 16-bit FAN tachometer register
392 			 */
393 			if (mode & (1 << i))
394 				sdata |= it_ec_readreg(sc,
395 				    IT_EC_FANEXTBASE + i) << 8;
396 			if (sdata == ((mode & (1 << i)) ? 0xffff : 0xff))
397 				sc->sc_sensors[IT_FAN_BASE + i].flags |=
398 				    SENSOR_FINVALID;
399 			else if (sdata != 0)
400 				sc->sc_sensors[IT_FAN_BASE + i].value =
401 				    675000 / sdata;
402 			else
403 				sc->sc_sensors[IT_FAN_BASE + i].value = 0;
404 		}
405 	}
406 }
407 
408 int
409 it_wdog_cb(void *arg, int period)
410 {
411 	struct it_softc *sc = arg;
412 
413 	/* enter MB PnP mode and select WDT device */
414 	it_enter(sc->sc_iot, sc->sc_ioh, sc->sc_iobase);
415 	it_writereg(sc->sc_iot, sc->sc_ioh, IT_LDN, IT_WDT_LDN);
416 
417 	/* disable watchdog timeout */
418 	it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR, IT_WDT_TCR_SECS);
419 
420 	/* 1000s should be enough for everyone */
421 	if (period > 1000)
422 		period = 1000;
423 	else if (period < 0)
424 		period = 0;
425 
426 	/* set watchdog timeout */
427 	it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TMO_MSB, period >> 8);
428 	it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TMO_LSB, period & 0xff);
429 
430 	if (period > 0)
431 		/* enable watchdog timeout */
432 		it_writereg(sc->sc_iot, sc->sc_ioh, IT_WDT_TCR,
433 		    IT_WDT_TCR_SECS | IT_WDT_TCR_KRST);
434 
435 	/* exit MB PnP mode */
436 	it_exit(sc->sc_iot, sc->sc_ioh);
437 
438 	return (period);
439 }
440 
441 
442 struct cfattach it_ca = {
443 	sizeof(struct it_softc),
444 	it_match,
445 	it_attach
446 };
447 
448 struct cfdriver it_cd = {
449 	NULL, "it", DV_DULL
450 };
451