753bab5d18c48edaa166d41934004262e76bfe44
2 * nand_read.c: Simple NAND read functions for booting from NAND
4 * This is used by cpu/arm920/start.S assembler code,
5 * and the board-specific linker script must make sure this
6 * file is linked within the first 4kB of NAND flash.
8 * Taken from GPLv2 licensed vivi bootloader,
9 * Copyright (C) 2002 MIZI Research, Inc.
11 * Author: Hwang, Chideok <hwang@mizi.com>
12 * Date : $Date: 2004/02/04 10:37:37 $
14 * u-boot integration and bad-block skipping (C) 2006 by OpenMoko, Inc.
15 * Author: Harald Welte <laforge@openmoko.org>
19 #include <linux/mtd/nand.h>
21 #ifdef CONFIG_S3C2410_NAND_BOOT
23 #define __REGb(x) (*(volatile unsigned char *)(x))
24 #define __REGw(x) (*(volatile unsigned short *)(x))
25 #define __REGi(x) (*(volatile unsigned int *)(x))
26 #define NF_BASE 0x4e000000
27 #if defined(CONFIG_S3C2410)
28 #define NFCONF __REGi(NF_BASE + 0x0)
29 #define NFCMD __REGb(NF_BASE + 0x4)
30 #define NFADDR __REGb(NF_BASE + 0x8)
31 #define NFDATA __REGb(NF_BASE + 0xc)
32 #define NFSTAT __REGb(NF_BASE + 0x10)
34 #define nand_select() (NFCONF &= ~0x800)
35 #define nand_deselect() (NFCONF |= 0x800)
36 #define nand_clear_RnB() do {} while (0)
37 #elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
38 #define NFCONF __REGi(NF_BASE + 0x0)
39 #define NFCONT __REGi(NF_BASE + 0x4)
40 #define NFCMD __REGb(NF_BASE + 0x8)
41 #define NFADDR __REGb(NF_BASE + 0xc)
42 #define NFDATA __REGb(NF_BASE + 0x10)
43 #define NFDATA16 __REGw(NF_BASE + 0x10)
44 #define NFSTAT __REGb(NF_BASE + 0x20)
46 #define nand_select() (NFCONT &= ~(1 << 1))
47 #define nand_deselect() (NFCONT |= (1 << 1))
48 #define nand_clear_RnB() (NFSTAT |= (1 << 2))
51 static inline void nand_wait(void)
55 while (!(NFSTAT
& NFSTAT_BUSY
))
67 #if defined(CONFIG_S3C2410) || defined(CONFIG_MINI2440)
68 /* configuration for 2410 with 512byte sized flash */
69 #define NAND_PAGE_SIZE 512
70 #define BAD_BLOCK_OFFSET 5
71 #define NAND_BLOCK_MASK (NAND_PAGE_SIZE - 1)
72 #define NAND_BLOCK_SIZE 0x4000
74 /* configuration for 2440 with 2048byte sized flash */
75 #define NAND_5_ADDR_CYCLE
76 #define NAND_PAGE_SIZE 2048
77 #define BAD_BLOCK_OFFSET NAND_PAGE_SIZE
78 #define NAND_BLOCK_MASK (NAND_PAGE_SIZE - 1)
79 #define NAND_BLOCK_SIZE (NAND_PAGE_SIZE * 64)
82 /* compile time failure in case of an invalid configuration */
83 #if defined(CONFIG_S3C2410) && (NAND_PAGE_SIZE != 512)
84 #error "S3C2410 does not support nand page size != 512"
88 static int is_bad_block(struct boot_nand_t
* nand
, unsigned long i
)
91 unsigned long page_num
;
94 if (nand
->page_size
== 512) {
95 NFCMD
= NAND_CMD_READOOB
; /* 0x50 */
96 NFADDR
= nand
->bad_block_offset
& 0xf;
97 NFADDR
= (i
>> 9) & 0xff;
98 NFADDR
= (i
>> 17) & 0xff;
99 NFADDR
= (i
>> 25) & 0xff;
100 } else if (nand
->page_size
== 2048) {
101 page_num
= i
>> 11; /* addr / 2048 */
102 NFCMD
= NAND_CMD_READ0
;
103 NFADDR
= nand
->bad_block_offset
& 0xff;
104 NFADDR
= (nand
->bad_block_offset
>> 8) & 0xff;
105 NFADDR
= page_num
& 0xff;
106 NFADDR
= (page_num
>> 8) & 0xff;
107 NFADDR
= (page_num
>> 16) & 0xff;
108 NFCMD
= NAND_CMD_READSTART
;
113 data
= (NFDATA
& 0xff);
120 static int nand_read_page_ll(struct boot_nand_t
* nand
, unsigned char *buf
, unsigned long addr
)
122 unsigned short *ptr16
= (unsigned short *)buf
;
123 unsigned int i
, page_num
;
127 NFCMD
= NAND_CMD_READ0
;
129 if (nand
->page_size
== 512) {
131 NFADDR
= addr
& 0xff;
132 NFADDR
= (addr
>> 9) & 0xff;
133 NFADDR
= (addr
>> 17) & 0xff;
134 NFADDR
= (addr
>> 25) & 0xff;
135 } else if (nand
->page_size
== 2048) {
136 page_num
= addr
>> 11; /* addr / 2048 */
140 NFADDR
= page_num
& 0xff;
141 NFADDR
= (page_num
>> 8) & 0xff;
142 NFADDR
= (page_num
>> 16) & 0xff;
143 NFCMD
= NAND_CMD_READSTART
;
149 #if defined(CONFIG_S3C2410)
150 for (i
= 0; i
< nand
->page_size
; i
++) {
151 *buf
= (NFDATA
& 0xff);
154 #elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
155 for (i
= 0; i
< (nand
->page_size
>>1); i
++) {
161 return nand
->page_size
;
164 static unsigned short nand_read_id()
166 unsigned short res
= 0;
167 NFCMD
= NAND_CMD_READID
;
170 res
= (res
<< 8) | NFDATA
;
174 extern unsigned int dynpart_size
[];
176 /* low level nand read function */
177 int nand_read_ll(unsigned char *buf
, unsigned long start_addr
, int size
)
180 unsigned short nand_id
;
181 struct boot_nand_t nand
;
187 for (i
= 0; i
< 10; i
++)
189 nand_id
= nand_read_id();
190 if (0) { /* dirty little hack to detect if nand id is misread */
191 unsigned short * nid
= (unsigned short *)0x31fffff0;
195 if (nand_id
== 0xec76) { /* Samsung K91208 */
196 nand
.page_size
= 512;
197 nand
.block_size
= 16 * 1024;
198 nand
.bad_block_offset
= 5;
199 nand
.size
= 0x4000000;
200 } else if (nand_id
== 0xecf1) { /* Samsung K9F1G08U0B */
201 nand
.page_size
= 2048;
202 nand
.block_size
= 128 * 1024;
203 nand
.bad_block_offset
= nand
.page_size
;
204 nand
.size
= 0x8000000;
208 if ((start_addr
& (nand
.block_size
-1)) || (size
& ((nand
.block_size
-1))))
209 return -1; /* invalid alignment */
211 for (i
=start_addr
; i
< (start_addr
+ size
);) {
212 #ifdef CONFIG_S3C2410_NAND_SKIP_BAD
213 if (i
& (nand
.block_size
-1)== 0) {
214 if (is_bad_block(&nand
, i
) ||
215 is_bad_block(&nand
, i
+ nand
.page_size
)) {
217 i
+= nand
.block_size
;
218 size
+= nand
.block_size
;
223 j
= nand_read_page_ll(&nand
, buf
, i
);
234 #endif /* CONFIG_S3C2410_NAND_BOOT */