/* * * Copyright (c) 2004 Cucy Systems (http://www.cucy.com) * Curt Brune * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #include #include flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; typedef enum { FLASH_DEV_U9_512KB = 0, FLASH_DEV_U7_2MB = 1 } FLASH_DEV; #define FLASH_DQ7 (0x80) #define FLASH_DQ5 (0x20) #define PROG_ADDR (0xAAA) #define SETUP_ADDR (0xAAA) #define ID_ADDR (0xAAA) #define UNLOCK_ADDR1 (0xAAA) #define UNLOCK_ADDR2 (0x555) #define UNLOCK_CMD1 (0xAA) #define UNLOCK_CMD2 (0x55) #define ERASE_SUSPEND_CMD (0xB0) #define ERASE_RESUME_CMD (0x30) #define RESET_CMD (0xF0) #define ID_CMD (0x90) #define SELECT_CMD (0x90) #define CHIPERASE_CMD (0x10) #define BYPASS_CMD (0x20) #define SECERASE_CMD (0x30) #define PROG_CMD (0xa0) #define SETUP_CMD (0x80) #if 0 #define WRITE_UNLOCK(addr) { \ PUT__U8( addr + UNLOCK_ADDR1, UNLOCK_CMD1); \ PUT__U8( addr + UNLOCK_ADDR2, UNLOCK_CMD2); \ } /* auto select command */ #define CMD_ID(addr) WRITE_UNLOCK(addr); { \ PUT__U8( addr + ID_ADDR, ID_CMD); \ } #define CMD_RESET(addr) WRITE_UNLOCK(addr); { \ PUT__U8( addr + ID_ADDR, RESET_CMD); \ } #define CMD_ERASE_SEC(base, addr) WRITE_UNLOCK(base); \ PUT__U8( base + SETUP_ADDR, SETUP_CMD); \ WRITE_UNLOCK(base); \ PUT__U8( addr, SECERASE_CMD); #define CMD_ERASE_CHIP(base) WRITE_UNLOCK(base); \ PUT__U8( base + SETUP_ADDR, SETUP_CMD); \ WRITE_UNLOCK(base); \ PUT__U8( base + SETUP_ADDR, CHIPERASE_CMD); /* prepare for bypass programming */ #define CMD_UNLOCK_BYPASS(addr) WRITE_UNLOCK(addr); { \ PUT__U8( addr + ID_ADDR, 0x20); \ } /* terminate bypass programming */ #define CMD_BYPASS_RESET(addr) { \ PUT__U8(addr, 0x90); \ PUT__U8(addr, 0x00); \ } #endif inline static void FLASH_CMD_UNLOCK (FLASH_DEV dev, u32 base) { switch (dev) { case FLASH_DEV_U7_2MB: PUT__U8 (base + 0xAAA, 0xAA); PUT__U8 (base + 0x555, 0x55); break; case FLASH_DEV_U9_512KB: PUT__U8 (base + 0x555, 0xAA); PUT__U8 (base + 0x2AA, 0x55); break; } } inline static void FLASH_CMD_SELECT (FLASH_DEV dev, u32 base) { switch (dev) { case FLASH_DEV_U7_2MB: FLASH_CMD_UNLOCK (dev, base); PUT__U8 (base + 0xAAA, SELECT_CMD); break; case FLASH_DEV_U9_512KB: FLASH_CMD_UNLOCK (dev, base); PUT__U8 (base + 0x555, SELECT_CMD); break; } } inline static void FLASH_CMD_RESET (FLASH_DEV dev, u32 base) { switch (dev) { case FLASH_DEV_U7_2MB: FLASH_CMD_UNLOCK (dev, base); PUT__U8 (base + 0xAAA, RESET_CMD); break; case FLASH_DEV_U9_512KB: FLASH_CMD_UNLOCK (dev, base); PUT__U8 (base + 0x555, RESET_CMD); break; } } inline static void FLASH_CMD_ERASE_SEC (FLASH_DEV dev, u32 base, u32 addr) { switch (dev) { case FLASH_DEV_U7_2MB: FLASH_CMD_UNLOCK (dev, base); PUT__U8 (base + 0xAAA, SETUP_CMD); FLASH_CMD_UNLOCK (dev, base); PUT__U8 (addr, SECERASE_CMD); break; case FLASH_DEV_U9_512KB: FLASH_CMD_UNLOCK (dev, base); PUT__U8 (base + 0x555, SETUP_CMD); FLASH_CMD_UNLOCK (dev, base); PUT__U8 (addr, SECERASE_CMD); break; } } inline static void FLASH_CMD_ERASE_CHIP (FLASH_DEV dev, u32 base) { switch (dev) { case FLASH_DEV_U7_2MB: FLASH_CMD_UNLOCK (dev, base); PUT__U8 (base + 0xAAA, SETUP_CMD); FLASH_CMD_UNLOCK (dev, base); PUT__U8 (base, CHIPERASE_CMD); break; case FLASH_DEV_U9_512KB: FLASH_CMD_UNLOCK (dev, base); PUT__U8 (base + 0x555, SETUP_CMD); FLASH_CMD_UNLOCK (dev, base); PUT__U8 (base, CHIPERASE_CMD); break; } } inline static void FLASH_CMD_UNLOCK_BYPASS (FLASH_DEV dev, u32 base) { switch (dev) { case FLASH_DEV_U7_2MB: FLASH_CMD_UNLOCK (dev, base); PUT__U8 (base + 0xAAA, BYPASS_CMD); break; case FLASH_DEV_U9_512KB: FLASH_CMD_UNLOCK (dev, base); PUT__U8 (base + 0x555, BYPASS_CMD); break; } } inline static void FLASH_CMD_BYPASS_RESET (FLASH_DEV dev, u32 base) { PUT__U8 (base, SELECT_CMD); PUT__U8 (base, 0x0); } /* poll for flash command completion */ static u16 _flash_poll (FLASH_DEV dev, u32 addr, u16 data, ulong timeOut) { u32 done = 0; ulong t0; u16 error = 0; volatile u16 flashData; data = data & 0xFF; t0 = get_timer (0); while (get_timer (t0) < timeOut) { /* for( i = 0; i < POLL_LOOPS; i++) { */ /* Read the Data */ flashData = GET__U8 (addr); /* FLASH_DQ7 = Data? */ if ((flashData & FLASH_DQ7) == (data & FLASH_DQ7)) { done = 1; break; } /* Check Timeout (FLASH_DQ5==1) */ if (flashData & FLASH_DQ5) { /* Read the Data */ flashData = GET__U8 (addr); /* FLASH_DQ7 = Data? */ if (!((flashData & FLASH_DQ7) == (data & FLASH_DQ7))) { printf ("_flash_poll(): FLASH_DQ7 & flashData not equal to write value\n"); error = ERR_PROG_ERROR; } FLASH_CMD_RESET (dev, addr); done = 1; break; } /* spin delay */ udelay (10); } /* error update */ if (!done) { printf ("_flash_poll(): Timeout\n"); error = ERR_TIMOUT; } /* Check the data */ if (!error) { /* Read the Data */ flashData = GET__U8 (addr); if (flashData != data) { error = ERR_PROG_ERROR; printf ("_flash_poll(): flashData(0x%04x) not equal to data(0x%04x)\n", flashData, data); } } return error; } /*----------------------------------------------------------------------- */ static int _flash_check_protection (flash_info_t * info, int s_first, int s_last) { int sect, prot = 0; for (sect = s_first; sect <= s_last; sect++) if (info->protect[sect]) { printf (" Flash sector %d protected.\n", sect); prot++; } return prot; } static int _detectFlash (FLASH_DEV dev, u32 base, u8 venId, u8 devId) { u32 baseAddr = base | CACHE_DISABLE_MASK; u8 vendorId, deviceId; /* printf(__FUNCTION__"(): detecting flash @ 0x%08x\n", base); */ /* Send auto select command and read manufacturer info */ FLASH_CMD_SELECT (dev, baseAddr); vendorId = GET__U8 (baseAddr); FLASH_CMD_RESET (dev, baseAddr); /* Send auto select command and read device info */ FLASH_CMD_SELECT (dev, baseAddr); if (dev == FLASH_DEV_U7_2MB) { deviceId = GET__U8 (baseAddr + 2); } else if (dev == FLASH_DEV_U9_512KB) { deviceId = GET__U8 (baseAddr + 1); } else { return 0; } FLASH_CMD_RESET (dev, baseAddr); /* printf (__FUNCTION__"(): found vendorId 0x%04x, deviceId 0x%04x\n", vendorId, deviceId); */ return (vendorId == venId) && (deviceId == devId); } /****************************************************************************** * * Public u-boot interface functions below * *****************************************************************************/ /*************************************************************************** * * Flash initialization * * This board has two banks of flash, but the base addresses depend on * how the board is jumpered. * * The two flash types are: * * AMD Am29LV160DB (2MB) sectors layout 16KB, 2x8KB, 32KB, 31x64KB * * AMD Am29LV040B (512KB) sectors: 8x64KB *****************************************************************************/ unsigned long flash_init (void) { flash_info_t *info; u16 i; u32 flashtest; s16 amd160 = -1; u32 amd160base = 0; #if CONFIG_SYS_MAX_FLASH_BANKS == 2 s16 amd040 = -1; u32 amd040base = 0; #endif /* configure PHYS_FLASH_1 */ if (_detectFlash (FLASH_DEV_U7_2MB, PHYS_FLASH_1, 0x1, 0x49)) { amd160 = 0; amd160base = PHYS_FLASH_1; #if CONFIG_SYS_MAX_FLASH_BANKS == 1 } #else if (_detectFlash (FLASH_DEV_U9_512KB, PHYS_FLASH_2, 0x1, 0x4F)) { amd040 = 1; amd040base = PHYS_FLASH_2; } else { printf (__FUNCTION__ "(): Unable to detect PHYS_FLASH_2: 0x%08x\n", PHYS_FLASH_2); } } else if (_detectFlash (FLASH_DEV_U9_512KB, PHYS_FLASH_1, 0x1, 0x4F)) { amd040 = 0; amd040base = PHYS_FLASH_1; if (_detectFlash (FLASH_DEV_U7_2MB, PHYS_FLASH_2, 0x1, 0x49)) { amd160 = 1; amd160base = PHYS_FLASH_2; } else { printf (__FUNCTION__ "(): Unable to detect PHYS_FLASH_2: 0x%08x\n", PHYS_FLASH_2); } } #endif else { printf ("flash_init(): Unable to detect PHYS_FLASH_1: 0x%08x\n", PHYS_FLASH_1); } /* Configure AMD Am29LV160DB (2MB) */ info = &flash_info[amd160]; info->flash_id = FLASH_DEV_U7_2MB; info->sector_count = 35; info->size = 2 * 1024 * 1024; /* 2MB */ /* 1*16K Boot Block 2*8K Parameter Block 1*32K Small Main Block */ info->start[0] = amd160base; info->start[1] = amd160base + 0x4000; info->start[2] = amd160base + 0x6000; info->start[3] = amd160base + 0x8000; for (i = 1; i < info->sector_count; i++) info->start[3 + i] = amd160base + i * (64 * 1024); for (i = 0; i < info->sector_count; i++) { /* Write auto select command sequence and query sector protection */ FLASH_CMD_SELECT (info->flash_id, info->start[i] | CACHE_DISABLE_MASK); flashtest = GET__U8 (((info->start[i] + 4) | CACHE_DISABLE_MASK)); FLASH_CMD_RESET (info->flash_id, amd160base | CACHE_DISABLE_MASK); info->protect[i] = (flashtest & 0x0001); } /* * protect monitor and environment sectors in 2MB flash */ flash_protect (FLAG_PROTECT_SET, amd160base, amd160base + monitor_flash_len - 1, info); flash_protect (FLAG_PROTECT_SET, CONFIG_ENV_ADDR, CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, info); #if CONFIG_SYS_MAX_FLASH_BANKS == 2 /* Configure AMD Am29LV040B (512KB) */ info = &flash_info[amd040]; info->flash_id = FLASH_DEV_U9_512KB; info->sector_count = 8; info->size = 512 * 1024; /* 512KB, 8 x 64KB */ for (i = 0; i < info->sector_count; i++) { info->start[i] = amd040base + i * (64 * 1024); /* Write auto select command sequence and query sector protection */ FLASH_CMD_SELECT (info->flash_id, info->start[i] | CACHE_DISABLE_MASK); flashtest = GET__U8 (((info->start[i] + 2) | CACHE_DISABLE_MASK)); FLASH_CMD_RESET (info->flash_id, amd040base | CACHE_DISABLE_MASK); info->protect[i] = (flashtest & 0x0001); } #endif return flash_info[0].size #if CONFIG_SYS_MAX_FLASH_BANKS == 2 + flash_info[1].size #endif ; } void flash_print_info (flash_info_t * info) { int i; if (info->flash_id == FLASH_DEV_U7_2MB) { printf ("AMD Am29LV160DB (2MB) 16KB,2x8KB,32KB,31x64KB\n"); } else if (info->flash_id == FLASH_DEV_U9_512KB) { printf ("AMD Am29LV040B (512KB) 8x64KB\n"); } else { printf ("Unknown flash_id ...\n"); return; } printf (" Size: %ld KB in %d Sectors\n", info->size >> 10, info->sector_count); printf (" Sector Start Addresses:"); for (i = 0; i < info->sector_count; i++) { if ((i % 4) == 0) printf ("\n "); printf (" S%02d @ 0x%08lX%s", i, info->start[i], info->protect[i] ? " !" : " "); } printf ("\n"); } int flash_erase (flash_info_t * info, int s_first, int s_last) { u16 i, error = 0; printf ("\n"); /* check flash protection bits */ if (_flash_check_protection (info, s_first, s_last)) { printf (" Flash erase aborted due to protected sectors\n"); return ERR_PROTECTED; } if ((s_first < info->sector_count) && (s_first <= s_last)) { for (i = s_first; i <= s_last && !error; i++) { printf (" Erasing Sector %d @ 0x%08lx ... ", i, info->start[i]); /* bypass the cache to access the flash memory */ FLASH_CMD_ERASE_SEC (info->flash_id, (info-> start[0] | CACHE_DISABLE_MASK), (info-> start[i] | CACHE_DISABLE_MASK)); /* look for sector to become 0xFF after erase */ error = _flash_poll (info->flash_id, info-> start[i] | CACHE_DISABLE_MASK, 0xFF, CONFIG_SYS_FLASH_ERASE_TOUT); FLASH_CMD_RESET (info->flash_id, (info-> start[0] | CACHE_DISABLE_MASK)); printf ("done\n"); if (error) { break; } } } else error = ERR_INVAL; return error; } int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) { u16 error = 0, i; u32 n; u8 *bp, *bps; /* Write Setup */ /* bypass the cache to access the flash memory */ FLASH_CMD_UNLOCK_BYPASS (info->flash_id, (info->start[0] | CACHE_DISABLE_MASK)); /* Write the Data to Flash */ bp = (u8 *) (addr | CACHE_DISABLE_MASK); bps = (u8 *) src; for (n = 0; n < cnt && !error; n++, bp++, bps++) { if (!(n % (cnt / 15))) { printf ("."); } /* write the flash command for flash memory */ *bp = 0xA0; /* Write the data */ *bp = *bps; /* Check if the write is done */ for (i = 0; i < 0xff; i++); error = _flash_poll (info->flash_id, (u32) bp, *bps, CONFIG_SYS_FLASH_WRITE_TOUT); if (error) { return error; } } /* Reset the Flash Mode to read */ FLASH_CMD_BYPASS_RESET (info->flash_id, info->start[0]); printf (" "); return error; }