Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/philmd-gitlab/tags/sdmmc-202101…
Browse files Browse the repository at this point in the history
…24' into staging

SD/MMC patches

- Various improvements for SD cards in SPI mode (Bin Meng)

# gpg: Signature made Sun 24 Jan 2021 19:16:55 GMT
# gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
# Primary key fingerprint: FAAB E75E 1291 7221 DCFD  6BB2 E3E3 2C2C DEAD C0DE

* remotes/philmd-gitlab/tags/sdmmc-20210124:
  hw/sd: sd.h: Cosmetic change of using spaces
  hw/sd: ssi-sd: Use macros for the dummy value and tokens in the transfer
  hw/sd: ssi-sd: Fix the wrong command index for STOP_TRANSMISSION
  hw/sd: ssi-sd: Add a state representing Nac
  hw/sd: ssi-sd: Suffix a data block with CRC16
  util: Add CRC16 (CCITT) calculation routines
  hw/sd: sd: Drop sd_crc16()
  hw/sd: sd: Support CMD59 for SPI mode
  hw/sd: ssi-sd: Fix incorrect card response sequence

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
  • Loading branch information
pm215 committed Jan 25, 2021
2 parents e672f1d + 3f20ccd commit 55d9895
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 59 deletions.
26 changes: 1 addition & 25 deletions hw/sd/sd.c
Expand Up @@ -271,23 +271,6 @@ static uint8_t sd_crc7(const void *message, size_t width)
return shift_reg;
}

static uint16_t sd_crc16(const void *message, size_t width)
{
int i, bit;
uint16_t shift_reg = 0x0000;
const uint16_t *msg = (const uint16_t *)message;
width <<= 1;

for (i = 0; i < width; i ++, msg ++)
for (bit = 15; bit >= 0; bit --) {
shift_reg <<= 1;
if ((shift_reg >> 15) ^ ((*msg >> bit) & 1))
shift_reg ^= 0x1011;
}

return shift_reg;
}

#define OCR_POWER_DELAY_NS 500000 /* 0.5ms */

FIELD(OCR, VDD_VOLTAGE_WINDOW, 0, 24)
Expand Down Expand Up @@ -843,7 +826,6 @@ static void sd_function_switch(SDState *sd, uint32_t arg)
sd->data[16 - (i >> 1)] |= new_func << ((i % 2) * 4);
}
memset(&sd->data[17], 0, 47);
stw_be_p(sd->data + 64, sd_crc16(sd->data, 64));
}

static inline bool sd_wp_addr(SDState *sd, uint64_t addr)
Expand Down Expand Up @@ -1517,18 +1499,12 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
if (!sd->spi) {
goto bad_cmd;
}
goto unimplemented_spi_cmd;
return sd_r1;

default:
bad_cmd:
qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd);
return sd_illegal;

unimplemented_spi_cmd:
/* Commands that are recognised but not yet implemented in SPI mode. */
qemu_log_mask(LOG_UNIMP, "SD: CMD%i not implemented in SPI mode\n",
req.cmd);
return sd_illegal;
}

qemu_log_mask(LOG_GUEST_ERROR, "SD: CMD%i in a wrong state\n", req.cmd);
Expand Down
59 changes: 46 additions & 13 deletions hw/sd/ssi-sd.c
Expand Up @@ -17,6 +17,7 @@
#include "hw/qdev-properties.h"
#include "hw/sd/sd.h"
#include "qapi/error.h"
#include "qemu/crc-ccitt.h"
#include "qemu/module.h"
#include "qom/object.h"

Expand All @@ -36,9 +37,12 @@ do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__);} while (0)
typedef enum {
SSI_SD_CMD = 0,
SSI_SD_CMDARG,
SSI_SD_PREP_RESP,
SSI_SD_RESPONSE,
SSI_SD_PREP_DATA,
SSI_SD_DATA_START,
SSI_SD_DATA_READ,
SSI_SD_DATA_CRC16,
} ssi_sd_mode;

struct ssi_sd_state {
Expand All @@ -47,6 +51,7 @@ struct ssi_sd_state {
int cmd;
uint8_t cmdarg[4];
uint8_t response[5];
uint16_t crc16;
int32_t arglen;
int32_t response_pos;
int32_t stopping;
Expand All @@ -73,27 +78,33 @@ OBJECT_DECLARE_SIMPLE_TYPE(ssi_sd_state, SSI_SD)
#define SSI_SDR_ADDRESS_ERROR 0x2000
#define SSI_SDR_PARAMETER_ERROR 0x4000

/* single block read/write, multiple block read */
#define SSI_TOKEN_SINGLE 0xfe

/* dummy value - don't care */
#define SSI_DUMMY 0xff

static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
{
ssi_sd_state *s = SSI_SD(dev);

/* Special case: allow CMD12 (STOP TRANSMISSION) while reading data. */
if (s->mode == SSI_SD_DATA_READ && val == 0x4d) {
if (s->mode == SSI_SD_DATA_READ && val == 0x4c) {
s->mode = SSI_SD_CMD;
/* There must be at least one byte delay before the card responds. */
s->stopping = 1;
}

switch (s->mode) {
case SSI_SD_CMD:
if (val == 0xff) {
if (val == SSI_DUMMY) {
DPRINTF("NULL command\n");
return 0xff;
return SSI_DUMMY;
}
s->cmd = val & 0x3f;
s->mode = SSI_SD_CMDARG;
s->arglen = 0;
return 0xff;
return SSI_DUMMY;
case SSI_SD_CMDARG:
if (s->arglen == 4) {
SDRequest request;
Expand Down Expand Up @@ -163,16 +174,20 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
s->response[1] = status;
DPRINTF("Card status 0x%02x\n", status);
}
s->mode = SSI_SD_RESPONSE;
s->mode = SSI_SD_PREP_RESP;
s->response_pos = 0;
} else {
s->cmdarg[s->arglen++] = val;
}
return 0xff;
return SSI_DUMMY;
case SSI_SD_PREP_RESP:
DPRINTF("Prepare card response (Ncr)\n");
s->mode = SSI_SD_RESPONSE;
return SSI_DUMMY;
case SSI_SD_RESPONSE:
if (s->stopping) {
s->stopping = 0;
return 0xff;
return SSI_DUMMY;
}
if (s->response_pos < s->arglen) {
DPRINTF("Response 0x%02x\n", s->response[s->response_pos]);
Expand All @@ -185,28 +200,44 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
DPRINTF("End of command\n");
s->mode = SSI_SD_CMD;
}
return 0xff;
return SSI_DUMMY;
case SSI_SD_PREP_DATA:
DPRINTF("Prepare data block (Nac)\n");
s->mode = SSI_SD_DATA_START;
return SSI_DUMMY;
case SSI_SD_DATA_START:
DPRINTF("Start read block\n");
s->mode = SSI_SD_DATA_READ;
return 0xfe;
s->response_pos = 0;
return SSI_TOKEN_SINGLE;
case SSI_SD_DATA_READ:
val = sdbus_read_byte(&s->sdbus);
s->crc16 = crc_ccitt_false(s->crc16, (uint8_t *)&val, 1);
if (!sdbus_data_ready(&s->sdbus)) {
DPRINTF("Data read end\n");
s->mode = SSI_SD_DATA_CRC16;
}
return val;
case SSI_SD_DATA_CRC16:
val = (s->crc16 & 0xff00) >> 8;
s->crc16 <<= 8;
s->response_pos++;
if (s->response_pos == 2) {
DPRINTF("CRC16 read end\n");
s->mode = SSI_SD_CMD;
s->response_pos = 0;
}
return val;
}
/* Should never happen. */
return 0xff;
return SSI_DUMMY;
}

static int ssi_sd_post_load(void *opaque, int version_id)
{
ssi_sd_state *s = (ssi_sd_state *)opaque;

if (s->mode > SSI_SD_DATA_READ) {
if (s->mode > SSI_SD_DATA_CRC16) {
return -EINVAL;
}
if (s->mode == SSI_SD_CMDARG &&
Expand All @@ -224,14 +255,15 @@ static int ssi_sd_post_load(void *opaque, int version_id)

static const VMStateDescription vmstate_ssi_sd = {
.name = "ssi_sd",
.version_id = 2,
.minimum_version_id = 2,
.version_id = 5,
.minimum_version_id = 5,
.post_load = ssi_sd_post_load,
.fields = (VMStateField []) {
VMSTATE_UINT32(mode, ssi_sd_state),
VMSTATE_INT32(cmd, ssi_sd_state),
VMSTATE_UINT8_ARRAY(cmdarg, ssi_sd_state, 4),
VMSTATE_UINT8_ARRAY(response, ssi_sd_state, 5),
VMSTATE_UINT16(crc16, ssi_sd_state),
VMSTATE_INT32(arglen, ssi_sd_state),
VMSTATE_INT32(response_pos, ssi_sd_state),
VMSTATE_INT32(stopping, ssi_sd_state),
Expand Down Expand Up @@ -283,6 +315,7 @@ static void ssi_sd_reset(DeviceState *dev)
s->cmd = 0;
memset(s->cmdarg, 0, sizeof(s->cmdarg));
memset(s->response, 0, sizeof(s->response));
s->crc16 = 0;
s->arglen = 0;
s->response_pos = 0;
s->stopping = 0;
Expand Down
42 changes: 21 additions & 21 deletions include/hw/sd/sd.h
Expand Up @@ -33,27 +33,27 @@
#include "hw/qdev-core.h"
#include "qom/object.h"

#define OUT_OF_RANGE (1 << 31)
#define ADDRESS_ERROR (1 << 30)
#define BLOCK_LEN_ERROR (1 << 29)
#define ERASE_SEQ_ERROR (1 << 28)
#define ERASE_PARAM (1 << 27)
#define WP_VIOLATION (1 << 26)
#define CARD_IS_LOCKED (1 << 25)
#define LOCK_UNLOCK_FAILED (1 << 24)
#define COM_CRC_ERROR (1 << 23)
#define ILLEGAL_COMMAND (1 << 22)
#define CARD_ECC_FAILED (1 << 21)
#define CC_ERROR (1 << 20)
#define SD_ERROR (1 << 19)
#define CID_CSD_OVERWRITE (1 << 16)
#define WP_ERASE_SKIP (1 << 15)
#define CARD_ECC_DISABLED (1 << 14)
#define ERASE_RESET (1 << 13)
#define CURRENT_STATE (7 << 9)
#define READY_FOR_DATA (1 << 8)
#define APP_CMD (1 << 5)
#define AKE_SEQ_ERROR (1 << 3)
#define OUT_OF_RANGE (1 << 31)
#define ADDRESS_ERROR (1 << 30)
#define BLOCK_LEN_ERROR (1 << 29)
#define ERASE_SEQ_ERROR (1 << 28)
#define ERASE_PARAM (1 << 27)
#define WP_VIOLATION (1 << 26)
#define CARD_IS_LOCKED (1 << 25)
#define LOCK_UNLOCK_FAILED (1 << 24)
#define COM_CRC_ERROR (1 << 23)
#define ILLEGAL_COMMAND (1 << 22)
#define CARD_ECC_FAILED (1 << 21)
#define CC_ERROR (1 << 20)
#define SD_ERROR (1 << 19)
#define CID_CSD_OVERWRITE (1 << 16)
#define WP_ERASE_SKIP (1 << 15)
#define CARD_ECC_DISABLED (1 << 14)
#define ERASE_RESET (1 << 13)
#define CURRENT_STATE (7 << 9)
#define READY_FOR_DATA (1 << 8)
#define APP_CMD (1 << 5)
#define AKE_SEQ_ERROR (1 << 3)

enum SDPhySpecificationVersion {
SD_PHY_SPECv1_10_VERS = 1,
Expand Down
33 changes: 33 additions & 0 deletions include/qemu/crc-ccitt.h
@@ -0,0 +1,33 @@
/*
* CRC16 (CCITT) Checksum Algorithm
*
* Copyright (c) 2021 Wind River Systems, Inc.
*
* Author:
* Bin Meng <bin.meng@windriver.com>
*
* From Linux kernel v5.10 include/linux/crc-ccitt.h
*
* SPDX-License-Identifier: GPL-2.0
*/

#ifndef _CRC_CCITT_H
#define _CRC_CCITT_H

extern uint16_t const crc_ccitt_table[256];
extern uint16_t const crc_ccitt_false_table[256];

extern uint16_t crc_ccitt(uint16_t crc, const uint8_t *buffer, size_t len);
extern uint16_t crc_ccitt_false(uint16_t crc, const uint8_t *buffer, size_t len);

static inline uint16_t crc_ccitt_byte(uint16_t crc, const uint8_t c)
{
return (crc >> 8) ^ crc_ccitt_table[(crc ^ c) & 0xff];
}

static inline uint16_t crc_ccitt_false_byte(uint16_t crc, const uint8_t c)
{
return (crc << 8) ^ crc_ccitt_false_table[(crc >> 8) ^ c];
}

#endif /* _CRC_CCITT_H */

0 comments on commit 55d9895

Please sign in to comment.