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