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