mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-19 00:18:51 +09:00
![Graf Yang](/assets/img/avatar_default.png)
We need to make sure the data written to the nand flash controller makes it there before we start polling its status register. Otherwise, we may get stale data and return before the controller is actually ready. Signed-off-by: Graf Yang <graf.yang@analog.com> Signed-off-by: Mike Frysinger <vapier@gentoo.org> Acked-by: Scott Wood <scottwood@freescale.com>
387 lines
8.9 KiB
C
387 lines
8.9 KiB
C
/*
|
|
* Driver for Blackfin on-chip NAND controller.
|
|
*
|
|
* Enter bugs at http://blackfin.uclinux.org/
|
|
*
|
|
* Copyright (c) 2007-2008 Analog Devices Inc.
|
|
*
|
|
* Licensed under the GPL-2 or later.
|
|
*/
|
|
|
|
/* TODO:
|
|
* - move bit defines into mach-common/bits/nand.h
|
|
* - try and replace all IRQSTAT usage with STAT polling
|
|
* - have software ecc mode use same algo as hw ecc ?
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/io.h>
|
|
|
|
#ifdef DEBUG
|
|
# define pr_stamp() printf("%s:%s:%i: here i am\n", __FILE__, __func__, __LINE__)
|
|
#else
|
|
# define pr_stamp()
|
|
#endif
|
|
|
|
#include <nand.h>
|
|
|
|
#include <asm/blackfin.h>
|
|
|
|
/* Bit masks for NFC_CTL */
|
|
|
|
#define WR_DLY 0xf /* Write Strobe Delay */
|
|
#define RD_DLY 0xf0 /* Read Strobe Delay */
|
|
#define NWIDTH 0x100 /* NAND Data Width */
|
|
#define PG_SIZE 0x200 /* Page Size */
|
|
|
|
/* Bit masks for NFC_STAT */
|
|
|
|
#define NBUSY 0x1 /* Not Busy */
|
|
#define WB_FULL 0x2 /* Write Buffer Full */
|
|
#define PG_WR_STAT 0x4 /* Page Write Pending */
|
|
#define PG_RD_STAT 0x8 /* Page Read Pending */
|
|
#define WB_EMPTY 0x10 /* Write Buffer Empty */
|
|
|
|
/* Bit masks for NFC_IRQSTAT */
|
|
|
|
#define NBUSYIRQ 0x1 /* Not Busy IRQ */
|
|
#define WB_OVF 0x2 /* Write Buffer Overflow */
|
|
#define WB_EDGE 0x4 /* Write Buffer Edge Detect */
|
|
#define RD_RDY 0x8 /* Read Data Ready */
|
|
#define WR_DONE 0x10 /* Page Write Done */
|
|
|
|
#define NAND_IS_512() (CONFIG_BFIN_NFC_CTL_VAL & 0x200)
|
|
|
|
/*
|
|
* hardware specific access to control-lines
|
|
*/
|
|
static void bfin_nfc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
|
{
|
|
pr_stamp();
|
|
|
|
if (cmd == NAND_CMD_NONE)
|
|
return;
|
|
|
|
while (bfin_read_NFC_STAT() & WB_FULL)
|
|
continue;
|
|
|
|
if (ctrl & NAND_CLE)
|
|
bfin_write_NFC_CMD(cmd);
|
|
else
|
|
bfin_write_NFC_ADDR(cmd);
|
|
SSYNC();
|
|
}
|
|
|
|
int bfin_nfc_devready(struct mtd_info *mtd)
|
|
{
|
|
pr_stamp();
|
|
return (bfin_read_NFC_STAT() & NBUSY ? 1 : 0);
|
|
}
|
|
|
|
/*
|
|
* PIO mode for buffer writing and reading
|
|
*/
|
|
static void bfin_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
|
{
|
|
pr_stamp();
|
|
|
|
int i;
|
|
|
|
/*
|
|
* Data reads are requested by first writing to NFC_DATA_RD
|
|
* and then reading back from NFC_READ.
|
|
*/
|
|
for (i = 0; i < len; ++i) {
|
|
while (bfin_read_NFC_STAT() & WB_FULL)
|
|
if (ctrlc())
|
|
return;
|
|
|
|
/* Contents do not matter */
|
|
bfin_write_NFC_DATA_RD(0x0000);
|
|
SSYNC();
|
|
|
|
while (!(bfin_read_NFC_IRQSTAT() & RD_RDY))
|
|
if (ctrlc())
|
|
return;
|
|
|
|
buf[i] = bfin_read_NFC_READ();
|
|
|
|
bfin_write_NFC_IRQSTAT(RD_RDY);
|
|
}
|
|
}
|
|
|
|
static uint8_t bfin_nfc_read_byte(struct mtd_info *mtd)
|
|
{
|
|
pr_stamp();
|
|
|
|
uint8_t val;
|
|
bfin_nfc_read_buf(mtd, &val, 1);
|
|
return val;
|
|
}
|
|
|
|
static void bfin_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
|
{
|
|
pr_stamp();
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
while (bfin_read_NFC_STAT() & WB_FULL)
|
|
if (ctrlc())
|
|
return;
|
|
|
|
bfin_write_NFC_DATA_WR(buf[i]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ECC functions
|
|
* These allow the bfin to use the controller's ECC
|
|
* generator block to ECC the data as it passes through
|
|
*/
|
|
|
|
/*
|
|
* ECC error correction function
|
|
*/
|
|
static int bfin_nfc_correct_data_256(struct mtd_info *mtd, u_char *dat,
|
|
u_char *read_ecc, u_char *calc_ecc)
|
|
{
|
|
u32 syndrome[5];
|
|
u32 calced, stored;
|
|
unsigned short failing_bit, failing_byte;
|
|
u_char data;
|
|
|
|
pr_stamp();
|
|
|
|
calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16);
|
|
stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16);
|
|
|
|
syndrome[0] = (calced ^ stored);
|
|
|
|
/*
|
|
* syndrome 0: all zero
|
|
* No error in data
|
|
* No action
|
|
*/
|
|
if (!syndrome[0] || !calced || !stored)
|
|
return 0;
|
|
|
|
/*
|
|
* sysdrome 0: only one bit is one
|
|
* ECC data was incorrect
|
|
* No action
|
|
*/
|
|
if (hweight32(syndrome[0]) == 1)
|
|
return 1;
|
|
|
|
syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF);
|
|
syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF);
|
|
syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF);
|
|
syndrome[4] = syndrome[2] ^ syndrome[3];
|
|
|
|
/*
|
|
* sysdrome 0: exactly 11 bits are one, each parity
|
|
* and parity' pair is 1 & 0 or 0 & 1.
|
|
* 1-bit correctable error
|
|
* Correct the error
|
|
*/
|
|
if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) {
|
|
failing_bit = syndrome[1] & 0x7;
|
|
failing_byte = syndrome[1] >> 0x3;
|
|
data = *(dat + failing_byte);
|
|
data = data ^ (0x1 << failing_bit);
|
|
*(dat + failing_byte) = data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sysdrome 0: random data
|
|
* More than 1-bit error, non-correctable error
|
|
* Discard data, mark bad block
|
|
*/
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int bfin_nfc_correct_data(struct mtd_info *mtd, u_char *dat,
|
|
u_char *read_ecc, u_char *calc_ecc)
|
|
{
|
|
int ret;
|
|
|
|
pr_stamp();
|
|
|
|
ret = bfin_nfc_correct_data_256(mtd, dat, read_ecc, calc_ecc);
|
|
|
|
/* If page size is 512, correct second 256 bytes */
|
|
if (NAND_IS_512()) {
|
|
dat += 256;
|
|
read_ecc += 8;
|
|
calc_ecc += 8;
|
|
ret |= bfin_nfc_correct_data_256(mtd, dat, read_ecc, calc_ecc);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void reset_ecc(void)
|
|
{
|
|
bfin_write_NFC_RST(0x1);
|
|
while (bfin_read_NFC_RST() & 1)
|
|
continue;
|
|
}
|
|
|
|
static void bfin_nfc_enable_hwecc(struct mtd_info *mtd, int mode)
|
|
{
|
|
reset_ecc();
|
|
}
|
|
|
|
static int bfin_nfc_calculate_ecc(struct mtd_info *mtd,
|
|
const u_char *dat, u_char *ecc_code)
|
|
{
|
|
u16 ecc0, ecc1;
|
|
u32 code[2];
|
|
u8 *p;
|
|
|
|
pr_stamp();
|
|
|
|
/* first 4 bytes ECC code for 256 page size */
|
|
ecc0 = bfin_read_NFC_ECC0();
|
|
ecc1 = bfin_read_NFC_ECC1();
|
|
|
|
code[0] = (ecc0 & 0x7FF) | ((ecc1 & 0x7FF) << 11);
|
|
|
|
/* first 3 bytes in ecc_code for 256 page size */
|
|
p = (u8 *) code;
|
|
memcpy(ecc_code, p, 3);
|
|
|
|
/* second 4 bytes ECC code for 512 page size */
|
|
if (NAND_IS_512()) {
|
|
ecc0 = bfin_read_NFC_ECC2();
|
|
ecc1 = bfin_read_NFC_ECC3();
|
|
code[1] = (ecc0 & 0x7FF) | ((ecc1 & 0x7FF) << 11);
|
|
|
|
/* second 3 bytes in ecc_code for second 256
|
|
* bytes of 512 page size
|
|
*/
|
|
p = (u8 *) (code + 1);
|
|
memcpy((ecc_code + 3), p, 3);
|
|
}
|
|
|
|
reset_ecc();
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_BFIN_NFC_BOOTROM_ECC
|
|
# define BOOTROM_ECC 1
|
|
#else
|
|
# define BOOTROM_ECC 0
|
|
#endif
|
|
|
|
static uint8_t bbt_pattern[] = { 0xff };
|
|
|
|
static struct nand_bbt_descr bootrom_bbt = {
|
|
.options = 0,
|
|
.offs = 63,
|
|
.len = 1,
|
|
.pattern = bbt_pattern,
|
|
};
|
|
|
|
static struct nand_ecclayout bootrom_ecclayout = {
|
|
.eccbytes = 24,
|
|
.eccpos = {
|
|
0x8 * 0, 0x8 * 0 + 1, 0x8 * 0 + 2,
|
|
0x8 * 1, 0x8 * 1 + 1, 0x8 * 1 + 2,
|
|
0x8 * 2, 0x8 * 2 + 1, 0x8 * 2 + 2,
|
|
0x8 * 3, 0x8 * 3 + 1, 0x8 * 3 + 2,
|
|
0x8 * 4, 0x8 * 4 + 1, 0x8 * 4 + 2,
|
|
0x8 * 5, 0x8 * 5 + 1, 0x8 * 5 + 2,
|
|
0x8 * 6, 0x8 * 6 + 1, 0x8 * 6 + 2,
|
|
0x8 * 7, 0x8 * 7 + 1, 0x8 * 7 + 2
|
|
},
|
|
.oobfree = {
|
|
{ 0x8 * 0 + 3, 5 },
|
|
{ 0x8 * 1 + 3, 5 },
|
|
{ 0x8 * 2 + 3, 5 },
|
|
{ 0x8 * 3 + 3, 5 },
|
|
{ 0x8 * 4 + 3, 5 },
|
|
{ 0x8 * 5 + 3, 5 },
|
|
{ 0x8 * 6 + 3, 5 },
|
|
{ 0x8 * 7 + 3, 5 },
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Board-specific NAND initialization. The following members of the
|
|
* argument are board-specific (per include/linux/mtd/nand.h):
|
|
* - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
|
|
* - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
|
|
* - cmd_ctrl: hardwarespecific function for accesing control-lines
|
|
* - dev_ready: hardwarespecific function for accesing device ready/busy line
|
|
* - enable_hwecc?: function to enable (reset) hardware ecc generator. Must
|
|
* only be provided if a hardware ECC is available
|
|
* - ecc.mode: mode of ecc, see defines
|
|
* - chip_delay: chip dependent delay for transfering data from array to
|
|
* read regs (tR)
|
|
* - options: various chip options. They can partly be set to inform
|
|
* nand_scan about special functionality. See the defines for further
|
|
* explanation
|
|
* Members with a "?" were not set in the merged testing-NAND branch,
|
|
* so they are not set here either.
|
|
*/
|
|
int board_nand_init(struct nand_chip *chip)
|
|
{
|
|
pr_stamp();
|
|
|
|
/* set width/ecc/timings/etc... */
|
|
bfin_write_NFC_CTL(CONFIG_BFIN_NFC_CTL_VAL);
|
|
|
|
/* clear interrupt status */
|
|
bfin_write_NFC_IRQMASK(0x0);
|
|
bfin_write_NFC_IRQSTAT(0xffff);
|
|
|
|
/* enable GPIO function enable register */
|
|
#ifdef __ADSPBF54x__
|
|
bfin_write_PORTJ_FER(bfin_read_PORTJ_FER() | 6);
|
|
#elif defined(__ADSPBF52x__)
|
|
bfin_write_PORTH_FER(bfin_read_PORTH_FER() | 0xFCFF);
|
|
bfin_write_PORTH_MUX(0);
|
|
#else
|
|
# error no support for this variant
|
|
#endif
|
|
|
|
chip->cmd_ctrl = bfin_nfc_cmd_ctrl;
|
|
chip->read_buf = bfin_nfc_read_buf;
|
|
chip->write_buf = bfin_nfc_write_buf;
|
|
chip->read_byte = bfin_nfc_read_byte;
|
|
|
|
#ifdef CONFIG_BFIN_NFC_NO_HW_ECC
|
|
# define ECC_HW 0
|
|
#else
|
|
# define ECC_HW 1
|
|
#endif
|
|
if (ECC_HW) {
|
|
if (BOOTROM_ECC) {
|
|
chip->badblock_pattern = &bootrom_bbt;
|
|
chip->ecc.layout = &bootrom_ecclayout;
|
|
}
|
|
if (!NAND_IS_512()) {
|
|
chip->ecc.bytes = 3;
|
|
chip->ecc.size = 256;
|
|
} else {
|
|
chip->ecc.bytes = 6;
|
|
chip->ecc.size = 512;
|
|
}
|
|
chip->ecc.mode = NAND_ECC_HW;
|
|
chip->ecc.calculate = bfin_nfc_calculate_ecc;
|
|
chip->ecc.correct = bfin_nfc_correct_data;
|
|
chip->ecc.hwctl = bfin_nfc_enable_hwecc;
|
|
} else
|
|
chip->ecc.mode = NAND_ECC_SOFT;
|
|
chip->dev_ready = bfin_nfc_devready;
|
|
chip->chip_delay = 0;
|
|
|
|
return 0;
|
|
}
|