blob: 1b9e9a65cbaa5a90af4372acdb58ef72e2f04dc9 [file] [log] [blame]
/*
* linux/drivers/mtd/nand/ls1024a_nand.c
*
* Copyright (C) Mindspeed Technologies, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Overview:
* This is a device driver for the NAND flash device found on the
* Comcerto board which utilizes the Toshiba TC58V64AFT part. This is
* a 128Mibit (8MiB x 8 bits) NAND flash device.
*/
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/bch.h>
#include <linux/bitrev.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/ratelimit.h>
#include <linux/platform_device.h>
#if 0
#include <mach/ecc.h>
#include <linux/mtd/exp_lock.h>
#endif
/* ECC Register Set */
/********************/
/* ECC Control Registers */
#define ECC_SHIFT_EN_CFG (0x0)
#define ECC_GEN_CFG (0x4)
#define ECC_TAG_CFG (0x8)
#define ECC_INIT_CFG (0xC)
#define ECC_PRTY_OUT_SEL_CFG (0x10)
#define ECC_POLY_START_CFG (0x14)
#define ECC_CS_SEL_CFG (0x18)
/* ECC Status Registers */
#define ECC_IDLE_STAT (0x1C)
#define ECC_POLY_STAT (0x20)
#define ECC_CORR_STAT (0x24)
#define ECC_CORR_DONE_STAT (0x28)
#define ECC_CORR_DATA_STAT (0x2C)
/* ECC general configuration register parameters */
#define HAMM_MODE BIT(28)
#define BCH_MODE (~ HAMM_MODE)
#define PRTY_MODE_MASK BIT_MASK(24)
#define PRTY_CALC PRTY_MODE_MASK
#define SYNDROME_CALC (~ PRTY_CALC)
#define ECC_LVL_MASK 0x3F0000
#define ECC_LVL_SHIFT 16
#define BLK_SIZE_MASK 0x7FF
#define ECC_LVL_2 0x2
#define ECC_LVL_4 0x4
#define ECC_LVL_6 0x6
#define ECC_LVL_8 0x8
#define ECC_LVL_10 0xA
#define ECC_LVL_12 0xC
#define ECC_LVL_14 0xE
#define ECC_LVL_16 0x10
#define ECC_LVL_18 0x12
#define ECC_LVL_20 0x14
#define ECC_LVL_22 0x16
#define ECC_LVL_24 0x18
#define ECC_LVL_26 0x1A
#define ECC_LVL_28 0x1C
#define ECC_LVL_30 0x1E
#define ECC_LVL_32 0x20
#if defined (CONFIG_NAND_LS1024A_ECC_24_HW_BCH)
#define ECC_LVL_VAL ECC_LVL_24 /* ECC Level 24 is used */
#elif defined (CONFIG_NAND_LS1024A_ECC_8_HW_BCH)
#define ECC_LVL_VAL ECC_LVL_8 /* ECC Level 8 is used */
#endif
/* Block size used in Bytes*/
#define ECC_BLOCK_SIZE_512 512
#define ECC_BLOCK_SIZE_1024 1024
/* Maximum value of ECC Block size is 2k-(1+14*ECC_LVL/8) Bytes */
#define ECC_BLOCK_SIZE ECC_BLOCK_SIZE_1024
#define ECC_BLOCK_SIZE_SHIFT ECC_BLOCK_SIZE_1024_SHIFT
#define ECC_CS4_SEL 0x10
#define ECC_CS3_SEL 0x08
#define ECC_CS2_SEL 0x04
#define ECC_CS1_SEL 0x02
#define ECC_CS0_SEL 0x01
#define ECC_INIT 0x1
#define ECC_SHIFT_ENABLE 0x1
#define ECC_SHIFT_DISABLE 0x0
#define ECC_PARITY_OUT_EN 0x1
#define ECC_PARITY_OUT_DISABLE 0x0
/* Polynomial Start Configuration (ECC_POLY_START_CFG) */
#define ECC_POLY_START (1 << 0)
/* Idle Status (ECC_IDLE_STAT) */
#define ECC_IDLE (1 << 0)
/* Polynomial Status (ECC_POLY_STAT) */
#define ECC_CORR_REQ (1 << 0)
#define ECC_ERASED_PAGE (1 << 1)
#define ECC_UNCORR_ERR_HAMM (1 << 2)
/* Correction Status (ECC_CORR_STAT) */
#define ECC_TAG_MASK 0xFFFF
#define ECC_NUM_ERR_MASK 0x3F
#define ECC_NUM_ERR_SHIFT 16
#define ECC_UNCORR (1 << 24)
/* Correction Done Status (ECC_CORR_DONE_STAT) */
#define ECC_DONE (1 << 0)
/* Correction Data Status (ECC_CORR_DATA_STAT), BCH Mode */
#define ECC_BCH_MASK 0xFFFF
#define ECC_BCH_INDEX_MASK 0x7FF
#define ECC_BCH_INDEX_SHIFT 16
#define ECC_BCH_VALID (1 << 31)
/* Correction Data Status (ECC_CORR_DATA_STAT), Hamming Mode */
#define ECC_HAMM_MASK 0xF
#define ECC_HAMM_INDEX_MASK 0x1FF
#define ECC_HAMM_INDEX_SHIFT 16
#define ECC_HAMM_VALID (1 << 31)
struct comcerto_bch_control {
struct bch_control *bch;
unsigned int *errloc;
};
/*
* MTD structure for Comcerto board
*/
struct comcerto_nand_info {
struct nand_chip chip;
struct mtd_info mtd;
struct gpio_desc *ce_gpio;
struct gpio_desc *br_gpio;
uint8_t *bit_reversed;
struct comcerto_bch_control cbc;
};
static inline
struct comcerto_nand_info *to_comerto_nand_info(struct nand_chip *chip)
{
return container_of(chip, struct comcerto_nand_info, chip);
}
static void __iomem *ecc_base_addr;
/*
* Define partitions for flash device
*/
uint32_t COMCERTO_NAND_ALE = 0x00000200;
uint32_t COMCERTO_NAND_CLE = 0x00000400;
#ifdef CONFIG_NAND_LS1024A_ECC_24_HW_BCH
/*
* spare area layout for BCH ECC bytes calculated over 512-Bytes ECC block size
*/
static struct nand_ecclayout comcerto_ecc_info_512_bch = {
.eccbytes = 42,
.eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41},
.oobfree = {
{.offset = 43, .length = 13}
}
};
/*
* spare area layout for BCH ECC bytes calculated over 1024-Bytes ECC block size
*/
static struct nand_ecclayout comcerto_ecc_info_1024_bch = {
.eccbytes = 42,
.eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41},
.oobfree = {
{.offset = 43, .length = 13}
}
};
#elif defined(CONFIG_NAND_LS1024A_ECC_8_HW_BCH)
/*
* spare area layout for BCH ECC bytes calculated over 512-Bytes ECC block size
*/
static struct nand_ecclayout comcerto_ecc_info_512_bch = {
.eccbytes = 14,
.eccpos = {0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13},
.oobfree = {
{.offset = 15, .length = 1}
}
};
/*
* spare area layout for BCH ECC bytes calculated over 1024-Bytes ECC block size
*/
static struct nand_ecclayout comcerto_ecc_info_1024_bch = {
.eccbytes = 14,
.eccpos = {0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13},
.oobfree = {
{.offset = 15, .length = 17}
}
};
#else
/*
* spare area layout for Hamming ECC bytes calculated over 512-Bytes ECC block
* size
*/
static struct nand_ecclayout comcerto_ecc_info_512_hamm = {
.eccbytes = 4,
.eccpos = {0, 1, 2, 3},
.oobfree = {
{.offset = 5, .length = 12}
}
};
/*
* spare area layout for Hamming ECC bytes calculated over 1024-Bytes ECC block
* size
*/
static struct nand_ecclayout comcerto_ecc_info_1024_hamm = {
.eccbytes = 4,
.eccpos = {0, 1, 2, 3},
.oobfree = {
{.offset = 5, .length = 28}
}
};
#endif
static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
static struct nand_bbt_descr bbt_main_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_8BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
.offs = 44,
.len = 4,
.veroffs = 48,
.maxblocks = 8,
.pattern = bbt_pattern,
};
static struct nand_bbt_descr bbt_mirror_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_8BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
.offs = 44,
.len = 4,
.veroffs = 48,
.maxblocks = 8,
.pattern = mirror_pattern,
};
static uint8_t scan_ff_pattern[] = { 0xff };
#ifdef CONFIG_NAND_LS1024A_ECC_24_HW_BCH
static struct nand_bbt_descr c2000_badblock_pattern = {
.offs = 42,
.len = 1,
.pattern = scan_ff_pattern
};
#elif defined(CONFIG_NAND_LS1024A_ECC_8_HW_BCH)
static struct nand_bbt_descr c2000_badblock_pattern = {
.offs = 14,
.len = 1,
.pattern = scan_ff_pattern
};
#endif
/** Disable/Enable shifting of data to parity module
*
* @param[in] en_dis_shift Enable or disable shift to parity module.
*
*/
static void comcerto_ecc_shift(uint8_t en_dis_shift)
{
writel_relaxed(en_dis_shift, ecc_base_addr + ECC_SHIFT_EN_CFG);
}
/** Initializes h/w ECC with proper configuration values.
*
* @param[in] mtd MTD device structure
* @param[in] mode Select between BCH and Hamming
*
*/
static void comcerto_enable_hw_ecc(struct mtd_info *mtd, int mode)
{
struct nand_chip *nand_device = (struct nand_chip *)(mtd->priv);
uint32_t ecc_gen_cfg_val = 0;
if (mode == NAND_ECC_READSYN) return;
/* CS4 will have the option for ECC calculation */
writel_relaxed(ECC_CS4_SEL, ecc_base_addr + ECC_CS_SEL_CFG);
/* parity calculation for write, syndrome calculation for read.*/
(mode == NAND_ECC_WRITE) ? (ecc_gen_cfg_val |= PRTY_CALC) : (ecc_gen_cfg_val &= SYNDROME_CALC);
#if defined (CONFIG_NAND_LS1024A_ECC_8_HW_BCH) || defined (CONFIG_NAND_LS1024A_ECC_24_HW_BCH)
ecc_gen_cfg_val &= BCH_MODE;
ecc_gen_cfg_val = (ecc_gen_cfg_val & ~(ECC_LVL_MASK)) | (ECC_LVL_VAL << ECC_LVL_SHIFT);
#else
ecc_gen_cfg_val |= HAMM_MODE;
#endif
ecc_gen_cfg_val = (ecc_gen_cfg_val & ~(BLK_SIZE_MASK)) | nand_device->ecc.size;
writel_relaxed(ecc_gen_cfg_val, ecc_base_addr + ECC_GEN_CFG);
/* Reset parity module and latch configured values */
writel_relaxed(ECC_INIT, ecc_base_addr + ECC_INIT_CFG);
comcerto_ecc_shift(ECC_SHIFT_ENABLE);
return;
}
/** writes ECC bytes generated by the parity module into the flash
*
* @param[in] mtd MTD device structure
* @param[in] dat raw data
* @param[in] ecc_code buffer for ECC
*
*/
static int comcerto_calculate_ecc(struct mtd_info *mtd,
const uint8_t *dat,
uint8_t *ecc_code)
{
struct nand_chip *nand_device = mtd->priv;
int ecc_bytes = nand_device->ecc.bytes;
uint8_t *ecc_calc = nand_device->buffers->ecccalc;
unsigned long timeo;
comcerto_ecc_shift(ECC_SHIFT_DISABLE);
/* Wait for syndrome calculation to complete */
timeo = jiffies + 4;
for (;;) {
int is_timeout = time_after_eq(jiffies, timeo);
int is_idle = readl_relaxed(ecc_base_addr + ECC_IDLE_STAT) & ECC_IDLE;
if (is_idle)
break;
if (is_timeout) {
pr_err("ECC Timeout waiting for parity module to become idle 1\n");
return -EIO;
}
touch_softlockup_watchdog();
}
comcerto_ecc_shift(ECC_SHIFT_ENABLE);
writel_relaxed(ECC_PARITY_OUT_EN, ecc_base_addr + ECC_PRTY_OUT_SEL_CFG);
/* Even though we do a dummy write to NAND flash, actual ECC bytes are
* written to the ECC location in the flash. The contents of ecc_calc
* are irrelevant. The ECC engine overwrites it with real ECC data. */
nand_device->write_buf(mtd, ecc_calc, ecc_bytes);
comcerto_ecc_shift(ECC_SHIFT_DISABLE);
writel_relaxed(ECC_PARITY_OUT_DISABLE, ecc_base_addr + ECC_PRTY_OUT_SEL_CFG);
return 0;
}
/** Checks ECC registers for errors and will correct them, if correctable
*
* @param[in] mtd MTD device structure
* @param[in] dat raw data
* @param[in] read_ecc ECC read out from flash
* @param[in] calc_ecc ECC calculated over the raw data
*
*/
static int comcerto_correct_ecc(struct mtd_info *mtd, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
struct nand_chip *nand_device = mtd->priv;
int num_zero_bits = 0;
int empty = 0;
#if defined (CONFIG_NAND_LS1024A_ECC_8_HW_BCH) || defined (CONFIG_NAND_LS1024A_ECC_24_HW_BCH)
uint8_t err_count = 0;
uint32_t err_corr_data_prev;
#endif
uint32_t err_corr_data;
uint16_t mask, index;
unsigned long timeo;
int ret;
num_zero_bits = nand_check_erased_ecc_chunk(dat, nand_device->ecc.size,
read_ecc, nand_device->ecc.bytes,
NULL, 0,
nand_device->ecc.strength);
if (num_zero_bits >= 0) {
/* Consider ECC chunk empty */
empty = 1;
if (num_zero_bits > 2) {
pr_err_ratelimited("ECC: ECC chunk is mostly empty but has %d zero bits\n", num_zero_bits);
}
/*
* Even though the ECC chunk is empty, we still continue, wait
* for the syndrome calculation to complete and read out all
* syndromes even though there should be none.
* */
}
/* Wait for syndrome calculation to complete */
timeo = jiffies + 4;
for (;;) {
int is_timeout = time_after_eq(jiffies, timeo);
int is_idle = readl_relaxed(ecc_base_addr + ECC_IDLE_STAT) & ECC_IDLE;
if (is_idle)
break;
if (is_timeout) {
pr_err("ECC Timeout waiting for parity module to become idle 2\n");
ret = -EIO;
goto out;
}
touch_softlockup_watchdog();
}
/* If no correction is required */
if (likely(!((readl_relaxed(ecc_base_addr + ECC_POLY_STAT)) & ECC_CORR_REQ))) {
ret = 0;
goto out;
}
/* Error found! Correction required */
#if defined (CONFIG_NAND_LS1024A_ECC_8_HW_BCH) || defined (CONFIG_NAND_LS1024A_ECC_24_HW_BCH)
/* Initiate correction operation */
writel_relaxed(ECC_POLY_START, ecc_base_addr + ECC_POLY_START_CFG);
udelay(25);
timeo = jiffies + 4;
err_corr_data_prev = 0;
/* Read Correction data status register till header is 0x7FD */
for (;;) {
int is_startcode;
int is_timeout = time_after_eq(jiffies, timeo);
err_corr_data_prev = readl_relaxed(ecc_base_addr + ECC_CORR_DATA_STAT);
is_startcode = (err_corr_data_prev >> ECC_BCH_INDEX_SHIFT) == 0x87FD;
if (is_startcode)
break;
if (is_timeout) {
pr_err("Timeout waiting for ECC correction data, reg=%08x\n",
err_corr_data_prev);
ret = -EIO;
goto out;
}
touch_softlockup_watchdog();
}
udelay(25);
err_corr_data = 0x0;
/* start reading error locations */
while (((err_corr_data >> 16) != 0x87FE)) {
err_corr_data = readl_relaxed(ecc_base_addr + ECC_CORR_DATA_STAT);
if ((err_corr_data >> 16) == 0x87FE)
break;
if (err_corr_data == err_corr_data_prev)
continue;
err_corr_data_prev = err_corr_data;
/*
* If we determined that the ECC chunk is empty, we ignore all
* syndromes.
* */
if (empty)
continue;
index = (err_corr_data >> 16) & 0x7FF;
mask = err_corr_data & 0xFFFF;
if (index * 2 >= nand_device->ecc.size) {
pr_err("ECC correction index out of "
"bounds. ECC_CORR_DATA_STAT %08x\n",
err_corr_data);
continue;
}
*((uint16_t *)(dat + (index * 2))) ^= mask;
while (mask) {
if (mask & 1)
err_count++;
mask >>= 1;
}
}
if (!((readl_relaxed(ecc_base_addr + ECC_CORR_DONE_STAT)) & ECC_DONE)) {
pr_err("ECC: uncorrectable error 1 !!!\n");
ret = -1;
goto out;
}
/* Check if the block has uncorrectable number of errors */
if ((readl_relaxed(ecc_base_addr + ECC_CORR_STAT)) & ECC_UNCORR) {
if (!empty)
pr_err("ECC: uncorrectable error 2 !!!\n");
ret = -EIO;
goto out;
}
#else /* Hamming Mode */
if (readl_relaxed(ecc_base_addr + ECC_POLY_STAT) == ECC_UNCORR_ERR_HAMM) {
/* 2 or more errors detected and hence cannot
be corrected */
ret = -1; /* uncorrectable */
goto out;
} else { /* 1-bit correctable error */
err_corr_data = readl_relaxed(ecc_base_addr + ECC_CORR_DATA_STAT);
index = (err_corr_data >> 16) & 0x1FF;
if (nand_device->options & NAND_BUSWIDTH_16) {
mask = 1 << (err_corr_data & 0xF);
*((uint16_t *)(dat + index)) ^= mask;
} else {
mask = 1 << (err_corr_data & 0x7);
*(dat + index) ^= mask;
}
ret = 1;
goto out;
}
#endif
ret = err_count;
out:
if (empty) {
/*
* If we previously determined that this ECC chunk is empty,
* just ignore whatever errors were detected while reading
* syndromes from the ECC engine.
* */
ret = num_zero_bits;
}
comcerto_ecc_shift(ECC_SHIFT_DISABLE);
return ret;
}
/** writes single page to the NAND device along with the ECC bytes
*
* @param[in] mtd MTD device structure
* @param[in] chip nand chip info structure
* @param[in] buf data buffer
*
*/
static int comcerto_nand_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
const uint8_t *p = buf;
uint8_t *oob = chip->oob_poi;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
chip->write_buf(mtd, p, eccsize);
chip->ecc.calculate(mtd, p, oob);
oob += eccbytes;
if (chip->ecc.postpad) {
chip->write_buf(mtd, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
}
/* Calculate remaining oob bytes */
i = mtd->oobsize - (oob - chip->oob_poi);
if (i)
chip->write_buf(mtd, oob, i);
return 0;
}
static void comcerto_hybrid_hwctl (struct mtd_info *mtd, int mode) {
if (mode == NAND_ECC_READ) comcerto_enable_hw_ecc(mtd, mode);
}
#if 0
static void comcerto_fake_hwctl (struct mtd_info *mtd, int mode) {
}
#endif
/* Replicate the LS1024A HW ECC engine in software. Compared to the BCH
* implemenation in Linux, the LS1024A HW ECC engine (seemingly licensed from
* Cyclic Design) does a few things differently:
*
* - They use 0x4443 as the primitive polynomial for BCH (Linux uses 0x402b by
* default).
* - They reverse the 8 bits in every byte before they calculate the ECC code.
* They then also reverse the bits in the ECC code before they write it to
* flash.
* - They don't XOR the inverse of the ECC code of an empty page (all 0xFF's)
* onto every calculated ECC code. (Linux does this to ensure that an empty
* page has a valid ECC code).
* */
static int comcerto_bch_calculate_ecc (struct mtd_info *mtd, const uint8_t *dat,
uint8_t *ecc_code) {
struct nand_chip *chip = mtd->priv;
struct comcerto_nand_info *info = to_comerto_nand_info(chip);
int i;
for (i=0;i<chip->ecc.size;i++) {
info->bit_reversed[i] = bitrev8(dat[i]);
}
memset(ecc_code, 0, chip->ecc.bytes);
encode_bch(info->cbc.bch, info->bit_reversed, chip->ecc.size, ecc_code);
for (i=0;i<chip->ecc.bytes;i++) {
ecc_code[i] = bitrev8(ecc_code[i]);
}
return 0;
}
/* We currently don't need comcerto_bch_correct_ecc() because we are using a
* hybrid approach where we use the HW ECC engine when we read from NAND. We
* only calculate the ECC code in software when we write to the flash. */
#if 0
/*
* This function replicates the Cyclic Design ECC engine that can
* be found in the LS1024A. It calculates the ECC code a little different from
* nand_bch_correct_data() (nand_bch.c)
* */
static int comcerto_bch_correct_ecc (struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
uint8_t *dummy) {
struct nand_chip *chip = mtd->priv;
struct comcerto_nand_info *info = to_comerto_nand_info(chip);
int num_zero_bits = 0;
int i, count;
num_zero_bits = nand_check_erased_ecc_chunk(dat, chip->ecc.size,
read_ecc, chip->ecc.bytes,
NULL, 0,
chip->ecc.strength);
if (num_zero_bits >= 0) {
if (num_zero_bits > 2) {
pr_err_ratelimited("ECC: ECC chunk is mostly empty but has %d zero bits\n", num_zero_bits);
}
return num_zero_bits;
}
for (i=0;i<chip->ecc.size;i++) {
info->bit_reversed[i] = bitrev8(dat[i]);
}
for (i=0;i<chip->ecc.bytes;i++) {
read_ecc[i] = bitrev8(read_ecc[i]);
}
count = decode_bch(info->cbc.bch, info->bit_reversed, chip->ecc.size, read_ecc, NULL,
NULL, info->cbc.errloc);
if (count > 0) {
for (i = 0; i < count; i++) {
if (info->cbc.errloc[i] < (chip->ecc.size*8))
/* error is located in data, correct it */
dat[info->cbc.errloc[i] >> 3] ^= (1 << (7 - (info->cbc.errloc[i] & 7)));
/* else error in ecc, no action needed */
}
} else if (count < 0) {
pr_err_ratelimited("ecc unrecoverable error\n");
count = -1;
}
return count;
}
#endif
/** reads single page from the NAND device and will read ECC bytes from flash. A
* function call to comcerto_correct_ecc() will be used to validate the data.
*
* @param[in] mtd MTD device structure
* @param[in] chip nand chip info structure
* @param[in] buf data buffer
*
*/
#if 0
static int comcerto_nand_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int page)
{
struct nand_chip *nand_device = mtd->priv;
int i, eccsize = nand_device->ecc.size;
int eccbytes = nand_device->ecc.bytes;
int eccsteps = nand_device->ecc.steps;
uint8_t *p = buf;
uint8_t *ecc_code = nand_device->buffers->ecccode;
int ecc_bytes = nand_device->ecc.bytes;
int stat;
uint8_t *oob = nand_device->oob_poi;
for (; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
chip->ecc.hwctl(mtd, NAND_ECC_READ);
chip->read_buf(mtd, p, eccsize);
chip->read_buf(mtd, ecc_code, ecc_bytes);
stat = chip->ecc.correct(mtd, p, oob, NULL);
if (stat < 0) {
mtd->ecc_stats.failed++;
pr_err("ECC correction failed for page 0x%08x\n", page);
} else {
int idx = eccsteps;
if (idx >= MTD_ECC_STAT_SUBPAGES) {
idx = MTD_ECC_STAT_SUBPAGES - 1;
}
mtd->ecc_stats.corrected += stat;
mtd->ecc_subpage_stats.subpage_corrected[idx] += stat;
}
if (chip->ecc.postpad) {
chip->read_buf(mtd, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
}
/* Calculate remaining oob bytes */
i = mtd->oobsize - (oob - chip->oob_poi);
if (i)
chip->read_buf(mtd, oob, i);
return 0;
}
#endif
/*********************************************************************
* NAND Hardware functions
*
*********************************************************************/
/*
* hardware specific access to control-lines
*/
void comcerto_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
struct comcerto_nand_info *info = to_comerto_nand_info(chip);
if (ctrl & NAND_CTRL_CHANGE) {
gpiod_set_value(info->ce_gpio, !(ctrl & NAND_NCE));
}
if (cmd == NAND_CMD_NONE)
return;
if (ctrl & NAND_CLE)
writeb(cmd, chip->IO_ADDR_W + COMCERTO_NAND_CLE);
else if (ctrl & NAND_ALE)
writeb(cmd, chip->IO_ADDR_W + COMCERTO_NAND_ALE);
else
return;
}
int comcerto_nand_ready(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
struct comcerto_nand_info *info = to_comerto_nand_info(chip);
return gpiod_get_value(info->br_gpio);
}
/*********************************************************************
* NAND Probe
*
*********************************************************************/
static int comcerto_nand_probe(struct platform_device *pdev)
{
struct comcerto_nand_info *info;
struct mtd_info *mtd;
struct nand_chip *chip;
struct resource *res;
int err = 0;
struct mtd_part_parser_data ppdata = {};
/* Allocate memory for info structure */
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
mtd = &info->mtd;
chip = &info->chip;
mtd->owner = THIS_MODULE;
/* Link the private data with the MTD structure */
mtd->priv = &info->chip;
info->ce_gpio = devm_gpiod_get(&pdev->dev, "ce",
GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT);
if (IS_ERR(info->ce_gpio)) {
dev_err(&pdev->dev, "Failed to get CE GPIO.\n");
goto out_info;
}
info->br_gpio = devm_gpiod_get(&pdev->dev, "br",
GPIOD_FLAGS_BIT_DIR_SET);
if (IS_ERR(info->br_gpio)) {
dev_err(&pdev->dev, "Failed to get BR GPIO.\n");
goto out_info;
}
/*Map physical address of nand into virtual space */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(chip->IO_ADDR_R)) {
pr_err("LS1024A NAND: cannot map nand memory\n");
err = PTR_ERR(chip->IO_ADDR_R);
goto out_info;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
ecc_base_addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ecc_base_addr)) {
pr_err("LS1024A NAND: cannot map ecc config\n");
err = PTR_ERR(chip->IO_ADDR_R);
goto out_iorc;
}
/* This is the same address to read and write */
chip->IO_ADDR_W = chip->IO_ADDR_R;
/* Set address of hardware control function */
chip->cmd_ctrl = comcerto_nand_hwcontrol;
chip->dev_ready = comcerto_nand_ready;
chip->ecc.mode = NAND_ECC_HW_SYNDROME;
// chip->ecc.mode = NAND_ECC_SOFT_BCH;
#if defined(CONFIG_C2K_ASIC) && defined(CONFIG_NAND_TYPE_SLC)
chip->options = NAND_BUSWIDTH_16;
#else
chip->options = 0;
#endif
/* Scan to find existence of the device */
if (nand_scan_ident(mtd, 1, NULL)) {
err = -ENXIO;
goto out_ior;
}
if (1 && chip->ecc.mode == NAND_ECC_HW_SYNDROME) {
/* We use a hybrid approach here where we use the ECC hardware
* in the read path to correct ECC errors on read. For writing,
* we calculate the ECC code in software. The reason for this
* is to work around a silicon bug where the ECC hardware would
* disturb and corrupt the NOR flash if the user tries to write
* to NOR flash and NAND flash concurrently.
*/
unsigned int m, t;
chip->ecc.hwctl = comcerto_hybrid_hwctl;
chip->ecc.calculate = comcerto_bch_calculate_ecc;
chip->ecc.correct = comcerto_correct_ecc;
dev_info(&pdev->dev, "Using hybrid hw/sw ECC\n");
chip->ecc.size = 1024;
chip->ecc.layout = &comcerto_ecc_info_1024_bch;
chip->ecc.strength = 24;
chip->ecc.prepad = 0;
chip->ecc.postpad = 14;
chip->ecc.steps = mtd->writesize / chip->ecc.size;
if(chip->ecc.steps * chip->ecc.size != mtd->writesize) {
dev_err(&pdev->dev, "Invalid ecc parameters\n");
BUG();
}
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
chip->ecc.bytes = DIV_ROUND_UP(
chip->ecc.strength * fls(8 * chip->ecc.size), 8);
if (chip->ecc.bytes != 42) {
dev_warn(&pdev->dev, "ecc->bytes != 42\n");
}
m = fls(1+8*chip->ecc.size);
t = (chip->ecc.bytes*8)/m;
/* The LS1024A HW ECC engine (seemingly licensed from Cyclic
* Design) uses 0x4443 as the primitive polynomial for BCH
* (Linux uses 0x402b by default). */
info->cbc.bch = init_bch(m, t, 0x4443);
if (!info->cbc.bch) {
err = -EINVAL;
goto out_ior;
}
/* verify that eccbytes has the expected value */
if (info->cbc.bch->ecc_bytes != chip->ecc.bytes) {
dev_warn(&pdev->dev, "invalid eccbytes %u, should be %u\n",
chip->ecc.bytes, info->cbc.bch->ecc_bytes);
free_bch(info->cbc.bch);
err = -EINVAL;
goto out_ior;
}
/* sanity checks */
if (8*(chip->ecc.size+chip->ecc.bytes) >= (1 << m)) {
dev_warn(&pdev->dev, "eccsize %u is too large\n", chip->ecc.size);
free_bch(info->cbc.bch);
err = -EINVAL;
goto out_ior;
}
#if 0
info->cbc.errloc = devm_kmalloc(&pdev->dev, t*sizeof(*info->cbc.errloc), GFP_KERNEL);
if (!info->cbc.errloc) {
free_bch(info->cbc.bch);
err = -ENOMEM;
goto out_ior;
}
#endif
info->bit_reversed = devm_kzalloc(&pdev->dev, chip->ecc.size, GFP_KERNEL);
if (!info->bit_reversed) {
free_bch(info->cbc.bch);
err = -ENOMEM;
goto out_ior;
}
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
chip->badblock_pattern = &c2000_badblock_pattern;
chip->bbt_options |= NAND_BBT_USE_FLASH;
} else if (chip->ecc.mode == NAND_ECC_HW_SYNDROME) {
chip->ecc.hwctl = comcerto_enable_hw_ecc;
chip->ecc.write_page = comcerto_nand_write_page_hwecc;
// chip->ecc.read_page = comcerto_nand_read_page_hwecc;
chip->ecc.calculate = comcerto_calculate_ecc;
chip->ecc.correct = comcerto_correct_ecc;
pr_info("hw_syndrome correction %d.\n", mtd->writesize);
switch (mtd->writesize) {
case 512:
chip->ecc.size = mtd->writesize;
#if defined (CONFIG_NAND_LS1024A_ECC_24_HW_BCH)
chip->ecc.layout = &comcerto_ecc_info_512_bch;
chip->ecc.bytes = 42;
chip->ecc.strength = 24;
chip->ecc.prepad = 0;
chip->ecc.postpad = 14;
#elif defined(CONFIG_NAND_LS1024A_ECC_8_HW_BCH)
chip->ecc.layout = &comcerto_ecc_info_512_bch;
chip->ecc.bytes = 14;
chip->ecc.strength = 8;
chip->ecc.prepad = 0;
chip->ecc.postpad = 2;
#else
chip->ecc.layout = &comcerto_ecc_info_512_hamm;
chip->ecc.bytes = 4;
chip->ecc.prepad = 0;
chip->ecc.postpad = 2;
#endif
break;
case 1024:
chip->ecc.size = mtd->writesize;
#ifdef CONFIG_NAND_LS1024A_ECC_24_HW_BCH
chip->ecc.layout = &comcerto_ecc_info_1024_bch;
chip->ecc.bytes = 42;
chip->ecc.strength = 24;
chip->ecc.prepad = 0;
chip->ecc.postpad = 14;
#elif defined(CONFIG_NAND_LS1024A_ECC_8_HW_BCH)
chip->ecc.layout = &comcerto_ecc_info_1024_bch;
chip->ecc.bytes = 14;
chip->ecc.strength = 8;
chip->ecc.prepad = 0;
chip->ecc.postpad = 18;
#else
chip->ecc.layout = &comcerto_ecc_info_1024_hamm;
chip->ecc.bytes = 4;
chip->ecc.prepad = 0;
chip->ecc.postpad = 18;
#endif
break;
default:
pr_err("Using default values for hw ecc\n");
chip->ecc.size = 1024;
#ifdef CONFIG_NAND_LS1024A_ECC_24_HW_BCH
chip->ecc.layout = &comcerto_ecc_info_1024_bch;
chip->ecc.bytes = 42;
chip->ecc.strength = 24;
chip->ecc.prepad = 0;
chip->ecc.postpad = 14;
#elif defined(CONFIG_NAND_LS1024A_ECC_8_HW_BCH)
chip->ecc.layout = &comcerto_ecc_info_1024_bch;
chip->ecc.bytes = 14;
chip->ecc.strength = 8;
chip->ecc.prepad = 0;
chip->ecc.postpad = 18;
#else
chip->ecc.layout = &comcerto_ecc_info_1024_hamm;
chip->ecc.bytes = 4;
chip->ecc.prepad = 0;
chip->ecc.postpad = 18;
#endif
break;
}
chip->ecc.steps = mtd->writesize / chip->ecc.size;
if(chip->ecc.steps * chip->ecc.size != mtd->writesize) {
pr_err("Invalid ecc parameters\n");
BUG();
}
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
chip->badblock_pattern = &c2000_badblock_pattern;
chip->bbt_options |= NAND_BBT_USE_FLASH;
} else {
pr_info("using soft ecc.\n");
chip->ecc.size = 1024;
chip->ecc.strength = 24;
chip->ecc.bytes = 42;
chip->ecc.mode = NAND_ECC_SOFT_BCH;
}
chip->options |= NAND_NO_SUBPAGE_WRITE;
if(nand_scan_tail(mtd)) {
pr_err("nand_scan_tail returned error\n");
err = -ENXIO;
goto out_ior;
}
/* Name of the mtd device */
mtd->name = dev_name(&pdev->dev);
/* Link the info stucture with platform_device */
platform_set_drvdata(pdev, info);
ppdata.of_node = pdev->dev.of_node;
err = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
if (err) {
nand_release(mtd);
goto out_ior;
}
goto out;
out_ior:
devm_iounmap(&pdev->dev, ecc_base_addr);
out_iorc:
devm_iounmap(&pdev->dev, chip->IO_ADDR_R);
out_info:
devm_kfree(&pdev->dev, info);
out:
return err;
}
/*********************************************************************
* NAND Remove
*
*********************************************************************/
static int comcerto_nand_remove(struct platform_device *pdev)
{
struct comcerto_nand_info *info =
(struct comcerto_nand_info *)platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
/* Release resources, unregister device */
nand_release(&info->mtd);
/*Deregister virtual address */
iounmap(info->chip.IO_ADDR_R);
iounmap(ecc_base_addr);
return 0;
}
/*********************************************************************
* Driver Registration
*
*********************************************************************/
static const struct of_device_id fsl_ls1024a_nand_match[] = {
{
.compatible = "fsl,ls1024a-nand",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_ls1024a_nand_match);
static struct platform_driver ls1024a_nand_driver = {
.probe = comcerto_nand_probe,
.remove = comcerto_nand_remove,
.driver = {
.name = "ls1024a_nand",
.of_match_table = fsl_ls1024a_nand_match,
},
};
module_platform_driver(ls1024a_nand_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Comcerto board");