diff options
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/sd-x1000.c')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/sd-x1000.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_x1000/sd-x1000.c b/firmware/target/mips/ingenic_x1000/sd-x1000.c new file mode 100644 index 0000000000..7fba617ce3 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/sd-x1000.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "storage.h" | ||
23 | #include "sdmmc.h" | ||
24 | #include "sd.h" | ||
25 | #include "msc-x1000.h" | ||
26 | #include <string.h> | ||
27 | |||
28 | /* #define LOGF_ENABLE */ | ||
29 | #include "logf.h" | ||
30 | |||
31 | static msc_drv* sd_to_msc[MSC_COUNT]; | ||
32 | static long _sd_last_disk_activity = 0; | ||
33 | |||
34 | static int sd_init_card(msc_drv* d) | ||
35 | { | ||
36 | int s; | ||
37 | if(s = msc_cmd_go_idle_state(d)) return -100 - s; | ||
38 | if(s = msc_cmd_send_if_cond(d)) return -110 - s; | ||
39 | if(s = msc_cmd_app_op_cond(d)) return -120 - s; | ||
40 | if(s = msc_cmd_all_send_cid(d)) return -130 - s; | ||
41 | if(s = msc_cmd_send_rca(d)) return -140 - s; | ||
42 | if(s = msc_cmd_send_csd(d)) return -150 - s; | ||
43 | if(s = msc_cmd_select_card(d)) return -160 - s; | ||
44 | if(s = msc_cmd_set_clr_card_detect(d, 0)) return -170 - s; | ||
45 | if(s = msc_cmd_set_bus_width(d, 4)) return -180 - s; | ||
46 | if(s = msc_cmd_switch_freq(d)) return -190 - s; | ||
47 | d->driver_flags |= MSC_DF_READY; | ||
48 | d->cardinfo.initialized = 1; | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static int sd_transfer(msc_drv* d, bool write, | ||
53 | unsigned long start, int count, void* buf) | ||
54 | { | ||
55 | int status = -1; | ||
56 | |||
57 | msc_lock(d); | ||
58 | if(!d->card_present) | ||
59 | goto _exit; | ||
60 | |||
61 | /* Hopefully puts the driver into a working state */ | ||
62 | if(d->driver_flags & MSC_DF_ERRSTATE) { | ||
63 | logf("MSC%d: attempting to reset after ERRSTATE", d->msc_nr); | ||
64 | msc_full_reset(d); | ||
65 | } | ||
66 | |||
67 | /* Init card if needed */ | ||
68 | if((d->driver_flags & MSC_DF_READY) == 0) { | ||
69 | if(status = sd_init_card(d)) { | ||
70 | logf("MSC%d: card init failed (code %d)", d->msc_nr, status); | ||
71 | d->driver_flags |= MSC_DF_ERRSTATE; | ||
72 | d->cardinfo.initialized = status; | ||
73 | goto _exit; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | /* Ensure parameters are within range */ | ||
78 | if(count < 1) | ||
79 | goto _exit; | ||
80 | if(start + count > d->cardinfo.numblocks) | ||
81 | goto _exit; | ||
82 | |||
83 | do { | ||
84 | /* We can only do 65536 blocks at a time */ | ||
85 | int xfer_count = count > 0xffff ? 0xffff : count; | ||
86 | |||
87 | /* Set block length. I think this is only necessary for non-HCS cards. | ||
88 | * HCS cards always use 512 bytes so we shouldn't need it. | ||
89 | */ | ||
90 | if((d->driver_flags & MSC_DF_HCS_CARD) == 0) | ||
91 | if(status = msc_cmd_set_block_len(d, SD_BLOCK_SIZE)) | ||
92 | goto _exit; | ||
93 | |||
94 | /* TODO - look into using CMD23 to improve transfer performance. | ||
95 | * This specifies the number of blocks ahead of time, instead of | ||
96 | * relying on CMD12 to stop transmission. CMD12 is still needed | ||
97 | * in the event of errors though. | ||
98 | */ | ||
99 | msc_req req = {0}; | ||
100 | req.data = buf; | ||
101 | req.nr_blocks = xfer_count; | ||
102 | req.block_len = SD_BLOCK_SIZE; | ||
103 | req.resptype = MSC_RESP_R1; | ||
104 | req.flags = MSC_RF_DATA; | ||
105 | if(xfer_count > 1) | ||
106 | req.flags |= MSC_RF_AUTO_CMD12; | ||
107 | if(write) { | ||
108 | req.command = xfer_count == 1 ? SD_WRITE_BLOCK | ||
109 | : SD_WRITE_MULTIPLE_BLOCK; | ||
110 | req.flags |= MSC_RF_PROG | MSC_RF_WRITE; | ||
111 | } else { | ||
112 | req.command = xfer_count == 1 ? SD_READ_SINGLE_BLOCK | ||
113 | : SD_READ_MULTIPLE_BLOCK; | ||
114 | } | ||
115 | |||
116 | if(d->driver_flags & MSC_DF_V2_CARD) | ||
117 | req.argument = start; | ||
118 | else | ||
119 | req.argument = start * SD_BLOCK_SIZE; | ||
120 | |||
121 | if(status = msc_cmd_exec(d, &req)) | ||
122 | goto _exit; | ||
123 | |||
124 | /* TODO - properly handle reading the last block of the SD card | ||
125 | * This is likely to fail if we're reading near the end because | ||
126 | * the SD card will try to read past the last sector and then | ||
127 | * signal an error. So we need to ignore that error, but only if | ||
128 | * it was expected to occur. (See SD spec sec. 4.3.3, "Block Read") | ||
129 | */ | ||
130 | if(status = msc_cmd_send_status(d)) | ||
131 | goto _exit; | ||
132 | |||
133 | /* Advance the buffer and adjust start/count */ | ||
134 | buf += xfer_count * SD_BLOCK_SIZE; | ||
135 | start += xfer_count; | ||
136 | count -= xfer_count; | ||
137 | } while(count > 0); | ||
138 | |||
139 | _exit: | ||
140 | msc_unlock(d); | ||
141 | return status; | ||
142 | } | ||
143 | |||
144 | int sd_read_sectors(IF_MD(int drive,) unsigned long start, | ||
145 | int count, void* buf) | ||
146 | { | ||
147 | return sd_transfer(sd_to_msc[IF_MD_DRV(drive)], false, | ||
148 | start, count, buf); | ||
149 | } | ||
150 | |||
151 | int sd_write_sectors(IF_MD(int drive,) unsigned long start, | ||
152 | int count, const void* buf) | ||
153 | { | ||
154 | return sd_transfer(sd_to_msc[IF_MD_DRV(drive)], true, | ||
155 | start, count, (void*)buf); | ||
156 | } | ||
157 | |||
158 | tCardInfo* card_get_info_target(int card_nr) | ||
159 | { | ||
160 | /* Defensive measures */ | ||
161 | if(card_nr < 0 || card_nr >= MSC_COUNT || sd_to_msc[card_nr] == NULL) { | ||
162 | static tCardInfo null_info = { 0 }; | ||
163 | return &null_info; | ||
164 | } | ||
165 | |||
166 | return &sd_to_msc[card_nr]->cardinfo; | ||
167 | } | ||
168 | |||
169 | int sd_event(long id, intptr_t data) | ||
170 | { | ||
171 | if(id == SYS_HOTSWAP_EXTRACTED) { | ||
172 | msc_drv* d = msc_get_by_drive(data); | ||
173 | if(d) | ||
174 | msc_full_reset(d); | ||
175 | return 0; | ||
176 | } else { | ||
177 | return storage_event_default_handler(id, data, _sd_last_disk_activity, | ||
178 | STORAGE_SD); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | long sd_last_disk_activity(void) | ||
183 | { | ||
184 | return _sd_last_disk_activity; | ||
185 | } | ||
186 | |||
187 | bool sd_present(IF_MD_NONVOID(int drive)) | ||
188 | { | ||
189 | /* Seems that volume_properties() in firmware/common/disk.c may pass | ||
190 | * drive = -1 when the SD card is not inserted, so just return false. | ||
191 | */ | ||
192 | if(drive < 0) | ||
193 | return false; | ||
194 | |||
195 | return sd_to_msc[IF_MD_DRV(drive)]->card_present; | ||
196 | } | ||
197 | |||
198 | bool sd_removable(IF_MD_NONVOID(int drive)) | ||
199 | { | ||
200 | /* Same reason as sd_present() */ | ||
201 | if(drive < 0) | ||
202 | return false; | ||
203 | |||
204 | return sd_to_msc[IF_MD_DRV(drive)]->config->cd_gpio.pin != 0; | ||
205 | } | ||
206 | |||
207 | #ifndef CONFIG_STORAGE_MULTI | ||
208 | static | ||
209 | #endif | ||
210 | int sd_num_drives(int first_drive) | ||
211 | { | ||
212 | int n = 0; | ||
213 | for(; n < MSC_COUNT; ++n) { | ||
214 | msc_drv* d = msc_get(MSC_TYPE_SD, n); | ||
215 | if(d == NULL) | ||
216 | break; | ||
217 | |||
218 | d->drive_nr = first_drive + n; | ||
219 | sd_to_msc[n] = d; | ||
220 | } | ||
221 | |||
222 | for(int i = n; i < MSC_COUNT; ++i) | ||
223 | sd_to_msc[i] = NULL; | ||
224 | |||
225 | return n; | ||
226 | } | ||
227 | |||
228 | int sd_init(void) | ||
229 | { | ||
230 | msc_init(); | ||
231 | #ifndef CONFIG_STORAGE_MULTI | ||
232 | sd_num_drives(0); | ||
233 | #endif | ||
234 | |||
235 | return 0; | ||
236 | } | ||