xref: /openbsd/sys/dev/i2c/gl518sm.c (revision 471aeecf)
1*471aeecfSnaddy /*	$OpenBSD: gl518sm.c,v 1.7 2022/04/06 18:59:28 naddy Exp $	*/
2cd91e2faSkettenis 
3cd91e2faSkettenis /*
4cd91e2faSkettenis  * Copyright (c) 2006 Mark Kettenis
5cd91e2faSkettenis  *
6cd91e2faSkettenis  * Permission to use, copy, modify, and distribute this software for any
7cd91e2faSkettenis  * purpose with or without fee is hereby granted, provided that the above
8cd91e2faSkettenis  * copyright notice and this permission notice appear in all copies.
9cd91e2faSkettenis  *
10cd91e2faSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11cd91e2faSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12cd91e2faSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13cd91e2faSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14cd91e2faSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15cd91e2faSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16cd91e2faSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17cd91e2faSkettenis  */
18cd91e2faSkettenis 
19cd91e2faSkettenis #include <sys/param.h>
20cd91e2faSkettenis #include <sys/systm.h>
21cd91e2faSkettenis #include <sys/device.h>
22cd91e2faSkettenis #include <sys/sensors.h>
23cd91e2faSkettenis 
24cd91e2faSkettenis #include <dev/i2c/i2cvar.h>
25cd91e2faSkettenis 
26cd91e2faSkettenis /* GL518SM registers */
27cd91e2faSkettenis #define GL518SM_CHIPID		0x00
28cd91e2faSkettenis #define GL518SM_REVISION	0x01
29cd91e2faSkettenis #define GL518SM_VENDORID	0x02
30cd91e2faSkettenis #define GL518SM_CONFIG		0x03
31cd91e2faSkettenis #define  GL518SM_CONFIG_START		0x40
32cd91e2faSkettenis #define  GL518SM_CONFIG_CLEARST		0x20
33cd91e2faSkettenis #define  GL518SM_CONFIG_NOFAN2		0x10
34cd91e2faSkettenis #define GL518SM_TEMP		0x04
35cd91e2faSkettenis #define GL518SM_TEMP_OVER	0x05
36cd91e2faSkettenis #define GL518SM_TEMP_HYST	0x06
37cd91e2faSkettenis #define GL518SM_FAN_COUNT	0x07
38cd91e2faSkettenis #define GL518SM_FAN_LIMIT	0x08
39cd91e2faSkettenis #define GL518SM_VIN1_LIMIT	0x09
40cd91e2faSkettenis #define GL518SM_VIN2_LIMIT	0x0a
41cd91e2faSkettenis #define GL518SM_VIN3_LIMIT	0x0b
42cd91e2faSkettenis #define GL518SM_VDD_LIMIT	0x0c
43cd91e2faSkettenis #define GL518SM_VOLTMETER	0x0d
44cd91e2faSkettenis #define GL518SM_MISC		0x0f
45cd91e2faSkettenis #define GL518SM_ALARM		0x10
46cd91e2faSkettenis #define GL518SM_MASK		0x11
47cd91e2faSkettenis #define GL518SM_INTSTAT		0x12
48cd91e2faSkettenis 
49cd91e2faSkettenis /* Sensors */
50cd91e2faSkettenis #define GLENV_VIN3		0
51cd91e2faSkettenis #define GLENV_TEMP		1
52cd91e2faSkettenis #define GLENV_FAN1		2
53cd91e2faSkettenis #define GLENV_FAN2		3
54cd91e2faSkettenis #define GLENV_NUM_SENSORS	4
55cd91e2faSkettenis 
56cd91e2faSkettenis struct glenv_softc {
57cd91e2faSkettenis 	struct device sc_dev;
58cd91e2faSkettenis 
59cd91e2faSkettenis 	i2c_tag_t sc_tag;
60cd91e2faSkettenis 	i2c_addr_t sc_addr;
61cd91e2faSkettenis 
62275cbf62Sderaadt 	struct ksensor sc_sensor[GLENV_NUM_SENSORS];
63275cbf62Sderaadt 	struct ksensordev sc_sensordev;
64cd91e2faSkettenis 	int	sc_fan1_div, sc_fan2_div;
65cd91e2faSkettenis };
66cd91e2faSkettenis 
67cd91e2faSkettenis int	glenv_match(struct device *, void *, void *);
68cd91e2faSkettenis void	glenv_attach(struct device *, struct device *, void *);
69cd91e2faSkettenis 
70cd91e2faSkettenis void	glenv_refresh(void *);
71cd91e2faSkettenis 
72*471aeecfSnaddy const struct cfattach glenv_ca = {
73cd91e2faSkettenis 	sizeof(struct glenv_softc), glenv_match, glenv_attach
74cd91e2faSkettenis };
75cd91e2faSkettenis 
76cd91e2faSkettenis struct cfdriver glenv_cd = {
77cd91e2faSkettenis 	NULL, "glenv", DV_DULL
78cd91e2faSkettenis };
79cd91e2faSkettenis 
80cd91e2faSkettenis int
glenv_match(struct device * parent,void * match,void * aux)81cd91e2faSkettenis glenv_match(struct device *parent, void *match, void *aux)
82cd91e2faSkettenis {
83cd91e2faSkettenis 	struct i2c_attach_args *ia = aux;
84cd91e2faSkettenis 
85cd91e2faSkettenis 	if (strcmp(ia->ia_name, "gl518sm") == 0)
86cd91e2faSkettenis 		return (1);
87cd91e2faSkettenis 	return (0);
88cd91e2faSkettenis }
89cd91e2faSkettenis 
90cd91e2faSkettenis void
glenv_attach(struct device * parent,struct device * self,void * aux)91cd91e2faSkettenis glenv_attach(struct device *parent, struct device *self, void *aux)
92cd91e2faSkettenis {
93cd91e2faSkettenis 	struct glenv_softc *sc = (struct glenv_softc *)self;
94cd91e2faSkettenis 	struct i2c_attach_args *ia = aux;
95cd91e2faSkettenis 	u_int8_t cmd, data;
96cd91e2faSkettenis 	int i;
97cd91e2faSkettenis 
98cd91e2faSkettenis 	sc->sc_tag = ia->ia_tag;
99cd91e2faSkettenis 	sc->sc_addr = ia->ia_addr;
100cd91e2faSkettenis 
101cd91e2faSkettenis 	iic_acquire_bus(sc->sc_tag, 0);
102cd91e2faSkettenis 
103cd91e2faSkettenis 	cmd = GL518SM_REVISION;
104cd91e2faSkettenis 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
105cd91e2faSkettenis 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
106cd91e2faSkettenis 		iic_release_bus(sc->sc_tag, 0);
107cd91e2faSkettenis 		printf(": cannot read revision register\n");
108cd91e2faSkettenis 		return;
109cd91e2faSkettenis 	}
110cd91e2faSkettenis 
111ee976787Skettenis 	printf(": GL518SM rev 0x%02x", data);
112cd91e2faSkettenis 
113cd91e2faSkettenis 	cmd = GL518SM_MISC;
114cd91e2faSkettenis 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
115cd91e2faSkettenis 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
116cd91e2faSkettenis 		iic_release_bus(sc->sc_tag, 0);
117cd91e2faSkettenis 		printf(", cannot read misc register\n");
118cd91e2faSkettenis 		return;
119cd91e2faSkettenis 	}
120cd91e2faSkettenis 	sc->sc_fan1_div = 1 << ((data >> 6) & 0x03);
121cd91e2faSkettenis 	sc->sc_fan2_div = 1 << ((data >> 4) & 0x03);
122cd91e2faSkettenis 
123cd91e2faSkettenis 	cmd = GL518SM_CONFIG;
124cd91e2faSkettenis 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
125cd91e2faSkettenis 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
126cd91e2faSkettenis 		iic_release_bus(sc->sc_tag, 0);
127cd91e2faSkettenis 		printf(", cannot read configuration register\n");
128cd91e2faSkettenis 		return;
129cd91e2faSkettenis 	}
130cd91e2faSkettenis 	if (data & GL518SM_CONFIG_NOFAN2)
131881f2378Skettenis 		sc->sc_fan2_div = 0;
132cd91e2faSkettenis 
133cd91e2faSkettenis 	/* Start monitoring and clear interrupt status. */
134cd91e2faSkettenis 	data = (data | GL518SM_CONFIG_START | GL518SM_CONFIG_CLEARST);
135cd91e2faSkettenis 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
136cd91e2faSkettenis 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
137cd91e2faSkettenis 			iic_release_bus(sc->sc_tag, 0);
138cd91e2faSkettenis 			printf(", cannot write configuration register\n");
139cd91e2faSkettenis 			return;
140cd91e2faSkettenis 	}
141cd91e2faSkettenis 
142cd91e2faSkettenis 	iic_release_bus(sc->sc_tag, 0);
143cd91e2faSkettenis 
144cd91e2faSkettenis 	/* Initialize sensor data. */
14527515a6bSderaadt 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
14627515a6bSderaadt 	    sizeof(sc->sc_sensordev.xname));
147cd91e2faSkettenis 
148cd91e2faSkettenis 	sc->sc_sensor[GLENV_VIN3].type = SENSOR_VOLTS_DC;
149cd91e2faSkettenis 
150cd91e2faSkettenis 	sc->sc_sensor[GLENV_TEMP].type = SENSOR_TEMP;
151cd91e2faSkettenis 
152cd91e2faSkettenis 	sc->sc_sensor[GLENV_FAN1].type = SENSOR_FANRPM;
153cd91e2faSkettenis 
154cd91e2faSkettenis 	sc->sc_sensor[GLENV_FAN2].type = SENSOR_FANRPM;
155cd91e2faSkettenis 	if (sc->sc_fan2_div == -1)
156cd91e2faSkettenis 		sc->sc_sensor[GLENV_FAN2].flags |= SENSOR_FINVALID;
157cd91e2faSkettenis 
158abd9fc28Sdlg 	if (sensor_task_register(sc, glenv_refresh, 5) == NULL) {
159cd91e2faSkettenis 		printf(", unable to register update task\n");
160cd91e2faSkettenis 		return;
161cd91e2faSkettenis 	}
162cd91e2faSkettenis 
163cd91e2faSkettenis 	for (i = 0; i < GLENV_NUM_SENSORS; i++)
16427515a6bSderaadt 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
16527515a6bSderaadt 	sensordev_install(&sc->sc_sensordev);
166cd91e2faSkettenis 
167cd91e2faSkettenis 	printf("\n");
168cd91e2faSkettenis }
169cd91e2faSkettenis 
170cd91e2faSkettenis void
glenv_refresh(void * arg)171cd91e2faSkettenis glenv_refresh(void *arg)
172cd91e2faSkettenis {
173cd91e2faSkettenis 	struct glenv_softc *sc = arg;
174cd91e2faSkettenis 	u_int8_t cmd, data, data2[2];
175cd91e2faSkettenis 	u_int tmp;
176cd91e2faSkettenis 
177cd91e2faSkettenis 	iic_acquire_bus(sc->sc_tag, 0);
178cd91e2faSkettenis 
179cd91e2faSkettenis 	cmd = GL518SM_VOLTMETER;
180cd91e2faSkettenis 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
181cd91e2faSkettenis 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
182cd91e2faSkettenis 		sc->sc_sensor[GLENV_VIN3].flags |= SENSOR_FINVALID;
183cd91e2faSkettenis 	} else {
184cd91e2faSkettenis 		sc->sc_sensor[GLENV_VIN3].flags &= ~SENSOR_FINVALID;
185cd91e2faSkettenis 		sc->sc_sensor[GLENV_VIN3].value = data * 19000;
186cd91e2faSkettenis 	}
187cd91e2faSkettenis 
188cd91e2faSkettenis 	cmd = GL518SM_TEMP;
189cd91e2faSkettenis 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
190cd91e2faSkettenis 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
191cd91e2faSkettenis 		sc->sc_sensor[GLENV_TEMP].flags |= SENSOR_FINVALID;
192cd91e2faSkettenis 	} else {
193cd91e2faSkettenis 		sc->sc_sensor[GLENV_TEMP].flags &= ~SENSOR_FINVALID;
194cd91e2faSkettenis 		sc->sc_sensor[GLENV_TEMP].value =
195cd91e2faSkettenis 			(data - 119) * 1000000 + 273150000;
196cd91e2faSkettenis 	}
197cd91e2faSkettenis 
198cd91e2faSkettenis 	cmd = GL518SM_FAN_COUNT;
199cd91e2faSkettenis 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
200cd91e2faSkettenis 	    sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
201cd91e2faSkettenis 		sc->sc_sensor[GLENV_FAN1].flags |= SENSOR_FINVALID;
202cd91e2faSkettenis 		sc->sc_sensor[GLENV_FAN2].flags |= SENSOR_FINVALID;
203cd91e2faSkettenis 	} else {
204cd91e2faSkettenis 		sc->sc_sensor[GLENV_FAN1].flags &= ~SENSOR_FINVALID;
205cd91e2faSkettenis 		tmp = data2[0] * sc->sc_fan1_div * 2;
206881f2378Skettenis 		if (tmp == 0)
207881f2378Skettenis 			sc->sc_sensor[GLENV_FAN1].flags |= SENSOR_FINVALID;
208881f2378Skettenis 		else
209881f2378Skettenis 			sc->sc_sensor[GLENV_FAN1].value = 960000 / tmp;
210881f2378Skettenis 
211cd91e2faSkettenis 		sc->sc_sensor[GLENV_FAN2].flags &= ~SENSOR_FINVALID;
212881f2378Skettenis 		tmp = data2[1] * sc->sc_fan2_div * 2;
213881f2378Skettenis 		if (tmp == 0)
214881f2378Skettenis 			sc->sc_sensor[GLENV_FAN2].flags |= SENSOR_FINVALID;
215881f2378Skettenis 		else
216881f2378Skettenis 			sc->sc_sensor[GLENV_FAN2].value = 960000 / tmp;
217cd91e2faSkettenis 	}
218cd91e2faSkettenis 
219cd91e2faSkettenis 	iic_release_bus(sc->sc_tag, 0);
220cd91e2faSkettenis }
221