1 /*
2 * Copyright (c) 2006-2022, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2020-05-23 liuduanfei first version
9 */
10 #include "board.h"
11
12 #ifdef RT_USING_SDIO
13
14 #if !defined(BSP_USING_SDIO1) && !defined(BSP_USING_SDIO2)
15 #error "Please define at least one BSP_USING_SDIOx"
16 #endif
17
18 #include "drv_sdio.h"
19
20 #define DBG_TAG "drv.sdio"
21 #ifdef DRV_DEBUG
22 #define DBG_LVL DBG_LOG
23 #else
24 #define DBG_LVL DBG_INFO
25 #endif /* DRV_DEBUG */
26 #include <rtdbg.h>
27
28 static struct rt_mmcsd_host *host1;
29 static struct rt_mmcsd_host *host2;
30 static rt_mutex_t mmcsd_mutex = RT_NULL;
31
32 #define SDIO_TX_RX_COMPLETE_TIMEOUT_LOOPS (1000000)
33
34 struct sdio_pkg
35 {
36 struct rt_mmcsd_cmd *cmd;
37 void *buff;
38 rt_uint32_t flag;
39 };
40
41 struct rthw_sdio
42 {
43 struct rt_mmcsd_host *host;
44 struct stm32_sdio_des sdio_des;
45 struct rt_event event;
46 struct sdio_pkg *pkg;
47 };
48
rt_align(SDIO_ALIGN_LEN)49 rt_align(SDIO_ALIGN_LEN)
50 static rt_uint8_t cache_buf[SDIO_BUFF_SIZE];
51
52 /**
53 * @brief This function get order from sdio.
54 * @param data
55 * @retval sdio order
56 */
57 static int get_order(rt_uint32_t data)
58 {
59 int order = 0;
60
61 switch (data)
62 {
63 case 1:
64 order = 0;
65 break;
66
67 case 2:
68 order = 1;
69 break;
70
71 case 4:
72 order = 2;
73 break;
74
75 case 8:
76 order = 3;
77 break;
78
79 case 16:
80 order = 4;
81 break;
82
83 case 32:
84 order = 5;
85 break;
86
87 case 64:
88 order = 6;
89 break;
90
91 case 128:
92 order = 7;
93 break;
94
95 case 256:
96 order = 8;
97 break;
98
99 case 512:
100 order = 9;
101 break;
102
103 case 1024:
104 order = 10;
105 break;
106
107 case 2048:
108 order = 11;
109 break;
110
111 case 4096:
112 order = 12;
113 break;
114
115 case 8192:
116 order = 13;
117 break;
118
119 case 16384:
120 order = 14;
121 break;
122
123 default :
124 order = 0;
125 break;
126 }
127
128 return order;
129 }
130
131 /**
132 * @brief This function wait sdio cmd completed.
133 * @param sdio rthw_sdio
134 * @retval None
135 */
rthw_sdio_wait_completed(struct rthw_sdio * sdio)136 static void rthw_sdio_wait_completed(struct rthw_sdio *sdio)
137 {
138 rt_uint32_t status;
139 struct rt_mmcsd_cmd *cmd = sdio->pkg->cmd;
140 struct stm32_sdio *hw_sdio = sdio->sdio_des.hw_sdio;
141
142 if (rt_event_recv(&sdio->event, 0xffffffff, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
143 rt_tick_from_millisecond(5000), &status) != RT_EOK)
144 {
145 LOG_E("wait cmd completed timeout");
146 cmd->err = -RT_ETIMEOUT;
147 return;
148 }
149
150 cmd->resp[0] = hw_sdio->resp1;
151
152 if (resp_type(cmd) == RESP_R2)
153 {
154 cmd->resp[1] = hw_sdio->resp2;
155 cmd->resp[2] = hw_sdio->resp3;
156 cmd->resp[3] = hw_sdio->resp4;
157 }
158
159 if (status & SDIO_ERRORS)
160 {
161 if ((status & SDMMC_STA_CCRCFAIL) && (resp_type(cmd) & (RESP_R3 | RESP_R4)))
162 {
163 cmd->err = RT_EOK;
164 }
165 else
166 {
167 cmd->err = -RT_ERROR;
168 }
169 }
170 else
171 {
172 cmd->err = RT_EOK;
173 }
174
175
176 if (cmd->err == RT_EOK)
177 {
178 LOG_D("sta:0x%08X [%08X %08X %08X %08X]", status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
179 }
180 else
181 {
182 LOG_D("send command error = %d", cmd->err);
183 }
184 }
185
186 /**
187 * @brief This function send command.
188 * @param sdio rthw_sdio
189 * @param pkg sdio package
190 * @retval None
191 */
rthw_sdio_send_command(struct rthw_sdio * sdio,struct sdio_pkg * pkg)192 static void rthw_sdio_send_command(struct rthw_sdio *sdio, struct sdio_pkg *pkg)
193 {
194 struct rt_mmcsd_cmd *cmd = pkg->cmd;
195 struct rt_mmcsd_data *data = cmd->data;
196 struct stm32_sdio *hw_sdio = sdio->sdio_des.hw_sdio;
197 rt_uint32_t reg_cmd;
198
199 rt_event_control(&sdio->event, RT_IPC_CMD_RESET, RT_NULL);
200 /* save pkg */
201 sdio->pkg = pkg;
202
203 LOG_D("CMD:%d ARG:0x%08x RES:%s%s%s%s%s%s%s%s%s rw:%c len:%d blksize:%d\n",
204 cmd->cmd_code,
205 cmd->arg,
206 resp_type(cmd) == RESP_NONE ? "NONE" : "",
207 resp_type(cmd) == RESP_R1 ? "R1" : "",
208 resp_type(cmd) == RESP_R1B ? "R1B" : "",
209 resp_type(cmd) == RESP_R2 ? "R2" : "",
210 resp_type(cmd) == RESP_R3 ? "R3" : "",
211 resp_type(cmd) == RESP_R4 ? "R4" : "",
212 resp_type(cmd) == RESP_R5 ? "R5" : "",
213 resp_type(cmd) == RESP_R6 ? "R6" : "",
214 resp_type(cmd) == RESP_R7 ? "R7" : "",
215 data ? (data->flags & DATA_DIR_WRITE ? 'w' : 'r') : '-',
216 data ? data->blks * data->blksize : 0,
217 data ? data->blksize : 0
218 );
219
220 hw_sdio->mask |= SDIO_MASKR_ALL;
221 reg_cmd = cmd->cmd_code | SDMMC_CMD_CPSMEN;
222
223 /* data pre configuration */
224 if (data != RT_NULL)
225 {
226 SCB_CleanInvalidateDCache();
227
228 reg_cmd |= SDMMC_CMD_CMDTRANS;
229 hw_sdio->mask &= ~(SDMMC_MASK_CMDRENDIE | SDMMC_MASK_CMDSENTIE);
230 hw_sdio->dtimer = HW_SDIO_DATATIMEOUT;
231 hw_sdio->dlen = data->blks * data->blksize;
232 hw_sdio->dctrl = (get_order(data->blksize) << 4) | (data->flags & DATA_DIR_READ ? SDMMC_DCTRL_DTDIR : 0);
233 hw_sdio->idmabase0r = (rt_uint32_t)cache_buf;
234 hw_sdio->idmatrlr = SDMMC_IDMA_IDMAEN;
235 }
236
237 if (resp_type(cmd) == RESP_R2)
238 reg_cmd |= SDMMC_CMD_WAITRESP;
239 else if(resp_type(cmd) != RESP_NONE)
240 reg_cmd |= SDMMC_CMD_WAITRESP_0;
241
242 hw_sdio->arg = cmd->arg;
243 hw_sdio->cmd = reg_cmd;
244 /* wait completed */
245 rthw_sdio_wait_completed(sdio);
246
247 /* Waiting for data to be sent to completion */
248 if (data != RT_NULL)
249 {
250 volatile rt_uint32_t count = SDIO_TX_RX_COMPLETE_TIMEOUT_LOOPS;
251
252 while (count && (hw_sdio->sta & SDMMC_STA_DPSMACT))
253 {
254 count--;
255 }
256
257 if ((count == 0) || (hw_sdio->sta & SDIO_ERRORS))
258 {
259 cmd->err = -RT_ERROR;
260 }
261 }
262
263 /* data post configuration */
264 if (data != RT_NULL)
265 {
266 if (data->flags & DATA_DIR_READ)
267 {
268 rt_memcpy(data->buf, cache_buf, data->blks * data->blksize);
269 SCB_CleanInvalidateDCache();
270 }
271 }
272 }
273
274 /**
275 * @brief This function send sdio request.
276 * @param sdio rthw_sdio
277 * @param req request
278 * @retval None
279 */
rthw_sdio_request(struct rt_mmcsd_host * host,struct rt_mmcsd_req * req)280 static void rthw_sdio_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
281 {
282 struct sdio_pkg pkg;
283 struct rthw_sdio *sdio = host->private_data;
284 struct rt_mmcsd_data *data;
285
286
287 rt_mutex_take(mmcsd_mutex, RT_WAITING_FOREVER);
288
289 if (req->cmd != RT_NULL)
290 {
291 rt_memset(&pkg, 0, sizeof(pkg));
292 data = req->cmd->data;
293 pkg.cmd = req->cmd;
294
295 if (data != RT_NULL)
296 {
297 rt_uint32_t size = data->blks * data->blksize;
298
299 RT_ASSERT(size <= SDIO_BUFF_SIZE);
300
301 if (data->flags & DATA_DIR_WRITE)
302 {
303 rt_memcpy(cache_buf, data->buf, size);
304 }
305 }
306
307 rthw_sdio_send_command(sdio, &pkg);
308 }
309
310 if (req->stop != RT_NULL)
311 {
312 rt_memset(&pkg, 0, sizeof(pkg));
313 pkg.cmd = req->stop;
314 rthw_sdio_send_command(sdio, &pkg);
315 }
316
317 mmcsd_req_complete(sdio->host);
318
319 rt_mutex_release(mmcsd_mutex);
320 }
321
322
323 /**
324 * @brief This function interrupt process function.
325 * @param host rt_mmcsd_host
326 * @retval None
327 */
rthw_sdio_irq_process(struct rt_mmcsd_host * host)328 void rthw_sdio_irq_process(struct rt_mmcsd_host *host)
329 {
330 struct rthw_sdio *sdio = host->private_data;
331 struct stm32_sdio *hw_sdio = sdio->sdio_des.hw_sdio;
332 rt_uint32_t intstatus = hw_sdio->sta;
333
334 /* clear irq flag*/
335 hw_sdio->icr = intstatus;
336
337 rt_event_send(&sdio->event, intstatus);
338 }
339
340 /**
341 * @brief This function config sdio.
342 * @param host rt_mmcsd_host
343 * @param io_cfg rt_mmcsd_io_cfg
344 * @retval None
345 */
rthw_sdio_iocfg(struct rt_mmcsd_host * host,struct rt_mmcsd_io_cfg * io_cfg)346 static void rthw_sdio_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
347 {
348 rt_uint32_t temp, clk_src;
349 rt_uint32_t clk = io_cfg->clock;
350 struct rthw_sdio *sdio = host->private_data;
351 struct stm32_sdio *hw_sdio = sdio->sdio_des.hw_sdio;
352
353 LOG_D("clk:%dK width:%s%s%s power:%s%s%s",
354 clk / 1000,
355 io_cfg->bus_width == MMCSD_BUS_WIDTH_8 ? "8" : "",
356 io_cfg->bus_width == MMCSD_BUS_WIDTH_4 ? "4" : "",
357 io_cfg->bus_width == MMCSD_BUS_WIDTH_1 ? "1" : "",
358 io_cfg->power_mode == MMCSD_POWER_OFF ? "OFF" : "",
359 io_cfg->power_mode == MMCSD_POWER_UP ? "UP" : "",
360 io_cfg->power_mode == MMCSD_POWER_ON ? "ON" : ""
361 );
362
363 clk_src = SDIO_CLOCK_FREQ;
364
365 if (clk > 0)
366 {
367 if (clk > host->freq_max)
368 clk = host->freq_max;
369
370 temp = DIV_ROUND_UP(clk_src, 2 * clk);
371
372 if (temp > 0x3FF)
373 temp = 0x3FF;
374 }
375
376 if (io_cfg->bus_width == MMCSD_BUS_WIDTH_4)
377 temp |= SDMMC_CLKCR_WIDBUS_0;
378 else if (io_cfg->bus_width == MMCSD_BUS_WIDTH_8)
379 temp |= SDMMC_CLKCR_WIDBUS_1;
380
381 hw_sdio->clkcr = temp;
382
383 if (io_cfg->power_mode == MMCSD_POWER_ON)
384 hw_sdio->power |= SDMMC_POWER_PWRCTRL;
385 }
386
387 static const struct rt_mmcsd_host_ops ops =
388 {
389 rthw_sdio_request,
390 rthw_sdio_iocfg,
391 RT_NULL,
392 RT_NULL,
393 };
394
395 /**
396 * @brief This function create mmcsd host.
397 * @param sdio_des stm32_sdio_des
398 * @retval rt_mmcsd_host
399 */
sdio_host_create(struct stm32_sdio_des * sdio_des)400 struct rt_mmcsd_host *sdio_host_create(struct stm32_sdio_des *sdio_des)
401 {
402 struct rt_mmcsd_host *host;
403 struct rthw_sdio *sdio = RT_NULL;
404
405 if (sdio_des == RT_NULL)
406 {
407 return RT_NULL;
408 }
409
410 sdio = rt_malloc(sizeof(struct rthw_sdio));
411
412 if (sdio == RT_NULL)
413 {
414 LOG_E("malloc rthw_sdio fail");
415 return RT_NULL;
416 }
417
418 rt_memset(sdio, 0, sizeof(struct rthw_sdio));
419
420 host = mmcsd_alloc_host();
421
422 if (host == RT_NULL)
423 {
424 LOG_E("alloc host fail");
425 goto err;
426 }
427
428 rt_memcpy(&sdio->sdio_des, sdio_des, sizeof(struct stm32_sdio_des));
429
430 if(sdio_des->hsd.Instance == SDMMC1)
431 {
432 sdio->sdio_des.hw_sdio = (struct stm32_sdio *)SDIO1_BASE_ADDRESS;
433 rt_event_init(&sdio->event, "sdio", RT_IPC_FLAG_FIFO);
434 }
435
436 if(sdio_des->hsd.Instance == SDMMC2)
437 {
438 sdio->sdio_des.hw_sdio = (struct stm32_sdio *)SDIO2_BASE_ADDRESS;
439 rt_event_init(&sdio->event, "sdio2", RT_IPC_FLAG_FIFO);
440 }
441
442 /* set host default attributes */
443 host->ops = &ops;
444 host->freq_min = 400 * 1000;
445 host->freq_max = SDIO_MAX_FREQ;
446 host->valid_ocr = VDD_32_33 | VDD_33_34;/* The voltage range supported is 3.2v-3.4v */
447 host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_HIGHSPEED;
448 host->max_seg_size = SDIO_BUFF_SIZE;
449 host->max_dma_segs = 1;
450 host->max_blk_size = 512;
451 host->max_blk_count = 512;
452
453 /* link up host and sdio */
454 sdio->host = host;
455 host->private_data = sdio;
456
457 return host;
458
459 err:
460
461 if (sdio) rt_free(sdio);
462
463 return RT_NULL;
464 }
465
SDMMC1_IRQHandler(void)466 void SDMMC1_IRQHandler(void)
467 {
468 /* enter interrupt */
469 rt_interrupt_enter();
470 /* Process All SDIO Interrupt Sources */
471 rthw_sdio_irq_process(host1);
472 /* leave interrupt */
473 rt_interrupt_leave();
474 }
475
SDMMC2_IRQHandler(void)476 void SDMMC2_IRQHandler(void)
477 {
478 /* enter interrupt */
479 rt_interrupt_enter();
480 /* Process All SDIO Interrupt Sources */
481 rthw_sdio_irq_process(host2);
482 /* leave interrupt */
483 rt_interrupt_leave();
484 }
485
rt_hw_sdio_init(void)486 int rt_hw_sdio_init(void)
487 {
488 #ifdef BSP_USING_SDIO1
489 struct stm32_sdio_des sdio_des1;
490 sdio_des1.hsd.Instance = SDMMC1;
491 HAL_SD_MspInit(&sdio_des1.hsd);
492
493 host1 = sdio_host_create(&sdio_des1);
494
495 if (host1 == RT_NULL)
496 {
497 LOG_E("host create fail");
498 return RT_NULL;
499 }
500
501 #endif
502
503 #ifdef BSP_USING_SDIO2
504 //sdmmc2 wifi
505 struct stm32_sdio_des sdio_des2;
506 sdio_des2.hsd.Instance = SDMMC2;
507 HAL_SD_MspInit(&sdio_des2.hsd);
508
509 host2 = sdio_host_create(&sdio_des2);
510
511 if (host2 == RT_NULL)
512 {
513 LOG_E("host2 create fail");
514 return RT_NULL;
515 }
516
517 /* wifi auto change */
518 mmcsd_change(host2);
519 #endif
520 mmcsd_mutex = rt_mutex_create("mmutex", RT_IPC_FLAG_PRIO);
521
522 if (mmcsd_mutex == RT_NULL)
523 {
524 rt_kprintf("create mmcsd mutex failed.\n");
525 return -1;
526 }
527
528 return 0;
529 }
530 INIT_DEVICE_EXPORT(rt_hw_sdio_init);
531
sdcard_change(void)532 void sdcard_change(void)
533 {
534 mmcsd_change(host1);
535 }
536
537 #endif /* RT_USING_SDIO */
538