BCM WL 6.30.102.9 (r366174)
[tomato.git] / release / src-rt / shared / nflash.c
blobdc01f36f949646dd23fc009fc7c5d0a125f9a336
1 /*
2 * Broadcom chipcommon NAND flash interface
4 * Copyright (C) 2011, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 * $Id: nflash.c 300516 2011-12-04 17:39:44Z $
21 #include <typedefs.h>
22 #include <osl.h>
23 #include <bcmutils.h>
24 #include <siutils.h>
25 #include <hndsoc.h>
26 #include <sbhndcpu.h>
27 #include <sbchipc.h>
28 #include <bcmdevs.h>
29 #include <nflash.h>
30 #include <hndpmu.h>
31 #include <bcmnvram.h>
33 #ifdef BCMDBG
34 #define NFL_MSG(args) printf args
35 #else
36 #define NFL_MSG(args)
37 #endif /* BCMDBG */
39 #define NF_RETRIES 1000000
41 #define NF_SMALL_BADBLOCK_POS 5
42 #define NF_LARGE_BADBLOCK_POS 0
44 /* Private global state */
45 static struct nflash nflash;
47 /* Private variables used for BCM4706 only */
48 static uint32 nflash_col_mask;
49 static uint32 nflash_row_shift;
52 void nflash_enable(si_t *sih, int enable)
54 if (sih->ccrev == 38) {
55 /* BCM5357 NAND boot */
56 if ((sih->chipst & (1 << 4)) != 0)
57 return;
59 if (enable)
60 si_pmu_chipcontrol(sih, 1, CCTRL5357_NFLASH, CCTRL5357_NFLASH);
61 else
62 si_pmu_chipcontrol(sih, 1, CCTRL5357_NFLASH, 0);
66 /* Issue a nand flash control command; this is used for BCM4706 */
67 static INLINE int
68 nflash_ctrlcmd(osl_t *osh, chipcregs_t *cc, uint ctrlcode)
70 int cnt = 0;
72 W_REG(osh, &cc->nflashctrl, ctrlcode | NFC_START);
74 while ((R_REG(osh, &cc->nflashctrl) & NFC_START) != 0) {
75 if (++cnt > NF_RETRIES) {
76 printf("nflash_ctrlcmd: not ready\n");
77 return -1;
81 return 0;
84 /* Issue a nand flash command */
85 static INLINE void
86 nflash_cmd(osl_t *osh, chipcregs_t *cc, uint opcode)
88 W_REG(osh, &cc->nand_cmd_start, opcode);
89 /* read after write to flush the command */
90 R_REG(osh, &cc->nand_cmd_start);
93 static bool firsttime = TRUE;
95 static char *nflash_check_id(uint8 *id)
97 char *name = NULL;
98 char *value; /* J++ */
99 int ntype; /* J++ */
101 switch (id[0]) {
102 case NFL_VENDOR_AMD:
103 name = "AMD";
104 break;
105 case NFL_VENDOR_NUMONYX:
106 name = "Numonyx";
107 break;
108 case NFL_VENDOR_MICRON:
109 name = "Micron";
110 break;
111 case NFL_VENDOR_TOSHIBA:
112 name = "Toshiba";
113 break;
114 case NFL_VENDOR_HYNIX:
115 name = "Hynix";
116 break;
117 case NFL_VENDOR_SAMSUNG:
118 name = "Samsung";
119 break;
120 default:
121 #if 0 /* J++ */
122 printf("No NAND flash type found\n");
123 #else
124 value = nvram_get("bootflags");
125 if (!value || (int) bcm_atoi(value) != 1)
126 ntype = -1;
127 else
129 value = nvram_get("ntype");
130 if (!value)
131 ntype = 0;
132 else
133 ntype = (int) bcm_atoi(value);
136 // printf("NAND type: %d\n", ntype);
138 switch (ntype) {
139 case 0:
140 name = "AMD";
141 break;
142 case 1:
143 name = "Numonyx";
144 break;
145 case 2:
146 name = "Micron";
147 break;
148 case 3:
149 name = "Toshiba";
150 break;
151 case 4:
152 name = "Hynix";
153 break;
154 case 5:
155 name = "Samsung";
156 break;
157 default:
158 printf("No NAND flash type found\n");
159 break;
161 #endif
162 break;
165 return name;
168 /* Initialize nand flash access */
169 struct nflash *
170 nflash_init(si_t *sih, chipcregs_t *cc)
172 uint32 id, id2;
173 char *name = "";
174 osl_t *osh;
175 int i;
176 uint32 ncf, val;
177 uint32 acc_control;
179 ASSERT(sih);
180 /* Only support chipcommon revision == 38 and BCM4706 for now */
181 if ((CHIPID(sih->chip) != BCM4706_CHIP_ID) && (sih->ccrev != 38))
182 return NULL;
184 /* Check if nand flash is mounted */
185 if ((sih->cccaps & CC_CAP_NFLASH) != CC_CAP_NFLASH)
186 return NULL;
188 if (!firsttime && nflash.size)
189 return &nflash;
191 osh = si_osh(sih);
192 bzero(&nflash, sizeof(nflash));
194 if (CHIPID(sih->chip) == BCM4706_CHIP_ID) {
195 uint32 cpu_freq, clk;
196 uint32 w0, w1, w2, w3, w4;
197 uint32 ctrlcode, val;
198 uint32 totbits = 0, colbits = 0, rowbits;
199 uint32 colbsize, rowbsize;
200 uint32 oobsz_per_sector, plane_sz, plane_num;
202 /* Enable nand flash interface of BCM4706 (usign CS1) */
203 val = R_REG(osh, &cc->eci.flashconf.flashstrconfig) | FLSTRCF4706_NF1;
204 W_REG(osh, &cc->eci.flashconf.flashstrconfig, val);
206 /* Configure nand flash bus timing */
207 if (R_REG(osh, &cc->chipstatus) & CST4706_PKG_OPTION) {
208 cpu_freq = (400000000 / 4);
209 } else {
210 /* Get N divider to determine CPU clock */
211 W_REG(osh, &cc->pllcontrol_addr, 4);
212 val = (R_REG(osh, &cc->pllcontrol_data) & 0xfff) >> 3;
214 /* Fixed reference clock 25MHz and m = 2 */
215 cpu_freq = (val * 25000000 / 2) / 4;
218 clk = cpu_freq / 1000000;
220 w0 = NFLASH_T_WP;
221 w1 = NFLASH_T_RR;
222 w2 = NFLASH_T_CS - NFLASH_T_WP;
223 w3 = NFLASH_T_WH;
224 w4 = NFLASH_T_WB;
226 w0 = nflash_ns_to_cycle(w0, clk);
227 w1 = nflash_ns_to_cycle(w1, clk);
228 w2 = nflash_ns_to_cycle(w2, clk);
229 w3 = nflash_ns_to_cycle(w3, clk);
230 w4 = nflash_ns_to_cycle(w4, clk) - 1;
232 val = (w4 << NFLASH_WAITCOUNT_W4_SHIFT) | (w3 << NFLASH_WAITCOUNT_W3_SHIFT)
233 | (w2 << NFLASH_WAITCOUNT_W2_SHIFT)
234 | (w1 << NFLASH_WAITCOUNT_W1_SHIFT) | w0;
235 W_REG(osh, &cc->nflashwaitcnt0, val);
237 /* Read nand flash ID */
238 ctrlcode = NFC_CSA | NFC_SPECADDR | NFC_CMD1W | NFC_CMD0 | NFCTRL_ID;
240 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
241 return NULL;
243 for (i = 0; i < 5; i++) {
244 if (i <= 4)
245 ctrlcode = (NFC_CSA | NFC_1BYTE | NFC_DREAD);
246 else
247 ctrlcode = (NFC_1BYTE | NFC_DREAD);
249 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
250 return NULL;
251 else
252 nflash.id[i] = R_REG(osh, &cc->nflashdata) & 0xff;
255 name = nflash_check_id(nflash.id);
256 if (name == NULL) {
257 /* Disable nand flash interface of BCM4706 (usign CS1) */
258 val = R_REG(osh, &cc->eci.flashconf.flashstrconfig) & ~FLSTRCF4706_NF1;
259 W_REG(osh, &cc->eci.flashconf.flashstrconfig, val);
261 return NULL;
263 nflash.type = nflash.id[0];
265 /* In unit of bytes */
266 nflash.pagesize = 1024 << (nflash.id[3] & 0x3);
267 nflash.blocksize = (64 * 1024) << ((nflash.id[3] >> 4) & 0x3);
269 oobsz_per_sector = (8 << ((nflash.id[3] >> 2) & 0x1));
270 nflash.oobsize = oobsz_per_sector * (nflash.pagesize / NFL_SECTOR_SIZE);
272 /* In unit of MBytes */
273 plane_sz = (8 << ((nflash.id[4] >> 4) & 0x7));
274 plane_num = (1 << ((nflash.id[4] >> 2) & 0x3));
275 nflash.size = plane_sz * plane_num;
277 for (i = 0; i < 32; i++) {
278 if ((0x1 << i) == nflash.size) {
279 totbits = i + 20;
280 break;
284 if (totbits == 0) {
285 #ifdef BCMDBG
286 NFL_MSG(("nflash_init: failed to find nflash total bits\n"));
287 #endif
288 return NULL;
291 for (i = 0; i < 32; i++) {
292 if ((0x1 << i) == nflash.pagesize) {
293 colbits = i + 1; /* including spare oob bytes */
294 break;
298 if (colbits == 0) {
299 #ifdef BCMDBG
300 NFL_MSG(("nflash_init: failed to find nflash column address bits\n"));
301 #endif
302 return NULL;
305 nflash_col_mask = (0x1 << colbits) - 1;
306 nflash_row_shift = colbits;
308 colbsize = (colbits + 7) / 8;
310 rowbits = totbits - colbits + 1;
311 rowbsize = (rowbits + 7) / 8;
313 val = ((rowbsize - 1) << NFCF_ROWSZ_SHIFT) | ((colbsize - 1) << NFCF_COLSZ_SHIFT)
314 | NFCF_DS_8 | NFCF_WE;
316 W_REG(osh, &cc->nflashconf, val);
317 } else {
318 nflash_enable(sih, 1);
319 nflash_cmd(osh, cc, NCMD_ID_RD);
320 if (nflash_poll(sih, cc) < 0) {
321 nflash_enable(sih, 0);
322 return NULL;
324 nflash_enable(sih, 0);
325 id = R_REG(osh, &cc->nand_devid);
326 id2 = R_REG(osh, &cc->nand_devid_x);
327 for (i = 0; i < 5; i++) {
328 if (i < 4)
329 nflash.id[i] = (id >> (8*i)) & 0xff;
330 else
331 nflash.id[i] = id2 & 0xff;
334 name = nflash_check_id(nflash.id);
335 if (name == NULL)
336 return NULL;
337 nflash.type = nflash.id[0];
339 ncf = R_REG(osh, &cc->nand_config);
340 /* Page size (# of bytes) */
341 val = (ncf & NCF_PAGE_SIZE_MASK) >> NCF_PAGE_SIZE_SHIFT;
342 switch (val) {
343 case 0:
344 nflash.pagesize = 512;
345 break;
346 case 1:
347 nflash.pagesize = (1 << 10) * 2;
348 break;
349 case 2:
350 nflash.pagesize = (1 << 10) * 4;
351 break;
352 case 3:
353 nflash.pagesize = (1 << 10) * 8;
354 break;
356 /* Block size (# of bytes) */
357 val = (ncf & NCF_BLOCK_SIZE_MASK) >> NCF_BLOCK_SIZE_SHIFT;
358 switch (val) {
359 case 0:
360 nflash.blocksize = (1 << 10) * 16;
361 break;
362 case 1:
363 nflash.blocksize = (1 << 10) * 128;
364 break;
365 case 2:
366 nflash.blocksize = (1 << 10) * 8;
367 break;
368 case 3:
369 nflash.blocksize = (1 << 10) * 512;
370 break;
371 case 4:
372 nflash.blocksize = (1 << 10) * 256;
373 break;
374 default:
375 printf("Unknown block size\n");
376 return NULL;
378 /* NAND flash size in MBytes */
379 val = (ncf & NCF_DEVICE_SIZE_MASK) >> NCF_DEVICE_SIZE_SHIFT;
380 if (val == 0) {
381 printf("Unknown flash size\n");
382 return NULL;
384 nflash.size = (1 << (val - 1)) * 8;
386 /* More attribues for ECC functions */
387 acc_control = R_REG(osh, &cc->nand_acc_control);
388 nflash.ecclevel = (acc_control & NAC_ECC_LEVEL_MASK) >> NAC_ECC_LEVEL_SHIFT;
389 nflash.ecclevel0 = (acc_control & NAC_ECC_LEVEL0_MASK) >> NAC_ECC_LEVEL0_SHIFT;
390 /* make sure that block-0 and block-n use the same ECC level */
391 if (nflash.ecclevel != nflash.ecclevel0) {
392 acc_control &= ~(NAC_ECC_LEVEL_MASK | NAC_ECC_LEVEL0_MASK);
393 acc_control |=
394 (nflash.ecclevel0 << NAC_ECC_LEVEL0_SHIFT) |
395 (nflash.ecclevel0 << NAC_ECC_LEVEL_SHIFT);
396 W_REG(osh, &cc->nand_acc_control, acc_control);
397 nflash.ecclevel = nflash.ecclevel0;
401 nflash.numblocks = (nflash.size * (1 << 10)) / (nflash.blocksize >> 10);
402 if (firsttime)
403 printf("Found a %s NAND flash with %uB pages or %dKB blocks; total size %dMB\n",
404 name, nflash.pagesize, (nflash.blocksize >> 10), nflash.size);
405 firsttime = FALSE;
406 return nflash.size ? &nflash : NULL;
409 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
411 nflash_read(si_t *sih, chipcregs_t *cc, uint offset, uint len, uchar *buf)
413 uint32 mask;
414 osl_t *osh;
415 int i;
416 uint32 *to;
417 uint32 val;
418 uint res;
420 ASSERT(sih);
421 mask = NFL_SECTOR_SIZE - 1;
422 if ((offset & mask) != 0 || (len & mask) != 0)
423 return 0;
424 if ((((offset + len) >> 20) > nflash.size) ||
425 ((((offset + len) >> 20) == nflash.size) &&
426 (((offset + len) & ((1 << 20) - 1)) != 0)))
427 return 0;
428 osh = si_osh(sih);
429 to = (uint32 *)buf;
430 res = len;
432 if (CHIPID(sih->chip) == BCM4706_CHIP_ID) {
433 uint32 page_addr, page_offset;
434 uint32 ctrlcode;
436 while (res > 0) {
437 page_offset = offset & (nflash.pagesize - 1);
438 page_addr = (offset & ~(nflash.pagesize - 1)) * 2;
439 page_addr += page_offset;
441 W_REG(osh, &cc->nflashcoladdr, page_addr & nflash_col_mask);
442 W_REG(osh, &cc->nflashrowaddr, page_addr >> nflash_row_shift);
444 ctrlcode = NFC_CSA | NFC_CMD1W | NFC_ROW | NFC_COL | NFC_CMD0 | NFCTRL_READ;
446 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
447 break;
449 if (nflash_poll(sih, cc) < 0)
450 break;
452 for (i = 0; i < NFL_SECTOR_SIZE; i += 4, to++) {
453 if (i < NFL_SECTOR_SIZE - 4)
454 ctrlcode = (NFC_CSA | NFC_4BYTES | NFC_DREAD);
455 else
456 ctrlcode = (NFC_4BYTES | NFC_DREAD);
458 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
459 return (len - res);
461 *to = R_REG(osh, &cc->nflashdata);
464 res -= NFL_SECTOR_SIZE;
465 offset += NFL_SECTOR_SIZE;
467 } else {
468 nflash_enable(sih, 1);
469 while (res > 0) {
470 W_REG(osh, &cc->nand_cmd_addr, offset);
471 nflash_cmd(osh, cc, NCMD_PAGE_RD);
472 if (nflash_poll(sih, cc) < 0)
473 break;
474 if (((val = R_REG(osh, &cc->nand_intfc_status)) & NIST_CACHE_VALID) == 0)
475 break;
476 W_REG(osh, &cc->nand_cache_addr, 0);
477 for (i = 0; i < NFL_SECTOR_SIZE; i += 4, to++) {
478 *to = R_REG(osh, &cc->nand_cache_data);
481 res -= NFL_SECTOR_SIZE;
482 offset += NFL_SECTOR_SIZE;
484 nflash_enable(sih, 0);
487 return (len - res);
490 /* Poll for command completion. Returns zero when complete. */
492 nflash_poll(si_t *sih, chipcregs_t *cc)
494 osl_t *osh;
495 int i;
496 uint32 pollmask;
498 ASSERT(sih);
499 osh = si_osh(sih);
501 if (CHIPID(sih->chip) == BCM4706_CHIP_ID) {
502 for (i = 0; i < NF_RETRIES; i++) {
503 if ((R_REG(osh, &cc->nflashctrl) & NFC_RDYBUSY) == NFC_RDYBUSY) {
504 if ((R_REG(osh, &cc->nflashctrl) & NFC_ERROR) == NFC_ERROR) {
505 printf("nflash_poll: error occurred\n");
506 return -1;
508 else
509 return 0;
512 } else {
513 pollmask = NIST_CTRL_READY|NIST_FLASH_READY;
514 for (i = 0; i < NF_RETRIES; i++) {
515 if ((R_REG(osh, &cc->nand_intfc_status) & pollmask) == pollmask) {
516 return 0;
521 printf("nflash_poll: not ready\n");
522 return -1;
525 /* Write len bytes starting at offset into buf. Returns number of bytes
526 * written.
529 nflash_write(si_t *sih, chipcregs_t *cc, uint offset, uint len, const uchar *buf)
531 uint32 mask;
532 osl_t *osh;
533 int i;
534 uint32 *from;
535 uint res;
536 uint32 reg;
537 int ret = 0;
538 uint8 status;
540 ASSERT(sih);
541 mask = nflash.pagesize - 1;
542 /* Check offset and length */
543 if ((offset & mask) != 0 || (len & mask) != 0)
544 return 0;
545 if ((((offset + len) >> 20) > nflash.size) ||
546 ((((offset + len) >> 20) == nflash.size) &&
547 (((offset + len) & ((1 << 20) - 1)) != 0)))
548 return 0;
549 osh = si_osh(sih);
551 from = (uint32 *)buf;
552 res = len;
554 if (CHIPID(sih->chip) == BCM4706_CHIP_ID) {
555 uint32 page_addr;
556 uint32 ctrlcode;
558 while (res > 0) {
559 page_addr = (offset & ~(nflash.pagesize - 1)) * 2;
561 W_REG(osh, &cc->nflashcoladdr, page_addr & nflash_col_mask);
562 W_REG(osh, &cc->nflashrowaddr, page_addr >> nflash_row_shift);
564 ctrlcode = NFC_CSA | NFC_ROW | NFC_COL | NFC_CMD0 | NFCTRL_PAGEPROG;
566 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
567 return -1;
569 for (i = 0; i < nflash.pagesize; i += 4, from++) {
570 W_REG(osh, &cc->nflashdata, *from);
572 if (i < nflash.pagesize - 4)
573 ctrlcode = (NFC_CSA | NFC_4BYTES | NFC_DWRITE);
574 else
575 ctrlcode = (NFC_4BYTES | NFC_DWRITE);
577 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
578 return (len - res);
581 if (nflash_ctrlcmd(osh, cc, (NFC_CMD0 | NFCTRL_PROGSTART)) != 0)
582 return -1;
584 if (nflash_poll(sih, cc) < 0)
585 return -1;
587 if (nflash_readst(sih, cc, &status) != 0)
588 return -1;
590 if (status & 1) {
591 printf("nflash_write: failed with status 0x%02x\n", status);
592 return -1;
595 res -= nflash.pagesize;
596 offset += nflash.pagesize;
598 } else {
599 nflash_enable(sih, 1);
600 /* disable partial page enable */
601 reg = R_REG(osh, &cc->nand_acc_control);
602 reg &= ~NAC_PARTIAL_PAGE_EN;
603 W_REG(osh, &cc->nand_acc_control, reg);
605 while (res > 0) {
606 W_REG(osh, &cc->nand_cache_addr, 0);
607 for (i = 0; i < nflash.pagesize; i += 4, from++) {
608 if (i % 512 == 0)
609 W_REG(osh, &cc->nand_cmd_addr, i);
610 W_REG(osh, &cc->nand_cache_data, *from);
612 W_REG(osh, &cc->nand_cmd_addr, offset + nflash.pagesize - 512);
613 nflash_cmd(osh, cc, NCMD_PAGE_PROG);
614 if (nflash_poll(sih, cc) < 0)
615 break;
617 /* Check status */
618 W_REG(osh, &cc->nand_cmd_start, NCMD_STATUS_RD);
619 if (nflash_poll(sih, cc) < 0)
620 break;
621 status = R_REG(osh, &cc->nand_intfc_status) & NIST_STATUS;
622 if (status & 1) {
623 ret = -1;
624 break;
626 res -= nflash.pagesize;
627 offset += nflash.pagesize;
630 nflash_enable(sih, 0);
631 if (ret)
632 return ret;
635 return (len - res);
638 /* Erase a region. Returns number of bytes scheduled for erasure.
639 * Caller should poll for completion.
642 nflash_erase(si_t *sih, chipcregs_t *cc, uint offset)
644 osl_t *osh;
645 int ret = 0;
646 uint8 status = 0;
648 ASSERT(sih);
650 osh = si_osh(sih);
651 if ((offset >> 20) >= nflash.size)
652 return -1;
653 if ((offset & (nflash.blocksize - 1)) != 0) {
654 return -1;
657 if (CHIPID(sih->chip) == BCM4706_CHIP_ID) {
658 uint32 ctrlcode;
659 uint32 row_addr;
661 row_addr = (offset & ~(nflash.blocksize - 1)) * 2;
663 W_REG(osh, &cc->nflashrowaddr, row_addr >> nflash_row_shift);
665 ctrlcode = (NFC_CMD1W | NFC_ROW | NFC_CMD0 | NFCTRL_ERASE);
667 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
668 return -1;
670 if (nflash_poll(sih, cc) < 0)
671 return -1;
673 if (nflash_readst(sih, cc, &status) != 0)
674 return -1;
676 if (status & 1) {
677 printf("nflash_erase: failed with status 0x%02x\n", status);
678 return -1;
680 } else {
681 nflash_enable(sih, 1);
682 W_REG(osh, &cc->nand_cmd_addr, offset);
683 nflash_cmd(osh, cc, NCMD_BLOCK_ERASE);
684 if (nflash_poll(sih, cc) < 0) {
685 ret = -1;
686 goto err;
688 /* Check status */
689 W_REG(osh, &cc->nand_cmd_start, NCMD_STATUS_RD);
690 if (nflash_poll(sih, cc) < 0) {
691 ret = -1;
692 goto err;
694 status = R_REG(osh, &cc->nand_intfc_status) & NIST_STATUS;
695 if (status & 1)
696 ret = -1;
697 err:
698 nflash_enable(sih, 0);
701 return ret;
705 nflash_checkbadb(si_t *sih, chipcregs_t *cc, uint offset)
707 osl_t *osh;
708 int i;
709 uint off;
710 uint32 nand_intfc_status;
711 int ret = 0;
713 ASSERT(sih);
715 osh = si_osh(sih);
716 if ((offset >> 20) >= nflash.size)
717 return -1;
718 if ((offset & (nflash.blocksize - 1)) != 0) {
719 return -1;
722 if (CHIPID(sih->chip) == BCM4706_CHIP_ID) {
723 uint8 oob_buf1[8], oob_buf2[8];
724 uint sec_page_offset;
725 int status1, status2, badblk_pos;
727 status1 = nflash_readoob(sih, cc, offset, sizeof(oob_buf1), oob_buf1);
729 sec_page_offset = offset + nflash.pagesize;
730 status2 = nflash_readoob(sih, cc, sec_page_offset, sizeof(oob_buf2), oob_buf2);
732 if (status1 <= 0 || status2 <= 0)
733 return -1;
735 /* Set the bad block position */
736 if (nflash.pagesize > 512)
737 badblk_pos = NF_LARGE_BADBLOCK_POS;
738 else
739 badblk_pos = NF_SMALL_BADBLOCK_POS;
741 if (oob_buf1[badblk_pos] != 0xff || oob_buf2[badblk_pos] != 0xff)
742 return -1;
743 else
744 return 0;
745 } else {
746 nflash_enable(sih, 1);
747 for (i = 0; i < 2; i++) {
748 off = offset + (nflash.pagesize * i);
749 W_REG(osh, &cc->nand_cmd_addr, off);
750 nflash_cmd(osh, cc, NCMD_SPARE_RD);
751 if (nflash_poll(sih, cc) < 0) {
752 ret = -1;
753 goto err;
755 nand_intfc_status = R_REG(osh, &cc->nand_intfc_status) & NIST_SPARE_VALID;
756 if (nand_intfc_status != NIST_SPARE_VALID) {
757 ret = -1;
758 goto err;
760 if ((R_REG(osh, &cc->nand_spare_rd0) & 0xff) != 0xff) {
761 ret = -1;
762 goto err;
765 err:
766 nflash_enable(sih, 0);
767 return ret;
772 nflash_mark_badb(si_t *sih, chipcregs_t *cc, uint offset)
774 osl_t *osh;
775 uint off;
776 int i, ret = 0;
777 uint32 reg;
779 ASSERT(sih);
781 osh = si_osh(sih);
782 if ((offset >> 20) >= nflash.size)
783 return -1;
784 if ((offset & (nflash.blocksize - 1)) != 0) {
785 return -1;
788 if (CHIPID(sih->chip) == BCM4706_CHIP_ID) {
789 unsigned char oob_buf[128];
791 /* Force OOB write even nflash_erase return failure */
792 nflash_erase(sih, cc, offset);
794 memset((void *)oob_buf, 0, nflash.oobsize);
796 if (nflash_writeoob(sih, cc, offset, nflash.oobsize, oob_buf) < 0)
797 return -1;
799 if (nflash_writeoob(sih, cc, offset + nflash.pagesize, nflash.oobsize, oob_buf) < 0)
800 return -1;
801 } else {
802 nflash_enable(sih, 1);
803 /* Erase block */
804 W_REG(osh, &cc->nand_cmd_addr, offset);
805 nflash_cmd(osh, cc, NCMD_BLOCK_ERASE);
806 if (nflash_poll(sih, cc) < 0) {
807 ret = -1;
808 goto err;
812 * Enable partial page programming and disable ECC checkbit generation
813 * for PROGRAM_SPARE_AREA
815 reg = R_REG(osh, &cc->nand_acc_control);
816 reg |= NAC_PARTIAL_PAGE_EN;
817 reg &= ~NAC_WR_ECC_EN;
818 W_REG(osh, &cc->nand_acc_control, reg);
820 for (i = 0; i < 2; i++) {
821 off = offset + (nflash.pagesize * i);
822 W_REG(osh, &cc->nand_cmd_addr, off);
824 W_REG(osh, &cc->nand_spare_wr0, 0);
825 W_REG(osh, &cc->nand_spare_wr4, 0);
826 W_REG(osh, &cc->nand_spare_wr8, 0);
827 W_REG(osh, &cc->nand_spare_wr12, 0);
829 nflash_cmd(osh, cc, NCMD_SPARE_PROG);
830 if (nflash_poll(sih, cc) < 0) {
831 ret = -1;
832 goto err;
835 err:
836 /* Restore the default value for spare area write registers */
837 W_REG(osh, &cc->nand_spare_wr0, 0xffffffff);
838 W_REG(osh, &cc->nand_spare_wr4, 0xffffffff);
839 W_REG(osh, &cc->nand_spare_wr8, 0xffffffff);
840 W_REG(osh, &cc->nand_spare_wr12, 0xffffffff);
843 * Disable partial page programming and enable ECC checkbit generation
844 * for PROGRAM_SPARE_AREA
846 reg = R_REG(osh, &cc->nand_acc_control);
847 reg &= ~NAC_PARTIAL_PAGE_EN;
848 reg |= NAC_WR_ECC_EN;
849 W_REG(osh, &cc->nand_acc_control, reg);
851 nflash_enable(sih, 0);
853 return ret;
857 nflash_readst(si_t *sih, chipcregs_t *cc, uint8 *status)
859 osl_t *osh;
860 int ret = 0;
862 ASSERT(sih);
864 osh = si_osh(sih);
866 if (CHIPID(sih->chip) == BCM4706_CHIP_ID) {
867 uint32 ctrlcode;
869 ctrlcode = (NFC_CSA | NFC_CMD0 | NFCTRL_STATUS);
871 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
872 return -1;
874 ctrlcode = (NFC_1BYTE | NFC_DREAD);
876 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
877 return -1;
879 *status = (uint8)(R_REG(osh, &cc->nflashdata) & 0xff);
881 return 0;
882 } else {
883 nflash_enable(sih, 1);
884 W_REG(osh, &cc->nand_cmd_start, NCMD_STATUS_RD);
886 if (nflash_poll(sih, cc) < 0)
887 ret = -1;
889 else
890 *status = (uint8)(R_REG(osh, &cc->nand_intfc_status) & NIST_STATUS);
892 nflash_enable(sih, 0);
893 return ret;
897 /* To read len bytes of oob data in the page specified in the page address offset */
899 nflash_readoob(si_t *sih, chipcregs_t *cc, uint offset, uint len, uchar *buf)
901 uint32 mask;
902 osl_t *osh;
903 int i;
904 uint32 *to;
905 uint res;
907 ASSERT(sih);
909 mask = nflash.pagesize - 1;
910 if ((offset & mask) != 0 || (len > nflash.oobsize) || (len & 0x3) != 0)
911 return -1;
913 osh = si_osh(sih);
914 to = (uint32 *)buf;
915 res = 0;
917 if (CHIPID(sih->chip) == BCM4706_CHIP_ID) {
918 uint32 page_addr, page_offset;
919 uint32 ctrlcode;
921 page_addr = (offset & ~(nflash.pagesize - 1)) * 2;
922 page_offset = nflash.pagesize;
923 page_addr += page_offset;
925 W_REG(osh, &cc->nflashcoladdr, page_addr & nflash_col_mask);
926 W_REG(osh, &cc->nflashrowaddr, page_addr >> nflash_row_shift);
928 ctrlcode = (NFC_CSA | NFC_CMD1W | NFC_ROW | NFC_COL | NFC_CMD0 | NFCTRL_READ);
930 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
931 return -1;
933 if (nflash_poll(sih, cc) < 0)
934 return -1;
936 for (i = 0; i < len; i += 4, to++) {
937 if (i < len - 4)
938 ctrlcode = (NFC_CSA | NFC_4BYTES | NFC_DREAD);
939 else
940 ctrlcode = (NFC_4BYTES | NFC_DREAD);
942 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
943 return -1;
945 *to = R_REG(osh, &cc->nflashdata);
947 res += 4;
949 } else
950 return -1;
952 return res;
955 /* To write len bytes of oob data in the page specified in the page address offset */
957 nflash_writeoob(si_t *sih, chipcregs_t *cc, uint offset, uint len, uchar *buf)
959 uint32 mask;
960 osl_t *osh;
961 int i;
962 uint32 *from;
963 uint res;
965 ASSERT(sih);
967 mask = nflash.pagesize - 1;
968 if ((offset & mask) != 0 || (len > nflash.oobsize) || (len & 0x3) != 0)
969 return -1;
971 osh = si_osh(sih);
972 from = (uint32 *)buf;
973 res = 0;
975 if (CHIPID(sih->chip) == BCM4706_CHIP_ID) {
976 uint32 page_addr, page_offset;
977 uint32 ctrlcode;
978 uint8 status;
980 page_addr = (offset & ~(nflash.pagesize - 1)) * 2;
981 page_offset = nflash.pagesize;
982 page_addr += page_offset;
984 W_REG(osh, &cc->nflashcoladdr, page_addr & nflash_col_mask);
985 W_REG(osh, &cc->nflashrowaddr, page_addr >> nflash_row_shift);
987 ctrlcode = (NFC_CSA | NFC_ROW | NFC_COL | NFC_CMD0 | NFCTRL_PAGEPROG);
989 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
990 return -1;
992 for (i = 0; i < len; i += 4, from++) {
993 W_REG(osh, &cc->nflashdata, *from);
995 if (i < len - 4)
996 ctrlcode = (NFC_CSA | NFC_4BYTES | NFC_DWRITE);
997 else
998 ctrlcode = (NFC_4BYTES | NFC_DWRITE);
1000 if (nflash_ctrlcmd(osh, cc, ctrlcode) != 0)
1001 return -1;
1003 res += 4;
1006 if (nflash_ctrlcmd(osh, cc, (NFC_CMD0 | NFCTRL_PROGSTART)) != 0)
1007 return -1;
1009 if (nflash_poll(sih, cc) < 0)
1010 return -1;
1012 if (nflash_readst(sih, cc, &status) != 0)
1013 return -1;
1015 if (status & 1) {
1016 printf("nflash_writeoob: failed with status 0x%02x\n", status);
1017 return -1;
1020 } else
1021 return -1;
1023 return res;