1 /*
2  * Copyright (c) 2020-2021, Bluetrum Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author            Notes
8  * 2020-11-30     greedyhao         first version
9  */
10 
11 #include "drv_sdio.h"
12 #include "interrupt.h"
13 #include <rthw.h>
14 
15 #ifdef BSP_USING_SDIO
16 
17 // #define DRV_DEBUG
18 #define LOG_TAG             "drv.sdio"
19 #include <drv_log.h>
20 
21 #define SDIO_USING_1_BIT
22 
23 static struct ab32_sdio_config sdio_config[] =
24 {
25     {.instance = SDMMC0_BASE,
26     },
27 };
28 static struct rt_mmcsd_host *host = RT_NULL;
29 
30 #define RTHW_SDIO_LOCK(_sdio)   rt_mutex_take(&(_sdio)->mutex, RT_WAITING_FOREVER)
31 #define RTHW_SDIO_UNLOCK(_sdio) rt_mutex_release(&(_sdio)->mutex);
32 
33 struct sdio_pkg
34 {
35     struct rt_mmcsd_cmd *cmd;
36     void *buff;
37     rt_uint32_t flag;
38     rt_uint32_t xfer_blks;
39 };
40 
41 struct rthw_sdio
42 {
43     struct rt_mmcsd_host *host;
44     struct ab32_sdio_des sdio_des;
45     struct rt_event event;
46     struct rt_mutex mutex;
47     struct sdio_pkg *pkg;
48 };
49 
50 rt_align(SDIO_ALIGN_LEN)
51 static rt_uint8_t cache_buf[SDIO_BUFF_SIZE];
52 
53 static rt_uint8_t sd_baud = 119;
54 
55 rt_uint8_t sysclk_update_baud(rt_uint8_t baud);
56 
ab32_sdio_clk_get(hal_sfr_t hw_sdio)57 static rt_uint32_t ab32_sdio_clk_get(hal_sfr_t hw_sdio)
58 {
59     return (get_sysclk_nhz() / (sd_baud+1));
60 }
61 
62 /**
63   * @brief  This function get order from sdio.
64   * @param  data
65   * @retval sdio  order
66   */
get_order(rt_uint32_t data)67 static int get_order(rt_uint32_t data)
68 {
69     int order = 0;
70 
71     switch (data)
72     {
73     case 1:
74         order = 0;
75         break;
76     case 2:
77         order = 1;
78         break;
79     case 4:
80         order = 2;
81         break;
82     case 8:
83         order = 3;
84         break;
85     case 16:
86         order = 4;
87         break;
88     case 32:
89         order = 5;
90         break;
91     case 64:
92         order = 6;
93         break;
94     case 128:
95         order = 7;
96         break;
97     case 256:
98         order = 8;
99         break;
100     case 512:
101         order = 9;
102         break;
103     case 1024:
104         order = 10;
105         break;
106     case 2048:
107         order = 11;
108         break;
109     case 4096:
110         order = 12;
111         break;
112     case 8192:
113         order = 13;
114         break;
115     case 16384:
116         order = 14;
117         break;
118     default :
119         order = 0;
120         break;
121     }
122 
123     return order;
124 }
125 
126 /**
127   * @brief  This function wait sdio completed.
128   * @param  sdio  rthw_sdio
129   * @retval None
130   */
rthw_sdio_wait_completed(struct rthw_sdio * sdio)131 static void rthw_sdio_wait_completed(struct rthw_sdio *sdio)
132 {
133     rt_uint32_t status = 0;
134     struct rt_mmcsd_cmd *cmd = sdio->pkg->cmd;
135     struct rt_mmcsd_data *data = cmd->data;
136     hal_sfr_t hw_sdio = sdio->sdio_des.hw_sdio;
137     rt_err_t tx_finish = -RT_ERROR;
138 
139     if (rt_event_recv(&sdio->event, 0xFFFFFFFF & ~HW_SDIO_CON_DFLAG, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
140                         rt_tick_from_millisecond(5000), &status) != RT_EOK)
141     {
142         LOG_E("wait completed timeout");
143         cmd->err = -RT_ETIMEOUT;
144         return;
145     }
146 
147     if (sdio->pkg == RT_NULL)
148     {
149         return;
150     }
151 
152     cmd->resp[0] = hw_sdio[SDxARG3];
153     cmd->resp[1] = hw_sdio[SDxARG2];
154     cmd->resp[2] = hw_sdio[SDxARG1];
155     cmd->resp[3] = hw_sdio[SDxARG0];
156 
157     if (!(status & HW_SDIO_CON_NRPS)) {
158         cmd->err = RT_EOK;
159         LOG_D("sta:0x%08X [%08X %08X %08X %08X]", status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
160     } else {
161         cmd->err = -RT_ERROR;
162     }
163 }
164 
165 /**
166   * @brief  This function transfer data by dma.
167   * @param  sdio  rthw_sdio
168   * @param  pkg   sdio package
169   * @retval None
170   */
rthw_sdio_transfer_by_dma(struct rthw_sdio * sdio,struct sdio_pkg * pkg)171 static void rthw_sdio_transfer_by_dma(struct rthw_sdio *sdio, struct sdio_pkg *pkg)
172 {
173     struct rt_mmcsd_data *data;
174     int size;
175     void *buff;
176     hal_sfr_t hw_sdio = sdio->sdio_des.hw_sdio;
177 
178     if ((RT_NULL == pkg) || (RT_NULL == sdio))
179     {
180         LOG_E("rthw_sdio_transfer_by_dma invalid args");
181         return;
182     }
183 
184     data = pkg->cmd->data;
185     if (RT_NULL == data)
186     {
187         LOG_E("rthw_sdio_transfer_by_dma invalid args");
188         return;
189     }
190 
191     buff = pkg->buff;
192     if (RT_NULL == buff)
193     {
194         LOG_E("rthw_sdio_transfer_by_dma invalid args");
195         return;
196     }
197     hw_sdio = sdio->sdio_des.hw_sdio;
198     size = data->blks * data->blksize;
199 
200     if (data->flags & DATA_DIR_WRITE)
201     {
202         LOG_D("DATA_DIR_WRITE %d", pkg->xfer_blks);
203         sdio->sdio_des.txconfig((rt_uint32_t *)((rt_uint8_t *)buff + (pkg->xfer_blks * data->blksize)), 512);
204     }
205     else if (data->flags & DATA_DIR_READ)
206     {
207         LOG_D("DATA_DIR_WRITE %d", pkg->xfer_blks);
208         sdio->sdio_des.rxconfig((rt_uint32_t *)((rt_uint8_t *)buff + (pkg->xfer_blks * data->blksize)), data->blksize);
209     }
210 }
211 
212 /**
213   * @brief  This function send command.
214   * @param  sdio  rthw_sdio
215   * @param  pkg   sdio package
216   * @retval None
217   */
rthw_sdio_send_command(struct rthw_sdio * sdio,struct sdio_pkg * pkg)218 static void rthw_sdio_send_command(struct rthw_sdio *sdio, struct sdio_pkg *pkg)
219 {
220     struct rt_mmcsd_cmd *cmd = pkg->cmd;
221     struct rt_mmcsd_data *data = cmd->data;
222     hal_sfr_t hw_sdio = sdio->sdio_des.hw_sdio;
223     rt_uint32_t reg_cmd = 0;
224     rt_uint32_t data_flag = 0;
225 
226     /* save pkg */
227     sdio->pkg = pkg;
228 
229     #define CK8E            BIT(11)             //在命令/数据包后加上8CLK
230     #define CBUSY           BIT(10)             //Busy Check
231     #define CLRSP           BIT(9)              //17Byte Long Rsp
232     #define CRSP            BIT(8)              //Need Rsp
233 
234     /* config cmd reg */
235     if (cmd->cmd_code != 18) {
236         reg_cmd = cmd->cmd_code | 0x40 | CK8E;
237     } else {
238         reg_cmd = cmd->cmd_code | 0x40;
239     }
240 
241     switch (resp_type(cmd))
242     {
243     case RESP_R1B:
244         reg_cmd |= CBUSY | CRSP;
245         break;
246     case RESP_R2:
247         reg_cmd |= CLRSP | CRSP;
248         break;
249     default:
250         reg_cmd |= CRSP;
251         break;
252     }
253 
254     LOG_D("CMD:%d 0x%04X ARG:0x%08x RES:%s%s%s%s%s%s%s%s%s rw:%c len:%d blksize:%d",
255           cmd->cmd_code,
256           reg_cmd,
257           cmd->arg,
258           resp_type(cmd) == RESP_NONE ? "NONE"  : "",
259           resp_type(cmd) == RESP_R1  ? "R1"  : "",
260           resp_type(cmd) == RESP_R1B ? "R1B"  : "",
261           resp_type(cmd) == RESP_R2  ? "R2"  : "",
262           resp_type(cmd) == RESP_R3  ? "R3"  : "",
263           resp_type(cmd) == RESP_R4  ? "R4"  : "",
264           resp_type(cmd) == RESP_R5  ? "R5"  : "",
265           resp_type(cmd) == RESP_R6  ? "R6"  : "",
266           resp_type(cmd) == RESP_R7  ? "R7"  : "",
267           data ? (data->flags & DATA_DIR_WRITE ?  'w' : 'r') : '-',
268           data ? data->blks * data->blksize : 0,
269           data ? data->blksize : 0
270          );
271 
272     /* config data reg */
273     if (data != RT_NULL)
274     {
275         rt_uint32_t dir = 0;
276         rt_uint32_t size = data->blks * data->blksize;
277         int order;
278 
279         order = get_order(data->blksize);
280         dir = (data->flags & DATA_DIR_READ) ? HW_SDIO_TO_HOST : 0;
281 
282         data_flag = data->flags;
283     }
284 
285     /* transfer config */
286     if (data_flag & DATA_DIR_READ)
287     {
288         rthw_sdio_transfer_by_dma(sdio, pkg);
289     }
290 
291     /* send cmd */
292     hw_sdio[SDxARG3] = cmd->arg;
293     hw_sdio[SDxCMD]  = reg_cmd;
294 
295     /* wait cmd completed */
296     rthw_sdio_wait_completed(sdio);
297 
298     /* transfer config */
299     if (data != RT_NULL)
300     {
301         do {
302             if ((data_flag & DATA_DIR_WRITE) || ((data_flag & DATA_DIR_READ) && (pkg->xfer_blks != 0))) {
303                 rthw_sdio_transfer_by_dma(sdio, pkg);
304             }
305 
306             rt_uint32_t status = 0;
307 
308             if (rt_event_recv(&sdio->event, 0xFFFFFFFF & ~HW_SDIO_CON_DFLAG, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
309                             rt_tick_from_millisecond(5000), &status) != RT_EOK)
310             {
311                 LOG_E("wait completed timeout");
312                 LOG_E("SDxCON=0x%X SDxCMD=0x%X\n", hw_sdio[SDxCON], hw_sdio[SDxCMD]);
313                 cmd->err = -RT_ETIMEOUT;
314             }
315 
316             if (data_flag & DATA_DIR_WRITE) {
317                 if (((hw_sdio[SDxCON] & HW_SDIO_CON_CRCS) >> 17) != 2) {
318                     LOG_E("Write CRC error!");
319                     cmd->err = -RT_ERROR;
320                     hw_sdio[SDxCPND] = HW_SDIO_CON_DFLAG;
321                 }
322             }
323         } while(++pkg->xfer_blks != data->blks);
324     }
325 
326     /* clear pkg */
327     sdio->pkg = RT_NULL;
328 }
329 
330 /**
331   * @brief  This function send sdio request.
332   * @param  host  rt_mmcsd_host
333   * @param  req   request
334   * @retval None
335   */
rthw_sdio_request(struct rt_mmcsd_host * host,struct rt_mmcsd_req * req)336 static void rthw_sdio_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
337 {
338     struct sdio_pkg pkg;
339     struct rthw_sdio *sdio = host->private_data;
340     struct rt_mmcsd_data *data;
341 
342     RTHW_SDIO_LOCK(sdio);
343 
344     if (req->cmd != RT_NULL)
345     {
346         rt_memset(&pkg, 0, sizeof(pkg));
347         data = req->cmd->data;
348         pkg.cmd = req->cmd;
349 
350         if (data != RT_NULL)
351         {
352             rt_uint32_t size = data->blks * data->blksize;
353 
354             RT_ASSERT(size <= SDIO_BUFF_SIZE);
355 
356             pkg.buff = data->buf;
357             if ((rt_uint32_t)data->buf & (SDIO_ALIGN_LEN - 1))
358             {
359                 pkg.buff = cache_buf;
360                 if (data->flags & DATA_DIR_WRITE)
361                 {
362                     rt_memcpy(cache_buf, data->buf, size);
363                 }
364             }
365         }
366 
367         rthw_sdio_send_command(sdio, &pkg);
368 
369         if ((data != RT_NULL) && (data->flags & DATA_DIR_READ) && ((rt_uint32_t)data->buf & (SDIO_ALIGN_LEN - 1)))
370         {
371             rt_memcpy(data->buf, cache_buf, data->blksize * data->blks);
372         }
373     }
374 
375     if (req->stop != RT_NULL)
376     {
377         rt_memset(&pkg, 0, sizeof(pkg));
378         pkg.cmd = req->stop;
379         rthw_sdio_send_command(sdio, &pkg);
380     }
381 
382     RTHW_SDIO_UNLOCK(sdio);
383 
384     mmcsd_req_complete(sdio->host);
385 }
386 
387 /**
388   * @brief  This function config sdio.
389   * @param  host    rt_mmcsd_host
390   * @param  io_cfg  rt_mmcsd_io_cfg
391   * @retval None
392   */
rthw_sdio_iocfg(struct rt_mmcsd_host * host,struct rt_mmcsd_io_cfg * io_cfg)393 static void rthw_sdio_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
394 {
395     rt_uint32_t clkcr, div, clk_src;
396     rt_uint32_t clk = io_cfg->clock;
397     struct rthw_sdio *sdio = host->private_data;
398     hal_sfr_t hw_sdio = sdio->sdio_des.hw_sdio;
399 
400     clk_src = sdio->sdio_des.clk_get(sdio->sdio_des.hw_sdio);
401     if (clk_src < 240 * 1000)
402     {
403         LOG_E("The clock rate is too low! rata:%d", clk_src);
404         return;
405     }
406 
407     if (clk > host->freq_max) clk = host->freq_max;
408 
409     if (clk > clk_src)
410     {
411         // LOG_W("Setting rate(%d) is greater than clock source rate(%d).", clk, clk_src);
412         // clk = clk_src;
413     }
414 
415     LOG_D("clk:%d width:%s%s%s power:%s%s%s",
416           clk,
417           io_cfg->bus_width == MMCSD_BUS_WIDTH_8 ? "8" : "",
418           io_cfg->bus_width == MMCSD_BUS_WIDTH_4 ? "4" : "",
419           io_cfg->bus_width == MMCSD_BUS_WIDTH_1 ? "1" : "",
420           io_cfg->power_mode == MMCSD_POWER_OFF ? "OFF" : "",
421           io_cfg->power_mode == MMCSD_POWER_UP ? "UP" : "",
422           io_cfg->power_mode == MMCSD_POWER_ON ? "ON" : ""
423          );
424 
425     RTHW_SDIO_LOCK(sdio);
426 
427     switch (io_cfg->power_mode)
428     {
429     case MMCSD_POWER_OFF:
430         hw_sdio[SDxCON] &= ~BIT(0);
431         break;
432     case MMCSD_POWER_UP:
433         sd_baud = 199;
434         hw_sdio[SDxCON] = 0;
435         rt_thread_mdelay(1);
436 
437         hw_sdio[SDxCON] |= BIT(0);                 /* SD control enable */
438         hw_sdio[SDxBAUD] = sysclk_update_baud(sd_baud);
439         hw_sdio[SDxCON] |= BIT(3);                 /* Keep clock output */
440         hw_sdio[SDxCON] |= BIT(4);
441         hw_sdio[SDxCON] |= BIT(5);                 /* Data interrupt enable */
442 
443         hal_mdelay(40);
444         break;
445     case MMCSD_POWER_ON:
446         if (clk == SDIO_MAX_FREQ) {
447             hw_sdio[SDxCON] &= ~BIT(3);
448             sd_baud = 3;
449             hw_sdio[SDxBAUD] = sysclk_update_baud(sd_baud);
450         }
451         break;
452     default:
453         LOG_W("unknown power_mode %d", io_cfg->power_mode);
454         break;
455     }
456 
457     RTHW_SDIO_UNLOCK(sdio);
458 }
459 
460 /**
461   * @brief  This function update sdio interrupt.
462   * @param  host    rt_mmcsd_host
463   * @param  enable
464   * @retval None
465   */
rthw_sdio_irq_update(struct rt_mmcsd_host * host,rt_int32_t enable)466 void rthw_sdio_irq_update(struct rt_mmcsd_host *host, rt_int32_t enable)
467 {
468     struct rthw_sdio *sdio = host->private_data;
469     hal_sfr_t hw_sdio = sdio->sdio_des.hw_sdio;
470 
471     if (enable)
472     {
473         LOG_D("enable sdio irq");
474         rt_hw_irq_enable(IRQ_SD_VECTOR);
475     }
476     else
477     {
478         LOG_D("disable sdio irq");
479         rt_hw_irq_disable(IRQ_SD_VECTOR);
480     }
481 }
482 
483 /**
484   * @brief  This function detect sdcard.
485   * @param  host    rt_mmcsd_host
486   * @retval 0x01
487   */
rthw_sd_detect(struct rt_mmcsd_host * host)488 static rt_int32_t rthw_sd_detect(struct rt_mmcsd_host *host)
489 {
490     LOG_D("try to detect device");
491     return 0x01;
492 }
493 
494 /**
495   * @brief  This function interrupt process function.
496   * @param  host  rt_mmcsd_host
497   * @retval None
498   */
499 rt_section(".irq.sdio")
rthw_sdio_irq_process(struct rt_mmcsd_host * host)500 void rthw_sdio_irq_process(struct rt_mmcsd_host *host)
501 {
502     int complete = 0;
503     struct rthw_sdio *sdio = host->private_data;
504     hal_sfr_t hw_sdio = sdio->sdio_des.hw_sdio;
505     rt_uint32_t intstatus = hw_sdio[SDxCON];
506 
507     /* clear flag */
508     if (intstatus & HW_SDIO_CON_CFLAG) {
509         complete = 1;
510         hw_sdio[SDxCPND] = HW_SDIO_CON_CFLAG;
511     }
512 
513     if (intstatus & HW_SDIO_CON_DFLAG) {
514         complete = 1;
515         hw_sdio[SDxCPND] = HW_SDIO_CON_DFLAG;
516     }
517 
518     if (complete)
519     {
520         rt_event_send(&sdio->event, intstatus);
521     }
522 }
523 
524 static const struct rt_mmcsd_host_ops ab32_sdio_ops =
525 {
526     rthw_sdio_request,
527     rthw_sdio_iocfg,
528     rthw_sd_detect,
529     rthw_sdio_irq_update,
530 };
531 
532 /**
533   * @brief  This function create mmcsd host.
534   * @param  sdio_des  ab32_sdio_des
535   * @retval rt_mmcsd_host
536   */
sdio_host_create(struct ab32_sdio_des * sdio_des)537 struct rt_mmcsd_host *sdio_host_create(struct ab32_sdio_des *sdio_des)
538 {
539     struct rt_mmcsd_host *host;
540     struct rthw_sdio *sdio = RT_NULL;
541 
542     if ((sdio_des == RT_NULL) || (sdio_des->txconfig == RT_NULL) || (sdio_des->rxconfig == RT_NULL))
543     {
544         LOG_E("L:%d F:%s %s %s %s",
545               (sdio_des == RT_NULL ? "sdio_des is NULL" : ""),
546               (sdio_des ? (sdio_des->txconfig ? "txconfig is NULL" : "") : ""),
547               (sdio_des ? (sdio_des->rxconfig ? "rxconfig is NULL" : "") : "")
548              );
549         return RT_NULL;
550     }
551 
552     sdio = rt_malloc(sizeof(struct rthw_sdio));
553     if (sdio == RT_NULL)
554     {
555         LOG_E("L:%d F:%s malloc rthw_sdio fail");
556         return RT_NULL;
557     }
558     rt_memset(sdio, 0, sizeof(struct rthw_sdio));
559 
560     host = mmcsd_alloc_host();
561     if (host == RT_NULL)
562     {
563         LOG_E("L:%d F:%s mmcsd alloc host fail");
564         rt_free(sdio);
565         return RT_NULL;
566     }
567 
568     rt_memcpy(&sdio->sdio_des, sdio_des, sizeof(struct ab32_sdio_des));
569     sdio->sdio_des.hw_sdio = (sdio_des->hw_sdio == RT_NULL ? SDMMC0_BASE : sdio_des->hw_sdio);
570     sdio->sdio_des.clk_get = (sdio_des->clk_get == RT_NULL ? ab32_sdio_clk_get : sdio_des->clk_get);
571 
572     rt_event_init(&sdio->event, "sdio", RT_IPC_FLAG_FIFO);
573     rt_mutex_init(&sdio->mutex, "sdio", RT_IPC_FLAG_PRIO);
574 
575     /* set host defautl attributes */
576     host->ops = &ab32_sdio_ops;
577     host->freq_min = 240 * 1000;
578     host->freq_max = SDIO_MAX_FREQ;
579     host->valid_ocr = 0X00FFFF80;/* The voltage range supported is 1.65v-3.6v */
580 #ifndef SDIO_USING_1_BIT
581     host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ;
582 #else
583     host->flags = MMCSD_MUTBLKWRITE | MMCSD_SUP_SDIO_IRQ;
584 #endif
585     host->max_seg_size = SDIO_BUFF_SIZE;
586     host->max_dma_segs = 1;
587     host->max_blk_size = 512;
588     host->max_blk_count = 512;
589 
590     /* link up host and sdio */
591     sdio->host = host;
592     host->private_data = sdio;
593 
594     rthw_sdio_irq_update(host, 1);
595 
596     /* ready to change */
597     mmcsd_change(host);
598 
599     return host;
600 }
601 
_dma_txconfig(rt_uint32_t * src,int Size)602 static rt_err_t _dma_txconfig(rt_uint32_t *src, int Size)
603 {
604     hal_sfr_t sdiox = sdio_config->instance;
605 
606     sdiox[SDxDMAADR] = DMA_ADR(src);
607     sdiox[SDxDMACNT] = BIT(18) | BIT(17) | BIT(16) | Size;
608     return RT_EOK;
609 }
610 
_dma_rxconfig(rt_uint32_t * dst,int Size)611 static rt_err_t _dma_rxconfig(rt_uint32_t *dst, int Size)
612 {
613     hal_sfr_t sdiox = sdio_config->instance;
614 
615     sdiox[SDxDMAADR] = DMA_ADR(dst);
616     sdiox[SDxDMACNT] = (Size);
617     return RT_EOK;
618 }
619 
620 rt_section(".irq.sdio")
sdio_isr(int vector,void * param)621 void sdio_isr(int vector, void *param)
622 {
623     /* enter interrupt */
624     rt_interrupt_enter();
625     /* Process All SDIO Interrupt Sources */
626     rthw_sdio_irq_process(host);
627 
628     /* leave interrupt */
629     rt_interrupt_leave();
630 }
631 
rt_hw_sdio_init(void)632 int rt_hw_sdio_init(void)
633 {
634     struct ab32_sdio_des sdio_des = {0};
635     struct sd_handle hsd = {0};
636     rt_uint8_t param = 0;
637     hsd.instance = SDMMC0_BASE;
638 
639     hal_rcu_periph_clk_enable(RCU_SD0);
640     hal_sd_mspinit(&hsd);
641 
642     rt_hw_interrupt_install(IRQ_SD_VECTOR, sdio_isr, &param, "sd_isr");
643 
644     sdio_des.clk_get    = ab32_sdio_clk_get;
645     sdio_des.hw_sdio    = SDMMC0_BASE;
646     sdio_des.rxconfig   = _dma_rxconfig;
647     sdio_des.txconfig   = _dma_txconfig;
648 
649     host = sdio_host_create(&sdio_des);
650 
651     return 0;
652 }
653 INIT_DEVICE_EXPORT(rt_hw_sdio_init);
654 
ab32_mmcsd_change(void)655 void ab32_mmcsd_change(void)
656 {
657     mmcsd_change(host);
658 }
659 
660 #endif // 0
661