MINI2440: Updated early nand loader
[u-boot-openmoko/mini2440.git] / cpu / arm920t / s3c24x0 / nand_read.c
blob7624c2f969fdb13f47f3f9328a7efeb44417e2fa
1 /*
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>
18 #include <common.h>
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)
33 #define NFSTAT_BUSY 1
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)
45 #define NFSTAT_BUSY 1
46 #define nand_select() (NFCONT &= ~(1 << 1))
47 #define nand_deselect() (NFCONT |= (1 << 1))
48 #define nand_clear_RnB() (NFSTAT |= (1 << 2))
49 #endif
51 static inline void nand_wait(void)
53 int i;
55 while (!(NFSTAT & NFSTAT_BUSY))
56 for (i=0; i<10; i++);
59 struct boot_nand_t {
60 int page_size;
61 int block_size;
62 int bad_block_offset;
63 int small_address;
66 #if 0
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
73 #else
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)
80 #endif
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"
85 #endif
86 #endif
88 static int is_bad_block(struct boot_nand_t * nand, unsigned long i)
90 unsigned char data;
91 unsigned long page_num;
93 nand_clear_RnB();
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 if (!nand->small_address)
100 NFADDR = (i >> 25) & 0xff;
101 } else if (nand->page_size == 2048) {
102 page_num = i >> 11; /* addr / 2048 */
103 NFCMD = NAND_CMD_READ0;
104 NFADDR = nand->bad_block_offset & 0xff;
105 NFADDR = (nand->bad_block_offset >> 8) & 0xff;
106 NFADDR = page_num & 0xff;
107 NFADDR = (page_num >> 8) & 0xff;
108 NFADDR = (page_num >> 16) & 0xff;
109 NFCMD = NAND_CMD_READSTART;
110 } else {
111 return -1;
113 nand_wait();
114 data = (NFDATA & 0xff);
115 if (data != 0xff)
116 return 1;
118 return 0;
121 static int nand_read_page_ll(struct boot_nand_t * nand, unsigned char *buf, unsigned long addr)
123 unsigned short *ptr16 = (unsigned short *)buf;
124 unsigned int i, page_num;
126 nand_clear_RnB();
128 NFCMD = NAND_CMD_READ0;
130 if (nand->page_size == 512) {
131 /* Write Address */
132 NFADDR = addr & 0xff;
133 NFADDR = (addr >> 9) & 0xff;
134 NFADDR = (addr >> 17) & 0xff;
135 if (!nand->small_address)
136 NFADDR = (addr >> 25) & 0xff;
137 } else if (nand->page_size == 2048) {
138 page_num = addr >> 11; /* addr / 2048 */
139 /* Write Address */
140 NFADDR = 0;
141 NFADDR = 0;
142 NFADDR = page_num & 0xff;
143 NFADDR = (page_num >> 8) & 0xff;
144 NFADDR = (page_num >> 16) & 0xff;
145 NFCMD = NAND_CMD_READSTART;
146 } else {
147 return -1;
149 nand_wait();
151 #if defined(CONFIG_S3C2410)
152 for (i = 0; i < nand->page_size; i++) {
153 *buf = (NFDATA & 0xff);
154 buf++;
156 #elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
157 for (i = 0; i < (nand->page_size>>1); i++) {
158 *ptr16 = NFDATA16;
159 ptr16++;
161 #endif
163 return nand->page_size;
166 static unsigned short nand_read_id()
168 unsigned short res = 0;
169 NFCMD = NAND_CMD_READID;
170 NFADDR = 0;
171 res = NFDATA;
172 res = (res << 8) | NFDATA;
173 return res;
176 extern unsigned int dynpart_size[];
178 /* low level nand read function */
179 int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
181 int i, j;
182 unsigned short nand_id;
183 struct boot_nand_t nand;
185 /* chip Enable */
186 nand_select();
187 nand_clear_RnB();
189 for (i = 0; i < 10; i++)
191 nand_id = nand_read_id();
192 if (0) { /* dirty little hack to detect if nand id is misread */
193 unsigned short * nid = (unsigned short *)0x31fffff0;
194 *nid = nand_id;
197 if (nand_id == 0xec75 /* Samsung K90608 */ ) {
198 nand.page_size = 512;
199 nand.block_size = 16 * 1024;
200 nand.bad_block_offset = 5;
201 /* 32MiB nand only uses 3 bytes addresses */
202 nand.small_address = 1;
203 } else if (
204 nand_id == 0xec76 || /* Samsung K91208 */
205 nand_id == 0xad76 /* Hynix HY27US08121A */ ) {
206 nand.page_size = 512;
207 nand.block_size = 16 * 1024;
208 nand.bad_block_offset = 5;
209 nand.small_address = 0;
210 } else { /* Any other new Nand, like Samsung K9F1G08U0B */
211 nand.page_size = 2048;
212 nand.block_size = 128 * 1024;
213 nand.bad_block_offset = nand.page_size;
215 if ((start_addr & (nand.block_size-1)) || (size & ((nand.block_size-1))))
216 return -1; /* invalid alignment */
218 for (i=start_addr; i < (start_addr + size);) {
219 #ifdef CONFIG_S3C2410_NAND_SKIP_BAD
220 if (i & (nand.block_size-1)== 0) {
221 if (is_bad_block(&nand, i) ||
222 is_bad_block(&nand, i + nand.page_size)) {
223 /* Bad block */
224 i += nand.block_size;
225 size += nand.block_size;
226 continue;
229 #endif
230 j = nand_read_page_ll(&nand, buf, i);
231 i += j;
232 buf += j;
235 /* chip Disable */
236 nand_deselect();
238 return 0;
241 #endif /* CONFIG_S3C2410_NAND_BOOT */