xref: /openbsd-src/sys/dev/isa/fins.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /*	$OpenBSD: fins.c,v 1.1 2008/03/19 19:33:09 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 
50 #define FINS_UNLOCK	0x87	/* magic constant - write 2x to select chip */
51 #define FINS_LOCK	0xaa	/* magic constant - write 1x to deselect reg */
52 
53 #define FINS_FUNC_SEL	0x07	/* select which subchip to access */
54 #  define FINS_FUNC_SENSORS 0x4
55 
56 /* ISA registers index to an internal register space on chip */
57 #define FINS_ADDR	0x00
58 #define FINS_DATA	0x01
59 
60 /* Chip identification regs and values in bank 0 */
61 #define FINS_MANUF	0x23	/* manufacturer ID */
62 # define FINTEK_ID	0x1934
63 #define FINS_CHIP	0x20	/* chip ID */
64 # define FINS_ID	0x0406
65 
66 /* in bank sensors of config space */
67 #define FINS_SENSADDR	0x60	/* sensors assigned I/O address (2 bytes) */
68 
69 /* in sensors space */
70 #define FINS_TMODE	0x01	/* temperature mode reg */
71 
72 #define FINS_MAX_SENSORS	20
73 /*
74  * Fintek chips typically measure voltages using 8mv steps.
75  * To measure higher voltages the input is attenuated with (external)
76  * resistors.  Negative voltages are measured using inverting op amps
77  * and resistors.  So we have to convert the sensor values back to
78  * real voltages by applying the appropriate resistor factor.
79  */
80 #define FRFACT_NONE	8000
81 #define FRFACT(x, y)	(FRFACT_NONE * ((x) + (y)) / (y))
82 #define FNRFACT(x, y)	(-FRFACT_NONE * (x) / (y))
83 
84 #if defined(FINSDEBUG)
85 #define DPRINTF(x)		do { printf x; } while (0)
86 #else
87 #define DPRINTF(x)
88 #endif
89 
90 struct fins_softc;
91 
92 struct fins_sensor {
93 	char *fs_desc;
94 	enum sensor_type fs_type;
95 	u_int8_t fs_aux;
96 	u_int8_t fs_reg;
97 	void (*fs_refresh)(struct fins_softc *, int);
98 	int fs_rfact;
99 };
100 
101 struct fins_softc {
102 	struct device sc_dev;
103 
104 	struct ksensor fins_ksensors[FINS_MAX_SENSORS];
105 	struct ksensordev fins_sensordev;
106 	struct sensor_task *fins_sensortask;
107 	struct fins_sensor *fins_sensors;
108 	u_int fins_numsensors;
109 	void (*refresh_sensor_data) (struct fins_softc *);
110 
111 	u_int8_t (*fins_readreg)(struct fins_softc *, int);
112 	void (*fins_writereg)(struct fins_softc *, int, int);
113 	u_int fins_tempsel;
114 };
115 
116 
117 struct fins_isa_softc {
118 	struct fins_softc sc_finssc;
119 
120 	bus_space_tag_t sc_iot;
121 	bus_space_handle_t sc_ioh;
122 };
123 
124 int  fins_isa_match(struct device *, void *, void *);
125 void fins_isa_attach(struct device *, struct device *, void *);
126 u_int8_t fins_isa_readreg(struct fins_softc *, int);
127 void fins_isa_writereg(struct fins_softc *, int, int);
128 
129 void fins_setup_sensors(struct fins_softc *, struct fins_sensor *);
130 void fins_refresh(void *);
131 
132 void fins_refresh_volt(struct fins_softc *, int);
133 void fins_refresh_temp(struct fins_softc *, int);
134 void fins_refresh_offset(struct fins_softc *, int);
135 void fins_refresh_fanrpm(struct fins_softc *, int);
136 void fins_attach(struct fins_softc *);
137 int fins_detach(struct fins_softc *);
138 
139 struct cfattach fins_ca = {
140 	sizeof(struct fins_isa_softc),
141 	fins_isa_match,
142 	fins_isa_attach
143 };
144 
145 struct cfdriver fins_cd = {
146 	NULL, "fins", DV_DULL
147 };
148 
149 struct fins_sensor fins_sensors[] = {
150 	/* Voltage */
151 	{ "+3.3V", SENSOR_VOLTS_DC, 0, 0x10, fins_refresh_volt, FRFACT(100, 100) },
152 	{ "Vtt", SENSOR_VOLTS_DC, 0, 0x11, fins_refresh_volt, FRFACT_NONE },
153 	{ "Vram", SENSOR_VOLTS_DC, 0, 0x12, fins_refresh_volt, FRFACT(100, 100) },
154 	{ "Vchips", SENSOR_VOLTS_DC, 0, 0x13, fins_refresh_volt, FRFACT(47, 100) },
155 	{ "+5V", SENSOR_VOLTS_DC, 0, 0x14, fins_refresh_volt, FRFACT(200, 47) },
156 	{ "+12V", SENSOR_VOLTS_DC, 0, 0x15, fins_refresh_volt, FRFACT(200, 20) },
157 	{ "Vcc 1.5V", SENSOR_VOLTS_DC, 0, 0x16, fins_refresh_volt, FRFACT_NONE },
158 	{ "VCore", SENSOR_VOLTS_DC, 0, 0x17, fins_refresh_volt, FRFACT_NONE },
159 	{ "Vsb", SENSOR_VOLTS_DC, 0, 0x18, fins_refresh_volt, FRFACT(200, 47) },
160 	{ "Vsbint", SENSOR_VOLTS_DC, 0, 0x19, fins_refresh_volt, FRFACT(200, 47) },
161 	{ "Vbat", SENSOR_VOLTS_DC, 0, 0x1A, fins_refresh_volt, FRFACT(200, 47) },
162 
163 	/* Temperature */
164 	{ "Temp1", SENSOR_TEMP, 0x01, 0x1B, fins_refresh_temp },
165 	{ "Temp2", SENSOR_TEMP, 0x02, 0x1C, fins_refresh_temp },
166 	{ "Temp3", SENSOR_TEMP, 0x04, 0x1D, fins_refresh_temp },
167 
168 	/* Fans */
169 	{ "Fan1", SENSOR_FANRPM, 0, 0x20, fins_refresh_fanrpm },
170 	{ "Fan2", SENSOR_FANRPM, 0, 0x22, fins_refresh_fanrpm },
171 	{ "Fan3", SENSOR_FANRPM, 0, 0x24, fins_refresh_fanrpm },
172 
173 	{ NULL }
174 };
175 
176 
177 int
178 fins_isa_match(struct device *parent, void *match, void *aux)
179 {
180 	bus_space_tag_t iot;
181 	bus_addr_t iobase;
182 	bus_space_handle_t ioh;
183 	struct isa_attach_args *ia = aux;
184 	u_int val;
185 
186 	iot = ia->ia_iot;
187 	iobase = ia->ipa_io[0].base;
188 
189 	if (bus_space_map(iot, iobase, 2, 0, &ioh))
190 		return (0);
191 
192 	/* Fintek uses magic cookie locks to distinguish their chips */
193 	bus_space_write_1(iot, ioh, 0, FINS_UNLOCK);
194 	bus_space_write_1(iot, ioh, 0, FINS_UNLOCK);
195 	bus_space_write_1(iot, ioh, 0, FINS_FUNC_SEL);
196 	bus_space_write_1(iot, ioh, 1, 0);	/* IDs appear only in space 0 */
197 	bus_space_write_1(iot, ioh, 0, FINS_MANUF);
198 	val = bus_space_read_1(iot, ioh, 1) << 8;
199 	bus_space_write_1(iot, ioh, 0, FINS_MANUF + 1);
200 	val |= bus_space_read_1(iot, ioh, 1);
201 	if (val != FINTEK_ID) {
202 		bus_space_write_1(iot, ioh, 0, FINS_LOCK);
203 		goto notfound;
204 	}
205 	bus_space_write_1(iot, ioh, 0, FINS_CHIP);
206 	val = bus_space_read_1(iot, ioh, 1) << 8;
207 	bus_space_write_1(iot, ioh, 0, FINS_CHIP + 1);
208 	val |= bus_space_read_1(iot, ioh, 1);
209 	/* If we cared which Fintek chip this was we would save the chip ID here */
210 	if (val != FINS_ID) {
211 		bus_space_write_1(iot, ioh, 0, FINS_LOCK);
212 		goto notfound;
213 	}
214 	/* select sensor function of the chip */
215 	bus_space_write_1(iot, ioh, FINS_ADDR, FINS_FUNC_SEL);
216 	bus_space_write_1(iot, ioh, FINS_DATA, FINS_FUNC_SENSORS);
217 	/* read I/O address assigned by BIOS to this function */
218 	bus_space_write_1(iot, ioh, FINS_ADDR, FINS_SENSADDR);
219 	val = bus_space_read_1(iot, ioh, FINS_DATA) << 8;
220 	bus_space_write_1(iot, ioh, FINS_ADDR, FINS_SENSADDR + 1);
221 	val |= bus_space_read_1(iot, ioh, FINS_DATA);
222 	bus_space_write_1(iot, ioh, 0, FINS_LOCK);
223 	ia->ipa_io[1].length = 2;
224 	ia->ipa_io[1].base = val;
225 
226 	bus_space_unmap(iot, ioh, 2);
227 	ia->ipa_nio = 2;
228 	ia->ipa_io[0].length = 2;
229 	ia->ipa_nmem = 0;
230 	ia->ipa_nirq = 0;
231 	ia->ipa_ndrq = 0;
232 	return (1);
233 
234  notfound:
235 	bus_space_unmap(iot, ioh, 2);
236 	return (0);
237 }
238 
239 void
240 fins_isa_attach(struct device *parent, struct device *self, void *aux)
241 {
242 	struct fins_isa_softc *sc = (struct fins_isa_softc *)self;
243 	struct isa_attach_args *ia = aux;
244 	bus_addr_t iobase;
245 
246 	sc->sc_iot = ia->ia_iot;
247 	iobase = ia->ipa_io[1].base;
248 	sc->sc_finssc.fins_writereg = fins_isa_writereg;
249 	sc->sc_finssc.fins_readreg = fins_isa_readreg;
250 	if (bus_space_map(sc->sc_iot, iobase, 2, 0, &sc->sc_ioh)) {
251 		printf(": can't map i/o space\n");
252 		return;
253 	}
254 	fins_attach(&sc->sc_finssc);
255 }
256 
257 u_int8_t
258 fins_isa_readreg(struct fins_softc *lmsc, int reg)
259 {
260 	struct fins_isa_softc *sc = (struct fins_isa_softc *)lmsc;
261 
262 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, FINS_ADDR, reg);
263 	return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, FINS_DATA));
264 }
265 
266 void
267 fins_isa_writereg(struct fins_softc *lmsc, int reg, int val)
268 {
269 	struct fins_isa_softc *sc = (struct fins_isa_softc *)lmsc;
270 
271 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, FINS_ADDR, reg);
272 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, FINS_DATA, val);
273 }
274 
275 void
276 fins_attach(struct fins_softc *sc)
277 {
278 	u_int i;
279 
280 	fins_setup_sensors(sc, fins_sensors);
281 	sc->fins_sensortask = sensor_task_register(sc, fins_refresh, 5);
282 	if (sc->fins_sensortask == NULL) {
283 		printf(": unable to register update task\n");
284 		return;
285 	}
286 
287 	printf("\n");
288 	/* Add sensors */
289 	for (i = 0; i < sc->fins_numsensors; ++i)
290 		sensor_attach(&sc->fins_sensordev, &sc->fins_ksensors[i]);
291 	sensordev_install(&sc->fins_sensordev);
292 }
293 
294 int
295 fins_detach(struct fins_softc *sc)
296 {
297 	int i;
298 
299 	/* Remove sensors */
300 	sensordev_deinstall(&sc->fins_sensordev);
301 	for (i = 0; i < sc->fins_numsensors; i++)
302 		sensor_detach(&sc->fins_sensordev, &sc->fins_ksensors[i]);
303 
304 	if (sc->fins_sensortask != NULL)
305 		sensor_task_unregister(sc->fins_sensortask);
306 
307 	return 0;
308 }
309 
310 
311 void
312 fins_setup_sensors(struct fins_softc *sc, struct fins_sensor *sensors)
313 {
314 	int i;
315 	struct ksensor *ksensor = sc->fins_ksensors;
316 
317 	strlcpy(sc->fins_sensordev.xname, sc->sc_dev.dv_xname,
318 	    sizeof(sc->fins_sensordev.xname));
319 
320 	for (i = 0; sensors[i].fs_desc; i++) {
321 		ksensor[i].type = sensors[i].fs_type;
322 		strlcpy(ksensor[i].desc, sensors[i].fs_desc,
323 			sizeof(ksensor[i].desc));
324 	}
325 	sc->fins_numsensors = i;
326 	sc->fins_sensors = sensors;
327 	sc->fins_tempsel = sc->fins_readreg(sc, FINS_TMODE);
328 }
329 
330 void
331 fins_refresh(void *arg)
332 {
333 	struct fins_softc *sc = arg;
334 	int i;
335 
336 	for (i = 0; i < sc->fins_numsensors; i++)
337 		sc->fins_sensors[i].fs_refresh(sc, i);
338 }
339 
340 void
341 fins_refresh_volt(struct fins_softc *sc, int n)
342 {
343 	struct ksensor *sensor = &sc->fins_ksensors[n];
344 	struct fins_sensor *fs = &sc->fins_sensors[n];
345 	int data;
346 
347 	data = sc->fins_readreg(sc, fs->fs_reg);
348 	if (data == 0xff || data == 0) {
349 		sensor->flags |= SENSOR_FINVALID;
350 		sensor->value = 0;
351 	} else {
352 		sensor->flags &= ~SENSOR_FINVALID;
353 		sensor->value = data * fs->fs_rfact;
354 	}
355 }
356 
357 /* The BIOS seems to add a fudge factor to the CPU temp of +5C */
358 void
359 fins_refresh_temp(struct fins_softc *sc, int n)
360 {
361 	struct ksensor *sensor = &sc->fins_ksensors[n];
362 	struct fins_sensor *fs = &sc->fins_sensors[n];
363 	u_int data;
364 	u_int max;
365 
366 	/*
367 	 * The data sheet says that the range of the temperature
368 	 * sensor is between 0 and 127 or 140 degrees C depending on
369 	 * what kind of sensor is used.
370 	 * A disconnected sensor seems to read over 110 or so.
371 	 */
372 	data = sc->fins_readreg(sc, fs->fs_reg) & 0xFF;
373 	max = (sc->fins_tempsel & fs->fs_aux) ? 111 : 128;
374 	if (data == 0 || data >= max) {	/* disconnected? */
375 		sensor->flags |= SENSOR_FINVALID;
376 		sensor->value = 0;
377 	} else {
378 		sensor->flags &= ~SENSOR_FINVALID;
379 		sensor->value = data * 1000000 + 273150000;
380 	}
381 }
382 
383 /* The chip holds a fudge factor for BJT sensors */
384 /* this is currently unused but might be reenabled */
385 void
386 fins_refresh_offset(struct fins_softc *sc, int n)
387 {
388 	struct ksensor *sensor = &sc->fins_ksensors[n];
389 	struct fins_sensor *fs = &sc->fins_sensors[n];
390 	u_int data;
391 
392 	sensor->flags &= ~SENSOR_FINVALID;
393 	data = sc->fins_readreg(sc, fs->fs_reg);
394 	data |= ~0 * (data & 0x40);	/* sign extend 7-bit value */
395 	sensor->value = data * 1000000 + 273150000;
396 }
397 
398 
399 /* fan speed appears to be a 12-bit number */
400 void
401 fins_refresh_fanrpm(struct fins_softc *sc, int n)
402 {
403 	struct ksensor *sensor = &sc->fins_ksensors[n];
404 	struct fins_sensor *fs = &sc->fins_sensors[n];
405 	int data;
406 
407 	data = sc->fins_readreg(sc, fs->fs_reg) << 8;
408 	data |= sc->fins_readreg(sc, fs->fs_reg + 1);
409 	if (data >= 0xfff) {
410 		sensor->value = 0;
411 		sensor->flags |= SENSOR_FINVALID;
412 	} else {
413 		sensor->value = 1500000 / data;
414 		sensor->flags &= ~SENSOR_FINVALID;
415 	}
416 }
417