#include #include #include #include #include #include #include #include #include #include #include #include #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)) #include #endif #include "../mpt3sas_base.h" #include "inc/diag_mpt.h" #include "inc/diag_debug.h" #include "inc/diag_platform.h" #include "inc/sasdiag.h" extern bool isIocNumValid(U8 ioc_num); extern struct MPT3SAS_ADAPTER *_scsih_get_ioc_address(int ioc_id); /*The Global parameter is for Menu#3 SAS board stress FIXTURE ioc2 , need to become all target. Use it in sasdiag_doFwdlHostBoot() do determine by Dirk */ U8 g_CFG_ID_CTRL_UUT_NARROW = 0; /* * IOC firmware for all IOCs on the ctrl board and HIC * Index to this array is the IOC ID * (if all IOCs can run the same image+NVDATA setting, then no need an array) */ struct ioc_fw_img iocFwFile[NUM_IOC_TOTAL] = { { "/root/sasDiag/iocfw/Katana_onboard_12038102.bin", 1777992 }, // mercator B0 used { "/root/sasDiag/iocfw/Katana_9405W-16e_10000000_10028010.bin", 1737448 } // HBA card used }; #define NUM_PRE_BUILT_IOC_FW_SUPPORTED 22 unsigned long *fwBuf[NUM_IOC_TOTAL] = { NULL }; unsigned long fwBuf_phys_aligned[NUM_IOC_TOTAL] = { 0 }; U32 winSize; U8 numMemPages = 8; // this is (2^8) = 256 pages static U32 RoundUpToAPowerOfTwo(U32 value) { U32 answer; for (answer = 1; answer != 0; answer <<= 1) { if (answer >= value) break; } return (answer); } /** * sasdiag_unlockIocDiagnosticsRegs - Writes the unlock sequence to the * IOC host interface registers. * @ioc: per adapter object */ void sasdiag_unlockIocDiagnosticsRegs(struct MPT3SAS_ADAPTER *ioc) { U32 enabled = 0, reg_val = 0, retry_count = 3; do { writel_sdk(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); writel_sdk(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence); writel_sdk(MPI2_WRSEQ_2ND_KEY_VALUE, &ioc->chip->WriteSequence); writel_sdk(MPI2_WRSEQ_3RD_KEY_VALUE, &ioc->chip->WriteSequence); writel_sdk(MPI2_WRSEQ_4TH_KEY_VALUE, &ioc->chip->WriteSequence); writel_sdk(MPI2_WRSEQ_5TH_KEY_VALUE, &ioc->chip->WriteSequence); writel_sdk(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence); reg_val = readl_sdk(&ioc->chip->HostDiagnostic); if((enabled = (reg_val & MPI2_DIAG_DIAG_WRITE_ENABLE))) break; else { retry_count--; msleep (100); } DBG_PRINT(DBG_FWDL, "%s unlockIocDiagosticReg: Diag not enabled, retry %d!", ioc->name, retry_count); } while (retry_count > 0); if ((retry_count == 0) && !enabled) DBG_PRINT(DBG_ERR, "%s unlockIocDiagosticReg: FAILED", ioc->name); } static int sasdiag_waitForDoorbellNotReset(struct MPT3SAS_ADAPTER *ioc, int timeout) { U32 count = 0, cntdn; U32 current_state; cntdn = 100*timeout; do { current_state = mpt3sas_base_get_iocstate(ioc, 1); //if (current_state == MPI2_IOC_STATE_RESET){ if (current_state == MPI2_IOC_STATE_READY) { return 0; } msleep(10); // 10 milisecond sleep each time count++; } while (--cntdn); if (!cntdn) { DBG_PRINT(DBG_ERR, "%s waitForDoorbellNotReset: %d ms wait timedout! ioc_state=x%08x\n", ioc->name, (count * 10), current_state); return -1; } else { DBG_PRINT(DBG_FWDL, "%s waitForDoorbellNotReset: ioc_state=x%08x\n", ioc->name, current_state); return 0; } } static int sasdiag_iocHardReset(struct MPT3SAS_ADAPTER *ioc) { U32 host_diagnostic; U32 wait_timeout = 0; U32 count; msleep (100); host_diagnostic = readl_sdk(&ioc->chip->HostDiagnostic); writel_sdk(host_diagnostic | MPI2_DIAG_RESET_ADAPTER, &ioc->chip->HostDiagnostic); count = 0; do { msleep (100); /* wait 100 msec */ if (count++ > 3000){ /* wait 300 upto seconds */ wait_timeout = 1; goto out; } host_diagnostic = readl_sdk(&ioc->chip->HostDiagnostic); } while ((host_diagnostic & MPI2_DIAG_RESET_ADAPTER)); msleep (1000); out: if (wait_timeout) { DBG_PRINT(DBG_ERR, "sasdiag_iocHardReset: IOC %s HardReset failed, " "host_diagnostic=x%08x\n", ioc->name, host_diagnostic); return -1; } else { return 0; } } /** * * sasdiag_iocResetToDefault - Re-load original Firmware for IOC. * @ioc: per adapter object * @type: FORCE_BIG_HAMMER or SOFT_RESET * * Returns 0 success, anything else error. */ int sasdiag_iocResetToDefault(struct MPT3SAS_ADAPTER *ioc, enum reset_type type) { int ret = 0; /* * Put the controller into ready state (if its not already) */ ret = mpt3sas_base_hard_reset_handler(ioc, type); printk(MPT3SAS_WARN_FMT "%s: hard reset: %s\n", ioc->name, __func__, (ret == 0) ? "success" : "failed"); return ret; } /** * * _base_Getmanufacturing_pg5 - Used to Set Falcon WWWID * * @ioc: per adapter object * * Returns 0 success, anything else error. */ int sasdiag_setManPage5(struct MPT3SAS_ADAPTER *ioc) { Mpi2ManufacturingPage5_t *man_page5, tmpPage; U64 SASAddress; U16 ioc_status, sz, i; Mpi2ConfigReply_t mpi_reply; int failCnt = 0; sz = offsetof(Mpi2ManufacturingPage5_t, Phy) + (8 * sizeof(Mpi2Manufacturing5Entry_t)); man_page5 = kzalloc(sz, GFP_KERNEL); if (!man_page5) { printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); return -1; } // // Read Man page5 to get the SAS address pre-programmed in NVDATA // (which was concatenated with the ITfw and currently running from memory) // replace a byte in SAS addr with the IOC logical id to give the IOC // unique addr. UUT ctrl has IOC logical id in 2nd byte, FIXTURE 3rd byte // if (!(config_get_manufacturing_pg5(ioc, &mpi_reply, man_page5, sz, MPI2_CONFIG_ACTION_PAGE_READ_CURRENT))) { ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS){ printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); failCnt++; goto out; } }else{ printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); failCnt++; goto out; } DBG_PRINT(DBG_DUMP, "\n\n=============== Manufacture Page 5 ================\n"); for ( i = 0; i < MAX_IOC_PHYS; ++i ) { memcpy(&SASAddress, &(man_page5->Phy[i].WWID), sizeof(U64)); DBG_PRINT(DBG_DUMP, "%s p->Phy[%d].WWID=0x%08x%08x\n", ioc->name, i, (u32)(SASAddress >> 32), (u32)(SASAddress & 0xffffffffLL)); memcpy(&SASAddress, &(man_page5->Phy[i].DeviceName), sizeof(U64)); DBG_PRINT(DBG_DUMP, "\t\tp->Phy[%d].DeviceName=0x%08x%08x\n", i, (u32)(SASAddress >> 32), (u32)(SASAddress & 0xffffffffLL)); } DBG_PRINT(DBG_DUMP, "\n\n=============== Set Manufacture Page 5 ================\n"); // save the original page before manipulating it memcpy(&tmpPage, man_page5, sizeof(Mpi2ManufacturingPage5_t)); for ( i = 0; i < MAX_IOC_PHYS; ++i ) { SASAddress = tmpPage.Phy[0].WWID; #if defined ARAPAHO_BOARD /* it's blank pre-programmed for Arapaho, set it first */ if (SASAddress == 0x0LL) SASAddress = 0x5000000000000000LL; #endif // check first 4 bytes to see if it's dummy address, change it if ((SASAddress & 0xFFFFFFFF00000000LL) == 0x5000000000000000LL) { // uut ctrl has ioc logical number in the 2nd lowest byte of IOC address SASAddress += ((ioc->id * 0x1000) + i); (man_page5->Phy[i].WWID) = SASAddress; (man_page5->Phy[i].DeviceName) = SASAddress; DBG_PRINT(DBG_DUMP,"%s New SAS addr in Man page5 p->Phy[%d].WWID=0x%08x%08x\n", ioc->name, i, (U32)((man_page5->Phy[i].WWID) >> 32), (U32)((man_page5->Phy[i].WWID) & 0xffffffffLL)); // printk("sasDiag: p->Phy[%d].DeviceName=0x%08x%08x\n", // i, (u32)(SASAddress >> 32), (u32)(SASAddress & 0xffffffffLL)); } } DBG_PRINT(DBG_DUMP,"\n\n"); if (!(config_set_manufacturing_pg5(ioc, &mpi_reply, man_page5, sz))) { ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS){ printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); failCnt++; goto out; } }else{ printk(MPT3SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); failCnt++; goto out; } out: if (man_page5) kfree(man_page5); if (failCnt) return -1; else return 0; } /** * * sasdiag_changeFirmware - Replace the firmware (which the IOC fw is written to the chip) * * @ioc_num: SAS chip ID * * Returns 0 success, anything else error. */ int sasdiag_changeFirmware(U8 bootType, U8 ioc_num) { struct MPT3SAS_ADAPTER *ioc = NULL; U8 i; if (!isIocNumValid(ioc_num)) { DBG_PRINT(DBG_CFG,"Invalid IOC num %x\n", ioc_num); return IOC_NUM_INVALID; } if (ioc_num == IOC_NUM_ALL) { DBG_PRINT(DBG_CFG, "changeFirmware: on all IOCs - configID x%02x\n", bootType); } else { DBG_PRINT(DBG_CFG, "changeFirmware: IOC %d - configID x%02x\n", ioc_num, bootType); ioc = _scsih_get_ioc_address(ioc_num); if (!ioc) { DBG_PRINT(DBG_ERR, "changeFirmware IOC %d failed to get ioc addr\n", ioc_num); return -1; } } switch (bootType) { case CFG_ID_IOC_SPECIFIC: { // See the pre-built fw table declared above if (ioc_num == IOC_NUM_ALL) { DBG_PRINT(DBG_ERR, "changeFirmware: Error - invalid ioc num %x\n", ioc_num); return IOC_NUM_INVALID; } break; } default: DBG_PRINT(DBG_CFG, "Cfg 0x%02x Not Supported yet\n", bootType); return 0; } // switch // Reset for new fw to be downloaded to IOC if (sasdiag_iocResetToDefault(ioc, FORCE_BIG_HAMMER) != 0) { DBG_PRINT(DBG_CFG,"reset on ioc %d failed\n", ioc_num); return -1; } return 0; } EXPORT_SYMBOL(sasdiag_changeFirmware); int sasdiag_doFwdlHostBoot(struct MPT3SAS_ADAPTER *ioc) { U32 state; U32 host_diagnostic; struct file *fp; mm_segment_t fs; unsigned long *bufPtr, *fwBuf_aligned = NULL; unsigned long fwBuf_phys = 0; U32 imgSize = 0, HOST_DATA_SIZE = 0x1000; int ret = 0; loff_t pos; if (ioc == NULL) { printk( "%s doFwdlHostBoot: FAILED, invalid ioc addr\n", ioc->name); return -1; } imgSize = iocFwFile[ioc->id].imgSize; printk( "doFwdlHostBoot: IOC [%d] PCI DEV 0x%04x\n" "\tOpen IOC FW image file %s size=%d\n", ioc->id, ioc->pdev->device, (char *)(iocFwFile[ioc->id].fullFwName), imgSize); fp = filp_open((char *)(iocFwFile[ioc->id].fullFwName), O_RDONLY, S_IRUSR); if(IS_ERR(fp) || fp == NULL) { printk(KERN_ERR "doFwdlHostBoot: IOC [%d] FAILED open fw file (err %d)\n", ioc->id, (int)fp); return -1; } // Save previous limit and use the new kernel limit fs = get_fs(); set_fs(KERNEL_DS); winSize = imgSize + HOST_DATA_SIZE; winSize = RoundUpToAPowerOfTwo(winSize); // Alloc buffer to hold the fw, assume mem page size = 4K printk("\tCalculate winSize=0x%x (%d bytes), buf = %d+1 mem pages\n", winSize, winSize, (winSize / 4096)); if ((fwBuf[ioc->id] = (unsigned long *)__get_free_pages(GFP_KERNEL, numMemPages + 1)) == NULL) { printk( "%s doFwdlHostBoot: FAILED NULL buffer\n", ioc->name); return -1; } // Get physical addr of the buf fwBuf_phys = virt_to_phys((void *)fwBuf[ioc->id]); // Make sure addr is 4KB aligned fwBuf_aligned = (unsigned long *) (((unsigned long) fwBuf[ioc->id] + 4095) & ~(4095)); fwBuf_phys_aligned[ioc->id] = (unsigned long)fwBuf_aligned - (unsigned long)fwBuf[ioc->id] + fwBuf_phys; if (((unsigned long)fwBuf_aligned % 4096) || ((U32)fwBuf_phys_aligned[ioc->id] % 4096)) { printk( "%s doFwdlHostBoot: FAILED - mem not aligned\n", ioc->name); set_fs(fs); filp_close(fp, NULL); return -1; } printk( "%s doFwldHostBoot:\n\tbuf=0x%p aligned=0x%lx\n" "\tphysbuf=0x%lx aligned=0x%lx\n", ioc->name, (void*)fwBuf[ioc->id], (ulong)fwBuf_aligned, (ulong)fwBuf_phys, (ulong)fwBuf_phys_aligned[ioc->id]); // Copy IOC fw image from a file to top of buffer bufPtr = (unsigned long *)fwBuf_aligned; pos = 0; if (!(vfs_read(fp, (char *)bufPtr, imgSize, &pos))) { printk("\tRead fw image to top mem buf 0x%p FAILED!!!\n", bufPtr); } else { printk( "\tTop mem buf=0x%p\n\tCopy ioc fw (read fw file) to top mem buf OK\n", bufPtr); } // Restore old limit set_fs(fs); filp_close(fp, NULL); // Start host boot process printk( "%s doFwdlHostBoot: start with buf phys mem=0x%lx size=0x%x\n", ioc->name, (long unsigned int)fwBuf_phys_aligned[ioc->id], winSize); // Enable Host Diag Reg writes and force HostBoot mode out of reset sasdiag_unlockIocDiagnosticsRegs(ioc); state = readl_sdk(&ioc->chip->HostDiagnostic); state |= MPI2_DIAG_FORCE_HCB_ON_RESET; writel_sdk(state, &ioc->chip->HostDiagnostic); // Assert reset adapter printk("\tIssue HardReset (ioc %d)\n", ioc->id); sasdiag_iocHardReset(ioc); // Re-enable Host Diag Reg writes printk("\tUnlock/access Diag reg\n"); sasdiag_unlockIocDiagnosticsRegs(ioc); // Clear mis status bits state = readl_sdk(&ioc->chip->HostDiagnostic); state |= MPI2_DIAG_CLEAR_FLASH_BAD_SIG; state &= ~(MPI2_DIAG_RESET_HISTORY | MPI2_DIAG_FORCE_HCB_ON_RESET | MPI2_DIAG_BOOT_DEVICE_SELECT_MASK); writel_sdk(state, &ioc->chip->HostDiagnostic); // Initialize HCB window #if (BITS_PER_LONG == 64) printk( "\tWrite to chip buf phys addr low=0x%08x hi=0x%08x\n", (U32)(fwBuf_phys_aligned[ioc->id] & 0xffffffffLL), (U32)(fwBuf_phys_aligned[ioc->id] >> 32)); writel_sdk((fwBuf_phys_aligned[ioc->id] & 0xffffffffLL), &ioc->chip->HCBAddressLow); writel_sdk((fwBuf_phys_aligned[ioc->id] >> 32), &ioc->chip->HCBAddressHigh); #else printk( "\tWrite to chip buf phys addr low=0x%08x hi=0x%08x\n", (U32)(fwBuf_phys_aligned[ioc->id] & 0xffffffff), 0); writel_sdk(fwBuf_phys_aligned[ioc->id], &ioc->chip->HCBAddressLow); writel_sdk(0, &ioc->chip->HCBAddressHigh); #endif writel_sdk((~(winSize - 1) | MPI2_HCB_SIZE_HCB_ENABLE), &ioc->chip->HCBSize); // Initalize BootDevSel bits to HCDW which transition 0->HCDW and // put the settings above in effect state |= MPI2_DIAG_BOOT_DEVICE_SELECT_MASK; writel_sdk(state, &ioc->chip->HostDiagnostic); // Make sure the flag bad sig cleared before clearing DiagHoldIocReset printk( "\tWait for flash bad signature clear\n"); host_diagnostic = readl_sdk(&ioc->chip->HostDiagnostic); while ((host_diagnostic & MPI2_DIAG_CLEAR_FLASH_BAD_SIG) != 0) { msleep (100); host_diagnostic = readl_sdk(&ioc->chip->HostDiagnostic); } printk("\tClear Hold Reset\n"); state &= ~(MPI2_DIAG_HOLD_IOC_RESET | MPI2_DIAG_FORCE_HCB_ON_RESET); writel_sdk(state, &ioc->chip->HostDiagnostic); printk("\tDisable Diag Write Reg\n"); writel_sdk(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence); printk( "\tWait for ioc %d to post READY to Doorbell\n", ioc->id); ret = sasdiag_waitForDoorbellNotReset(ioc, 5); if (ret) { // must be timedout, ioc didn't get to ready state printk("\n%s doFwdlHostBoot: exit FAILED ioc didn't go to READY state\n", ioc->name); } else printk("%s doFwdlHostBoot: exit SUCCESSFUL\n", ioc->name); printk( "%s doFwdlHostBoot: Release resources\n", ioc->name); if (fwBuf[ioc->id]) { printk( "\tRelease ioc %d mem buf at 0x%p (2^%d + 1 mem pages)\n\n", ioc->id, (void *)fwBuf[ioc->id], numMemPages+1 ); free_pages((unsigned long *)(fwBuf[ioc->id]), numMemPages+1 ); fwBuf[ioc->id] = NULL; } return ret; }