diff options
Diffstat (limited to 'board/etin/debris/flash.c')
-rw-r--r-- | board/etin/debris/flash.c | 720 |
1 files changed, 720 insertions, 0 deletions
diff --git a/board/etin/debris/flash.c b/board/etin/debris/flash.c new file mode 100644 index 0000000..a4100e5 --- /dev/null +++ b/board/etin/debris/flash.c @@ -0,0 +1,720 @@ +/* + * board/eva/flash.c + * + * (C) Copyright 2002 + * Sangmoon Kim, Etin Systems, dogoil@etinsys.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/processor.h> +#include <asm/pci_io.h> +#include <mpc824x.h> + +int (*do_flash_erase)(flash_info_t*, uint32_t, uint32_t); +int (*write_dword)(flash_info_t*, ulong, uint64_t); + +typedef uint64_t cfi_word; + +#define cfi_read(flash, addr) *((volatile cfi_word*)(flash->start[0] + addr)) + +#define cfi_write(flash, val, addr) \ + move64((cfi_word*)&val, \ + (cfi_word*)(flash->start[0] + addr)) + +#define CMD(x) ((((cfi_word)x)<<48)|(((cfi_word)x)<<32)|(((cfi_word)x)<<16)|(((cfi_word)x))) + +static void write32(unsigned long addr, uint32_t value) +{ + *(volatile uint32_t*)(addr) = value; + asm volatile("sync"); +} + +static uint32_t read32(unsigned long addr) +{ + uint32_t value; + value = *(volatile uint32_t*)addr; + asm volatile("sync"); + return value; +} + +static cfi_word cfi_cmd(flash_info_t *flash, uint8_t cmd, uint32_t addr) +{ + uint32_t base = flash->start[0]; + uint32_t val=(cmd << 16) | cmd; + addr <<= 3; + write32(base + addr, val); + return addr; +} + +static uint16_t cfi_read_query(flash_info_t *flash, uint32_t addr) +{ + uint32_t base = flash->start[0]; + addr <<= 3; + return (uint16_t)read32(base + addr); +} + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +static void move64(uint64_t *src, uint64_t *dest) +{ + asm volatile("lfd 0, 0(3)\n\t" /* fpr0 = *scr */ + "stfd 0, 0(4)" /* *dest = fpr0 */ + : : : "fr0" ); /* Clobbers fr0 */ + return; +} + +static int cfi_write_dword(flash_info_t *flash, ulong dest, cfi_word data) +{ + unsigned long start; + cfi_word status = 0; + + status = cfi_read(flash, dest); + data &= status; + + cfi_cmd(flash, 0x40, 0); + cfi_write(flash, data, dest); + + udelay(10); + start = get_timer (0); + for(;;) { + status = cfi_read(flash, dest); + status &= CMD(0x80); + if(status == CMD(0x80)) + break; + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { + cfi_cmd(flash, 0xff, 0); + return 1; + } + udelay(1); + } + cfi_cmd(flash, 0xff, 0); + + return 0; +} + +static int jedec_write_dword (flash_info_t *flash, ulong dest, cfi_word data) +{ + ulong start; + cfi_word status = 0; + + status = cfi_read(flash, dest); + if(status != CMD(0xffff)) return 2; + + cfi_cmd(flash, 0xaa, 0x555); + cfi_cmd(flash, 0x55, 0x2aa); + cfi_cmd(flash, 0xa0, 0x555); + + cfi_write(flash, data, dest); + + udelay(10); + start = get_timer (0); + status = ~data; + while(status != data) { + if (get_timer(start) > CFG_FLASH_WRITE_TOUT) + return 1; + status = cfi_read(flash, dest); + udelay(1); + } + return 0; +} + +static __inline__ unsigned long get_msr(void) +{ + unsigned long msr; + __asm__ __volatile__ ("mfmsr %0" : "=r" (msr) :); + return msr; +} + +static __inline__ void set_msr(unsigned long msr) +{ + __asm__ __volatile__ ("mtmsr %0" : : "r" (msr)); +} + +int write_buff (flash_info_t *flash, uchar *src, ulong addr, ulong cnt) +{ + ulong wp; + int i, s, l, rc; + cfi_word data; + uint8_t *t = (uint8_t*)&data; + unsigned long base = flash->start[0]; + uint32_t msr; + + if (flash->flash_id == FLASH_UNKNOWN) + return 4; + + if (cnt == 0) + return 0; + + addr -= base; + + msr = get_msr(); + set_msr(msr|MSR_FP); + + wp = (addr & ~7); /* get lower word aligned address */ + + if((addr-wp) != 0) { + data = cfi_read(flash, wp); + s = addr & 7; + l = ( cnt < (8-s) ) ? cnt : (8-s); + for(i = 0; i < l; i++) + t[s+i] = *src++; + if ((rc = write_dword(flash, wp, data)) != 0) + goto DONE; + wp += 8; + cnt -= l; + } + + while (cnt >= 8) { + for (i = 0; i < 8; i++) + t[i] = *src++; + if ((rc = write_dword(flash, wp, data)) != 0) + goto DONE; + wp += 8; + cnt -= 8; + } + + if (cnt == 0) { + rc = 0; + goto DONE; + } + + data = cfi_read(flash, wp); + for(i = 0; i < cnt; i++) + t[i] = *src++; + rc = write_dword(flash, wp, data); +DONE: + set_msr(msr); + return rc; +} + +static int cfi_erase_oneblock(flash_info_t *flash, uint32_t sect) +{ + int sa; + int flag; + ulong start, last, now; + cfi_word status; + + flag = disable_interrupts(); + + sa = (flash->start[sect] - flash->start[0]); + write32(flash->start[sect], 0x00200020); + write32(flash->start[sect], 0x00d000d0); + + if (flag) + enable_interrupts(); + + udelay(1000); + start = get_timer (0); + last = start; + + for (;;) { + status = cfi_read(flash, sa); + status &= CMD(0x80); + if (status == CMD(0x80)) + break; + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + cfi_cmd(flash, 0xff, 0); + printf ("Timeout\n"); + return ERR_TIMOUT; + } + + if ((now - last) > 1000) { + serial_putc ('.'); + last = now; + } + udelay(10); + } + cfi_cmd(flash, 0xff, 0); + return ERR_OK; +} + +static int cfi_erase(flash_info_t *flash, uint32_t s_first, uint32_t s_last) +{ + int sect; + int rc = ERR_OK; + + for (sect = s_first; sect <= s_last; sect++) { + if (flash->protect[sect] == 0) { + rc = cfi_erase_oneblock(flash, sect); + if (rc != ERR_OK) break; + } + } + printf (" done\n"); + return rc; +} + +static int jedec_erase(flash_info_t *flash, uint32_t s_first, uint32_t s_last) +{ + int sect; + cfi_word status; + int sa = -1; + int flag; + ulong start, last, now; + + flag = disable_interrupts(); + + cfi_cmd(flash, 0xaa, 0x555); + cfi_cmd(flash, 0x55, 0x2aa); + cfi_cmd(flash, 0x80, 0x555); + cfi_cmd(flash, 0xaa, 0x555); + cfi_cmd(flash, 0x55, 0x2aa); + for ( sect = s_first; sect <= s_last; sect++) { + if (flash->protect[sect] == 0) { + sa = flash->start[sect] - flash->start[0]; + write32(flash->start[sect], 0x00300030); + } + } + if (flag) + enable_interrupts(); + + if (sa < 0) + goto DONE; + + udelay (1000); + start = get_timer (0); + last = start; + for(;;) { + status = cfi_read(flash, sa); + if (status == CMD(0xffff)) + break; + + if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { + printf ("Timeout\n"); + return ERR_TIMOUT; + } + + if ((now - last) > 1000) { + serial_putc ('.'); + last = now; + } + udelay(10); + } +DONE: + cfi_cmd(flash, 0xf0, 0); + + printf (" done\n"); + + return ERR_OK; +} + +int flash_erase (flash_info_t *flash, int s_first, int s_last) +{ + int sect; + int prot; + + if ((s_first < 0) || (s_first > s_last)) { + if (flash->flash_id == FLASH_UNKNOWN) + printf ("- missing\n"); + else + printf ("- no sectors to erase\n"); + return ERR_NOT_ERASED; + } + if (flash->flash_id == FLASH_UNKNOWN) { + printf ("Can't erase unknown flash type - aborted\n"); + return ERR_NOT_ERASED; + } + + prot = 0; + for (sect = s_first; sect <= s_last; sect++) + if (flash->protect[sect]) prot++; + + if (prot) + printf ("- Warning: %d protected sectors will not be erased!\n", + prot); + else + printf ("\n"); + + return do_flash_erase(flash, s_first, s_last); +} + +struct jedec_flash_info { + const uint16_t mfr_id; + const uint16_t dev_id; + const char *name; + const int DevSize; + const int InterfaceDesc; + const int NumEraseRegions; + const ulong regions[4]; +}; + +#define ERASEINFO(size,blocks) (size<<8)|(blocks-1) + +#define SIZE_1MiB 20 +#define SIZE_2MiB 21 +#define SIZE_4MiB 22 + +static const struct jedec_flash_info jedec_table[] = { + { + mfr_id: (uint16_t)AMD_MANUFACT, + dev_id: (uint16_t)AMD_ID_LV800T, + name: "AMD AM29LV800T", + DevSize: SIZE_1MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + mfr_id: (uint16_t)AMD_MANUFACT, + dev_id: (uint16_t)AMD_ID_LV800B, + name: "AMD AM29LV800B", + DevSize: SIZE_1MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + mfr_id: (uint16_t)AMD_MANUFACT, + dev_id: (uint16_t)AMD_ID_LV160T, + name: "AMD AM29LV160T", + DevSize: SIZE_2MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + mfr_id: (uint16_t)AMD_MANUFACT, + dev_id: (uint16_t)AMD_ID_LV160B, + name: "AMD AM29LV160B", + DevSize: SIZE_2MiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { + mfr_id: (uint16_t)AMD_MANUFACT, + dev_id: (uint16_t)AMD_ID_LV320T, + name: "AMD AM29LV320T", + DevSize: SIZE_4MiB, + NumEraseRegions: 2, + regions: {ERASEINFO(0x10000,63), + ERASEINFO(0x02000,8) + } + + }, { + mfr_id: (uint16_t)AMD_MANUFACT, + dev_id: (uint16_t)AMD_ID_LV320B, + name: "AMD AM29LV320B", + DevSize: SIZE_4MiB, + NumEraseRegions: 2, + regions: {ERASEINFO(0x02000,8), + ERASEINFO(0x10000,63) + } + } +}; + +static ulong cfi_init(uint32_t base, flash_info_t *flash) +{ + int sector; + int block; + int block_count; + int offset = 0; + int reverse = 0; + int primary; + int mfr_id; + int dev_id; + + flash->start[0] = base; + cfi_cmd(flash, 0xF0, 0); + cfi_cmd(flash, 0x98, 0); + if ( !( cfi_read_query(flash, 0x10) == 'Q' && + cfi_read_query(flash, 0x11) == 'R' && + cfi_read_query(flash, 0x12) == 'Y' )) { + cfi_cmd(flash, 0xff, 0); + return 0; + } + + flash->size = 1 << cfi_read_query(flash, 0x27); + flash->size *= 4; + block_count = cfi_read_query(flash, 0x2c); + primary = cfi_read_query(flash, 0x15); + if ( cfi_read_query(flash, primary + 4) == 0x30) + reverse = (cfi_read_query(flash, 0x1) & 0x01); + else + reverse = (cfi_read_query(flash, primary+15) == 3); + + flash->sector_count = 0; + + for ( block = reverse ? block_count - 1 : 0; + reverse ? block >= 0 : block < block_count; + reverse ? block-- : block ++) { + int sector_size = + (cfi_read_query(flash, 0x2d + block*4+2) | + (cfi_read_query(flash, 0x2d + block*4+3) << 8)) << 8; + int sector_count = + (cfi_read_query(flash, 0x2d + block*4+0) | + (cfi_read_query(flash, 0x2d + block*4+1) << 8)) + 1; + for(sector = 0; sector < sector_count; sector++) { + flash->start[flash->sector_count++] = base + offset; + offset += sector_size * 4; + } + } + mfr_id = cfi_read_query(flash, 0x00); + dev_id = cfi_read_query(flash, 0x01); + + cfi_cmd(flash, 0xff, 0); + + flash->flash_id = (mfr_id << 16) | dev_id; + + for (sector = 0; sector < flash->sector_count; sector++) { + write32(flash->start[sector], 0x00600060); + write32(flash->start[sector], 0x00d000d0); + } + cfi_cmd(flash, 0xff, 0); + + for (sector = 0; sector < flash->sector_count; sector++) + flash->protect[sector] = 0; + + do_flash_erase = cfi_erase; + write_dword = cfi_write_dword; + + return flash->size; +} + +static ulong jedec_init(unsigned long base, flash_info_t *flash) +{ + int i; + int block, block_count; + int sector, offset; + int mfr_id, dev_id; + flash->start[0] = base; + cfi_cmd(flash, 0xF0, 0x000); + cfi_cmd(flash, 0xAA, 0x555); + cfi_cmd(flash, 0x55, 0x2AA); + cfi_cmd(flash, 0x90, 0x555); + mfr_id = cfi_read_query(flash, 0x000); + dev_id = cfi_read_query(flash, 0x0001); + cfi_cmd(flash, 0xf0, 0x000); + + for(i=0; i<sizeof(jedec_table)/sizeof(struct jedec_flash_info); i++) { + if((jedec_table[i].mfr_id == mfr_id) && + (jedec_table[i].dev_id == dev_id)) { + + flash->flash_id = (mfr_id << 16) | dev_id; + flash->size = 1 << jedec_table[0].DevSize; + flash->size *= 4; + block_count = jedec_table[i].NumEraseRegions; + offset = 0; + flash->sector_count = 0; + for (block = 0; block < block_count; block++) { + int sector_size = jedec_table[i].regions[block]; + int sector_count = (sector_size & 0xff) + 1; + sector_size >>= 8; + for (sector=0; sector<sector_count; sector++) { + flash->start[flash->sector_count++] = + base + offset; + offset += sector_size * 4; + } + } + break; + } + } + + for (sector = 0; sector < flash->sector_count; sector++) + flash->protect[sector] = 0; + + do_flash_erase = jedec_erase; + write_dword = jedec_write_dword; + + return flash->size; +} + +inline void mtibat1u(unsigned int x) +{ + __asm__ __volatile__ ("mtspr 530, %0" :: "r" (x)); +} + +inline void mtibat1l(unsigned int x) +{ + __asm__ __volatile__ ("mtspr 531, %0" :: "r" (x)); +} + +inline void mtdbat1u(unsigned int x) +{ + __asm__ __volatile__ ("mtspr 538, %0" :: "r" (x)); +} + +inline void mtdbat1l(unsigned int x) +{ + __asm__ __volatile__ ("mtspr 539, %0" :: "r" (x)); +} + +unsigned long flash_init (void) +{ + unsigned long size = 0; + int i; + unsigned int msr; + + /* BAT1 */ + CONFIG_WRITE_WORD(ERCR3, 0x0C00000C); + CONFIG_WRITE_WORD(ERCR4, 0x0800000C); + msr = get_msr(); + set_msr(msr & ~(MSR_IR | MSR_DR)); + mtibat1l(0x70000000 | BATL_PP_10 | BATL_CACHEINHIBIT); + mtibat1u(0x70000000 | BATU_BL_256M | BATU_VS | BATU_VP); + mtdbat1l(0x70000000 | BATL_PP_10 | BATL_CACHEINHIBIT); + mtdbat1u(0x70000000 | BATU_BL_256M | BATU_VS | BATU_VP); + set_msr(msr); + + for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) + flash_info[i].flash_id = FLASH_UNKNOWN; + size = cfi_init(FLASH_BASE0_PRELIM, &flash_info[0]); + if (!size) + size = jedec_init(FLASH_BASE0_PRELIM, &flash_info[0]); + + if (flash_info[0].flash_id == FLASH_UNKNOWN) + printf ("# Unknown FLASH on Bank 1 - Size = 0x%08lx = %ld MB\n", + size, size<<20); + + return size; +} + +void flash_print_info (flash_info_t *flash) +{ + int i; + int k; + int size; + int erased; + volatile unsigned long *p; + + if (flash->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + flash_init(); + } + + if (flash->flash_id == FLASH_UNKNOWN) { + printf ("missing or unknown FLASH type\n"); + return; + } + + switch (((flash->flash_id) >> 16) & 0xff) { + case 0x01: + printf ("AMD "); + break; + case 0x04: + printf("FUJITSU "); + break; + case 0x20: + printf("STM "); + break; + case 0xBF: + printf("SST "); + break; + case 0x89: + case 0xB0: + printf("INTEL "); + break; + default: + printf ("Unknown Vendor "); + break; + } + + switch ((flash->flash_id) & 0xffff) { + case (uint16_t)AMD_ID_LV800T: + printf ("AM29LV800T\n"); + break; + case (uint16_t)AMD_ID_LV800B: + printf ("AM29LV800B\n"); + break; + case (uint16_t)AMD_ID_LV160T: + printf ("AM29LV160T\n"); + break; + case (uint16_t)AMD_ID_LV160B: + printf ("AM29LV160B\n"); + break; + case (uint16_t)AMD_ID_LV320T: + printf ("AM29LV320T\n"); + break; + case (uint16_t)AMD_ID_LV320B: + printf ("AM29LV320B\n"); + break; + case (uint16_t)INTEL_ID_28F800C3T: + printf ("28F800C3T\n"); + break; + case (uint16_t)INTEL_ID_28F800C3B: + printf ("28F800C3B\n"); + break; + case (uint16_t)INTEL_ID_28F160C3T: + printf ("28F160C3T\n"); + break; + case (uint16_t)INTEL_ID_28F160C3B: + printf ("28F160C3B\n"); + break; + case (uint16_t)INTEL_ID_28F320C3T: + printf ("28F320C3T\n"); + break; + case (uint16_t)INTEL_ID_28F320C3B: + printf ("28F320C3B\n"); + break; + case (uint16_t)INTEL_ID_28F640C3T: + printf ("28F640C3T\n"); + break; + case (uint16_t)INTEL_ID_28F640C3B: + printf ("28F640C3B\n"); + break; + default: + printf ("Unknown Chip Type\n"); + break; + } + + if (flash->size >= (1 << 20)) { + printf (" Size: %ld MB in %d Sectors\n", + flash->size >> 20, flash->sector_count); + } else { + printf (" Size: %ld kB in %d Sectors\n", + flash->size >> 10, flash->sector_count); + } + + printf (" Sector Start Addresses:"); + for (i = 0; i < flash->sector_count; ++i) { + /* Check if whole sector is erased*/ + if (i != (flash->sector_count-1)) + size = flash->start[i+1] - flash->start[i]; + else + size = flash->start[0] + flash->size - flash->start[i]; + + erased = 1; + p = (volatile unsigned long *)flash->start[i]; + size = size >> 2; /* divide by 4 for longword access */ + for (k=0; k<size; k++) { + if (*p++ != 0xffffffff) { + erased = 0; + break; + } + } + + if ((i % 5) == 0) + printf ("\n "); + + printf (" %08lX%s%s", + flash->start[i], + erased ? " E" : " ", + flash->protect[i] ? "RO " : " "); + } + printf ("\n"); +} |