diff options
author | Jane Chu <Jane.Chu@Sun.COM> | 2009-07-20 10:00:36 -0700 |
---|---|---|
committer | Jane Chu <Jane.Chu@Sun.COM> | 2009-07-20 10:00:36 -0700 |
commit | 2ee4dfc799525d35706ee64a901cd970a7080bb6 (patch) | |
tree | 3e498f3775dd50b9f96f965f90e405dc06ee8985 | |
parent | 0ba964ea1a9461d43c8e39e5a9a3ab857e6786c7 (diff) | |
download | illumos-joyent-2ee4dfc799525d35706ee64a901cd970a7080bb6.tar.gz |
PSARC/2009/310 Disk IO PM Enhancement
6832245 "pm-capable" property updates
6832232 enhanced sd(7D) to support power condition field in Start-Stop Unit command
6781588 sata(7D) to support additional Mode Sense and Log Sense for PM
6832247 sata(7D) enhancement for SBC-3 power conditions and ATA power conditions translation
6827942 pm_trans_check enhancement
-rw-r--r-- | usr/src/uts/common/io/sata/impl/sata.c | 985 | ||||
-rw-r--r-- | usr/src/uts/common/io/scsi/targets/sd.c | 595 | ||||
-rw-r--r-- | usr/src/uts/common/os/sunpm.c | 13 | ||||
-rw-r--r-- | usr/src/uts/common/sys/sata/impl/sata.h | 50 | ||||
-rw-r--r-- | usr/src/uts/common/sys/sata/sata_defs.h | 49 | ||||
-rw-r--r-- | usr/src/uts/common/sys/scsi/generic/inquiry.h | 20 | ||||
-rw-r--r-- | usr/src/uts/common/sys/scsi/generic/mode.h | 87 | ||||
-rw-r--r-- | usr/src/uts/common/sys/scsi/impl/uscsi.h | 10 | ||||
-rw-r--r-- | usr/src/uts/common/sys/scsi/targets/sddef.h | 117 | ||||
-rw-r--r-- | usr/src/uts/common/sys/sunpm.h | 14 |
10 files changed, 1554 insertions, 386 deletions
diff --git a/usr/src/uts/common/io/sata/impl/sata.c b/usr/src/uts/common/io/sata/impl/sata.c index 60e6c6bbd4..ff165291bf 100644 --- a/usr/src/uts/common/io/sata/impl/sata.c +++ b/usr/src/uts/common/io/sata/impl/sata.c @@ -189,29 +189,30 @@ static int sata_txlt_test_unit_ready(sata_pkt_txlate_t *); static int sata_txlt_start_stop_unit(sata_pkt_txlate_t *); static int sata_txlt_read_capacity(sata_pkt_txlate_t *); static int sata_txlt_request_sense(sata_pkt_txlate_t *); -static int sata_txlt_read(sata_pkt_txlate_t *); -static int sata_txlt_write(sata_pkt_txlate_t *); -static int sata_txlt_log_sense(sata_pkt_txlate_t *); -static int sata_txlt_log_select(sata_pkt_txlate_t *); -static int sata_txlt_mode_sense(sata_pkt_txlate_t *); -static int sata_txlt_mode_select(sata_pkt_txlate_t *); -static int sata_txlt_synchronize_cache(sata_pkt_txlate_t *); -static int sata_txlt_write_buffer(sata_pkt_txlate_t *); -static int sata_txlt_nodata_cmd_immediate(sata_pkt_txlate_t *); - -static int sata_hba_start(sata_pkt_txlate_t *, int *); +static int sata_txlt_read(sata_pkt_txlate_t *); +static int sata_txlt_write(sata_pkt_txlate_t *); +static int sata_txlt_log_sense(sata_pkt_txlate_t *); +static int sata_txlt_log_select(sata_pkt_txlate_t *); +static int sata_txlt_mode_sense(sata_pkt_txlate_t *); +static int sata_txlt_mode_select(sata_pkt_txlate_t *); +static int sata_txlt_synchronize_cache(sata_pkt_txlate_t *); +static int sata_txlt_write_buffer(sata_pkt_txlate_t *); +static int sata_txlt_nodata_cmd_immediate(sata_pkt_txlate_t *); + +static int sata_hba_start(sata_pkt_txlate_t *, int *); static int sata_txlt_invalid_command(sata_pkt_txlate_t *); +static int sata_txlt_check_condition(sata_pkt_txlate_t *, uchar_t, uchar_t); static int sata_txlt_lba_out_of_range(sata_pkt_txlate_t *); -static void sata_txlt_rw_completion(sata_pkt_t *); -static void sata_txlt_nodata_cmd_completion(sata_pkt_t *); -static void sata_txlt_download_mcode_cmd_completion(sata_pkt_t *); -static int sata_emul_rw_completion(sata_pkt_txlate_t *); -static struct scsi_extended_sense *sata_immediate_error_response( +static void sata_txlt_rw_completion(sata_pkt_t *); +static void sata_txlt_nodata_cmd_completion(sata_pkt_t *); +static void sata_txlt_download_mcode_cmd_completion(sata_pkt_t *); +static int sata_emul_rw_completion(sata_pkt_txlate_t *); +static struct scsi_extended_sense *sata_immediate_error_response( sata_pkt_txlate_t *, int); static struct scsi_extended_sense *sata_arq_sense(sata_pkt_txlate_t *); -static int sata_txlt_atapi(sata_pkt_txlate_t *); -static void sata_txlt_atapi_completion(sata_pkt_t *); +static int sata_txlt_atapi(sata_pkt_txlate_t *); +static void sata_txlt_atapi_completion(sata_pkt_t *); /* * Local functions for ioctl @@ -291,11 +292,13 @@ static int sata_build_msense_page_1(sata_drive_info_t *, int, uint8_t *); static int sata_build_msense_page_8(sata_drive_info_t *, int, uint8_t *); static int sata_build_msense_page_1a(sata_drive_info_t *, int, uint8_t *); static int sata_build_msense_page_1c(sata_drive_info_t *, int, uint8_t *); +static int sata_build_msense_page_30(sata_drive_info_t *, int, uint8_t *); static int sata_mode_select_page_8(sata_pkt_txlate_t *, struct mode_cache_scsi3 *, int, int *, int *, int *); +static int sata_mode_select_page_1a(sata_pkt_txlate_t *, + struct mode_info_power_cond *, int, int *, int *, int *); static int sata_mode_select_page_1c(sata_pkt_txlate_t *, struct mode_info_excpt_page *, int, int *, int *, int *); -static int sata_build_msense_page_30(sata_drive_info_t *, int, uint8_t *); static int sata_mode_select_page_30(sata_pkt_txlate_t *, struct mode_acoustic_management *, int, int *, int *, int *); @@ -306,6 +309,14 @@ static int sata_build_lsense_page_2f(sata_drive_info_t *, uint8_t *, sata_hba_inst_t *); static int sata_build_lsense_page_30(sata_drive_info_t *, uint8_t *, sata_hba_inst_t *); +static int sata_build_lsense_page_0e(sata_drive_info_t *, uint8_t *, + sata_pkt_txlate_t *); + +static void sata_set_arq_data(sata_pkt_t *); +static void sata_build_read_verify_cmd(sata_cmd_t *, uint16_t, uint64_t); +static void sata_build_generic_cmd(sata_cmd_t *, uint8_t); +static uint8_t sata_get_standby_timer(uint8_t *timer); + static void sata_save_drive_settings(sata_drive_info_t *); static void sata_show_drive_info(sata_hba_inst_t *, sata_drive_info_t *); static void sata_log(sata_hba_inst_t *, uint_t, char *fmt, ...); @@ -1809,8 +1820,11 @@ sata_scsi_tgt_probe(struct scsi_device *sd, int (*callback)(void)) sata_hba_inst_t *sata_hba_inst = (sata_hba_inst_t *)(sd->sd_address.a_hba_tran->tran_hba_private); int rval; + uint32_t pm_cap; rval = scsi_hba_probe(sd, callback); + pm_cap = SATA_CAP_POWER_CONDITON | SATA_CAP_SMART_PAGE | + SATA_CAP_LOG_SENSE; if (rval == SCSIPROBE_EXISTS) { /* @@ -1819,7 +1833,7 @@ sata_scsi_tgt_probe(struct scsi_device *sd, int (*callback)(void)) * before enabling device power-management. */ if ((ddi_prop_update_int(DDI_DEV_T_NONE, sd->sd_dev, - "pm-capable", 1)) != DDI_PROP_SUCCESS) { + "pm-capable", pm_cap)) != DDI_PROP_SUCCESS) { sata_log(sata_hba_inst, CE_WARN, "SATA device at port %d: " "will not be power-managed ", @@ -2163,6 +2177,20 @@ sata_scsi_start(struct scsi_address *ap, struct scsi_pkt *pkt) } mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport))); + /* + * Checking for power state, if it was on + * STOPPED state, then the drive is not capable + * of processing media access command. And + * TEST_UNIT_READY, REQUEST_SENSE has special handling + * in the function for different power state. + */ + if (((sdinfo->satadrv_power_level == SATA_POWER_STANDBY) || + (sdinfo->satadrv_power_level == SATA_POWER_STOPPED)) && + (SATA_IS_MEDIUM_ACCESS_CMD(pkt->pkt_cdbp[0]))) { + return (sata_txlt_check_condition(spx, KEY_NOT_READY, + SD_SCSI_ASC_LU_NOT_READY)); + } + /* ATA Disk commands processing starts here */ bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp; @@ -3027,12 +3055,53 @@ sata_txlt_invalid_command(sata_pkt_txlate_t *spx) } /* + * Scsi response set up for check condition with special sense key + * and additional sense code. + * + * Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields. + */ +static int +sata_txlt_check_condition(sata_pkt_txlate_t *spx, uchar_t key, uchar_t code) +{ + sata_hba_inst_t *shi = SATA_TXLT_HBA_INST(spx); + int cport = SATA_TXLT_CPORT(spx); + struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; + struct scsi_extended_sense *sense; + + mutex_enter(&SATA_CPORT_MUTEX(shi, cport)); + scsipkt->pkt_reason = CMD_CMPLT; + scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | + STATE_SENT_CMD | STATE_GOT_STATUS; + + *scsipkt->pkt_scbp = STATUS_CHECK; + + sense = sata_arq_sense(spx); + sense->es_key = key; + sense->es_add_code = code; + + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + + SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, + "Scsi_pkt completion reason %x\n", scsipkt->pkt_reason); + + if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0) + /* scsi callback required */ + if (taskq_dispatch(SATA_TXLT_TASKQ(spx), + (task_func_t *)scsi_hba_pkt_comp, + (void *)spx->txlt_scsi_pkt, + TQ_SLEEP) == NULL) + /* Scheduling the callback failed */ + return (TRAN_BUSY); + return (TRAN_ACCEPT); +} + +/* * Scsi response setup for * emulated non-data command that requires no action/return data * * Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields. */ -static int +static int sata_txlt_nodata_cmd_immediate(sata_pkt_txlate_t *spx) { int rval; @@ -3289,10 +3358,10 @@ done: /* * SATA translate command: Request Sense. - * Emulated command (ATA version for SATA hard disks) - * Always NO SENSE, because any sense data should be reported by ARQ sense. * * Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields. + * At the moment this is an emulated command (ATA version for SATA hard disks). + * May be translated into Check Power Mode command in the future. * * Note: There is a mismatch between already implemented Informational * Exception Mode Select page 0x1C and this function. @@ -3306,7 +3375,9 @@ sata_txlt_request_sense(sata_pkt_txlate_t *spx) struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; struct scsi_extended_sense sense; struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp; - int rval, reason; + sata_drive_info_t *sdinfo; + sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd; + int rval, reason, power_state = 0; mutex_enter(&(SATA_TXLT_CPORT_MUTEX(spx))); @@ -3315,14 +3386,68 @@ sata_txlt_request_sense(sata_pkt_txlate_t *spx) mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); return (rval); } - mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); - scsipkt->pkt_reason = CMD_CMPLT; scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD | STATE_GOT_STATUS; *scsipkt->pkt_scbp = STATUS_GOOD; + /* + * when CONTROL field's NACA bit == 1 + * return ILLEGAL_REQUEST + */ + if (scsipkt->pkt_cdbp[5] & CTL_BYTE_NACA_MASK) { + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + return (sata_txlt_check_condition(spx, KEY_ILLEGAL_REQUEST, + SD_SCSI_ASC_CMD_SEQUENCE_ERR)); + } + + sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst, + &spx->txlt_sata_pkt->satapkt_device); + ASSERT(sdinfo != NULL); + + spx->txlt_sata_pkt->satapkt_op_mode = SATA_OPMODE_SYNCH; + + sata_build_generic_cmd(scmd, SATAC_CHECK_POWER_MODE); + scmd->satacmd_flags.sata_copy_out_sec_count_lsb = B_TRUE; + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + return (sata_txlt_check_condition(spx, KEY_NO_SENSE, + SD_SCSI_ASC_NO_ADD_SENSE)); + } + } + + switch (scmd->satacmd_sec_count_lsb) { + case SATA_PWRMODE_STANDBY: /* device in standby mode */ + if (sdinfo->satadrv_power_level == SATA_POWER_STOPPED) + power_state = SATA_POWER_STOPPED; + else { + power_state = SATA_POWER_STANDBY; + sdinfo->satadrv_power_level = SATA_POWER_STANDBY; + } + break; + case SATA_PWRMODE_IDLE: /* device in idle mode */ + power_state = SATA_POWER_IDLE; + sdinfo->satadrv_power_level = SATA_POWER_IDLE; + break; + case SATA_PWRMODE_ACTIVE: /* device in active or idle mode */ + default: /* 0x40, 0x41 active mode */ + if (sdinfo->satadrv_power_level == SATA_POWER_IDLE) + power_state = SATA_POWER_IDLE; + else { + power_state = SATA_POWER_ACTIVE; + sdinfo->satadrv_power_level = SATA_POWER_ACTIVE; + } + break; + } + + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + if (bp != NULL && bp->b_un.b_addr && bp->b_bcount) { /* * Because it is fully emulated command storing data @@ -3342,17 +3467,29 @@ sata_txlt_request_sense(sata_pkt_txlate_t *spx) bcopy(&sense, bp->b_un.b_addr, count); scsipkt->pkt_state |= STATE_XFERRED_DATA; scsipkt->pkt_resid = 0; + switch (power_state) { + case SATA_POWER_IDLE: + case SATA_POWER_STANDBY: + sense.es_add_code = + SD_SCSI_ASC_LOW_POWER_CONDITION_ON; + break; + case SATA_POWER_STOPPED: + sense.es_add_code = SD_SCSI_ASC_NO_ADD_SENSE; + break; + case SATA_POWER_ACTIVE: + default: + break; + } } SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, "Scsi_pkt completion reason %x\n", scsipkt->pkt_reason); - if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 && - scsipkt->pkt_comp != NULL) + if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0) /* scsi callback required */ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), - (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt, + (task_func_t *)scsi_hba_pkt_comp, (void *) scsipkt, TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); @@ -3371,6 +3508,8 @@ sata_txlt_test_unit_ready(sata_pkt_txlate_t *spx) { struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; struct scsi_extended_sense *sense; + sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd; + sata_drive_info_t *sdinfo; int power_state; int rval, reason; @@ -3381,37 +3520,59 @@ sata_txlt_test_unit_ready(sata_pkt_txlate_t *spx) mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); return (rval); } - mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); - /* At this moment, emulate it rather than execute anything */ - power_state = SATA_PWRMODE_ACTIVE; + sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst, + &spx->txlt_sata_pkt->satapkt_device); + ASSERT(sdinfo != NULL); + + spx->txlt_sata_pkt->satapkt_op_mode = SATA_OPMODE_SYNCH; - scsipkt->pkt_reason = CMD_CMPLT; - scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | - STATE_SENT_CMD | STATE_GOT_STATUS; + /* send CHECK POWER MODE command */ + sata_build_generic_cmd(scmd, SATAC_CHECK_POWER_MODE); + scmd->satacmd_flags.sata_copy_out_sec_count_lsb = B_TRUE; + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + return (sata_txlt_check_condition(spx, KEY_NOT_READY, + SD_SCSI_ASC_LU_NOT_RESPONSE)); + } + } - switch (power_state) { - case SATA_PWRMODE_ACTIVE: - case SATA_PWRMODE_IDLE: - *scsipkt->pkt_scbp = STATUS_GOOD; - break; - default: - /* PWR mode standby */ + power_state = scmd->satacmd_sec_count_lsb; + + /* + * return NOT READY when device in STOPPED mode + */ + if (power_state == SATA_PWRMODE_STANDBY && + sdinfo->satadrv_power_level == SATA_POWER_STOPPED) { *scsipkt->pkt_scbp = STATUS_CHECK; sense = sata_arq_sense(spx); sense->es_key = KEY_NOT_READY; sense->es_add_code = SD_SCSI_ASC_LU_NOT_READY; - break; + } else { + /* + * For other power mode, return GOOD status + */ + *scsipkt->pkt_scbp = STATUS_GOOD; } + scsipkt->pkt_reason = CMD_CMPLT; + scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | + STATE_SENT_CMD | STATE_GOT_STATUS; + + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, "Scsi_pkt completion reason %x\n", scsipkt->pkt_reason); - if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 && - scsipkt->pkt_comp != NULL) + if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0) /* scsi callback required */ if (taskq_dispatch(SATA_TXLT_TASKQ(spx), - (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt, + (task_func_t *)scsi_hba_pkt_comp, (void *) scsipkt, TQ_SLEEP) == NULL) /* Scheduling the callback failed */ return (TRAN_BUSY); @@ -3419,16 +3580,26 @@ sata_txlt_test_unit_ready(sata_pkt_txlate_t *spx) return (TRAN_ACCEPT); } - /* * SATA translate command: Start Stop Unit * Translation depends on a command: - * Start Unit translated into Idle Immediate - * Stop Unit translated into Standby Immediate + * + * Power condition bits will be supported + * and the power level should be maintained by SATL, + * When SATL received a command, it will check the + * power level firstly, and return the status according + * to SAT2 v2.6 and SAT-2 Standby Modifications + * + * SPC-4/SBC-3 SATL ATA power condition SATL SPC/SBC + * ----------------------------------------------------------------------- + * SSU_PC1 Active <==> ATA Active <==> SSU:start_bit =1 + * SSU_PC2 Idle <==> ATA Idle <==> N/A + * SSU_PC3 Standby <==> ATA Standby <==> N/A + * SSU_PC4 Stopped <==> ATA Standby <==> SSU:start_bit = 0 + * * Unload Media / NOT SUPPORTED YET * Load Media / NOT SUPPROTED YET - * Power condition bits are ignored, so is Immediate bit - * Requesting synchronous execution. + * Immediate bit / NOT SUPPORTED YET (deferred error) * * Returns TRAN_ACCEPT or code returned by sata_hba_start() and * appropriate values in scsi_pkt fields. @@ -3438,11 +3609,11 @@ sata_txlt_start_stop_unit(sata_pkt_txlate_t *spx) { struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd; - struct scsi_extended_sense *sense; sata_hba_inst_t *shi = SATA_TXLT_HBA_INST(spx); int cport = SATA_TXLT_CPORT(spx); int rval, reason; - int synch; + sata_drive_info_t *sdinfo; + sata_id_t *sata_id; SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, "sata_txlt_start_stop_unit: %d\n", scsipkt->pkt_scbp[4] & 1); @@ -3455,81 +3626,304 @@ sata_txlt_start_stop_unit(sata_pkt_txlate_t *spx) return (rval); } - if (scsipkt->pkt_cdbp[4] & 2) { - /* Load/Unload Media - invalid request */ - *scsipkt->pkt_scbp = STATUS_CHECK; - sense = sata_arq_sense(spx); - sense->es_key = KEY_ILLEGAL_REQUEST; - sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB; - mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); - - SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, - "Scsi_pkt completion reason %x\n", scsipkt->pkt_reason); - - if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 && - scsipkt->pkt_comp != NULL) - /* scsi callback required */ - if (taskq_dispatch(SATA_TXLT_TASKQ(spx), - (task_func_t *)scsipkt->pkt_comp, (void *) scsipkt, - TQ_SLEEP) == NULL) - /* Scheduling the callback failed */ - return (TRAN_BUSY); - - return (TRAN_ACCEPT); - } - scmd->satacmd_addr_type = 0; - scmd->satacmd_sec_count_lsb = 0; - scmd->satacmd_lba_low_lsb = 0; - scmd->satacmd_lba_mid_lsb = 0; - scmd->satacmd_lba_high_lsb = 0; - scmd->satacmd_features_reg = 0; - scmd->satacmd_device_reg = 0; - scmd->satacmd_status_reg = 0; - if (scsipkt->pkt_cdbp[4] & 1) { - /* Start Unit */ - scmd->satacmd_cmd_reg = SATAC_IDLE_IM; - } else { - /* Stop Unit */ - scmd->satacmd_cmd_reg = SATAC_STANDBY_IM; + if (scsipkt->pkt_cdbp[1] & START_STOP_IMMED_MASK) { + /* IMMED bit - not supported */ + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (sata_txlt_check_condition(spx, KEY_ILLEGAL_REQUEST, + SD_SCSI_ASC_INVALID_FIELD_IN_CDB)); } - if (!(spx->txlt_sata_pkt->satapkt_op_mode & SATA_OPMODE_SYNCH)) { - /* Need to set-up a callback function */ - spx->txlt_sata_pkt->satapkt_comp = - sata_txlt_nodata_cmd_completion; - synch = FALSE; - } else { - spx->txlt_sata_pkt->satapkt_op_mode = SATA_OPMODE_SYNCH; - synch = TRUE; - } + spx->txlt_sata_pkt->satapkt_op_mode = SATA_OPMODE_SYNCH; + spx->txlt_sata_pkt->satapkt_comp = NULL; - /* Transfer command to HBA */ - if (sata_hba_start(spx, &rval) != 0) { - /* Pkt not accepted for execution */ + sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst, + &spx->txlt_sata_pkt->satapkt_device); + ASSERT(sdinfo != NULL); + sata_id = &sdinfo->satadrv_id; + + switch ((scsipkt->pkt_cdbp[4] & START_STOP_POWER_COND_MASK) >> 4) { + case 0: + if (scsipkt->pkt_cdbp[4] & START_STOP_LOEJ_MASK) { + /* Load/Unload Media - invalid request */ + goto err_out; + } + if (scsipkt->pkt_cdbp[4] & START_STOP_START_MASK) { + /* Start Unit */ + sata_build_read_verify_cmd(scmd, 1, 5); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + /* Transfer command to HBA */ + if (sata_hba_start(spx, &rval) != 0) { + /* Pkt not accepted for execution */ + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + sdinfo->satadrv_power_level = SATA_POWER_ACTIVE; + } else { + /* Stop Unit */ + sata_build_generic_cmd(scmd, SATAC_FLUSH_CACHE); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + /* ata standby immediate command */ + sata_build_generic_cmd(scmd, SATAC_STANDBY_IM); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + sdinfo->satadrv_power_level = SATA_POWER_STOPPED; + } + break; + case 0x1: + sata_build_generic_cmd(scmd, SATAC_IDLE); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + sata_build_read_verify_cmd(scmd, 1, 5); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + /* Transfer command to HBA */ + if (sata_hba_start(spx, &rval) != 0) { + /* Pkt not accepted for execution */ + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + sdinfo->satadrv_power_level = SATA_POWER_ACTIVE; + break; + case 0x2: + sata_build_generic_cmd(scmd, SATAC_FLUSH_CACHE); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (!(scsipkt->pkt_cdbp[4] & START_STOP_NOFLUSH_MASK)) { + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + } + sata_build_generic_cmd(scmd, SATAC_IDLE); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + if ((scsipkt->pkt_cdbp[3] & START_STOP_MODIFIER_MASK)) { + /* + * POWER CONDITION MODIFIER bit set + * to 0x1 or larger it will be handled + * on the same way as bit = 0x1 + */ + if (!(sata_id->ai_cmdset84 & + SATA_IDLE_UNLOAD_SUPPORTED)) { + sdinfo->satadrv_power_level = SATA_POWER_IDLE; + break; + } + sata_build_generic_cmd(scmd, SATAC_IDLE_IM); + scmd->satacmd_features_reg = 0x44; + scmd->satacmd_lba_low_lsb = 0x4c; + scmd->satacmd_lba_mid_lsb = 0x4e; + scmd->satacmd_lba_high_lsb = 0x55; + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + } + sdinfo->satadrv_power_level = SATA_POWER_IDLE; + break; + case 0x3: + sata_build_generic_cmd(scmd, SATAC_FLUSH_CACHE); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (!(scsipkt->pkt_cdbp[4] & START_STOP_NOFLUSH_MASK)) { + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + } + sata_build_generic_cmd(scmd, SATAC_STANDBY); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + sdinfo->satadrv_power_level = SATA_POWER_STANDBY; + break; + case 0x7: + sata_build_generic_cmd(scmd, SATAC_CHECK_POWER_MODE); + scmd->satacmd_flags.sata_copy_out_sec_count_lsb = B_TRUE; + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + switch (scmd->satacmd_sec_count_lsb) { + case SATA_PWRMODE_STANDBY: + sata_build_generic_cmd(scmd, SATAC_STANDBY); + scmd->satacmd_sec_count_msb = sata_get_standby_timer( + sdinfo->satadrv_standby_timer); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + break; + case SATA_PWRMODE_IDLE: + sata_build_generic_cmd(scmd, SATAC_IDLE); + scmd->satacmd_sec_count_msb = sata_get_standby_timer( + sdinfo->satadrv_standby_timer); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + break; + case SATA_PWRMODE_ACTIVE_SPINDOWN: + case SATA_PWRMODE_ACTIVE_SPINUP: + case SATA_PWRMODE_ACTIVE: + sata_build_generic_cmd(scmd, SATAC_IDLE); + scmd->satacmd_sec_count_msb = sata_get_standby_timer( + sdinfo->satadrv_standby_timer); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + sata_build_read_verify_cmd(scmd, 1, 5); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + break; + default: + goto err_out; + } + break; + case 0xb: + if ((sata_get_standby_timer(sdinfo->satadrv_standby_timer) == + 0) || (!(sata_id->ai_cap & SATA_STANDBYTIMER))) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (sata_txlt_check_condition(spx, + KEY_ILLEGAL_REQUEST, + SD_SCSI_ASC_INVALID_FIELD_IN_CDB)); + } + sata_build_generic_cmd(scmd, SATAC_FLUSH_CACHE); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (!(scsipkt->pkt_cdbp[4] & START_STOP_NOFLUSH_MASK)) { + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + sata_build_generic_cmd(scmd, SATAC_STANDBY_IM); + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + if (sata_hba_start(spx, &rval) != 0) { + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + return (rval); + } else { + if (scmd->satacmd_error_reg != 0) { + goto err_out; + } + } + } + bzero(sdinfo->satadrv_standby_timer, sizeof (uchar_t) * 4); + break; + default: +err_out: mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); - return (rval); + return (sata_txlt_check_condition(spx, KEY_ILLEGAL_REQUEST, + SD_SCSI_ASC_INVALID_FIELD_IN_CDB)); } /* - * If execution is non-synchronous, - * a callback function will handle potential errors, translate - * the response and will do a callback to a target driver. - * If it was synchronous, check execution status using the same - * framework callback. + * since it was synchronous commands, + * a callback function will be called directely. */ mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); - if (synch) { - SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, - "synchronous execution status %x\n", - spx->txlt_sata_pkt->satapkt_reason); + SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, + "synchronous execution status %x\n", + spx->txlt_sata_pkt->satapkt_reason); - sata_txlt_nodata_cmd_completion(spx->txlt_sata_pkt); + if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0) { + sata_set_arq_data(spx->txlt_sata_pkt); + if (taskq_dispatch(SATA_TXLT_TASKQ(spx), + (task_func_t *)scsi_hba_pkt_comp, (void *) scsipkt, + TQ_SLEEP) == 0) { + return (TRAN_BUSY); + } } + else + + sata_txlt_nodata_cmd_completion(spx->txlt_sata_pkt); + return (TRAN_ACCEPT); } - /* * SATA translate command: Read Capacity. * Emulated command for SATA disks. @@ -3681,7 +4075,7 @@ sata_txlt_mode_sense(sata_pkt_txlate_t *spx) /* Build mode parameter header */ if (spx->txlt_scsi_pkt->pkt_cdbp[0] == SCMD_MODE_SENSE) { /* 4-byte mode parameter header */ - buf[len++] = 0; /* mode data length */ + buf[len++] = 0; /* mode data length */ buf[len++] = 0; /* medium type */ buf[len++] = 0; /* dev-specific param */ buf[len++] = bdlen; /* Block Descriptor length */ @@ -3837,7 +4231,7 @@ sata_txlt_mode_sense(sata_pkt_txlate_t *spx) /* fix total mode data length */ if (spx->txlt_scsi_pkt->pkt_cdbp[0] == SCMD_MODE_SENSE) { /* 4-byte mode parameter header */ - buf[0] = len - 1; /* mode data length */ + buf[0] = len - 1; /* mode data length */ } else { buf[0] = (len -2) >> 8; buf[1] = (len -2) & 0xff; @@ -3902,6 +4296,7 @@ done: * - caching page * - informational exception page * - acoustic management page + * - power condition page * Caching setup is remembered so it could be re-stored in case of * an unexpected device reset. * @@ -4101,6 +4496,30 @@ sata_txlt_mode_select(sata_pkt_txlate_t *spx) } break; + case MODEPAGE_POWER_COND: + stat = sata_mode_select_page_1a(spx, + (struct mode_info_power_cond *)&buf[len], + pllen, &pagelen, &rval, &dmod); + /* + * The pagelen value indicates the number of + * parameter bytes already processed. + * The rval is the return value from + * sata_tran_start(). + * The stat indicates the overall status of + * the operation(s). + */ + if (stat != SATA_SUCCESS) + /* + * Page processing did not succeed - + * all error info is already set-up, + * just return + */ + pllen = 0; /* this breaks the loop */ + else { + len += pagelen; + pllen -= pagelen; + } + break; default: *scsipkt->pkt_scbp = STATUS_CHECK; sense = sata_arq_sense(spx); @@ -4261,6 +4680,7 @@ sata_txlt_log_sense(sata_pkt_txlate_t *spx) case PAGE_CODE_SELF_TEST_RESULTS: case PAGE_CODE_INFORMATION_EXCEPTIONS: case PAGE_CODE_SMART_READ_DATA: + case PAGE_CODE_START_STOP_CYCLE_COUNTER: break; default: *scsipkt->pkt_scbp = STATUS_CHECK; @@ -4371,6 +4791,30 @@ sata_txlt_log_sense(sata_pkt_txlate_t *spx) len = sata_build_lsense_page_30(sdinfo, buf, spx->txlt_sata_hba_inst); goto no_header; + case PAGE_CODE_START_STOP_CYCLE_COUNTER: + sata_id = &sdinfo->satadrv_id; + if (! (sata_id->ai_cmdset82 & SATA_SMART_SUPPORTED)) { + *scsipkt->pkt_scbp = STATUS_CHECK; + sense = sata_arq_sense(spx); + sense->es_key = KEY_ILLEGAL_REQUEST; + sense->es_add_code = + SD_SCSI_ASC_INVALID_FIELD_IN_CDB; + + goto done; + } + if (! (sata_id->ai_features85 & SATA_SMART_ENABLED)) { + *scsipkt->pkt_scbp = STATUS_CHECK; + sense = sata_arq_sense(spx); + sense->es_key = KEY_ABORTED_COMMAND; + sense->es_add_code = + SCSI_ASC_ATA_DEV_FEAT_NOT_ENABLED; + sense->es_qual_code = + SCSI_ASCQ_ATA_DEV_FEAT_NOT_ENABLED; + + goto done; + } + len = sata_build_lsense_page_0e(sdinfo, buf, spx); + goto no_header; default: /* Invalid request */ *scsipkt->pkt_scbp = STATUS_CHECK; @@ -4429,7 +4873,7 @@ done: * Translate command: Log Select * Not implemented at this time - returns invalid command response. */ -static int +static int sata_txlt_log_select(sata_pkt_txlate_t *spx) { SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, @@ -5846,12 +6290,26 @@ sata_txlt_rw_completion(sata_pkt_t *sata_pkt) * This function may be used only if scsi_pkt is non-NULL. */ -static void +static void sata_txlt_nodata_cmd_completion(sata_pkt_t *sata_pkt) { sata_pkt_txlate_t *spx = (sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private; struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; + + sata_set_arq_data(sata_pkt); + + if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0) + /* scsi callback required */ + scsi_hba_pkt_comp(scsipkt); +} + +static void +sata_set_arq_data(sata_pkt_t *sata_pkt) +{ + sata_pkt_txlate_t *spx = + (sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private; + struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; struct scsi_extended_sense *sense; scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | @@ -5917,10 +6375,6 @@ sata_txlt_nodata_cmd_completion(sata_pkt_t *sata_pkt) } SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, "Scsi_pkt completion reason %x\n", scsipkt->pkt_reason); - - if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0) - /* scsi callback required */ - scsi_hba_pkt_comp(scsipkt); } @@ -6097,20 +6551,36 @@ sata_build_msense_page_30(sata_drive_info_t *sdinfo, int pcntrl, uint8_t *buf) /* - * Build Mode sense power condition page - * NOT IMPLEMENTED. + * Build Mode sense power condition page. */ static int sata_build_msense_page_1a(sata_drive_info_t *sdinfo, int pcntrl, uint8_t *buf) { -#ifndef __lock_lint - _NOTE(ARGUNUSED(sdinfo)) - _NOTE(ARGUNUSED(pcntrl)) - _NOTE(ARGUNUSED(buf)) -#endif - return (0); -} + struct mode_info_power_cond *page = (struct mode_info_power_cond *)buf; + sata_id_t *sata_id = &sdinfo->satadrv_id; + + /* + * Most of the fields are set to 0, being not supported and/or disabled + * power condition page length was 0x0a + */ + bzero(buf, sizeof (struct mode_info_power_cond)); + + if (pcntrl == P_CNTRL_DEFAULT) { + /* default paramters not supported */ + return (0); + } + + page->mode_page.code = MODEPAGE_POWER_COND; + page->mode_page.length = sizeof (struct mode_info_power_cond); + if (sata_id->ai_cap && SATA_STANDBYTIMER) { + page->standby = 1; + bcopy(sdinfo->satadrv_standby_timer, page->standby_cond_timer, + sizeof (uchar_t) * 4); + } + + return (sizeof (struct mode_info_power_cond)); +} /* * Process mode select caching page 8 (scsi3 format only). @@ -6468,16 +6938,96 @@ sata_mode_select_page_30(sata_pkt_txlate_t *spx, struct return (SATA_SUCCESS); } +/* + * Process mode select power condition page 0x1a + * + * This function has to be called with a port mutex held. + * + * Returns SATA_SUCCESS if operation was successful, SATA_FAILURE otherwise. + * + * Cannot be called in the interrupt context. + */ +int +sata_mode_select_page_1a(sata_pkt_txlate_t *spx, struct + mode_info_power_cond *page, int parmlen, int *pagelen, + int *rval, int *dmod) +{ + struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; + sata_drive_info_t *sdinfo; + sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd; + sata_id_t *sata_id; + struct scsi_extended_sense *sense; + uint8_t ata_count; + int i, len; + + sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst, + &spx->txlt_sata_pkt->satapkt_device); + sata_id = &sdinfo->satadrv_id; + *dmod = 0; + + len = sizeof (struct mode_info_power_cond); + len += sizeof (struct mode_page); + + /* If parmlen is too short or the feature is not supported, drop it */ + if ((len < parmlen) || (page->idle == 1) || + (!(sata_id->ai_cap && SATA_STANDBYTIMER) && page->standby == 1)) { + *scsipkt->pkt_scbp = STATUS_CHECK; + sense = sata_arq_sense(spx); + sense->es_key = KEY_ILLEGAL_REQUEST; + sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_PARAMS_LIST; + *pagelen = parmlen; + *rval = TRAN_ACCEPT; + return (SATA_FAILURE); + } + + *pagelen = len; + + /* + * Set-up Internal STANDBY command(s) + */ + if (page->standby == 0) + goto out; + ata_count = sata_get_standby_timer(page->standby_cond_timer); + scmd->satacmd_addr_type = 0; + scmd->satacmd_sec_count_lsb = ata_count; + scmd->satacmd_lba_low_lsb = 0; + scmd->satacmd_lba_mid_lsb = 0; + scmd->satacmd_lba_high_lsb = 0; + scmd->satacmd_features_reg = 0; + scmd->satacmd_device_reg = 0; + scmd->satacmd_status_reg = 0; + scmd->satacmd_cmd_reg = SATAC_STANDBY; + scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE; + + /* Transfer command to HBA */ + if (sata_hba_start(spx, rval) != 0) { + return (SATA_FAILURE); + } else { + if ((scmd->satacmd_error_reg != 0) || + (spx->txlt_sata_pkt->satapkt_reason != + SATA_PKT_COMPLETED)) { + sata_xlate_errors(spx); + return (SATA_FAILURE); + } + } + + for (i = 0; i < 4; i++) { + sdinfo->satadrv_standby_timer[i] = page->standby_cond_timer[i]; + } +out: + *dmod = 1; + return (SATA_SUCCESS); +} /* * sata_build_lsense_page0() is used to create the * SCSI LOG SENSE page 0 (supported log pages) * - * Currently supported pages are 0, 0x10, 0x2f and 0x30 + * Currently supported pages are 0, 0x10, 0x2f, 0x30 and 0x0e * (supported log pages, self-test results, informational exceptions - * and Sun vendor specific ATA SMART data). + * Sun vendor specific ATA SMART data, and start stop cycle counter). * * Takes a sata_drive_info t * and the address of a buffer * in which to create the page information. @@ -6506,6 +7056,8 @@ sata_build_lsense_page_0(sata_drive_info_t *sdinfo, uint8_t *buf) ++num_pages_supported; *page_ptr++ = PAGE_CODE_SMART_READ_DATA; ++num_pages_supported; + *page_ptr++ = PAGE_CODE_START_STOP_CYCLE_COUNTER; + ++num_pages_supported; } lpp->param_len = num_pages_supported; @@ -7037,6 +7589,171 @@ sata_build_lsense_page_30( return (sizeof (struct smart_data)); } +/* + * sata_build_lsense_page_0e() is used to create the + * SCSI LOG SENSE page 0e (supported log pages) + * + * Date of Manufacture (0x0001) + * YEAR = "0000" + * WEEK = "00" + * Accounting Date (0x0002) + * 6 ASCII space character(20h) + * Specified cycle count over device lifetime + * VALUE - THRESH - the delta between max and min; + * Accumulated start-stop cycles + * VALUE - WORST - the accumulated cycles; + * + * ID FLAG THRESH VALUE WORST RAW on start/stop counter attribute + * + * Takes a sata_drive_info t * and the address of a buffer + * in which to create the page information as well as a sata_hba_inst_t *. + * + * Returns the number of bytes valid in the buffer. + */ +static int +sata_build_lsense_page_0e(sata_drive_info_t *sdinfo, uint8_t *buf, + sata_pkt_txlate_t *spx) +{ + struct start_stop_cycle_counter_log *log_page; + int i, rval, index; + uint8_t smart_data[512], id, value, worst, thresh; + uint32_t max_count, cycles; + + /* Now do the SMART READ DATA */ + rval = sata_fetch_smart_data(spx->txlt_sata_hba_inst, sdinfo, + (struct smart_data *)smart_data); + if (rval == -1) + return (0); + for (i = 0, id = 0; i < SMART_START_STOP_COUNT_ID * 2; i++) { + index = (i * 12) + 2; + id = smart_data[index]; + if (id != SMART_START_STOP_COUNT_ID) + continue; + else { + thresh = smart_data[index + 2]; + value = smart_data[index + 3]; + worst = smart_data[index + 4]; + break; + } + } + if (id != SMART_START_STOP_COUNT_ID) + return (0); + max_count = value - thresh; + cycles = value - worst; + + log_page = (struct start_stop_cycle_counter_log *)buf; + bzero(log_page, sizeof (struct start_stop_cycle_counter_log)); + log_page->code = 0x0e; + log_page->page_len_low = 0x24; + + log_page->manufactor_date_low = 0x1; + log_page->param_1.fmt_link = 0x1; /* 01b */ + log_page->param_len_1 = 0x06; + for (i = 0; i < 4; i++) { + log_page->year_manu[i] = 0x30; + if (i < 2) + log_page->week_manu[i] = 0x30; + } + + log_page->account_date_low = 0x02; + log_page->param_2.fmt_link = 0x01; /* 01b */ + log_page->param_len_2 = 0x06; + for (i = 0; i < 4; i++) { + log_page->year_account[i] = 0x20; + if (i < 2) + log_page->week_account[i] = 0x20; + } + + log_page->lifetime_code_low = 0x03; + log_page->param_3.fmt_link = 0x03; /* 11b */ + log_page->param_len_3 = 0x04; + /* VALUE - THRESH - the delta between max and min */ + log_page->cycle_code_low = 0x04; + log_page->param_4.fmt_link = 0x03; /* 11b */ + log_page->param_len_4 = 0x04; + /* WORST - THRESH - the distance from 'now' to min */ + + for (i = 0; i < 4; i++) { + log_page->cycle_lifetime[i] = + (max_count >> (8 * (3 - i))) & 0xff; + log_page->cycle_accumulated[i] = + (cycles >> (8 * (3 - i))) & 0xff; + } + + return (sizeof (struct start_stop_cycle_counter_log)); +} + +/* + * This function was used for build a ATA read verify sector command + */ +static void +sata_build_read_verify_cmd(sata_cmd_t *scmd, uint16_t sec, uint64_t lba) +{ + scmd->satacmd_cmd_reg = SATAC_RDVER; + scmd->satacmd_addr_type = ATA_ADDR_LBA28; + + scmd->satacmd_sec_count_lsb = sec & 0xff; + scmd->satacmd_lba_low_lsb = lba & 0xff; + scmd->satacmd_lba_mid_lsb = (lba >> 8) & 0xff; + scmd->satacmd_lba_high_lsb = (lba >> 16) & 0xff; + scmd->satacmd_device_reg = (SATA_ADH_LBA | (lba >> 24) & 0xf); + scmd->satacmd_features_reg = 0; + scmd->satacmd_status_reg = 0; + scmd->satacmd_error_reg = 0; +} + +/* + * This function was used for building an ATA + * command, and only command register need to + * be defined, other register will be zero or na. + */ +static void +sata_build_generic_cmd(sata_cmd_t *scmd, uint8_t cmd) +{ + scmd->satacmd_addr_type = 0; + scmd->satacmd_cmd_reg = cmd; + scmd->satacmd_device_reg = 0; + scmd->satacmd_sec_count_lsb = 0; + scmd->satacmd_lba_low_lsb = 0; + scmd->satacmd_lba_mid_lsb = 0; + scmd->satacmd_lba_high_lsb = 0; + scmd->satacmd_features_reg = 0; + scmd->satacmd_status_reg = 0; + scmd->satacmd_error_reg = 0; +} + +/* + * This function was used for changing the standby + * timer format from SCSI to ATA. + */ +static uint8_t +sata_get_standby_timer(uint8_t *timer) +{ + uint32_t i = 0, count = 0; + uint8_t ata_count; + + for (i = 0; i < 4; i++) { + count = count << 8 | timer[i]; + } + + if (count == 0) + return (0); + + if (count >= 1 && count <= 12000) + ata_count = (count -1) / 50 + 1; + else if (count > 12000 && count <= 12600) + ata_count = 0xfc; + else if (count > 12601 && count <= 12750) + ata_count = 0xff; + else if (count > 12750 && count <= 17999) + ata_count = 0xf1; + else if (count > 18000 && count <= 198000) + ata_count = count / 18000 + 240; + else + ata_count = 0xfd; + return (ata_count); +} + /* ************************** ATAPI-SPECIFIC FUNCTIONS ********************** */ /* @@ -8982,6 +9699,10 @@ sata_initialize_device(sata_hba_inst_t *sata_hba_inst, /* DMA supported, not no DMA transfer mode is selected !? */ sdinfo->satadrv_settings &= ~SATA_DEV_DMA; + if ((sdinfo->satadrv_id.ai_cmdset83 & 0x20) && + (sdinfo->satadrv_id.ai_features86 & 0x20)) + sdinfo->satadrv_power_level = SATA_POWER_STANDBY; + return (rval); } diff --git a/usr/src/uts/common/io/scsi/targets/sd.c b/usr/src/uts/common/io/scsi/targets/sd.c index 566565afd1..7b699117bd 100644 --- a/usr/src/uts/common/io/scsi/targets/sd.c +++ b/usr/src/uts/common/io/scsi/targets/sd.c @@ -354,6 +354,33 @@ _NOTE(MUTEX_PROTECTS_DATA(sd_scsi_probe_cache_mutex, _NOTE(MUTEX_PROTECTS_DATA(sd_scsi_probe_cache_mutex, sd_scsi_probe_cache_head)) +/* + * Power attribute table + */ +static sd_power_attr_ss sd_pwr_ss = { + { "NAME=spindle-motor", "0=off", "1=on", NULL }, + {0, 100}, + {30, 0}, + {20000, 0} +}; + +static sd_power_attr_pc sd_pwr_pc = { + { "NAME=spindle-motor", "0=stopped", "1=standby", "2=idle", + "3=active", NULL }, + {0, 0, 0, 100}, + {90, 90, 20, 0}, + {15000, 15000, 1000, 0} +}; + +/* + * Power level to power condition + */ +static int sd_pl2pc[] = { + SD_TARGET_START_VALID, + SD_TARGET_STANDBY, + SD_TARGET_IDLE, + SD_TARGET_ACTIVE +}; /* * Vendor specific data name property declarations @@ -864,9 +891,8 @@ static int sd_pm_idletime = 1; #define sd_setup_pm ssd_setup_pm #define sd_create_pm_components ssd_create_pm_components #define sd_ddi_suspend ssd_ddi_suspend -#define sd_ddi_pm_suspend ssd_ddi_pm_suspend #define sd_ddi_resume ssd_ddi_resume -#define sd_ddi_pm_resume ssd_ddi_pm_resume +#define sd_pm_state_change ssd_pm_state_change #define sdpower ssdpower #define sdattach ssdattach #define sddetach ssddetach @@ -1206,9 +1232,8 @@ static void sd_setup_pm(sd_ssc_t *ssc, dev_info_t *devi); static void sd_create_pm_components(dev_info_t *devi, struct sd_lun *un); static int sd_ddi_suspend(dev_info_t *devi); -static int sd_ddi_pm_suspend(struct sd_lun *un); static int sd_ddi_resume(dev_info_t *devi); -static int sd_ddi_pm_resume(struct sd_lun *un); +static int sd_pm_state_change(struct sd_lun *un, int level, int flag); static int sdpower(dev_info_t *devi, int component, int level); static int sdattach(dev_info_t *devi, ddi_attach_cmd_t cmd); @@ -1467,8 +1492,8 @@ static int sd_send_scsi_READ_CAPACITY(sd_ssc_t *ssc, uint64_t *capp, uint32_t *lbap, int path_flag); static int sd_send_scsi_READ_CAPACITY_16(sd_ssc_t *ssc, uint64_t *capp, uint32_t *lbap, uint32_t *psp, int path_flag); -static int sd_send_scsi_START_STOP_UNIT(sd_ssc_t *ssc, int flag, - int path_flag); +static int sd_send_scsi_START_STOP_UNIT(sd_ssc_t *ssc, int pc_flag, + int flag, int path_flag); static int sd_send_scsi_INQUIRY(sd_ssc_t *ssc, uchar_t *bufaddr, size_t buflen, uchar_t evpd, uchar_t page_code, size_t *residp); static int sd_send_scsi_TEST_UNIT_READY(sd_ssc_t *ssc, int flag); @@ -3177,9 +3202,11 @@ sd_spin_up_unit(sd_ssc_t *ssc) * condition (0x2/0x4/0x3) if the device is "inactive," but * we don't want to fail the attach because it may become * "active" later. + * We don't know if power condition is supported or not at + * this stage, use START STOP bit. */ - status = sd_send_scsi_START_STOP_UNIT(ssc, SD_TARGET_START, - SD_PATH_DIRECT); + status = sd_send_scsi_START_STOP_UNIT(ssc, SD_START_STOP, + SD_TARGET_START, SD_PATH_DIRECT); if (status != 0) { if (status == EACCES) @@ -4022,6 +4049,20 @@ sd_set_properties(struct sd_lun *un, char *name, char *value) return; } + if (strcasecmp(name, "power-condition") == 0) { + if (strcasecmp(value, "true") == 0) { + un->un_f_power_condition_disabled = FALSE; + } else if (strcasecmp(value, "false") == 0) { + un->un_f_power_condition_disabled = TRUE; + } else { + goto value_invalid; + } + SD_INFO(SD_LOG_ATTACH_DETACH, un, "sd_set_properties: " + "power condition disabled flag set to %d\n", + un->un_f_power_condition_disabled); + return; + } + if (strcasecmp(name, "timeout-releasereservation") == 0) { if (ddi_strtol(value, &endptr, 0, &val) == 0) { un->un_reserve_release_time = val; @@ -4233,6 +4274,12 @@ sd_get_tunables_from_conf(struct sd_lun *un, int flags, int *data_list, suppress_cache_flush = %d" "\n", values->sdt_suppress_cache_flush); break; + case SD_CONF_BSET_PC_DISABLED: + values->sdt_disk_sort_dis = data_list[i]; + SD_INFO(SD_LOG_ATTACH_DETACH, un, + "sd_get_tunables_from_conf: power_condition_dis = " + "%d\n", values->sdt_power_condition_dis); + break; } } } @@ -4714,6 +4761,16 @@ sd_set_vers1_properties(struct sd_lun *un, int flags, sd_tunables *prop_list) prop_list->sdt_suppress_cache_flush); } + if (flags & SD_CONF_BSET_PC_DISABLED) { + un->un_f_power_condition_disabled = + (prop_list->sdt_power_condition_dis != 0) ? + TRUE : FALSE; + SD_INFO(SD_LOG_ATTACH_DETACH, un, + "sd_set_vers1_properties: power_condition_disabled " + "flag set to %d\n", + prop_list->sdt_power_condition_dis); + } + /* * Validate the throttle values. * If any of the numbers are invalid, set everything to defaults. @@ -5752,6 +5809,9 @@ sd_setup_pm(sd_ssc_t *ssc, dev_info_t *devi) * This complies with the new power management framework * for certain desktop machines. Create the pm_components * property as a string array property. + * If un_f_pm_supported is TRUE, that means the disk + * attached HBA has set the "pm-capable" property and + * the value of this property is bigger than 0. */ if (un->un_f_pm_supported) { /* @@ -5762,9 +5822,19 @@ sd_setup_pm(sd_ssc_t *ssc, dev_info_t *devi) * device has a motor. */ un->un_f_start_stop_supported = TRUE; - rval = sd_send_scsi_START_STOP_UNIT(ssc, SD_TARGET_START, - SD_PATH_DIRECT); + if (un->un_f_power_condition_supported) { + rval = sd_send_scsi_START_STOP_UNIT(ssc, + SD_POWER_CONDITION, SD_TARGET_ACTIVE, + SD_PATH_DIRECT); + if (rval != 0) { + un->un_f_power_condition_supported = FALSE; + } + } + if (!un->un_f_power_condition_supported) { + rval = sd_send_scsi_START_STOP_UNIT(ssc, + SD_START_STOP, SD_TARGET_START, SD_PATH_DIRECT); + } if (rval != 0) { sd_ssc_assessment(ssc, SD_FMT_IGNORE); un->un_f_start_stop_supported = FALSE; @@ -5774,11 +5844,39 @@ sd_setup_pm(sd_ssc_t *ssc, dev_info_t *devi) * create pm properties anyways otherwise the parent can't * go to sleep */ - (void) sd_create_pm_components(devi, un); un->un_f_pm_is_enabled = TRUE; + (void) sd_create_pm_components(devi, un); + + /* + * If it claims that log sense is supported, check it out. + */ + if (un->un_f_log_sense_supported) { + rval = sd_log_page_supported(ssc, + START_STOP_CYCLE_PAGE); + if (rval == 1) { + /* Page found, use it. */ + un->un_start_stop_cycle_page = + START_STOP_CYCLE_PAGE; + } else { + /* + * Page not found or log sense is not + * supported. + * Notice we do not check the old style + * START_STOP_CYCLE_VU_PAGE because this + * code path does not apply to old disks. + */ + un->un_f_log_sense_supported = FALSE; + un->un_f_pm_log_sense_smart = FALSE; + } + } + return; } + /* + * For the disk whose attached HBA has not set the "pm-capable" + * property, check if it supports the power management. + */ if (!un->un_f_log_sense_supported) { un->un_power_level = SD_SPINDLE_ON; un->un_f_pm_is_enabled = FALSE; @@ -5796,7 +5894,7 @@ sd_setup_pm(sd_ssc_t *ssc, dev_info_t *devi) /* * If the start-stop cycle counter log page is not supported - * or if the pm-capable property is SD_PM_CAPABLE_FALSE (0) + * or if the pm-capable property is set to be false (0), * then we should not create the pm_components property. */ if (rval == -1) { @@ -5886,44 +5984,53 @@ sd_setup_pm(sd_ssc_t *ssc, dev_info_t *devi) static void sd_create_pm_components(dev_info_t *devi, struct sd_lun *un) { - char *pm_comp[] = { "NAME=spindle-motor", "0=off", "1=on", NULL }; - ASSERT(!mutex_owned(SD_MUTEX(un))); - if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi, - "pm-components", pm_comp, 3) == DDI_PROP_SUCCESS) { - /* - * When components are initially created they are idle, - * power up any non-removables. - * Note: the return value of pm_raise_power can't be used - * for determining if PM should be enabled for this device. - * Even if you check the return values and remove this - * property created above, the PM framework will not honor the - * change after the first call to pm_raise_power. Hence, - * removal of that property does not help if pm_raise_power - * fails. In the case of removable media, the start/stop - * will fail if the media is not present. - */ - if (un->un_f_attach_spinup && (pm_raise_power(SD_DEVINFO(un), 0, - SD_SPINDLE_ON) == DDI_SUCCESS)) { - mutex_enter(SD_MUTEX(un)); + if (un->un_f_power_condition_supported) { + if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi, + "pm-components", sd_pwr_pc.pm_comp, 5) + != DDI_PROP_SUCCESS) { + un->un_power_level = SD_SPINDLE_ACTIVE; + un->un_f_pm_is_enabled = FALSE; + return; + } + } else { + if (ddi_prop_update_string_array(DDI_DEV_T_NONE, devi, + "pm-components", sd_pwr_ss.pm_comp, 3) + != DDI_PROP_SUCCESS) { un->un_power_level = SD_SPINDLE_ON; - mutex_enter(&un->un_pm_mutex); - /* Set to on and not busy. */ - un->un_pm_count = 0; - } else { - mutex_enter(SD_MUTEX(un)); - un->un_power_level = SD_SPINDLE_OFF; - mutex_enter(&un->un_pm_mutex); - /* Set to off. */ - un->un_pm_count = -1; + un->un_f_pm_is_enabled = FALSE; + return; } - mutex_exit(&un->un_pm_mutex); - mutex_exit(SD_MUTEX(un)); + } + /* + * When components are initially created they are idle, + * power up any non-removables. + * Note: the return value of pm_raise_power can't be used + * for determining if PM should be enabled for this device. + * Even if you check the return values and remove this + * property created above, the PM framework will not honor the + * change after the first call to pm_raise_power. Hence, + * removal of that property does not help if pm_raise_power + * fails. In the case of removable media, the start/stop + * will fail if the media is not present. + */ + if (un->un_f_attach_spinup && (pm_raise_power(SD_DEVINFO(un), 0, + SD_PM_STATE_ACTIVE(un)) == DDI_SUCCESS)) { + mutex_enter(SD_MUTEX(un)); + un->un_power_level = SD_PM_STATE_ACTIVE(un); + mutex_enter(&un->un_pm_mutex); + /* Set to on and not busy. */ + un->un_pm_count = 0; } else { - un->un_power_level = SD_SPINDLE_ON; - un->un_f_pm_is_enabled = FALSE; + mutex_enter(SD_MUTEX(un)); + un->un_power_level = SD_PM_STATE_STOPPED(un); + mutex_enter(&un->un_pm_mutex); + /* Set to off. */ + un->un_pm_count = -1; } + mutex_exit(&un->un_pm_mutex); + mutex_exit(SD_MUTEX(un)); } @@ -6127,69 +6234,6 @@ sd_ddi_suspend(dev_info_t *devi) /* - * Function: sd_ddi_pm_suspend - * - * Description: Set the drive state to low power. - * Someone else is required to actually change the drive - * power level. - * - * Arguments: un - driver soft state (unit) structure - * - * Return Code: DDI_FAILURE or DDI_SUCCESS - * - * Context: Kernel thread context - */ - -static int -sd_ddi_pm_suspend(struct sd_lun *un) -{ - ASSERT(un != NULL); - SD_TRACE(SD_LOG_POWER, un, "sd_ddi_pm_suspend: entry\n"); - - ASSERT(!mutex_owned(SD_MUTEX(un))); - mutex_enter(SD_MUTEX(un)); - - /* - * Exit if power management is not enabled for this device, or if - * the device is being used by HA. - */ - if ((un->un_f_pm_is_enabled == FALSE) || (un->un_resvd_status & - (SD_RESERVE | SD_WANT_RESERVE | SD_LOST_RESERVE))) { - mutex_exit(SD_MUTEX(un)); - SD_TRACE(SD_LOG_POWER, un, "sd_ddi_pm_suspend: exiting\n"); - return (DDI_SUCCESS); - } - - SD_INFO(SD_LOG_POWER, un, "sd_ddi_pm_suspend: un_ncmds_in_driver=%ld\n", - un->un_ncmds_in_driver); - - /* - * See if the device is not busy, ie.: - * - we have no commands in the driver for this device - * - not waiting for resources - */ - if ((un->un_ncmds_in_driver == 0) && - (un->un_state != SD_STATE_RWAIT)) { - /* - * The device is not busy, so it is OK to go to low power state. - * Indicate low power, but rely on someone else to actually - * change it. - */ - mutex_enter(&un->un_pm_mutex); - un->un_pm_count = -1; - mutex_exit(&un->un_pm_mutex); - un->un_power_level = SD_SPINDLE_OFF; - } - - mutex_exit(SD_MUTEX(un)); - - SD_TRACE(SD_LOG_POWER, un, "sd_ddi_pm_suspend: exit\n"); - - return (DDI_SUCCESS); -} - - -/* * Function: sd_ddi_resume * * Description: Performs system power-up operations.. @@ -6245,7 +6289,8 @@ sd_ddi_resume(dev_info_t *devi) */ if (un->un_f_attach_spinup) { mutex_exit(SD_MUTEX(un)); - (void) pm_raise_power(SD_DEVINFO(un), 0, SD_SPINDLE_ON); + (void) pm_raise_power(SD_DEVINFO(un), 0, + SD_PM_STATE_ACTIVE(un)); mutex_enter(SD_MUTEX(un)); } @@ -6295,43 +6340,77 @@ sd_ddi_resume(dev_info_t *devi) /* - * Function: sd_ddi_pm_resume + * Function: sd_pm_state_change * - * Description: Set the drive state to powered on. - * Someone else is required to actually change the drive - * power level. + * Description: Change the driver power state. + * Someone else is required to actually change the driver + * power level. * * Arguments: un - driver soft state (unit) structure + * level - the power level that is changed to + * flag - to decide how to change the power state * * Return Code: DDI_SUCCESS * * Context: Kernel thread context */ - static int -sd_ddi_pm_resume(struct sd_lun *un) +sd_pm_state_change(struct sd_lun *un, int level, int flag) { ASSERT(un != NULL); + SD_TRACE(SD_LOG_POWER, un, "sd_pm_state_change: entry\n"); ASSERT(!mutex_owned(SD_MUTEX(un))); mutex_enter(SD_MUTEX(un)); - un->un_power_level = SD_SPINDLE_ON; - ASSERT(!mutex_owned(&un->un_pm_mutex)); - mutex_enter(&un->un_pm_mutex); - if (SD_DEVICE_IS_IN_LOW_POWER(un)) { - un->un_pm_count++; - ASSERT(un->un_pm_count == 0); + if (flag == SD_PM_STATE_ROLLBACK || SD_PM_IS_IO_CAPABLE(un, level)) { + un->un_power_level = level; + ASSERT(!mutex_owned(&un->un_pm_mutex)); + mutex_enter(&un->un_pm_mutex); + if (SD_DEVICE_IS_IN_LOW_POWER(un)) { + un->un_pm_count++; + ASSERT(un->un_pm_count == 0); + } + mutex_exit(&un->un_pm_mutex); + } else { + /* + * Exit if power management is not enabled for this device, + * or if the device is being used by HA. + */ + if ((un->un_f_pm_is_enabled == FALSE) || (un->un_resvd_status & + (SD_RESERVE | SD_WANT_RESERVE | SD_LOST_RESERVE))) { + mutex_exit(SD_MUTEX(un)); + SD_TRACE(SD_LOG_POWER, un, + "sd_pm_state_change: exiting\n"); + return (DDI_FAILURE); + } + + SD_INFO(SD_LOG_POWER, un, "sd_pm_state_change: " + "un_ncmds_in_driver=%ld\n", un->un_ncmds_in_driver); + /* - * Note: no longer do the cv_broadcast on un_suspend_cv. The - * un_suspend_cv is for a system resume, not a power management - * device resume. (4297749) - * cv_broadcast(&un->un_suspend_cv); + * See if the device is not busy, ie.: + * - we have no commands in the driver for this device + * - not waiting for resources */ + if ((un->un_ncmds_in_driver == 0) && + (un->un_state != SD_STATE_RWAIT)) { + /* + * The device is not busy, so it is OK to go to low + * power state. Indicate low power, but rely on someone + * else to actually change it. + */ + mutex_enter(&un->un_pm_mutex); + un->un_pm_count = -1; + mutex_exit(&un->un_pm_mutex); + un->un_power_level = level; + } } - mutex_exit(&un->un_pm_mutex); + mutex_exit(SD_MUTEX(un)); + SD_TRACE(SD_LOG_POWER, un, "sd_pm_state_change: exit\n"); + return (DDI_SUCCESS); } @@ -6452,12 +6531,12 @@ sdpower(dev_info_t *devi, int component, int level) uchar_t state_before_pm; int got_semaphore_here; sd_ssc_t *ssc; + int last_power_level; instance = ddi_get_instance(devi); if (((un = ddi_get_soft_state(sd_state, instance)) == NULL) || - (SD_SPINDLE_OFF > level) || (level > SD_SPINDLE_ON) || - component != 0) { + !SD_PM_IS_LEVEL_VALID(un, level) || component != 0) { return (DDI_FAILURE); } @@ -6488,10 +6567,11 @@ sdpower(dev_info_t *devi, int component, int level) * If un_ncmds_in_driver is non-zero it indicates commands are * already being processed in the driver, or if the semaphore was * not gotten here it indicates an open or close is being processed. - * At the same time somebody is requesting to go low power which - * can't happen, therefore we need to return failure. + * At the same time somebody is requesting to go to a lower power + * that can't perform I/O, which can't happen, therefore we need to + * return failure. */ - if ((level == SD_SPINDLE_OFF) && + if ((!SD_PM_IS_IO_CAPABLE(un, level)) && ((un->un_ncmds_in_driver != 0) || (got_semaphore_here == 0))) { mutex_exit(SD_MUTEX(un)); @@ -6537,11 +6617,12 @@ sdpower(dev_info_t *devi, int component, int level) mutex_exit(SD_MUTEX(un)); /* - * If "pm-capable" property is set to TRUE by HBA drivers, - * bypass the following checking, otherwise, check the log - * sense information for this device + * If log sense command is not supported, bypass the + * following checking, otherwise, check the log sense + * information for this device. */ - if ((level == SD_SPINDLE_OFF) && un->un_f_log_sense_supported) { + if (SD_PM_STOP_MOTOR_NEEDED(un, level) && + un->un_f_log_sense_supported) { /* * Get the log sense information to understand whether the * the powercycle counts have gone beyond the threshhold. @@ -6602,17 +6683,24 @@ sdpower(dev_info_t *devi, int component, int level) (log_page_data[0x1c] << 24) | (log_page_data[0x1d] << 16) | (log_page_data[0x1E] << 8) | log_page_data[0x1F]; - sd_pm_tran_data.un.scsi_cycles.lifemax = maxcycles; - ncycles = (log_page_data[0x24] << 24) | (log_page_data[0x25] << 16) | (log_page_data[0x26] << 8) | log_page_data[0x27]; - sd_pm_tran_data.un.scsi_cycles.ncycles = ncycles; - - for (i = 0; i < DC_SCSI_MFR_LEN; i++) { - sd_pm_tran_data.un.scsi_cycles.svc_date[i] = - log_page_data[8+i]; + if (un->un_f_pm_log_sense_smart) { + sd_pm_tran_data.un.smart_count.allowed = maxcycles; + sd_pm_tran_data.un.smart_count.consumed = ncycles; + sd_pm_tran_data.un.smart_count.flag = 0; + sd_pm_tran_data.format = DC_SMART_FORMAT; + } else { + sd_pm_tran_data.un.scsi_cycles.lifemax = maxcycles; + sd_pm_tran_data.un.scsi_cycles.ncycles = ncycles; + for (i = 0; i < DC_SCSI_MFR_LEN; i++) { + sd_pm_tran_data.un.scsi_cycles.svc_date[i] = + log_page_data[8+i]; + } + sd_pm_tran_data.un.scsi_cycles.flag = 0; + sd_pm_tran_data.format = DC_SCSI_FORMAT; } kmem_free(log_page_data, log_page_size); @@ -6621,10 +6709,6 @@ sdpower(dev_info_t *devi, int component, int level) * Call pm_trans_check routine to get the Ok from * the global policy */ - - sd_pm_tran_data.format = DC_SCSI_FORMAT; - sd_pm_tran_data.un.scsi_cycles.flag = 0; - rval = pm_trans_check(&sd_pm_tran_data, &intvlp); #ifdef SDDEBUG if (sd_force_pm_supported) { @@ -6705,13 +6789,14 @@ sdpower(dev_info_t *devi, int component, int level) } } - if (level == SD_SPINDLE_OFF) { + if (!SD_PM_IS_IO_CAPABLE(un, level)) { /* * Save the last state... if the STOP FAILS we need it * for restoring */ mutex_enter(SD_MUTEX(un)); save_state = un->un_last_state; + last_power_level = un->un_power_level; /* * There must not be any cmds. getting processed * in the driver when we get here. Power to the @@ -6721,10 +6806,11 @@ sdpower(dev_info_t *devi, int component, int level) mutex_exit(SD_MUTEX(un)); /* - * For now suspend the device completely before spindle is + * For now PM suspend the device completely before spindle is * turned off */ - if ((rval = sd_ddi_pm_suspend(un)) == DDI_FAILURE) { + if ((rval = sd_pm_state_change(un, level, SD_PM_STATE_CHANGE)) + == DDI_FAILURE) { if (got_semaphore_here != 0) { sema_v(&un->un_semoclose); } @@ -6735,6 +6821,7 @@ sdpower(dev_info_t *devi, int component, int level) */ mutex_enter(SD_MUTEX(un)); un->un_state = state_before_pm; + un->un_power_level = last_power_level; cv_broadcast(&un->un_suspend_cv); mutex_exit(SD_MUTEX(un)); SD_TRACE(SD_LOG_IO_PM, un, @@ -6757,19 +6844,28 @@ sdpower(dev_info_t *devi, int component, int level) * attention. Don't do retries. Bypass the PM layer, otherwise * a deadlock on un_pm_busy_cv will occur. */ - if (level == SD_SPINDLE_ON) { + if (SD_PM_IS_IO_CAPABLE(un, level)) { sval = sd_send_scsi_TEST_UNIT_READY(ssc, SD_DONT_RETRY_TUR | SD_BYPASS_PM); if (sval != 0) sd_ssc_assessment(ssc, SD_FMT_IGNORE); } - SD_TRACE(SD_LOG_IO_PM, un, "sdpower: sending \'%s\' unit\n", - ((level == SD_SPINDLE_ON) ? "START" : "STOP")); - - sval = sd_send_scsi_START_STOP_UNIT(ssc, - ((level == SD_SPINDLE_ON) ? SD_TARGET_START : SD_TARGET_STOP), - SD_PATH_DIRECT); + if (un->un_f_power_condition_supported) { + char *pm_condition_name[] = {"STOPPED", "STANDBY", + "IDLE", "ACTIVE"}; + SD_TRACE(SD_LOG_IO_PM, un, + "sdpower: sending \'%s\' power condition", + pm_condition_name[level]); + sval = sd_send_scsi_START_STOP_UNIT(ssc, SD_POWER_CONDITION, + sd_pl2pc[level], SD_PATH_DIRECT); + } else { + SD_TRACE(SD_LOG_IO_PM, un, "sdpower: sending \'%s\' unit\n", + ((level == SD_SPINDLE_ON) ? "START" : "STOP")); + sval = sd_send_scsi_START_STOP_UNIT(ssc, SD_START_STOP, + ((level == SD_SPINDLE_ON) ? SD_TARGET_START : + SD_TARGET_STOP), SD_PATH_DIRECT); + } if (sval != 0) { if (sval == EIO) sd_ssc_assessment(ssc, SD_FMT_STATUS_CHECK); @@ -6791,8 +6887,7 @@ sdpower(dev_info_t *devi, int component, int level) * In all other cases we setup for the new state * and return success. */ - switch (level) { - case SD_SPINDLE_OFF: + if (!SD_PM_IS_IO_CAPABLE(un, level)) { if ((medium_present == TRUE) && (sval != 0)) { /* The stop command from above failed */ rval = DDI_FAILURE; @@ -6802,17 +6897,14 @@ sdpower(dev_info_t *devi, int component, int level) * sd_pm_resume() and set the state back to * it's previous value. */ - (void) sd_ddi_pm_resume(un); + (void) sd_pm_state_change(un, last_power_level, + SD_PM_STATE_ROLLBACK); mutex_enter(SD_MUTEX(un)); un->un_last_state = save_state; mutex_exit(SD_MUTEX(un)); - break; - } - /* - * The stop command from above succeeded. - */ - if (un->un_f_monitor_media_state) { + } else if (un->un_f_monitor_media_state) { /* + * The stop command from above succeeded. * Terminate watch thread in case of removable media * devices going into low power state. This is as per * the requirements of pm framework, otherwise commands @@ -6832,10 +6924,9 @@ sdpower(dev_info_t *devi, int component, int level) mutex_exit(SD_MUTEX(un)); } } - break; - - default: /* The level requested is spindle on... */ + } else { /* + * The level requested is I/O capable. * Legacy behavior: return success on a failed spinup * if there is no media in the drive. * Do this by looking at medium_present here. @@ -6843,37 +6934,39 @@ sdpower(dev_info_t *devi, int component, int level) if ((sval != 0) && medium_present) { /* The start command from above failed */ rval = DDI_FAILURE; - break; - } - /* - * The start command from above succeeded - * Resume the devices now that we have - * started the disks - */ - (void) sd_ddi_pm_resume(un); + } else { + /* + * The start command from above succeeded + * PM resume the devices now that we have + * started the disks + */ + (void) sd_pm_state_change(un, level, + SD_PM_STATE_CHANGE); - /* - * Resume the watch thread since it was suspended - * when the device went into low power mode. - */ - if (un->un_f_monitor_media_state) { - mutex_enter(SD_MUTEX(un)); - if (un->un_f_watcht_stopped == TRUE) { - opaque_t temp_token; + /* + * Resume the watch thread since it was suspended + * when the device went into low power mode. + */ + if (un->un_f_monitor_media_state) { + mutex_enter(SD_MUTEX(un)); + if (un->un_f_watcht_stopped == TRUE) { + opaque_t temp_token; - un->un_f_watcht_stopped = FALSE; + un->un_f_watcht_stopped = FALSE; + mutex_exit(SD_MUTEX(un)); + temp_token = scsi_watch_request_submit( + SD_SCSI_DEVP(un), + sd_check_media_time, + SENSE_LENGTH, sd_media_watch_cb, + (caddr_t)dev); + mutex_enter(SD_MUTEX(un)); + un->un_swr_token = temp_token; + } mutex_exit(SD_MUTEX(un)); - temp_token = scsi_watch_request_submit( - SD_SCSI_DEVP(un), - sd_check_media_time, - SENSE_LENGTH, sd_media_watch_cb, - (caddr_t)dev); - mutex_enter(SD_MUTEX(un)); - un->un_swr_token = temp_token; } - mutex_exit(SD_MUTEX(un)); } } + if (got_semaphore_here != 0) { sema_v(&un->un_semoclose); } @@ -8594,8 +8687,8 @@ sd_unit_detach(dev_info_t *devi) } else { mutex_exit(&un->un_pm_mutex); if ((un->un_f_pm_is_enabled == TRUE) && - (pm_lower_power(SD_DEVINFO(un), 0, SD_SPINDLE_OFF) != - DDI_SUCCESS)) { + (pm_lower_power(SD_DEVINFO(un), 0, SD_PM_STATE_STOPPED(un)) + != DDI_SUCCESS)) { SD_ERROR(SD_LOG_ATTACH_DETACH, un, "sd_dr_detach: Lower power request failed, ignoring.\n"); /* @@ -9719,12 +9812,11 @@ sd_pm_entry(struct sd_lun *un) /* * pm_raise_power will cause sdpower to be called * which brings the device power level to the - * desired state, ON in this case. If successful, - * un_pm_count and un_power_level will be updated - * appropriately. + * desired state, If successful, un_pm_count and + * un_power_level will be updated appropriately. */ return_status = pm_raise_power(SD_DEVINFO(un), 0, - SD_SPINDLE_ON); + SD_PM_STATE_ACTIVE(un)); mutex_enter(&un->un_pm_mutex); @@ -11725,6 +11817,7 @@ sd_ssc_fini(sd_ssc_t *ssc) * Return Code: 0 - successful completion of the given command * EIO - scsi_uscsi_handle_command() failed * ENXIO - soft state not found for specified dev + * ECANCELED - command cancelled due to low power * EINVAL * EFAULT - copyin/copyout error * return code of scsi_uscsi_handle_command(): @@ -11780,6 +11873,23 @@ sd_ssc_send(sd_ssc_t *ssc, struct uscsi_cmd *incmd, int flag, */ ssc->ssc_flags |= SSC_FLAGS_NEED_ASSESSMENT; + /* + * if USCSI_PMFAILFAST is set and un is in low power, fail the + * command immediately. + */ + mutex_enter(SD_MUTEX(un)); + mutex_enter(&un->un_pm_mutex); + if ((uscmd->uscsi_flags & USCSI_PMFAILFAST) && + SD_DEVICE_IS_IN_LOW_POWER(un)) { + SD_TRACE(SD_LOG_IO, un, "sd_ssc_send:" + "un:0x%p is in low power\n", un); + mutex_exit(&un->un_pm_mutex); + mutex_exit(SD_MUTEX(un)); + return (ECANCELED); + } + mutex_exit(&un->un_pm_mutex); + mutex_exit(SD_MUTEX(un)); + #ifdef SDDEBUG switch (dataspace) { case UIO_USERSPACE: @@ -20060,6 +20170,8 @@ sd_send_scsi_READ_CAPACITY_16(sd_ssc_t *ssc, uint64_t *capp, * * Arguments: ssc - ssc contatins pointer to driver soft state (unit) * structure for this target. + * pc_flag - SD_POWER_CONDITION + * SD_START_STOP * flag - SD_TARGET_START * SD_TARGET_STOP * SD_TARGET_EJECT @@ -20079,7 +20191,8 @@ sd_send_scsi_READ_CAPACITY_16(sd_ssc_t *ssc, uint64_t *capp, */ static int -sd_send_scsi_START_STOP_UNIT(sd_ssc_t *ssc, int flag, int path_flag) +sd_send_scsi_START_STOP_UNIT(sd_ssc_t *ssc, int pc_flag, int flag, + int path_flag) { struct scsi_extended_sense sense_buf; union scsi_cdb cdb; @@ -20096,7 +20209,7 @@ sd_send_scsi_START_STOP_UNIT(sd_ssc_t *ssc, int flag, int path_flag) "sd_send_scsi_START_STOP_UNIT: entry: un:0x%p\n", un); if (un->un_f_check_start_stop && - ((flag == SD_TARGET_START) || (flag == SD_TARGET_STOP)) && + ((pc_flag == SD_START_STOP) && (flag != SD_TARGET_EJECT)) && (un->un_f_start_stop_supported != TRUE)) { return (0); } @@ -20120,7 +20233,8 @@ sd_send_scsi_START_STOP_UNIT(sd_ssc_t *ssc, int flag, int path_flag) bzero(&sense_buf, sizeof (struct scsi_extended_sense)); cdb.scc_cmd = SCMD_START_STOP; - cdb.cdb_opaque[4] = (uchar_t)flag; + cdb.cdb_opaque[4] = (pc_flag == SD_POWER_CONDITION) ? + (uchar_t)(flag << 4) : (uchar_t)flag; ucmd_buf.uscsi_cdb = (char *)&cdb; ucmd_buf.uscsi_cdblen = CDB_GROUP0; @@ -20215,6 +20329,7 @@ sd_start_stop_unit_task(void *arg) { struct sd_lun *un = arg; sd_ssc_t *ssc; + int power_level; int rval; ASSERT(un != NULL); @@ -20233,16 +20348,30 @@ sd_start_stop_unit_task(void *arg) } mutex_exit(SD_MUTEX(un)); + ssc = sd_ssc_init(un); /* * When a START STOP command is issued from here, it is part of a * failure recovery operation and must be issued before any other * commands, including any pending retries. Thus it must be sent * using SD_PATH_DIRECT_PRIORITY. It doesn't matter if the spin up * succeeds or not, we will start I/O after the attempt. + * If power condition is supported and the current power level + * is capable of performing I/O, we should set the power condition + * to that level. Otherwise, set the power condition to ACTIVE. */ - ssc = sd_ssc_init(un); - rval = sd_send_scsi_START_STOP_UNIT(ssc, SD_TARGET_START, - SD_PATH_DIRECT_PRIORITY); + if (un->un_f_power_condition_supported) { + mutex_enter(SD_MUTEX(un)); + ASSERT(SD_PM_IS_LEVEL_VALID(un, un->un_power_level)); + power_level = sd_pwr_pc.ran_perf[un->un_power_level] + > 0 ? un->un_power_level : SD_SPINDLE_ACTIVE; + mutex_exit(SD_MUTEX(un)); + rval = sd_send_scsi_START_STOP_UNIT(ssc, SD_POWER_CONDITION, + sd_pl2pc[power_level], SD_PATH_DIRECT_PRIORITY); + } else { + rval = sd_send_scsi_START_STOP_UNIT(ssc, SD_START_STOP, + SD_TARGET_START, SD_PATH_DIRECT_PRIORITY); + } + if (rval != 0) sd_ssc_assessment(ssc, SD_FMT_IGNORE); sd_ssc_fini(ssc); @@ -22286,8 +22415,8 @@ skip_ready_valid: if (!ISCD(un)) { err = ENOTTY; } else { - err = sd_send_scsi_START_STOP_UNIT(ssc, SD_TARGET_STOP, - SD_PATH_STANDARD); + err = sd_send_scsi_START_STOP_UNIT(ssc, SD_START_STOP, + SD_TARGET_STOP, SD_PATH_STANDARD); goto done_with_assess; } break; @@ -22297,8 +22426,8 @@ skip_ready_valid: if (!ISCD(un)) { err = ENOTTY; } else { - err = sd_send_scsi_START_STOP_UNIT(ssc, SD_TARGET_START, - SD_PATH_STANDARD); + err = sd_send_scsi_START_STOP_UNIT(ssc, SD_START_STOP, + SD_TARGET_START, SD_PATH_STANDARD); goto done_with_assess; } break; @@ -22308,8 +22437,8 @@ skip_ready_valid: if (!ISCD(un)) { err = ENOTTY; } else { - err = sd_send_scsi_START_STOP_UNIT(ssc, SD_TARGET_CLOSE, - SD_PATH_STANDARD); + err = sd_send_scsi_START_STOP_UNIT(ssc, SD_START_STOP, + SD_TARGET_CLOSE, SD_PATH_STANDARD); goto done_with_assess; } break; @@ -25346,7 +25475,8 @@ sddump(dev_t dev, caddr_t addr, daddr_t blkno, int nblk) /* * use pm framework to power on HBA 1st */ - (void) pm_raise_power(SD_DEVINFO(un), 0, SD_SPINDLE_ON); + (void) pm_raise_power(SD_DEVINFO(un), 0, + SD_PM_STATE_ACTIVE(un)); /* * Dump no long uses sdpower to power on a device, it's @@ -25379,7 +25509,8 @@ sddump(dev_t dev, caddr_t addr, daddr_t blkno, int nblk) return (EIO); } scsi_destroy_pkt(start_pktp); - (void) sd_ddi_pm_resume(un); + (void) sd_pm_state_change(un, SD_PM_STATE_ACTIVE(un), + SD_PM_STATE_CHANGE); } else { mutex_exit(&un->un_pm_mutex); } @@ -28359,8 +28490,8 @@ sr_eject(dev_t dev) } ssc = sd_ssc_init(un); - rval = sd_send_scsi_START_STOP_UNIT(ssc, SD_TARGET_EJECT, - SD_PATH_STANDARD); + rval = sd_send_scsi_START_STOP_UNIT(ssc, SD_START_STOP, + SD_TARGET_EJECT, SD_PATH_STANDARD); sd_ssc_fini(ssc); if (rval == 0) { @@ -30307,7 +30438,7 @@ sd_faultinjection(struct scsi_pkt *pktp) static void sd_set_unit_attributes(struct sd_lun *un, dev_info_t *devi) { - int pm_capable_prop; + int pm_cap; ASSERT(un->un_sd); ASSERT(un->un_sd->sd_inq); @@ -30427,33 +30558,47 @@ sd_set_unit_attributes(struct sd_lun *un, dev_info_t *devi) * power manage the device without checking the start/stop * cycle count log sense page. * - * If "pm-capable" exists and is SD_PM_CAPABLE_FALSE (0) + * If "pm-capable" exists and is set to be false (0), * then we should not power manage the device. * - * If "pm-capable" doesn't exist then pm_capable_prop will + * If "pm-capable" doesn't exist then pm_cap will * be set to SD_PM_CAPABLE_UNDEFINED (-1). In this case, * sd will check the start/stop cycle count log sense page * and power manage the device if the cycle count limit has * not been exceeded. */ - pm_capable_prop = ddi_prop_get_int(DDI_DEV_T_ANY, devi, + pm_cap = ddi_prop_get_int(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, "pm-capable", SD_PM_CAPABLE_UNDEFINED); - if (pm_capable_prop == SD_PM_CAPABLE_UNDEFINED) { + if (SD_PM_CAPABLE_IS_UNDEFINED(pm_cap)) { un->un_f_log_sense_supported = TRUE; + if (!un->un_f_power_condition_disabled && + SD_INQUIRY(un)->inq_ansi == 6) { + un->un_f_power_condition_supported = TRUE; + } } else { /* * pm-capable property exists. * - * Convert "TRUE" values for pm_capable_prop to - * SD_PM_CAPABLE_TRUE (1) to make it easier to check - * later. "TRUE" values are any values except - * SD_PM_CAPABLE_FALSE (0) and - * SD_PM_CAPABLE_UNDEFINED (-1) + * Convert "TRUE" values for pm_cap to + * SD_PM_CAPABLE_IS_TRUE to make it easier to check + * later. "TRUE" values are any values defined in + * inquiry.h. */ - if (pm_capable_prop == SD_PM_CAPABLE_FALSE) { + if (SD_PM_CAPABLE_IS_FALSE(pm_cap)) { un->un_f_log_sense_supported = FALSE; } else { + /* SD_PM_CAPABLE_IS_TRUE case */ un->un_f_pm_supported = TRUE; + if (!un->un_f_power_condition_disabled && + SD_PM_CAPABLE_IS_SPC_4(pm_cap)) { + un->un_f_power_condition_supported = + TRUE; + } + if (SD_PM_CAP_LOG_SUPPORTED(pm_cap)) { + un->un_f_log_sense_supported = TRUE; + un->un_f_pm_log_sense_smart = + SD_PM_CAP_SMART_LOG(pm_cap); + } } SD_INFO(SD_LOG_ATTACH_DETACH, un, diff --git a/usr/src/uts/common/os/sunpm.c b/usr/src/uts/common/os/sunpm.c index 7570cd29e6..c5850599ef 100644 --- a/usr/src/uts/common/os/sunpm.c +++ b/usr/src/uts/common/os/sunpm.c @@ -6590,6 +6590,7 @@ pm_trans_check(struct pm_trans_data *datap, time_t *intervalp) char *ptr; int lower_bound_cycles, upper_bound_cycles, cycles_allowed; int cycles_diff, cycles_over; + struct pm_smart_count *smart_p; if (datap == NULL) { PMD(PMD_TCHECK, ("%s: NULL data pointer!\n", pmf)) @@ -6771,6 +6772,18 @@ pm_trans_check(struct pm_trans_data *datap, time_t *intervalp) PMD(PMD_TCHECK, ("%s: no cycle is allowed in %ld secs\n", pmf, *intervalp)) return (0); + } else if (datap->format == DC_SMART_FORMAT) { + /* + * power cycles of SATA disks are reported from SMART + * attributes. + */ + smart_p = &datap->un.smart_count; + if (smart_p->consumed >= smart_p->allowed) { + *intervalp = (LONG_MAX / hz); + PMD(PMD_TCHECK, ("%s: exceeded lifemax cycles.\n", pmf)) + return (0); + } else + return (1); } PMD(PMD_TCHECK, ("%s: unknown format!\n", pmf)) diff --git a/usr/src/uts/common/sys/sata/impl/sata.h b/usr/src/uts/common/sys/sata/impl/sata.h index ee502b958b..cdea69a4cf 100644 --- a/usr/src/uts/common/sys/sata/impl/sata.h +++ b/usr/src/uts/common/sys/sata/impl/sata.h @@ -227,6 +227,13 @@ struct sata_drive_info { uint64_t satadrv_max_queue_depth; /* maximum queue depth */ sata_id_t satadrv_id; /* Device Identify Data */ struct sata_drive_stats satadrv_stats; /* drive statistics */ + + /* + * saved standby timer + * [0] - [3] = high - low + */ + uint8_t satadrv_standby_timer[4]; + uint8_t satadrv_power_level; /* saved power level */ }; typedef struct sata_drive_info sata_drive_info_t; @@ -313,6 +320,22 @@ struct sata_pmport_info { typedef struct sata_pmport_info sata_pmport_info_t; /* + * sata drive's power level + * default value is active + */ +#define SATA_POWER_ACTIVE 0x00 +#define SATA_POWER_IDLE 0x01 +#define SATA_POWER_STANDBY 0x02 +#define SATA_POWER_STOPPED 0x03 + +/* + * pm-capable value definition according to PSARC 2009/310 + */ +#define SATA_CAP_POWER_CONDITON PM_CAPABLE_SPC4 +#define SATA_CAP_SMART_PAGE PM_CAPABLE_SMART_LOG +#define SATA_CAP_LOG_SENSE PM_CAPABLE_LOG_SUPPORTED + +/* * Port SSTATUS register (sata_port_scr sport_sstatus field). * Link bits are valid only in port active state. */ @@ -470,9 +493,12 @@ _NOTE(SCHEME_PROTECTS_DATA("unshared data", scsi_pkt)) #define SD_SCSI_ASC_INVALID_COMMAND_CODE 0x20 #define SD_SCSI_ASC_LBA_OUT_OF_RANGE 0x21 #define SD_SCSI_ASC_INVALID_FIELD_IN_CDB 0x24 -#define SD_SCSI_ASC_INVALID_FIELD_IN_PARAMS_LIST 0x26 +#define SD_SCSI_ASC_INVALID_FIELD_IN_PARAMS_LIST 0x26 #define SD_SCSI_ASC_RESET 0x29 #define SD_SCSI_ASC_SAVING_PARAMS_NOT_SUPPORTED 0x39 +#define SD_SCSI_ASC_CMD_SEQUENCE_ERR 0x2c +#define SD_SCSI_ASC_LOW_POWER_CONDITION_ON 0x5e +#define SD_SCSI_ASC_LU_NOT_RESPONSE 0x05 /* SCSI defs missing from scsi headers */ @@ -485,6 +511,28 @@ _NOTE(SCHEME_PROTECTS_DATA("unshared data", scsi_pkt)) #define MODEPAGE_RW_ERRRECOV 0x01 /* read/write recovery */ /* + * medium access command + */ +#define SATA_IS_MEDIUM_ACCESS_CMD(cmd) \ + (((cmd) == SCMD_READ) || ((cmd) == SCMD_WRITE) || \ + ((cmd) == SCMD_READ_G1) || ((cmd) == SCMD_WRITE_G1) || \ + ((cmd) == SCMD_READ_G4) || ((cmd) == SCMD_WRITE_G4) || \ + ((cmd) == SCMD_READ_G5) || ((cmd) == SCMD_WRITE_G5) || \ + ((cmd) == SCMD_VERIFY) || ((cmd) == SCMD_VERIFY_G4) || \ + ((cmd) == SCMD_VERIFY_G5) || ((cmd) == 0x7f) /* VERIFY(32) */|| \ + ((cmd) == SCMD_SYNCHRONIZE_CACHE) || ((cmd) == SCMD_SPACE_G4) || \ + ((cmd) == SCMD_READ_POSITION) || \ + ((cmd) == 0x90) /* PRE-FETCH(16) */ || \ + ((cmd) == SCMD_READ_DEFECT_LIST) || \ + ((cmd) == 0xb7) /* READ DEFECT DATA */ || \ + ((cmd) == SCMD_READ_LONG) || ((cmd) == SCMD_SVC_ACTION_IN_G4) || \ + ((cmd) == SCMD_WRITE_LONG) || ((cmd) == SCMD_SVC_ACTION_OUT_G4) || \ + ((cmd) == 0x41) || ((cmd) == 0x93) || /* WRITE SAME */ \ + ((cmd) == 0x52) || ((cmd) == 0x50) || /* XDREAD & XDWRITE */ \ + ((cmd) == 0x53) || ((cmd) == 0x51) || /* XDWRITEREAD & XPWRITE */ \ + ((cmd) == 0x7f)) + +/* * Macros for accessing various structure fields */ diff --git a/usr/src/uts/common/sys/sata/sata_defs.h b/usr/src/uts/common/sys/sata/sata_defs.h index 7f499367ab..c5109ec57a 100644 --- a/usr/src/uts/common/sys/sata/sata_defs.h +++ b/usr/src/uts/common/sys/sata/sata_defs.h @@ -45,6 +45,7 @@ extern "C" { #define SATAC_DOOR_LOCK 0xde /* door lock */ #define SATAC_DOOR_UNLOCK 0xdf /* door unlock */ #define SATAC_IDLE 0xe3 /* idle */ +#define SATAC_STANDBY 0xe2 /* standby */ /* * ATA/ATAPI disk commands (subset) @@ -94,9 +95,11 @@ extern "C" { */ #define SATAC_CHECK_POWER_MODE 0xe5 /* check power mode */ -#define SATA_PWRMODE_STANDBY 0 /* standby mode */ -#define SATA_PWRMODE_IDLE 0x80 /* idle mode */ -#define SATA_PWRMODE_ACTIVE 0xFF /* active or idle mode, rev7 spec */ +#define SATA_PWRMODE_STANDBY 0 /* standby mode */ +#define SATA_PWRMODE_IDLE 0x80 /* idle mode */ +#define SATA_PWRMODE_ACTIVE_SPINDOWN 0x40 /* PM0 and spinning down */ +#define SATA_PWRMODE_ACTIVE_SPINUP 0x41 /* PM0 and spinning up */ +#define SATA_PWRMODE_ACTIVE 0xFF /* active or idle mode */ /* @@ -152,8 +155,8 @@ extern "C" { * Following is the ATA Device Identify data layout */ typedef struct sata_id { -/* WORD */ -/* OFFSET COMMENT */ +/* WORD */ +/* OFFSET COMMENT */ ushort_t ai_config; /* 0 general configuration bits */ ushort_t ai_fixcyls; /* 1 # of cylinders (obsolete) */ ushort_t ai_resv0; /* 2 # reserved */ @@ -245,7 +248,7 @@ typedef struct sata_id { #define SATA_ATA_TYPE_MASK 0x8001 /* ATA Device type mask */ #define SATA_ATA_TYPE 0x0000 /* ATA device */ -#define SATA_REM_MEDIA 0x0080 /* Removable media */ +#define SATA_REM_MEDIA 0x0080 /* Removable media */ #define SATA_INCOMPLETE_DATA 0x0004 /* Incomplete Identify Device data */ #define SATA_CFA_TYPE 0x848a /* CFA feature set device */ @@ -297,6 +300,8 @@ typedef struct sata_id { /* Identify Device: command set supported/enabled bits - words 84 & 87 */ #define SATA_SMART_SELF_TEST_SUPPORTED 0x0002 /* SMART self-test supported */ +/* IDLE IMMEDIATE with UNLOAD FEATURE supported */ +#define SATA_IDLE_UNLOAD_SUPPORTED 0x2000 /* Identify (Packet) Device word 63, ATA/ATAPI-6 & 7 */ #define SATA_MDMA_SEL_MASK 0x0700 /* Multiword DMA selected */ @@ -329,11 +334,11 @@ typedef struct sata_id { /* Identify Packet Device: general config bits - word 0 */ #define SATA_ATAPI_TYPE_MASK 0xc000 -#define SATA_ATAPI_TYPE 0x8000 /* ATAPI device */ -#define SATA_ATAPI_ID_PKT_SZ 0x0003 /* Packet size mask */ +#define SATA_ATAPI_TYPE 0x8000 /* ATAPI device */ +#define SATA_ATAPI_ID_PKT_SZ 0x0003 /* Packet size mask */ #define SATA_ATAPI_ID_PKT_12B 0x0000 /* Packet size 12 bytes */ #define SATA_ATAPI_ID_PKT_16B 0x0001 /* Packet size 16 bytes */ -#define SATA_ATAPI_ID_DRQ_TYPE 0x0060 /* DRQ asserted in 3ms after pkt */ +#define SATA_ATAPI_ID_DRQ_TYPE 0x0060 /* DRQ asserted in 3ms after pkt */ #define SATA_ATAPI_ID_DRQ_INTR 0x0020 /* Obsolete in ATA/ATAPI 7 */ #define SATA_ATAPI_ID_DEV_TYPE 0x0f00 /* device type/command set mask */ @@ -413,9 +418,9 @@ typedef struct sata_id { * Status bits from AT_STATUS register */ #define SATA_STATUS_BSY 0x80 /* controller busy */ -#define SATA_STATUS_DRDY 0x40 /* drive ready */ +#define SATA_STATUS_DRDY 0x40 /* drive ready */ #define SATA_STATUS_DF 0x20 /* device fault */ -#define SATA_STATUS_DSC 0x10 /* seek operation complete */ +#define SATA_STATUS_DSC 0x10 /* seek operation complete */ #define SATA_STATUS_DRQ 0x08 /* data request */ #define SATA_STATUS_CORR 0x04 /* obsolete */ #define SATA_STATUS_IDX 0x02 /* obsolete */ @@ -508,6 +513,9 @@ struct sata_ncq_error_recovery_page { uint8_t ncq_checksum; }; +/* SMART attribute of Start/Stop Count */ +#define SMART_START_STOP_COUNT_ID 0x4 + /* * SMART data structures */ @@ -574,6 +582,24 @@ struct read_log_ext_directory { }; /* + * The definition of CONTROL byte field in SCSI command + * according to SAM 5 + */ +#define CTL_BYTE_VENDOR_MASK 0xc0 +#define CTL_BYTE_NACA_MASK 0x04 + +/* + * The definition of mask in START STOP UNIT command + */ +#define START_STOP_IMMED_MASK 0x01 +#define START_STOP_POWER_COND_MASK 0xF0 +#define START_STOP_START_MASK 0x01 +#define START_STOP_LOEJ_MASK 0x02 +#define START_STOP_NOFLUSH_MASK 0x04 +#define START_STOP_MODIFIER_MASK 0x0f +#define START_STOP_POWER_COND_SHIFT 4 + +/* * SMART specific data * These eventually need to go to a generic scsi hearder file * for now they will reside here @@ -583,6 +609,7 @@ struct read_log_ext_directory { #define PAGE_CODE_SELF_TEST_RESULTS 0x10 #define PAGE_CODE_INFORMATION_EXCEPTIONS 0x2f #define PAGE_CODE_SMART_READ_DATA 0x30 +#define PAGE_CODE_START_STOP_CYCLE_COUNTER 0x0e struct log_parameter { diff --git a/usr/src/uts/common/sys/scsi/generic/inquiry.h b/usr/src/uts/common/sys/scsi/generic/inquiry.h index 952144de51..ddfd683169 100644 --- a/usr/src/uts/common/sys/scsi/generic/inquiry.h +++ b/usr/src/uts/common/sys/scsi/generic/inquiry.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -425,6 +425,24 @@ struct vpd_desc { /* ---- data follows ---- */ }; +/* + * "pm-capable" integer property bit mask definitions + */ +#define PM_CAPABLE_PM_MASK 0x0000ffff /* use lower 16 bits to */ + /* indicate PM mode */ +#define PM_CAPABLE_CCS RDF_CCS +#define PM_CAPABLE_SCSI2 RDF_SCSI2 +#define PM_CAPABLE_SPC RDF_SCSI_SPC +#define PM_CAPABLE_SPC2 RDF_SCSI_SPC2 +#define PM_CAPABLE_SPC3 RDF_SCSI_SPC3 +#define PM_CAPABLE_SPC4 RDF_SCSI_SPC4 +#define PM_CAPABLE_LOG_MASK 0xffff0000 /* use upper 16 bit to */ + /* indicate log specifics */ +#define PM_CAPABLE_LOG_SUPPORTED 0x10000 /* Log page 0xE might be */ + /* supported */ +#define PM_CAPABLE_SMART_LOG 0x20000 /* Log page 0xE reports SMART */ + /* attributes instead of the */ + /* default SCSI Log pages */ #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/sys/scsi/generic/mode.h b/usr/src/uts/common/sys/scsi/generic/mode.h index 3098ea61e9..78463423de 100644 --- a/usr/src/uts/common/sys/scsi/generic/mode.h +++ b/usr/src/uts/common/sys/scsi/generic/mode.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_SCSI_GENERIC_MODE_H #define _SYS_SCSI_GENERIC_MODE_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -391,6 +389,89 @@ struct mode_info_excpt_page { #define MRIE_NO_SENSE 0x5 #define MRIE_ONLY_ON_REQUEST 0x6 +struct mode_info_power_cond { + struct mode_page mode_page; /* common mode page header */ + uchar_t reserved; +#if defined(_BIT_FIELDS_LTOH) + uchar_t standby :1, /* standby bit */ + idle :1, /* idle bit */ + :6; /* reserved */ +#elif defined(_BIT_FIELDS_HTOL) + uchar_t :6, /* reserved */ + idle :1, /* idle bit */ + standby :1; /* standby bit */ +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + uchar_t idle_cond_timer_high; + uchar_t idle_cond_timer_low; + uchar_t standby_cond_timer[4]; +}; + +struct parameter_control { +#if defined(_BIT_FIELDS_LTOH) + uchar_t fmt_link:2, /* format and link bit */ + tmc :2, /* tmc bit */ + etc :1, /* etc bit */ + tsd :1, /* tsd bit */ + reserv :1, /* obsolete */ + du :1; /* du bit */ +#elif defined(_BIT_FIELDS_HTOL) + uchar_t du :1, /* du bit */ + reserv :1, /* obsolete */ + tsd :1, /* tsd bit */ + etc :1, /* etc bit */ + tmc :2, /* tmc bit */ + fmt_link:2; /* format and link bit */ +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif +}; + +struct start_stop_cycle_counter_log { +#if defined(_BIT_FIELDS_LTOH) + uchar_t code :6, /* page code bit */ + spf :1, /* spf bit */ + ds :1; /* ds bit */ +#elif defined(_BIT_FIELDS_HTOL) + uchar_t ds :1, /* ds bit */ + spf :1, /* spf bit */ + code :6; /* page code bit */ +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif + uchar_t sub_page_code; + uchar_t page_len_high; + uchar_t page_len_low; + + uchar_t manufactor_date_high; + uchar_t manufactor_date_low; + struct parameter_control param_1; + uchar_t param_len_1; + uchar_t year_manu[4]; + uchar_t week_manu[2]; + + uchar_t account_date_high; + uchar_t account_date_low; + struct parameter_control param_2; + uchar_t param_len_2; + uchar_t year_account[4]; + uchar_t week_account[2]; + + uchar_t lifetime_code_high; + uchar_t lifetime_code_low; + struct parameter_control param_3; + uchar_t param_len_3; + uchar_t cycle_lifetime[4]; + + uchar_t cycle_code_high; + uchar_t cycle_code_low; + struct parameter_control param_4; + uchar_t param_len_4; + uchar_t cycle_accumulated[4]; +}; + + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/sys/scsi/impl/uscsi.h b/usr/src/uts/common/sys/scsi/impl/uscsi.h index fe6495856a..afce8ec081 100644 --- a/usr/src/uts/common/sys/scsi/impl/uscsi.h +++ b/usr/src/uts/common/sys/scsi/impl/uscsi.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -152,8 +152,14 @@ struct uscsi_cmd32 { #define USCSI_NOPARITY 0x00000010 /* run command without parity */ #define USCSI_NODISCON 0x00000020 /* run command without disconnects */ +/* + * suitable for FMA module for PM purpose + */ +#define USCSI_PMFAILFAST 0x00100000 /* fail command if device is */ + /* in low power */ + -#define USCSI_RESERVED 0xfff00000 /* Reserved Bits, must be zero */ +#define USCSI_RESERVED 0xffe00000 /* Reserved Bits, must be zero */ struct uscsi_rqs { int rqs_flags; /* see below */ diff --git a/usr/src/uts/common/sys/scsi/targets/sddef.h b/usr/src/uts/common/sys/scsi/targets/sddef.h index 93705cc42b..0c3823792e 100644 --- a/usr/src/uts/common/sys/scsi/targets/sddef.h +++ b/usr/src/uts/common/sys/scsi/targets/sddef.h @@ -125,7 +125,7 @@ extern "C" { * Macro to retrieve the DDI instance number from the given buf struct. * The instance number is encoded in the minor device number. */ -#define SD_GET_INSTANCE_FROM_BUF(bp) \ +#define SD_GET_INSTANCE_FROM_BUF(bp) \ (getminor((bp)->b_edev) >> SDUNIT_SHIFT) @@ -447,7 +447,13 @@ struct sd_lun { /* sent in sdclose */ un_f_devid_transport_defined :1, /* devid defined by transport */ un_f_rmw_type :2, /* RMW type */ - un_f_reserved :10; + un_f_power_condition_disabled :1, /* power condition disabled */ + /* through sd configuration */ + un_f_power_condition_supported :1, /* support power condition */ + /* field by hardware */ + un_f_pm_log_sense_smart :1, /* log sense support SMART */ + /* feature attribute */ + un_f_reserved :7; /* Ptr to table of strings for ASC/ASCQ error message printing */ struct scsi_asq_key_strings *un_additional_codes; @@ -1783,11 +1789,28 @@ struct sd_fm_internal { #define SD_CONF_NOT_USED 32 /* - * Return values from "pm-capable" property + * "pm-capable" property values and macros */ #define SD_PM_CAPABLE_UNDEFINED -1 -#define SD_PM_CAPABLE_FALSE 0 -#define SD_PM_CAPABLE_TRUE 1 + +#define SD_PM_CAPABLE_IS_UNDEFINED(pm_cap) \ + (pm_cap == SD_PM_CAPABLE_UNDEFINED) + +#define SD_PM_CAPABLE_IS_FALSE(pm_cap) \ + ((pm_cap & PM_CAPABLE_PM_MASK) == 0) + +#define SD_PM_CAPABLE_IS_TRUE(pm_cap) \ + (!SD_PM_CAPABLE_IS_UNDEFINED(pm_cap) && \ + ((pm_cap & PM_CAPABLE_PM_MASK) > 0)) + +#define SD_PM_CAPABLE_IS_SPC_4(pm_cap) \ + ((pm_cap & PM_CAPABLE_PM_MASK) == PM_CAPABLE_SPC4) + +#define SD_PM_CAP_LOG_SUPPORTED(pm_cap) \ + ((pm_cap & PM_CAPABLE_LOG_SUPPORTED) ? TRUE : FALSE) + +#define SD_PM_CAP_SMART_LOG(pm_cap) \ + ((pm_cap & PM_CAPABLE_SMART_LOG) ? TRUE : FALSE) /* * Property data values used in static configuration table @@ -2022,6 +2045,13 @@ struct sd_fm_internal { #define SD_CONF_BSET_CACHE_IS_NV (1 << SD_CONF_SET_CACHE_IS_NV) /* + * Bit in flags telling driver that the power condition flag from [s]sd.conf + * [s]sd-config-list and driver table. + */ +#define SD_CONF_SET_PC_DISABLED 19 +#define SD_CONF_BSET_PC_DISABLED (1 << SD_CONF_SET_PC_DISABLED) + +/* * This is the number of items currently settable in the sd.conf * sd-config-list. The mask value is defined for parameter checking. The * item count and mask should be updated when new properties are added. @@ -2040,6 +2070,7 @@ typedef struct { int sdt_disk_sort_dis; int sdt_lun_reset_enable; int sdt_suppress_cache_flush; + int sdt_power_condition_dis; } sd_tunables; /* Type definition for static configuration table entries */ @@ -2050,13 +2081,21 @@ typedef struct sd_disk_config { } sd_disk_config_t; /* - * byte 4 options for 1bh command + * first 2 bits of byte 4 options for 1bh command */ #define SD_TARGET_STOP 0x00 #define SD_TARGET_START 0x01 #define SD_TARGET_EJECT 0x02 #define SD_TARGET_CLOSE 0x03 +/* + * power condition of byte 4 for 1bh command + */ +#define SD_TARGET_START_VALID 0x00 +#define SD_TARGET_ACTIVE 0x01 +#define SD_TARGET_IDLE 0x02 +#define SD_TARGET_STANDBY 0x03 + #define SD_MODE_SENSE_PAGE3_CODE 0x03 #define SD_MODE_SENSE_PAGE4_CODE 0x04 @@ -2143,7 +2182,47 @@ _NOTE(SCHEME_PROTECTS_DATA("Unshared data", mode_header_grp2)) #define SD_SPINDLE_UNINIT (-1) #define SD_SPINDLE_OFF 0 #define SD_SPINDLE_ON 1 -#define SD_PM_NOT_SUPPORTED 2 +#define SD_SPINDLE_STOPPED 0 +#define SD_SPINDLE_STANDBY 1 +#define SD_SPINDLE_IDLE 2 +#define SD_SPINDLE_ACTIVE 3 +#define SD_PM_NOT_SUPPORTED 4 + +/* + * Power method flag + */ +#define SD_START_STOP 0 +#define SD_POWER_CONDITION 1 + + +/* + * Number of power level for start stop or power condition + */ +#define SD_PM_NUM_LEVEL_SSU_SS 2 +#define SD_PM_NUM_LEVEL_SSU_PC 4 + +/* + * SD internal power state change flag + */ +#define SD_PM_STATE_CHANGE 0 +#define SD_PM_STATE_ROLLBACK 1 + +/* + * Power attribute table + */ +typedef struct disk_power_attr_ss { + char *pm_comp[SD_PM_NUM_LEVEL_SSU_SS + 2]; /* pm component */ + int ran_perf[SD_PM_NUM_LEVEL_SSU_SS]; /* random performance */ + int pwr_saving[SD_PM_NUM_LEVEL_SSU_SS]; /* power saving */ + int latency[SD_PM_NUM_LEVEL_SSU_SS]; /* latency */ +}sd_power_attr_ss; + +typedef struct disk_power_attr_pc { + char *pm_comp[SD_PM_NUM_LEVEL_SSU_PC + 2]; /* pm component */ + int ran_perf[SD_PM_NUM_LEVEL_SSU_PC]; /* random performance */ + int pwr_saving[SD_PM_NUM_LEVEL_SSU_PC]; /* power saving */ + int latency[SD_PM_NUM_LEVEL_SSU_PC]; /* latency */ +}sd_power_attr_pc; /* @@ -2166,6 +2245,30 @@ _NOTE(SCHEME_PROTECTS_DATA("Unshared data", mode_header_grp2)) #define SD_OK_TO_SUSPEND_SCSI_WATCHER(un) (un->un_swr_token != NULL) #define SD_DEVICE_IS_IN_LOW_POWER(un) ((un->un_f_pm_is_enabled) && \ (un->un_pm_count < 0)) +#define SD_PM_STATE_ACTIVE(un) \ + (un->un_f_power_condition_supported ? \ + SD_SPINDLE_ACTIVE : SD_SPINDLE_ON) +#define SD_PM_STATE_STOPPED(un) \ + (un->un_f_power_condition_supported ? \ + SD_SPINDLE_STOPPED : SD_SPINDLE_OFF) +#define SD_PM_IS_LEVEL_VALID(un, level) \ + ((un->un_f_power_condition_supported && \ + level >= SD_SPINDLE_STOPPED && \ + level <= SD_SPINDLE_ACTIVE) || \ + (!un->un_f_power_condition_supported && \ + level >= SD_SPINDLE_OFF && \ + level <= SD_SPINDLE_ON)) +#define SD_PM_IS_IO_CAPABLE(un, level) \ + ((un->un_f_power_condition_supported && \ + sd_pwr_pc.ran_perf[level] > 0) || \ + (!un->un_f_power_condition_supported && \ + sd_pwr_ss.ran_perf[level] > 0)) +#define SD_PM_STOP_MOTOR_NEEDED(un, level) \ + ((un->un_f_power_condition_supported && \ + level <= SD_SPINDLE_STANDBY) || \ + (!un->un_f_power_condition_supported && \ + level == SD_SPINDLE_OFF)) + /* * Could move this define to some thing like log sense.h in SCSA headers * But for now let it live here. diff --git a/usr/src/uts/common/sys/sunpm.h b/usr/src/uts/common/sys/sunpm.h index c02f17a8bb..f505eb2237 100644 --- a/usr/src/uts/common/sys/sunpm.h +++ b/usr/src/uts/common/sys/sunpm.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_SUNPM_H #define _SYS_SUNPM_H -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Sun Specific Power Management definitions */ @@ -51,9 +49,10 @@ extern "C" { #ifdef _KERNEL /* - * Power cycle transition check is supported for these devices. + * Power cycle transition check is supported for SCSI and SATA devices. */ #define DC_SCSI_FORMAT 0x1 /* SCSI */ +#define DC_SMART_FORMAT 0x2 /* SMART */ #define DC_SCSI_MFR_LEN 6 /* YYYYWW */ @@ -64,10 +63,17 @@ struct pm_scsi_cycles { int flag; /* reserved for future */ }; +struct pm_smart_count { + int allowed; /* normalized max cycles allowed */ + int consumed; /* normalized consumed cycles */ + int flag; /* type of cycles */ +}; + struct pm_trans_data { int format; /* data format */ union { struct pm_scsi_cycles scsi_cycles; + struct pm_smart_count smart_count; } un; }; |