xref: /openbsd-src/sys/dev/i2c/w83l784r.c (revision d095f71341fedbadd002e8f3b7308751f716f065)
1 /*	$OpenBSD: w83l784r.c,v 1.14 2022/04/08 15:02:28 naddy Exp $	*/
2 
3 /*
4  * Copyright (c) 2006 Mark Kettenis
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 /* W83L784R registers */
27 #define W83L784R_VCORE		0x20
28 #define W83L784R_VBAT		0x21
29 #define W83L784R_3_3V		0x22
30 #define W83L784R_VCC		0x23
31 #define W83L784R_TEMP1		0x27
32 #define W83L784R_FAN1		0x28
33 #define W83L784R_FAN2		0x29
34 #define W83L784R_CONFIG		0x40
35 #define W83L784R_FANDIV		0x49
36 #define W83L784R_T23ADDR	0x4b
37 #define W83L784R_CHIPID		0x4e
38 
39 #define W83L784R_TEMP23		0x00
40 
41 /* W83L785R registers */
42 #define W83L785R_2_5V		0x21
43 #define W83L785R_1_5V		0x22
44 #define W83L785R_VCC		0x23
45 #define W83L785R_TEMP2		0x26
46 #define W83L785R_FANDIV		0x47
47 
48 /* Chip IDs */
49 #define WBENV_CHIPID_W83L784R		0x50
50 #define WBENV_CHIPID_W83L785R		0x60
51 #define WBENV_CHIPID_W83L785TS_L	0x70
52 
53 #define WBENV_MAX_SENSORS  9
54 
55 /*
56  * The W83L784R/W83L785R can measure voltages up to 4.096/2.048 V.
57  * To measure higher voltages the input is attenuated with (external)
58  * resistors.  So we have to convert the sensor values back to real
59  * voltages by applying the appropriate resistor factor.
60  */
61 #define RFACT_NONE	10000
62 #define RFACT(x, y)	(RFACT_NONE * ((x) + (y)) / (y))
63 
64 struct wbenv_softc;
65 
66 struct wbenv_sensor {
67 	char *desc;
68 	enum sensor_type type;
69 	u_int8_t reg;
70 	void (*refresh)(struct wbenv_softc *, int);
71 	int rfact;
72 };
73 
74 struct wbenv_softc {
75 	struct device sc_dev;
76 
77 	i2c_tag_t sc_tag;
78 	i2c_addr_t sc_addr[3];
79 	u_int8_t sc_chip_id;
80 
81 	struct ksensor sc_sensors[WBENV_MAX_SENSORS];
82 	struct ksensordev sc_sensordev;
83 	const struct wbenv_sensor *sc_wbenv_sensors;
84 	int sc_numsensors;
85 };
86 
87 int	wbenv_match(struct device *, void *, void *);
88 void	wbenv_attach(struct device *, struct device *, void *);
89 
90 void	wbenv_setup_sensors(struct wbenv_softc *, const struct wbenv_sensor *);
91 void	wbenv_refresh(void *);
92 
93 void	w83l784r_refresh_volt(struct wbenv_softc *, int);
94 void	w83l785r_refresh_volt(struct wbenv_softc *, int);
95 void	wbenv_refresh_temp(struct wbenv_softc *, int);
96 void	w83l784r_refresh_temp(struct wbenv_softc *, int);
97 void	w83l784r_refresh_fanrpm(struct wbenv_softc *, int);
98 void	w83l785r_refresh_fanrpm(struct wbenv_softc *, int);
99 
100 u_int8_t wbenv_readreg(struct wbenv_softc *, u_int8_t);
101 void	wbenv_writereg(struct wbenv_softc *, u_int8_t, u_int8_t);
102 
103 const struct cfattach wbenv_ca = {
104 	sizeof(struct wbenv_softc), wbenv_match, wbenv_attach
105 };
106 
107 struct cfdriver wbenv_cd = {
108 	NULL, "wbenv", DV_DULL
109 };
110 
111 const struct wbenv_sensor w83l784r_sensors[] =
112 {
113 	{ "VCore", SENSOR_VOLTS_DC, W83L784R_VCORE, w83l784r_refresh_volt, RFACT_NONE },
114 	{ "VBAT", SENSOR_VOLTS_DC, W83L784R_VBAT, w83l784r_refresh_volt, RFACT(232, 99) },
115 	{ "+3.3V", SENSOR_VOLTS_DC, W83L784R_3_3V, w83l784r_refresh_volt, RFACT_NONE },
116 	{ "+5V", SENSOR_VOLTS_DC, W83L784R_VCC, w83l784r_refresh_volt, RFACT(50, 34) },
117 	{ "", SENSOR_TEMP, W83L784R_TEMP1, wbenv_refresh_temp },
118 	{ "", SENSOR_TEMP, 1, w83l784r_refresh_temp },
119 	{ "", SENSOR_TEMP, 2, w83l784r_refresh_temp },
120 	{ "", SENSOR_FANRPM, W83L784R_FAN1, w83l784r_refresh_fanrpm },
121 	{ "", SENSOR_FANRPM, W83L784R_FAN2, w83l784r_refresh_fanrpm },
122 
123 	{ NULL }
124 };
125 
126 const struct wbenv_sensor w83l785r_sensors[] =
127 {
128 	{ "VCore", SENSOR_VOLTS_DC, W83L784R_VCORE, w83l785r_refresh_volt, RFACT_NONE },
129 	{ "+2.5V", SENSOR_VOLTS_DC, W83L785R_2_5V, w83l785r_refresh_volt, RFACT(100, 100) },
130 	{ "+1.5V", SENSOR_VOLTS_DC, W83L785R_1_5V, w83l785r_refresh_volt, RFACT_NONE },
131 	{ "+3.3V", SENSOR_VOLTS_DC, W83L785R_VCC, w83l785r_refresh_volt, RFACT(20, 40) },
132 	{ "", SENSOR_TEMP, W83L784R_TEMP1, wbenv_refresh_temp },
133 	{ "", SENSOR_TEMP, W83L785R_TEMP2, wbenv_refresh_temp },
134 	{ "", SENSOR_FANRPM, W83L784R_FAN1, w83l785r_refresh_fanrpm },
135 	{ "", SENSOR_FANRPM, W83L784R_FAN2, w83l785r_refresh_fanrpm },
136 
137 	{ NULL }
138 };
139 
140 const struct wbenv_sensor w83l785ts_l_sensors[] =
141 {
142 	{ "", SENSOR_TEMP, W83L784R_TEMP1, wbenv_refresh_temp },
143 
144 	{ NULL }
145 };
146 
147 int
wbenv_match(struct device * parent,void * match,void * aux)148 wbenv_match(struct device *parent, void *match, void *aux)
149 {
150 	struct i2c_attach_args *ia = aux;
151 
152 	if (strcmp(ia->ia_name, "w83l784r") == 0 ||
153 	    strcmp(ia->ia_name, "w83l785r") == 0 ||
154 	    strcmp(ia->ia_name, "w83l785ts-l") == 0)
155 		return (1);
156 	return (0);
157 }
158 
159 void
wbenv_attach(struct device * parent,struct device * self,void * aux)160 wbenv_attach(struct device *parent, struct device *self, void *aux)
161 {
162 	struct wbenv_softc *sc = (struct wbenv_softc *)self;
163 	struct i2c_attach_args *ia = aux;
164 	u_int8_t cmd, data, config;
165 	int i;
166 
167 	sc->sc_tag = ia->ia_tag;
168 	sc->sc_addr[0] = ia->ia_addr;
169 
170 	iic_acquire_bus(sc->sc_tag, 0);
171 
172 	cmd = W83L784R_CHIPID;
173 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
174 	    sc->sc_addr[0], &cmd, sizeof cmd, &data, sizeof data, 0)) {
175 		iic_release_bus(sc->sc_tag, 0);
176 		printf(": cannot read chip ID register\n");
177 		return;
178 	}
179 
180 	iic_release_bus(sc->sc_tag, 0);
181 
182 	sc->sc_chip_id = data;
183 
184 	switch (sc->sc_chip_id) {
185 	case WBENV_CHIPID_W83L784R:
186 		printf(": W83L784R\n");
187 		wbenv_setup_sensors(sc, w83l784r_sensors);
188 		break;
189 	case WBENV_CHIPID_W83L785R:
190 		printf(": W83L785R\n");
191 		wbenv_setup_sensors(sc, w83l785r_sensors);
192 		goto start;
193 	case WBENV_CHIPID_W83L785TS_L:
194 		printf(": W83L785TS-L\n");
195 		wbenv_setup_sensors(sc, w83l785ts_l_sensors);
196 		goto start;
197 	default:
198 		printf(": unknown Winbond chip (ID 0x%x)\n", sc->sc_chip_id);
199 		return;
200 	}
201 
202 	iic_acquire_bus(sc->sc_tag, 0);
203 
204 	cmd = W83L784R_T23ADDR;
205 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
206 	    sc->sc_addr[0], &cmd, sizeof cmd, &data, sizeof data, 0)) {
207 		iic_release_bus(sc->sc_tag, 0);
208 		printf(": cannot read address register\n");
209 		return;
210 	}
211 
212 	iic_release_bus(sc->sc_tag, 0);
213 
214 	sc->sc_addr[1] = 0x48 + (data & 0x7);
215 	sc->sc_addr[2] = 0x48 + ((data >> 4) & 0x7);
216 
217 	/* Make the bus scan ignore the satellites. */
218 	iic_ignore_addr(sc->sc_addr[1]);
219 	iic_ignore_addr(sc->sc_addr[2]);
220 
221  start:
222 	if (sensor_task_register(sc, wbenv_refresh, 5) == NULL) {
223 		printf("%s: unable to register update task\n",
224 		    sc->sc_dev.dv_xname);
225 		return;
226 	}
227 
228 	/* Start the monitoring loop */
229 	config = wbenv_readreg(sc, W83L784R_CONFIG);
230 	wbenv_writereg(sc, W83L784R_CONFIG, config | 0x01);
231 
232 	/* Add sensors */
233 	for (i = 0; i < sc->sc_numsensors; ++i)
234 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
235 	sensordev_install(&sc->sc_sensordev);
236 }
237 
238 void
wbenv_setup_sensors(struct wbenv_softc * sc,const struct wbenv_sensor * sensors)239 wbenv_setup_sensors(struct wbenv_softc *sc, const struct wbenv_sensor *sensors)
240 {
241 	int i;
242 
243 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
244 	    sizeof(sc->sc_sensordev.xname));
245 
246 	for (i = 0; sensors[i].desc; i++) {
247 		sc->sc_sensors[i].type = sensors[i].type;
248 		strlcpy(sc->sc_sensors[i].desc, sensors[i].desc,
249 		    sizeof(sc->sc_sensors[i].desc));
250 		sc->sc_numsensors++;
251 	}
252 	sc->sc_wbenv_sensors = sensors;
253 }
254 
255 void
wbenv_refresh(void * arg)256 wbenv_refresh(void *arg)
257 {
258 	struct wbenv_softc *sc = arg;
259 	int i;
260 
261 	iic_acquire_bus(sc->sc_tag, 0);
262 
263 	for (i = 0; i < sc->sc_numsensors; i++)
264 		sc->sc_wbenv_sensors[i].refresh(sc, i);
265 
266 	iic_release_bus(sc->sc_tag, 0);
267 }
268 
269 void
w83l784r_refresh_volt(struct wbenv_softc * sc,int n)270 w83l784r_refresh_volt(struct wbenv_softc *sc, int n)
271 {
272 	struct ksensor *sensor = &sc->sc_sensors[n];
273 	int data, reg = sc->sc_wbenv_sensors[n].reg;
274 
275 	data = wbenv_readreg(sc, reg);
276 	sensor->value = (data << 4); /* 16 mV LSB */
277 	sensor->value *= sc->sc_wbenv_sensors[n].rfact;
278 	sensor->value /= 10;
279 }
280 
281 void
w83l785r_refresh_volt(struct wbenv_softc * sc,int n)282 w83l785r_refresh_volt(struct wbenv_softc *sc, int n)
283 {
284 	struct ksensor *sensor = &sc->sc_sensors[n];
285 	int data, reg = sc->sc_wbenv_sensors[n].reg;
286 
287 	data = wbenv_readreg(sc, reg);
288 	sensor->value = (data << 3); /* 8 mV LSB */
289 	sensor->value *= sc->sc_wbenv_sensors[n].rfact;
290 	sensor->value /= 10;
291 }
292 
293 void
wbenv_refresh_temp(struct wbenv_softc * sc,int n)294 wbenv_refresh_temp(struct wbenv_softc *sc, int n)
295 {
296 	struct ksensor *sensor = &sc->sc_sensors[n];
297 	int sdata;
298 
299 	sdata = wbenv_readreg(sc, sc->sc_wbenv_sensors[n].reg);
300 	if (sdata & 0x80)
301 		sdata -= 0x100;
302 	sensor->value = sdata * 1000000 + 273150000;
303 }
304 
305 void
w83l784r_refresh_temp(struct wbenv_softc * sc,int n)306 w83l784r_refresh_temp(struct wbenv_softc *sc, int n)
307 {
308 	struct ksensor *sensor = &sc->sc_sensors[n];
309 	int16_t sdata;
310 	u_int8_t cmd = 0;
311 
312 	iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
313 	    sc->sc_addr[sc->sc_wbenv_sensors[n].reg],
314 	    &cmd, sizeof cmd, &sdata, sizeof sdata, 0);
315 	sensor->value = (sdata >> 7) * 500000 + 273150000;
316 }
317 
318 void
w83l784r_refresh_fanrpm(struct wbenv_softc * sc,int n)319 w83l784r_refresh_fanrpm(struct wbenv_softc *sc, int n)
320 {
321 	struct ksensor *sensor = &sc->sc_sensors[n];
322 	int data, divisor;
323 
324 	data = wbenv_readreg(sc, W83L784R_FANDIV);
325 	if (sc->sc_wbenv_sensors[n].reg == W83L784R_FAN1)
326 		divisor = data & 0x07;
327 	else
328 		divisor = (data >> 4) & 0x07;
329 
330 	data = wbenv_readreg(sc, sc->sc_wbenv_sensors[n].reg);
331 	if (data == 0xff || data == 0x00) {
332 		sensor->flags |= SENSOR_FINVALID;
333 		sensor->value = 0;
334 	} else {
335 		sensor->flags &= ~SENSOR_FINVALID;
336 		sensor->value = 1350000 / (data << divisor);
337 	}
338 }
339 
340 void
w83l785r_refresh_fanrpm(struct wbenv_softc * sc,int n)341 w83l785r_refresh_fanrpm(struct wbenv_softc *sc, int n)
342 {
343 	struct ksensor *sensor = &sc->sc_sensors[n];
344 	int data, divisor;
345 
346 	data = wbenv_readreg(sc, W83L785R_FANDIV);
347 	if (sc->sc_wbenv_sensors[n].reg == W83L784R_FAN1)
348 		divisor = data & 0x07;
349 	else
350 		divisor = (data >> 4) & 0x07;
351 
352 	data = wbenv_readreg(sc, sc->sc_wbenv_sensors[n].reg);
353 	if (data == 0xff || data == 0x00) {
354 		sensor->flags |= SENSOR_FINVALID;
355 		sensor->value = 0;
356 	} else {
357 		sensor->flags &= ~SENSOR_FINVALID;
358 		sensor->value = 1350000 / (data << divisor);
359 	}
360 }
361 
362 u_int8_t
wbenv_readreg(struct wbenv_softc * sc,u_int8_t reg)363 wbenv_readreg(struct wbenv_softc *sc, u_int8_t reg)
364 {
365 	u_int8_t data;
366 
367 	iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
368 	    sc->sc_addr[0], &reg, sizeof reg, &data, sizeof data, 0);
369 
370 	return data;
371 }
372 
373 void
wbenv_writereg(struct wbenv_softc * sc,u_int8_t reg,u_int8_t data)374 wbenv_writereg(struct wbenv_softc *sc, u_int8_t reg, u_int8_t data)
375 {
376 	iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
377 	    sc->sc_addr[0], &reg, sizeof reg, &data, sizeof data, 0);
378 }
379