4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * This file contains the environmental PICL plug-in module.
34 * This plugin sets up the PICLTREE for Taco.
35 * It provides functionality to get/set temperatures
38 * The environmental monitoring policy is the default
39 * auto mode as programmed by OBP at boot time.
44 #include <sys/sysmacros.h>
50 #include <sys/processor.h>
59 #include <libdevinfo.h>
63 #include <sys/utsname.h>
64 #include <sys/systeminfo.h>
66 #include <sys/i2c/clients/i2c_client.h>
67 #include <sys/i2c/clients/adm1031.h>
71 * PICL plguin entry points
73 static void piclenvd_register(void);
74 static void piclenvd_init(void);
75 static void piclenvd_fini(void);
80 extern void env_picl_setup(void);
81 extern void env_picl_destroy(void);
82 extern int env_picl_setup_tuneables(void);
85 * Sleep routine used for polling
87 static uint_t
envd_sleep(uint_t
);
89 #pragma init(piclenvd_register)
92 * Plugin registration information
94 static picld_plugin_reg_t my_reg_info
= {
96 PICLD_PLUGIN_CRITICAL
,
103 * ES Segment data structures
105 static sensor_ctrl_blk_t sensor_ctrl
[MAX_SENSORS
];
106 static fan_ctrl_blk_t fan_ctrl
[MAX_FANS
];
107 static fruenvseg_t
*envfru
= NULL
;
110 * Env thread variables
112 static boolean_t system_shutdown_started
= B_FALSE
;
113 static boolean_t ovtemp_thr_created
= B_FALSE
;
114 static pthread_t ovtemp_thr_id
;
115 static pthread_attr_t thr_attr
;
119 * PM thread related variabled
121 static pthread_t pmthr_tid
; /* pmthr thread ID */
122 static int pm_fd
= -1; /* PM device file descriptor */
123 static boolean_t pmthr_created
= B_FALSE
;
124 static int cur_lpstate
; /* cur low power state */
127 * Envd plug-in verbose flag set by SUNW_PICLENVD_DEBUG environment var
128 * Setting the verbose tuneable also enables debugging for better
136 static env_fan_t envd_sys_out_fan
= {
137 ENV_SYSTEM_OUT_FAN
, ENV_SYSTEM_FAN_DEVFS
, NULL
,
138 SYSTEM_FAN_ID
, SYSTEM_OUT_FAN_SPEED_MIN
,
139 SYSTEM_OUT_FAN_SPEED_MAX
, -1, -1,
142 static env_fan_t envd_sys_in_fan
= {
143 ENV_SYSTEM_INTAKE_FAN
, ENV_SYSTEM_FAN_DEVFS
, NULL
,
144 SYSTEM_FAN_ID
, SYSTEM_INTAKE_FAN_SPEED_MIN
,
145 SYSTEM_INTAKE_FAN_SPEED_MAX
, -1, -1,
148 static env_fan_t envd_cpu_fan
= {
149 ENV_CPU_FAN
, ENV_CPU_FAN_DEVFS
, NULL
,
150 CPU_FAN_ID
, CPU_FAN_SPEED_MIN
, CPU_FAN_SPEED_MAX
, -1, -1,
154 * NULL terminated array of fans
156 static env_fan_t
*envd_fans
[] = {
164 * ADM1031 speedrange map is indexed by a 2-bit value
166 static int adm_speedrange_map
[] = {1, 2, 4, 8};
171 static char *hwm_devs
[] = {
172 CPU_HWM_DEVFS
, /* CPU_HWM_ID */
176 * Fan names associated with each ADM1031 hwms - used to
177 * print fault messages
179 static char *hwm_fans
[MAX_HWMS
][2] = {
180 {ENV_CPU_FAN
, ENV_SYSTEM_IN_OUT_FANS
}
184 * Temperature sensors
186 static env_sensor_t envd_sensors
[] = {
187 { SENSOR_CPU_DIE
, SENSOR_CPU_DIE_DEVFS
, NULL
,
188 CPU_SENSOR_ID
, CPU_HWM_ID
, (void *)&envd_cpu_fan
, -1},
189 { SENSOR_INT_AMB
, SENSOR_INT_AMB_DEVFS
, NULL
,
190 INT_AMB_SENSOR_ID
, CPU_HWM_ID
, NULL
, -1},
191 { SENSOR_SYS_IN
, SENSOR_SYS_IN_DEVFS
, NULL
,
192 SYS_IN_SENSOR_ID
, CPU_HWM_ID
, (void *)&envd_sys_in_fan
, -1},
194 #define N_ENVD_SENSORS (sizeof (envd_sensors)/sizeof (envd_sensors[0]))
199 #define TACH_UNKNOWN 255
200 #define FAN_OUT_OF_RANGE (TACH_UNKNOWN)
201 #define ADM_HYSTERISIS 5
202 #define N_SEQ_TACH 15
204 #define TMIN_MASK (0xF8)
205 #define TMIN_SHIFT (3)
206 #define TMIN_UNITS (4) /* increments of 4 degrees celsius */
207 #define TRANGE_MASK (0x7)
209 #define TMIN(regval) (((regval & TMIN_MASK) >> TMIN_SHIFT) * TMIN_UNITS)
210 #define TRANGE(regval) (regval & TRANGE_MASK)
212 #define GET_TMIN_RANGE(tmin, trange) \
213 ((((tmin / TMIN_UNITS) & TMIN_MASK) << TMIN_SHIFT) | \
214 (trange & TRANGE_MASK))
216 #define TACH_ENABLE_MASK (0x0C)
217 #define MONITOR_ENABLE_MASK (0x01)
218 #define ADM_SETFANSPEED_CONV(speed) (15 * speed / 100)
226 static int get_monitor_mode(ptree_rarg_t
*parg
, void *buf
);
227 static int set_monitor_mode(ptree_warg_t
*parg
, const void *buf
);
228 static int get_int_val(ptree_rarg_t
*parg
, void *buf
);
229 static int set_int_val(ptree_warg_t
*parg
, const void *buf
);
230 static int get_string_val(ptree_rarg_t
*parg
, void *buf
);
231 static int set_string_val(ptree_warg_t
*parg
, const void *buf
);
232 static int get_tach(ptree_rarg_t
*parg
, void *buf
);
233 static int set_tach(ptree_warg_t
*parg
, const void *buf
);
235 static int shutdown_override
= 0;
236 static int sensor_poll_interval
= SENSORPOLL_INTERVAL
;
237 static int warning_interval
= WARNING_INTERVAL
;
238 static int shutdown_interval
= SHUTDOWN_INTERVAL
;
239 static int ovtemp_monitor
= 1; /* enabled */
240 static int pm_monitor
= 1; /* enabled */
241 static int mon_fanstat
= 1; /* enabled */
244 static int hwm_tach_enable
;
245 static char shutdown_cmd
[] = SHUTDOWN_CMD
;
247 env_tuneable_t tuneables
[] = {
248 {"ovtemp-monitor", PICL_PTYPE_INT
, &ovtemp_monitor
,
249 &get_int_val
, &set_int_val
, sizeof (int)},
251 {"pm-monitor", PICL_PTYPE_INT
, &pm_monitor
,
252 &get_int_val
, &set_int_val
, sizeof (int)},
254 {"shutdown-override", PICL_PTYPE_INT
, &shutdown_override
,
255 &get_int_val
, &set_int_val
, sizeof (int)},
257 {"hwm-automode-enable", PICL_PTYPE_INT
, &hwm_mode
,
258 &get_monitor_mode
, &set_monitor_mode
, sizeof (int)},
260 {"sensor-poll-interval", PICL_PTYPE_INT
,
261 &sensor_poll_interval
,
262 &get_int_val
, &set_int_val
,
265 {"warning-interval", PICL_PTYPE_INT
, &warning_interval
,
266 &get_int_val
, &set_int_val
,
269 {"shutdown-interval", PICL_PTYPE_INT
, &shutdown_interval
,
270 &get_int_val
, &set_int_val
,
273 {"shutdown-command", PICL_PTYPE_CHARSTRING
, shutdown_cmd
,
274 &get_string_val
, &set_string_val
,
275 sizeof (shutdown_cmd
)},
277 {"tach-enable", PICL_PTYPE_INT
, &hwm_tach_enable
,
278 &get_tach
, &set_tach
,
281 {"monitor-fanstat", PICL_PTYPE_INT
, &mon_fanstat
,
282 &get_int_val
, &set_int_val
, sizeof (int)},
284 {"verbose", PICL_PTYPE_INT
, &env_debug
,
285 &get_int_val
, &set_int_val
, sizeof (int)},
290 * We use this to figure out how many tuneables there are
291 * This is variable because the publishing routine needs this info
294 int ntuneables
= (sizeof (tuneables
)/sizeof (tuneables
[0]));
297 * Table Handling Code
300 fini_table(table_t
*tblp
)
309 init_table(int npoints
)
317 if ((tblp
= malloc(sizeof (*tblp
))) == NULL
)
320 if ((xy
= malloc(sizeof (*xy
) * npoints
)) == NULL
) {
325 tblp
->nentries
= npoints
;
332 * function: calculates y for a given x based on a table of points
333 * for monotonically increasing x values.
334 * 'tbl' specifies the table to use, 'val' specifies the 'x', returns 'y'
337 y_of_x(table_t
*tbl
, int xval
)
345 entries
= tbl
->nentries
;
347 if (xval
<= xymap
[0].x
)
349 else if (xval
>= xymap
[entries
- 1].x
)
350 return (xymap
[entries
- 1].y
);
352 for (i
= 1; i
< entries
- 1; i
++) {
353 if (xval
== xymap
[i
].x
)
355 if (xval
< xymap
[i
].x
)
360 * Use linear interpolation
362 dy
= (float)(xymap
[i
].y
- xymap
[i
-1].y
);
363 dx
= (float)(xymap
[i
].x
- xymap
[i
-1].x
);
365 newval
= xymap
[i
- 1].y
+ slope
* (xval
- xymap
[i
- 1].x
);
366 return ((int)(newval
+ (newval
>= 0 ? 0.5 : -0.5)));
370 * Get environmental segment from the specified FRU SEEPROM
373 get_envseg(int fd
, void **envsegp
, int *envseglenp
)
375 int i
, segcnt
, envseglen
;
376 section_layout_t section
;
377 segment_layout_t segment
;
380 if (lseek(fd
, (long)SECTION_HDR_OFFSET
, 0) == -1L ||
381 read(fd
, §ion
, sizeof (section
)) != sizeof (section
)) {
386 * Verify we have the correct section and contents are valid
387 * For now, we don't verify the CRC.
389 if (section
.header_tag
!= SECTION_HDR_TAG
||
390 GET_UNALIGN16(§ion
.header_version
[0]) != SECTION_HDR_VER
) {
393 "Invalid section header tag:%x version:%x\n",
395 GET_UNALIGN16(§ion
.header_version
));
400 * Locate our environmental segment
402 segcnt
= section
.segment_count
;
403 for (i
= 0; i
< segcnt
; i
++) {
404 if (read(fd
, &segment
, sizeof (segment
)) != sizeof (segment
)) {
409 "Seg name: %x desc:%x off:%x len:%x\n",
410 GET_UNALIGN16(&segment
.name
),
411 GET_UNALIGN32(&segment
.descriptor
[0]),
412 GET_UNALIGN16(&segment
.offset
),
413 GET_UNALIGN16(&segment
.length
));
414 if (GET_UNALIGN16(&segment
.name
) == ENVSEG_NAME
)
423 * Allocate memory to hold the environmental segment data.
425 envseglen
= GET_UNALIGN16(&segment
.length
);
426 if ((envseg
= malloc(envseglen
)) == NULL
) {
430 if (lseek(fd
, (long)GET_UNALIGN16(&segment
.offset
), 0) == -1L ||
431 read(fd
, envseg
, envseglen
) != envseglen
) {
436 *envseglenp
= envseglen
;
441 * Get all environmental segments
444 get_fru_envsegs(void)
446 fruenvseg_t
*fruenvsegs
;
447 envseg_layout_t
*envsegp
;
449 int fd
, envseglen
, hdrlen
;
453 fruenvsegs
= malloc(sizeof (*fruenvsegs
));
454 if (fruenvsegs
== NULL
) {
459 * Now get the environmental segment from this FRU
461 (void) snprintf(path
, sizeof (path
), "%s%s", I2C_DEVFS
, MBFRU_DEV
);
462 fd
= open(path
, O_RDONLY
);
464 envd_log(LOG_ERR
, ENV_FRU_OPEN_FAIL
, errno
, path
);
470 * Read environmental segment from this FRU SEEPROM
472 if (get_envseg(fd
, &envsegbufp
, &envseglen
) != 0) {
473 envd_log(LOG_ERR
, ENV_FRU_BAD_ENVSEG
, path
);
480 * Validate envseg version number and header length
482 envsegp
= (envseg_layout_t
*)envsegbufp
;
483 hdrlen
= sizeof (envseg_layout_t
) -
484 sizeof (envseg_sensor_t
) +
485 (envsegp
->sensor_count
) * sizeof (envseg_sensor_t
);
487 if (envsegp
->version
!= ENVSEG_VERSION
||
488 envseglen
< hdrlen
) {
490 * version mismatch or header not big enough
492 envd_log(LOG_CRIT
, ENV_FRU_BAD_ENVSEG
, path
);
493 if (envsegbufp
!= NULL
)
494 (void) free(envsegbufp
);
500 fruenvsegs
->envseglen
= envseglen
;
501 fruenvsegs
->envsegbufp
= envsegbufp
;
507 process_fru_seeprom(unsigned char *buff
)
515 env_sensor_t
*snodep
;
517 #define NSENSOR_OFFSET 1
518 #define ID_OFF_SIZE 6
519 #define NFANS_OFFSET(x) ((x * ID_OFF_SIZE) + 2)
521 nsensors
= (int)buff
[NSENSOR_OFFSET
];
522 if (nsensors
!= MAX_SENSORS
) {
523 envd_log(LOG_CRIT
, ENV_FRU_BAD_ENVSEG
, FRU_SEEPROM_NAME
);
527 nfans
= (int)buff
[NFANS_OFFSET(nsensors
)];
528 if (nfans
!= MAX_FANS
) {
529 envd_log(LOG_CRIT
, ENV_FRU_BAD_ENVSEG
, FRU_SEEPROM_NAME
);
533 while (nsensors
> 0) {
534 (void) memcpy((char *)&id
, (char *)&buff
[id_offset
+ 2],
538 envd_log(LOG_ERR
, "\n Sensor Id %x offset %x",
541 if (id
.id
> MAX_SENSOR_ID
) {
542 envd_log(LOG_CRIT
, ENV_FRU_BAD_ENVSEG
,
548 * Copy into the sensor control block array according to the
551 (void) memcpy((char *)&sensor_ctrl
[id
.id
],
552 (char *)&buff
[id
.offset
],
553 sizeof (sensor_ctrl_blk_t
));
555 id_offset
+= ID_OFF_SIZE
;
559 * Skip past no of Fan entry(single byte)
563 (void) memcpy((char *)&id
, (char *)&buff
[id_offset
+ 2],
567 envd_log(LOG_ERR
, "\n Fan Id %x offset %x", id
.id
,
570 (void) memcpy((char *)&fan_ctrl
[id
.id
],
571 (char *)&buff
[id
.offset
], sizeof (fan_ctrl_blk_t
));
574 id_offset
+= ID_OFF_SIZE
;
578 * Match Sensor/ES ID and point correct data
581 for (snodep
= envd_sensors
; snodep
->name
!= NULL
; snodep
++)
582 snodep
->es_ptr
= &sensor_ctrl
[snodep
->id
];
585 * Match Fan/ES ID and point to correct ES Data
588 for (i
= 0; (fnodep
= envd_fans
[i
]) != NULL
; i
++)
589 fnodep
->es_ptr
= &fan_ctrl
[fnodep
->id
];
597 envfru
= get_fru_envsegs();
598 if (envfru
== NULL
) {
599 envd_log(LOG_CRIT
, ENV_FRU_BAD_ENVSEG
, FRU_SEEPROM_NAME
);
602 return (process_fru_seeprom((uchar_t
*)envfru
->envsegbufp
));
609 free(envfru
->envsegbufp
);
613 * Lookup fan and return a pointer to env_fan_t data structure.
616 fan_lookup(char *name
)
621 for (i
= 0; (fanp
= envd_fans
[i
]) != NULL
; i
++) {
622 if (strcmp(fanp
->name
, name
) == 0)
629 * Lookup sensor and return a pointer to env_sensor_t data structure.
632 sensor_lookup(char *name
)
634 env_sensor_t
*sensorp
;
637 for (i
= 0; i
< N_ENVD_SENSORS
; ++i
) {
638 sensorp
= &envd_sensors
[i
];
639 if (strcmp(sensorp
->name
, name
) == 0)
646 * Get current temperature
647 * Returns -1 on error, 0 if successful
650 get_temperature(env_sensor_t
*sensorp
, tempr_t
*temp
)
652 int fd
= sensorp
->fd
;
657 else if (ioctl(fd
, I2C_GET_TEMPERATURE
, temp
) == -1) {
659 if (sensorp
->error
== 0) {
661 envd_log(LOG_WARNING
, ENV_SENSOR_ACCESS_FAIL
,
662 sensorp
->name
, errno
, strerror(errno
));
664 } else if (sensorp
->error
!= 0) {
666 envd_log(LOG_WARNING
, ENV_SENSOR_ACCESS_OK
, sensorp
->name
);
668 if (sensorp
->crtbl
!= NULL
) {
669 *temp
= (tempr_t
)y_of_x(sensorp
->crtbl
, *temp
);
676 * Get uncorrected current temperature
677 * Returns -1 on error, 0 if successful
680 get_raw_temperature(env_sensor_t
*sensorp
, tempr_t
*temp
)
682 int fd
= sensorp
->fd
;
687 else if (ioctl(fd
, I2C_GET_TEMPERATURE
, temp
) == -1) {
695 * Return Fan RPM given N & tach
696 * count and N are retrived from the
700 tach_to_rpm(int n
, uint8_t tach
)
704 return ((ADCSAMPLE
* 60) / (n
* tach
));
708 get_raw_fan_speed(env_fan_t
*fanp
, uint8_t *fanspeedp
)
717 else if (ioctl(fan_fd
, I2C_GET_FAN_SPEED
, fanspeedp
) == -1) {
725 * Get current fan speed
726 * Returns -1 on error, 0 if successful
729 get_fan_speed(env_fan_t
*fanp
, fanspeed_t
*fanspeedp
)
737 else if (ioctl(fan_fd
, I2C_GET_FAN_SPEED
, &tach
) == -1) {
742 * Fanspeeds are reported as 0
743 * if the tach is out of range or fan status is off
744 * and if monitoring fan status is enabled.
746 if (mon_fanstat
&& (!fanp
->fanstat
|| tach
== FAN_OUT_OF_RANGE
)) {
750 tach_to_rpm(fanp
->speedrange
, tach
);
758 * Returns -1 on error, 0 if successful
761 set_fan_speed(env_fan_t
*fanp
, fanspeed_t fanspeed
)
771 if (fanspeed
< 0 || fanspeed
> 100)
774 speed
= (uint8_t)ADM_SETFANSPEED_CONV(fanspeed
);
776 if (ioctl(fan_fd
, I2C_SET_FAN_SPEED
, &speed
) == -1) {
783 * close all fan devices
786 envd_close_fans(void)
791 for (i
= 0; (fanp
= envd_fans
[i
]) != NULL
; i
++) {
792 if (fanp
->fd
!= -1) {
793 (void) close(fanp
->fd
);
800 * Close sensor devices and freeup resources
803 envd_close_sensors(void)
805 env_sensor_t
*sensorp
;
808 for (i
= 0; i
< N_ENVD_SENSORS
; ++i
) {
809 sensorp
= &envd_sensors
[i
];
810 if (sensorp
->fd
!= -1) {
811 (void) close(sensorp
->fd
);
814 if (sensorp
->crtbl
!= NULL
)
815 fini_table(sensorp
->crtbl
);
820 * Open fan devices and initialize per fan data structure.
821 * Returns #fans found.
824 envd_setup_fans(void)
832 for (i
= 0; (fanp
= envd_fans
[i
]) != NULL
; i
++) {
833 (void) strcpy(path
, "/devices");
834 (void) strlcat(path
, fanp
->devfs_path
, sizeof (path
));
835 fd
= open(path
, O_RDWR
);
838 ENV_FAN_OPEN_FAIL
, fanp
->name
,
839 fanp
->devfs_path
, errno
, strerror(errno
));
840 fanp
->present
= B_FALSE
;
844 if (ioctl(fd
, ADM1031_GET_FAN_FEATURE
, &n
) != -1) {
846 adm_speedrange_map
[(n
>> 6) & 0x03];
848 fanp
->speedrange
= FAN_RANGE_DEFAULT
;
851 fanp
->present
= B_TRUE
;
853 fanp
->cspeed
= TACH_UNKNOWN
;
854 fanp
->lspeed
= TACH_UNKNOWN
;
862 * Open temperature sensor devices and initialize per sensor data structure.
863 * Returns #sensors found.
866 envd_setup_sensors(void)
868 env_sensor_t
*sensorp
;
869 sensor_ctrl_blk_t
*es_ptr
;
876 for (i
= 0; i
< N_ENVD_SENSORS
; ++i
) {
877 sensorp
= &envd_sensors
[i
];
878 /* Initialize sensor's initial state */
879 sensorp
->shutdown_initiated
= B_FALSE
;
880 sensorp
->warning_tstamp
= 0;
881 sensorp
->shutdown_tstamp
= 0;
883 sensorp
->crtbl
= NULL
;
885 (void) strcpy(path
, "/devices");
886 (void) strlcat(path
, sensorp
->devfs_path
,
888 sensorp
->fd
= open(path
, O_RDWR
);
889 if (sensorp
->fd
== -1) {
890 envd_log(LOG_ERR
, ENV_SENSOR_OPEN_FAIL
,
891 sensorp
->name
, sensorp
->devfs_path
,
892 errno
, strerror(errno
));
893 sensorp
->present
= B_FALSE
;
896 sensorp
->present
= B_TRUE
;
903 if (ioctl(sensorp
->fd
, ADM1031_GET_TEMP_MIN_RANGE
,
905 sensorp
->tmin
= TMIN(tmin
);
910 envd_log(LOG_ERR
, "Sensor %s tmin %d",
911 sensorp
->name
, sensorp
->tmin
);
914 * Create a correction table
915 * if correction pairs are present in es
918 es_ptr
= sensorp
->es_ptr
;
920 if (es_ptr
== NULL
) {
923 nentries
= es_ptr
->correctionEntries
;
927 envd_log(LOG_CRIT
, "sensor correction <2");
931 sensorp
->crtbl
= init_table(nentries
);
932 if (sensorp
->crtbl
== NULL
)
934 tblp
= sensorp
->crtbl
;
936 (char)es_ptr
->correctionPair
[0].measured
;
938 (char)es_ptr
->correctionPair
[0].corrected
;
940 for (j
= 1; j
< nentries
; ++j
) {
942 (char)es_ptr
->correctionPair
[j
].measured
;
944 (char)es_ptr
->correctionPair
[j
].corrected
;
946 if (tblp
->xymap
[j
].x
<= tblp
->xymap
[j
- 1].x
) {
948 sensorp
->crtbl
= NULL
;
949 envd_log(LOG_CRIT
, ENV_FRU_BAD_ENVSEG
,
956 envd_log(LOG_CRIT
, "Sensor correction %s",
958 for (j
= 0; j
< nentries
; j
++)
959 envd_log(LOG_CRIT
, " %d %d",
960 tblp
->xymap
[j
].x
, tblp
->xymap
[j
].y
);
966 * Modify ADM Tmin/ranges depending what power level
970 updateadm_ranges(char *name
, uchar_t cur_lpstate
)
972 env_sensor_t
*sensorp
;
973 fan_ctrl_blk_t
*fanctl
;
978 uchar_t sys_id
= CPU_HWM_ID
;
980 static uint16_t tsave
= 0;
982 sensorp
= sensor_lookup(name
);
987 * If there is only one Control pairs then return
989 fanctl
= ((env_fan_t
*)sensorp
->fanp
)->es_ptr
;
991 if (fanctl
!= NULL
&& fanctl
->no_ctl_pairs
<= 1)
995 * if fan control specifies that ranges are same then
996 * we skip re-programming adm chip.
999 tmin
= fanctl
->fan_ctl_pairs
[0].tMin
;
1000 trange
= fanctl
->fan_ctl_pairs
[0].tRange
;
1001 if ((tmin
== fanctl
->fan_ctl_pairs
[1].tMin
) &&
1002 (trange
== fanctl
->fan_ctl_pairs
[1].tRange
))
1005 sysfd
= open(hwm_devs
[sys_id
], O_RDWR
);
1008 envd_log(LOG_ERR
, ENV_ADM_OPEN_FAIL
, hwm_devs
[sys_id
],
1009 errno
, strerror(errno
));
1012 /* Read ADM default value only for the first time */
1014 if (ioctl(sensorp
->fd
, ADM1031_GET_TEMP_MIN_RANGE
,
1018 "read tminrange ioctl failed");
1019 (void) close(sysfd
);
1025 * Need to reinit ADM to manual mode for Tmin range to be
1028 mode
= ADM1031_MANUAL_MODE
;
1029 if (ioctl(sysfd
, ADM1031_SET_MONITOR_MODE
, &mode
) == -1) {
1031 envd_log(LOG_ERR
, ENV_ADM_MANUAL_MODE
);
1032 (void) close(sysfd
);
1036 if (cur_lpstate
== 1) {
1038 * ADM 1031 Tmin/Trange register need to be reprogrammed.
1040 tdata
= ((fanctl
->fan_ctl_pairs
[cur_lpstate
].tMin
/ TMIN_UNITS
)
1042 /* Need to pack tRange in ADM bits 2:0 */
1043 switch (fanctl
->fan_ctl_pairs
[cur_lpstate
].tRange
) {
1066 if (ioctl(sensorp
->fd
, ADM1031_SET_TEMP_MIN_RANGE
,
1068 sensorp
->tmin
= TMIN(tdata
);
1070 sensorp
->tmin
= TMIN(tdata
);
1072 mode
= ADM1031_AUTO_MODE
;
1073 if (ioctl(sysfd
, ADM1031_SET_MONITOR_MODE
, &mode
) == -1) {
1075 envd_log(LOG_ERR
, ENV_ADM_AUTO_MODE
);
1077 (void) close(sysfd
);
1084 pm_state_change_t pmstate
;
1085 char physpath
[PATH_MAX
];
1088 pmstate
.physpath
= physpath
;
1089 pmstate
.size
= sizeof (physpath
);
1093 pm_fd
= open(PM_DEVICE
, O_RDWR
);
1095 envd_log(LOG_ERR
, PM_THREAD_EXITING
, errno
, strerror(errno
));
1100 * Get PM state change events to check if the system
1101 * is in lowest power state and adjust ADM hardware
1102 * monitor's fan speed settings.
1104 * To minimize polling, we use the blocking interface
1105 * to get the power state change event here.
1107 if (ioctl(pm_fd
, PM_GET_STATE_CHANGE_WAIT
, &pmstate
) != 0) {
1115 "pmstate event:0x%x flags:%x comp:%d "
1116 "oldval:%d newval:%d path:%s\n",
1117 pmstate
.event
, pmstate
.flags
,
1124 (pmstate
.flags
& PSC_ALL_LOWEST
) ? 1 : 0;
1125 } while (ioctl(pm_fd
, PM_GET_STATE_CHANGE
, &pmstate
) == 0);
1127 * Change ADM ranges as per E* Requirements. Update
1128 * happens only for valid state changes.
1130 if (pre_lpstate
!= cur_lpstate
) {
1131 pre_lpstate
= cur_lpstate
;
1132 updateadm_ranges(SENSOR_SYS_IN
, cur_lpstate
);
1140 * This function is used to reasonably predict the
1141 * state of the fan (ON/OFF) using tmin and current temperature.
1143 * We know the fan is on if temp >= tmin and fan is off if
1144 * temp < (Tmin - Hysterisis).
1146 * When the temperature is in between we don't know if the fan is on/off
1147 * because the temperature could be decreasing and not have crossed
1148 * Tmin - hysterisis and vice a versa.
1152 * -------------------------------------------
1156 * --------------------------------------------
1160 * To solve the problem of finding out if the fan is on/off in our gray region
1161 * we keep track of the last read tach and the current read tach. From
1162 * experimentation and from discussions with analog devices it is unlikely that
1163 * if the fans are on we will get a constant tach reading more than 5 times in
1164 * a row. This is not the most fool proof approach but the best we can do.
1166 * This routine implements the above logic for a sensor with an
1167 * associated fan. The caller garauntees sensorp and fanp are not null.
1170 check_fanstat(env_sensor_t
*sensorp
)
1172 env_fan_t
*fanp
= sensorp
->fanp
;
1176 if (get_raw_temperature(sensorp
, &temp
) == -1)
1179 if (temp
< (sensorp
->tmin
- ADM_HYSTERISIS
)) {
1181 fanp
->fanstat
= 0; /* Fan off */
1182 fanp
->lspeed
= TACH_UNKNOWN
; /* Reset Last read tach */
1185 } else if (temp
>= sensorp
->tmin
) {
1187 fanp
->fanstat
= 1; /* Fan on */
1188 fanp
->lspeed
= TACH_UNKNOWN
;
1192 if (get_raw_fan_speed(fanp
, &fanspeed
) == -1)
1195 fanp
->cspeed
= fanspeed
;
1197 * First time in the gray area
1198 * set last read speed to current speed
1200 if (fanp
->lspeed
== TACH_UNKNOWN
) {
1201 fanp
->lspeed
= fanspeed
;
1203 if (fanp
->lspeed
!= fanp
->cspeed
) {
1209 if (fanp
->conccnt
>= N_SEQ_TACH
)
1212 fanp
->lspeed
= fanp
->cspeed
;
1217 * There is an issue with the ADM1031 chip that causes the chip
1218 * to not update the tach register in case the fan stops. The
1219 * fans stop when the temperature measured (temp) drops below
1220 * Tmin - Hysterisis and turns the fan on when the temp >= tmin.
1222 * Since the tach registers don't update and remain stuck at the
1223 * last read tach value our get_fan_speed function always returns
1224 * a non-zero RPM reading.
1226 * To fix this we need to figure out when the fans will be on/off
1227 * depending on the current temperature. Currently we poll for
1228 * interrupts, we can use that loop to determine what the current
1229 * temperature is and if the fans should be on/off.
1231 * We get current temperature and check the fans.
1234 monitor_fanstat(void)
1236 env_sensor_t
*sensorp
;
1240 for (i
= 0; i
< N_ENVD_SENSORS
; i
++) {
1241 sensorp
= &envd_sensors
[i
];
1246 fanp
= sensorp
->fanp
;
1251 if (sensorp
->tmin
!= -1) {
1252 check_fanstat(sensorp
);
1258 * On Taco both the system fans are driven by one
1259 * sensor (sys-in) and connected to the sys-in tach.
1261 envd_sys_out_fan
.fanstat
= envd_sys_in_fan
.fanstat
;
1266 handle_overtemp_interrupt(int hwm_id
)
1268 env_sensor_t
*sensorp
;
1270 uchar_t smap
[MAX_SENSORS
];
1273 char msgbuf
[BUFSIZ
];
1274 char syscmd
[BUFSIZ
];
1275 boolean_t return_flag
;
1277 /* Clear Map of Sensor Entries */
1278 (void) memset(smap
, SENSOR_OK
, sizeof (smap
));
1281 for (i
= 0; i
< N_ENVD_SENSORS
; i
++) {
1282 sensorp
= &envd_sensors
[i
];
1285 * Check whether the sensor belongs to the
1286 * interrupting ADM hardware monitor
1288 if (sensorp
->hwm_id
!= hwm_id
)
1292 * if shutdown is initiated then we simply loop
1293 * through the sensors until shutdown
1295 if (sensorp
->shutdown_initiated
== B_TRUE
)
1298 /* get current temp for this sensor */
1299 if (get_temperature(sensorp
, &temp
) == -1)
1302 sensorp
->cur_temp
= temp
;
1306 "sensor name %s, cur temp %d, "
1307 "HW %d LW %d SD %d LS %d\n",
1308 sensorp
->name
, temp
,
1309 sensorp
->es_ptr
->high_warning
,
1310 (int)sensorp
->es_ptr
->low_warning
,
1311 sensorp
->es_ptr
->high_shutdown
,
1312 (int)sensorp
->es_ptr
->low_shutdown
);
1314 if (TEMP_IN_WARNING_RANGE(sensorp
->cur_temp
, sensorp
)) {
1316 * Log on warning atmost one second
1318 ct
= (time_t)(gethrtime() / NANOSEC
);
1319 if ((ct
- sensorp
->warning_tstamp
) >=
1322 ENV_WARNING_MSG
, sensorp
->name
,
1324 sensorp
->es_ptr
->low_warning
,
1325 sensorp
->es_ptr
->high_warning
);
1326 sensorp
->warning_tstamp
= ct
;
1328 smap
[i
] = SENSOR_WARN
;
1331 * We will fall in this caterory only if
1332 * Temperature drops/increases from warning
1333 * threshold. If so we set sensor map to
1334 * OK so that we can exit the loop if
1335 * shutdown not initiated.
1337 smap
[i
] = SENSOR_OK
;
1340 if (TEMP_IN_SHUTDOWN_RANGE(temp
, sensorp
) &&
1341 !shutdown_override
) {
1342 ct
= (time_t)(gethrtime() / NANOSEC
);
1343 if (sensorp
->shutdown_tstamp
== 0)
1344 sensorp
->shutdown_tstamp
= ct
;
1345 if ((ct
- sensorp
->shutdown_tstamp
) >=
1346 shutdown_interval
) {
1347 sensorp
->shutdown_initiated
= B_TRUE
;
1348 (void) snprintf(msgbuf
, sizeof (msgbuf
),
1349 ENV_SHUTDOWN_MSG
, sensorp
->name
,
1351 sensorp
->es_ptr
->low_shutdown
,
1352 sensorp
->es_ptr
->high_shutdown
);
1353 envd_log(LOG_ALERT
, msgbuf
);
1355 if (system_shutdown_started
== B_FALSE
) {
1356 (void) snprintf(syscmd
, sizeof (syscmd
),
1357 "%s \"%s\"", SHUTDOWN_CMD
, msgbuf
);
1358 envd_log(LOG_ALERT
, syscmd
);
1359 system_shutdown_started
= B_TRUE
;
1360 (void) system(syscmd
);
1362 } else if (sensorp
->shutdown_tstamp
!= 0)
1363 sensorp
->shutdown_tstamp
= 0;
1367 * Sweep thorugh Sensor Map and if warnings OR shutdown
1368 * are not logged then return to caller.
1370 return_flag
= B_TRUE
;
1371 for (i
= 0; i
< N_ENVD_SENSORS
; i
++)
1372 if (smap
[i
] == SENSOR_WARN
)
1373 return_flag
= B_FALSE
;
1375 if ((return_flag
== B_TRUE
) &&
1376 (system_shutdown_started
== B_FALSE
)) {
1380 (void) envd_sleep(SENSORPOLL_INTERVAL
);
1385 * This is env thread which monitors the current temperature when
1386 * warning threshold is exceeded. The job is to make sure it does
1387 * not execced/decrease shutdown threshold. If it does it will start
1388 * forced shutdown to avoid reaching hardware poweroff via THERM interrupt.
1389 * For Taco there will be one thread for the ADM chip.
1392 ovtemp_thr(void *args
)
1396 int hwm_id
= (int)args
;
1399 fd
= open(hwm_devs
[hwm_id
], O_RDWR
);
1401 envd_log(LOG_ERR
, ENV_ADM_OPEN_FAIL
, hwm_devs
[hwm_id
],
1402 errno
, strerror(errno
));
1409 * Monitor the sensors to update status
1415 * Sleep for specified seconds before issuing IOCTL
1418 (void) envd_sleep(INTERRUPTPOLL_INTERVAL
);
1421 * Read ADM1031 two Status Register to determine source of
1424 if ((err
= ioctl(fd
, ADM1031_GET_STATUS_1
, &stat
[0])) != -1)
1425 err
= ioctl(fd
, ADM1031_GET_STATUS_2
, &stat
[1]);
1429 envd_log(LOG_ERR
, "OverTemp: Status Error");
1434 envd_log(LOG_ERR
, "INTR %s Stat1 %x, Stat2 %x",
1435 hwm_devs
[hwm_id
], stat
[0], stat
[1]);
1437 if (stat
[0] & FANFAULT
)
1438 envd_log(LOG_ERR
, ENV_FAN_FAULT
,
1439 hwm_devs
[hwm_id
], hwm_fans
[hwm_id
][HWM_FAN1
]);
1441 if (stat
[1] & FANFAULT
)
1442 envd_log(LOG_ERR
, ENV_FAN_FAULT
,
1443 hwm_devs
[hwm_id
], hwm_fans
[hwm_id
][HWM_FAN2
]);
1446 * Check respective Remote/Local High, Low before start
1449 if ((stat
[0] & STAT1MASK
) || (stat
[1] & STAT2MASK
))
1450 (void) handle_overtemp_interrupt(hwm_id
);
1457 * Setup envrionmental monitor state and start threads to monitor
1458 * temperature and power management state.
1459 * Returns -1 on error, 0 if successful.
1467 if (getenv("SUNW_piclenvd_debug") != NULL
)
1470 if (pthread_attr_init(&thr_attr
) != 0 ||
1471 pthread_attr_setscope(&thr_attr
, PTHREAD_SCOPE_SYSTEM
) != 0) {
1475 ret
= envd_es_setup();
1483 * Setup temperature sensors and fail if we can't open
1484 * at least one sensor.
1486 if (envd_setup_sensors() <= 0) {
1491 * Setup fan device (don't fail even if we can't access
1492 * the fan as we can still monitor temeperature.
1494 (void) envd_setup_fans();
1496 /* If ES Segment setup failed,don't create thread */
1498 if (ovtemp_monitor
&& ovtemp_thr_created
== B_FALSE
) {
1499 if (pthread_create(&ovtemp_thr_id
, &thr_attr
, ovtemp_thr
,
1500 (void *)CPU_HWM_ID
) != 0)
1501 envd_log(LOG_ERR
, ENVTHR_THREAD_CREATE_FAILED
);
1503 ovtemp_thr_created
= B_TRUE
;
1507 * Create a thread to monitor PM state
1509 if (pm_monitor
&& pmthr_created
== B_FALSE
) {
1510 if (pthread_create(&pmthr_tid
, &thr_attr
, pmthr
,
1512 envd_log(LOG_CRIT
, PM_THREAD_CREATE_FAILED
);
1514 pmthr_created
= B_TRUE
;
1520 piclenvd_register(void)
1522 picld_plugin_register(&my_reg_info
);
1529 (void) env_picl_setup_tuneables();
1532 * Setup the environmental data structures
1534 if (envd_setup() != 0) {
1535 envd_log(LOG_CRIT
, ENVD_PLUGIN_INIT_FAILED
);
1540 * Now setup/populate PICL tree
1550 * Invoke env_picl_destroy() to remove any PICL nodes/properties
1551 * (including volatile properties) we created. Once this call
1552 * returns, there can't be any more calls from the PICL framework
1553 * to get current temperature or fan speed.
1556 envd_close_sensors();
1563 envd_log(int pri
, const char *fmt
, ...)
1568 vsyslog(pri
, fmt
, ap
);
1574 * Redefine sigwait to posix style external declaration so that LINT
1575 * does not check against libc version of sigwait() and complain as
1576 * it uses different number of arguments.
1578 #define sigwait my_posix_sigwait
1579 extern int my_posix_sigwait(const sigset_t
*set
, int *sig
);
1583 envd_sleep(uint_t sleep_tm
)
1592 (void) sigemptyset(&alrm_mask
);
1593 (void) sigaddset(&alrm_mask
, SIGALRM
);
1595 (void) alarm(sleep_tm
);
1596 (void) sigwait(&alrm_mask
, &sig
);
1603 * Tunables support functions
1605 static env_tuneable_t
*
1606 tuneable_lookup(picl_prophdl_t proph
)
1609 env_tuneable_t
*tuneablep
= NULL
;
1611 for (i
= 0; i
< ntuneables
; i
++) {
1612 tuneablep
= &tuneables
[i
];
1613 if (tuneablep
->proph
== proph
)
1621 get_tach(ptree_rarg_t
*parg
, void *buf
)
1623 picl_prophdl_t proph
;
1624 env_tuneable_t
*tuneablep
;
1628 proph
= parg
->proph
;
1630 tuneablep
= tuneable_lookup(proph
);
1632 if (tuneablep
== NULL
)
1633 return (PICL_FAILURE
);
1635 fd
= open(CPU_HWM_DEVFS
, O_RDWR
);
1638 return (PICL_FAILURE
);
1641 if (ioctl(fd
, ADM1031_GET_CONFIG_2
, &cfg
) == -1) {
1642 return (PICL_FAILURE
);
1645 if ((cfg
& TACH_ENABLE_MASK
) == TACH_ENABLE_MASK
) {
1646 *((int *)tuneablep
->value
) = ENABLE
;
1649 *((int *)tuneablep
->value
) = DISABLE
;
1652 (void) memcpy(buf
, tuneablep
->value
,
1656 return (PICL_SUCCESS
);
1660 set_tach(ptree_warg_t
*parg
, const void *buf
)
1662 picl_prophdl_t proph
;
1663 env_tuneable_t
*tuneablep
;
1667 if (parg
->cred
.dc_euid
!= 0)
1668 return (PICL_PERMDENIED
);
1670 proph
= parg
->proph
;
1672 tuneablep
= tuneable_lookup(proph
);
1674 if (tuneablep
== NULL
)
1675 return (PICL_FAILURE
);
1677 fd
= open(CPU_HWM_DEVFS
, O_RDWR
);
1680 return (PICL_FAILURE
);
1683 if (ioctl(fd
, ADM1031_GET_CONFIG_2
, &cfg
) == -1) {
1684 return (PICL_FAILURE
);
1687 (void) memcpy(&val
, (caddr_t
)buf
, sizeof (val
));
1689 if (val
== ENABLE
) {
1690 cfg
|= TACH_ENABLE_MASK
;
1691 } else if (val
== DISABLE
) {
1692 cfg
&= ~TACH_ENABLE_MASK
;
1695 if (ioctl(fd
, ADM1031_SET_CONFIG_2
, &cfg
) == -1) {
1696 return (PICL_FAILURE
);
1700 return (PICL_SUCCESS
);
1704 get_monitor_mode(ptree_rarg_t
*parg
, void *buf
)
1706 picl_prophdl_t proph
;
1707 env_tuneable_t
*tuneablep
;
1711 proph
= parg
->proph
;
1713 tuneablep
= tuneable_lookup(proph
);
1715 if (tuneablep
== NULL
)
1716 return (PICL_FAILURE
);
1718 fd
= open(CPU_HWM_DEVFS
, O_RDWR
);
1721 return (PICL_FAILURE
);
1724 if (ioctl(fd
, ADM1031_GET_MONITOR_MODE
, &mmode
) == -1) {
1725 return (PICL_FAILURE
);
1728 if (mmode
== ADM1031_AUTO_MODE
) {
1729 *((int *)tuneablep
->value
) = ENABLE
;
1731 *((int *)tuneablep
->value
) = DISABLE
;
1734 (void) memcpy(buf
, tuneablep
->value
,
1738 return (PICL_SUCCESS
);
1742 set_monitor_mode(ptree_warg_t
*parg
, const void *buf
)
1744 picl_prophdl_t proph
;
1745 env_tuneable_t
*tuneablep
;
1749 if (parg
->cred
.dc_euid
!= 0)
1750 return (PICL_PERMDENIED
);
1752 proph
= parg
->proph
;
1754 tuneablep
= tuneable_lookup(proph
);
1756 if (tuneablep
== NULL
)
1757 return (PICL_FAILURE
);
1759 fd
= open(CPU_HWM_DEVFS
, O_RDWR
);
1761 return (PICL_FAILURE
);
1763 (void) memcpy(&val
, buf
, sizeof (val
));
1764 if (val
== ENABLE
) {
1765 mmode
= ADM1031_AUTO_MODE
;
1766 } else if (val
== DISABLE
) {
1767 mmode
= ADM1031_MANUAL_MODE
;
1770 if (ioctl(fd
, ADM1031_SET_MONITOR_MODE
, &mmode
) == -1) {
1771 return (PICL_FAILURE
);
1775 return (PICL_SUCCESS
);
1779 get_string_val(ptree_rarg_t
*parg
, void *buf
)
1781 picl_prophdl_t proph
;
1782 env_tuneable_t
*tuneablep
;
1784 proph
= parg
->proph
;
1786 tuneablep
= tuneable_lookup(proph
);
1788 if (tuneablep
== NULL
)
1789 return (PICL_FAILURE
);
1791 (void) memcpy(buf
, (caddr_t
)tuneablep
->value
,
1794 return (PICL_SUCCESS
);
1798 set_string_val(ptree_warg_t
*parg
, const void *buf
)
1800 picl_prophdl_t proph
;
1801 env_tuneable_t
*tuneablep
;
1803 proph
= parg
->proph
;
1805 if (parg
->cred
.dc_euid
!= 0)
1806 return (PICL_PERMDENIED
);
1808 tuneablep
= tuneable_lookup(proph
);
1810 if (tuneablep
== NULL
)
1811 return (PICL_FAILURE
);
1813 (void) memcpy((caddr_t
)tuneables
->value
, (caddr_t
)buf
,
1816 return (PICL_SUCCESS
);
1820 get_int_val(ptree_rarg_t
*parg
, void *buf
)
1822 picl_prophdl_t proph
;
1823 env_tuneable_t
*tuneablep
;
1825 proph
= parg
->proph
;
1827 tuneablep
= tuneable_lookup(proph
);
1829 if (tuneablep
== NULL
)
1830 return (PICL_FAILURE
);
1832 (void) memcpy((int *)buf
, (int *)tuneablep
->value
,
1835 return (PICL_SUCCESS
);
1839 set_int_val(ptree_warg_t
*parg
, const void *buf
)
1841 picl_prophdl_t proph
;
1842 env_tuneable_t
*tuneablep
;
1844 if (parg
->cred
.dc_euid
!= 0)
1845 return (PICL_PERMDENIED
);
1847 proph
= parg
->proph
;
1849 tuneablep
= tuneable_lookup(proph
);
1851 if (tuneablep
== NULL
)
1852 return (PICL_FAILURE
);
1854 (void) memcpy((int *)tuneablep
->value
, (int *)buf
,
1857 return (PICL_SUCCESS
);