diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/target/arm/rk27xx/nand-rk27xx.c | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/firmware/target/arm/rk27xx/nand-rk27xx.c b/firmware/target/arm/rk27xx/nand-rk27xx.c index c2c855efee..84e60a47e1 100644 --- a/firmware/target/arm/rk27xx/nand-rk27xx.c +++ b/firmware/target/arm/rk27xx/nand-rk27xx.c | |||
@@ -22,6 +22,400 @@ | |||
22 | #include "config.h" | 22 | #include "config.h" |
23 | #include "nand-target.h" | 23 | #include "nand-target.h" |
24 | 24 | ||
25 | #if 0 | ||
26 | /* This is for documentation purpose as FTL has not been reverse engineered yet | ||
27 | * Raw nand handling functions based on OF disassembly and partially inspired | ||
28 | * by Rockchip patent | ||
29 | */ | ||
30 | |||
31 | #define MAX_FLASH_NUM 4 | ||
32 | #define CMD_READ_STATUS 0x70 | ||
33 | #define CMD_RESET 0xFF | ||
34 | #define CMD_READ_ID 0x90 | ||
35 | #define READ_PAGE_CMD 0x30 | ||
36 | |||
37 | /* this is the struct OF uses */ | ||
38 | struct flashspec_t | ||
39 | { | ||
40 | uint8_t cache_prog; | ||
41 | uint8_t mul_plane; | ||
42 | uint8_t interleave; | ||
43 | uint8_t large; | ||
44 | uint8_t five; | ||
45 | uint8_t mlc; | ||
46 | uint8_t vendor; | ||
47 | uint8_t access_time; | ||
48 | uint8_t sec_per_page; | ||
49 | uint8_t sec_per_page_raw; | ||
50 | uint16_t sec_per_block; | ||
51 | uint16_t sec_per_block_raw; | ||
52 | uint16_t page_per_block; | ||
53 | uint16_t page_per_block_raw; | ||
54 | |||
55 | uint32_t tot_logic_sec; | ||
56 | uint32_t total_phy_sec; | ||
57 | uint32_t total_bloks; | ||
58 | |||
59 | uint32_t cmd; | ||
60 | uint32_t addr; | ||
61 | uint32_t data; | ||
62 | }; | ||
63 | |||
64 | /* holds nand chips characteristics */ | ||
65 | struct flashspec_t flash_spec[MAX_FLASH_NUM]; | ||
66 | |||
67 | /* sum of all phy sectors in all chips */ | ||
68 | uint32_t total_phy_sec; | ||
69 | |||
70 | enum vendor_t { | ||
71 | SAMSUNG, | ||
72 | TOSHIBA, | ||
73 | HYNIX, | ||
74 | INFINEON, | ||
75 | MICRON, | ||
76 | RENESAS, | ||
77 | ST | ||
78 | }; | ||
79 | |||
80 | /* taken from OF */ | ||
81 | const uint8_t device_code[] = { | ||
82 | 0x76, | ||
83 | 0x79, | ||
84 | 0xf1, | ||
85 | 0xda, | ||
86 | 0xdc, | ||
87 | 0xd3, | ||
88 | 0xd7 | ||
89 | }; | ||
90 | |||
91 | const uint8_t manufacture_id_tbl[] = | ||
92 | { | ||
93 | 0xec, /* SAMSUNG */ | ||
94 | 0x98, /* TOSHIBA */ | ||
95 | 0xad, /* HYNIX */ | ||
96 | 0xc1, /* INFINEON */ | ||
97 | 0x2c, /* MICRON */ | ||
98 | 0x07, /* RENESAS */ | ||
99 | 0x20 /* ST */ | ||
100 | }; | ||
101 | |||
102 | /* phisical sectors */ | ||
103 | const uint32_t device_info[] = | ||
104 | { | ||
105 | 0x20000, /* 64M, small page */ | ||
106 | 0x40000, /* 128M, small page */ | ||
107 | 0x40000, /* 128M, large page */ | ||
108 | 0x80000, /* 256M, large page */ | ||
109 | 0x100000, /* 512M, large page */ | ||
110 | 0x200000, /* 1G, large page */ | ||
111 | 0x400000, /* 2G, large page */ | ||
112 | 0x800000 /* 4G, large page */ | ||
113 | }; | ||
114 | |||
115 | static int flash_delay(int n) | ||
116 | { | ||
117 | volatile int cnt, i, j; | ||
118 | |||
119 | for (j=0; j<n; j++) | ||
120 | for (i=0; i<10000; i++) | ||
121 | cnt++; | ||
122 | |||
123 | return cnt; | ||
124 | } | ||
125 | |||
126 | |||
127 | static void flash_wait_busy(void) | ||
128 | { | ||
129 | unsigned int i; | ||
130 | |||
131 | for (i=0; i<0x2000; i++) | ||
132 | { | ||
133 | if (FMCTL & FM_RDY) | ||
134 | break; | ||
135 | |||
136 | flash_delay(1); | ||
137 | } | ||
138 | } | ||
139 | |||
140 | void flash_chip_deselect(void) | ||
141 | { | ||
142 | uint32_t tmp; | ||
143 | tmp = FMCTL; | ||
144 | tmp &= 0xf0; | ||
145 | FMCTL = tmp; | ||
146 | } | ||
147 | |||
148 | void flash_chip_select(uint8_t chip) | ||
149 | { | ||
150 | uint32_t tmp; | ||
151 | |||
152 | /* Maybe we should handle IOMUX here as well? | ||
153 | * for chip == 0,1 it is not needed | ||
154 | */ | ||
155 | tmp = FMCTL; | ||
156 | tmp &= 0xf0; | ||
157 | tmp |= 1<<chip; | ||
158 | FMCTL = tmp; | ||
159 | } | ||
160 | |||
161 | void flash_init(void) | ||
162 | { | ||
163 | uint8_t buff[5]; /* buff for CMD_READ_ID response */ | ||
164 | uint32_t i,j; | ||
165 | |||
166 | mlc_refresh_row = 0xffffffff; | ||
167 | total_phy_sec = 0; | ||
168 | flash_pend_cmd.valid = 0; | ||
169 | flash_read_status_cmd = CMD_READ_STATUS; | ||
170 | |||
171 | FMWAIT = 0x1081; | ||
172 | FLCTL = FL_RST; | ||
173 | |||
174 | for (i=0; i< MAX_FLASH_NUM; i++) | ||
175 | { | ||
176 | /* Redundat - we will use special macros | ||
177 | * just for reference what OF does | ||
178 | */ | ||
179 | flash_spec[i].cmd = 0x180E8200 + (i<<9); | ||
180 | flash_spec[i].addr = 0x180E204 + (i<<9); | ||
181 | flash_spec[i].data = 0x180E208 + (i<<9); | ||
182 | |||
183 | flash_chip_select(i); | ||
184 | FLASH_CMD(i) = CMD_RESET; /* write cmd to flash chip */ | ||
185 | flash_delay(2); | ||
186 | flash_wait_busy(); | ||
187 | FLASH_CMD(i) = CMD_READ_ID; /* write cmd to flash chip */ | ||
188 | FLASH_ADDR(i) = 0x00; | ||
189 | |||
190 | /* read 5 bytes of CMD_READ_ID response */ | ||
191 | for (j=0; j<5; j++) | ||
192 | buff[j] = FLASH_DATA(i); | ||
193 | |||
194 | flash_chip_deselect(); | ||
195 | |||
196 | /* Get the vendor of the chip */ | ||
197 | for (j=0; j<sizeof(manufacture_id_tbl); j++) | ||
198 | { | ||
199 | /* store Manufacturer index */ | ||
200 | if (ManufactureIDTbl[j] == buff[0]) | ||
201 | { | ||
202 | flash_spec[i].vendor = j; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | for (j=0; j<sizeof(device_code); j++) | ||
207 | { | ||
208 | /* look for matching device code | ||
209 | * and store total phys sectors | ||
210 | */ | ||
211 | if (DeviceCode[j] == buff[1]) | ||
212 | { | ||
213 | flash_spec[i].total_phy_sec = device_info[j]; | ||
214 | break; | ||
215 | } | ||
216 | } | ||
217 | |||
218 | /* div zero is fatal for us (not for OF :P) */ | ||
219 | if (flash_spec[i].total_phy_sec == 0) | ||
220 | continue; | ||
221 | |||
222 | /* loc_7e8 */ | ||
223 | flash_spec[i].mlc = 0; | ||
224 | flash_spec[i].large = 0; | ||
225 | flash_spec[i].five = 1; | ||
226 | flash_spec[i].mul_plane = 1; | ||
227 | flash_spec[i].interleave = 0; | ||
228 | |||
229 | flash_spec[i].cache_prog = buff[2] & 0x80; | ||
230 | |||
231 | /* flash access time (ns) */ | ||
232 | switch (buff[3] & 0x88) | ||
233 | { | ||
234 | case 0: | ||
235 | flash_spec[i].access_time = 50; | ||
236 | break; | ||
237 | case 0x80: | ||
238 | flash_spec[i].access_time = 25; | ||
239 | break; | ||
240 | case 0x08: | ||
241 | flash_spec[i].access_time = 20; | ||
242 | break; | ||
243 | default: | ||
244 | flash_spec[i].access_time = 60; | ||
245 | } | ||
246 | |||
247 | /* set_large | ||
248 | * j is index in device_code table | ||
249 | */ | ||
250 | if (j < 2) | ||
251 | { | ||
252 | /* small block */ | ||
253 | flash_spec[i].large = 0; | ||
254 | flash_spec[i].sec_per_page_raw = 1; | ||
255 | flash_spec[i].sec_per_block_raw = 32; | ||
256 | } | ||
257 | else | ||
258 | { | ||
259 | flash_spec[i].large = 1; | ||
260 | if (j == 2) | ||
261 | flash_spec[i].five = 0; | ||
262 | |||
263 | |||
264 | /* cell type */ | ||
265 | flash_spec[i].mlc = (buff[2] >> 2) & 0x03; | ||
266 | |||
267 | flash_spec[i].sec_per_page_raw = 2; /* 1KB~8KB */ | ||
268 | |||
269 | /* set_sec_per_page_raw */ | ||
270 | flash_spec[i].sec_per_page_raw <<= (buff[3] & 3); | ||
271 | |||
272 | flash_spec[i].sec_per_block_raw = 128; /* 64KB~512KB */ | ||
273 | |||
274 | /* set_sec_per_block_raw */ | ||
275 | flash_spec[i].sec_per_block_raw <<= ((buff[3]>>4) & 3); | ||
276 | |||
277 | /* simult_prog */ | ||
278 | if (buff[2] & 0x30) | ||
279 | { | ||
280 | /* buff4_mulplane */ | ||
281 | flash_spec[i].mul_plane <<= ((buff[4]>>2) & 3); | ||
282 | } | ||
283 | |||
284 | /* set_interleave */ | ||
285 | if (flash_spec[i].vendor == TOSHIBA) | ||
286 | { | ||
287 | flash_spec[i].mul_plane = 2; | ||
288 | if (buff[2] & 3) | ||
289 | flash_spec[i].interleave = 1; | ||
290 | } | ||
291 | |||
292 | } /* large block */ | ||
293 | |||
294 | if (flash_spec[i].mul_plane > 2) | ||
295 | { | ||
296 | flash_spec[i].mul_plane = 2; | ||
297 | flash_spec[i].interleave = 1; | ||
298 | } | ||
299 | |||
300 | flash_spec[i].page_per_block_raw = flash_spec[i].sec_per_block_raw/flash_spec[i].sec_per_page_raw; | ||
301 | flash_spec[i].page_per_block = flash_spec[i].page_per_block_raw * flash_spec[i].mul_plane; | ||
302 | flash_spec[i].sec_per_block = flash_spec[i].sec_per_block_raw * flash_spec[i].mul_plane; | ||
303 | flash_spec[i].sec_per_page = flash_spec[i].sec_per_page_raw * flash_spec[i].mul_plane; | ||
304 | flash_spec[i].total_bloks = flash_spec[i].total_phy_sec / flash_spec[i].sec_per_block; | ||
305 | |||
306 | total_phy_sec += flash_spec[i].total_phy_sec; | ||
307 | } | ||
308 | |||
309 | /* read ID block and propagate SysDiskCapacity and SysResBlocks */ | ||
310 | } | ||
311 | |||
312 | /* read single page in unbuffered mode */ | ||
313 | void flash_read_page(int page, unsigned char *pgbuff) | ||
314 | { | ||
315 | unsigned int i; | ||
316 | |||
317 | flash_chip_select(0); | ||
318 | flash_delay(2); | ||
319 | flash_wait_busy(); | ||
320 | |||
321 | /* setup transfer */ | ||
322 | FLASH_CMD(0) = 0x00; | ||
323 | FLASH_ADDR(0) = 0x00; /* column */ | ||
324 | FLASH_ADDR(0) = 0x00; /* column */ | ||
325 | FLASH_ADDR(0) = page & 0xff; /* row */ | ||
326 | FLASH_ADDR(0) = (page >> 8) & 0xff; /* row */ | ||
327 | FLASH_ADDR(0) = (page >> 16) & 0xff; /* row */ | ||
328 | FLASH_CMD(0) = READ_PAGE_CMD; | ||
329 | |||
330 | /* wait for operation complete */ | ||
331 | flash_wait_busy(); | ||
332 | |||
333 | /* copy data from page register | ||
334 | * WARNING flash page size can be different | ||
335 | * for different chips. This value should be set | ||
336 | * based on initialization. | ||
337 | */ | ||
338 | for (i=0; i<(4096+218); i++) | ||
339 | pgbuff[i] = FLASH_DATA(0); | ||
340 | |||
341 | flash_chip_deselect(); | ||
342 | } | ||
343 | |||
344 | void flash_read_sector(int page, unsigned char *secbuf, int nsec) | ||
345 | { | ||
346 | int i = 0; | ||
347 | int j = 0; | ||
348 | |||
349 | /* WARNING this code assumes only one nand chip | ||
350 | * it does not handle data split across different nand chips | ||
351 | */ | ||
352 | flash_chip_select(0); | ||
353 | flash_delay(2); | ||
354 | flash_wait_busy(); | ||
355 | |||
356 | FLASH_CMD(0) = 0x00; | ||
357 | FLASH_ADDR(0) = 0x00; | ||
358 | FLASH_ADDR(0) = 0x00; | ||
359 | FLASH_ADDR(0) = page & 0xff; | ||
360 | FLASH_ADDR(0) = (page >> 8) & 0xff; | ||
361 | FLASH_ADDR(0) = (page >> 16) & 0xff; | ||
362 | FLASH_CMD(0) = READ_PAGE_CMD; | ||
363 | |||
364 | flash_delay(1); | ||
365 | |||
366 | /* wait for operation to complete */ | ||
367 | flash_wait_busy(); | ||
368 | |||
369 | /* enables hw checksum control most probably */ | ||
370 | BCHCTL = 1; | ||
371 | |||
372 | /* This initializes the transfer from the nand to the buffer | ||
373 | * There are 4 consecutive hw buffers 512 bytes long for data (PAGE_BUF) | ||
374 | * and 4 16 bytes long for metadata (BCH code checksum) (SPARE_BUF) | ||
375 | */ | ||
376 | FLCTL = 0xA24; | ||
377 | |||
378 | /* This scheme utilizes some overlap in data transfers - | ||
379 | * data are copied from buffer to the mem and from nand to the buf | ||
380 | * at the same time. | ||
381 | */ | ||
382 | while (++j < nsec) | ||
383 | { | ||
384 | /* wait for transfer to complete */ | ||
385 | while(! (FLCTL & FL_RDY)); | ||
386 | |||
387 | /* initialize next transfer to the next buffer */ | ||
388 | FLCTL = 0xA24 | (j&3)<<3; | ||
389 | |||
390 | /* copy data chunk */ | ||
391 | memcpy(secbuf, (((unsigned char *)&PAGE_BUF)+((i&3)<<9)), 0x200); | ||
392 | secbuf += 0x200; | ||
393 | |||
394 | /* copy metadata chunk (BCH) | ||
395 | * in real application this can be discarded | ||
396 | */ | ||
397 | memcpy(secbuf, (((unsigned char *)&SPARE_BUF)+((i&3)<<4)), 0x10); | ||
398 | secbuf += 0x10; | ||
399 | i++; | ||
400 | } | ||
401 | |||
402 | /* wait for transfer to complete */ | ||
403 | while(! (FLCTL & FL_RDY)); | ||
404 | |||
405 | /* copy data chunk */ | ||
406 | memcpy(secbuf, (((unsigned char *)&PAGE_BUF)+((i&3)<<9)), 0x200); | ||
407 | secbuf += 0x200; | ||
408 | |||
409 | /* copy metadata chunk (BCH) | ||
410 | * in real application this can be discarded | ||
411 | */ | ||
412 | memcpy(secbuf, (((unsigned char *)&SPARE_BUF)+((i&3)<<4)), 0x10); | ||
413 | secbuf += 0x10; | ||
414 | |||
415 | flash_chip_deselect(); | ||
416 | } | ||
417 | |||
418 | #endif | ||
25 | const struct nand_device_info_type* nand_get_device_type(uint32_t bank); | 419 | const struct nand_device_info_type* nand_get_device_type(uint32_t bank); |
26 | 420 | ||
27 | 421 | ||