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