uboot/drivers/mtd/nand/s3c64xx.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2006 DENX Software Engineering
   3 *
   4 * Implementation for U-Boot 1.1.6 by Samsung
   5 *
   6 * (C) Copyright 2008
   7 * Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de>
   8 *
   9 * See file CREDITS for list of people who contributed to this
  10 * project.
  11 *
  12 * This program is free software; you can redistribute it and/or
  13 * modify it under the terms of the GNU General Public License as
  14 * published by the Free Software Foundation; either version 2 of
  15 * the License, or (at your option) any later version.
  16 *
  17 * This program is distributed in the hope that it will be useful,
  18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 * GNU General Public License for more details.
  21 *
  22 * You should have received a copy of the GNU General Public License
  23 * along with this program; if not, write to the Free Software
  24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  25 * MA 02111-1307 USA
  26 */
  27
  28#include <common.h>
  29
  30#include <nand.h>
  31#include <linux/mtd/nand.h>
  32
  33#include <asm/arch/s3c6400.h>
  34
  35#include <asm/io.h>
  36#include <asm/errno.h>
  37
  38#define MAX_CHIPS       2
  39static int nand_cs[MAX_CHIPS] = {0, 1};
  40
  41#ifdef CONFIG_NAND_SPL
  42#define printf(arg...) do {} while (0)
  43#endif
  44
  45/* Nand flash definition values by jsgood */
  46#ifdef S3C_NAND_DEBUG
  47/*
  48 * Function to print out oob buffer for debugging
  49 * Written by jsgood
  50 */
  51static void print_oob(const char *header, struct mtd_info *mtd)
  52{
  53        int i;
  54        struct nand_chip *chip = mtd->priv;
  55
  56        printf("%s:\t", header);
  57
  58        for (i = 0; i < 64; i++)
  59                printf("%02x ", chip->oob_poi[i]);
  60
  61        printf("\n");
  62}
  63#endif /* S3C_NAND_DEBUG */
  64
  65static void s3c_nand_select_chip(struct mtd_info *mtd, int chip)
  66{
  67        int ctrl = readl(NFCONT);
  68
  69        switch (chip) {
  70        case -1:
  71                ctrl |= 6;
  72                break;
  73        case 0:
  74                ctrl &= ~2;
  75                break;
  76        case 1:
  77                ctrl &= ~4;
  78                break;
  79        default:
  80                return;
  81        }
  82
  83        writel(ctrl, NFCONT);
  84}
  85
  86/*
  87 * Hardware specific access to control-lines function
  88 * Written by jsgood
  89 */
  90static void s3c_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
  91{
  92        struct nand_chip *this = mtd->priv;
  93
  94        if (ctrl & NAND_CTRL_CHANGE) {
  95                if (ctrl & NAND_CLE)
  96                        this->IO_ADDR_W = (void __iomem *)NFCMMD;
  97                else if (ctrl & NAND_ALE)
  98                        this->IO_ADDR_W = (void __iomem *)NFADDR;
  99                else
 100                        this->IO_ADDR_W = (void __iomem *)NFDATA;
 101                if (ctrl & NAND_NCE)
 102                        s3c_nand_select_chip(mtd, *(int *)this->priv);
 103                else
 104                        s3c_nand_select_chip(mtd, -1);
 105        }
 106
 107        if (cmd != NAND_CMD_NONE)
 108                writeb(cmd, this->IO_ADDR_W);
 109}
 110
 111/*
 112 * Function for checking device ready pin
 113 * Written by jsgood
 114 */
 115static int s3c_nand_device_ready(struct mtd_info *mtdinfo)
 116{
 117        return !!(readl(NFSTAT) & NFSTAT_RnB);
 118}
 119
 120#ifdef CONFIG_SYS_S3C_NAND_HWECC
 121/*
 122 * This function is called before encoding ecc codes to ready ecc engine.
 123 * Written by jsgood
 124 */
 125static void s3c_nand_enable_hwecc(struct mtd_info *mtd, int mode)
 126{
 127        u_long nfcont, nfconf;
 128
 129        /*
 130         * The original driver used 4-bit ECC for "new" MLC chips, i.e., for
 131         * those with non-zero ID[3][3:2], which anyway only holds for ST
 132         * (Numonyx) chips
 133         */
 134        nfconf = readl(NFCONF) & ~NFCONF_ECC_4BIT;
 135
 136        writel(nfconf, NFCONF);
 137
 138        /* Initialize & unlock */
 139        nfcont = readl(NFCONT);
 140        nfcont |= NFCONT_INITECC;
 141        nfcont &= ~NFCONT_MECCLOCK;
 142
 143        if (mode == NAND_ECC_WRITE)
 144                nfcont |= NFCONT_ECC_ENC;
 145        else if (mode == NAND_ECC_READ)
 146                nfcont &= ~NFCONT_ECC_ENC;
 147
 148        writel(nfcont, NFCONT);
 149}
 150
 151/*
 152 * This function is called immediately after encoding ecc codes.
 153 * This function returns encoded ecc codes.
 154 * Written by jsgood
 155 */
 156static int s3c_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
 157                                  u_char *ecc_code)
 158{
 159        u_long nfcont, nfmecc0;
 160
 161        /* Lock */
 162        nfcont = readl(NFCONT);
 163        nfcont |= NFCONT_MECCLOCK;
 164        writel(nfcont, NFCONT);
 165
 166        nfmecc0 = readl(NFMECC0);
 167
 168        ecc_code[0] = nfmecc0 & 0xff;
 169        ecc_code[1] = (nfmecc0 >> 8) & 0xff;
 170        ecc_code[2] = (nfmecc0 >> 16) & 0xff;
 171        ecc_code[3] = (nfmecc0 >> 24) & 0xff;
 172
 173        return 0;
 174}
 175
 176/*
 177 * This function determines whether read data is good or not.
 178 * If SLC, must write ecc codes to controller before reading status bit.
 179 * If MLC, status bit is already set, so only reading is needed.
 180 * If status bit is good, return 0.
 181 * If correctable errors occured, do that.
 182 * If uncorrectable errors occured, return -1.
 183 * Written by jsgood
 184 */
 185static int s3c_nand_correct_data(struct mtd_info *mtd, u_char *dat,
 186                                 u_char *read_ecc, u_char *calc_ecc)
 187{
 188        int ret = -1;
 189        u_long nfestat0, nfmeccdata0, nfmeccdata1, err_byte_addr;
 190        u_char err_type, repaired;
 191
 192        /* SLC: Write ecc to compare */
 193        nfmeccdata0 = (calc_ecc[1] << 16) | calc_ecc[0];
 194        nfmeccdata1 = (calc_ecc[3] << 16) | calc_ecc[2];
 195        writel(nfmeccdata0, NFMECCDATA0);
 196        writel(nfmeccdata1, NFMECCDATA1);
 197
 198        /* Read ecc status */
 199        nfestat0 = readl(NFESTAT0);
 200        err_type = nfestat0 & 0x3;
 201
 202        switch (err_type) {
 203        case 0: /* No error */
 204                ret = 0;
 205                break;
 206
 207        case 1:
 208                /*
 209                 * 1 bit error (Correctable)
 210                 * (nfestat0 >> 7) & 0x7ff      :error byte number
 211                 * (nfestat0 >> 4) & 0x7        :error bit number
 212                 */
 213                err_byte_addr = (nfestat0 >> 7) & 0x7ff;
 214                repaired = dat[err_byte_addr] ^ (1 << ((nfestat0 >> 4) & 0x7));
 215
 216                printf("S3C NAND: 1 bit error detected at byte %ld. "
 217                       "Correcting from 0x%02x to 0x%02x...OK\n",
 218                       err_byte_addr, dat[err_byte_addr], repaired);
 219
 220                dat[err_byte_addr] = repaired;
 221
 222                ret = 1;
 223                break;
 224
 225        case 2: /* Multiple error */
 226        case 3: /* ECC area error */
 227                printf("S3C NAND: ECC uncorrectable error detected. "
 228                       "Not correctable.\n");
 229                ret = -1;
 230                break;
 231        }
 232
 233        return ret;
 234}
 235#endif /* CONFIG_SYS_S3C_NAND_HWECC */
 236
 237/*
 238 * Board-specific NAND initialization. The following members of the
 239 * argument are board-specific (per include/linux/mtd/nand.h):
 240 * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
 241 * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
 242 * - hwcontrol: hardwarespecific function for accesing control-lines
 243 * - dev_ready: hardwarespecific function for  accesing device ready/busy line
 244 * - enable_hwecc?: function to enable (reset)  hardware ecc generator. Must
 245 *   only be provided if a hardware ECC is available
 246 * - eccmode: mode of ecc, see defines
 247 * - chip_delay: chip dependent delay for transfering data from array to
 248 *   read regs (tR)
 249 * - options: various chip options. They can partly be set to inform
 250 *   nand_scan about special functionality. See the defines for further
 251 *   explanation
 252 * Members with a "?" were not set in the merged testing-NAND branch,
 253 * so they are not set here either.
 254 */
 255int board_nand_init(struct nand_chip *nand)
 256{
 257        static int chip_n;
 258
 259        if (chip_n >= MAX_CHIPS)
 260                return -ENODEV;
 261
 262        NFCONT_REG = (NFCONT_REG & ~NFCONT_WP) | NFCONT_ENABLE | 0x6;
 263
 264        nand->IO_ADDR_R         = (void __iomem *)NFDATA;
 265        nand->IO_ADDR_W         = (void __iomem *)NFDATA;
 266        nand->cmd_ctrl          = s3c_nand_hwcontrol;
 267        nand->dev_ready         = s3c_nand_device_ready;
 268        nand->select_chip       = s3c_nand_select_chip;
 269        nand->options           = 0;
 270#ifdef CONFIG_NAND_SPL
 271        nand->read_byte         = nand_read_byte;
 272        nand->write_buf         = nand_write_buf;
 273        nand->read_buf          = nand_read_buf;
 274#endif
 275
 276#ifdef CONFIG_SYS_S3C_NAND_HWECC
 277        nand->ecc.hwctl         = s3c_nand_enable_hwecc;
 278        nand->ecc.calculate     = s3c_nand_calculate_ecc;
 279        nand->ecc.correct       = s3c_nand_correct_data;
 280
 281        /*
 282         * If you get more than 1 NAND-chip with different page-sizes on the
 283         * board one day, it will get more complicated...
 284         */
 285        nand->ecc.mode          = NAND_ECC_HW;
 286        nand->ecc.size          = CONFIG_SYS_NAND_ECCSIZE;
 287        nand->ecc.bytes         = CONFIG_SYS_NAND_ECCBYTES;
 288#else
 289        nand->ecc.mode          = NAND_ECC_SOFT;
 290#endif /* ! CONFIG_SYS_S3C_NAND_HWECC */
 291
 292        nand->priv              = nand_cs + chip_n++;
 293
 294        return 0;
 295}
 296