Commit 266dead2 authored by Leo (Hao) Chen's avatar Leo (Hao) Chen Committed by David Woodhouse

mtd: add bcmring nand driver

Signed-off-by: default avatarLeo Hao Chen <leochen@broadcom.com>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 4b56ffca
......@@ -76,9 +76,19 @@ static struct ctl_table bcmring_sysctl_reboot[] = {
{}
};
static struct resource nand_resource[] = {
[0] = {
.start = MM_ADDR_IO_NAND,
.end = MM_ADDR_IO_NAND + 0x1000 - 1,
.flags = IORESOURCE_MEM,
},
};
static struct platform_device nand_device = {
.name = "bcm-nand",
.id = -1,
.resource = nand_resource,
.num_resources = ARRAY_SIZE(nand_resource),
};
static struct platform_device *devices[] __initdata = {
......
/*****************************************************************************
* Copyright 2001 - 2008 Broadcom Corporation. All rights reserved.
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2, available at
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
*
* Notwithstanding the above, under no circumstances may you combine this
* software in any way with any other Broadcom software provided under a
* license other than the GPL, without Broadcom's express prior written
* consent.
*****************************************************************************/
/*
*
*****************************************************************************
*
* REG_NAND.h
*
* PURPOSE:
*
* This file contains definitions for the nand registers:
*
* NOTES:
*
*****************************************************************************/
#if !defined(__ASM_ARCH_REG_NAND_H)
#define __ASM_ARCH_REG_NAND_H
/* ---- Include Files ---------------------------------------------------- */
#include <csp/reg.h>
#include <mach/reg_umi.h>
/* ---- Constants and Types ---------------------------------------------- */
#define HW_NAND_BASE MM_IO_BASE_NAND /* NAND Flash */
/* DMA accesses by the bootstrap need hard nonvirtual addresses */
#define REG_NAND_CMD __REG16(HW_NAND_BASE + 0)
#define REG_NAND_ADDR __REG16(HW_NAND_BASE + 4)
#define REG_NAND_PHYS_DATA16 (HW_NAND_BASE + 8)
#define REG_NAND_PHYS_DATA8 (HW_NAND_BASE + 8)
#define REG_NAND_DATA16 __REG16(REG_NAND_PHYS_DATA16)
#define REG_NAND_DATA8 __REG8(REG_NAND_PHYS_DATA8)
/* use appropriate offset to make sure it start at the 1K boundary */
#define REG_NAND_PHYS_DATA_DMA (HW_NAND_BASE + 0x400)
#define REG_NAND_DATA_DMA __REG32(REG_NAND_PHYS_DATA_DMA)
/* Linux DMA requires physical address of the data register */
#define REG_NAND_DATA16_PADDR HW_IO_VIRT_TO_PHYS(REG_NAND_PHYS_DATA16)
#define REG_NAND_DATA8_PADDR HW_IO_VIRT_TO_PHYS(REG_NAND_PHYS_DATA8)
#define REG_NAND_DATA_PADDR HW_IO_VIRT_TO_PHYS(REG_NAND_PHYS_DATA_DMA)
#define NAND_BUS_16BIT() (0)
#define NAND_BUS_8BIT() (!NAND_BUS_16BIT())
/* Register offsets */
#define REG_NAND_CMD_OFFSET (0)
#define REG_NAND_ADDR_OFFSET (4)
#define REG_NAND_DATA8_OFFSET (8)
#endif
This diff is collapsed.
......@@ -203,6 +203,22 @@ config MTD_NAND_S3C2410_CLKSTOP
when the is NAND chip selected or released, but will save
approximately 5mA of power when there is nothing happening.
config MTD_NAND_BCM_UMI
tristate "NAND Flash support for BCM Reference Boards"
depends on ARCH_BCMRING && MTD_NAND
help
This enables the NAND flash controller on the BCM UMI block.
No board specfic support is done by this driver, each board
must advertise a platform_device for the driver to attach.
config MTD_NAND_BCM_UMI_HWCS
bool "BCM UMI NAND Hardware CS"
depends on MTD_NAND_BCM_UMI
help
Enable the use of the BCM UMI block's internal CS using NAND.
This should only be used if you know the external NAND CS can toggle.
config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
depends on EXPERIMENTAL
......
......@@ -42,5 +42,6 @@ obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o
obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o
nand-objs := nand_base.o nand_bbt.o
/*****************************************************************************
* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved.
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2, available at
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
*
* Notwithstanding the above, under no circumstances may you combine this
* software in any way with any other Broadcom software provided under a
* license other than the GPL, without Broadcom's express prior written
* consent.
*****************************************************************************/
/* ---- Include Files ---------------------------------------------------- */
#include "nand_bcm_umi.h"
/* ---- External Variable Declarations ----------------------------------- */
/* ---- External Function Prototypes ------------------------------------- */
/* ---- Public Variables ------------------------------------------------- */
/* ---- Private Constants and Types -------------------------------------- */
/* ---- Private Function Prototypes -------------------------------------- */
static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int page);
static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf);
/* ---- Private Variables ------------------------------------------------ */
/*
** nand_hw_eccoob
** New oob placement block for use with hardware ecc generation.
*/
static struct nand_ecclayout nand_hw_eccoob_512 = {
/* Reserve 5 for BI indicator */
.oobfree = {
#if (NAND_ECC_NUM_BYTES > 3)
{.offset = 0, .length = 2}
#else
{.offset = 0, .length = 5},
{.offset = 6, .length = 7}
#endif
}
};
/*
** We treat the OOB for a 2K page as if it were 4 512 byte oobs,
** except the BI is at byte 0.
*/
static struct nand_ecclayout nand_hw_eccoob_2048 = {
/* Reserve 0 as BI indicator */
.oobfree = {
#if (NAND_ECC_NUM_BYTES > 10)
{.offset = 1, .length = 2},
#elif (NAND_ECC_NUM_BYTES > 7)
{.offset = 1, .length = 5},
{.offset = 16, .length = 6},
{.offset = 32, .length = 6},
{.offset = 48, .length = 6}
#else
{.offset = 1, .length = 8},
{.offset = 16, .length = 9},
{.offset = 32, .length = 9},
{.offset = 48, .length = 9}
#endif
}
};
/* We treat the OOB for a 4K page as if it were 8 512 byte oobs,
* except the BI is at byte 0. */
static struct nand_ecclayout nand_hw_eccoob_4096 = {
/* Reserve 0 as BI indicator */
.oobfree = {
#if (NAND_ECC_NUM_BYTES > 10)
{.offset = 1, .length = 2},
{.offset = 16, .length = 3},
{.offset = 32, .length = 3},
{.offset = 48, .length = 3},
{.offset = 64, .length = 3},
{.offset = 80, .length = 3},
{.offset = 96, .length = 3},
{.offset = 112, .length = 3}
#else
{.offset = 1, .length = 5},
{.offset = 16, .length = 6},
{.offset = 32, .length = 6},
{.offset = 48, .length = 6},
{.offset = 64, .length = 6},
{.offset = 80, .length = 6},
{.offset = 96, .length = 6},
{.offset = 112, .length = 6}
#endif
}
};
/* ---- Private Functions ------------------------------------------------ */
/* ==== Public Functions ================================================= */
/****************************************************************************
*
* bcm_umi_bch_read_page_hwecc - hardware ecc based page read function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
*
***************************************************************************/
static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t * buf,
int page)
{
int sectorIdx = 0;
int eccsize = chip->ecc.size;
int eccsteps = chip->ecc.steps;
uint8_t *datap = buf;
uint8_t eccCalc[NAND_ECC_NUM_BYTES];
int sectorOobSize = mtd->oobsize / eccsteps;
int stat;
for (sectorIdx = 0; sectorIdx < eccsteps;
sectorIdx++, datap += eccsize) {
if (sectorIdx > 0) {
/* Seek to page location within sector */
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize,
-1);
}
/* Enable hardware ECC before reading the buf */
nand_bcm_umi_bch_enable_read_hwecc();
/* Read in data */
bcm_umi_nand_read_buf(mtd, datap, eccsize);
/* Pause hardware ECC after reading the buf */
nand_bcm_umi_bch_pause_read_ecc_calc();
/* Read the OOB ECC */
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
mtd->writesize + sectorIdx * sectorOobSize, -1);
nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc,
NAND_ECC_NUM_BYTES,
chip->oob_poi +
sectorIdx * sectorOobSize);
/* Correct any ECC detected errors */
stat =
nand_bcm_umi_bch_correct_page(datap, eccCalc,
NAND_ECC_NUM_BYTES);
/* Update Stats */
if (stat < 0) {
#if defined(NAND_BCM_UMI_DEBUG)
printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n",
__func__, sectorIdx);
printk(KERN_WARNING
"%s data %02x %02x %02x %02x "
"%02x %02x %02x %02x\n",
__func__, datap[0], datap[1], datap[2], datap[3],
datap[4], datap[5], datap[6], datap[7]);
printk(KERN_WARNING
"%s ecc %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x "
"%02x %02x %02x\n",
__func__, eccCalc[0], eccCalc[1], eccCalc[2],
eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6],
eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10],
eccCalc[11], eccCalc[12]);
BUG();
#endif
mtd->ecc_stats.failed++;
} else {
#if defined(NAND_BCM_UMI_DEBUG)
if (stat > 0) {
printk(KERN_INFO
"%s %d correctable_errors detected\n",
__func__, stat);
}
#endif
mtd->ecc_stats.corrected += stat;
}
}
return 0;
}
/****************************************************************************
*
* bcm_umi_bch_write_page_hwecc - hardware ecc based page write function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: data buffer
*
***************************************************************************/
static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf)
{
int sectorIdx = 0;
int eccsize = chip->ecc.size;
int eccsteps = chip->ecc.steps;
const uint8_t *datap = buf;
uint8_t *oobp = chip->oob_poi;
int sectorOobSize = mtd->oobsize / eccsteps;
for (sectorIdx = 0; sectorIdx < eccsteps;
sectorIdx++, datap += eccsize, oobp += sectorOobSize) {
/* Enable hardware ECC before writing the buf */
nand_bcm_umi_bch_enable_write_hwecc();
bcm_umi_nand_write_buf(mtd, datap, eccsize);
nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp,
NAND_ECC_NUM_BYTES);
}
bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
}
This diff is collapsed.
/*****************************************************************************
* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved.
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2, available at
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
*
* Notwithstanding the above, under no circumstances may you combine this
* software in any way with any other Broadcom software provided under a
* license other than the GPL, without Broadcom's express prior written
* consent.
*****************************************************************************/
/* ---- Include Files ---------------------------------------------------- */
#include <mach/reg_umi.h>
#include "nand_bcm_umi.h"
#ifdef BOOT0_BUILD
#include <uart.h>
#endif
/* ---- External Variable Declarations ----------------------------------- */
/* ---- External Function Prototypes ------------------------------------- */
/* ---- Public Variables ------------------------------------------------- */
/* ---- Private Constants and Types -------------------------------------- */
/* ---- Private Function Prototypes -------------------------------------- */
/* ---- Private Variables ------------------------------------------------ */
/* ---- Private Functions ------------------------------------------------ */
#if NAND_ECC_BCH
/****************************************************************************
* nand_bch_ecc_flip_bit - Routine to flip an errored bit
*
* PURPOSE:
* This is a helper routine that flips the bit (0 -> 1 or 1 -> 0) of the
* errored bit specified
*
* PARAMETERS:
* datap - Container that holds the 512 byte data
* errorLocation - Location of the bit that needs to be flipped
*
* RETURNS:
* None
****************************************************************************/
static void nand_bcm_umi_bch_ecc_flip_bit(uint8_t *datap, int errorLocation)
{
int locWithinAByte = (errorLocation & REG_UMI_BCH_ERR_LOC_BYTE) >> 0;
int locWithinAWord = (errorLocation & REG_UMI_BCH_ERR_LOC_WORD) >> 3;
int locWithinAPage = (errorLocation & REG_UMI_BCH_ERR_LOC_PAGE) >> 5;
uint8_t errorByte = 0;
uint8_t byteMask = 1 << locWithinAByte;
/* BCH uses big endian, need to change the location
* bits to little endian */
locWithinAWord = 3 - locWithinAWord;
errorByte = datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord];
#ifdef BOOT0_BUILD
puthexs("\nECC Correct Offset: ",
locWithinAPage * sizeof(uint32_t) + locWithinAWord);
puthexs(" errorByte:", errorByte);
puthex8(" Bit: ", locWithinAByte);
#endif
if (errorByte & byteMask) {
/* bit needs to be cleared */
errorByte &= ~byteMask;
} else {
/* bit needs to be set */
errorByte |= byteMask;
}
/* write back the value with the fixed bit */
datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord] = errorByte;
}
/****************************************************************************
* nand_correct_page_bch - Routine to correct bit errors when reading NAND
*
* PURPOSE:
* This routine reads the BCH registers to determine if there are any bit
* errors during the read of the last 512 bytes of data + ECC bytes. If
* errors exists, the routine fixes it.
*
* PARAMETERS:
* datap - Container that holds the 512 byte data
*
* RETURNS:
* 0 or greater = Number of errors corrected
* (No errors are found or errors have been fixed)
* -1 = Error(s) cannot be fixed
****************************************************************************/
int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData,
int numEccBytes)
{
int numErrors;
int errorLocation;
int idx;
uint32_t regValue;
/* wait for read ECC to be valid */
regValue = nand_bcm_umi_bch_poll_read_ecc_calc();
/*
* read the control status register to determine if there
* are error'ed bits
* see if errors are correctible
*/
if ((regValue & REG_UMI_BCH_CTRL_STATUS_UNCORR_ERR) > 0) {
int i;
for (i = 0; i < numEccBytes; i++) {
if (readEccData[i] != 0xff) {
/* errors cannot be fixed, return -1 */
return -1;
}
}
/* If ECC is unprogrammed then we can't correct,
* assume everything OK */
return 0;
}
if ((regValue & REG_UMI_BCH_CTRL_STATUS_CORR_ERR) == 0) {
/* no errors */
return 0;
}
/*
* Fix errored bits by doing the following:
* 1. Read the number of errors in the control and status register
* 2. Read the error location registers that corresponds to the number
* of errors reported
* 3. Invert the bit in the data
*/
numErrors = (regValue & REG_UMI_BCH_CTRL_STATUS_NB_CORR_ERROR) >> 20;
for (idx = 0; idx < numErrors; idx++) {
errorLocation =
REG_UMI_BCH_ERR_LOC_ADDR(idx) & REG_UMI_BCH_ERR_LOC_MASK;
/* Flip bit */
nand_bcm_umi_bch_ecc_flip_bit(datap, errorLocation);
}
/* Errors corrected */
return numErrors;
}
#endif
/*****************************************************************************
* Copyright 2003 - 2009 Broadcom Corporation. All rights reserved.
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2, available at
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
*
* Notwithstanding the above, under no circumstances may you combine this
* software in any way with any other Broadcom software provided under a
* license other than the GPL, without Broadcom's express prior written
* consent.
*****************************************************************************/
#ifndef NAND_BCM_UMI_H
#define NAND_BCM_UMI_H
/* ---- Include Files ---------------------------------------------------- */
#include <mach/reg_umi.h>
#include <mach/reg_nand.h>
#include <cfg_global.h>
/* ---- Constants and Types ---------------------------------------------- */
#if (CFG_GLOBAL_CHIP_FAMILY == CFG_GLOBAL_CHIP_FAMILY_BCMRING)
#define NAND_ECC_BCH (CFG_GLOBAL_CHIP_REV > 0xA0)
#else
#define NAND_ECC_BCH 0
#endif
#define CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES 13
#if NAND_ECC_BCH
#ifdef BOOT0_BUILD
#define NAND_ECC_NUM_BYTES 13
#else
#define NAND_ECC_NUM_BYTES CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES
#endif
#else
#define NAND_ECC_NUM_BYTES 3
#endif
#define NAND_DATA_ACCESS_SIZE 512
/* ---- Variable Externs ------------------------------------------ */
/* ---- Function Prototypes --------------------------------------- */
int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData,
int numEccBytes);
/* Check in device is ready */
static inline int nand_bcm_umi_dev_ready(void)
{
return REG_UMI_NAND_RCSR & REG_UMI_NAND_RCSR_RDY;
}
/* Wait until device is ready */
static inline void nand_bcm_umi_wait_till_ready(void)
{
while (nand_bcm_umi_dev_ready() == 0)
;
}
/* Enable Hamming ECC */
static inline void nand_bcm_umi_hamming_enable_hwecc(void)
{
/* disable and reset ECC, 512 byte page */
REG_UMI_NAND_ECC_CSR &= ~(REG_UMI_NAND_ECC_CSR_ECC_ENABLE |
REG_UMI_NAND_ECC_CSR_256BYTE);
/* enable ECC */
REG_UMI_NAND_ECC_CSR |= REG_UMI_NAND_ECC_CSR_ECC_ENABLE;
}
#if NAND_ECC_BCH
/* BCH ECC specifics */
#define ECC_BITS_PER_CORRECTABLE_BIT 13
/* Enable BCH Read ECC */
static inline void nand_bcm_umi_bch_enable_read_hwecc(void)
{
/* disable and reset ECC */
REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID;
/* Turn on ECC */
REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN;
}
/* Enable BCH Write ECC */
static inline void nand_bcm_umi_bch_enable_write_hwecc(void)
{
/* disable and reset ECC */
REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID;
/* Turn on ECC */
REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_WR_EN;
}
/* Config number of BCH ECC bytes */
static inline void nand_bcm_umi_bch_config_ecc(uint8_t numEccBytes)
{
uint32_t nValue;
uint32_t tValue;
uint32_t kValue;
uint32_t numBits = numEccBytes * 8;
/* disable and reset ECC */
REG_UMI_BCH_CTRL_STATUS =
REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID |
REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID;
/* Every correctible bit requires 13 ECC bits */
tValue = (uint32_t) (numBits / ECC_BITS_PER_CORRECTABLE_BIT);
/* Total data in number of bits for generating and computing BCH ECC */
nValue = (NAND_DATA_ACCESS_SIZE + numEccBytes) * 8;
/* K parameter is used internally. K = N - (T * 13) */
kValue = nValue - (tValue * ECC_BITS_PER_CORRECTABLE_BIT);
/* Write the settings */
REG_UMI_BCH_N = nValue;
REG_UMI_BCH_T = tValue;
REG_UMI_BCH_K = kValue;
}
/* Pause during ECC read calculation to skip bytes in OOB */
static inline void nand_bcm_umi_bch_pause_read_ecc_calc(void)
{
REG_UMI_BCH_CTRL_STATUS =
REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN |
REG_UMI_BCH_CTRL_STATUS_PAUSE_ECC_DEC;
}
/* Resume during ECC read calculation after skipping bytes in OOB */
static inline void nand_bcm_umi_bch_resume_read_ecc_calc(void)
{
REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN;
}
/* Poll read ECC calc to check when hardware completes */
static inline uint32_t nand_bcm_umi_bch_poll_read_ecc_calc(void)
{
uint32_t regVal;
do {
/* wait for ECC to be valid */
regVal = REG_UMI_BCH_CTRL_STATUS;
} while ((regVal & REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID) == 0);
return regVal;
}
/* Poll write ECC calc to check when hardware completes */
static inline void nand_bcm_umi_bch_poll_write_ecc_calc(void)
{
/* wait for ECC to be valid */
while ((REG_UMI_BCH_CTRL_STATUS & REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID)
== 0)
;
}
/* Read the OOB and ECC, for kernel write OOB to a buffer */
#if defined(__KERNEL__) && !defined(STANDALONE)
static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize,
uint8_t *eccCalc, int numEccBytes, uint8_t *oobp)
#else
static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize,
uint8_t *eccCalc, int numEccBytes)
#endif
{
int eccPos = 0;
int numToRead = 16; /* There are 16 bytes per sector in the OOB */
/* ECC is already paused when this function is called */
if (pageSize == NAND_DATA_ACCESS_SIZE) {
while (numToRead > numEccBytes) {
/* skip free oob region */
#if defined(__KERNEL__) && !defined(STANDALONE)
*oobp++ = REG_NAND_DATA8;
#else
REG_NAND_DATA8;
#endif
numToRead--;
}
/* read ECC bytes before BI */
nand_bcm_umi_bch_resume_read_ecc_calc();
while (numToRead > 11) {
#if defined(__KERNEL__) && !defined(STANDALONE)
*oobp = REG_NAND_DATA8;
eccCalc[eccPos++] = *oobp;
oobp++;
#else
eccCalc[eccPos++] = REG_NAND_DATA8;
#endif
}
nand_bcm_umi_bch_pause_read_ecc_calc();
if (numToRead == 11) {
/* read BI */
#if defined(__KERNEL__) && !defined(STANDALONE)
*oobp++ = REG_NAND_DATA8;
#else
REG_NAND_DATA8;
#endif
numToRead--;
}
/* read ECC bytes */
nand_bcm_umi_bch_resume_read_ecc_calc();
while (numToRead) {
#if defined(__KERNEL__) && !defined(STANDALONE)
*oobp = REG_NAND_DATA8;
eccCalc[eccPos++] = *oobp;
oobp++;
#else
eccCalc[eccPos++] = REG_NAND_DATA8;
#endif
numToRead--;
}
} else {
/* skip BI */
#if defined(__KERNEL__) && !defined(STANDALONE)
*oobp++ = REG_NAND_DATA8;
#else
REG_NAND_DATA8;
#endif
numToRead--;
while (numToRead > numEccBytes) {
/* skip free oob region */
#if defined(__KERNEL__) && !defined(STANDALONE)
*oobp++ = REG_NAND_DATA8;
#else
REG_NAND_DATA8;
#endif
numToRead--;
}
/* read ECC bytes */
nand_bcm_umi_bch_resume_read_ecc_calc();
while (numToRead) {
#if defined(__KERNEL__) && !defined(STANDALONE)
*oobp = REG_NAND_DATA8;
eccCalc[eccPos++] = *oobp;
oobp++;
#else
eccCalc[eccPos++] = REG_NAND_DATA8;
#endif
numToRead--;
}
}
}
/* Helper function to write ECC */
static inline void NAND_BCM_UMI_ECC_WRITE(int numEccBytes, int eccBytePos,
uint8_t *oobp, uint8_t eccVal)
{
if (eccBytePos <= numEccBytes)
*oobp = eccVal;
}
/* Write OOB with ECC */
static inline void nand_bcm_umi_bch_write_oobEcc(uint32_t pageSize,
uint8_t *oobp, int numEccBytes)
{
uint32_t eccVal = 0xffffffff;
/* wait for write ECC to be valid */
nand_bcm_umi_bch_poll_write_ecc_calc();
/*
** Get the hardware ecc from the 32-bit result registers.
** Read after 512 byte accesses. Format B3B2B1B0
** where B3 = ecc3, etc.
*/
if (pageSize == NAND_DATA_ACCESS_SIZE) {
/* Now fill in the ECC bytes */
if (numEccBytes >= 13)
eccVal = REG_UMI_BCH_WR_ECC_3;
/* Usually we skip CM in oob[0,1] */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[0],
(eccVal >> 16) & 0xff);
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[1],
(eccVal >> 8) & 0xff);
/* Write ECC in oob[2,3,4] */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[2],
eccVal & 0xff); /* ECC 12 */
if (numEccBytes >= 9)
eccVal = REG_UMI_BCH_WR_ECC_2;
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[3],
(eccVal >> 24) & 0xff); /* ECC11 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[4],
(eccVal >> 16) & 0xff); /* ECC10 */
/* Always Skip BI in oob[5] */
} else {
/* Always Skip BI in oob[0] */
/* Now fill in the ECC bytes */
if (numEccBytes >= 13)
eccVal = REG_UMI_BCH_WR_ECC_3;
/* Usually skip CM in oob[1,2] */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[1],
(eccVal >> 16) & 0xff);
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[2],
(eccVal >> 8) & 0xff);
/* Write ECC in oob[3-15] */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[3],
eccVal & 0xff); /* ECC12 */
if (numEccBytes >= 9)
eccVal = REG_UMI_BCH_WR_ECC_2;
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[4],
(eccVal >> 24) & 0xff); /* ECC11 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[5],
(eccVal >> 16) & 0xff); /* ECC10 */
}
/* Fill in the remainder of ECC locations */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 10, &oobp[6],
(eccVal >> 8) & 0xff); /* ECC9 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 9, &oobp[7],
eccVal & 0xff); /* ECC8 */
if (numEccBytes >= 5)
eccVal = REG_UMI_BCH_WR_ECC_1;
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 8, &oobp[8],
(eccVal >> 24) & 0xff); /* ECC7 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 7, &oobp[9],
(eccVal >> 16) & 0xff); /* ECC6 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 6, &oobp[10],
(eccVal >> 8) & 0xff); /* ECC5 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 5, &oobp[11],
eccVal & 0xff); /* ECC4 */
if (numEccBytes >= 1)
eccVal = REG_UMI_BCH_WR_ECC_0;
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 4, &oobp[12],
(eccVal >> 24) & 0xff); /* ECC3 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 3, &oobp[13],
(eccVal >> 16) & 0xff); /* ECC2 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 2, &oobp[14],
(eccVal >> 8) & 0xff); /* ECC1 */
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 1, &oobp[15],
eccVal & 0xff); /* ECC0 */
}
#endif
#endif /* NAND_BCM_UMI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment