xref: /dragonfly/sys/dev/powermng/it/it.c (revision 5d302545124b16bb6e9f48d720cba81afba1adb3)
1 /*
2  * Copyright (c) 2003 Julien Bordet <zejames@greyhats.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * $OpenBSD: it.c,v 1.22 2007/03/22 16:55:31 deraadt Exp $
26  */
27 
28 #include <sys/param.h>
29 #include <sys/kernel.h>
30 #include <sys/bus.h>
31 #include <sys/module.h>
32 #include <sys/rman.h>
33 
34 #include <bus/isa/isavar.h>
35 #include <sys/systm.h>
36 
37 #include <sys/sensors.h>
38 
39 #include "itvar.h"
40 
41 #if defined(ITDEBUG)
42 #define DPRINTF(x)            do { kprintf x; } while (0)
43 #else
44 #define DPRINTF(x)
45 #endif
46 
47 /*
48  * IT87-compatible chips can typically measure voltages up to 4.096 V.
49  * To measure higher voltages the input is attenuated with (external)
50  * resistors.  Negative voltages are measured using a reference
51  * voltage.  So we have to convert the sensor values back to real
52  * voltages by applying the appropriate resistor factor.
53  */
54 #define RFACT_NONE  10000
55 #define RFACT(x, y) (RFACT_NONE * ((x) + (y)) / (y))
56 
57 int it_probe(device_t);
58 int it_attach(device_t);
59 int it_detach(device_t);
60 u_int8_t it_readreg(struct it_softc *, int);
61 void it_writereg(struct it_softc *, int, int);
62 void it_setup_volt(struct it_softc *, int, int);
63 void it_setup_temp(struct it_softc *, int, int);
64 void it_setup_fan(struct it_softc *, int, int);
65 
66 void it_generic_stemp(struct it_softc *, struct ksensor *);
67 void it_generic_svolt(struct it_softc *, struct ksensor *);
68 void it_generic_fanrpm(struct it_softc *, struct ksensor *);
69 
70 void it_refresh_sensor_data(struct it_softc *);
71 void it_refresh(void *);
72 
73 static device_method_t it_methods[] = {
74           /* Methods from the device interface */
75           DEVMETHOD(device_probe,         it_probe),
76           DEVMETHOD(device_attach,        it_attach),
77           DEVMETHOD(device_detach,        it_detach),
78 
79           /* Terminate method list */
80           DEVMETHOD_END
81 };
82 
83 static driver_t it_driver = {
84           "it",
85           it_methods,
86           sizeof (struct it_softc)
87 };
88 
89 static devclass_t it_devclass;
90 
91 DRIVER_MODULE(it, isa, it_driver, it_devclass, NULL, NULL);
92 
93 
94 const int it_vrfact[] = {
95           RFACT_NONE,
96           RFACT_NONE,
97           RFACT_NONE,
98           RFACT(68, 100),
99           RFACT(30, 10),
100           RFACT(21, 10),
101           RFACT(83, 20),
102           RFACT(68, 100),
103           RFACT_NONE
104 };
105 
106 int
it_probe(device_t dev)107 it_probe(device_t dev)
108 {
109           struct resource *iores;
110           int iorid = 0;
111           bus_space_tag_t iot;
112           bus_space_handle_t ioh;
113           u_int8_t cr;
114 
115           iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
116               0ul, ~0ul, 8, RF_ACTIVE);
117           if (iores == NULL) {
118                     DPRINTF(("%s: can't map i/o space\n", __func__));
119                     return 1;
120           }
121           iot = rman_get_bustag(iores);
122           ioh = rman_get_bushandle(iores);
123 
124           /* Check Vendor ID */
125           bus_space_write_1(iot, ioh, ITC_ADDR, ITD_CHIPID);
126           cr = bus_space_read_1(iot, ioh, ITC_DATA);
127           bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
128           DPRINTF(("it: vendor id 0x%x\n", cr));
129           if (cr != IT_ID_IT87)
130                     return 1;
131 
132           return 0;
133 }
134 
135 int
it_attach(device_t dev)136 it_attach(device_t dev)
137 {
138           struct it_softc *sc = device_get_softc(dev);
139           int i;
140           u_int8_t cr;
141 
142           sc->sc_dev = dev;
143           sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
144               0ul, ~0ul, 8, RF_ACTIVE);
145           if (sc->sc_iores == NULL) {
146                     device_printf(dev, "can't map i/o space\n");
147                     return 1;
148           }
149           sc->sc_iot = rman_get_bustag(sc->sc_iores);
150           sc->sc_ioh = rman_get_bushandle(sc->sc_iores);
151 
152           sc->numsensors = IT_NUM_SENSORS;
153 
154           it_setup_fan(sc, 0, 3);
155           it_setup_volt(sc, 3, 9);
156           it_setup_temp(sc, 12, 3);
157 
158           sensor_task_register(sc, it_refresh, 5);
159 
160           /* Activate monitoring */
161           cr = it_readreg(sc, ITD_CONFIG);
162           cr |= 0x01 | 0x08;
163           it_writereg(sc, ITD_CONFIG, cr);
164 
165           /* Initialize sensors */
166           strlcpy(sc->sensordev.xname, device_get_nameunit(sc->sc_dev),
167               sizeof(sc->sensordev.xname));
168           for (i = 0; i < sc->numsensors; ++i)
169                     sensor_attach(&sc->sensordev, &sc->sensors[i]);
170           sensordev_install(&sc->sensordev);
171 
172           return 0;
173 }
174 
175 int
it_detach(device_t dev)176 it_detach(device_t dev)
177 {
178           struct it_softc *sc = device_get_softc(dev);
179           int error;
180 
181           sensordev_deinstall(&sc->sensordev);
182           sensor_task_unregister(sc);
183 
184           error = bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid,
185               sc->sc_iores);
186           if (error)
187                     return error;
188 
189           return 0;
190 }
191 
192 u_int8_t
it_readreg(struct it_softc * sc,int reg)193 it_readreg(struct it_softc *sc, int reg)
194 {
195           bus_space_write_1(sc->sc_iot, sc->sc_ioh, ITC_ADDR, reg);
196           return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, ITC_DATA));
197 }
198 
199 void
it_writereg(struct it_softc * sc,int reg,int val)200 it_writereg(struct it_softc *sc, int reg, int val)
201 {
202           bus_space_write_1(sc->sc_iot, sc->sc_ioh, ITC_ADDR, reg);
203           bus_space_write_1(sc->sc_iot, sc->sc_ioh, ITC_DATA, val);
204 }
205 
206 void
it_setup_volt(struct it_softc * sc,int start,int n)207 it_setup_volt(struct it_softc *sc, int start, int n)
208 {
209           int i;
210 
211           for (i = 0; i < n; ++i) {
212                     sc->sensors[start + i].type = SENSOR_VOLTS_DC;
213           }
214 
215           ksnprintf(sc->sensors[start + 0].desc, sizeof(sc->sensors[0].desc),
216               "VCORE_A");
217           ksnprintf(sc->sensors[start + 1].desc, sizeof(sc->sensors[1].desc),
218               "VCORE_B");
219           ksnprintf(sc->sensors[start + 2].desc, sizeof(sc->sensors[2].desc),
220               "+3.3V");
221           ksnprintf(sc->sensors[start + 3].desc, sizeof(sc->sensors[3].desc),
222               "+5V");
223           ksnprintf(sc->sensors[start + 4].desc, sizeof(sc->sensors[4].desc),
224               "+12V");
225           ksnprintf(sc->sensors[start + 5].desc, sizeof(sc->sensors[5].desc),
226               "Unused");
227           ksnprintf(sc->sensors[start + 6].desc, sizeof(sc->sensors[6].desc),
228               "-12V");
229           ksnprintf(sc->sensors[start + 7].desc, sizeof(sc->sensors[7].desc),
230               "+5VSB");
231           ksnprintf(sc->sensors[start + 8].desc, sizeof(sc->sensors[8].desc),
232               "VBAT");
233 }
234 
235 void
it_setup_temp(struct it_softc * sc,int start,int n)236 it_setup_temp(struct it_softc *sc, int start, int n)
237 {
238           int i;
239 
240           for (i = 0; i < n; ++i)
241                     sc->sensors[start + i].type = SENSOR_TEMP;
242 }
243 
244 void
it_setup_fan(struct it_softc * sc,int start,int n)245 it_setup_fan(struct it_softc *sc, int start, int n)
246 {
247           int i;
248 
249           for (i = 0; i < n; ++i)
250                     sc->sensors[start + i].type = SENSOR_FANRPM;
251 }
252 
253 void
it_generic_stemp(struct it_softc * sc,struct ksensor * sensors)254 it_generic_stemp(struct it_softc *sc, struct ksensor *sensors)
255 {
256           int i, sdata;
257 
258           for (i = 0; i < 3; i++) {
259                     sdata = it_readreg(sc, ITD_SENSORTEMPBASE + i);
260                     /* Convert temperature to Fahrenheit degres */
261                     sensors[i].value = sdata * 1000000 + 273150000;
262           }
263 }
264 
265 void
it_generic_svolt(struct it_softc * sc,struct ksensor * sensors)266 it_generic_svolt(struct it_softc *sc, struct ksensor *sensors)
267 {
268           int i, sdata;
269 
270           for (i = 0; i < 9; i++) {
271                     sdata = it_readreg(sc, ITD_SENSORVOLTBASE + i);
272                     DPRINTF(("sdata[volt%d] 0x%x\n", i, sdata));
273                     /* voltage returned as (mV >> 4) */
274                     sensors[i].value = (sdata << 4);
275                     /* these two values are negative and formula is different */
276                     if (i == 5 || i == 6)
277                               sensors[i].value = ((sdata << 4) - IT_VREF);
278                     /* rfact is (factor * 10^4) */
279                     sensors[i].value *= it_vrfact[i];
280                     /* division by 10 gets us back to uVDC */
281                     sensors[i].value /= 10;
282                     if (i == 5 || i == 6)
283                               sensors[i].value += IT_VREF * 1000;
284           }
285 }
286 
287 void
it_generic_fanrpm(struct it_softc * sc,struct ksensor * sensors)288 it_generic_fanrpm(struct it_softc *sc, struct ksensor *sensors)
289 {
290           int i, sdata, divisor, odivisor, ndivisor;
291 
292           odivisor = ndivisor = divisor = it_readreg(sc, ITD_FAN);
293           for (i = 0; i < 3; i++, divisor >>= 3) {
294                     sensors[i].flags &= ~SENSOR_FINVALID;
295                     if ((sdata = it_readreg(sc, ITD_SENSORFANBASE + i)) == 0xff) {
296                               sensors[i].flags |= SENSOR_FINVALID;
297                               if (i == 2)
298                                         ndivisor ^= 0x40;
299                               else {
300                                         ndivisor &= ~(7 << (i * 3));
301                                         ndivisor |= ((divisor + 1) & 7) << (i * 3);
302                               }
303                     } else if (sdata == 0) {
304                               sensors[i].value = 0;
305                     } else {
306                               if (i == 2)
307                                         divisor = divisor & 1 ? 3 : 1;
308                               sensors[i].value = 1350000 / (sdata << (divisor & 7));
309                     }
310           }
311           if (ndivisor != odivisor)
312                     it_writereg(sc, ITD_FAN, ndivisor);
313 }
314 
315 /*
316  * pre:  last read occurred >= 1.5 seconds ago
317  * post: sensors[] current data are the latest from the chip
318  */
319 void
it_refresh_sensor_data(struct it_softc * sc)320 it_refresh_sensor_data(struct it_softc *sc)
321 {
322           /* Refresh our stored data for every sensor */
323           it_generic_stemp(sc, &sc->sensors[12]);
324           it_generic_svolt(sc, &sc->sensors[3]);
325           it_generic_fanrpm(sc, &sc->sensors[0]);
326 }
327 
328 void
it_refresh(void * arg)329 it_refresh(void *arg)
330 {
331           struct it_softc *sc = (struct it_softc *)arg;
332 
333           it_refresh_sensor_data(sc);
334 }
335