ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /os/port/flashnand.c/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "flashif.h"
typedef struct Nandtab Nandtab;
struct Nandtab {
short manufacturer;
uchar id;
uchar l2bytesperpage;
ushort pagesperblock;
ushort blocks;
uchar tPROGms;
ushort tBERASEms;
uchar tRus;
};
static Nandtab nandtab[] = {
{ 0xec, 0xe6, 9, 16, 1024, 1, 4, 7 }, /* Samsung KM29U64000T */
{ 0x98, 0xe6, 9, 16, 1024, 1, 4, 25 }, /* Toshiba TC58V64AFT */
{ 0x98, 0x73, 9, 32, 1024, 1, 10, 25}, /* Toshiba TC56V128AFT */
/* Generic entries which take timings from Toshiba SMIL example code */
{ -1, 0xea, 8, 16, 512, 20, 400, 100 },
{ -1, 0xe3, 9, 16, 512, 20, 400, 100 },
{ -1, 0xe5, 9, 16, 512, 20, 400, 100 },
{ -1, 0x73, 9, 32, 1024, 20, 400, 100 },
{ -1, 0x75, 9, 32, 2048, 20, 400, 100 },
{ -1, 0x76, 9, 32, 4096, 20, 400, 100 },
};
enum {
ReadMode1 = 0x00,
ReadMode2 = 0x01,
Program = 0x10,
ReadMode3 = 0x50,
Erase1 = 0x60,
ReadStatus = 0x70,
Write = 0x80,
Identify = 0x90,
Erase2 = 0xd0,
StatusReady = 0x40,
StatusFail = 0x01,
};
/*
* NAND flash driver
*/
#define DPRINT if(0)print
#define EPRINT if(1)print
static int idchip(Flash *f);
static void
nand_writebyte(Flash *f, uchar b)
{
archnand_write(f, &b, 1);
}
static uchar
nand_readbyte(Flash *f)
{
uchar b;
archnand_read(f, &b, 1);
return b;
}
static int
idchip(Flash *f)
{
int x;
uchar maker, device;
f->id = 0;
f->devid = 0;
f->width = 1;
archnand_claim(f, 1);
archnand_setCLEandALE(f, 1, 0);
nand_writebyte(f, Identify);
archnand_setCLEandALE(f, 0, 1);
nand_writebyte(f, 0);
archnand_setCLEandALE(f, 0, 0);
maker = nand_readbyte(f);
device = nand_readbyte(f);
archnand_claim(f, 0);
iprint("man=%#ux device=%#ux\n", maker, device);
for(x = 0; x < sizeof(nandtab) / sizeof(nandtab[0]); x++){
if(nandtab[x].id == (device & 0xff)
&& (nandtab[x].manufacturer == maker || nandtab[x].manufacturer == -1)){
ulong bpp;
f->id = maker;
f->devid = device;
f->nr = 1;
bpp = 1 << nandtab[x].l2bytesperpage;
bpp |= bpp >> 5;
f->regions[0].erasesize = bpp * nandtab[x].pagesperblock;
f->size = f->regions[0].erasesize * nandtab[x].blocks;
f->regions[0].n = nandtab[x].blocks;
f->regions[0].start = 0;
f->regions[0].end = f->size;
f->regions[0].pagesize = bpp;
f->data = &nandtab[x];
return 0;
}
}
print("nand: device %#.2ux/%#.2ux not recognised\n", maker, device);
return -1;
}
static int
erasezone(Flash *f, Flashregion *r, ulong byteaddr)
{
Nandtab *nt = f->data;
int paddress;
uchar val;
int rv;
uchar addr[2];
if(byteaddr%r->erasesize || byteaddr >= f->size)
return -1; /* bad zone */
paddress = byteaddr/r->erasesize * nt->pagesperblock; /* can simplify ... */
//print("erasezone(%.8lux) page %d %.8lux\n", byteaddr, paddress, r->erasesize);
archnand_claim(f, 1);
archnand_setCLEandALE(f, 1, 0); // command mode
nand_writebyte(f, Erase1);
archnand_setCLEandALE(f, 0, 1); // address mode
addr[0] = paddress;
addr[1] = paddress >> 8;
archnand_write(f, addr, 2);
archnand_setCLEandALE(f, 1, 0); // command mode
nand_writebyte(f, Erase2);
nand_writebyte(f, ReadStatus);
archnand_setCLEandALE(f, 0, 0); // data mode
do {
val = nand_readbyte(f);
} while((val & StatusReady) != StatusReady);
if((val & StatusFail) != 0){
print("erasezone failed: %.2ux\n", val);
rv = -1;
}
else
rv = 0;
archnand_claim(f, 0); // turn off chip
return rv;
}
static int
writepage(Flash *f, ulong page, ushort addr, void *buf, long n)
{
uchar cmd;
uchar val;
int rv;
uchar cmdbuf[3];
//print("writepage(%ld, %d, %ld)\n", page, addr, n);
// Fake a read to set the pointer
if(addr < 256)
cmd = ReadMode1;
else if(addr < 512){
cmd = ReadMode2;
addr -= 256;
}else{
cmd = ReadMode3;
addr -= 512;
}
archnand_claim(f, 1);
archnand_setCLEandALE(f, 1, 0); // command mode
nand_writebyte(f, cmd);
nand_writebyte(f, Write);
archnand_setCLEandALE(f, 0, 1); // address mode
cmdbuf[0] = addr;
cmdbuf[1] = page;
cmdbuf[2] = page >> 8;
archnand_write(f, cmdbuf, 3);
archnand_setCLEandALE(f, 0, 0); // data mode
archnand_write(f, buf, n);
archnand_setCLEandALE(f, 1, 0); // command mode
nand_writebyte(f, Program);
nand_writebyte(f, ReadStatus);
archnand_setCLEandALE(f, 0, 0); // data mode
do {
val = nand_readbyte(f);
}while((val & StatusReady) != StatusReady);
if((val & StatusFail) != 0){
print("writepage failed: %.2ux\n", val);
rv = -1;
}else
rv = 0;
archnand_claim(f, 0);
return rv;
}
static int
write(Flash *f, ulong offset, void *buf, long n)
{
Nandtab *nt = f->data;
ulong page;
ulong addr;
ulong xbpp;
//print("write(%ld, %ld)\n", offset, n);
xbpp = (1 << nt->l2bytesperpage);
xbpp |= (xbpp >> 5);
page = offset / xbpp;
addr = offset % xbpp;
while(n > 0){
int count;
count = xbpp - addr;
if(count > n)
count = n;
if(writepage(f, page, addr, buf, count) < 0)
return -1;
offset += count;
n -= count;
buf = (uchar *)buf + count;
addr = 0;
}
//print("write done\n");
return 0;
}
static int
read(Flash *f, ulong offset, void *buf, long n)
{
Nandtab *nt = f->data;
uchar cmd;
ulong page;
ulong addr;
ushort bytesperpage, xbytesperpage, skip, partialaddr;
uchar cmdbuf[3];
int toread;
//print("read(%ld, %.8lux, %ld)\n", offset, buf, n);
bytesperpage = (1 << nt->l2bytesperpage);
xbytesperpage = bytesperpage;
xbytesperpage += bytesperpage >> 5; // 512 => 16, 256 => 8
page = offset / xbytesperpage;
partialaddr = offset % xbytesperpage;
skip = 0;
if(partialaddr >= bytesperpage && xbytesperpage - partialaddr < n){
// cannot start read in extended area, and then chain into main area,
// so start on last byte of main area, and skip the extra bytes
// stupid chip design this one
skip = partialaddr - bytesperpage + 1;
n += skip;
partialaddr = bytesperpage - 1;
}
addr = partialaddr;
if(addr >= bytesperpage){
cmd = ReadMode3;
addr -= bytesperpage;
}else if(addr >= 256){
cmd = ReadMode2;
addr -= 256;
}else
cmd = ReadMode1;
//print("cmd %.2x page %.4lux addr %.8lux partialaddr %d skip %d\n", cmd, page, addr, partialaddr, skip);
// Read first page
archnand_claim(f, 1);
archnand_setCLEandALE(f, 1, 0);
nand_writebyte(f, cmd);
archnand_setCLEandALE(f, 0, 1);
cmdbuf[0] = addr;
cmdbuf[1] = page;
cmdbuf[2] = page >> 8;
archnand_write(f, cmdbuf, 3);
archnand_setCLEandALE(f, 0, 0);
if(partialaddr){
// partial first page
microdelay(nt->tRus);
toread = partialaddr < xbytesperpage ? xbytesperpage - partialaddr : 0;
if(toread > n)
toread = n;
if(skip){
archnand_read(f, 0, skip);
toread -= skip;
n -= skip;
// partialaddr += skip;
}
archnand_read(f, buf, toread);
n -= toread;
// partialaddr += toread;
buf = (uchar *)buf + toread;
}
while(n){
microdelay(nt->tRus);
toread = xbytesperpage;
if(n < toread)
toread = n;
archnand_read(f, buf, toread);
n -= toread;
buf = (uchar *)buf + toread;
}
archnand_claim(f, 0);
//print("readn done\n");
return 0;
}
static int
reset(Flash *f)
{
//iprint("nandreset\n");
if(f->data != nil)
return 1;
f->write = write;
f->read = read;
f->eraseall = nil;
f->erasezone = erasezone;
f->suspend = nil;
f->resume = nil;
f->sort = "nand";
archnand_init(f);
return idchip(f);
}
void
flashnandlink(void)
{
addflashcard("nand", reset);
}