pcf8591_envctrl.c revision 1.11.2.1 1 /* $NetBSD: pcf8591_envctrl.c,v 1.11.2.1 2020/12/14 14:38:02 thorpej Exp $ */
2 /* $OpenBSD: pcf8591_envctrl.c,v 1.6 2007/10/25 21:17:20 kettenis Exp $ */
3
4 /*
5 * Copyright (c) 2006 Damien Miller <djm (at) openbsd.org>
6 * Copyright (c) 2007 Mark Kettenis <kettenis (at) openbsd.org>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21 #include <sys/cdefs.h>
22 __KERNEL_RCSID(0, "$NetBSD: pcf8591_envctrl.c,v 1.11.2.1 2020/12/14 14:38:02 thorpej Exp $");
23
24 #include <sys/param.h>
25 #include <sys/systm.h>
26 #include <sys/kernel.h>
27 #include <sys/device.h>
28
29 #include <dev/sysmon/sysmonvar.h>
30 #include <dev/sysmon/sysmon_taskq.h>
31
32 #include <machine/autoconf.h>
33
34 #include <dev/ofw/openfirm.h>
35 #include <dev/i2c/i2cvar.h>
36
37 #ifdef ECADC_DEBUG
38 #define DPRINTF printf
39 #else
40 #define DPRINTF if (0) printf
41 #endif
42
43 /* Translation tables contain 254 entries */
44 #define XLATE_SIZE 256
45 #define XLATE_MAX (XLATE_SIZE - 2)
46
47 #define PCF8591_CHANNELS 4
48
49 #define PCF8591_CTRL_CH0 0x00
50 #define PCF8591_CTRL_CH1 0x01
51 #define PCF8591_CTRL_CH2 0x02
52 #define PCF8591_CTRL_CH3 0x03
53 #define PCF8591_CTRL_AUTOINC 0x04
54 #define PCF8591_CTRL_OSCILLATOR 0x40
55
56 #define PCF8591_TEMP_SENS 0x00
57 #define PCF8591_CPU_FAN_CTRL 0x01
58 #define PCF8591_PS_FAN_CTRL 0x02
59
60 struct ecadc_channel {
61 u_int chan_num;
62 u_int chan_type;
63 envsys_data_t chan_sensor;
64 u_char *chan_xlate;
65 int64_t chan_factor;
66 int64_t chan_min;
67 int64_t chan_warn;
68 int64_t chan_crit;
69 u_int8_t chan_speed;
70 };
71
72 struct ecadc_softc {
73 device_t sc_dev;
74 i2c_tag_t sc_tag;
75 i2c_addr_t sc_addr;
76 u_char sc_ps_xlate[XLATE_SIZE];
77 u_char sc_cpu_xlate[XLATE_SIZE];
78 u_char sc_cpu_fan_spd[XLATE_SIZE];
79 u_int sc_nchan;
80 struct ecadc_channel sc_channels[PCF8591_CHANNELS];
81 struct sysmon_envsys *sc_sme;
82 int sc_hastimer;
83 callout_t sc_timer;
84 };
85
86 static int ecadc_match(device_t, cfdata_t, void *);
87 static void ecadc_attach(device_t, device_t, void *);
88 static int ecadc_detach(device_t, int);
89 static void ecadc_refresh(struct sysmon_envsys *, envsys_data_t *);
90 static void ecadc_get_limits(struct sysmon_envsys *, envsys_data_t *,
91 sysmon_envsys_lim_t *, u_int32_t *);
92 static void ecadc_timeout(void *);
93 static void ecadc_fan_adjust(void *);
94
95 CFATTACH_DECL3_NEW(ecadc, sizeof(struct ecadc_softc),
96 ecadc_match, ecadc_attach, ecadc_detach, NULL, NULL, NULL,
97 DVF_DETACH_SHUTDOWN);
98
99 static const struct device_compatible_entry compat_data[] = {
100 { "ecadc", 0 },
101 { NULL, 0 }
102 };
103
104 static int
105 ecadc_match(device_t parent, cfdata_t cf, void *aux)
106 {
107 struct i2c_attach_args *ia = aux;
108 int match_result;
109
110 if (iic_use_direct_match(ia, cf, compat_data, &match_result))
111 return match_result;
112
113 /* This driver is direct-config only. */
114
115 return 0;
116 }
117
118 static void
119 ecadc_attach(device_t parent, device_t self, void *aux)
120 {
121 struct i2c_attach_args *ia = aux;
122 struct ecadc_softc *sc = device_private(self);
123 u_char term[256];
124 u_char *cp, *desc;
125 int64_t minv, warnv, crit, num, den;
126 u_int8_t junk[PCF8591_CHANNELS + 1];
127 envsys_data_t *sensor;
128 int len, error, addr, chan, node = (int)ia->ia_cookie;
129 u_int i;
130
131 sc->sc_dev = self;
132 sc->sc_nchan = 0;
133 sc->sc_hastimer = 0;
134
135 DPRINTF("\n");
136 if ((len = OF_getprop(node, "thermisters", term,
137 sizeof(term))) < 0) {
138 aprint_error(": couldn't find \"thermisters\" property\n");
139 return;
140 }
141
142 if (OF_getprop(node, "cpu-temp-factors", &sc->sc_cpu_xlate[2],
143 XLATE_MAX) < 0) {
144 aprint_error(": couldn't find \"cpu-temp-factors\" property\n");
145 return;
146 }
147 sc->sc_cpu_xlate[0] = sc->sc_cpu_xlate[1] = sc->sc_cpu_xlate[2];
148
149 /* Only the Sun Enterprise 450 has these. */
150 OF_getprop(node, "ps-temp-factors", &sc->sc_ps_xlate[2], XLATE_MAX);
151 sc->sc_ps_xlate[0] = sc->sc_ps_xlate[1] = sc->sc_ps_xlate[2];
152
153 cp = term;
154 while (cp < term + len) {
155 addr = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
156 chan = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
157 minv = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
158 warnv = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
159 crit = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
160 num = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
161 den = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
162 desc = cp;
163 while (cp < term + len && *cp++);
164
165 if (addr != (ia->ia_addr << 1))
166 continue;
167
168 if (num == 0 || den == 0)
169 num = den = 1;
170
171 sc->sc_channels[sc->sc_nchan].chan_num = chan;
172 sc->sc_channels[sc->sc_nchan].chan_type = PCF8591_TEMP_SENS;
173
174 sensor = &sc->sc_channels[sc->sc_nchan].chan_sensor;
175 sensor->units = ENVSYS_STEMP;
176 sensor->flags |= ENVSYS_FMONLIMITS;
177 sensor->state = ENVSYS_SINVALID;
178 strlcpy(sensor->desc, desc, sizeof(sensor->desc));
179
180 if (strncmp(desc, "CPU", 3) == 0) {
181 sc->sc_channels[sc->sc_nchan].chan_xlate =
182 sc->sc_cpu_xlate;
183 DPRINTF("%s: "
184 "added %s sensor (chan %d) with cpu_xlate\n",
185 device_xname(sc->sc_dev), desc, chan);
186 } else if (strncmp(desc, "PS", 2) == 0) {
187 sc->sc_channels[sc->sc_nchan].chan_xlate =
188 sc->sc_ps_xlate;
189 DPRINTF("%s: "
190 "added %s sensor (chan %d) with ps_xlate\n",
191 device_xname(sc->sc_dev), desc, chan);
192 } else {
193 sc->sc_channels[sc->sc_nchan].chan_factor =
194 (1000000 * num) / den;
195 DPRINTF("%s: "
196 "added %s sensor (chan %d) without xlate\n",
197 device_xname(sc->sc_dev), desc, chan);
198 }
199 sc->sc_channels[sc->sc_nchan].chan_min =
200 273150000 + 1000000 * minv;
201 sc->sc_channels[sc->sc_nchan].chan_warn =
202 273150000 + 1000000 * warnv;
203 sc->sc_channels[sc->sc_nchan].chan_crit =
204 273150000 + 1000000 * crit;
205 sc->sc_nchan++;
206 }
207
208 /*
209 * Fan speed changing information is missing from OFW
210 * The E250 CPU fan is connected to the sensor at addr 0x4a, channel 1
211 */
212 if (ia->ia_addr == 0x4a && !strcmp(machine_model, "SUNW,Ultra-250") &&
213 OF_getprop(node, "cpu-fan-speeds", &sc->sc_cpu_fan_spd,
214 XLATE_MAX) > 0) {
215 sc->sc_channels[sc->sc_nchan].chan_num = 1;
216 sc->sc_channels[sc->sc_nchan].chan_type = PCF8591_CPU_FAN_CTRL;
217 sc->sc_channels[sc->sc_nchan].chan_speed = 0;
218 sensor = &sc->sc_channels[sc->sc_nchan].chan_sensor;
219 sensor->units = ENVSYS_INTEGER;
220 sensor->flags = ENVSYS_FMONNOTSUPP;
221 sensor->state = ENVSYS_SINVALID;
222 strlcpy(sensor->desc, "CPUFAN", sizeof(sensor->desc));
223 sc->sc_channels[sc->sc_nchan].chan_xlate = sc->sc_cpu_fan_spd;
224 DPRINTF("%s: "
225 "added CPUFAN sensor (chan %d) with cpu-fan xlate\n",
226 device_xname(sc->sc_dev),
227 sc->sc_channels[sc->sc_nchan].chan_num);
228 sc->sc_nchan++;
229
230 sc->sc_hastimer = 1;
231 }
232
233 sc->sc_tag = ia->ia_tag;
234 sc->sc_addr = ia->ia_addr;
235
236 iic_acquire_bus(sc->sc_tag, 0);
237
238 /* Try a read now, so we can fail if this component isn't present */
239 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
240 NULL, 0, junk, sc->sc_nchan + 1, 0)) {
241 aprint_normal(": read failed\n");
242 iic_release_bus(sc->sc_tag, 0);
243 return;
244 }
245
246 iic_release_bus(sc->sc_tag, 0);
247
248 /* Hook us into the sysmon_envsys subsystem */
249 sc->sc_sme = sysmon_envsys_create();
250 sc->sc_sme->sme_name = device_xname(self);
251 sc->sc_sme->sme_cookie = sc;
252 sc->sc_sme->sme_refresh = ecadc_refresh;
253 sc->sc_sme->sme_get_limits = ecadc_get_limits;
254
255 /* Initialize sensor data. */
256 for (i = 0; i < sc->sc_nchan; i++)
257 sysmon_envsys_sensor_attach(sc->sc_sme,
258 &sc->sc_channels[i].chan_sensor);
259
260 error = sysmon_envsys_register(sc->sc_sme);
261 if (error) {
262 aprint_error_dev(self, "error %d registering with sysmon\n",
263 error);
264 sysmon_envsys_destroy(sc->sc_sme);
265 sc->sc_sme = NULL;
266 return;
267 }
268
269 if (sc->sc_hastimer) {
270 callout_init(&sc->sc_timer, CALLOUT_MPSAFE);
271 callout_reset(&sc->sc_timer, hz*20, ecadc_timeout, sc);
272 }
273
274 aprint_naive(": Temp Sensors\n");
275 aprint_normal(": %s Temp Sensors (%d channels)\n", ia->ia_name,
276 sc->sc_nchan);
277 }
278
279 static int
280 ecadc_detach(device_t self, int flags)
281 {
282 struct ecadc_softc *sc = device_private(self);
283 if (sc->sc_hastimer) {
284 callout_halt(&sc->sc_timer, NULL);
285 callout_destroy(&sc->sc_timer);
286 }
287
288 if (sc->sc_sme != NULL)
289 sysmon_envsys_unregister(sc->sc_sme);
290
291 return 0;
292 }
293
294 static void
295 ecadc_refresh(struct sysmon_envsys *sme, envsys_data_t *sensor)
296 {
297 struct ecadc_softc *sc = sme->sme_cookie;
298 u_int i;
299 u_int8_t data[PCF8591_CHANNELS + 1];
300 u_int8_t ctrl = PCF8591_CTRL_CH0 | PCF8591_CTRL_AUTOINC |
301 PCF8591_CTRL_OSCILLATOR;
302
303 if (iic_acquire_bus(sc->sc_tag, 0))
304 return;
305 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
306 &ctrl, 1, NULL, 0, 0)) {
307 iic_release_bus(sc->sc_tag, 0);
308 return;
309 }
310 /*
311 * Each data byte that we read is the result of the previous request,
312 * so read num_channels + 1 and update envsys values from chan + 1.
313 */
314 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
315 NULL, 0, data, PCF8591_CHANNELS + 1, 0)) {
316 iic_release_bus(sc->sc_tag, 0);
317 return;
318 }
319 iic_release_bus(sc->sc_tag, 0);
320
321 /* Temperature with/without translation or relative (ADC value) */
322 for (i = 0; i < sc->sc_nchan; i++) {
323 struct ecadc_channel *chp = &sc->sc_channels[i];
324
325 if (chp->chan_type == PCF8591_TEMP_SENS) {
326
327 /* Encode the raw value to use for the fan control */
328 if (chp->chan_xlate) {
329 int32_t temp;
330
331 temp = 273150000 + 1000000 *
332 chp->chan_xlate[data[1 + chp->chan_num]];
333 temp &= ~0xff;
334 temp += data[1 + chp->chan_num];
335 chp->chan_sensor.value_cur = temp;
336 DPRINTF("%s: xlate %s sensor = %d"
337 " (0x%x > 0x%x)\n",
338 device_xname(sc->sc_dev),
339 chp->chan_sensor.desc, temp,
340 data[1 + chp->chan_num],
341 chp->chan_xlate[data[1 + chp->chan_num]]);
342 } else {
343 chp->chan_sensor.value_cur = 273150000 +
344 chp->chan_factor * data[1 + chp->chan_num];
345 DPRINTF("%s: read %s sensor = %d (0x%x)\n",
346 device_xname(sc->sc_dev),
347 chp->chan_sensor.desc,
348 chp->chan_sensor.value_cur,
349 data[1 + chp->chan_num]);
350 }
351 chp->chan_sensor.flags |= ENVSYS_FMONLIMITS;
352 }
353 if (chp->chan_type == PCF8591_CPU_FAN_CTRL ||
354 chp->chan_type == PCF8591_PS_FAN_CTRL)
355 chp->chan_sensor.value_cur = data[1 + chp->chan_num];
356
357 chp->chan_sensor.state = ENVSYS_SVALID;
358 chp->chan_sensor.flags &= ~ENVSYS_FNEED_REFRESH;
359 }
360 }
361
362 static void
363 ecadc_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
364 sysmon_envsys_lim_t *limits, u_int32_t *props)
365 {
366 struct ecadc_softc *sc = sme->sme_cookie;
367 int i;
368
369 for (i = 0; i < sc->sc_nchan; i++) {
370 if (edata != &sc->sc_channels[i].chan_sensor)
371 continue;
372 if (sc->sc_channels[i].chan_type == PCF8591_TEMP_SENS) {
373 *props |= PROP_WARNMIN|PROP_WARNMAX|PROP_CRITMAX;
374 limits->sel_warnmin = sc->sc_channels[i].chan_min;
375 limits->sel_warnmax = sc->sc_channels[i].chan_warn;
376 limits->sel_critmax = sc->sc_channels[i].chan_crit;
377 }
378 return;
379 }
380 }
381
382 static void
383 ecadc_timeout(void *v)
384 {
385 struct ecadc_softc *sc = v;
386
387 sysmon_task_queue_sched(0, ecadc_fan_adjust, sc);
388 callout_reset(&sc->sc_timer, hz*60, ecadc_timeout, sc);
389 }
390
391 static bool
392 is_cpu_temp(const envsys_data_t *edata)
393 {
394 if (edata->units != ENVSYS_STEMP)
395 return false;
396 return strncmp(edata->desc, "CPU", 3) == 0;
397 }
398
399 static int
400 ecadc_set_fan_speed(struct ecadc_softc *sc, u_int8_t chan, u_int8_t val)
401 {
402 u_int8_t ctrl = PCF8591_CTRL_AUTOINC | PCF8591_CTRL_OSCILLATOR;
403 int ret;
404
405 ctrl |= chan;
406 ret = iic_acquire_bus(sc->sc_tag, 0);
407 if (ret) {
408 aprint_error_dev(sc->sc_dev,
409 "error acquiring i2c bus (ch %d)\n", chan);
410 return ret;
411 }
412 ret = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
413 &ctrl, 1, &val, 1, 0);
414 if (ret)
415 aprint_error_dev(sc->sc_dev,
416 "error changing fan speed (ch %d)\n", chan);
417 else
418 DPRINTF("%s changed fan speed (ch %d) to 0x%x\n",
419 device_xname(sc->sc_dev), chan, val);
420 iic_release_bus(sc->sc_tag, 0);
421 return ret;
422 }
423
424 static void
425 ecadc_fan_adjust(void *v)
426 {
427 struct ecadc_softc *sc = v;
428 int i;
429 u_int8_t temp, speed;
430
431 for (i = 0; i < sc->sc_nchan; i++) {
432 struct ecadc_channel *chp = &sc->sc_channels[i];
433
434 if (chp->chan_type == PCF8591_CPU_FAN_CTRL) {
435 /* Extract the raw value from the max CPU temp */
436 temp = sysmon_envsys_get_max_value(is_cpu_temp, true)
437 & 0xff;
438 if (!temp) {
439 aprint_error_dev(sc->sc_dev,
440 "skipping temp adjustment"
441 " - no sensor values\n");
442 return;
443 }
444 if (temp > XLATE_MAX)
445 temp = XLATE_MAX;
446 speed = chp->chan_xlate[temp];
447 if (speed != chp->chan_speed) {
448 if (!ecadc_set_fan_speed(sc, chp->chan_num,
449 speed))
450 chp->chan_speed = speed;
451 }
452 }
453 }
454 }
455