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