No empty .Rs/.Re
[netbsd-mini2440.git] / sys / arch / sparc64 / dev / envctrl.c
blobf8a9527c838dc44e352dc55d7c46ea7bc2260c33
1 /* $NetBSD: envctrl.c,v 1.10 2008/03/26 20:21:38 xtraeme Exp $ */
3 /*-
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tobias Nygren.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * SUNW,envctrl Sun Ultra Enterprise 450 environmental monitoring driver
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: envctrl.c,v 1.10 2008/03/26 20:21:38 xtraeme Exp $");
39 #include <sys/param.h>
40 #include <sys/device.h>
41 #include <sys/kernel.h>
42 #include <sys/systm.h>
43 #include <sys/kthread.h>
44 #include <sys/condvar.h>
45 #include <sys/mutex.h>
46 #include <sys/envsys.h>
48 #include <machine/autoconf.h>
49 #include <machine/promlib.h>
51 #include <dev/ebus/ebusreg.h>
52 #include <dev/ebus/ebusvar.h>
53 #include <dev/i2c/i2cvar.h>
54 #include <sparc64/dev/envctrlreg.h>
55 #include <dev/sysmon/sysmonvar.h>
57 #include <dev/i2c/pcf8583reg.h> /* for WDT */
58 #include <dev/ic/pcf8584var.h>
60 static int envctrlmatch(device_t, cfdata_t, void *);
61 static void envctrlattach(device_t, device_t, void *);
63 struct envctrl_softc {
64 device_t sc_dev;
65 bus_space_tag_t sc_iot;
66 bus_space_handle_t sc_ioh;
68 struct pcf8584_handle sc_pcfiic;
70 lwp_t *sc_thread;
71 kcondvar_t sc_sleepcond;
72 kmutex_t sc_sleepmtx;
74 struct sysmon_envsys *sc_sme;
75 envsys_data_t sc_sensor[13];
76 uint8_t sc_keyswitch;
77 uint8_t sc_fanstate;
78 uint8_t sc_ps_state[3];
79 int sc_ps_temp_factors[256];
80 int sc_cpu_temp_factors[256];
81 uint8_t sc_ps_fan_speeds[112];
82 uint8_t sc_cpu_fan_speeds[112];
85 CFATTACH_DECL_NEW(envctrl, sizeof(struct envctrl_softc),
86 envctrlmatch, envctrlattach, NULL, NULL);
88 static void envctrl_thread(void *);
89 static void envctrl_sleep(struct envctrl_softc *, int);
90 static int envctrl_write_1(struct envctrl_softc *, int, uint8_t);
91 static int envctrl_write_2(struct envctrl_softc *, int, uint8_t, uint8_t);
92 static int envctrl_read(struct envctrl_softc *, int, uint8_t *, int);
93 static int envctrl_get_cputemp(struct envctrl_softc *, uint8_t);
94 static int envctrl_get_pstemp(struct envctrl_softc *, uint8_t);
95 static int envctrl_get_ambtemp(struct envctrl_softc *);
96 static int envctrl_set_fanvoltage(struct envctrl_softc *, uint8_t, uint8_t);
97 static uint8_t envctrl_cputemp_to_voltage(struct envctrl_softc *, int);
98 static uint8_t envctrl_pstemp_to_voltage(struct envctrl_softc *, int);
99 static void envctrl_init_components(struct envctrl_softc *);
100 static int envctrl_init_tables(struct envctrl_softc *, int);
101 static void envctrl_update_sensors(struct envctrl_softc *);
102 static void envctrl_interpolate_ob_table(int *, uint8_t *);
104 static int
105 envctrlmatch(device_t parent, cfdata_t cf, void *aux)
107 struct ebus_attach_args *ea = aux;
109 return (strcmp("SUNW,envctrl", ea->ea_name) == 0);
112 static void
113 envctrlattach(device_t parent, device_t self, void *aux)
115 struct envctrl_softc *sc = device_private(self);
116 struct ebus_attach_args *ea = aux;
117 bus_addr_t devaddr;
118 int i, error;
120 sc->sc_dev = self;
121 sc->sc_iot = ea->ea_bustag;
122 devaddr = EBUS_ADDR_FROM_REG(&ea->ea_reg[0]);
123 if (bus_space_map(sc->sc_iot, devaddr, ea->ea_reg[0].size,
124 0, &sc->sc_ioh) != 0) {
125 aprint_error(": unable to map device registers\n");
126 return;
128 if (envctrl_init_tables(sc, ea->ea_node) != 0) {
129 aprint_error(": unable to initialize tables\n");
130 return;
133 * initialize envctrl bus
135 sc->sc_pcfiic.ha_parent = self;
136 sc->sc_pcfiic.ha_iot = sc->sc_iot;
137 sc->sc_pcfiic.ha_ioh = sc->sc_ioh;
138 pcf8584_init(&sc->sc_pcfiic);
140 if (envctrl_write_1(sc, ENVCTRL_FANFAIL_ADDR, 0xFF)) {
141 aprint_error(": i2c probe failed\n");
142 return;
144 aprint_normal("\n%s: Ultra Enterprise 450 environmental monitoring\n",
145 device_xname(self));
147 envctrl_init_components(sc);
150 * fill envsys sensor structures
152 sc->sc_sme = sysmon_envsys_create();
154 for (i = 0; i < 8; i++)
155 sc->sc_sensor[i].units = ENVSYS_STEMP;
157 for (i = 0; i < 4; i++)
158 sprintf(sc->sc_sensor[i].desc, "CPU%i", i);
160 for (i = 4; i < 7; i++)
161 sprintf(sc->sc_sensor[i].desc, "PS%i", i - 4);
163 for (i = 8; i < 10; i++)
164 sc->sc_sensor[i].units = ENVSYS_SVOLTS_DC;
166 for (i = 10; i < 12; i++) {
167 sc->sc_sensor[i].units = ENVSYS_INTEGER;
168 sc->sc_sensor[i].flags = ENVSYS_FMONNOTSUPP;
171 sprintf(sc->sc_sensor[7].desc, "ambient");
172 sprintf(sc->sc_sensor[8].desc, "cpufan voltage");
173 sprintf(sc->sc_sensor[9].desc, "psfan voltage");
174 sprintf(sc->sc_sensor[10].desc, "ps failed");
175 sprintf(sc->sc_sensor[11].desc, "fans failed");
177 for (i = 0; i < 12; i++) {
178 if (sysmon_envsys_sensor_attach(sc->sc_sme,
179 &sc->sc_sensor[i])) {
180 sysmon_envsys_destroy(sc->sc_sme);
181 return;
185 sc->sc_sme->sme_name = device_xname(self);
186 sc->sc_sme->sme_flags = SME_DISABLE_REFRESH;
188 if (sysmon_envsys_register(sc->sc_sme)) {
189 aprint_error("%s: unable to register with sysmon\n",
190 device_xname(self));
191 sysmon_envsys_destroy(sc->sc_sme);
192 return;
195 envctrl_update_sensors(sc);
197 for (i = 0; i < 3; i++) {
198 aprint_normal("%s: PS %i: ", device_xname(self), i);
199 if (sc->sc_ps_state[i] & ENVCTRL_PS_PRESENT) {
200 aprint_normal("absent\n");
201 continue;
203 aprint_normal("%iW, %s",
204 (sc->sc_ps_state[i] & ENVCTRL_PS_550W) ? 650 : 550,
205 (sc->sc_ps_state[i] & ENVCTRL_PS_OK) ?
206 "online" : "FAILED");
207 if ((sc->sc_ps_state[i] & ENVCTRL_PS_OVERLOAD) == 0)
208 aprint_normal(" ***OVERLOADED***");
209 if ((sc->sc_ps_state[i] & ENVCTRL_PS_LOADSHARE_ERROR) == 0)
210 aprint_normal(" ***LOADSHARE ERROR***");
211 aprint_normal("\n");
214 aprint_verbose("%s: keyswitch is ", device_xname(self));
215 if ((sc->sc_keyswitch & ENVCTRL_KEY_LOCK) == 0)
216 aprint_verbose("unlocked\n");
217 else {
218 if ((sc->sc_keyswitch & ENVCTRL_KEY_DIAG) == 0)
219 aprint_verbose("in diagnostic mode\n");
220 else
221 aprint_verbose("locked\n");
224 mutex_init(&sc->sc_sleepmtx, MUTEX_DEFAULT, IPL_NONE);
225 cv_init(&sc->sc_sleepcond, "envidle");
227 error = kthread_create(PRI_NONE, 0, NULL, envctrl_thread, sc,
228 &sc->sc_thread, "envctrl");
229 if (error)
230 panic("cannot start envctrl thread; error %d", error);
233 static void
234 envctrl_sleep(struct envctrl_softc *sc, int ms)
237 cv_timedwait(&sc->sc_sleepcond, &sc->sc_sleepmtx, mstohz(ms));
241 * read cpu temperature, in microCelcius
243 static int
244 envctrl_get_cputemp(struct envctrl_softc *sc, uint8_t cpu)
246 uint8_t r;
248 if (envctrl_write_1(sc, ENVCTRL_CPUTEMP_ADDR, (1 << 6) | cpu))
249 return -1;
250 if (envctrl_read(sc, ENVCTRL_CPUTEMP_ADDR, &r, 1))
251 return -1;
252 if (envctrl_write_1(sc, ENVCTRL_CPUTEMP_ADDR, 0))
253 return -1;
255 return sc->sc_cpu_temp_factors[r];
259 * read power supply temperature in microCelcius
261 static int
262 envctrl_get_pstemp(struct envctrl_softc *sc, uint8_t ps)
264 uint8_t r;
266 if (envctrl_write_1(sc, ENVCTRL_PS0TEMP_ADDR + ps, (1 << 6)))
267 return -1;
268 if (envctrl_read(sc, ENVCTRL_PS0TEMP_ADDR + ps, &r, 1))
269 return -1;
270 if (envctrl_write_1(sc, ENVCTRL_PS0TEMP_ADDR + ps, 0))
271 return -1;
273 return sc->sc_ps_temp_factors[r];
277 * read ambient temperature in microCelcius
279 static int
280 envctrl_get_ambtemp(struct envctrl_softc *sc)
282 int8_t temp[2];
284 if (envctrl_write_1(sc, ENVCTRL_AMB_ADDR, 0))
285 return -1;
286 if (envctrl_read(sc, ENVCTRL_AMB_ADDR, temp, 2))
287 return -1;
289 return ((temp[0] << 1) | ((temp[1] >> 7) & 1)) * 500000;
293 * set fan voltage, 0 - 63, scaled to 0V-12V.
295 static int
296 envctrl_set_fanvoltage(struct envctrl_softc *sc, uint8_t port, uint8_t value)
299 return envctrl_write_2(sc, ENVCTRL_FANVOLTAGE_ADDR, port, value);
302 static uint8_t
303 envctrl_cputemp_to_voltage(struct envctrl_softc *sc, int temp)
305 uint8_t ret;
307 temp -= 20; /* magic offset, from opensolaris */
309 if (temp < 0)
310 temp = 0;
311 if (temp > 111)
312 temp = 111;
314 ret = sc->sc_cpu_fan_speeds[temp];
315 if (ret < ENVCTRL_FANVOLTAGE_MIN)
316 return ENVCTRL_FANVOLTAGE_MIN;
317 if (ret > ENVCTRL_FANVOLTAGE_MAX)
318 return ENVCTRL_FANVOLTAGE_MAX;
319 return ret;
322 static uint8_t
323 envctrl_pstemp_to_voltage(struct envctrl_softc *sc, int temp)
325 uint8_t ret;
327 temp -= 30; /* magic offset, from opensolaris */
329 if (temp < 0)
330 temp = 0;
332 if (temp > 111)
333 temp = 111;
335 ret = sc->sc_ps_fan_speeds[temp];
336 if (ret < ENVCTRL_FANVOLTAGE_MIN)
337 return ENVCTRL_FANVOLTAGE_MIN;
338 if (ret > ENVCTRL_FANVOLTAGE_MAX)
339 return ENVCTRL_FANVOLTAGE_MAX;
340 return ret;
343 static void
344 envctrl_init_components(struct envctrl_softc *sc)
347 /* configure ports as pure inputs */
348 envctrl_write_1(sc, ENVCTRL_FANFAIL_ADDR, ~0);
349 envctrl_write_1(sc, ENVCTRL_PS0_ADDR, ~0);
350 envctrl_write_1(sc, ENVCTRL_PS1_ADDR, ~0);
351 envctrl_write_1(sc, ENVCTRL_PS2_ADDR, ~0);
353 /* light up power LED */
354 envctrl_write_1(sc, ENVCTRL_LED_ADDR, ~ENVCTRL_LED_PWR);
357 * Set fans to full speed. The last two ports are usually not
358 * connected, but it doesn't hurt to enable them.
360 envctrl_set_fanvoltage(sc, ENVCTRL_FANPORT_CPU, ENVCTRL_FANVOLTAGE_MAX);
361 envctrl_set_fanvoltage(sc, ENVCTRL_FANPORT_PS, ENVCTRL_FANVOLTAGE_MAX);
362 envctrl_set_fanvoltage(sc, ENVCTRL_FANPORT_AFB, ENVCTRL_FANVOLTAGE_MAX);
363 envctrl_set_fanvoltage(sc, 3, ENVCTRL_FANVOLTAGE_MAX);
365 /* set fan watchdog timer to a 60 sec timeout */
366 envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR, PCF8583_REG_CSR, 0x80);
367 envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR, PCF8583_REG_CSR, 0x80);
368 envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR, PCF8583_REG_TIMER, 0);
369 envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR, PCF8583_REG_ALMTIMER, 60);
370 envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR, PCF8583_REG_ALMCTL, 0xca);
371 envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR, PCF8583_REG_CSR, 0x04);
373 /* recover from previous watchdog failure, if any */
374 envctrl_write_1(sc, ENVCTRL_INTR_ADDR, ~0);
375 envctrl_write_1(sc, ENVCTRL_INTR_ADDR, ~ENVCTRL_INTR_WDT_RST);
376 envctrl_write_1(sc, ENVCTRL_INTR_ADDR, (uint8_t) ~ENVCTRL_INTR_ENABLE);
379 #if 0
381 * Service routine for environmental monitoring events. Currently unused.
383 static void
384 envctrl_isr(void)
386 uint8_t intstate;
387 uint8_t v;
389 envctrl_read(ENVCTRL_INTR_ADDR, 1, &intstate);
390 if ((intstate & ENVCTRL_INTR_PS0) == 0 ||
391 (intstate & ENVCTRL_INTR_PS1) == 0 ||
392 (intstate & ENVCTRL_INTR_PS2) == 0) {
393 printf("envctrl: power supply event\n");
394 envctrl_read(ENVCTRL_PS0_ADDR, 1, &v);
395 envctrl_read(ENVCTRL_PS1_ADDR, 1, &v);
396 envctrl_read(ENVCTRL_PS2_ADDR, 1, &v);
398 if ((intstate & ENVCTRL_INTR_FANFAIL) == 0) {
399 printf("envctrl: fan failure event\n");
400 envctrl_read(ENVCTRL_FANFAIL_ADDR, 1, &v);
402 if ((intstate & ENVCTRL_INTR_UNKNOWN1) == 0) {
404 if ((intstate & ENVCTRL_INTR_UNKNOWN2) == 0) {
407 #endif
410 * Refresh all sensor readings
412 static void
413 envctrl_update_sensors(struct envctrl_softc *sc)
415 int cputemp_max;
416 int pstemp_max;
417 uint8_t cpufan_voltage;
418 uint8_t psfan_voltage;
419 int temp;
420 int nfail;
421 int i;
423 /* read cpu temperatures */
424 cputemp_max = -1;
425 for (i = 0; i < 4; i++) {
426 temp = envctrl_get_cputemp(sc, i);
427 if (temp != -1) {
428 if (cputemp_max < temp)
429 cputemp_max = temp;
430 sc->sc_sensor[i].value_cur = temp + 273150000;
431 sc->sc_sensor[i].state = ENVSYS_SVALID;
432 } else
433 sc->sc_sensor[i].state = ENVSYS_SINVALID;
436 /* read power supply state & temperature */
437 pstemp_max = -1;
438 nfail = 0;
439 for (i = 0; i < 3; i++) {
440 if (envctrl_read(sc, ENVCTRL_PS0_ADDR - i,
441 &sc->sc_ps_state[i], 1))
442 sc->sc_ps_state[i] = ENVCTRL_PS_PRESENT;
444 if ((sc->sc_ps_state[i] & ENVCTRL_PS_PRESENT) == 0) {
445 if ((sc->sc_ps_state[i] & 0x38) != 0x38)
446 nfail++;
447 temp = envctrl_get_pstemp(sc, i);
448 if (pstemp_max < temp)
449 pstemp_max = temp;
450 sc->sc_sensor[i + 4].value_cur = temp + 273150000;
451 sc->sc_sensor[i + 4].state = ENVSYS_SVALID;
452 } else
453 sc->sc_sensor[i + 4].state = ENVSYS_SINVALID;
455 sc->sc_sensor[10].value_cur = nfail;
456 sc->sc_sensor[10].state = ENVSYS_SVALID;
458 /* read ambient temperature */
459 temp = envctrl_get_ambtemp(sc);
460 if (temp != -1) {
461 sc->sc_sensor[7].value_cur = temp + 273150000;
462 sc->sc_sensor[7].state = ENVSYS_SVALID;
463 } else
464 sc->sc_sensor[7].state = ENVSYS_SINVALID;
466 /* read fan state */
467 if (envctrl_read(sc, ENVCTRL_FANFAIL_ADDR, &sc->sc_fanstate, 1)) {
468 sc->sc_fanstate = 0;
469 sc->sc_sensor[11].state = ENVSYS_SINVALID;
470 } else {
471 nfail = 0;
472 for (i = 0; i < 8; i++) {
473 if (((sc->sc_fanstate >> i) & 1) == 0)
474 nfail++;
476 sc->sc_sensor[11].value_cur = nfail;
477 sc->sc_sensor[11].state = ENVSYS_SVALID;
480 /* read keyswitch */
481 envctrl_read(sc, ENVCTRL_LED_ADDR, &sc->sc_keyswitch, 1);
484 * Update fan voltages. If any fans have failed, set voltage
485 * to max to compensate for lost air flow.
487 cpufan_voltage = envctrl_cputemp_to_voltage(sc, cputemp_max / 1000000);
488 if (cputemp_max == -1 || sc->sc_fanstate != 0xFF)
489 cpufan_voltage = ENVCTRL_FANVOLTAGE_MAX;
491 psfan_voltage = envctrl_pstemp_to_voltage(sc, pstemp_max / 1000000);
492 if (pstemp_max == -1 || sc->sc_fanstate != 0xFF)
493 psfan_voltage = ENVCTRL_FANVOLTAGE_MAX;
495 envctrl_set_fanvoltage(sc, ENVCTRL_FANPORT_CPU, cpufan_voltage);
496 envctrl_set_fanvoltage(sc, ENVCTRL_FANPORT_PS, psfan_voltage);
498 sc->sc_sensor[8].value_cur = cpufan_voltage * ENVCTRL_UVFACT;
499 sc->sc_sensor[9].value_cur = psfan_voltage * ENVCTRL_UVFACT;
500 sc->sc_sensor[8].state = ENVSYS_SVALID;
501 sc->sc_sensor[9].state = ENVSYS_SVALID;
505 * envctrl worker thread. Reads sensors periodically.
507 static void
508 envctrl_thread(void *arg)
511 #ifdef BLINK
512 int i;
513 #endif
514 struct envctrl_softc *sc = arg;
516 mutex_enter(&sc->sc_sleepmtx);
519 * Poll sensors every 15 seconds. Optionally blink the activity LED.
521 for (;;) {
522 /* kick wdt */
523 envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR,
524 PCF8583_REG_TIMER, 0);
526 /* refresh sensor readings, update fan speeds */
527 envctrl_update_sensors(sc);
529 #ifdef BLINK
530 for (i = 0; i < 15; i++) {
531 envctrl_write_1(sc, ENVCTRL_LED_ADDR,
532 ~(ENVCTRL_LED_PWR | ENVCTRL_LED_ACT));
533 envctrl_sleep(sc, 500);
534 envctrl_write_1(sc, ENVCTRL_LED_ADDR,
535 ~ENVCTRL_LED_PWR);
536 envctrl_sleep(sc, 500);
538 #else
539 envctrl_sleep(sc, 15 * 1000);
540 #endif
545 * Make a table with interpolated values based on an OPB temperature table.
547 static void
548 envctrl_interpolate_ob_table(int *ftbl, uint8_t *utbl)
550 int i;
551 int e;
552 int k;
553 int y;
555 i = 255;
556 e = i;
557 while (e >= 0) {
558 e = i;
559 y = utbl[i] * 1000000;
560 do {
561 i--;
563 while (utbl[e] == utbl[i] && i > 0);
564 k = (utbl[i] * 1000000 - y) / (e - i);
565 while (e >= i) {
566 ftbl[e] = y;
567 y += k;
568 e--;
574 * Copy temperature tables from OBP. Return 0 on success.
576 static int
577 envctrl_init_tables(struct envctrl_softc *sc, int node)
579 uint8_t buf[256];
580 uint8_t *p;
581 int nitem;
582 int err;
584 p = buf;
585 nitem = 1;
586 err = prom_getprop(node, "cpu-temp-factors", 254, &nitem, &p);
587 if (nitem != 1 || err != 0)
588 return -1;
590 buf[255] = buf[253];
591 buf[254] = buf[253];
592 envctrl_interpolate_ob_table(sc->sc_cpu_temp_factors, buf);
594 p = buf;
595 nitem = 1;
596 err = prom_getprop(node, "ps-temp-factors", 254, &nitem, &p);
597 if (nitem != 1 || err != 0)
598 return -1;
600 buf[255] = buf[253];
601 buf[254] = buf[253];
602 envctrl_interpolate_ob_table(sc->sc_ps_temp_factors, buf);
604 p = sc->sc_cpu_fan_speeds;
605 nitem = 1;
606 err = prom_getprop(node, "cpu-fan-speeds", 112, &nitem, &p);
607 if (nitem != 1 || err != 0)
608 return -1;
610 p = sc->sc_ps_fan_speeds;
611 nitem = 1;
612 err = prom_getprop(node, "ps-fan-speeds", 112, &nitem, &p);
613 if (nitem != 1 || err != 0)
614 return -1;
616 return 0;
619 static int
620 envctrl_write_1(struct envctrl_softc *sc, int addr, uint8_t v1)
623 return iic_exec(&sc->sc_pcfiic.ha_i2c, I2C_OP_WRITE_WITH_STOP, addr,
624 NULL, 0, &v1, 1, cold ? I2C_F_POLL : 0);
628 static int
629 envctrl_write_2(struct envctrl_softc *sc, int addr, uint8_t v1, uint8_t v2)
632 uint8_t buf[] = {v1, v2};
633 return iic_exec(&sc->sc_pcfiic.ha_i2c, I2C_OP_WRITE_WITH_STOP, addr,
634 NULL, 0, buf, 2, cold ? I2C_F_POLL : 0);
637 static int
638 envctrl_read(struct envctrl_softc *sc, int addr, uint8_t *buf, int len)
641 return iic_exec(&sc->sc_pcfiic.ha_i2c, I2C_OP_READ_WITH_STOP, addr,
642 NULL, 0, buf, len, cold ? I2C_F_POLL : 0);