fsl_elbc_nand: implement support for flash-based BBT
[oweals/u-boot.git] / drivers / mtd / nand / fsl_elbc_nand.c
index 0bd1bdbcf00c4c45254774992fd761a4cd8e1925..e4e4de0870db62b2b23d08fed7cb94ea123afa08 100644 (file)
@@ -122,6 +122,48 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = {
        .oobavail = 48,
 };
 
+/*
+ * fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset
+ * 1, so we have to adjust bad block pattern. This pattern should be used for
+ * x8 chips only. So far hardware does not support x16 chips anyway.
+ */
+static u8 scan_ff_pattern[] = { 0xff, };
+
+static struct nand_bbt_descr largepage_memorybased = {
+       .options = 0,
+       .offs = 0,
+       .len = 1,
+       .pattern = scan_ff_pattern,
+};
+
+/*
+ * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt,
+ * interfere with ECC positions, that's why we implement our own descriptors.
+ * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0.
+ */
+static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
+static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+                  NAND_BBT_2BIT | NAND_BBT_VERSION,
+       .offs = 11,
+       .len = 4,
+       .veroffs = 15,
+       .maxblocks = 4,
+       .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+                  NAND_BBT_2BIT | NAND_BBT_VERSION,
+       .offs = 11,
+       .len = 4,
+       .veroffs = 15,
+       .maxblocks = 4,
+       .pattern = mirror_pattern,
+};
+
 /*=================================*/
 
 /*
@@ -141,14 +183,14 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
        if (priv->page_size) {
                out_be32(&lbc->fbar, page_addr >> 6);
                out_be32(&lbc->fpar,
-                        ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) |
-                        (oob ? FPAR_LP_MS : 0) | column);
+                        ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) |
+                        (oob ? FPAR_LP_MS : 0) | column);
                buf_num = (page_addr & 1) << 2;
        } else {
                out_be32(&lbc->fbar, page_addr >> 5);
                out_be32(&lbc->fpar,
-                        ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) |
-                        (oob ? FPAR_SP_MS : 0) | column);
+                        ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) |
+                        (oob ? FPAR_SP_MS : 0) | column);
                buf_num = page_addr & 7;
        }
 
@@ -227,24 +269,24 @@ static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
 
        if (priv->page_size) {
                out_be32(&lbc->fir,
-                        (FIR_OP_CW0 << FIR_OP0_SHIFT) |
-                        (FIR_OP_CA  << FIR_OP1_SHIFT) |
-                        (FIR_OP_PA  << FIR_OP2_SHIFT) |
-                        (FIR_OP_CW1 << FIR_OP3_SHIFT) |
-                        (FIR_OP_RBW << FIR_OP4_SHIFT));
+                        (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+                        (FIR_OP_CA  << FIR_OP1_SHIFT) |
+                        (FIR_OP_PA  << FIR_OP2_SHIFT) |
+                        (FIR_OP_CW1 << FIR_OP3_SHIFT) |
+                        (FIR_OP_RBW << FIR_OP4_SHIFT));
 
                out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
-                                   (NAND_CMD_READSTART << FCR_CMD1_SHIFT));
+                                   (NAND_CMD_READSTART << FCR_CMD1_SHIFT));
        } else {
                out_be32(&lbc->fir,
-                        (FIR_OP_CW0 << FIR_OP0_SHIFT) |
-                        (FIR_OP_CA  << FIR_OP1_SHIFT) |
-                        (FIR_OP_PA  << FIR_OP2_SHIFT) |
-                        (FIR_OP_RBW << FIR_OP3_SHIFT));
+                        (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+                        (FIR_OP_CA  << FIR_OP1_SHIFT) |
+                        (FIR_OP_PA  << FIR_OP2_SHIFT) |
+                        (FIR_OP_RBW << FIR_OP3_SHIFT));
 
                if (oob)
                        out_be32(&lbc->fcr,
-                                NAND_CMD_READOOB << FCR_CMD0_SHIFT);
+                                NAND_CMD_READOOB << FCR_CMD0_SHIFT);
                else
                        out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
        }
@@ -252,7 +294,7 @@ static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
 
 /* cmdfunc send commands to the FCM */
 static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
-                             int column, int page_addr)
+                            int column, int page_addr)
 {
        struct nand_chip *chip = mtd->priv;
        struct fsl_elbc_mtd *priv = chip->priv;
@@ -306,8 +348,8 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                vdbg("fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
 
                out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) |
-                                   (FIR_OP_UA  << FIR_OP1_SHIFT) |
-                                   (FIR_OP_RBW << FIR_OP2_SHIFT));
+                                   (FIR_OP_UA  << FIR_OP1_SHIFT) |
+                                   (FIR_OP_RBW << FIR_OP2_SHIFT));
                out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT);
                /* 5 bytes for manuf, device and exts */
                out_be32(&lbc->fbcr, 5);
@@ -331,13 +373,13 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                vdbg("fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
 
                out_be32(&lbc->fir,
-                        (FIR_OP_CW0 << FIR_OP0_SHIFT) |
-                        (FIR_OP_PA  << FIR_OP1_SHIFT) |
-                        (FIR_OP_CM1 << FIR_OP2_SHIFT));
+                        (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+                        (FIR_OP_PA  << FIR_OP1_SHIFT) |
+                        (FIR_OP_CM1 << FIR_OP2_SHIFT));
 
                out_be32(&lbc->fcr,
-                        (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
-                        (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT));
+                        (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
+                        (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT));
 
                out_be32(&lbc->fbcr, 0);
                ctrl->read_bytes = 0;
@@ -360,22 +402,22 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                              (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT);
 
                        out_be32(&lbc->fir,
-                                (FIR_OP_CW0 << FIR_OP0_SHIFT) |
-                                (FIR_OP_CA  << FIR_OP1_SHIFT) |
-                                (FIR_OP_PA  << FIR_OP2_SHIFT) |
-                                (FIR_OP_WB  << FIR_OP3_SHIFT) |
-                                (FIR_OP_CW1 << FIR_OP4_SHIFT));
+                                (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+                                (FIR_OP_CA  << FIR_OP1_SHIFT) |
+                                (FIR_OP_PA  << FIR_OP2_SHIFT) |
+                                (FIR_OP_WB  << FIR_OP3_SHIFT) |
+                                (FIR_OP_CW1 << FIR_OP4_SHIFT));
                } else {
                        fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) |
                              (NAND_CMD_SEQIN << FCR_CMD2_SHIFT);
 
                        out_be32(&lbc->fir,
-                                (FIR_OP_CW0 << FIR_OP0_SHIFT) |
-                                (FIR_OP_CM2 << FIR_OP1_SHIFT) |
-                                (FIR_OP_CA  << FIR_OP2_SHIFT) |
-                                (FIR_OP_PA  << FIR_OP3_SHIFT) |
-                                (FIR_OP_WB  << FIR_OP4_SHIFT) |
-                                (FIR_OP_CW1 << FIR_OP5_SHIFT));
+                                (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+                                (FIR_OP_CM2 << FIR_OP1_SHIFT) |
+                                (FIR_OP_CA  << FIR_OP2_SHIFT) |
+                                (FIR_OP_PA  << FIR_OP3_SHIFT) |
+                                (FIR_OP_WB  << FIR_OP4_SHIFT) |
+                                (FIR_OP_CW1 << FIR_OP5_SHIFT));
 
                        if (column >= mtd->writesize) {
                                /* OOB area --> READOOB */
@@ -430,7 +472,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                        fsl_elbc_run_command(mtd);
 
                        memcpy_fromio(ctrl->oob_poi + 6,
-                                     &ctrl->addr[ctrl->index], 3);
+                                     &ctrl->addr[ctrl->index], 3);
                        ctrl->index += 3;
                }
 
@@ -442,8 +484,8 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
        /* Note - it does not wait for the ready line */
        case NAND_CMD_STATUS:
                out_be32(&lbc->fir,
-                        (FIR_OP_CM0 << FIR_OP0_SHIFT) |
-                        (FIR_OP_RBW << FIR_OP1_SHIFT));
+                        (FIR_OP_CM0 << FIR_OP0_SHIFT) |
+                        (FIR_OP_RBW << FIR_OP1_SHIFT));
                out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
                out_be32(&lbc->fbcr, 1);
                set_addr(mtd, 0, 0, 0);
@@ -467,7 +509,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
        default:
                printf("fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
-                       command);
+                       command);
        }
 }
 
@@ -559,7 +601,7 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
  * Verify buffer against the FCM Controller Data Buffer
  */
 static int fsl_elbc_verify_buf(struct mtd_info *mtd,
-                               const u_char *buf, int len)
+                              const u_char *buf, int len)
 {
        struct nand_chip *chip = mtd->priv;
        struct fsl_elbc_mtd *priv = chip->priv;
@@ -603,8 +645,8 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
        /* Use READ_STATUS command, but wait for the device to be ready */
        ctrl->use_mdr = 0;
        out_be32(&lbc->fir,
-                (FIR_OP_CW0 << FIR_OP0_SHIFT) |
-                (FIR_OP_RBW << FIR_OP1_SHIFT));
+                (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+                (FIR_OP_RBW << FIR_OP1_SHIFT));
        out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
        out_be32(&lbc->fbcr, 1);
        set_addr(mtd, 0, 0, 0);
@@ -623,8 +665,8 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
 }
 
 static int fsl_elbc_read_page(struct mtd_info *mtd,
-                              struct nand_chip *chip,
-                              uint8_t *buf)
+                             struct nand_chip *chip,
+                             uint8_t *buf)
 {
        fsl_elbc_read_buf(mtd, buf, mtd->writesize);
        fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -639,8 +681,8 @@ static int fsl_elbc_read_page(struct mtd_info *mtd,
  * waitfunc.
  */
 static void fsl_elbc_write_page(struct mtd_info *mtd,
-                                struct nand_chip *chip,
-                                const uint8_t *buf)
+                               struct nand_chip *chip,
+                               const uint8_t *buf)
 {
        struct fsl_elbc_mtd *priv = chip->priv;
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
@@ -724,7 +766,12 @@ int board_nand_init(struct nand_chip *nand)
        nand->waitfunc = fsl_elbc_wait;
 
        /* set up nand options */
-       nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR;
+       nand->bbt_td = &bbt_main_descr;
+       nand->bbt_md = &bbt_mirror_descr;
+
+       /* set up nand options */
+       nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
+                       NAND_USE_FLASH_BBT;
 
        nand->controller = &elbc_ctrl->controller;
        nand->priv = priv;
@@ -737,8 +784,8 @@ int board_nand_init(struct nand_chip *nand)
                nand->ecc.mode = NAND_ECC_HW;
 
                nand->ecc.layout = (priv->fmr & FMR_ECCM) ?
-                                  &fsl_elbc_oob_sp_eccm1 :
-                                  &fsl_elbc_oob_sp_eccm0;
+                                  &fsl_elbc_oob_sp_eccm1 :
+                                  &fsl_elbc_oob_sp_eccm0;
 
                nand->ecc.size = 512;
                nand->ecc.bytes = 3;
@@ -750,16 +797,17 @@ int board_nand_init(struct nand_chip *nand)
 
        priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT);
 
-       /* adjust Option Register and ECC to match Flash page size */
+       /* Large-page-specific setup */
        if (or & OR_FCM_PGS) {
                priv->page_size = 1;
+               nand->badblock_pattern = &largepage_memorybased;
 
                /* adjust ecc setup if needed */
                if ((br & BR_DECC) == BR_DECC_CHK_GEN) {
                        nand->ecc.steps = 4;
                        nand->ecc.layout = (priv->fmr & FMR_ECCM) ?
-                                          &fsl_elbc_oob_lp_eccm1 :
-                                          &fsl_elbc_oob_lp_eccm0;
+                                          &fsl_elbc_oob_lp_eccm1 :
+                                          &fsl_elbc_oob_lp_eccm0;
                }
        }