+/* Search IOC page 3 to determine if this is hidden physical disk
+ */
+static int
+mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id)
+{
+ int i;
+
+ if (!ioc->raid_data.isRaid || !ioc->raid_data.pIocPg3)
+ return 0;
+
+ for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
+ if (id == ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID)
+ return 1;
+ }
+
+ return 0;
+}
+
+#ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_domainValidation - Top level handler for domain validation.
+ * @hd: Pointer to MPT_SCSI_HOST structure.
+ *
+ * Uses the ISR, but with special processing.
+ * Called from schedule, should not be in interrupt mode.
+ * While thread alive, do dv for all devices needing dv
+ *
+ * Return: None.
+ */
+static void
+mptscsih_domainValidation(void *arg)
+{
+ MPT_SCSI_HOST *hd;
+ MPT_ADAPTER *ioc;
+ unsigned long flags;
+ int id, maxid, dvStatus, did;
+ int ii, isPhysDisk;
+
+ spin_lock_irqsave(&dvtaskQ_lock, flags);
+ dvtaskQ_active = 1;
+ if (dvtaskQ_release) {
+ dvtaskQ_active = 0;
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+
+ /* For this ioc, loop through all devices and do dv to each device.
+ * When complete with this ioc, search through the ioc list, and
+ * for each scsi ioc found, do dv for all devices. Exit when no
+ * device needs dv.
+ */
+ did = 1;
+ while (did) {
+ did = 0;
+ list_for_each_entry(ioc, &ioc_list, list) {
+ spin_lock_irqsave(&dvtaskQ_lock, flags);
+ if (dvtaskQ_release) {
+ dvtaskQ_active = 0;
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+
+ msleep(250);
+
+ /* DV only to SPI adapters */
+ if (ioc->bus_type != SPI)
+ continue;
+
+ /* Make sure everything looks ok */
+ if (ioc->sh == NULL)
+ continue;
+
+ hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
+ if (hd == NULL)
+ continue;
+
+ if ((ioc->spi_data.forceDv & MPT_SCSICFG_RELOAD_IOC_PG3) != 0) {
+ mpt_read_ioc_pg_3(ioc);
+ if (ioc->raid_data.pIocPg3) {
+ Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk;
+ int numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks;
+
+ while (numPDisk) {
+ if (ioc->spi_data.dvStatus[pPDisk->PhysDiskID] & MPT_SCSICFG_DV_NOT_DONE)
+ ioc->spi_data.dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV;
+
+ pPDisk++;
+ numPDisk--;
+ }
+ }
+ ioc->spi_data.forceDv &= ~MPT_SCSICFG_RELOAD_IOC_PG3;
+ }
+
+ maxid = min_t(int, ioc->sh->max_id, MPT_MAX_SCSI_DEVICES);
+
+ for (id = 0; id < maxid; id++) {
+ spin_lock_irqsave(&dvtaskQ_lock, flags);
+ if (dvtaskQ_release) {
+ dvtaskQ_active = 0;
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+ dvStatus = hd->ioc->spi_data.dvStatus[id];
+
+ if (dvStatus & MPT_SCSICFG_NEED_DV) {
+ did++;
+ hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_DV_PENDING;
+ hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_NEED_DV;
+
+ msleep(250);
+
+ /* If hidden phys disk, block IO's to all
+ * raid volumes
+ * else, process normally
+ */
+ isPhysDisk = mptscsih_is_phys_disk(ioc, id);
+ if (isPhysDisk) {
+ for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
+ if (hd->ioc->raid_data.isRaid & (1 << ii)) {
+ hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_PENDING;
+ }
+ }
+ }
+
+ if(mpt_alt_ioc_wait(hd->ioc)!=0) {
+ ddvprintk((MYIOC_s_WARN_FMT "alt_ioc busy!\n",
+ hd->ioc->name));
+ continue;
+ }
+
+ if (mptscsih_doDv(hd, 0, id) == 1) {
+ /* Untagged device was busy, try again
+ */
+ hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_NEED_DV;
+ hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_PENDING;
+ } else {
+ /* DV is complete. Clear flags.
+ */
+ hd->ioc->spi_data.dvStatus[id] &= ~(MPT_SCSICFG_DV_NOT_DONE | MPT_SCSICFG_DV_PENDING);
+ }
+
+ spin_lock(&hd->ioc->initializing_hba_lock);
+ hd->ioc->initializing_hba_lock_flag=0;
+ spin_unlock(&hd->ioc->initializing_hba_lock);
+
+ if (isPhysDisk) {
+ for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
+ if (hd->ioc->raid_data.isRaid & (1 << ii)) {
+ hd->ioc->spi_data.dvStatus[ii] &= ~MPT_SCSICFG_DV_PENDING;
+ }
+ }
+ }
+
+ if (hd->ioc->spi_data.noQas)
+ mptscsih_qas_check(hd, id);
+ }
+ }
+ }
+ }
+
+ spin_lock_irqsave(&dvtaskQ_lock, flags);
+ dvtaskQ_active = 0;
+ spin_unlock_irqrestore(&dvtaskQ_lock, flags);
+
+ return;
+}
+
+/* Write SDP1 if no QAS has been enabled
+ */
+static void
+mptscsih_qas_check(MPT_SCSI_HOST *hd, int id)
+{
+ VirtTarget *vtarget;
+ int ii;
+
+ if (hd->Targets == NULL)
+ return;
+
+ for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) {
+ if (ii == id)
+ continue;
+
+ if ((hd->ioc->spi_data.dvStatus[ii] & MPT_SCSICFG_DV_NOT_DONE) != 0)
+ continue;
+
+ vtarget = hd->Targets[ii];
+
+ if ((vtarget != NULL) && (!vtarget->raidVolume)) {
+ if ((vtarget->negoFlags & hd->ioc->spi_data.noQas) == 0) {
+ vtarget->negoFlags |= hd->ioc->spi_data.noQas;
+ dnegoprintk(("writeSDP1: id=%d flags=0\n", id));
+ mptscsih_writeSDP1(hd, 0, ii, 0);
+ }
+ } else {
+ if (mptscsih_is_phys_disk(hd->ioc, ii) == 1) {
+ dnegoprintk(("writeSDP1: id=%d SCSICFG_USE_NVRAM\n", id));
+ mptscsih_writeSDP1(hd, 0, ii, MPT_SCSICFG_USE_NVRAM);
+ }
+ }
+ }
+ return;
+}
+
+
+
+#define MPT_GET_NVRAM_VALS 0x01
+#define MPT_UPDATE_MAX 0x02
+#define MPT_SET_MAX 0x04
+#define MPT_SET_MIN 0x08
+#define MPT_FALLBACK 0x10
+#define MPT_SAVE 0x20
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_doDv - Perform domain validation to a target.
+ * @hd: Pointer to MPT_SCSI_HOST structure.
+ * @portnum: IOC port number.
+ * @target: Physical ID of this target
+ *
+ * Uses the ISR, but with special processing.
+ * MUST be single-threaded.
+ * Test will exit if target is at async & narrow.
+ *
+ * Return: None.
+ */
+static int
+mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id)
+{
+ MPT_ADAPTER *ioc = hd->ioc;
+ VirtTarget *vtarget;
+ SCSIDevicePage1_t *pcfg1Data;
+ SCSIDevicePage0_t *pcfg0Data;
+ u8 *pbuf1;
+ u8 *pbuf2;
+ u8 *pDvBuf;
+ dma_addr_t dvbuf_dma = -1;
+ dma_addr_t buf1_dma = -1;
+ dma_addr_t buf2_dma = -1;
+ dma_addr_t cfg1_dma_addr = -1;
+ dma_addr_t cfg0_dma_addr = -1;
+ ConfigPageHeader_t header1;
+ ConfigPageHeader_t header0;
+ DVPARAMETERS dv;
+ INTERNAL_CMD iocmd;
+ CONFIGPARMS cfg;
+ int dv_alloc = 0;
+ int rc, sz = 0;
+ int bufsize = 0;
+ int dataBufSize = 0;
+ int echoBufSize = 0;
+ int notDone;
+ int patt;
+ int repeat;
+ int retcode = 0;
+ int nfactor = MPT_ULTRA320;
+ char firstPass = 1;
+ char doFallback = 0;
+ char readPage0;
+ char bus, lun;
+ char inq0 = 0;
+
+ if (ioc->spi_data.sdp1length == 0)
+ return 0;
+
+ if (ioc->spi_data.sdp0length == 0)
+ return 0;
+
+ /* If multiple buses are used, require that the initiator
+ * id be the same on all buses.
+ */
+ if (id == ioc->pfacts[0].PortSCSIID)
+ return 0;
+
+ lun = 0;
+ bus = (u8) bus_number;
+ ddvtprintk((MYIOC_s_NOTE_FMT
+ "DV started: bus=%d, id=%d dv @ %p\n",
+ ioc->name, bus, id, &dv));
+
+ /* Prep DV structure
+ */
+ memset (&dv, 0, sizeof(DVPARAMETERS));
+ dv.id = id;
+
+ /* Populate tmax with the current maximum
+ * transfer parameters for this target.
+ * Exit if narrow and async.
+ */
+ dv.cmd = MPT_GET_NVRAM_VALS;
+ mptscsih_dv_parms(hd, &dv, NULL);
+
+ /* Prep SCSI IO structure
+ */
+ iocmd.id = id;
+ iocmd.bus = bus;
+ iocmd.lun = lun;
+ iocmd.flags = 0;
+ iocmd.physDiskNum = -1;
+ iocmd.rsvd = iocmd.rsvd2 = 0;
+
+ vtarget = hd->Targets[id];
+
+ /* Use tagged commands if possible.
+ */
+ if (vtarget) {
+ if (vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)
+ iocmd.flags |= MPT_ICFLAG_TAGGED_CMD;
+ else {
+ if (hd->ioc->facts.FWVersion.Word < 0x01000600)
+ return 0;
+
+ if ((hd->ioc->facts.FWVersion.Word >= 0x01010000) &&
+ (hd->ioc->facts.FWVersion.Word < 0x01010B00))
+ return 0;
+ }
+ }
+
+ /* Prep cfg structure
+ */
+ cfg.pageAddr = (bus<<8) | id;
+ cfg.cfghdr.hdr = NULL;
+
+ /* Prep SDP0 header
+ */
+ header0.PageVersion = ioc->spi_data.sdp0version;
+ header0.PageLength = ioc->spi_data.sdp0length;
+ header0.PageNumber = 0;
+ header0.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
+
+ /* Prep SDP1 header
+ */
+ header1.PageVersion = ioc->spi_data.sdp1version;
+ header1.PageLength = ioc->spi_data.sdp1length;
+ header1.PageNumber = 1;
+ header1.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
+
+ if (header0.PageLength & 1)
+ dv_alloc = (header0.PageLength * 4) + 4;
+
+ dv_alloc += (2048 + (header1.PageLength * 4));
+
+ pDvBuf = pci_alloc_consistent(ioc->pcidev, dv_alloc, &dvbuf_dma);
+ if (pDvBuf == NULL)
+ return 0;
+
+ sz = 0;
+ pbuf1 = (u8 *)pDvBuf;
+ buf1_dma = dvbuf_dma;
+ sz +=1024;
+
+ pbuf2 = (u8 *) (pDvBuf + sz);
+ buf2_dma = dvbuf_dma + sz;
+ sz +=1024;
+
+ pcfg0Data = (SCSIDevicePage0_t *) (pDvBuf + sz);
+ cfg0_dma_addr = dvbuf_dma + sz;
+ sz += header0.PageLength * 4;
+
+ /* 8-byte alignment
+ */
+ if (header0.PageLength & 1)
+ sz += 4;
+
+ pcfg1Data = (SCSIDevicePage1_t *) (pDvBuf + sz);
+ cfg1_dma_addr = dvbuf_dma + sz;
+
+ /* Skip this ID? Set cfg.cfghdr.hdr to force config page write
+ */
+ {
+ SpiCfgData *pspi_data = &hd->ioc->spi_data;
+ if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) {
+ /* Set the factor from nvram */
+ nfactor = (pspi_data->nvram[id] & MPT_NVRAM_SYNC_MASK) >> 8;
+ if (nfactor < pspi_data->minSyncFactor )
+ nfactor = pspi_data->minSyncFactor;
+
+ if (!(pspi_data->nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE) ||
+ (pspi_data->PortFlags == MPI_SCSIPORTPAGE2_PORT_FLAGS_OFF_DV) ) {
+
+ ddvprintk((MYIOC_s_NOTE_FMT "DV Skipped: bus, id, lun (%d, %d, %d)\n",
+ ioc->name, bus, id, lun));
+
+ dv.cmd = MPT_SET_MAX;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+ cfg.cfghdr.hdr = &header1;
+
+ /* Save the final negotiated settings to
+ * SCSI device page 1.
+ */
+ cfg.physAddr = cfg1_dma_addr;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ cfg.dir = 1;
+ mpt_config(hd->ioc, &cfg);
+ goto target_done;
+ }
+ }
+ }
+
+ /* Finish iocmd inititialization - hidden or visible disk? */
+ if (ioc->raid_data.pIocPg3) {
+ /* Search IOC page 3 for matching id
+ */
+ Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk;
+ int numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks;
+
+ while (numPDisk) {
+ if (pPDisk->PhysDiskID == id) {
+ /* match */
+ iocmd.flags |= MPT_ICFLAG_PHYS_DISK;
+ iocmd.physDiskNum = pPDisk->PhysDiskNum;
+
+ /* Quiesce the IM
+ */
+ if (mptscsih_do_raid(hd, MPI_RAID_ACTION_QUIESCE_PHYS_IO, &iocmd) < 0) {
+ ddvprintk((MYIOC_s_ERR_FMT "RAID Queisce FAILED!\n", ioc->name));
+ goto target_done;
+ }
+ break;
+ }
+ pPDisk++;
+ numPDisk--;
+ }
+ }
+
+ /* RAID Volume ID's may double for a physical device. If RAID but
+ * not a physical ID as well, skip DV.
+ */
+ if ((hd->ioc->raid_data.isRaid & (1 << id)) && !(iocmd.flags & MPT_ICFLAG_PHYS_DISK))
+ goto target_done;
+
+
+ /* Basic Test.
+ * Async & Narrow - Inquiry
+ * Async & Narrow - Inquiry
+ * Maximum transfer rate - Inquiry
+ * Compare buffers:
+ * If compare, test complete.
+ * If miscompare and first pass, repeat
+ * If miscompare and not first pass, fall back and repeat
+ */
+ hd->pLocal = NULL;
+ readPage0 = 0;
+ sz = SCSI_MAX_INQUIRY_BYTES;
+ rc = MPT_SCANDV_GOOD;
+ while (1) {
+ ddvprintk((MYIOC_s_NOTE_FMT "DV: Start Basic test on id=%d\n", ioc->name, id));
+ retcode = 0;
+ dv.cmd = MPT_SET_MIN;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+
+ cfg.cfghdr.hdr = &header1;
+ cfg.physAddr = cfg1_dma_addr;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ cfg.dir = 1;
+ if (mpt_config(hd->ioc, &cfg) != 0)
+ goto target_done;
+
+ /* Wide - narrow - wide workaround case
+ */
+ if ((rc == MPT_SCANDV_ISSUE_SENSE) && dv.max.width) {
+ /* Send an untagged command to reset disk Qs corrupted
+ * when a parity error occurs on a Request Sense.
+ */
+ if ((hd->ioc->facts.FWVersion.Word >= 0x01000600) ||
+ ((hd->ioc->facts.FWVersion.Word >= 0x01010000) &&
+ (hd->ioc->facts.FWVersion.Word < 0x01010B00)) ) {
+
+ iocmd.cmd = REQUEST_SENSE;
+ iocmd.data_dma = buf1_dma;
+ iocmd.data = pbuf1;
+ iocmd.size = 0x12;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else {
+ if (hd->pLocal == NULL)
+ goto target_done;
+ rc = hd->pLocal->completion;
+ if ((rc == MPT_SCANDV_GOOD) || (rc == MPT_SCANDV_SENSE)) {
+ dv.max.width = 0;
+ doFallback = 0;
+ } else
+ goto target_done;
+ }
+ } else
+ goto target_done;
+ }
+
+ iocmd.cmd = INQUIRY;
+ iocmd.data_dma = buf1_dma;
+ iocmd.data = pbuf1;
+ iocmd.size = sz;
+ memset(pbuf1, 0x00, sz);
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else {
+ if (hd->pLocal == NULL)
+ goto target_done;
+ rc = hd->pLocal->completion;
+ if (rc == MPT_SCANDV_GOOD) {
+ if (hd->pLocal->scsiStatus == SAM_STAT_BUSY) {
+ if ((iocmd.flags & MPT_ICFLAG_TAGGED_CMD) == 0)
+ retcode = 1;
+ else
+ retcode = 0;
+
+ goto target_done;
+ }
+ } else if (rc == MPT_SCANDV_SENSE) {
+ ;
+ } else {
+ /* If first command doesn't complete
+ * with a good status or with a check condition,
+ * exit.
+ */
+ goto target_done;
+ }
+ }
+
+ /* Reset the size for disks
+ */
+ inq0 = (*pbuf1) & 0x1F;
+ if ((inq0 == 0) && vtarget && !vtarget->raidVolume) {
+ sz = 0x40;
+ iocmd.size = sz;
+ }
+
+ /* Another GEM workaround. Check peripheral device type,
+ * if PROCESSOR, quit DV.
+ */
+ if (inq0 == TYPE_PROCESSOR) {
+ mptscsih_initTarget(hd,
+ vtarget,
+ lun,
+ pbuf1,
+ sz);
+ goto target_done;
+ }
+
+ if (inq0 > 0x08)
+ goto target_done;
+
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+
+ if (sz == 0x40) {
+ if ((vtarget->maxWidth == 1) && (vtarget->maxOffset) && (nfactor < 0x0A)
+ && (vtarget->minSyncFactor > 0x09)) {
+ if ((pbuf1[56] & 0x04) == 0)
+ ;
+ else if ((pbuf1[56] & 0x01) == 1) {
+ vtarget->minSyncFactor =
+ nfactor > MPT_ULTRA320 ? nfactor : MPT_ULTRA320;
+ } else {
+ vtarget->minSyncFactor =
+ nfactor > MPT_ULTRA160 ? nfactor : MPT_ULTRA160;
+ }
+
+ dv.max.factor = vtarget->minSyncFactor;
+
+ if ((pbuf1[56] & 0x02) == 0) {
+ vtarget->negoFlags |= MPT_TARGET_NO_NEGO_QAS;
+ hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS;
+ ddvprintk((MYIOC_s_NOTE_FMT
+ "DV: Start Basic noQas on id=%d due to pbuf1[56]=%x\n",
+ ioc->name, id, pbuf1[56]));
+ }
+ }
+ }
+
+ if (doFallback)
+ dv.cmd = MPT_FALLBACK;
+ else
+ dv.cmd = MPT_SET_MAX;
+
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+ if (mpt_config(hd->ioc, &cfg) != 0)
+ goto target_done;
+
+ if ((!dv.now.width) && (!dv.now.offset))
+ goto target_done;
+
+ iocmd.cmd = INQUIRY;
+ iocmd.data_dma = buf2_dma;
+ iocmd.data = pbuf2;
+ iocmd.size = sz;
+ memset(pbuf2, 0x00, sz);
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else if (hd->pLocal == NULL)
+ goto target_done;
+ else {
+ /* Save the return code.
+ * If this is the first pass,
+ * read SCSI Device Page 0
+ * and update the target max parameters.
+ */
+ rc = hd->pLocal->completion;
+ doFallback = 0;
+ if (rc == MPT_SCANDV_GOOD) {
+ if (!readPage0) {
+ u32 sdp0_info;
+ u32 sdp0_nego;
+
+ cfg.cfghdr.hdr = &header0;
+ cfg.physAddr = cfg0_dma_addr;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+ cfg.dir = 0;
+
+ if (mpt_config(hd->ioc, &cfg) != 0)
+ goto target_done;
+
+ sdp0_info = le32_to_cpu(pcfg0Data->Information) & 0x0E;
+ sdp0_nego = (le32_to_cpu(pcfg0Data->NegotiatedParameters) & 0xFF00 ) >> 8;
+
+ /* Quantum and Fujitsu workarounds.
+ * Quantum: PPR U320 -> PPR reply with Ultra2 and wide
+ * Fujitsu: PPR U320 -> Msg Reject and Ultra2 and wide
+ * Resetart with a request for U160.
+ */
+ if ((dv.now.factor == MPT_ULTRA320) && (sdp0_nego == MPT_ULTRA2)) {
+ doFallback = 1;
+ } else {
+ dv.cmd = MPT_UPDATE_MAX;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg0Data);
+ /* Update the SCSI device page 1 area
+ */
+ pcfg1Data->RequestedParameters = pcfg0Data->NegotiatedParameters;
+ readPage0 = 1;
+ }
+ }
+
+ /* Quantum workaround. Restart this test will the fallback
+ * flag set.
+ */
+ if (doFallback == 0) {
+ if (memcmp(pbuf1, pbuf2, sz) != 0) {
+ if (!firstPass)
+ doFallback = 1;
+ } else {
+ ddvprintk((MYIOC_s_NOTE_FMT
+ "DV:Inquiry compared id=%d, calling initTarget\n", ioc->name, id));
+ hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_NOT_DONE;
+ mptscsih_initTarget(hd,
+ vtarget,
+ lun,
+ pbuf1,
+ sz);
+ break; /* test complete */
+ }
+ }
+
+
+ } else if (rc == MPT_SCANDV_ISSUE_SENSE)
+ doFallback = 1; /* set fallback flag */
+ else if ((rc == MPT_SCANDV_DID_RESET) ||
+ (rc == MPT_SCANDV_SENSE) ||
+ (rc == MPT_SCANDV_FALLBACK))
+ doFallback = 1; /* set fallback flag */
+ else
+ goto target_done;
+
+ firstPass = 0;
+ }
+ }
+ ddvprintk((MYIOC_s_NOTE_FMT "DV: Basic test on id=%d completed OK.\n", ioc->name, id));
+
+ if (ioc->spi_data.mpt_dv == 0)
+ goto target_done;
+
+ inq0 = (*pbuf1) & 0x1F;
+
+ /* Continue only for disks
+ */
+ if (inq0 != 0)
+ goto target_done;
+
+ if ( ioc->spi_data.PortFlags == MPI_SCSIPORTPAGE2_PORT_FLAGS_BASIC_DV_ONLY )
+ goto target_done;
+
+ /* Start the Enhanced Test.
+ * 0) issue TUR to clear out check conditions
+ * 1) read capacity of echo (regular) buffer
+ * 2) reserve device
+ * 3) do write-read-compare data pattern test
+ * 4) release
+ * 5) update nego parms to target struct
+ */
+ cfg.cfghdr.hdr = &header1;
+ cfg.physAddr = cfg1_dma_addr;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ cfg.dir = 1;
+
+ iocmd.cmd = TEST_UNIT_READY;
+ iocmd.data_dma = -1;
+ iocmd.data = NULL;
+ iocmd.size = 0;
+ notDone = 1;
+ while (notDone) {
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+
+ if (hd->pLocal == NULL)
+ goto target_done;
+
+ rc = hd->pLocal->completion;
+ if (rc == MPT_SCANDV_GOOD)
+ notDone = 0;
+ else if (rc == MPT_SCANDV_SENSE) {
+ u8 skey = hd->pLocal->sense[2] & 0x0F;
+ u8 asc = hd->pLocal->sense[12];
+ u8 ascq = hd->pLocal->sense[13];
+ ddvprintk((MYIOC_s_INFO_FMT
+ "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
+ ioc->name, skey, asc, ascq));
+
+ if (skey == UNIT_ATTENTION)
+ notDone++; /* repeat */
+ else if ((skey == NOT_READY) &&
+ (asc == 0x04)&&(ascq == 0x01)) {
+ /* wait then repeat */
+ mdelay (2000);
+ notDone++;
+ } else if ((skey == NOT_READY) && (asc == 0x3A)) {
+ /* no medium, try read test anyway */
+ notDone = 0;
+ } else {
+ /* All other errors are fatal.
+ */
+ ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.",
+ ioc->name));
+ goto target_done;
+ }
+ } else
+ goto target_done;
+ }
+
+ iocmd.cmd = READ_BUFFER;
+ iocmd.data_dma = buf1_dma;
+ iocmd.data = pbuf1;
+ iocmd.size = 4;
+ iocmd.flags |= MPT_ICFLAG_BUF_CAP;
+
+ dataBufSize = 0;
+ echoBufSize = 0;
+ for (patt = 0; patt < 2; patt++) {
+ if (patt == 0)
+ iocmd.flags |= MPT_ICFLAG_ECHO;
+ else
+ iocmd.flags &= ~MPT_ICFLAG_ECHO;
+
+ notDone = 1;
+ while (notDone) {
+ bufsize = 0;
+
+ /* If not ready after 8 trials,
+ * give up on this device.
+ */
+ if (notDone > 8)
+ goto target_done;
+
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else if (hd->pLocal == NULL)
+ goto target_done;
+ else {
+ rc = hd->pLocal->completion;
+ ddvprintk(("ReadBuffer Comp Code %d", rc));
+ ddvprintk((" buff: %0x %0x %0x %0x\n",
+ pbuf1[0], pbuf1[1], pbuf1[2], pbuf1[3]));
+
+ if (rc == MPT_SCANDV_GOOD) {
+ notDone = 0;
+ if (iocmd.flags & MPT_ICFLAG_ECHO) {
+ bufsize = ((pbuf1[2] & 0x1F) <<8) | pbuf1[3];
+ if (pbuf1[0] & 0x01)
+ iocmd.flags |= MPT_ICFLAG_EBOS;
+ } else {
+ bufsize = pbuf1[1]<<16 | pbuf1[2]<<8 | pbuf1[3];
+ }
+ } else if (rc == MPT_SCANDV_SENSE) {
+ u8 skey = hd->pLocal->sense[2] & 0x0F;
+ u8 asc = hd->pLocal->sense[12];
+ u8 ascq = hd->pLocal->sense[13];
+ ddvprintk((MYIOC_s_INFO_FMT
+ "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
+ ioc->name, skey, asc, ascq));
+ if (skey == ILLEGAL_REQUEST) {
+ notDone = 0;
+ } else if (skey == UNIT_ATTENTION) {
+ notDone++; /* repeat */
+ } else if ((skey == NOT_READY) &&
+ (asc == 0x04)&&(ascq == 0x01)) {
+ /* wait then repeat */
+ mdelay (2000);
+ notDone++;
+ } else {
+ /* All other errors are fatal.
+ */
+ ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.",
+ ioc->name));
+ goto target_done;
+ }
+ } else {
+ /* All other errors are fatal
+ */
+ goto target_done;
+ }
+ }
+ }
+
+ if (iocmd.flags & MPT_ICFLAG_ECHO)
+ echoBufSize = bufsize;
+ else
+ dataBufSize = bufsize;
+ }
+ sz = 0;
+ iocmd.flags &= ~MPT_ICFLAG_BUF_CAP;
+
+ /* Use echo buffers if possible,
+ * Exit if both buffers are 0.
+ */
+ if (echoBufSize > 0) {
+ iocmd.flags |= MPT_ICFLAG_ECHO;
+ if (dataBufSize > 0)
+ bufsize = min(echoBufSize, dataBufSize);
+ else
+ bufsize = echoBufSize;
+ } else if (dataBufSize == 0)
+ goto target_done;
+
+ ddvprintk((MYIOC_s_INFO_FMT "%s Buffer Capacity %d\n", ioc->name,
+ (iocmd.flags & MPT_ICFLAG_ECHO) ? "Echo" : " ", bufsize));
+
+ /* Data buffers for write-read-compare test max 1K.
+ */
+ sz = min(bufsize, 1024);
+
+ /* --- loop ----
+ * On first pass, always issue a reserve.
+ * On additional loops, only if a reset has occurred.
+ * iocmd.flags indicates if echo or regular buffer
+ */
+ for (patt = 0; patt < 4; patt++) {
+ ddvprintk(("Pattern %d\n", patt));
+ if ((iocmd.flags & MPT_ICFLAG_RESERVED) && (iocmd.flags & MPT_ICFLAG_DID_RESET)) {
+ iocmd.cmd = TEST_UNIT_READY;
+ iocmd.data_dma = -1;
+ iocmd.data = NULL;
+ iocmd.size = 0;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+
+ iocmd.cmd = RELEASE;
+ iocmd.data_dma = -1;
+ iocmd.data = NULL;
+ iocmd.size = 0;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else if (hd->pLocal == NULL)
+ goto target_done;
+ else {
+ rc = hd->pLocal->completion;
+ ddvprintk(("Release rc %d\n", rc));
+ if (rc == MPT_SCANDV_GOOD)
+ iocmd.flags &= ~MPT_ICFLAG_RESERVED;
+ else
+ goto target_done;
+ }
+ iocmd.flags &= ~MPT_ICFLAG_RESERVED;
+ }
+ iocmd.flags &= ~MPT_ICFLAG_DID_RESET;
+
+ if (iocmd.flags & MPT_ICFLAG_EBOS)
+ goto skip_Reserve;
+
+ repeat = 5;
+ while (repeat && (!(iocmd.flags & MPT_ICFLAG_RESERVED))) {
+ iocmd.cmd = RESERVE;
+ iocmd.data_dma = -1;
+ iocmd.data = NULL;
+ iocmd.size = 0;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else if (hd->pLocal == NULL)
+ goto target_done;
+ else {
+ rc = hd->pLocal->completion;
+ if (rc == MPT_SCANDV_GOOD) {
+ iocmd.flags |= MPT_ICFLAG_RESERVED;
+ } else if (rc == MPT_SCANDV_SENSE) {
+ /* Wait if coming ready
+ */
+ u8 skey = hd->pLocal->sense[2] & 0x0F;
+ u8 asc = hd->pLocal->sense[12];
+ u8 ascq = hd->pLocal->sense[13];
+ ddvprintk((MYIOC_s_INFO_FMT
+ "DV: Reserve Failed: ", ioc->name));
+ ddvprintk(("SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n",
+ skey, asc, ascq));
+
+ if ((skey == NOT_READY) && (asc == 0x04)&&
+ (ascq == 0x01)) {
+ /* wait then repeat */
+ mdelay (2000);
+ notDone++;
+ } else {
+ ddvprintk((MYIOC_s_INFO_FMT
+ "DV: Reserved Failed.", ioc->name));
+ goto target_done;
+ }
+ } else {
+ ddvprintk((MYIOC_s_INFO_FMT "DV: Reserved Failed.",
+ ioc->name));
+ goto target_done;
+ }
+ }
+ }
+
+skip_Reserve:
+ mptscsih_fillbuf(pbuf1, sz, patt, 1);
+ iocmd.cmd = WRITE_BUFFER;
+ iocmd.data_dma = buf1_dma;
+ iocmd.data = pbuf1;
+ iocmd.size = sz;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else if (hd->pLocal == NULL)
+ goto target_done;
+ else {
+ rc = hd->pLocal->completion;
+ if (rc == MPT_SCANDV_GOOD)
+ ; /* Issue read buffer */
+ else if (rc == MPT_SCANDV_DID_RESET) {
+ /* If using echo buffers, reset to data buffers.
+ * Else do Fallback and restart
+ * this test (re-issue reserve
+ * because of bus reset).
+ */
+ if ((iocmd.flags & MPT_ICFLAG_ECHO) && (dataBufSize >= bufsize)) {
+ iocmd.flags &= ~MPT_ICFLAG_ECHO;
+ } else {
+ dv.cmd = MPT_FALLBACK;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+
+ if (mpt_config(hd->ioc, &cfg) != 0)
+ goto target_done;
+
+ if ((!dv.now.width) && (!dv.now.offset))
+ goto target_done;
+ }
+
+ iocmd.flags |= MPT_ICFLAG_DID_RESET;
+ patt = -1;
+ continue;
+ } else if (rc == MPT_SCANDV_SENSE) {
+ /* Restart data test if UA, else quit.
+ */
+ u8 skey = hd->pLocal->sense[2] & 0x0F;
+ ddvprintk((MYIOC_s_INFO_FMT
+ "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey,
+ hd->pLocal->sense[12], hd->pLocal->sense[13]));
+ if (skey == UNIT_ATTENTION) {
+ patt = -1;
+ continue;
+ } else if (skey == ILLEGAL_REQUEST) {
+ if (iocmd.flags & MPT_ICFLAG_ECHO) {
+ if (dataBufSize >= bufsize) {
+ iocmd.flags &= ~MPT_ICFLAG_ECHO;
+ patt = -1;
+ continue;
+ }
+ }
+ goto target_done;
+ }
+ else
+ goto target_done;
+ } else {
+ /* fatal error */
+ goto target_done;
+ }
+ }
+
+ iocmd.cmd = READ_BUFFER;
+ iocmd.data_dma = buf2_dma;
+ iocmd.data = pbuf2;
+ iocmd.size = sz;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ goto target_done;
+ else if (hd->pLocal == NULL)
+ goto target_done;
+ else {
+ rc = hd->pLocal->completion;
+ if (rc == MPT_SCANDV_GOOD) {
+ /* If buffers compare,
+ * go to next pattern,
+ * else, do a fallback and restart
+ * data transfer test.
+ */
+ if (memcmp (pbuf1, pbuf2, sz) == 0) {
+ ; /* goto next pattern */
+ } else {
+ /* Miscompare with Echo buffer, go to data buffer,
+ * if that buffer exists.
+ * Miscompare with Data buffer, check first 4 bytes,
+ * some devices return capacity. Exit in this case.
+ */
+ if (iocmd.flags & MPT_ICFLAG_ECHO) {
+ if (dataBufSize >= bufsize)
+ iocmd.flags &= ~MPT_ICFLAG_ECHO;
+ else
+ goto target_done;
+ } else {
+ if (dataBufSize == (pbuf2[1]<<16 | pbuf2[2]<<8 | pbuf2[3])) {
+ /* Argh. Device returning wrong data.
+ * Quit DV for this device.
+ */
+ goto target_done;
+ }
+
+ /* Had an actual miscompare. Slow down.*/
+ dv.cmd = MPT_FALLBACK;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+
+ if (mpt_config(hd->ioc, &cfg) != 0)
+ goto target_done;
+
+ if ((!dv.now.width) && (!dv.now.offset))
+ goto target_done;
+ }
+
+ patt = -1;
+ continue;
+ }
+ } else if (rc == MPT_SCANDV_DID_RESET) {
+ /* Do Fallback and restart
+ * this test (re-issue reserve
+ * because of bus reset).
+ */
+ dv.cmd = MPT_FALLBACK;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+
+ if (mpt_config(hd->ioc, &cfg) != 0)
+ goto target_done;
+
+ if ((!dv.now.width) && (!dv.now.offset))
+ goto target_done;
+
+ iocmd.flags |= MPT_ICFLAG_DID_RESET;
+ patt = -1;
+ continue;
+ } else if (rc == MPT_SCANDV_SENSE) {
+ /* Restart data test if UA, else quit.
+ */
+ u8 skey = hd->pLocal->sense[2] & 0x0F;
+ ddvprintk((MYIOC_s_INFO_FMT
+ "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey,
+ hd->pLocal->sense[12], hd->pLocal->sense[13]));
+ if (skey == UNIT_ATTENTION) {
+ patt = -1;
+ continue;
+ }
+ else
+ goto target_done;
+ } else {
+ /* fatal error */
+ goto target_done;
+ }
+ }
+
+ } /* --- end of patt loop ---- */
+
+target_done:
+ if (iocmd.flags & MPT_ICFLAG_RESERVED) {
+ iocmd.cmd = RELEASE;
+ iocmd.data_dma = -1;
+ iocmd.data = NULL;
+ iocmd.size = 0;
+ if (mptscsih_do_cmd(hd, &iocmd) < 0)
+ printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
+ ioc->name, id);
+ else if (hd->pLocal) {
+ if (hd->pLocal->completion == MPT_SCANDV_GOOD)
+ iocmd.flags &= ~MPT_ICFLAG_RESERVED;
+ } else {
+ printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
+ ioc->name, id);
+ }
+ }
+
+
+ /* Set if cfg1_dma_addr contents is valid
+ */
+ if ((cfg.cfghdr.hdr != NULL) && (retcode == 0)){
+ /* If disk, not U320, disable QAS
+ */
+ if ((inq0 == 0) && (dv.now.factor > MPT_ULTRA320)) {
+ hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS;
+ ddvprintk((MYIOC_s_NOTE_FMT
+ "noQas set due to id=%d has factor=%x\n", ioc->name, id, dv.now.factor));
+ }
+
+ dv.cmd = MPT_SAVE;
+ mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
+
+ /* Double writes to SDP1 can cause problems,
+ * skip save of the final negotiated settings to
+ * SCSI device page 1.
+ *
+ cfg.cfghdr.hdr = &header1;
+ cfg.physAddr = cfg1_dma_addr;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ cfg.dir = 1;
+ mpt_config(hd->ioc, &cfg);
+ */
+ }
+
+ /* If this is a RAID Passthrough, enable internal IOs
+ */
+ if (iocmd.flags & MPT_ICFLAG_PHYS_DISK) {
+ if (mptscsih_do_raid(hd, MPI_RAID_ACTION_ENABLE_PHYS_IO, &iocmd) < 0)
+ ddvprintk((MYIOC_s_ERR_FMT "RAID Enable FAILED!\n", ioc->name));
+ }
+
+ /* Done with the DV scan of the current target
+ */
+ if (pDvBuf)
+ pci_free_consistent(ioc->pcidev, dv_alloc, pDvBuf, dvbuf_dma);
+
+ ddvtprintk((MYIOC_s_INFO_FMT "DV Done id=%d\n",
+ ioc->name, id));
+
+ return retcode;
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptscsih_dv_parms - perform a variety of operations on the
+ * parameters used for negotiation.
+ * @hd: Pointer to a SCSI host.
+ * @dv: Pointer to a structure that contains the maximum and current
+ * negotiated parameters.
+ */
+static void
+mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage)
+{
+ VirtTarget *vtarget;
+ SCSIDevicePage0_t *pPage0;
+ SCSIDevicePage1_t *pPage1;
+ int val = 0, data, configuration;
+ u8 width = 0;
+ u8 offset = 0;
+ u8 factor = 0;
+ u8 negoFlags = 0;
+ u8 cmd = dv->cmd;
+ u8 id = dv->id;
+
+ switch (cmd) {
+ case MPT_GET_NVRAM_VALS:
+ ddvprintk((MYIOC_s_NOTE_FMT "Getting NVRAM: ",
+ hd->ioc->name));
+ /* Get the NVRAM values and save in tmax
+ * If not an LVD bus, the adapter minSyncFactor has been
+ * already throttled back.
+ */
+ negoFlags = hd->ioc->spi_data.noQas;
+ if ((hd->Targets)&&((vtarget = hd->Targets[(int)id]) != NULL) && !vtarget->raidVolume) {
+ width = vtarget->maxWidth;
+ offset = vtarget->maxOffset;
+ factor = vtarget->minSyncFactor;
+ negoFlags |= vtarget->negoFlags;
+ } else {
+ if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) {
+ data = hd->ioc->spi_data.nvram[id];
+ width = data & MPT_NVRAM_WIDE_DISABLE ? 0 : 1;
+ if ((offset = hd->ioc->spi_data.maxSyncOffset) == 0)
+ factor = MPT_ASYNC;
+ else {
+ factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT;
+ if ((factor == 0) || (factor == MPT_ASYNC)){
+ factor = MPT_ASYNC;
+ offset = 0;
+ }
+ }
+ } else {
+ width = MPT_NARROW;
+ offset = 0;
+ factor = MPT_ASYNC;
+ }
+
+ /* Set the negotiation flags */
+ if (!width)
+ negoFlags |= MPT_TARGET_NO_NEGO_WIDE;
+
+ if (!offset)
+ negoFlags |= MPT_TARGET_NO_NEGO_SYNC;
+ }
+
+ /* limit by adapter capabilities */
+ width = min(width, hd->ioc->spi_data.maxBusWidth);
+ offset = min(offset, hd->ioc->spi_data.maxSyncOffset);
+ factor = max(factor, hd->ioc->spi_data.minSyncFactor);
+
+ /* Check Consistency */
+ if (offset && (factor < MPT_ULTRA2) && !width)
+ factor = MPT_ULTRA2;
+
+ dv->max.width = width;
+ dv->max.offset = offset;
+ dv->max.factor = factor;
+ dv->max.flags = negoFlags;
+ ddvprintk((" id=%d width=%d factor=%x offset=%x flags=%x\n",
+ id, width, factor, offset, negoFlags));
+ break;
+
+ case MPT_UPDATE_MAX:
+ ddvprintk((MYIOC_s_NOTE_FMT
+ "Updating with SDP0 Data: ", hd->ioc->name));
+ /* Update tmax values with those from Device Page 0.*/
+ pPage0 = (SCSIDevicePage0_t *) pPage;
+ if (pPage0) {
+ val = le32_to_cpu(pPage0->NegotiatedParameters);
+ dv->max.width = val & MPI_SCSIDEVPAGE0_NP_WIDE ? 1 : 0;
+ dv->max.offset = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK) >> 16;
+ dv->max.factor = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK) >> 8;
+ }
+
+ dv->now.width = dv->max.width;
+ dv->now.offset = dv->max.offset;
+ dv->now.factor = dv->max.factor;
+ ddvprintk(("id=%d width=%d factor=%x offset=%x flags=%x\n",
+ id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags));
+ break;
+
+ case MPT_SET_MAX:
+ ddvprintk((MYIOC_s_NOTE_FMT "Setting Max: ",
+ hd->ioc->name));
+ /* Set current to the max values. Update the config page.*/
+ dv->now.width = dv->max.width;
+ dv->now.offset = dv->max.offset;
+ dv->now.factor = dv->max.factor;
+ dv->now.flags = dv->max.flags;
+
+ pPage1 = (SCSIDevicePage1_t *)pPage;
+ if (pPage1) {
+ mptscsih_setDevicePage1Flags (dv->now.width, dv->now.factor,
+ dv->now.offset, &val, &configuration, dv->now.flags);
+ dnegoprintk(("Setting Max: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
+ id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration));
+ pPage1->RequestedParameters = cpu_to_le32(val);
+ pPage1->Reserved = 0;
+ pPage1->Configuration = cpu_to_le32(configuration);
+ }
+
+ ddvprintk(("id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x configuration=%x\n",
+ id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration));
+ break;
+
+ case MPT_SET_MIN:
+ ddvprintk((MYIOC_s_NOTE_FMT "Setting Min: ",
+ hd->ioc->name));
+ /* Set page to asynchronous and narrow
+ * Do not update now, breaks fallback routine. */
+ width = MPT_NARROW;
+ offset = 0;
+ factor = MPT_ASYNC;
+ negoFlags = dv->max.flags;
+
+ pPage1 = (SCSIDevicePage1_t *)pPage;
+ if (pPage1) {
+ mptscsih_setDevicePage1Flags (width, factor,
+ offset, &val, &configuration, negoFlags);
+ dnegoprintk(("Setting Min: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n",
+ id, width, factor, offset, negoFlags, val, configuration));
+ pPage1->RequestedParameters = cpu_to_le32(val);
+ pPage1->Reserved = 0;
+ pPage1->Configuration = cpu_to_le32(configuration);
+ }
+ ddvprintk(("id=%d width=%d factor=%x offset=%x request=%x config=%x negoFlags=%x\n",
+ id, width, factor, offset, val, configuration, negoFlags));
+ break;
+
+ case MPT_FALLBACK:
+ ddvprintk((MYIOC_s_NOTE_FMT
+ "Fallback: Start: offset %d, factor %x, width %d \n",
+ hd->ioc->name, dv->now.offset,
+ dv->now.factor, dv->now.width));
+ width = dv->now.width;
+ offset = dv->now.offset;
+ factor = dv->now.factor;
+ if ((offset) && (dv->max.width)) {
+ if (factor < MPT_ULTRA160)
+ factor = MPT_ULTRA160;
+ else if (factor < MPT_ULTRA2) {
+ factor = MPT_ULTRA2;
+ width = MPT_WIDE;
+ } else if ((factor == MPT_ULTRA2) && width) {
+ factor = MPT_ULTRA2;
+ width = MPT_NARROW;
+ } else if (factor < MPT_ULTRA) {
+ factor = MPT_ULTRA;
+ width = MPT_WIDE;
+ } else if ((factor == MPT_ULTRA) && width) {
+ width = MPT_NARROW;
+ } else if (factor < MPT_FAST) {
+ factor = MPT_FAST;
+ width = MPT_WIDE;
+ } else if ((factor == MPT_FAST) && width) {
+ factor = MPT_FAST;
+ width = MPT_NARROW;
+ } else if (factor < MPT_SCSI) {
+ factor = MPT_SCSI;
+ width = MPT_WIDE;
+ } else if ((factor == MPT_SCSI) && width) {
+ factor = MPT_SCSI;
+ width = MPT_NARROW;
+ } else {
+ factor = MPT_ASYNC;
+ offset = 0;
+ }
+
+ } else if (offset) {
+ width = MPT_NARROW;
+ if (factor < MPT_ULTRA)
+ factor = MPT_ULTRA;
+ else if (factor < MPT_FAST)
+ factor = MPT_FAST;
+ else if (factor < MPT_SCSI)
+ factor = MPT_SCSI;
+ else {
+ factor = MPT_ASYNC;
+ offset = 0;
+ }
+
+ } else {
+ width = MPT_NARROW;
+ factor = MPT_ASYNC;
+ }
+ dv->max.flags |= MPT_TARGET_NO_NEGO_QAS;
+ dv->max.flags &= ~MPT_TAPE_NEGO_IDP;
+
+ dv->now.width = width;
+ dv->now.offset = offset;
+ dv->now.factor = factor;
+ dv->now.flags = dv->max.flags;
+
+ pPage1 = (SCSIDevicePage1_t *)pPage;
+ if (pPage1) {
+ mptscsih_setDevicePage1Flags (width, factor, offset, &val,
+ &configuration, dv->now.flags);
+ dnegoprintk(("Finish: id=%d width=%d offset=%d factor=%x negoFlags=%x request=%x config=%x\n",
+ id, width, offset, factor, dv->now.flags, val, configuration));
+
+ pPage1->RequestedParameters = cpu_to_le32(val);
+ pPage1->Reserved = 0;
+ pPage1->Configuration = cpu_to_le32(configuration);
+ }
+
+ ddvprintk(("Finish: id=%d offset=%d factor=%x width=%d request=%x config=%x\n",
+ id, dv->now.offset, dv->now.factor, dv->now.width, val, configuration));
+ break;
+
+ case MPT_SAVE:
+ ddvprintk((MYIOC_s_NOTE_FMT
+ "Saving to Target structure: ", hd->ioc->name));
+ ddvprintk(("id=%d width=%x factor=%x offset=%d flags=%x\n",
+ id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags));
+
+ /* Save these values to target structures
+ * or overwrite nvram (phys disks only).
+ */
+
+ if ((hd->Targets)&&((vtarget = hd->Targets[(int)id]) != NULL) && !vtarget->raidVolume ) {
+ vtarget->maxWidth = dv->now.width;
+ vtarget->maxOffset = dv->now.offset;
+ vtarget->minSyncFactor = dv->now.factor;
+ vtarget->negoFlags = dv->now.flags;
+ } else {
+ /* Preserv all flags, use
+ * read-modify-write algorithm
+ */
+ if (hd->ioc->spi_data.nvram) {
+ data = hd->ioc->spi_data.nvram[id];
+
+ if (dv->now.width)
+ data &= ~MPT_NVRAM_WIDE_DISABLE;
+ else
+ data |= MPT_NVRAM_WIDE_DISABLE;
+
+ if (!dv->now.offset)
+ factor = MPT_ASYNC;
+
+ data &= ~MPT_NVRAM_SYNC_MASK;
+ data |= (dv->now.factor << MPT_NVRAM_SYNC_SHIFT) & MPT_NVRAM_SYNC_MASK;
+
+ hd->ioc->spi_data.nvram[id] = data;
+ }
+ }
+ break;
+ }
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* mptscsih_fillbuf - fill a buffer with a special data pattern
+ * cleanup. For bus scan only.
+ *
+ * @buffer: Pointer to data buffer to be filled.
+ * @size: Number of bytes to fill
+ * @index: Pattern index
+ * @width: bus width, 0 (8 bits) or 1 (16 bits)
+ */
+static void
+mptscsih_fillbuf(char *buffer, int size, int index, int width)
+{
+ char *ptr = buffer;
+ int ii;
+ char byte;
+ short val;
+
+ switch (index) {
+ case 0:
+
+ if (width) {
+ /* Pattern: 0000 FFFF 0000 FFFF
+ */
+ for (ii=0; ii < size; ii++, ptr++) {
+ if (ii & 0x02)
+ *ptr = 0xFF;
+ else
+ *ptr = 0x00;
+ }
+ } else {
+ /* Pattern: 00 FF 00 FF
+ */
+ for (ii=0; ii < size; ii++, ptr++) {
+ if (ii & 0x01)
+ *ptr = 0xFF;
+ else
+ *ptr = 0x00;
+ }
+ }
+ break;
+
+ case 1:
+ if (width) {
+ /* Pattern: 5555 AAAA 5555 AAAA 5555
+ */
+ for (ii=0; ii < size; ii++, ptr++) {
+ if (ii & 0x02)
+ *ptr = 0xAA;
+ else
+ *ptr = 0x55;
+ }
+ } else {
+ /* Pattern: 55 AA 55 AA 55
+ */
+ for (ii=0; ii < size; ii++, ptr++) {
+ if (ii & 0x01)
+ *ptr = 0xAA;
+ else
+ *ptr = 0x55;
+ }
+ }
+ break;
+
+ case 2:
+ /* Pattern: 00 01 02 03 04 05
+ * ... FE FF 00 01..
+ */
+ for (ii=0; ii < size; ii++, ptr++)
+ *ptr = (char) ii;
+ break;
+
+ case 3:
+ if (width) {
+ /* Wide Pattern: FFFE 0001 FFFD 0002
+ * ... 4000 DFFF 8000 EFFF
+ */
+ byte = 0;
+ for (ii=0; ii < size/2; ii++) {
+ /* Create the base pattern
+ */
+ val = (1 << byte);
+ /* every 64 (0x40) bytes flip the pattern
+ * since we fill 2 bytes / iteration,
+ * test for ii = 0x20
+ */
+ if (ii & 0x20)
+ val = ~(val);
+
+ if (ii & 0x01) {
+ *ptr = (char)( (val & 0xFF00) >> 8);
+ ptr++;
+ *ptr = (char)(val & 0xFF);
+ byte++;
+ byte &= 0x0F;
+ } else {
+ val = ~val;
+ *ptr = (char)( (val & 0xFF00) >> 8);
+ ptr++;
+ *ptr = (char)(val & 0xFF);
+ }
+
+ ptr++;
+ }
+ } else {
+ /* Narrow Pattern: FE 01 FD 02 FB 04
+ * .. 7F 80 01 FE 02 FD ... 80 7F
+ */
+ byte = 0;
+ for (ii=0; ii < size; ii++, ptr++) {
+ /* Base pattern - first 32 bytes
+ */
+ if (ii & 0x01) {
+ *ptr = (1 << byte);
+ byte++;
+ byte &= 0x07;
+ } else {
+ *ptr = (char) (~(1 << byte));
+ }
+
+ /* Flip the pattern every 32 bytes
+ */
+ if (ii & 0x20)
+ *ptr = ~(*ptr);
+ }
+ }
+ break;
+ }
+}
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* If DV disabled (negoNvram set to USE_NVARM) or if not LUN 0, return.
+ * Else set the NEED_DV flag after Read Capacity Issued (disks)
+ * or Mode Sense (cdroms).
+ *
+ * Tapes, initTarget will set this flag on completion of Inquiry command.
+ * Called only if DV_NOT_DONE flag is set
+ */
+static void
+mptscsih_set_dvflags(MPT_SCSI_HOST *hd, struct scsi_cmnd *sc)
+{
+ MPT_ADAPTER *ioc = hd->ioc;
+ u8 cmd;
+ SpiCfgData *pSpi;
+
+ ddvtprintk((MYIOC_s_NOTE_FMT
+ " set_dvflags: id=%d lun=%d negoNvram=%x cmd=%x\n",
+ hd->ioc->name, sc->device->id, sc->device->lun , hd->negoNvram, sc->cmnd[0]));
+
+ if ((sc->device->lun != 0) || (hd->negoNvram != 0))
+ return;
+
+ cmd = sc->cmnd[0];
+
+ if ((cmd == READ_CAPACITY) || (cmd == MODE_SENSE)) {
+ pSpi = &ioc->spi_data;
+ if ((ioc->raid_data.isRaid & (1 << sc->device->id)) && ioc->raid_data.pIocPg3) {
+ /* Set NEED_DV for all hidden disks
+ */
+ Ioc3PhysDisk_t *pPDisk = ioc->raid_data.pIocPg3->PhysDisk;
+ int numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks;
+
+ while (numPDisk) {
+ pSpi->dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV;
+ ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID));
+ pPDisk++;
+ numPDisk--;
+ }
+ }
+ pSpi->dvStatus[sc->device->id] |= MPT_SCSICFG_NEED_DV;
+ ddvtprintk(("NEED_DV set for visible disk id %d\n", sc->device->id));
+ }
+}
+
+/* mptscsih_raid_set_dv_flags()
+ *
+ * New or replaced disk. Set DV flag and schedule DV.
+ */
+static void
+mptscsih_set_dvflags_raid(MPT_SCSI_HOST *hd, int id)
+{
+ MPT_ADAPTER *ioc = hd->ioc;
+ SpiCfgData *pSpi = &ioc->spi_data;
+ Ioc3PhysDisk_t *pPDisk;
+ int numPDisk;
+
+ if (hd->negoNvram != 0)
+ return;
+
+ ddvtprintk(("DV requested for phys disk id %d\n", id));
+ if (ioc->raid_data.pIocPg3) {
+ pPDisk = ioc->raid_data.pIocPg3->PhysDisk;
+ numPDisk = ioc->raid_data.pIocPg3->NumPhysDisks;
+ while (numPDisk) {
+ if (id == pPDisk->PhysDiskNum) {
+ pSpi->dvStatus[pPDisk->PhysDiskID] =
+ (MPT_SCSICFG_NEED_DV | MPT_SCSICFG_DV_NOT_DONE);
+ pSpi->forceDv = MPT_SCSICFG_NEED_DV;
+ ddvtprintk(("NEED_DV set for phys disk id %d\n",
+ pPDisk->PhysDiskID));
+ break;
+ }
+ pPDisk++;
+ numPDisk--;
+ }
+
+ if (numPDisk == 0) {
+ /* The physical disk that needs DV was not found
+ * in the stored IOC Page 3. The driver must reload
+ * this page. DV routine will set the NEED_DV flag for
+ * all phys disks that have DV_NOT_DONE set.
+ */
+ pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
+ ddvtprintk(("phys disk %d not found. Setting reload IOC Pg3 Flag\n",id));
+ }
+ }
+}
+#endif /* ~MPTSCSIH_ENABLE_DOMAIN_VALIDATION */
+