]> git.ipfire.org Git - people/ms/u-boot.git/blobdiff - drivers/mtd/nand/fsl_elbc_nand.c
mtd: nand: Add page argument to write_page() etc.
[people/ms/u-boot.git] / drivers / mtd / nand / fsl_elbc_nand.c
index 9076ad4cdc36ac517ef1658f91a0955c2223ab16..f621f141223360a263d0727c907adc13685e9f06 100644 (file)
@@ -5,19 +5,7 @@
  * Authors: Nick Spence <nick.spence@freescale.com>,
  *          Scott Wood <scottwood@freescale.com>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
@@ -49,7 +37,6 @@
 
 #define MAX_BANKS 8
 #define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
-#define FCM_TIMEOUT_MSECS 10 /* Maximum number of mSecs to wait for FCM */
 
 #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
 
@@ -167,8 +154,8 @@ static struct nand_bbt_descr bbt_mirror_descr = {
  */
 static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
 {
-       struct nand_chip *chip = mtd->priv;
-       struct fsl_elbc_mtd *priv = chip->priv;
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
        fsl_lbc_t *lbc = ctrl->regs;
        int buf_num;
@@ -207,11 +194,12 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
  */
 static int fsl_elbc_run_command(struct mtd_info *mtd)
 {
-       struct nand_chip *chip = mtd->priv;
-       struct fsl_elbc_mtd *priv = chip->priv;
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
        fsl_lbc_t *lbc = ctrl->regs;
-       long long end_tick;
+       u32 timeo = (CONFIG_SYS_HZ * 10) / 1000;
+       u32 time_start;
        u32 ltesr;
 
        /* Setup the FMR[OP] to execute without write protection */
@@ -230,10 +218,10 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
        out_be32(&lbc->lsor, priv->bank);
 
        /* wait for FCM complete flag or timeout */
-       end_tick = usec2ticks(FCM_TIMEOUT_MSECS * 1000) + get_ticks();
+       time_start = get_timer(0);
 
        ltesr = 0;
-       while (end_tick > get_ticks()) {
+       while (get_timer(time_start) < timeo) {
                ltesr = in_be32(&lbc->ltesr);
                if (ltesr & LTESR_CC)
                        break;
@@ -258,7 +246,7 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
 
 static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
 {
-       struct fsl_elbc_mtd *priv = chip->priv;
+       struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
        fsl_lbc_t *lbc = ctrl->regs;
 
@@ -291,8 +279,8 @@ static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
 static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                             int column, int page_addr)
 {
-       struct nand_chip *chip = mtd->priv;
-       struct fsl_elbc_mtd *priv = chip->priv;
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
        fsl_lbc_t *lbc = ctrl->regs;
 
@@ -501,8 +489,8 @@ static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip)
  */
 static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
 {
-       struct nand_chip *chip = mtd->priv;
-       struct fsl_elbc_mtd *priv = chip->priv;
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
        unsigned int bufsize = mtd->writesize + mtd->oobsize;
 
@@ -538,8 +526,8 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
  */
 static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
 {
-       struct nand_chip *chip = mtd->priv;
-       struct fsl_elbc_mtd *priv = chip->priv;
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
 
        /* If there are still bytes in the FCM, then use the next byte. */
@@ -555,8 +543,8 @@ static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
  */
 static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
 {
-       struct nand_chip *chip = mtd->priv;
-       struct fsl_elbc_mtd *priv = chip->priv;
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
        int avail;
 
@@ -573,45 +561,12 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
                       len, avail);
 }
 
-/*
- * Verify buffer against the FCM Controller Data Buffer
- */
-static int fsl_elbc_verify_buf(struct mtd_info *mtd,
-                              const u_char *buf, int len)
-{
-       struct nand_chip *chip = mtd->priv;
-       struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
-       int i;
-
-       if (len < 0) {
-               printf("write_buf of %d bytes", len);
-               return -EINVAL;
-       }
-
-       if ((unsigned int)len > ctrl->read_bytes - ctrl->index) {
-               printf("verify_buf beyond end of buffer "
-                      "(%d requested, %u available)\n",
-                      len, ctrl->read_bytes - ctrl->index);
-
-               ctrl->index = ctrl->read_bytes;
-               return -EINVAL;
-       }
-
-       for (i = 0; i < len; i++)
-               if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i])
-                       break;
-
-       ctrl->index += len;
-       return i == len && ctrl->status == LTESR_CC ? 0 : -EIO;
-}
-
 /* This function is called after Program and Erase Operations to
  * check for success or failure.
  */
 static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
 {
-       struct fsl_elbc_mtd *priv = chip->priv;
+       struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
        fsl_lbc_t *lbc = ctrl->regs;
 
@@ -640,9 +595,8 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
        return fsl_elbc_read_byte(mtd);
 }
 
-static int fsl_elbc_read_page(struct mtd_info *mtd,
-                             struct nand_chip *chip,
-                             uint8_t *buf, int page)
+static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+                             uint8_t *buf, int oob_required, int page)
 {
        fsl_elbc_read_buf(mtd, buf, mtd->writesize);
        fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -656,16 +610,31 @@ static int fsl_elbc_read_page(struct mtd_info *mtd,
 /* ECC will be calculated automatically, and errors will be detected in
  * waitfunc.
  */
-static void fsl_elbc_write_page(struct mtd_info *mtd,
-                               struct nand_chip *chip,
-                               const uint8_t *buf)
+static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+                               const uint8_t *buf, int oob_required,
+                               int page)
 {
        fsl_elbc_write_buf(mtd, buf, mtd->writesize);
        fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+       return 0;
 }
 
 static struct fsl_elbc_ctrl *elbc_ctrl;
 
+/* ECC will be calculated automatically, and errors will be detected in
+ * waitfunc.
+ */
+static int fsl_elbc_write_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+                               uint32_t offset, uint32_t data_len,
+                               const uint8_t *buf, int oob_required, int page)
+{
+       fsl_elbc_write_buf(mtd, buf, mtd->writesize);
+       fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+       return 0;
+}
+
 static void fsl_elbc_ctrl_init(void)
 {
        elbc_ctrl = kzalloc(sizeof(*elbc_ctrl), GFP_KERNEL);
@@ -688,7 +657,7 @@ static void fsl_elbc_ctrl_init(void)
 
 static int fsl_elbc_chip_init(int devnum, u8 *addr)
 {
-       struct mtd_info *mtd = &nand_info[devnum];
+       struct mtd_info *mtd;
        struct nand_chip *nand;
        struct fsl_elbc_mtd *priv;
        uint32_t br = 0, or = 0;
@@ -724,11 +693,12 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr)
        if (priv->bank >= MAX_BANKS) {
                printf("fsl_elbc_nand: address did not match any "
                       "chip selects\n");
+               kfree(priv);
                return -ENODEV;
        }
 
        nand = &priv->chip;
-       mtd->priv = nand;
+       mtd = nand_to_mtd(nand);
 
        elbc_ctrl->chips[priv->bank] = priv;
 
@@ -737,7 +707,6 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr)
        nand->read_byte = fsl_elbc_read_byte;
        nand->write_buf = fsl_elbc_write_buf;
        nand->read_buf = fsl_elbc_read_buf;
-       nand->verify_buf = fsl_elbc_verify_buf;
        nand->select_chip = fsl_elbc_select_chip;
        nand->cmdfunc = fsl_elbc_cmdfunc;
        nand->waitfunc = fsl_elbc_wait;
@@ -747,29 +716,18 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr)
        nand->bbt_md = &bbt_mirror_descr;
 
        /* set up nand options */
-       nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
-                       NAND_USE_FLASH_BBT;
+       nand->options = NAND_NO_SUBPAGE_WRITE;
+       nand->bbt_options = NAND_BBT_USE_FLASH;
 
        nand->controller = &elbc_ctrl->controller;
-       nand->priv = priv;
+       nand_set_controller_data(nand, priv);
 
        nand->ecc.read_page = fsl_elbc_read_page;
        nand->ecc.write_page = fsl_elbc_write_page;
+       nand->ecc.write_subpage = fsl_elbc_write_subpage;
 
-#ifdef CONFIG_FSL_ELBC_FMR
-       priv->fmr = CONFIG_FSL_ELBC_FMR;
-#else
        priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT);
 
-       /*
-        * Hardware expects small page has ECCM0, large page has ECCM1
-        * when booting from NAND.  Board config can override if not
-        * booting from NAND.
-        */
-       if (or & OR_FCM_PGS)
-               priv->fmr |= FMR_ECCM;
-#endif
-
        /* If CS Base Register selects full hardware ECC then use it */
        if ((br & BR_DECC) == BR_DECC_CHK_GEN) {
                nand->ecc.mode = NAND_ECC_HW;
@@ -781,16 +739,36 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr)
                nand->ecc.size = 512;
                nand->ecc.bytes = 3;
                nand->ecc.steps = 1;
+               nand->ecc.strength = 1;
        } else {
-               /* otherwise fall back to default software ECC */
+               /* otherwise fall back to software ECC */
+#if defined(CONFIG_NAND_ECC_BCH)
+               nand->ecc.mode = NAND_ECC_SOFT_BCH;
+#else
                nand->ecc.mode = NAND_ECC_SOFT;
+#endif
        }
 
+       ret = nand_scan_ident(mtd, 1, NULL);
+       if (ret)
+               return ret;
+
        /* Large-page-specific setup */
-       if (or & OR_FCM_PGS) {
+       if (mtd->writesize == 2048) {
+               setbits_be32(&elbc_ctrl->regs->bank[priv->bank].or,
+                            OR_FCM_PGS);
+               in_be32(&elbc_ctrl->regs->bank[priv->bank].or);
+
                priv->page_size = 1;
                nand->badblock_pattern = &largepage_memorybased;
 
+               /*
+                * Hardware expects small page has ECCM0, large page has
+                * ECCM1 when booting from NAND, and we follow that even
+                * when not booting from NAND.
+                */
+               priv->fmr |= FMR_ECCM;
+
                /* adjust ecc setup if needed */
                if ((br & BR_DECC) == BR_DECC_CHK_GEN) {
                        nand->ecc.steps = 4;
@@ -798,17 +776,19 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr)
                                           &fsl_elbc_oob_lp_eccm1 :
                                           &fsl_elbc_oob_lp_eccm0;
                }
+       } else if (mtd->writesize == 512) {
+               clrbits_be32(&elbc_ctrl->regs->bank[priv->bank].or,
+                            OR_FCM_PGS);
+               in_be32(&elbc_ctrl->regs->bank[priv->bank].or);
+       } else {
+               return -ENODEV;
        }
 
-       ret = nand_scan_ident(mtd, 1, NULL);
-       if (ret)
-               return ret;
-
        ret = nand_scan_tail(mtd);
        if (ret)
                return ret;
 
-       ret = nand_register(devnum);
+       ret = nand_register(devnum, mtd);
        if (ret)
                return ret;