#include <linux/bch.h>
#include <linux/compiler.h>
#include <nand.h>
-#include <asm/omap_elm.h>
+#include <linux/mtd/omap_elm.h>
#define BADBLOCK_MARKER_LENGTH 2
#define SECTOR_BYTES 512
}
/*
- * Generic BCH interface
+ * Driver configurations
*/
-struct nand_bch_priv {
- uint8_t mode;
- uint8_t type;
- uint8_t nibbles;
+struct omap_nand_info {
struct bch_control *control;
enum omap_ecc ecc_scheme;
};
-/* bch types */
-#define ECC_BCH4 0
-#define ECC_BCH8 1
-#define ECC_BCH16 2
-
-/* BCH nibbles for diff bch levels */
-#define ECC_BCH4_NIBBLES 13
-#define ECC_BCH8_NIBBLES 26
-#define ECC_BCH16_NIBBLES 52
-
/*
* This can be a single instance cause all current users have only one NAND
* with nearly the same setup (BCH8, some with ELM and others with sw BCH
* library).
* When some users with other BCH strength will exists this have to change!
*/
-static __maybe_unused struct nand_bch_priv bch_priv = {
- .type = ECC_BCH8,
- .nibbles = ECC_BCH8_NIBBLES,
+static __maybe_unused struct omap_nand_info omap_nand_info = {
.control = NULL
};
static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
{
struct nand_chip *nand = mtd->priv;
- struct nand_bch_priv *bch = nand->priv;
+ struct omap_nand_info *info = nand->priv;
unsigned int dev_width = (nand->options & NAND_BUSWIDTH_16) ? 1 : 0;
unsigned int ecc_algo = 0;
unsigned int bch_type = 0;
u32 ecc_config_val = 0;
/* configure GPMC for specific ecc-scheme */
- switch (bch->ecc_scheme) {
+ switch (info->ecc_scheme) {
case OMAP_ECC_HAM1_CODE_SW:
return;
case OMAP_ECC_HAM1_CODE_HW:
uint8_t *ecc_code)
{
struct nand_chip *chip = mtd->priv;
- struct nand_bch_priv *bch = chip->priv;
+ struct omap_nand_info *info = chip->priv;
uint32_t *ptr, val = 0;
int8_t i = 0, j;
- switch (bch->ecc_scheme) {
+ switch (info->ecc_scheme) {
case OMAP_ECC_HAM1_CODE_HW:
val = readl(&gpmc_cfg->ecc1_result);
ecc_code[0] = val & 0xFF;
return -EINVAL;
}
/* ECC scheme specific syndrome customizations */
- switch (bch->ecc_scheme) {
+ switch (info->ecc_scheme) {
case OMAP_ECC_HAM1_CODE_HW:
break;
#ifdef CONFIG_BCH
uint8_t *read_ecc, uint8_t *calc_ecc)
{
struct nand_chip *chip = mtd->priv;
- struct nand_bch_priv *bch = chip->priv;
+ struct omap_nand_info *info = chip->priv;
uint32_t eccbytes = chip->ecc.bytes;
uint32_t error_count = 0, error_max;
uint32_t error_loc[8];
+ enum bch_level bch_type;
uint32_t i, ecc_flag = 0;
uint8_t count, err = 0;
uint32_t byte_pos, bit_pos;
* while reading ECC result we read it in big endian.
* Hence while loading to ELM we have rotate to get the right endian.
*/
- switch (bch->ecc_scheme) {
+ switch (info->ecc_scheme) {
case OMAP_ECC_BCH8_CODE_HW:
+ bch_type = BCH_8_BIT;
omap_reverse_list(calc_ecc, eccbytes - 1);
break;
default:
return -EINVAL;
}
/* use elm module to check for errors */
- elm_config((enum bch_level)(bch->type));
- if (elm_check_error(calc_ecc, bch->nibbles, &error_count, error_loc)) {
+ elm_config(bch_type);
+ if (elm_check_error(calc_ecc, bch_type, &error_count, error_loc)) {
printf("nand: error: uncorrectable ECC errors\n");
return -EINVAL;
}
/* correct bch error */
for (count = 0; count < error_count; count++) {
- switch (bch->type) {
- case ECC_BCH8:
+ switch (info->ecc_scheme) {
+ case OMAP_ECC_BCH8_CODE_HW:
/* 14th byte in ECC is reserved to match ROM layout */
error_max = SECTOR_BYTES + (eccbytes - 1);
break;
dat[byte_pos] ^= 1 << bit_pos;
printf("nand: bit-flip corrected @data=%d\n", byte_pos);
} else if (byte_pos < error_max) {
- read_ecc[byte_pos - SECTOR_BYTES] = 1 << bit_pos;
+ read_ecc[byte_pos - SECTOR_BYTES] ^= 1 << bit_pos;
printf("nand: bit-flip corrected @oob=%d\n", byte_pos -
SECTOR_BYTES);
} else {
/* cannot correct more than 8 errors */
unsigned int errloc[8];
struct nand_chip *chip = mtd->priv;
- struct nand_bch_priv *chip_priv = chip->priv;
- struct bch_control *bch = chip_priv->control;
+ struct omap_nand_info *info = chip->priv;
- count = decode_bch(bch, NULL, 512, read_ecc, calc_ecc, NULL, errloc);
+ count = decode_bch(info->control, NULL, 512, read_ecc, calc_ecc,
+ NULL, errloc);
if (count > 0) {
/* correct errors */
for (i = 0; i < count; i++) {
static void __maybe_unused omap_free_bch(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
- struct nand_bch_priv *chip_priv = chip->priv;
- struct bch_control *bch = NULL;
-
- if (chip_priv)
- bch = chip_priv->control;
+ struct omap_nand_info *info = chip->priv;
- if (bch) {
- free_bch(bch);
- chip_priv->control = NULL;
+ if (info->control) {
+ free_bch(info->control);
+ info->control = NULL;
}
}
#endif /* CONFIG_BCH */
*/
static int omap_select_ecc_scheme(struct nand_chip *nand,
enum omap_ecc ecc_scheme, unsigned int pagesize, unsigned int oobsize) {
- struct nand_bch_priv *bch = nand->priv;
+ struct omap_nand_info *info = nand->priv;
struct nand_ecclayout *ecclayout = &omap_ecclayout;
int eccsteps = pagesize / SECTOR_BYTES;
int i;
debug("nand: selected OMAP_ECC_HAM1_CODE_SW\n");
/* For this ecc-scheme, ecc.bytes, ecc.layout, ... are
* initialized in nand_scan_tail(), so just set ecc.mode */
- bch_priv.control = NULL;
- bch_priv.type = 0;
+ info->control = NULL;
nand->ecc.mode = NAND_ECC_SOFT;
nand->ecc.layout = NULL;
nand->ecc.size = 0;
- bch->ecc_scheme = OMAP_ECC_HAM1_CODE_SW;
break;
case OMAP_ECC_HAM1_CODE_HW:
(3 * eccsteps) + BADBLOCK_MARKER_LENGTH));
return -EINVAL;
}
- bch_priv.control = NULL;
- bch_priv.type = 0;
+ info->control = NULL;
/* populate ecc specific fields */
memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
nand->ecc.mode = NAND_ECC_HW;
ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
BADBLOCK_MARKER_LENGTH;
- bch->ecc_scheme = OMAP_ECC_HAM1_CODE_HW;
break;
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
return -EINVAL;
}
/* check if BCH S/W library can be used for error detection */
- bch_priv.control = init_bch(13, 8, 0x201b);
- if (!bch_priv.control) {
+ info->control = init_bch(13, 8, 0x201b);
+ if (!info->control) {
printf("nand: error: could not init_bch()\n");
return -ENODEV;
}
- bch_priv.type = ECC_BCH8;
/* populate ecc specific fields */
memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
nand->ecc.mode = NAND_ECC_HW;
ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
BADBLOCK_MARKER_LENGTH;
- bch->ecc_scheme = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
break;
#else
printf("nand: error: CONFIG_BCH required for ECC\n");
}
/* intialize ELM for ECC error detection */
elm_init();
- bch_priv.type = ECC_BCH8;
+ info->control = NULL;
/* populate ecc specific fields */
memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl));
nand->ecc.mode = NAND_ECC_HW;
ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH;
ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes -
BADBLOCK_MARKER_LENGTH;
- bch->ecc_scheme = OMAP_ECC_BCH8_CODE_HW;
break;
#else
printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n");
if (ecc_scheme != OMAP_ECC_HAM1_CODE_SW)
nand->ecc.layout = ecclayout;
+ info->ecc_scheme = ecc_scheme;
return 0;
}
nand->IO_ADDR_R = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat;
nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd;
- nand->priv = &bch_priv;
+ nand->priv = &omap_nand_info;
nand->cmd_ctrl = omap_nand_hwcontrol;
nand->options |= NAND_NO_PADDING | NAND_CACHEPRG;
/* If we are 16 bit dev, our gpmc config tells us that */