1 /* $OpenBSD: w83793g.c,v 1.6 2022/04/06 18:59:28 naddy 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 const 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
wbng_match(struct device * parent,void * match,void * aux)98 wbng_match(struct device *parent, void *match, void *aux)
99 {
100 struct i2c_attach_args *ia = aux;
101
102 if (strcmp(ia->ia_name, "w83793g") == 0)
103 return 1;
104 return 0;
105 }
106
107 void
wbng_attach(struct device * parent,struct device * self,void * aux)108 wbng_attach(struct device *parent, struct device *self, void *aux)
109 {
110 struct wbng_softc *sc = (struct wbng_softc *)self;
111 struct i2c_attach_args *ia = aux;
112 int i, j;
113
114 sc->sc_tag = ia->ia_tag;
115 sc->sc_addr = ia->ia_addr;
116
117 printf(": %s", ia->ia_name);
118
119 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
120 sizeof(sc->sc_sensordev.xname));
121
122 for (i = 0; i < WB_NUM_VOLTS; i++) {
123 strlcpy(sc->sc_sensors[i].desc, wb_volt_desc[i],
124 sizeof(sc->sc_sensors[i].desc));
125 sc->sc_sensors[i].type = SENSOR_VOLTS_DC;
126 }
127
128 for (j = i + WB_NUM_TEMPS; i < j; i++)
129 sc->sc_sensors[i].type = SENSOR_TEMP;
130
131 for (j = i + WB_NUM_FANS; i < j; i++)
132 sc->sc_sensors[i].type = SENSOR_FANRPM;
133
134 for (i = 0; i < WB_NUM_VOLTS + WB_NUM_TEMPS + WB_NUM_FANS; i++)
135 sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
136
137 if (sensor_task_register(sc, wbng_refresh, 5) == NULL) {
138 printf(", unable to register update task\n");
139 return;
140 }
141
142 sensordev_install(&sc->sc_sensordev);
143 printf("\n");
144 }
145
146 void
wbng_refresh(void * arg)147 wbng_refresh(void *arg)
148 {
149 struct wbng_softc *sc = arg;
150 uint8_t bsr;
151
152 iic_acquire_bus(sc->sc_tag, 0);
153
154 bsr = wbng_readreg(sc, WB_BANKSELECT);
155 if ((bsr & 0x07) != 0x0)
156 wbng_writereg(sc, WB_BANKSELECT, bsr & 0xf8);
157
158 wbng_refresh_volts(sc);
159 wbng_refresh_temps(sc);
160 wbng_refresh_fans(sc);
161
162 if ((bsr & 0x07) != 0x0)
163 wbng_writereg(sc, WB_BANKSELECT, bsr);
164
165 iic_release_bus(sc->sc_tag, 0);
166 }
167
168 void
wbng_refresh_volts(struct wbng_softc * sc)169 wbng_refresh_volts(struct wbng_softc *sc)
170 {
171 struct ksensor *s = &sc->sc_sensors[0];
172 uint8_t vlow, data;
173
174 /* high precision voltage sensors */
175
176 vlow = wbng_readreg(sc, WB_VLOW);
177
178 data = wbng_readreg(sc, WB_VCOREA);
179 s[0].value = ((data << 3) | (((vlow & 0x03)) << 1)) * 1000;
180
181 data = wbng_readreg(sc, WB_VCOREB);
182 s[1].value = ((data << 3) | (((vlow & 0x0c) >> 2) << 1)) * 1000;
183
184 data = wbng_readreg(sc, WB_VTT);
185 s[2].value = ((data << 3) | (((vlow & 0x30) >> 4) << 1)) * 1000;
186
187 /* low precision voltage sensors */
188
189 data = wbng_readreg(sc, WB_VSENS1);
190 s[3].value = (data << 4) * 1000;
191
192 data = wbng_readreg(sc, WB_VSENS2);
193 s[4].value = (data << 4) * 1000;
194
195 data = wbng_readreg(sc, WB_3VSEN);
196 s[5].value = (data << 4) * 1000;
197
198 data = wbng_readreg(sc, WB_12VSEN);
199 s[6].value = (data << 4) * 6100; /*XXX, the factor is a guess */
200
201 data = wbng_readreg(sc, WB_5VDD);
202 s[7].value = (data << 4) * 1500 + 150000;
203
204 data = wbng_readreg(sc, WB_5VSB);
205 s[8].value = (data << 4) * 1500 + 150000;
206
207 data = wbng_readreg(sc, WB_VBAT);
208 s[9].value = (data << 4) * 1000;
209 }
210
211 void
wbng_refresh_temps(struct wbng_softc * sc)212 wbng_refresh_temps(struct wbng_softc *sc)
213 {
214 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 tdlow = wbng_readreg(sc, WB_TDLOW);
220 for (i = 0; i < WB_TD_COUNT; i++) {
221 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 if (data > 0x7f && data < 0xc9) {
227 s[i].flags |= SENSOR_FINVALID;
228 s[i].value = 0;
229 continue;
230 }
231 if (data & 0x80)
232 data -= 0x100;
233 low = (tdlow & (0x03 << (i * 2))) >> (i * 2);
234 s[i].value = data * 1000000 + low * 250000 + 273150000;
235 s[i].flags &= ~SENSOR_FINVALID;
236 }
237 s += i;
238
239 /* low precision temperature sensors */
240 for (i = 0; i < WB_TR_COUNT; i++) {
241 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 if (data > 0x7f && data < 0xc9) {
247 s[i].flags |= SENSOR_FINVALID;
248 s[i].value = 0;
249 continue;
250 }
251 if (data & 0x80)
252 data -= 0x100;
253 s[i].value = data * 1000000 + 273150000;
254 s[i].flags &= ~SENSOR_FINVALID;
255 }
256 }
257
258 void
wbng_refresh_fans(struct wbng_softc * sc)259 wbng_refresh_fans(struct wbng_softc *sc)
260 {
261 struct ksensor *s = &sc->sc_sensors[WB_NUM_VOLTS + WB_NUM_TEMPS];
262 int i;
263
264 for (i = 0; i < WB_NUM_FANS; i++) {
265 uint8_t h = wbng_readreg(sc, WB_FAN_START + i * 2);
266 uint8_t l = wbng_readreg(sc, WB_FAN_START + i * 2 + 1);
267 uint16_t b = h << 8 | l;
268
269 if (b >= 0x0fff || b == 0x0f00 || b == 0x0000) {
270 s[i].flags |= SENSOR_FINVALID;
271 s[i].value = 0;
272 } else {
273 s[i].flags &= ~SENSOR_FINVALID;
274 s[i].value = 1350000 / b;
275 }
276 }
277 }
278
279 uint8_t
wbng_readreg(struct wbng_softc * sc,uint8_t reg)280 wbng_readreg(struct wbng_softc *sc, uint8_t reg)
281 {
282 uint8_t data;
283
284 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
285 sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0);
286
287 return data;
288 }
289
290 void
wbng_writereg(struct wbng_softc * sc,uint8_t reg,uint8_t data)291 wbng_writereg(struct wbng_softc *sc, uint8_t reg, uint8_t data)
292 {
293 iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
294 sc->sc_addr, ®, sizeof reg, &data, sizeof data, 0);
295 }
296