summaryrefslogtreecommitdiff
path: root/firmware/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers')
-rw-r--r--firmware/drivers/ata.c234
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;
92static int multisectors; /* number of supported multisectors */ 92static int multisectors; /* number of supported multisectors */
93static unsigned short identify_info[SECTOR_SIZE]; 93static unsigned short identify_info[SECTOR_SIZE];
94 94
95#ifdef MAX_PHYS_SECTOR_SIZE
96struct 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
103static struct sector_cache_entry sector_cache;
104static int phys_sector_mult = 1;
105#endif
106
95static int ata_power_on(void); 107static int ata_power_on(void);
96static int perform_soft_reset(void); 108static int perform_soft_reset(void);
97static int set_multiple_mode(int sectors); 109static 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
217static int _read_sectors(unsigned long start,
218 int incount,
219 void* inbuf)
220#else
204int ata_read_sectors(IF_MV2(int drive,) 221int 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
424static int _write_sectors(unsigned long start,
425 int count,
426 const void* buf)
427#else
404int ata_write_sectors(IF_MV2(int drive,) 428int 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
544static 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
566static inline int flush_current_sector(void)
567{
568 return _write_sectors(sector_cache.sectornum, phys_sector_mult,
569 sector_cache.data);
570}
571
572int 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
638int 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
514static int check_registers(void) 716static 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(&sector_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 */