diff options
Diffstat (limited to 'firmware/target/arm/ata-nand-telechips.c')
-rw-r--r-- | firmware/target/arm/ata-nand-telechips.c | 892 |
1 files changed, 892 insertions, 0 deletions
diff --git a/firmware/target/arm/ata-nand-telechips.c b/firmware/target/arm/ata-nand-telechips.c new file mode 100644 index 0000000000..fc4418cc44 --- /dev/null +++ b/firmware/target/arm/ata-nand-telechips.c | |||
@@ -0,0 +1,892 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2008 Rob Purchase | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "ata.h" | ||
22 | #include "ata-nand-target.h" | ||
23 | #include "system.h" | ||
24 | #include <string.h> | ||
25 | #include "led.h" | ||
26 | #include "panic.h" | ||
27 | |||
28 | /* The NAND driver is currently work-in-progress and as such contains | ||
29 | some dead code and debug stuff, such as the next few lines. */ | ||
30 | #include "lcd.h" | ||
31 | #include "font.h" | ||
32 | #include "button.h" | ||
33 | #include <sprintf.h> | ||
34 | |||
35 | /* #define USE_TCC_LPT */ | ||
36 | /* #define USE_ECC_CORRECTION */ | ||
37 | |||
38 | /* for compatibility */ | ||
39 | int ata_spinup_time = 0; | ||
40 | |||
41 | long last_disk_activity = -1; | ||
42 | |||
43 | /** static, private data **/ | ||
44 | static bool initialized = false; | ||
45 | |||
46 | static struct mutex ata_mtx SHAREDBSS_ATTR; | ||
47 | |||
48 | #define SECTOR_SIZE 512 | ||
49 | |||
50 | #ifdef COWON_D2 | ||
51 | #define SEGMENT_ID_BIGENDIAN | ||
52 | #define BLOCKS_PER_SEGMENT 4 | ||
53 | #else | ||
54 | #define BLOCKS_PER_SEGMENT 1 | ||
55 | #endif | ||
56 | /* NB: blocks_per_segment should become a runtime check based on NAND id */ | ||
57 | |||
58 | /* Segment type identifiers - main data area */ | ||
59 | #define SEGMENT_MAIN_LPT 0x12 | ||
60 | #define SEGMENT_MAIN_DATA1 0x13 | ||
61 | #define SEGMENT_MAIN_CACHE 0x15 | ||
62 | #define SEGMENT_MAIN_DATA2 0x17 | ||
63 | |||
64 | /* We don't touch the hidden area at all - these are for reference */ | ||
65 | #define SEGMENT_HIDDEN_LPT 0x22 | ||
66 | #define SEGMENT_HIDDEN_DATA1 0x23 | ||
67 | #define SEGMENT_HIDDEN_CACHE 0x25 | ||
68 | #define SEGMENT_HIDDEN_DATA2 0x27 | ||
69 | |||
70 | /* Offsets to spare area data */ | ||
71 | #define OFF_CACHE_PAGE_LOBYTE 2 | ||
72 | #define OFF_CACHE_PAGE_HIBYTE 3 | ||
73 | #define OFF_SEGMENT_TYPE 4 | ||
74 | |||
75 | #ifdef SEGMENT_ID_BIGENDIAN | ||
76 | #define OFF_LOG_SEG_LOBYTE 7 | ||
77 | #define OFF_LOG_SEG_HIBYTE 6 | ||
78 | #else | ||
79 | #define OFF_LOG_SEG_LOBYTE 6 | ||
80 | #define OFF_LOG_SEG_HIBYTE 7 | ||
81 | #endif | ||
82 | |||
83 | /* Chip characteristics, initialised by nand_get_chip_info() */ | ||
84 | |||
85 | static int page_size = 0; | ||
86 | static int spare_size = 0; | ||
87 | static int pages_per_block = 0; | ||
88 | static int blocks_per_bank = 0; | ||
89 | static int pages_per_bank = 0; | ||
90 | static int row_cycles = 0; | ||
91 | static int col_cycles = 0; | ||
92 | static int total_banks = 0; | ||
93 | static int sectors_per_page = 0; | ||
94 | static int bytes_per_segment = 0; | ||
95 | static int sectors_per_segment = 0; | ||
96 | static int segments_per_bank = 0; | ||
97 | |||
98 | /* Maximum values for static buffers */ | ||
99 | |||
100 | #define MAX_PAGE_SIZE 4096 | ||
101 | #define MAX_SPARE_SIZE 128 | ||
102 | #define MAX_BLOCKS_PER_BANK 8192 | ||
103 | #define MAX_PAGES_PER_BLOCK 128 | ||
104 | #define MAX_BANKS 4 | ||
105 | |||
106 | #define MAX_SEGMENTS (MAX_BLOCKS_PER_BANK * MAX_BANKS / BLOCKS_PER_SEGMENT) | ||
107 | |||
108 | /* Logical/Physical translation table */ | ||
109 | |||
110 | struct lpt_entry | ||
111 | { | ||
112 | short bank; | ||
113 | short phys_segment; | ||
114 | }; | ||
115 | static struct lpt_entry lpt_lookup[MAX_SEGMENTS]; | ||
116 | |||
117 | /* Write Caches */ | ||
118 | |||
119 | #define MAX_WRITE_CACHES 8 | ||
120 | |||
121 | struct write_cache | ||
122 | { | ||
123 | short bank; | ||
124 | short phys_segment; | ||
125 | short log_segment; | ||
126 | short page_map[MAX_PAGES_PER_BLOCK * BLOCKS_PER_SEGMENT]; | ||
127 | }; | ||
128 | static struct write_cache write_caches[MAX_WRITE_CACHES]; | ||
129 | |||
130 | static int write_caches_in_use = 0; | ||
131 | |||
132 | #ifdef USE_TCC_LPT | ||
133 | /* Read buffer (used for reading LPT blocks only) */ | ||
134 | static unsigned char page_buf[MAX_PAGE_SIZE + MAX_SPARE_SIZE] | ||
135 | __attribute__ ((aligned (4))); | ||
136 | #endif | ||
137 | |||
138 | #ifdef USE_ECC_CORRECTION | ||
139 | static unsigned int ecc_sectors_corrected = 0; | ||
140 | static unsigned int ecc_bits_corrected = 0; | ||
141 | static unsigned int ecc_fail_count = 0; | ||
142 | #endif | ||
143 | |||
144 | |||
145 | /* Conversion functions */ | ||
146 | |||
147 | static inline int phys_segment_to_page_addr(int phys_segment, int page_in_seg) | ||
148 | { | ||
149 | #if BLOCKS_PER_SEGMENT == 4 /* D2 */ | ||
150 | int page_addr = phys_segment * pages_per_block * 2; | ||
151 | |||
152 | if (page_in_seg & 1) | ||
153 | { | ||
154 | /* Data is located in block+1 */ | ||
155 | page_addr += pages_per_block; | ||
156 | } | ||
157 | |||
158 | if (page_in_seg & 2) | ||
159 | { | ||
160 | /* Data is located in second plane */ | ||
161 | page_addr += (blocks_per_bank/2) * pages_per_block; | ||
162 | } | ||
163 | |||
164 | page_addr += page_in_seg/4; | ||
165 | #elif BLOCKS_PER_SEGMENT == 1 /* M200 */ | ||
166 | int page_addr = (phys_segment * pages_per_block) + page_in_seg; | ||
167 | #endif | ||
168 | |||
169 | return page_addr; | ||
170 | } | ||
171 | |||
172 | |||
173 | /* NAND physical access functions */ | ||
174 | |||
175 | static void nand_chip_select(int bank) | ||
176 | { | ||
177 | if (bank == -1) | ||
178 | { | ||
179 | /* Disable both chip selects */ | ||
180 | NAND_GPIO_CLEAR(CS_GPIO_BIT); | ||
181 | NFC_CTRL |= NFC_CS0 | NFC_CS1; | ||
182 | } | ||
183 | else | ||
184 | { | ||
185 | /* NFC chip select */ | ||
186 | #ifdef USE_TCC_LPT | ||
187 | if (!(bank & 1)) | ||
188 | #else | ||
189 | if (bank & 1) | ||
190 | #endif | ||
191 | { | ||
192 | NFC_CTRL &= ~NFC_CS0; | ||
193 | NFC_CTRL |= NFC_CS1; | ||
194 | } | ||
195 | else | ||
196 | { | ||
197 | NFC_CTRL |= NFC_CS0; | ||
198 | NFC_CTRL &= ~NFC_CS1; | ||
199 | } | ||
200 | |||
201 | /* Secondary chip select */ | ||
202 | if (bank & 2) | ||
203 | NAND_GPIO_SET(CS_GPIO_BIT); | ||
204 | else | ||
205 | NAND_GPIO_CLEAR(CS_GPIO_BIT); | ||
206 | } | ||
207 | } | ||
208 | |||
209 | |||
210 | static void nand_read_id(int bank, unsigned char* id_buf) | ||
211 | { | ||
212 | int i; | ||
213 | |||
214 | /* Enable NFC bus clock */ | ||
215 | BCLKCTR |= DEV_NAND; | ||
216 | |||
217 | /* Reset NAND controller */ | ||
218 | NFC_RST = 0; | ||
219 | |||
220 | /* Set slow cycle timings since the chip is as yet unidentified */ | ||
221 | NFC_CTRL = (NFC_CTRL &~0xFFF) | 0x353; | ||
222 | |||
223 | nand_chip_select(bank); | ||
224 | |||
225 | /* Set write protect */ | ||
226 | NAND_GPIO_CLEAR(WE_GPIO_BIT); | ||
227 | |||
228 | /* Reset command */ | ||
229 | NFC_CMD = 0xFF; | ||
230 | |||
231 | /* Set 8-bit data width */ | ||
232 | NFC_CTRL &= ~NFC_16BIT; | ||
233 | |||
234 | /* Read ID command, single address cycle */ | ||
235 | NFC_CMD = 0x90; | ||
236 | NFC_SADDR = 0x00; | ||
237 | |||
238 | /* Read the 5 chip ID bytes */ | ||
239 | for (i = 0; i < 5; i++) | ||
240 | { | ||
241 | id_buf[i] = NFC_SDATA & 0xFF; | ||
242 | } | ||
243 | |||
244 | nand_chip_select(-1); | ||
245 | |||
246 | /* Disable NFC bus clock */ | ||
247 | BCLKCTR &= ~DEV_NAND; | ||
248 | } | ||
249 | |||
250 | |||
251 | static void nand_read_uid(int bank, unsigned int* uid_buf) | ||
252 | { | ||
253 | int i; | ||
254 | |||
255 | /* Enable NFC bus clock */ | ||
256 | BCLKCTR |= DEV_NAND; | ||
257 | |||
258 | /* Set cycle timing (stp = 1, pw = 3, hold = 1) */ | ||
259 | NFC_CTRL = (NFC_CTRL &~0xFFF) | 0x131; | ||
260 | |||
261 | nand_chip_select(bank); | ||
262 | |||
263 | /* Set write protect */ | ||
264 | NAND_GPIO_CLEAR(WE_GPIO_BIT); | ||
265 | |||
266 | /* Set 8-bit data width */ | ||
267 | NFC_CTRL &= ~NFC_16BIT; | ||
268 | |||
269 | /* Undocumented (SAMSUNG specific?) commands set the chip into a | ||
270 | special mode allowing a normally-hidden UID block to be read. */ | ||
271 | NFC_CMD = 0x30; | ||
272 | NFC_CMD = 0x65; | ||
273 | |||
274 | /* Read command */ | ||
275 | NFC_CMD = 0x00; | ||
276 | |||
277 | /* Write row/column address */ | ||
278 | for (i = 0; i < col_cycles; i++) NFC_SADDR = 0; | ||
279 | for (i = 0; i < row_cycles; i++) NFC_SADDR = 0; | ||
280 | |||
281 | /* End of read */ | ||
282 | NFC_CMD = 0x30; | ||
283 | |||
284 | /* Wait until complete */ | ||
285 | while (!(NFC_CTRL & NFC_READY)) {}; | ||
286 | |||
287 | /* Copy data to buffer (data repeats after 8 words) */ | ||
288 | for (i = 0; i < 8; i++) | ||
289 | { | ||
290 | uid_buf[i] = NFC_WDATA; | ||
291 | } | ||
292 | |||
293 | /* Reset the chip back to normal mode */ | ||
294 | NFC_CMD = 0xFF; | ||
295 | |||
296 | nand_chip_select(-1); | ||
297 | |||
298 | /* Disable NFC bus clock */ | ||
299 | BCLKCTR &= ~DEV_NAND; | ||
300 | } | ||
301 | |||
302 | |||
303 | static void nand_read_raw(int bank, int row, int column, int size, void* buf) | ||
304 | { | ||
305 | int i; | ||
306 | |||
307 | /* Enable NFC bus clock */ | ||
308 | BCLKCTR |= DEV_NAND; | ||
309 | |||
310 | /* Set cycle timing (stp = 1, pw = 3, hold = 1) */ | ||
311 | NFC_CTRL = (NFC_CTRL &~0xFFF) | 0x131; | ||
312 | |||
313 | nand_chip_select(bank); | ||
314 | |||
315 | /* Set write protect */ | ||
316 | NAND_GPIO_CLEAR(WE_GPIO_BIT); | ||
317 | |||
318 | /* Set 8-bit data width */ | ||
319 | NFC_CTRL &= ~NFC_16BIT; | ||
320 | |||
321 | /* Read command */ | ||
322 | NFC_CMD = 0x00; | ||
323 | |||
324 | /* Write column address */ | ||
325 | for (i = 0; i < col_cycles; i++) | ||
326 | { | ||
327 | NFC_SADDR = column & 0xFF; | ||
328 | column = column >> 8; | ||
329 | } | ||
330 | |||
331 | /* Write row address */ | ||
332 | for (i = 0; i < row_cycles; i++) | ||
333 | { | ||
334 | NFC_SADDR = row & 0xFF; | ||
335 | row = row >> 8; | ||
336 | } | ||
337 | |||
338 | /* End of read command */ | ||
339 | NFC_CMD = 0x30; | ||
340 | |||
341 | /* Wait until complete */ | ||
342 | while (!(NFC_CTRL & NFC_READY)) {}; | ||
343 | |||
344 | /* Read data into page buffer */ | ||
345 | if (((unsigned int)buf & 3) || (size & 3)) | ||
346 | { | ||
347 | /* Use byte copy since either the buffer or size are not word-aligned */ | ||
348 | /* TODO: Byte copy only where necessary (use words for mid-section) */ | ||
349 | for (i = 0; i < size; i++) | ||
350 | { | ||
351 | ((unsigned char*)buf)[i] = NFC_SDATA; | ||
352 | } | ||
353 | } | ||
354 | else | ||
355 | { | ||
356 | /* Use 4-byte copy as buffer and size are both word-aligned */ | ||
357 | for (i = 0; i < (size/4); i++) | ||
358 | { | ||
359 | ((unsigned int*)buf)[i] = NFC_WDATA; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | nand_chip_select(-1); | ||
364 | |||
365 | /* Disable NFC bus clock */ | ||
366 | BCLKCTR &= ~DEV_NAND; | ||
367 | } | ||
368 | |||
369 | |||
370 | static void nand_get_chip_info(void) | ||
371 | { | ||
372 | bool found = false; | ||
373 | unsigned char manuf_id; | ||
374 | unsigned char id_buf[8]; | ||
375 | |||
376 | /* Read chip id from bank 0 */ | ||
377 | nand_read_id(0, id_buf); | ||
378 | |||
379 | manuf_id = id_buf[0]; | ||
380 | |||
381 | switch (manuf_id) | ||
382 | { | ||
383 | case 0xEC: /* SAMSUNG */ | ||
384 | |||
385 | switch(id_buf[1]) /* Chip Id */ | ||
386 | { | ||
387 | case 0xD3: /* K9K8G08UOM */ | ||
388 | |||
389 | page_size = 2048; | ||
390 | spare_size = 64; | ||
391 | pages_per_block = 64; | ||
392 | blocks_per_bank = 8192; | ||
393 | col_cycles = 2; | ||
394 | row_cycles = 3; | ||
395 | |||
396 | found = true; | ||
397 | break; | ||
398 | |||
399 | case 0xD5: /* K9LAG08UOM */ | ||
400 | |||
401 | page_size = 2048; | ||
402 | spare_size = 64; | ||
403 | pages_per_block = 128; | ||
404 | blocks_per_bank = 8192; | ||
405 | col_cycles = 2; | ||
406 | row_cycles = 3; | ||
407 | |||
408 | found = true; | ||
409 | break; | ||
410 | |||
411 | case 0xD7: /* K9LBG08UOM */ | ||
412 | |||
413 | page_size = 4096; | ||
414 | spare_size = 128; | ||
415 | pages_per_block = 128; | ||
416 | blocks_per_bank = 8192; | ||
417 | col_cycles = 2; | ||
418 | row_cycles = 3; | ||
419 | |||
420 | found = true; | ||
421 | break; | ||
422 | } | ||
423 | break; | ||
424 | } | ||
425 | |||
426 | if (!found) | ||
427 | { | ||
428 | panicf("Unknown NAND: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", | ||
429 | id_buf[0],id_buf[1],id_buf[2],id_buf[3],id_buf[4]); | ||
430 | } | ||
431 | |||
432 | pages_per_bank = blocks_per_bank * pages_per_block; | ||
433 | segments_per_bank = blocks_per_bank / BLOCKS_PER_SEGMENT; | ||
434 | bytes_per_segment = page_size * pages_per_block * BLOCKS_PER_SEGMENT; | ||
435 | sectors_per_page = page_size / SECTOR_SIZE; | ||
436 | sectors_per_segment = bytes_per_segment / SECTOR_SIZE; | ||
437 | |||
438 | /* Establish how many banks are present */ | ||
439 | nand_read_id(1, id_buf); | ||
440 | |||
441 | if (id_buf[0] == manuf_id) | ||
442 | { | ||
443 | /* Bank 1 is populated, now check if banks 2/3 are valid */ | ||
444 | nand_read_id(2, id_buf); | ||
445 | |||
446 | if (id_buf[0] == manuf_id) | ||
447 | { | ||
448 | /* Bank 2 returned matching id - check if 2/3 are shadowing 0/1 */ | ||
449 | unsigned int uid_buf0[8]; | ||
450 | unsigned int uid_buf2[8]; | ||
451 | |||
452 | nand_read_uid(0, uid_buf0); | ||
453 | nand_read_uid(2, uid_buf2); | ||
454 | |||
455 | if (memcmp(uid_buf0, uid_buf2, 32) == 0) | ||
456 | { | ||
457 | /* UIDs match, assume banks 2/3 are shadowing 0/1 */ | ||
458 | total_banks = 2; | ||
459 | } | ||
460 | else | ||
461 | { | ||
462 | /* UIDs differ, assume banks 2/3 are valid */ | ||
463 | total_banks = 4; | ||
464 | } | ||
465 | } | ||
466 | else | ||
467 | { | ||
468 | /* Bank 2 returned differing id - assume 2/3 are junk */ | ||
469 | total_banks = 2; | ||
470 | } | ||
471 | } | ||
472 | else | ||
473 | { | ||
474 | /* Bank 1 returned differing id - assume it is junk */ | ||
475 | total_banks = 1; | ||
476 | } | ||
477 | |||
478 | /* | ||
479 | Sanity checks: | ||
480 | 1. "BMP" tag at block 0, page 0, offset <page_size> [always present] | ||
481 | 2. On most D2s, <page_size>+3 is 'M' and <page_size>+4 is no. of banks. | ||
482 | This is not present on some older players (formatted with early FW?) | ||
483 | */ | ||
484 | |||
485 | nand_read_raw(0, /* bank */ | ||
486 | 0, /* page */ | ||
487 | page_size, /* offset */ | ||
488 | 8, id_buf); | ||
489 | |||
490 | if (strncmp(id_buf, "BMP", 3)) panicf("BMP tag not present"); | ||
491 | |||
492 | if (id_buf[3] == 'M') | ||
493 | { | ||
494 | if (id_buf[4] != total_banks) panicf("BMPM total_banks mismatch"); | ||
495 | } | ||
496 | } | ||
497 | |||
498 | |||
499 | static bool nand_read_sector_of_phys_page(int bank, int page, | ||
500 | int sector, void* buf) | ||
501 | { | ||
502 | #ifndef USE_ECC_CORRECTION | ||
503 | nand_read_raw(bank, page, | ||
504 | sector * (SECTOR_SIZE+16), | ||
505 | SECTOR_SIZE, buf); | ||
506 | return true; | ||
507 | #else | ||
508 | /* Not yet implemented */ | ||
509 | return false; | ||
510 | #endif | ||
511 | } | ||
512 | |||
513 | |||
514 | static bool nand_read_sector_of_phys_segment(int bank, int phys_segment, | ||
515 | int page_in_seg, int sector, | ||
516 | void* buf) | ||
517 | { | ||
518 | int page_addr = phys_segment_to_page_addr(phys_segment, | ||
519 | page_in_seg); | ||
520 | |||
521 | return nand_read_sector_of_phys_page(bank, page_addr, sector, buf); | ||
522 | } | ||
523 | |||
524 | |||
525 | static bool nand_read_sector_of_logical_segment(int log_segment, int sector, | ||
526 | void* buf) | ||
527 | { | ||
528 | int page_in_segment = sector / sectors_per_page; | ||
529 | int sector_in_page = sector % sectors_per_page; | ||
530 | |||
531 | int bank = lpt_lookup[log_segment].bank; | ||
532 | int phys_segment = lpt_lookup[log_segment].phys_segment; | ||
533 | |||
534 | /* Check if any of the write caches refer to this segment/page. | ||
535 | If present we need to read the cached page instead. */ | ||
536 | |||
537 | int cache_num = 0; | ||
538 | bool found = false; | ||
539 | |||
540 | while (!found && cache_num < write_caches_in_use) | ||
541 | { | ||
542 | if (write_caches[cache_num].log_segment == log_segment | ||
543 | && write_caches[cache_num].page_map[page_in_segment] != -1) | ||
544 | { | ||
545 | found = true; | ||
546 | bank = write_caches[cache_num].bank; | ||
547 | phys_segment = write_caches[cache_num].phys_segment; | ||
548 | page_in_segment = write_caches[cache_num].page_map[page_in_segment]; | ||
549 | } | ||
550 | else | ||
551 | { | ||
552 | cache_num++; | ||
553 | } | ||
554 | } | ||
555 | |||
556 | return nand_read_sector_of_phys_segment(bank, phys_segment, | ||
557 | page_in_segment, | ||
558 | sector_in_page, buf); | ||
559 | } | ||
560 | |||
561 | |||
562 | #ifdef USE_TCC_LPT | ||
563 | |||
564 | /* Reading the LPT from NAND is not yet fully understood. This code is therefore | ||
565 | not enabled by default, as it gives much worse results than the bank-scanning | ||
566 | approach currently used. */ | ||
567 | |||
568 | static void read_lpt_block(int bank, int phys_segment) | ||
569 | { | ||
570 | int page = 1; /* table starts at page 1 of segment */ | ||
571 | bool cont = true; | ||
572 | |||
573 | struct lpt_entry* lpt_ptr = NULL; | ||
574 | |||
575 | while (cont && page < pages_per_block) | ||
576 | { | ||
577 | int i = 0; | ||
578 | unsigned int* int_buf = (int*)page_buf; | ||
579 | |||
580 | nand_read_sector_of_phys_segment(bank, phys_segment, | ||
581 | page, 0, /* only sector 0 is used */ | ||
582 | page_buf); | ||
583 | |||
584 | /* Find out which chunk of the LPT table this section contains. | ||
585 | Do this by reading the logical segment number of entry 0 */ | ||
586 | if (lpt_ptr == NULL) | ||
587 | { | ||
588 | int first_bank = int_buf[0] / segments_per_bank; | ||
589 | int first_phys_segment = int_buf[0] % segments_per_bank; | ||
590 | |||
591 | unsigned char spare_buf[16]; | ||
592 | |||
593 | nand_read_raw(first_bank, | ||
594 | phys_segment_to_page_addr(first_phys_segment, 0), | ||
595 | SECTOR_SIZE, /* offset */ | ||
596 | 16, spare_buf); | ||
597 | |||
598 | int first_log_segment = (spare_buf[OFF_LOG_SEG_HIBYTE] << 8) | | ||
599 | spare_buf[OFF_LOG_SEG_LOBYTE]; | ||
600 | |||
601 | lpt_ptr = &lpt_lookup[first_log_segment]; | ||
602 | |||
603 | #if defined(BOOTLOADER) && 1 | ||
604 | printf("lpt @ %lx:%lx (ls:%lx)", | ||
605 | first_bank, first_phys_segment, first_log_segment); | ||
606 | #endif | ||
607 | } | ||
608 | |||
609 | while (cont && (i < SECTOR_SIZE/4)) | ||
610 | { | ||
611 | if (int_buf[i] != 0xFFFFFFFF) | ||
612 | { | ||
613 | lpt_ptr->bank = int_buf[i] / segments_per_bank; | ||
614 | lpt_ptr->phys_segment = int_buf[i] % segments_per_bank; | ||
615 | |||
616 | lpt_ptr++; | ||
617 | i++; | ||
618 | } | ||
619 | else cont = false; | ||
620 | } | ||
621 | page++; | ||
622 | } | ||
623 | } | ||
624 | |||
625 | #endif /* USE_TCC_LPT */ | ||
626 | |||
627 | |||
628 | static void read_write_cache_segment(int bank, int phys_segment) | ||
629 | { | ||
630 | int page; | ||
631 | unsigned char spare_buf[16]; | ||
632 | |||
633 | if (write_caches_in_use == MAX_WRITE_CACHES) | ||
634 | panicf("Max NAND write caches reached"); | ||
635 | |||
636 | write_caches[write_caches_in_use].bank = bank; | ||
637 | write_caches[write_caches_in_use].phys_segment = phys_segment; | ||
638 | |||
639 | /* Loop over each page in the phys segment (from page 1 onwards). | ||
640 | Read spare for 1st sector, store location of page in array. */ | ||
641 | for (page = 1; page < pages_per_block * BLOCKS_PER_SEGMENT; page++) | ||
642 | { | ||
643 | unsigned short cached_page; | ||
644 | unsigned short log_segment; | ||
645 | |||
646 | nand_read_raw(bank, phys_segment_to_page_addr(phys_segment, page), | ||
647 | SECTOR_SIZE, /* offset to first sector's spare */ | ||
648 | 16, spare_buf); | ||
649 | |||
650 | cached_page = (spare_buf[OFF_CACHE_PAGE_HIBYTE] << 8) | | ||
651 | spare_buf[OFF_CACHE_PAGE_LOBYTE]; | ||
652 | |||
653 | log_segment = (spare_buf[OFF_LOG_SEG_HIBYTE] << 8) | | ||
654 | spare_buf[OFF_LOG_SEG_LOBYTE]; | ||
655 | |||
656 | if (cached_page != 0xFFFF) | ||
657 | { | ||
658 | write_caches[write_caches_in_use].log_segment = log_segment; | ||
659 | write_caches[write_caches_in_use].page_map[cached_page] = page; | ||
660 | } | ||
661 | } | ||
662 | write_caches_in_use++; | ||
663 | } | ||
664 | |||
665 | |||
666 | int ata_read_sectors(IF_MV2(int drive,) unsigned long start, int incount, | ||
667 | void* inbuf) | ||
668 | { | ||
669 | #ifdef HAVE_MULTIVOLUME | ||
670 | (void)drive; /* unused for now */ | ||
671 | #endif | ||
672 | mutex_lock(&ata_mtx); | ||
673 | |||
674 | while (incount > 0) | ||
675 | { | ||
676 | int done = 0; | ||
677 | int segment = start / sectors_per_segment; | ||
678 | int secmod = start % sectors_per_segment; | ||
679 | |||
680 | while (incount > 0 && secmod < sectors_per_segment) | ||
681 | { | ||
682 | if (!nand_read_sector_of_logical_segment(segment, secmod, inbuf)) | ||
683 | { | ||
684 | mutex_unlock(&ata_mtx); | ||
685 | return -1; | ||
686 | } | ||
687 | |||
688 | inbuf += SECTOR_SIZE; | ||
689 | incount--; | ||
690 | secmod++; | ||
691 | done++; | ||
692 | } | ||
693 | |||
694 | if (done < 0) | ||
695 | { | ||
696 | mutex_unlock(&ata_mtx); | ||
697 | return -1; | ||
698 | } | ||
699 | start += done; | ||
700 | } | ||
701 | |||
702 | mutex_unlock(&ata_mtx); | ||
703 | return 0; | ||
704 | } | ||
705 | |||
706 | int ata_write_sectors(IF_MV2(int drive,) unsigned long start, int count, | ||
707 | const void* outbuf) | ||
708 | { | ||
709 | #ifdef HAVE_MULTIVOLUME | ||
710 | (void)drive; /* unused for now */ | ||
711 | #endif | ||
712 | |||
713 | /* TODO: Learn more about TNFTL and implement this one day... */ | ||
714 | (void)start; | ||
715 | (void)count; | ||
716 | (void)outbuf; | ||
717 | return -1; | ||
718 | } | ||
719 | |||
720 | void ata_spindown(int seconds) | ||
721 | { | ||
722 | /* null */ | ||
723 | (void)seconds; | ||
724 | } | ||
725 | |||
726 | bool ata_disk_is_active(void) | ||
727 | { | ||
728 | /* null */ | ||
729 | return 0; | ||
730 | } | ||
731 | |||
732 | void ata_sleep(void) | ||
733 | { | ||
734 | /* null */ | ||
735 | } | ||
736 | |||
737 | void ata_spin(void) | ||
738 | { | ||
739 | /* null */ | ||
740 | } | ||
741 | |||
742 | /* Hardware reset protocol as specified in chapter 9.1, ATA spec draft v5 */ | ||
743 | int ata_hard_reset(void) | ||
744 | { | ||
745 | /* null */ | ||
746 | return 0; | ||
747 | } | ||
748 | |||
749 | int ata_soft_reset(void) | ||
750 | { | ||
751 | /* null */ | ||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | void ata_enable(bool on) | ||
756 | { | ||
757 | /* null - flash controller is enabled/disabled as needed. */ | ||
758 | (void)on; | ||
759 | } | ||
760 | |||
761 | int ata_init(void) | ||
762 | { | ||
763 | int i, bank, phys_segment; | ||
764 | unsigned char spare_buf[16]; | ||
765 | |||
766 | if (initialized) return 0; | ||
767 | |||
768 | #ifdef CPU_TCC77X | ||
769 | CSCFG2 = 0x318a8010; | ||
770 | |||
771 | GPIOC_FUNC &= ~(CS_GPIO_BIT | WE_GPIO_BIT); | ||
772 | GPIOC_FUNC |= 0x1; | ||
773 | #endif | ||
774 | |||
775 | /* Set GPIO direction for chip select & write protect */ | ||
776 | NAND_GPIO_OUT_EN(CS_GPIO_BIT | WE_GPIO_BIT); | ||
777 | |||
778 | /* Get chip characteristics and number of banks */ | ||
779 | nand_get_chip_info(); | ||
780 | |||
781 | for (i = 0; i < MAX_SEGMENTS; i++) | ||
782 | { | ||
783 | lpt_lookup[i].bank = -1; | ||
784 | lpt_lookup[i].phys_segment = -1; | ||
785 | } | ||
786 | |||
787 | write_caches_in_use = 0; | ||
788 | |||
789 | for (i = 0; i < MAX_WRITE_CACHES; i++) | ||
790 | { | ||
791 | int page; | ||
792 | |||
793 | write_caches[i].log_segment = -1; | ||
794 | write_caches[i].bank = -1; | ||
795 | write_caches[i].phys_segment = -1; | ||
796 | |||
797 | for (page = 0; page < MAX_PAGES_PER_BLOCK * BLOCKS_PER_SEGMENT; page++) | ||
798 | { | ||
799 | write_caches[i].page_map[page] = -1; | ||
800 | } | ||
801 | } | ||
802 | |||
803 | /* Scan banks to build up block translation table */ | ||
804 | for (bank = 0; bank < total_banks; bank++) | ||
805 | { | ||
806 | for (phys_segment = 0; phys_segment < segments_per_bank; phys_segment++) | ||
807 | { | ||
808 | /* Read spare bytes from first sector of each segment */ | ||
809 | nand_read_raw(bank, phys_segment_to_page_addr(phys_segment, 0), | ||
810 | SECTOR_SIZE, /* offset */ | ||
811 | 16, spare_buf); | ||
812 | |||
813 | switch (spare_buf[4]) /* block type */ | ||
814 | { | ||
815 | #ifdef USE_TCC_LPT | ||
816 | case SEGMENT_MAIN_LPT: | ||
817 | { | ||
818 | /* Log->Phys Translation table (for Main data area) */ | ||
819 | read_lpt_block(bank, phys_segment); | ||
820 | break; | ||
821 | } | ||
822 | #else | ||
823 | case SEGMENT_MAIN_DATA2: | ||
824 | { | ||
825 | /* Main data area segment */ | ||
826 | unsigned short log_segment | ||
827 | = (spare_buf[OFF_LOG_SEG_HIBYTE] << 8) | | ||
828 | spare_buf[OFF_LOG_SEG_LOBYTE]; | ||
829 | |||
830 | if (log_segment < MAX_SEGMENTS) | ||
831 | { | ||
832 | lpt_lookup[log_segment].bank = bank; | ||
833 | lpt_lookup[log_segment].phys_segment = phys_segment; | ||
834 | } | ||
835 | break; | ||
836 | } | ||
837 | #endif | ||
838 | |||
839 | case SEGMENT_MAIN_CACHE: | ||
840 | { | ||
841 | /* Recently-written page data (for Main data area) */ | ||
842 | read_write_cache_segment(bank, phys_segment); | ||
843 | break; | ||
844 | } | ||
845 | } | ||
846 | } | ||
847 | } | ||
848 | |||
849 | #ifndef USE_TCC_LPT | ||
850 | /* Scan banks a second time as 0x13 segments appear to override 0x17 */ | ||
851 | for (bank = 0; bank < total_banks; bank++) | ||
852 | { | ||
853 | for (phys_segment = 0; phys_segment < segments_per_bank; phys_segment++) | ||
854 | { | ||
855 | /* Read spare bytes from first sector of each segment */ | ||
856 | nand_read_raw(bank, phys_segment_to_page_addr(phys_segment, 0), | ||
857 | SECTOR_SIZE, /* offset */ | ||
858 | 16, spare_buf); | ||
859 | |||
860 | switch (spare_buf[4]) /* block type */ | ||
861 | { | ||
862 | case SEGMENT_MAIN_DATA1: | ||
863 | { | ||
864 | /* Main data area segment */ | ||
865 | unsigned short log_segment | ||
866 | = (spare_buf[OFF_LOG_SEG_HIBYTE] << 8) | | ||
867 | spare_buf[OFF_LOG_SEG_LOBYTE]; | ||
868 | |||
869 | if (log_segment < MAX_SEGMENTS) | ||
870 | { | ||
871 | /* 0x13 seems to override 0x17, so store in our LPT */ | ||
872 | lpt_lookup[log_segment].bank = bank; | ||
873 | lpt_lookup[log_segment].phys_segment = phys_segment; | ||
874 | } | ||
875 | break; | ||
876 | } | ||
877 | } | ||
878 | } | ||
879 | } | ||
880 | #endif | ||
881 | |||
882 | initialized = true; | ||
883 | |||
884 | return 0; | ||
885 | } | ||
886 | |||
887 | |||
888 | /* TEMP: This will return junk, it's here for compilation only */ | ||
889 | unsigned short* ata_get_identify(void) | ||
890 | { | ||
891 | return (unsigned short*)0x21000000; /* Unused DRAM */ | ||
892 | } | ||