1*471aeecfSnaddy /* $OpenBSD: fcu.c,v 1.8 2022/04/06 18:59:28 naddy Exp $ */
2eacaf845Sderaadt
3eacaf845Sderaadt /*
4eacaf845Sderaadt * Copyright (c) 2005 Mark Kettenis
5eacaf845Sderaadt *
6eacaf845Sderaadt * Permission to use, copy, modify, and distribute this software for any
7eacaf845Sderaadt * purpose with or without fee is hereby granted, provided that the above
8eacaf845Sderaadt * copyright notice and this permission notice appear in all copies.
9eacaf845Sderaadt *
10eacaf845Sderaadt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11eacaf845Sderaadt * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12eacaf845Sderaadt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13eacaf845Sderaadt * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14eacaf845Sderaadt * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15eacaf845Sderaadt * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16eacaf845Sderaadt * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17eacaf845Sderaadt */
18eacaf845Sderaadt
19eacaf845Sderaadt #include <sys/param.h>
20eacaf845Sderaadt #include <sys/systm.h>
21eacaf845Sderaadt #include <sys/device.h>
22eacaf845Sderaadt #include <sys/sensors.h>
23eacaf845Sderaadt
24eacaf845Sderaadt #include <dev/i2c/i2cvar.h>
25eacaf845Sderaadt
26eacaf845Sderaadt /* FCU registers */
27eacaf845Sderaadt #define FCU_FAN_FAIL 0x0b /* fans states in bits 0<1-6>7 */
28eacaf845Sderaadt #define FCU_FAN_ACTIVE 0x0d
29eacaf845Sderaadt #define FCU_FANREAD(x) 0x11 + (x)*2
30eacaf845Sderaadt #define FCU_FANSET(x) 0x10 + (x)*2
31eacaf845Sderaadt #define FCU_PWM_FAIL 0x2b
32eacaf845Sderaadt #define FCU_PWM_ACTIVE 0x2d
33eacaf845Sderaadt #define FCU_PWMREAD(x) 0x30 + (x)*2
34eacaf845Sderaadt
35eacaf845Sderaadt /* Sensors */
36eacaf845Sderaadt #define FCU_RPM1 0
37eacaf845Sderaadt #define FCU_RPM2 1
38eacaf845Sderaadt #define FCU_RPM3 2
39eacaf845Sderaadt #define FCU_RPM4 3
40eacaf845Sderaadt #define FCU_RPM5 4
41eacaf845Sderaadt #define FCU_RPM6 5
42eacaf845Sderaadt #define FCU_FANS 6
43eacaf845Sderaadt #define FCU_PWM1 6
44eacaf845Sderaadt #define FCU_PWM2 7
45eacaf845Sderaadt #define FCU_PWMS 2
46eacaf845Sderaadt #define FCU_NUM_SENSORS 8
47eacaf845Sderaadt
48eacaf845Sderaadt struct fcu_softc {
49eacaf845Sderaadt struct device sc_dev;
50eacaf845Sderaadt i2c_tag_t sc_tag;
51eacaf845Sderaadt i2c_addr_t sc_addr;
52eacaf845Sderaadt
53275cbf62Sderaadt struct ksensor sc_sensor[FCU_NUM_SENSORS];
54275cbf62Sderaadt struct ksensordev sc_sensordev;
55eacaf845Sderaadt };
56eacaf845Sderaadt
57eacaf845Sderaadt int fcu_match(struct device *, void *, void *);
58eacaf845Sderaadt void fcu_attach(struct device *, struct device *, void *);
59eacaf845Sderaadt
60eacaf845Sderaadt void fcu_refresh(void *);
61eacaf845Sderaadt
62*471aeecfSnaddy const struct cfattach fcu_ca = {
63eacaf845Sderaadt sizeof(struct fcu_softc), fcu_match, fcu_attach
64eacaf845Sderaadt };
65eacaf845Sderaadt
66eacaf845Sderaadt struct cfdriver fcu_cd = {
67eacaf845Sderaadt NULL, "fcu", DV_DULL
68eacaf845Sderaadt };
69eacaf845Sderaadt
70eacaf845Sderaadt int
fcu_match(struct device * parent,void * match,void * aux)71eacaf845Sderaadt fcu_match(struct device *parent, void *match, void *aux)
72eacaf845Sderaadt {
73eacaf845Sderaadt struct i2c_attach_args *ia = aux;
74eacaf845Sderaadt
759b06f7aeSderaadt if (strcmp(ia->ia_name, "fcu") == 0)
76eacaf845Sderaadt return (1);
77eacaf845Sderaadt return (0);
78eacaf845Sderaadt }
79eacaf845Sderaadt
80eacaf845Sderaadt void
fcu_attach(struct device * parent,struct device * self,void * aux)81eacaf845Sderaadt fcu_attach(struct device *parent, struct device *self, void *aux)
82eacaf845Sderaadt {
83eacaf845Sderaadt struct fcu_softc *sc = (struct fcu_softc *)self;
84eacaf845Sderaadt struct i2c_attach_args *ia = aux;
85eacaf845Sderaadt int i;
86eacaf845Sderaadt
87eacaf845Sderaadt sc->sc_tag = ia->ia_tag;
88eacaf845Sderaadt sc->sc_addr = ia->ia_addr;
89eacaf845Sderaadt
90eacaf845Sderaadt /* Initialize sensor data. */
9127515a6bSderaadt strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
9227515a6bSderaadt sizeof(sc->sc_sensordev.xname));
9327515a6bSderaadt for (i = 0; i < FCU_FANS; i++)
94eacaf845Sderaadt sc->sc_sensor[i].type = SENSOR_FANRPM;
95eacaf845Sderaadt for (i = 0; i < FCU_PWMS; i++) {
964017dd19Sderaadt sc->sc_sensor[FCU_PWM1 + i].type = SENSOR_PERCENT;
9727515a6bSderaadt strlcpy(sc->sc_sensor[FCU_PWM1 + i].desc, "PWM",
9827515a6bSderaadt sizeof(sc->sc_sensor[FCU_PWM1 + i].desc));
99eacaf845Sderaadt }
100eacaf845Sderaadt
101abd9fc28Sdlg if (sensor_task_register(sc, fcu_refresh, 5) == NULL) {
102eacaf845Sderaadt printf(", unable to register update task\n");
103eacaf845Sderaadt return;
104eacaf845Sderaadt }
105eacaf845Sderaadt
106eacaf845Sderaadt for (i = 0; i < FCU_NUM_SENSORS; i++)
10727515a6bSderaadt sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]);
10827515a6bSderaadt sensordev_install(&sc->sc_sensordev);
109eacaf845Sderaadt
110eacaf845Sderaadt printf("\n");
111eacaf845Sderaadt }
112eacaf845Sderaadt
113eacaf845Sderaadt void
fcu_refresh(void * arg)114eacaf845Sderaadt fcu_refresh(void *arg)
115eacaf845Sderaadt {
116eacaf845Sderaadt struct fcu_softc *sc = arg;
117eacaf845Sderaadt u_int8_t cmd, fail, fan[2], active;
118eacaf845Sderaadt int i;
119eacaf845Sderaadt
120eacaf845Sderaadt iic_acquire_bus(sc->sc_tag, 0);
121eacaf845Sderaadt
122eacaf845Sderaadt cmd = FCU_FAN_FAIL;
123eacaf845Sderaadt if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
124eacaf845Sderaadt sc->sc_addr, &cmd, sizeof cmd, &fail, sizeof fail, 0))
125eacaf845Sderaadt goto abort;
126eacaf845Sderaadt cmd = FCU_FAN_ACTIVE;
127eacaf845Sderaadt if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
128eacaf845Sderaadt sc->sc_addr, &cmd, sizeof cmd, &active, sizeof active, 0))
129eacaf845Sderaadt goto abort;
130eacaf845Sderaadt fail &= active;
131eacaf845Sderaadt
132eacaf845Sderaadt for (i = 0; i < FCU_FANS; i++) {
133eacaf845Sderaadt if (fail & (1 << (i + 1)))
134eacaf845Sderaadt sc->sc_sensor[i].flags |= SENSOR_FINVALID;
135eacaf845Sderaadt else
136eacaf845Sderaadt sc->sc_sensor[i].flags &= ~SENSOR_FINVALID;
137eacaf845Sderaadt }
138eacaf845Sderaadt
139eacaf845Sderaadt cmd = FCU_PWM_FAIL;
140eacaf845Sderaadt if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
141eacaf845Sderaadt sc->sc_addr, &cmd, sizeof cmd, &fail, sizeof fail, 0))
142eacaf845Sderaadt goto abort;
143eacaf845Sderaadt cmd = FCU_PWM_ACTIVE;
144eacaf845Sderaadt if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
145eacaf845Sderaadt sc->sc_addr, &cmd, sizeof cmd, &active, sizeof active, 0))
146eacaf845Sderaadt goto abort;
147eacaf845Sderaadt fail &= active;
148eacaf845Sderaadt
149eacaf845Sderaadt for (i = 0; i < FCU_PWMS; i++) {
150eacaf845Sderaadt if (fail & (1 << (i + 1)))
151eacaf845Sderaadt sc->sc_sensor[FCU_PWMS + i].flags |= SENSOR_FINVALID;
152eacaf845Sderaadt else
153eacaf845Sderaadt sc->sc_sensor[FCU_PWMS + i].flags &= ~SENSOR_FINVALID;
154eacaf845Sderaadt }
155eacaf845Sderaadt
156eacaf845Sderaadt for (i = 0; i < FCU_FANS; i++) {
157eacaf845Sderaadt cmd = FCU_FANREAD(i + 1);
158eacaf845Sderaadt if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
159eacaf845Sderaadt sc->sc_addr, &cmd, sizeof cmd, &fan, sizeof fan, 0)) {
160eacaf845Sderaadt sc->sc_sensor[FCU_RPM1 + i].flags |= SENSOR_FINVALID;
161eacaf845Sderaadt continue;
162eacaf845Sderaadt }
163eacaf845Sderaadt sc->sc_sensor[FCU_RPM1 + i].value = (fan[0] << 5) | (fan[1] >> 3);
164eacaf845Sderaadt }
165eacaf845Sderaadt
166eacaf845Sderaadt for (i = 0; i < FCU_PWMS; i++) {
167eacaf845Sderaadt cmd = FCU_PWMREAD(i + 1);
168eacaf845Sderaadt if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
169eacaf845Sderaadt sc->sc_addr, &cmd, sizeof cmd, &fan, sizeof fan, 0)) {
170eacaf845Sderaadt sc->sc_sensor[FCU_PWM1 + i].flags |= SENSOR_FINVALID;
171eacaf845Sderaadt continue;
172eacaf845Sderaadt }
1734017dd19Sderaadt sc->sc_sensor[FCU_PWM1 + i].value = (fan[0] * 100 * 1000) / 255;
174eacaf845Sderaadt }
175eacaf845Sderaadt
176eacaf845Sderaadt abort:
177eacaf845Sderaadt iic_release_bus(sc->sc_tag, 0);
178eacaf845Sderaadt }
179