1 | /* |
---|
2 | * Board/device specific connection between the generic |
---|
3 | * nand command and the CSB740's NAND memory access through |
---|
4 | * the OMAP3530. |
---|
5 | * |
---|
6 | * The CSB740 uses K9K4GO8UOM NAND (512Mx8bit) device from Samsung. |
---|
7 | * It is connected to the OMAP3530's CS3 through the CPLD. |
---|
8 | * The device ID for this part number should be: 0xECDC109554 |
---|
9 | * |
---|
10 | * Maker code: 0xEC |
---|
11 | * Device code: 0xDC |
---|
12 | * Third cycle: 0x10 |
---|
13 | * Fourth cycle: 0x95 |
---|
14 | * Fifth cycle: 0x54 |
---|
15 | * |
---|
16 | * Blocksize: 128K (smallest eraseable chunk) |
---|
17 | * Pagesize: 2K (64 pages per block) |
---|
18 | * |
---|
19 | * Taken from the NAND datasheet... |
---|
20 | * The 528M byte physical space requires 30 addresses, thereby |
---|
21 | * requiring five cycles for addressing : |
---|
22 | * 2 cycles of column address, 3 cycles of row address, in that |
---|
23 | * order. Page Read and Page Program need the same five address |
---|
24 | * cycles following the required command input. In Block Erase |
---|
25 | * operation, however, only the three row address cycles are used. |
---|
26 | * Device operations are selected by writing specific commands into |
---|
27 | * the command register. |
---|
28 | * |
---|
29 | * Taken from ONFI Spec: |
---|
30 | * The address is comprised of a row address and a column address. |
---|
31 | * The row address identifies the page, block, and LUN to be accessed. |
---|
32 | * The column address identifies the byte or word within a page to access. |
---|
33 | * |
---|
34 | * Note: |
---|
35 | * Apparently this Sansung device is not ONFI compliant. There was an |
---|
36 | * earlier version of the CSB740 that had a Micron part on it and it was |
---|
37 | * ONFI compliant (ONFI=Open Nand Flash Interface specification). |
---|
38 | * |
---|
39 | */ |
---|
40 | #include "config.h" |
---|
41 | #if INCLUDE_NANDCMD |
---|
42 | #include "stddefs.h" |
---|
43 | #include "genlib.h" |
---|
44 | #include "omap3530.h" |
---|
45 | #include "omap3530_mem.h" |
---|
46 | #include "nand.h" |
---|
47 | |
---|
48 | static int pgSiz; |
---|
49 | static int blkSiz; |
---|
50 | static char onfi; |
---|
51 | |
---|
52 | #define NANDSTAT_FAIL 0x01 |
---|
53 | #define NANDSTAT_READY 0x40 |
---|
54 | #define NANDSTAT_NOTWP 0x80 |
---|
55 | |
---|
56 | #define NAND_CMD(cs) (vuchar *)(0x6e00007C + (0x30 * cs)) |
---|
57 | #define NAND_ADR(cs) (vuchar *)(0x6e000080 + (0x30 * cs)) |
---|
58 | #define NAND_DAT(cs) (vuchar *)(0x6e000084 + (0x30 * cs)) |
---|
59 | |
---|
60 | #define PREFETCH_READ_MODE() GPMC_REG(GPMC_PREFETCH_CONFIG1) &= ~1 |
---|
61 | #define WRITE_POSTING_MODE() GPMC_REG(GPMC_PREFETCH_CONFIG1) |= 1 |
---|
62 | |
---|
63 | #define NAND_CS_BASEADDR 0x18000000 |
---|
64 | |
---|
65 | /* some discussion to follow: |
---|
66 | * http://e2e.ti.com/support/dsp/omap_applications_processors/f/42/p/29683/103192.aspx |
---|
67 | * Address is "page/block/column"... |
---|
68 | */ |
---|
69 | |
---|
70 | /* nandBusyWait(): |
---|
71 | * Poll the WAIT3 bit of the gmpc-status register, waiting for |
---|
72 | * it to go active. |
---|
73 | * The R/B (ready/busy) pin of the NAND device is low when busy. |
---|
74 | * It is tied to WAIT3 of the CPU. |
---|
75 | */ |
---|
76 | void |
---|
77 | nandHardBusyWait(void) |
---|
78 | { |
---|
79 | while((GPMC_REG(GPMC_STATUS) & 0x800) == 0x800); |
---|
80 | //while((GPMC_REG(GPMC_STATUS) & 0x800) == 0); |
---|
81 | } |
---|
82 | |
---|
83 | void |
---|
84 | nandSoftBusyWait(void) |
---|
85 | { |
---|
86 | do { |
---|
87 | *NAND_CMD(3) = 0x70; |
---|
88 | } while((*NAND_DAT(3) & NANDSTAT_READY) == 0); |
---|
89 | } |
---|
90 | |
---|
91 | void |
---|
92 | nandSetColAddr(unsigned long addr) |
---|
93 | { |
---|
94 | // Column1 (A07|A06|A05|A04|A03|A02|A01|A00): |
---|
95 | *NAND_ADR(3) = ((addr & 0x000000ff)); |
---|
96 | |
---|
97 | // Column2 (---|---|---|---|A11|A10|A09|A08): |
---|
98 | *NAND_ADR(3) = ((addr & 0x00000f00) >> 8); |
---|
99 | } |
---|
100 | |
---|
101 | void |
---|
102 | nandSetRowAddr(unsigned long addr) |
---|
103 | { |
---|
104 | // Row1 (A19|A18|A17|A16|A15|A14|A13|A12): |
---|
105 | *NAND_ADR(3) = ((addr & 0x000ff000) >> 12); |
---|
106 | |
---|
107 | // Row2 (A27|A26|A25|A24|A23|A22|A21|A20): |
---|
108 | *NAND_ADR(3) = ((addr & 0x0ff00000) >> 20); |
---|
109 | |
---|
110 | // Row3 (---|---|---|---|---|---|A29|A28): |
---|
111 | *NAND_ADR(3) = ((addr & 0x30000000) >> 28); |
---|
112 | } |
---|
113 | |
---|
114 | /* nandReadChunk(): |
---|
115 | * Transfer some chunk of memory from NAND to a destination. |
---|
116 | */ |
---|
117 | int |
---|
118 | nandReadChunk(char *src, char *dest, int len) |
---|
119 | { |
---|
120 | unsigned long addr = (long)src; |
---|
121 | |
---|
122 | PREFETCH_READ_MODE(); |
---|
123 | |
---|
124 | if (nandVerbose) |
---|
125 | printf("nandReadChunk(src=0x%x,dest=0x%x,len=%d)\n",src,dest,len); |
---|
126 | |
---|
127 | while(len > 0) { |
---|
128 | int tot; |
---|
129 | |
---|
130 | *NAND_CMD(3) = 0x00; |
---|
131 | nandSetColAddr(addr); |
---|
132 | nandSetRowAddr(addr); |
---|
133 | *NAND_CMD(3) = 0x30; |
---|
134 | |
---|
135 | nandHardBusyWait(); |
---|
136 | |
---|
137 | tot = len > pgSiz ? pgSiz : len; |
---|
138 | memcpy(dest,(char *)NAND_CS_BASEADDR,tot); |
---|
139 | |
---|
140 | len -= tot; |
---|
141 | dest += tot; |
---|
142 | } |
---|
143 | return(0); |
---|
144 | } |
---|
145 | |
---|
146 | int |
---|
147 | nandWriteChunk(char *dest, char *src, int len) |
---|
148 | { |
---|
149 | unsigned long addr = (long)dest; |
---|
150 | |
---|
151 | WRITE_POSTING_MODE(); |
---|
152 | |
---|
153 | if (nandVerbose) |
---|
154 | printf("nandWriteBlock(dest=0x%x,src=0x%x,len=%d)\n",dest,src,len); |
---|
155 | |
---|
156 | *NAND_CMD(3) = 0x80; |
---|
157 | nandSetColAddr(addr); |
---|
158 | nandSetRowAddr(addr); |
---|
159 | memcpy((char *)NAND_CS_BASEADDR,src,len); |
---|
160 | *NAND_CMD(3) = 0x10; |
---|
161 | |
---|
162 | nandSoftBusyWait(); |
---|
163 | |
---|
164 | return(0); |
---|
165 | } |
---|
166 | |
---|
167 | int |
---|
168 | nandEraseChunk(char *base, int len) |
---|
169 | { |
---|
170 | unsigned long addr = (long)base; |
---|
171 | |
---|
172 | if (nandVerbose) |
---|
173 | printf("nandEraseChunk(addr=0x%x,len=%d)\n",addr,len); |
---|
174 | |
---|
175 | *NAND_CMD(3) = 0x60; |
---|
176 | nandSetRowAddr(addr); |
---|
177 | *NAND_CMD(3) = 0xd0; |
---|
178 | |
---|
179 | nandSoftBusyWait(); |
---|
180 | |
---|
181 | return(0); |
---|
182 | } |
---|
183 | |
---|
184 | void |
---|
185 | nandId(void) |
---|
186 | { |
---|
187 | uchar d[5]; |
---|
188 | uchar d1[4]; |
---|
189 | |
---|
190 | *NAND_CMD(3) = 0x90; |
---|
191 | *NAND_ADR(3) = 0x00; |
---|
192 | d[0] = *NAND_DAT(3); |
---|
193 | d[1] = *NAND_DAT(3); |
---|
194 | d[2] = *NAND_DAT(3); |
---|
195 | d[3] = *NAND_DAT(3); |
---|
196 | d[4] = *NAND_DAT(3); |
---|
197 | |
---|
198 | switch(d[3] & 3) { |
---|
199 | case 0: |
---|
200 | pgSiz = 1024; |
---|
201 | break; |
---|
202 | case 1: |
---|
203 | pgSiz = 1024*2; |
---|
204 | break; |
---|
205 | case 2: |
---|
206 | pgSiz = 1024*4; |
---|
207 | break; |
---|
208 | case 3: |
---|
209 | pgSiz = 1024*8; |
---|
210 | break; |
---|
211 | } |
---|
212 | switch((d[3] & 0x30) >> 4) { |
---|
213 | case 0: |
---|
214 | blkSiz = 1024*64; |
---|
215 | break; |
---|
216 | case 1: |
---|
217 | blkSiz = 1024*128; |
---|
218 | break; |
---|
219 | case 2: |
---|
220 | blkSiz = 1024*256; |
---|
221 | break; |
---|
222 | case 3: |
---|
223 | blkSiz = 1024*512; |
---|
224 | break; |
---|
225 | } |
---|
226 | |
---|
227 | *NAND_CMD(3) = 0x90; |
---|
228 | *NAND_ADR(3) = 0x20; |
---|
229 | d1[0] = *NAND_DAT(3); |
---|
230 | d1[1] = *NAND_DAT(3); |
---|
231 | d1[2] = *NAND_DAT(3); |
---|
232 | d1[3] = *NAND_DAT(3); |
---|
233 | if (memcmp((char *)d1,"ONFI",4) == 0) |
---|
234 | onfi = 1; |
---|
235 | else |
---|
236 | onfi = 0; |
---|
237 | |
---|
238 | if (nandVerbose) { |
---|
239 | printf("nandID(): %02x%02x%02x%02x%02x\n",d[0],d[1],d[2],d[3],d[4]); |
---|
240 | printf("nandID+(): %02x%02x%02x%02x%02x\n",d1[0],d1[1],d1[2],d1[3]); |
---|
241 | printf("Page size: 0x%x\n",pgSiz); |
---|
242 | printf("Block size: 0x%x\n",blkSiz); |
---|
243 | printf("%sONFI compliant\n",onfi ? "" : "Not "); |
---|
244 | } |
---|
245 | } |
---|
246 | |
---|
247 | int |
---|
248 | nandInit(void) |
---|
249 | { |
---|
250 | vulong cfgreg; |
---|
251 | |
---|
252 | #if 0 |
---|
253 | printf("CFG1: dm -4 0x%08x 1 = %08x\n", |
---|
254 | MYGPMC_REG(GPMC_CS3_CONFIG1),GPMC_REG(GPMC_CS3_CONFIG1)); |
---|
255 | printf("CFG2: dm -4 0x%08x 1 = %08x\n", |
---|
256 | MYGPMC_REG(GPMC_CS3_CONFIG2),GPMC_REG(GPMC_CS3_CONFIG2)); |
---|
257 | printf("CFG3: dm -4 0x%08x 1 = %08x\n", |
---|
258 | MYGPMC_REG(GPMC_CS3_CONFIG3),GPMC_REG(GPMC_CS3_CONFIG3)); |
---|
259 | printf("CFG4: dm -4 0x%08x 1 = %08x\n", |
---|
260 | MYGPMC_REG(GPMC_CS3_CONFIG4),GPMC_REG(GPMC_CS3_CONFIG4)); |
---|
261 | printf("CFG5: dm -4 0x%08x 1 = %08x\n", |
---|
262 | MYGPMC_REG(GPMC_CS3_CONFIG5),GPMC_REG(GPMC_CS3_CONFIG5)); |
---|
263 | printf("CFG6: dm -4 0x%08x 1 = %08x\n", |
---|
264 | MYGPMC_REG(GPMC_CS3_CONFIG6),GPMC_REG(GPMC_CS3_CONFIG6)); |
---|
265 | printf("CFG7: dm -4 0x%08x 1 = %08x\n", |
---|
266 | MYGPMC_REG(GPMC_CS3_CONFIG7),GPMC_REG(GPMC_CS3_CONFIG7)); |
---|
267 | #endif |
---|
268 | |
---|
269 | |
---|
270 | /* WAIT3 of the CPU is tied to the NAND's READY pin. The NAND |
---|
271 | * is on CS3. Referring to section 11.1.7.2.10 of the OMAP3530 TRM, |
---|
272 | * the GPMC_CONFIGX registers must be programmed... |
---|
273 | */ |
---|
274 | GPMC_REG(GPMC_CONFIG) |= 0x1; // Force posted write. |
---|
275 | GPMC_REG(GPMC_CONFIG) &= ~0x800; // WAIT3 active low. |
---|
276 | GPMC_REG(GPMC_CS3_CONFIG7) = 0x00000858; // Base addr 0x18000000 |
---|
277 | GPMC_REG(GPMC_CS3_CONFIG1) = 0x00030800; // 8-bit NAND, WAIT3 |
---|
278 | GPMC_REG(GPMC_CS3_CONFIG2) = 0x00000000; // Chipselect timing |
---|
279 | GPMC_REG(GPMC_CS3_CONFIG3) |= 0x7; |
---|
280 | GPMC_REG(GPMC_CS3_CONFIG6) = 0x8f0307c0; |
---|
281 | |
---|
282 | // Set drive strength of BE0/CLE |
---|
283 | *(vulong *)0x48002444 |= 0x00000020; |
---|
284 | |
---|
285 | /*********************************************************** |
---|
286 | * |
---|
287 | * ALE and CLE on the NAND are both active high, so we want |
---|
288 | * them to be pulled low... |
---|
289 | * |
---|
290 | * ALE config is the low half of this config register, so we only |
---|
291 | * touch the bottom half... |
---|
292 | */ |
---|
293 | cfgreg = SCM_REG(PADCONFS_GPMC_NADV_ALE); // NOE[31:16], NADV_ALE[15:0] |
---|
294 | cfgreg &= 0xffff0000; |
---|
295 | cfgreg |= 0x00000008; |
---|
296 | SCM_REG(PADCONFS_GPMC_NADV_ALE) = cfgreg; |
---|
297 | /* |
---|
298 | * CLE config is the upper half of this config register, so we only |
---|
299 | * touch the upper half... |
---|
300 | */ |
---|
301 | cfgreg = SCM_REG(PADCONFS_GPMC_NWE); // NBE0_CLE[31:16], NWE[15:0] |
---|
302 | cfgreg &= 0x0000ffff; |
---|
303 | cfgreg |= 0x00080000; |
---|
304 | SCM_REG(PADCONFS_GPMC_NWE) = cfgreg; |
---|
305 | |
---|
306 | |
---|
307 | /* WAIT3 of CPU is tied to R/B pin of NAND... |
---|
308 | * So, we configure that pin to run as WAIT3. |
---|
309 | * NOTE: |
---|
310 | * There is some confusion between this and what is in cpuio.c. |
---|
311 | * The PADCONFS_GPMC_WAIT2 register sets this pin as GPIO-65 there; |
---|
312 | * however the comments are confusing. |
---|
313 | */ |
---|
314 | cfgreg = SCM_REG(PADCONFS_GPMC_WAIT2); // WAIT3[31:16], WAIT2[15:0] |
---|
315 | cfgreg &= 0x0000ffff; |
---|
316 | cfgreg |= 0x00080000; |
---|
317 | SCM_REG(PADCONFS_GPMC_NWE) = cfgreg; |
---|
318 | |
---|
319 | nandId(); |
---|
320 | return(0); |
---|
321 | } |
---|
322 | |
---|
323 | int |
---|
324 | nandInfo(void) |
---|
325 | { |
---|
326 | nandId(); |
---|
327 | return(0); |
---|
328 | } |
---|
329 | |
---|
330 | |
---|
331 | #endif |
---|
332 | |
---|
333 | |
---|