diff options
author | Solomon Peachy <pizza@shaftnet.org> | 2024-11-01 19:58:22 -0400 |
---|---|---|
committer | Solomon Peachy <pizza@shaftnet.org> | 2024-11-04 07:33:26 -0500 |
commit | 2824bd5f1644a6b9129a0c0fcfe2bafab91a7225 (patch) | |
tree | c50259c7efcd751ed4666e4e538609097f43ce80 /firmware/drivers | |
parent | d60dee6188ee134ddd3019a219c41e39df1ae5ff (diff) | |
download | rockbox-2824bd5f1644a6b9129a0c0fcfe2bafab91a7225.tar.gz rockbox-2824bd5f1644a6b9129a0c0fcfe2bafab91a7225.zip |
ipod6g: Support MAX_PHYS_SECTOR_SIZE of 4K
This lets us *natively* handle varying physical sector sizes
without playing games and lying about the logical sector size.
(The original drives use 4K _physical_ sectors with 512B logical
sectors, but you have to access everything in 4K blocks...)
Achieve this by splitting the MAX_PHYS_SECTOR_SIZE code out
of the main ATA driver and re-using it.
Change-Id: I0bc615ab4562f1e3e83171a8633c74fb60c7da1f
Diffstat (limited to 'firmware/drivers')
-rw-r--r-- | firmware/drivers/ata-common.c | 238 | ||||
-rw-r--r-- | firmware/drivers/ata.c | 218 |
2 files changed, 245 insertions, 211 deletions
diff --git a/firmware/drivers/ata-common.c b/firmware/drivers/ata-common.c new file mode 100644 index 0000000000..53a7780262 --- /dev/null +++ b/firmware/drivers/ata-common.c | |||
@@ -0,0 +1,238 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2024 Solomon Peachy | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * as published by the Free Software Foundation; either version 2 | ||
14 | * of the License, or (at your option) any later version. | ||
15 | * | ||
16 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
17 | * KIND, either express or implied. | ||
18 | * | ||
19 | ****************************************************************************/ | ||
20 | |||
21 | /* This is intended to be #included into the ATA driver */ | ||
22 | |||
23 | #ifdef MAX_PHYS_SECTOR_SIZE | ||
24 | |||
25 | struct sector_cache_entry { | ||
26 | unsigned char data[MAX_PHYS_SECTOR_SIZE]; | ||
27 | sector_t sectornum; /* logical sector */ | ||
28 | bool inuse; | ||
29 | }; | ||
30 | /* buffer for reading and writing large physical sectors */ | ||
31 | static struct sector_cache_entry sector_cache STORAGE_ALIGN_ATTR; | ||
32 | static int phys_sector_mult = 1; | ||
33 | |||
34 | static int cache_sector(sector_t sector) | ||
35 | { | ||
36 | int rc; | ||
37 | |||
38 | /* round down to physical sector boundary */ | ||
39 | sector &= ~(phys_sector_mult - 1); | ||
40 | |||
41 | /* check whether the sector is already cached */ | ||
42 | if (sector_cache.inuse && (sector_cache.sectornum == sector)) | ||
43 | return 0; | ||
44 | |||
45 | /* not found: read the sector */ | ||
46 | sector_cache.inuse = false; | ||
47 | rc = ata_transfer_sectors(sector, phys_sector_mult, sector_cache.data, false); | ||
48 | if (!rc) | ||
49 | { | ||
50 | sector_cache.sectornum = sector; | ||
51 | sector_cache.inuse = true; | ||
52 | } | ||
53 | return rc; | ||
54 | } | ||
55 | |||
56 | static inline int flush_current_sector(void) | ||
57 | { | ||
58 | return ata_transfer_sectors(sector_cache.sectornum, phys_sector_mult, | ||
59 | sector_cache.data, true); | ||
60 | } | ||
61 | |||
62 | int ata_read_sectors(IF_MD(int drive,) | ||
63 | sector_t start, | ||
64 | int incount, | ||
65 | void* inbuf) | ||
66 | { | ||
67 | int rc = 0; | ||
68 | int offset; | ||
69 | |||
70 | #ifdef HAVE_MULTIDRIVE | ||
71 | (void)drive; /* unused for now */ | ||
72 | #endif | ||
73 | mutex_lock(&ata_mutex); | ||
74 | |||
75 | offset = start & (phys_sector_mult - 1); | ||
76 | |||
77 | if (offset) /* first partial sector */ | ||
78 | { | ||
79 | int partcount = MIN(incount, phys_sector_mult - offset); | ||
80 | |||
81 | rc = cache_sector(start); | ||
82 | if (rc) | ||
83 | { | ||
84 | rc = rc * 10 - 1; | ||
85 | goto error; | ||
86 | } | ||
87 | memcpy(inbuf, sector_cache.data + offset * SECTOR_SIZE, | ||
88 | partcount * SECTOR_SIZE); | ||
89 | |||
90 | start += partcount; | ||
91 | inbuf += partcount * SECTOR_SIZE; | ||
92 | incount -= partcount; | ||
93 | } | ||
94 | if (incount) | ||
95 | { | ||
96 | offset = incount & (phys_sector_mult - 1); | ||
97 | incount -= offset; | ||
98 | |||
99 | if (incount) | ||
100 | { | ||
101 | rc = ata_transfer_sectors(start, incount, inbuf, false); | ||
102 | if (rc) | ||
103 | { | ||
104 | rc = rc * 10 - 2; | ||
105 | goto error; | ||
106 | } | ||
107 | start += incount; | ||
108 | inbuf += incount * SECTOR_SIZE; | ||
109 | } | ||
110 | if (offset) | ||
111 | { | ||
112 | rc = cache_sector(start); | ||
113 | if (rc) | ||
114 | { | ||
115 | rc = rc * 10 - 3; | ||
116 | goto error; | ||
117 | } | ||
118 | memcpy(inbuf, sector_cache.data, offset * SECTOR_SIZE); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | error: | ||
123 | mutex_unlock(&ata_mutex); | ||
124 | |||
125 | return rc; | ||
126 | } | ||
127 | |||
128 | int ata_write_sectors(IF_MD(int drive,) | ||
129 | sector_t start, | ||
130 | int count, | ||
131 | const void* buf) | ||
132 | { | ||
133 | int rc = 0; | ||
134 | int offset; | ||
135 | |||
136 | #ifdef HAVE_MULTIDRIVE | ||
137 | (void)drive; /* unused for now */ | ||
138 | #endif | ||
139 | mutex_lock(&ata_mutex); | ||
140 | |||
141 | offset = start & (phys_sector_mult - 1); | ||
142 | |||
143 | if (offset) /* first partial sector */ | ||
144 | { | ||
145 | int partcount = MIN(count, phys_sector_mult - offset); | ||
146 | |||
147 | rc = cache_sector(start); | ||
148 | if (rc) | ||
149 | { | ||
150 | rc = rc * 10 - 1; | ||
151 | goto error; | ||
152 | } | ||
153 | memcpy(sector_cache.data + offset * SECTOR_SIZE, buf, | ||
154 | partcount * SECTOR_SIZE); | ||
155 | rc = flush_current_sector(); | ||
156 | if (rc) | ||
157 | { | ||
158 | rc = rc * 10 - 2; | ||
159 | goto error; | ||
160 | } | ||
161 | start += partcount; | ||
162 | buf += partcount * SECTOR_SIZE; | ||
163 | count -= partcount; | ||
164 | } | ||
165 | if (count) | ||
166 | { | ||
167 | offset = count & (phys_sector_mult - 1); | ||
168 | count -= offset; | ||
169 | |||
170 | if (count) | ||
171 | { | ||
172 | rc = ata_transfer_sectors(start, count, (void*)buf, true); | ||
173 | if (rc) | ||
174 | { | ||
175 | rc = rc * 10 - 3; | ||
176 | goto error; | ||
177 | } | ||
178 | start += count; | ||
179 | buf += count * SECTOR_SIZE; | ||
180 | } | ||
181 | if (offset) | ||
182 | { | ||
183 | rc = cache_sector(start); | ||
184 | if (rc) | ||
185 | { | ||
186 | rc = rc * 10 - 4; | ||
187 | goto error; | ||
188 | } | ||
189 | memcpy(sector_cache.data, buf, offset * SECTOR_SIZE); | ||
190 | rc = flush_current_sector(); | ||
191 | if (rc) | ||
192 | { | ||
193 | rc = rc * 10 - 5; | ||
194 | goto error; | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | |||
199 | error: | ||
200 | mutex_unlock(&ata_mutex); | ||
201 | |||
202 | return rc; | ||
203 | } | ||
204 | |||
205 | static int ata_get_phys_sector_mult(void) | ||
206 | { | ||
207 | int rc = 0; | ||
208 | |||
209 | /* Find out the physical sector size */ | ||
210 | if((identify_info[106] & 0xe000) == 0x6000) /* B14, B13 */ | ||
211 | phys_sector_mult = BIT_N(identify_info[106] & 0x000f); | ||
212 | else | ||
213 | phys_sector_mult = 1; | ||
214 | |||
215 | DEBUGF("ata: %d logical sectors per phys sector", phys_sector_mult); | ||
216 | |||
217 | if (phys_sector_mult > 1) | ||
218 | { | ||
219 | /* Check if drive really needs emulation - if we can access | ||
220 | sector 1 then assume the drive supports "512e" and will handle | ||
221 | it better than us, so ignore the large physical sectors. | ||
222 | */ | ||
223 | char throwaway[SECTOR_SIZE]; | ||
224 | rc = ata_transfer_sectors(1, 1, &throwaway, false); | ||
225 | if (rc == 0) | ||
226 | phys_sector_mult = 1; | ||
227 | } | ||
228 | |||
229 | if (phys_sector_mult > (MAX_PHYS_SECTOR_SIZE/SECTOR_SIZE)) | ||
230 | panicf("Unsupported physical sector size: %d", | ||
231 | phys_sector_mult * SECTOR_SIZE); | ||
232 | |||
233 | memset(§or_cache, 0, sizeof(sector_cache)); | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | #endif /* MAX_PHYS_SECTOR_SIZE */ | ||
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c index 119297ff02..7b43d3c536 100644 --- a/firmware/drivers/ata.c +++ b/firmware/drivers/ata.c | |||
@@ -115,17 +115,6 @@ static int multisectors; /* number of supported multisectors */ | |||
115 | 115 | ||
116 | static unsigned short identify_info[ATA_IDENTIFY_WORDS] STORAGE_ALIGN_ATTR; | 116 | static unsigned short identify_info[ATA_IDENTIFY_WORDS] STORAGE_ALIGN_ATTR; |
117 | 117 | ||
118 | #ifdef MAX_PHYS_SECTOR_SIZE | ||
119 | struct sector_cache_entry { | ||
120 | unsigned char data[MAX_PHYS_SECTOR_SIZE]; | ||
121 | sector_t sectornum; /* logical sector */ | ||
122 | bool inuse; | ||
123 | }; | ||
124 | /* buffer for reading and writing large physical sectors */ | ||
125 | static struct sector_cache_entry sector_cache STORAGE_ALIGN_ATTR; | ||
126 | static int phys_sector_mult = 1; | ||
127 | #endif | ||
128 | |||
129 | #ifdef HAVE_ATA_DMA | 118 | #ifdef HAVE_ATA_DMA |
130 | static int dma_mode = 0; | 119 | static int dma_mode = 0; |
131 | #endif | 120 | #endif |
@@ -600,6 +589,8 @@ static int ata_transfer_sectors(uint64_t start, | |||
600 | return ret; | 589 | return ret; |
601 | } | 590 | } |
602 | 591 | ||
592 | #include "ata-common.c" | ||
593 | |||
603 | #ifndef MAX_PHYS_SECTOR_SIZE | 594 | #ifndef MAX_PHYS_SECTOR_SIZE |
604 | int ata_read_sectors(IF_MD(int drive,) | 595 | int ata_read_sectors(IF_MD(int drive,) |
605 | sector_t start, | 596 | sector_t start, |
@@ -632,179 +623,6 @@ int ata_write_sectors(IF_MD(int drive,) | |||
632 | } | 623 | } |
633 | #endif /* ndef MAX_PHYS_SECTOR_SIZE */ | 624 | #endif /* ndef MAX_PHYS_SECTOR_SIZE */ |
634 | 625 | ||
635 | #ifdef MAX_PHYS_SECTOR_SIZE | ||
636 | static int cache_sector(sector_t sector) | ||
637 | { | ||
638 | int rc; | ||
639 | |||
640 | /* round down to physical sector boundary */ | ||
641 | sector &= ~(phys_sector_mult - 1); | ||
642 | |||
643 | /* check whether the sector is already cached */ | ||
644 | if (sector_cache.inuse && (sector_cache.sectornum == sector)) | ||
645 | return 0; | ||
646 | |||
647 | /* not found: read the sector */ | ||
648 | sector_cache.inuse = false; | ||
649 | rc = ata_transfer_sectors(sector, phys_sector_mult, sector_cache.data, false); | ||
650 | if (!rc) | ||
651 | { | ||
652 | sector_cache.sectornum = sector; | ||
653 | sector_cache.inuse = true; | ||
654 | } | ||
655 | return rc; | ||
656 | } | ||
657 | |||
658 | static inline int flush_current_sector(void) | ||
659 | { | ||
660 | return ata_transfer_sectors(sector_cache.sectornum, phys_sector_mult, | ||
661 | sector_cache.data, true); | ||
662 | } | ||
663 | |||
664 | int ata_read_sectors(IF_MD(int drive,) | ||
665 | sector_t start, | ||
666 | int incount, | ||
667 | void* inbuf) | ||
668 | { | ||
669 | int rc = 0; | ||
670 | int offset; | ||
671 | |||
672 | #ifdef HAVE_MULTIDRIVE | ||
673 | (void)drive; /* unused for now */ | ||
674 | #endif | ||
675 | mutex_lock(&ata_mutex); | ||
676 | |||
677 | offset = start & (phys_sector_mult - 1); | ||
678 | |||
679 | if (offset) /* first partial sector */ | ||
680 | { | ||
681 | int partcount = MIN(incount, phys_sector_mult - offset); | ||
682 | |||
683 | rc = cache_sector(start); | ||
684 | if (rc) | ||
685 | { | ||
686 | rc = rc * 10 - 1; | ||
687 | goto error; | ||
688 | } | ||
689 | memcpy(inbuf, sector_cache.data + offset * SECTOR_SIZE, | ||
690 | partcount * SECTOR_SIZE); | ||
691 | |||
692 | start += partcount; | ||
693 | inbuf += partcount * SECTOR_SIZE; | ||
694 | incount -= partcount; | ||
695 | } | ||
696 | if (incount) | ||
697 | { | ||
698 | offset = incount & (phys_sector_mult - 1); | ||
699 | incount -= offset; | ||
700 | |||
701 | if (incount) | ||
702 | { | ||
703 | rc = ata_transfer_sectors(start, incount, inbuf, false); | ||
704 | if (rc) | ||
705 | { | ||
706 | rc = rc * 10 - 2; | ||
707 | goto error; | ||
708 | } | ||
709 | start += incount; | ||
710 | inbuf += incount * SECTOR_SIZE; | ||
711 | } | ||
712 | if (offset) | ||
713 | { | ||
714 | rc = cache_sector(start); | ||
715 | if (rc) | ||
716 | { | ||
717 | rc = rc * 10 - 3; | ||
718 | goto error; | ||
719 | } | ||
720 | memcpy(inbuf, sector_cache.data, offset * SECTOR_SIZE); | ||
721 | } | ||
722 | } | ||
723 | |||
724 | error: | ||
725 | mutex_unlock(&ata_mutex); | ||
726 | |||
727 | return rc; | ||
728 | } | ||
729 | |||
730 | int ata_write_sectors(IF_MD(int drive,) | ||
731 | sector_t start, | ||
732 | int count, | ||
733 | const void* buf) | ||
734 | { | ||
735 | int rc = 0; | ||
736 | int offset; | ||
737 | |||
738 | #ifdef HAVE_MULTIDRIVE | ||
739 | (void)drive; /* unused for now */ | ||
740 | #endif | ||
741 | mutex_lock(&ata_mutex); | ||
742 | |||
743 | offset = start & (phys_sector_mult - 1); | ||
744 | |||
745 | if (offset) /* first partial sector */ | ||
746 | { | ||
747 | int partcount = MIN(count, phys_sector_mult - offset); | ||
748 | |||
749 | rc = cache_sector(start); | ||
750 | if (rc) | ||
751 | { | ||
752 | rc = rc * 10 - 1; | ||
753 | goto error; | ||
754 | } | ||
755 | memcpy(sector_cache.data + offset * SECTOR_SIZE, buf, | ||
756 | partcount * SECTOR_SIZE); | ||
757 | rc = flush_current_sector(); | ||
758 | if (rc) | ||
759 | { | ||
760 | rc = rc * 10 - 2; | ||
761 | goto error; | ||
762 | } | ||
763 | start += partcount; | ||
764 | buf += partcount * SECTOR_SIZE; | ||
765 | count -= partcount; | ||
766 | } | ||
767 | if (count) | ||
768 | { | ||
769 | offset = count & (phys_sector_mult - 1); | ||
770 | count -= offset; | ||
771 | |||
772 | if (count) | ||
773 | { | ||
774 | rc = ata_transfer_sectors(start, count, (void*)buf, true); | ||
775 | if (rc) | ||
776 | { | ||
777 | rc = rc * 10 - 3; | ||
778 | goto error; | ||
779 | } | ||
780 | start += count; | ||
781 | buf += count * SECTOR_SIZE; | ||
782 | } | ||
783 | if (offset) | ||
784 | { | ||
785 | rc = cache_sector(start); | ||
786 | if (rc) | ||
787 | { | ||
788 | rc = rc * 10 - 4; | ||
789 | goto error; | ||
790 | } | ||
791 | memcpy(sector_cache.data, buf, offset * SECTOR_SIZE); | ||
792 | rc = flush_current_sector(); | ||
793 | if (rc) | ||
794 | { | ||
795 | rc = rc * 10 - 5; | ||
796 | goto error; | ||
797 | } | ||
798 | } | ||
799 | } | ||
800 | |||
801 | error: | ||
802 | mutex_unlock(&ata_mutex); | ||
803 | |||
804 | return rc; | ||
805 | } | ||
806 | #endif /* MAX_PHYS_SECTOR_SIZE */ | ||
807 | |||
808 | static int STORAGE_INIT_ATTR check_registers(void) | 626 | static int STORAGE_INIT_ATTR check_registers(void) |
809 | { | 627 | { |
810 | int i; | 628 | int i; |
@@ -1242,9 +1060,6 @@ int STORAGE_INIT_ATTR ata_init(void) | |||
1242 | ata_led(false); | 1060 | ata_led(false); |
1243 | ata_device_init(); | 1061 | ata_device_init(); |
1244 | ata_enable(true); | 1062 | ata_enable(true); |
1245 | #ifdef MAX_PHYS_SECTOR_SIZE | ||
1246 | memset(§or_cache, 0, sizeof(sector_cache)); | ||
1247 | #endif | ||
1248 | 1063 | ||
1249 | if (ata_state == ATA_BOOT) { | 1064 | if (ata_state == ATA_BOOT) { |
1250 | ata_state = ATA_OFF; | 1065 | ata_state = ATA_OFF; |
@@ -1309,31 +1124,12 @@ int STORAGE_INIT_ATTR ata_init(void) | |||
1309 | } | 1124 | } |
1310 | 1125 | ||
1311 | #ifdef MAX_PHYS_SECTOR_SIZE | 1126 | #ifdef MAX_PHYS_SECTOR_SIZE |
1312 | /* Find out the physical sector size */ | 1127 | rc = ata_get_phys_sector_mult(); |
1313 | if((identify_info[106] & 0xe000) == 0x6000) /* B14, B13 */ | 1128 | if (rc) { |
1314 | phys_sector_mult = BIT_N(identify_info[106] & 0x000f); | 1129 | rc = -70 + rc; |
1315 | else | 1130 | goto error; |
1316 | phys_sector_mult = 1; | ||
1317 | |||
1318 | DEBUGF("ata: %d logical sectors per phys sector", phys_sector_mult); | ||
1319 | |||
1320 | if (phys_sector_mult > 1) | ||
1321 | { | ||
1322 | /* Check if drive really needs emulation - if we can access | ||
1323 | sector 1 then assume the drive supports "512e" and will handle | ||
1324 | it better than us, so ignore the large physical sectors. | ||
1325 | */ | ||
1326 | char throwaway[SECTOR_SIZE]; | ||
1327 | rc = ata_transfer_sectors(1, 1, &throwaway, false); | ||
1328 | if (rc == 0) | ||
1329 | phys_sector_mult = 1; | ||
1330 | } | 1131 | } |
1331 | 1132 | #endif | |
1332 | if (phys_sector_mult > (MAX_PHYS_SECTOR_SIZE/SECTOR_SIZE)) | ||
1333 | panicf("Unsupported physical sector size: %d", | ||
1334 | phys_sector_mult * SECTOR_SIZE); | ||
1335 | #endif /* MAX_PHYS_SECTOR_SIZE */ | ||
1336 | |||
1337 | ata_state = ATA_ON; | 1133 | ata_state = ATA_ON; |
1338 | keep_ata_active(); | 1134 | keep_ata_active(); |
1339 | } | 1135 | } |