it.c revision 1.28
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
49int it_match(struct device *, void *, void *);
50void it_attach(struct device *, struct device *, void *);
51u_int8_t it_readreg(bus_space_tag_t, bus_space_handle_t, int);
52void it_writereg(bus_space_tag_t, bus_space_handle_t, int, u_int8_t);
53void it_enter(bus_space_tag_t, bus_space_handle_t, int);
54void it_exit(bus_space_tag_t, bus_space_handle_t);
55
56u_int8_t it_ec_readreg(struct it_softc *, int);
57void it_ec_writereg(struct it_softc *, int, u_int8_t);
58void it_ec_refresh(void *arg);
59
60int it_wdog_cb(void *, int);
61
62
63struct {
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
95int 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
101LIST_HEAD(, it_softc) it_softc_list = LIST_HEAD_INITIALIZER(&it_softc_list);
102
103
104int
105it_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
172void
173it_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
260u_int8_t
261it_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
267void
268it_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
274void
275it_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
286void
287it_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
293u_int8_t
294it_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
300void
301it_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
307void
308it_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
394int
395it_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
427struct cfattach it_ca = {
428	sizeof(struct it_softc),
429	it_match,
430	it_attach
431};
432
433struct cfdriver it_cd = {
434	NULL, "it", DV_DULL
435};
436