1 /* $OpenBSD: fcu.c,v 1.8 2022/04/06 18:59:28 naddy 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 const 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
fcu_match(struct device * parent,void * match,void * aux)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
fcu_attach(struct device * parent,struct device * self,void * aux)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
fcu_refresh(void * arg)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