/**************************************************************** * Copyright (c) 2009 by Quanta Computer, Inc. All rights reserved. * * File Name: expander_flash_diag.c 1.00 * * Project Name: PikesPeak * Software Component: PikesPeak Diagnostics * * * Notes: * History: * 2009/05/19 Luap - Initial Implement *** END OF REVISION HISTORY ***********************************/ #include #include #include #include #include #include #include #include #include #include "flashExpDiag.h" #define DEBUG_MSG_EXPANDER_FLASH #ifdef DEBUG_MSG_EXPANDER_FLASH #define msg(format, arg...) printk(format "\n", ##arg) #else #define msg(format, arg...) #endif extern void GetTime(void); /*********************************************************** Name: SetExpFlashWriteEnable Description: Set Flash Write enable to 1 Input Variables: None Output Variables: None Return Value: None Globals: ***********************************************************/ void SetExpFlashWriteEnable(void) { UINT8 tmp; tmp = fpgaEppReadByte(0x1C000000+0x006); tmp = tmp | 0x01; fpgaEppWriteByte(0x1C000000+0x006, tmp); } /******************************************************************* Name:GetFlashSectorSizeExpander Description: Get flash sector size according to MX29LV320D Memory Map Input Variables: UINT32 flashadr : the program address Output Variables: None Return Value: the size of MX29LV320D's sector Globals: None *******************************************************************/ static UINT32 GetFlashSectorSizeExpander(UINT32 flashadr) { if (flashadr >= (EXPANDER_FLASH_ADDRESS + EXPANDER_FLASH_64K_FIRST_SECTOR_ADDRESS)){ return EXPANDER_FLASH_SECTOR_SIZE; }else if ((flashadr >= EXPANDER_FLASH_ADDRESS) && (flashadr <= (EXPANDER_FLASH_ADDRESS + EXPANDER_FLASH_8K_LAST_SECTOR_ADDRESS))) { return EXPANDER_FLASH_SECTOR_SIZE_8K; } printk("Get Expander Flash Sector Size fail!\n"); return 0; } #if 0 /******************************************************************* Name: GetFlashDeviceIDExpander Description: Get the device ID of MX29LV320D Input Variables: UINT32 base : the base address of MX29LV320D Output Variables: None Return Value: the device ID of MX29LV320D Globals: None *******************************************************************/ static UINT8 GetFlashDeviceIDExpander(UINT32 base) { INT32 i; UINT8 ret; fpgaEppWriteByte(base,0xF0); for (i=0;i<1000;i++); //Enter autoselect mb(); fpgaEppWriteByte((base+0xAAA),0xAA); fpgaEppWriteByte((base+0x555),0x55); fpgaEppWriteByte((base+0xAAA),0x90); mb(); for (i=0;i<1000;i++); ret = fpgaEppReadByte(base+0x02); msg("Device ID:[%x]",ret); //Exit autoselect by issuing reset fpgaEppWriteByte((base+0x555),0xF0); return ret; } #endif /******************************************************************* Name: AMD_FlashProgram_expander Description: Write one byte data to programmed address. Input Variables: UINT32 base : the base address of MX29LV320D UINT32 offset : the offset of programmed address UINT8 data : the programmed data Output Variables: None Return Value: None Globals: None *******************************************************************/ INT32 AMD_FlashProgram_expander(UINT32 base, UINT32 offset, UINT8 data) { INT32 timeout=100000; UINT8 value1, value2; UINT32 flash_adr; flash_adr = EXPANDER_FLASH_ADDRESS; mb(); fpgaEppWriteByte((flash_adr+0xAAA),0xAA); fpgaEppWriteByte((flash_adr+0x555),0x55); fpgaEppWriteByte((flash_adr+0xAAA),0xA0); fpgaEppWriteByte((base+offset),data); mb(); // mdelay(1); /* checking Toggle Bit & Data Poll */ do{ value1 = fpgaEppReadByte(base+offset);; value1 &= 0x44; value2 = fpgaEppReadByte(base+offset); value2 &= 0x44; timeout--; //mdelay(1); // ndelay(100); }while((value1 != value2) && (timeout>0)); if (timeout == 0){ printk("Program timeout\n"); return -1; } return 0; } /******************************************************************* Name: ProgramFlashExpander Description: Write some data to programmed address Input Variables: UINT32 flashadr : the programmed address UINT32 len : data length UINT8* data : the start address of data buffer Output Variables: None Return Value: 0 : Success else : Fail Globals: *******************************************************************/ static INT32 ProgramFlashExpander(UINT32 flashadr, UINT32 len, UINT8* data) { UINT32 i, j=0; INT32 num; UINT32 perdata = 0x8000;/* 32K data per time */ // msg("programming "); do{ if(len < perdata) perdata = len ; for(i = j, num = 0; i < perdata + j; i++,num++){ AMD_FlashProgram_expander(flashadr, i, data[i]); if (num == 0x800){ //DiagPostProgress("[EXPANDER FLASH] Programming Progress ", i, len); num = 0; } } len = len - perdata; j = j + perdata ; commPostProgress("[SAS Expander] Flash Programing Progress", flashadr - EXPANDER_FLASH_ADDRESS + j + 0x1FFF7, EXPANDER_FLASH_SIZE); }while(len > 0); //DiagPostProgress("[EXPANDER FLASH] Programming Progress ", len, len); return 0; } /******************************************************************* Name: ProgramFlash_4byte_expander Description: Write 4 byte to flash Input Variables: UINT32 flashadr : the programmed address UINT32 data : the programmed data Output Variables: None Return Value: 0 : Success else : Fail Globals: None *******************************************************************/ static INT32 ProgramFlash_4byte_expander(UINT32 flashadr, UINT32 data) { UINT32 i; UINT8 pattern[4]; pattern[0] = (INT8)data; pattern[1] = (INT8)(data>>8); pattern[2] = (INT8)(data>>16); pattern[3] = (INT8)(data>>24); //msg("Programming 4 byte..."); for(i = 0; i < 4; i++){ AMD_FlashProgram_expander(flashadr, i, pattern[i]); } return 0; } #if 0 /******************************************************************* Name: FlashChipErase_expander Description: Erase a chip Input Variables: UINT32 base : the base address of MX29LV320D Output Variables: None Return Value: 0 : Success else : Fail Globals: None *******************************************************************/ static INT32 FlashChipErase_expander(UINT32 base) { UINT32 timeout=1000; UINT8 dq5,dq7; //FIXME: base should equal sectorbase mb(); fpgaEppWriteByte((base+0xAAA), 0xAA); fpgaEppWriteByte((base+0x555), 0x55); fpgaEppWriteByte((base+0xAAA), 0x80); fpgaEppWriteByte((base+0xAAA), 0xAA); fpgaEppWriteByte((base+0x555), 0x55); fpgaEppWriteByte((base+0xAAA), 0x10); mb(); mdelay(15000); //additional read to prevent error do{ mdelay(1000); if(timeout-- <10) return -1; dq7 = fpgaEppReadByte(base); dq7 &= 0x80; if(dq7) break; dq5 = fpgaEppReadByte(base); dq5 &= 0x20; if(dq5){ dq7 = fpgaEppReadByte(base); dq7 &= 0x80; if(dq7) break; else return -1; } }while(1); return 0; } #endif /******************************************************************* Name: AMD_FlashSectorErase_expander Description: Erase one sectores Input Variables: UINT32 base : the base address of MX29LV320D UINT32 sector_size : the size of sector Output Variables: None Return Value: 0 : Success else Fail Globals: *******************************************************************/ static INT32 AMD_FlashSectorErase_expander(UINT32 base, UINT32 sector_size) { UINT32 timeout = 5000; UINT8 dq5, dq7; UINT8 doprint = 0; UINT32 SectorAdr; #if 1 SectorAdr = (base / sector_size ) * sector_size; //printk("[Debug Message] AMD_FlashSectorErase_expander base:[0x%x] SectorAdr:[0x%x]!\n", base, SectorAdr); mb(); fpgaEppWriteByte((EXPANDER_FLASH_ADDRESS+0xAAA), 0xAA); fpgaEppWriteByte((EXPANDER_FLASH_ADDRESS+0x555), 0x55); fpgaEppWriteByte((EXPANDER_FLASH_ADDRESS+0xAAA), 0x80); fpgaEppWriteByte((EXPANDER_FLASH_ADDRESS+0xAAA), 0xAA); fpgaEppWriteByte((EXPANDER_FLASH_ADDRESS+0x555), 0x55); fpgaEppWriteByte(SectorAdr, 0x30); mb(); udelay(1000); //additional read to prevent error do{ if(timeout-- <5){ printk("Timeout is over 5000\n"); return -1; }else if (timeout < 4000 && doprint==0){ printk("Warning: timeout over 2 sec\n"); doprint=1; } dq7 = fpgaEppReadByte(SectorAdr); dq7 &= 0x80; if(dq7) break; dq5 = fpgaEppReadByte(SectorAdr); dq5 &= 0x20; if(dq5){ dq7 = fpgaEppReadByte(SectorAdr); dq7 &= 0x80; if(dq7) break; else return -1; } mdelay(2); }while(1); #else //FIXME: base should equal sectorbase mb(); fpgaEppWriteByte((base+0xAAA), 0xAA); fpgaEppWriteByte((base+0x555), 0x55); fpgaEppWriteByte((base+0xAAA), 0x80); fpgaEppWriteByte((base+0xAAA), 0xAA); fpgaEppWriteByte((base+0x555), 0x55); fpgaEppWriteByte(base, 0x30); mb(); udelay(1000); //additional read to prevent error do{ if(timeout-- <5){ printk("Timeout is over 5000\n"); return -1; }else if (timeout < 4000 && doprint==0){ printk("Warning: timeout over 2 sec\n"); doprint=1; } dq7 = fpgaEppReadByte(base); dq7 &= 0x80; if(dq7) break; dq5 = fpgaEppReadByte(base); dq5 &= 0x20; if(dq5){ dq7 = fpgaEppReadByte(base); dq7 &= 0x80; if(dq7) break; else return -1; } mdelay(2); }while(1); #endif return 0; } /******************************************************************* Name: EraseFlashExpander Description: Erase mutil sectors Input Variables: UINT32 flashadr : the start adress of erase area UINT32 len : the length of erase sector area Output Variables: None Return Value: 0 : Success else : Fail Globals: *******************************************************************/ static INT32 EraseFlashExpander(UINT32 flashadr, UINT32 len) { INT32 i=0,sector_size; // if (len != 0) // msg("8K--- i=%x len=%x", i, len); //msg(" adr=%x len=%x", flashadr, len); /* For Security Sector 8 * 8K */ if ((flashadr >= EXPANDER_FLASH_ADDRESS) && (flashadr <= (EXPANDER_FLASH_ADDRESS + EXPANDER_FLASH_8K_LAST_SECTOR_ADDRESS))) { sector_size = EXPANDER_FLASH_SECTOR_SIZE_8K; for (i = 0; ( i < len ) && (flashadr <= (EXPANDER_FLASH_ADDRESS + EXPANDER_FLASH_8K_LAST_SECTOR_ADDRESS)); i += sector_size) { if (AMD_FlashSectorErase_expander(flashadr, sector_size)){ printk("Erase Flash error\n"); return -1; } flashadr += sector_size; } } if(i > len) len = 0; else len = len - i; if (flashadr >= (EXPANDER_FLASH_ADDRESS + EXPANDER_FLASH_64K_FIRST_SECTOR_ADDRESS)){ sector_size = EXPANDER_FLASH_SECTOR_SIZE; for (i = 0; (i < len) && (flashadr >= (EXPANDER_FLASH_ADDRESS + EXPANDER_FLASH_64K_FIRST_SECTOR_ADDRESS)); i += sector_size){ if (AMD_FlashSectorErase_expander(flashadr, sector_size)){ printk("Erase Flash error\n"); return -1; } flashadr += sector_size; } } if( i > len) len = 0; else len = len - i; if (len > 0) printk("[Debug Message] Fatal error occured in EraseFlashExpander len:[%d]!\n", len); return 0; } /******************************************************************* Name: WalkingSector_Verify_expander Description: Verify walking sector Input Variables: UINT32 adr : the start address of verify area Output Variables: None Return Value: 0 : Success else : Fail Globals: *******************************************************************/ #if 0 static INT32 WalkingSector_Verify_expander(UINT32 adr) { INT32 i; UINT32 data, inverse,pattern,offset=0,flash_size; UINT8 tmp[4]; flash_size = EXPANDER_FLASH_SIZE; for (i = 0; offset < (flash_size - EXPANDER_FLASH_SECTOR_SIZE) ;i++){ inverse = 0x00000001<= 18 ) diag_offset = offset + EXPANDER_FLASH_DIAG_OFFSET_2; else diag_offset = offset + EXPANDER_FLASH_DIAG_OFFSET_1; } printk("\n"); /* Atart address line test */ offset = 0; diag_offset = offset + EXPANDER_FLASH_DIAG_OFFSET_1; retry_cnt = 0; for (i = 0; i < (EXPANDER_FLASH_ADDRESS_LINE) && (retry_cnt < EXPANDER_FLASH_ERROR_RETRY); i++){ //msg("line = %d offset = 0x%x", i, diag_offset); program_ret = AMD_FlashProgram_expander(flash_base, diag_offset, i); if (program_ret < 0){ printk("[Expander FLASH] Program Fail --- Retry %d \n", retry_cnt); sector_size = GetFlashSectorSizeExpander(flash_base + diag_offset); erase_ret = EraseFlashExpander(flash_base + diag_offset, sector_size); retry_cnt++; i=-1; offset = 0x0; continue; } if (program_ret < 0){ printk("[Expander FLASH] Address Line:[%d] Program Fail!\n", i); commReportError(MEC_SAS_EXPANDER, EXPANDER_FLASH_DIAG_ADDRLINE, EXPANDER_FLASH_ERROR_ADDRLINE, EXPANDER_FLASH_ERROR_MSG_ADDRLINE, i, 0, 0, DG_EXPANDER_FLASH_FORMAT); return -1; } offset = 0x01 << i; if ( i >= 18 ) diag_offset = offset + EXPANDER_FLASH_DIAG_OFFSET_2; else diag_offset = offset + EXPANDER_FLASH_DIAG_OFFSET_1; } /* Verify */ offset = 0; diag_offset = offset + EXPANDER_FLASH_DIAG_OFFSET_1; for (i = 0; i < (EXPANDER_FLASH_ADDRESS_LINE ); i++){ //printk("[Expander FLASH] i:[%d] offset:[0x%x] diag_offset:[0x%x]!\n", i, offset, diag_offset); tmp = fpgaEppReadByte(flash_base + diag_offset); if (tmp != i && i != 19) { printk("[Expander FLASH] Address Line Test Fail on %d line \n",i); ret = -1; break; } offset = 0x01 << i; if ( i >= 18 ) diag_offset = offset + EXPANDER_FLASH_DIAG_OFFSET_2; else diag_offset = offset + EXPANDER_FLASH_DIAG_OFFSET_1; } if (ret < 0) { commReportError(MEC_SAS_EXPANDER, EXPANDER_FLASH_DIAG_ADDRLINE, EXPANDER_FLASH_ERROR_ADDRLINE, EXPANDER_FLASH_ERROR_MSG_ADDRLINE, i, 0, 0, DG_EXPANDER_FLASH_FORMAT); return -1; } printk("[Expander FLASH] Expander Flash Address Line Test - PASS\n"); return 0; } /*********************************************************** Name: ExpFlashDLDg Description: Testing the address line is OK or not Input Variables: None Output Variables: None Return Value: 0 : Success else : Fail Globals: ***********************************************************/ INT32 ExpFlashDLDg(void) { UINT32 flash_base; INT32 ret = 0, i, erase_ret = 0, program_ret = 0, retry_cnt = 0; UINT8 tmp, data=0; fpgaEppInit(); SetExpFlashWriteEnable(); flash_base = EXPANDER_FLASH_ADDRESS+0x3E0000; printk("[Expander FLASH] Erasing Data line test sectors...\n"); erase_ret = EraseFlashExpander(flash_base, EXPANDER_FLASH_SECTOR_SIZE); if (erase_ret < 0){ for (retry_cnt = 1; (retry_cnt < EXPANDER_FLASH_ERROR_RETRY) && (erase_ret < 0); retry_cnt++){ printk("[Expander FLASH]Erase Fail --- Retry %d \n", retry_cnt); erase_ret = EraseFlashExpander(flash_base, EXPANDER_FLASH_SECTOR_SIZE); } } if (erase_ret < 0){ printk("[Expander FLASH] Data Line Test Erase Fail\n"); commReportError(MEC_SAS_EXPANDER, EXPANDER_FLASH_DIAG_DATALINE, EXPANDER_FLASH_ERROR_DATALINE, "Data Line Test Erase Fail", 0, 0, 0, DG_FORMAT_NONE); return -1; } retry_cnt = 0; /* Start data line test */ for (i = 0; i < (EXPANDER_FLASH_DATA_LINE + 1) && (retry_cnt < EXPANDER_FLASH_ERROR_RETRY); i++){ //msg("line = %d data = %d", i, data); program_ret = AMD_FlashProgram_expander(flash_base, i, data); if (program_ret < 0){ printk("[Expander FLASH] Program Fail --- Retry %d \n", retry_cnt); erase_ret = EraseFlashExpander(flash_base, EXPANDER_FLASH_SECTOR_SIZE); retry_cnt++; i=-1; data = 0x0; continue; } if (program_ret < 0){ printk("[Expander FLASH] Data Line:[%d] Program Fail!\n", i); commReportError(MEC_SAS_EXPANDER, EXPANDER_FLASH_DIAG_DATALINE, EXPANDER_FLASH_ERROR_DATALINE, EXPANDER_FLASH_ERROR_MSG_DATALINE, i, 0, 0, DG_EXPANDER_FLASH_FORMAT); return -1; } data = 0x01 << i; } /* Verify */ data = 0; for (i = 0; i < (EXPANDER_FLASH_DATA_LINE + 1); i++){ tmp = fpgaEppReadByte(flash_base+i); if (tmp != data) { //printk("[Expander FLASH] Data Line i:[%d] Test Fail tmp=%x data=%x \n",i, tmp, data); ret = -1; break; } data = 0x01 << i; } if (ret < 0) { commReportError(MEC_SAS_EXPANDER, EXPANDER_FLASH_DIAG_DATALINE, EXPANDER_FLASH_ERROR_DATALINE, EXPANDER_FLASH_ERROR_MSG_DATALINE, i, 0, 0, DG_EXPANDER_FLASH_FORMAT); return -1; } printk("[Expander FLASH] Expander Flash Data Line Test - PASS \n"); return 0; } /* For SAS Expander flash restore */ INT32 ExpProgramFlash(UINT32 offset, UINT32 len, UINT8 *data) { INT32 ret; fpgaEppInit(); SetExpFlashWriteEnable(); ret = ImageProgramExpander(offset, len, data); return ret; } EXPORT_SYMBOL(ExpProgramFlash); MODULE_AUTHOR( "merck.hung@netapp.com" ); MODULE_DESCRIPTION( "NetApp Expander Flash Diag Code" ); MODULE_LICENSE( "GPL" ); MODULE_VERSION( "0.1" );