summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000/sd-x1000.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/sd-x1000.c')
-rw-r--r--firmware/target/mips/ingenic_x1000/sd-x1000.c236
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
31static msc_drv* sd_to_msc[MSC_COUNT];
32static long _sd_last_disk_activity = 0;
33
34static 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
52static 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
144int 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
151int 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
158tCardInfo* 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
169int 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
182long sd_last_disk_activity(void)
183{
184 return _sd_last_disk_activity;
185}
186
187bool 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
198bool 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
208static
209#endif
210int 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
228int sd_init(void)
229{
230 msc_init();
231#ifndef CONFIG_STORAGE_MULTI
232 sd_num_drives(0);
233#endif
234
235 return 0;
236}