summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJane Chu <Jane.Chu@Sun.COM>2009-07-20 10:00:36 -0700
committerJane Chu <Jane.Chu@Sun.COM>2009-07-20 10:00:36 -0700
commit2ee4dfc799525d35706ee64a901cd970a7080bb6 (patch)
tree3e498f3775dd50b9f96f965f90e405dc06ee8985
parent0ba964ea1a9461d43c8e39e5a9a3ab857e6786c7 (diff)
downloadillumos-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.c985
-rw-r--r--usr/src/uts/common/io/scsi/targets/sd.c595
-rw-r--r--usr/src/uts/common/os/sunpm.c13
-rw-r--r--usr/src/uts/common/sys/sata/impl/sata.h50
-rw-r--r--usr/src/uts/common/sys/sata/sata_defs.h49
-rw-r--r--usr/src/uts/common/sys/scsi/generic/inquiry.h20
-rw-r--r--usr/src/uts/common/sys/scsi/generic/mode.h87
-rw-r--r--usr/src/uts/common/sys/scsi/impl/uscsi.h10
-rw-r--r--usr/src/uts/common/sys/scsi/targets/sddef.h117
-rw-r--r--usr/src/uts/common/sys/sunpm.h14
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;
};