xref: /openbsd/sys/dev/i2c/w83793g.c (revision 471aeecf)
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, &reg, 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, &reg, sizeof reg, &data, sizeof data, 0);
295 }
296