l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../..
TARGET := include src
include $(L4DIR)/mk/subdir.mk
src: include

View File

@@ -0,0 +1,4 @@
PKGDIR = ../..
L4DIR ?= $(PKGDIR)/../..
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,36 @@
#pragma once
#include <l4/sys/types.h>
#include "transfer.h"
class Nand_ctrl;
class Nand
{
public:
Nand(Nand_ctrl *nand_ctrl);
int read_page(l4_addr_t page, Transfer &transfer);
int write_page(l4_addr_t page, Transfer &transfer);
int erase(l4_addr_t block);
int handle_irq();
long unsigned page_size;
long unsigned spare_size;
long unsigned block_size;
long unsigned num_blocks;
private:
Nand_ctrl *_nand_ctrl;
};
class Nand_drv
{
public:
virtual int probe(const char *configstr) = 0;
virtual Nand_ctrl *create(l4_umword_t base) = 0;
};
class Nand *arm_nand_probe(const char *configstr, l4_addr_t base);
void arm_nand_register_driver(Nand_drv *nand_drv);

View File

@@ -0,0 +1,67 @@
#pragma once
struct Buffer
{
Buffer()
: addr(0), size(0)
{}
Buffer(unsigned char *a, unsigned s)
: addr(a), size(s)
{}
unsigned char *addr;
unsigned size;
};
struct Transfer
{
enum
{ Max = 4, };
#if 0
struct Elem
{
unsigned char *addr;
unsigned size;
};
#endif
Transfer()
: num(0), len(0)
{}
unsigned num;
unsigned len;
void clear()
{
num = 0;
len = 0;
}
void add(unsigned char *addr, unsigned size)
{
if (num == Max)
return;
_elems[num].addr = addr;
_elems[num].size = size;
len += size;
num++;
}
void add(Buffer buffer)
{
add(buffer.addr, buffer.size);
}
Buffer &operator [] (int index)
{
//assert(index < Max);
return _elems[index];
}
private:
Buffer _elems[Max];
};

View File

@@ -0,0 +1,12 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
TARGET = libarm_nand.o.a
PC_FILENAME := libdrivers-nand
SRC_CC := lib_nand.cc nand.cc nand_ids.cc gpmc.cc mpc5121.cc
PRIVATE_INCDIR := $(SRCDIR)
#CFLAGS += -D__KERNEL__ -DHAVE_NET_DEVICE_OPS
include $(L4DIR)/mk/lib.mk

View File

@@ -0,0 +1,15 @@
#pragma once
#if 0
#include "l4/util/util.h"
inline void udelay(unsigned us)
{
l4_usleep(us);
}
#else
// environment using this library needs to supply a udelay function
void udelay(unsigned us);
#endif

View File

@@ -0,0 +1,124 @@
#include <stdio.h>
#include "l4/drivers-frst/lib_nand.h"
#include "nand.h"
#include "gpmc.h"
Gpmc::Gpmc(addr base_addr)
: _reg((Reg *)base_addr)
{
for (int i = 0; i < Num_cs; ++i)
{
/* Check if NAND type is set */
if (((_reg->cs[i].config1) & 0xc00) == 0x800)
{
//printf("found NAND chip\n");
/* Found it!! */
break;
}
}
// XXX just scan for one chip
scan(1);
// disable write protection
u32 config = _reg->config;
config |= 0x10;
_reg->config = config;
// enable interrupts
_reg->irqstatus = 256;
_reg->irqenable = 256;
}
void Gpmc::add(Nand_chip *chip)
{
chip->add_options(Opt_no_padding | Opt_cacheprg | Opt_no_autoincr);
if ((_reg->cs[0].config1 & 0x3000) == 0x1000)
chip->add_options(Opt_buswidth_16);
//chip->delay = 100;
_chips[0] = chip;
}
Nand_chip *Gpmc::select(loff_t /*addr*/)
{
// XXX hack:we currently have only one chip
return _chips[0];
}
void Gpmc::wr_cmd(u8 c)
{ *((volatile u8 *)&_reg->cs[0].nand_cmd) = c; }
void Gpmc::wr_adr(u8 a)
{ *((volatile u8 *)&_reg->cs[0].nand_adr) = a; }
void Gpmc::wr_dat(u8 d)
{ *((volatile u8 *)&_reg->cs[0].nand_dat) = d; }
u8 Gpmc::rd_dat()
{ return (u8)(*((volatile u16 *)&_reg->cs[0].nand_dat)); }
void Gpmc::rd_dat(u8 *buf, unsigned len)
{
u16 *p = (u16 *)buf;
len >>= 1;
for (unsigned i = 0; i < len; i++)
p[i] = *((volatile u16 *)&_reg->cs[0].nand_dat);
#if 0
for (int i = 0; i < len; i+=4)
{
p[i] = *((volatile u16 *)&_reg->cs[0].nand_dat);
p[i+1] = *((volatile u16 *)&_reg->cs[0].nand_dat);
p[i+2] = *((volatile u16 *)&_reg->cs[0].nand_dat);
p[i+3] = *((volatile u16 *)&_reg->cs[0].nand_dat);
}
#endif
}
void Gpmc::wr_dat(const u8 *buf, unsigned len)
{
u16 *p = (u16 *)buf;
len >>= 1;
for (unsigned i = 0; i < len; i++)
*((volatile u16 *)&_reg->cs[0].nand_dat) = p[i];
}
int Gpmc::handle_irq()
{
int ret = 0;
u32 v = _reg->irqstatus;
u32 v2 = _reg->irqstatus;
if (v & 256)
{
ret = _chips[0]->handle_irq();
_reg->irqstatus = 256;
}
else
{
printf("spurious interrupt\n");
printf("irqstatus:%x %x\n", v, v2);
return -1;
}
return ret;
}
class Gpmc_drv : public Nand_drv
{
public:
Gpmc_drv()
{ arm_nand_register_driver(this); }
int probe(const char *configstr)
{ return (strcmp(configstr, "GPMC")) ? 0 : 1; }
Nand_ctrl *create(addr base)
{ return new Gpmc(base); }
};
static Gpmc_drv gpmc_drv;

View File

@@ -0,0 +1,60 @@
#pragma once
#include "nand.h"
class Gpmc : public Nand_ctrl
{
enum
{ Num_cs = 8, };
struct Reg_cs
{
u32 config1;
u32 config2;
u32 config3;
u32 config4;
u32 config5;
u32 config6;
u32 config7;
volatile u32 nand_cmd;
volatile u32 nand_adr;
volatile u32 nand_dat;
};
struct Reg
{
u8 res1[0x10];
u32 sysconfig;
u8 res2[0x04];
u32 irqstatus;
u32 irqenable;
u8 res3[0x20];
u8 timeout_control;
u8 res4[0x0c];
u32 config;
u32 status;
u8 res5[0x08];
Reg_cs cs[Num_cs];
};
public:
Gpmc(addr base_addr);
protected:
void add(Nand_chip *chip);
Nand_chip *select(loff_t addr);
void wr_cmd(u8 c);
void wr_adr(u8 a);
void wr_dat(u8 d);
u8 rd_dat();
void rd_dat(u8 *buf, unsigned len);
void wr_dat(const u8 *buf, unsigned len);
int handle_irq();
private:
volatile Reg *_reg;
Nand_chip *_chips[Num_cs];
};

View File

@@ -0,0 +1,75 @@
#include <l4/drivers-frst/lib_nand.h>
#include "nand.h"
enum { Max_drivers = 10 };
static Nand_drv *nand_drv[Max_drivers];
static int nr_drivers;
Nand *arm_nand_probe(const char *configstr, l4_umword_t base)
{
for (int i = 0; i < nr_drivers; i++)
if (nand_drv[i] && nand_drv[i]->probe(configstr))
return new Nand(nand_drv[i]->create(base));
return 0;
}
void arm_nand_register_driver(Nand_drv *drv)
{
if (nr_drivers < Max_drivers)
nand_drv[nr_drivers++] = drv;
}
Nand::Nand(Nand_ctrl *nand_ctrl)
: _nand_ctrl(nand_ctrl)
{
page_size = nand_ctrl->sz_write;
spare_size = nand_ctrl->sz_spare;
block_size = nand_ctrl->sz_erase;
num_blocks = nand_ctrl->size / nand_ctrl->sz_erase;
}
int Nand::read_page(l4_addr_t page, Transfer &transfer)
{
if (!_nand_ctrl)
return -1;
static Read_op op;
op.addr = page * _nand_ctrl->sz_write;
op.transfer = &transfer;
return _nand_ctrl->read(&op);
}
int Nand::write_page(l4_addr_t page, Transfer &transfer)
{
if (!_nand_ctrl)
return -1;
static Write_op op;
op.addr = page * _nand_ctrl->sz_write;
op.transfer = &transfer;
return _nand_ctrl->write(&op);
}
int Nand::erase(l4_addr_t block)
{
if (!_nand_ctrl)
return -1;
static Erase_op op;
op.addr = block * _nand_ctrl->sz_erase;
op.len = _nand_ctrl->sz_erase;
_nand_ctrl->erase(&op);
return 0;
}
int Nand::handle_irq()
{
if (!_nand_ctrl)
return -1;
return _nand_ctrl->handle_irq();
}

View File

@@ -0,0 +1,326 @@
#include <stdio.h>
#include <stdlib.h>
#include "l4/drivers-frst/lib_nand.h"
#include "common.h"
#include "nand.h"
#include "mpc5121.h"
Mpc5121::Mpc5121(addr base_addr)
: _reg((Reg *)(base_addr + 0x1e00)),
_buffer_main((u32 *)base_addr), _buffer_spare((u32 *)(base_addr + 0x1000))
{
// reset controller
_reg->nand_flash_config1 |= 0x40;
unsigned num_us_reset = 0;
while (_reg->nand_flash_config1 & 0x40)
{
if (num_us_reset++ >= Max_us_reset)
{
printf("[Mpc5121] Timeout while resetting controller!\n");
return;
}
udelay(1);
}
// unlock buffer memory
_reg->nfc_configuration = 0x2;
// disable write protection
_reg->unlock_start_blk_add0 = 0x0000;
_reg->unlock_end_blk_add0 = 0xffff;
_reg->nf_wr_prot = 0x4;
// mask and configure interrupts only after full page transfer
_reg->nand_flash_config1 = 0x810;
// detect NAND chips
if (scan(1))
{
printf(": NAND Flash not found !\n");
return;
}
// set spare area size
_reg->spas = sz_spare >> 1;
// set erase block size
switch (sz_erase / sz_write)
{
case 32:
_reg->nand_flash_config1 |= 0x000;
break;
case 64:
_reg->nand_flash_config1 |= 0x200;
break;
case 128:
_reg->nand_flash_config1 |= 0x400;
break;
case 256:
_reg->nand_flash_config1 |= 0x600;
break;
default:
printf("[Mpc5121] Unsupported NAND flash chip!\n");
}
return;
}
void Mpc5121::add(Nand_chip *chip)
{
chip->add_options(Opt_no_autoincr);
_chips[0] = chip;
}
Nand_chip *Mpc5121::select(loff_t /*addr*/)
{
return _chips[0];
}
void Mpc5121::wr_cmd(u8 c)
{
switch (c)
{
case Cmd_pageprog:
_start_write();
_enable_irq();
break;
case Cmd_erase2:
_enable_irq();
break;
default:
break;
}
_reg->nand_flash_cmd = c;
_reg->nand_flash_config2 = 0x1;
// wait for completion
if ((c != Cmd_pageprog) && (c != Cmd_erase2))
// XXX udelay
while (_reg->nand_flash_config2 & 0x1) ;
switch (c)
{
case Cmd_status:
_get_status();
break;
case Cmd_readstart:
_start_read();
break;
case Cmd_seqin:
_buffer_ptr = 0;
break;
default:
break;
}
}
void Mpc5121::wr_adr(u8 a)
{
_reg->nand_flash_addr = a;
_reg->nand_flash_config2 = 0x2;
// XXX udelay
while (_reg->nand_flash_config2 & 0x2) ;
}
void Mpc5121::wr_dat(u8 d)
{ _buffer_main[0] = d; }
u8 Mpc5121::rd_dat()
{ return _buffer_main[0]; }
void Mpc5121::rd_dat(u8 *buf, unsigned len)
{
while (len && (_buffer_ptr < (sz_write + sz_spare)))
{
if (_buffer_ptr < sz_write)
{
int l = len < sz_write ? len : sz_write;
_copy_from_data(buf, l);
_buffer_ptr += l;
len -= l;
}
else if (_buffer_ptr < (sz_write + sz_spare))
{
int l = len < sz_spare ? len : sz_spare;
_copy_from_spare(buf, l);
_buffer_ptr += l;
len -= l;
}
}
}
void Mpc5121::wr_dat(const u8 *buf, unsigned len)
{
while (len && (_buffer_ptr < (sz_write + sz_spare)))
{
if (_buffer_ptr < sz_write)
{
int l = len < sz_write ? len : sz_write;
_copy_to_data(buf, l);
_buffer_ptr += l;
len -= l;
}
else if (_buffer_ptr < (sz_write + sz_spare))
{
int l = len < sz_spare ? len : sz_spare;
_copy_to_spare(buf, l);
_buffer_ptr += l;
len -= l;
}
}
unsigned sum = 0;
for (int i = 0; i < 4096/4; i++)
sum += _buffer_main[i];
printf("buffer sum:%d\n", sum);
}
static void spare_copy(u32 *dst, const u32 *src, int len)
{
for (int i = 0; i < (len/4); ++i)
dst[i] = src[i];
switch (len % 4)
{
case 1:
dst[len/4] = (dst[len/4] & 0xffffff00) | (src[len/4] & 0xff);
break;
case 2:
dst[len/4] = (dst[len/4] & 0xffff0000) | (src[len/4] & 0xffff);
break;
case 3:
dst[len/4] = (dst[len/4] & 0xff000000) | (src[len/4] & 0xffffff);
break;
default:
break;
}
}
void Mpc5121::_copy_from_data(u8 *buf, int len)
{
u32 *p = (u32 *)buf;
len >>= 2;
for (int i = 0; i < len; i++)
p[i] = _buffer_main[i];
}
void Mpc5121::_copy_to_data(const u8 *buf, int len)
{
u32 *p = (u32 *)buf;
len >>= 2;
for (int i = 0; i < len; i++)
_buffer_main[i] = p[i];
}
void Mpc5121::_copy_from_spare(u8 *buf, int len)
{
int section_len = (sz_spare / (sz_write >> 9) >> 1) << 1;
unsigned i = 0;
for (i = 0; i < (sz_write >> 9) - 1; ++i)
spare_copy((u32 *)&buf[i * Spare_section_len], &_buffer_spare[(i * Spare_section_len)/4], section_len);
spare_copy((u32 *)&buf[i * Spare_section_len], &_buffer_spare[(i * Spare_section_len)/4], len - i * section_len);
}
void Mpc5121::_copy_to_spare(const u8 *buf, int len)
{
int section_len = (sz_spare / (sz_write >> 9) >> 1) << 1;
unsigned i = 0;
for (i = 0; i < (sz_write >> 9) - 1; ++i)
spare_copy(&_buffer_spare[i * Spare_section_len/4], (u32 *)&buf[i * Spare_section_len], section_len);
spare_copy(&_buffer_spare[i * Spare_section_len/4], (u32 *)&buf[i * Spare_section_len], len - i * section_len);
}
void Mpc5121::_get_status()
{
_reg->nand_flash_config2 = 0x20;
while (_reg->nand_flash_config2 & 0x20) ;
}
int Mpc5121::get_id(char id[4])
{
wr_cmd(Cmd_readid);
wr_adr(0x00);
// transfer data
_reg->nand_flash_config2 = 0x10;
while (_reg->nand_flash_config2 & 0x10) ;
id[0] = _buffer_main[0];
id[1] = _buffer_main[0] >> 8;
id[2] = _buffer_main[1];
id[3] = _buffer_main[1] >> 8;
return 0;
}
void Mpc5121::_start_read()
{
// clear interrupt status
_reg->nand_flash_config2 = 0x0;
// unmask interrupt
_reg->nand_flash_config1 &= ~0x10;
// start data transfer
_reg->nand_flash_config2 = 0x8;
// reset buffer ptr
_buffer_ptr = 0;
}
void Mpc5121::_start_write()
{
// transfer data to chip
_reg->nand_flash_config2 = 0x4;
while (_reg->nand_flash_config2 & 0x4) ;
}
void Mpc5121::_enable_irq()
{
// clear interrupt status
_reg->nand_flash_config2 = 0x0;
// unmask interrupt
_reg->nand_flash_config1 &= ~0x10;
}
int Mpc5121::handle_irq()
{
// mask interrupt
_reg->nand_flash_config1 |= 0x10;
_chips[0]->handle_irq();
return 0;
}
class Mpc5121_drv : public Nand_drv
{
public:
Mpc5121_drv()
{ arm_nand_register_driver(this); }
int probe(const char *configstr)
{ return (strcmp(configstr, "MPC5121")) ? 0 : 1; }
Nand_ctrl *create(addr base)
{ return new Mpc5121(base); }
};
static Mpc5121_drv mpc5121_drv;

View File

@@ -0,0 +1,76 @@
#pragma once
#include "nand.h"
class Mpc5121 : public Nand_ctrl
{
enum
{ Max_cs = 4, };
enum
{ Max_us_reset = 100, };
enum
{ Spare_section_len = 64, };
struct Reg
{
u16 _res1[2];
u16 ram_buffer_addr;
u16 nand_flash_addr;
u16 nand_flash_cmd;
u16 nfc_configuration;
u16 ecc_status_result1;
u16 ecc_status_result2;
u16 spas;
u16 nf_wr_prot;
u16 _res2[2];
u16 nand_flash_wr_pr_st;
u16 nand_flash_config1;
u16 nand_flash_config2;
u16 _res3;
u16 unlock_start_blk_add0;
u16 unlock_end_blk_add0;
u16 unlock_start_blk_add1;
u16 unlock_end_blk_add1;
u16 unlock_start_blk_add2;
u16 unlock_end_blk_add2;
u16 unlock_start_blk_add3;
u16 unlock_end_blk_add3;
};
public:
Mpc5121(addr base_addr);
protected:
void add(Nand_chip *chip);
Nand_chip *select(loff_t addr);
void wr_cmd(u8 c);
void wr_adr(u8 a);
void wr_dat(u8 d);
u8 rd_dat();
void rd_dat(u8 *buf, unsigned len);
void wr_dat(const u8 *buf, unsigned len);
int handle_irq();
int get_id(char id[4]);
private:
void _get_status();
void _start_read();
void _start_write();
void _enable_irq();
void _copy_from_data(u8 *buf, int len);
void _copy_to_data(const u8 *buf, int len);
void _copy_from_spare(u8 *buf, int len);
void _copy_to_spare(const u8 *buf, int len);
volatile Reg *_reg;
Nand_chip *_chips[Max_cs];
volatile u32 *_buffer_main;
u32 *_buffer_spare;
u32 _buffer_ptr = 0;
};

View File

@@ -0,0 +1,315 @@
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "common.h"
#include "nand.h"
Nand_chip::Nand_chip(Nand_ctrl *ctrl, Dev_desc *dev, Mfr_desc *mfr, int ext_id)
: _state(Ready), _ongoing_op(0), _ctrl(ctrl), _dev(dev), _mfr(mfr)
{
/* New devices have all the information in additional id bytes */
if (!dev->sz_page)
{
/* The 3rd id byte holds MLC / multichip data */
_sz_write = 1024 << (ext_id & 0x3);
ext_id >>= 2;
_sz_spare = (8 << (ext_id & 0x01)) * (_sz_write >> 9);
ext_id >>= 2;
_sz_erase = (64 * 1024) << (ext_id & 0x03);
ext_id >>= 2;
_bus_width = (ext_id & 0x01) ? Opt_buswidth_16 : 0;
}
/* Old devices have chip data hardcoded in the device id table */
else
{
_sz_write = dev->sz_page;
_sz_spare = dev->sz_spare ? dev->sz_spare : _sz_write / 32;
_sz_erase = dev->sz_erase;
_bus_width = dev->options & Opt_buswidth_16;
}
_sz_chip = (long long unsigned)dev->sz_chip << 20;
if (_bus_width != (_options & Opt_buswidth_16))
{
printf("NAND bus width %d instead %d bit\n",
(_options & Opt_buswidth_16) ? 16 : 8, _bus_width ? 16 : 8);
}
_options |= dev->options;
_options |= Opt_no_autoincr;
printf("NAND chip: Mfr ID: 0x%02x(%s), Device ID: 0x%02x(%s)\n",
mfr->id, mfr->name, dev->id, dev->name);
}
int Nand_chip::handle_irq()
{
switch (_state)
{
case Reading:
return _ctrl->done_read(static_cast<Read_op *>(_ongoing_op));
break;
case Writing:
return _ctrl->done_write(static_cast<Write_op *>(_ongoing_op));
break;
case Erasing:
return _ctrl->done_erase(static_cast<Erase_op *>(_ongoing_op));
break;
default:
_state = Ready;
break;
}
return 0;
}
bool Nand_ctrl::is_wp()
{
wr_cmd(Cmd_status);
return (rd_dat() & Status_wp) ? false : true;
}
int Nand_ctrl::get_status()
{
wr_cmd(Cmd_status);
return rd_dat();
}
int Nand_ctrl::read(Read_op *op)
{
Nand_chip *chip = select(op->addr);
if (!op->transfer->len)
return -EINVAL;
u16 col = op->addr & (chip->sz_write() - 1);
u32 row = (op->addr >> chip->page_shift()) & chip->page_mask();
assert(!col);
assert(op->transfer->len + col <= (chip->sz_write() + chip->sz_spare()));
wr_cmd(Cmd_read0);
wr_adr(col);
wr_adr(col >> 8);
wr_adr(row);
wr_adr(row >> 8);
wr_adr(row >> 16);
wr_cmd(Cmd_readstart);
chip->set_state(Nand_chip::Reading, op);
return 0;
}
int Nand_ctrl::done_read(Read_op *op)
{
Nand_chip *chip = select(op->addr);
assert(chip->state() == Nand_chip::Reading);
for (unsigned i = 0; i < op->transfer->num; ++i)
rd_dat((*op->transfer)[i].addr, (*op->transfer)[i].size);
chip->set_state(Nand_chip::Ready);
return 0;
}
int Nand_ctrl::write(Write_op *op)
{
Nand_chip *chip = select(op->addr);
if (!op->transfer->len)
return -EINVAL;
// check end of device
if ((op->addr + op->transfer->len) > size)
return -EINVAL;
// check page aligning
if (!aligned(op->addr))
{
printf("NAND: Attempt to write not page aligned data\n");
return -EINVAL;
}
if (is_wp())
{
printf("NAND: Device is write protected\n");
return -EIO;
}
u32 page = (op->addr >> chip->page_shift()) & chip->page_mask();
wr_cmd(Cmd_seqin);
wr_adr(0x00);
wr_adr(0x00);
wr_adr(page);
wr_adr(page >> 8);
wr_adr(page >> 16);
for (unsigned i = 0; i < op->transfer->num; ++i)
wr_dat((u8 *)(*op->transfer)[i].addr, (*op->transfer)[i].size);
wr_cmd(Cmd_pageprog);
chip->set_state(Nand_chip::Writing, op);
return 0;
}
int Nand_ctrl::done_write(Write_op *op)
{
Nand_chip *chip = select(op->addr);
assert(chip->state() == Nand_chip::Writing);
chip->set_state(Nand_chip::Ready);
return (get_status() & Status_fail) ? -EIO : 0;
}
int Nand_ctrl::erase(Erase_op *op)
{
Nand_chip *chip = select(op->addr);
#if 0
printf("nand: erase: start = 0x%08x, len = %u\n",
(unsigned int) op->addr, op->len);
#endif
/* address must align on block boundary */
if (op->addr & chip->erase_mask())
{
printf("nand: erase: Unaligned address\n");
return -EINVAL;
}
/* length must align on block boundary */
if (op->len & chip->erase_mask())
{
printf("nand: erase: Length not block aligned\n");
return -EINVAL;
}
/* Do not allow erase past end of device */
if ((op->len + op->addr) > size)
{
printf("nand: erase: Erase past end of device\n");
return -EINVAL;
}
/* Check, if it is write protected */
if (is_wp())
{
printf("nand_erase: Device is write protected!!!\n");
return -EIO;
}
int page = op->addr >> chip->page_shift();
wr_cmd(Cmd_erase1);
wr_adr(page);
wr_adr(page >> 8);
wr_adr(page >> 16);
wr_cmd(Cmd_erase2);
chip->set_state(Nand_chip::Erasing, op);
return 0;
}
int Nand_ctrl::done_erase(Erase_op *op)
{
Nand_chip *chip = select(op->addr);
assert(chip->state() == Nand_chip::Erasing);
chip->set_state(Nand_chip::Ready);
return (get_status() & Status_fail) ? -EIO : 0;
}
int Nand_ctrl::get_id(char id[4])
{
wr_cmd(Cmd_readid);
wr_adr(0x00);
wr_dat(0xff);
id[0] = rd_dat();
id[1] = rd_dat();
id[2] = rd_dat();
id[3] = rd_dat();
return 0;
}
int Nand_ctrl::scan(int maxchips)
{
for (int i = 0; i < maxchips; ++i)
{
wr_cmd(Cmd_reset);
udelay(100);
wr_cmd(Cmd_status);
while (!(rd_dat() & Status_ready));
char id1[4], id2[4];
get_id(id1);
get_id(id2);
if (id1[0] != id2[0] || id1[1] != id2[1])
{
printf("manufacturer or device id corrupt:\n");
printf("mfr-id1:%02x mfr-id2:%02x\n", id1[0], id2[0]);
printf("dev-id1:%02x dev-id2:%02x\n", id1[1], id2[1]);
return -1;
}
/* identify manufacturer */
Mfr_desc *mfr = 0;
for (int j = 0; _mfr_ids[j].id != 0; j++)
{
if (id1[0] == _mfr_ids[j].id)
{
mfr = &_mfr_ids[j];
break;
}
}
/* identify device */
Dev_desc *dev = 0;
for (int j = 0; _dev_ids[j].name != 0; j++)
{
if (id1[1] == _dev_ids[j].id)
{
dev = &_dev_ids[j];
break;
}
}
if (!dev)
{
printf("no device device found\n");
continue;
}
if (!mfr)
{
printf("no manufacturer found\n");
continue;
}
Nand_chip *chip = new Nand_chip(this, dev, mfr, id1[3]);
add(chip);
// XXX all chips should have the same features
sz_write = chip->sz_write();
sz_spare = chip->sz_spare();
sz_erase = chip->sz_erase();
size += chip->sz_chip();
numchips++;
}
//printf("%d NAND chips detected\n", numchips);
return 0;
}
Nand_ctrl::Nand_ctrl()
{}

View File

@@ -0,0 +1,204 @@
#pragma once
#include <string.h>
#include "types.h"
#include <l4/drivers-frst/transfer.h>
struct Op
{};
struct Read_op : Op
{
u32 addr;
Transfer *transfer;
};
struct Write_op : Op
{
u32 addr;
Transfer *transfer;
};
struct Erase_op : Op
{
u32 addr;
u32 len;
u_char state;
};
struct Dev_desc
{
int id;
const char *name;
unsigned long sz_page; // bytes
unsigned long sz_spare; // bytes
unsigned long sz_chip; // MiB
unsigned long sz_erase; // bytes
unsigned long options;
};
struct Mfr_desc
{
int id;
const char *name;
};
extern Dev_desc _dev_ids[];
extern Mfr_desc _mfr_ids[];
enum
{
Opt_no_autoincr = 0x001,
Opt_buswidth_16 = 0x002,
Opt_no_padding = 0x004,
Opt_cacheprg = 0x008,
Opt_copyback = 0x010,
Opt_is_and = 0x020,
Opt_4page_array = 0x040,
/* Chip does not require ready check on read. True
* for all large page devices, as they do not support autoincrement.*/
Opt_no_readrdy = 0x100,
Opt_no_subpage_write = 0x200,
};
class Nand_ctrl;
class Nand_chip
{
public:
enum State
{
Ready,
Reading,
Writing,
Erasing,
Cachedprg,
};
Nand_chip(Nand_ctrl *ctrl, Dev_desc *dev, Mfr_desc *mfr, int ext_id = 0);
State state() {return _state; }
void set_state(State state) { _state = state; }
void set_state(State state, Op *op)
{
_state = state;
_ongoing_op = op;
}
u32 sz_write() const
{ return _sz_write; }
u32 sz_spare() const
{ return _sz_spare; }
u32 sz_erase() const
{ return _sz_erase; }
u64 sz_chip() const
{ return _sz_chip; }
void add_options(int options)
{ _options |= options; }
int page_shift() { return ffs(_sz_write) - 1; }
int page_mask() { return (sz_chip() >> page_shift()) - 1; }
int erase_shift() { return ffs(_sz_erase) - 1; }
int erase_mask() { return (sz_chip() >> erase_shift()) - 1; }
int handle_irq();
protected:
u32 _sz_write;
u32 _sz_spare;
u32 _sz_erase;
u64 _sz_chip;
int _bus_width;
int _options = 0;
private:
State _state;
Op *_ongoing_op;
Nand_ctrl *_ctrl;
Dev_desc *_dev;
Mfr_desc *_mfr;
};
class Nand_ctrl
{
protected:
// commands
enum
{
// standard device commands
Cmd_read0 = 0x00,
Cmd_read1 = 0x01,
Cmd_rndout = 0x05,
Cmd_pageprog = 0x10,
Cmd_readoob = 0x50,
Cmd_erase1 = 0x60,
Cmd_status = 0x70,
Cmd_seqin = 0x80,
Cmd_rndin = 0x85,
Cmd_readid = 0x90,
Cmd_erase2 = 0xd0,
Cmd_reset = 0xff,
// large page device commands
Cmd_readstart = 0x30,
Cmd_rndoutstart = 0xe0,
Cmd_cachedprog = 0x15,
};
/* status bits */
enum
{
Status_fail = 0x01,
Status_fail_n1 = 0x02,
Status_true_ready = 0x20,
Status_ready = 0x40,
Status_wp = 0x80,
};
public:
Nand_ctrl();
int read(Read_op *op);
int write(Write_op *op);
int erase(Erase_op *op);
int done_read(Read_op *op);
int done_write(Write_op *op);
int done_erase(Erase_op *op);
virtual int handle_irq() = 0;
protected:
int scan(int maxchips);
virtual void add(Nand_chip *chip) = 0;
virtual Nand_chip *select(loff_t addr) = 0;
virtual void wr_cmd(u8 c) = 0;
virtual void wr_adr(u8 a) = 0;
virtual void wr_dat(u8 d) = 0;
bool aligned(u32 addr) const
{ return !(addr & (sz_write - 1)); }
virtual u8 rd_dat() = 0;
virtual void rd_dat(u8 *buf, unsigned len) = 0;
virtual void wr_dat(const u8 *buf, unsigned len) = 0;
bool is_wp();
int get_status();
virtual int get_id(char id[4]);
public:
const char *name = 0;
unsigned numchips = 0;
u64 size = 0;
u32 sz_erase = 0;
u32 sz_write = 0;
u32 sz_spare = 0;
};

View File

@@ -0,0 +1,115 @@
#include "common.h"
#include "nand.h"
/*
* Chip ID list
*
* Id, name, page size, spare size, chip size in MegaByte, erase block size, options
*/
struct Dev_desc _dev_ids[] =
{
{0x6e, "NAND 1MiB 5V 8-bit", 256, 0, 1, 0x1000, 0},
{0x64, "NAND 2MiB 5V 8-bit", 256, 0, 2, 0x1000, 0},
{0x6b, "NAND 4MiB 5V 8-bit", 512, 0, 4, 0x2000, 0},
{0xe8, "NAND 1MiB 3,3V 8-bit", 256, 0, 1, 0x1000, 0},
{0xec, "NAND 1MiB 3,3V 8-bit", 256, 0, 1, 0x1000, 0},
{0xea, "NAND 2MiB 3,3V 8-bit", 256, 0, 2, 0x1000, 0},
{0xd5, "NAND 4MiB 3,3V 8-bit", 512, 0, 4, 0x2000, 0},
{0xe3, "NAND 4MiB 3,3V 8-bit", 512, 0, 4, 0x2000, 0},
{0xe5, "NAND 4MiB 3,3V 8-bit", 512, 0, 4, 0x2000, 0},
{0xd6, "NAND 8MiB 3,3V 8-bit", 512, 0, 8, 0x2000, 0},
{0x39, "NAND 8MiB 1,8V 8-bit", 512, 0, 8, 0x2000, 0},
{0xe6, "NAND 8MiB 3,3V 8-bit", 512, 0, 8, 0x2000, 0},
{0x49, "NAND 8MiB 1,8V 16-bit", 512, 0, 8, 0x2000, Opt_buswidth_16},
{0x59, "NAND 8MiB 3,3V 16-bit", 512, 0, 8, 0x2000, Opt_buswidth_16},
{0x33, "NAND 16MiB 1,8V 8-bit", 512, 0, 16, 0x4000, 0},
{0x73, "NAND 16MiB 3,3V 8-bit", 512, 0, 16, 0x4000, 0},
{0x43, "NAND 16MiB 1,8V 16-bit", 512, 0, 16, 0x4000, Opt_buswidth_16},
{0x53, "NAND 16MiB 3,3V 16-bit", 512, 0, 16, 0x4000, Opt_buswidth_16},
{0x35, "NAND 32MiB 1,8V 8-bit", 512, 0, 32, 0x4000, 0},
{0x75, "NAND 32MiB 3,3V 8-bit", 512, 0, 32, 0x4000, 0},
{0x45, "NAND 32MiB 1,8V 16-bit", 512, 0, 32, 0x4000, Opt_buswidth_16},
{0x55, "NAND 32MiB 3,3V 16-bit", 512, 0, 32, 0x4000, Opt_buswidth_16},
{0x36, "NAND 64MiB 1,8V 8-bit", 512, 0, 64, 0x4000, 0},
{0x76, "NAND 64MiB 3,3V 8-bit", 512, 0, 64, 0x4000, 0},
{0x46, "NAND 64MiB 1,8V 16-bit", 512, 0, 64, 0x4000, Opt_buswidth_16},
{0x56, "NAND 64MiB 3,3V 16-bit", 512, 0, 64, 0x4000, Opt_buswidth_16},
{0x78, "NAND 128MiB 1,8V 8-bit", 512, 0, 128, 0x4000, 0},
{0x39, "NAND 128MiB 1,8V 8-bit", 512, 0, 128, 0x4000, 0},
{0x79, "NAND 128MiB 3,3V 8-bit", 512, 0, 128, 0x4000, 0},
{0x72, "NAND 128MiB 1,8V 16-bit", 512, 0, 128, 0x4000, Opt_buswidth_16},
{0x49, "NAND 128MiB 1,8V 16-bit", 512, 0, 128, 0x4000, Opt_buswidth_16},
{0x74, "NAND 128MiB 3,3V 16-bit", 512, 0, 128, 0x4000, Opt_buswidth_16},
{0x59, "NAND 128MiB 3,3V 16-bit", 512, 0, 128, 0x4000, Opt_buswidth_16},
{0x71, "NAND 256MiB 3,3V 8-bit", 512, 0, 256, 0x4000, 0},
/*
* These are the new chips with large page size. The pagesize and the
* erasesize is determined from the extended id bytes
*/
#define LP_OPTIONS (Opt_no_readrdy | Opt_no_autoincr)
#define LP_OPTIONS16 (LP_OPTIONS | Opt_buswidth_16)
/*512 Megabit */
{0xA2, "NAND 64MiB 1,8V 8-bit", 0, 0, 64, 0, LP_OPTIONS},
{0xF2, "NAND 64MiB 3,3V 8-bit", 0, 0, 64, 0, LP_OPTIONS},
{0xB2, "NAND 64MiB 1,8V 16-bit", 0, 0, 64, 0, LP_OPTIONS16},
{0xC2, "NAND 64MiB 3,3V 16-bit", 0, 0, 64, 0, LP_OPTIONS16},
/* 1 Gigabit */
{0xA1, "NAND 128MiB 1,8V 8-bit", 0, 0, 128, 0, LP_OPTIONS},
{0xF1, "NAND 128MiB 3,3V 8-bit", 0, 0, 128, 0, LP_OPTIONS},
{0xB1, "NAND 128MiB 1,8V 16-bit", 0, 0, 128, 0, LP_OPTIONS16},
{0xC1, "NAND 128MiB 3,3V 16-bit", 0, 0, 128, 0, LP_OPTIONS16},
/* 2 Gigabit */
{0xAA, "NAND 256MiB 1,8V 8-bit", 0, 0, 256, 0, LP_OPTIONS},
{0xDA, "NAND 256MiB 3,3V 8-bit", 0, 0, 256, 0, LP_OPTIONS},
{0xBA, "NAND 256MiB 1,8V 16-bit", 0, 0, 256, 0, LP_OPTIONS16},
{0xCA, "NAND 256MiB 3,3V 16-bit", 0, 0, 256, 0, LP_OPTIONS16},
/* 4 Gigabit */
{0xAC, "NAND 512MiB 1,8V 8-bit", 0, 0, 512, 0, LP_OPTIONS},
{0xDC, "NAND 512MiB 3,3V 8-bit", 0, 0, 512, 0, LP_OPTIONS},
{0xBC, "NAND 512MiB 1,8V 16-bit", 0, 0, 512, 0, LP_OPTIONS16},
{0xCC, "NAND 512MiB 3,3V 16-bit", 0, 0, 512, 0, LP_OPTIONS16},
/* 8 Gigabit */
{0xA3, "NAND 1GiB 1,8V 8-bit", 0, 0, 1024, 0, LP_OPTIONS},
{0xD3, "NAND 1GiB 3,3V 8-bit", 0, 0, 1024, 0, LP_OPTIONS},
{0xB3, "NAND 1GiB 1,8V 16-bit", 0, 0, 1024, 0, LP_OPTIONS16},
{0xC3, "NAND 1GiB 3,3V 16-bit", 0, 0, 1024, 0, LP_OPTIONS16},
/* 16 Gigabit */
{0xA5, "NAND 2GiB 1,8V 8-bit", 0, 0, 2048, 0, LP_OPTIONS},
{0xD5, "NAND 2GiB 3,3V 8-bit", 0, 0, 2048, 0, LP_OPTIONS},
{0xB5, "NAND 2GiB 1,8V 16-bit", 0, 0, 2048, 0, LP_OPTIONS16},
{0xC5, "NAND 2GiB 3,3V 16-bit", 0, 0, 2048, 0, LP_OPTIONS16},
{0xD7, "NAND 4GiB ?V 16-bit", 4096, 218, 4096, 0x80000, LP_OPTIONS16},
{0, 0, 0, 0, 0, 0, 0}
};
/*
* Manufacturer ID list
*/
struct Mfr_desc _mfr_ids[] =
{
{0x01, "AMD"},
{0x04, "Fujitsu"},
{0x07, "Renesas"},
{0x20, "ST Micro"},
{0x2c, "Micron"},
{0x84, "National"},
{0x98, "Toshiba"},
{0xad, "Hynix"},
{0xec, "Samsung"},
{0x00, "Unknown"},
};

View File

@@ -0,0 +1,15 @@
#pragma once
#include "sys/types.h"
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
typedef signed long long s64;
typedef long unsigned addr;