diff options
author | Jens Arnold <amiconn@rockbox.org> | 2007-05-23 17:51:18 +0000 |
---|---|---|
committer | Jens Arnold <amiconn@rockbox.org> | 2007-05-23 17:51:18 +0000 |
commit | c5d71aab1677741d445367820f78b0c85a6f4cde (patch) | |
tree | 2d547276eaec8dc1bef0133bd01dd54d92ae8694 /firmware/drivers | |
parent | c5302d5101962ea21265a271c2494cacea088fea (diff) | |
download | rockbox-c5d71aab1677741d445367820f78b0c85a6f4cde.tar.gz rockbox-c5d71aab1677741d445367820f78b0c85a6f4cde.zip |
ATA driver: * Support for drives with large physical sectors and no support for partial access in the firmware (i.e. Toshiba MK8010GAH - iPod G5.5/80GB). Sequential writes with a single 512-byte buffer to that disk are really slow, so this is an intermediate solution that allows to adjust the FAT driver and the file system gradually. * Assume multisectors = 16 if the value reported by the drive is invalid (also MK8010GAH).
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13480 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/drivers')
-rw-r--r-- | firmware/drivers/ata.c | 234 |
1 files changed, 228 insertions, 6 deletions
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c index 35967d7d78..e9e567761b 100644 --- a/firmware/drivers/ata.c +++ b/firmware/drivers/ata.c | |||
@@ -92,6 +92,18 @@ long last_disk_activity = -1; | |||
92 | static int multisectors; /* number of supported multisectors */ | 92 | static int multisectors; /* number of supported multisectors */ |
93 | static unsigned short identify_info[SECTOR_SIZE]; | 93 | static unsigned short identify_info[SECTOR_SIZE]; |
94 | 94 | ||
95 | #ifdef MAX_PHYS_SECTOR_SIZE | ||
96 | struct sector_cache_entry { | ||
97 | bool inuse; | ||
98 | unsigned long sectornum; /* logical sector */ | ||
99 | unsigned char data[MAX_PHYS_SECTOR_SIZE]; | ||
100 | }; | ||
101 | /* buffer for reading and writing large physical sectors */ | ||
102 | #define NUMCACHES 2 | ||
103 | static struct sector_cache_entry sector_cache; | ||
104 | static int phys_sector_mult = 1; | ||
105 | #endif | ||
106 | |||
95 | static int ata_power_on(void); | 107 | static int ata_power_on(void); |
96 | static int perform_soft_reset(void); | 108 | static int perform_soft_reset(void); |
97 | static int set_multiple_mode(int sectors); | 109 | static int set_multiple_mode(int sectors); |
@@ -201,10 +213,16 @@ STATICIRAM void copy_read_sectors(unsigned char* buf, int wordcount) | |||
201 | } | 213 | } |
202 | #endif /* !ATA_OPTIMIZED_READING */ | 214 | #endif /* !ATA_OPTIMIZED_READING */ |
203 | 215 | ||
216 | #ifdef MAX_PHYS_SECTOR_SIZE | ||
217 | static int _read_sectors(unsigned long start, | ||
218 | int incount, | ||
219 | void* inbuf) | ||
220 | #else | ||
204 | int ata_read_sectors(IF_MV2(int drive,) | 221 | int ata_read_sectors(IF_MV2(int drive,) |
205 | unsigned long start, | 222 | unsigned long start, |
206 | int incount, | 223 | int incount, |
207 | void* inbuf) | 224 | void* inbuf) |
225 | #endif | ||
208 | { | 226 | { |
209 | int ret = 0; | 227 | int ret = 0; |
210 | long timeout; | 228 | long timeout; |
@@ -212,10 +230,12 @@ int ata_read_sectors(IF_MV2(int drive,) | |||
212 | void* buf; | 230 | void* buf; |
213 | long spinup_start; | 231 | long spinup_start; |
214 | 232 | ||
233 | #ifndef MAX_PHYS_SECTOR_SIZE | ||
215 | #ifdef HAVE_MULTIVOLUME | 234 | #ifdef HAVE_MULTIVOLUME |
216 | (void)drive; /* unused for now */ | 235 | (void)drive; /* unused for now */ |
217 | #endif | 236 | #endif |
218 | spinlock_lock(&ata_mtx); | 237 | spinlock_lock(&ata_mtx); |
238 | #endif | ||
219 | 239 | ||
220 | last_disk_activity = current_tick; | 240 | last_disk_activity = current_tick; |
221 | spinup_start = current_tick; | 241 | spinup_start = current_tick; |
@@ -318,9 +338,6 @@ int ata_read_sectors(IF_MV2(int drive,) | |||
318 | /* read the status register exactly once per loop */ | 338 | /* read the status register exactly once per loop */ |
319 | status = ATA_STATUS; | 339 | status = ATA_STATUS; |
320 | 340 | ||
321 | /* if destination address is odd, use byte copying, | ||
322 | otherwise use word copying */ | ||
323 | |||
324 | if (count >= multisectors ) | 341 | if (count >= multisectors ) |
325 | sectors = multisectors; | 342 | sectors = multisectors; |
326 | else | 343 | else |
@@ -358,7 +375,9 @@ int ata_read_sectors(IF_MV2(int drive,) | |||
358 | } | 375 | } |
359 | ata_led(false); | 376 | ata_led(false); |
360 | 377 | ||
378 | #ifndef MAX_PHYS_SECTOR_SIZE | ||
361 | spinlock_unlock(&ata_mtx); | 379 | spinlock_unlock(&ata_mtx); |
380 | #endif | ||
362 | 381 | ||
363 | return ret; | 382 | return ret; |
364 | } | 383 | } |
@@ -401,22 +420,30 @@ STATICIRAM void copy_write_sectors(const unsigned char* buf, int wordcount) | |||
401 | } | 420 | } |
402 | #endif /* !ATA_OPTIMIZED_WRITING */ | 421 | #endif /* !ATA_OPTIMIZED_WRITING */ |
403 | 422 | ||
423 | #ifdef MAX_PHYS_SECTOR_SIZE | ||
424 | static int _write_sectors(unsigned long start, | ||
425 | int count, | ||
426 | const void* buf) | ||
427 | #else | ||
404 | int ata_write_sectors(IF_MV2(int drive,) | 428 | int ata_write_sectors(IF_MV2(int drive,) |
405 | unsigned long start, | 429 | unsigned long start, |
406 | int count, | 430 | int count, |
407 | const void* buf) | 431 | const void* buf) |
432 | #endif | ||
408 | { | 433 | { |
409 | int i; | 434 | int i; |
410 | int ret = 0; | 435 | int ret = 0; |
411 | long spinup_start; | 436 | long spinup_start; |
412 | 437 | ||
413 | #ifdef HAVE_MULTIVOLUME | ||
414 | (void)drive; /* unused for now */ | ||
415 | #endif | ||
416 | if (start == 0) | 438 | if (start == 0) |
417 | panicf("Writing on sector 0\n"); | 439 | panicf("Writing on sector 0\n"); |
418 | 440 | ||
441 | #ifndef MAX_PHYS_SECTOR_SIZE | ||
442 | #ifdef HAVE_MULTIVOLUME | ||
443 | (void)drive; /* unused for now */ | ||
444 | #endif | ||
419 | spinlock_lock(&ata_mtx); | 445 | spinlock_lock(&ata_mtx); |
446 | #endif | ||
420 | 447 | ||
421 | last_disk_activity = current_tick; | 448 | last_disk_activity = current_tick; |
422 | spinup_start = current_tick; | 449 | spinup_start = current_tick; |
@@ -506,11 +533,186 @@ int ata_write_sectors(IF_MV2(int drive,) | |||
506 | 533 | ||
507 | ata_led(false); | 534 | ata_led(false); |
508 | 535 | ||
536 | #ifndef MAX_PHYS_SECTOR_SIZE | ||
509 | spinlock_unlock(&ata_mtx); | 537 | spinlock_unlock(&ata_mtx); |
538 | #endif | ||
510 | 539 | ||
511 | return ret; | 540 | return ret; |
512 | } | 541 | } |
513 | 542 | ||
543 | #ifdef MAX_PHYS_SECTOR_SIZE | ||
544 | static int cache_sector(unsigned long sector) | ||
545 | { | ||
546 | int rc; | ||
547 | |||
548 | sector &= ~(phys_sector_mult - 1); | ||
549 | /* round down to physical sector boundary */ | ||
550 | |||
551 | /* check whether the sector is already cached */ | ||
552 | if (sector_cache.inuse && (sector_cache.sectornum == sector)) | ||
553 | return 0; | ||
554 | |||
555 | /* not found: read the sector */ | ||
556 | sector_cache.inuse = false; | ||
557 | rc = _read_sectors(sector, phys_sector_mult, sector_cache.data); | ||
558 | if (!rc) | ||
559 | { | ||
560 | sector_cache.sectornum = sector; | ||
561 | sector_cache.inuse = true; | ||
562 | } | ||
563 | return rc; | ||
564 | } | ||
565 | |||
566 | static inline int flush_current_sector(void) | ||
567 | { | ||
568 | return _write_sectors(sector_cache.sectornum, phys_sector_mult, | ||
569 | sector_cache.data); | ||
570 | } | ||
571 | |||
572 | int ata_read_sectors(IF_MV2(int drive,) | ||
573 | unsigned long start, | ||
574 | int incount, | ||
575 | void* inbuf) | ||
576 | { | ||
577 | int rc = 0; | ||
578 | int offset; | ||
579 | |||
580 | #ifdef HAVE_MULTIVOLUME | ||
581 | (void)drive; /* unused for now */ | ||
582 | #endif | ||
583 | spinlock_lock(&ata_mtx); | ||
584 | |||
585 | offset = start & (phys_sector_mult - 1); | ||
586 | |||
587 | if (offset) /* first partial sector */ | ||
588 | { | ||
589 | int partcount = MIN(incount, phys_sector_mult - offset); | ||
590 | |||
591 | rc = cache_sector(start); | ||
592 | if (rc) | ||
593 | { | ||
594 | rc = rc * 10 - 1; | ||
595 | goto error; | ||
596 | } | ||
597 | memcpy(inbuf, sector_cache.data + offset * SECTOR_SIZE, | ||
598 | partcount * SECTOR_SIZE); | ||
599 | |||
600 | start += partcount; | ||
601 | inbuf += partcount * SECTOR_SIZE; | ||
602 | incount -= partcount; | ||
603 | } | ||
604 | if (incount) | ||
605 | { | ||
606 | offset = incount & (phys_sector_mult - 1); | ||
607 | incount -= offset; | ||
608 | |||
609 | if (incount) | ||
610 | { | ||
611 | rc = _read_sectors(start, incount, inbuf); | ||
612 | if (rc) | ||
613 | { | ||
614 | rc = rc * 10 - 2; | ||
615 | goto error; | ||
616 | } | ||
617 | start += incount; | ||
618 | inbuf += incount * SECTOR_SIZE; | ||
619 | } | ||
620 | if (offset) | ||
621 | { | ||
622 | rc = cache_sector(start); | ||
623 | if (rc) | ||
624 | { | ||
625 | rc = rc * 10 - 3; | ||
626 | goto error; | ||
627 | } | ||
628 | memcpy(inbuf, sector_cache.data, offset * SECTOR_SIZE); | ||
629 | } | ||
630 | } | ||
631 | |||
632 | error: | ||
633 | spinlock_unlock(&ata_mtx); | ||
634 | |||
635 | return rc; | ||
636 | } | ||
637 | |||
638 | int ata_write_sectors(IF_MV2(int drive,) | ||
639 | unsigned long start, | ||
640 | int count, | ||
641 | const void* buf) | ||
642 | { | ||
643 | int rc = 0; | ||
644 | int offset; | ||
645 | |||
646 | #ifdef HAVE_MULTIVOLUME | ||
647 | (void)drive; /* unused for now */ | ||
648 | #endif | ||
649 | spinlock_lock(&ata_mtx); | ||
650 | |||
651 | offset = start & (phys_sector_mult - 1); | ||
652 | |||
653 | if (offset) /* first partial sector */ | ||
654 | { | ||
655 | int partcount = MIN(count, phys_sector_mult - offset); | ||
656 | |||
657 | rc = cache_sector(start); | ||
658 | if (rc) | ||
659 | { | ||
660 | rc = rc * 10 - 1; | ||
661 | goto error; | ||
662 | } | ||
663 | memcpy(sector_cache.data + offset * SECTOR_SIZE, buf, | ||
664 | partcount * SECTOR_SIZE); | ||
665 | rc = flush_current_sector(); | ||
666 | if (rc) | ||
667 | { | ||
668 | rc = rc * 10 - 2; | ||
669 | goto error; | ||
670 | } | ||
671 | start += partcount; | ||
672 | buf += partcount * SECTOR_SIZE; | ||
673 | count -= partcount; | ||
674 | } | ||
675 | if (count) | ||
676 | { | ||
677 | offset = count & (phys_sector_mult - 1); | ||
678 | count -= offset; | ||
679 | |||
680 | if (count) | ||
681 | { | ||
682 | rc = _write_sectors(start, count, buf); | ||
683 | if (rc) | ||
684 | { | ||
685 | rc = rc * 10 - 3; | ||
686 | goto error; | ||
687 | } | ||
688 | start += count; | ||
689 | buf += count * SECTOR_SIZE; | ||
690 | } | ||
691 | if (offset) | ||
692 | { | ||
693 | rc = cache_sector(start); | ||
694 | if (rc) | ||
695 | { | ||
696 | rc = rc * 10 - 4; | ||
697 | goto error; | ||
698 | } | ||
699 | memcpy(sector_cache.data, buf, offset * SECTOR_SIZE); | ||
700 | rc = flush_current_sector(); | ||
701 | if (rc) | ||
702 | { | ||
703 | rc = rc * 10 - 5; | ||
704 | goto error; | ||
705 | } | ||
706 | } | ||
707 | } | ||
708 | |||
709 | error: | ||
710 | spinlock_unlock(&ata_mtx); | ||
711 | |||
712 | return rc; | ||
713 | } | ||
714 | #endif /* MAX_PHYS_SECTOR_SIZE */ | ||
715 | |||
514 | static int check_registers(void) | 716 | static int check_registers(void) |
515 | { | 717 | { |
516 | #if (CONFIG_CPU == PP5002) | 718 | #if (CONFIG_CPU == PP5002) |
@@ -941,6 +1143,9 @@ int ata_init(void) | |||
941 | ata_device_init(); | 1143 | ata_device_init(); |
942 | sleeping = false; | 1144 | sleeping = false; |
943 | ata_enable(true); | 1145 | ata_enable(true); |
1146 | #ifdef MAX_PHYS_SECTOR_SIZE | ||
1147 | memset(§or_cache, 0, sizeof(sector_cache)); | ||
1148 | #endif | ||
944 | 1149 | ||
945 | if ( !initialized ) { | 1150 | if ( !initialized ) { |
946 | if (!ide_powered()) /* somebody has switched it off */ | 1151 | if (!ide_powered()) /* somebody has switched it off */ |
@@ -966,8 +1171,25 @@ int ata_init(void) | |||
966 | return -40 + rc; | 1171 | return -40 + rc; |
967 | 1172 | ||
968 | multisectors = identify_info[47] & 0xff; | 1173 | multisectors = identify_info[47] & 0xff; |
1174 | if (multisectors == 0) /* Invalid multisector info, try with 16 */ | ||
1175 | multisectors = 16; | ||
1176 | |||
969 | DEBUGF("ata: %d sectors per ata request\n",multisectors); | 1177 | DEBUGF("ata: %d sectors per ata request\n",multisectors); |
970 | 1178 | ||
1179 | #ifdef MAX_PHYS_SECTOR_SIZE | ||
1180 | /* Find out the physical sector size */ | ||
1181 | if((identify_info[106] & 0xe000) == 0x6000) | ||
1182 | phys_sector_mult = 1 << (identify_info[106] & 0x000f); | ||
1183 | else | ||
1184 | phys_sector_mult = 1; | ||
1185 | |||
1186 | DEBUGF("ata: %d logical sectors per phys sector", phys_sector_mult); | ||
1187 | |||
1188 | if (phys_sector_mult > (MAX_PHYS_SECTOR_SIZE/SECTOR_SIZE)) | ||
1189 | panicf("Unsupported physical sector size: %d", | ||
1190 | phys_sector_mult * SECTOR_SIZE); | ||
1191 | #endif | ||
1192 | |||
971 | #ifdef HAVE_LBA48 | 1193 | #ifdef HAVE_LBA48 |
972 | if (identify_info[83] & 0x0400 /* 48 bit address support */ | 1194 | if (identify_info[83] & 0x0400 /* 48 bit address support */ |
973 | && identify_info[60] == 0xFFFF /* and disk size >= 128 GiB */ | 1195 | && identify_info[60] == 0xFFFF /* and disk size >= 128 GiB */ |