xref: /openbsd-src/sys/dev/i2c/fintek.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: fintek.c,v 1.7 2007/10/31 20:46:17 cnst 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 	struct ksensor sc_sensor[F_NUM_SENSORS];
42 	struct ksensordev sc_sensordev;
43 };
44 
45 int	fintek_match(struct device *, void *, void *);
46 void	fintek_attach(struct device *, struct device *, void *);
47 
48 void	fintek_refresh(void *);
49 int	fintek_read_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data,
50 	    size_t size);
51 int	fintek_write_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data,
52 	    size_t size);
53 void	fintek_fullspeed(struct fintek_softc *sc);
54 
55 struct cfattach fintek_ca = {
56 	sizeof(struct fintek_softc), fintek_match, fintek_attach
57 };
58 
59 struct cfdriver fintek_cd = {
60 	NULL, "fintek", DV_DULL
61 };
62 
63 #define FINTEK_CONFIG1		0x01
64 #define  FINTEK_FAN1_LINEAR_MODE	0x10
65 #define  FINTEK_FAN2_LINEAR_MODE	0x20
66 #define FINTEK_VOLT0		0x10
67 #define FINTEK_VOLT1		0x11
68 #define FINTEK_VOLT2		0x12
69 #define FINTEK_VOLT3		0x13
70 #define FINTEK_TEMP1		0x14
71 #define FINTEK_TEMP2		0x15
72 #define FINTEK_FAN1		0x16
73 #define FINTEK_FAN2		0x18
74 #define FINTEK_VERSION		0x5c
75 #define FINTEK_RSTCR		0x60
76 #define  FINTEK_FAN1_MODE_MANUAL	0x30
77 #define  FINTEK_FAN2_MODE_MANUAL	0xc0
78 #define FINTEK_PWM_DUTY1	0x76
79 #define FINTEK_PWM_DUTY2	0x86
80 
81 /* Options passed via the 'flags' config keyword. */
82 #define FINTEK_OPTION_FULLSPEED	0x0001
83 
84 int
85 fintek_match(struct device *parent, void *match, void *aux)
86 {
87 	struct i2c_attach_args *ia = aux;
88 
89 	if (strcmp(ia->ia_name, "f75375") == 0)
90 		return (1);
91 	return (0);
92 }
93 
94 int
95 fintek_read_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data,
96     size_t size)
97 {
98 	return iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
99 	    sc->sc_addr, &cmd, sizeof cmd, data, size, 0);
100 }
101 
102 int
103 fintek_write_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data,
104     size_t size)
105 {
106 	return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
107 	    sc->sc_addr, &cmd, sizeof cmd, data, size, 0);
108 }
109 
110 void
111 fintek_attach(struct device *parent, struct device *self, void *aux)
112 {
113 	struct fintek_softc *sc = (struct fintek_softc *)self;
114 	struct i2c_attach_args *ia = aux;
115 	u_int8_t cmd, data;
116 	int i;
117 
118 	sc->sc_tag = ia->ia_tag;
119 	sc->sc_addr = ia->ia_addr;
120 
121 	iic_acquire_bus(sc->sc_tag, 0);
122 
123 	cmd = FINTEK_VERSION;
124 	if (fintek_read_reg(sc, cmd, &data, sizeof data))
125 		goto failread;
126 
127 	printf(": F75375 rev %d.%d", data>> 4, data & 0xf);
128 
129 	/*
130 	 * It seems the fan in the Thecus n2100 doesn't provide a
131 	 * reliable fan count.  As a result the automatic fan
132 	 * controlling mode that the chip comes up in after reset
133 	 * doesn't work reliably.  So we have a flag to drive the fan
134 	 * at maximum voltage such that the box doesn't overheat.
135 	 */
136 	if (sc->sc_dev.dv_cfdata->cf_flags & FINTEK_OPTION_FULLSPEED)
137 		fintek_fullspeed(sc);
138 
139 	iic_release_bus(sc->sc_tag, 0);
140 
141 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
142 	    sizeof(sc->sc_sensordev.xname));
143 
144 	sc->sc_sensor[F_VCC].type = SENSOR_VOLTS_DC;
145 	strlcpy(sc->sc_sensor[F_VCC].desc, "Vcc",
146 	    sizeof(sc->sc_sensor[F_VCC].desc));
147 
148 	sc->sc_sensor[F_V1].type = SENSOR_VOLTS_DC;
149 	sc->sc_sensor[F_V2].type = SENSOR_VOLTS_DC;
150 	sc->sc_sensor[F_V3].type = SENSOR_VOLTS_DC;
151 
152 	sc->sc_sensor[F_TEMP1].type = SENSOR_TEMP;
153 	sc->sc_sensor[F_TEMP2].type = SENSOR_TEMP;
154 
155 	sc->sc_sensor[F_FAN1].type = SENSOR_FANRPM;
156 	sc->sc_sensor[F_FAN2].type = SENSOR_FANRPM;
157 
158 	if (sensor_task_register(sc, fintek_refresh, 5) == NULL) {
159 		printf(", unable to register update task\n");
160 		return;
161 	}
162 
163 	for (i = 0; i < F_NUM_SENSORS; i++)
164 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
165 	sensordev_install(&sc->sc_sensordev);
166 
167 	printf("\n");
168 	return;
169 
170 failread:
171 	printf("unable to read reg %d\n", cmd);
172 	iic_release_bus(sc->sc_tag, 0);
173 	return;
174 }
175 
176 
177 struct {
178 	char		sensor;
179 	u_int8_t	cmd;
180 } fintek_worklist[] = {
181 	{ F_VCC, FINTEK_VOLT0 },
182 	{ F_V1, FINTEK_VOLT1 },
183 	{ F_V2, FINTEK_VOLT2 },
184 	{ F_V3, FINTEK_VOLT3 },
185 	{ F_TEMP1, FINTEK_TEMP1 },
186 	{ F_TEMP2, FINTEK_TEMP2 },
187 	{ F_FAN1, FINTEK_FAN1 },
188 	{ F_FAN2, FINTEK_FAN2 }
189 };
190 #define FINTEK_WORKLIST_SZ (sizeof(fintek_worklist) / sizeof(fintek_worklist[0]))
191 
192 void
193 fintek_refresh(void *arg)
194 {
195 	struct fintek_softc *sc =  arg;
196 	u_int8_t cmd, data, data2;
197 	int i;
198 
199 	iic_acquire_bus(sc->sc_tag, 0);
200 
201 	for (i = 0; i < FINTEK_WORKLIST_SZ; i++){
202 		cmd = fintek_worklist[i].cmd;
203 		if (fintek_read_reg(sc, cmd, &data, sizeof data)) {
204 			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
205 			continue;
206 		}
207 		sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
208 		switch (fintek_worklist[i].sensor) {
209 		case  F_VCC:
210 			sc->sc_sensor[i].value = data * 16000;
211 			break;
212 		case  F_V1:
213 			/* FALLTHROUGH */
214 		case  F_V2:
215 			/* FALLTHROUGH */
216 		case  F_V3:
217 			sc->sc_sensor[i].value = data * 8000;
218 			break;
219 		case  F_TEMP1:
220 			/* FALLTHROUGH */
221 		case  F_TEMP2:
222 			sc->sc_sensor[i].value = 273150000 + data * 1000000;
223 			break;
224 		case  F_FAN1:
225 			/* FALLTHROUGH */
226 		case  F_FAN2:
227 			/* FANx LSB follows FANx MSB */
228 			cmd = fintek_worklist[i].cmd + 1;
229 			if (fintek_read_reg(sc, cmd, &data2, sizeof data2)) {
230 				sc->sc_sensor[i].flags |= SENSOR_FINVALID;
231 				continue;
232 			}
233 			if ((data == 0xff && data2 == 0xff) ||
234 			    (data == 0 && data2 == 0))
235 				sc->sc_sensor[i].value = 0;
236 			else
237 				sc->sc_sensor[i].value = 1500000 /
238 				    (data << 8 | data2);
239 			break;
240 		default:
241 			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
242 			break;
243 		}
244 	}
245 
246 	iic_release_bus(sc->sc_tag, 0);
247 }
248 
249 void
250 fintek_fullspeed(struct fintek_softc *sc)
251 {
252 	u_int8_t data;
253 
254 	data = FINTEK_FAN1_LINEAR_MODE | FINTEK_FAN2_LINEAR_MODE;
255 	fintek_write_reg(sc, FINTEK_CONFIG1, &data, sizeof data);
256 
257 	data = FINTEK_FAN1_MODE_MANUAL | FINTEK_FAN2_MODE_MANUAL;
258 	fintek_write_reg(sc, FINTEK_RSTCR, &data, sizeof data);
259 
260 	data = 0xff;		/* Maximum voltage */
261 	fintek_write_reg(sc, FINTEK_PWM_DUTY1, &data, sizeof data);
262 	fintek_write_reg(sc, FINTEK_PWM_DUTY2, &data, sizeof data);
263 }
264