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