Line data Source code
1 : /* $OpenBSD: w83793g.c,v 1.5 2009/01/26 15:07:49 kettenis Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2007 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : #include <sys/param.h>
20 : #include <sys/systm.h>
21 : #include <sys/device.h>
22 : #include <sys/sensors.h>
23 :
24 : #include <dev/i2c/i2cvar.h>
25 :
26 : /* Winbond W83793G Hardware Monitor */
27 :
28 : #define WB_BANKSELECT 0x00
29 :
30 : /* Voltage */
31 : #define WB_NUM_VOLTS 10
32 :
33 : static const char *wb_volt_desc[WB_NUM_VOLTS] = {
34 : "VCore", "VCore", "VTT",
35 : "", "", "3.3V", "12V", "5VDD", "5VSB", "VBat"
36 : };
37 :
38 : #define WB_VCOREA 0x10
39 : #define WB_VCOREB 0x11
40 : #define WB_VTT 0x12
41 : #define WB_VLOW 0x1b
42 :
43 : #define WB_VSENS1 0x14
44 : #define WB_VSENS2 0x15
45 : #define WB_3VSEN 0x16
46 : #define WB_12VSEN 0x17
47 : #define WB_5VDD 0x18
48 : #define WB_5VSB 0x19
49 : #define WB_VBAT 0x1a
50 :
51 : /* Temperature */
52 : #define WB_NUM_TEMPS 6
53 :
54 : #define WB_TD_COUNT 4
55 : #define WB_TD_START 0x1c
56 : #define WB_TDLOW 0x22
57 :
58 : #define WB_TR_COUNT 2
59 : #define WB_TR_START 0x20
60 :
61 : /* Fan */
62 : #define WB_NUM_FANS 12
63 : #define WB_FAN_START 0x23
64 :
65 :
66 : struct wbng_softc {
67 : struct device sc_dev;
68 : i2c_tag_t sc_tag;
69 : i2c_addr_t sc_addr;
70 :
71 : struct ksensor sc_sensors[WB_NUM_VOLTS + WB_NUM_TEMPS + WB_NUM_FANS];
72 : struct ksensordev sc_sensordev;
73 : };
74 :
75 :
76 : int wbng_match(struct device *, void *, void *);
77 : void wbng_attach(struct device *, struct device *, void *);
78 : void wbng_refresh(void *);
79 :
80 : void wbng_refresh_volts(struct wbng_softc *);
81 : void wbng_refresh_temps(struct wbng_softc *);
82 : void wbng_refresh_fans(struct wbng_softc *);
83 :
84 : uint8_t wbng_readreg(struct wbng_softc *, uint8_t);
85 : void wbng_writereg(struct wbng_softc *, uint8_t, uint8_t);
86 :
87 :
88 : struct cfattach wbng_ca = {
89 : sizeof(struct wbng_softc), wbng_match, wbng_attach
90 : };
91 :
92 : struct cfdriver wbng_cd = {
93 : NULL, "wbng", DV_DULL
94 : };
95 :
96 :
97 : int
98 0 : wbng_match(struct device *parent, void *match, void *aux)
99 : {
100 0 : struct i2c_attach_args *ia = aux;
101 :
102 0 : if (strcmp(ia->ia_name, "w83793g") == 0)
103 0 : return 1;
104 0 : return 0;
105 0 : }
106 :
107 : void
108 0 : wbng_attach(struct device *parent, struct device *self, void *aux)
109 : {
110 0 : struct wbng_softc *sc = (struct wbng_softc *)self;
111 0 : struct i2c_attach_args *ia = aux;
112 : int i, j;
113 :
114 0 : sc->sc_tag = ia->ia_tag;
115 0 : sc->sc_addr = ia->ia_addr;
116 :
117 0 : printf(": %s", ia->ia_name);
118 :
119 0 : strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
120 : sizeof(sc->sc_sensordev.xname));
121 :
122 0 : for (i = 0; i < WB_NUM_VOLTS; i++) {
123 0 : strlcpy(sc->sc_sensors[i].desc, wb_volt_desc[i],
124 : sizeof(sc->sc_sensors[i].desc));
125 0 : sc->sc_sensors[i].type = SENSOR_VOLTS_DC;
126 : }
127 :
128 0 : for (j = i + WB_NUM_TEMPS; i < j; i++)
129 0 : sc->sc_sensors[i].type = SENSOR_TEMP;
130 :
131 0 : for (j = i + WB_NUM_FANS; i < j; i++)
132 0 : sc->sc_sensors[i].type = SENSOR_FANRPM;
133 :
134 0 : for (i = 0; i < WB_NUM_VOLTS + WB_NUM_TEMPS + WB_NUM_FANS; i++)
135 0 : sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
136 :
137 0 : if (sensor_task_register(sc, wbng_refresh, 5) == NULL) {
138 0 : printf(", unable to register update task\n");
139 0 : return;
140 : }
141 :
142 0 : sensordev_install(&sc->sc_sensordev);
143 0 : printf("\n");
144 0 : }
145 :
146 : void
147 0 : wbng_refresh(void *arg)
148 : {
149 0 : struct wbng_softc *sc = arg;
150 : uint8_t bsr;
151 :
152 0 : iic_acquire_bus(sc->sc_tag, 0);
153 :
154 0 : bsr = wbng_readreg(sc, WB_BANKSELECT);
155 0 : if ((bsr & 0x07) != 0x0)
156 0 : wbng_writereg(sc, WB_BANKSELECT, bsr & 0xf8);
157 :
158 0 : wbng_refresh_volts(sc);
159 0 : wbng_refresh_temps(sc);
160 0 : wbng_refresh_fans(sc);
161 :
162 0 : if ((bsr & 0x07) != 0x0)
163 0 : wbng_writereg(sc, WB_BANKSELECT, bsr);
164 :
165 0 : iic_release_bus(sc->sc_tag, 0);
166 0 : }
167 :
168 : void
169 0 : wbng_refresh_volts(struct wbng_softc *sc)
170 : {
171 0 : struct ksensor *s = &sc->sc_sensors[0];
172 : uint8_t vlow, data;
173 :
174 : /* high precision voltage sensors */
175 :
176 0 : vlow = wbng_readreg(sc, WB_VLOW);
177 :
178 0 : data = wbng_readreg(sc, WB_VCOREA);
179 0 : s[0].value = ((data << 3) | (((vlow & 0x03)) << 1)) * 1000;
180 :
181 0 : data = wbng_readreg(sc, WB_VCOREB);
182 0 : s[1].value = ((data << 3) | (((vlow & 0x0c) >> 2) << 1)) * 1000;
183 :
184 0 : data = wbng_readreg(sc, WB_VTT);
185 0 : s[2].value = ((data << 3) | (((vlow & 0x30) >> 4) << 1)) * 1000;
186 :
187 : /* low precision voltage sensors */
188 :
189 0 : data = wbng_readreg(sc, WB_VSENS1);
190 0 : s[3].value = (data << 4) * 1000;
191 :
192 0 : data = wbng_readreg(sc, WB_VSENS2);
193 0 : s[4].value = (data << 4) * 1000;
194 :
195 0 : data = wbng_readreg(sc, WB_3VSEN);
196 0 : s[5].value = (data << 4) * 1000;
197 :
198 0 : data = wbng_readreg(sc, WB_12VSEN);
199 0 : s[6].value = (data << 4) * 6100; /*XXX, the factor is a guess */
200 :
201 0 : data = wbng_readreg(sc, WB_5VDD);
202 0 : s[7].value = (data << 4) * 1500 + 150000;
203 :
204 0 : data = wbng_readreg(sc, WB_5VSB);
205 0 : s[8].value = (data << 4) * 1500 + 150000;
206 :
207 0 : data = wbng_readreg(sc, WB_VBAT);
208 0 : s[9].value = (data << 4) * 1000;
209 0 : }
210 :
211 : void
212 0 : wbng_refresh_temps(struct wbng_softc *sc)
213 : {
214 0 : struct ksensor *s = &sc->sc_sensors[WB_NUM_VOLTS];
215 : int data, i;
216 : uint8_t tdlow, low;
217 :
218 : /* high precision temperature sensors */
219 0 : tdlow = wbng_readreg(sc, WB_TDLOW);
220 0 : for (i = 0; i < WB_TD_COUNT; i++) {
221 0 : data = wbng_readreg(sc, WB_TD_START + i);
222 : /*
223 : * XXX: datasheet says nothing about acceptable values,
224 : * let's consider only values between -55 degC and +125 degC.
225 : */
226 0 : if (data > 0x7f && data < 0xc9) {
227 0 : s[i].flags |= SENSOR_FINVALID;
228 0 : s[i].value = 0;
229 0 : continue;
230 : }
231 0 : if (data & 0x80)
232 0 : data -= 0x100;
233 0 : low = (tdlow & (0x03 << (i * 2))) >> (i * 2);
234 0 : s[i].value = data * 1000000 + low * 250000 + 273150000;
235 0 : s[i].flags &= ~SENSOR_FINVALID;
236 0 : }
237 0 : s += i;
238 :
239 : /* low precision temperature sensors */
240 0 : for (i = 0; i < WB_TR_COUNT; i++) {
241 0 : data = wbng_readreg(sc, WB_TR_START + i);
242 : /*
243 : * XXX: datasheet says nothing about acceptable values,
244 : * let's consider only values between -55 degC and +125 degC.
245 : */
246 0 : if (data > 0x7f && data < 0xc9) {
247 0 : s[i].flags |= SENSOR_FINVALID;
248 0 : s[i].value = 0;
249 0 : continue;
250 : }
251 0 : if (data & 0x80)
252 0 : data -= 0x100;
253 0 : s[i].value = data * 1000000 + 273150000;
254 0 : s[i].flags &= ~SENSOR_FINVALID;
255 0 : }
256 0 : }
257 :
258 : void
259 0 : wbng_refresh_fans(struct wbng_softc *sc)
260 : {
261 0 : struct ksensor *s = &sc->sc_sensors[WB_NUM_VOLTS + WB_NUM_TEMPS];
262 : int i;
263 :
264 0 : for (i = 0; i < WB_NUM_FANS; i++) {
265 0 : uint8_t h = wbng_readreg(sc, WB_FAN_START + i * 2);
266 0 : uint8_t l = wbng_readreg(sc, WB_FAN_START + i * 2 + 1);
267 0 : uint16_t b = h << 8 | l;
268 :
269 0 : if (b >= 0x0fff || b == 0x0f00 || b == 0x0000) {
270 0 : s[i].flags |= SENSOR_FINVALID;
271 0 : s[i].value = 0;
272 0 : } else {
273 0 : s[i].flags &= ~SENSOR_FINVALID;
274 0 : s[i].value = 1350000 / b;
275 : }
276 : }
277 0 : }
278 :
279 : uint8_t
280 0 : wbng_readreg(struct wbng_softc *sc, uint8_t reg)
281 : {
282 0 : uint8_t data;
283 :
284 0 : iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
285 0 : sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0);
286 :
287 0 : return data;
288 0 : }
289 :
290 : void
291 0 : wbng_writereg(struct wbng_softc *sc, uint8_t reg, uint8_t data)
292 : {
293 0 : iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
294 0 : sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0);
295 0 : }
|