1 /*
2  * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <string.h>
8 #include "esp_err.h"
9 #include "esp_log.h"
10 #include "sys/lock.h"
11 #include "driver/sdmmc_types.h"
12 #include "driver/sdmmc_defs.h"
13 #include "driver/sdmmc_types.h"
14 #include "sdspi_private.h"
15 #include "sdspi_crc.h"
16 
17 static const char* TAG = "sdspi_transaction";
18 
19 static _lock_t s_lock;
20 static bool s_app_cmd;
21 
sdspi_msg_crc7(sdspi_hw_cmd_t * hw_cmd)22 static uint8_t sdspi_msg_crc7(sdspi_hw_cmd_t* hw_cmd)
23 {
24     const size_t bytes_to_crc = offsetof(sdspi_hw_cmd_t, arguments) +
25             sizeof(hw_cmd->arguments); /* can't take address of bit fields */
26     return sdspi_crc7((const uint8_t *)hw_cmd, bytes_to_crc);
27 }
28 
make_hw_cmd(uint32_t opcode,uint32_t arg,int timeout_ms,sdspi_hw_cmd_t * hw_cmd)29 void make_hw_cmd(uint32_t opcode, uint32_t arg, int timeout_ms, sdspi_hw_cmd_t *hw_cmd)
30 {
31     hw_cmd->start_bit = 0;
32     hw_cmd->transmission_bit = 1;
33     hw_cmd->cmd_index = opcode;
34     hw_cmd->stop_bit = 1;
35     hw_cmd->r1 = 0xff;
36     memset(hw_cmd->response, 0xff, sizeof(hw_cmd->response));
37     hw_cmd->ncr = 0xff;
38     uint32_t arg_s = __builtin_bswap32(arg);
39     memcpy(hw_cmd->arguments, &arg_s, sizeof(arg_s));
40     hw_cmd->crc7 = sdspi_msg_crc7(hw_cmd);
41     hw_cmd->timeout_ms = timeout_ms;
42 }
43 
r1_response_to_err(uint8_t r1,int cmd,esp_err_t * out_err)44 static void r1_response_to_err(uint8_t r1, int cmd, esp_err_t *out_err)
45 {
46     if (r1 & SD_SPI_R1_NO_RESPONSE) {
47         ESP_LOGD(TAG, "cmd=%d, R1 response not found", cmd);
48         *out_err = ESP_ERR_TIMEOUT;
49     } else if (r1 & SD_SPI_R1_CMD_CRC_ERR) {
50         ESP_LOGD(TAG, "cmd=%d, R1 response: command CRC error", cmd);
51         *out_err = ESP_ERR_INVALID_CRC;
52     } else if (r1 & SD_SPI_R1_ILLEGAL_CMD) {
53         ESP_LOGD(TAG, "cmd=%d, R1 response: command not supported", cmd);
54         *out_err = ESP_ERR_NOT_SUPPORTED;
55     } else if (r1 & SD_SPI_R1_ADDR_ERR) {
56         ESP_LOGD(TAG, "cmd=%d, R1 response: alignment error", cmd);
57         *out_err = ESP_ERR_INVALID_ARG;
58     } else if (r1 & SD_SPI_R1_PARAM_ERR) {
59         ESP_LOGD(TAG, "cmd=%d, R1 response: size error", cmd);
60         *out_err = ESP_ERR_INVALID_SIZE;
61     } else if ((r1 & SD_SPI_R1_ERASE_RST) ||
62                (r1 & SD_SPI_R1_ERASE_SEQ_ERR)) {
63         *out_err = ESP_ERR_INVALID_STATE;
64     } else if (r1 & SD_SPI_R1_IDLE_STATE) {
65         // Idle state is handled at command layer
66     } else if (r1 != 0) {
67         ESP_LOGD(TAG, "cmd=%d, R1 response: unexpected value 0x%02x", cmd, r1);
68         *out_err = ESP_ERR_INVALID_RESPONSE;
69     }
70 }
71 
r1_sdio_response_to_err(uint8_t r1,int cmd,esp_err_t * out_err)72 static void r1_sdio_response_to_err(uint8_t r1, int cmd, esp_err_t *out_err)
73 {
74     if (r1 & SD_SPI_R1_NO_RESPONSE) {
75         ESP_LOGI(TAG, "cmd=%d, R1 response not found", cmd);
76         *out_err = ESP_ERR_TIMEOUT;
77     } else if (r1 & SD_SPI_R1_CMD_CRC_ERR) {
78         ESP_LOGI(TAG, "cmd=%d, R1 response: command CRC error", cmd);
79         *out_err = ESP_ERR_INVALID_CRC;
80     } else if (r1 & SD_SPI_R1_ILLEGAL_CMD) {
81         ESP_LOGI(TAG, "cmd=%d, R1 response: command not supported", cmd);
82         *out_err = ESP_ERR_NOT_SUPPORTED;
83     } else if (r1 & SD_SPI_R1_PARAM_ERR) {
84         ESP_LOGI(TAG, "cmd=%d, R1 response: size error", cmd);
85         *out_err = ESP_ERR_INVALID_SIZE;
86     } else if (r1 & SDIO_R1_FUNC_NUM_ERR) {
87         ESP_LOGI(TAG, "cmd=%d, R1 response: function number error", cmd);
88         *out_err = ESP_ERR_INVALID_ARG;
89     } else if (r1 & SD_SPI_R1_IDLE_STATE) {
90         // Idle state is handled at command layer
91     } else if (r1 != 0) {
92         ESP_LOGI(TAG, "cmd=%d, R1 response: unexpected value 0x%02x", cmd, r1);
93         *out_err = ESP_ERR_INVALID_RESPONSE;
94     }
95 }
96 
sdspi_host_do_transaction(int slot,sdmmc_command_t * cmdinfo)97 esp_err_t sdspi_host_do_transaction(int slot, sdmmc_command_t *cmdinfo)
98 {
99     _lock_acquire(&s_lock);
100     // Convert the command to wire format
101     WORD_ALIGNED_ATTR sdspi_hw_cmd_t hw_cmd;
102     make_hw_cmd(cmdinfo->opcode, cmdinfo->arg, cmdinfo->timeout_ms, &hw_cmd);
103 
104     // Flags indicate which of the transfer types should be used
105     int flags = 0;
106     if (SCF_CMD(cmdinfo->flags) == SCF_CMD_ADTC) {
107         flags = SDSPI_CMD_FLAG_DATA | SDSPI_CMD_FLAG_WRITE;
108     } else if (SCF_CMD(cmdinfo->flags) == (SCF_CMD_ADTC | SCF_CMD_READ)) {
109         flags = SDSPI_CMD_FLAG_DATA;
110     }
111 
112     // The block size is 512, when larger than 512, the data must send in multi blocks
113     if (cmdinfo->datalen > SDSPI_MAX_DATA_LEN) {
114         flags |= SDSPI_CMD_FLAG_MULTI_BLK;
115     }
116 
117     // In SD host, response format is encoded using SCF_RSP_* flags which come
118     // as part of sdmmc_command_t from the upper layer (sdmmc_cmd.c).
119     // SPI mode uses different command formats. In fact, most of the commands
120     // use R1 response. Therefore, instead of adding another parallel set of
121     // response flags for the SPI mode, response format is determined here:
122     if (!s_app_cmd && cmdinfo->opcode == SD_SEND_IF_COND) {
123         flags |= SDSPI_CMD_FLAG_RSP_R7;
124     } else if (!s_app_cmd && cmdinfo->opcode == MMC_SEND_STATUS) {
125         flags |= SDSPI_CMD_FLAG_RSP_R2;
126     } else if (!s_app_cmd && cmdinfo->opcode == SD_READ_OCR) {
127         flags |= SDSPI_CMD_FLAG_RSP_R3;
128     } else if (s_app_cmd && cmdinfo->opcode == SD_APP_SD_STATUS) {
129         flags |= SDSPI_CMD_FLAG_RSP_R2;
130     } else if (!s_app_cmd && cmdinfo->opcode == MMC_GO_IDLE_STATE &&
131             !(cmdinfo->flags & SCF_RSP_R1)) {
132         /* used to send CMD0 without expecting a response */
133         flags |= SDSPI_CMD_FLAG_NORSP;
134     } else if (!s_app_cmd && cmdinfo->opcode == SD_IO_SEND_OP_COND) {
135         flags |= SDSPI_CMD_FLAG_RSP_R4;
136     } else if (!s_app_cmd && cmdinfo->opcode == SD_IO_RW_DIRECT) {
137         flags |= SDSPI_CMD_FLAG_RSP_R5;
138     } else if (!s_app_cmd && cmdinfo->opcode == SD_IO_RW_EXTENDED) {
139         flags |= SDSPI_CMD_FLAG_RSP_R5 | SDSPI_CMD_FLAG_DATA;
140         if (cmdinfo->arg & SD_ARG_CMD53_WRITE) flags |= SDSPI_CMD_FLAG_WRITE;
141         // The CMD53 can assign block mode in the arg when the length is exactly 512 bytes
142         if (cmdinfo->arg & SD_ARG_CMD53_BLOCK_MODE) flags |= SDSPI_CMD_FLAG_MULTI_BLK;
143     } else if (!s_app_cmd && (cmdinfo->opcode == MMC_ERASE || cmdinfo->opcode == MMC_STOP_TRANSMISSION)) {
144         flags |= SDSPI_CMD_FLAG_RSP_R1B;
145     } else {
146         flags |= SDSPI_CMD_FLAG_RSP_R1;
147     }
148 
149     // Send the command and get the response.
150     esp_err_t ret = sdspi_host_start_command(slot, &hw_cmd,
151             cmdinfo->data, cmdinfo->datalen, flags);
152 
153     // Extract response bytes and store them into cmdinfo structure
154     if (ret == ESP_OK) {
155         ESP_LOGV(TAG, "r1 = 0x%02x hw_cmd.r[0]=0x%08"PRIx32, hw_cmd.r1, hw_cmd.response[0]);
156         // Some errors should be reported using return code
157         if (flags & (SDSPI_CMD_FLAG_RSP_R1 | SDSPI_CMD_FLAG_RSP_R1B)) {
158             cmdinfo->response[0] = hw_cmd.r1;
159             r1_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
160         } else if (flags & SDSPI_CMD_FLAG_RSP_R2) {
161             cmdinfo->response[0] = ((uint32_t)hw_cmd.r1) | ((hw_cmd.response[0] & 0xff) << 8);
162         } else if (flags & (SDSPI_CMD_FLAG_RSP_R3 | SDSPI_CMD_FLAG_RSP_R7)) {
163             r1_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
164             cmdinfo->response[0] = __builtin_bswap32(hw_cmd.response[0]);
165         } else if (flags & SDSPI_CMD_FLAG_RSP_R4) {
166             r1_sdio_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
167             cmdinfo->response[0] = __builtin_bswap32(hw_cmd.response[0]);
168         } else if (flags & SDSPI_CMD_FLAG_RSP_R5) {
169             r1_sdio_response_to_err(hw_cmd.r1, cmdinfo->opcode, &ret);
170             cmdinfo->response[0] = hw_cmd.response[0];
171         }
172     }
173 
174     // Save a flag whether the next command is expected to be an app command
175     if (ret == ESP_OK) {
176         s_app_cmd = (cmdinfo->opcode == MMC_APP_CMD);
177     } else {
178         s_app_cmd = false;
179     }
180     _lock_release(&s_lock);
181     return ret;
182 }
183