summaryrefslogtreecommitdiff
path: root/firmware/drivers/ata-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/ata-common.c')
-rw-r--r--firmware/drivers/ata-common.c238
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
25struct 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 */
31static struct sector_cache_entry sector_cache STORAGE_ALIGN_ATTR;
32static int phys_sector_mult = 1;
33
34static 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
56static 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
62int 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
128int 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
205static 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(&sector_cache, 0, sizeof(sector_cache));
234
235 return 0;
236}
237
238#endif /* MAX_PHYS_SECTOR_SIZE */