Mercurial > illumos > illumos-gate
changeset 4014:360fee62f0eb
6539787 marvell88sx driver needs to support multiple block PIO commands
6538627 x4500 Message logs contain multiple device disk resets but nothing is logged in FMA. CU is concerned
6401058 sata framework should support drive firmware download operation
author | ls24207 |
---|---|
date | Wed, 11 Apr 2007 19:57:44 -0700 |
parents | da49edb39e10 |
children | 9a00a50342cd |
files | usr/src/uts/common/io/sata/impl/sata.c usr/src/uts/common/sys/sata/sata_defs.h usr/src/uts/common/sys/sata/sata_hba.h |
diffstat | 3 files changed, 344 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/sata/impl/sata.c Wed Apr 11 11:30:21 2007 -0700 +++ b/usr/src/uts/common/io/sata/impl/sata.c Wed Apr 11 19:57:44 2007 -0700 @@ -179,6 +179,7 @@ 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 *); @@ -187,6 +188,7 @@ static void sata_txlt_rw_completion(sata_pkt_t *); static void sata_txlt_atapi_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( @@ -229,6 +231,8 @@ struct read_log_ext_directory *); static void sata_gen_sysevent(sata_hba_inst_t *, sata_address_t *, int); static void sata_xlate_errors(sata_pkt_txlate_t *); +static void sata_decode_device_error(sata_pkt_txlate_t *, + struct scsi_extended_sense *); static void sata_set_device_removed(dev_info_t *); static boolean_t sata_check_device_removed(dev_info_t *); static void sata_set_target_node_cleanup(sata_hba_inst_t *, int cport); @@ -2821,6 +2825,7 @@ * SCMD_READ_G4 * SCMD_READ_G5 * SCMD_WRITE + * SCMD_WRITE_BUFFER * SCMD_WRITE_G1 * SCMD_WRITE_G4 * SCMD_WRITE_G5 @@ -2974,6 +2979,11 @@ case SCMD_READ_G5: rval = sata_txlt_read(spx); break; + case SCMD_WRITE_BUFFER: + if (bp != NULL && (bp->b_flags & (B_PHYS | B_PAGEIO))) + bp_mapin(bp); + rval = sata_txlt_write_buffer(spx); + break; case SCMD_WRITE: case SCMD_WRITE_G1: @@ -5481,6 +5491,302 @@ /* + * Implements SCSI SBC WRITE BUFFER command download microcode option + */ +static int +sata_txlt_write_buffer(sata_pkt_txlate_t *spx) +{ +#define WB_DOWNLOAD_MICROCODE_AND_REVERT_MODE 4 +#define WB_DOWNLOAD_MICROCODE_AND_SAVE_MODE 5 + + struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; + sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd; + struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp; + struct scsi_extended_sense *sense; + int rval, mode, sector_count; + sata_hba_inst_t *shi = SATA_TXLT_HBA_INST(spx); + int cport = SATA_TXLT_CPORT(spx); + boolean_t synch; + + synch = (spx->txlt_sata_pkt->satapkt_op_mode & SATA_OPMODE_SYNCH) != 0; + mode = scsipkt->pkt_cdbp[1] & 0x1f; + + SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, + "sata_txlt_write_buffer, mode 0x%x\n", mode); + + mutex_enter(&(SATA_TXLT_CPORT_MUTEX(spx))); + + if ((rval = sata_txlt_generic_pkt_info(spx)) != TRAN_ACCEPT) { + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + return (rval); + } + + scmd->satacmd_flags.sata_data_direction = SATA_DIR_WRITE; + + scsipkt->pkt_reason = CMD_CMPLT; + scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | + STATE_SENT_CMD | STATE_GOT_STATUS; + + /* + * The SCSI to ATA translation specification only calls + * for WB_DOWNLOAD_MICROCODE_AND_SAVE_MODE. + * WB_DOWNLOAD_MICROC_AND_REVERT_MODE is implemented, but + * ATA 8 (draft) got rid of download microcode for temp + * and it is even optional for ATA 7, so it may be aborted. + * WB_DOWNLOAD_MICROCODE_WITH_OFFSET is not implemented as + * it is not specified and the buffer offset for SCSI is a 16-bit + * value in bytes, but for ATA it is a 16-bit offset in 512 byte + * sectors. Thus the offset really doesn't buy us anything. + * If and when ATA 8 is stabilized and the SCSI to ATA specification + * is revised, this can be revisisted. + */ + /* Reject not supported request */ + switch (mode) { + case WB_DOWNLOAD_MICROCODE_AND_REVERT_MODE: + scmd->satacmd_features_reg = SATA_DOWNLOAD_MCODE_TEMP; + break; + case WB_DOWNLOAD_MICROCODE_AND_SAVE_MODE: + scmd->satacmd_features_reg = SATA_DOWNLOAD_MCODE_SAVE; + break; + default: + goto bad_param; + } + + *scsipkt->pkt_scbp = STATUS_GOOD; /* Presumed outcome */ + + scmd->satacmd_cmd_reg = SATAC_DOWNLOAD_MICROCODE; + if ((bp->b_bcount % SATA_DISK_SECTOR_SIZE) != 0) + goto bad_param; + sector_count = bp->b_bcount / SATA_DISK_SECTOR_SIZE; + scmd->satacmd_sec_count_lsb = (uint8_t)sector_count; + scmd->satacmd_lba_low_lsb = ((uint16_t)sector_count) >> 8; + scmd->satacmd_lba_mid_lsb = 0; + scmd->satacmd_lba_high_lsb = 0; + scmd->satacmd_device_reg = 0; + spx->txlt_sata_pkt->satapkt_comp = + sata_txlt_download_mcode_cmd_completion; + scmd->satacmd_addr_type = 0; + + /* 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); + } + + mutex_exit(&SATA_CPORT_MUTEX(shi, cport)); + /* + * 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. + */ + if (synch) { + SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, + "synchronous execution\n", NULL); + /* Calling pre-set completion routine */ + (*spx->txlt_sata_pkt->satapkt_comp)(spx->txlt_sata_pkt); + } + return (TRAN_ACCEPT); + +bad_param: + mutex_exit(&(SATA_TXLT_CPORT_MUTEX(spx))); + *scsipkt->pkt_scbp = STATUS_CHECK; + sense = sata_arq_sense(spx); + sense->es_key = KEY_ILLEGAL_REQUEST; + sense->es_add_code = SD_SCSI_INVALID_FIELD_IN_CDB; + 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) == 0) { + /* Scheduling the callback failed */ + rval = TRAN_BUSY; + } + } + return (rval); +} + + +/* + * Retry identify device when command returns SATA_INCOMPLETE_DATA + * after doing a firmware download. + */ +static void +sata_retry_identify_device(void *arg) +{ +#define DOWNLOAD_WAIT_TIME_SECS 60 +#define DOWNLOAD_WAIT_INTERVAL_SECS 1 + int rval; + int retry_cnt; + sata_pkt_t *sata_pkt = (sata_pkt_t *)arg; + sata_pkt_txlate_t *spx = + (sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private; + struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt; + sata_hba_inst_t *sata_hba_inst = spx->txlt_sata_hba_inst; + sata_device_t sata_device = spx->txlt_sata_pkt->satapkt_device; + sata_drive_info_t *sdinfo; + + /* + * Before returning good status, probe device. + * Device probing will get IDENTIFY DEVICE data, if possible. + * The assumption is that the new microcode is applied by the + * device. It is a caller responsibility to verify this. + */ + for (retry_cnt = 0; + retry_cnt < DOWNLOAD_WAIT_TIME_SECS / DOWNLOAD_WAIT_INTERVAL_SECS; + retry_cnt++) { + rval = sata_probe_device(sata_hba_inst, &sata_device); + + if (rval == SATA_SUCCESS) { /* Set default features */ + sdinfo = sata_get_device_info(sata_hba_inst, + &sata_device); + if (sata_initialize_device(sata_hba_inst, sdinfo) != + SATA_SUCCESS) { + /* retry */ + (void) sata_initialize_device(sata_hba_inst, + sdinfo); + } + if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 && + scsipkt->pkt_comp != NULL) + (*scsipkt->pkt_comp)(scsipkt); + return; + } else if (rval == SATA_RETRY) { + delay(drv_usectohz(1000000 * + DOWNLOAD_WAIT_INTERVAL_SECS)); + continue; + } else /* failed - no reason to retry */ + break; + } + + /* + * Something went wrong, device probing failed. + */ + SATA_LOG_D((sata_hba_inst, CE_WARN, + "Cannot probe device after downloading microcode\n")); + + /* Reset device to force retrying the probe. */ + (void) (*SATA_RESET_DPORT_FUNC(sata_hba_inst)) + (SATA_DIP(sata_hba_inst), &sata_device); + + if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 && + scsipkt->pkt_comp != NULL) + (*scsipkt->pkt_comp)(scsipkt); +} + +/* + * Translate completion status of download microcode command. + * pkt completion_reason is checked to determine the completion status. + * Do scsi callback if necessary (FLAG_NOINTR == 0) + * + * Note: this function may be called also for synchronously executed + * command. + * This function may be used only if scsi_pkt is non-NULL. + */ +static void +sata_txlt_download_mcode_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; + struct scsi_extended_sense *sense; + sata_drive_info_t *sdinfo; + sata_hba_inst_t *sata_hba_inst = spx->txlt_sata_hba_inst; + sata_device_t sata_device = spx->txlt_sata_pkt->satapkt_device; + int rval; + + scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | + STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS; + if (sata_pkt->satapkt_reason == SATA_PKT_COMPLETED) { + scsipkt->pkt_reason = CMD_CMPLT; + + rval = sata_probe_device(sata_hba_inst, &sata_device); + + if (rval == SATA_SUCCESS) { /* Set default features */ + sdinfo = sata_get_device_info(sata_hba_inst, + &sata_device); + if (sata_initialize_device(sata_hba_inst, sdinfo) != + SATA_SUCCESS) { + /* retry */ + (void) sata_initialize_device(sata_hba_inst, + sdinfo); + } + if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 && + scsipkt->pkt_comp != NULL) + (*scsipkt->pkt_comp)(scsipkt); + } else { + (void) ddi_taskq_dispatch( + (ddi_taskq_t *)SATA_TXLT_TASKQ(spx), + sata_retry_identify_device, + (void *)sata_pkt, TQ_NOSLEEP); + } + + + } else { + /* Something went wrong, microcode download command failed */ + scsipkt->pkt_reason = CMD_INCOMPLETE; + *scsipkt->pkt_scbp = STATUS_CHECK; + sense = sata_arq_sense(spx); + switch (sata_pkt->satapkt_reason) { + case SATA_PKT_PORT_ERROR: + /* + * We have no device data. Assume no data transfered. + */ + sense->es_key = KEY_HARDWARE_ERROR; + break; + + case SATA_PKT_DEV_ERROR: + if (sata_pkt->satapkt_cmd.satacmd_status_reg & + SATA_STATUS_ERR) { + /* + * determine dev error reason from error + * reg content + */ + sata_decode_device_error(spx, sense); + break; + } + /* No extended sense key - no info available */ + break; + + case SATA_PKT_TIMEOUT: + /* scsipkt->pkt_reason = CMD_TIMEOUT; */ + scsipkt->pkt_reason = CMD_INCOMPLETE; + /* No extended sense key ? */ + break; + + case SATA_PKT_ABORTED: + scsipkt->pkt_reason = CMD_ABORTED; + /* No extended sense key ? */ + break; + + case SATA_PKT_RESET: + /* pkt aborted by an explicit reset from a host */ + scsipkt->pkt_reason = CMD_RESET; + break; + + default: + SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN, + "sata_txlt_nodata_cmd_completion: " + "invalid packet completion reason %d", + sata_pkt->satapkt_reason)); + scsipkt->pkt_reason = CMD_TRAN_ERR; + 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) + /* scsi callback required */ + (*scsipkt->pkt_comp)(scsipkt); + } +} + + +/* * NOTE: NOT FUNCTIONAL IMPLEMENTATION. THIS IS A PLACEHOLDER for the function * that will be fixed in phase 2 of the development. * Currently ATAPI is not supported. ATAPI devices are threated as not-valid @@ -8013,7 +8319,7 @@ * update sata port state and set device type */ sata_update_port_info(sata_hba_inst, sata_device); - cportinfo->cport_state |= SATA_STATE_PROBED; + cportinfo->cport_state &= ~SATA_STATE_PROBING; /* * Sanity check - Port is active? Is the link active? @@ -8363,10 +8669,12 @@ * sata_device has to refer to the valid sata port(s) for HBA described * by sata_hba_inst structure. * - * Returns: SATA_SUCCESS if device type was successfully probed and port-linked - * drive info structure was updated; + * Returns: + * SATA_SUCCESS if device type was successfully probed and port-linked + * drive info structure was updated; * SATA_FAILURE if there is no device, or device was not probed - * successully. + * successully; + * SATA_RETRY if device probe can be retried later. * If a device cannot be identified, sata_device's dev_state and dev_type * fields are set to unknown. * There are no retries in this function. Any retries should be managed by @@ -8415,7 +8723,7 @@ sata_device->satadev_addr.cport))); new_sdinfo.satadrv_type = SATA_DTYPE_ATADISK; rval = sata_identify_device(sata_hba_inst, &new_sdinfo); - if (rval == 1) { + if (rval == SATA_RETRY) { /* We may try to check for ATAPI device */ if (SATA_FEATURES(sata_hba_inst) & SATA_CTLF_ATAPI) { /* @@ -8425,10 +8733,9 @@ new_sdinfo.satadrv_type = SATA_DTYPE_ATAPICD; rval = sata_identify_device(sata_hba_inst, &new_sdinfo); } - } - if (rval == -1) + } else if (rval == SATA_FAILURE) goto failure; - if (rval == 0) { + else /* if (rval == SATA_SUCCESS) */ { /* * Got something responding to ATA Identify Device or to * Identify Packet Device cmd. @@ -8495,7 +8802,7 @@ } mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, sata_device->satadev_addr.cport))); - return (SATA_FAILURE); + return (rval); } @@ -8563,9 +8870,10 @@ * Cannot be called from interrupt level. * * Returns: - * 0 if device was identified as a supported device, - * 1 if device was not idenitfied but identify attempt could be retried, - * -1if device was not idenitfied and identify attempt should not be retried. + * SATA_SUCCESS if the device was identified as a supported device, + * SATA_RETRY if the device was not identified but could be retried, + * SATA_FAILURE if the device was not identified and identify attempt + * should not be retried. */ static int sata_identify_device(sata_hba_inst_t *sata_hba_inst, @@ -8609,7 +8917,7 @@ sata_log(sata_hba_inst, CE_WARN, "SATA disk device at port %d does not support LBA", sdinfo->satadrv_addr.cport); - rval = -1; + rval = SATA_FAILURE; goto fail_unknown; } } @@ -8630,11 +8938,11 @@ "mode 4 or higher", sdinfo->satadrv_addr.cport); SATA_LOG_D((sata_hba_inst, CE_WARN, "mode 4 or higher required, %d supported", i)); - rval = -1; + rval = SATA_FAILURE; goto fail_unknown; } - return (0); + return (SATA_SUCCESS); fail_unknown: /* Invalidate sata_drive_info ? */ @@ -9443,9 +9751,9 @@ * device identify command). * * Returns: - * 0 if cmd succeeded - * 1 if cmd was rejected and could be retried, - * -1if cmd failed and should not be retried (port error) + * SATA_SUCCESS if cmd succeeded + * SATA_RETRY if cmd was rejected and could be retried, + * SATA_FAILURE if cmd failed and should not be retried (port error) * * Cannot be called in an interrupt context. */ @@ -9466,7 +9774,7 @@ spkt = sata_pkt_alloc(spx, SLEEP_FUNC); if (spkt == NULL) { kmem_free(spx, sizeof (sata_pkt_txlate_t)); - return (1); /* may retry later */ + return (SATA_RETRY); /* may retry later */ } /* address is needed now */ spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr; @@ -9481,7 +9789,7 @@ SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_fetch_device_identify_data: " "cannot allocate buffer for ID")); - return (1); /* may retry later */ + return (SATA_RETRY); /* may retry later */ } /* Fill sata_pkt */ @@ -9524,7 +9832,7 @@ "SATA disk device at port %d - " "partial Identify Data", sdinfo->satadrv_addr.cport)); - rval = 1; /* may retry later */ + rval = SATA_RETRY; /* may retry later */ goto fail; } /* Update sata_drive_info */ @@ -9581,23 +9889,23 @@ if ((sdinfo->satadrv_features_support & SATA_DEV_F_NCQ) || (sdinfo->satadrv_features_support & SATA_DEV_F_TCQ)) ++sdinfo->satadrv_queue_depth; - rval = 0; + rval = SATA_SUCCESS; } else { /* * Woops, no Identify Data. */ if (rval == SATA_TRAN_BUSY || rval == SATA_TRAN_QUEUE_FULL) { - rval = 1; /* may retry later */ + rval = SATA_RETRY; /* may retry later */ } else if (rval == SATA_TRAN_ACCEPTED) { if (spkt->satapkt_reason == SATA_PKT_DEV_ERROR || spkt->satapkt_reason == SATA_PKT_ABORTED || spkt->satapkt_reason == SATA_PKT_TIMEOUT || spkt->satapkt_reason == SATA_PKT_RESET) - rval = 1; /* may retry later */ + rval = SATA_RETRY; /* may retry later */ else - rval = -1; + rval = SATA_FAILURE; } else { - rval = -1; + rval = SATA_FAILURE; } } fail:
--- a/usr/src/uts/common/sys/sata/sata_defs.h Wed Apr 11 11:30:21 2007 -0700 +++ b/usr/src/uts/common/sys/sata/sata_defs.h Wed Apr 11 19:57:44 2007 -0700 @@ -50,6 +50,7 @@ * ATA/ATAPI disk commands (subset) */ #define SATAC_DEVICE_RESET 0x08 /* ATAPI device reset */ +#define SATAC_DOWNLOAD_MICROCODE 0x92 /* Download microcode */ #define SATAC_EJECT 0xed /* media eject */ #define SATAC_FLUSH_CACHE 0xe7 /* flush write-cache */ #define SATAC_ID_DEVICE 0xec /* IDENTIFY DEVICE */ @@ -128,6 +129,13 @@ #define SATAC_TRANSFER_MODE_MULTI_WORD_DMA 0x20 #define SATAC_TRANSFER_MODE_ULTRA_DMA 0x40 +/* + * Download microcode subcommands + */ +#define SATA_DOWNLOAD_MCODE_TEMP 1 /* Revert on/ reset/pwr cycle */ +#define SATA_DOWNLOAD_MCODE_SAVE 7 /* No offset, keep mcode */ + + /* Generic ATA definitions */ #define SATA_TAG_QUEUING_SHIFT 3
--- a/usr/src/uts/common/sys/sata/sata_hba.h Wed Apr 11 11:30:21 2007 -0700 +++ b/usr/src/uts/common/sys/sata/sata_hba.h Wed Apr 11 19:57:44 2007 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -47,6 +47,7 @@ #endif #define SATA_SUCCESS 0 +#define SATA_RETRY 1 #define SATA_FAILURE -1