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