diff options
Diffstat (limited to 'firmware/drivers/ata-common.c')
-rw-r--r-- | firmware/drivers/ata-common.c | 238 |
1 files changed, 238 insertions, 0 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 */ | ||