linux/drivers/mtd/nand/bcm_umi_bch.c
<<
>>
Prefs
   1/*****************************************************************************
   2* Copyright 2004 - 2009 Broadcom Corporation.  All rights reserved.
   3*
   4* Unless you and Broadcom execute a separate written software license
   5* agreement governing use of this software, this software is licensed to you
   6* under the terms of the GNU General Public License version 2, available at
   7* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
   8*
   9* Notwithstanding the above, under no circumstances may you combine this
  10* software in any way with any other Broadcom software provided under a
  11* license other than the GPL, without Broadcom's express prior written
  12* consent.
  13*****************************************************************************/
  14
  15/* ---- Include Files ---------------------------------------------------- */
  16#include "nand_bcm_umi.h"
  17
  18/* ---- External Variable Declarations ----------------------------------- */
  19/* ---- External Function Prototypes ------------------------------------- */
  20/* ---- Public Variables ------------------------------------------------- */
  21/* ---- Private Constants and Types -------------------------------------- */
  22
  23/* ---- Private Function Prototypes -------------------------------------- */
  24static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
  25        struct nand_chip *chip, uint8_t *buf, int page);
  26static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
  27        struct nand_chip *chip, const uint8_t *buf);
  28
  29/* ---- Private Variables ------------------------------------------------ */
  30
  31/*
  32** nand_hw_eccoob
  33** New oob placement block for use with hardware ecc generation.
  34*/
  35static struct nand_ecclayout nand_hw_eccoob_512 = {
  36        /* Reserve 5 for BI indicator */
  37        .oobfree = {
  38#if (NAND_ECC_NUM_BYTES > 3)
  39                    {.offset = 0, .length = 2}
  40#else
  41                    {.offset = 0, .length = 5},
  42                    {.offset = 6, .length = 7}
  43#endif
  44                    }
  45};
  46
  47/*
  48** We treat the OOB for a 2K page as if it were 4 512 byte oobs,
  49** except the BI is at byte 0.
  50*/
  51static struct nand_ecclayout nand_hw_eccoob_2048 = {
  52        /* Reserve 0 as BI indicator */
  53        .oobfree = {
  54#if (NAND_ECC_NUM_BYTES > 10)
  55                    {.offset = 1, .length = 2},
  56#elif (NAND_ECC_NUM_BYTES > 7)
  57                    {.offset = 1, .length = 5},
  58                    {.offset = 16, .length = 6},
  59                    {.offset = 32, .length = 6},
  60                    {.offset = 48, .length = 6}
  61#else
  62                    {.offset = 1, .length = 8},
  63                    {.offset = 16, .length = 9},
  64                    {.offset = 32, .length = 9},
  65                    {.offset = 48, .length = 9}
  66#endif
  67                    }
  68};
  69
  70/* We treat the OOB for a 4K page as if it were 8 512 byte oobs,
  71 * except the BI is at byte 0. */
  72static struct nand_ecclayout nand_hw_eccoob_4096 = {
  73        /* Reserve 0 as BI indicator */
  74        .oobfree = {
  75#if (NAND_ECC_NUM_BYTES > 10)
  76                    {.offset = 1, .length = 2},
  77                    {.offset = 16, .length = 3},
  78                    {.offset = 32, .length = 3},
  79                    {.offset = 48, .length = 3},
  80                    {.offset = 64, .length = 3},
  81                    {.offset = 80, .length = 3},
  82                    {.offset = 96, .length = 3},
  83                    {.offset = 112, .length = 3}
  84#else
  85                    {.offset = 1, .length = 5},
  86                    {.offset = 16, .length = 6},
  87                    {.offset = 32, .length = 6},
  88                    {.offset = 48, .length = 6},
  89                    {.offset = 64, .length = 6},
  90                    {.offset = 80, .length = 6},
  91                    {.offset = 96, .length = 6},
  92                    {.offset = 112, .length = 6}
  93#endif
  94                    }
  95};
  96
  97/* ---- Private Functions ------------------------------------------------ */
  98/* ==== Public Functions ================================================= */
  99
 100/****************************************************************************
 101*
 102*  bcm_umi_bch_read_page_hwecc - hardware ecc based page read function
 103*  @mtd:        mtd info structure
 104*  @chip:       nand chip info structure
 105*  @buf:        buffer to store read data
 106*
 107***************************************************************************/
 108static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
 109                                       struct nand_chip *chip, uint8_t * buf,
 110                                                 int page)
 111{
 112        int sectorIdx = 0;
 113        int eccsize = chip->ecc.size;
 114        int eccsteps = chip->ecc.steps;
 115        uint8_t *datap = buf;
 116        uint8_t eccCalc[NAND_ECC_NUM_BYTES];
 117        int sectorOobSize = mtd->oobsize / eccsteps;
 118        int stat;
 119
 120        for (sectorIdx = 0; sectorIdx < eccsteps;
 121                        sectorIdx++, datap += eccsize) {
 122                if (sectorIdx > 0) {
 123                        /* Seek to page location within sector */
 124                        chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize,
 125                                      -1);
 126                }
 127
 128                /* Enable hardware ECC before reading the buf */
 129                nand_bcm_umi_bch_enable_read_hwecc();
 130
 131                /* Read in data */
 132                bcm_umi_nand_read_buf(mtd, datap, eccsize);
 133
 134                /* Pause hardware ECC after reading the buf */
 135                nand_bcm_umi_bch_pause_read_ecc_calc();
 136
 137                /* Read the OOB ECC */
 138                chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
 139                              mtd->writesize + sectorIdx * sectorOobSize, -1);
 140                nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc,
 141                                             NAND_ECC_NUM_BYTES,
 142                                             chip->oob_poi +
 143                                             sectorIdx * sectorOobSize);
 144
 145                /* Correct any ECC detected errors */
 146                stat =
 147                    nand_bcm_umi_bch_correct_page(datap, eccCalc,
 148                                                  NAND_ECC_NUM_BYTES);
 149
 150                /* Update Stats */
 151                if (stat < 0) {
 152#if defined(NAND_BCM_UMI_DEBUG)
 153                        printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n",
 154                               __func__, sectorIdx);
 155                        printk(KERN_WARNING
 156                               "%s data %02x %02x %02x %02x "
 157                                         "%02x %02x %02x %02x\n",
 158                               __func__, datap[0], datap[1], datap[2], datap[3],
 159                               datap[4], datap[5], datap[6], datap[7]);
 160                        printk(KERN_WARNING
 161                               "%s ecc  %02x %02x %02x %02x "
 162                                         "%02x %02x %02x %02x %02x %02x "
 163                                         "%02x %02x %02x\n",
 164                               __func__, eccCalc[0], eccCalc[1], eccCalc[2],
 165                               eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6],
 166                               eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10],
 167                               eccCalc[11], eccCalc[12]);
 168                        BUG();
 169#endif
 170                        mtd->ecc_stats.failed++;
 171                } else {
 172#if defined(NAND_BCM_UMI_DEBUG)
 173                        if (stat > 0) {
 174                                printk(KERN_INFO
 175                                       "%s %d correctable_errors detected\n",
 176                                       __func__, stat);
 177                        }
 178#endif
 179                        mtd->ecc_stats.corrected += stat;
 180                }
 181        }
 182        return 0;
 183}
 184
 185/****************************************************************************
 186*
 187*  bcm_umi_bch_write_page_hwecc - hardware ecc based page write function
 188*  @mtd:        mtd info structure
 189*  @chip:       nand chip info structure
 190*  @buf:        data buffer
 191*
 192***************************************************************************/
 193static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
 194        struct nand_chip *chip, const uint8_t *buf)
 195{
 196        int sectorIdx = 0;
 197        int eccsize = chip->ecc.size;
 198        int eccsteps = chip->ecc.steps;
 199        const uint8_t *datap = buf;
 200        uint8_t *oobp = chip->oob_poi;
 201        int sectorOobSize = mtd->oobsize / eccsteps;
 202
 203        for (sectorIdx = 0; sectorIdx < eccsteps;
 204             sectorIdx++, datap += eccsize, oobp += sectorOobSize) {
 205                /* Enable hardware ECC before writing the buf */
 206                nand_bcm_umi_bch_enable_write_hwecc();
 207                bcm_umi_nand_write_buf(mtd, datap, eccsize);
 208                nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp,
 209                                              NAND_ECC_NUM_BYTES);
 210        }
 211
 212        bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
 213}
 214