This is a port of the Linux Blackfin on-chip NFC driver to U-Boot. Signed-off-by: Mike Frysinger <vapier@gentoo.org>master
parent
4148e02aba
commit
be9d8c780e
@ -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