|  | /* | 
|  | * U-boot - flash.c Flash driver for PSD4256GV | 
|  | * | 
|  | * Copyright (c) 2005 blackfin.uclinux.org | 
|  | * 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., 59 Temple Place, Suite 330, Boston, | 
|  | * MA 02111-1307 USA | 
|  | */ | 
|  |  | 
|  | #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)	{ | 
|  | #ifdef DEBUG | 
|  | printf("Device ID of the Flash is %x\n", id); | 
|  | #endif | 
|  | FlagDev = 0; | 
|  | } | 
|  | info->flash_id = id; | 
|  |  | 
|  | switch (bank_flag) { | 
|  | case 0: | 
|  | for (i = PriFlashABegin; i < SecFlashABegin; i++) | 
|  | info->start[i] = (baseaddr + (i * AFP_SectorSize1)); | 
|  | info->size = 0x200000; | 
|  | info->sector_count = 32; | 
|  | 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_b0, size_b1, size_b2; | 
|  | int i; | 
|  |  | 
|  | size_b0 = size_b1 = size_b2 = 0; | 
|  | #ifdef DEBUG | 
|  | printf("Flash Memory Start 0x%x\n", CFG_FLASH_BASE); | 
|  | printf("Memory Map for the Flash\n"); | 
|  | printf("0x20000000 - 0x200FFFFF Flash A Primary (1MB)\n"); | 
|  | printf("0x20100000 - 0x201FFFFF Flash B Primary (1MB)\n"); | 
|  | printf("0x20200000 - 0x2020FFFF Flash A Secondary (64KB)\n"); | 
|  | printf("0x20280000 - 0x2028FFFF Flash B Secondary (64KB)\n"); | 
|  | printf("Please type command flinfo for information on Sectors \n"); | 
|  | #endif | 
|  | for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { | 
|  | flash_info[i].flash_id = FLASH_UNKNOWN; | 
|  | } | 
|  |  | 
|  | size_b0 = flash_get_size(CFG_FLASH0_BASE, &flash_info[0], 0); | 
|  | size_b1 = flash_get_size(CFG_FLASH0_BASE, &flash_info[1], 1); | 
|  | size_b2 = flash_get_size(CFG_FLASH0_BASE, &flash_info[2], 2); | 
|  |  | 
|  | if (flash_info[0].flash_id == FLASH_UNKNOWN || size_b0 == 0) { | 
|  | printf("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", | 
|  | size_b0, size_b0 >> 20); | 
|  | } | 
|  |  | 
|  | (void)flash_protect(FLAG_PROTECT_SET,CFG_FLASH0_BASE,(flash_info[0].start[2] - 1),&flash_info[0]); | 
|  |  | 
|  | return (size_b0 + size_b1 + size_b2); | 
|  | } | 
|  |  | 
|  | 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 FLASH_PSD4256GV: | 
|  | printf("ST Microelectronics "); | 
|  | break; | 
|  | default: | 
|  | printf("Unknown Vendor "); | 
|  | 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 (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, info->start[i]) < 0) { | 
|  | printf("Error Sector erasing \n"); | 
|  | return FLASH_FAIL; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return FLASH_SUCCESS; | 
|  | } | 
|  |  | 
|  | int write_buff(flash_info_t * info, uchar * src, ulong addr, ulong cnt) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | ret = write_data(addr, cnt, 1, (int *) src); | 
|  | if(ret == FLASH_FAIL) | 
|  | return ERR_NOT_ERASED; | 
|  | return FLASH_SUCCESS; | 
|  | } | 
|  |  | 
|  |  | 
|  | int write_data(long lStart, long lCount, long lStride, int *pnData) | 
|  | { | 
|  | long i = 0; | 
|  | int j = 0; | 
|  | unsigned long ulOffset = lStart - CFG_FLASH_BASE; | 
|  | int d; | 
|  | int iShift = 0; | 
|  | int iNumWords = 2; | 
|  | int nLeftover = lCount % 4; | 
|  | int nSector = 0; | 
|  |  | 
|  | for (i = 0; (i < lCount / 4) && (i < BUFFER_SIZE); i++) { | 
|  | for (iShift = 0, j = 0; (j < iNumWords); | 
|  | j++, ulOffset += (lStride * 2)) { | 
|  | if ((ulOffset >= INVALIDLOCNSTART) | 
|  | && (ulOffset < INVALIDLOCNEND)) { | 
|  | printf("Invalid locations, Try writing to another location \n"); | 
|  | return FLASH_FAIL; | 
|  | } | 
|  | 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); | 
|  | if(write_flash(ulOffset, (pnData[i] >> iShift)) < 0) { | 
|  | printf("Error programming the flash \n"); | 
|  | return FLASH_FAIL; | 
|  | } | 
|  | iShift += 16; | 
|  | } | 
|  | } | 
|  | if (nLeftover > 0) { | 
|  | if ((ulOffset >= INVALIDLOCNSTART) | 
|  | && (ulOffset < INVALIDLOCNEND)) | 
|  | return FLASH_FAIL; | 
|  | get_sector_number(ulOffset, &nSector); | 
|  | read_flash(ulOffset,&d); | 
|  | if(d != 0xffff) { | 
|  | printf("Flash already programmed. Please erase to reprogram \n"); | 
|  | printf("uloffset = 0x%x \t d = 0x%x\n",ulOffset,d); | 
|  | return FLASH_FAIL; | 
|  | } | 
|  | unlock_flash(ulOffset); | 
|  | if(write_flash(ulOffset, pnData[i]) < 0) { | 
|  | printf("Error programming the flash \n"); | 
|  | return FLASH_FAIL; | 
|  | } | 
|  | } | 
|  | return FLASH_SUCCESS; | 
|  | } | 
|  |  | 
|  | int read_data(long ulStart, long lCount, long lStride, int *pnData) | 
|  | { | 
|  | long i = 0; | 
|  | int j = 0; | 
|  | long ulOffset = ulStart; | 
|  | int iShift = 0; | 
|  | int iNumWords = 2; | 
|  | int nLeftover = lCount % 4; | 
|  | int nHi, nLow; | 
|  | int nSector = 0; | 
|  |  | 
|  | for (i = 0; (i < lCount / 4) && (i < BUFFER_SIZE); i++) { | 
|  | for (iShift = 0, j = 0; j < iNumWords; j += 2) { | 
|  | if ((ulOffset >= INVALIDLOCNSTART) | 
|  | && (ulOffset < INVALIDLOCNEND)) | 
|  | return FLASH_FAIL; | 
|  |  | 
|  | get_sector_number(ulOffset, &nSector); | 
|  | read_flash(ulOffset, &nLow); | 
|  | ulOffset += (lStride * 2); | 
|  | read_flash(ulOffset, &nHi); | 
|  | ulOffset += (lStride * 2); | 
|  | pnData[i] = (nHi << 16) | nLow; | 
|  | } | 
|  | } | 
|  | if (nLeftover > 0) { | 
|  | if ((ulOffset >= INVALIDLOCNSTART) | 
|  | && (ulOffset < INVALIDLOCNEND)) | 
|  | return FLASH_FAIL; | 
|  |  | 
|  | get_sector_number(ulOffset, &nSector); | 
|  | read_flash(ulOffset, &pnData[i]); | 
|  | } | 
|  | return FLASH_SUCCESS; | 
|  | } | 
|  |  | 
|  | int write_flash(long nOffset, int nValue) | 
|  | { | 
|  | long addr; | 
|  |  | 
|  | addr = (CFG_FLASH_BASE + nOffset); | 
|  | asm("ssync;"); | 
|  | *(unsigned volatile short *) addr = nValue; | 
|  | asm("ssync;"); | 
|  | if(poll_toggle_bit(nOffset) < 0) | 
|  | return FLASH_FAIL; | 
|  | return FLASH_SUCCESS; | 
|  | } | 
|  |  | 
|  | int read_flash(long nOffset, int *pnValue) | 
|  | { | 
|  | int nValue = 0x0; | 
|  | long addr = (CFG_FLASH_BASE + nOffset); | 
|  |  | 
|  | if (nOffset != 0x2) | 
|  | reset_flash(); | 
|  | asm("ssync;"); | 
|  | nValue = *(volatile unsigned short *) addr; | 
|  | asm("ssync;"); | 
|  | *pnValue = nValue; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | int poll_toggle_bit(long lOffset) | 
|  | { | 
|  | unsigned int u1,u2; | 
|  | unsigned long timeout = 0xFFFFFFFF; | 
|  | volatile unsigned long *FB = (volatile unsigned long *)(0x20000000 + lOffset); | 
|  | while(1) { | 
|  | if(timeout < 0) | 
|  | break; | 
|  | u1 = *(volatile unsigned short *)FB; | 
|  | u2 = *(volatile unsigned short *)FB; | 
|  | if((u1 & 0x0040) == (u2 & 0x0040)) | 
|  | return FLASH_SUCCESS; | 
|  | if((u2 & 0x0020) == 0x0000) | 
|  | continue; | 
|  | u1 = *(volatile unsigned short *)FB; | 
|  | if((u2 & 0x0040) == (u1 & 0x0040)) | 
|  | return FLASH_SUCCESS; | 
|  | else { | 
|  | reset_flash(); | 
|  | return FLASH_FAIL; | 
|  | } | 
|  | timeout--; | 
|  | } | 
|  | printf("Time out occured \n"); | 
|  | if(timeout <0)	return FLASH_FAIL; | 
|  | } | 
|  |  | 
|  | 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; | 
|  |  | 
|  | write_flash(SecFlashAOff + WRITESEQ1, WRITEDATA1); | 
|  | write_flash(SecFlashAOff + WRITESEQ2, WRITEDATA2); | 
|  | write_flash(SecFlashAOff + WRITESEQ3, WRITEDATA3); | 
|  | write_flash(SecFlashAOff + WRITESEQ4, WRITEDATA4); | 
|  | write_flash(SecFlashAOff + WRITESEQ5, WRITEDATA5); | 
|  | write_flash(SecFlashAOff + WRITESEQ6, WRITEDATA6); | 
|  |  | 
|  | if(poll_toggle_bit(SecFlashASec1Off) < 0) | 
|  | return FLASH_FAIL; | 
|  |  | 
|  | write_flash(PriFlashBOff + WRITESEQ1, WRITEDATA1); | 
|  | write_flash(PriFlashBOff + WRITESEQ2, WRITEDATA2); | 
|  | write_flash(PriFlashBOff + WRITESEQ3, WRITEDATA3); | 
|  | write_flash(PriFlashBOff + WRITESEQ4, WRITEDATA4); | 
|  | write_flash(PriFlashBOff + WRITESEQ5, WRITEDATA5); | 
|  | write_flash(PriFlashBOff + WRITESEQ6, WRITEDATA6); | 
|  |  | 
|  | if(poll_toggle_bit(PriFlashBOff) <0) | 
|  | return FLASH_FAIL; | 
|  |  | 
|  | write_flash(SecFlashBOff + WRITESEQ1, WRITEDATA1); | 
|  | write_flash(SecFlashBOff + WRITESEQ2, WRITEDATA2); | 
|  | write_flash(SecFlashBOff + WRITESEQ3, WRITEDATA3); | 
|  | write_flash(SecFlashBOff + WRITESEQ4, WRITEDATA4); | 
|  | write_flash(SecFlashBOff + WRITESEQ5, WRITEDATA5); | 
|  | write_flash(SecFlashBOff + WRITESEQ6, WRITEDATA6); | 
|  |  | 
|  | if(poll_toggle_bit(SecFlashBOff) < 0) | 
|  | return FLASH_FAIL; | 
|  |  | 
|  | return FLASH_SUCCESS; | 
|  | } | 
|  |  | 
|  | int erase_block_flash(int nBlock, unsigned long address) | 
|  | { | 
|  | long ulSectorOff = 0x0; | 
|  |  | 
|  | if ((nBlock < 0) || (nBlock > AFP_NumSectors)) | 
|  | return FALSE; | 
|  |  | 
|  | ulSectorOff = (address - CFG_FLASH_BASE); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | 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(0x0002, &dev_id); | 
|  | dev_id &= 0x00FF; | 
|  |  | 
|  | reset_flash(); | 
|  |  | 
|  | return dev_id; | 
|  | } | 
|  |  | 
|  | void get_sector_number(long ulOffset, int *pnSector) | 
|  | { | 
|  | int nSector = 0; | 
|  |  | 
|  | if (ulOffset >= SecFlashAOff) { | 
|  | if ((ulOffset < SecFlashASec1Off) | 
|  | && (ulOffset < SecFlashASec2Off)) { | 
|  | nSector = SECT32; | 
|  | } else if ((ulOffset >= SecFlashASec2Off) | 
|  | && (ulOffset < SecFlashASec3Off)) { | 
|  | nSector = SECT33; | 
|  | } else if ((ulOffset >= SecFlashASec3Off) | 
|  | && (ulOffset < SecFlashASec4Off)) { | 
|  | nSector = SECT34; | 
|  | } else if ((ulOffset >= SecFlashASec4Off) | 
|  | && (ulOffset < SecFlashAEndOff)) { | 
|  | nSector = SECT35; | 
|  | } | 
|  | } else if (ulOffset >= SecFlashBOff) { | 
|  | if ((ulOffset < SecFlashBSec1Off) | 
|  | && (ulOffset < SecFlashBSec2Off)) { | 
|  | nSector = SECT36; | 
|  | } | 
|  | if ((ulOffset < SecFlashBSec2Off) | 
|  | && (ulOffset < SecFlashBSec3Off)) { | 
|  | nSector = SECT37; | 
|  | } | 
|  | if ((ulOffset < SecFlashBSec3Off) | 
|  | && (ulOffset < SecFlashBSec4Off)) { | 
|  | nSector = SECT38; | 
|  | } | 
|  | if ((ulOffset < SecFlashBSec4Off) | 
|  | && (ulOffset < SecFlashBEndOff)) { | 
|  | nSector = SECT39; | 
|  | } | 
|  | } else if ((ulOffset >= PriFlashAOff) && (ulOffset < SecFlashAOff)) { | 
|  | nSector = ulOffset & 0xffff0000; | 
|  | nSector = ulOffset >> 16; | 
|  | nSector = nSector & 0x000ff; | 
|  | } | 
|  |  | 
|  | if ((nSector >= 0) && (nSector < AFP_NumSectors)) { | 
|  | *pnSector = nSector; | 
|  | } | 
|  | } |