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
64struct wbenv_softc;
65
66struct 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
74struct 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
87int	wbenv_match(struct device *, void *, void *);
88void	wbenv_attach(struct device *, struct device *, void *);
89
90void	wbenv_setup_sensors(struct wbenv_softc *, const struct wbenv_sensor *);
91void	wbenv_refresh(void *);
92
93void	w83l784r_refresh_volt(struct wbenv_softc *, int);
94void	w83l785r_refresh_volt(struct wbenv_softc *, int);
95void	wbenv_refresh_temp(struct wbenv_softc *, int);
96void	w83l784r_refresh_temp(struct wbenv_softc *, int);
97void	w83l784r_refresh_fanrpm(struct wbenv_softc *, int);
98void	w83l785r_refresh_fanrpm(struct wbenv_softc *, int);
99
100u_int8_t wbenv_readreg(struct wbenv_softc *, u_int8_t);
101void	wbenv_writereg(struct wbenv_softc *, u_int8_t, u_int8_t);
102
103const struct cfattach wbenv_ca = {
104	sizeof(struct wbenv_softc), wbenv_match, wbenv_attach
105};
106
107struct cfdriver wbenv_cd = {
108	NULL, "wbenv", DV_DULL
109};
110
111const 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
126const 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
140const struct wbenv_sensor w83l785ts_l_sensors[] =
141{
142	{ "", SENSOR_TEMP, W83L784R_TEMP1, wbenv_refresh_temp },
143
144	{ NULL }
145};
146
147int
148wbenv_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
159void
160wbenv_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
238void
239wbenv_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
255void
256wbenv_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
269void
270w83l784r_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
281void
282w83l785r_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
293void
294wbenv_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
305void
306w83l784r_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
318void
319w83l784r_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
340void
341w83l785r_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
362u_int8_t
363wbenv_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
373void
374wbenv_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