1775616c3Spbrook /* 2775616c3Spbrook * SSI to SD card adapter. 3775616c3Spbrook * 45493e33fSPaul Brook * Copyright (c) 2007-2009 CodeSourcery. 5775616c3Spbrook * Written by Paul Brook 6775616c3Spbrook * 7d56f3efaSBin Meng * Copyright (c) 2021 Wind River Systems, Inc. 8d56f3efaSBin Meng * Improved by Bin Meng <bin.meng@windriver.com> 9d56f3efaSBin Meng * 10d56f3efaSBin Meng * Validated with U-Boot v2021.01 and Linux v5.10 mmc_spi driver 11d56f3efaSBin Meng * 128e31bf38SMatthew Fernandez * This code is licensed under the GNU GPL v2. 136b620ca3SPaolo Bonzini * 146b620ca3SPaolo Bonzini * Contributions after 2012-01-13 are licensed under the terms of the 156b620ca3SPaolo Bonzini * GNU GPL, version 2 or (at your option) any later version. 16775616c3Spbrook */ 17775616c3Spbrook 180430891cSPeter Maydell #include "qemu/osdep.h" 199c17d615SPaolo Bonzini #include "sysemu/blockdev.h" 208fd06719SAlistair Francis #include "hw/ssi/ssi.h" 21d6454270SMarkus Armbruster #include "migration/vmstate.h" 22a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 23e3382ef0SSai Pavan Boddu #include "hw/sd/sd.h" 247673bb4cSCédric Le Goater #include "qapi/error.h" 252d174cc3SBin Meng #include "qemu/crc-ccitt.h" 260b8fa32fSMarkus Armbruster #include "qemu/module.h" 27db1015e9SEduardo Habkost #include "qom/object.h" 28775616c3Spbrook 29775616c3Spbrook //#define DEBUG_SSI_SD 1 30775616c3Spbrook 31775616c3Spbrook #ifdef DEBUG_SSI_SD 32001faf32SBlue Swirl #define DPRINTF(fmt, ...) \ 33001faf32SBlue Swirl do { printf("ssi_sd: " fmt , ## __VA_ARGS__); } while (0) 34001faf32SBlue Swirl #define BADF(fmt, ...) \ 35001faf32SBlue Swirl do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) 36775616c3Spbrook #else 37001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while(0) 38001faf32SBlue Swirl #define BADF(fmt, ...) \ 39001faf32SBlue Swirl do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__);} while (0) 40775616c3Spbrook #endif 41775616c3Spbrook 42775616c3Spbrook typedef enum { 432ccfd336SDr. David Alan Gilbert SSI_SD_CMD = 0, 44775616c3Spbrook SSI_SD_CMDARG, 45281c5c95SBin Meng SSI_SD_PREP_RESP, 46775616c3Spbrook SSI_SD_RESPONSE, 473a67cbe6SBin Meng SSI_SD_PREP_DATA, 48775616c3Spbrook SSI_SD_DATA_START, 49775616c3Spbrook SSI_SD_DATA_READ, 502d174cc3SBin Meng SSI_SD_DATA_CRC16, 515020e3cbSBin Meng SSI_SD_DATA_WRITE, 525020e3cbSBin Meng SSI_SD_SKIP_CRC16, 53775616c3Spbrook } ssi_sd_mode; 54775616c3Spbrook 55db1015e9SEduardo Habkost struct ssi_sd_state { 56ec7e429bSPhilippe Mathieu-Daudé SSIPeripheral ssidev; 572ccfd336SDr. David Alan Gilbert uint32_t mode; 58775616c3Spbrook int cmd; 59775616c3Spbrook uint8_t cmdarg[4]; 60775616c3Spbrook uint8_t response[5]; 612d174cc3SBin Meng uint16_t crc16; 621365d863SBin Meng int32_t read_bytes; 635020e3cbSBin Meng int32_t write_bytes; 642ccfd336SDr. David Alan Gilbert int32_t arglen; 652ccfd336SDr. David Alan Gilbert int32_t response_pos; 662ccfd336SDr. David Alan Gilbert int32_t stopping; 67c3abd913SPhilippe Mathieu-Daudé SDBus sdbus; 68db1015e9SEduardo Habkost }; 69775616c3Spbrook 708046d44fSPeter Maydell #define TYPE_SSI_SD "ssi-sd" 718063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(ssi_sd_state, SSI_SD) 728046d44fSPeter Maydell 73775616c3Spbrook /* State word bits. */ 74775616c3Spbrook #define SSI_SDR_LOCKED 0x0001 75775616c3Spbrook #define SSI_SDR_WP_ERASE 0x0002 76775616c3Spbrook #define SSI_SDR_ERROR 0x0004 77775616c3Spbrook #define SSI_SDR_CC_ERROR 0x0008 78775616c3Spbrook #define SSI_SDR_ECC_FAILED 0x0010 79775616c3Spbrook #define SSI_SDR_WP_VIOLATION 0x0020 80775616c3Spbrook #define SSI_SDR_ERASE_PARAM 0x0040 81775616c3Spbrook #define SSI_SDR_OUT_OF_RANGE 0x0080 82775616c3Spbrook #define SSI_SDR_IDLE 0x0100 83775616c3Spbrook #define SSI_SDR_ERASE_RESET 0x0200 84775616c3Spbrook #define SSI_SDR_ILLEGAL_COMMAND 0x0400 85775616c3Spbrook #define SSI_SDR_COM_CRC_ERROR 0x0800 86775616c3Spbrook #define SSI_SDR_ERASE_SEQ_ERROR 0x1000 87775616c3Spbrook #define SSI_SDR_ADDRESS_ERROR 0x2000 88775616c3Spbrook #define SSI_SDR_PARAMETER_ERROR 0x4000 89775616c3Spbrook 90d56f3efaSBin Meng /* multiple block write */ 91d56f3efaSBin Meng #define SSI_TOKEN_MULTI_WRITE 0xfc 92d56f3efaSBin Meng /* terminate multiple block write */ 93d56f3efaSBin Meng #define SSI_TOKEN_STOP_TRAN 0xfd 94bc1edaf2SBin Meng /* single block read/write, multiple block read */ 95bc1edaf2SBin Meng #define SSI_TOKEN_SINGLE 0xfe 96bc1edaf2SBin Meng 97bc1edaf2SBin Meng /* dummy value - don't care */ 98bc1edaf2SBin Meng #define SSI_DUMMY 0xff 99bc1edaf2SBin Meng 1005020e3cbSBin Meng /* data accepted */ 1015020e3cbSBin Meng #define DATA_RESPONSE_ACCEPTED 0x05 1025020e3cbSBin Meng 103ec7e429bSPhilippe Mathieu-Daudé static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) 104775616c3Spbrook { 105213f63dfSPeter Maydell ssi_sd_state *s = SSI_SD(dev); 106d56f3efaSBin Meng SDRequest request; 107d56f3efaSBin Meng uint8_t longresp[16]; 108775616c3Spbrook 1091365d863SBin Meng /* 1101365d863SBin Meng * Special case: allow CMD12 (STOP TRANSMISSION) while reading data. 1111365d863SBin Meng * 1121365d863SBin Meng * See "Physical Layer Specification Version 8.00" chapter 7.5.2.2, 1131365d863SBin Meng * to avoid conflict between CMD12 response and next data block, 1141365d863SBin Meng * timing of CMD12 should be controlled as follows: 1151365d863SBin Meng * 1161365d863SBin Meng * - CMD12 issued at the timing that end bit of CMD12 and end bit of 1171365d863SBin Meng * data block is overlapped 1181365d863SBin Meng * - CMD12 issued after one clock cycle after host receives a token 1191365d863SBin Meng * (either Start Block token or Data Error token) 1201365d863SBin Meng * 1211365d863SBin Meng * We need to catch CMD12 in all of the data read states. 1221365d863SBin Meng */ 1231365d863SBin Meng if (s->mode >= SSI_SD_PREP_DATA && s->mode <= SSI_SD_DATA_CRC16) { 1241365d863SBin Meng if (val == 0x4c) { 125775616c3Spbrook s->mode = SSI_SD_CMD; 1261365d863SBin Meng /* There must be at least one byte delay before the card responds */ 127775616c3Spbrook s->stopping = 1; 128775616c3Spbrook } 1291365d863SBin Meng } 130775616c3Spbrook 131775616c3Spbrook switch (s->mode) { 132775616c3Spbrook case SSI_SD_CMD: 1335020e3cbSBin Meng switch (val) { 1345020e3cbSBin Meng case SSI_DUMMY: 135775616c3Spbrook DPRINTF("NULL command\n"); 136bc1edaf2SBin Meng return SSI_DUMMY; 1375020e3cbSBin Meng break; 1385020e3cbSBin Meng case SSI_TOKEN_SINGLE: 139d56f3efaSBin Meng case SSI_TOKEN_MULTI_WRITE: 1405020e3cbSBin Meng DPRINTF("Start write block\n"); 1415020e3cbSBin Meng s->mode = SSI_SD_DATA_WRITE; 1425020e3cbSBin Meng return SSI_DUMMY; 143d56f3efaSBin Meng case SSI_TOKEN_STOP_TRAN: 144d56f3efaSBin Meng DPRINTF("Stop multiple write\n"); 145d56f3efaSBin Meng 146d56f3efaSBin Meng /* manually issue cmd12 to stop the transfer */ 147d56f3efaSBin Meng request.cmd = 12; 148d56f3efaSBin Meng request.arg = 0; 149d56f3efaSBin Meng s->arglen = sdbus_do_command(&s->sdbus, &request, longresp); 150d56f3efaSBin Meng if (s->arglen <= 0) { 151d56f3efaSBin Meng s->arglen = 1; 152d56f3efaSBin Meng /* a zero value indicates the card is busy */ 153d56f3efaSBin Meng s->response[0] = 0; 154d56f3efaSBin Meng DPRINTF("SD card busy\n"); 155d56f3efaSBin Meng } else { 156d56f3efaSBin Meng s->arglen = 1; 157d56f3efaSBin Meng /* a non-zero value indicates the card is ready */ 158d56f3efaSBin Meng s->response[0] = SSI_DUMMY; 159d56f3efaSBin Meng } 160d56f3efaSBin Meng 161d56f3efaSBin Meng return SSI_DUMMY; 162775616c3Spbrook } 1635020e3cbSBin Meng 164775616c3Spbrook s->cmd = val & 0x3f; 165775616c3Spbrook s->mode = SSI_SD_CMDARG; 166775616c3Spbrook s->arglen = 0; 167bc1edaf2SBin Meng return SSI_DUMMY; 168775616c3Spbrook case SSI_SD_CMDARG: 169775616c3Spbrook if (s->arglen == 4) { 170775616c3Spbrook /* FIXME: Check CRC. */ 171775616c3Spbrook request.cmd = s->cmd; 172b3141c06SPhilippe Mathieu-Daudé request.arg = ldl_be_p(s->cmdarg); 173775616c3Spbrook DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg); 174c3abd913SPhilippe Mathieu-Daudé s->arglen = sdbus_do_command(&s->sdbus, &request, longresp); 175775616c3Spbrook if (s->arglen <= 0) { 176775616c3Spbrook s->arglen = 1; 177775616c3Spbrook s->response[0] = 4; 178775616c3Spbrook DPRINTF("SD command failed\n"); 1796ae29af3SBin Meng } else if (s->cmd == 8 || s->cmd == 58) { 1806ae29af3SBin Meng /* CMD8/CMD58 returns R3/R7 response */ 1816ae29af3SBin Meng DPRINTF("Returned R3/R7\n"); 182775616c3Spbrook s->arglen = 5; 183775616c3Spbrook s->response[0] = 1; 184775616c3Spbrook memcpy(&s->response[1], longresp, 4); 185775616c3Spbrook } else if (s->arglen != 4) { 186775616c3Spbrook BADF("Unexpected response to cmd %d\n", s->cmd); 187775616c3Spbrook /* Illegal command is about as near as we can get. */ 188775616c3Spbrook s->arglen = 1; 189775616c3Spbrook s->response[0] = 4; 190775616c3Spbrook } else { 191775616c3Spbrook /* All other commands return status. */ 192775616c3Spbrook uint32_t cardstatus; 193775616c3Spbrook uint16_t status; 194775616c3Spbrook /* CMD13 returns a 2-byte statuse work. Other commands 195775616c3Spbrook only return the first byte. */ 196775616c3Spbrook s->arglen = (s->cmd == 13) ? 2 : 1; 19717674695SBin Meng 19817674695SBin Meng /* handle R1b */ 19917674695SBin Meng if (s->cmd == 28 || s->cmd == 29 || s->cmd == 38) { 20017674695SBin Meng s->stopping = 1; 20117674695SBin Meng } 20217674695SBin Meng 203b3141c06SPhilippe Mathieu-Daudé cardstatus = ldl_be_p(longresp); 204775616c3Spbrook status = 0; 205775616c3Spbrook if (((cardstatus >> 9) & 0xf) < 4) 206775616c3Spbrook status |= SSI_SDR_IDLE; 207775616c3Spbrook if (cardstatus & ERASE_RESET) 208775616c3Spbrook status |= SSI_SDR_ERASE_RESET; 209775616c3Spbrook if (cardstatus & ILLEGAL_COMMAND) 210775616c3Spbrook status |= SSI_SDR_ILLEGAL_COMMAND; 211775616c3Spbrook if (cardstatus & COM_CRC_ERROR) 212775616c3Spbrook status |= SSI_SDR_COM_CRC_ERROR; 213775616c3Spbrook if (cardstatus & ERASE_SEQ_ERROR) 214775616c3Spbrook status |= SSI_SDR_ERASE_SEQ_ERROR; 215775616c3Spbrook if (cardstatus & ADDRESS_ERROR) 216775616c3Spbrook status |= SSI_SDR_ADDRESS_ERROR; 217775616c3Spbrook if (cardstatus & CARD_IS_LOCKED) 218775616c3Spbrook status |= SSI_SDR_LOCKED; 219775616c3Spbrook if (cardstatus & (LOCK_UNLOCK_FAILED | WP_ERASE_SKIP)) 220775616c3Spbrook status |= SSI_SDR_WP_ERASE; 221775616c3Spbrook if (cardstatus & SD_ERROR) 222775616c3Spbrook status |= SSI_SDR_ERROR; 223775616c3Spbrook if (cardstatus & CC_ERROR) 224775616c3Spbrook status |= SSI_SDR_CC_ERROR; 225775616c3Spbrook if (cardstatus & CARD_ECC_FAILED) 226775616c3Spbrook status |= SSI_SDR_ECC_FAILED; 227775616c3Spbrook if (cardstatus & WP_VIOLATION) 228775616c3Spbrook status |= SSI_SDR_WP_VIOLATION; 229775616c3Spbrook if (cardstatus & ERASE_PARAM) 230775616c3Spbrook status |= SSI_SDR_ERASE_PARAM; 231775616c3Spbrook if (cardstatus & (OUT_OF_RANGE | CID_CSD_OVERWRITE)) 232775616c3Spbrook status |= SSI_SDR_OUT_OF_RANGE; 233775616c3Spbrook /* ??? Don't know what Parameter Error really means, so 234775616c3Spbrook assume it's set if the second byte is nonzero. */ 235775616c3Spbrook if (status & 0xff) 236775616c3Spbrook status |= SSI_SDR_PARAMETER_ERROR; 237775616c3Spbrook s->response[0] = status >> 8; 238775616c3Spbrook s->response[1] = status; 239775616c3Spbrook DPRINTF("Card status 0x%02x\n", status); 240775616c3Spbrook } 241281c5c95SBin Meng s->mode = SSI_SD_PREP_RESP; 242775616c3Spbrook s->response_pos = 0; 243775616c3Spbrook } else { 244775616c3Spbrook s->cmdarg[s->arglen++] = val; 245775616c3Spbrook } 246bc1edaf2SBin Meng return SSI_DUMMY; 247281c5c95SBin Meng case SSI_SD_PREP_RESP: 248281c5c95SBin Meng DPRINTF("Prepare card response (Ncr)\n"); 249281c5c95SBin Meng s->mode = SSI_SD_RESPONSE; 250bc1edaf2SBin Meng return SSI_DUMMY; 251775616c3Spbrook case SSI_SD_RESPONSE: 252775616c3Spbrook if (s->response_pos < s->arglen) { 253775616c3Spbrook DPRINTF("Response 0x%02x\n", s->response[s->response_pos]); 254775616c3Spbrook return s->response[s->response_pos++]; 255775616c3Spbrook } 2565b45a366SBin Meng if (s->stopping) { 2575b45a366SBin Meng s->stopping = 0; 2585b45a366SBin Meng s->mode = SSI_SD_CMD; 2595b45a366SBin Meng return SSI_DUMMY; 2605b45a366SBin Meng } 261c3abd913SPhilippe Mathieu-Daudé if (sdbus_data_ready(&s->sdbus)) { 262775616c3Spbrook DPRINTF("Data read\n"); 263775616c3Spbrook s->mode = SSI_SD_DATA_START; 264775616c3Spbrook } else { 265775616c3Spbrook DPRINTF("End of command\n"); 266775616c3Spbrook s->mode = SSI_SD_CMD; 267775616c3Spbrook } 268bc1edaf2SBin Meng return SSI_DUMMY; 2693a67cbe6SBin Meng case SSI_SD_PREP_DATA: 2703a67cbe6SBin Meng DPRINTF("Prepare data block (Nac)\n"); 2713a67cbe6SBin Meng s->mode = SSI_SD_DATA_START; 272bc1edaf2SBin Meng return SSI_DUMMY; 273775616c3Spbrook case SSI_SD_DATA_START: 274775616c3Spbrook DPRINTF("Start read block\n"); 275775616c3Spbrook s->mode = SSI_SD_DATA_READ; 2762d174cc3SBin Meng s->response_pos = 0; 277bc1edaf2SBin Meng return SSI_TOKEN_SINGLE; 278775616c3Spbrook case SSI_SD_DATA_READ: 2798467f622SPhilippe Mathieu-Daudé val = sdbus_read_byte(&s->sdbus); 2801365d863SBin Meng s->read_bytes++; 2812d174cc3SBin Meng s->crc16 = crc_ccitt_false(s->crc16, (uint8_t *)&val, 1); 2821365d863SBin Meng if (!sdbus_data_ready(&s->sdbus) || s->read_bytes == 512) { 283775616c3Spbrook DPRINTF("Data read end\n"); 2842d174cc3SBin Meng s->mode = SSI_SD_DATA_CRC16; 2852d174cc3SBin Meng } 2862d174cc3SBin Meng return val; 2872d174cc3SBin Meng case SSI_SD_DATA_CRC16: 2882d174cc3SBin Meng val = (s->crc16 & 0xff00) >> 8; 2892d174cc3SBin Meng s->crc16 <<= 8; 2902d174cc3SBin Meng s->response_pos++; 2912d174cc3SBin Meng if (s->response_pos == 2) { 2922d174cc3SBin Meng DPRINTF("CRC16 read end\n"); 2931365d863SBin Meng if (s->read_bytes == 512 && s->cmd != 17) { 2941365d863SBin Meng s->mode = SSI_SD_PREP_DATA; 2951365d863SBin Meng } else { 296775616c3Spbrook s->mode = SSI_SD_CMD; 2971365d863SBin Meng } 2981365d863SBin Meng s->read_bytes = 0; 2992d174cc3SBin Meng s->response_pos = 0; 300775616c3Spbrook } 301775616c3Spbrook return val; 3025020e3cbSBin Meng case SSI_SD_DATA_WRITE: 3035020e3cbSBin Meng sdbus_write_byte(&s->sdbus, val); 3045020e3cbSBin Meng s->write_bytes++; 3055020e3cbSBin Meng if (!sdbus_receive_ready(&s->sdbus) || s->write_bytes == 512) { 3065020e3cbSBin Meng DPRINTF("Data write end\n"); 3075020e3cbSBin Meng s->mode = SSI_SD_SKIP_CRC16; 3085020e3cbSBin Meng s->response_pos = 0; 3095020e3cbSBin Meng } 3105020e3cbSBin Meng return val; 3115020e3cbSBin Meng case SSI_SD_SKIP_CRC16: 3125020e3cbSBin Meng /* we don't verify the crc16 */ 3135020e3cbSBin Meng s->response_pos++; 3145020e3cbSBin Meng if (s->response_pos == 2) { 3155020e3cbSBin Meng DPRINTF("CRC16 receive end\n"); 3165020e3cbSBin Meng s->mode = SSI_SD_RESPONSE; 3175020e3cbSBin Meng s->write_bytes = 0; 3185020e3cbSBin Meng s->arglen = 1; 3195020e3cbSBin Meng s->response[0] = DATA_RESPONSE_ACCEPTED; 3205020e3cbSBin Meng s->response_pos = 0; 3215020e3cbSBin Meng } 3225020e3cbSBin Meng return SSI_DUMMY; 323775616c3Spbrook } 324775616c3Spbrook /* Should never happen. */ 325bc1edaf2SBin Meng return SSI_DUMMY; 326775616c3Spbrook } 327775616c3Spbrook 3282ccfd336SDr. David Alan Gilbert static int ssi_sd_post_load(void *opaque, int version_id) 32923e39294Spbrook { 33023e39294Spbrook ssi_sd_state *s = (ssi_sd_state *)opaque; 33123e39294Spbrook 3325020e3cbSBin Meng if (s->mode > SSI_SD_SKIP_CRC16) { 33323e39294Spbrook return -EINVAL; 3342ccfd336SDr. David Alan Gilbert } 335a9c380dbSMichael S. Tsirkin if (s->mode == SSI_SD_CMDARG && 336a9c380dbSMichael S. Tsirkin (s->arglen < 0 || s->arglen >= ARRAY_SIZE(s->cmdarg))) { 337a9c380dbSMichael S. Tsirkin return -EINVAL; 338a9c380dbSMichael S. Tsirkin } 339a9c380dbSMichael S. Tsirkin if (s->mode == SSI_SD_RESPONSE && 340a9c380dbSMichael S. Tsirkin (s->response_pos < 0 || s->response_pos >= ARRAY_SIZE(s->response) || 341a9c380dbSMichael S. Tsirkin (!s->stopping && s->arglen > ARRAY_SIZE(s->response)))) { 342a9c380dbSMichael S. Tsirkin return -EINVAL; 343a9c380dbSMichael S. Tsirkin } 34423e39294Spbrook 34523e39294Spbrook return 0; 34623e39294Spbrook } 34723e39294Spbrook 3482ccfd336SDr. David Alan Gilbert static const VMStateDescription vmstate_ssi_sd = { 3492ccfd336SDr. David Alan Gilbert .name = "ssi_sd", 3505020e3cbSBin Meng .version_id = 7, 3515020e3cbSBin Meng .minimum_version_id = 7, 3522ccfd336SDr. David Alan Gilbert .post_load = ssi_sd_post_load, 353307119baSRichard Henderson .fields = (const VMStateField []) { 3542ccfd336SDr. David Alan Gilbert VMSTATE_UINT32(mode, ssi_sd_state), 3552ccfd336SDr. David Alan Gilbert VMSTATE_INT32(cmd, ssi_sd_state), 3562ccfd336SDr. David Alan Gilbert VMSTATE_UINT8_ARRAY(cmdarg, ssi_sd_state, 4), 3572ccfd336SDr. David Alan Gilbert VMSTATE_UINT8_ARRAY(response, ssi_sd_state, 5), 3582d174cc3SBin Meng VMSTATE_UINT16(crc16, ssi_sd_state), 3591365d863SBin Meng VMSTATE_INT32(read_bytes, ssi_sd_state), 3605020e3cbSBin Meng VMSTATE_INT32(write_bytes, ssi_sd_state), 3612ccfd336SDr. David Alan Gilbert VMSTATE_INT32(arglen, ssi_sd_state), 3622ccfd336SDr. David Alan Gilbert VMSTATE_INT32(response_pos, ssi_sd_state), 3632ccfd336SDr. David Alan Gilbert VMSTATE_INT32(stopping, ssi_sd_state), 364ec7e429bSPhilippe Mathieu-Daudé VMSTATE_SSI_PERIPHERAL(ssidev, ssi_sd_state), 3652ccfd336SDr. David Alan Gilbert VMSTATE_END_OF_LIST() 3662ccfd336SDr. David Alan Gilbert } 3672ccfd336SDr. David Alan Gilbert }; 3682ccfd336SDr. David Alan Gilbert 369ec7e429bSPhilippe Mathieu-Daudé static void ssi_sd_realize(SSIPeripheral *d, Error **errp) 370775616c3Spbrook { 371213f63dfSPeter Maydell ssi_sd_state *s = SSI_SD(d); 372775616c3Spbrook 373d637e1dcSPeter Maydell qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(d), "sd-bus"); 374775616c3Spbrook } 3755493e33fSPaul Brook 3768046d44fSPeter Maydell static void ssi_sd_reset(DeviceState *dev) 3778046d44fSPeter Maydell { 3788046d44fSPeter Maydell ssi_sd_state *s = SSI_SD(dev); 3798046d44fSPeter Maydell 3808046d44fSPeter Maydell s->mode = SSI_SD_CMD; 3818046d44fSPeter Maydell s->cmd = 0; 3828046d44fSPeter Maydell memset(s->cmdarg, 0, sizeof(s->cmdarg)); 3838046d44fSPeter Maydell memset(s->response, 0, sizeof(s->response)); 3842d174cc3SBin Meng s->crc16 = 0; 3851365d863SBin Meng s->read_bytes = 0; 3865020e3cbSBin Meng s->write_bytes = 0; 3878046d44fSPeter Maydell s->arglen = 0; 3888046d44fSPeter Maydell s->response_pos = 0; 3898046d44fSPeter Maydell s->stopping = 0; 3908046d44fSPeter Maydell } 3918046d44fSPeter Maydell 392cd6c4cf2SAnthony Liguori static void ssi_sd_class_init(ObjectClass *klass, void *data) 393cd6c4cf2SAnthony Liguori { 3942ccfd336SDr. David Alan Gilbert DeviceClass *dc = DEVICE_CLASS(klass); 395ec7e429bSPhilippe Mathieu-Daudé SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); 396cd6c4cf2SAnthony Liguori 3977673bb4cSCédric Le Goater k->realize = ssi_sd_realize; 398cd6c4cf2SAnthony Liguori k->transfer = ssi_sd_transfer; 3998120e714SPeter A. G. Crosthwaite k->cs_polarity = SSI_CS_LOW; 4002ccfd336SDr. David Alan Gilbert dc->vmsd = &vmstate_ssi_sd; 401*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, ssi_sd_reset); 40236aa285fSMarkus Armbruster /* Reason: GPIO chip-select line should be wired up */ 40361e9e3cbSMarkus Armbruster dc->user_creatable = false; 404cd6c4cf2SAnthony Liguori } 405cd6c4cf2SAnthony Liguori 40688d2198cSPhilippe Mathieu-Daudé static const TypeInfo ssi_sd_types[] = { 40788d2198cSPhilippe Mathieu-Daudé { 4088046d44fSPeter Maydell .name = TYPE_SSI_SD, 409ec7e429bSPhilippe Mathieu-Daudé .parent = TYPE_SSI_PERIPHERAL, 41039bffca2SAnthony Liguori .instance_size = sizeof(ssi_sd_state), 411cd6c4cf2SAnthony Liguori .class_init = ssi_sd_class_init, 41288d2198cSPhilippe Mathieu-Daudé }, 4135493e33fSPaul Brook }; 4145493e33fSPaul Brook 41588d2198cSPhilippe Mathieu-Daudé DEFINE_TYPES(ssi_sd_types) 416