xref: /openbsd/sys/dev/i2c/fcu.c (revision 404b540a)
1 /*	$OpenBSD: fcu.c,v 1.7 2007/06/24 05:34:35 dlg Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 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 /* FCU registers */
27 #define FCU_FAN_FAIL	0x0b		/* fans states in bits 0<1-6>7 */
28 #define FCU_FAN_ACTIVE	0x0d
29 #define FCU_FANREAD(x)	0x11 + (x)*2
30 #define FCU_FANSET(x)	0x10 + (x)*2
31 #define FCU_PWM_FAIL	0x2b
32 #define FCU_PWM_ACTIVE	0x2d
33 #define FCU_PWMREAD(x)	0x30 + (x)*2
34 
35 /* Sensors */
36 #define FCU_RPM1		0
37 #define FCU_RPM2		1
38 #define FCU_RPM3		2
39 #define FCU_RPM4		3
40 #define FCU_RPM5		4
41 #define FCU_RPM6		5
42 #define  FCU_FANS		6
43 #define FCU_PWM1		6
44 #define FCU_PWM2		7
45 #define  FCU_PWMS		2
46 #define FCU_NUM_SENSORS		8
47 
48 struct fcu_softc {
49 	struct device sc_dev;
50 	i2c_tag_t sc_tag;
51 	i2c_addr_t sc_addr;
52 
53 	struct ksensor sc_sensor[FCU_NUM_SENSORS];
54 	struct ksensordev sc_sensordev;
55 };
56 
57 int	fcu_match(struct device *, void *, void *);
58 void	fcu_attach(struct device *, struct device *, void *);
59 
60 void	fcu_refresh(void *);
61 
62 struct cfattach fcu_ca = {
63 	sizeof(struct fcu_softc), fcu_match, fcu_attach
64 };
65 
66 struct cfdriver fcu_cd = {
67 	NULL, "fcu", DV_DULL
68 };
69 
70 int
71 fcu_match(struct device *parent, void *match, void *aux)
72 {
73 	struct i2c_attach_args *ia = aux;
74 
75 	if (strcmp(ia->ia_name, "fcu") == 0)
76 		return (1);
77 	return (0);
78 }
79 
80 void
81 fcu_attach(struct device *parent, struct device *self, void *aux)
82 {
83 	struct fcu_softc *sc = (struct fcu_softc *)self;
84 	struct i2c_attach_args *ia = aux;
85 	int i;
86 
87 	sc->sc_tag = ia->ia_tag;
88 	sc->sc_addr = ia->ia_addr;
89 
90 	/* Initialize sensor data. */
91 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
92 	    sizeof(sc->sc_sensordev.xname));
93 	for (i = 0; i < FCU_FANS; i++)
94 		sc->sc_sensor[i].type = SENSOR_FANRPM;
95 	for (i = 0; i < FCU_PWMS; i++) {
96 		sc->sc_sensor[FCU_PWM1 + i].type = SENSOR_PERCENT;
97 		strlcpy(sc->sc_sensor[FCU_PWM1 + i].desc, "PWM",
98 		    sizeof(sc->sc_sensor[FCU_PWM1 + i].desc));
99 	}
100 
101 	if (sensor_task_register(sc, fcu_refresh, 5) == NULL) {
102 		printf(", unable to register update task\n");
103 		return;
104 	}
105 
106 	for (i = 0; i < FCU_NUM_SENSORS; i++)
107 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
108 	sensordev_install(&sc->sc_sensordev);
109 
110 	printf("\n");
111 }
112 
113 void
114 fcu_refresh(void *arg)
115 {
116 	struct fcu_softc *sc = arg;
117 	u_int8_t cmd, fail, fan[2], active;
118 	int i;
119 
120 	iic_acquire_bus(sc->sc_tag, 0);
121 
122 	cmd = FCU_FAN_FAIL;
123 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
124 	    sc->sc_addr, &cmd, sizeof cmd, &fail, sizeof fail, 0))
125 		goto abort;
126 	cmd = FCU_FAN_ACTIVE;
127 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
128 	    sc->sc_addr, &cmd, sizeof cmd, &active, sizeof active, 0))
129 		goto abort;
130 	fail &= active;
131 
132 	for (i = 0; i < FCU_FANS; i++) {
133 		if (fail & (1 << (i + 1)))
134 			sc->sc_sensor[i].flags |= SENSOR_FINVALID;
135 		else
136 			sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
137 	}
138 
139 	cmd = FCU_PWM_FAIL;
140 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
141 	    sc->sc_addr, &cmd, sizeof cmd, &fail, sizeof fail, 0))
142 		goto abort;
143 	cmd = FCU_PWM_ACTIVE;
144 	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
145 	    sc->sc_addr, &cmd, sizeof cmd, &active, sizeof active, 0))
146 		goto abort;
147 	fail &= active;
148 
149 	for (i = 0; i < FCU_PWMS; i++) {
150 		if (fail & (1 << (i + 1)))
151 			sc->sc_sensor[FCU_PWMS + i].flags |= SENSOR_FINVALID;
152 		else
153 			sc->sc_sensor[FCU_PWMS + i].flags &= ~SENSOR_FINVALID;
154 	}
155 
156 	for (i = 0; i < FCU_FANS; i++) {
157 		cmd = FCU_FANREAD(i + 1);
158 		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
159 		    sc->sc_addr, &cmd, sizeof cmd, &fan, sizeof fan, 0)) {
160 			sc->sc_sensor[FCU_RPM1 + i].flags |= SENSOR_FINVALID;
161 			continue;
162 		}
163 		sc->sc_sensor[FCU_RPM1 + i].value = (fan[0] << 5) | (fan[1] >> 3);
164 	}
165 
166 	for (i = 0; i < FCU_PWMS; i++) {
167 		cmd = FCU_PWMREAD(i + 1);
168 		if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
169 		    sc->sc_addr, &cmd, sizeof cmd, &fan, sizeof fan, 0)) {
170 			sc->sc_sensor[FCU_PWM1 + i].flags |= SENSOR_FINVALID;
171 			continue;
172 		}
173 		sc->sc_sensor[FCU_PWM1 + i].value = (fan[0] * 100 * 1000) / 255;
174 	}
175 
176 abort:
177 	iic_release_bus(sc->sc_tag, 0);
178 }
179