xref: /dragonfly/sys/dev/powermng/it/it.c (revision 6b5c5d0d)
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  * $DragonFly: src/sys/dev/powermng/it/it.c,v 1.1 2007/10/02 13:37:38 hasso Exp $
27  */
28 
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/bus.h>
33 #include <sys/module.h>
34 #include <sys/rman.h>
35 
36 #include <bus/isa/isavar.h>
37 #include <sys/systm.h>
38 
39 #include <sys/sensors.h>
40 
41 #include "itvar.h"
42 
43 #if defined(ITDEBUG)
44 #define DPRINTF(x)		do { printf x; } while (0)
45 #else
46 #define DPRINTF(x)
47 #endif
48 
49 /*
50  * IT87-compatible chips can typically measure voltages up to 4.096 V.
51  * To measure higher voltages the input is attenuated with (external)
52  * resistors.  Negative voltages are measured using a reference
53  * voltage.  So we have to convert the sensor values back to real
54  * voltages by applying the appropriate resistor factor.
55  */
56 #define RFACT_NONE	10000
57 #define RFACT(x, y)	(RFACT_NONE * ((x) + (y)) / (y))
58 
59 int it_probe(struct device *);
60 int it_attach(struct device *);
61 int it_detach(struct device *);
62 u_int8_t it_readreg(struct it_softc *, int);
63 void it_writereg(struct it_softc *, int, int);
64 void it_setup_volt(struct it_softc *, int, int);
65 void it_setup_temp(struct it_softc *, int, int);
66 void it_setup_fan(struct it_softc *, int, int);
67 
68 void it_generic_stemp(struct it_softc *, struct ksensor *);
69 void it_generic_svolt(struct it_softc *, struct ksensor *);
70 void it_generic_fanrpm(struct it_softc *, struct ksensor *);
71 
72 void it_refresh_sensor_data(struct it_softc *);
73 void it_refresh(void *);
74 
75 extern struct cfdriver it_cd;
76 
77 static device_method_t it_methods[] = {
78 	/* Methods from the device interface */
79 	DEVMETHOD(device_probe,         it_probe),
80 	DEVMETHOD(device_attach,        it_attach),
81 	DEVMETHOD(device_detach,        it_detach),
82 
83 	/* Terminate method list */
84 	{ 0, 0 }
85 };
86 
87 static driver_t it_driver = {
88 	"it",
89 	it_methods,
90 	sizeof (struct it_softc)
91 };
92 
93 static devclass_t it_devclass;
94 
95 DRIVER_MODULE(it, isa, it_driver, it_devclass, NULL, NULL);
96 
97 
98 const int it_vrfact[] = {
99 	RFACT_NONE,
100 	RFACT_NONE,
101 	RFACT_NONE,
102 	RFACT(68, 100),
103 	RFACT(30, 10),
104 	RFACT(21, 10),
105 	RFACT(83, 20),
106 	RFACT(68, 100),
107 	RFACT_NONE
108 };
109 
110 int
111 it_probe(struct device *dev)
112 {
113 	struct resource *iores;
114 	int iorid = 0;
115 	bus_space_tag_t iot;
116 	bus_space_handle_t ioh;
117 	u_int8_t cr;
118 
119 	iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &iorid,
120 	    0ul, ~0ul, 8, RF_ACTIVE);
121 	if (iores == NULL) {
122 		DPRINTF(("%s: can't map i/o space\n", __func__));
123 		return 1;
124 	}
125 	iot = rman_get_bustag(iores);
126 	ioh = rman_get_bushandle(iores);
127 
128 	/* Check Vendor ID */
129 	bus_space_write_1(iot, ioh, ITC_ADDR, ITD_CHIPID);
130 	cr = bus_space_read_1(iot, ioh, ITC_DATA);
131 	bus_release_resource(dev, SYS_RES_IOPORT, iorid, iores);
132 	DPRINTF(("it: vendor id 0x%x\n", cr));
133 	if (cr != IT_ID_IT87)
134 		return 1;
135 
136 	return 0;
137 }
138 
139 int
140 it_attach(struct device *dev)
141 {
142 	struct it_softc *sc = device_get_softc(dev);
143 	int i;
144 	u_int8_t cr;
145 
146 	sc->sc_dev = dev;
147 	sc->sc_iores = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->sc_iorid,
148 	    0ul, ~0ul, 8, RF_ACTIVE);
149 	if (sc->sc_iores == NULL) {
150 		device_printf(dev, "can't map i/o space\n");
151 		return 1;
152 	}
153 	sc->sc_iot = rman_get_bustag(sc->sc_iores);
154 	sc->sc_ioh = rman_get_bushandle(sc->sc_iores);
155 
156 	sc->numsensors = IT_NUM_SENSORS;
157 
158 	it_setup_fan(sc, 0, 3);
159 	it_setup_volt(sc, 3, 9);
160 	it_setup_temp(sc, 12, 3);
161 
162 	if (sensor_task_register(sc, it_refresh, 5)) {
163 		device_printf(sc->sc_dev, "unable to register update task\n");
164 		return 1;
165 	}
166 
167 	/* Activate monitoring */
168 	cr = it_readreg(sc, ITD_CONFIG);
169 	cr |= 0x01 | 0x08;
170 	it_writereg(sc, ITD_CONFIG, cr);
171 
172 	/* Initialize sensors */
173 	strlcpy(sc->sensordev.xname, device_get_nameunit(sc->sc_dev),
174 	    sizeof(sc->sensordev.xname));
175 	for (i = 0; i < sc->numsensors; ++i)
176 		sensor_attach(&sc->sensordev, &sc->sensors[i]);
177 	sensordev_install(&sc->sensordev);
178 
179 	return 0;
180 }
181 
182 int
183 it_detach(struct device *dev)
184 {
185 	struct it_softc *sc = device_get_softc(dev);
186 	int error;
187 
188 	sensordev_deinstall(&sc->sensordev);
189 	sensor_task_unregister(sc);
190 
191 	error = bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_iorid,
192 	    sc->sc_iores);
193 	if (error)
194 		return error;
195 
196 	return 0;
197 }
198 
199 u_int8_t
200 it_readreg(struct it_softc *sc, int reg)
201 {
202 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ITC_ADDR, reg);
203 	return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, ITC_DATA));
204 }
205 
206 void
207 it_writereg(struct it_softc *sc, int reg, int val)
208 {
209 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ITC_ADDR, reg);
210 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ITC_DATA, val);
211 }
212 
213 void
214 it_setup_volt(struct it_softc *sc, int start, int n)
215 {
216 	int i;
217 
218 	for (i = 0; i < n; ++i) {
219 		sc->sensors[start + i].type = SENSOR_VOLTS_DC;
220 	}
221 
222 	ksnprintf(sc->sensors[start + 0].desc, sizeof(sc->sensors[0].desc),
223 	    "VCORE_A");
224 	ksnprintf(sc->sensors[start + 1].desc, sizeof(sc->sensors[1].desc),
225 	    "VCORE_B");
226 	ksnprintf(sc->sensors[start + 2].desc, sizeof(sc->sensors[2].desc),
227 	    "+3.3V");
228 	ksnprintf(sc->sensors[start + 3].desc, sizeof(sc->sensors[3].desc),
229 	    "+5V");
230 	ksnprintf(sc->sensors[start + 4].desc, sizeof(sc->sensors[4].desc),
231 	    "+12V");
232 	ksnprintf(sc->sensors[start + 5].desc, sizeof(sc->sensors[5].desc),
233 	    "Unused");
234 	ksnprintf(sc->sensors[start + 6].desc, sizeof(sc->sensors[6].desc),
235 	    "-12V");
236 	ksnprintf(sc->sensors[start + 7].desc, sizeof(sc->sensors[7].desc),
237 	    "+5VSB");
238 	ksnprintf(sc->sensors[start + 8].desc, sizeof(sc->sensors[8].desc),
239 	    "VBAT");
240 }
241 
242 void
243 it_setup_temp(struct it_softc *sc, int start, int n)
244 {
245 	int i;
246 
247 	for (i = 0; i < n; ++i)
248 		sc->sensors[start + i].type = SENSOR_TEMP;
249 }
250 
251 void
252 it_setup_fan(struct it_softc *sc, int start, int n)
253 {
254 	int i;
255 
256 	for (i = 0; i < n; ++i)
257 		sc->sensors[start + i].type = SENSOR_FANRPM;
258 }
259 
260 void
261 it_generic_stemp(struct it_softc *sc, struct ksensor *sensors)
262 {
263 	int i, sdata;
264 
265 	for (i = 0; i < 3; i++) {
266 		sdata = it_readreg(sc, ITD_SENSORTEMPBASE + i);
267 		/* Convert temperature to Fahrenheit degres */
268 		sensors[i].value = sdata * 1000000 + 273150000;
269 	}
270 }
271 
272 void
273 it_generic_svolt(struct it_softc *sc, struct ksensor *sensors)
274 {
275 	int i, sdata;
276 
277 	for (i = 0; i < 9; i++) {
278 		sdata = it_readreg(sc, ITD_SENSORVOLTBASE + i);
279 		DPRINTF(("sdata[volt%d] 0x%x\n", i, sdata));
280 		/* voltage returned as (mV >> 4) */
281 		sensors[i].value = (sdata << 4);
282 		/* these two values are negative and formula is different */
283 		if (i == 5 || i == 6)
284 			sensors[i].value = ((sdata << 4) - IT_VREF);
285 		/* rfact is (factor * 10^4) */
286 		sensors[i].value *= it_vrfact[i];
287 		/* division by 10 gets us back to uVDC */
288 		sensors[i].value /= 10;
289 		if (i == 5 || i == 6)
290 			sensors[i].value += IT_VREF * 1000;
291 	}
292 }
293 
294 void
295 it_generic_fanrpm(struct it_softc *sc, struct ksensor *sensors)
296 {
297 	int i, sdata, divisor, odivisor, ndivisor;
298 
299 	odivisor = ndivisor = divisor = it_readreg(sc, ITD_FAN);
300 	for (i = 0; i < 3; i++, divisor >>= 3) {
301 		sensors[i].flags &= ~SENSOR_FINVALID;
302 		if ((sdata = it_readreg(sc, ITD_SENSORFANBASE + i)) == 0xff) {
303 			sensors[i].flags |= SENSOR_FINVALID;
304 			if (i == 2)
305 				ndivisor ^= 0x40;
306 			else {
307 				ndivisor &= ~(7 << (i * 3));
308 				ndivisor |= ((divisor + 1) & 7) << (i * 3);
309 			}
310 		} else if (sdata == 0) {
311 			sensors[i].value = 0;
312 		} else {
313 			if (i == 2)
314 				divisor = divisor & 1 ? 3 : 1;
315 			sensors[i].value = 1350000 / (sdata << (divisor & 7));
316 		}
317 	}
318 	if (ndivisor != odivisor)
319 		it_writereg(sc, ITD_FAN, ndivisor);
320 }
321 
322 /*
323  * pre:  last read occurred >= 1.5 seconds ago
324  * post: sensors[] current data are the latest from the chip
325  */
326 void
327 it_refresh_sensor_data(struct it_softc *sc)
328 {
329 	/* Refresh our stored data for every sensor */
330 	it_generic_stemp(sc, &sc->sensors[12]);
331 	it_generic_svolt(sc, &sc->sensors[3]);
332 	it_generic_fanrpm(sc, &sc->sensors[0]);
333 }
334 
335 void
336 it_refresh(void *arg)
337 {
338 	struct it_softc *sc = (struct it_softc *)arg;
339 
340 	it_refresh_sensor_data(sc);
341 }
342