1 /*
2 * Copyright (c) 2006-2024, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2017-08-08 Yang the first version
9 * 2019-07-19 yandld The first version for MCXN
10 * 2023-02-0 Alex Yang update driver
11 */
12
13
14 #include <rtdevice.h>
15 #include "fsl_usdhc.h"
16
17
18 #ifdef RT_USING_SDIO
19
20 //#define MMCSD_DEBUG
21
22 #ifdef MMCSD_DEBUG
23 #define MMCSD_DGB rt_kprintf
24 #else
25 #define MMCSD_DGB(fmt, ...)
26 #endif
27
28
29 #define USDHC_ADMA_TABLE_WORDS (32U) /* define the ADMA descriptor table length */
30 #define USDHC_ADMA2_ADDR_ALIGN (4U) /* define the ADMA2 descriptor table addr align size */
31 AT_NONCACHEABLE_SECTION_ALIGN(uint32_t g_usdhcAdma2Table[USDHC_ADMA_TABLE_WORDS], USDHC_ADMA2_ADDR_ALIGN);
32
33 struct mcx_mmcsd
34 {
35 struct rt_mmcsd_host *host;
36 USDHC_Type *USDHC;
37 uint32_t *usdhc_adma2_table;
38 };
39
40
41 #define USDHC_DMA_MODE kUSDHC_DmaModeAdma2
42 #define USDHC_ENDIAN_MODE kUSDHC_EndianModeLittle
43
44
45 #define USDHC_READ_BURST_LEN (8U) /*!< number of words USDHC read in a single burst */
46 #define USDHC_WRITE_BURST_LEN (8U) /*!< number of words USDHC write in a single burst */
47 #define USDHC_DATA_TIMEOUT (0xFU) /*!< data timeout counter value */
48 #define SDMMCHOST_SUPPORT_MAX_BLOCK_LENGTH (4096U)
49 #define SDMMCHOST_SUPPORT_MAX_BLOCK_COUNT (USDHC_MAX_BLOCK_COUNT)
50
51 /* Read/write watermark level. The bigger value indicates DMA has higher read/write performance. */
52 #define USDHC_READ_WATERMARK_LEVEL (0x80U)
53 #define USDHC_WRITE_WATERMARK_LEVEL (0x80U)
54
55
SDMMCHOST_ErrorRecovery(USDHC_Type * base)56 static void SDMMCHOST_ErrorRecovery(USDHC_Type *base)
57 {
58 uint32_t status = 0U;
59 /* get host present status */
60 status = USDHC_GetPresentStatusFlags(base);
61 /* check command inhibit status flag */
62 if ((status & kUSDHC_CommandInhibitFlag) != 0U)
63 {
64 /* reset command line */
65 USDHC_Reset(base, kUSDHC_ResetCommand, 1000U);
66 }
67 /* check data inhibit status flag */
68 if ((status & kUSDHC_DataInhibitFlag) != 0U)
69 {
70 /* reset data line */
71 USDHC_Reset(base, kUSDHC_ResetData, 1000U);
72 }
73 }
74
75
76
77
mcx_sdmmc_request(struct rt_mmcsd_host * host,struct rt_mmcsd_req * req)78 static void mcx_sdmmc_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
79 {
80 struct mcx_mmcsd *mmcsd;
81 struct rt_mmcsd_cmd *cmd;
82 struct rt_mmcsd_data *data;
83 status_t error;
84 usdhc_adma_config_t dmaConfig;
85 usdhc_transfer_t fsl_content = {0};
86 usdhc_command_t fsl_command = {0};
87 usdhc_data_t fsl_data = {0};
88 rt_uint32_t *buf = NULL;
89
90 RT_ASSERT(host != RT_NULL);
91 RT_ASSERT(req != RT_NULL);
92
93 mmcsd = (struct mcx_mmcsd *)host->private_data;
94 RT_ASSERT(mmcsd != RT_NULL);
95
96 cmd = req->cmd;
97 RT_ASSERT(cmd != RT_NULL);
98
99 MMCSD_DGB("\tcmd->cmd_code: %02d, cmd->arg: %08x, cmd->flags: %08x --> ", cmd->cmd_code, cmd->arg, cmd->flags);
100
101 data = cmd->data;
102
103 rt_memset(&dmaConfig, 0, sizeof(usdhc_adma_config_t));
104 /* config adma */
105 dmaConfig.dmaMode = USDHC_DMA_MODE;
106 dmaConfig.burstLen = kUSDHC_EnBurstLenForINCR;
107 dmaConfig.admaTable = mmcsd->usdhc_adma2_table;
108 dmaConfig.admaTableWords = USDHC_ADMA_TABLE_WORDS;
109
110 fsl_command.index = cmd->cmd_code;
111 fsl_command.argument = cmd->arg;
112
113 if (cmd->cmd_code == STOP_TRANSMISSION)
114 fsl_command.type = kCARD_CommandTypeAbort;
115 else
116 fsl_command.type = kCARD_CommandTypeNormal;
117
118 switch (cmd->flags & RESP_MASK)
119 {
120 case RESP_NONE:
121 fsl_command.responseType = kCARD_ResponseTypeNone;
122 break;
123 case RESP_R1:
124 fsl_command.responseType = kCARD_ResponseTypeR1;
125 break;
126 case RESP_R1B:
127 fsl_command.responseType = kCARD_ResponseTypeR1b;
128 break;
129 case RESP_R2:
130 fsl_command.responseType = kCARD_ResponseTypeR2;
131 break;
132 case RESP_R3:
133 fsl_command.responseType = kCARD_ResponseTypeR3;
134 break;
135 case RESP_R4:
136 fsl_command.responseType = kCARD_ResponseTypeR4;
137 break;
138 case RESP_R6:
139 fsl_command.responseType = kCARD_ResponseTypeR6;
140 break;
141 case RESP_R7:
142 fsl_command.responseType = kCARD_ResponseTypeR7;
143 break;
144 case RESP_R5:
145 fsl_command.responseType = kCARD_ResponseTypeR5;
146 break;
147 default:
148 RT_ASSERT(NULL);
149 }
150
151 fsl_command.flags = 0;
152 fsl_content.command = &fsl_command;
153
154 if (data)
155 {
156 if (req->stop != NULL)
157 fsl_data.enableAutoCommand12 = true;
158 else
159 fsl_data.enableAutoCommand12 = false;
160
161 fsl_data.enableAutoCommand23 = false;
162
163 fsl_data.enableIgnoreError = false;
164 fsl_data.dataType = kUSDHC_TransferDataNormal; //todo : update data type
165 fsl_data.blockSize = data->blksize;
166 fsl_data.blockCount = data->blks;
167
168 MMCSD_DGB(" blksize:%d, blks:%d ", fsl_data.blockSize, fsl_data.blockCount);
169
170 if ((cmd->cmd_code == WRITE_BLOCK) || (cmd->cmd_code == WRITE_MULTIPLE_BLOCK))
171 {
172 if (buf)
173 {
174 MMCSD_DGB(" write(data->buf to buf) ");
175 rt_memcpy(buf, data->buf, fsl_data.blockSize * fsl_data.blockCount);
176 fsl_data.txData = (uint32_t const *)buf;
177 }
178 else
179 {
180 fsl_data.txData = (uint32_t const *)data->buf;
181 }
182
183 fsl_data.rxData = NULL;
184 }
185 else
186 {
187 if (buf)
188 {
189 fsl_data.rxData = (uint32_t *)buf;
190 }
191 else
192 {
193 fsl_data.rxData = (uint32_t *)data->buf;
194 }
195
196 fsl_data.txData = NULL;
197 }
198
199 fsl_content.data = &fsl_data;
200 }
201 else
202 {
203 fsl_content.data = NULL;
204 }
205
206 error = USDHC_TransferBlocking(mmcsd->USDHC, &dmaConfig, &fsl_content);
207 if (error != kStatus_Success)
208 {
209 SDMMCHOST_ErrorRecovery(mmcsd->USDHC);
210 MMCSD_DGB(" ***USDHC_TransferBlocking error: %d*** --> \n", error);
211 cmd->err = -RT_ERROR;
212 }
213
214 if (buf)
215 {
216 if (fsl_data.rxData)
217 {
218 MMCSD_DGB("read copy buf to data->buf ");
219 rt_memcpy(data->buf, buf, fsl_data.blockSize * fsl_data.blockCount);
220 }
221
222 rt_free_align(buf);
223 }
224
225 if ((cmd->flags & RESP_MASK) == RESP_R2)
226 {
227 cmd->resp[3] = fsl_command.response[0];
228 cmd->resp[2] = fsl_command.response[1];
229 cmd->resp[1] = fsl_command.response[2];
230 cmd->resp[0] = fsl_command.response[3];
231 MMCSD_DGB(" resp 0x%08X 0x%08X 0x%08X 0x%08X\n",
232 cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
233 }
234 else
235 {
236 cmd->resp[0] = fsl_command.response[0];
237 MMCSD_DGB(" resp 0x%08X\n", cmd->resp[0]);
238 }
239
240 mmcsd_req_complete(host);
241
242 return;
243
244 }
245
246
247
mcx_sdmmc_set_iocfg(struct rt_mmcsd_host * host,struct rt_mmcsd_io_cfg * io_cfg)248 static void mcx_sdmmc_set_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
249 {
250 MMCSD_DGB("%s\r\n", __FUNCTION__);
251 struct mcx_mmcsd *mmcsd;
252
253 mmcsd = (struct mcx_mmcsd *) host->private_data;
254
255 uint32_t sdxc_clock = io_cfg->clock;
256
257 MMCSD_DGB("sdxc_clock:%d\r\n", sdxc_clock);
258 MMCSD_DGB("bus_width:%d\r\n", io_cfg->bus_width);
259
260 if (sdxc_clock != 0U)
261 {
262 USDHC_SetSdClock(mmcsd->USDHC, CLOCK_GetUsdhcClkFreq(), sdxc_clock);
263
264 switch (io_cfg->bus_width)
265 {
266 case MMCSD_BUS_WIDTH_4:
267 USDHC_SetDataBusWidth(mmcsd->USDHC, kUSDHC_DataBusWidth4Bit);
268 break;
269 case MMCSD_BUS_WIDTH_8:
270 USDHC_SetDataBusWidth(mmcsd->USDHC, kUSDHC_DataBusWidth4Bit);
271 break;
272 default:
273 USDHC_SetDataBusWidth(mmcsd->USDHC, kUSDHC_DataBusWidth1Bit);
274 break;
275 }
276 }
277
278 rt_thread_mdelay(20);
279 }
280
281 static const struct rt_mmcsd_host_ops mcx_mmcsd_host_ops =
282 {
283 .request = mcx_sdmmc_request,
284 .set_iocfg = mcx_sdmmc_set_iocfg,
285 .get_card_status = NULL,
286 .enable_sdio_irq = NULL, // Do not use the interrupt mode, use DMA instead
287 };
288
289
290
291
rt_hw_sdio_init(void)292 int rt_hw_sdio_init(void)
293 {
294 struct rt_mmcsd_host *host = RT_NULL;
295 struct mcx_mmcsd *mmcsd = RT_NULL;
296
297 host = mmcsd_alloc_host();
298 if (!host)
299 {
300 return -RT_ERROR;
301 }
302
303 mmcsd = rt_malloc(sizeof(struct mcx_mmcsd));
304 if (!mmcsd)
305 {
306 MMCSD_DGB("alloc mci failed\n");
307 goto err;
308 }
309
310 rt_memset(mmcsd, 0, sizeof(struct mcx_mmcsd));
311
312 mmcsd->USDHC = USDHC0;
313 mmcsd->usdhc_adma2_table = g_usdhcAdma2Table;
314
315 host->ops = &mcx_mmcsd_host_ops;
316 host->freq_min = 375000;
317 host->freq_max = 50000000;
318 host->valid_ocr = VDD_32_33 | VDD_33_34;
319 host->flags = MMCSD_MUTBLKWRITE | MMCSD_BUSWIDTH_4 | MMCSD_SUP_HIGHSPEED | MMCSD_SUP_SDIO_IRQ;
320
321 host->max_seg_size = 65535;
322 host->max_dma_segs = 2;
323 host->max_blk_size = SDMMCHOST_SUPPORT_MAX_BLOCK_LENGTH;
324 host->max_blk_count = SDMMCHOST_SUPPORT_MAX_BLOCK_COUNT;
325
326 mmcsd->host = host;
327
328 /* attach FRO HF to USDHC */
329 CLOCK_SetClkDiv(kCLOCK_DivUSdhcClk, 1u);
330 CLOCK_AttachClk(kFRO_HF_to_USDHC);
331
332 MMCSD_DGB("SDIO clock:%dHz\r\n", CLOCK_GetUsdhcClkFreq());
333
334 /* Initializes SDHC. */
335 usdhc_config_t config;
336 config.dataTimeout = USDHC_DATA_TIMEOUT;
337 config.endianMode = USDHC_ENDIAN_MODE;
338 config.readWatermarkLevel = USDHC_READ_WATERMARK_LEVEL;
339 config.writeWatermarkLevel = USDHC_WRITE_WATERMARK_LEVEL;
340 config.readBurstLen = USDHC_READ_BURST_LEN;
341 config.writeBurstLen = USDHC_WRITE_BURST_LEN;
342 USDHC_Init(USDHC0, &config);
343
344 host->private_data = mmcsd;
345
346 mmcsd_change(host);
347
348 return 0;
349
350 err:
351 mmcsd_free_host(host);
352
353 return -RT_ENOMEM;
354 }
355 INIT_DEVICE_EXPORT(rt_hw_sdio_init);
356
357 #endif /* endif RT_USING_SDIO */
358