1 /* $OpenBSD: fins.c,v 1.6 2022/04/08 15:02:28 naddy 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 const 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 int fins_activate(struct device *, int);
128
129 void fins_unlock(bus_space_tag_t, bus_space_handle_t);
130 void fins_lock(bus_space_tag_t, bus_space_handle_t);
131
132 u_int8_t fins_read(bus_space_tag_t, bus_space_handle_t, int);
133 u_int16_t fins_read_2(bus_space_tag_t, bus_space_handle_t, int);
134 void fins_write(bus_space_tag_t, bus_space_handle_t, int, u_int8_t);
135
136 static __inline u_int8_t fins_read_sens(struct fins_softc *, int);
137 static __inline u_int16_t fins_read_sens_2(struct fins_softc *, int);
138
139 static __inline u_int8_t fins_read_wdog(struct fins_softc *, int);
140 static __inline void fins_write_wdog(struct fins_softc *, int, u_int8_t);
141
142 void fins_setup_sensors(struct fins_softc *, const struct fins_sensor *);
143 void fins_refresh(void *);
144
145 void fins_get_rpm(struct fins_softc *, int);
146 void fins_get_temp(struct fins_softc *, int);
147 void fins_get_volt(struct fins_softc *, int);
148
149 int fins_wdog_cb(void *, int);
150
151 const struct cfattach fins_ca = {
152 sizeof(struct fins_softc),
153 fins_match,
154 fins_attach,
155 NULL,
156 fins_activate
157 };
158
159 struct cfdriver fins_cd = {
160 NULL, "fins", DV_DULL
161 };
162
163 const struct fins_sensor fins_71805_sensors[] = {
164 { "+3.3V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100), 0x10 },
165 { "Vtt", fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE, 0x11 },
166 { "Vram", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100), 0x12 },
167 { "Vchips", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(47, 100), 0x13 },
168 { "+5V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47), 0x14 },
169 { "+12V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 20), 0x15 },
170 { "+1.5V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE, 0x16 },
171 { "Vcore", fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE, 0x17 },
172 { "Vsb", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47), 0x18 },
173 { "Vsbint", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47), 0x19 },
174 { "Vbat", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47), 0x1a },
175
176 { NULL, fins_get_temp, SENSOR_TEMP, 0x01, 0x1b },
177 { NULL, fins_get_temp, SENSOR_TEMP, 0x02, 0x1c },
178 { NULL, fins_get_temp, SENSOR_TEMP, 0x04, 0x1d },
179
180 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0x20 },
181 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0x22 },
182 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0x24 },
183
184 { NULL }
185 };
186
187 const struct fins_sensor fins_71882_sensors[] = {
188 { "+3.3V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100), 0x20 },
189 { "Vcore", fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE, 0x21 },
190 { "Vram", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100), 0x22 },
191 { "Vchips", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(47, 100), 0x23 },
192 { "+5V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 47), 0x24 },
193 { "+12V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(200, 20), 0x25 },
194 { "+1.5V", fins_get_volt, SENSOR_VOLTS_DC, FRFACT_NONE, 0x26 },
195 { "Vsb", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100), 0x27 },
196 { "Vbat", fins_get_volt, SENSOR_VOLTS_DC, FRFACT(100, 100), 0x28 },
197
198 { NULL, fins_get_temp, SENSOR_TEMP, 0x02, 0x72 },
199 { NULL, fins_get_temp, SENSOR_TEMP, 0x04, 0x74 },
200 { NULL, fins_get_temp, SENSOR_TEMP, 0x08, 0x76 },
201
202 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0xa0 },
203 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0xb0 },
204 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0xc0 },
205 { NULL, fins_get_rpm, SENSOR_FANRPM, 0, 0xd0 },
206
207 { NULL }
208 };
209
210 int
fins_match(struct device * parent,void * match,void * aux)211 fins_match(struct device *parent, void *match, void *aux)
212 {
213 struct isa_attach_args *ia = aux;
214 bus_space_handle_t ioh;
215 bus_space_tag_t iot;
216 int ret = 0;
217 u_int16_t id;
218
219 iot = ia->ia_iot;
220 if (bus_space_map(iot, ia->ipa_io[0].base, 2, 0, &ioh))
221 return (0);
222
223 /* Fintek uses magic cookie locks to distinguish their chips */
224 fins_unlock(iot, ioh);
225
226 fins_write(iot, ioh, FINS_FUNC_SEL, 0); /* IDs appear only in space 0 */
227 if (fins_read_2(iot, ioh, FINS_MANUF) != FINTEK_ID)
228 goto match_done;
229 id = fins_read_2(iot, ioh, FINS_CHIP);
230 switch(id) {
231 case FINS_71882:
232 case FINS_71862:
233 ia->ipa_nio = 3;
234 fins_write(iot, ioh, FINS_FUNC_SEL, FINS_FUNC_WATCHDOG);
235 ia->ipa_io[2].base = fins_read_2(iot, ioh, FINS_BASEADDR);
236 ia->ipa_io[2].length = 8;
237 fins_write(iot, ioh, FINS_FUNC_SEL, FINS_FUNC_SENSORS);
238 ia->ipa_io[1].base = fins_read_2(iot, ioh, FINS_BASEADDR);
239 ia->ipa_io[1].base += 5;
240 break;
241 case FINS_71806:
242 case FINS_71805:
243 ia->ipa_nio = 2;
244 fins_write(iot, ioh, FINS_FUNC_SEL, FINS_FUNC_SENSORS);
245 ia->ipa_io[1].base = fins_read_2(iot, ioh, FINS_BASEADDR);
246 break;
247 default:
248 goto match_done;
249 }
250 ia->ipa_io[0].length = ia->ipa_io[1].length = 2;
251 ia->ipa_nmem = ia->ipa_nirq = ia->ipa_ndrq = 0;
252 ia->ia_aux = (void *)(u_long)id;
253 ret = 1;
254 match_done:
255 fins_lock(iot, ioh);
256 return (ret);
257 }
258
259 void
fins_attach(struct device * parent,struct device * self,void * aux)260 fins_attach(struct device *parent, struct device *self, void *aux)
261 {
262 struct fins_softc *sc = (struct fins_softc *)self;
263 struct isa_attach_args *ia = aux;
264 bus_addr_t iobase;
265 u_int32_t iosize;
266 u_int i;
267
268 sc->sc_iot = ia->ia_iot;
269 sc->fins_chipid = (u_int16_t)(u_long)ia->ia_aux;
270 iobase = ia->ipa_io[1].base;
271 iosize = ia->ipa_io[1].length;
272 if (bus_space_map(sc->sc_iot, iobase, iosize, 0, &sc->sc_ioh_sens)) {
273 printf(": can't map sensor i/o space\n");
274 return;
275 }
276 switch(sc->fins_chipid) {
277 case FINS_71882:
278 case FINS_71862:
279 fins_setup_sensors(sc, fins_71882_sensors);
280 break;
281 case FINS_71806:
282 case FINS_71805:
283 fins_setup_sensors(sc, fins_71805_sensors);
284 break;
285 }
286 sc->fins_sensortask = sensor_task_register(sc, fins_refresh, 5);
287 if (sc->fins_sensortask == NULL) {
288 printf(": can't register update task\n");
289 return;
290 }
291 for (i = 0; sc->fins_sensors[i].fs_refresh != NULL; ++i)
292 sensor_attach(&sc->fins_sensordev, &sc->fins_ksensors[i]);
293 sensordev_install(&sc->fins_sensordev);
294
295 if (sc->fins_chipid <= FINS_71805)
296 goto attach_done;
297 iobase = ia->ipa_io[2].base;
298 iosize = ia->ipa_io[2].length;
299 if (bus_space_map(sc->sc_iot, iobase, iosize, 0, &sc->sc_ioh_wdog)) {
300 printf(": can't map watchdog i/o space\n");
301 return;
302 }
303 sc->fins_wdog_cr = fins_read_wdog(sc, FINS_WDOG_CR1);
304 sc->fins_wdog_cr &= ~(FINS_WDOG_MINS | FINS_WDOG_EN);
305 fins_write_wdog(sc, FINS_WDOG_CR1, sc->fins_wdog_cr);
306 wdog_register(fins_wdog_cb, sc);
307 attach_done:
308 printf("\n");
309 }
310
311 int
fins_activate(struct device * self,int act)312 fins_activate(struct device *self, int act)
313 {
314 switch (act) {
315 case DVACT_POWERDOWN:
316 wdog_shutdown(self);
317 break;
318 }
319
320 return (0);
321 }
322
323 u_int8_t
fins_read(bus_space_tag_t iot,bus_space_handle_t ioh,int reg)324 fins_read(bus_space_tag_t iot, bus_space_handle_t ioh, int reg)
325 {
326 bus_space_write_1(iot, ioh, FINS_ADDR, reg);
327 return (bus_space_read_1(iot, ioh, FINS_DATA));
328 }
329
330 u_int16_t
fins_read_2(bus_space_tag_t iot,bus_space_handle_t ioh,int reg)331 fins_read_2(bus_space_tag_t iot, bus_space_handle_t ioh, int reg)
332 {
333 u_int16_t val;
334
335 bus_space_write_1(iot, ioh, FINS_ADDR, reg);
336 val = bus_space_read_1(iot, ioh, FINS_DATA) << 8;
337 bus_space_write_1(iot, ioh, FINS_ADDR, reg + 1);
338 return (val | bus_space_read_1(iot, ioh, FINS_DATA));
339 }
340
341 void
fins_write(bus_space_tag_t iot,bus_space_handle_t ioh,int reg,u_int8_t val)342 fins_write(bus_space_tag_t iot, bus_space_handle_t ioh, int reg, u_int8_t val)
343 {
344 bus_space_write_1(iot, ioh, FINS_ADDR, reg);
345 bus_space_write_1(iot, ioh, FINS_DATA, val);
346 }
347
348 static __inline u_int8_t
fins_read_sens(struct fins_softc * sc,int reg)349 fins_read_sens(struct fins_softc *sc, int reg)
350 {
351 return (fins_read(sc->sc_iot, sc->sc_ioh_sens, reg));
352 }
353
354 static __inline u_int16_t
fins_read_sens_2(struct fins_softc * sc,int reg)355 fins_read_sens_2(struct fins_softc *sc, int reg)
356 {
357 return (fins_read_2(sc->sc_iot, sc->sc_ioh_sens, reg));
358 }
359
360 static __inline u_int8_t
fins_read_wdog(struct fins_softc * sc,int reg)361 fins_read_wdog(struct fins_softc *sc, int reg)
362 {
363 return (bus_space_read_1(sc->sc_iot, sc->sc_ioh_wdog, reg));
364 }
365
366 static __inline void
fins_write_wdog(struct fins_softc * sc,int reg,u_int8_t val)367 fins_write_wdog(struct fins_softc *sc, int reg, u_int8_t val)
368 {
369 bus_space_write_1(sc->sc_iot, sc->sc_ioh_wdog, reg, val);
370 }
371
372 void
fins_unlock(bus_space_tag_t iot,bus_space_handle_t ioh)373 fins_unlock(bus_space_tag_t iot, bus_space_handle_t ioh)
374 {
375 bus_space_write_1(iot, ioh, 0, FINS_UNLOCK);
376 bus_space_write_1(iot, ioh, 0, FINS_UNLOCK);
377 }
378
379 void
fins_lock(bus_space_tag_t iot,bus_space_handle_t ioh)380 fins_lock(bus_space_tag_t iot, bus_space_handle_t ioh)
381 {
382 bus_space_write_1(iot, ioh, 0, FINS_LOCK);
383 bus_space_unmap(iot, ioh, 2);
384 }
385
386 void
fins_setup_sensors(struct fins_softc * sc,const struct fins_sensor * sensors)387 fins_setup_sensors(struct fins_softc *sc, const struct fins_sensor *sensors)
388 {
389 int i;
390
391 for (i = 0; sensors[i].fs_refresh != NULL; ++i) {
392 sc->fins_ksensors[i].type = sensors[i].fs_type;
393 if (sensors[i].fs_desc != NULL)
394 strlcpy(sc->fins_ksensors[i].desc, sensors[i].fs_desc,
395 sizeof(sc->fins_ksensors[i].desc));
396 }
397 strlcpy(sc->fins_sensordev.xname, sc->sc_dev.dv_xname,
398 sizeof(sc->fins_sensordev.xname));
399 sc->fins_sensors = sensors;
400 sc->fins_tempsel = fins_read_sens(sc, FINS_SENS_TMODE(sc));
401 }
402
403 #if 0
404 void
405 fins_get_dividers(struct fins_softc *sc)
406 {
407 int i, p, m;
408 u_int16_t r = fins_read_sens_2(sc, FINS_SENS_VDIVS);
409
410 for (i = 0; i < 6; ++i) {
411 p = (i < 4) ? i : i + 2;
412 m = (r & (0x03 << p)) >> p;
413 if (m == 3)
414 m = 4;
415 fins_71882_sensors[i + 1].fs_aux = FRFACT_NONE << m;
416 }
417 }
418 #endif
419
420 void
fins_refresh(void * arg)421 fins_refresh(void *arg)
422 {
423 struct fins_softc *sc = arg;
424 int i;
425
426 for (i = 0; sc->fins_sensors[i].fs_refresh != NULL; ++i)
427 sc->fins_sensors[i].fs_refresh(sc, i);
428 }
429
430 void
fins_get_volt(struct fins_softc * sc,int n)431 fins_get_volt(struct fins_softc *sc, int n)
432 {
433 struct ksensor *sensor = &sc->fins_ksensors[n];
434 const struct fins_sensor *fs = &sc->fins_sensors[n];
435 int data;
436
437 data = fins_read_sens(sc, fs->fs_reg);
438 if (data == 0xff || data == 0) {
439 sensor->flags |= SENSOR_FINVALID;
440 sensor->value = 0;
441 } else {
442 sensor->flags &= ~SENSOR_FINVALID;
443 sensor->value = data * fs->fs_aux;
444 }
445 }
446
447 /* The BIOS seems to add a fudge factor to the CPU temp of +5C */
448 void
fins_get_temp(struct fins_softc * sc,int n)449 fins_get_temp(struct fins_softc *sc, int n)
450 {
451 struct ksensor *sensor = &sc->fins_ksensors[n];
452 const struct fins_sensor *fs = &sc->fins_sensors[n];
453 u_int data;
454 u_int max;
455
456 /*
457 * The data sheet says that the range of the temperature
458 * sensor is between 0 and 127 or 140 degrees C depending on
459 * what kind of sensor is used.
460 * A disconnected sensor seems to read over 110 or so.
461 */
462 data = fins_read_sens(sc, fs->fs_reg);
463 max = (sc->fins_tempsel & fs->fs_aux) ? 111 : 128;
464 if (data == 0 || data >= max) { /* disconnected? */
465 sensor->flags |= SENSOR_FINVALID;
466 sensor->value = 0;
467 } else {
468 sensor->flags &= ~SENSOR_FINVALID;
469 sensor->value = data * 1000000 + 273150000;
470 }
471 }
472
473 /* The chip holds a fudge factor for BJT sensors */
474 /* this is currently unused but might be reenabled */
475 #if 0
476 void
477 fins_refresh_offset(struct fins_softc *sc, int n)
478 {
479 struct ksensor *sensor = &sc->fins_ksensors[n];
480 const struct fins_sensor *fs = &sc->fins_sensors[n];
481 u_int data;
482
483 sensor->flags &= ~SENSOR_FINVALID;
484 data = fins_read_sens(sc, fs->fs_reg);
485 data |= ~0 * (data & 0x40); /* sign extend 7-bit value */
486 sensor->value = data * 1000000 + 273150000;
487 }
488 #endif
489
490 /* fan speed appears to be a 12-bit number */
491 void
fins_get_rpm(struct fins_softc * sc,int n)492 fins_get_rpm(struct fins_softc *sc, int n)
493 {
494 struct ksensor *sensor = &sc->fins_ksensors[n];
495 const struct fins_sensor *fs = &sc->fins_sensors[n];
496 int data;
497
498 data = fins_read_sens_2(sc, fs->fs_reg);
499 if (data >= 0xfff) {
500 sensor->value = 0;
501 sensor->flags |= SENSOR_FINVALID;
502 } else {
503 sensor->value = 1500000 / data;
504 sensor->flags &= ~SENSOR_FINVALID;
505 }
506 }
507
508 int
fins_wdog_cb(void * arg,int period)509 fins_wdog_cb(void *arg, int period)
510 {
511 struct fins_softc *sc = arg;
512 u_int8_t cr0, cr1, t;
513
514 cr0 = fins_read_wdog(sc, FINS_WDOG_CR0) & ~FINS_WDOG_OUTEN;
515 fins_write_wdog(sc, FINS_WDOG_CR0, cr0);
516
517 cr1 = sc->fins_wdog_cr;
518 if (period > 0xff) {
519 cr1 |= FINS_WDOG_MINS;
520 t = (period + 59) / 60;
521 period = (int)t * 60;
522 } else if (period > 0)
523 t = period;
524 else
525 return (0);
526
527 fins_write_wdog(sc, FINS_WDOG_TIMER, t);
528 fins_write_wdog(sc, FINS_WDOG_CR0, cr0 | FINS_WDOG_OUTEN);
529 fins_write_wdog(sc, FINS_WDOG_CR1, cr1 | FINS_WDOG_EN);
530 return (period);
531 }
532