xref: /openbsd/sys/dev/i2c/fintek.c (revision 471aeecf)
1 /*	$OpenBSD: fintek.c,v 1.9 2022/04/06 18:59:28 naddy Exp $ */
2 /*
3  * Copyright (c) 2006 Dale Rahn <drahn@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/sensors.h>
22 
23 #include <dev/i2c/i2cvar.h>
24 
25 /* Sensors */
26 #define F_VCC	0
27 #define F_V1	1
28 #define F_V2	2
29 #define F_V3	3
30 #define F_TEMP1	4
31 #define F_TEMP2	5
32 #define F_FAN1	6
33 #define F_FAN2	7
34 #define F_NUM_SENSORS	8
35 
36 struct fintek_softc {
37 	struct device sc_dev;
38 	i2c_tag_t sc_tag;
39 	i2c_addr_t sc_addr;
40 
41 #ifndef SMALL_KERNEL
42 	struct ksensor sc_sensor[F_NUM_SENSORS];
43 	struct ksensordev sc_sensordev;
44 #endif
45 };
46 
47 int	fintek_match(struct device *, void *, void *);
48 void	fintek_attach(struct device *, struct device *, void *);
49 
50 void	fintek_refresh(void *);
51 int	fintek_read_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data,
52 	    size_t size);
53 int	fintek_write_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data,
54 	    size_t size);
55 void	fintek_fullspeed(struct fintek_softc *sc);
56 
57 const struct cfattach fintek_ca = {
58 	sizeof(struct fintek_softc), fintek_match, fintek_attach
59 };
60 
61 struct cfdriver fintek_cd = {
62 	NULL, "fintek", DV_DULL
63 };
64 
65 #define FINTEK_CONFIG1		0x01
66 #define  FINTEK_FAN1_LINEAR_MODE	0x10
67 #define  FINTEK_FAN2_LINEAR_MODE	0x20
68 #define FINTEK_VOLT0		0x10
69 #define FINTEK_VOLT1		0x11
70 #define FINTEK_VOLT2		0x12
71 #define FINTEK_VOLT3		0x13
72 #define FINTEK_TEMP1		0x14
73 #define FINTEK_TEMP2		0x15
74 #define FINTEK_FAN1		0x16
75 #define FINTEK_FAN2		0x18
76 #define FINTEK_VERSION		0x5c
77 #define FINTEK_RSTCR		0x60
78 #define  FINTEK_FAN1_MODE_MANUAL	0x30
79 #define  FINTEK_FAN2_MODE_MANUAL	0xc0
80 #define FINTEK_PWM_DUTY1	0x76
81 #define FINTEK_PWM_DUTY2	0x86
82 
83 /* Options passed via the 'flags' config keyword. */
84 #define FINTEK_OPTION_FULLSPEED	0x0001
85 
86 int
fintek_match(struct device * parent,void * match,void * aux)87 fintek_match(struct device *parent, void *match, void *aux)
88 {
89 	struct i2c_attach_args *ia = aux;
90 
91 	if (strcmp(ia->ia_name, "f75375") == 0)
92 		return (1);
93 	return (0);
94 }
95 
96 int
fintek_read_reg(struct fintek_softc * sc,u_int8_t cmd,u_int8_t * data,size_t size)97 fintek_read_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data,
98     size_t size)
99 {
100 	return iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
101 	    sc->sc_addr, &cmd, sizeof cmd, data, size, 0);
102 }
103 
104 int
fintek_write_reg(struct fintek_softc * sc,u_int8_t cmd,u_int8_t * data,size_t size)105 fintek_write_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data,
106     size_t size)
107 {
108 	return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
109 	    sc->sc_addr, &cmd, sizeof cmd, data, size, 0);
110 }
111 
112 void
fintek_attach(struct device * parent,struct device * self,void * aux)113 fintek_attach(struct device *parent, struct device *self, void *aux)
114 {
115 	struct fintek_softc *sc = (struct fintek_softc *)self;
116 	struct i2c_attach_args *ia = aux;
117 	u_int8_t cmd, data;
118 #ifndef SMALL_KERNEL
119 	int i;
120 #endif
121 
122 	sc->sc_tag = ia->ia_tag;
123 	sc->sc_addr = ia->ia_addr;
124 
125 	iic_acquire_bus(sc->sc_tag, 0);
126 
127 	cmd = FINTEK_VERSION;
128 	if (fintek_read_reg(sc, cmd, &data, sizeof data))
129 		goto failread;
130 
131 	printf(": F75375 rev %d.%d", data>> 4, data & 0xf);
132 
133 	/*
134 	 * It seems the fan in the Thecus n2100 doesn't provide a
135 	 * reliable fan count.  As a result the automatic fan
136 	 * controlling mode that the chip comes up in after reset
137 	 * doesn't work reliably.  So we have a flag to drive the fan
138 	 * at maximum voltage such that the box doesn't overheat.
139 	 */
140 	if (sc->sc_dev.dv_cfdata->cf_flags & FINTEK_OPTION_FULLSPEED)
141 		fintek_fullspeed(sc);
142 
143 	iic_release_bus(sc->sc_tag, 0);
144 
145 #ifndef SMALL_KERNEL
146 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
147 	    sizeof(sc->sc_sensordev.xname));
148 
149 	sc->sc_sensor[F_VCC].type = SENSOR_VOLTS_DC;
150 	strlcpy(sc->sc_sensor[F_VCC].desc, "Vcc",
151 	    sizeof(sc->sc_sensor[F_VCC].desc));
152 
153 	sc->sc_sensor[F_V1].type = SENSOR_VOLTS_DC;
154 	sc->sc_sensor[F_V2].type = SENSOR_VOLTS_DC;
155 	sc->sc_sensor[F_V3].type = SENSOR_VOLTS_DC;
156 
157 	sc->sc_sensor[F_TEMP1].type = SENSOR_TEMP;
158 	sc->sc_sensor[F_TEMP2].type = SENSOR_TEMP;
159 
160 	sc->sc_sensor[F_FAN1].type = SENSOR_FANRPM;
161 	sc->sc_sensor[F_FAN2].type = SENSOR_FANRPM;
162 
163 	if (sensor_task_register(sc, fintek_refresh, 5) == NULL) {
164 		printf(", unable to register update task\n");
165 		return;
166 	}
167 
168 	for (i = 0; i < F_NUM_SENSORS; i++)
169 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
170 	sensordev_install(&sc->sc_sensordev);
171 #endif
172 
173 	printf("\n");
174 	return;
175 
176 failread:
177 	printf("unable to read reg %d\n", cmd);
178 	iic_release_bus(sc->sc_tag, 0);
179 	return;
180 }
181 
182 
183 #ifndef SMALL_KERNEL
184 struct {
185 	char		sensor;
186 	u_int8_t	cmd;
187 } fintek_worklist[] = {
188 	{ F_VCC, FINTEK_VOLT0 },
189 	{ F_V1, FINTEK_VOLT1 },
190 	{ F_V2, FINTEK_VOLT2 },
191 	{ F_V3, FINTEK_VOLT3 },
192 	{ F_TEMP1, FINTEK_TEMP1 },
193 	{ F_TEMP2, FINTEK_TEMP2 },
194 	{ F_FAN1, FINTEK_FAN1 },
195 	{ F_FAN2, FINTEK_FAN2 }
196 };
197 #define FINTEK_WORKLIST_SZ (sizeof(fintek_worklist) / sizeof(fintek_worklist[0]))
198 
199 void
fintek_refresh(void * arg)200 fintek_refresh(void *arg)
201 {
202 	struct fintek_softc *sc =  arg;
203 	u_int8_t cmd, data, data2;
204 	int i;
205 
206 	iic_acquire_bus(sc->sc_tag, 0);
207 
208 	for (i = 0; i < FINTEK_WORKLIST_SZ; i++){
209 		cmd = fintek_worklist[i].cmd;
210 		if (fintek_read_reg(sc, cmd, &data, sizeof data)) {
211 			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
212 			continue;
213 		}
214 		sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
215 		switch (fintek_worklist[i].sensor) {
216 		case  F_VCC:
217 			sc->sc_sensor[i].value = data * 16000;
218 			break;
219 		case  F_V1:
220 			/* FALLTHROUGH */
221 		case  F_V2:
222 			/* FALLTHROUGH */
223 		case  F_V3:
224 			sc->sc_sensor[i].value = data * 8000;
225 			break;
226 		case  F_TEMP1:
227 			/* FALLTHROUGH */
228 		case  F_TEMP2:
229 			sc->sc_sensor[i].value = 273150000 + data * 1000000;
230 			break;
231 		case  F_FAN1:
232 			/* FALLTHROUGH */
233 		case  F_FAN2:
234 			/* FANx LSB follows FANx MSB */
235 			cmd = fintek_worklist[i].cmd + 1;
236 			if (fintek_read_reg(sc, cmd, &data2, sizeof data2)) {
237 				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
238 				continue;
239 			}
240 			if ((data == 0xff && data2 == 0xff) ||
241 			    (data == 0 && data2 == 0))
242 				sc->sc_sensor[i].value = 0;
243 			else
244 				sc->sc_sensor[i].value = 1500000 /
245 				    (data << 8 | data2);
246 			break;
247 		default:
248 			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
249 			break;
250 		}
251 	}
252 
253 	iic_release_bus(sc->sc_tag, 0);
254 }
255 #endif
256 
257 void
fintek_fullspeed(struct fintek_softc * sc)258 fintek_fullspeed(struct fintek_softc *sc)
259 {
260 	u_int8_t data;
261 
262 	data = FINTEK_FAN1_LINEAR_MODE | FINTEK_FAN2_LINEAR_MODE;
263 	fintek_write_reg(sc, FINTEK_CONFIG1, &data, sizeof data);
264 
265 	data = FINTEK_FAN1_MODE_MANUAL | FINTEK_FAN2_MODE_MANUAL;
266 	fintek_write_reg(sc, FINTEK_RSTCR, &data, sizeof data);
267 
268 	data = 0xff;		/* Maximum voltage */
269 	fintek_write_reg(sc, FINTEK_PWM_DUTY1, &data, sizeof data);
270 	fintek_write_reg(sc, FINTEK_PWM_DUTY2, &data, sizeof data);
271 }
272