|  | /* | 
|  | * | 
|  | * 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[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; | 
|  | } |