1 /*
2  * Copyright 2021 MindMotion Microelectronics Co., Ltd.
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "hal_sdio.h"
9 
10 static void SDIO_SetPeriphClockBaseClock(SDIO_Type * SDIOx, uint32_t busclk_hz, uint32_t periph_1mhz);
11 
SDIO_Init(SDIO_Type * SDIOx,SDIO_Init_Type * init)12 void SDIO_Init(SDIO_Type * SDIOx, SDIO_Init_Type * init)
13 {
14     SDIO_Enable(SDIOx, true);
15 
16     SDIOx->MMCCTRL = SDIO_MMCCTRL_OPMSEL(1) /* SD mode as default. */
17                    | SDIO_MMCCTRL_SELSM(1)   /* hardware auto mode. */
18                    | SDIO_MMCCTRL_OUTM(0)    /* open drain io mode. */
19                    | SDIO_MMCCTRL_CLKSP(init->ClkLineSpeedDiv)
20                    | SDIO_MMCCTRL_SELPTSM(init->BaseClkSrc)
21                    | SDIO_MMCCTRL_DATWT(0)   /* use 1b data width as default. */
22                    | SDIO_MMCCTRL_MDEN(0)    /* SD card as default. */
23                    | SDIO_MMCCTRL_INTEN(0)   /* unlock the interrupt switchers. */
24                    | SDIO_MMCCTRL_RDWTEN(0)  /* disable the read wait signal. */
25                    ;
26 
27     SDIOx->MMCCTRL |= SDIO_MMCCTRL_SELSM_MASK;
28 
29     if (init->BaseClkSrc == SDIO_BaseClkSrc_1MHz)
30     {
31         SDIO_SetPeriphClockBaseClock(SDIOx, init->BusClkHz, 1000000u);
32     }
33 
34     SDIO_SetDataBusWidth(SDIOx, SDIO_DataBusWidth_1b);
35 
36     SDIOx->MMCCRCCTL = 0u; /* disable all the crc feature. */
37     SDIOx->MMCIOMBCTL = 0u;
38     SDIOx->MMCCRCCTL   = SDIO_MMCCRCCTL_CMDCRCEN_MASK | SDIO_MMCCRCCTL_DATCRCEN_MASK;
39 }
40 
SDIO_Enable(SDIO_Type * SDIOx,bool enable)41 void SDIO_Enable(SDIO_Type * SDIOx, bool enable)
42 {
43     if (enable)
44     {
45         SDIOx->MMCCARDSEL |= SDIO_MMCCARDSEL_CTREN_MASK  /* enable the card controller. */
46                            | SDIO_MMCCARDSEL_ENPCLK_MASK /* enable the card clk. */
47                            ;
48     }
49     else
50     {
51         SDIOx->MMCCARDSEL &= ~(SDIO_MMCCARDSEL_CTREN_MASK | SDIO_MMCCARDSEL_ENPCLK_MASK);
52     }
53 }
54 
SDIO_SetPeriphClockBaseClock(SDIO_Type * SDIOx,uint32_t busclk_hz,uint32_t periph_1mhz)55 static void SDIO_SetPeriphClockBaseClock(SDIO_Type * SDIOx, uint32_t busclk_hz, uint32_t periph_1mhz)
56 {
57     uint32_t div = busclk_hz / periph_1mhz / 2 - 1u;
58 
59     SDIOx->MMCCARDSEL = (SDIOx->MMCCARDSEL & ~SDIO_MMCCARDSEL_TSCALE_MASK) | SDIO_MMCCARDSEL_TSCALE(div);
60 }
61 
SDIO_SetDataBusWidth(SDIO_Type * SDIOx,SDIO_DataBusWidth_Type width)62 void SDIO_SetDataBusWidth(SDIO_Type *SDIOx, SDIO_DataBusWidth_Type width)
63 {
64     if (width == SDIO_DataBusWidth_1b)
65     {
66         SDIOx->MMCCTRL &= ~SDIO_MMCCTRL_DATWT_MASK;
67     }
68     else if (width == SDIO_DataBusWidth_4b)
69     {
70         SDIOx->MMCCTRL |= SDIO_MMCCTRL_DATWT_MASK;
71     }
72 }
73 
74 /* return "SDIO_FLAG_XXXX." */
SDIO_GetStatus(SDIO_Type * SDIOx)75 uint32_t SDIO_GetStatus(SDIO_Type * SDIOx)
76 {
77     uint32_t flags = SDIOx->CLRMMCINT;
78 
79     /* add the flags for data fifo. */
80     if (SDIOx->BUFCTL & SDIO_BUFCTL_DBF_MASK)
81     {
82         flags |= SDIO_STATUS_DAT_BUF_FULL;
83     }
84     if (SDIOx->BUFCTL & SDIO_BUFCTL_DBE_MASK)
85     {
86         flags |= SDIO_STATUS_DAT_BUF_EMPTY;
87     }
88 
89     return flags;
90 }
91 
92 /* parameter flgas, refre to "SDIO_FLAG_XXXX." */
SDIO_ClearStatus(SDIO_Type * SDIOx,uint32_t flags)93 void SDIO_ClearStatus(SDIO_Type * SDIOx, uint32_t flags)
94 {
95     SDIOx->CLRMMCINT = (SDIO_STATUS_DAT0_BUSY - 1u) & flags;
96 
97     /* SDIO_STATUS_DAT_BUF_FULL and SDIO_STATUS_DAT_BUF_EMPYT would be cleared by hardware automatically. */
98 }
99 
100 /* parameter interrupts, refre to "SDIO_INT_XXXX." */
SDIO_EnableInterrupts(SDIO_Type * SDIOx,uint32_t interrupts,bool enable)101 void SDIO_EnableInterrupts(SDIO_Type * SDIOx, uint32_t interrupts, bool enable)
102 {
103     if (enable)
104     {
105         SDIOx->MMCINTMASK |= interrupts;
106     }
107     else
108     {
109         SDIOx->MMCINTMASK &= ~interrupts;
110     }
111 
112 }
113 
SDIO_EnableFifoDMA(SDIO_Type * SDIOx,bool enable)114 void SDIO_EnableFifoDMA(SDIO_Type * SDIOx, bool enable)
115 {
116     uint32_t bufctl = SDIOx->BUFCTL & ~(SDIO_BUFCTL_DRM_MASK | SDIO_BUFCTL_DMAHEN_MASK);
117 
118     if (enable)
119     {
120         SDIOx->BUFCTL  = bufctl | SDIO_BUFCTL_DMAHEN_MASK ;
121     }
122     else
123     {
124         SDIOx->BUFCTL = SDIOx->BUFCTL | SDIO_BUFCTL_DRM_MASK;
125     }
126 }
127 
128 /* parameter flgas, refre to "SDIO_CMD_FLAG_XXXX." */
SDIO_ExecuteCmd(SDIO_Type * SDIOx,uint8_t cmd_index,uint32_t param,uint32_t flags)129 void SDIO_ExecuteCmd(SDIO_Type * SDIOx, uint8_t cmd_index, uint32_t param, uint32_t flags)
130 {
131     /* setup parameter. */
132     SDIOx->CMDBUF[4] = 0x40 | cmd_index;
133     SDIOx->CMDBUF[3] = ((param & 0xff000000) >> 24);
134     SDIOx->CMDBUF[2] = ((param & 0xff0000  ) >> 16);
135     SDIOx->CMDBUF[1] = ((param & 0xff00    ) >> 8);
136     SDIOx->CMDBUF[0] = ( param & 0xff      );
137 
138     /* prepare the command. */
139     uint32_t cmd_io = SDIO_MMCIO_AUTOTR_MASK;
140     uint32_t cmd_io_ext = SDIOx->MMCIOMBCTL
141                         & ~(  SDIO_MMCIOMBCTL_SPMBDTR_MASK
142                             | SDIO_MMCIOMBCTL_SMBDTD_MASK
143                             | SDIO_MMCIOMBCTL_PAUTOTR_MASK );
144 
145     if (0u != (flags & (SDIO_CMD_FLAG_READ_BLOCKS | SDIO_CMD_FLAG_READ_BLOCKS) ) )
146     {
147         if (0u != (flags & SDIO_CMD_FLAG_READ_BLOCKS))
148         {
149             cmd_io_ext |= SDIO_MMCIOMBCTL_SMBDTD_MASK;
150         }
151 
152         /* write MMCIO and MMCIOMBCTL to execute the cmd. */
153         SDIOx->MMCIO = 0u;
154         SDIOx->MMCIOMBCTL = cmd_io_ext | SDIO_MMCIOMBCTL_SPMBDTR_MASK;
155     }
156     else
157     {
158         if (0u != (flags & SDIO_CMD_FLAG_READ_BLOCK) )
159         {
160             cmd_io |= SDIO_MMCIO_TRANSFDIR_MASK;
161         }
162         if (0u != (flags & SDIO_CMD_FLAG_READ_CID_CSD) )
163         {
164             cmd_io |= SDIO_MMCIO_CIDCSDRD_MASK;
165         }
166         //if (0u != (flags & SDIO_CMD_FLAG_FOLLOWED_DATA_BLOCK) )
167         //{
168         //    cmd_io |= SDIO_MMCIO_CMDCH_MASK;
169         //}
170         //if (0u != (flags & SDIO_CMD_FLAG_STOP_TRAN) )
171         //{
172         //    cmd_io |= SDIO_MMCIO_CMDAF_MASK;
173         //}
174         if (0u != (flags & SDIO_CMD_FLAG_ENABLE_DATA_XFER) )
175         {
176             cmd_io |= SDIO_MMCIO_AUTODATTR_MASK;
177         }
178         SDIOx->MMCIOMBCTL = cmd_io_ext;
179         SDIOx->MMCIO = cmd_io;
180     }
181 
182     /* pending for the xfer done. */
183     while ( 0u == (SDIO_STATUS_CMD_DONE & SDIO_GetStatus(SDIOx)) )
184     {}
185     SDIO_ClearStatus(SDIOx, SDIO_STATUS_CMD_DONE);
186 }
187 
SDIO_ExecuteData(SDIO_Type * SDIOx,uint32_t cmd_flags)188 void SDIO_ExecuteData(SDIO_Type * SDIOx, uint32_t cmd_flags)
189 {
190     uint32_t cmd_io_ext = SDIOx->MMCIOMBCTL
191                     & ~(  SDIO_MMCIOMBCTL_SPMBDTR_MASK
192                         | SDIO_MMCIOMBCTL_SMBDTD_MASK
193                         | SDIO_MMCIOMBCTL_PAUTOTR_MASK );
194     uint32_t cmd_io = 0u;
195 
196     if (0u != (cmd_flags & SDIO_CMD_FLAG_WRITE_BLOCK) )
197     {
198         //SDIOx->MMCIO = SDIO_MMCIO_AUTODATTR_MASK;
199         cmd_io |= SDIO_MMCIO_AUTODATTR_MASK;
200     }
201     if (0u != (cmd_flags & SDIO_CMD_FLAG_READ_BLOCK) )
202     {
203         //SDIOx->MMCIO = SDIO_MMCIO_AUTODATTR_MASK | SDIO_MMCIO_TRANSFDIR_MASK;
204         cmd_io |= SDIO_MMCIO_AUTODATTR_MASK | SDIO_MMCIO_TRANSFDIR_MASK;
205     }
206     if (0u != (cmd_flags & SDIO_CMD_FLAG_WRITE_BLOCKS) )
207     {
208         cmd_io_ext |= SDIO_MMCIOMBCTL_SPMBDTR_MASK;
209         //SDIOx->MMCIOMBCTL = cmd_io_ext;
210     }
211     else if (0u != (cmd_flags & SDIO_CMD_FLAG_READ_BLOCKS) )
212     {
213         //SDIOx->MMCIO = SDIO_MMCIO_RESPCMDSEL_MASK | SDIO_MMCIO_AUTOTR_MASK;
214         //SDIOx->MMCIOMBCTL = cmd_io_ext | SDIO_MMCIOMBCTL_SPMBDTR_MASK | SDIO_MMCIOMBCTL_SMBDTD_MASK;
215         cmd_io_ext |= SDIO_MMCIOMBCTL_SPMBDTR_MASK | SDIO_MMCIOMBCTL_SMBDTD_MASK;
216     }
217 
218     SDIOx->MMCIO = cmd_io;
219     SDIOx->MMCIOMBCTL = cmd_io_ext;
220 
221 }
222 
SDIO_RequestResp(SDIO_Type * SDIOx,SDIO_RespType_Type type,uint32_t * resp)223 void SDIO_RequestResp(SDIO_Type * SDIOx, SDIO_RespType_Type type, uint32_t *resp)
224 {
225     uint32_t cmd_io = SDIO_MMCIO_AUTOTR_MASK | SDIO_MMCIO_RESPCMDSEL_MASK;
226 
227     if (type == SDIO_RespType_R2)
228     {
229         cmd_io |= SDIO_MMCIO_CIDCSDRD_MASK;
230     }
231 
232     SDIOx->MMCIO = cmd_io;
233 
234     /* pending for the xfer done. */
235     while ( 0u == (SDIO_STATUS_CMD_DONE & SDIO_GetStatus(SDIOx)) )
236     {}
237     SDIO_ClearStatus(SDIOx, SDIO_STATUS_CMD_DONE | SDIO_STATUS_CMD_CRC_ERR);
238 
239 
240     *resp = (SDIOx->CMDBUF[3] << 24)
241           | (SDIOx->CMDBUF[2] << 16)
242           | (SDIOx->CMDBUF[1] << 8 )
243           | (SDIOx->CMDBUF[0]);
244 
245     if (type == SDIO_RespType_R2)
246     {
247         resp++;
248         *resp = (SDIOx->CMDBUF[7] << 24)
249               | (SDIOx->CMDBUF[6] << 16)
250               | (SDIOx->CMDBUF[5] << 8 )
251               | (SDIOx->CMDBUF[4]);
252         resp++;
253         *resp = (SDIOx->CMDBUF[11] << 24)
254               | (SDIOx->CMDBUF[10] << 16)
255               | (SDIOx->CMDBUF[9 ] << 8 )
256               | (SDIOx->CMDBUF[8 ]);
257         resp++;
258         *resp = (SDIOx->CMDBUF[15] << 24)
259               | (SDIOx->CMDBUF[14] << 16)
260               | (SDIOx->CMDBUF[13] << 8 )
261               | (SDIOx->CMDBUF[12]);
262     }
263 }
264 
SDIO_PutFifoData(SDIO_Type * SDIOx,uint32_t dat)265 void SDIO_PutFifoData(SDIO_Type * SDIOx, uint32_t dat)
266 {
267     SDIOx->DATABUF[0] = dat;
268 }
269 
SDIO_GetFifoData(SDIO_Type * SDIOx)270 uint32_t SDIO_GetFifoData(SDIO_Type * SDIOx)
271 {
272     return SDIOx->DATABUF[0];
273 }
274 
275 /* SDIO_BUFCTL_DBFEN bit would be cleared automatically by hardware. */
SDIO_ClearFifoData(SDIO_Type * SDIOx)276 void SDIO_ClearFifoData(SDIO_Type * SDIOx)
277 {
278     SDIOx->BUFCTL |= SDIO_BUFCTL_DBFEN_MASK;
279 }
280 
281 /* the direction of fifo operation is read. need to switch to write before any write operation,
282  * and back to read mannually before the read operation. */
SDIO_SwitchFifoWrite(SDIO_Type * SDIOx,bool write)283 void SDIO_SwitchFifoWrite(SDIO_Type * SDIOx, bool write)
284 {
285     if (write)
286     {
287         SDIOx->BUFCTL |= SDIO_BUFCTL_SBAD_MASK;
288     }
289     else
290     {
291         SDIOx->BUFCTL &= ~SDIO_BUFCTL_SBAD_MASK;
292     }
293 }
294 
295 /* word_cnt is for 32-bit type. */
SDIO_SetFifoWatermark(SDIO_Type * SDIOx,uint32_t word_cnt)296 void SDIO_SetFifoWatermark(SDIO_Type * SDIOx, uint32_t word_cnt)
297 {
298     SDIOx->BUFCTL = (SDIOx->BUFCTL & ~SDIO_BUFCTL_DBML_MASK) | SDIO_BUFCTL_DBML(word_cnt);
299 }
300 
SDIO_SetMultiBlockCount(SDIO_Type * SDIOx,uint32_t blk_cnt)301 void SDIO_SetMultiBlockCount(SDIO_Type * SDIOx, uint32_t blk_cnt)
302 {
303     SDIOx->MMCBLOCKCNT = blk_cnt;
304 }
305 
SDIO_EnableFifoReadWait(SDIO_Type * SDIOx,bool enable)306 void     SDIO_EnableFifoReadWait(SDIO_Type * SDIOx, bool enable)
307 {
308     if (enable)
309     {
310         SDIOx->MMCCTRL |= SDIO_MMCCTRL_RDWTEN_MASK;
311     }
312     else
313     {
314         SDIOx->MMCCTRL &= ~SDIO_MMCCTRL_RDWTEN_MASK;
315     }
316 }
317 
318 /* EOF. */
319 
320