]>
Commit | Line | Data |
---|---|---|
932394ac WD |
1 | /* |
2 | * drivers/mtd/nand.c | |
3 | * | |
4 | * Overview: | |
5 | * This is the generic MTD driver for NAND flash devices. It should be | |
6 | * capable of working with almost all NAND chips currently available. | |
7 | * Basic support for AG-AND chips is provided. | |
ac7eb8a3 | 8 | * |
932394ac | 9 | * Additional technical information is available on |
c45912d8 | 10 | * http://www.linux-mtd.infradead.org/doc/nand.html |
ac7eb8a3 | 11 | * |
932394ac | 12 | * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) |
cfa460ad | 13 | * 2002-2006 Thomas Gleixner (tglx@linutronix.de) |
932394ac | 14 | * |
cfa460ad | 15 | * Credits: |
ac7eb8a3 WD |
16 | * David Woodhouse for adding multichip support |
17 | * | |
932394ac WD |
18 | * Aleph One Ltd. and Toby Churchill Ltd. for supporting the |
19 | * rework for 2K page size chips | |
20 | * | |
cfa460ad | 21 | * TODO: |
932394ac WD |
22 | * Enable cached programming for 2k page size chips |
23 | * Check, if mtd->ecctype should be set to MTD_ECC_HW | |
dfe64e2c | 24 | * if we have HW ECC support. |
932394ac WD |
25 | * The AG-AND chips have nice features for speed improvement, |
26 | * which are not supported yet. Read / program 4 pages in one go. | |
c45912d8 | 27 | * BBT table is not serialized, has to be fixed |
932394ac | 28 | * |
932394ac WD |
29 | * This program is free software; you can redistribute it and/or modify |
30 | * it under the terms of the GNU General Public License version 2 as | |
31 | * published by the Free Software Foundation. | |
32 | * | |
33 | */ | |
34 | ||
932394ac | 35 | #include <common.h> |
addb2e16 | 36 | |
cfa460ad WJ |
37 | #define ENOTSUPP 524 /* Operation is not supported */ |
38 | ||
932394ac WD |
39 | #include <malloc.h> |
40 | #include <watchdog.h> | |
cfa460ad | 41 | #include <linux/err.h> |
7b15e2bb | 42 | #include <linux/compat.h> |
932394ac WD |
43 | #include <linux/mtd/mtd.h> |
44 | #include <linux/mtd/nand.h> | |
45 | #include <linux/mtd/nand_ecc.h> | |
4c6de856 | 46 | #include <linux/mtd/nand_bch.h> |
932394ac | 47 | |
10bb62d8 SR |
48 | #ifdef CONFIG_MTD_PARTITIONS |
49 | #include <linux/mtd/partitions.h> | |
50 | #endif | |
51 | ||
932394ac WD |
52 | #include <asm/io.h> |
53 | #include <asm/errno.h> | |
54 | ||
8da60128 PT |
55 | /* |
56 | * CONFIG_SYS_NAND_RESET_CNT is used as a timeout mechanism when resetting | |
57 | * a flash. NAND flash is initialized prior to interrupts so standard timers | |
58 | * can't be used. CONFIG_SYS_NAND_RESET_CNT should be set to a value | |
59 | * which is greater than (max NAND reset time / NAND status read time). | |
60 | * A conservative default of 200000 (500 us / 25 ns) is used as a default. | |
61 | */ | |
62 | #ifndef CONFIG_SYS_NAND_RESET_CNT | |
63 | #define CONFIG_SYS_NAND_RESET_CNT 200000 | |
64 | #endif | |
65 | ||
932394ac | 66 | /* Define default oob placement schemes for large and small page devices */ |
cfa460ad | 67 | static struct nand_ecclayout nand_oob_8 = { |
932394ac WD |
68 | .eccbytes = 3, |
69 | .eccpos = {0, 1, 2}, | |
cfa460ad WJ |
70 | .oobfree = { |
71 | {.offset = 3, | |
72 | .length = 2}, | |
73 | {.offset = 6, | |
90e3f395 | 74 | .length = 2} } |
932394ac WD |
75 | }; |
76 | ||
cfa460ad | 77 | static struct nand_ecclayout nand_oob_16 = { |
932394ac WD |
78 | .eccbytes = 6, |
79 | .eccpos = {0, 1, 2, 3, 6, 7}, | |
cfa460ad WJ |
80 | .oobfree = { |
81 | {.offset = 8, | |
90e3f395 | 82 | . length = 8} } |
932394ac WD |
83 | }; |
84 | ||
cfa460ad | 85 | static struct nand_ecclayout nand_oob_64 = { |
932394ac WD |
86 | .eccbytes = 24, |
87 | .eccpos = { | |
cfa460ad WJ |
88 | 40, 41, 42, 43, 44, 45, 46, 47, |
89 | 48, 49, 50, 51, 52, 53, 54, 55, | |
90 | 56, 57, 58, 59, 60, 61, 62, 63}, | |
91 | .oobfree = { | |
92 | {.offset = 2, | |
90e3f395 | 93 | .length = 38} } |
932394ac WD |
94 | }; |
95 | ||
cfa460ad | 96 | static struct nand_ecclayout nand_oob_128 = { |
248ae5cf SP |
97 | .eccbytes = 48, |
98 | .eccpos = { | |
90e3f395 CH |
99 | 80, 81, 82, 83, 84, 85, 86, 87, |
100 | 88, 89, 90, 91, 92, 93, 94, 95, | |
101 | 96, 97, 98, 99, 100, 101, 102, 103, | |
cfa460ad WJ |
102 | 104, 105, 106, 107, 108, 109, 110, 111, |
103 | 112, 113, 114, 115, 116, 117, 118, 119, | |
104 | 120, 121, 122, 123, 124, 125, 126, 127}, | |
105 | .oobfree = { | |
106 | {.offset = 2, | |
90e3f395 | 107 | .length = 78} } |
932394ac WD |
108 | }; |
109 | ||
cfa460ad WJ |
110 | static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, |
111 | int new_state); | |
112 | ||
113 | static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, | |
114 | struct mtd_oob_ops *ops); | |
115 | ||
116 | static int nand_wait(struct mtd_info *mtd, struct nand_chip *this); | |
248ae5cf | 117 | |
2a8e0fc8 CH |
118 | static int check_offs_len(struct mtd_info *mtd, |
119 | loff_t ofs, uint64_t len) | |
120 | { | |
121 | struct nand_chip *chip = mtd->priv; | |
122 | int ret = 0; | |
123 | ||
124 | /* Start address must align on block boundary */ | |
125 | if (ofs & ((1 << chip->phys_erase_shift) - 1)) { | |
126 | MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); | |
127 | ret = -EINVAL; | |
128 | } | |
129 | ||
130 | /* Length must align on block boundary */ | |
131 | if (len & ((1 << chip->phys_erase_shift) - 1)) { | |
132 | MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", | |
133 | __func__); | |
134 | ret = -EINVAL; | |
135 | } | |
136 | ||
2a8e0fc8 CH |
137 | return ret; |
138 | } | |
139 | ||
932394ac WD |
140 | /** |
141 | * nand_release_device - [GENERIC] release chip | |
dfe64e2c | 142 | * @mtd: MTD device structure |
ac7eb8a3 | 143 | * |
dfe64e2c | 144 | * Deselect, release chip lock and wake up anyone waiting on the device. |
932394ac | 145 | */ |
90e3f395 | 146 | static void nand_release_device(struct mtd_info *mtd) |
8e9655f8 | 147 | { |
2a8e0fc8 CH |
148 | struct nand_chip *chip = mtd->priv; |
149 | ||
150 | /* De-select the NAND device */ | |
151 | chip->select_chip(mtd, -1); | |
8e9655f8 | 152 | } |
932394ac WD |
153 | |
154 | /** | |
155 | * nand_read_byte - [DEFAULT] read one byte from the chip | |
dfe64e2c | 156 | * @mtd: MTD device structure |
932394ac | 157 | * |
dfe64e2c | 158 | * Default read function for 8bit buswidth. |
932394ac | 159 | */ |
82645f81 | 160 | uint8_t nand_read_byte(struct mtd_info *mtd) |
932394ac | 161 | { |
cfa460ad WJ |
162 | struct nand_chip *chip = mtd->priv; |
163 | return readb(chip->IO_ADDR_R); | |
932394ac WD |
164 | } |
165 | ||
166 | /** | |
167 | * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip | |
dfe64e2c SL |
168 | * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip |
169 | * @mtd: MTD device structure | |
170 | * | |
171 | * Default read function for 16bit buswidth with endianness conversion. | |
932394ac | 172 | * |
932394ac | 173 | */ |
cfa460ad | 174 | static uint8_t nand_read_byte16(struct mtd_info *mtd) |
932394ac | 175 | { |
cfa460ad WJ |
176 | struct nand_chip *chip = mtd->priv; |
177 | return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R)); | |
932394ac WD |
178 | } |
179 | ||
180 | /** | |
181 | * nand_read_word - [DEFAULT] read one word from the chip | |
dfe64e2c | 182 | * @mtd: MTD device structure |
932394ac | 183 | * |
dfe64e2c | 184 | * Default read function for 16bit buswidth without endianness conversion. |
932394ac WD |
185 | */ |
186 | static u16 nand_read_word(struct mtd_info *mtd) | |
187 | { | |
cfa460ad WJ |
188 | struct nand_chip *chip = mtd->priv; |
189 | return readw(chip->IO_ADDR_R); | |
932394ac WD |
190 | } |
191 | ||
192 | /** | |
193 | * nand_select_chip - [DEFAULT] control CE line | |
dfe64e2c SL |
194 | * @mtd: MTD device structure |
195 | * @chipnr: chipnumber to select, -1 for deselect | |
932394ac WD |
196 | * |
197 | * Default select function for 1 chip devices. | |
198 | */ | |
cfa460ad | 199 | static void nand_select_chip(struct mtd_info *mtd, int chipnr) |
932394ac | 200 | { |
cfa460ad WJ |
201 | struct nand_chip *chip = mtd->priv; |
202 | ||
203 | switch (chipnr) { | |
932394ac | 204 | case -1: |
cfa460ad | 205 | chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); |
932394ac WD |
206 | break; |
207 | case 0: | |
932394ac WD |
208 | break; |
209 | ||
210 | default: | |
211 | BUG(); | |
212 | } | |
213 | } | |
214 | ||
215 | /** | |
216 | * nand_write_buf - [DEFAULT] write buffer to chip | |
dfe64e2c SL |
217 | * @mtd: MTD device structure |
218 | * @buf: data buffer | |
219 | * @len: number of bytes to write | |
932394ac | 220 | * |
dfe64e2c | 221 | * Default write function for 8bit buswidth. |
932394ac | 222 | */ |
82645f81 | 223 | void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) |
932394ac WD |
224 | { |
225 | int i; | |
cfa460ad | 226 | struct nand_chip *chip = mtd->priv; |
932394ac | 227 | |
cfa460ad WJ |
228 | for (i = 0; i < len; i++) |
229 | writeb(buf[i], chip->IO_ADDR_W); | |
932394ac WD |
230 | } |
231 | ||
232 | /** | |
ac7eb8a3 | 233 | * nand_read_buf - [DEFAULT] read chip data into buffer |
dfe64e2c SL |
234 | * @mtd: MTD device structure |
235 | * @buf: buffer to store date | |
236 | * @len: number of bytes to read | |
932394ac | 237 | * |
dfe64e2c | 238 | * Default read function for 8bit buswidth. |
932394ac | 239 | */ |
12c2f1ee | 240 | void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) |
932394ac WD |
241 | { |
242 | int i; | |
cfa460ad | 243 | struct nand_chip *chip = mtd->priv; |
932394ac | 244 | |
cfa460ad WJ |
245 | for (i = 0; i < len; i++) |
246 | buf[i] = readb(chip->IO_ADDR_R); | |
932394ac WD |
247 | } |
248 | ||
249 | /** | |
ac7eb8a3 | 250 | * nand_verify_buf - [DEFAULT] Verify chip data against buffer |
dfe64e2c SL |
251 | * @mtd: MTD device structure |
252 | * @buf: buffer containing the data to compare | |
253 | * @len: number of bytes to compare | |
932394ac | 254 | * |
dfe64e2c | 255 | * Default verify function for 8bit buswidth. |
932394ac | 256 | */ |
cfa460ad | 257 | static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) |
932394ac WD |
258 | { |
259 | int i; | |
cfa460ad | 260 | struct nand_chip *chip = mtd->priv; |
932394ac | 261 | |
cfa460ad WJ |
262 | for (i = 0; i < len; i++) |
263 | if (buf[i] != readb(chip->IO_ADDR_R)) | |
932394ac | 264 | return -EFAULT; |
932394ac WD |
265 | return 0; |
266 | } | |
267 | ||
268 | /** | |
269 | * nand_write_buf16 - [DEFAULT] write buffer to chip | |
dfe64e2c SL |
270 | * @mtd: MTD device structure |
271 | * @buf: data buffer | |
272 | * @len: number of bytes to write | |
932394ac | 273 | * |
dfe64e2c | 274 | * Default write function for 16bit buswidth. |
932394ac | 275 | */ |
82645f81 | 276 | void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) |
932394ac WD |
277 | { |
278 | int i; | |
cfa460ad | 279 | struct nand_chip *chip = mtd->priv; |
932394ac WD |
280 | u16 *p = (u16 *) buf; |
281 | len >>= 1; | |
ac7eb8a3 | 282 | |
cfa460ad WJ |
283 | for (i = 0; i < len; i++) |
284 | writew(p[i], chip->IO_ADDR_W); | |
ac7eb8a3 | 285 | |
932394ac WD |
286 | } |
287 | ||
288 | /** | |
ac7eb8a3 | 289 | * nand_read_buf16 - [DEFAULT] read chip data into buffer |
dfe64e2c SL |
290 | * @mtd: MTD device structure |
291 | * @buf: buffer to store date | |
292 | * @len: number of bytes to read | |
932394ac | 293 | * |
dfe64e2c | 294 | * Default read function for 16bit buswidth. |
932394ac | 295 | */ |
12c2f1ee | 296 | void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) |
932394ac WD |
297 | { |
298 | int i; | |
cfa460ad | 299 | struct nand_chip *chip = mtd->priv; |
932394ac WD |
300 | u16 *p = (u16 *) buf; |
301 | len >>= 1; | |
302 | ||
cfa460ad WJ |
303 | for (i = 0; i < len; i++) |
304 | p[i] = readw(chip->IO_ADDR_R); | |
932394ac WD |
305 | } |
306 | ||
307 | /** | |
ac7eb8a3 | 308 | * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer |
dfe64e2c SL |
309 | * @mtd: MTD device structure |
310 | * @buf: buffer containing the data to compare | |
311 | * @len: number of bytes to compare | |
932394ac | 312 | * |
dfe64e2c | 313 | * Default verify function for 16bit buswidth. |
932394ac | 314 | */ |
cfa460ad | 315 | static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) |
932394ac WD |
316 | { |
317 | int i; | |
cfa460ad | 318 | struct nand_chip *chip = mtd->priv; |
932394ac WD |
319 | u16 *p = (u16 *) buf; |
320 | len >>= 1; | |
321 | ||
cfa460ad WJ |
322 | for (i = 0; i < len; i++) |
323 | if (p[i] != readw(chip->IO_ADDR_R)) | |
932394ac WD |
324 | return -EFAULT; |
325 | ||
326 | return 0; | |
327 | } | |
328 | ||
329 | /** | |
330 | * nand_block_bad - [DEFAULT] Read bad block marker from the chip | |
dfe64e2c SL |
331 | * @mtd: MTD device structure |
332 | * @ofs: offset from device start | |
333 | * @getchip: 0, if the chip is already selected | |
932394ac | 334 | * |
ac7eb8a3 | 335 | * Check, if the block is bad. |
932394ac WD |
336 | */ |
337 | static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) | |
338 | { | |
dfe64e2c | 339 | int page, chipnr, res = 0, i = 0; |
cfa460ad | 340 | struct nand_chip *chip = mtd->priv; |
932394ac WD |
341 | u16 bad; |
342 | ||
dfe64e2c | 343 | if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) |
2a8e0fc8 CH |
344 | ofs += mtd->erasesize - mtd->writesize; |
345 | ||
cfa460ad | 346 | page = (int)(ofs >> chip->page_shift) & chip->pagemask; |
a7988659 | 347 | |
932394ac | 348 | if (getchip) { |
cfa460ad | 349 | chipnr = (int)(ofs >> chip->chip_shift); |
932394ac | 350 | |
cfa460ad | 351 | nand_get_device(chip, mtd, FL_READING); |
932394ac WD |
352 | |
353 | /* Select the NAND device */ | |
cfa460ad | 354 | chip->select_chip(mtd, chipnr); |
a7988659 | 355 | } |
932394ac | 356 | |
dfe64e2c SL |
357 | do { |
358 | if (chip->options & NAND_BUSWIDTH_16) { | |
359 | chip->cmdfunc(mtd, NAND_CMD_READOOB, | |
360 | chip->badblockpos & 0xFE, page); | |
361 | bad = cpu_to_le16(chip->read_word(mtd)); | |
362 | if (chip->badblockpos & 0x1) | |
363 | bad >>= 8; | |
364 | else | |
365 | bad &= 0xFF; | |
366 | } else { | |
367 | chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, | |
368 | page); | |
369 | bad = chip->read_byte(mtd); | |
370 | } | |
ac7eb8a3 | 371 | |
dfe64e2c SL |
372 | if (likely(chip->badblockbits == 8)) |
373 | res = bad != 0xFF; | |
374 | else | |
375 | res = hweight8(bad) < chip->badblockbits; | |
376 | ofs += mtd->writesize; | |
377 | page = (int)(ofs >> chip->page_shift) & chip->pagemask; | |
378 | i++; | |
379 | } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)); | |
2a8e0fc8 | 380 | |
cfa460ad | 381 | if (getchip) |
932394ac | 382 | nand_release_device(mtd); |
ac7eb8a3 | 383 | |
932394ac WD |
384 | return res; |
385 | } | |
386 | ||
387 | /** | |
388 | * nand_default_block_markbad - [DEFAULT] mark a block bad | |
dfe64e2c SL |
389 | * @mtd: MTD device structure |
390 | * @ofs: offset from device start | |
932394ac | 391 | * |
dfe64e2c SL |
392 | * This is the default implementation, which can be overridden by a hardware |
393 | * specific driver. We try operations in the following order, according to our | |
394 | * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): | |
395 | * (1) erase the affected block, to allow OOB marker to be written cleanly | |
396 | * (2) update in-memory BBT | |
397 | * (3) write bad block marker to OOB area of affected block | |
398 | * (4) update flash-based BBT | |
399 | * Note that we retain the first error encountered in (3) or (4), finish the | |
400 | * procedures, and dump the error in the end. | |
932394ac WD |
401 | */ |
402 | static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |
403 | { | |
cfa460ad WJ |
404 | struct nand_chip *chip = mtd->priv; |
405 | uint8_t buf[2] = { 0, 0 }; | |
dfe64e2c SL |
406 | int block, res, ret = 0, i = 0; |
407 | int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); | |
2a8e0fc8 | 408 | |
dfe64e2c SL |
409 | if (write_oob) { |
410 | struct erase_info einfo; | |
411 | ||
412 | /* Attempt erase before marking OOB */ | |
413 | memset(&einfo, 0, sizeof(einfo)); | |
414 | einfo.mtd = mtd; | |
415 | einfo.addr = ofs; | |
416 | einfo.len = 1 << chip->phys_erase_shift; | |
417 | nand_erase_nand(mtd, &einfo, 0); | |
418 | } | |
ac7eb8a3 | 419 | |
932394ac | 420 | /* Get block number */ |
cfa460ad | 421 | block = (int)(ofs >> chip->bbt_erase_shift); |
dfe64e2c | 422 | /* Mark block bad in memory-based BBT */ |
cfa460ad WJ |
423 | if (chip->bbt) |
424 | chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); | |
932394ac | 425 | |
dfe64e2c SL |
426 | /* Write bad block marker to OOB */ |
427 | if (write_oob) { | |
428 | struct mtd_oob_ops ops; | |
429 | loff_t wr_ofs = ofs; | |
430 | ||
c45912d8 | 431 | nand_get_device(chip, mtd, FL_WRITING); |
ac7eb8a3 | 432 | |
dfe64e2c SL |
433 | ops.datbuf = NULL; |
434 | ops.oobbuf = buf; | |
435 | ops.ooboffs = chip->badblockpos; | |
436 | if (chip->options & NAND_BUSWIDTH_16) { | |
437 | ops.ooboffs &= ~0x01; | |
438 | ops.len = ops.ooblen = 2; | |
439 | } else { | |
440 | ops.len = ops.ooblen = 1; | |
441 | } | |
442 | ops.mode = MTD_OPS_PLACE_OOB; | |
2a8e0fc8 | 443 | |
dfe64e2c SL |
444 | /* Write to first/last page(s) if necessary */ |
445 | if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) | |
446 | wr_ofs += mtd->erasesize - mtd->writesize; | |
447 | do { | |
448 | res = nand_do_write_oob(mtd, wr_ofs, &ops); | |
449 | if (!ret) | |
450 | ret = res; | |
2a8e0fc8 | 451 | |
2a8e0fc8 | 452 | i++; |
dfe64e2c SL |
453 | wr_ofs += mtd->writesize; |
454 | } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); | |
2a8e0fc8 | 455 | |
c45912d8 | 456 | nand_release_device(mtd); |
cfa460ad | 457 | } |
dfe64e2c SL |
458 | |
459 | /* Update flash-based bad block table */ | |
460 | if (chip->bbt_options & NAND_BBT_USE_FLASH) { | |
461 | res = nand_update_bbt(mtd, ofs); | |
462 | if (!ret) | |
463 | ret = res; | |
464 | } | |
465 | ||
cfa460ad WJ |
466 | if (!ret) |
467 | mtd->ecc_stats.badblocks++; | |
c45912d8 | 468 | |
cfa460ad | 469 | return ret; |
932394ac WD |
470 | } |
471 | ||
ac7eb8a3 | 472 | /** |
932394ac | 473 | * nand_check_wp - [GENERIC] check if the chip is write protected |
dfe64e2c | 474 | * @mtd: MTD device structure |
932394ac | 475 | * |
dfe64e2c SL |
476 | * Check, if the device is write protected. The function expects, that the |
477 | * device is already selected. | |
932394ac | 478 | */ |
cfa460ad | 479 | static int nand_check_wp(struct mtd_info *mtd) |
932394ac | 480 | { |
cfa460ad | 481 | struct nand_chip *chip = mtd->priv; |
2a8e0fc8 | 482 | |
dfe64e2c | 483 | /* Broken xD cards report WP despite being writable */ |
2a8e0fc8 CH |
484 | if (chip->options & NAND_BROKEN_XD) |
485 | return 0; | |
486 | ||
932394ac | 487 | /* Check the WP bit */ |
cfa460ad WJ |
488 | chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); |
489 | return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; | |
932394ac | 490 | } |