]>
Commit | Line | Data |
---|---|---|
775616c3 PB |
1 | /* |
2 | * SSI to SD card adapter. | |
3 | * | |
5493e33f | 4 | * Copyright (c) 2007-2009 CodeSourcery. |
775616c3 PB |
5 | * Written by Paul Brook |
6 | * | |
d56f3efa BM |
7 | * Copyright (c) 2021 Wind River Systems, Inc. |
8 | * Improved by Bin Meng <bin.meng@windriver.com> | |
9 | * | |
10 | * Validated with U-Boot v2021.01 and Linux v5.10 mmc_spi driver | |
11 | * | |
8e31bf38 | 12 | * This code is licensed under the GNU GPL v2. |
6b620ca3 PB |
13 | * |
14 | * Contributions after 2012-01-13 are licensed under the terms of the | |
15 | * GNU GPL, version 2 or (at your option) any later version. | |
775616c3 PB |
16 | */ |
17 | ||
0430891c | 18 | #include "qemu/osdep.h" |
9c17d615 | 19 | #include "sysemu/blockdev.h" |
8fd06719 | 20 | #include "hw/ssi/ssi.h" |
d6454270 | 21 | #include "migration/vmstate.h" |
a27bd6c7 | 22 | #include "hw/qdev-properties.h" |
e3382ef0 | 23 | #include "hw/sd/sd.h" |
7673bb4c | 24 | #include "qapi/error.h" |
2d174cc3 | 25 | #include "qemu/crc-ccitt.h" |
0b8fa32f | 26 | #include "qemu/module.h" |
db1015e9 | 27 | #include "qom/object.h" |
775616c3 PB |
28 | |
29 | //#define DEBUG_SSI_SD 1 | |
30 | ||
31 | #ifdef DEBUG_SSI_SD | |
001faf32 BS |
32 | #define DPRINTF(fmt, ...) \ |
33 | do { printf("ssi_sd: " fmt , ## __VA_ARGS__); } while (0) | |
34 | #define BADF(fmt, ...) \ | |
35 | do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) | |
775616c3 | 36 | #else |
001faf32 BS |
37 | #define DPRINTF(fmt, ...) do {} while(0) |
38 | #define BADF(fmt, ...) \ | |
39 | do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__);} while (0) | |
775616c3 PB |
40 | #endif |
41 | ||
42 | typedef enum { | |
2ccfd336 | 43 | SSI_SD_CMD = 0, |
775616c3 | 44 | SSI_SD_CMDARG, |
281c5c95 | 45 | SSI_SD_PREP_RESP, |
775616c3 | 46 | SSI_SD_RESPONSE, |
3a67cbe6 | 47 | SSI_SD_PREP_DATA, |
775616c3 PB |
48 | SSI_SD_DATA_START, |
49 | SSI_SD_DATA_READ, | |
2d174cc3 | 50 | SSI_SD_DATA_CRC16, |
5020e3cb BM |
51 | SSI_SD_DATA_WRITE, |
52 | SSI_SD_SKIP_CRC16, | |
775616c3 PB |
53 | } ssi_sd_mode; |
54 | ||
db1015e9 | 55 | struct ssi_sd_state { |
ec7e429b | 56 | SSIPeripheral ssidev; |
2ccfd336 | 57 | uint32_t mode; |
775616c3 PB |
58 | int cmd; |
59 | uint8_t cmdarg[4]; | |
60 | uint8_t response[5]; | |
2d174cc3 | 61 | uint16_t crc16; |
1365d863 | 62 | int32_t read_bytes; |
5020e3cb | 63 | int32_t write_bytes; |
2ccfd336 DDAG |
64 | int32_t arglen; |
65 | int32_t response_pos; | |
66 | int32_t stopping; | |
c3abd913 | 67 | SDBus sdbus; |
db1015e9 | 68 | }; |
775616c3 | 69 | |
8046d44f | 70 | #define TYPE_SSI_SD "ssi-sd" |
8063396b | 71 | OBJECT_DECLARE_SIMPLE_TYPE(ssi_sd_state, SSI_SD) |
8046d44f | 72 | |
775616c3 PB |
73 | /* State word bits. */ |
74 | #define SSI_SDR_LOCKED 0x0001 | |
75 | #define SSI_SDR_WP_ERASE 0x0002 | |
76 | #define SSI_SDR_ERROR 0x0004 | |
77 | #define SSI_SDR_CC_ERROR 0x0008 | |
78 | #define SSI_SDR_ECC_FAILED 0x0010 | |
79 | #define SSI_SDR_WP_VIOLATION 0x0020 | |
80 | #define SSI_SDR_ERASE_PARAM 0x0040 | |
81 | #define SSI_SDR_OUT_OF_RANGE 0x0080 | |
82 | #define SSI_SDR_IDLE 0x0100 | |
83 | #define SSI_SDR_ERASE_RESET 0x0200 | |
84 | #define SSI_SDR_ILLEGAL_COMMAND 0x0400 | |
85 | #define SSI_SDR_COM_CRC_ERROR 0x0800 | |
86 | #define SSI_SDR_ERASE_SEQ_ERROR 0x1000 | |
87 | #define SSI_SDR_ADDRESS_ERROR 0x2000 | |
88 | #define SSI_SDR_PARAMETER_ERROR 0x4000 | |
89 | ||
d56f3efa BM |
90 | /* multiple block write */ |
91 | #define SSI_TOKEN_MULTI_WRITE 0xfc | |
92 | /* terminate multiple block write */ | |
93 | #define SSI_TOKEN_STOP_TRAN 0xfd | |
bc1edaf2 BM |
94 | /* single block read/write, multiple block read */ |
95 | #define SSI_TOKEN_SINGLE 0xfe | |
96 | ||
97 | /* dummy value - don't care */ | |
98 | #define SSI_DUMMY 0xff | |
99 | ||
5020e3cb BM |
100 | /* data accepted */ |
101 | #define DATA_RESPONSE_ACCEPTED 0x05 | |
102 | ||
ec7e429b | 103 | static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) |
775616c3 | 104 | { |
213f63df | 105 | ssi_sd_state *s = SSI_SD(dev); |
d56f3efa BM |
106 | SDRequest request; |
107 | uint8_t longresp[16]; | |
775616c3 | 108 | |
1365d863 BM |
109 | /* |
110 | * Special case: allow CMD12 (STOP TRANSMISSION) while reading data. | |
111 | * | |
112 | * See "Physical Layer Specification Version 8.00" chapter 7.5.2.2, | |
113 | * to avoid conflict between CMD12 response and next data block, | |
114 | * timing of CMD12 should be controlled as follows: | |
115 | * | |
116 | * - CMD12 issued at the timing that end bit of CMD12 and end bit of | |
117 | * data block is overlapped | |
118 | * - CMD12 issued after one clock cycle after host receives a token | |
119 | * (either Start Block token or Data Error token) | |
120 | * | |
121 | * We need to catch CMD12 in all of the data read states. | |
122 | */ | |
123 | if (s->mode >= SSI_SD_PREP_DATA && s->mode <= SSI_SD_DATA_CRC16) { | |
124 | if (val == 0x4c) { | |
125 | s->mode = SSI_SD_CMD; | |
126 | /* There must be at least one byte delay before the card responds */ | |
127 | s->stopping = 1; | |
128 | } | |
775616c3 PB |
129 | } |
130 | ||
131 | switch (s->mode) { | |
132 | case SSI_SD_CMD: | |
5020e3cb BM |
133 | switch (val) { |
134 | case SSI_DUMMY: | |
775616c3 | 135 | DPRINTF("NULL command\n"); |
bc1edaf2 | 136 | return SSI_DUMMY; |
5020e3cb BM |
137 | break; |
138 | case SSI_TOKEN_SINGLE: | |
d56f3efa | 139 | case SSI_TOKEN_MULTI_WRITE: |
5020e3cb BM |
140 | DPRINTF("Start write block\n"); |
141 | s->mode = SSI_SD_DATA_WRITE; | |
d56f3efa BM |
142 | return SSI_DUMMY; |
143 | case SSI_TOKEN_STOP_TRAN: | |
144 | DPRINTF("Stop multiple write\n"); | |
145 | ||
146 | /* manually issue cmd12 to stop the transfer */ | |
147 | request.cmd = 12; | |
148 | request.arg = 0; | |
149 | s->arglen = sdbus_do_command(&s->sdbus, &request, longresp); | |
150 | if (s->arglen <= 0) { | |
151 | s->arglen = 1; | |
152 | /* a zero value indicates the card is busy */ | |
153 | s->response[0] = 0; | |
154 | DPRINTF("SD card busy\n"); | |
155 | } else { | |
156 | s->arglen = 1; | |
157 | /* a non-zero value indicates the card is ready */ | |
158 | s->response[0] = SSI_DUMMY; | |
159 | } | |
160 | ||
5020e3cb | 161 | return SSI_DUMMY; |
775616c3 | 162 | } |
5020e3cb | 163 | |
775616c3 PB |
164 | s->cmd = val & 0x3f; |
165 | s->mode = SSI_SD_CMDARG; | |
166 | s->arglen = 0; | |
bc1edaf2 | 167 | return SSI_DUMMY; |
775616c3 PB |
168 | case SSI_SD_CMDARG: |
169 | if (s->arglen == 4) { | |
775616c3 PB |
170 | /* FIXME: Check CRC. */ |
171 | request.cmd = s->cmd; | |
b3141c06 | 172 | request.arg = ldl_be_p(s->cmdarg); |
775616c3 | 173 | DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg); |
c3abd913 | 174 | s->arglen = sdbus_do_command(&s->sdbus, &request, longresp); |
775616c3 PB |
175 | if (s->arglen <= 0) { |
176 | s->arglen = 1; | |
177 | s->response[0] = 4; | |
178 | DPRINTF("SD command failed\n"); | |
179 | } else if (s->cmd == 58) { | |
180 | /* CMD58 returns R3 response (OCR) */ | |
181 | DPRINTF("Returned OCR\n"); | |
182 | s->arglen = 5; | |
183 | s->response[0] = 1; | |
184 | memcpy(&s->response[1], longresp, 4); | |
185 | } else if (s->arglen != 4) { | |
186 | BADF("Unexpected response to cmd %d\n", s->cmd); | |
187 | /* Illegal command is about as near as we can get. */ | |
188 | s->arglen = 1; | |
189 | s->response[0] = 4; | |
190 | } else { | |
191 | /* All other commands return status. */ | |
192 | uint32_t cardstatus; | |
193 | uint16_t status; | |
194 | /* CMD13 returns a 2-byte statuse work. Other commands | |
195 | only return the first byte. */ | |
196 | s->arglen = (s->cmd == 13) ? 2 : 1; | |
b3141c06 | 197 | cardstatus = ldl_be_p(longresp); |
775616c3 PB |
198 | status = 0; |
199 | if (((cardstatus >> 9) & 0xf) < 4) | |
200 | status |= SSI_SDR_IDLE; | |
201 | if (cardstatus & ERASE_RESET) | |
202 | status |= SSI_SDR_ERASE_RESET; | |
203 | if (cardstatus & ILLEGAL_COMMAND) | |
204 | status |= SSI_SDR_ILLEGAL_COMMAND; | |
205 | if (cardstatus & COM_CRC_ERROR) | |
206 | status |= SSI_SDR_COM_CRC_ERROR; | |
207 | if (cardstatus & ERASE_SEQ_ERROR) | |
208 | status |= SSI_SDR_ERASE_SEQ_ERROR; | |
209 | if (cardstatus & ADDRESS_ERROR) | |
210 | status |= SSI_SDR_ADDRESS_ERROR; | |
211 | if (cardstatus & CARD_IS_LOCKED) | |
212 | status |= SSI_SDR_LOCKED; | |
213 | if (cardstatus & (LOCK_UNLOCK_FAILED | WP_ERASE_SKIP)) | |
214 | status |= SSI_SDR_WP_ERASE; | |
215 | if (cardstatus & SD_ERROR) | |
216 | status |= SSI_SDR_ERROR; | |
217 | if (cardstatus & CC_ERROR) | |
218 | status |= SSI_SDR_CC_ERROR; | |
219 | if (cardstatus & CARD_ECC_FAILED) | |
220 | status |= SSI_SDR_ECC_FAILED; | |
221 | if (cardstatus & WP_VIOLATION) | |
222 | status |= SSI_SDR_WP_VIOLATION; | |
223 | if (cardstatus & ERASE_PARAM) | |
224 | status |= SSI_SDR_ERASE_PARAM; | |
225 | if (cardstatus & (OUT_OF_RANGE | CID_CSD_OVERWRITE)) | |
226 | status |= SSI_SDR_OUT_OF_RANGE; | |
227 | /* ??? Don't know what Parameter Error really means, so | |
228 | assume it's set if the second byte is nonzero. */ | |
229 | if (status & 0xff) | |
230 | status |= SSI_SDR_PARAMETER_ERROR; | |
231 | s->response[0] = status >> 8; | |
232 | s->response[1] = status; | |
233 | DPRINTF("Card status 0x%02x\n", status); | |
234 | } | |
281c5c95 | 235 | s->mode = SSI_SD_PREP_RESP; |
775616c3 PB |
236 | s->response_pos = 0; |
237 | } else { | |
238 | s->cmdarg[s->arglen++] = val; | |
239 | } | |
bc1edaf2 | 240 | return SSI_DUMMY; |
281c5c95 BM |
241 | case SSI_SD_PREP_RESP: |
242 | DPRINTF("Prepare card response (Ncr)\n"); | |
243 | s->mode = SSI_SD_RESPONSE; | |
bc1edaf2 | 244 | return SSI_DUMMY; |
775616c3 PB |
245 | case SSI_SD_RESPONSE: |
246 | if (s->stopping) { | |
247 | s->stopping = 0; | |
bc1edaf2 | 248 | return SSI_DUMMY; |
775616c3 PB |
249 | } |
250 | if (s->response_pos < s->arglen) { | |
251 | DPRINTF("Response 0x%02x\n", s->response[s->response_pos]); | |
252 | return s->response[s->response_pos++]; | |
253 | } | |
c3abd913 | 254 | if (sdbus_data_ready(&s->sdbus)) { |
775616c3 PB |
255 | DPRINTF("Data read\n"); |
256 | s->mode = SSI_SD_DATA_START; | |
257 | } else { | |
258 | DPRINTF("End of command\n"); | |
259 | s->mode = SSI_SD_CMD; | |
260 | } | |
bc1edaf2 | 261 | return SSI_DUMMY; |
3a67cbe6 BM |
262 | case SSI_SD_PREP_DATA: |
263 | DPRINTF("Prepare data block (Nac)\n"); | |
264 | s->mode = SSI_SD_DATA_START; | |
bc1edaf2 | 265 | return SSI_DUMMY; |
775616c3 PB |
266 | case SSI_SD_DATA_START: |
267 | DPRINTF("Start read block\n"); | |
268 | s->mode = SSI_SD_DATA_READ; | |
2d174cc3 | 269 | s->response_pos = 0; |
bc1edaf2 | 270 | return SSI_TOKEN_SINGLE; |
775616c3 | 271 | case SSI_SD_DATA_READ: |
8467f622 | 272 | val = sdbus_read_byte(&s->sdbus); |
1365d863 | 273 | s->read_bytes++; |
2d174cc3 | 274 | s->crc16 = crc_ccitt_false(s->crc16, (uint8_t *)&val, 1); |
1365d863 | 275 | if (!sdbus_data_ready(&s->sdbus) || s->read_bytes == 512) { |
775616c3 | 276 | DPRINTF("Data read end\n"); |
2d174cc3 BM |
277 | s->mode = SSI_SD_DATA_CRC16; |
278 | } | |
279 | return val; | |
280 | case SSI_SD_DATA_CRC16: | |
281 | val = (s->crc16 & 0xff00) >> 8; | |
282 | s->crc16 <<= 8; | |
283 | s->response_pos++; | |
284 | if (s->response_pos == 2) { | |
285 | DPRINTF("CRC16 read end\n"); | |
1365d863 BM |
286 | if (s->read_bytes == 512 && s->cmd != 17) { |
287 | s->mode = SSI_SD_PREP_DATA; | |
288 | } else { | |
289 | s->mode = SSI_SD_CMD; | |
290 | } | |
291 | s->read_bytes = 0; | |
2d174cc3 | 292 | s->response_pos = 0; |
775616c3 PB |
293 | } |
294 | return val; | |
5020e3cb BM |
295 | case SSI_SD_DATA_WRITE: |
296 | sdbus_write_byte(&s->sdbus, val); | |
297 | s->write_bytes++; | |
298 | if (!sdbus_receive_ready(&s->sdbus) || s->write_bytes == 512) { | |
299 | DPRINTF("Data write end\n"); | |
300 | s->mode = SSI_SD_SKIP_CRC16; | |
301 | s->response_pos = 0; | |
302 | } | |
303 | return val; | |
304 | case SSI_SD_SKIP_CRC16: | |
305 | /* we don't verify the crc16 */ | |
306 | s->response_pos++; | |
307 | if (s->response_pos == 2) { | |
308 | DPRINTF("CRC16 receive end\n"); | |
309 | s->mode = SSI_SD_RESPONSE; | |
310 | s->write_bytes = 0; | |
311 | s->arglen = 1; | |
312 | s->response[0] = DATA_RESPONSE_ACCEPTED; | |
313 | s->response_pos = 0; | |
314 | } | |
315 | return SSI_DUMMY; | |
775616c3 PB |
316 | } |
317 | /* Should never happen. */ | |
bc1edaf2 | 318 | return SSI_DUMMY; |
775616c3 PB |
319 | } |
320 | ||
2ccfd336 | 321 | static int ssi_sd_post_load(void *opaque, int version_id) |
23e39294 PB |
322 | { |
323 | ssi_sd_state *s = (ssi_sd_state *)opaque; | |
23e39294 | 324 | |
5020e3cb | 325 | if (s->mode > SSI_SD_SKIP_CRC16) { |
23e39294 | 326 | return -EINVAL; |
2ccfd336 | 327 | } |
a9c380db MT |
328 | if (s->mode == SSI_SD_CMDARG && |
329 | (s->arglen < 0 || s->arglen >= ARRAY_SIZE(s->cmdarg))) { | |
330 | return -EINVAL; | |
331 | } | |
a9c380db MT |
332 | if (s->mode == SSI_SD_RESPONSE && |
333 | (s->response_pos < 0 || s->response_pos >= ARRAY_SIZE(s->response) || | |
334 | (!s->stopping && s->arglen > ARRAY_SIZE(s->response)))) { | |
335 | return -EINVAL; | |
336 | } | |
23e39294 PB |
337 | |
338 | return 0; | |
339 | } | |
340 | ||
2ccfd336 DDAG |
341 | static const VMStateDescription vmstate_ssi_sd = { |
342 | .name = "ssi_sd", | |
5020e3cb BM |
343 | .version_id = 7, |
344 | .minimum_version_id = 7, | |
2ccfd336 DDAG |
345 | .post_load = ssi_sd_post_load, |
346 | .fields = (VMStateField []) { | |
347 | VMSTATE_UINT32(mode, ssi_sd_state), | |
348 | VMSTATE_INT32(cmd, ssi_sd_state), | |
349 | VMSTATE_UINT8_ARRAY(cmdarg, ssi_sd_state, 4), | |
350 | VMSTATE_UINT8_ARRAY(response, ssi_sd_state, 5), | |
2d174cc3 | 351 | VMSTATE_UINT16(crc16, ssi_sd_state), |
1365d863 | 352 | VMSTATE_INT32(read_bytes, ssi_sd_state), |
5020e3cb | 353 | VMSTATE_INT32(write_bytes, ssi_sd_state), |
2ccfd336 DDAG |
354 | VMSTATE_INT32(arglen, ssi_sd_state), |
355 | VMSTATE_INT32(response_pos, ssi_sd_state), | |
356 | VMSTATE_INT32(stopping, ssi_sd_state), | |
ec7e429b | 357 | VMSTATE_SSI_PERIPHERAL(ssidev, ssi_sd_state), |
2ccfd336 DDAG |
358 | VMSTATE_END_OF_LIST() |
359 | } | |
360 | }; | |
361 | ||
ec7e429b | 362 | static void ssi_sd_realize(SSIPeripheral *d, Error **errp) |
775616c3 | 363 | { |
de1b3800 | 364 | ERRP_GUARD(); |
213f63df | 365 | ssi_sd_state *s = SSI_SD(d); |
c3abd913 | 366 | DeviceState *carddev; |
13839974 | 367 | DriveInfo *dinfo; |
775616c3 | 368 | |
c3abd913 PMD |
369 | qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, |
370 | DEVICE(d), "sd-bus"); | |
371 | ||
372 | /* Create and plug in the sd card */ | |
af9e40aa | 373 | /* FIXME use a qdev drive property instead of drive_get_next() */ |
13839974 | 374 | dinfo = drive_get_next(IF_SD); |
3e80f690 | 375 | carddev = qdev_new(TYPE_SD_CARD); |
c3abd913 | 376 | if (dinfo) { |
0c0e618d | 377 | if (!qdev_prop_set_drive_err(carddev, "drive", |
de1b3800 | 378 | blk_by_legacy_dinfo(dinfo), errp)) { |
709dfb64 VSO |
379 | goto fail; |
380 | } | |
c3abd913 | 381 | } |
709dfb64 | 382 | |
de1b3800 | 383 | if (!object_property_set_bool(OBJECT(carddev), "spi", true, errp)) { |
709dfb64 VSO |
384 | goto fail; |
385 | } | |
386 | ||
de1b3800 | 387 | if (!qdev_realize_and_unref(carddev, BUS(&s->sdbus), errp)) { |
709dfb64 | 388 | goto fail; |
4f8a066b | 389 | } |
709dfb64 VSO |
390 | |
391 | return; | |
392 | ||
393 | fail: | |
de1b3800 | 394 | error_prepend(errp, "failed to init SD card: "); |
775616c3 | 395 | } |
5493e33f | 396 | |
8046d44f PM |
397 | static void ssi_sd_reset(DeviceState *dev) |
398 | { | |
399 | ssi_sd_state *s = SSI_SD(dev); | |
400 | ||
401 | s->mode = SSI_SD_CMD; | |
402 | s->cmd = 0; | |
403 | memset(s->cmdarg, 0, sizeof(s->cmdarg)); | |
404 | memset(s->response, 0, sizeof(s->response)); | |
2d174cc3 | 405 | s->crc16 = 0; |
1365d863 | 406 | s->read_bytes = 0; |
5020e3cb | 407 | s->write_bytes = 0; |
8046d44f PM |
408 | s->arglen = 0; |
409 | s->response_pos = 0; | |
410 | s->stopping = 0; | |
8046d44f PM |
411 | } |
412 | ||
cd6c4cf2 AL |
413 | static void ssi_sd_class_init(ObjectClass *klass, void *data) |
414 | { | |
2ccfd336 | 415 | DeviceClass *dc = DEVICE_CLASS(klass); |
ec7e429b | 416 | SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); |
cd6c4cf2 | 417 | |
7673bb4c | 418 | k->realize = ssi_sd_realize; |
cd6c4cf2 | 419 | k->transfer = ssi_sd_transfer; |
8120e714 | 420 | k->cs_polarity = SSI_CS_LOW; |
2ccfd336 | 421 | dc->vmsd = &vmstate_ssi_sd; |
8046d44f | 422 | dc->reset = ssi_sd_reset; |
61e9e3cb MA |
423 | /* Reason: init() method uses drive_get_next() */ |
424 | dc->user_creatable = false; | |
cd6c4cf2 AL |
425 | } |
426 | ||
8c43a6f0 | 427 | static const TypeInfo ssi_sd_info = { |
8046d44f | 428 | .name = TYPE_SSI_SD, |
ec7e429b | 429 | .parent = TYPE_SSI_PERIPHERAL, |
39bffca2 AL |
430 | .instance_size = sizeof(ssi_sd_state), |
431 | .class_init = ssi_sd_class_init, | |
5493e33f PB |
432 | }; |
433 | ||
83f7d43a | 434 | static void ssi_sd_register_types(void) |
5493e33f | 435 | { |
39bffca2 | 436 | type_register_static(&ssi_sd_info); |
5493e33f PB |
437 | } |
438 | ||
83f7d43a | 439 | type_init(ssi_sd_register_types) |