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