Merge with /home/mk/git/u-boot-generic_ohci#generic_ohci
[oweals/u-boot.git] / drivers / nand / nand_util.c
1 /*
2  * drivers/nand/nand_util.c
3  *
4  * Copyright (C) 2006 by Weiss-Electronic GmbH.
5  * All rights reserved.
6  *
7  * @author:     Guido Classen <clagix@gmail.com>
8  * @descr:      NAND Flash support
9  * @references: borrowed heavily from Linux mtd-utils code:
10  *              flash_eraseall.c by Arcom Control System Ltd
11  *              nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com)
12  *                             and Thomas Gleixner (tglx@linutronix.de)
13  *
14  * See file CREDITS for list of people who contributed to this
15  * project.
16  *
17  * This program is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU General Public License version
19  * 2 as published by the Free Software Foundation.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
29  * MA 02111-1307 USA
30  *
31  */
32
33 #include <common.h>
34
35 #if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
36
37 #include <command.h>
38 #include <watchdog.h>
39 #include <malloc.h>
40
41 #include <nand.h>
42 #include <jffs2/jffs2.h>
43
44 typedef struct erase_info erase_info_t;
45 typedef struct mtd_info   mtd_info_t;
46
47 /* support only for native endian JFFS2 */
48 #define cpu_to_je16(x) (x)
49 #define cpu_to_je32(x) (x)
50
51 /*****************************************************************************/
52 static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip)
53 {
54         return 0;
55 }
56
57 /**
58  * nand_erase_opts: - erase NAND flash with support for various options
59  *                    (jffs2 formating)
60  *
61  * @param meminfo       NAND device to erase
62  * @param opts          options,  @see struct nand_erase_options
63  * @return              0 in case of success
64  *
65  * This code is ported from flash_eraseall.c from Linux mtd utils by
66  * Arcom Control System Ltd.
67  */
68 int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
69 {
70         struct jffs2_unknown_node cleanmarker;
71         int clmpos = 0;
72         int clmlen = 8;
73         erase_info_t erase;
74         ulong erase_length;
75         int isNAND;
76         int bbtest = 1;
77         int result;
78         int percent_complete = -1;
79         int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL;
80         const char *mtd_device = meminfo->name;
81
82         memset(&erase, 0, sizeof(erase));
83
84         erase.mtd = meminfo;
85         erase.len  = meminfo->erasesize;
86         erase.addr = opts->offset;
87         erase_length = opts->length;
88
89         isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0;
90
91         if (opts->jffs2) {
92                 cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
93                 cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
94                 if (isNAND) {
95                         struct nand_oobinfo *oobinfo = &meminfo->oobinfo;
96
97                         /* check for autoplacement */
98                         if (oobinfo->useecc == MTD_NANDECC_AUTOPLACE) {
99                                 /* get the position of the free bytes */
100                                 if (!oobinfo->oobfree[0][1]) {
101                                         printf(" Eeep. Autoplacement selected "
102                                                "and no empty space in oob\n");
103                                         return -1;
104                                 }
105                                 clmpos = oobinfo->oobfree[0][0];
106                                 clmlen = oobinfo->oobfree[0][1];
107                                 if (clmlen > 8)
108                                         clmlen = 8;
109                         } else {
110                                 /* legacy mode */
111                                 switch (meminfo->oobsize) {
112                                 case 8:
113                                         clmpos = 6;
114                                         clmlen = 2;
115                                         break;
116                                 case 16:
117                                         clmpos = 8;
118                                         clmlen = 8;
119                                         break;
120                                 case 64:
121                                         clmpos = 16;
122                                         clmlen = 8;
123                                         break;
124                                 }
125                         }
126
127                         cleanmarker.totlen = cpu_to_je32(8);
128                 } else {
129                         cleanmarker.totlen =
130                                 cpu_to_je32(sizeof(struct jffs2_unknown_node));
131                 }
132                 cleanmarker.hdr_crc =  cpu_to_je32(
133                         crc32_no_comp(0, (unsigned char *) &cleanmarker,
134                                       sizeof(struct jffs2_unknown_node) - 4));
135         }
136
137         /* scrub option allows to erase badblock. To prevent internal
138          * check from erase() method, set block check method to dummy
139          * and disable bad block table while erasing.
140          */
141         if (opts->scrub) {
142                 struct nand_chip *priv_nand = meminfo->priv;
143
144                 nand_block_bad_old = priv_nand->block_bad;
145                 priv_nand->block_bad = nand_block_bad_scrub;
146                 /* we don't need the bad block table anymore...
147                  * after scrub, there are no bad blocks left!
148                  */
149                 if (priv_nand->bbt) {
150                         kfree(priv_nand->bbt);
151                 }
152                 priv_nand->bbt = NULL;
153         }
154
155         for (;
156              erase.addr < opts->offset + erase_length;
157              erase.addr += meminfo->erasesize) {
158
159                 WATCHDOG_RESET ();
160
161                 if (!opts->scrub && bbtest) {
162                         int ret = meminfo->block_isbad(meminfo, erase.addr);
163                         if (ret > 0) {
164                                 if (!opts->quiet)
165                                         printf("\rSkipping bad block at  "
166                                                "0x%08x                   "
167                                                "                         \n",
168                                                erase.addr);
169                                 continue;
170
171                         } else if (ret < 0) {
172                                 printf("\n%s: MTD get bad block failed: %d\n",
173                                        mtd_device,
174                                        ret);
175                                 return -1;
176                         }
177                 }
178
179                 result = meminfo->erase(meminfo, &erase);
180                 if (result != 0) {
181                         printf("\n%s: MTD Erase failure: %d\n",
182                                mtd_device, result);
183                         continue;
184                 }
185
186                 /* format for JFFS2 ? */
187                 if (opts->jffs2) {
188
189                         /* write cleanmarker */
190                         if (isNAND) {
191                                 size_t written;
192                                 result = meminfo->write_oob(meminfo,
193                                                             erase.addr + clmpos,
194                                                             clmlen,
195                                                             &written,
196                                                             (unsigned char *)
197                                                             &cleanmarker);
198                                 if (result != 0) {
199                                         printf("\n%s: MTD writeoob failure: %d\n",
200                                                mtd_device, result);
201                                         continue;
202                                 }
203                         } else {
204                                 printf("\n%s: this erase routine only supports"
205                                        " NAND devices!\n",
206                                        mtd_device);
207                         }
208                 }
209
210                 if (!opts->quiet) {
211                         int percent = (int)
212                                 ((unsigned long long)
213                                  (erase.addr+meminfo->erasesize-opts->offset)
214                                  * 100 / erase_length);
215
216                         /* output progress message only at whole percent
217                          * steps to reduce the number of messages printed
218                          * on (slow) serial consoles
219                          */
220                         if (percent != percent_complete) {
221                                 percent_complete = percent;
222
223                                 printf("\rErasing at 0x%x -- %3d%% complete.",
224                                        erase.addr, percent);
225
226                                 if (opts->jffs2 && result == 0)
227                                         printf(" Cleanmarker written at 0x%x.",
228                                                erase.addr);
229                         }
230                 }
231         }
232         if (!opts->quiet)
233                 printf("\n");
234
235         if (nand_block_bad_old) {
236                 struct nand_chip *priv_nand = meminfo->priv;
237
238                 priv_nand->block_bad = nand_block_bad_old;
239                 priv_nand->scan_bbt(meminfo);
240         }
241
242         return 0;
243 }
244
245 #define MAX_PAGE_SIZE   2048
246 #define MAX_OOB_SIZE    64
247
248 /*
249  * buffer array used for writing data
250  */
251 static unsigned char data_buf[MAX_PAGE_SIZE];
252 static unsigned char oob_buf[MAX_OOB_SIZE];
253
254 /* OOB layouts to pass into the kernel as default */
255 static struct nand_oobinfo none_oobinfo = {
256         .useecc = MTD_NANDECC_OFF,
257 };
258
259 static struct nand_oobinfo jffs2_oobinfo = {
260         .useecc = MTD_NANDECC_PLACE,
261         .eccbytes = 6,
262         .eccpos = { 0, 1, 2, 3, 6, 7 }
263 };
264
265 static struct nand_oobinfo yaffs_oobinfo = {
266         .useecc = MTD_NANDECC_PLACE,
267         .eccbytes = 6,
268         .eccpos = { 8, 9, 10, 13, 14, 15}
269 };
270
271 static struct nand_oobinfo autoplace_oobinfo = {
272         .useecc = MTD_NANDECC_AUTOPLACE
273 };
274
275 /**
276  * nand_write_opts: - write image to NAND flash with support for various options
277  *
278  * @param meminfo       NAND device to erase
279  * @param opts          write options (@see nand_write_options)
280  * @return              0 in case of success
281  *
282  * This code is ported from nandwrite.c from Linux mtd utils by
283  * Steven J. Hill and Thomas Gleixner.
284  */
285 int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
286 {
287         int imglen = 0;
288         int pagelen;
289         int baderaseblock;
290         int blockstart = -1;
291         loff_t offs;
292         int readlen;
293         int oobinfochanged = 0;
294         int percent_complete = -1;
295         struct nand_oobinfo old_oobinfo;
296         ulong mtdoffset = opts->offset;
297         ulong erasesize_blockalign;
298         u_char *buffer = opts->buffer;
299         size_t written;
300         int result;
301
302         if (opts->pad && opts->writeoob) {
303                 printf("Can't pad when oob data is present.\n");
304                 return -1;
305         }
306
307         /* set erasesize to specified number of blocks - to match
308          * jffs2 (virtual) block size */
309         if (opts->blockalign == 0) {
310                 erasesize_blockalign = meminfo->erasesize;
311         } else {
312                 erasesize_blockalign = meminfo->erasesize * opts->blockalign;
313         }
314
315         /* make sure device page sizes are valid */
316         if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512)
317             && !(meminfo->oobsize == 8 && meminfo->oobblock == 256)
318             && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) {
319                 printf("Unknown flash (not normal NAND)\n");
320                 return -1;
321         }
322
323         /* read the current oob info */
324         memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo));
325
326         /* write without ecc? */
327         if (opts->noecc) {
328                 memcpy(&meminfo->oobinfo, &none_oobinfo,
329                        sizeof(meminfo->oobinfo));
330                 oobinfochanged = 1;
331         }
332
333         /* autoplace ECC? */
334         if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {
335
336                 memcpy(&meminfo->oobinfo, &autoplace_oobinfo,
337                        sizeof(meminfo->oobinfo));
338                 oobinfochanged = 1;
339         }
340
341         /* force OOB layout for jffs2 or yaffs? */
342         if (opts->forcejffs2 || opts->forceyaffs) {
343                 struct nand_oobinfo *oobsel =
344                         opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo;
345
346                 if (meminfo->oobsize == 8) {
347                         if (opts->forceyaffs) {
348                                 printf("YAFSS cannot operate on "
349                                        "256 Byte page size\n");
350                                 goto restoreoob;
351                         }
352                         /* Adjust number of ecc bytes */
353                         jffs2_oobinfo.eccbytes = 3;
354                 }
355
356                 memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo));
357         }
358
359         /* get image length */
360         imglen = opts->length;
361         pagelen = meminfo->oobblock
362                 + ((opts->writeoob != 0) ? meminfo->oobsize : 0);
363
364         /* check, if file is pagealigned */
365         if ((!opts->pad) && ((imglen % pagelen) != 0)) {
366                 printf("Input block length is not page aligned\n");
367                 goto restoreoob;
368         }
369
370         /* check, if length fits into device */
371         if (((imglen / pagelen) * meminfo->oobblock)
372              > (meminfo->size - opts->offset)) {
373                 printf("Image %d bytes, NAND page %d bytes, "
374                        "OOB area %u bytes, device size %u bytes\n",
375                        imglen, pagelen, meminfo->oobblock, meminfo->size);
376                 printf("Input block does not fit into device\n");
377                 goto restoreoob;
378         }
379
380         if (!opts->quiet)
381                 printf("\n");
382
383         /* get data from input and write to the device */
384         while (imglen && (mtdoffset < meminfo->size)) {
385
386                 WATCHDOG_RESET ();
387
388                 /*
389                  * new eraseblock, check for bad block(s). Stay in the
390                  * loop to be sure if the offset changes because of
391                  * a bad block, that the next block that will be
392                  * written to is also checked. Thus avoiding errors if
393                  * the block(s) after the skipped block(s) is also bad
394                  * (number of blocks depending on the blockalign
395                  */
396                 while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) {
397                         blockstart = mtdoffset & (~erasesize_blockalign+1);
398                         offs = blockstart;
399                         baderaseblock = 0;
400
401                         /* check all the blocks in an erase block for
402                          * bad blocks */
403                         do {
404                                 int ret = meminfo->block_isbad(meminfo, offs);
405
406                                 if (ret < 0) {
407                                         printf("Bad block check failed\n");
408                                         goto restoreoob;
409                                 }
410                                 if (ret == 1) {
411                                         baderaseblock = 1;
412                                         if (!opts->quiet)
413                                                 printf("\rBad block at 0x%lx "
414                                                        "in erase block from "
415                                                        "0x%x will be skipped\n",
416                                                        (long) offs,
417                                                        blockstart);
418                                 }
419
420                                 if (baderaseblock) {
421                                         mtdoffset = blockstart
422                                                 + erasesize_blockalign;
423                                 }
424                                 offs +=  erasesize_blockalign
425                                         / opts->blockalign;
426                         } while (offs < blockstart + erasesize_blockalign);
427                 }
428
429                 readlen = meminfo->oobblock;
430                 if (opts->pad && (imglen < readlen)) {
431                         readlen = imglen;
432                         memset(data_buf + readlen, 0xff,
433                                meminfo->oobblock - readlen);
434                 }
435
436                 /* read page data from input memory buffer */
437                 memcpy(data_buf, buffer, readlen);
438                 buffer += readlen;
439
440                 if (opts->writeoob) {
441                         /* read OOB data from input memory block, exit
442                          * on failure */
443                         memcpy(oob_buf, buffer, meminfo->oobsize);
444                         buffer += meminfo->oobsize;
445
446                         /* write OOB data first, as ecc will be placed
447                          * in there*/
448                         result = meminfo->write_oob(meminfo,
449                                                     mtdoffset,
450                                                     meminfo->oobsize,
451                                                     &written,
452                                                     (unsigned char *)
453                                                     &oob_buf);
454
455                         if (result != 0) {
456                                 printf("\nMTD writeoob failure: %d\n",
457                                        result);
458                                 goto restoreoob;
459                         }
460                         imglen -= meminfo->oobsize;
461                 }
462
463                 /* write out the page data */
464                 result = meminfo->write(meminfo,
465                                         mtdoffset,
466                                         meminfo->oobblock,
467                                         &written,
468                                         (unsigned char *) &data_buf);
469
470                 if (result != 0) {
471                         printf("writing NAND page at offset 0x%lx failed\n",
472                                mtdoffset);
473                         goto restoreoob;
474                 }
475                 imglen -= readlen;
476
477                 if (!opts->quiet) {
478                         int percent = (int)
479                                 ((unsigned long long)
480                                  (opts->length-imglen) * 100
481                                  / opts->length);
482                         /* output progress message only at whole percent
483                          * steps to reduce the number of messages printed
484                          * on (slow) serial consoles
485                          */
486                         if (percent != percent_complete) {
487                                 printf("\rWriting data at 0x%x "
488                                        "-- %3d%% complete.",
489                                        mtdoffset, percent);
490                                 percent_complete = percent;
491                         }
492                 }
493
494                 mtdoffset += meminfo->oobblock;
495         }
496
497         if (!opts->quiet)
498                 printf("\n");
499
500 restoreoob:
501         if (oobinfochanged) {
502                 memcpy(&meminfo->oobinfo, &old_oobinfo,
503                        sizeof(meminfo->oobinfo));
504         }
505
506         if (imglen > 0) {
507                 printf("Data did not fit into device, due to bad blocks\n");
508                 return -1;
509         }
510
511         /* return happy */
512         return 0;
513 }
514
515 /**
516  * nand_read_opts: - read image from NAND flash with support for various options
517  *
518  * @param meminfo       NAND device to erase
519  * @param opts          read options (@see struct nand_read_options)
520  * @return              0 in case of success
521  *
522  */
523 int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)
524 {
525         int imglen = opts->length;
526         int pagelen;
527         int baderaseblock;
528         int blockstart = -1;
529         int percent_complete = -1;
530         loff_t offs;
531         size_t readlen;
532         ulong mtdoffset = opts->offset;
533         u_char *buffer = opts->buffer;
534         int result;
535
536         /* make sure device page sizes are valid */
537         if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512)
538             && !(meminfo->oobsize == 8 && meminfo->oobblock == 256)
539             && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) {
540                 printf("Unknown flash (not normal NAND)\n");
541                 return -1;
542         }
543
544         pagelen = meminfo->oobblock
545                 + ((opts->readoob != 0) ? meminfo->oobsize : 0);
546
547         /* check, if length is not larger than device */
548         if (((imglen / pagelen) * meminfo->oobblock)
549              > (meminfo->size - opts->offset)) {
550                 printf("Image %d bytes, NAND page %d bytes, "
551                        "OOB area %u bytes, device size %u bytes\n",
552                        imglen, pagelen, meminfo->oobblock, meminfo->size);
553                 printf("Input block is larger than device\n");
554                 return -1;
555         }
556
557         if (!opts->quiet)
558                 printf("\n");
559
560         /* get data from input and write to the device */
561         while (imglen && (mtdoffset < meminfo->size)) {
562
563                 WATCHDOG_RESET ();
564
565                 /*
566                  * new eraseblock, check for bad block(s). Stay in the
567                  * loop to be sure if the offset changes because of
568                  * a bad block, that the next block that will be
569                  * written to is also checked. Thus avoiding errors if
570                  * the block(s) after the skipped block(s) is also bad
571                  * (number of blocks depending on the blockalign
572                  */
573                 while (blockstart != (mtdoffset & (~meminfo->erasesize+1))) {
574                         blockstart = mtdoffset & (~meminfo->erasesize+1);
575                         offs = blockstart;
576                         baderaseblock = 0;
577
578                         /* check all the blocks in an erase block for
579                          * bad blocks */
580                         do {
581                                 int ret = meminfo->block_isbad(meminfo, offs);
582
583                                 if (ret < 0) {
584                                         printf("Bad block check failed\n");
585                                         return -1;
586                                 }
587                                 if (ret == 1) {
588                                         baderaseblock = 1;
589                                         if (!opts->quiet)
590                                                 printf("\rBad block at 0x%lx "
591                                                        "in erase block from "
592                                                        "0x%x will be skipped\n",
593                                                        (long) offs,
594                                                        blockstart);
595                                 }
596
597                                 if (baderaseblock) {
598                                         mtdoffset = blockstart
599                                                 + meminfo->erasesize;
600                                 }
601                                 offs +=  meminfo->erasesize;
602
603                         } while (offs < blockstart + meminfo->erasesize);
604                 }
605
606
607                 /* read page data to memory buffer */
608                 result = meminfo->read(meminfo,
609                                        mtdoffset,
610                                        meminfo->oobblock,
611                                        &readlen,
612                                        (unsigned char *) &data_buf);
613
614                 if (result != 0) {
615                         printf("reading NAND page at offset 0x%lx failed\n",
616                                mtdoffset);
617                         return -1;
618                 }
619
620                 if (imglen < readlen) {
621                         readlen = imglen;
622                 }
623
624                 memcpy(buffer, data_buf, readlen);
625                 buffer += readlen;
626                 imglen -= readlen;
627
628                 if (opts->readoob) {
629                         result = meminfo->read_oob(meminfo,
630                                                    mtdoffset,
631                                                    meminfo->oobsize,
632                                                    &readlen,
633                                                    (unsigned char *)
634                                                    &oob_buf);
635
636                         if (result != 0) {
637                                 printf("\nMTD readoob failure: %d\n",
638                                        result);
639                                 return -1;
640                         }
641
642
643                         if (imglen < readlen) {
644                                 readlen = imglen;
645                         }
646
647                         memcpy(buffer, oob_buf, readlen);
648
649                         buffer += readlen;
650                         imglen -= readlen;
651                 }
652
653                 if (!opts->quiet) {
654                         int percent = (int)
655                                 ((unsigned long long)
656                                  (opts->length-imglen) * 100
657                                  / opts->length);
658                         /* output progress message only at whole percent
659                          * steps to reduce the number of messages printed
660                          * on (slow) serial consoles
661                          */
662                         if (percent != percent_complete) {
663                         if (!opts->quiet)
664                                 printf("\rReading data from 0x%x "
665                                        "-- %3d%% complete.",
666                                        mtdoffset, percent);
667                                 percent_complete = percent;
668                         }
669                 }
670
671                 mtdoffset += meminfo->oobblock;
672         }
673
674         if (!opts->quiet)
675                 printf("\n");
676
677         if (imglen > 0) {
678                 printf("Could not read entire image due to bad blocks\n");
679                 return -1;
680         }
681
682         /* return happy */
683         return 0;
684 }
685
686 /******************************************************************************
687  * Support for locking / unlocking operations of some NAND devices
688  *****************************************************************************/
689
690 #define NAND_CMD_LOCK           0x2a
691 #define NAND_CMD_LOCK_TIGHT     0x2c
692 #define NAND_CMD_UNLOCK1        0x23
693 #define NAND_CMD_UNLOCK2        0x24
694 #define NAND_CMD_LOCK_STATUS    0x7a
695
696 /**
697  * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT
698  *            state
699  *
700  * @param meminfo       nand mtd instance
701  * @param tight         bring device in lock tight mode
702  *
703  * @return              0 on success, -1 in case of error
704  *
705  * The lock / lock-tight command only applies to the whole chip. To get some
706  * parts of the chip lock and others unlocked use the following sequence:
707  *
708  * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin)
709  * - Call nand_unlock() once for each consecutive area to be unlocked
710  * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1)
711  *
712  *   If the device is in lock-tight state software can't change the
713  *   current active lock/unlock state of all pages. nand_lock() / nand_unlock()
714  *   calls will fail. It is only posible to leave lock-tight state by
715  *   an hardware signal (low pulse on _WP pin) or by power down.
716  */
717 int nand_lock(nand_info_t *meminfo, int tight)
718 {
719         int ret = 0;
720         int status;
721         struct nand_chip *this = meminfo->priv;
722
723         /* select the NAND device */
724         this->select_chip(meminfo, 0);
725
726         this->cmdfunc(meminfo,
727                       (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK),
728                       -1, -1);
729
730         /* call wait ready function */
731         status = this->waitfunc(meminfo, this, FL_WRITING);
732
733         /* see if device thinks it succeeded */
734         if (status & 0x01) {
735                 ret = -1;
736         }
737
738         /* de-select the NAND device */
739         this->select_chip(meminfo, -1);
740         return ret;
741 }
742
743 /**
744  * nand_get_lock_status: - query current lock state from one page of NAND
745  *                         flash
746  *
747  * @param meminfo       nand mtd instance
748  * @param offset        page address to query (muss be page aligned!)
749  *
750  * @return              -1 in case of error
751  *                      >0 lock status:
752  *                        bitfield with the following combinations:
753  *                        NAND_LOCK_STATUS_TIGHT: page in tight state
754  *                        NAND_LOCK_STATUS_LOCK:  page locked
755  *                        NAND_LOCK_STATUS_UNLOCK: page unlocked
756  *
757  */
758 int nand_get_lock_status(nand_info_t *meminfo, ulong offset)
759 {
760         int ret = 0;
761         int chipnr;
762         int page;
763         struct nand_chip *this = meminfo->priv;
764
765         /* select the NAND device */
766         chipnr = (int)(offset >> this->chip_shift);
767         this->select_chip(meminfo, chipnr);
768
769
770         if ((offset & (meminfo->oobblock - 1)) != 0) {
771                 printf ("nand_get_lock_status: "
772                         "Start address must be beginning of "
773                         "nand page!\n");
774                 ret = -1;
775                 goto out;
776         }
777
778         /* check the Lock Status */
779         page = (int)(offset >> this->page_shift);
780         this->cmdfunc(meminfo, NAND_CMD_LOCK_STATUS, -1, page & this->pagemask);
781
782         ret = this->read_byte(meminfo) & (NAND_LOCK_STATUS_TIGHT
783                                           | NAND_LOCK_STATUS_LOCK
784                                           | NAND_LOCK_STATUS_UNLOCK);
785
786  out:
787         /* de-select the NAND device */
788         this->select_chip(meminfo, -1);
789         return ret;
790 }
791
792 /**
793  * nand_unlock: - Unlock area of NAND pages
794  *                only one consecutive area can be unlocked at one time!
795  *
796  * @param meminfo       nand mtd instance
797  * @param start         start byte address
798  * @param length        number of bytes to unlock (must be a multiple of
799  *                      page size nand->oobblock)
800  *
801  * @return              0 on success, -1 in case of error
802  */
803 int nand_unlock(nand_info_t *meminfo, ulong start, ulong length)
804 {
805         int ret = 0;
806         int chipnr;
807         int status;
808         int page;
809         struct nand_chip *this = meminfo->priv;
810         printf ("nand_unlock: start: %08x, length: %d!\n",
811                 (int)start, (int)length);
812
813         /* select the NAND device */
814         chipnr = (int)(start >> this->chip_shift);
815         this->select_chip(meminfo, chipnr);
816
817         /* check the WP bit */
818         this->cmdfunc(meminfo, NAND_CMD_STATUS, -1, -1);
819         if ((this->read_byte(meminfo) & 0x80) == 0) {
820                 printf ("nand_unlock: Device is write protected!\n");
821                 ret = -1;
822                 goto out;
823         }
824
825         if ((start & (meminfo->oobblock - 1)) != 0) {
826                 printf ("nand_unlock: Start address must be beginning of "
827                         "nand page!\n");
828                 ret = -1;
829                 goto out;
830         }
831
832         if (length == 0 || (length & (meminfo->oobblock - 1)) != 0) {
833                 printf ("nand_unlock: Length must be a multiple of nand page "
834                         "size!\n");
835                 ret = -1;
836                 goto out;
837         }
838
839         /* submit address of first page to unlock */
840         page = (int)(start >> this->page_shift);
841         this->cmdfunc(meminfo, NAND_CMD_UNLOCK1, -1, page & this->pagemask);
842
843         /* submit ADDRESS of LAST page to unlock */
844         page += (int)(length >> this->page_shift) - 1;
845         this->cmdfunc(meminfo, NAND_CMD_UNLOCK2, -1, page & this->pagemask);
846
847         /* call wait ready function */
848         status = this->waitfunc(meminfo, this, FL_WRITING);
849         /* see if device thinks it succeeded */
850         if (status & 0x01) {
851                 /* there was an error */
852                 ret = -1;
853                 goto out;
854         }
855
856  out:
857         /* de-select the NAND device */
858         this->select_chip(meminfo, -1);
859         return ret;
860 }
861
862 #endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY) */