diff options
Diffstat (limited to 'board/evb4510/flash.c')
-rw-r--r-- | board/evb4510/flash.c | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/board/evb4510/flash.c b/board/evb4510/flash.c new file mode 100644 index 0000000..5491827 --- /dev/null +++ b/board/evb4510/flash.c @@ -0,0 +1,540 @@ +/* + * + * Copyright (c) 2004 Cucy Systems (http://www.cucy.com) + * Curt Brune <curt@cucy.com> + * + * 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 <common.h> +#include <asm/hardware.h> +#include <flash.h> + +flash_info_t flash_info[CFG_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 CFG_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 CFG_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 (__FUNCTION__ + "(): 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, + CFG_ENV_ADDR, CFG_ENV_ADDR + CFG_ENV_SIZE - 1, info); + +#if CFG_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 CFG_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, CFG_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, + CFG_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; +} |