xref: /openbsd-src/sys/dev/isa/fins.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: fins.c,v 1.2 2009/03/30 00:36:26 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2005, 2006 Mark Kettenis
5  * Copyright (c) 2007, 2008 Geoff Steckel
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/device.h>
23 #include <sys/kernel.h>
24 #include <sys/queue.h>
25 #include <sys/sensors.h>
26 #include <machine/bus.h>
27 
28 #include <dev/isa/isareg.h>
29 #include <dev/isa/isavar.h>
30 
31 /* Derived from LM78 code.  Only handles chips attached to ISA bus */
32 
33 /*
34  * Fintek F71805 registers and constants
35  * http://www.fintek.com.tw/files/productfiles/F71805F_V025.pdf
36  * This chip is a multi-io chip with many functions.
37  * Each function may be relocated in I/O space by the BIOS.
38  * The base address (2E or 4E) accesses a configuration space which
39  * has pointers to the individual functions. The config space must be
40  * unlocked with a cookie and relocked afterwards. The chip ID is stored
41  * in config space so it is not normally visible.
42  *
43  * We assume that the monitor is enabled. We don't try to start or stop it.
44  * The voltage dividers specified are from reading the chips on one board.
45  * There is no way to determine what they are in the general case.
46  * This section of the chip controls the fans. We don't do anything to them.
47  */
48 
49 #define FINS_UNLOCK	0x87	/* magic constant - write 2x to select chip */
50 #define FINS_LOCK	0xaa	/* magic constant - write 1x to deselect reg */
51 
52 /* ISA registers index to an internal register space on chip */
53 #define FINS_ADDR	0x00
54 #define FINS_DATA	0x01
55 
56 #define FINS_FUNC_SEL	0x07	/* select which chip function to access */
57 #define FINS_CHIP	0x20	/* chip ID */
58 #define FINS_MANUF	0x23	/* manufacturer ID */
59 #define FINS_BASEADDR	0x60	/* I/O base of chip function */
60 
61 #define FINS_71806	0x0341	/* same as F71872 */
62 #define FINS_71805	0x0406
63 #define FINS_71882	0x0541	/* same as F71883 */
64 #define FINS_71862	0x0601	/* same as F71863 */
65 #define FINTEK_ID	0x1934
66 
67 #define FINS_FUNC_SENSORS	0x04
68 #define FINS_FUNC_WATCHDOG	0x07
69 
70 /* sensors device registers */
71 #define FINS_SENS_TMODE(sc)	((sc)->fins_chipid <= FINS_71805 ? 0x01 : 0x6b)
72 #define FINS_SENS_VDIVS		0x0e
73 
74 /* watchdog device registers (mapped straight to i/o port offsets) */
75 #define FINS_WDOG_CR0	0x00
76 #define FINS_WDOG_CR1	0x05
77 #define FINS_WDOG_TIMER	0x06
78 
79 /* CR0 flags */
80 #define FINS_WDOG_OUTEN	0x80
81 
82 /* CR1 flags */
83 #define FINS_WDOG_EN	0x20
84 #define FINS_WDOG_MINS	0x08
85 
86 #define FINS_MAX_SENSORS 18
87 /*
88  * Fintek chips typically measure voltages using 8mv steps.
89  * To measure higher voltages the input is attenuated with (external)
90  * resistors.  Negative voltages are measured using inverting op amps
91  * and resistors.  So we have to convert the sensor values back to
92  * real voltages by applying the appropriate resistor factor.
93  */
94 #define FRFACT_NONE	8000
95 #define FRFACT(x, y)	(FRFACT_NONE * ((x) + (y)) / (y))
96 #define FNRFACT(x, y)	(-FRFACT_NONE * (x) / (y))
97 
98 struct fins_softc;
99 
100 struct fins_sensor {
101 	char *fs_desc;
102 	void (*fs_refresh)(struct fins_softc *, int);
103 	enum sensor_type fs_type;
104 	int fs_aux;
105 	u_int8_t fs_reg;
106 };
107 
108 struct fins_softc {
109 	struct device sc_dev;
110 
111 	struct ksensor fins_ksensors[FINS_MAX_SENSORS];
112 	struct ksensordev fins_sensordev;
113 	struct sensor_task *fins_sensortask;
114 	struct fins_sensor *fins_sensors;
115 
116 	bus_space_handle_t sc_ioh_sens;
117 	bus_space_handle_t sc_ioh_wdog;
118 	bus_space_tag_t sc_iot;
119 
120 	u_int16_t fins_chipid;
121 	u_int8_t fins_tempsel;
122 	u_int8_t fins_wdog_cr;
123 };
124 
125 int  fins_match(struct device *, void *, void *);
126 void fins_attach(struct device *, struct device *, void *);
127 
128 void fins_unlock(bus_space_tag_t, bus_space_handle_t);
129 void fins_lock(bus_space_tag_t, bus_space_handle_t);
130 
131 u_int8_t fins_read(bus_space_tag_t, bus_space_handle_t, int);
132 u_int16_t fins_read_2(bus_space_tag_t, bus_space_handle_t, int);
133 void fins_write(bus_space_tag_t, bus_space_handle_t, int, u_int8_t);
134 
135 static __inline u_int8_t fins_read_sens(struct fins_softc *, int);
136 static __inline u_int16_t fins_read_sens_2(struct fins_softc *, int);
137 
138 static __inline u_int8_t fins_read_wdog(struct fins_softc *, int);
139 static __inline void fins_write_wdog(struct fins_softc *, int, u_int8_t);
140 
141 void fins_setup_sensors(struct fins_softc *, struct fins_sensor *);
142 void fins_refresh(void *);
143 
144 void fins_get_rpm(struct fins_softc *, int);
145 void fins_get_temp(struct fins_softc *, int);
146 void fins_get_volt(struct fins_softc *, int);
147 
148 int fins_wdog_cb(void *, int);
149 
150 struct cfattach fins_ca = {
151 	sizeof(struct fins_softc),
152 	fins_match,
153 	fins_attach
154 };
155 
156 struct cfdriver fins_cd = {
157 	NULL, "fins", DV_DULL
158 };
159 
160 struct fins_sensor fins_71805_sensors[] = {
161 	{ "+3.3V",  fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100),	0x10 },
162 	{ "Vtt",    fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE,	0x11 },
163 	{ "Vram",   fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100),	0x12 },
164 	{ "Vchips", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(47, 100),	0x13 },
165 	{ "+5V",    fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47),	0x14 },
166 	{ "+12V",   fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 20),	0x15 },
167 	{ "+1.5V",  fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE,	0x16 },
168 	{ "Vcore",  fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE,	0x17 },
169 	{ "Vsb",    fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47),	0x18 },
170 	{ "Vsbint", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47),	0x19 },
171 	{ "Vbat",   fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47),	0x1a },
172 
173 	{ NULL, fins_get_temp, SENSOR_TEMP, 0x01, 0x1b },
174 	{ NULL, fins_get_temp, SENSOR_TEMP, 0x02, 0x1c },
175 	{ NULL, fins_get_temp, SENSOR_TEMP, 0x04, 0x1d },
176 
177 	{ NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0x20 },
178 	{ NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0x22 },
179 	{ NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0x24 },
180 
181 	{ NULL }
182 };
183 
184 struct fins_sensor fins_71882_sensors[] = {
185 	{ "+3.3V",  fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100),	0x20 },
186 	{ "Vcore",  fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE,	0x21 },
187 	{ "Vram",   fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100),	0x22 },
188 	{ "Vchips", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(47, 100),	0x23 },
189 	{ "+5V",    fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47),	0x24 },
190 	{ "+12V",   fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 20),	0x25 },
191 	{ "+1.5V",  fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE,	0x26 },
192 	{ "Vsb",    fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100),	0x27 },
193 	{ "Vbat",   fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100),	0x28 },
194 
195 	{ NULL, fins_get_temp, SENSOR_TEMP, 0x02, 0x72 },
196 	{ NULL, fins_get_temp, SENSOR_TEMP, 0x04, 0x74 },
197 	{ NULL, fins_get_temp, SENSOR_TEMP, 0x08, 0x76 },
198 
199 	{ NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0xa0 },
200 	{ NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0xb0 },
201 	{ NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0xc0 },
202 	{ NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0xd0 },
203 
204 	{ NULL }
205 };
206 
207 int
208 fins_match(struct device *parent, void *match, void *aux)
209 {
210 	struct isa_attach_args *ia = aux;
211 	bus_space_handle_t ioh;
212 	bus_space_tag_t iot;
213 	int ret = 0;
214 	u_int16_t id;
215 
216 	iot = ia->ia_iot;
217 	if (bus_space_map(iot, ia->ipa_io[0].base, 2, 0, &ioh))
218 		return (0);
219 
220 	/* Fintek uses magic cookie locks to distinguish their chips */
221 	fins_unlock(iot, ioh);
222 
223 	fins_write(iot, ioh, FINS_FUNC_SEL, 0);	/* IDs appear only in space 0 */
224 	if (fins_read_2(iot, ioh, FINS_MANUF) != FINTEK_ID)
225 		goto match_done;
226 	id = fins_read_2(iot, ioh, FINS_CHIP);
227 	switch(id) {
228 	case FINS_71882:
229 	case FINS_71862:
230 		ia->ipa_nio = 3;
231 		fins_write(iot, ioh, FINS_FUNC_SEL, FINS_FUNC_WATCHDOG);
232 		ia->ipa_io[2].base = fins_read_2(iot, ioh, FINS_BASEADDR);
233 		ia->ipa_io[2].length = 8;
234 		fins_write(iot, ioh, FINS_FUNC_SEL, FINS_FUNC_SENSORS);
235 		ia->ipa_io[1].base = fins_read_2(iot, ioh, FINS_BASEADDR);
236 		ia->ipa_io[1].base += 5;
237 		break;
238 	case FINS_71806:
239 	case FINS_71805:
240 		ia->ipa_nio = 2;
241 		fins_write(iot, ioh, FINS_FUNC_SEL, FINS_FUNC_SENSORS);
242 		ia->ipa_io[1].base = fins_read_2(iot, ioh, FINS_BASEADDR);
243 		break;
244 	default:
245 		goto match_done;
246 	}
247 	ia->ipa_io[0].length = ia->ipa_io[1].length = 2;
248 	ia->ipa_nmem = ia->ipa_nirq = ia->ipa_ndrq = 0;
249 	ia->ia_aux = (void *)(u_long)id;
250 	ret = 1;
251 match_done:
252 	fins_lock(iot, ioh);
253 	return (ret);
254 }
255 
256 void
257 fins_attach(struct device *parent, struct device *self, void *aux)
258 {
259 	struct fins_softc *sc = (struct fins_softc *)self;
260 	struct isa_attach_args *ia = aux;
261 	bus_addr_t iobase;
262 	u_int32_t iosize;
263 	u_int i;
264 
265 	sc->sc_iot = ia->ia_iot;
266 	sc->fins_chipid = (u_int16_t)(u_long)ia->ia_aux;
267 	iobase = ia->ipa_io[1].base;
268 	iosize = ia->ipa_io[1].length;
269 	if (bus_space_map(sc->sc_iot, iobase, iosize, 0, &sc->sc_ioh_sens)) {
270 		printf(": can't map sensor i/o space\n");
271 		return;
272 	}
273 	switch(sc->fins_chipid) {
274 	case FINS_71882:
275 	case FINS_71862:
276 		fins_setup_sensors(sc, fins_71882_sensors);
277 		break;
278 	case FINS_71806:
279 	case FINS_71805:
280 		fins_setup_sensors(sc, fins_71805_sensors);
281 		break;
282 	}
283 	sc->fins_sensortask = sensor_task_register(sc, fins_refresh, 5);
284 	if (sc->fins_sensortask == NULL) {
285 		printf(": can't register update task\n");
286 		return;
287 	}
288 	for (i = 0; sc->fins_sensors[i].fs_refresh != NULL; ++i)
289 		sensor_attach(&sc->fins_sensordev, &sc->fins_ksensors[i]);
290 	sensordev_install(&sc->fins_sensordev);
291 
292 	if (sc->fins_chipid <= FINS_71805)
293 		goto attach_done;
294 	iobase = ia->ipa_io[2].base;
295 	iosize = ia->ipa_io[2].length;
296 	if (bus_space_map(sc->sc_iot, iobase, iosize, 0, &sc->sc_ioh_wdog)) {
297 		printf(": can't map watchdog i/o space\n");
298 		return;
299 	}
300 	sc->fins_wdog_cr = fins_read_wdog(sc, FINS_WDOG_CR1);
301 	sc->fins_wdog_cr &= ~(FINS_WDOG_MINS | FINS_WDOG_EN);
302 	fins_write_wdog(sc, FINS_WDOG_CR1, sc->fins_wdog_cr);
303 	wdog_register(sc, fins_wdog_cb);
304 attach_done:
305 	printf("\n");
306 }
307 
308 u_int8_t
309 fins_read(bus_space_tag_t iot, bus_space_handle_t ioh, int reg)
310 {
311 	bus_space_write_1(iot, ioh, FINS_ADDR, reg);
312 	return (bus_space_read_1(iot, ioh, FINS_DATA));
313 }
314 
315 u_int16_t
316 fins_read_2(bus_space_tag_t iot, bus_space_handle_t ioh, int reg)
317 {
318 	u_int16_t val;
319 
320 	bus_space_write_1(iot, ioh, FINS_ADDR, reg);
321 	val = bus_space_read_1(iot, ioh, FINS_DATA) << 8;
322 	bus_space_write_1(iot, ioh, FINS_ADDR, reg + 1);
323 	return (val | bus_space_read_1(iot, ioh, FINS_DATA));
324 }
325 
326 void
327 fins_write(bus_space_tag_t iot, bus_space_handle_t ioh, int reg, u_int8_t val)
328 {
329 	bus_space_write_1(iot, ioh, FINS_ADDR, reg);
330 	bus_space_write_1(iot, ioh, FINS_DATA, val);
331 }
332 
333 static __inline u_int8_t
334 fins_read_sens(struct fins_softc *sc, int reg)
335 {
336 	return (fins_read(sc->sc_iot, sc->sc_ioh_sens, reg));
337 }
338 
339 static __inline u_int16_t
340 fins_read_sens_2(struct fins_softc *sc, int reg)
341 {
342 	return (fins_read_2(sc->sc_iot, sc->sc_ioh_sens, reg));
343 }
344 
345 static __inline u_int8_t
346 fins_read_wdog(struct fins_softc *sc, int reg)
347 {
348 	return (bus_space_read_1(sc->sc_iot, sc->sc_ioh_wdog, reg));
349 }
350 
351 static __inline void
352 fins_write_wdog(struct fins_softc *sc, int reg, u_int8_t val)
353 {
354 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_wdog, reg, val);
355 }
356 
357 void
358 fins_unlock(bus_space_tag_t iot, bus_space_handle_t ioh)
359 {
360 	bus_space_write_1(iot, ioh, 0, FINS_UNLOCK);
361 	bus_space_write_1(iot, ioh, 0, FINS_UNLOCK);
362 }
363 
364 void
365 fins_lock(bus_space_tag_t iot, bus_space_handle_t ioh)
366 {
367 	bus_space_write_1(iot, ioh, 0, FINS_LOCK);
368 	bus_space_unmap(iot, ioh, 2);
369 }
370 
371 void
372 fins_setup_sensors(struct fins_softc *sc, struct fins_sensor *sensors)
373 {
374 	int i;
375 
376 	for (i = 0; sensors[i].fs_refresh != NULL; ++i) {
377 		sc->fins_ksensors[i].type = sensors[i].fs_type;
378 		if (sensors[i].fs_desc != NULL)
379 			strlcpy(sc->fins_ksensors[i].desc, sensors[i].fs_desc,
380 				sizeof(sc->fins_ksensors[i].desc));
381 	}
382 	strlcpy(sc->fins_sensordev.xname, sc->sc_dev.dv_xname,
383 		sizeof(sc->fins_sensordev.xname));
384 	sc->fins_sensors = sensors;
385 	sc->fins_tempsel = fins_read_sens(sc, FINS_SENS_TMODE(sc));
386 }
387 
388 #if 0
389 void
390 fins_get_dividers(struct fins_softc *sc)
391 {
392 	int i, p, m;
393 	u_int16_t r = fins_read_sens_2(sc, FINS_SENS_VDIVS);
394 
395 	for (i = 0; i < 6; ++i) {
396 		p = (i < 4) ? i : i + 2;
397 		m = (r & (0x03 << p)) >> p;
398 		if (m == 3)
399 			m = 4;
400 		fins_71882_sensors[i + 1].fs_aux = FRFACT_NONE << m;
401 	}
402 }
403 #endif
404 
405 void
406 fins_refresh(void *arg)
407 {
408 	struct fins_softc *sc = arg;
409 	int i;
410 
411 	for (i = 0; sc->fins_sensors[i].fs_refresh != NULL; ++i)
412 		sc->fins_sensors[i].fs_refresh(sc, i);
413 }
414 
415 void
416 fins_get_volt(struct fins_softc *sc, int n)
417 {
418 	struct ksensor *sensor = &sc->fins_ksensors[n];
419 	struct fins_sensor *fs = &sc->fins_sensors[n];
420 	int data;
421 
422 	data = fins_read_sens(sc, fs->fs_reg);
423 	if (data == 0xff || data == 0) {
424 		sensor->flags |= SENSOR_FINVALID;
425 		sensor->value = 0;
426 	} else {
427 		sensor->flags &= ~SENSOR_FINVALID;
428 		sensor->value = data * fs->fs_aux;
429 	}
430 }
431 
432 /* The BIOS seems to add a fudge factor to the CPU temp of +5C */
433 void
434 fins_get_temp(struct fins_softc *sc, int n)
435 {
436 	struct ksensor *sensor = &sc->fins_ksensors[n];
437 	struct fins_sensor *fs = &sc->fins_sensors[n];
438 	u_int data;
439 	u_int max;
440 
441 	/*
442 	 * The data sheet says that the range of the temperature
443 	 * sensor is between 0 and 127 or 140 degrees C depending on
444 	 * what kind of sensor is used.
445 	 * A disconnected sensor seems to read over 110 or so.
446 	 */
447 	data = fins_read_sens(sc, fs->fs_reg);
448 	max = (sc->fins_tempsel & fs->fs_aux) ? 111 : 128;
449 	if (data == 0 || data >= max) {	/* disconnected? */
450 		sensor->flags |= SENSOR_FINVALID;
451 		sensor->value = 0;
452 	} else {
453 		sensor->flags &= ~SENSOR_FINVALID;
454 		sensor->value = data * 1000000 + 273150000;
455 	}
456 }
457 
458 /* The chip holds a fudge factor for BJT sensors */
459 /* this is currently unused but might be reenabled */
460 #if 0
461 void
462 fins_refresh_offset(struct fins_softc *sc, int n)
463 {
464 	struct ksensor *sensor = &sc->fins_ksensors[n];
465 	struct fins_sensor *fs = &sc->fins_sensors[n];
466 	u_int data;
467 
468 	sensor->flags &= ~SENSOR_FINVALID;
469 	data = fins_read_sens(sc, fs->fs_reg);
470 	data |= ~0 * (data & 0x40);	/* sign extend 7-bit value */
471 	sensor->value = data * 1000000 + 273150000;
472 }
473 #endif
474 
475 /* fan speed appears to be a 12-bit number */
476 void
477 fins_get_rpm(struct fins_softc *sc, int n)
478 {
479 	struct ksensor *sensor = &sc->fins_ksensors[n];
480 	struct fins_sensor *fs = &sc->fins_sensors[n];
481 	int data;
482 
483 	data = fins_read_sens_2(sc, fs->fs_reg);
484 	if (data >= 0xfff) {
485 		sensor->value = 0;
486 		sensor->flags |= SENSOR_FINVALID;
487 	} else {
488 		sensor->value = 1500000 / data;
489 		sensor->flags &= ~SENSOR_FINVALID;
490 	}
491 }
492 
493 int
494 fins_wdog_cb(void *arg, int period)
495 {
496 	struct fins_softc *sc = arg;
497 	u_int8_t cr0, cr1, t;
498 
499 	cr0 = fins_read_wdog(sc, FINS_WDOG_CR0) & ~FINS_WDOG_OUTEN;
500 	fins_write_wdog(sc, FINS_WDOG_CR0, cr0);
501 
502 	cr1 = sc->fins_wdog_cr;
503 	if (period > 0xff) {
504 		cr1 |= FINS_WDOG_MINS;
505 		t = (period + 59) / 60;
506 		period = (int)t * 60;
507 	} else if (period > 0)
508 		t = period;
509 	else
510 		return (0);
511 
512 	fins_write_wdog(sc, FINS_WDOG_TIMER, t);
513 	fins_write_wdog(sc, FINS_WDOG_CR0, cr0 | FINS_WDOG_OUTEN);
514 	fins_write_wdog(sc, FINS_WDOG_CR1, cr1 | FINS_WDOG_EN);
515 	return (period);
516 }
517