mirror of
https://github.com/u-boot/u-boot.git
synced 2025-01-17 00:03:37 +08:00
Blackfin: add driver for on-chip NAND controller
This is a port of the Linux Blackfin on-chip NFC driver to U-Boot. Signed-off-by: Mike Frysinger <vapier@gentoo.org>
This commit is contained in:
parent
4148e02aba
commit
be9d8c780e
@ -35,6 +35,7 @@ COBJS-y += nand_ids.o
|
||||
COBJS-y += nand_util.o
|
||||
endif
|
||||
|
||||
COBJS-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o
|
||||
COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o
|
||||
COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o
|
||||
COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o
|
||||
|
385
drivers/mtd/nand/bfin_nand.c
Normal file
385
drivers/mtd/nand/bfin_nand.c
Normal file
@ -0,0 +1,385 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user