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