xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4u/cherrystone/psvcpolicy/psvcpolicy.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Cherrystone platform specific environment monitoring policies
29  */
30 
31 #include	<syslog.h>
32 #include	<unistd.h>
33 #include	<stdio.h>
34 #include 	<libintl.h>
35 #include	<string.h>
36 #include	<stdlib.h>
37 #include	<errno.h>
38 #include	<fcntl.h>
39 #include	<sys/types.h>
40 #include	<sys/time.h>
41 #include	<sys/time_impl.h>
42 #include	<sys/signal.h>
43 #include	<sys/devctl.h>
44 #include	<libdevinfo.h>
45 #include	<libdevice.h>
46 #include	<picl.h>
47 #include	<picltree.h>
48 #include	<sys/i2c/clients/i2c_client.h>
49 #include	<hbaapi.h>
50 #include	<limits.h>
51 #include	<sys/systeminfo.h>
52 
53 #include	<psvc_objects.h>
54 
55 /* Device paths for power supply hotplug handling */
56 #define	SEG5_ADDR		0x30
57 #define	EBUS_DEV_NAME		"/devices/pci@9,700000/ebus@1/"
58 #define	SEG5_DEV_NAME		EBUS_DEV_NAME "i2c@1,30/"
59 #define	SEG5_ADDR_DEV_FMT	EBUS_DEV_NAME "i2c@1,%x:devctl"
60 
61 #define	QLC_NODE		 "/pci@9,600000/SUNW,qlc@2"
62 
63 #define	DISK_DRV  "ssd"
64 #define	MAX_DISKS 2
65 #define	WWN_SIZE 8
66 #define	ONBOARD_CONTR	"../../devices/pci@9,600000/SUNW,qlc@2/fp@0,0:fc"
67 
68 /* Bit masks so we don't "wedge" the inputs */
69 #define	PCF8574_BIT_WRITE_VALUE(byte, bit, value)\
70 				((value << bit) | (byte & (~(0x01 << bit))))
71 
72 #define	PDB_MUST_BE_1		0xBF
73 #define	PSU_MUST_BE_1		0x7F
74 #define	DISKBP_MUST_BE_1	0x0F
75 
76 /*LINTLIBRARY*/
77 
78 #define	PSVC_MAX_STR_LEN	32
79 
80 #define	PS_MAX_FAULT_SENSORS 3
81 
82 /*
83  * Keep track of the power supply's fail status for reporting if/when
84  * they go good.
85  * ID's:
86  * O	PSx_FAULT_SENSOR
87  * 1	Doesn't matter	-- only need 0 to be PSx_FAULT_SENSOR
88  * 2	Doesn't matter
89  */
90 static char	*ps_prev_id[2][3] =
91 		{{NULL, NULL, NULL}, {NULL, NULL, NULL}};
92 static int	ps_prev_failed[2][3] = {{0, 0, 0}, {0, 0, 0}};
93 
94 /*
95  * Keep track of the power supply's previous presence
96  * because PSVC doesn't do that for us.
97  */
98 static boolean_t ps_prev_present[2];
99 static boolean_t ps_present[2];
100 
101 /* Local Routines for the environmental policies */
102 static int ac_unplugged(psvc_opaque_t, char *);
103 static int ac_power_check(psvc_opaque_t, char *, char *);
104 
105 /*
106  * The I2C bus is noisy, and the state may be incorrectly reported as
107  * having changed.  When the state changes, we attempt to confirm by
108  * retrying.  If any retries indicate that the state has not changed, we
109  * assume the state change(s) were incorrect and the state has not changed.
110  * The following variables are used to store the tuneable values read in
111  * from the optional i2cparam.conf file for this shared object library.
112  */
113 static int n_retry_fan = PSVC_NUM_OF_RETRIES;
114 static int retry_sleep_fan = 1;
115 static int n_retry_ps_status = PSVC_NUM_OF_RETRIES;
116 static int retry_sleep_ps_status = 1;
117 static int n_retry_pshp = PSVC_NUM_OF_RETRIES;
118 static int retry_sleep_pshp = 1;
119 static int n_retry_diskhp = PSVC_NUM_OF_RETRIES;
120 static int retry_sleep_diskhp = 1;
121 static int n_retry_temp_shutdown = PSVC_NUM_OF_RETRIES;
122 static int retry_sleep_temp_shutdown = 1;
123 static int n_retry_fsp_fault = PSVC_NUM_OF_RETRIES;
124 static int retry_sleep_fsp_fault = 1;
125 
126 typedef struct {
127 	int *pvar;
128 	char *texttag;
129 } i2c_noise_param_t;
130 
131 static i2c_noise_param_t i2cparams[] = {
132 	&n_retry_fan, "n_retry_fan",
133 	&retry_sleep_fan, "retry_sleep_fan",
134 	&n_retry_ps_status, "n_retry_ps_status",
135 	&retry_sleep_ps_status, "retry_sleep_ps_status",
136 	&n_retry_pshp, "n_retry_pshp",
137 	&retry_sleep_pshp, "retry_sleep_pshp",
138 	&n_retry_diskhp, "n_retry_diskhp",
139 	&retry_sleep_diskhp, "retry_sleep_diskhp",
140 	&n_retry_temp_shutdown, "n_retry_temp_shutdown",
141 	&retry_sleep_temp_shutdown, "retry_sleep_temp_shutdown",
142 	&n_retry_fsp_fault, "n_retry_fsp_fault",
143 	&retry_sleep_fsp_fault, "retry_sleep_fsp_fault",
144 	NULL, NULL
145 };
146 
147 #pragma init(i2cparams_load)
148 
149 static void
150 i2cparams_debug(i2c_noise_param_t *pi2cparams, char *platform,
151 	int usingDefaults)
152 {
153 	char s[128];
154 	i2c_noise_param_t *p;
155 
156 	if (!usingDefaults) {
157 		(void) snprintf(s, sizeof (s),
158 		    "# Values from /usr/platform/%s/lib/i2cparam.conf\n",
159 		    platform);
160 		syslog(LOG_WARNING, "%s", s);
161 	} else {
162 		/* no file - we're using the defaults */
163 		(void) snprintf(s, sizeof (s),
164 "# No /usr/platform/%s/lib/i2cparam.conf file, using defaults\n",
165 		    platform);
166 	}
167 	(void) fputs(s, stdout);
168 	p = pi2cparams;
169 	while (p->pvar != NULL) {
170 		(void) snprintf(s, sizeof (s), "%s %d\n", p->texttag,
171 		    *(p->pvar));
172 		if (!usingDefaults)
173 			syslog(LOG_WARNING, "%s", s);
174 		(void) fputs(s, stdout);
175 		p++;
176 	}
177 }
178 
179 static void
180 i2cparams_load(void)
181 {
182 	FILE *fp;
183 	char filename[PATH_MAX];
184 	char platform[64];
185 	char s[128];
186 	char var[128];
187 	int val;
188 	i2c_noise_param_t *p;
189 
190 	if (sysinfo(SI_PLATFORM, platform, sizeof (platform)) == -1) {
191 		syslog(LOG_ERR, "sysinfo error %s\n", strerror(errno));
192 		return;
193 	}
194 	(void) snprintf(filename, sizeof (filename),
195 	    "/usr/platform/%s/lib/i2cparam.conf", platform);
196 	/* read thru the i2cparam.conf file and set variables */
197 	if ((fp = fopen(filename, "r")) != NULL) {
198 		while (fgets(s, sizeof (s), fp) != NULL) {
199 			if (s[0] == '#') /* skip comment lines */
200 				continue;
201 			/* try to find a string match and get the value */
202 			if (sscanf(s, "%127s %d", var, &val) != 2)
203 				continue;
204 			if (val < 1)
205 				val = 1;  /* clamp min value */
206 			p = &(i2cparams[0]);
207 			while (p->pvar != NULL) {
208 				if (strncmp(p->texttag, var, sizeof (var)) ==
209 				    0) {
210 					*(p->pvar) = val;
211 					break;
212 				}
213 				p++;
214 			}
215 		}
216 		(void) fclose(fp);
217 	}
218 	/* output the values of the parameters */
219 	i2cparams_debug(&(i2cparams[0]), platform, ((fp == NULL)? 1 : 0));
220 }
221 
222 /*
223  * Create an I2C device node.
224  */
225 static int
226 create_i2c_node(char *nd_name, char *nd_compat, int nd_nexi, int *nd_reg)
227 {
228 	devctl_ddef_t	ddef_hdl = NULL;
229 	devctl_hdl_t	bus_hdl = NULL;
230 	devctl_hdl_t	dev_hdl = NULL;
231 	char		buf[MAXPATHLEN];
232 	char		dev_path[MAXPATHLEN];
233 	int		rv = PSVC_FAILURE;
234 
235 	(void) snprintf(buf, sizeof (buf), SEG5_ADDR_DEV_FMT, nd_nexi);
236 	bus_hdl = devctl_bus_acquire(buf, 0);
237 	if (bus_hdl == NULL)
238 		goto bad;
239 
240 	/* device definition properties */
241 	ddef_hdl = devctl_ddef_alloc(nd_name, 0);
242 	(void) devctl_ddef_string(ddef_hdl, "compatible", nd_compat);
243 	(void) devctl_ddef_int_array(ddef_hdl, "reg", 2, nd_reg);
244 
245 	/* create the device node */
246 	if (devctl_bus_dev_create(bus_hdl, ddef_hdl, 0, &dev_hdl))
247 		goto bad;
248 
249 	if (devctl_get_pathname(dev_hdl, dev_path, MAXPATHLEN) == NULL)
250 		goto bad;
251 
252 #ifdef DEBUG
253 	syslog(LOG_ERR, "PSVC: create_i2c_node: Device node created: (%s)",
254 		dev_path);
255 #endif
256 	rv = PSVC_SUCCESS;
257 bad:
258 	if (dev_hdl)  devctl_release(dev_hdl);
259 	if (ddef_hdl) devctl_ddef_free(ddef_hdl);
260 	if (bus_hdl)  devctl_release(bus_hdl);
261 	return (rv);
262 }
263 
264 /*
265  * Delete an I2C device node given the device path.
266  */
267 static void
268 delete_i2c_node(char *nd)
269 {
270 	int		rv;
271 	devctl_hdl_t	dev_hdl;
272 
273 	dev_hdl = devctl_device_acquire(nd, 0);
274 	if (dev_hdl == NULL) {
275 		return;
276 	}
277 
278 	rv = devctl_device_remove(dev_hdl);
279 	if (rv != DDI_SUCCESS)
280 		perror(nd);
281 #ifdef DEBUG
282 	else
283 		syslog(LOG_ERR, "Device node deleted: (%s)", nd);
284 #endif
285 	devctl_release(dev_hdl);
286 }
287 
288 
289 /* PCF8574 Reset Function */
290 static int
291 send_pcf8574_reset(psvc_opaque_t hdlp, char *reset_dev)
292 {
293 	int	err;
294 	uint8_t reset_bits[2] = {0x7F, 0xFF};
295 	int	i;
296 	for (i = 0; i < 2; i++) {
297 		err = psvc_set_attr(hdlp, reset_dev, PSVC_GPIO_VALUE_ATTR,
298 			&reset_bits[i]);
299 		if (err != PSVC_SUCCESS) {
300 #ifdef DEBUG
301 			syslog(LOG_ERR,
302 				gettext("Reset to %s with 0x%x failed"),
303 				reset_dev, reset_bits[i]);
304 #endif
305 			return (err);
306 		}
307 	}
308 	/* Need to give u-code a chance to update */
309 	sleep(3);
310 	return (err);
311 }
312 
313 static int
314 pcf8574_write_bit(psvc_opaque_t hdlp, char *id, uint8_t bit_num,
315 	uint8_t bit_val, uint8_t write_must_be_1)
316 {
317 	int	rv = PSVC_FAILURE;
318 	uint8_t	byte;
319 
320 	rv = psvc_get_attr(hdlp, id, PSVC_GPIO_VALUE_ATTR, &byte);
321 	if (rv != PSVC_SUCCESS)
322 		return (rv);
323 
324 	byte = PCF8574_BIT_WRITE_VALUE(byte, bit_num, bit_val);
325 	byte |= write_must_be_1;
326 	rv = psvc_set_attr(hdlp, id, PSVC_GPIO_VALUE_ATTR, &byte);
327 	return (rv);
328 }
329 
330 /*
331  * To enable the i2c bus, we must toggle bit 6 on the PDB's
332  * PCF8574 (0x4C) high->low->high
333  */
334 static int
335 pdb_enable_i2c(psvc_opaque_t hdlp)
336 {
337 	int		rv = PSVC_SUCCESS, i;
338 	int		bit_vals[3] = {1, 0, 1};
339 	int		bit_num = 6;
340 
341 	for (i = 0; i < 3; i++) {
342 		rv = pcf8574_write_bit(hdlp, "PDB_PORT", bit_num, bit_vals[i],
343 			PDB_MUST_BE_1);
344 		if (rv != PSVC_SUCCESS) {
345 			goto bad;
346 		}
347 	}
348 	return (rv);
349 bad:
350 #ifdef DEBUG
351 	syslog(LOG_ERR, gettext("PDB I2C Bus Enabling Failed"));
352 #endif
353 	return (rv);
354 }
355 
356 int32_t
357 psvc_init_disk_bp_policy_0(psvc_opaque_t hdlp, char *id)
358 {
359 	uint8_t	reset = 0xFF;
360 	return (psvc_set_attr(hdlp, id, PSVC_GPIO_VALUE_ATTR,
361 		&reset));
362 }
363 
364 int32_t
365 pcf8574_init_policy_0(psvc_opaque_t hdlp, char *id)
366 {
367 	return (send_pcf8574_reset(hdlp, id));
368 }
369 
370 static int32_t
371 check_fan(psvc_opaque_t hdlp, char *tray_id, char *fan_id, boolean_t *fault_on)
372 {
373 	int		status;
374 	int		speed;
375 	int		low_thresh;
376 	boolean_t	have_fault = 0;
377 	char		*tach_id;
378 	char		state[PSVC_MAX_STR_LEN];
379 	char		prev_state[PSVC_MAX_STR_LEN];
380 	char		fault_state[PSVC_MAX_STR_LEN];
381 	int		retry;
382 
383 	/* Get this fan object's corresponding fan tach */
384 	status = psvc_get_attr(hdlp, fan_id, PSVC_ASSOC_ID_ATTR,
385 		&tach_id, PSVC_FAN_SPEED_TACHOMETER, 0);
386 	if (status != PSVC_SUCCESS)
387 		return (status);
388 
389 	/* Get the low fan speed threshold */
390 	status = psvc_get_attr(hdlp, tach_id, PSVC_LO_WARN_ATTR, &low_thresh);
391 	if (status != PSVC_SUCCESS)
392 		return (status);
393 
394 	retry = 0;
395 	do {
396 		if (retry)
397 			(void) sleep(retry_sleep_fan);
398 		/* Get the fan speed */
399 		status = psvc_get_attr(hdlp, tach_id, PSVC_SENSOR_VALUE_ATTR,
400 		    &speed);
401 		if (status != PSVC_SUCCESS)
402 		return (status);
403 
404 		if (speed <= low_thresh) { /* We see a fault */
405 			strlcpy(fault_state, "DEVICE_FAIL",
406 			    sizeof (fault_state));
407 			strlcpy(state, PSVC_ERROR, sizeof (state));
408 			have_fault = 1;
409 		} else { /* Fault gone? */
410 			strlcpy(fault_state, PSVC_NO_FAULT,
411 			    sizeof (fault_state));
412 			strlcpy(state, PSVC_OK, sizeof (state));
413 			have_fault = 0;
414 		}
415 		retry++;
416 	} while ((retry < n_retry_fan) && (speed <= low_thresh));
417 
418 	/* Assign new states to the fan object */
419 	status = psvc_set_attr(hdlp, fan_id, PSVC_FAULTID_ATTR, fault_state);
420 	if (status != PSVC_SUCCESS)
421 		return (status);
422 	status = psvc_set_attr(hdlp, fan_id, PSVC_STATE_ATTR, state);
423 	if (status != PSVC_SUCCESS)
424 		return (status);
425 
426 	/* Get state and previous state */
427 	status = psvc_get_attr(hdlp, fan_id, PSVC_STATE_ATTR, state);
428 	if (status != PSVC_SUCCESS)
429 		return (status);
430 	status = psvc_get_attr(hdlp, fan_id, PSVC_PREV_STATE_ATTR, prev_state);
431 	if (status != PSVC_SUCCESS)
432 		return (status);
433 
434 	/* Display notices */
435 	if (strcmp(state, PSVC_OK) != 0) {
436 		syslog(LOG_ERR,	gettext("WARNING: %s (%s) failure detected"),
437 			tray_id, fan_id);
438 	} else {
439 		if (strcmp(state, prev_state) != 0) {
440 		syslog(LOG_ERR,	gettext("NOTICE: Device %s (%s) OK"),
441 			tray_id, fan_id);
442 		}
443 	}
444 
445 	*fault_on |= have_fault;
446 	return (PSVC_SUCCESS);
447 }
448 
449 /*
450  * This policy acts on fan trays.  It looks at each of its fans
451  * and checks the speeds.  If the fan speed is less than the threshold,
452  * then indicate:  console, log, LED.
453  */
454 int32_t
455 psvc_fan_fault_check_policy_0(psvc_opaque_t hdlp, char *id)
456 {
457 	int		fan_count;
458 	int		led_count;
459 	int		err, i;
460 	char		*led_id;
461 	char		*fan_id;
462 	char		led_state[PSVC_MAX_STR_LEN];
463 	char		state[PSVC_MAX_STR_LEN];
464 	char		prev_state[PSVC_MAX_STR_LEN];
465 	boolean_t	fault_on = 0;
466 
467 	/* Get the number of fans associated with this fan tray. */
468 	err = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &fan_count,
469 		PSVC_FAN_TRAY_FANS);
470 	if (err != PSVC_SUCCESS)
471 		return (err);
472 
473 	for (i = 0; i < fan_count; i++) {
474 		err = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
475 			&fan_id, PSVC_FAN_TRAY_FANS, i);
476 		if (err != PSVC_SUCCESS)
477 			return (err);
478 
479 		err = check_fan(hdlp, id, fan_id, &fault_on);
480 		if (err != PSVC_SUCCESS)
481 			return (err);
482 	}
483 
484 	if (fault_on) {
485 		strlcpy(led_state, PSVC_LED_ON, sizeof (led_state));
486 		err = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, PSVC_ERROR);
487 		if (err != PSVC_SUCCESS)
488 			return (err);
489 
490 	} else {
491 		strlcpy(led_state, PSVC_LED_OFF, sizeof (led_state));
492 		err = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, PSVC_OK);
493 		if (err != PSVC_SUCCESS)
494 			return (err);
495 	}
496 
497 	err = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state);
498 	if (err != PSVC_SUCCESS)
499 		return (err);
500 	err = psvc_get_attr(hdlp, id, PSVC_PREV_STATE_ATTR, prev_state);
501 	if (err != PSVC_SUCCESS)
502 		return (err);
503 
504 	/*
505 	 * Set leds according to the fan tray's states.
506 	 * (we only do this if there is a change of state in order
507 	 *  to reduce i2c traffic)
508 	 */
509 	if (strcmp(state, prev_state) != 0) {
510 		err = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR,
511 			&led_count, PSVC_DEV_FAULT_LED);
512 		if (err != PSVC_SUCCESS)
513 			return (err);
514 		for (i = 0; i < led_count; i++) {
515 			err = psvc_get_attr(hdlp, id,
516 				PSVC_ASSOC_ID_ATTR, &led_id,
517 				PSVC_DEV_FAULT_LED, i);
518 			if (err != PSVC_SUCCESS)
519 				return (err);
520 			err = psvc_set_attr(hdlp, led_id,
521 				PSVC_LED_STATE_ATTR, led_state);
522 			if (err != PSVC_SUCCESS)
523 				return (err);
524 			err = psvc_get_attr(hdlp, led_id,
525 				PSVC_LED_STATE_ATTR, led_state);
526 			if (err != PSVC_SUCCESS)
527 				return (err);
528 		}
529 	}
530 	return (err);
531 }
532 
533 static int32_t
534 check_cpu_temp_fault(psvc_opaque_t hdlp, char *cpu, int32_t cpu_count)
535 {
536 	char *sensorid;
537 	int32_t sensor_count;
538 	int32_t status = PSVC_SUCCESS;
539 	int32_t i;
540 	char fault[PSVC_MAX_STR_LEN];
541 	int		retry;
542 
543 	psvc_get_attr(hdlp, cpu, PSVC_ASSOC_MATCHES_ATTR, &sensor_count,
544 		PSVC_DEV_TEMP_SENSOR);
545 	for (i = 0; i < sensor_count; ++i) {
546 		status = psvc_get_attr(hdlp, cpu, PSVC_ASSOC_ID_ATTR,
547 			&sensorid, PSVC_DEV_TEMP_SENSOR, i);
548 		if (status == PSVC_FAILURE)
549 			return (status);
550 
551 		retry = 0;
552 		do {
553 			if (retry)
554 				(void) sleep(retry_sleep_temp_shutdown);
555 			status = psvc_get_attr(hdlp, sensorid,
556 			    PSVC_FAULTID_ATTR, fault);
557 			if (status == PSVC_FAILURE)
558 				return (status);
559 			retry++;
560 		} while (((strcmp(fault, PSVC_TEMP_LO_SHUT) == 0) ||
561 		    (strcmp(fault, PSVC_TEMP_HI_SHUT) == 0)) &&
562 		    (retry < n_retry_temp_shutdown));
563 
564 		if ((strcmp(fault, PSVC_TEMP_HI_SHUT) == 0) ||
565 			(strcmp(fault, PSVC_TEMP_LO_SHUT) == 0)) {
566 			system("shutdown -y -g 60 -i 5 \"OVERTEMP condition\"");
567 		}
568 	}
569 
570 	return (status);
571 }
572 
573 int32_t
574 psvc_shutdown_policy_0(psvc_opaque_t hdlp, char *id)
575 {
576 	int32_t cpu_count;
577 	char *cpuid;
578 	int32_t i;
579 	boolean_t present;
580 	int32_t status = PSVC_SUCCESS;
581 
582 	psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &cpu_count,
583 		PSVC_CPU);
584 	for (i = 0; i < cpu_count; ++i) {
585 
586 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &cpuid,
587 			PSVC_CPU, i);
588 		if (status == PSVC_FAILURE)
589 			return (status);
590 
591 		status = psvc_get_attr(hdlp, cpuid,
592 			PSVC_PRESENCE_ATTR, &present);
593 		if (status == PSVC_FAILURE && present == PSVC_PRESENT)
594 			return (status);
595 		if (present == PSVC_PRESENT) {
596 			status = check_cpu_temp_fault(hdlp, cpuid, cpu_count);
597 			if (status == PSVC_FAILURE && errno != ENODEV)
598 				return (status);
599 		}
600 	}
601 
602 	return (PSVC_SUCCESS);
603 }
604 
605 /*
606  * Checks device specified by the PSVC_DEV_FAULT_SENSOR association
607  * for errors, and if there is, then report and turn on the FSP Fault
608  * Led.
609  */
610 int32_t
611 psvc_fsp_device_fault_check_policy_0(psvc_opaque_t hdlp, char *id)
612 {
613 	int32_t	status;
614 	int32_t	i;
615 	int32_t	device_count = 0;
616 	char	device_state[PSVC_MAX_STR_LEN];
617 	char	*device_id;
618 	int32_t	failed_count = 0;
619 	static int32_t led_on = 0;
620 	int		retry;
621 
622 	status = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR,
623 		&device_count, PSVC_DEV_FAULT_SENSOR);
624 	if (status != PSVC_SUCCESS)
625 		return (status);
626 
627 	for (i = 0; i < device_count; i++) {
628 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
629 			&device_id, PSVC_DEV_FAULT_SENSOR, i);
630 		if (status != PSVC_SUCCESS)
631 			return (status);
632 
633 		retry = 0;
634 		do {
635 			if (retry)
636 				(void) sleep(retry_sleep_fsp_fault);
637 			status = psvc_get_attr(hdlp, device_id, PSVC_STATE_ATTR,
638 			    device_state);
639 			if (status != PSVC_SUCCESS)
640 				return (status);
641 
642 			if (strcmp(device_state, PSVC_OK) != 0 &&
643 			    strcmp(device_state, PSVC_HOTPLUGGED) != 0 &&
644 			    strcmp(device_state, "NO AC POWER") != 0 &&
645 			    strlen(device_state) != 0) {
646 			    failed_count++;
647 			}
648 			retry++;
649 		} while ((retry < n_retry_fsp_fault) && (failed_count));
650 	}
651 	if (failed_count == 0 && led_on) {
652 		syslog(LOG_ERR, gettext("%s has turned OFF"), id);
653 		status = psvc_set_attr(hdlp, id, PSVC_LED_STATE_ATTR,
654 			PSVC_LED_OFF);
655 		led_on = 0;
656 	}
657 
658 	if (failed_count > 0 && ! led_on) {
659 		syslog(LOG_ERR,
660 			gettext("%s has turned ON"), id);
661 		status = psvc_set_attr(hdlp, id, PSVC_LED_STATE_ATTR,
662 			PSVC_LED_ON);
663 		led_on = 1;
664 	}
665 
666 	return (PSVC_SUCCESS);
667 }
668 
669 /* Power Supply Policy Helper and Worker Functions */
670 static void
671 ps_reset_prev_failed(int index)
672 {
673 	int	i;
674 	/* Reset the power supply's failure information */
675 	for (i = 0; i < 3; i++) {
676 		ps_prev_id[index][i] = NULL;
677 		ps_prev_failed[index][i] = 0;
678 	}
679 
680 }
681 static int
682 check_i2c_access(psvc_opaque_t hdlp, char *id)
683 {
684 	int		rv;
685 	char		state[PSVC_MAX_STR_LEN];
686 	char		ps_fault_sensor[PSVC_MAX_STR_LEN];
687 
688 	snprintf(ps_fault_sensor, sizeof (ps_fault_sensor),
689 		"%s_FAULT_SENSOR", id);
690 
691 	rv = psvc_get_attr(hdlp, ps_fault_sensor, PSVC_SWITCH_STATE_ATTR,
692 		&state);
693 	return (rv);
694 }
695 
696 /*
697  * This routine takes in the PSVC handle pointer, the PS name, and the
698  * instance number (0 or 1). It simply make a psvc_get call to get the
699  * presence of each of the children under the PS. This call will set the
700  * presence state of the child device if it was not there when the system
701  * was booted.
702  */
703 static int
704 handle_ps_hotplug_children_presence(psvc_opaque_t hdlp, char *id)
705 {
706 	char *child_add_on[4] = {"_RESET", "_LOGICAL_STATE", "_AC_IN_SENSOR",
707 				"_FAULT_SENSOR"};
708 	int add_ons = 4;
709 	char addon_id[PICL_PROPNAMELEN_MAX];
710 	char *sensor_id;
711 	int32_t	status = PSVC_SUCCESS;
712 	boolean_t presence;
713 	int j;
714 
715 	/* Go through the add on list and set presence */
716 	for (j = 0; j < add_ons; j++) {
717 		snprintf(addon_id, sizeof (addon_id), "%s%s", id,
718 		    child_add_on[j]);
719 		status = psvc_get_attr(hdlp, addon_id, PSVC_PRESENCE_ATTR,
720 		    &presence);
721 		if (status != PSVC_SUCCESS)
722 			return (status);
723 	}
724 
725 	/* Go through each PS's fault sensors */
726 	for (j = 0; j < PS_MAX_FAULT_SENSORS; j++) {
727 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
728 		    &(sensor_id), PSVC_DEV_FAULT_SENSOR, j);
729 		if (status != PSVC_SUCCESS)
730 			return (status);
731 		status = psvc_get_attr(hdlp, sensor_id, PSVC_PRESENCE_ATTR,
732 		    &presence);
733 		if (status != PSVC_SUCCESS)
734 			return (status);
735 	}
736 
737 	/* Go through each PS's onboard i2c hardware */
738 	for (j = 0; j < 2; j++) {
739 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
740 		    &(sensor_id), PSVC_PHYSICAL_DEVICE, j);
741 		if (status != PSVC_SUCCESS)
742 			return (status);
743 		status = psvc_get_attr(hdlp, sensor_id, PSVC_PRESENCE_ATTR,
744 		    &presence);
745 		if (status != PSVC_SUCCESS)
746 			return (status);
747 	}
748 
749 	return (status);
750 }
751 
752 static int
753 handle_ps_hotplug(psvc_opaque_t hdlp, char *id, boolean_t present)
754 {
755 	int32_t		status = PSVC_SUCCESS;
756 	int32_t		instance;
757 	picl_nodehdl_t	parent_node;
758 	picl_nodehdl_t	child_node;
759 	char		info[PSVC_MAX_STR_LEN];
760 	char		ps_logical_state[PICL_PROPNAMELEN_MAX];
761 	char		parent_path[PICL_PROPNAMELEN_MAX];
762 	char		ps_path[PICL_PROPNAMELEN_MAX];
763 	static int	fruprom_addr[2][2] = { {0, 0xa2}, {0, 0xa0} };
764 	static int	pcf8574_addr[2][2] = { {0, 0x72}, {0, 0x70} };
765 	char		dev_path[MAXPATHLEN];
766 
767 	/* Convert name to node and parent path */
768 	psvcplugin_lookup(id, parent_path, &child_node);
769 
770 	/*
771 	 * Get the power supply's instance.
772 	 * Used to index the xxx_addr arrays
773 	 */
774 	status = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance);
775 	if (status != PSVC_SUCCESS)
776 		return (status);
777 
778 	if (present == PSVC_PRESENT && !ps_prev_present[instance]) {
779 		/* Service Power Supply Insertion */
780 		syslog(LOG_ERR, gettext("Device %s inserted"), id);
781 
782 		/* PICL Tree Maintenance */
783 		ptree_get_node_by_path(parent_path, &parent_node);
784 		ptree_add_node(parent_node, child_node);
785 		snprintf(ps_path, sizeof (ps_path), "%s/%s", parent_path, id);
786 		psvcplugin_add_children(ps_path);
787 
788 		/*
789 		 * This code to update the presences of power supply
790 		 * child devices in the event that picld was started
791 		 * without a power supply present.  This call makes
792 		 * the devices available after that initial insertion.
793 		 */
794 		status = handle_ps_hotplug_children_presence(hdlp, id);
795 
796 		/*
797 		 * Device Tree Maintenance
798 		 * Add the devinfo tree node entry for the pcf8574 and seeprom
799 		 * and attach their drivers.
800 		 */
801 		status |= create_i2c_node("ioexp", "i2c-pcf8574", SEG5_ADDR,
802 			pcf8574_addr[instance]);
803 		status |= create_i2c_node("fru", "i2c-at24c64", SEG5_ADDR,
804 			fruprom_addr[instance]);
805 	} else {
806 		/* Service Power Supply Removal */
807 		syslog(LOG_ERR, gettext("Device %s removed"), id);
808 
809 		/* Reset the power supply's failure information */
810 		ps_reset_prev_failed(instance);
811 
812 		/* PICL Tree Maintenance */
813 		if (ptree_delete_node(child_node) != PICL_SUCCESS)
814 			syslog(LOG_ERR, "ptree_delete_node failed!");
815 
816 		/*
817 		 * The hardcoded subscript in pcf8574_add[instance][1]
818 		 * refers to the address.  We are appending the address to
819 		 * device path.  Both elements are used when creating
820 		 * the i2c node (above).
821 		 */
822 		snprintf(dev_path, sizeof (dev_path),
823 			SEG5_DEV_NAME"ioexp@0,%x:pcf8574",
824 			pcf8574_addr[instance][1]);
825 		delete_i2c_node(dev_path);
826 
827 		snprintf(dev_path, sizeof (dev_path),
828 			SEG5_DEV_NAME"fru@0,%x:fru", fruprom_addr[instance][1]);
829 			delete_i2c_node(dev_path);
830 	}
831 
832 	snprintf(ps_logical_state, sizeof (ps_logical_state),
833 		"%s_LOGICAL_STATE", id);
834 
835 	strlcpy(info, PSVC_OK, sizeof (info));
836 	status |= psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, info);
837 	status |= psvc_set_attr(hdlp, ps_logical_state,	PSVC_STATE_ATTR, info);
838 
839 	strlcpy(info, PSVC_NO_FAULT, sizeof (info));
840 	status |= psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, info);
841 
842 	/* Enable the i2c connection to the power supply */
843 	status |= pdb_enable_i2c(hdlp);
844 	return (status);
845 }
846 
847 /*
848  * check_ps_state() Checks for:
849  *
850  * - Failure bits:
851  *	Power Supply Fan Failure
852  *	Power Supply Temperature Failure
853  *	Power Supply Generic Fault
854  *	Power Supply AC Cord Plugged In
855  *
856  * - If we see a "bad" state we will report an error.
857  *
858  * - "Bad" states:
859  *	Fault bit shows fault.
860  *	Temperature fault shows fault.
861  *	Fan fault shows fault.
862  *	AC power NOT okay to supply.
863  *
864  * - If we see that the AC Cord is not plugged in, then the the other
865  *   failure bits are invalid.
866  *
867  * - Send pcf8574_reset at the end of the policy if we see
868  *   any "bad" states.
869  */
870 static int32_t
871 check_ps_state(psvc_opaque_t hdlp, char *id)
872 {
873 	int32_t		sensor_count;
874 	int32_t		status = PSVC_SUCCESS;
875 	int32_t		i;
876 	int32_t		fault_on = 0;
877 	char		*sensor_id;
878 	char		ps_ok_sensor[PICL_PROPNAMELEN_MAX];
879 	char		ps_logical_state[PICL_PROPNAMELEN_MAX];
880 	char		ps_reset[PICL_PROPNAMELEN_MAX];
881 	char		previous_state[PSVC_MAX_STR_LEN];
882 	char		state[PSVC_MAX_STR_LEN];
883 	char		fault[PSVC_MAX_STR_LEN];
884 	int		ps_okay = 1;	/* Keep track of the PDB PS OK Bit */
885 	int		instance;
886 	int		retry;
887 
888 	/* Logical state id */
889 	snprintf(ps_logical_state, sizeof (ps_logical_state),
890 		"%s_LOGICAL_STATE", id);
891 
892 	/*
893 	 * ac_power_check updates the Power Supply state with "NO AC POWER" if
894 	 * the power cord is out OR PSVC_OK if the power cord is in.
895 	 */
896 	status = ac_power_check(hdlp, id, ps_logical_state);
897 	if (status == PSVC_FAILURE)
898 		return (status);
899 
900 	/*
901 	 * After running ac_power_check we now need to get the current state
902 	 * of the PS.  If the power supply state is "NO AC POWER" then we do
903 	 * not need to check for failures and we return.
904 	 */
905 	status = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state);
906 	if (status != PSVC_SUCCESS)
907 		return (status);
908 
909 	if (strcmp(state, "NO AC POWER") == 0)
910 		return (status);
911 
912 	status = psvc_get_attr(hdlp, id, PSVC_PREV_STATE_ATTR, previous_state);
913 	if (status != PSVC_SUCCESS)
914 		return (status);
915 
916 	snprintf(ps_ok_sensor, sizeof (ps_ok_sensor), "%s_OK_SENSOR", id);
917 	retry = 0;
918 	do {
919 		if (retry)
920 			(void) sleep(retry_sleep_ps_status);
921 		/* Handle the PDB P/S OK Bit */
922 		status = psvc_get_attr(hdlp, ps_ok_sensor,
923 		    PSVC_SWITCH_STATE_ATTR, state);
924 		if (status != PSVC_SUCCESS)
925 			return (status);
926 		retry++;
927 	} while ((retry < n_retry_ps_status) &&
928 	    (strcmp(previous_state, state)));
929 
930 
931 	/*
932 	 * If there is a change of state (current state differs from
933 	 * previous state, then assign the error values.
934 	 */
935 	if (strcmp(previous_state, state) != 0) {
936 		if (strcmp(state, PSVC_SWITCH_OFF) == 0) {
937 			strlcpy(state, PSVC_ERROR, sizeof (state));
938 			strlcpy(fault, "DEVICE_FAIL", sizeof (fault));
939 			fault_on = 1;
940 			syslog(LOG_ERR,	gettext(
941 				"Device %s: Failure Detected -- %s "
942 				"shutdown!"), id, id);
943 			ps_okay = 0;
944 		} else {
945 			strlcpy(state, PSVC_OK, sizeof (state));
946 			strlcpy(fault, PSVC_NO_FAULT, sizeof (fault));
947 		}
948 
949 		status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, state);
950 		if (status != PSVC_SUCCESS)
951 			return (status);
952 
953 		status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, fault);
954 		if (status != PSVC_SUCCESS)
955 			return (status);
956 	}
957 
958 	status = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance);
959 	if (status != PSVC_SUCCESS)
960 		return (status);
961 
962 	status = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &sensor_count,
963 		PSVC_DEV_FAULT_SENSOR);
964 	if (status != PSVC_SUCCESS) {
965 		return (status);
966 	}
967 
968 	/* Handle the power supply fail bits. */
969 	for (i = 0; i < sensor_count; i++) {
970 		status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
971 			&sensor_id, PSVC_DEV_FAULT_SENSOR, i);
972 		if (status != PSVC_SUCCESS)
973 			return (status);
974 
975 		retry = 0;
976 		do {
977 			if (retry)
978 				(void) sleep(retry_sleep_ps_status);
979 			status = psvc_get_attr(hdlp, sensor_id,
980 			    PSVC_SWITCH_STATE_ATTR, state);
981 			if (status != PSVC_SUCCESS)
982 				return (status);
983 			retry++;
984 		} while ((retry < n_retry_ps_status) &&
985 		    (strcmp(state, PSVC_SWITCH_ON) == 0));
986 
987 		if (strcmp(state, PSVC_SWITCH_ON) == 0) {
988 			if (ps_prev_id[instance][i] == NULL)
989 				ps_prev_id[instance][i] = sensor_id;
990 
991 			if (ps_prev_failed[instance][i] != 1)
992 				ps_prev_failed[instance][i] = 1;
993 			fault_on = 1;
994 			/*
995 			 * The first sensor in the list is:
996 			 * PSx_DEV_FAULT_SENSOR.  If this is on, we do not
997 			 * want to merely report that it's on, but rather
998 			 * report that there was a fault detected, thus
999 			 * improving diagnosability.
1000 			 */
1001 			if (i == 0) {
1002 				/*
1003 				 * Don't notify if the PDB PS OKAY Bit is
1004 				 * "0"
1005 				 */
1006 				if (ps_okay)
1007 					syslog(LOG_ERR, gettext(
1008 						"Device %s: Fault Detected"),
1009 							id);
1010 			} else {
1011 				syslog(LOG_ERR, gettext("Warning %s: %s is ON"),
1012 					id, sensor_id);
1013 			}
1014 		}
1015 	}
1016 
1017 	status = psvc_get_attr(hdlp, ps_logical_state,
1018 		PSVC_STATE_ATTR, state);
1019 	if (status != PSVC_SUCCESS)
1020 		return (status);
1021 
1022 	status = psvc_get_attr(hdlp, ps_logical_state,
1023 		PSVC_PREV_STATE_ATTR, previous_state);
1024 	if (status != PSVC_SUCCESS)
1025 		return (status);
1026 
1027 	/*
1028 	 * If we encountered a fault of any kind (something before
1029 	 * has set 'fault_on' to '1') then we want to send the reset
1030 	 * signal to the power supply's PCF8574 and also set
1031 	 * 'ps_logical_state' to "ERROR" so that the FSP General Fault
1032 	 * LED will light.
1033 	 */
1034 	if (fault_on) {
1035 		if (ps_okay) {
1036 			status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR,
1037 				PSVC_GEN_FAULT);
1038 			if (status != PSVC_SUCCESS)
1039 				return (status);
1040 		}
1041 		status = psvc_set_attr(hdlp, ps_logical_state,
1042 			PSVC_STATE_ATTR, PSVC_ERROR);
1043 		if (status != PSVC_SUCCESS)
1044 			return (status);
1045 		/*
1046 		 * "id" is in the form of "PSx", We need to make it
1047 		 * PSx_RESET.
1048 		 */
1049 		snprintf(ps_reset, sizeof (ps_reset), "%s_RESET", id);
1050 		status = send_pcf8574_reset(hdlp, ps_reset);
1051 		return (status);
1052 	}
1053 
1054 	/*
1055 	 * There was no fault encountered so we want to
1056 	 * set 'ps_logical_state' to "OK"
1057 	 */
1058 	if (strcmp(state, PSVC_OK) != 0) {
1059 		for (i = 0; i < 3; i++) {
1060 			char	*sensor = ps_prev_id[instance][i];
1061 			int	*prev_failed = &ps_prev_failed[instance][i];
1062 			if (sensor == NULL)
1063 				continue;
1064 			if (*prev_failed == 0)
1065 				continue;
1066 			*prev_failed = 0;
1067 			if (i == 0) {
1068 				/*
1069 				 * Don't notifiy if we have a power supply
1070 				 * failure (PDB PS OKAY == 0
1071 				 */
1072 				if (ps_okay)
1073 					syslog(LOG_ERR, gettext(
1074 						"Notice %s: Fault Cleared"),
1075 							id);
1076 			} else {
1077 				syslog(LOG_ERR, gettext("Notice %s: %s is OFF"),
1078 					id, sensor);
1079 			}
1080 		}
1081 
1082 		status = psvc_set_attr(hdlp, ps_logical_state,
1083 			PSVC_STATE_ATTR, PSVC_OK);
1084 		if (status != PSVC_SUCCESS)
1085 			return (status);
1086 		syslog(LOG_ERR, gettext("Device %s Okay"), id);
1087 	}
1088 
1089 	return (PSVC_SUCCESS);
1090 }
1091 
1092 /*
1093  * This routine takes in a handle pointer and a Power Supply id. It then gets
1094  * the switch state for the PSx_AC_IN_SENSOR. If the switch is OFF the cord is
1095  * unplugged and we return a true (1). If the switch is ON then the cord is
1096  * plugged in and we return a false (0). If the get_attr call fails we return
1097  * PSVC_FAILURE (-1).
1098  */
1099 static int
1100 ac_unplugged(psvc_opaque_t hdlp, char *id)
1101 {
1102 	int32_t		status = PSVC_SUCCESS;
1103 	char		ac_sensor_id[PICL_PROPNAMELEN_MAX];
1104 	char		ac_switch_state[PSVC_MAX_STR_LEN];
1105 
1106 	snprintf(ac_sensor_id, sizeof (ac_sensor_id), "%s_AC_IN_SENSOR", id);
1107 
1108 	status = psvc_get_attr(hdlp, ac_sensor_id, PSVC_SWITCH_STATE_ATTR,
1109 		ac_switch_state);
1110 	if (status == PSVC_FAILURE) {
1111 		return (status);
1112 	}
1113 
1114 	if (strcmp(ac_switch_state, PSVC_SWITCH_OFF) == 0) {
1115 		return (1);
1116 	} else {
1117 		return (0);
1118 	}
1119 }
1120 
1121 /*
1122  * This routine expects a handle pointer, a Power Supply ID, and a PS logical
1123  * state switch ID.  It check to see if the power cord has been removed from or
1124  * inserted to the power supply. It then updates the PS state accordingly.
1125  */
1126 static int
1127 ac_power_check(psvc_opaque_t hdlp, char *id, char *ps_logical_state)
1128 {
1129 	int32_t		status = PSVC_SUCCESS;
1130 	int32_t		sensor_count;
1131 	char		*sensor_id;
1132 	char		state[PSVC_MAX_STR_LEN];
1133 	int		unplugged, i;
1134 
1135 	status = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state);
1136 	if (status != PSVC_SUCCESS)
1137 		return (status);
1138 
1139 	/*
1140 	 * Check for AC Power Cord. ac_unplugged will return true if the PS is
1141 	 * unplugged, a false is the PS is plugged in, and PSVC_FAILURE if the
1142 	 * call to get the state fails.
1143 	 */
1144 	unplugged = ac_unplugged(hdlp, id);
1145 	if (status == PSVC_FAILURE) {
1146 		return (status);
1147 	}
1148 
1149 	/*
1150 	 * If power cord is not in, then we set the fault and error
1151 	 * states to "".
1152 	 * If power cord is in, then we check the devices.
1153 	 */
1154 	status = psvc_get_attr(hdlp, id, PSVC_ASSOC_MATCHES_ATTR, &sensor_count,
1155 		PSVC_DEV_FAULT_SENSOR);
1156 	if (status != PSVC_SUCCESS) {
1157 		return (status);
1158 	}
1159 
1160 	if ((unplugged) && (strcmp(state, "NO AC POWER") != 0)) {
1161 		/* set id's state to "NO AC POWER" */
1162 		status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR,
1163 		    "NO AC POWER");
1164 		if (status != PSVC_SUCCESS)
1165 			return (status);
1166 		/*
1167 		 * Set this state so that the FSP Fault LED lights
1168 		 * when there is no AC Power to the power supply.
1169 		 */
1170 		status = psvc_set_attr(hdlp, ps_logical_state, PSVC_STATE_ATTR,
1171 		    PSVC_ERROR);
1172 		if (status != PSVC_SUCCESS)
1173 			return (status);
1174 
1175 		status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR,
1176 		    "NO AC POWER");
1177 		if (status != PSVC_SUCCESS)
1178 			return (status);
1179 
1180 		syslog(LOG_ERR, gettext("Device %s AC UNAVAILABLE"), id);
1181 
1182 		/* Set fault sensor states to "" */
1183 		for (i = 0; i < sensor_count; ++i) {
1184 			status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
1185 				&sensor_id, PSVC_DEV_FAULT_SENSOR, i);
1186 			if (status != PSVC_SUCCESS)
1187 				return (status);
1188 
1189 			status = psvc_set_attr(hdlp, sensor_id,
1190 				PSVC_FAULTID_ATTR, "");
1191 			if (status != PSVC_SUCCESS)
1192 				return (status);
1193 		}
1194 	}
1195 
1196 	/* Power cord is plugged in */
1197 	if ((!unplugged) && (strcmp(state, "NO AC POWER") == 0)) {
1198 		/* Default the state to "OK" */
1199 		status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR,
1200 			PSVC_OK);
1201 		if (status != PSVC_SUCCESS)
1202 			return (status);
1203 		/* Default the PS_LOGICAL_STATE to "OK" */
1204 		status = psvc_set_attr(hdlp, ps_logical_state, PSVC_STATE_ATTR,
1205 			PSVC_OK);
1206 		if (status != PSVC_SUCCESS)
1207 			return (status);
1208 		/* Display message */
1209 		syslog(LOG_ERR, gettext("Device %s AC AVAILABLE"), id);
1210 	}
1211 
1212 	return (status);
1213 }
1214 
1215 int32_t
1216 psvc_init_ps_presence(psvc_opaque_t hdlp, char *id)
1217 {
1218 	int		err;
1219 	int		instance;
1220 	boolean_t	presence;
1221 
1222 	err = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance);
1223 	err |= psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR, &presence);
1224 	ps_prev_present[instance] = ps_present[instance] = presence;
1225 	return (err);
1226 }
1227 
1228 int32_t
1229 psvc_ps_monitor_policy_0(psvc_opaque_t hdlp, char *id)
1230 {
1231 	int		err;
1232 	int		instance;
1233 	static	int	failed_last_time[2] = {0, 0};
1234 	int	retry;
1235 
1236 	err = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance);
1237 	if (err != PSVC_SUCCESS)
1238 		return (err);
1239 
1240 	/* copy current presence to previous presence */
1241 	ps_prev_present[instance] = ps_present[instance];
1242 
1243 	retry = 0;
1244 	do {
1245 		if (retry)
1246 			(void) sleep(retry_sleep_pshp);
1247 		/* Get new presence */
1248 		err = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR,
1249 		    &ps_present[instance]);
1250 		if (err != PSVC_SUCCESS)
1251 			goto out;
1252 		retry++;
1253 	} while ((retry < n_retry_pshp) &&
1254 	    (ps_present[instance] != ps_prev_present[instance]));
1255 
1256 	/* Sustained Hotplug detected */
1257 	if (ps_present[instance] != ps_prev_present[instance]) {
1258 		err = handle_ps_hotplug(hdlp, id, ps_present[instance]);
1259 		return (err);
1260 	}
1261 
1262 	/* If our power supply is not present, we're done */
1263 	if (!ps_present[instance])
1264 		return (PSVC_SUCCESS);
1265 
1266 	err = check_i2c_access(hdlp, id);
1267 	if (err != PSVC_SUCCESS) {
1268 		/* Quickie hotplug */
1269 		if (ps_present[instance] == PSVC_PRESENT &&
1270 		    ps_prev_present[instance] == PSVC_PRESENT) {
1271 			syslog(LOG_ERR, "Device %s removed", id);
1272 			/* Reset prev_failed information */
1273 			ps_reset_prev_failed(instance);
1274 			ps_prev_present[instance] = 0;
1275 			handle_ps_hotplug(hdlp, id, ps_present[instance]);
1276 			/* We ignore the error on a quickie hotplug */
1277 			return (PSVC_SUCCESS);
1278 		}
1279 		/* There was an actual i2c access error */
1280 		goto out;
1281 	}
1282 
1283 	err = check_ps_state(hdlp, id);
1284 	if (err != PSVC_SUCCESS)
1285 		goto out;
1286 
1287 	failed_last_time[instance] = 0;
1288 	return (err);
1289 
1290 out:
1291 	if (! failed_last_time[instance]) {
1292 		/*
1293 		 * We ignore the error condition the first time thru
1294 		 * because the PS could have been removed after (or
1295 		 * during) our call to check_ps_hotplug().
1296 		 *
1297 		 * If the problem is still there the next time, then
1298 		 * we'll raise a flag.
1299 		 *
1300 		 * The instance determines which power supply the policy
1301 		 * errored on.  For instance PS0 might have failed and then
1302 		 * PS1 might have failed, but we'll display a warning
1303 		 * even though there might not be anything actually wrong.
1304 		 * The instance keeps track of which failure occurred so
1305 		 * we warn on the corresponding occurrence of errors.
1306 		 */
1307 		failed_last_time[instance] = 1;
1308 		return (PSVC_SUCCESS);
1309 	}
1310 	return (err);
1311 }
1312 
1313 static int
1314 light_disk_fault_leds(psvc_opaque_t hdlp, char *id, boolean_t disk_presence)
1315 {
1316 	int		err;
1317 	int		bit_nums[MAX_DISKS] = {6, 7};
1318 	uint8_t		led_masks[MAX_DISKS] = {0x40, 0x80};
1319 	int		instance;
1320 	int		bit_value;
1321 	char		state[PSVC_MAX_STR_LEN];
1322 	uint8_t		byte;
1323 
1324 	if (disk_presence != PSVC_PRESENT)
1325 		return (PSVC_SUCCESS);
1326 
1327 	err = psvc_get_attr(hdlp, id, PSVC_INSTANCE_ATTR, &instance);
1328 	if (err != PSVC_SUCCESS)
1329 		return (err);
1330 
1331 	err = psvc_get_attr(hdlp, "DISK_PORT", PSVC_GPIO_VALUE_ATTR,
1332 		&byte);
1333 	if (err != PSVC_SUCCESS)
1334 		return (err);
1335 
1336 	err = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, state);
1337 	if (err != PSVC_SUCCESS)
1338 		return (err);
1339 	if (strcmp(state, PSVC_OK) == 0 || strcmp(state, "") == 0) { /* OK */
1340 		if (byte & led_masks[instance]) { /* Led is OFF */
1341 			return (err); /* Done. */
1342 		} else { /* Led is ON, Turn if OFF */
1343 			bit_value = 1;	/* Active Low */
1344 			err = pcf8574_write_bit(hdlp, "DISK_PORT",
1345 				bit_nums[instance], bit_value,
1346 				DISKBP_MUST_BE_1);
1347 			if (err != PSVC_SUCCESS)
1348 				return (err);
1349 		}
1350 	} else { /* Disk is NOT OK */
1351 		if (byte & led_masks[instance]) { /* Led is OFF, Turn it ON */
1352 			bit_value = 0;	/* Active Low */
1353 			err = pcf8574_write_bit(hdlp, "DISK_PORT",
1354 				bit_nums[instance], bit_value,
1355 				DISKBP_MUST_BE_1);
1356 			if (err != PSVC_SUCCESS)
1357 				return (err);
1358 		} else {
1359 			return (err); /* Done. */
1360 		}
1361 	}
1362 	return (err);
1363 }
1364 
1365 int
1366 verify_disk_wwn(char *wwn)
1367 {
1368 	HBA_PORTATTRIBUTES	hbaPortAttrs, discPortAttrs;
1369 	HBA_HANDLE	handle;
1370 	HBA_STATUS	status;
1371 	HBA_ADAPTERATTRIBUTES	hbaAttrs;
1372 	HBA_UINT32	numberOfAdapters, hbaCount, hbaPort, discPort;
1373 	char	adaptername[256];
1374 	char	vwwn[WWN_SIZE * 2];
1375 	char	OSDeviceName[PATH_MAX + 1];
1376 	int	count, linksize;
1377 
1378 	/* Load common lib */
1379 	status = HBA_LoadLibrary();
1380 	if (status != HBA_STATUS_OK) {
1381 		(void) HBA_FreeLibrary();
1382 		return (HBA_STATUS_ERROR);
1383 	}
1384 
1385 	/*
1386 	 * Since devfs can store multiple instances
1387 	 * of a target the validity of the WWN of a disk is
1388 	 * verified with an actual probe of internal disks
1389 	 */
1390 
1391 	/* Cycle through FC-AL Adapters and search for WWN */
1392 	numberOfAdapters = HBA_GetNumberOfAdapters();
1393 	for (hbaCount = 0; hbaCount < numberOfAdapters; hbaCount++) {
1394 		if ((status = HBA_GetAdapterName(hbaCount, adaptername)) !=
1395 		    HBA_STATUS_OK)
1396 			continue;
1397 
1398 		handle = HBA_OpenAdapter(adaptername);
1399 		if (handle == 0)
1400 			continue;
1401 
1402 		/* Get Adapter Attributes */
1403 		if ((status = HBA_GetAdapterAttributes(handle,
1404 		    &hbaAttrs)) != HBA_STATUS_OK) {
1405 			HBA_CloseAdapter(handle);
1406 			continue;
1407 		}
1408 
1409 		/* Get Adapter's Port Attributes */
1410 		for (hbaPort = 0;
1411 		    hbaPort < hbaAttrs.NumberOfPorts; hbaPort++) {
1412 			if ((status = HBA_GetAdapterPortAttributes(handle,
1413 			    hbaPort, &hbaPortAttrs)) != HBA_STATUS_OK)
1414 				continue;
1415 
1416 			/*
1417 			 * Verify whether this is onboard controller.
1418 			 * HBAAPI provides path of symbol link to
1419 			 * to the qlc node therefore readlink() is
1420 			 * needed to obtain hard link
1421 			 */
1422 			linksize = readlink(hbaPortAttrs.OSDeviceName,
1423 			    OSDeviceName, PATH_MAX);
1424 
1425 			/*
1426 			 * If readlink does not return size of onboard
1427 			 * controller than don't bother checking device
1428 			 */
1429 			if ((linksize + 1) != sizeof (ONBOARD_CONTR))
1430 				continue;
1431 
1432 			OSDeviceName[linksize] = '\0';
1433 			if (strcmp(OSDeviceName, ONBOARD_CONTR) != 0)
1434 				continue;
1435 
1436 			/* Get Discovered Port Attributes */
1437 			for (discPort = 0;
1438 			    discPort < hbaPortAttrs.NumberofDiscoveredPorts;
1439 			    discPort++) {
1440 				status = HBA_GetDiscoveredPortAttributes(
1441 					handle, hbaPort, discPort,
1442 						&discPortAttrs);
1443 				if (status != HBA_STATUS_OK)
1444 					continue;
1445 
1446 				/* Get target info */
1447 				for (count = 0; count < WWN_SIZE; count++)
1448 					(void) sprintf(&vwwn[count * 2],
1449 					    "%2.2x",
1450 					    discPortAttrs.NodeWWN.wwn[count]);
1451 
1452 				if (strcmp(wwn, vwwn) == 0) {
1453 					HBA_CloseAdapter(handle);
1454 					(void) HBA_FreeLibrary();
1455 					return (HBA_STATUS_OK);
1456 				}
1457 
1458 			}
1459 		}
1460 		HBA_CloseAdapter(handle);
1461 	}
1462 	(void) HBA_FreeLibrary();
1463 	return (HBA_STATUS_ERROR_ILLEGAL_WWN);
1464 }
1465 
1466 static int
1467 light_disk_ok2remove_leds(psvc_opaque_t hdlp, boolean_t *disk_present)
1468 {
1469 	di_node_t	node;
1470 	di_node_t	root_node;
1471 	di_minor_t	min_node;
1472 	int		*prop;
1473 	int		n;
1474 	int		target;
1475 	int		rv;
1476 	int		disk_online = 0;
1477 	static int	prev_online[MAX_DISKS] = {-1, -1};
1478 	int		bit_nums[MAX_DISKS] = {4, 5};
1479 	int		bit_val;
1480 	int		count;
1481 	char		*dev_path;
1482 	char		wwn[WWN_SIZE * 2];
1483 	uchar_t		*prop_wwn;
1484 
1485 	root_node = di_init("/", DINFOCPYALL);
1486 	if (root_node == DI_NODE_NIL)
1487 		return (PSVC_FAILURE);
1488 
1489 	for (node = di_drv_first_node(DISK_DRV, root_node);
1490 		node != DI_NODE_NIL;
1491 		node = di_drv_next_node(node)) {
1492 		n = di_prop_lookup_ints(DDI_DEV_T_ANY, node, "target", &prop);
1493 		if (n == -1)
1494 			continue;
1495 		target = *prop;
1496 		if (target < 0 || target > 1)
1497 			continue;
1498 
1499 		if (! disk_present[target])
1500 			continue;
1501 
1502 		dev_path = di_devfs_path(node);
1503 		if (memcmp(dev_path, QLC_NODE, (sizeof (QLC_NODE) - 1)) != 0) {
1504 			/*
1505 			 * This isn't our FC-AL controller, so this
1506 			 * must be an external disk on Loop B.  Skip it.
1507 			 */
1508 			di_devfs_path_free(dev_path);
1509 			continue;
1510 		}
1511 		di_devfs_path_free(dev_path);
1512 
1513 		/*
1514 		 * Verify if disk is valid by checking WWN
1515 		 * because devfs retains stale data.
1516 		 */
1517 		n = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
1518 		    "node-wwn", &prop_wwn);
1519 		if (n == -1)
1520 			continue;
1521 
1522 		for (count = 0; count < WWN_SIZE; count++)
1523 			(void) sprintf(&wwn[count * 2], "%2.2x",
1524 			    prop_wwn[count]);
1525 
1526 		n = verify_disk_wwn(wwn);
1527 		if (n == HBA_STATUS_ERROR_ILLEGAL_WWN)
1528 			continue;
1529 
1530 		min_node = di_minor_next(node, DI_MINOR_NIL);
1531 		disk_online = (min_node != DI_MINOR_NIL);
1532 		if ((disk_online == 0) && (prev_online[target] == 1)) {
1533 			/* Light Led */
1534 			bit_val = 0;
1535 			rv = pcf8574_write_bit(hdlp, "DISK_PORT",
1536 				bit_nums[target], bit_val, DISKBP_MUST_BE_1);
1537 			if (rv != PSVC_SUCCESS)
1538 				goto done;
1539 		} else if ((prev_online[target] == 0) && (disk_online == 1)) {
1540 			/* Unlight Led */
1541 			bit_val = 1;
1542 			rv = pcf8574_write_bit(hdlp, "DISK_PORT",
1543 				bit_nums[target], bit_val, DISKBP_MUST_BE_1);
1544 			if (rv != PSVC_SUCCESS)
1545 				goto done;
1546 		}
1547 		if (disk_online != prev_online[target])
1548 			prev_online[target] = disk_online;
1549 	}
1550 done:
1551 	di_fini(root_node);
1552 	return (rv);
1553 }
1554 
1555 static int
1556 check_disk_fault(psvc_opaque_t hdlp, char *id, boolean_t disk_presence)
1557 {
1558 	int32_t		status = PSVC_SUCCESS;
1559 	int32_t		fault_on = 0;
1560 	char		*sensor_id;
1561 	char		disk_state[PSVC_MAX_STR_LEN];
1562 	char		state[PSVC_MAX_STR_LEN];
1563 	char		fault[PSVC_MAX_STR_LEN];
1564 	boolean_t	change_of_state = 0;
1565 
1566 	if (disk_presence != PSVC_PRESENT)
1567 		return (PSVC_SUCCESS);
1568 
1569 	status = psvc_get_attr(hdlp, id, PSVC_STATE_ATTR, disk_state);
1570 	if (status != PSVC_SUCCESS)
1571 		return (status);
1572 
1573 	status = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR,
1574 		&sensor_id, PSVC_DEV_FAULT_SENSOR, 0);
1575 	if (status != PSVC_SUCCESS)
1576 		return (status);
1577 
1578 	status = psvc_get_attr(hdlp, sensor_id, PSVC_SWITCH_STATE_ATTR, state);
1579 	if (status != PSVC_SUCCESS)
1580 		return (status);
1581 
1582 	/* Fault detected */
1583 	if (strcmp(state, PSVC_SWITCH_ON) == 0) {
1584 		strlcpy(state, PSVC_ERROR, sizeof (state));
1585 		strlcpy(fault, PSVC_GEN_FAULT, sizeof (fault));
1586 		fault_on = 1;
1587 	} else { /* No fault detected */
1588 		if (strcmp(disk_state, PSVC_OK) != 0)
1589 			change_of_state = 1;
1590 		strlcpy(state, PSVC_OK, sizeof (state));
1591 		strlcpy(fault, PSVC_NO_FAULT, sizeof (fault));
1592 	}
1593 
1594 	status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR, state);
1595 	if (status != PSVC_SUCCESS)
1596 		return (status);
1597 
1598 	status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR, fault);
1599 	if (status != PSVC_SUCCESS)
1600 		return (status);
1601 
1602 	if (fault_on) {
1603 		syslog(LOG_ERR, gettext("Fault detected: %s"), id);
1604 
1605 	} else {
1606 		if (change_of_state)
1607 			syslog(LOG_ERR, gettext("Notice: %s okay"), id);
1608 	}
1609 	return (PSVC_SUCCESS);
1610 }
1611 
1612 static int
1613 check_disk_hotplug(psvc_opaque_t hdlp, char *id, boolean_t *disk_presence,
1614 	int disk_instance)
1615 {
1616 	boolean_t	presence;
1617 	boolean_t	previous_presence;
1618 	int32_t		status = PSVC_SUCCESS;
1619 	char		label[PSVC_MAX_STR_LEN];
1620 	uint8_t		disk_leds[MAX_DISKS][2] = {{4, 6}, {5, 7}};
1621 	int	retry;
1622 
1623 	status = psvc_get_attr(hdlp, id, PSVC_PREV_PRESENCE_ATTR,
1624 		&previous_presence);
1625 	if (status != PSVC_SUCCESS)
1626 		return (status);
1627 
1628 	retry = 0;
1629 	do {
1630 		if (retry)
1631 			(void) sleep(retry_sleep_diskhp);
1632 		status = psvc_get_attr(hdlp, id, PSVC_PRESENCE_ATTR,
1633 		    &presence);
1634 		if (status != PSVC_SUCCESS)
1635 			return (status);
1636 		retry++;
1637 	} while ((retry < n_retry_diskhp) &&
1638 	    (presence != previous_presence));
1639 
1640 	*disk_presence = presence;
1641 
1642 	if (presence != previous_presence) {
1643 		char		parent_path[PICL_PROPNAMELEN_MAX];
1644 		picl_nodehdl_t	child_node;
1645 
1646 		status = psvc_get_attr(hdlp, id, PSVC_LABEL_ATTR, label);
1647 		if (status != PSVC_SUCCESS)
1648 			return (status);
1649 
1650 		/* return parent path and node for an object */
1651 		psvcplugin_lookup(id, parent_path, &child_node);
1652 
1653 		if (presence == PSVC_PRESENT) {
1654 			picl_nodehdl_t	parent_node;
1655 			char		state[PSVC_MAX_STR_LEN];
1656 			char		fault[PSVC_MAX_STR_LEN];
1657 
1658 			syslog(LOG_ERR, gettext("Device %s inserted"), label);
1659 			strlcpy(state, PSVC_OK, sizeof (state));
1660 			status = psvc_set_attr(hdlp, id, PSVC_STATE_ATTR,
1661 				state);
1662 			if (status != PSVC_SUCCESS)
1663 				return (status);
1664 
1665 			strlcpy(fault, PSVC_NO_FAULT, sizeof (fault));
1666 			status = psvc_set_attr(hdlp, id, PSVC_FAULTID_ATTR,
1667 				fault);
1668 			if (status != PSVC_SUCCESS) {
1669 				return (status);
1670 			}
1671 
1672 			status = ptree_get_node_by_path(parent_path,
1673 				&parent_node);
1674 			if (status != PICL_SUCCESS)
1675 				return (PSVC_FAILURE);
1676 			status = ptree_add_node(parent_node, child_node);
1677 			if (status != PICL_SUCCESS)
1678 				return (PSVC_FAILURE);
1679 		} else {
1680 			/*
1681 			 * Disk Removed so we need to turn off these LEDs:
1682 			 * DISKx_FLT_LED
1683 			 * DISKx_REMOVE_LED
1684 			 */
1685 			int i;
1686 			int bit_val = 1;  /* Active Low */
1687 			for (i = 0; i < 2; i++) {
1688 				status = pcf8574_write_bit(hdlp, "DISK_PORT",
1689 					disk_leds[disk_instance][i], bit_val,
1690 					DISKBP_MUST_BE_1);
1691 				if (status != PSVC_SUCCESS)
1692 					syslog(LOG_ERR, "Failed in turning off"
1693 						" %d's LEDs", id);
1694 			}
1695 			syslog(LOG_ERR, gettext("Device %s removed"), label);
1696 			ptree_delete_node(child_node);
1697 		}
1698 	}
1699 
1700 	status = psvc_set_attr(hdlp, id, PSVC_PREV_PRESENCE_ATTR, &presence);
1701 	if (status != PSVC_SUCCESS)
1702 		return (status);
1703 
1704 	return (status);
1705 }
1706 
1707 int32_t
1708 psvc_disk_monitor_policy_0(psvc_opaque_t hdlp, char *id)
1709 {
1710 	int		rv, err, i;
1711 	char		*disks[MAX_DISKS] = {"DISK0", "DISK1"};
1712 	int		saved_errno = 0;
1713 	boolean_t	disk_present[MAX_DISKS] = {0, 0};
1714 
1715 	for (i = 0; i < MAX_DISKS; i++) {
1716 		err = check_disk_hotplug(hdlp, disks[i], &disk_present[i], i);
1717 		if (err) saved_errno = errno;
1718 		rv = err;
1719 
1720 		err = check_disk_fault(hdlp, disks[i], disk_present[i]);
1721 		if (err) saved_errno = errno;
1722 		rv |= err;
1723 
1724 		err |= light_disk_fault_leds(hdlp, disks[i], disk_present[i]);
1725 		if (err) saved_errno = errno;
1726 		rv |= err;
1727 	}
1728 
1729 	err = light_disk_ok2remove_leds(hdlp, disk_present);
1730 	if (err) saved_errno = errno;
1731 	rv |= err;
1732 
1733 	errno = saved_errno;
1734 	return (rv);
1735 }
1736 
1737 /*
1738  * Read in temperature thresholds from FRU Prom and update the
1739  * default values.
1740  */
1741 
1742 #define	START_OFFSET		0x1800	/* Last 2K of SEEPROM */
1743 #define	NUM_SEG_OFFSET		0x1805	/* Number of segments */
1744 #define	SEG_TABLE_OFFSET	0x1806	/* Segment description tables */
1745 
1746 static int32_t
1747 read_sc_segment(psvc_opaque_t hdlp, char *id, char *fru_id, int offset)
1748 {
1749 	static int thresh_names[] = {
1750 		PSVC_HW_LO_SHUT_ATTR,
1751 		PSVC_LO_SHUT_ATTR,
1752 		PSVC_LO_WARN_ATTR,
1753 		PSVC_NOT_USED,			/* LOW MODE  */
1754 		PSVC_OPTIMAL_TEMP_ATTR,
1755 		PSVC_HI_WARN_ATTR,
1756 		PSVC_HI_SHUT_ATTR,
1757 		PSVC_HW_HI_SHUT_ATTR
1758 	};
1759 	int8_t		amb_temp_array[8];
1760 	int		i;
1761 	fru_info_t	fru_info;
1762 	int		err;
1763 
1764 	fru_info.buf_start = offset + 8;
1765 	fru_info.buf = amb_temp_array;
1766 	fru_info.read_size = 8;
1767 
1768 	err = psvc_get_attr(hdlp, fru_id, PSVC_FRU_INFO_ATTR, &fru_info);
1769 	if (err != PSVC_SUCCESS)
1770 		return (err);
1771 
1772 	for (i = 0; i < 8; i++) {
1773 		int32_t temp = amb_temp_array[i];
1774 		if (thresh_names[i] == PSVC_NOT_USED)
1775 			continue;
1776 		err = psvc_set_attr(hdlp, id, thresh_names[i], &temp);
1777 		if (err != PSVC_SUCCESS)
1778 			return (err);
1779 	}
1780 	return (PSVC_SUCCESS);
1781 }
1782 
1783 int32_t
1784 update_disk_bp_temp_thresholds(psvc_opaque_t hdlp, char *id)
1785 {
1786 
1787 	char		*fru;
1788 	fru_info_t	fru_info;
1789 	int16_t		seg_offset;
1790 	int8_t		byte;
1791 	int8_t		seg_count;
1792 	char		seg_name[2];
1793 	int		current_offset, i, err;
1794 
1795 	err = psvc_get_attr(hdlp, id, PSVC_ASSOC_ID_ATTR, &fru, PSVC_FRU, 0);
1796 	if (err != PSVC_SUCCESS)
1797 		return (err);
1798 
1799 	/* Sanity Check */
1800 	fru_info.buf_start = START_OFFSET;
1801 	fru_info.buf = &byte;
1802 	fru_info.read_size = 1;
1803 
1804 	err = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR, &fru_info);
1805 	if (err != PSVC_SUCCESS)
1806 		return (err);
1807 	if (*fru_info.buf != 8) {
1808 		syslog(LOG_ERR, "Notice: FRU Prom %s not programmed", fru);
1809 	}
1810 	/* Should do CRC Check on fru */
1811 
1812 	/* Get Segment Count */
1813 	fru_info.buf_start = NUM_SEG_OFFSET;
1814 	fru_info.buf = &seg_count;
1815 	fru_info.read_size = 1;
1816 
1817 	err = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR, &fru_info);
1818 	if (err != PSVC_SUCCESS)
1819 		return (err);
1820 
1821 	current_offset = SEG_TABLE_OFFSET;
1822 	for (i = 0; i < seg_count; i++) {
1823 		fru_info.buf_start = current_offset;
1824 		fru_info.buf = seg_name;
1825 		fru_info.read_size = 2;
1826 		err = psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR, &fru_info);
1827 		if (err != PSVC_SUCCESS)
1828 			return (err);
1829 
1830 		if (memcmp(seg_name, "SC", 2) == 0) {
1831 			current_offset += 6;	/* Skip over description */
1832 			fru_info.buf_start = current_offset;
1833 			fru_info.buf = (char *)&seg_offset;
1834 			fru_info.read_size = 2;
1835 			psvc_get_attr(hdlp, fru, PSVC_FRU_INFO_ATTR,
1836 				&fru_info);
1837 			return (read_sc_segment(hdlp, id, fru, seg_offset));
1838 		}
1839 		current_offset += 10;
1840 	}
1841 
1842 	return (PSVC_SUCCESS);
1843 }
1844