xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4u/enchilada/envd/piclenvd.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * CDDL HEADER START
3  *
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
7  * with the License.
8  *
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.
13  *
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]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * This file contains the environmental PICL plug-in module.
29  */
30 
31 /*
32  * This plugin sets up the PICLTREE for Enchilada WS.
33  * It provides functionality to get/set temperatures and
34  * fan speeds.
35  *
36  * The environmental policy defaults to the auto mode
37  * as programmed by OBP at boot time.
38  */
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <sys/sysmacros.h>
43 #include <limits.h>
44 #include <string.h>
45 #include <strings.h>
46 #include <stdarg.h>
47 #include <alloca.h>
48 #include <unistd.h>
49 #include <sys/processor.h>
50 #include <syslog.h>
51 #include <errno.h>
52 #include <fcntl.h>
53 #include <picl.h>
54 #include <picltree.h>
55 #include <picldefs.h>
56 #include <pthread.h>
57 #include <signal.h>
58 #include <libdevinfo.h>
59 #include <sys/pm.h>
60 #include <sys/open.h>
61 #include <sys/time.h>
62 #include <sys/utsname.h>
63 #include <sys/systeminfo.h>
64 #include <note.h>
65 #include <sys/i2c/clients/i2c_client.h>
66 #include <sys/i2c/clients/adm1031.h>
67 #include <sys/i2c/clients/pic16f819_reg.h>
68 #include "envd.h"
69 #include <sys/scsi/scsi.h>
70 #include <sys/scsi/generic/commands.h>
71 
72 
73 /*
74  * PICL plugin entry points
75  */
76 static void piclenvd_register(void);
77 static void piclenvd_init(void);
78 static void piclenvd_fini(void);
79 
80 /*
81  * Env setup routines
82  */
83 extern void env_picl_setup(void);
84 extern void env_picl_destroy(void);
85 extern int env_picl_setup_tuneables(void);
86 
87 /*
88  * Sleep routine used for polling
89  */
90 static int get_dimm_fan_speed(int, fanspeed_t *);
91 static int is_dimm_fan_failed(void);
92 
93 #pragma	init(piclenvd_register)
94 
95 /*
96  * Plugin registration information
97  */
98 static picld_plugin_reg_t my_reg_info = {
99 	PICLD_PLUGIN_VERSION,
100 	PICLD_PLUGIN_CRITICAL,
101 	"SUNW_piclenvd",
102 	piclenvd_init,
103 	piclenvd_fini,
104 };
105 
106 #define	REGISTER_INFORMATION_STRING_LENGTH	16
107 static char dimm_fan_rpm_string[REGISTER_INFORMATION_STRING_LENGTH] = {0};
108 static char dimm_fan_status_string[REGISTER_INFORMATION_STRING_LENGTH] = {0};
109 static char dimm_fan_command_string[REGISTER_INFORMATION_STRING_LENGTH] = {0};
110 static char dimm_fan_debug_string[REGISTER_INFORMATION_STRING_LENGTH] = {0};
111 
112 static int	scsi_log_sense(int fd, uchar_t page_code, uchar_t *pagebuf,
113 			uint16_t pagelen);
114 static int	get_disk_temp(env_disk_t *);
115 /*
116  * ES Segment data structures
117  */
118 static sensor_ctrl_blk_t	sensor_ctrl[MAX_SENSORS];
119 static fan_ctrl_blk_t		fan_ctrl[MAX_FANS];
120 static fruenvseg_t		*envfru = NULL;
121 
122 /*
123  * Env thread variables
124  */
125 static boolean_t  system_shutdown_started = B_FALSE;
126 static boolean_t  ovtemp_thr1_created = B_FALSE;
127 static pthread_t  ovtemp_thr1_id;
128 static pthread_attr_t thr_attr;
129 static boolean_t  ovtemp_thr2_created = B_FALSE;
130 static pthread_t  ovtemp_thr2_id;
131 static boolean_t  dimm_fan_thr_created = B_FALSE;
132 static pthread_t  dimm_fan_thr_id;
133 static boolean_t  disk_temp_thr_created = B_FALSE;
134 static pthread_t  disk_temp_thr_id;
135 
136 /*
137  * PM thread related variables
138  */
139 static pthread_t	pmthr_tid;	/* pmthr thread ID */
140 static int		pm_fd = -1;	/* PM device file descriptor */
141 static boolean_t	pmthr_created = B_FALSE;
142 static int		cur_lpstate;	/* cur low power state */
143 
144 /*
145  * Envd plug-in verbose flag set by SUNW_PICLENVD_DEBUG environment var
146  * Setting the verbose tuneable also enables debugging for better
147  * control
148  */
149 int	env_debug = 0;
150 
151 /*
152  * Fan devices
153  */
154 static env_fan_t envd_sys_out_fan = {
155 	ENV_SYSTEM_OUT_FAN, ENV_SYSTEM_OUT_FAN_DEVFS, NULL,
156 	SYSTEM_OUT_FAN_ID, SYSTEM_OUT_FAN_SPEED_MIN, SYSTEM_OUT_FAN_SPEED_MAX,
157 	-1, -1,
158 };
159 
160 static env_fan_t envd_sys_in_fan = {
161 	ENV_SYSTEM_INTAKE_FAN, ENV_SYSTEM_INTAKE_FAN_DEVFS, NULL,
162 	SYSTEM_INTAKE_FAN_ID, SYSTEM_INTAKE_FAN_SPEED_MIN,
163 	SYSTEM_INTAKE_FAN_SPEED_MAX, -1, -1,
164 };
165 
166 static env_fan_t envd_cpu0_fan = {
167 	ENV_CPU0_FAN, ENV_CPU0_FAN_DEVFS, NULL,
168 	CPU0_FAN_ID, CPU_FAN_SPEED_MIN, CPU_FAN_SPEED_MAX, -1, -1,
169 };
170 
171 static env_fan_t envd_cpu1_fan = {
172 	ENV_CPU1_FAN, ENV_CPU1_FAN_DEVFS, NULL,
173 	CPU1_FAN_ID, CPU_FAN_SPEED_MIN, CPU_FAN_SPEED_MAX, -1, -1,
174 };
175 
176 static env_fan_t envd_dimm_fan = {
177 	ENV_DIMM_FAN, ENV_DIMM_FAN_DEVFS, NULL,
178 	DIMM_FAN_ID, 100, 100, -1, -1,
179 };
180 
181 static env_disk_t envd_disk0 = {
182 	ENV_DISK0, ENV_DISK0_DEVFS, DISK0_PHYSPATH, DISK0_NODE_PATH,
183 	DISK0_ID, -1, -1,
184 };
185 
186 static env_disk_t envd_disk1 = {
187 	ENV_DISK1, ENV_DISK1_DEVFS, DISK1_PHYSPATH, DISK1_NODE_PATH,
188 	DISK1_ID, -1, -1,
189 };
190 
191 /*
192  * The vendor-id and device-id are the properties associated with
193  * the SCSI controller. This is used to identify a particular controller
194  * like LSI1030.
195  */
196 #define	VENDOR_ID	"vendor-id"
197 #define	DEVICE_ID	"device-id"
198 
199 /*
200  * The implementation for SCSI disk drives to supply info. about
201  * temperature is not mandatory. Hence we first determine if the
202  * temperature page is supported. To do this we need to scan the list
203  * of pages supported.
204  */
205 #define	SUPPORTED_LPAGES	0
206 #define	TEMPERATURE_PAGE	0x0D
207 #define	LOGPAGEHDRSIZE	4
208 
209 /*
210  * NULL terminated array of fans
211  */
212 static env_fan_t *envd_fans[] = {
213 	&envd_cpu0_fan,
214 	&envd_cpu1_fan,
215 	&envd_sys_out_fan,
216 	&envd_sys_in_fan,
217 	&envd_dimm_fan,
218 	NULL
219 };
220 
221 static	env_disk_t	*envd_disks[] = {
222 	&envd_disk0,
223 	&envd_disk1,
224 	NULL
225 };
226 
227 /*
228  * ADM1031 speedrange map is indexed by a 2-bit value
229  */
230 static int	adm_speedrange_map[] = {1, 2, 4, 8};
231 
232 /*
233  * ADM1031 devices
234  */
235 static char	*hwm_devs[] = {
236 	CPU_HWM_DEVFS,	/* CPU_HWM_ID */
237 	SYS_HWM_DEVFS	/* SYS_HWM_ID */
238 };
239 
240 /*
241  * Fan names associated with each ADM1031 hwms - used to
242  * print fault messages.
243  */
244 static char	*hwm_fans[MAX_HWMS][2] = {
245 	{ENV_CPU0_FAN, ENV_CPU1_FAN},
246 	{ENV_SYSTEM_INTAKE_FAN, ENV_SYSTEM_OUT_FAN}
247 };
248 
249 /*
250  * Temperature sensors
251  */
252 static env_sensor_t envd_sensors[] = {
253 	{ SENSOR_CPU0_DIE, SENSOR_CPU0_DIE_DEVFS, NULL,
254 	    CPU0_SENSOR_ID, CPU_HWM_ID, (void *)&envd_cpu0_fan, -1},
255 	{ SENSOR_CPU1_DIE, SENSOR_CPU1_DIE_DEVFS, NULL,
256 	    CPU1_SENSOR_ID, CPU_HWM_ID, (void *)&envd_cpu1_fan, -1},
257 	{ SENSOR_INT_AMB_0, SENSOR_INT_AMB_0_DEVFS, NULL,
258 	    INT_AMB0_SENSOR_ID, CPU_HWM_ID, NULL, -1},
259 	{ SENSOR_SYS_OUT, SENSOR_SYS_OUT_DEVFS, NULL,
260 	    SYS_OUT_SENSOR_ID, SYS_HWM_ID, (void *)&envd_sys_out_fan, -1},
261 	{ SENSOR_INT_AMB_1, SENSOR_INT_AMB_1_DEVFS, NULL,
262 	    INT_AMB1_SENSOR_ID, SYS_HWM_ID, NULL, -1},
263 	{ SENSOR_SYS_IN, SENSOR_SYS_IN_DEVFS, NULL,
264 	    SYS_IN_SENSOR_ID, SYS_HWM_ID, (void *)&envd_sys_in_fan, -1},
265 };
266 #define	N_ENVD_SENSORS	(sizeof (envd_sensors)/sizeof (envd_sensors[0]))
267 
268 #define	NOT_AVAILABLE	"NA"
269 
270 /*
271  * ADM1031 macros
272  */
273 #define	TACH_UNKNOWN	255
274 #define	FAN_OUT_OF_RANGE	(TACH_UNKNOWN)
275 #define	ADM_HYSTERISIS	5
276 #define	N_SEQ_TACH	15
277 
278 #define	TMIN_MASK	(0xF8)
279 #define	TMIN_SHIFT	(3)
280 #define	TMIN_UNITS	(4)	/* increments of 4 degrees celsius */
281 #define	TRANGE_MASK	(0x7)
282 
283 #define	TMIN(regval)	(((regval & TMIN_MASK) >> TMIN_SHIFT) * TMIN_UNITS)
284 #define	TRANGE(regval)	(regval & TRANGE_MASK)
285 
286 #define	GET_TMIN_RANGE(tmin, trange) \
287 	((((tmin / TMIN_UNITS) & TMIN_MASK) << TMIN_SHIFT) | \
288 	(trange & TRANGE_MASK))
289 
290 #define	TACH_ENABLE_MASK		(0x0C)
291 #define	ADM_SETFANSPEED_CONV(speed)	(15 * speed / 100)
292 
293 /*
294  * Tuneables
295  */
296 #define	ENABLE	1
297 #define	DISABLE	0
298 
299 int	monitor_disk_temp	= 1;	/* enabled */
300 static	int	disk_high_warn_temperature	= DISK_HIGH_WARN_TEMPERATURE;
301 static	int	disk_low_warn_temperature	= DISK_LOW_WARN_TEMPERATURE;
302 static	int	disk_high_shutdown_temperature	=
303 						DISK_HIGH_SHUTDOWN_TEMPERATURE;
304 static	int	disk_low_shutdown_temperature	= DISK_LOW_SHUTDOWN_TEMPERATURE;
305 static	int	disk_scan_interval		= DISK_SCAN_INTERVAL;
306 
307 static int get_monitor_cpu_mode(ptree_rarg_t *parg, void *buf);
308 static int set_monitor_cpu_mode(ptree_warg_t *parg, const void *buf);
309 static int get_monitor_sys_mode(ptree_rarg_t *parg, void *buf);
310 static int set_monitor_sys_mode(ptree_warg_t *parg, const void *buf);
311 static int get_int_val(ptree_rarg_t *parg, void *buf);
312 static int set_int_val(ptree_warg_t *parg, const void *buf);
313 static int get_string_val(ptree_rarg_t *parg, void *buf);
314 static int set_string_val(ptree_warg_t *parg, const void *buf);
315 static int get_cpu_tach(ptree_rarg_t *parg, void *buf);
316 static int set_cpu_tach(ptree_warg_t *parg, const void *buf);
317 static int get_sys_tach(ptree_rarg_t *parg, void *buf);
318 static int set_sys_tach(ptree_warg_t *parg, const void *buf);
319 
320 static int	shutdown_override	= 0;
321 static int	sensor_poll_interval	= SENSORPOLL_INTERVAL;
322 static int	warning_interval	= WARNING_INTERVAL;
323 static int	disk_warning_interval	= DISK_WARNING_INTERVAL;
324 static int	disk_warning_duration	= DISK_WARNING_DURATION;
325 static int	shutdown_interval	= SHUTDOWN_INTERVAL;
326 static int	disk_shutdown_interval	= DISK_SHUTDOWN_INTERVAL;
327 static int	ovtemp_monitor		= 1;	/* enabled */
328 static int	pm_monitor		= 1;	/* enabled */
329 static int	mon_fanstat		= 1;	/* enabled */
330 
331 static int	cpu_mode;
332 static int	sys_mode;
333 static int	cpu_tach;
334 static int	sys_tach;
335 static char	shutdown_cmd[] = SHUTDOWN_CMD;
336 
337 env_tuneable_t tuneables[] = {
338 	{"ovtemp-monitor", PICL_PTYPE_INT, &ovtemp_monitor,
339 	    &get_int_val, &set_int_val, sizeof (int)},
340 
341 	{"pm-monitor", PICL_PTYPE_INT, &pm_monitor,
342 	    &get_int_val, &set_int_val, sizeof (int)},
343 
344 	{"shutdown-override", PICL_PTYPE_INT, &shutdown_override,
345 	    &get_int_val, &set_int_val, sizeof (int)},
346 
347 	{"cpu-hm-automode-enable", PICL_PTYPE_INT, &cpu_mode,
348 	    &get_monitor_cpu_mode, &set_monitor_cpu_mode,
349 	    sizeof (int)},
350 
351 	{"sys-hm-automode-enable", PICL_PTYPE_INT, &sys_mode,
352 	    &get_monitor_sys_mode, &set_monitor_sys_mode,
353 	    sizeof (int)},
354 
355 	{"sensor-poll-interval", PICL_PTYPE_INT,
356 	    &sensor_poll_interval,
357 	    &get_int_val, &set_int_val,
358 	    sizeof (int)},
359 
360 	{"disk-scan-interval", PICL_PTYPE_INT,
361 	    &disk_scan_interval,
362 	    &get_int_val, &set_int_val,
363 	    sizeof (int)},
364 
365 	{"warning-interval", PICL_PTYPE_INT, &warning_interval,
366 	    &get_int_val, &set_int_val,
367 	    sizeof (int)},
368 
369 	{"shutdown-interval", PICL_PTYPE_INT, &shutdown_interval,
370 	    &get_int_val, &set_int_val,
371 	    sizeof (int)},
372 
373 	{"disk_warning-interval", PICL_PTYPE_INT, &disk_warning_interval,
374 	    &get_int_val, &set_int_val,
375 	    sizeof (int)},
376 
377 	{"disk_warning-duration", PICL_PTYPE_INT, &disk_warning_duration,
378 	    &get_int_val, &set_int_val,
379 	    sizeof (int)},
380 
381 	{"disk_shutdown-interval", PICL_PTYPE_INT, &disk_shutdown_interval,
382 	    &get_int_val, &set_int_val,
383 	    sizeof (int)},
384 
385 	{"shutdown-command", PICL_PTYPE_CHARSTRING, shutdown_cmd,
386 	    &get_string_val, &set_string_val,
387 	    sizeof (shutdown_cmd)},
388 
389 	{"cpu-tach-enable", PICL_PTYPE_INT, &cpu_tach,
390 	    &get_cpu_tach, &set_cpu_tach,
391 	    sizeof (int)},
392 
393 	{"sys-tach-enable", PICL_PTYPE_INT, &sys_tach,
394 	    &get_sys_tach, &set_sys_tach,
395 	    sizeof (int)},
396 
397 	{"monitor-fanstat", PICL_PTYPE_INT, &mon_fanstat,
398 	    &get_int_val, &set_int_val, sizeof (int)},
399 
400 	{"monitor-disk-temp", PICL_PTYPE_INT, &monitor_disk_temp,
401 	    &get_int_val, &set_int_val, sizeof (int)},
402 
403 	{"disk-high-warn-temperature", PICL_PTYPE_INT,
404 	    &disk_high_warn_temperature, &get_int_val,
405 	    &set_int_val, sizeof (int)},
406 
407 	{"disk-low-warn-temperature", PICL_PTYPE_INT,
408 	    &disk_low_warn_temperature, &get_int_val,
409 	    &set_int_val, sizeof (int)},
410 
411 	{"disk-high-shutdown-temperature", PICL_PTYPE_INT,
412 	    &disk_high_shutdown_temperature, &get_int_val,
413 	    &set_int_val, sizeof (int)},
414 
415 	{"disk-low-shutdown-temperature", PICL_PTYPE_INT,
416 	    &disk_low_shutdown_temperature, &get_int_val,
417 	    &set_int_val, sizeof (int)},
418 
419 	{"verbose", PICL_PTYPE_INT, &env_debug,
420 	    &get_int_val, &set_int_val, sizeof (int)},
421 
422 
423 };
424 
425 /*
426  * We use this to figure out how many tuneables there are
427  * This is variable because the publishing routine needs this info
428  * in piclenvsetup.c
429  */
430 int	ntuneables = (sizeof (tuneables)/sizeof (tuneables[0]));
431 
432 /*
433  * Table Handling Code
434  */
435 static void
436 fini_table(table_t *tblp)
437 {
438 	if (tblp == NULL)
439 		return;
440 	free(tblp->xymap);
441 	free(tblp);
442 }
443 
444 static table_t *
445 init_table(int npoints)
446 {
447 	table_t		*tblp;
448 	point_t		*xy;
449 
450 	if (npoints == 0)
451 		return (NULL);
452 
453 	if ((tblp = malloc(sizeof (*tblp))) == NULL)
454 		return (NULL);
455 
456 	if ((xy = malloc(sizeof (*xy) * npoints)) == NULL) {
457 		free(tblp);
458 		return (NULL);
459 	}
460 
461 	tblp->nentries = npoints;
462 	tblp->xymap = xy;
463 
464 	return (tblp);
465 }
466 
467 /*
468  * function: calculates y for a given x based on a table of points
469  * for monotonically increasing x values.
470  * 'tbl' specifies the table to use, 'val' specifies the 'x', returns 'y'
471  */
472 static int
473 y_of_x(table_t *tbl, int xval)
474 {
475 	int		i;
476 	int		entries;
477 	point_t		*xymap;
478 	float		newval;
479 	float		dy, dx, slope;
480 
481 	entries = tbl->nentries;
482 	xymap = tbl->xymap;
483 	/*
484 	 * If the temperature is outside the correction table
485 	 * then simply return the original value.
486 	 */
487 	if ((xval < xymap[0].x) || (xval > xymap[entries - 1].x))
488 		return (xval);
489 	if (xval == xymap[0].x)
490 		return (xymap[0].y);
491 	if (xval == xymap[entries - 1].x)
492 		return (xymap[entries - 1].y);
493 
494 	for (i = 1; i < entries - 1; i++) {
495 		if (xval == xymap[i].x)
496 			return (xymap[i].y);
497 		if (xval < xymap[i].x)
498 			break;
499 	}
500 
501 	/*
502 	 * Use linear interpolation
503 	 */
504 	dy = (float)(xymap[i].y - xymap[i-1].y);
505 	dx = (float)(xymap[i].x - xymap[i-1].x);
506 	slope = dy/dx;
507 	newval = xymap[i - 1].y + slope * (xval - xymap[i - 1].x);
508 	return ((int)(newval + (newval >= 0 ? 0.5 : -0.5)));
509 }
510 
511 /*
512  * Get environmental segment from the specified FRU SEEPROM
513  */
514 static int
515 get_envseg(int fd, void **envsegp, int *envseglenp)
516 {
517 	int			i, segcnt, envseglen;
518 	section_layout_t	section;
519 	segment_layout_t	segment;
520 	uint8_t			*envseg;
521 
522 	if (lseek(fd, (long)SECTION_HDR_OFFSET, 0) == -1L ||
523 	    read(fd, &section, sizeof (section)) != sizeof (section)) {
524 		return (EINVAL);
525 	}
526 
527 	/*
528 	 * Verify we have the correct section and contents are valid
529 	 * For now, we don't verify the CRC.
530 	 */
531 	if (section.header_tag != SECTION_HDR_TAG ||
532 	    GET_UNALIGN16(&section.header_version[0]) != SECTION_HDR_VER) {
533 		if (env_debug)
534 			envd_log(LOG_INFO,
535 			    "Invalid section header tag:%x  version:%x\n",
536 			    section.header_tag,
537 			    GET_UNALIGN16(&section.header_version));
538 		return (EINVAL);
539 	}
540 
541 	/*
542 	 * Locate our environmental segment
543 	 */
544 	segcnt = section.segment_count;
545 	for (i = 0; i < segcnt; i++) {
546 		if (read(fd, &segment, sizeof (segment)) != sizeof (segment)) {
547 			return (EINVAL);
548 		}
549 		if (env_debug)
550 			envd_log(LOG_INFO,
551 			    "Seg name: %x  desc:%x off:%x  len:%x\n",
552 			    GET_UNALIGN16(&segment.name),
553 			    GET_UNALIGN32(&segment.descriptor[0]),
554 			    GET_UNALIGN16(&segment.offset),
555 			    GET_UNALIGN16(&segment.length));
556 		if (GET_UNALIGN16(&segment.name) == ENVSEG_NAME)
557 			break;
558 	}
559 
560 	if (i >= segcnt) {
561 		return (ENOENT);
562 	}
563 
564 	/*
565 	 * Allocate memory to hold the environmental segment data.
566 	 */
567 	envseglen = GET_UNALIGN16(&segment.length);
568 	if ((envseg = malloc(envseglen)) == NULL) {
569 		return (ENOMEM);
570 	}
571 
572 	if (lseek(fd, (long)GET_UNALIGN16(&segment.offset), 0) == -1L ||
573 	    read(fd, envseg, envseglen) != envseglen) {
574 		(void) free(envseg);
575 		return (EIO);
576 	}
577 	*envsegp = envseg;
578 	*envseglenp = envseglen;
579 	return (0);
580 }
581 
582 /*
583  * Get all environmental segments
584  * Return NULL on error
585  */
586 static fruenvseg_t *
587 get_fru_envsegs(void)
588 {
589 	fruenvseg_t		*fruenvsegs;
590 	envseg_layout_t		*envsegp;
591 	void			*envsegbufp;
592 	int			fd, envseglen, hdrlen;
593 	char			path[PATH_MAX];
594 
595 	fruenvsegs = NULL;
596 	fruenvsegs = malloc(sizeof (*fruenvsegs));
597 	if (fruenvsegs == NULL) {
598 		return (NULL);
599 	}
600 
601 	/*
602 	 * Now get the environmental segment from this FRU
603 	 */
604 	(void) snprintf(path, sizeof (path), "%s%s", I2C_DEVFS, MBFRU_DEV);
605 	fd = open(path, O_RDONLY);
606 	if (fd == -1) {
607 		envd_log(LOG_ERR, ENV_FRU_OPEN_FAIL, errno, path);
608 		free(fruenvsegs);
609 		return (NULL);
610 	}
611 
612 	/*
613 	 * Read environmental segment from this FRU SEEPROM
614 	 */
615 	if (get_envseg(fd, &envsegbufp, &envseglen) != 0) {
616 		envd_log(LOG_ERR, ENV_FRU_BAD_ENVSEG, path);
617 		free(fruenvsegs);
618 		(void) close(fd);
619 		return (NULL);
620 	}
621 
622 	/*
623 	 * Validate envseg version number and header length
624 	 */
625 	envsegp = (envseg_layout_t *)envsegbufp;
626 	hdrlen = sizeof (envseg_layout_t) -
627 	    sizeof (envseg_sensor_t) +
628 	    (envsegp->sensor_count) * sizeof (envseg_sensor_t);
629 
630 	if (envsegp->version != ENVSEG_VERSION ||
631 	    envseglen < hdrlen) {
632 		/*
633 		 * version mismatch or header not big enough
634 		 */
635 		envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG, FRU_SEEPROM_NAME);
636 		if (envsegbufp != NULL)
637 			(void) free(envsegbufp);
638 		free(fruenvsegs);
639 		(void) close(fd);
640 		return (NULL);
641 	}
642 
643 	fruenvsegs->envseglen = envseglen;
644 	fruenvsegs->envsegbufp = envsegbufp;
645 	(void) close(fd);
646 	return (fruenvsegs);
647 }
648 
649 static int
650 process_fru_seeprom(unsigned char *buff)
651 {
652 	id_off_t id;
653 	int  i;
654 	int  id_offset = 0;
655 	int  nsensors;
656 	int  nfans;
657 	env_fan_t *fnodep;
658 	env_sensor_t *snodep;
659 
660 #define	NSENSOR_OFFSET	1
661 #define	ID_OFF_SIZE	6
662 #define	NFANS_OFFSET(x)	((x * ID_OFF_SIZE) + 2)
663 
664 	nsensors = (int)buff[NSENSOR_OFFSET];
665 	if (nsensors != MAX_SENSORS) {
666 		envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG, FRU_SEEPROM_NAME);
667 		return (-1);
668 	}
669 
670 	nfans = (int)buff[NFANS_OFFSET(nsensors)];
671 	if (nfans != MAX_FANS) {
672 		envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG, FRU_SEEPROM_NAME);
673 		return (-1);
674 	}
675 
676 	while (nsensors > 0) {
677 		(void) memcpy((char *)&id,
678 		    (char *)&buff[id_offset + 2],
679 		    ID_OFF_SIZE);
680 
681 		if (env_debug)
682 			envd_log(LOG_ERR, "\n Sensor Id %x offset %x",
683 			    id.id, id.offset);
684 
685 		if (id.id > MAX_SENSOR_ID) {
686 			envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG,
687 			    FRU_SEEPROM_NAME);
688 			return (-1);
689 		}
690 
691 		/*
692 		 * Copy into the sensor control block array according to the
693 		 * sensor ID
694 		 */
695 		(void) memcpy((char *)&sensor_ctrl[id.id],
696 		    (char *)&buff[id.offset],
697 		    sizeof (sensor_ctrl_blk_t));
698 		nsensors--;
699 		id_offset += ID_OFF_SIZE;
700 	}
701 
702 	/*
703 	 * Skip past no of Fan entry(single byte)
704 	 */
705 	id_offset++;
706 	while (nfans > 0) {
707 		(void) memcpy((char *)&id, (char *)&buff[id_offset + 2],
708 		    ID_OFF_SIZE);
709 
710 		if (env_debug)
711 			envd_log(LOG_ERR, "\n Fan Id %x offset %x", id.id,
712 			    id.offset);
713 
714 		if (id.id > 3) {
715 			envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG,
716 			    FRU_SEEPROM_NAME);
717 			return (-1);
718 		}
719 
720 		(void) memcpy((char *)&fan_ctrl[id.id],
721 		    (char *)&buff[id.offset], sizeof (fan_ctrl_blk_t));
722 
723 		nfans--;
724 		id_offset += ID_OFF_SIZE;
725 	}
726 
727 	/*
728 	 * Match Sensor/ES ID and point correct data
729 	 * based on IDs
730 	 */
731 	for (i = 0; i < N_ENVD_SENSORS; i++) {
732 		snodep = &envd_sensors[i];
733 		snodep->es_ptr = &sensor_ctrl[snodep->id];
734 	}
735 
736 	/*
737 	 * Match Fan/ES ID and point to correct ES Data
738 	 * based on IDs
739 	 */
740 	for (i = 0; (fnodep = envd_fans[i]) != NULL; i++)
741 		fnodep->es_ptr = &fan_ctrl[fnodep->id];
742 
743 	return (0);
744 }
745 
746 static int
747 envd_es_setup(void)
748 {
749 	envfru = get_fru_envsegs();
750 	if (envfru == NULL) {
751 		envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG, FRU_SEEPROM_NAME);
752 		return (-1);
753 	}
754 	return (process_fru_seeprom((uchar_t *)envfru->envsegbufp));
755 }
756 
757 static void
758 envd_es_destroy(void)
759 {
760 	if (envfru != NULL)
761 		free(envfru->envsegbufp);
762 }
763 
764 /*
765  * Lookup fan and return a pointer to env_fan_t data structure.
766  */
767 env_fan_t *
768 fan_lookup(char *name)
769 {
770 	int		i;
771 	env_fan_t	*fanp;
772 
773 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
774 		if (strcmp(fanp->name, name) == 0)
775 			return (fanp);
776 	}
777 	return (NULL);
778 }
779 
780 /*
781  * Lookup sensor and return a pointer to env_sensor_t data structure.
782  */
783 env_sensor_t *
784 sensor_lookup(char *name)
785 {
786 	env_sensor_t	*sensorp;
787 	int		i;
788 
789 	for (i = 0; i < N_ENVD_SENSORS; ++i) {
790 		sensorp = &envd_sensors[i];
791 		if (strcmp(sensorp->name, name) == 0)
792 			return (sensorp);
793 	}
794 	return (NULL);
795 }
796 
797 /*
798  * Lookup disk and return a pointer to env_disk_t data structure.
799  */
800 env_disk_t *
801 disk_lookup(char *name)
802 {
803 	int		i;
804 	env_disk_t	*diskp;
805 
806 	for (i = 0; (diskp = envd_disks[i]) != NULL; i++) {
807 		if (strncmp(diskp->name, name, strlen(name)) == 0)
808 			return (diskp);
809 	}
810 	return (NULL);
811 }
812 
813 /*
814  * Get current temperature
815  * Returns -1 on error, 0 if successful
816  */
817 int
818 get_temperature(env_sensor_t *sensorp, tempr_t *temp)
819 {
820 	int	fd = sensorp->fd;
821 	int	retval = 0;
822 
823 	if (fd == -1)
824 		retval = -1;
825 	else if (ioctl(fd, I2C_GET_TEMPERATURE, temp) == -1) {
826 
827 		retval = -1;
828 
829 		if (sensorp->error == 0) {
830 			sensorp->error = 1;
831 			envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_FAIL,
832 			    sensorp->name, errno, strerror(errno));
833 		}
834 	} else if (sensorp->error != 0) {
835 		sensorp->error = 0;
836 		envd_log(LOG_WARNING, ENV_SENSOR_ACCESS_OK, sensorp->name);
837 	}
838 	if (sensorp->crtbl != NULL) {
839 		*temp = (tempr_t)y_of_x(sensorp->crtbl, *temp);
840 	}
841 
842 	return (retval);
843 }
844 
845 /*
846  * Get current disk temperature
847  * Returns -1 on error, 0 if successful
848  */
849 int
850 disk_temperature(env_disk_t *diskp, tempr_t *temp)
851 {
852 	int	retval = 0;
853 
854 	if (diskp == NULL)
855 		retval = -1;
856 	else  {
857 		*temp = diskp->current_temp;
858 	}
859 	return (retval);
860 }
861 
862 /*
863  * Get uncorrected current temperature
864  * Returns -1 on error, 0 if successful
865  */
866 static int
867 get_raw_temperature(env_sensor_t *sensorp, tempr_t *temp)
868 {
869 	int	fd = sensorp->fd;
870 	int	retval = 0;
871 
872 	if (fd == -1)
873 		retval = -1;
874 	else if (ioctl(fd, I2C_GET_TEMPERATURE, temp) == -1) {
875 		retval = -1;
876 	}
877 
878 	return (retval);
879 }
880 
881 /*
882  * Return Fan RPM given N & tach
883  * count and N are retrived from the
884  * ADM1031 chip.
885  */
886 static int
887 tach_to_rpm(int n, uint8_t tach)
888 {
889 	if (n * tach == 0)
890 		return (0);
891 	return ((ADCSAMPLE * 60) / (n * tach));
892 }
893 
894 static int
895 get_raw_fan_speed(env_fan_t *fanp, uint8_t *fanspeedp)
896 {
897 	int	fan_fd;
898 	int	retval = 0;
899 
900 	fan_fd = fanp->fd;
901 
902 	if (fan_fd == -1)
903 		retval = -1;
904 	else if (ioctl(fan_fd, I2C_GET_FAN_SPEED, fanspeedp) == -1) {
905 		retval = -1;
906 	}
907 
908 
909 	return (retval);
910 }
911 
912 /*
913  * Get current fan speed
914  * This function returns a RPM value for fanspeed
915  * in fanspeedp.
916  * Returns -1 on error, 0 if successful
917  */
918 int
919 get_fan_speed(env_fan_t *fanp, fanspeed_t *fanspeedp)
920 {
921 	int	fan_fd;
922 	uint8_t tach;
923 
924 	fan_fd = fanp->fd;
925 
926 	if (fan_fd == -1)
927 		return (-1);
928 	if (fanp->id == DIMM_FAN_ID) {
929 		return (get_dimm_fan_speed(fan_fd, fanspeedp));
930 	}
931 	if (ioctl(fan_fd, I2C_GET_FAN_SPEED, &tach) == -1) {
932 		return (-1);
933 	}
934 
935 	/*
936 	 * Fanspeeds are reported as 0
937 	 * if the tach is out of range or fan status is off
938 	 * and if monitoring fan status is enabled.
939 	 */
940 	if (mon_fanstat && (!fanp->fanstat || tach == FAN_OUT_OF_RANGE)) {
941 		*fanspeedp = 0;
942 	} else {
943 		*fanspeedp =
944 		    tach_to_rpm(fanp->speedrange, tach);
945 	}
946 
947 	return (0);
948 }
949 
950 /*
951  * Set fan speed
952  * This function accepts a percentage of fan speed
953  * from 0-100 and programs the HW monitor fans to the corresponding
954  * fanspeed value.
955  * Returns -1 on error, -2 on invalid args passed, 0 if successful
956  */
957 int
958 set_fan_speed(env_fan_t *fanp, fanspeed_t fanspeed)
959 {
960 	int	fan_fd;
961 	int	retval = 0;
962 	uint8_t	speed;
963 
964 	fan_fd = fanp->fd;
965 	if (fan_fd == -1)
966 		return (-1);
967 
968 	if (fanspeed < 0 || fanspeed > 100)
969 		return (-2);
970 
971 	speed = (uint8_t)ADM_SETFANSPEED_CONV(fanspeed);
972 
973 	if (ioctl(fan_fd, I2C_SET_FAN_SPEED, &speed) == -1) {
974 		retval = -1;
975 	}
976 	return (retval);
977 }
978 
979 /*
980  * close all fan devices
981  */
982 static void
983 envd_close_fans(void)
984 {
985 	int		i;
986 	env_fan_t	*fanp;
987 
988 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
989 		if (fanp->fd != -1) {
990 			(void) close(fanp->fd);
991 			fanp->fd = -1;
992 		}
993 	}
994 }
995 
996 /*
997  * Close sensor devices and freeup resources
998  */
999 static void
1000 envd_close_sensors(void)
1001 {
1002 	env_sensor_t	*sensorp;
1003 	int		i;
1004 
1005 	for (i = 0; i < N_ENVD_SENSORS; ++i) {
1006 		sensorp = &envd_sensors[i];
1007 		if (sensorp->fd != -1) {
1008 			(void) close(sensorp->fd);
1009 			sensorp->fd = -1;
1010 		}
1011 		if (sensorp->crtbl != NULL)
1012 			fini_table(sensorp->crtbl);
1013 	}
1014 }
1015 
1016 /*
1017  * Open fan devices and initialize per fan data structure.
1018  * Returns #fans found.
1019  */
1020 static int
1021 envd_setup_fans(void)
1022 {
1023 	int		i, fd;
1024 	env_fan_t	*fanp;
1025 	char		path[PATH_MAX];
1026 	int		fancnt = 0;
1027 	uint8_t		n = 0;
1028 	picl_nodehdl_t tnodeh;
1029 	i2c_reg_t	i2c_reg;
1030 
1031 	for (i = 0; (fanp = envd_fans[i]) != NULL; i++) {
1032 		/* make sure cpu0/1 present for validating cpu fans */
1033 		if (fanp->id == CPU0_FAN_ID) {
1034 			if (ptree_get_node_by_path(CPU0_PATH, &tnodeh) !=
1035 			    PICL_SUCCESS) {
1036 				fanp->present = B_FALSE;
1037 				continue;
1038 			}
1039 		}
1040 		if (fanp->id == CPU1_FAN_ID) {
1041 			if (ptree_get_node_by_path(CPU1_PATH, &tnodeh) !=
1042 			    PICL_SUCCESS) {
1043 				fanp->present = B_FALSE;
1044 				continue;
1045 			}
1046 		}
1047 		if (fanp->id == DIMM_FAN_ID) {
1048 			if (ptree_get_node_by_path(DIMM_FAN_CONTROLLER_PATH,
1049 			    &tnodeh) != PICL_SUCCESS) {
1050 				if (env_debug)
1051 					envd_log(LOG_ERR,
1052 					    "dimm Fan not found in the "
1053 					    "system.\n");
1054 				fanp->present = B_FALSE;
1055 				continue;
1056 			}
1057 		}
1058 		(void) strcpy(path, "/devices");
1059 		(void) strlcat(path, fanp->devfs_path, sizeof (path));
1060 		fd = open(path, O_RDWR);
1061 		if (fd == -1) {
1062 			envd_log(LOG_CRIT,
1063 			    ENV_FAN_OPEN_FAIL, fanp->name,
1064 			    fanp->devfs_path, errno, strerror(errno));
1065 			fanp->present = B_FALSE;
1066 			continue;
1067 		}
1068 		fanp->fd = fd;
1069 		if (fanp->id == DIMM_FAN_ID) {
1070 			/*
1071 			 * set the SW aware bit in command register.
1072 			 * Clear the Fan fault latch bit.
1073 			 */
1074 			i2c_reg.reg_num = PIC16F819_COMMAND_REGISTER;
1075 			i2c_reg.reg_value = (PIC16F819_SW_AWARE_MODE |
1076 			    PIC16F819_FAN_FAULT_CLEAR);
1077 			if (ioctl(fd, I2C_SET_REG, &i2c_reg) == -1) {
1078 				if (env_debug)
1079 					envd_log(LOG_ERR,
1080 					    "Error in writing to COMMAND reg. "
1081 					    "of DIMM FAN controller\n");
1082 			}
1083 		} else {
1084 			/* Get speed range value */
1085 			if (ioctl(fd, ADM1031_GET_FAN_FEATURE, &n) != -1) {
1086 				fanp->speedrange =
1087 				    adm_speedrange_map[(n >> 6) & 0x03];
1088 			} else {
1089 				fanp->speedrange = FAN_RANGE_DEFAULT;
1090 			}
1091 		}
1092 		fanp->present = B_TRUE;
1093 		fanp->fanstat = 0;
1094 		fanp->cspeed = TACH_UNKNOWN;
1095 		fanp->lspeed = TACH_UNKNOWN;
1096 		fanp->conccnt = 0;
1097 		fancnt++;
1098 	}
1099 	return (fancnt);
1100 }
1101 
1102 static int
1103 envd_setup_disks(void)
1104 {
1105 	int	ret, i, page_index, page_len;
1106 	picl_nodehdl_t tnodeh;
1107 	env_disk_t	*diskp;
1108 	uint_t	vendor_id;
1109 	uint_t	device_id;
1110 	uchar_t	log_page[256];
1111 
1112 	/*
1113 	 * Check if the SCSi controller on the system is 1010 or 1030
1114 	 */
1115 
1116 	if (ptree_get_node_by_path(SCSI_CONTROLLER_NODE_PATH,
1117 	    &tnodeh) != PICL_SUCCESS) {
1118 		if (env_debug)
1119 			envd_log(LOG_ERR,
1120 			    "On-Board SCSI controller not found "
1121 			    "in the system.\n");
1122 		monitor_disk_temp = 0;
1123 		return (-1);
1124 	}
1125 
1126 	if ((ret = ptree_get_propval_by_name(tnodeh, VENDOR_ID,
1127 	    &vendor_id, sizeof (vendor_id))) != 0) {
1128 		if (env_debug)
1129 			envd_log(LOG_ERR,
1130 			    "Error in getting vendor-id for SCSI controller. "
1131 			    "ret = %d errno = 0x%d\n",
1132 			    ret, errno);
1133 		monitor_disk_temp = 0;
1134 		return (-1);
1135 	}
1136 	if ((ret = ptree_get_propval_by_name(tnodeh, DEVICE_ID,
1137 	    &device_id, sizeof (device_id))) != 0) {
1138 		if (env_debug)
1139 			envd_log(LOG_ERR,
1140 			    "Error in getting device-id for SCSI controller. "
1141 			    "ret = %d errno = 0x%d\n", ret, errno);
1142 		monitor_disk_temp = 0;
1143 		return (-1);
1144 	}
1145 	if (env_debug)
1146 		envd_log(LOG_ERR, "vendor-id=0x%x device-id=0x%x\n",
1147 		    vendor_id, device_id);
1148 	if ((vendor_id != LSI1030_VENDOR_ID) ||
1149 	    (device_id != LSI1030_DEVICE_ID)) {
1150 		monitor_disk_temp = 0;
1151 		return (-1);
1152 	}
1153 	/*
1154 	 * We have found LSI1030 SCSi controller onboard.
1155 	 */
1156 
1157 	for (i = 0; (diskp = envd_disks[i]) != NULL; i++) {
1158 
1159 		if (ptree_get_node_by_path(diskp->nodepath,
1160 		    &tnodeh) != PICL_SUCCESS) {
1161 			diskp->present = B_FALSE;
1162 			if (env_debug)
1163 				envd_log(LOG_ERR,
1164 				    "DISK %d not found in the system.\n",
1165 				    diskp->id);
1166 			continue;
1167 		}
1168 		diskp->fd = open(diskp->devfs_path, O_RDONLY);
1169 		if (diskp->fd == -1) {
1170 			diskp->present = B_FALSE;
1171 			envd_log(LOG_ERR,
1172 			    "Error in opening %s errno = 0x%x\n",
1173 			    diskp->devfs_path, errno);
1174 			continue;
1175 		}
1176 		diskp->present = B_TRUE;
1177 		diskp->tpage_supported = B_FALSE;
1178 		/*
1179 		 * Find out if the Temperature page is supported by the disk.
1180 		 */
1181 		ret = scsi_log_sense(diskp->fd, SUPPORTED_LPAGES,
1182 		    log_page, sizeof (log_page));
1183 		if (ret != 0) {
1184 			continue;
1185 		}
1186 		page_len = ((log_page[2] << 8) & 0xFF00) | log_page[3];
1187 
1188 		for (page_index = LOGPAGEHDRSIZE;
1189 		    page_index < page_len + LOGPAGEHDRSIZE; page_index++) {
1190 			switch (log_page[page_index]) {
1191 			case TEMPERATURE_PAGE:
1192 				diskp->tpage_supported = B_TRUE;
1193 				if (env_debug)
1194 					envd_log(LOG_ERR,
1195 					    "tpage supported for %s\n",
1196 					    diskp->nodepath);
1197 			default:
1198 				break;
1199 			}
1200 		}
1201 		diskp->warning_tstamp = 0;
1202 		diskp->shutdown_tstamp = 0;
1203 		diskp->high_warning = disk_high_warn_temperature;
1204 		diskp->low_warning = disk_low_warn_temperature;
1205 		diskp->high_shutdown = disk_high_shutdown_temperature;
1206 		diskp->low_shutdown = disk_low_shutdown_temperature;
1207 		ret = get_disk_temp(diskp);
1208 	}
1209 	return (0);
1210 }
1211 
1212 /*
1213  * Open temperature sensor devices and initialize per sensor data structure.
1214  * Returns #sensors found.
1215  */
1216 static int
1217 envd_setup_sensors(void)
1218 {
1219 	env_sensor_t	*sensorp;
1220 	sensor_ctrl_blk_t *es_ptr;
1221 	table_t		*tblp;
1222 	char		path[PATH_MAX];
1223 	int		sensorcnt = 0;
1224 	int		i, j, nentries;
1225 	int16_t		tmin = 0;
1226 	picl_nodehdl_t tnodeh;
1227 
1228 	for (i = 0; i < N_ENVD_SENSORS; ++i) {
1229 		sensorp = &envd_sensors[i];
1230 		/* Initialize sensor's initial state */
1231 		sensorp->shutdown_initiated = B_FALSE;
1232 		sensorp->warning_tstamp = 0;
1233 		sensorp->shutdown_tstamp = 0;
1234 		sensorp->error = 0;
1235 		sensorp->crtbl = NULL;
1236 		/* make sure cpu0/1 sensors are present */
1237 		if (sensorp->id == CPU0_SENSOR_ID) {
1238 			if (ptree_get_node_by_path(CPU0_PATH, &tnodeh) !=
1239 			    PICL_SUCCESS) {
1240 				sensorp->present = B_FALSE;
1241 				continue;
1242 			}
1243 		}
1244 		if (sensorp->id == CPU1_SENSOR_ID) {
1245 			if (ptree_get_node_by_path(CPU1_PATH, &tnodeh) !=
1246 			    PICL_SUCCESS) {
1247 				sensorp->present = B_FALSE;
1248 				continue;
1249 			}
1250 		}
1251 		(void) strcpy(path, "/devices");
1252 		(void) strlcat(path, sensorp->devfs_path,
1253 		    sizeof (path));
1254 		sensorp->fd = open(path, O_RDWR);
1255 		if (sensorp->fd == -1) {
1256 			envd_log(LOG_ERR, ENV_SENSOR_OPEN_FAIL,
1257 			    sensorp->name, sensorp->devfs_path,
1258 			    errno, strerror(errno));
1259 			sensorp->present = B_FALSE;
1260 			continue;
1261 		}
1262 		sensorp->present = B_TRUE;
1263 		sensorcnt++;
1264 
1265 		/*
1266 		 * Get Tmin
1267 		 */
1268 
1269 		if (ioctl(sensorp->fd, ADM1031_GET_TEMP_MIN_RANGE,
1270 		    &tmin) != -1) {
1271 			sensorp->tmin = TMIN(tmin);
1272 		} else {
1273 			sensorp->tmin = -1;
1274 		}
1275 		if (env_debug)
1276 			envd_log(LOG_ERR, "Sensor %s tmin %d",
1277 			    sensorp->name, sensorp->tmin);
1278 
1279 		/*
1280 		 * Create a correction table
1281 		 * if correction pairs are present in es
1282 		 * segment.
1283 		 */
1284 		es_ptr = sensorp->es_ptr;
1285 
1286 		if (es_ptr == NULL) {
1287 			continue;
1288 		}
1289 		nentries = es_ptr->correctionEntries;
1290 
1291 		if (nentries <= 2) {
1292 			if (env_debug)
1293 				envd_log(LOG_CRIT, "sensor correction <2");
1294 			continue;
1295 		}
1296 
1297 		sensorp->crtbl = init_table(nentries);
1298 		if (sensorp->crtbl == NULL)
1299 			continue;
1300 		tblp = sensorp->crtbl;
1301 		tblp->xymap[0].x =
1302 		    (char)es_ptr->correctionPair[0].measured;
1303 		tblp->xymap[0].y =
1304 		    (char)es_ptr->correctionPair[0].corrected;
1305 
1306 		for (j = 1; j < nentries; ++j) {
1307 			tblp->xymap[j].x =
1308 			    (char)es_ptr->correctionPair[j].measured;
1309 			tblp->xymap[j].y =
1310 			    (char)es_ptr->correctionPair[j].corrected;
1311 
1312 			if (tblp->xymap[j].x <= tblp->xymap[j - 1].x) {
1313 				fini_table(tblp);
1314 				sensorp->crtbl = NULL;
1315 				envd_log(LOG_CRIT, ENV_FRU_BAD_ENVSEG,
1316 				    FRU_SEEPROM_NAME);
1317 				break;
1318 			}
1319 		}
1320 
1321 		if (env_debug) {
1322 			envd_log(LOG_CRIT, "Sensor correction  %s",
1323 			    sensorp->name);
1324 			for (j = 0; j < nentries; j++)
1325 				envd_log(LOG_CRIT, " %d	%d",
1326 				    tblp->xymap[j].x, tblp->xymap[j].y);
1327 		}
1328 	}
1329 	return (sensorcnt);
1330 }
1331 
1332 /*
1333  * Modify ADM Tmin/ranges depending what power level
1334  * we are from.
1335  */
1336 static void
1337 updateadm_ranges(char *name, uchar_t cur_lpstate)
1338 {
1339 	env_sensor_t *sensorp;
1340 	fan_ctrl_blk_t *fanctl;
1341 	uchar_t tmin;
1342 	uchar_t trange;
1343 	uint16_t tdata;
1344 	int sysfd;
1345 	uchar_t sys_id = SYS_HWM_ID;
1346 	uint8_t mode;
1347 	static uint16_t tsave[2] = {0, 0};
1348 	/* Index of saved Tmin/Trange for two sensors */
1349 	uint16_t tindex = 0;
1350 
1351 	sensorp = sensor_lookup(name);
1352 	if (sensorp == NULL)
1353 		return;
1354 
1355 	/*
1356 	 * If there is only one Control pairs then return
1357 	 */
1358 	fanctl = ((env_fan_t *)sensorp->fanp)->es_ptr;
1359 
1360 	if (fanctl != NULL && fanctl->no_ctl_pairs <= 1)
1361 		return;
1362 
1363 	/*
1364 	 * if fan control specifies that ranges are same then
1365 	 * we skip re-programming adm chip.
1366 	 */
1367 
1368 	tmin = fanctl->fan_ctl_pairs[0].tMin;
1369 	trange = fanctl->fan_ctl_pairs[0].tRange;
1370 	if ((tmin == fanctl->fan_ctl_pairs[1].tMin) &&
1371 	    (trange == fanctl->fan_ctl_pairs[1].tRange))
1372 			return;
1373 
1374 	sysfd = open(hwm_devs[sys_id], O_RDWR);
1375 	if (sysfd == -1) {
1376 		if (env_debug)
1377 			envd_log(LOG_ERR, ENV_ADM_OPEN_FAIL, hwm_devs[sys_id],
1378 			    errno, strerror(errno));
1379 		return;
1380 	}
1381 	tindex = ((strcmp(name, SENSOR_SYS_IN) == 0) ? 0 : 1);
1382 
1383 	/* Read ADM default value only for the first time */
1384 	if (tsave[tindex] == 0) {
1385 		if (ioctl(sensorp->fd, ADM1031_GET_TEMP_MIN_RANGE,
1386 		    &tsave[tindex]) == -1) {
1387 			if (env_debug)
1388 				envd_log(LOG_ERR,
1389 				    "read tminrange ioctl failed");
1390 			(void) close(sysfd);
1391 			return;
1392 		}
1393 	}
1394 	/*
1395 	 * Need to reinit ADM to manual mode for Tmin range to be
1396 	 * effective.
1397 	 */
1398 	mode = ADM1031_MANUAL_MODE;
1399 	if (ioctl(sysfd, ADM1031_SET_MONITOR_MODE, &mode) == -1) {
1400 		if (env_debug)
1401 			envd_log(LOG_ERR, ENV_ADM_MANUAL_MODE);
1402 		(void) close(sysfd);
1403 		return;
1404 	}
1405 
1406 	if (cur_lpstate == 1) {
1407 		/*
1408 		 * ADM 1031 Tmin/Trange register need to be reprogrammed.
1409 		 */
1410 		tdata = ((fanctl->fan_ctl_pairs[cur_lpstate].tMin / TMIN_UNITS)
1411 		    << TMIN_SHIFT);
1412 		/* Need to pack tRange in ADM bits 2:0 */
1413 		switch (fanctl->fan_ctl_pairs[cur_lpstate].tRange) {
1414 		case 5:
1415 			break;
1416 
1417 		case 10:
1418 			tdata |= 1;
1419 			break;
1420 
1421 		case 20:
1422 			tdata |= 2;
1423 			break;
1424 
1425 		case 40:
1426 			tdata |= 3;
1427 			break;
1428 
1429 		case 80:
1430 			tdata |= 4;
1431 			break;
1432 		}
1433 	} else
1434 		tdata = tsave[tindex];
1435 
1436 	if (ioctl(sensorp->fd, ADM1031_SET_TEMP_MIN_RANGE,
1437 	    &tdata) != -1)
1438 		sensorp->tmin = TMIN(tdata);
1439 
1440 	mode = ADM1031_AUTO_MODE;
1441 	if (ioctl(sysfd, ADM1031_SET_MONITOR_MODE, &mode) == -1) {
1442 		if (env_debug)
1443 			envd_log(LOG_ERR, ENV_ADM_AUTO_MODE);
1444 	}
1445 	(void) close(sysfd);
1446 }
1447 
1448 /* ARGSUSED */
1449 static void *
1450 pmthr(void *args)
1451 {
1452 	pm_state_change_t	pmstate;
1453 	char			physpath[PATH_MAX];
1454 	int				pre_lpstate;
1455 
1456 	pmstate.physpath = physpath;
1457 	pmstate.size = sizeof (physpath);
1458 	cur_lpstate = 0;
1459 	pre_lpstate = 1;
1460 
1461 	pm_fd = open(PM_DEVICE, O_RDWR);
1462 	if (pm_fd == -1) {
1463 		envd_log(LOG_ERR, PM_THREAD_EXITING, errno, strerror(errno));
1464 		return (NULL);
1465 	}
1466 	for (;;) {
1467 		/*
1468 		 * Get PM state change events to check if the system
1469 		 * is in lowest power state and adjust ADM hardware
1470 		 * monitor's fan speed settings.
1471 		 *
1472 		 * To minimize polling, we use the blocking interface
1473 		 * to get the power state change event here.
1474 		 */
1475 		if (ioctl(pm_fd, PM_GET_STATE_CHANGE_WAIT, &pmstate) != 0) {
1476 			if (errno != EINTR)
1477 				break;
1478 			continue;
1479 		}
1480 		do {
1481 			if (env_debug)  {
1482 				envd_log(LOG_INFO,
1483 				    "pmstate event:0x%x flags:%x"
1484 				    "comp:%d oldval:%d newval:%d path:%s\n",
1485 				    pmstate.event, pmstate.flags,
1486 				    pmstate.component,
1487 				    pmstate.old_level,
1488 				    pmstate.new_level,
1489 				    pmstate.physpath);
1490 			}
1491 			cur_lpstate =
1492 			    (pmstate.flags & PSC_ALL_LOWEST) ? 1 : 0;
1493 		} while (ioctl(pm_fd, PM_GET_STATE_CHANGE, &pmstate) == 0);
1494 		/*
1495 		 * Change ADM ranges as per E* Requirements. Update
1496 		 * happens only for valid state changes.
1497 		 */
1498 		if (pre_lpstate != cur_lpstate) {
1499 			pre_lpstate = cur_lpstate;
1500 			updateadm_ranges(SENSOR_SYS_OUT, cur_lpstate);
1501 			updateadm_ranges(SENSOR_SYS_IN, cur_lpstate);
1502 		}
1503 	}
1504 	/* Not reached */
1505 	return (NULL);
1506 }
1507 
1508 /*
1509  * This function is used to reasonably predict the
1510  * state of the fan (ON/OFF) using tmin and current temperature.
1511  *
1512  * We know the fan is on  if temp >= tmin and fan is off if
1513  * temp < (Tmin - Hysterisis).
1514  *
1515  * When the temperature is in between we don't know if the fan is on/off
1516  * because the temperature could be decreasing and not have crossed
1517  * Tmin - hysterisis and vice a versa.
1518  *
1519  *			FAN ON
1520  * Tmin
1521  *	-------------------------------------------
1522  *
1523  *			FAN ON/OFF
1524  *
1525  *	--------------------------------------------
1526  * Tmin - Hysterisis
1527  *			FAN OFF
1528  *
1529  * To solve the problem of finding out if the fan is on/off in our gray region
1530  * we keep track of the last read tach and the current read tach. From
1531  * experimentation and from discussions with analog devices it is unlikely that
1532  * if the fans are on we will get a constant tach reading  more than 5 times in
1533  * a row. This is not but the most fool proof approach but the  best we can do.
1534  *
1535  * This routine implements the above logic for a sensor with an
1536  * associated fan. The caller garauntees sensorp and fanp are not null.
1537  */
1538 
1539 static void
1540 check_fanstat(env_sensor_t *sensorp)
1541 {
1542 	env_fan_t *fanp = sensorp->fanp;
1543 	tempr_t	temp;
1544 	uint8_t fanspeed;
1545 
1546 	if (get_raw_temperature(sensorp, &temp) == -1)
1547 		return;
1548 
1549 	if (temp < (sensorp->tmin - ADM_HYSTERISIS)) {
1550 
1551 		fanp->fanstat = 0;		/* Fan off */
1552 		fanp->lspeed = TACH_UNKNOWN;	/* Reset Last read tach */
1553 		fanp->conccnt = 0;
1554 
1555 	} else if (temp >= sensorp->tmin) {
1556 
1557 		fanp->fanstat = 1;		/* Fan on */
1558 		fanp->lspeed = TACH_UNKNOWN;
1559 		fanp->conccnt = 0;
1560 
1561 	} else {
1562 		if (get_raw_fan_speed(fanp, &fanspeed) == -1)
1563 			return;
1564 
1565 		fanp->cspeed = fanspeed;
1566 		/*
1567 		 * First time in the gray area
1568 		 * set last read speed to current speed
1569 		 */
1570 		if (fanp->lspeed == TACH_UNKNOWN) {
1571 			fanp->lspeed = fanspeed;
1572 		} else {
1573 			if (fanp->lspeed != fanp->cspeed) {
1574 				fanp->conccnt = 0;
1575 				fanp->fanstat = 1;
1576 			} else {
1577 				fanp->conccnt++;
1578 
1579 				if (fanp->conccnt >= N_SEQ_TACH)
1580 					fanp->fanstat = 0;
1581 			}
1582 			fanp->lspeed = fanp->cspeed;
1583 		}
1584 	}
1585 }
1586 /*
1587  * There is an issue with the ADM1031 chip that causes the chip
1588  * to not update the tach register in case the fan stops. The
1589  * fans stop when the temperature measured (temp) drops below
1590  * Tmin - Hysterisis  and turn on when the temp >= Tmin.
1591  *
1592  * Since the tach registers don't update and remain stuck at the
1593  * last read tach value our get_fan_speed function always returns
1594  * a non-zero RPM reading.
1595  *
1596  * To fix this we need to figure out when the fans will be on/off
1597  * depending on the current temperature. Currently we poll for
1598  * interrupts, we can use that loop to determine what the current
1599  * temperature is and if the fans should be on/off.
1600  *
1601  * We get current temperature and check the fans.
1602  */
1603 static void
1604 monitor_fanstat(void)
1605 {
1606 	env_sensor_t *sensorp;
1607 	env_fan_t *fanp;
1608 	int i;
1609 
1610 	for (i = 0; i < N_ENVD_SENSORS; i++) {
1611 		sensorp = &envd_sensors[i];
1612 
1613 		if (!sensorp)
1614 			continue;
1615 
1616 		fanp = sensorp->fanp;
1617 
1618 		if (!(fanp && fanp->present))
1619 			continue;
1620 
1621 		if (sensorp->tmin != -1) {
1622 			check_fanstat(sensorp);
1623 		} else {
1624 			fanp->fanstat = 1;
1625 		}
1626 
1627 	}
1628 }
1629 
1630 static int
1631 handle_overtemp_interrupt(int hwm_id)
1632 {
1633 	env_sensor_t *sensorp;
1634 	tempr_t  temp;
1635 	uchar_t smap[MAX_SENSORS];
1636 	time_t  ct;
1637 	uchar_t i;
1638 	char msgbuf[BUFSIZ];
1639 	char syscmd[BUFSIZ];
1640 	boolean_t return_flag;
1641 	int	ret;
1642 	timespec_t	to;
1643 	pthread_mutex_t	env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER;
1644 	pthread_cond_t	env_monitor_cv = PTHREAD_COND_INITIALIZER;
1645 
1646 	/* Clear Map of Sensor Entries */
1647 	(void) memset(smap, SENSOR_OK, sizeof (smap));
1648 
1649 	for (;;) {
1650 		for (i = 0; i < N_ENVD_SENSORS; i++) {
1651 			sensorp = &envd_sensors[i];
1652 
1653 			/*
1654 			 * Check whether the sensor belongs to the
1655 			 * interrupting ADM hardware monitor
1656 			 */
1657 			if (sensorp->hwm_id != hwm_id)
1658 				continue;
1659 
1660 			if (sensorp->present == B_FALSE)
1661 				continue;
1662 			/*
1663 			 * if shutdown is initiated then we simply loop
1664 			 * through the sensors until shutdown
1665 			 */
1666 			if (sensorp->shutdown_initiated == B_TRUE)
1667 				continue;
1668 
1669 			/* get current temp for this sensor */
1670 			if (get_temperature(sensorp, &temp) == -1)
1671 				continue;
1672 
1673 			sensorp->cur_temp = temp;
1674 
1675 			if (env_debug)
1676 				envd_log(LOG_ERR,
1677 				    "sensor name %s, cur temp %d, "
1678 				    "HW %d LW %d SD %d LS %d\n",
1679 				    sensorp->name, temp,
1680 				    sensorp->es_ptr->high_warning,
1681 				    (int)sensorp->es_ptr->low_warning,
1682 				    sensorp->es_ptr->high_shutdown,
1683 				    (int)sensorp->es_ptr->low_shutdown);
1684 
1685 			if (TEMP_IN_WARNING_RANGE(sensorp->cur_temp, sensorp)) {
1686 				/*
1687 				 * Log on warning atmost one second
1688 				 */
1689 				ct = (time_t)(gethrtime() / NANOSEC);
1690 				if ((ct - sensorp->warning_tstamp) >=
1691 				    warning_interval) {
1692 					envd_log(LOG_CRIT,
1693 					    ENV_WARNING_MSG, sensorp->name,
1694 					    temp,
1695 					    sensorp->es_ptr->low_warning,
1696 					    sensorp->es_ptr->high_warning);
1697 					sensorp->warning_tstamp = ct;
1698 				}
1699 				smap[i] = SENSOR_WARN;
1700 			} else {
1701 				/*
1702 				 * We will fall in this caterory only if
1703 				 * Temperature drops/increases from warning
1704 				 * threshold. If so we set sensor map to
1705 				 * OK so that we can exit the loop if
1706 				 * shutdown not initiated.
1707 				 */
1708 				smap[i] = SENSOR_OK;
1709 			}
1710 
1711 			if (TEMP_IN_SHUTDOWN_RANGE(temp, sensorp) &&
1712 			    !shutdown_override) {
1713 				ct = (time_t)(gethrtime() / NANOSEC);
1714 				if (sensorp->shutdown_tstamp == 0)
1715 					sensorp->shutdown_tstamp = ct;
1716 				if ((ct - sensorp->shutdown_tstamp) >=
1717 				    shutdown_interval) {
1718 					sensorp->shutdown_initiated = B_TRUE;
1719 					(void) snprintf(msgbuf, sizeof (msgbuf),
1720 					    ENV_SHUTDOWN_MSG, sensorp->name,
1721 					    temp,
1722 					    sensorp->es_ptr->low_shutdown,
1723 					    sensorp->es_ptr->high_shutdown);
1724 					envd_log(LOG_ALERT, msgbuf);
1725 				}
1726 				if (system_shutdown_started == B_FALSE) {
1727 					(void) snprintf(syscmd, sizeof (syscmd),
1728 					    "%s \"%s\"", SHUTDOWN_CMD, msgbuf);
1729 					envd_log(LOG_ALERT, syscmd);
1730 					system_shutdown_started = B_TRUE;
1731 					(void) system(syscmd);
1732 				}
1733 			} else if (sensorp->shutdown_tstamp != 0)
1734 				sensorp->shutdown_tstamp = 0;
1735 		}
1736 
1737 		/*
1738 		 * Sweep thorugh Sensor Map and if warnings OR shutdown
1739 		 * are not logged then return to caller.
1740 		 */
1741 		return_flag = B_TRUE;
1742 		for (i = 0; i < N_ENVD_SENSORS; i++)
1743 			if (smap[i] == SENSOR_WARN)
1744 				return_flag = B_FALSE;
1745 
1746 		if ((return_flag == B_TRUE) &&
1747 		    (system_shutdown_started == B_FALSE)) {
1748 			return (1);
1749 		}
1750 
1751 wait_till_timeout:
1752 		/*
1753 		 * We use pthread_cond_reltimedwait_np to sleep for
1754 		 * fixed interval of time.
1755 		 * earlier implementation used alarm() call which
1756 		 * fails in Multi threaded environment. If multiple
1757 		 * threads call alarm() only one of the threads is
1758 		 * sent the SIGALRM signal.
1759 		 */
1760 		(void) pthread_mutex_lock(&env_monitor_mutex);
1761 		ret = pthread_cond_reltimedwait_np(&env_monitor_cv,
1762 		    &env_monitor_mutex, &to);
1763 		to.tv_sec = SENSORPOLL_INTERVAL;
1764 		to.tv_nsec = 0;
1765 		if (ret != ETIMEDOUT) {
1766 			(void) pthread_mutex_unlock(&env_monitor_mutex);
1767 			goto wait_till_timeout;
1768 		}
1769 		(void) pthread_mutex_unlock(&env_monitor_mutex);
1770 	}
1771 }
1772 
1773 /*
1774  * This is env thread which monitors the current temperature when
1775  * warning threshold is exceeded. The job is to make sure it does
1776  * not execced/decrease shutdown threshold. If it does it will start
1777  * forced shutdown to avoid reaching hardware poweroff via THERM interrupt.
1778  * For Enchilada there will be two threads, one for each ADM chip.
1779  */
1780 static void *
1781 ovtemp_thr(void *args)
1782 {
1783 	int	fd;
1784 	uint8_t stat[2];
1785 	int	hwm_id = (int)args;
1786 	int    err;
1787 	env_fan_t *fanp;
1788 	timespec_t	to;
1789 	int	ret;
1790 	pthread_mutex_t	env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER;
1791 	pthread_cond_t	env_monitor_cv = PTHREAD_COND_INITIALIZER;
1792 
1793 	fd = open(hwm_devs[hwm_id], O_RDWR);
1794 	if (fd == -1) {
1795 		envd_log(LOG_ERR, ENV_ADM_OPEN_FAIL, hwm_devs[hwm_id],
1796 		    errno, strerror(errno));
1797 		return (NULL);
1798 	}
1799 	if (env_debug)
1800 		envd_log(LOG_ERR, "ovtemp thread for %s running...\n",
1801 		    hwm_devs[hwm_id]);
1802 
1803 	for (;;) {
1804 		/*
1805 		 * Sleep for specified seconds before issuing IOCTL
1806 		 * again.
1807 		 */
1808 
1809 		/*
1810 		 * We use pthread_cond_reltimedwait_np to sleep for
1811 		 * fixed interval of time.
1812 		 * earlier implementation used alarm() call which
1813 		 * fails in Multi threaded environment. If multiple
1814 		 * threads call alarm() only one of the threads is
1815 		 * sent the SIGALRM signal.
1816 		 */
1817 		(void) pthread_mutex_lock(&env_monitor_mutex);
1818 		ret = pthread_cond_reltimedwait_np(&env_monitor_cv,
1819 		    &env_monitor_mutex, &to);
1820 		to.tv_sec = INTERRUPTPOLL_INTERVAL;
1821 		to.tv_nsec = 0;
1822 		if (ret != ETIMEDOUT) {
1823 			(void) pthread_mutex_unlock(&env_monitor_mutex);
1824 			continue;
1825 		}
1826 		(void) pthread_mutex_unlock(&env_monitor_mutex);
1827 		/*
1828 		 * Monitor the sensors to update fan status
1829 		 */
1830 		if (mon_fanstat)
1831 			monitor_fanstat();
1832 
1833 		/*
1834 		 * Read ADM1031 two Status Registers to determine source
1835 		 * of Interrupts.
1836 		 */
1837 
1838 		if ((err = ioctl(fd, ADM1031_GET_STATUS_1, &stat[0])) != -1)
1839 			err = ioctl(fd, ADM1031_GET_STATUS_2, &stat[1]);
1840 
1841 		if (err == -1) {
1842 			if (env_debug)
1843 				envd_log(LOG_ERR,
1844 				    "OverTemp: Status Error");
1845 			continue;
1846 		}
1847 
1848 		if (env_debug)
1849 			envd_log(LOG_ERR, "INTR %s, Stat1 %x, Stat2 %x",
1850 			    hwm_devs[hwm_id], stat[0], stat[1]);
1851 
1852 		if (stat[0] & FANFAULT) {
1853 			fanp = fan_lookup(hwm_fans[hwm_id][HWM_FAN1]);
1854 			if (fanp && fanp->present)
1855 				envd_log(LOG_ERR, ENV_FAN_FAULT,
1856 				    hwm_devs[hwm_id],
1857 				    hwm_fans[hwm_id][HWM_FAN1]);
1858 		}
1859 		if (stat[1] & FANFAULT) {
1860 			fanp = fan_lookup(hwm_fans[hwm_id][HWM_FAN2]);
1861 			if (fanp && fanp->present)
1862 				envd_log(LOG_ERR, ENV_FAN_FAULT,
1863 				    hwm_devs[hwm_id],
1864 				    hwm_fans[hwm_id][HWM_FAN2]);
1865 		}
1866 		/*
1867 		 * Check respective Remote/Local High, Low before start
1868 		 * manual monitoring
1869 		 */
1870 		if ((stat[0] & STAT1MASK) || (stat[1] & STAT2MASK))
1871 			(void) handle_overtemp_interrupt(hwm_id);
1872 
1873 	}	/* end of for ever loop */
1874 	/*NOTREACHED*/
1875 	return (NULL);
1876 }
1877 
1878 static void *
1879 dimm_fan_thr(void *args)
1880 {
1881 	char syscmd[BUFSIZ];
1882 	char msgbuf[BUFSIZ];
1883 	i2c_reg_t	i2c_reg;
1884 	timespec_t	to;
1885 	int	ret;
1886 	pthread_mutex_t	env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER;
1887 	pthread_cond_t	env_monitor_cv = PTHREAD_COND_INITIALIZER;
1888 
1889 	for (;;) {
1890 		/*
1891 		 * Sleep for specified seconds before issuing IOCTL
1892 		 * again.
1893 		 */
1894 		(void) pthread_mutex_lock(&env_monitor_mutex);
1895 		ret = pthread_cond_reltimedwait_np(&env_monitor_cv,
1896 		    &env_monitor_mutex, &to);
1897 		to.tv_sec = INTERRUPTPOLL_INTERVAL;
1898 		to.tv_nsec = 0;
1899 		if (ret != ETIMEDOUT) {
1900 			(void) pthread_mutex_unlock(&env_monitor_mutex);
1901 			continue;
1902 		}
1903 		(void) pthread_mutex_unlock(&env_monitor_mutex);
1904 		/*
1905 		 * We write to the comand register periodically
1906 		 * to inform the PIC firmware that Solaris is
1907 		 * Monitoring the dimm fan periodically.
1908 		 */
1909 		i2c_reg.reg_num = PIC16F819_COMMAND_REGISTER;
1910 		i2c_reg.reg_value = PIC16F819_SW_AWARE_MODE;
1911 		if (ioctl(envd_dimm_fan.fd,
1912 		    I2C_SET_REG, &i2c_reg) == -1) {
1913 			if (env_debug)
1914 				envd_log(LOG_ERR,
1915 				    "Error in writing to COMMAND reg. "
1916 				    "of DIMM FAN controller\n");
1917 		}
1918 		/*
1919 		 * We initiate shutdown if fan status indicates
1920 		 * failure.
1921 		 */
1922 		if (is_dimm_fan_failed() != 0) {
1923 			/*
1924 			 * Mark Dimm fan present as False so that we
1925 			 * do not WARN the user of the Fan failure
1926 			 * repeatedly.
1927 			 */
1928 			envd_dimm_fan.present = B_FALSE;
1929 			(void) snprintf(msgbuf, sizeof (msgbuf),
1930 			    ENV_DIMM_FAN_FAILURE_SHUTDOWN_MSG,
1931 			    ENV_DIMM_FAN,
1932 			    dimm_fan_rpm_string, dimm_fan_status_string,
1933 			    dimm_fan_command_string,
1934 			    dimm_fan_debug_string);
1935 			envd_log(LOG_ALERT, msgbuf);
1936 
1937 			if (system_shutdown_started == B_FALSE) {
1938 				system_shutdown_started = B_TRUE;
1939 				(void) snprintf(syscmd, sizeof (syscmd),
1940 				    "%s \"%s\"",
1941 				    SHUTDOWN_CMD,
1942 				    msgbuf);
1943 				envd_log(LOG_ALERT, syscmd);
1944 				(void) system(syscmd);
1945 			}
1946 		}
1947 	}
1948 	/*NOTREACHED*/
1949 	return (NULL);
1950 }
1951 static int
1952 scsi_log_sense(int fd, uchar_t page_code, uchar_t *pagebuf, uint16_t pagelen)
1953 {
1954 	struct uscsi_cmd	ucmd_buf;
1955 	uchar_t		cdb_buf[CDB_GROUP1];
1956 	struct	scsi_extended_sense	sense_buf;
1957 	int	ret_val;
1958 
1959 	bzero((void *)&cdb_buf, sizeof (cdb_buf));
1960 	bzero((void *)&ucmd_buf, sizeof (ucmd_buf));
1961 	bzero((void *)&sense_buf, sizeof (sense_buf));
1962 
1963 	cdb_buf[0] = SCMD_LOG_SENSE_G1;
1964 	cdb_buf[2] = (0x01 << 6) | page_code;
1965 	cdb_buf[7] = (uchar_t)((pagelen & 0xFF00) >> 8);
1966 	cdb_buf[8] = (uchar_t)(pagelen  & 0x00FF);
1967 
1968 	ucmd_buf.uscsi_cdb = (char *)cdb_buf;
1969 	ucmd_buf.uscsi_cdblen = sizeof (cdb_buf);
1970 	ucmd_buf.uscsi_bufaddr = (caddr_t)pagebuf;
1971 	ucmd_buf.uscsi_buflen = pagelen;
1972 	ucmd_buf.uscsi_rqbuf = (caddr_t)&sense_buf;
1973 	ucmd_buf.uscsi_rqlen = sizeof (struct scsi_extended_sense);
1974 	ucmd_buf.uscsi_flags = USCSI_RQENABLE | USCSI_READ | USCSI_SILENT;
1975 	ucmd_buf.uscsi_timeout = 60;
1976 
1977 	ret_val = ioctl(fd, USCSICMD, ucmd_buf);
1978 	if (ret_val == 0 && ucmd_buf.uscsi_status == 0) {
1979 		if (env_debug)
1980 			envd_log(LOG_ERR,
1981 			    "log sense command for page_code 0x%x succeeded\n",
1982 			    page_code);
1983 		return (ret_val);
1984 	}
1985 	if (env_debug)
1986 		envd_log(LOG_ERR,
1987 		    "log sense command failed.ret_val = 0x%x status = 0x%x "
1988 		    "errno = 0x%x\n",
1989 		    ret_val, ucmd_buf.uscsi_status, errno);
1990 	return (1);
1991 }
1992 
1993 static int
1994 get_disk_temp(env_disk_t *diskp)
1995 {
1996 	int	ret;
1997 	uchar_t	tpage[256];
1998 
1999 	ret = scsi_log_sense(diskp->fd,
2000 	    TEMPERATURE_PAGE,
2001 	    tpage, sizeof (tpage));
2002 	if (ret != 0) {
2003 		diskp->current_temp = DISK_INVALID_TEMP;
2004 		diskp->ref_temp = DISK_INVALID_TEMP;
2005 		return (-1);
2006 	}
2007 	/*
2008 	 * For the current temperature verify that the parameter
2009 	 * length is 0x02 and the parameter code is 0x00
2010 	 * Temperature value of 255(0xFF) is considered INVALID.
2011 	 */
2012 	if ((tpage[7] == 0x02) && (tpage[4] == 0x00) &&
2013 	    (tpage[5] == 0x00)) {
2014 		if (tpage[9] == 0xFF) {
2015 			diskp->current_temp = DISK_INVALID_TEMP;
2016 			return (-1);
2017 		} else {
2018 			diskp->current_temp = tpage[9];
2019 		}
2020 	}
2021 
2022 	/*
2023 	 * For the reference temperature verify that the parameter
2024 	 * length is 0x02 and the parameter code is 0x01
2025 	 * Temperature value of 255(0xFF) is considered INVALID.
2026 	 */
2027 	if ((tpage[13] == 0x02) && (tpage[10] == 0x00) &&
2028 	    (tpage[11] == 0x01)) {
2029 		if (tpage[15] == 0xFF) {
2030 			diskp->ref_temp = DISK_INVALID_TEMP;
2031 		} else {
2032 			diskp->ref_temp = tpage[15];
2033 		}
2034 	}
2035 	return (0);
2036 }
2037 
2038 /* ARGSUSED */
2039 static void *
2040 disk_temp_thr(void *args)
2041 {
2042 	char syscmd[BUFSIZ];
2043 	char msgbuf[BUFSIZ];
2044 	timespec_t	to;
2045 	int	ret, i;
2046 	env_disk_t	*diskp;
2047 	pthread_mutex_t	env_monitor_mutex = PTHREAD_MUTEX_INITIALIZER;
2048 	pthread_cond_t	env_monitor_cv = PTHREAD_COND_INITIALIZER;
2049 	pm_state_change_t	pmstate;
2050 	int	idle_time;
2051 	int	disk_pm_fd;
2052 	time_t	ct;
2053 
2054 	disk_pm_fd = open(PM_DEVICE, O_RDWR);
2055 	if (disk_pm_fd == -1) {
2056 		envd_log(LOG_ERR,
2057 		    DISK_TEMP_THREAD_EXITING,
2058 		    errno, strerror(errno));
2059 		return (NULL);
2060 	}
2061 	for (;;) {
2062 		/*
2063 		 * Sleep for specified seconds before issuing IOCTL
2064 		 * again.
2065 		 */
2066 		(void) pthread_mutex_lock(&env_monitor_mutex);
2067 		ret = pthread_cond_reltimedwait_np(&env_monitor_cv,
2068 		    &env_monitor_mutex, &to);
2069 		to.tv_sec = disk_scan_interval;
2070 		to.tv_nsec = 0;
2071 		if (ret != ETIMEDOUT) {
2072 			(void) pthread_mutex_unlock(&env_monitor_mutex);
2073 			continue;
2074 		}
2075 		(void) pthread_mutex_unlock(&env_monitor_mutex);
2076 		for (i = 0; (diskp = envd_disks[i]) != NULL; i++) {
2077 			if (diskp->present == B_FALSE)
2078 				continue;
2079 			if (diskp->tpage_supported == B_FALSE)
2080 				continue;
2081 			/*
2082 			 * If the disk temperature is above the warning
2083 			 * threshold continue monitoring until the temperature
2084 			 * drops below warning threshold.
2085 			 * If the temperature is in the NORMAL range monitor
2086 			 * only when the disk is BUSY.
2087 			 * We do not want to read the disk temperature if the
2088 			 * disk is is idling. The reason for this is disk will
2089 			 * never get into lowest power mode if we scan the disk
2090 			 * temperature peridoically.
2091 			 * To avoid this situation we first determine
2092 			 * the idle_time of the disk. If the disk has been
2093 			 * IDLE since we scanned the temperature last time
2094 			 * we will not read the temperature.
2095 			 */
2096 			if (!DISK_TEMP_IN_WARNING_RANGE(diskp->current_temp,
2097 			    diskp)) {
2098 				pmstate.physpath = diskp->physpath;
2099 				pmstate.size = strlen(diskp->physpath);
2100 				pmstate.component = 0;
2101 				if ((idle_time =
2102 				    ioctl(disk_pm_fd,
2103 				    PM_GET_TIME_IDLE, &pmstate)) == -1) {
2104 					if (errno != EINTR) {
2105 						if (env_debug) {
2106 							envd_log(LOG_ERR,
2107 							    "ioctl "
2108 							    "PM_GET_TIME_IDLE "
2109 							    "failed for DISK0."
2110 							    " errno=0x%x\n",
2111 							    errno);
2112 						}
2113 						continue;
2114 					}
2115 					continue;
2116 				}
2117 				if (idle_time >= (disk_scan_interval/2)) {
2118 					if (env_debug) {
2119 						envd_log(LOG_ERR,
2120 						    "%s idle time = %d\n",
2121 						    diskp->name, idle_time);
2122 					}
2123 					continue;
2124 				}
2125 			}
2126 			ret = get_disk_temp(diskp);
2127 			if (ret != 0)
2128 				continue;
2129 			if (env_debug) {
2130 				envd_log(LOG_ERR,
2131 				    "%s temp = %d ref. temp = %d\n",
2132 				    diskp->name, diskp->current_temp,
2133 				    diskp->ref_temp);
2134 			}
2135 			/*
2136 			 * If this disk already triggered system shutdown, don't
2137 			 * log any more shutdown/warning messages for it.
2138 			 */
2139 			if (diskp->shutdown_initiated)
2140 				continue;
2141 
2142 			/*
2143 			 * Check for the temperature in warning and shutdown
2144 			 * range and take appropriate action.
2145 			 */
2146 			if (DISK_TEMP_IN_WARNING_RANGE(diskp->current_temp,
2147 			    diskp)) {
2148 				/*
2149 				 * Check if the temperature has been in warning
2150 				 * range during last disk_warning_duration
2151 				 * interval.
2152 				 * If so, the temperature is truly in warning
2153 				 * range and we need to log a warning message,
2154 				 * but no more than once every
2155 				 * disk_warning_interval seconds.
2156 				 */
2157 				time_t	wtstamp = diskp->warning_tstamp;
2158 
2159 				ct = (time_t)(gethrtime() / NANOSEC);
2160 				if (diskp->warning_start == 0)
2161 					diskp->warning_start = ct;
2162 				if (((ct - diskp->warning_start) >=
2163 				    disk_warning_duration) && (wtstamp == 0 ||
2164 				    (ct - wtstamp) >= disk_warning_interval)) {
2165 					envd_log(LOG_CRIT, ENV_WARNING_MSG,
2166 					    diskp->name, diskp->current_temp,
2167 					    diskp->low_warning,
2168 					    diskp->high_warning);
2169 					diskp->warning_tstamp = ct;
2170 				}
2171 			} else if (diskp->warning_start != 0)
2172 				diskp->warning_start = 0;
2173 
2174 			if (!shutdown_override &&
2175 			    DISK_TEMP_IN_SHUTDOWN_RANGE(diskp->current_temp,
2176 			    diskp)) {
2177 				ct = (time_t)(gethrtime() / NANOSEC);
2178 				if (diskp->shutdown_tstamp == 0)
2179 					diskp->shutdown_tstamp = ct;
2180 
2181 				/*
2182 				 * Shutdown the system if the temperature
2183 				 * remains in the shutdown range for over
2184 				 * disk_shutdown_interval seconds.
2185 				 */
2186 				if ((ct - diskp->shutdown_tstamp) >=
2187 				    disk_shutdown_interval) {
2188 					/* log error */
2189 					diskp->shutdown_initiated = B_TRUE;
2190 					(void) snprintf(msgbuf, sizeof (msgbuf),
2191 					    ENV_SHUTDOWN_MSG, diskp->name,
2192 					    diskp->current_temp,
2193 					    diskp->low_shutdown,
2194 					    diskp->high_shutdown);
2195 					envd_log(LOG_ALERT, msgbuf);
2196 
2197 					/* shutdown the system (only once) */
2198 					if (system_shutdown_started ==
2199 					    B_FALSE) {
2200 						(void) snprintf(syscmd,
2201 						    sizeof (syscmd),
2202 						    "%s \"%s\"", shutdown_cmd,
2203 						    msgbuf);
2204 						envd_log(LOG_ALERT, syscmd);
2205 						system_shutdown_started =
2206 						    B_TRUE;
2207 						(void) system(syscmd);
2208 					}
2209 				}
2210 			} else if (diskp->shutdown_tstamp != 0)
2211 				diskp->shutdown_tstamp = 0;
2212 
2213 		}
2214 	}	/* end of forever loop */
2215 }
2216 
2217 /*
2218  * Setup envrionmental monitor state and start threads to monitor
2219  * temperature and power management state.
2220  * Returns -1 on error, 0 if successful.
2221  */
2222 static int
2223 envd_setup(void)
2224 {
2225 	int	ret;
2226 
2227 	if (getenv("SUNW_piclenvd_debug") != NULL)
2228 			env_debug = 1;
2229 
2230 	if (pthread_attr_init(&thr_attr) != 0 ||
2231 	    pthread_attr_setscope(&thr_attr, PTHREAD_SCOPE_SYSTEM) != 0) {
2232 		return (-1);
2233 	}
2234 
2235 	ret = envd_es_setup();
2236 	if (ret < 0) {
2237 		ovtemp_monitor = 0;
2238 		pm_monitor = 0;
2239 	}
2240 
2241 	/*
2242 	 * Setup temperature sensors and fail if we can't open
2243 	 * at least one sensor.
2244 	 */
2245 	if (envd_setup_sensors() <= 0) {
2246 		return (0);
2247 	}
2248 
2249 	/*
2250 	 * Setup fan device (don't fail even if we can't access
2251 	 * the fan as we can still monitor temeperature.
2252 	 */
2253 	(void) envd_setup_fans();
2254 
2255 	(void) envd_setup_disks();
2256 
2257 	/* If ES Segment setup failed,don't create  thread */
2258 
2259 	if (ovtemp_monitor && ovtemp_thr1_created == B_FALSE) {
2260 		if (pthread_create(&ovtemp_thr1_id, &thr_attr, ovtemp_thr,
2261 		    (void *)CPU_HWM_ID) != 0)
2262 			envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED);
2263 		else
2264 			ovtemp_thr1_created = B_TRUE;
2265 	}
2266 
2267 	if (ovtemp_monitor && ovtemp_thr2_created == B_FALSE) {
2268 		if (pthread_create(&ovtemp_thr2_id, &thr_attr, ovtemp_thr,
2269 		    (void *)SYS_HWM_ID) != 0)
2270 			envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED);
2271 		else
2272 			ovtemp_thr2_created = B_TRUE;
2273 	}
2274 
2275 	if (envd_dimm_fan.present) {
2276 		if (dimm_fan_thr_created == B_FALSE) {
2277 			if (pthread_create(&dimm_fan_thr_id, &thr_attr,
2278 			    dimm_fan_thr, NULL) != 0)
2279 				envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED);
2280 			else
2281 				dimm_fan_thr_created = B_TRUE;
2282 		}
2283 	}
2284 
2285 	/*
2286 	 * Create a thread to monitor PM state
2287 	 */
2288 	if (pm_monitor && pmthr_created == B_FALSE) {
2289 		if (pthread_create(&pmthr_tid, &thr_attr, pmthr,
2290 		    NULL) != 0)
2291 			envd_log(LOG_CRIT, PM_THREAD_CREATE_FAILED);
2292 		else
2293 			pmthr_created = B_TRUE;
2294 	}
2295 	if (monitor_disk_temp) {
2296 		if (disk_temp_thr_created == B_FALSE) {
2297 			if (pthread_create(&disk_temp_thr_id, &thr_attr,
2298 			    disk_temp_thr, NULL) != 0)
2299 				envd_log(LOG_ERR, ENVTHR_THREAD_CREATE_FAILED);
2300 			else
2301 				disk_temp_thr_created = B_TRUE;
2302 		}
2303 	}
2304 	return (0);
2305 }
2306 
2307 static void
2308 piclenvd_register(void)
2309 {
2310 	picld_plugin_register(&my_reg_info);
2311 }
2312 
2313 static void
2314 piclenvd_init(void)
2315 {
2316 
2317 	(void) env_picl_setup_tuneables();
2318 
2319 	/*
2320 	 * Setup the environmental data structures
2321 	 */
2322 	if (envd_setup() != 0) {
2323 		envd_log(LOG_CRIT, ENVD_PLUGIN_INIT_FAILED);
2324 		return;
2325 	}
2326 
2327 	/*
2328 	 * Now setup/populate PICL tree
2329 	 */
2330 	env_picl_setup();
2331 }
2332 
2333 static void
2334 piclenvd_fini(void)
2335 {
2336 
2337 	/*
2338 	 * Invoke env_picl_destroy() to remove any PICL nodes/properties
2339 	 * (including volatile properties) we created. Once this call
2340 	 * returns, there can't be any more calls from the PICL framework
2341 	 * to get current temperature or fan speed.
2342 	 */
2343 	env_picl_destroy();
2344 	envd_close_sensors();
2345 	envd_close_fans();
2346 	envd_es_destroy();
2347 }
2348 
2349 /*VARARGS2*/
2350 void
2351 envd_log(int pri, const char *fmt, ...)
2352 {
2353 	va_list	ap;
2354 
2355 	va_start(ap, fmt);
2356 	vsyslog(pri, fmt, ap);
2357 	va_end(ap);
2358 }
2359 
2360 /*
2361  * Tunables support functions
2362  */
2363 static env_tuneable_t *
2364 tuneable_lookup(picl_prophdl_t proph)
2365 {
2366 	int i;
2367 	env_tuneable_t	*tuneablep = NULL;
2368 
2369 	for (i = 0; i < ntuneables; i++) {
2370 		tuneablep = &tuneables[i];
2371 		if (tuneablep->proph == proph)
2372 			return (tuneablep);
2373 	}
2374 
2375 	return (NULL);
2376 }
2377 
2378 static int
2379 get_cpu_tach(ptree_rarg_t *parg, void *buf)
2380 {
2381 	picl_prophdl_t	proph;
2382 	env_tuneable_t	*tuneablep;
2383 	int		fd;
2384 	int8_t		cfg;
2385 
2386 	proph = parg->proph;
2387 
2388 	tuneablep = tuneable_lookup(proph);
2389 
2390 	if (tuneablep == NULL)
2391 		return (PICL_FAILURE);
2392 
2393 	fd = open(CPU_HWM_DEVFS, O_RDWR);
2394 
2395 	if (fd == -1) {
2396 		return (PICL_FAILURE);
2397 	}
2398 
2399 	if (ioctl(fd, ADM1031_GET_CONFIG_2, &cfg) == -1) {
2400 		return (PICL_FAILURE);
2401 	}
2402 
2403 	if ((cfg & TACH_ENABLE_MASK) == TACH_ENABLE_MASK) {
2404 		*((int *)tuneablep->value) = ENABLE;
2405 	} else {
2406 		*((int *)tuneablep->value) = DISABLE;
2407 	}
2408 
2409 	(void) memcpy(buf, tuneablep->value,
2410 	    tuneablep->nbytes);
2411 
2412 	(void) close(fd);
2413 	return (PICL_SUCCESS);
2414 }
2415 
2416 static int
2417 set_cpu_tach(ptree_warg_t *parg, const void *buf)
2418 {
2419 	picl_prophdl_t	proph;
2420 	env_tuneable_t	*tuneablep;
2421 	int		 fd, val;
2422 	int8_t		 cfg;
2423 
2424 	if (parg->cred.dc_euid != 0)
2425 		return (PICL_PERMDENIED);
2426 
2427 	proph = parg->proph;
2428 
2429 	tuneablep = tuneable_lookup(proph);
2430 
2431 	if (tuneablep == NULL)
2432 		return (PICL_FAILURE);
2433 
2434 
2435 	fd = open(CPU_HWM_DEVFS, O_RDWR);
2436 
2437 	if (fd == -1) {
2438 		return (PICL_FAILURE);
2439 	}
2440 
2441 	if (ioctl(fd, ADM1031_GET_CONFIG_2, &cfg) == -1) {
2442 		return (PICL_FAILURE);
2443 	}
2444 
2445 	(void) memcpy(&val, (caddr_t)buf, sizeof (val));
2446 
2447 	if (val == ENABLE) {
2448 		cfg |= TACH_ENABLE_MASK;
2449 	} else if (val == DISABLE) {
2450 		cfg &= ~TACH_ENABLE_MASK;
2451 	}
2452 
2453 
2454 	if (ioctl(fd, ADM1031_SET_CONFIG_2, &cfg) == -1) {
2455 		return (PICL_FAILURE);
2456 	}
2457 
2458 	(void) close(fd);
2459 	return (PICL_SUCCESS);
2460 }
2461 
2462 static int
2463 get_sys_tach(ptree_rarg_t *parg, void *buf)
2464 {
2465 	picl_prophdl_t	proph;
2466 	env_tuneable_t	*tuneablep;
2467 	int		fd;
2468 	int8_t		cfg;
2469 
2470 	proph = parg->proph;
2471 
2472 	tuneablep = tuneable_lookup(proph);
2473 
2474 	if (tuneablep == NULL)
2475 		return (PICL_FAILURE);
2476 
2477 	fd = open(SYS_HWM_DEVFS, O_RDWR);
2478 
2479 	if (fd == -1) {
2480 		return (PICL_FAILURE);
2481 	}
2482 
2483 	if (ioctl(fd, ADM1031_GET_CONFIG_2, &cfg) == -1) {
2484 		return (PICL_FAILURE);
2485 	}
2486 
2487 	if ((cfg & TACH_ENABLE_MASK) == TACH_ENABLE_MASK) {
2488 		*((int *)tuneablep->value) = ENABLE;
2489 	} else {
2490 		*((int *)tuneablep->value) = DISABLE;
2491 	}
2492 
2493 	(void) memcpy(buf, tuneablep->value,
2494 	    tuneablep->nbytes);
2495 
2496 	(void) close(fd);
2497 	return (PICL_SUCCESS);
2498 }
2499 
2500 static int
2501 set_sys_tach(ptree_warg_t *parg, const void *buf)
2502 {
2503 	picl_prophdl_t	proph;
2504 	env_tuneable_t	*tuneablep;
2505 	int		fd, val;
2506 	int8_t		cfg;
2507 
2508 	if (parg->cred.dc_euid != 0)
2509 		return (PICL_PERMDENIED);
2510 
2511 	proph = parg->proph;
2512 
2513 	tuneablep = tuneable_lookup(proph);
2514 
2515 	if (tuneablep == NULL)
2516 		return (PICL_FAILURE);
2517 
2518 
2519 	fd = open(SYS_HWM_DEVFS, O_RDWR);
2520 
2521 	if (fd == -1) {
2522 		return (PICL_FAILURE);
2523 	}
2524 
2525 	if (ioctl(fd, ADM1031_GET_CONFIG_2, &cfg) == -1) {
2526 		return (PICL_FAILURE);
2527 	}
2528 
2529 	(void) memcpy(&val, buf, sizeof (val));
2530 
2531 	if (val == ENABLE) {
2532 		cfg |= TACH_ENABLE_MASK;
2533 	} else if (val == DISABLE) {
2534 		cfg &= ~TACH_ENABLE_MASK;
2535 	}
2536 
2537 
2538 	if (ioctl(fd, ADM1031_SET_CONFIG_2, &cfg) == -1) {
2539 		return (PICL_FAILURE);
2540 	}
2541 
2542 	(void) close(fd);
2543 	return (PICL_SUCCESS);
2544 }
2545 
2546 static int
2547 get_monitor_cpu_mode(ptree_rarg_t *parg, void *buf)
2548 {
2549 	picl_prophdl_t	proph;
2550 	env_tuneable_t	*tuneablep;
2551 	int		fd;
2552 	int8_t		mmode;
2553 
2554 	proph = parg->proph;
2555 
2556 	tuneablep = tuneable_lookup(proph);
2557 
2558 	if (tuneablep == NULL)
2559 		return (PICL_FAILURE);
2560 
2561 	fd = open(CPU_HWM_DEVFS, O_RDWR);
2562 
2563 	if (fd == -1) {
2564 		return (PICL_FAILURE);
2565 	}
2566 
2567 	if (ioctl(fd, ADM1031_GET_MONITOR_MODE, &mmode) == -1) {
2568 		return (PICL_FAILURE);
2569 	}
2570 
2571 	if (mmode == ADM1031_AUTO_MODE) {
2572 		*((int *)tuneablep->value) = ENABLE;
2573 	} else {
2574 		*((int *)tuneablep->value) = DISABLE;
2575 	}
2576 
2577 	(void) memcpy(buf, tuneablep->value,
2578 	    tuneablep->nbytes);
2579 
2580 	(void) close(fd);
2581 	return (PICL_SUCCESS);
2582 }
2583 
2584 static int
2585 set_monitor_cpu_mode(ptree_warg_t *parg, const void *buf)
2586 {
2587 	picl_prophdl_t	proph;
2588 	env_tuneable_t	*tuneablep;
2589 	int		fd, val;
2590 	int8_t		mmode;
2591 
2592 	if (parg->cred.dc_euid != 0)
2593 		return (PICL_PERMDENIED);
2594 
2595 	proph = parg->proph;
2596 
2597 	tuneablep = tuneable_lookup(proph);
2598 
2599 	if (tuneablep == NULL)
2600 		return (PICL_FAILURE);
2601 
2602 	fd = open(CPU_HWM_DEVFS, O_RDWR);
2603 
2604 	if (fd == -1) {
2605 		return (PICL_FAILURE);
2606 	}
2607 
2608 	(void) memcpy(&val, buf, sizeof (val));
2609 
2610 	if (val == ENABLE) {
2611 		mmode = ADM1031_AUTO_MODE;
2612 	} else if (val == DISABLE) {
2613 		mmode = ADM1031_MANUAL_MODE;
2614 	}
2615 
2616 	if (ioctl(fd, ADM1031_SET_MONITOR_MODE, &mmode) == -1) {
2617 		return (PICL_FAILURE);
2618 	}
2619 
2620 	(void) close(fd);
2621 	return (PICL_SUCCESS);
2622 }
2623 
2624 static int
2625 get_monitor_sys_mode(ptree_rarg_t *parg, void *buf)
2626 {
2627 	picl_prophdl_t	proph;
2628 	env_tuneable_t	*tuneablep;
2629 	int		fd;
2630 	int8_t		mmode;
2631 
2632 	proph = parg->proph;
2633 
2634 	tuneablep = tuneable_lookup(proph);
2635 
2636 	if (tuneablep == NULL)
2637 		return (PICL_FAILURE);
2638 
2639 	fd = open(SYS_HWM_DEVFS, O_RDWR);
2640 
2641 	if (fd == -1) {
2642 		return (PICL_FAILURE);
2643 	}
2644 
2645 	if (ioctl(fd, ADM1031_GET_MONITOR_MODE, &mmode) == -1) {
2646 		return (PICL_FAILURE);
2647 	}
2648 
2649 	if (mmode == ADM1031_AUTO_MODE) {
2650 		*((int *)tuneablep->value) = ENABLE;
2651 	} else {
2652 		*((int *)tuneablep->value) = DISABLE;
2653 	}
2654 
2655 	(void) memcpy(buf, tuneablep->value,
2656 	    tuneablep->nbytes);
2657 
2658 	(void) close(fd);
2659 	return (PICL_SUCCESS);
2660 }
2661 
2662 static int
2663 set_monitor_sys_mode(ptree_warg_t *parg, const void *buf)
2664 {
2665 	picl_prophdl_t	proph;
2666 	env_tuneable_t	*tuneablep;
2667 	int		fd, val;
2668 	int8_t		mmode;
2669 
2670 	if (parg->cred.dc_euid != 0)
2671 		return (PICL_PERMDENIED);
2672 
2673 	proph = parg->proph;
2674 
2675 	tuneablep = tuneable_lookup(proph);
2676 
2677 	if (tuneablep == NULL)
2678 		return (PICL_FAILURE);
2679 
2680 	fd = open(SYS_HWM_DEVFS, O_RDWR);
2681 
2682 	if (fd == -1) {
2683 		return (PICL_FAILURE);
2684 	}
2685 
2686 	(void) memcpy(&val, buf, sizeof (val));
2687 
2688 	if (val == ENABLE) {
2689 		mmode = ADM1031_AUTO_MODE;
2690 	} else if (val == DISABLE) {
2691 		mmode = ADM1031_MANUAL_MODE;
2692 	}
2693 
2694 	if (ioctl(fd, ADM1031_SET_MONITOR_MODE, &mmode) == -1) {
2695 		return (PICL_FAILURE);
2696 	}
2697 
2698 	(void) close(fd);
2699 	return (PICL_SUCCESS);
2700 }
2701 
2702 static int
2703 get_string_val(ptree_rarg_t *parg, void *buf)
2704 {
2705 	picl_prophdl_t	proph;
2706 	env_tuneable_t	*tuneablep;
2707 
2708 	proph = parg->proph;
2709 
2710 	tuneablep = tuneable_lookup(proph);
2711 
2712 	if (tuneablep == NULL)
2713 		return (PICL_FAILURE);
2714 
2715 	(void) memcpy(buf, (caddr_t)tuneablep->value,
2716 	    tuneablep->nbytes);
2717 
2718 	return (PICL_SUCCESS);
2719 }
2720 
2721 static int
2722 set_string_val(ptree_warg_t *parg, const void *buf)
2723 {
2724 	picl_prophdl_t	proph;
2725 	env_tuneable_t	*tuneablep;
2726 
2727 	if (parg->cred.dc_euid != 0)
2728 		return (PICL_PERMDENIED);
2729 
2730 	proph = parg->proph;
2731 
2732 	tuneablep = tuneable_lookup(proph);
2733 
2734 	if (tuneablep == NULL)
2735 		return (PICL_FAILURE);
2736 
2737 	(void) memcpy((caddr_t)tuneables->value, (caddr_t)buf,
2738 	    tuneables->nbytes);
2739 
2740 
2741 	return (PICL_SUCCESS);
2742 }
2743 
2744 static int
2745 get_int_val(ptree_rarg_t *parg, void *buf)
2746 {
2747 	picl_prophdl_t	proph;
2748 	env_tuneable_t	*tuneablep;
2749 
2750 	proph = parg->proph;
2751 
2752 	tuneablep = tuneable_lookup(proph);
2753 
2754 	if (tuneablep == NULL)
2755 		return (PICL_FAILURE);
2756 
2757 	(void) memcpy((int *)buf, (int *)tuneablep->value,
2758 	    tuneablep->nbytes);
2759 
2760 	return (PICL_SUCCESS);
2761 }
2762 
2763 static int
2764 set_int_val(ptree_warg_t *parg, const void *buf)
2765 {
2766 	picl_prophdl_t	proph;
2767 	env_tuneable_t	*tuneablep;
2768 
2769 	if (parg->cred.dc_euid != 0)
2770 		return (PICL_PERMDENIED);
2771 
2772 	proph = parg->proph;
2773 
2774 	tuneablep = tuneable_lookup(proph);
2775 
2776 	if (tuneablep == NULL)
2777 		return (PICL_FAILURE);
2778 
2779 	(void) memcpy((int *)tuneablep->value, (int *)buf,
2780 	    tuneablep->nbytes);
2781 
2782 	return (PICL_SUCCESS);
2783 }
2784 
2785 int
2786 get_dimm_fan_speed(int fan_fd, fanspeed_t *fanspeedp)
2787 {
2788 	int16_t	dimm_fan_period;
2789 	i2c_reg_t	i2c_reg;
2790 
2791 	/*
2792 	 * The dimm fan period is 16 bit value and we need to read
2793 	 * registers 2 and 3 to get the LSB and MSB values.
2794 	 */
2795 	i2c_reg.reg_num = PIC16F819_FAN_PERIOD_MSB_REGISTER;
2796 	if (ioctl(fan_fd, I2C_GET_REG, &i2c_reg) == -1) {
2797 		if (env_debug)
2798 			envd_log(LOG_ERR,
2799 			"Error in reading FAN_PERIOD MSB REGISTER\n");
2800 		return (-1);
2801 	}
2802 	dimm_fan_period = (i2c_reg.reg_value << 8);
2803 	i2c_reg.reg_num = PIC16F819_FAN_PERIOD_LSB_REGISTER;
2804 	if (ioctl(fan_fd, I2C_GET_REG, &i2c_reg) == -1) {
2805 		if (env_debug)
2806 			envd_log(LOG_ERR,
2807 			"Error in reading FAN_PERIOD LSB REGISTER\n");
2808 		return (-1);
2809 	}
2810 	dimm_fan_period |= i2c_reg.reg_value;
2811 	if (env_debug)
2812 		envd_log(LOG_ERR,
2813 		" dimm fan tach period is 0x%x\n", dimm_fan_period);
2814 	if (dimm_fan_period == 0) {
2815 		if (env_debug)
2816 			envd_log(LOG_ERR,
2817 			"dimm fan tach period read as zero. Illegal value.\n");
2818 		return (-1);
2819 	}
2820 	*fanspeedp = PIC16F819_FAN_TACH_TO_RPM(dimm_fan_period);
2821 	return (0);
2822 }
2823 
2824 int
2825 is_dimm_fan_failed(void)
2826 {
2827 	i2c_reg_t	i2c_reg;
2828 	fanspeed_t	fan_speed;
2829 	int		retry_count;
2830 
2831 	if (envd_dimm_fan.fd == -1)
2832 		return (-1);
2833 	/*
2834 	 * read register 1 to look at Fan fault bit.
2835 	 */
2836 	i2c_reg.reg_num = PIC16F819_STATUS_REGISTER;
2837 	retry_count = MAX_RETRIES_FOR_PIC16F819_REG_READ;
2838 	while (retry_count > 0) {
2839 		if (ioctl(envd_dimm_fan.fd, I2C_GET_REG, &i2c_reg) == -1) {
2840 			retry_count--;
2841 			continue;
2842 		} else break;
2843 	}
2844 	if (retry_count != MAX_RETRIES_FOR_PIC16F819_REG_READ) {
2845 		if (env_debug)
2846 			envd_log(LOG_ERR,
2847 			    "%d retries attempted in reading STATUS "
2848 			    "register.\n",
2849 			    (MAX_RETRIES_FOR_PIC16F819_REG_READ - retry_count));
2850 	}
2851 	if (retry_count == 0) {
2852 		(void) strncpy(dimm_fan_status_string, NOT_AVAILABLE,
2853 		    sizeof (dimm_fan_status_string));
2854 		(void) strncpy(dimm_fan_command_string, NOT_AVAILABLE,
2855 		    sizeof (dimm_fan_command_string));
2856 		(void) strncpy(dimm_fan_debug_string, NOT_AVAILABLE,
2857 		    sizeof (dimm_fan_debug_string));
2858 		(void) strncpy(dimm_fan_rpm_string, NOT_AVAILABLE,
2859 		    sizeof (dimm_fan_rpm_string));
2860 		return (-1);
2861 	}
2862 	if (env_debug)
2863 		envd_log(LOG_ERR,
2864 		"DIMM FAN STATUS reg = 0x%x\n", i2c_reg.reg_value);
2865 	if (i2c_reg.reg_value & PIC16F819_FAN_FAILED) {
2866 		(void) snprintf(dimm_fan_status_string,
2867 		    sizeof (dimm_fan_status_string), "0x%x",
2868 		    i2c_reg.reg_value);
2869 		i2c_reg.reg_num = PIC16F819_DEBUG_REGISTER;
2870 		if (ioctl(envd_dimm_fan.fd, I2C_GET_REG, &i2c_reg) == -1) {
2871 			(void) strncpy(dimm_fan_debug_string, NOT_AVAILABLE,
2872 			    sizeof (dimm_fan_debug_string));
2873 		} else {
2874 			(void) snprintf(dimm_fan_debug_string,
2875 			    sizeof (dimm_fan_debug_string),
2876 			    "0x%x", i2c_reg.reg_value);
2877 		}
2878 		i2c_reg.reg_num = PIC16F819_COMMAND_REGISTER;
2879 		if (ioctl(envd_dimm_fan.fd, I2C_GET_REG, &i2c_reg) == -1) {
2880 			(void) strncpy(dimm_fan_command_string, NOT_AVAILABLE,
2881 			    sizeof (dimm_fan_command_string));
2882 		} else {
2883 			(void) snprintf(dimm_fan_command_string,
2884 			    sizeof (dimm_fan_command_string),
2885 			    "0x%x", i2c_reg.reg_value);
2886 		}
2887 		if (get_dimm_fan_speed(envd_dimm_fan.fd, &fan_speed) == -1) {
2888 			(void) strncpy(dimm_fan_rpm_string, NOT_AVAILABLE,
2889 			    sizeof (dimm_fan_rpm_string));
2890 		} else {
2891 			(void) snprintf(dimm_fan_rpm_string,
2892 			    sizeof (dimm_fan_rpm_string),
2893 			    "%d", fan_speed);
2894 		}
2895 		return (1);
2896 	} else return (0);
2897 }
2898