sciphone_g2: Support for NAND with HW ECC
Implementation of MT62xx NAND controller (NFI) which supports large page devices and small page devices. It also enables hardware ECC control. Signed-off-by: Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com>
This commit is contained in:
parent
254b41cd0c
commit
a025f73fd7
|
@ -38,6 +38,62 @@
|
|||
#define MTK_NFI_PSTA (MTK_NFI_BASE + 0x40)
|
||||
#define MTK_NFI_FIFOSTA (MTK_NFI_BASE + 0x44)
|
||||
#define MTK_NFI_CON (MTK_NFI_BASE + 0x60)
|
||||
#define MTK_NFI_INTR_EN (MTK_NFI_BASE + 0x68)
|
||||
#define MTK_NFI_PAGECNTR (MTK_NFI_BASE + 0x70)
|
||||
#define MTK_NFI_ADDRCNTR (MTK_NFI_BASE + 0x74)
|
||||
#define MTK_NFI_SYM0_ADDR (MTK_NFI_BASE + 0x80)
|
||||
#define MTK_NFI_SYM1_ADDR (MTK_NFI_BASE + 0x84)
|
||||
#define MTK_NFI_SYM2_ADDR (MTK_NFI_BASE + 0x88)
|
||||
#define MTK_NFI_SYM3_ADDR (MTK_NFI_BASE + 0x8C)
|
||||
#define MTK_NFI_SYM4_ADDR (MTK_NFI_BASE + 0x90)
|
||||
#define MTK_NFI_SYM5_ADDR (MTK_NFI_BASE + 0x94)
|
||||
#define MTK_NFI_SYM6_ADDR (MTK_NFI_BASE + 0x98)
|
||||
#define MTK_NFI_SYM7_ADDR (MTK_NFI_BASE + 0x9C)
|
||||
#define MTK_NFI_SYMS0_ADDR (MTK_NFI_BASE + 0xA0)
|
||||
#define MTK_NFI_SYMS1_ADDR (MTK_NFI_BASE + 0xA4)
|
||||
#define MTK_NFI_SYMS2_ADDR (MTK_NFI_BASE + 0xA8)
|
||||
#define MTK_NFI_SYMS3_ADDR (MTK_NFI_BASE + 0xAC)
|
||||
#define MTK_NFI_SYM0_DATA (MTK_NFI_BASE + 0xB0)
|
||||
#define MTK_NFI_SYM1_DATA (MTK_NFI_BASE + 0xB4)
|
||||
#define MTK_NFI_SYM2_DATA (MTK_NFI_BASE + 0xB8)
|
||||
#define MTK_NFI_SYM3_DATA (MTK_NFI_BASE + 0xBC)
|
||||
#define MTK_NFI_SYM4_DATA (MTK_NFI_BASE + 0xC0)
|
||||
#define MTK_NFI_SYM5_DATA (MTK_NFI_BASE + 0xC4)
|
||||
#define MTK_NFI_SYM6_DATA (MTK_NFI_BASE + 0xC8)
|
||||
#define MTK_NFI_SYM7_DATA (MTK_NFI_BASE + 0xCC)
|
||||
#define MTK_NFI_SYMS0_DATA (MTK_NFI_BASE + 0xD0)
|
||||
#define MTK_NFI_SYMS1_DATA (MTK_NFI_BASE + 0xD4)
|
||||
#define MTK_NFI_SYMS2_DATA (MTK_NFI_BASE + 0xD8)
|
||||
#define MTK_NFI_SYMS3_DATA (MTK_NFI_BASE + 0xDC)
|
||||
#define MTK_NFI_PAR_0P (MTK_NFI_BASE + 0xE0)
|
||||
#define MTK_NFI_PAR_0C (MTK_NFI_BASE + 0xE4)
|
||||
#define MTK_NFI_PAR_1P (MTK_NFI_BASE + 0xE8)
|
||||
#define MTK_NFI_PAR_1C (MTK_NFI_BASE + 0xEC)
|
||||
#define MTK_NFI_PAR_2P (MTK_NFI_BASE + 0xF0)
|
||||
#define MTK_NFI_PAR_2C (MTK_NFI_BASE + 0xF4)
|
||||
#define MTK_NFI_PAR_3P (MTK_NFI_BASE + 0xF8)
|
||||
#define MTK_NFI_PAR_3C (MTK_NFI_BASE + 0xFC)
|
||||
#define MTK_NFI_PAR_4P (MTK_NFI_BASE + 0x100)
|
||||
#define MTK_NFI_PAR_4C (MTK_NFI_BASE + 0x104)
|
||||
#define MTK_NFI_PAR_5P (MTK_NFI_BASE + 0x108)
|
||||
#define MTK_NFI_PAR_5C (MTK_NFI_BASE + 0x10C)
|
||||
#define MTK_NFI_PAR_6P (MTK_NFI_BASE + 0x110)
|
||||
#define MTK_NFI_PAR_6C (MTK_NFI_BASE + 0x114)
|
||||
#define MTK_NFI_PAR_7P (MTK_NFI_BASE + 0x118)
|
||||
#define MTK_NFI_PAR_7C (MTK_NFI_BASE + 0x11C)
|
||||
#define MTK_NFI_PARS_0P (MTK_NFI_BASE + 0x120)
|
||||
#define MTK_NFI_PARS_0C (MTK_NFI_BASE + 0x124)
|
||||
#define MTK_NFI_PARS_1P (MTK_NFI_BASE + 0x128)
|
||||
#define MTK_NFI_PARS_1C (MTK_NFI_BASE + 0x12C)
|
||||
#define MTK_NFI_PARS_2P (MTK_NFI_BASE + 0x130)
|
||||
#define MTK_NFI_PARS_2C (MTK_NFI_BASE + 0x134)
|
||||
#define MTK_NFI_PARS_3P (MTK_NFI_BASE + 0x138)
|
||||
#define MTK_NFI_PARS_3C (MTK_NFI_BASE + 0x13C)
|
||||
#define MTK_NFI_ERRDET (MTK_NFI_BASE + 0x140)
|
||||
#define MTK_NFI_PARERR (MTK_NFI_BASE + 0x144)
|
||||
#define MTK_NFI_SCON (MTK_NFI_BASE + 0x148)
|
||||
#define MTK_NFI_CSEL (MTK_NFI_BASE + 0x200)
|
||||
#define MTK_NFI_IOCON (MTK_NFI_BASE + 0x204)
|
||||
|
||||
/* NFI_ACCCON bit fields definitions */
|
||||
#define NFI_ACCCON_RLT_SHIFT (0)
|
||||
|
@ -84,8 +140,8 @@
|
|||
/* NFI_CON bit fields definitions */
|
||||
#define NFI_CON_DMA_RD_EN (1 << 0)
|
||||
#define NFI_CON_DMA_WR_EN (1 << 1)
|
||||
#define NFI_CON_AUTO_ECC_DEC_EN (1 << 2)
|
||||
#define NFI_CON_AUTO_ECC_ENC_EN (1 << 3)
|
||||
#define NFI_CON_AUTOECC_DEC_EN (1 << 2)
|
||||
#define NFI_CON_AUTOECC_ENC_EN (1 << 3)
|
||||
#define NFI_CON_MULTI_PAGE_RD_EN (1 << 4)
|
||||
#define NFI_CON_SPARE_EN (1 << 5)
|
||||
#define NFI_CON_DMA_PAUSE_EN (1 << 6)
|
||||
|
|
|
@ -25,57 +25,339 @@
|
|||
#include <asm/io.h>
|
||||
#include <asm/arch-mtk/nfi.h>
|
||||
#include <asm/arch-mtk/system.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
static void mtk_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
static int addr_bit = 0;
|
||||
static int address = 0;
|
||||
static int command = 0;
|
||||
|
||||
if (ctrl == (NAND_CTRL_CHANGE | NAND_NCE)) {
|
||||
chip->IO_ADDR_R = (void *)MTK_NFI_DATARB;
|
||||
if (addr_bit) {
|
||||
/*
|
||||
while(!((readw(MTK_NFI_FIFOSTA) & 0xFF) == NFI_FIFOSTA_RD_EMPTY))
|
||||
readb(MTK_NFI_DATARB);*/
|
||||
writel(address, MTK_NFI_ADDRL);
|
||||
if (command == NAND_CMD_READID ||
|
||||
command == NAND_CMD_STATUS) {
|
||||
writeb(1, MTK_NFI_ADDRNOB);
|
||||
while(readl(MTK_NFI_PSTA) & NFI_PSTA_ADDR)
|
||||
;
|
||||
writel(NFI_OPCON_SRD, MTK_NFI_OPCON);
|
||||
} else {
|
||||
writeb(addr_bit >> 3, MTK_NFI_ADDRNOB);
|
||||
while(readl(MTK_NFI_PSTA) & NFI_PSTA_ADDR)
|
||||
;
|
||||
writel(NFI_OPCON_BRD, MTK_NFI_OPCON);
|
||||
}
|
||||
addr_bit = 0;
|
||||
}
|
||||
} else if ((ctrl & NAND_CLE) && (cmd != NAND_CMD_NONE)) {
|
||||
command = cmd;
|
||||
writel(cmd, MTK_NFI_CMD);
|
||||
while(readl(MTK_NFI_PSTA) & NFI_PSTA_CMD)
|
||||
;
|
||||
} else if (ctrl & NAND_ALE) {
|
||||
if (!addr_bit)
|
||||
address = cmd;
|
||||
else
|
||||
address |= cmd << addr_bit;
|
||||
* MT62XX NAND controller (NFI) can suppport small page (512B) and
|
||||
* large page (2048B) NAND devices. It also supports hardware ECC control.
|
||||
* ECC is calculated by hardware for given block of bytes (128, 256, 512, 1024).
|
||||
* NFI controller can acutomatically detect if read page has no errors -
|
||||
* comparing calculated ECC with ECC bytes written in spare area.
|
||||
* Problem with hardware ECC is that ECC calculation is available after
|
||||
* addtional cycle of read/write (after reading of whole page).
|
||||
* I.e. page size is 512, so after reading whole page addtional read/write cycle
|
||||
* has to be issued to get ECC calculation (NFI_ADDRCNTR has to show more than
|
||||
* 512).
|
||||
*
|
||||
* In case of reading it's not a problem as NFI controller has 16 bytes FIFO and
|
||||
* in burst mode it'll issue 16 reads in advance, so after reading whole page
|
||||
* from FIFO, ECC calculation will be already available (as 16 addtional bytes
|
||||
* are already placed in FIFO). To have ECC calculations working, SPARE_EN bit
|
||||
* has to be enabled, otherwise there will be no additional read cycle. In this
|
||||
* case AUTO_ECC_DEC_EN mode works (automatic ECC errors detection).
|
||||
*
|
||||
* Problem is with writing mode, as additional byte has to be written to spare
|
||||
* area and only after that ECC calculation will be available. It means that
|
||||
* when using HW ECC it's not possible to place calculation at first byte of
|
||||
* spare area. There is AUTO_ECC_ENC_EN mode which doesn't seem to work.
|
||||
* In this mode NFI controller should automatically write calculated ECC bytes
|
||||
* in spare area. To have it working SPARE_EN bit should be disabled, which
|
||||
* indicates that NFI controller has control over spare area, not the user.
|
||||
* Unfortunatelly even after disabling SPARE_EN bit, NFI controller doesn't
|
||||
* write ECC calculations in spare automatically.
|
||||
* Because of these NFI controller issues, driver has to have some hacks.
|
||||
*
|
||||
* Sciphone G2 specific informations:
|
||||
*
|
||||
* Sciphone G2 comes with small page and large page NAND devices.
|
||||
* For both configurations ecc block size is the same:
|
||||
*
|
||||
* ecc_block_size = 256
|
||||
*
|
||||
* Placement of ecc bytes in spare area is as follows:
|
||||
*
|
||||
* --------------------------------------------------------------
|
||||
* | SPARE |
|
||||
* --------------------------------------------------------------
|
||||
* | | ECC0 | | ECC1 | | ECC2 | | ECC3 |
|
||||
* --------------------------------------------------------------
|
||||
* 0 8 16 24 32 40 48 56 64
|
||||
*
|
||||
* ECC0 = 12 bits (from 1st ECC block) + 12 bits (from 2nd ECC block)
|
||||
* ECC1 = 12 bits (from 3rd ECC block) + 12 bits (from 4th ECC block)
|
||||
* ECC2 = 12 bits (from 5th ECC block) + 12 bits (from 6th ECC block)
|
||||
* ECC2 = 12 bits (from 7th ECC block) + 12 bits (from 8th ECC block)
|
||||
*
|
||||
* This information is pretty important as built-in bootloader in MT62xx chips
|
||||
* has hardware ECC enabled and it won't load code from NAND if ECC layout will
|
||||
* not match.
|
||||
*/
|
||||
|
||||
addr_bit += 8;
|
||||
/* MT62XX NFI controller has 2 chipselects */
|
||||
#define NAND_CHIPS_MAX 2
|
||||
|
||||
/*
|
||||
* Configure which byte in spare area will store ECC calculations.
|
||||
* This value should be dword (4 bytes) aligned.
|
||||
*/
|
||||
#define ECC_SPARE_BYTE_POS 8
|
||||
|
||||
static void nand_ctrl_change(int cmd, int address, int *addr_cycle,
|
||||
int write_size)
|
||||
{
|
||||
int burst_read = 0;
|
||||
|
||||
if (write_size > 512) {
|
||||
/* Large page device - set burst read on READSTART command */
|
||||
if (cmd == NAND_CMD_READSTART)
|
||||
burst_read = 1;
|
||||
} else {
|
||||
/* Small page device - set burst read on all read commands */
|
||||
if ((cmd == NAND_CMD_READ0) |
|
||||
(cmd == NAND_CMD_READ1) |
|
||||
(cmd == NAND_CMD_READOOB))
|
||||
burst_read = 1;
|
||||
}
|
||||
|
||||
/* Any address to write? */
|
||||
if (*addr_cycle) {
|
||||
writel(address, MTK_NFI_ADDRL);
|
||||
|
||||
if (cmd == NAND_CMD_READID) {
|
||||
/* For READID command just one address byte is used */
|
||||
writel(1, MTK_NFI_ADDRNOB);
|
||||
while(readl(MTK_NFI_PSTA) & NFI_PSTA_ADDR)
|
||||
;
|
||||
/* Set single read mode */
|
||||
writel(NFI_OPCON_SRD, MTK_NFI_OPCON);
|
||||
while(readl(MTK_NFI_PSTA) & NFI_PSTA_DATAR)
|
||||
;
|
||||
} else {
|
||||
/* Write calculated number of address bytes */
|
||||
writel(*addr_cycle, MTK_NFI_ADDRNOB);
|
||||
while(readl(MTK_NFI_PSTA) & NFI_PSTA_ADDR)
|
||||
;
|
||||
}
|
||||
|
||||
*addr_cycle = 0;
|
||||
}
|
||||
|
||||
if (burst_read) {
|
||||
/* Set burst read mode */
|
||||
writel(NFI_OPCON_BRD, MTK_NFI_OPCON);
|
||||
while(readl(MTK_NFI_PSTA) & NFI_PSTA_DATAR)
|
||||
;
|
||||
} else if (cmd == NAND_CMD_STATUS) {
|
||||
/* Set single read mode */
|
||||
writel(NFI_OPCON_SRD, MTK_NFI_OPCON);
|
||||
while(readl(MTK_NFI_PSTA) & NFI_PSTA_DATAR)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static int mtk_dev_ready(struct mtd_info *mtd)
|
||||
static void nand_write_command(int cmd)
|
||||
{
|
||||
/*
|
||||
* Clear BRD and SRD bits before issuing command,
|
||||
* otherwise there can be some zeroes at the beggining of read.
|
||||
*/
|
||||
writew(readl(MTK_NFI_OPCON) & ~(NFI_OPCON_BRD | NFI_OPCON_SRD),
|
||||
MTK_NFI_OPCON);
|
||||
writel(cmd, MTK_NFI_CMD);
|
||||
while(readl(MTK_NFI_PSTA) & NFI_PSTA_CMD)
|
||||
;
|
||||
}
|
||||
|
||||
static void mt62xx_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
static int addr_cycle = 0;
|
||||
static int address = 0;
|
||||
static int command = 0;
|
||||
|
||||
if (ctrl == (NAND_CTRL_CHANGE | NAND_NCE))
|
||||
nand_ctrl_change(command, address, &addr_cycle, mtd->writesize);
|
||||
else if ((ctrl & NAND_CLE) && (cmd != NAND_CMD_NONE)) {
|
||||
command = cmd;
|
||||
nand_write_command(cmd);
|
||||
} else if (ctrl & NAND_ALE) {
|
||||
/*
|
||||
* Calculate address and address bytes which will be written
|
||||
* in nand_ctrl_change() function.
|
||||
*/
|
||||
if (!addr_cycle)
|
||||
address = cmd;
|
||||
else
|
||||
address |= cmd << (addr_cycle*8);
|
||||
|
||||
addr_cycle++;
|
||||
}
|
||||
}
|
||||
|
||||
static int mt62xx_nand_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return !(readl(MTK_NFI_PSTA) & NFI_PSTA_NAND_BUSY);
|
||||
}
|
||||
|
||||
int board_nand_init(struct nand_chip *nand)
|
||||
static void mt62xx_nand_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
if (chip > NAND_CHIPS_MAX) {
|
||||
printk(KERN_ERR "Wrong NAND chip number!\n");
|
||||
return;
|
||||
}
|
||||
writel(chip, MTK_NFI_CSEL);
|
||||
}
|
||||
|
||||
static void mt62xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
{
|
||||
uint32_t *buf_32 = (uint32_t *)buf;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
int i;
|
||||
|
||||
if (len % 4)
|
||||
printk(KERN_ERR "Length parameter is not aligned\n");
|
||||
|
||||
for (i = 0; i < len/4; ++i)
|
||||
buf_32[i] = readl(chip->IO_ADDR_R);
|
||||
}
|
||||
|
||||
static uint8_t mt62xx_nand_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
uint16_t nfi_con = readl(MTK_NFI_CON);
|
||||
uint8_t byte;
|
||||
|
||||
/* Enable byte mode reading */
|
||||
writel(nfi_con | NFI_CON_BYTE_RW, MTK_NFI_CON);
|
||||
byte = readb(chip->IO_ADDR_R);
|
||||
/* Disable byte mode reading */
|
||||
writel(nfi_con & ~NFI_CON_BYTE_RW, MTK_NFI_CON);
|
||||
return byte;
|
||||
}
|
||||
|
||||
static void mt62xx_nand_write_ecc(struct mtd_info *mtd, int len)
|
||||
{
|
||||
int i, ecc_nr, ecc_blocks;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
uint8_t ecc[8];
|
||||
|
||||
/*
|
||||
* Two ECC blocks are combined in Sciphone G2 format,
|
||||
* that's why there is division by 2.
|
||||
*/
|
||||
ecc_blocks = mtd->writesize/chip->ecc.size/2;
|
||||
|
||||
for (ecc_nr = 0; ecc_nr < ecc_blocks; ++ecc_nr) {
|
||||
int ecc_p, ecc_c, pos = 0;
|
||||
|
||||
/*
|
||||
* After first write in this loop, ECC calculations will
|
||||
* be available in NFI_PAR_P and NFI_PAR_C registers.
|
||||
*/
|
||||
for (i = 0; i < ECC_SPARE_BYTE_POS/4; ++i)
|
||||
writel(~0, chip->IO_ADDR_W);
|
||||
|
||||
/*
|
||||
* Read calculated ECC bytes and write them
|
||||
* to buffer in proper format (as Sciphone G2 expects).
|
||||
*/
|
||||
for (i = 0; i < 2; ++i) {
|
||||
ecc_p = readw((uint32_t *)MTK_NFI_PAR_0P + ecc_nr*2 + i*2);
|
||||
ecc_c = readw((uint32_t *)MTK_NFI_PAR_0C + ecc_nr*2 + i*2);
|
||||
ecc[pos++] = ecc_p >> 4;
|
||||
ecc[pos++] = ((ecc_p & 0x0F) << 4) | (ecc_c >> 8);
|
||||
ecc[pos++] = ecc_c & 0xFF;
|
||||
}
|
||||
|
||||
/* Fill rest of buffer with 0xFF */
|
||||
while (pos < sizeof(ecc))
|
||||
ecc[pos++] = 0xFF;
|
||||
|
||||
/* Write calculated ECC bytes */
|
||||
for (i = 0; i < sizeof(ecc)/4; ++i)
|
||||
writel(((uint32_t *)ecc)[i], chip->IO_ADDR_W);
|
||||
}
|
||||
}
|
||||
|
||||
static void mt62xx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
|
||||
int len)
|
||||
{
|
||||
uint32_t *buf_32 = (uint32_t *)buf;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
int i;
|
||||
|
||||
/* Writing in spare area? */
|
||||
if (readw(MTK_NFI_ADDRCNTR) >= mtd->writesize) {
|
||||
/*
|
||||
* Due to issue that ECC calculations are available after
|
||||
* additional read/write cycle, ECC calculations
|
||||
* are handled here.
|
||||
*/
|
||||
mt62xx_nand_write_ecc(mtd, len);
|
||||
} else {
|
||||
if (len % 4)
|
||||
printk(KERN_ERR "Length parameter is not aligned\n");
|
||||
|
||||
for (i = 0; i < len/4; ++i)
|
||||
writel(buf_32[i], chip->IO_ADDR_W);
|
||||
}
|
||||
}
|
||||
|
||||
static int mt62xx_nand_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
|
||||
uint8_t *ecc_code)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
/*
|
||||
* Calculations are done automatically and there is no need for
|
||||
* additional operations.
|
||||
* This function is used to pass ECC block number to ecc_correct()
|
||||
* function to be able to correct proper ECC block.
|
||||
* MT62XX chips have address counter which points to address in page
|
||||
* which is currently read. We can use it to calculate which ECC block
|
||||
* has been already read.
|
||||
*/
|
||||
*ecc_code = readw(MTK_NFI_ADDRCNTR)/chip->ecc.size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt62xx_nand_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
|
||||
uint8_t *read_ecc, uint8_t *ecc_block_nr)
|
||||
{
|
||||
int ecc_block_mask, ecc_status, ret;
|
||||
|
||||
/* ECC block number is calculated in ecc_calculate() function */
|
||||
if (*ecc_block_nr == 0)
|
||||
/* Should neve happen */
|
||||
return -1;
|
||||
|
||||
/* Decrease value to get proper index */
|
||||
(*ecc_block_nr)--;
|
||||
ecc_block_mask = 1 << *ecc_block_nr;
|
||||
ecc_block_mask |= ecc_block_mask << 16;
|
||||
|
||||
ecc_status = readl(MTK_NFI_ERRDET) & ecc_block_mask;
|
||||
if (ecc_status & 0xFF)
|
||||
/* Uncorrectable error detected */
|
||||
ret = -1;
|
||||
else if(ecc_status >> 16) {
|
||||
/* Correctable error detected */
|
||||
int address;
|
||||
uint32_t *buffer = (uint32_t *)dat;
|
||||
|
||||
/* Read address (for given block) where error occured */
|
||||
address = readw((uint32_t *)MTK_NFI_SYM0_ADDR + *ecc_block_nr);
|
||||
/* Correct error using syndrome word for given block */
|
||||
buffer[address >> 2] ^=
|
||||
readw((uint32_t *)MTK_NFI_SYM0_DATA + *ecc_block_nr);
|
||||
ret = 1;
|
||||
} else
|
||||
/* Data read without errors */
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mt62xx_nand_ecc_hwctl(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
/* HW ECC doesn't need to be controlled, it's turned on during init */
|
||||
}
|
||||
|
||||
int board_nand_init(struct nand_chip *chip)
|
||||
{
|
||||
static int chip_nr = 0;
|
||||
struct mtd_info *mtd;
|
||||
|
||||
/* Power on NFI controller */
|
||||
writel(PDN_CON1_NFI, MTK_CONFG_PDN_CLR1);
|
||||
|
||||
|
@ -92,16 +374,47 @@ int board_nand_init(struct nand_chip *nand)
|
|||
writel(NFI_OPCON_FIFO_FLUSH | NFI_OPCON_FIFO_RST, MTK_NFI_OPCON);
|
||||
while(readl(MTK_NFI_OPCON))
|
||||
;
|
||||
writel(NFI_PAGEFMT_PSIZE_2048 |
|
||||
NFI_PAGEFMT_ADRMODE_LARGE_8IO |
|
||||
NFI_PAGEFMT_ECCBLKSIZE_1024,
|
||||
MTK_NFI_PAGEFMT);
|
||||
|
||||
writel(NFI_CON_SPARE_EN, MTK_NFI_CON);
|
||||
/* Enable spare area and ECC decoding for main */
|
||||
writel(NFI_CON_AUTOECC_DEC_EN |
|
||||
NFI_CON_MAIN_ECC_EN |
|
||||
NFI_CON_SPARE_EN,
|
||||
MTK_NFI_CON);
|
||||
/* Setup byte number in spare for ECC */
|
||||
writel(ECC_SPARE_BYTE_POS, MTK_NFI_SCON);
|
||||
|
||||
nand->cmd_ctrl = mtk_cmd_ctrl;
|
||||
nand->dev_ready = mtk_dev_ready;
|
||||
nand->ecc.mode = NAND_ECC_SOFT;
|
||||
chip->IO_ADDR_R = (uint32_t *)MTK_NFI_DATAR;
|
||||
chip->IO_ADDR_W = (uint32_t *)MTK_NFI_DATAW;
|
||||
chip->cmd_ctrl = mt62xx_nand_cmd_ctrl;
|
||||
chip->dev_ready = mt62xx_nand_dev_ready;
|
||||
chip->read_buf = mt62xx_nand_read_buf;
|
||||
chip->read_byte = mt62xx_nand_read_byte;
|
||||
chip->write_buf = mt62xx_nand_write_buf;
|
||||
chip->select_chip = mt62xx_nand_select_chip;
|
||||
|
||||
/* ECC settings */
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.calculate = mt62xx_nand_ecc_calculate;
|
||||
chip->ecc.correct = mt62xx_nand_ecc_correct;
|
||||
chip->ecc.hwctl = mt62xx_nand_ecc_hwctl;
|
||||
chip->ecc.size = 256;
|
||||
|
||||
mtd = &nand_info[chip_nr++];
|
||||
|
||||
/* Detect NAND chip */
|
||||
if (nand_scan_ident(mtd, 1, NULL))
|
||||
return -ENXIO;
|
||||
|
||||
if (mtd->writesize > 512)
|
||||
/* Large page NAND detected */
|
||||
writel(NFI_PAGEFMT_PSIZE_2048 |
|
||||
NFI_PAGEFMT_ADRMODE_LARGE_8IO |
|
||||
NFI_PAGEFMT_ECCBLKSIZE_256,
|
||||
MTK_NFI_PAGEFMT);
|
||||
else
|
||||
/* Small page NAND detected */
|
||||
writel(NFI_PAGEFMT_ECCBLKSIZE_256,
|
||||
MTK_NFI_PAGEFMT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue