mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-25 04:23:46 +00:00
65cc0e2a65
The rest of the unmigrated CONFIG symbols in the CONFIG_SYS namespace do not easily transition to Kconfig. In many cases they likely should come from the device tree instead. Move these out of CONFIG namespace and in to CFG namespace. Signed-off-by: Tom Rini <trini@konsulko.com> Reviewed-by: Simon Glass <sjg@chromium.org>
455 lines
9.3 KiB
C
455 lines
9.3 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2000-2003
|
|
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
|
*
|
|
* Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
|
|
* TsiChung Liew (Tsi-Chung.Liew@freescale.com)
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <flash.h>
|
|
#include <init.h>
|
|
#include <irq_func.h>
|
|
|
|
#include <asm/immap.h>
|
|
|
|
#ifndef CONFIG_SYS_FLASH_CFI
|
|
typedef unsigned short FLASH_PORT_WIDTH;
|
|
typedef volatile unsigned short FLASH_PORT_WIDTHV;
|
|
|
|
#define FPW FLASH_PORT_WIDTH
|
|
#define FPWV FLASH_PORT_WIDTHV
|
|
|
|
#define FLASH_CYCLE1 0x5555
|
|
#define FLASH_CYCLE2 0x2aaa
|
|
|
|
#define SYNC __asm__("nop")
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* Functions
|
|
*/
|
|
|
|
ulong flash_get_size(FPWV * addr, flash_info_t * info);
|
|
int flash_get_offsets(ulong base, flash_info_t * info);
|
|
int write_word(flash_info_t * info, FPWV * dest, u16 data);
|
|
static inline void spin_wheel(void);
|
|
|
|
flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];
|
|
|
|
ulong flash_init(void)
|
|
{
|
|
ulong size = 0;
|
|
ulong fbase = 0;
|
|
|
|
fbase = (ulong) CFG_SYS_FLASH_BASE;
|
|
flash_get_size((FPWV *) fbase, &flash_info[0]);
|
|
flash_get_offsets((ulong) fbase, &flash_info[0]);
|
|
fbase += flash_info[0].size;
|
|
size += flash_info[0].size;
|
|
|
|
/* Protect monitor and environment sectors */
|
|
flash_protect(FLAG_PROTECT_SET,
|
|
CONFIG_SYS_MONITOR_BASE,
|
|
CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, &flash_info[0]);
|
|
|
|
return size;
|
|
}
|
|
|
|
int flash_get_offsets(ulong base, flash_info_t * info)
|
|
{
|
|
int i;
|
|
|
|
if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) {
|
|
|
|
info->start[0] = base;
|
|
info->protect[0] = 0;
|
|
for (i = 1; i < CFG_SYS_SST_SECT; i++) {
|
|
info->start[i] = info->start[i - 1]
|
|
+ CFG_SYS_SST_SECTSZ;
|
|
info->protect[i] = 0;
|
|
}
|
|
}
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
void flash_print_info(flash_info_t * info)
|
|
{
|
|
int i;
|
|
|
|
switch (info->flash_id & FLASH_VENDMASK) {
|
|
case FLASH_MAN_SST:
|
|
printf("SST ");
|
|
break;
|
|
default:
|
|
printf("Unknown Vendor ");
|
|
break;
|
|
}
|
|
|
|
switch (info->flash_id & FLASH_TYPEMASK) {
|
|
case FLASH_SST6401B:
|
|
printf("SST39VF6401B\n");
|
|
break;
|
|
default:
|
|
printf("Unknown Chip Type\n");
|
|
return;
|
|
}
|
|
|
|
if (info->size > 0x100000) {
|
|
int remainder;
|
|
|
|
printf(" Size: %ld", info->size >> 20);
|
|
|
|
remainder = (info->size % 0x100000);
|
|
if (remainder) {
|
|
remainder >>= 10;
|
|
remainder = (int)((float)
|
|
(((float)remainder / (float)1024) *
|
|
10000));
|
|
printf(".%d ", remainder);
|
|
}
|
|
|
|
printf("MB in %d Sectors\n", info->sector_count);
|
|
} else
|
|
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 % 5) == 0)
|
|
printf("\n ");
|
|
printf(" %08lX%s",
|
|
info->start[i], info->protect[i] ? " (RO)" : " ");
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/*
|
|
* The following code cannot be run from FLASH!
|
|
*/
|
|
ulong flash_get_size(FPWV * addr, flash_info_t * info)
|
|
{
|
|
u16 value;
|
|
|
|
addr[FLASH_CYCLE1] = (FPWV) 0x00AA00AA; /* for Atmel, Intel ignores this */
|
|
addr[FLASH_CYCLE2] = (FPWV) 0x00550055; /* for Atmel, Intel ignores this */
|
|
addr[FLASH_CYCLE1] = (FPWV) 0x00900090; /* selects Intel or Atmel */
|
|
|
|
switch (addr[0] & 0xffff) {
|
|
case (u8) SST_MANUFACT:
|
|
info->flash_id = FLASH_MAN_SST;
|
|
value = addr[1];
|
|
break;
|
|
default:
|
|
printf("Unknown Flash\n");
|
|
info->flash_id = FLASH_UNKNOWN;
|
|
info->sector_count = 0;
|
|
info->size = 0;
|
|
|
|
*addr = (FPW) 0x00F000F0;
|
|
return (0); /* no or unknown flash */
|
|
}
|
|
|
|
switch (value) {
|
|
case (u16) SST_ID_xF6401B:
|
|
info->flash_id += FLASH_SST6401B;
|
|
break;
|
|
default:
|
|
info->flash_id = FLASH_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
info->sector_count = 0;
|
|
info->size = 0;
|
|
info->sector_count = CFG_SYS_SST_SECT;
|
|
info->size = CFG_SYS_SST_SECT * CFG_SYS_SST_SECTSZ;
|
|
|
|
/* reset ID mode */
|
|
*addr = (FPWV) 0x00F000F0;
|
|
|
|
if (info->sector_count > CONFIG_SYS_MAX_FLASH_SECT) {
|
|
printf("** ERROR: sector count %d > max (%d) **\n",
|
|
info->sector_count, CONFIG_SYS_MAX_FLASH_SECT);
|
|
info->sector_count = CONFIG_SYS_MAX_FLASH_SECT;
|
|
}
|
|
|
|
return (info->size);
|
|
}
|
|
|
|
int flash_erase(flash_info_t * info, int s_first, int s_last)
|
|
{
|
|
FPWV *addr;
|
|
int flag, prot, sect, count;
|
|
ulong type, start;
|
|
int rcode = 0, flashtype = 0;
|
|
|
|
if ((s_first < 0) || (s_first > s_last)) {
|
|
if (info->flash_id == FLASH_UNKNOWN)
|
|
printf("- missing\n");
|
|
else
|
|
printf("- no sectors to erase\n");
|
|
return 1;
|
|
}
|
|
|
|
type = (info->flash_id & FLASH_VENDMASK);
|
|
|
|
switch (type) {
|
|
case FLASH_MAN_SST:
|
|
flashtype = 1;
|
|
break;
|
|
default:
|
|
type = (info->flash_id & FLASH_VENDMASK);
|
|
printf("Can't erase unknown flash type %08lx - aborted\n",
|
|
info->flash_id);
|
|
return 1;
|
|
}
|
|
|
|
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");
|
|
|
|
flag = disable_interrupts();
|
|
|
|
start = get_timer(0);
|
|
|
|
if ((s_last - s_first) == (CFG_SYS_SST_SECT - 1)) {
|
|
if (prot == 0) {
|
|
addr = (FPWV *) info->start[0];
|
|
|
|
addr[FLASH_CYCLE1] = 0x00AA; /* unlock */
|
|
addr[FLASH_CYCLE2] = 0x0055; /* unlock */
|
|
addr[FLASH_CYCLE1] = 0x0080; /* erase mode */
|
|
addr[FLASH_CYCLE1] = 0x00AA; /* unlock */
|
|
addr[FLASH_CYCLE2] = 0x0055; /* unlock */
|
|
*addr = 0x0030; /* erase chip */
|
|
|
|
count = 0;
|
|
start = get_timer(0);
|
|
|
|
while ((*addr & 0x0080) != 0x0080) {
|
|
if (count++ > 0x10000) {
|
|
spin_wheel();
|
|
count = 0;
|
|
}
|
|
|
|
/* check timeout, 1000ms */
|
|
if (get_timer(start) > 1000) {
|
|
printf("Timeout\n");
|
|
*addr = 0x00F0; /* reset to read mode */
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
*addr = 0x00F0; /* reset to read mode */
|
|
|
|
printf("\b. done\n");
|
|
|
|
if (flag)
|
|
enable_interrupts();
|
|
|
|
return 0;
|
|
} else if (prot == CFG_SYS_SST_SECT) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Start erase on unprotected sectors */
|
|
for (sect = s_first; sect <= s_last; sect++) {
|
|
if (info->protect[sect] == 0) { /* not protected */
|
|
|
|
addr = (FPWV *) (info->start[sect]);
|
|
|
|
printf(".");
|
|
|
|
/* arm simple, non interrupt dependent timer */
|
|
start = get_timer(0);
|
|
|
|
switch (flashtype) {
|
|
case 1:
|
|
{
|
|
FPWV *base; /* first address in bank */
|
|
|
|
flag = disable_interrupts();
|
|
|
|
base = (FPWV *) (CFG_SYS_FLASH_BASE); /* First sector */
|
|
|
|
base[FLASH_CYCLE1] = 0x00AA; /* unlock */
|
|
base[FLASH_CYCLE2] = 0x0055; /* unlock */
|
|
base[FLASH_CYCLE1] = 0x0080; /* erase mode */
|
|
base[FLASH_CYCLE1] = 0x00AA; /* unlock */
|
|
base[FLASH_CYCLE2] = 0x0055; /* unlock */
|
|
*addr = 0x0050; /* erase sector */
|
|
|
|
if (flag)
|
|
enable_interrupts();
|
|
|
|
while ((*addr & 0x0080) != 0x0080) {
|
|
/* check timeout, 1000ms */
|
|
if (get_timer(start) > 1000) {
|
|
printf("Timeout\n");
|
|
*addr = 0x00F0; /* reset to read mode */
|
|
|
|
rcode = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*addr = 0x00F0; /* reset to read mode */
|
|
break;
|
|
}
|
|
} /* switch (flashtype) */
|
|
}
|
|
}
|
|
printf(" done\n");
|
|
|
|
if (flag)
|
|
enable_interrupts();
|
|
|
|
return rcode;
|
|
}
|
|
|
|
int write_buff(flash_info_t * info, uchar * src, ulong addr, ulong cnt)
|
|
{
|
|
ulong wp, count;
|
|
u16 data;
|
|
int rc;
|
|
|
|
if (info->flash_id == FLASH_UNKNOWN)
|
|
return 4;
|
|
|
|
/* get lower word aligned address */
|
|
wp = addr;
|
|
|
|
/* handle unaligned start bytes */
|
|
if (wp & 1) {
|
|
data = *((FPWV *) wp);
|
|
data = (data << 8) | *src;
|
|
|
|
if ((rc = write_word(info, (FPWV *) wp, data)) != 0)
|
|
return (rc);
|
|
|
|
wp++;
|
|
cnt -= 1;
|
|
src++;
|
|
}
|
|
|
|
while (cnt >= 2) {
|
|
/*
|
|
* handle word aligned part
|
|
*/
|
|
count = 0;
|
|
data = *((FPWV *) src);
|
|
|
|
if ((rc = write_word(info, (FPWV *) wp, data)) != 0)
|
|
return (rc);
|
|
|
|
wp += 2;
|
|
src += 2;
|
|
cnt -= 2;
|
|
|
|
if (count++ > 0x800) {
|
|
spin_wheel();
|
|
count = 0;
|
|
}
|
|
}
|
|
/* handle word aligned part */
|
|
if (cnt) {
|
|
/* handle word aligned part */
|
|
count = 0;
|
|
data = *((FPWV *) wp);
|
|
|
|
data = (data & 0x00FF) | (*src << 8);
|
|
|
|
if ((rc = write_word(info, (FPWV *) wp, data)) != 0)
|
|
return (rc);
|
|
|
|
wp++;
|
|
src++;
|
|
cnt -= 1;
|
|
if (count++ > 0x800) {
|
|
spin_wheel();
|
|
count = 0;
|
|
}
|
|
}
|
|
|
|
if (cnt == 0)
|
|
return ERR_OK;
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* Write a word to Flash
|
|
* A word is 16 bits, whichever the bus width of the flash bank
|
|
* (not an individual chip) is.
|
|
*
|
|
* returns:
|
|
* 0 - OK
|
|
* 1 - write timeout
|
|
* 2 - Flash not erased
|
|
*/
|
|
int write_word(flash_info_t * info, FPWV * dest, u16 data)
|
|
{
|
|
ulong start;
|
|
int flag;
|
|
int res = 0; /* result, assume success */
|
|
FPWV *base; /* first address in flash bank */
|
|
|
|
/* Check if Flash is (sufficiently) erased */
|
|
if ((*dest & (u8) data) != (u8) data) {
|
|
return (2);
|
|
}
|
|
|
|
base = (FPWV *) (CFG_SYS_FLASH_BASE);
|
|
|
|
/* Disable interrupts which might cause a timeout here */
|
|
flag = disable_interrupts();
|
|
|
|
base[FLASH_CYCLE1] = (u8) 0x00AA00AA; /* unlock */
|
|
base[FLASH_CYCLE2] = (u8) 0x00550055; /* unlock */
|
|
base[FLASH_CYCLE1] = (u8) 0x00A000A0; /* selects program mode */
|
|
|
|
*dest = data; /* start programming the data */
|
|
|
|
/* re-enable interrupts if necessary */
|
|
if (flag)
|
|
enable_interrupts();
|
|
|
|
start = get_timer(0);
|
|
|
|
/* data polling for D7 */
|
|
while (res == 0
|
|
&& (*dest & (u8) 0x00800080) != (data & (u8) 0x00800080)) {
|
|
/* check timeout, 500ms */
|
|
if (get_timer(start) > 500) {
|
|
*dest = (u8) 0x00F000F0; /* reset bank */
|
|
res = 1;
|
|
}
|
|
}
|
|
|
|
*dest++ = (u8) 0x00F000F0; /* reset bank */
|
|
|
|
return (res);
|
|
}
|
|
|
|
static inline void spin_wheel(void)
|
|
{
|
|
static int p = 0;
|
|
static char w[] = "\\/-";
|
|
|
|
printf("\010%c", w[p]);
|
|
(++p == 3) ? (p = 0) : 0;
|
|
}
|
|
|
|
#endif
|