/*------------------------------------------------------------------------------------------*\ Direkte Ansteuerung des NOR-Flash im Panic Mode \*------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include "tffs_direct_nor.h" #if defined(CONFIG_MIPS_UR8) || defined(CONFIG_FUSIV_VX180) || defined(CONFIG_AR9) || defined(CONFIG_VR9) || defined(CONFIG_AR724x) #define FLASH_BASE KSEG1ADDR(VX180_FLASH_START) #else #error no known CPU defined #endif #define NDEBUG /*--- debug abschalten ---*/ #ifndef NDEBUG #define DEBUG_FLASH #define DBG_FLASH(...) printk(__VA_ARGS__) #else #define DBG_FLASH(...) #endif #define FLASH_MAX_WAIT 125 struct tffs_nor_flash_device tffs_nor_device; extern int tffs_mtd_offset[2]; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void FlashReset(unsigned short Cmd) { unsigned int Delay; /*--- irgend eine (gltige) Adresse ---*/ unsigned char tmp; volatile unsigned char *Dummy = (volatile unsigned char *)&tmp; unsigned char cDummy; /*--- bei einem Write_Buffer_Abort muss die Komandosequenz vorangestellt werden ---*/ if (Cmd == MX_Write_Buffer) { *((volatile unsigned short *)(FLASH_BASE + MX_Enable_Rom_Write1)) = MX_Enable_Rom_Write1_Data; *((volatile unsigned short *)(FLASH_BASE + MX_Enable_Rom_Write2)) = MX_Enable_Rom_Write2_Data; *((volatile unsigned short *)(FLASH_BASE + MX_Enable_Rom_Write1)) = MX_Flash_Reset; } else { *((volatile unsigned short *)FLASH_BASE) = Cmd; *((volatile unsigned short *)FLASH_BASE) = Cmd; /*--- um beim ST-Flash aus dem CFI-Mode zu kommen ---*/ } for(Delay = 0 ; Delay < 10000 * 5 ; Delay++) { cDummy = *Dummy; } } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ int MX_Cmd(volatile unsigned int Addr, unsigned int Cmd, unsigned short Wert, unsigned int Error) { volatile unsigned short ReadWert; /*--- DBG_FLASH("MX- Program: %s (adr=%x Value=%x)\n\r", Cmd == MX_Erase ? "Erase":"Write", Addr, Wert); ---*/ *((volatile unsigned short *)(FLASH_BASE + MX_Enable_Rom_Write1)) = MX_Enable_Rom_Write1_Data; *((volatile unsigned short *)(FLASH_BASE + MX_Enable_Rom_Write2)) = MX_Enable_Rom_Write2_Data; if (Cmd == MX_Write_Buffer) { *((volatile unsigned short *)(Addr)) = Cmd; *((volatile unsigned short *)(Addr)) = Wert; return PROGRAM_ERROR_NO_ERROR; } else { *((volatile unsigned short *)(FLASH_BASE + MX_Enable_Rom_Write3)) = Cmd; } *((volatile unsigned short *)(Addr)) = Wert; while (1) { ReadWert = *((volatile unsigned short *)Addr); if ((ReadWert & 0x40) != (*((volatile unsigned short *)Addr) & 0x40)) { /*--- Q6 still toggling ---*/ if (ReadWert & 0x20) { break; /*--- Q5 set ---*/ } } else { break; /*--- Q6 not toggling ---*/ } } ReadWert = *((volatile unsigned short *)Addr); if (Wert == ReadWert) { return PROGRAM_ERROR_NO_ERROR; } else { FlashReset(MX_Flash_Reset); } DBG_FLASH(": %s Error: Addr 0x%x Read=%x Wert=%x ", Cmd == MX_Erase ? "Erase":"Write", Addr, ReadWert, Wert); return Error; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int MX_Flash_Cmd(volatile unsigned int Addr, unsigned short Wert) { return MX_Cmd(Addr, MX_Program, Wert, PROGRAM_ERROR_WRITE_FAILED); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int flash_GetBlockSize(unsigned int address) { int i; unsigned int regions = 0; if((address >= FLASH_BASE) && (address < (FLASH_BASE + tffs_nor_device.Size))) { for (i=0;i\n", Addr); return 0; } if (datalen > buffer_size) { __printk(KERN_ERR "Error: \n"); return 0; } if ((unsigned int)pdata & 1) { memset(buffer, 0xff, FLASH_BUFFER_SIZE); memcpy(buffer, pdata, datalen); /*--- die daten sind unaligned ---*/ pShort = (unsigned short *)&buffer; } else { pShort = (unsigned short *)pdata; } if (!tffs_nor_device.Blockmode) { int retval = MX_Flash_Cmd(Addr, *pShort); if (retval == PROGRAM_ERROR_NO_ERROR) return (sizeof(unsigned short)); return 0; } /*--- wir dürfen nicht über eine Sectorgrenze schreiben ---*/ if (((unsigned int)pAddr % SectorSize) && (datalen > ((unsigned int)pAddr % SectorSize))) { datalen -= (unsigned int)pAddr % SectorSize; } /*--- nicht über die Grenze des Writebuffers schreiben ---*/ if (((unsigned int)pAddr % buffer_size) && (datalen > (buffer_size - ((unsigned int)pAddr % buffer_size)))) { datalen = (buffer_size - ((unsigned int)pAddr % buffer_size)); } /*--- Datenbuffer im Flash vorbereiten ---*/ MX_Cmd(Addr, MX_Write_Buffer, (datalen >> 1)-1, PROGRAM_ERROR_WRITE_FAILED); /*--- Daten in Buffer schreiben ---*/ for (i=0;i<(datalen>>1);i++) { *((volatile unsigned short *)(pAddr)) = *pShort; pShort++; pAddr++; } --pAddr; --pShort; /*------------------------------------------------------------------------------------------*\ toggle wurde eingeführt um einen sicheren Abbruch zu haben, wenn eine Zelle nicht programmiert werden kann - die MX-Mirrorbits antworten nicht mit Abort wenn in einer Zelle 0x00 steht und mit 0x12 beschrieben wird \*------------------------------------------------------------------------------------------*/ toggle = tmp = *pShort; /*--- und nun ins Flash schreiben - die Adresse ist die letzte an die Daten geschrieben werden ---*/ *((volatile unsigned short *)(pAddr)) = MX_Program_Buffer; retry: while (1) { ReadWert = *((volatile unsigned short *)pAddr); DBG_FLASH("0x%x 0x%x\n", ReadWert, toggle); if ((ReadWert != tmp) && (ReadWert != toggle)) { /*--- Q7 != Data ---*/ if (ReadWert & 0x20) { /*--- Q5 set (timeout) ---*/ DBG_FLASH("Q5 set 0x%x 0x%x 0x%x\n", (unsigned int)pAddr, (unsigned int)ReadWert, (unsigned int)tmp); status = 0x20; break; } if (ReadWert & 0x02) { /*--- Q1 set (abort) ---*/ DBG_FLASH("Q1 set 0x%x 0x%x 0x%x\n", (unsigned int)pAddr, (unsigned int)ReadWert, (unsigned int)tmp); status = 0x02; break; } toggle = ReadWert; } else { break; /*--- Q7 == Data ---*/ } } ReadWert = *((volatile unsigned short *)pAddr); if (ReadWert != tmp) { if (j < 20) { /*--- es gab einen Effekt, dass ein timeout signalisiert wurde, obwohl der Flash noch ---*/ j++; /*--- beim flashen war, nach mehrmaligem lesen war alles i.O. ---*/ DBG_FLASH("retry 0x%x 0x%x\n", ReadWert, (unsigned int)tmp); goto retry; } else { __printk(KERN_ERR "\n: %sError: Addr 0x%x should=0x%x read=0x%x read1=0x%x!\n", (ReadWert & 0x02) ? "Buffer ":"", (unsigned int)pAddr, *pShort, (unsigned int)ReadWert, *((volatile unsigned short *)pAddr)); if (status == 0x02) FlashReset(MX_Write_Buffer); /*--- hier Write_Buffer_Abort ---*/ FlashReset(MX_Flash_Reset); for (i=0;i buffer_size ? buffer_size : (len - Bytes); write &= ~1; /*--- Länge muss gerade sein ---*/ written = MX_Write_Block((unsigned int)pAddr, pdata, write); } pdata += written; pAddr += written; Bytes += written; if (written == 0) { __printk(KERN_ERR "\n", (int)pAddr); break; } } return Bytes; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned short cfi_read_short(unsigned short offset) { unsigned short tmp; tmp = (*((volatile unsigned short *)(FLASH_BASE + FLASH_OFFSET(offset + 0))) & 0xFF); tmp += (*((volatile unsigned short *)(FLASH_BASE + FLASH_OFFSET(offset + 1))) & 0xFF) << 8; return tmp; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned char cfi_read_char(unsigned short offset) { return (*((volatile unsigned short *)(FLASH_BASE + FLASH_OFFSET(offset))) & 0xFF); } /*------------------------------------------------------------------------------------------*\ * stellt die Flashparameter entsprechend den Werten des CFI ein \*------------------------------------------------------------------------------------------*/ unsigned int tffs_direct_nor_init(void) { unsigned char *pchar; unsigned int ret = FLASH_SUCCESS; int i = 0; unsigned char addr; unsigned short bottom; unsigned short write_buffer; FlashReset(MX_Flash_Reset); memset(&tffs_nor_device, 0, sizeof(struct tffs_nor_flash_device)); /*--- mal sehen ob der Intelflash die Kommandosequenz verträgt ---*/ *((volatile unsigned short *)(FLASH_BASE + MX_Enable_Rom_Write1)) = MX_Enable_Rom_Write1_Data; *((volatile unsigned short *)(FLASH_BASE + MX_Enable_Rom_Write2)) = MX_Enable_Rom_Write2_Data; *((volatile unsigned short *)(FLASH_BASE + MX_Enable_Rom_Write3)) = Autosel; /*--- zuerst den Hersteller herausfinden ---*/ tffs_nor_device.Manufacturer = cfi_read_char(0x00); tffs_nor_device.ID = cfi_read_char(0x01); if (tffs_nor_device.ID == MIRRORBIT_DEVICE_ID) { /*--- bei Mirrorbit steht Device-ID bei 0x0E ---*/ tffs_nor_device.ID = cfi_read_char(0x0E); } /*--- enable CFI- Query Read ---*/ *((volatile unsigned short *)(FLASH_BASE + Enable_CFI_Query)) = CFI_Query; /*--- lesen der CFI- Identification Data Values ---*/ /*--- zuerst testen des "QRY" Strings ---*/ pchar = (unsigned char *)&tffs_nor_device.Ident.Query; for (i=0;i<3;i++) *pchar++ = cfi_read_char(CFI_QRY_STRING + i) & 0xFF; if (strcmp(tffs_nor_device.Ident.Query, "QRY")) { ret = FLASH_QUERY_FAILED; } else { /*--- lesen der restlichen Werte ---*/ unsigned int *ptmp_int = (unsigned int *)&tffs_nor_device.Ident.PriVendorCmdSet_ID; for (i=0;i<8;i+=2) *ptmp_int++ = cfi_read_short(CFI_PRIM_VENDOR_CMD + i); } /*--- System-Data lesen ---*/ pchar = (unsigned char *)&tffs_nor_device.System; for (i=0;i 4) ret = FLASH_ERASE_REGIONS; /*--- read Erase region information ---*/ addr = CFI_GEO_ERASE_INFO; for (i=0;i 0x200000) { /*--- Primary Vendor-Specific Extended Query Data lesen ---*/ addr = tffs_nor_device.Ident.Addr_ext_Query_Table; bottom = cfi_read_char(addr + MX_BOOTSECTOR_FLAG_OFFSET); } else { bottom = (tffs_nor_device.ID & 1) ? 2 : 3; /*--- do it by hand ---*/ } switch (bottom) { case 2: tffs_nor_device.Organisation = BOTTOM_FLASH; break; case 3: tffs_nor_device.Organisation = TOP_FLASH; break; case 4: case 5: tffs_nor_device.Organisation = UNIFORM_FLASH; break; case 0: if (tffs_nor_device.Manufacturer == ST_MANUFACTURE_ID) { tffs_nor_device.Organisation = UNIFORM_FLASH; break; } default: ret = FLASH_MX_BOTTOM_FAILED; } /*--- bei den TOP- Flashs sind die Erase- Regions vertauscht angegeben, die Indetifikation der ---*/ /*--- TOP- Flashs ist je nach Flashsize unterschiedlich ---*/ /*--- der Einfachheit halber lesen wir die Erase- Regions in umgekehrter Reihenfolge ---*/ if (((tffs_nor_device.Organisation == BOTTOM_FLASH) && (tffs_nor_device.Size > 0x200000)) || (tffs_nor_device.Organisation == TOP_FLASH)){ addr = CFI_GEO_ERASE_INFO + ((tffs_nor_device.Geometry.num_erase_regions - 1)<<2); for (i=0; i: no valid Flash detected 0x%x\n", ret); } return ret; }