/*<br /> * (C) Copyright 2003-2004<br /> * Wolfgang Denk, DENX Software Engineering, wd@denx.de.<br /> *<br /> * (C) Copyright 2004<br /> * Martin Krause, TQ-Systems GmbH, martin.krause@tqs.de<br /> *<br /> * Modified for the CMC PU2 by (C) Copyright 2004 Gary Jennejohn<br /> * garyj@denx.de<br /> *<br /> * See file CREDITS for list of people who contributed to this<br /> * project.<br /> *<br /> * This program is free software; you can redistribute it and/or<br /> * modify it under the terms of the GNU General Public License as<br /> * published by the Free Software Foundation; either version 2 of<br /> * the License, or (at your option) any later version.<br /> *<br /> * This program is distributed in the hope that it will be useful,<br /> * but WITHOUT ANY WARRANTY; without even the implied warranty of<br /> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br /> * GNU General Public License for more details.<br /> *<br /> * You should have received a copy of the GNU General Public License<br /> * along with this program; if not, write to the Free Software<br /> * Foundation, Inc., 59 Temple Place, Suite 330, Boston,<br /> * MA 02111-1307 USA<br /> */<br /><br />#include <common.h><br /><br />#ifndef CFG_ENV_ADDR<br />#define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET)<br />#endif<br /><br />flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */<br /><br />#define FLASH_CYCLE1 0x0555<br />#define FLASH_CYCLE2 0x02AA<br /><br />/*-----------------------------------------------------------------------<br /> * Functions<br /> */<br />static ulong flash_get_size(vu_short *addr, flash_info_t *info);<br />static void flash_reset(flash_info_t *info);<br />static int write_word_amd(flash_info_t *info, vu_short *dest, ushort data);<br />static flash_info_t *flash_get_info(ulong base);<br /><br />/*-----------------------------------------------------------------------<br /> * flash_init()<br /> *<br /> * sets up flash_info and returns size of FLASH (bytes)<br /> */<br />unsigned long flash_init (void)<br />{<br /> unsigned long size = 0;<br /> ulong flashbase = CFG_FLASH_BASE;<br /><br /> /* Init: no FLASHes known */<br /> memset(&flash_info[0], 0, sizeof(flash_info_t));<br /><br /> flash_info[0].size = flash_get_size((vu_short *)flashbase, &flash_info[0]);<br /><br /> size = flash_info[0].size;<br /><br />#if CFG_MONITOR_BASE >= CFG_FLASH_BASE<br /> /* monitor protection ON by default */<br /> flash_protect(FLAG_PROTECT_SET,<br /> CFG_MONITOR_BASE,<br /> CFG_MONITOR_BASE+monitor_flash_len-1,<br /> flash_get_info(CFG_MONITOR_BASE));<br />#endif<br /><br />#ifdef CFG_ENV_IS_IN_FLASH<br /> /* ENV protection ON by default */<br /> flash_protect(FLAG_PROTECT_SET,<br /> CFG_ENV_ADDR,<br /> CFG_ENV_ADDR+CFG_ENV_SIZE-1,<br /> flash_get_info(CFG_ENV_ADDR));<br />#endif<br /><br /> return size ? size : 1;<br />}<br /><br />/*-----------------------------------------------------------------------<br /> */<br />static void flash_reset(flash_info_t *info)<br />{<br /> vu_short *base = (vu_short *)(info->start[0]);<br /><br /> /* Put FLASH back in read mode */<br /> if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL)<br /> *base = 0x00FF; /* Intel Read Mode */<br /> else if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_AMD)<br /> *base = 0x00F0; /* AMD Read Mode */<br />}<br /><br />/*-----------------------------------------------------------------------<br /> */<br /><br />static flash_info_t *flash_get_info(ulong base)<br />{<br /> int i;<br /> flash_info_t * info;<br /><br /> info = NULL;<br /> for (i = 0; i < CFG_MAX_FLASH_BANKS; i ++) {<br /> info = & flash_info;<br /> if (info->size && info->start[0] <= base &&<br /> base <= info->start[0] + info->size - 1)<br /> break;<br /> }<br /><br /> return i == CFG_MAX_FLASH_BANKS ? 0 : info;<br />}<br /><br />/*-----------------------------------------------------------------------<br /> */<br /><br />void flash_print_info (flash_info_t *info)<br />{<br /> int i;<br /><br /> if (info->flash_id == FLASH_UNKNOWN) {<br /> printf ("missing or unknown FLASH type\n");<br /> return;<br /> }<br /><br /> switch (info->flash_id & FLASH_VENDMASK) {<br /> case FLASH_MAN_AMD: printf ("AMD "); break;<br /> case FLASH_MAN_BM: printf ("BRIGHT MICRO "); break;<br /> case FLASH_MAN_FUJ: printf ("FUJITSU "); break;<br /> case FLASH_MAN_SST: printf ("SST "); break;<br /> case FLASH_MAN_STM: printf ("STM "); break;<br /> case FLASH_MAN_INTEL: printf ("INTEL "); break;<br /> default: printf ("Unknown Vendor "); break;<br /> }<br /><br /> switch (info->flash_id & FLASH_TYPEMASK) {<br /> case FLASH_S29GL064M:<br /> printf ("S29GL064M-R6 (64Mbit, uniform sector size)\n");<br /> break;<br /> default:<br /> printf ("Unknown Chip Type\n");<br /> break;<br /> }<br /><br /> printf (" Size: %ld MB in %d Sectors\n",<br /> info->size >> 20,<br /> info->sector_count);<br /><br /> printf (" Sector Start Addresses:");<br /><br /> for (i=0; i<info->sector_count; ++i) {<br /> if ((i % 5) == 0) {<br /> printf ("\n ");<br /> }<br /> printf (" %08lX%s",<br /> info->start,<br /> info->protect ? " (RO)" : " ");<br /> }<br /> printf ("\n");<br /> return;<br />}<br /><br />/*-----------------------------------------------------------------------<br /> */<br /><br />/*<br /> * The following code cannot be run from FLASH!<br /> */<br /><br />ulong flash_get_size (vu_short *addr, flash_info_t *info)<br />{<br /> int i;<br /> ushort value;<br /> ulong base = (ulong)addr;<br /><br /> /* Write auto select command sequence */<br /> addr[FLASH_CYCLE1] = 0x00AA; /* for AMD, Intel ignores this */<br /> addr[FLASH_CYCLE2] = 0x0055; /* for AMD, Intel ignores this */<br /> addr[FLASH_CYCLE1] = 0x0090; /* selects Intel or AMD */<br /><br /> /* read Manufacturer ID */<br /> udelay(100);<br /> value = addr[0];<br /> debug ("Manufacturer ID: %04X\n", value);<br /><br /> switch (value) {<br /><br /> case (AMD_MANUFACT & 0xFFFF):<br /> debug ("Manufacturer: AMD (Spansion)\n");<br /> info->flash_id = FLASH_MAN_AMD;<br /> break;<br /><br /> case (INTEL_MANUFACT & 0xFFFF):<br /> debug ("Manufacturer: Intel (not supported yet)\n");<br /> info->flash_id = FLASH_MAN_INTEL;<br /> break;<br /><br /> default:<br /> printf ("Unknown Manufacturer ID: %04X\n", value);<br /> info->flash_id = FLASH_UNKNOWN;<br /> info->sector_count = 0;<br /> info->size = 0;<br /> goto out;<br /> }<br /><br /> value = addr[1];<br /> debug ("Device ID: %04X\n", value);<br /><br /> switch (addr[1]) {<br /><br /> case (AMD_ID_MIRROR & 0xFFFF):<br /> debug ("Mirror Bit flash: addr[14] = %08X addr[15] = %08X\n",<br /> addr[14], addr[15]);<br /><br /> switch(addr[14]) {<br /> case (AMD_ID_GL064M_2 & 0xFFFF):<br /> if (addr[15] != (AMD_ID_GL064M_3 & 0xffff)) {<br /> printf ("Chip: S29GLxxxM -> unknown\n");<br /> info->flash_id = FLASH_UNKNOWN;<br /> info->sector_count = 0;<br /> info->size = 0;<br /> } else {<br /> debug ("Chip: S29GL064M-R6\n");<br /> info->flash_id += FLASH_S29GL064M;<br /> info->sector_count = 128;<br /> info->size = 0x00800000;<br /> for (i = 0; i < info->sector_count; i++) {<br /> info->start = base;<br /> base += 0x10000;<br /> }<br /> }<br /> break; /* => 16 MB */<br /> default:<br /> printf ("Chip: *** unknown ***\n");<br /> info->flash_id = FLASH_UNKNOWN;<br /> info->sector_count = 0;<br /> info->size = 0;<br /> break;<br /> }<br /> break;<br /><br /> default:<br /> printf ("Unknown Device ID: %04X\n", value);<br /> info->flash_id = FLASH_UNKNOWN;<br /> info->sector_count = 0;<br /> info->size = 0;<br /> break;<br /> }<br /><br />out:<br /> /* Put FLASH back in read mode */<br /> flash_reset(info);<br /><br /> return (info->size);<br />}<br /><br />/*-----------------------------------------------------------------------<br /> */<br /><br />int flash_erase (flash_info_t *info, int s_first, int s_last)<br />{<br /> vu_short *addr = (vu_short *)(info->start[0]);<br /> int flag, prot, sect, ssect, l_sect;<br /> ulong now, last;<br /><br /> debug ("flash_erase: first: %d last: %d\n", s_first, s_last);<br /><br /> if ((s_first < 0) || (s_first > s_last)) {<br /> if (info->flash_id == FLASH_UNKNOWN) {<br /> printf ("- missing\n");<br /> } else {<br /> printf ("- no sectors to erase\n");<br /> }<br /> return 1;<br /> }<br /><br /> if ((info->flash_id == FLASH_UNKNOWN) ||<br /> (info->flash_id > FLASH_AMD_COMP)) {<br /> printf ("Can't erase unknown flash type %08lx - aborted\n",<br /> info->flash_id);<br /> return 1;<br /> }<br /><br /> prot = 0;<br /> for (sect=s_first; sect<=s_last; ++sect) {<br /> if (info->protect[sect]) {<br /> prot++;<br /> }<br /> }<br /><br /> if (prot) {<br /> printf ("- Warning: %d protected sectors will not be erased!\n",<br /> prot);<br /> } else {<br /> printf ("\n");<br /> }<br /><br /> /* Disable interrupts which might cause a timeout here */<br /> flag = disable_interrupts();<br /><br /> /*<br /> * Start erase on unprotected sectors.<br /> * Since the flash can erase multiple sectors with one command<br /> * we take advantage of that by doing the erase in chunks of<br /> * 3 sectors.<br /> */<br /> for (sect = s_first; sect <= s_last; ) {<br /> l_sect = -1;<br /><br /> addr[FLASH_CYCLE1] = 0x00AA;<br /> addr[FLASH_CYCLE2] = 0x0055;<br /> addr[FLASH_CYCLE1] = 0x0080;<br /> addr[FLASH_CYCLE1] = 0x00AA;<br /> addr[FLASH_CYCLE2] = 0x0055;<br /><br /> /* do the erase in chunks of at most 3 sectors */<br /> for (ssect = 0; ssect < 3; ssect++) {<br /> if ((sect + ssect) > s_last)<br /> break;<br /> if (info->protect[sect + ssect] == 0) { /* not protected */<br /> addr = (vu_short *)(info->start[sect + ssect]);<br /> addr[0] = 0x0030;<br /> l_sect = sect + ssect;<br /> }<br /> }<br /> /* wait at least 80us - let's wait 1 ms */<br /> udelay (1000);<br /><br /> /*<br /> * We wait for the last triggered sector<br /> */<br /> if (l_sect < 0)<br /> goto DONE;<br /><br /> reset_timer_masked ();<br /> last = 0;<br /> addr = (vu_short *)(info->start[l_sect]);<br /> while ((addr[0] & 0x0080) != 0x0080) {<br /> if ((now = get_timer_masked ()) > CFG_FLASH_ERASE_TOUT) {<br /> printf ("Timeout\n");<br /> return 1;<br /> }<br /> /* show that we're waiting */<br /> if ((now - last) > 1000) { /* every second */<br /> putc ('.');<br /> last = now;<br /> }<br /> }<br /> addr = (vu_short *)info->start[0];<br /> addr[0] = 0x00F0; /* reset bank */<br /> sect += ssect;<br /> }<br /><br /> /* re-enable interrupts if necessary */<br /> if (flag)<br /> enable_interrupts();<br /><br />DONE:<br /> /* reset to read mode */<br /> addr = (vu_short *)info->start[0];<br /> addr[0] = 0x00F0; /* reset bank */<br /><br /> printf (" done\n");<br /> return 0;<br />}<br /><br />/*-----------------------------------------------------------------------<br /> * Copy memory to flash, returns:<br /> * 0 - OK<br /> * 1 - write timeout<br /> * 2 - Flash not erased<br /> */<br /><br />int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt)<br />{<br /> ulong wp, data;<br /> int rc;<br /><br /> if (addr & 1) {<br /> printf ("unaligned destination not supported\n");<br /> return ERR_ALIGN;<br /> };<br /><br /> if ((int) src & 1) {<br /> printf ("unaligned source not supported\n");<br /> return ERR_ALIGN;<br /> };<br /><br /> wp = addr;<br /><br /> while (cnt >= 2) {<br /> data = *((vu_short *)src);<br /> if ((rc = write_word_amd(info, (vu_short *)wp, data)) != 0) {<br />printf ("write_buff 1: write_word_amd() rc=%d\n", rc);<br /> return (rc);<br /> }<br /> src += 2;<br /> wp += 2;<br /> cnt -= 2;<br /> }<br /><br /> if (cnt == 0) {<br /> return (ERR_OK);<br /> }<br /><br /> if (cnt == 1) {<br /> data = (*((volatile u8 *) src)) | (*((volatile u8 *) (wp + 1)) << 8);<br /> if ((rc = write_word_amd(info, (vu_short *)wp, data)) != 0) {<br />printf ("write_buff 1: write_word_amd() rc=%d\n", rc);<br /> return (rc);<br /> }<br /> src += 1;<br /> wp += 1;<br /> cnt -= 1;<br /> }<br /><br /> return ERR_OK;<br />}<br /><br />/*-----------------------------------------------------------------------<br /> * Write a word to Flash for AMD FLASH<br /> * A word is 16 or 32 bits, whichever the bus width of the flash bank<br /> * (not an individual chip) is.<br /> *<br /> * returns:<br /> * 0 - OK<br /> * 1 - write timeout<br /> * 2 - Flash not erased<br /> */<br />static int write_word_amd (flash_info_t *info, vu_short *dest, ushort data)<br />{<br /> int flag;<br /> vu_short *base; /* first address in flash bank */<br /><br /> /* Check if Flash is (sufficiently) erased */<br /> if ((*dest & data) != data) {<br /> return (2);<br /> }<br /><br /> base = (vu_short *)(info->start[0]);<br /><br /> /* Disable interrupts which might cause a timeout here */<br /> flag = disable_interrupts();<br /><br /> base[FLASH_CYCLE1] = 0x00AA; /* unlock */<br /> base[FLASH_CYCLE2] = 0x0055; /* unlock */<br /> base[FLASH_CYCLE1] = 0x00A0; /* selects program mode */<br /><br /> *dest = data; /* start programming the data */<br /><br /> /* re-enable interrupts if necessary */<br /> if (flag)<br /> enable_interrupts();<br /><br /> reset_timer_masked ();<br /><br /> /* data polling for D7 */<br /> while ((*dest & 0x0080) != (data & 0x0080)) {<br /> if (get_timer_masked () > CFG_FLASH_WRITE_TOUT) {<br /> *dest = 0x00F0; /* reset bank */<br /> return (1);<br /> }<br /> }<br /> return (0);<br />}
|