/* * U-boot - flash.c Flash driver for PSD4256GV * * Copyright (c) 2005-2007 Analog Devices Inc. * This file is based on BF533EzFlash.c originally written by Analog Devices, Inc. * * (C) Copyright 2000-2004 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * 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., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA */ #include #include #include #include "flash-defines.h" void flash_reset(void) { reset_flash(); } unsigned long flash_get_size(ulong baseaddr, flash_info_t * info, int bank_flag) { int id = 0, i = 0; static int FlagDev = 1; id = get_codes(); if (FlagDev) { FlagDev = 0; } info->flash_id = id; switch (bank_flag) { case 0: for (i = PriFlashABegin; i < SecFlashABegin; i++) info->start[i] = (baseaddr + (i * AFP_SectorSize1)); for (i = SecFlashABegin; i < NUM_SECTORS; i++) info->start[i] = (baseaddr + SecFlashAOff + ((i - SecFlashABegin) * AFP_SectorSize2)); info->size = 0x400000; info->sector_count = NUM_SECTORS; break; case 1: info->start[0] = baseaddr + SecFlashASec1Off; info->start[1] = baseaddr + SecFlashASec2Off; info->start[2] = baseaddr + SecFlashASec3Off; info->start[3] = baseaddr + SecFlashASec4Off; info->size = 0x10000; info->sector_count = 4; break; case 2: info->start[0] = baseaddr + SecFlashBSec1Off; info->start[1] = baseaddr + SecFlashBSec2Off; info->start[2] = baseaddr + SecFlashBSec3Off; info->start[3] = baseaddr + SecFlashBSec4Off; info->size = 0x10000; info->sector_count = 4; break; } return (info->size); } unsigned long flash_init(void) { unsigned long size_b; int i; size_b = 0; for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { flash_info[i].flash_id = FLASH_UNKNOWN; } size_b = flash_get_size(CFG_FLASH_BASE, &flash_info[0], 0); if (flash_info[0].flash_id == FLASH_UNKNOWN || size_b == 0) { printf("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", size_b, size_b >> 20); } /* flash_protect (int flag, ulong from, ulong to, flash_info_t *info) */ (void)flash_protect(FLAG_PROTECT_SET, CFG_FLASH_BASE, (flash_info[0].start[2] - 1), &flash_info[0]); #if (BFIN_BOOT_MODE == BF537_BYPASS_BOOT) (void)flash_protect(FLAG_PROTECT_SET, 0x203F0000, 0x203FFFFF, &flash_info[0]); #endif return (size_b); } void flash_print_info(flash_info_t * info) { int i; if (info->flash_id == FLASH_UNKNOWN) { printf("missing or unknown FLASH type\n"); return; } switch (info->flash_id) { case (STM_ID_29W320EB & 0xFFFF): case (STM_ID_29W320DB & 0xFFFF): printf("ST Microelectronics "); break; default: printf("Unknown Vendor: (0x%08X) ", info->flash_id); break; } for (i = 0; i < info->sector_count; ++i) { if ((i % 5) == 0) printf("\n "); printf(" %08lX%s", info->start[i], info->protect[i] ? " (RO)" : " "); } printf("\n"); return; } int flash_erase(flash_info_t * info, int s_first, int s_last) { int cnt = 0, i; int prot, sect; prot = 0; for (sect = s_first; sect <= s_last; ++sect) { if (info->protect[sect]) prot++; } if (prot) printf("- Warning: %d protected sectors will not be erased!\n", prot); else printf("\n"); cnt = s_last - s_first + 1; #if (BFIN_BOOT_MODE == BF537_BYPASS_BOOT) printf("Erasing Flash locations, Please Wait\n"); for (i = s_first; i <= s_last; i++) { if (info->protect[i] == 0) { /* not protected */ if (erase_block_flash(i) < 0) { printf("Error Sector erasing \n"); return FLASH_FAIL; } } } #elif (BFIN_BOOT_MODE == BF537_SPI_MASTER_BOOT) if (cnt == FLASH_TOT_SECT) { printf("Erasing flash, Please Wait \n"); if (erase_flash() < 0) { printf("Erasing flash failed \n"); return FLASH_FAIL; } } else { printf("Erasing Flash locations, Please Wait\n"); for (i = s_first; i <= s_last; i++) { if (info->protect[i] == 0) { /* not protected */ if (erase_block_flash(i) < 0) { printf("Error Sector erasing \n"); return FLASH_FAIL; } } } } #endif printf("\n"); return FLASH_SUCCESS; } int write_buff(flash_info_t * info, uchar * src, ulong addr, ulong cnt) { int d; if (addr % 2) { read_flash(addr - 1 - CFG_FLASH_BASE, &d); d = (int)((d & 0x00FF) | (*src++ << 8)); write_data(addr - 1, 2, (uchar *) & d); write_data(addr + 1, cnt - 1, src); } else write_data(addr, cnt, src); return FLASH_SUCCESS; } int write_data(long lStart, long lCount, uchar * pnData) { long i = 0; unsigned long ulOffset = lStart - CFG_FLASH_BASE; int d; int nSector = 0; int flag = 0; if (lCount % 2) { flag = 1; lCount = lCount - 1; } for (i = 0; i < lCount - 1; i += 2, ulOffset += 2) { get_sector_number(ulOffset, &nSector); read_flash(ulOffset, &d); if (d != 0xffff) { printf ("Flash not erased at offset 0x%x Please erase to reprogram \n", ulOffset); return FLASH_FAIL; } unlock_flash(ulOffset); d = (int)(pnData[i] | pnData[i + 1] << 8); write_flash(ulOffset, d); if (poll_toggle_bit(ulOffset) < 0) { printf("Error programming the flash \n"); return FLASH_FAIL; } if ((i > 0) && (!(i % AFP_SectorSize2))) printf("."); } if (flag) { get_sector_number(ulOffset, &nSector); read_flash(ulOffset, &d); if (d != 0xffff) { printf ("Flash not erased at offset 0x%x Please erase to reprogram \n", ulOffset); return FLASH_FAIL; } unlock_flash(ulOffset); d = (int)(pnData[i] | (d & 0xFF00)); write_flash(ulOffset, d); if (poll_toggle_bit(ulOffset) < 0) { printf("Error programming the flash \n"); return FLASH_FAIL; } } return FLASH_SUCCESS; } int write_flash(long nOffset, int nValue) { long addr; addr = (CFG_FLASH_BASE + nOffset); *(unsigned volatile short *)addr = nValue; sync(); #if (BFIN_BOOT_MODE == BF537_SPI_MASTER_BOOT) if (icache_status()) udelay(CONFIG_CCLK_HZ / 1000000); #endif return FLASH_SUCCESS; } int read_flash(long nOffset, int *pnValue) { unsigned short *pFlashAddr = (unsigned short *)(CFG_FLASH_BASE + nOffset); *pnValue = *pFlashAddr; return TRUE; } int poll_toggle_bit(long lOffset) { unsigned int u1, u2; volatile unsigned long *FB = (volatile unsigned long *)(CFG_FLASH_BASE + lOffset); while (1) { u1 = *(volatile unsigned short *)FB; u2 = *(volatile unsigned short *)FB; u1 ^= u2; if (!(u1 & 0x0040)) break; if (!(u2 & 0x0020)) continue; else { u1 = *(volatile unsigned short *)FB; u2 = *(volatile unsigned short *)FB; u1 ^= u2; if (!(u1 & 0x0040)) break; else { reset_flash(); return FLASH_FAIL; } } } return FLASH_SUCCESS; } void reset_flash(void) { write_flash(WRITESEQ1, RESET_VAL); /* Wait for 10 micro seconds */ udelay(10); } int erase_flash(void) { write_flash(WRITESEQ1, WRITEDATA1); write_flash(WRITESEQ2, WRITEDATA2); write_flash(WRITESEQ3, WRITEDATA3); write_flash(WRITESEQ4, WRITEDATA4); write_flash(WRITESEQ5, WRITEDATA5); write_flash(WRITESEQ6, WRITEDATA6); if (poll_toggle_bit(0x0000) < 0) return FLASH_FAIL; return FLASH_SUCCESS; } int erase_block_flash(int nBlock) { long ulSectorOff = 0x0; if ((nBlock < 0) || (nBlock > AFP_NumSectors)) return FALSE; /* figure out the offset of the block in flash */ if ((nBlock >= 0) && (nBlock < SecFlashABegin)) ulSectorOff = nBlock * AFP_SectorSize1; else if ((nBlock >= SecFlashABegin) && (nBlock < NUM_SECTORS)) ulSectorOff = SecFlashAOff + (nBlock - SecFlashABegin) * AFP_SectorSize2; /* no such sector */ else return FLASH_FAIL; write_flash((WRITESEQ1 | ulSectorOff), WRITEDATA1); write_flash((WRITESEQ2 | ulSectorOff), WRITEDATA2); write_flash((WRITESEQ3 | ulSectorOff), WRITEDATA3); write_flash((WRITESEQ4 | ulSectorOff), WRITEDATA4); write_flash((WRITESEQ5 | ulSectorOff), WRITEDATA5); write_flash(ulSectorOff, BlockEraseVal); if (poll_toggle_bit(ulSectorOff) < 0) return FLASH_FAIL; printf("."); return FLASH_SUCCESS; } void unlock_flash(long ulOffset) { unsigned long ulOffsetAddr = ulOffset; ulOffsetAddr &= 0xFFFF0000; write_flash((WRITESEQ1 | ulOffsetAddr), UNLOCKDATA1); write_flash((WRITESEQ2 | ulOffsetAddr), UNLOCKDATA2); write_flash((WRITESEQ3 | ulOffsetAddr), UNLOCKDATA3); } int get_codes() { int dev_id = 0; write_flash(WRITESEQ1, GETCODEDATA1); write_flash(WRITESEQ2, GETCODEDATA2); write_flash(WRITESEQ3, GETCODEDATA3); read_flash(0x0402, &dev_id); dev_id &= 0x0000FFFF; reset_flash(); return dev_id; } void get_sector_number(long ulOffset, int *pnSector) { int nSector = 0; long lMainEnd = 0x400000; long lBootEnd = 0x10000; /* sector numbers for the FLASH A boot sectors */ if (ulOffset < lBootEnd) { nSector = (int)ulOffset / AFP_SectorSize1; } /* sector numbers for the FLASH B boot sectors */ else if ((ulOffset >= lBootEnd) && (ulOffset < lMainEnd)) { nSector = ((ulOffset / (AFP_SectorSize2)) + 7); } /* if it is a valid sector, set it */ if ((nSector >= 0) && (nSector < AFP_NumSectors)) *pnSector = nSector; }