]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/mtd/nand/fsl_elbc_nand.c
Merge branch 'master' of git://git.denx.de/u-boot-spi
[karo-tx-uboot.git] / drivers / mtd / nand / fsl_elbc_nand.c
index 0bd1bdbcf00c4c45254774992fd761a4cd8e1925..0fa776ae91947541cfde27385b5b852403512122 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <common.h>
 #include <malloc.h>
+#include <nand.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
@@ -57,7 +58,6 @@ struct fsl_elbc_ctrl;
 /* mtd information per set */
 
 struct fsl_elbc_mtd {
-       struct mtd_info mtd;
        struct nand_chip chip;
        struct fsl_elbc_ctrl *ctrl;
 
@@ -75,7 +75,7 @@ struct fsl_elbc_ctrl {
        struct fsl_elbc_mtd *chips[MAX_BANKS];
 
        /* device info */
-       lbus83xx_t *regs;
+       fsl_lbc_t *regs;
        u8 __iomem *addr;        /* Address of assigned FCM buffer        */
        unsigned int page;       /* Last page written to / read from      */
        unsigned int read_bytes; /* Number of bytes read during command   */
@@ -85,7 +85,6 @@ struct fsl_elbc_ctrl {
        unsigned int mdr;        /* UPM/FCM Data Register value           */
        unsigned int use_mdr;    /* Non zero if the MDR is to be set      */
        unsigned int oob;        /* Non zero if operating on OOB data     */
-       uint8_t *oob_poi;        /* Place to write ECC after read back    */
 };
 
 /* These map to the positions used by the FCM hardware ECC generator */
@@ -95,7 +94,6 @@ static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = {
        .eccbytes = 3,
        .eccpos = {6, 7, 8},
        .oobfree = { {0, 5}, {9, 7} },
-       .oobavail = 12,
 };
 
 /* Small Page FLASH with FMR[ECCM] = 1 */
@@ -103,7 +101,6 @@ static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = {
        .eccbytes = 3,
        .eccpos = {8, 9, 10},
        .oobfree = { {0, 5}, {6, 2}, {11, 5} },
-       .oobavail = 12,
 };
 
 /* Large Page FLASH with FMR[ECCM] = 0 */
@@ -111,7 +108,6 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = {
        .eccbytes = 12,
        .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56},
        .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} },
-       .oobavail = 48,
 };
 
 /* Large Page FLASH with FMR[ECCM] = 1 */
@@ -119,7 +115,48 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = {
        .eccbytes = 12,
        .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58},
        .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} },
-       .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,
 };
 
 /*=================================*/
@@ -133,7 +170,7 @@ 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 fsl_elbc_ctrl *ctrl = priv->ctrl;
-       lbus83xx_t *lbc = ctrl->regs;
+       fsl_lbc_t *lbc = ctrl->regs;
        int buf_num;
 
        ctrl->page = page_addr;
@@ -141,14 +178,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;
        }
 
@@ -173,7 +210,7 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
        struct nand_chip *chip = mtd->priv;
        struct fsl_elbc_mtd *priv = chip->priv;
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
-       lbus83xx_t *lbc = ctrl->regs;
+       fsl_lbc_t *lbc = ctrl->regs;
        long long end_tick;
        u32 ltesr;
 
@@ -223,28 +260,28 @@ static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
 {
        struct fsl_elbc_mtd *priv = chip->priv;
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
-       lbus83xx_t *lbc = ctrl->regs;
+       fsl_lbc_t *lbc = ctrl->regs;
 
        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,12 +289,12 @@ 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;
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
-       lbus83xx_t *lbc = ctrl->regs;
+       fsl_lbc_t *lbc = ctrl->regs;
 
        ctrl->use_mdr = 0;
 
@@ -303,18 +340,21 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
        /* READID must read all 5 possible bytes while CEB is active */
        case NAND_CMD_READID:
-               vdbg("fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
+       case NAND_CMD_PARAM:
+               vdbg("fsl_elbc_cmdfunc: NAND_CMD 0x%x.\n", command);
 
                out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_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);
-               ctrl->read_bytes = 5;
+                                   (FIR_OP_UA  << FIR_OP1_SHIFT) |
+                                   (FIR_OP_RBW << FIR_OP2_SHIFT));
+               out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT);
+               /*
+                * although currently it's 8 bytes for READID, we always read
+                * the maximum 256 bytes(for PARAM)
+                */
+               out_be32(&lbc->fbcr, 256);
+               ctrl->read_bytes = 256;
                ctrl->use_mdr = 1;
-               ctrl->mdr = 0;
-
+               ctrl->mdr = column;
                set_addr(mtd, 0, 0, 0);
                fsl_elbc_run_command(mtd);
                return;
@@ -331,13 +371,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 +400,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 */
@@ -398,7 +438,6 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
 
        /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
        case NAND_CMD_PAGEPROG: {
-               int full_page;
                vdbg("fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
                     "writing %d bytes.\n", ctrl->index);
 
@@ -407,34 +446,13 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
                 * write so the HW generates the ECC.
                 */
                if (ctrl->oob || ctrl->column != 0 ||
-                   ctrl->index != mtd->writesize + mtd->oobsize) {
+                   ctrl->index != mtd->writesize + mtd->oobsize)
                        out_be32(&lbc->fbcr, ctrl->index);
-                       full_page = 0;
-               } else {
+               else
                        out_be32(&lbc->fbcr, 0);
-                       full_page = 1;
-               }
 
                fsl_elbc_run_command(mtd);
 
-               /* Read back the page in order to fill in the ECC for the
-                * caller.  Is this really needed?
-                */
-               if (full_page && ctrl->oob_poi) {
-                       out_be32(&lbc->fbcr, 3);
-                       set_addr(mtd, 6, page_addr, 1);
-
-                       ctrl->read_bytes = mtd->writesize + 9;
-
-                       fsl_elbc_do_read(chip, 1);
-                       fsl_elbc_run_command(mtd);
-
-                       memcpy_fromio(ctrl->oob_poi + 6,
-                                     &ctrl->addr[ctrl->index], 3);
-                       ctrl->index += 3;
-               }
-
-               ctrl->oob_poi = NULL;
                return;
        }
 
@@ -442,8 +460,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 +485,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 +577,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;
@@ -595,7 +613,7 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
 {
        struct fsl_elbc_mtd *priv = chip->priv;
        struct fsl_elbc_ctrl *ctrl = priv->ctrl;
-       lbus83xx_t *lbc = ctrl->regs;
+       fsl_lbc_t *lbc = ctrl->regs;
 
        if (ctrl->status != LTESR_CC)
                return NAND_STATUS_FAIL;
@@ -603,8 +621,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);
@@ -622,9 +640,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)
+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);
@@ -638,30 +655,24 @@ 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)
 {
-       struct fsl_elbc_mtd *priv = chip->priv;
-       struct fsl_elbc_ctrl *ctrl = priv->ctrl;
-
        fsl_elbc_write_buf(mtd, buf, mtd->writesize);
        fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
 
-       ctrl->oob_poi = chip->oob_poi;
+       return 0;
 }
 
 static struct fsl_elbc_ctrl *elbc_ctrl;
 
 static void fsl_elbc_ctrl_init(void)
 {
-       immap_t *im = (immap_t *)CFG_IMMR;
-
        elbc_ctrl = kzalloc(sizeof(*elbc_ctrl), GFP_KERNEL);
        if (!elbc_ctrl)
                return;
 
-       elbc_ctrl->regs = &im->lbus;
+       elbc_ctrl->regs = LBC_BASE_ADDR;
 
        /* clear event registers */
        out_be32(&elbc_ctrl->regs->ltesr, LTESR_NAND_MASK);
@@ -675,10 +686,13 @@ static void fsl_elbc_ctrl_init(void)
        elbc_ctrl->addr = NULL;
 }
 
-int board_nand_init(struct nand_chip *nand)
+static int fsl_elbc_chip_init(int devnum, u8 *addr)
 {
+       struct mtd_info *mtd = &nand_info[devnum];
+       struct nand_chip *nand;
        struct fsl_elbc_mtd *priv;
-       uint32_t br, or;
+       uint32_t br = 0, or = 0;
+       int ret;
 
        if (!elbc_ctrl) {
                fsl_elbc_ctrl_init();
@@ -691,17 +705,19 @@ int board_nand_init(struct nand_chip *nand)
                return -ENOMEM;
 
        priv->ctrl = elbc_ctrl;
-       priv->vbase = nand->IO_ADDR_R;
+       priv->vbase = addr;
 
        /* Find which chip select it is connected to.  It'd be nice
         * if we could pass more than one datum to the NAND driver...
         */
        for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) {
+               phys_addr_t phys_addr = virt_to_phys(addr);
+
                br = in_be32(&elbc_ctrl->regs->bank[priv->bank].br);
                or = in_be32(&elbc_ctrl->regs->bank[priv->bank].or);
 
                if ((br & BR_V) && (br & BR_MSEL) == BR_MS_FCM &&
-                   (br & or & BR_BA) == (phys_addr_t)nand->IO_ADDR_R)
+                   (br & or & BR_BA) == BR_PHYS_ADDR(phys_addr))
                        break;
        }
 
@@ -711,6 +727,9 @@ int board_nand_init(struct nand_chip *nand)
                return -ENODEV;
        }
 
+       nand = &priv->chip;
+       mtd->priv = nand;
+
        elbc_ctrl->chips[priv->bank] = priv;
 
        /* fill in nand_chip structure */
@@ -724,7 +743,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_SUBPAGE_WRITE;
+       nand->bbt_options = NAND_BBT_USE_FLASH;
 
        nand->controller = &elbc_ctrl->controller;
        nand->priv = priv;
@@ -732,36 +756,82 @@ int board_nand_init(struct nand_chip *nand)
        nand->ecc.read_page = fsl_elbc_read_page;
        nand->ecc.write_page = fsl_elbc_write_page;
 
+       priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT);
+
        /* 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;
 
                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;
                nand->ecc.steps = 1;
+               nand->ecc.strength = 1;
        } else {
                /* otherwise fall back to default software ECC */
                nand->ecc.mode = NAND_ECC_SOFT;
        }
 
-       priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT);
+       ret = nand_scan_ident(mtd, 1, NULL);
+       if (ret)
+               return ret;
+
+       /* Large-page-specific setup */
+       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);
 
-       /* adjust Option Register and ECC to match Flash page size */
-       if (or & OR_FCM_PGS) {
                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;
                        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;
                }
+       } 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_tail(mtd);
+       if (ret)
+               return ret;
+
+       ret = nand_register(devnum);
+       if (ret)
+               return ret;
+
        return 0;
 }
+
+#ifndef CONFIG_SYS_NAND_BASE_LIST
+#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
+#endif
+
+static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] =
+       CONFIG_SYS_NAND_BASE_LIST;
+
+void board_nand_init(void)
+{
+       int i;
+
+       for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
+               fsl_elbc_chip_init(i, (u8 *)base_address[i]);
+}