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