1 /* $NetBSD: envctrl.c,v 1.10 2008/03/26 20:21:38 xtraeme Exp $ */
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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
{
65 bus_space_tag_t sc_iot
;
66 bus_space_handle_t sc_ioh
;
68 struct pcf8584_handle sc_pcfiic
;
71 kcondvar_t sc_sleepcond
;
74 struct sysmon_envsys
*sc_sme
;
75 envsys_data_t sc_sensor
[13];
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 *);
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);
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
;
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");
128 if (envctrl_init_tables(sc
, ea
->ea_node
) != 0) {
129 aprint_error(": unable to initialize tables\n");
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");
144 aprint_normal("\n%s: Ultra Enterprise 450 environmental monitoring\n",
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
);
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",
191 sysmon_envsys_destroy(sc
->sc_sme
);
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");
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***");
214 aprint_verbose("%s: keyswitch is ", device_xname(self
));
215 if ((sc
->sc_keyswitch
& ENVCTRL_KEY_LOCK
) == 0)
216 aprint_verbose("unlocked\n");
218 if ((sc
->sc_keyswitch
& ENVCTRL_KEY_DIAG
) == 0)
219 aprint_verbose("in diagnostic mode\n");
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");
230 panic("cannot start envctrl thread; error %d", error
);
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
244 envctrl_get_cputemp(struct envctrl_softc
*sc
, uint8_t cpu
)
248 if (envctrl_write_1(sc
, ENVCTRL_CPUTEMP_ADDR
, (1 << 6) | cpu
))
250 if (envctrl_read(sc
, ENVCTRL_CPUTEMP_ADDR
, &r
, 1))
252 if (envctrl_write_1(sc
, ENVCTRL_CPUTEMP_ADDR
, 0))
255 return sc
->sc_cpu_temp_factors
[r
];
259 * read power supply temperature in microCelcius
262 envctrl_get_pstemp(struct envctrl_softc
*sc
, uint8_t ps
)
266 if (envctrl_write_1(sc
, ENVCTRL_PS0TEMP_ADDR
+ ps
, (1 << 6)))
268 if (envctrl_read(sc
, ENVCTRL_PS0TEMP_ADDR
+ ps
, &r
, 1))
270 if (envctrl_write_1(sc
, ENVCTRL_PS0TEMP_ADDR
+ ps
, 0))
273 return sc
->sc_ps_temp_factors
[r
];
277 * read ambient temperature in microCelcius
280 envctrl_get_ambtemp(struct envctrl_softc
*sc
)
284 if (envctrl_write_1(sc
, ENVCTRL_AMB_ADDR
, 0))
286 if (envctrl_read(sc
, ENVCTRL_AMB_ADDR
, temp
, 2))
289 return ((temp
[0] << 1) | ((temp
[1] >> 7) & 1)) * 500000;
293 * set fan voltage, 0 - 63, scaled to 0V-12V.
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
);
303 envctrl_cputemp_to_voltage(struct envctrl_softc
*sc
, int temp
)
307 temp
-= 20; /* magic offset, from opensolaris */
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
;
323 envctrl_pstemp_to_voltage(struct envctrl_softc
*sc
, int temp
)
327 temp
-= 30; /* magic offset, from opensolaris */
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
;
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
);
381 * Service routine for environmental monitoring events. Currently unused.
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) {
410 * Refresh all sensor readings
413 envctrl_update_sensors(struct envctrl_softc
*sc
)
417 uint8_t cpufan_voltage
;
418 uint8_t psfan_voltage
;
423 /* read cpu temperatures */
425 for (i
= 0; i
< 4; i
++) {
426 temp
= envctrl_get_cputemp(sc
, i
);
428 if (cputemp_max
< temp
)
430 sc
->sc_sensor
[i
].value_cur
= temp
+ 273150000;
431 sc
->sc_sensor
[i
].state
= ENVSYS_SVALID
;
433 sc
->sc_sensor
[i
].state
= ENVSYS_SINVALID
;
436 /* read power supply state & temperature */
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)
447 temp
= envctrl_get_pstemp(sc
, i
);
448 if (pstemp_max
< temp
)
450 sc
->sc_sensor
[i
+ 4].value_cur
= temp
+ 273150000;
451 sc
->sc_sensor
[i
+ 4].state
= ENVSYS_SVALID
;
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
);
461 sc
->sc_sensor
[7].value_cur
= temp
+ 273150000;
462 sc
->sc_sensor
[7].state
= ENVSYS_SVALID
;
464 sc
->sc_sensor
[7].state
= ENVSYS_SINVALID
;
467 if (envctrl_read(sc
, ENVCTRL_FANFAIL_ADDR
, &sc
->sc_fanstate
, 1)) {
469 sc
->sc_sensor
[11].state
= ENVSYS_SINVALID
;
472 for (i
= 0; i
< 8; i
++) {
473 if (((sc
->sc_fanstate
>> i
) & 1) == 0)
476 sc
->sc_sensor
[11].value_cur
= nfail
;
477 sc
->sc_sensor
[11].state
= ENVSYS_SVALID
;
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.
508 envctrl_thread(void *arg
)
514 struct envctrl_softc
*sc
= arg
;
516 mutex_enter(&sc
->sc_sleepmtx
);
519 * Poll sensors every 15 seconds. Optionally blink the activity LED.
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
);
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
,
536 envctrl_sleep(sc
, 500);
539 envctrl_sleep(sc
, 15 * 1000);
545 * Make a table with interpolated values based on an OPB temperature table.
548 envctrl_interpolate_ob_table(int *ftbl
, uint8_t *utbl
)
559 y
= utbl
[i
] * 1000000;
563 while (utbl
[e
] == utbl
[i
] && i
> 0);
564 k
= (utbl
[i
] * 1000000 - y
) / (e
- i
);
574 * Copy temperature tables from OBP. Return 0 on success.
577 envctrl_init_tables(struct envctrl_softc
*sc
, int node
)
586 err
= prom_getprop(node
, "cpu-temp-factors", 254, &nitem
, &p
);
587 if (nitem
!= 1 || err
!= 0)
592 envctrl_interpolate_ob_table(sc
->sc_cpu_temp_factors
, buf
);
596 err
= prom_getprop(node
, "ps-temp-factors", 254, &nitem
, &p
);
597 if (nitem
!= 1 || err
!= 0)
602 envctrl_interpolate_ob_table(sc
->sc_ps_temp_factors
, buf
);
604 p
= sc
->sc_cpu_fan_speeds
;
606 err
= prom_getprop(node
, "cpu-fan-speeds", 112, &nitem
, &p
);
607 if (nitem
!= 1 || err
!= 0)
610 p
= sc
->sc_ps_fan_speeds
;
612 err
= prom_getprop(node
, "ps-fan-speeds", 112, &nitem
, &p
);
613 if (nitem
!= 1 || err
!= 0)
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);
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);
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);