xref: /openbsd/sys/dev/i2c/gl518sm.c (revision 471aeecf)
1 /*	$OpenBSD: gl518sm.c,v 1.7 2022/04/06 18:59: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 /* GL518SM registers */
27 #define GL518SM_CHIPID		0x00
28 #define GL518SM_REVISION	0x01
29 #define GL518SM_VENDORID	0x02
30 #define GL518SM_CONFIG		0x03
31 #define  GL518SM_CONFIG_START		0x40
32 #define  GL518SM_CONFIG_CLEARST		0x20
33 #define  GL518SM_CONFIG_NOFAN2		0x10
34 #define GL518SM_TEMP		0x04
35 #define GL518SM_TEMP_OVER	0x05
36 #define GL518SM_TEMP_HYST	0x06
37 #define GL518SM_FAN_COUNT	0x07
38 #define GL518SM_FAN_LIMIT	0x08
39 #define GL518SM_VIN1_LIMIT	0x09
40 #define GL518SM_VIN2_LIMIT	0x0a
41 #define GL518SM_VIN3_LIMIT	0x0b
42 #define GL518SM_VDD_LIMIT	0x0c
43 #define GL518SM_VOLTMETER	0x0d
44 #define GL518SM_MISC		0x0f
45 #define GL518SM_ALARM		0x10
46 #define GL518SM_MASK		0x11
47 #define GL518SM_INTSTAT		0x12
48 
49 /* Sensors */
50 #define GLENV_VIN3		0
51 #define GLENV_TEMP		1
52 #define GLENV_FAN1		2
53 #define GLENV_FAN2		3
54 #define GLENV_NUM_SENSORS	4
55 
56 struct glenv_softc {
57 	struct device sc_dev;
58 
59 	i2c_tag_t sc_tag;
60 	i2c_addr_t sc_addr;
61 
62 	struct ksensor sc_sensor[GLENV_NUM_SENSORS];
63 	struct ksensordev sc_sensordev;
64 	int	sc_fan1_div, sc_fan2_div;
65 };
66 
67 int	glenv_match(struct device *, void *, void *);
68 void	glenv_attach(struct device *, struct device *, void *);
69 
70 void	glenv_refresh(void *);
71 
72 const struct cfattach glenv_ca = {
73 	sizeof(struct glenv_softc), glenv_match, glenv_attach
74 };
75 
76 struct cfdriver glenv_cd = {
77 	NULL, "glenv", DV_DULL
78 };
79 
80 int
glenv_match(struct device * parent,void * match,void * aux)81 glenv_match(struct device *parent, void *match, void *aux)
82 {
83 	struct i2c_attach_args *ia = aux;
84 
85 	if (strcmp(ia->ia_name, "gl518sm") == 0)
86 		return (1);
87 	return (0);
88 }
89 
90 void
glenv_attach(struct device * parent,struct device * self,void * aux)91 glenv_attach(struct device *parent, struct device *self, void *aux)
92 {
93 	struct glenv_softc *sc = (struct glenv_softc *)self;
94 	struct i2c_attach_args *ia = aux;
95 	u_int8_t cmd, data;
96 	int i;
97 
98 	sc->sc_tag = ia->ia_tag;
99 	sc->sc_addr = ia->ia_addr;
100 
101 	iic_acquire_bus(sc->sc_tag, 0);
102 
103 	cmd = GL518SM_REVISION;
104 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
105 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
106 		iic_release_bus(sc->sc_tag, 0);
107 		printf(": cannot read revision register\n");
108 		return;
109 	}
110 
111 	printf(": GL518SM rev 0x%02x", data);
112 
113 	cmd = GL518SM_MISC;
114 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
115 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
116 		iic_release_bus(sc->sc_tag, 0);
117 		printf(", cannot read misc register\n");
118 		return;
119 	}
120 	sc->sc_fan1_div = 1 << ((data >> 6) & 0x03);
121 	sc->sc_fan2_div = 1 << ((data >> 4) & 0x03);
122 
123 	cmd = GL518SM_CONFIG;
124 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
125 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
126 		iic_release_bus(sc->sc_tag, 0);
127 		printf(", cannot read configuration register\n");
128 		return;
129 	}
130 	if (data & GL518SM_CONFIG_NOFAN2)
131 		sc->sc_fan2_div = 0;
132 
133 	/* Start monitoring and clear interrupt status. */
134 	data = (data | GL518SM_CONFIG_START | GL518SM_CONFIG_CLEARST);
135 	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
136 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
137 			iic_release_bus(sc->sc_tag, 0);
138 			printf(", cannot write configuration register\n");
139 			return;
140 	}
141 
142 	iic_release_bus(sc->sc_tag, 0);
143 
144 	/* Initialize sensor data. */
145 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
146 	    sizeof(sc->sc_sensordev.xname));
147 
148 	sc->sc_sensor[GLENV_VIN3].type = SENSOR_VOLTS_DC;
149 
150 	sc->sc_sensor[GLENV_TEMP].type = SENSOR_TEMP;
151 
152 	sc->sc_sensor[GLENV_FAN1].type = SENSOR_FANRPM;
153 
154 	sc->sc_sensor[GLENV_FAN2].type = SENSOR_FANRPM;
155 	if (sc->sc_fan2_div == -1)
156 		sc->sc_sensor[GLENV_FAN2].flags |= SENSOR_FINVALID;
157 
158 	if (sensor_task_register(sc, glenv_refresh, 5) == NULL) {
159 		printf(", unable to register update task\n");
160 		return;
161 	}
162 
163 	for (i = 0; i < GLENV_NUM_SENSORS; i++)
164 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
165 	sensordev_install(&sc->sc_sensordev);
166 
167 	printf("\n");
168 }
169 
170 void
glenv_refresh(void * arg)171 glenv_refresh(void *arg)
172 {
173 	struct glenv_softc *sc = arg;
174 	u_int8_t cmd, data, data2[2];
175 	u_int tmp;
176 
177 	iic_acquire_bus(sc->sc_tag, 0);
178 
179 	cmd = GL518SM_VOLTMETER;
180 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
181 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
182 		sc->sc_sensor[GLENV_VIN3].flags |= SENSOR_FINVALID;
183 	} else {
184 		sc->sc_sensor[GLENV_VIN3].flags &= ~SENSOR_FINVALID;
185 		sc->sc_sensor[GLENV_VIN3].value = data * 19000;
186 	}
187 
188 	cmd = GL518SM_TEMP;
189 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
190 	    sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
191 		sc->sc_sensor[GLENV_TEMP].flags |= SENSOR_FINVALID;
192 	} else {
193 		sc->sc_sensor[GLENV_TEMP].flags &= ~SENSOR_FINVALID;
194 		sc->sc_sensor[GLENV_TEMP].value =
195 			(data - 119) * 1000000 + 273150000;
196 	}
197 
198 	cmd = GL518SM_FAN_COUNT;
199 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
200 	    sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
201 		sc->sc_sensor[GLENV_FAN1].flags |= SENSOR_FINVALID;
202 		sc->sc_sensor[GLENV_FAN2].flags |= SENSOR_FINVALID;
203 	} else {
204 		sc->sc_sensor[GLENV_FAN1].flags &= ~SENSOR_FINVALID;
205 		tmp = data2[0] * sc->sc_fan1_div * 2;
206 		if (tmp == 0)
207 			sc->sc_sensor[GLENV_FAN1].flags |= SENSOR_FINVALID;
208 		else
209 			sc->sc_sensor[GLENV_FAN1].value = 960000 / tmp;
210 
211 		sc->sc_sensor[GLENV_FAN2].flags &= ~SENSOR_FINVALID;
212 		tmp = data2[1] * sc->sc_fan2_div * 2;
213 		if (tmp == 0)
214 			sc->sc_sensor[GLENV_FAN2].flags |= SENSOR_FINVALID;
215 		else
216 			sc->sc_sensor[GLENV_FAN2].value = 960000 / tmp;
217 	}
218 
219 	iic_release_bus(sc->sc_tag, 0);
220 }
221