You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

596 lines
14 KiB

/*************************************************************
* File: lib/nvram.c
* Purpose: Part of C runtime library
* Author: Phil Bunce (pjb@carmel.com)
* Revision History:
* 970304 Start of revision history
* 970311 Added flush_cache(DCACHE) for 4010 copy-back
* 970605 Added writeFlash
* 970620 Cygnus requires Uchar in flash29.
* 970910 Added support for Am29F080.
* 971124 Don't create msg for famcode=0000.
* 971125 Added code to writeFlash for 4011 copy from boot eprom
* 980114 Added delay to start of nvType for GHS+4011.
* 980120 Replaced outb() macro with a function. Fixes prob w
* nvType check on 4011. Removed delay.
* 980324 Fixed problem with outb. It wasn't doing the right thing.
* 980508 Added needcpy.
* 980508 Flush D before I in nvCheck.
* 980509 Discovered that for needcpy=1 outb MUST be a macro.
* 980509 The new outb macro works on 4011 but fails on 4101.
* 980510 Reverted to old outb macro until real fix is found.
* 980602 Delay shrc execution until after the powerup banner.
* Do this by setting shrc_needed.
* 980715 Modified do_shrc and shrc to support 2 separate init
* blocks. Before p2 is executed before the banner. After
* p2 is executed after the banner. Only commands that
* were specified in the "set nvram" command are located
* after the p2.
*/
/*************************************************************
* Performance data:
* 3 secs to erase one sector
* 128 secs/MB for writes
*/
#include <stdio.h>
#include <mips.h>
#include <termio.h>
#include <mon.h>
#define inb(x) (*(volatile Uchar *)(x))
#if 0
#define outb(a,v) *((Uchar *)(a))=((Uchar)(v))
#else
/*************************************************************
* outb(a,v)
* store byte with write buffer flush.
* For 4011 you MUST use Kseg1 addresses for tmp1 and tmp2.
* 980324 Added & for wbflushtmpX and volatile for wbftX.
*/
#define outb(a,v) \
{ \
volatile int *wbft1,*wbft2; \
\
*((Uchar *)(a))=((Uchar)(v)); \
wbft1 = (int *)(((Ulong)&wbflushtmp1)|K1BASE); \
wbft2 = (int *)(((Ulong)&wbflushtmp2)|K1BASE); \
*wbft1 = 0; \
*wbft2 = *wbft1; \
}
#endif
int nvType(),nvType_end();
int flash28(),flash28end();
int flash29(),flash29end();
int xl28(),xl28end();
NvType nvTypes[] = {
{0x01a2,"Am28F010",0, 128*1024,flash28,flash28end},
{0x01a7,"Am28F010",0, 128*1024,flash28,flash28end},
{0x89b4,"i28F010", 0, 128*1024,flash28,flash28end},
{0x89bd,"i28F020", 0, 256*1024,flash28,flash28end},
{0x0120,"Am29F010",16*1024,128*1024,flash29,flash29end},
{0x01a4,"Am29F040",64*1024,512*1024,flash29,flash29end},
{0xc0a4,"Am29F040",64*1024,512*1024,flash29,flash29end},
{0x01d5,"Am29F080",64*1024,1024*1024,flash29,flash29end},
{0x1f5b,"AT29F040",64*1024,512*1024,flash29,flash29end}, /* atmel */
{0x5010,"XL28C64", 1, 1024,xl28,xl28end},
{0}};
NvInfo nvInfo;
#define SPC_BITMSK (3<<21)
#define SPC_8BIT (1<<21)
#define SPC_16BIT (2<<21)
#define SPC_32BIT (3<<21)
int machtype;
volatile int wbflushtmp1;
volatile int wbflushtmp2;
int shrc_needed;
#ifdef TEST
/*************************************************************
* main()
*/
main(argc,argv)
int argc;
char *argv[];
{
int n,i,j,c;
char nvtype[40];
machtype = getmachtype();
if (!nvCheck(nvtype)) printf("nvCheck FAILED\n");;
printf("nvram: %s\n",nvtype);
printf("reading: ");
for (i=0;;i++) {
c = nvRead(i);
if (c == 0 || c == 0xff) break;
putchar(c);
}
putchar('\n');
if (argc == 1) return;
for (j=1,i=0;j<argc;j++) {
if (argv[j][0] == '-') {
if (argv[j][1] == 'c') nvClear();
else if (argv[j][1] == 'v') {
j++;
for (i=0;j<argc;j++) {
for (p=argv[j];*p;p++) nvWrite(i++,*p);
nvWrite(i++,' ');
}
}
else {
printf("%s: bad option\n",argv[j][1]);
printf("usage: [-c] [-v msg]\n");
exit(1);
}
}
else {
printf("%s: bad argument\n",argv[j]);
printf("usage: [-c] [-v msg]\n");
exit(1);
}
}
printf("reading: ");
for (i=0;;i++) {
c = nvRead(i);
if (c == 0 || c == 0xff) break;
putchar(c);
}
putchar('\n');
}
#endif
/*************************************************************
* int nvCheck(char *msg)
*/
nvCheck(msg)
char *msg;
{
Ulong r;
int len,i,famcode;
Func *f;
int needcpy;
*msg = 0;
if (nvInfo.nvbase) return(1);
#if 0
/* check to see if I am executing out of flash */
/* do this by checking if I am in the same 8MB as nvInfo.start */
/* if so, I must copy some routines to RAM before executing them */
if ((((Ulong)nvCheck)&~(0x800000-1)) == nvInfo.start&~(0x800000-1)) needcpy=1;
else needcpy = 0;
#else
needcpy=1;
#endif
/* first, find out what nvType we have */
if (needcpy) {
len = ((Ulong)nvType_end)-((Ulong)nvType);
f = (Func *)malloc(len + jalBytes(nvType,len/4));
if (!f) {
strcpy(msg,"malloc failure 1");
return(0);
}
codecpy(f,nvType,len/4);
flush_cache(DCACHE);
flush_cache(ICACHE);
famcode = (* f)();
free(f);
}
else famcode = nvType();
/* look up the famcode in the nvTypes table */
for (i=0;nvTypes[i].driver;i++) {
if (nvTypes[i].famcode == famcode) break;
}
if (!nvTypes[i].driver) { /* famcode not found */
nvInfo.nvbase = 0;
if (msg && famcode != 0) sprintf(msg,"%04x: Bad flashcode",famcode);
return(0);
}
nvInfo.name = nvTypes[i].name;
/* now, install the correct driver */
if (needcpy) {
len = ((Ulong)nvTypes[i].edriver)-((Ulong)nvTypes[i].driver);
f = (Func *)malloc(len + jalBytes(nvTypes[i].driver,len/4));
if (!f) {
strcpy(msg,"malloc failure 2");
return(0);
}
codecpy(f,nvTypes[i].driver,len/4);
flush_cache(DCACHE);
flush_cache(ICACHE);
nvInfo.driver = f;
}
else nvInfo.driver = nvTypes[i].driver;
nvInfo.size = nvTypes[i].size;
nvInfo.type = &nvTypes[i];
/* return if this device doesn't support sector erase */
if (!nvTypes[i].se) {
nvInfo.nvbase = 0;
if (msg) strcpy(msg,"Function not supported");
return(0);
}
/* calculate the correct value for nvbase */
if (nvInfo.nvbase == 0) {
len = nvInfo.size*nvInfo.width;
len -= nvTypes[i].se*nvInfo.width;
#if 0 /* we aught to check if there's enough room */
if (len <= PMONsize) {
nvInfo.nvbase = 0;
if (msg) strcpy(msg,"Insufficient space");
return(0);
}
#endif
nvInfo.nvbase = nvInfo.start + len;
len -= nvTypes[i].se*nvInfo.width;
nvInfo.dvrsa = nvInfo.start + len;
}
/*printf("famcode=%04x ",famcode); /* */
if (msg) strcpy(msg,nvInfo.name);
return(1);
}
/*************************************************************
* nvWrite(adr,val)
*/
nvWrite(adr,val)
Ulong adr;
int val;
{
if (!nvInfo.nvbase) return;
if (adr == 0) nvClear();
(* nvInfo.driver)(NV_WRITE,nvInfo.nvbase+(adr*nvInfo.gap),val);
}
/*************************************************************
* nvRead(adr)
*/
nvRead(adr)
Ulong adr;
{
if (!nvInfo.nvbase) return(0);
return inb(nvInfo.nvbase+(adr*nvInfo.gap));
}
/*************************************************************
* nvClear()
*/
nvClear()
{
int i;
if (!nvInfo.nvbase) return;
if (nvInfo.start == nvInfo.nvbase) (* nvInfo.driver)(NV_ERASE,nvInfo.start);
else {
for (i=0;i<nvInfo.width;i++) {
(* nvInfo.driver)(NV_SERASE,nvInfo.nvbase+i);
}
}
}
/*************************************************************
* nvType()
* determine family code
* what we do know at start:
* start address of device
* gap
* width
*/
nvType()
{
Ulong adrA,adrB,adr0,adr1;
Ulong msk;
int sh,b1,b2,h0,h1;
sh = nvInfo.width/2;
adrA = (nvInfo.start|(0x5555<<sh));
adrB = (nvInfo.start|(0x2aaa<<sh));
adr0 = (nvInfo.start|(0x0000<<sh));
adr1 = (nvInfo.start|(0x0001<<sh));
b1 = inb(adr0);
b2 = inb(adr1);
h0 = (b1<<8)|b2; /* read the existing value */
outb(adrA,0xaa); outb(adrB,0x55); outb(adrA,0x90);
b1 = inb(adr0);
b2 = inb(adr1);
outb(adrA,0xaa); outb(adrB,0x55); outb(adrA,0xf0);
h1 = (b1<<8)|b2; /* read the famcode */
if (h0 == h1) return(0); /* must be eprom */
return(h1);
}
nvType_end() {}
/*************************************************************
* flash28(op,adr,val)
*/
flash28(op,adr,val)
int op,val;
Ulong adr;
{
Uchar pvd;
Ulong X,cs;
int plscnt,dev,sh,i;
printf("flash28: op=%d adr=%08x val=%02x\n",op,adr,val&0xff);
switch (op) {
case NV_WRITE :
printf("flash28w: NOT TESTED\n");
cs = nvInfo.nvbase&~3;
sh = nvInfo.width/2;
dev = adr&((1<<sh)-1);
X = cs|dev; /* any address within the device */
printf("nvbase=%08x X=%08x\n",nvInfo.nvbase,X);
for (plscnt=0;plscnt<25;plscnt++) {
outb(X,0x40); /* prog */
outb(adr,val);
for (i=500;i>0;i++) wbflushtmp1 = 0; /* 10us delay */
outb(X,0xc0); /* prog verify */
for (i=300;i>0;i++) wbflushtmp1 = 0; /* 6us delay */
pvd = inb(X);
if (val == pvd) break;
}
printf("pvd=%02x plscnt=%d\n",pvd,plscnt);
/* outb(X,0xff); outb(X,0xff); outb(X,0x00); /* reset-rd */
if (plscnt >= 25) return(0);
return(1);
case NV_ERASE :
printf("flash28e: NOT IMPLEMENTED\n");
break;
case NV_SERASE :
printf("flash28se: NOT supported by device\n");
break;
}
}
flash28end() {}
#define P29MAX 10 /* max polling count for 29F devices */
int diagcnt;
/*************************************************************
* flash29(op,adr,val)
*/
flash29(op,adr,val)
int op;
Ulong adr;
Uchar val;
{
Ulong adrA,adrB,adr0,adr1;
Uchar epd;
int sh,cnt,i;
switch (op) {
case NV_WRITE :
sh = nvInfo.width/2;
adrA = ((adr&~(0x1ffff<<sh))|(0x5555<<sh));
adrB = ((adr&~(0x1ffff<<sh))|(0x2aaa<<sh));
#if 0
printf("flash29w: adr=%08x val=%02x adrA=%08x adrB=%08x\n",
adr,val,adrA,adrB);
#endif
outb(adrA,0xaa); outb(adrB,0x55); outb(adrA,0xa0);
outb(adr,val);
for (i=0;i<3000;i++) wbflushtmp1 = 0; /* min 100us delay */
for (cnt=0;;cnt++) {
epd = inb(adr);
if ((epd&0x80) == (val&0x80) && epd == val) break;
if (epd&(1<<5)) break;
}
#if 0
if (epd != val) printf("flash29w: ## TIMEOUT\n");
#endif
if (epd != val) return(0);
return(1);
break;
case NV_SERASE :
sh = nvInfo.width/2;
adrA = ((adr&~(0x1ffff<<sh))|(0x5555<<sh));
adrB = ((adr&~(0x1ffff<<sh))|(0x2aaa<<sh));
#if 0
printf("flash29se: adr=%08x adrA=%08x adrB=%08x\n",
adr,adrA,adrB);
#endif
/* unlock, setup */
outb(adrA,0xaa); outb(adrB,0x55); outb(adrA,0x80);
/* unlock, sectEra */
outb(adrA,0xaa); outb(adrB,0x55); outb(adr,0x30);
for (i=0;i<3000;i++) wbflushtmp1 = 0; /* min 100us delay */
val = 0xff;
for (cnt=0;cnt<5000000;cnt++) { /* cnt=1.3M is normal */
epd = inb(adr);
if ((epd&0x80) == (val&0x80) && epd == val) break;
if (epd&(1<<5)) break;
}
#if 0
if (epd != val) printf("flash29se: ## TIMEOUT\n");
printf("flash29se: epd=%02x cnt=%d\n",epd,cnt);
#endif
if (epd != val) return(0);
return(1);
break;
case NV_ERASE :
printf("flash29e: NOT IMPLEMENTED\n");
break;
default: printf("flash29: %d: bad opcode\n",op);
}
}
flash29end() {}
#define XL28_TIMEOUT 100000
/*************************************************************
* xl28(op,adr,val)
*/
xl28(op,adr,val)
int op,val;
Ulong adr;
{
int i,n;
Ulong a;
switch (op) {
case NV_WRITE :
outb(adr,val);
for (i=0;i<XL28_TIMEOUT && inb(adr) != val;i++) wbflushtmp1=0;
if (i >= XL28_TIMEOUT) return(0);
break;
case NV_ERASE :
case NV_SERASE :
/* clear nvram */
val = 0xff;
for (n=0;n<nvInfo.size;n++) {
if (nvRead(n) != val) {
a = nvInfo.nvbase+(n*nvInfo.gap);
outb(a,val);
for (i=0;i<XL28_TIMEOUT && inb(adr) != val;i++)
wbflushtmp1=0;
if (i >= XL28_TIMEOUT) return(0);
}
}
break;
}
}
xl28end() {}
/*************************************************************
* do_shrc()
*/
do_shrc()
{
int c,n;
char ch,buf[80];
c = 'n';
ioctl(STDIN,FIONREAD,&n);
if (n != 0) {
for (;;) {
ioctl(STDIN,FIONREAD,&n);
if (n == 0) break;
read(STDIN,&ch,1);
}
for (;;) {
printf("Skip NVRAM read? (y/n)? >");
gets(buf);
c = *buf;
if (c == 'n' || c == 'y') break;
if (c == 'N' || c == 'Y') break;
}
}
if (c == 'Y' || c == 'y') return;
shrc(0);
shrc_needed = 1;
}
/*************************************************************
* shrc(n)
* n=0 do part1
* n=1 do part2
*/
shrc(n)
int n;
{
int c;
char buf[LINESZ];
unsigned long adr;
adr = 0;
for (;;) {
c = getln(&adr,buf);
if (c == 0) break;
if (strequ(buf,"p2")) {
if (n == 0) break;
if (n == 1) n++;
}
else if (n == 2 || n == 0) do_cmd(buf);
}
}
/*************************************************************
* getln(adr,p)
*/
getln(adr,p)
unsigned long *adr;
unsigned char *p;
{
unsigned int c = 0;
long addr;
addr = *adr;
for (;;) {
c = nvRead(addr);
if (c == 0xff) c = 0;
if (c == 0) break;
addr++;
if (c == '\n') break;
*p++ = c;
}
*adr = addr;
*p = 0;
return(c);
}
/*************************************************************
* writeFlash(adr,val)
* This is used by self-updating flashes (xflash.c)
*/
writeFlash(adr,val)
Ulong adr;
Uchar val;
{
int se,width;
Ulong msk;
char nvtype[20];
if (!nvInfo.type) return(0);
se = nvInfo.type->se;
width = nvInfo.width;
msk = ((se*width)-1) & ~(width-1);
if ((adr&msk) == 0) (* nvInfo.driver)(NV_SERASE,adr);
(* nvInfo.driver)(NV_WRITE,adr,val);
return(0);
}
#if 0
/*************************************************************
*/
printNvInfo()
{
printf("name=%s start=%08x width=%d size=0x%x gap=%d nvbase=%08x driver=%08x\n",
nvInfo.name, nvInfo.start, nvInfo.width, nvInfo.size, nvInfo.gap,
nvInfo.nvbase,nvInfo.driver);
if (nvInfo.type)
printf("famcode=%04x name=%s se=0x%x size=0x%x driver=%08x\n",
nvInfo.type->famcode, nvInfo.type->name, nvInfo.type->se,
nvInfo.type->size,nvInfo.type->driver);
}
#endif