1 /******************************************************************************
2 *
3 * Copyright (C) 2022-2023 Maxim Integrated Products, Inc. (now owned by
4 * Analog Devices, Inc.),
5 * Copyright (C) 2023-2024 Analog Devices, Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 ******************************************************************************/
20
21 /* **** Includes **** */
22 #include <string.h>
23 #include "mxc_device.h"
24 #include "mxc_assert.h"
25 #include "mxc_sys.h"
26 #include "sdhc.h"
27 #include "sdhc_reva.h"
28
29 /* **** Definitions **** */
30
31 /* **** Globals **** */
32 mxc_sdhc_callback_fn sdhc_callback = NULL;
33
34 /* **** Functions **** */
35 static void MXC_SDHC_FreeCallback(int error);
36 static int MXC_SDHC_TransSetup(mxc_sdhc_reva_regs_t *sdhc, mxc_sdhc_cmd_cfg_t *sd_cmd_cfg);
37
38 /* ************************************************************************** */
MXC_SDHC_RevA_Set_Clock_Config(mxc_sdhc_reva_regs_t * sdhc,unsigned int clk_div)39 void MXC_SDHC_RevA_Set_Clock_Config(mxc_sdhc_reva_regs_t *sdhc, unsigned int clk_div)
40 {
41 sdhc->clk_cn = 0;
42 /* clk_div is split across two fields in the register. Break it up accordingly */
43 sdhc->clk_cn = (clk_div & 0xff) << MXC_F_SDHC_REVA_CLK_CN_SDCLK_FREQ_SEL_POS;
44 sdhc->clk_cn |= ((clk_div & 0x300) >> 8) << MXC_F_SDHC_REVA_CLK_CN_UPPER_SDCLK_FREQ_SEL_POS;
45 sdhc->clk_cn |= MXC_F_SDHC_REVA_CLK_CN_INTERNAL_CLK_EN;
46
47 while (!(sdhc->clk_cn & MXC_F_SDHC_REVA_CLK_CN_INTERNAL_CLK_STABLE)) {}
48
49 sdhc->clk_cn |= MXC_F_SDHC_REVA_CLK_CN_SD_CLK_EN;
50 }
51
52 /* ************************************************************************** */
MXC_SDHC_RevA_Get_Clock_Config(mxc_sdhc_reva_regs_t * sdhc)53 unsigned int MXC_SDHC_RevA_Get_Clock_Config(mxc_sdhc_reva_regs_t *sdhc)
54 {
55 /* clk_div is split across two fields in the register. Build it up accordingly */
56 return ((((sdhc->clk_cn >> MXC_F_SDHC_REVA_CLK_CN_UPPER_SDCLK_FREQ_SEL_POS) << 8) & 0x300) |
57 ((sdhc->clk_cn >> MXC_F_SDHC_REVA_CLK_CN_SDCLK_FREQ_SEL_POS) & 0xff));
58 }
59
60 /* ************************************************************************** */
MXC_SDHC_RevA_Init(mxc_sdhc_reva_regs_t * sdhc,const mxc_sdhc_cfg_t * cfg)61 int MXC_SDHC_RevA_Init(mxc_sdhc_reva_regs_t *sdhc, const mxc_sdhc_cfg_t *cfg)
62 {
63 MXC_ASSERT(cfg);
64
65 if (cfg->clk_div > 0x3FF) {
66 return E_BAD_PARAM;
67 }
68
69 MXC_SDHC_Reset();
70
71 /* Turn on bus supply and enable clock */
72 sdhc->pwr = (cfg->bus_voltage << MXC_F_SDHC_REVA_PWR_BUS_VOLT_SEL_POS) &
73 MXC_F_SDHC_REVA_PWR_BUS_VOLT_SEL;
74
75 sdhc->blk_gap = cfg->block_gap;
76
77 sdhc->host_cn_1 = 0x00;
78
79 MXC_SDHC_Set_Clock_Config(cfg->clk_div);
80
81 /* Set TO to max until we know better */
82 sdhc->to = MXC_F_SDHC_REVA_TO_DATA_COUNT_VALUE;
83
84 /* Note: This only enables bits to show up in the int_stat register */
85 /* The int_signal register is really what you want to generate interrupts out of the IP block */
86 sdhc->int_en = 0xffff;
87 sdhc->er_int_en = 0xffff;
88
89 return E_NO_ERROR;
90 }
91
92 /* ************************************************************************** */
MXC_SDHC_RevA_PowerUp(mxc_sdhc_reva_regs_t * sdhc)93 void MXC_SDHC_RevA_PowerUp(mxc_sdhc_reva_regs_t *sdhc)
94 {
95 sdhc->pwr |= MXC_F_SDHC_REVA_PWR_BUS_POWER;
96 }
97
98 /* ************************************************************************** */
MXC_SDHC_RevA_PowerDown(mxc_sdhc_reva_regs_t * sdhc)99 void MXC_SDHC_RevA_PowerDown(mxc_sdhc_reva_regs_t *sdhc)
100 {
101 sdhc->pwr &= ~MXC_F_SDHC_REVA_PWR_BUS_POWER;
102 }
103
104 /* ************************************************************************** */
MXC_SDHC_RevA_Shutdown(mxc_sdhc_reva_regs_t * sdhc)105 int MXC_SDHC_RevA_Shutdown(mxc_sdhc_reva_regs_t *sdhc)
106 {
107 /* Disable and clear interrupts */
108 sdhc->int_en = 0;
109 sdhc->er_int_en = 0;
110 sdhc->int_stat = sdhc->int_stat;
111 sdhc->er_int_stat = sdhc->er_int_stat;
112
113 if (sdhc_callback != NULL) {
114 MXC_SDHC_FreeCallback(E_SHUTDOWN);
115 }
116
117 return E_NO_ERROR;
118 }
119
120 /* ************************************************************************** */
MXC_SDHC_TransSetup(mxc_sdhc_reva_regs_t * sdhc,mxc_sdhc_cmd_cfg_t * sd_cmd_cfg)121 static int MXC_SDHC_TransSetup(mxc_sdhc_reva_regs_t *sdhc, mxc_sdhc_cmd_cfg_t *sd_cmd_cfg)
122 {
123 if (!MXC_SDHC_Card_Inserted()) {
124 return E_NO_DEVICE;
125 }
126
127 if (sdhc->present & MXC_F_SDHC_REVA_PRESENT_CMD) {
128 /* Command already in progress */
129 return E_BAD_STATE;
130 }
131
132 sdhc->clk_cn |= MXC_F_SDHC_REVA_CLK_CN_SD_CLK_EN;
133
134 sdhc->arg_1 = sd_cmd_cfg->arg_1;
135
136 uint32_t hc1 = sd_cmd_cfg->host_control_1;
137
138 if (sd_cmd_cfg->direction == MXC_SDHC_DIRECTION_WRITE ||
139 sd_cmd_cfg->direction == MXC_SDHC_DIRECTION_READ) {
140 hc1 &=
141 ~(MXC_F_SDHC_REVA_HOST_CN_1_DMA_SELECT | MXC_F_SDHC_REVA_HOST_CN_1_CARD_DETECT_SIGNAL);
142 }
143
144 sdhc->host_cn_1 = hc1;
145
146 /* Clear all flags */
147 sdhc->int_stat = sdhc->int_stat;
148 sdhc->er_int_stat = sdhc->er_int_stat;
149
150 /* Set up Transfer registers */
151 if (sd_cmd_cfg->direction != MXC_SDHC_DIRECTION_CFG) {
152 sdhc->trans = 0;
153 sdhc->sdma = sd_cmd_cfg->sdma;
154
155 if (sd_cmd_cfg->dma) {
156 sdhc->trans |= MXC_F_SDHC_REVA_TRANS_DMA_EN;
157 }
158
159 if (sd_cmd_cfg->direction == MXC_SDHC_DIRECTION_WRITE) {
160 sdhc->trans &= ~(MXC_F_SDHC_REVA_TRANS_READ_WRITE);
161 } else {
162 sdhc->trans |= MXC_F_SDHC_REVA_TRANS_READ_WRITE;
163 }
164
165 sdhc->blk_size = MXC_F_SDHC_REVA_BLK_SIZE_HOST_BUFF |
166 ((sd_cmd_cfg->block_size << MXC_F_SDHC_REVA_BLK_SIZE_TRANS_POS) &
167 MXC_F_SDHC_REVA_BLK_SIZE_TRANS);
168
169 /* Determine transfer size and options */
170 if (sd_cmd_cfg->block_count > 1) {
171 /* Enable multi-block transfers, enable block count register, and automatically issue CMD12 to stop transfer */
172 sdhc->trans |= (MXC_F_SDHC_REVA_TRANS_MULTI | MXC_F_SDHC_REVA_TRANS_BLK_CNT_EN |
173 MXC_S_SDHC_REVA_TRANS_AUTO_CMD_EN_CMD12);
174 sdhc->blk_cnt = sd_cmd_cfg->block_count;
175 }
176
177 } else {
178 sdhc->trans = 0;
179 sdhc->sdma = 0;
180 }
181
182 return E_NO_ERROR;
183 }
184
185 /* ************************************************************************** */
MXC_SDHC_RevA_SendCommand(mxc_sdhc_reva_regs_t * sdhc,mxc_sdhc_cmd_cfg_t * sd_cmd_cfg)186 int MXC_SDHC_RevA_SendCommand(mxc_sdhc_reva_regs_t *sdhc, mxc_sdhc_cmd_cfg_t *sd_cmd_cfg)
187 {
188 int err;
189
190 if ((err = MXC_SDHC_TransSetup(sdhc, sd_cmd_cfg)) != E_NO_ERROR) {
191 return err;
192 }
193
194 /* Start transfer */
195 sdhc->cmd = sd_cmd_cfg->command;
196
197 /* Block on completion */
198 if (sd_cmd_cfg->direction == MXC_SDHC_DIRECTION_CFG) {
199 /* No data transfer, just command */
200 while (!(sdhc->int_stat & MXC_F_SDHC_REVA_INT_STAT_CMD_COMP) &&
201 !(sdhc->int_stat & MXC_F_SDHC_REVA_INT_STAT_ERR_INTR)) {}
202 } else {
203 while (!(sdhc->int_stat & MXC_F_SDHC_REVA_INT_STAT_TRANS_COMP) &&
204 !(sdhc->int_stat & MXC_F_SDHC_REVA_INT_STAT_ERR_INTR)) {}
205 }
206
207 /* Determine if transfer was successful or not */
208 if (sdhc->int_stat & MXC_F_SDHC_REVA_INT_STAT_ERR_INTR) {
209 if (sdhc->er_int_stat &
210 (MXC_F_SDHC_REVA_ER_INT_STAT_CMD_TO | MXC_F_SDHC_REVA_ER_INT_STAT_DATA_TO)) {
211 return E_TIME_OUT;
212 } else {
213 return E_COMM_ERR;
214 }
215 } else {
216 return E_NO_ERROR;
217 }
218 }
219
220 /* ************************************************************************** */
MXC_SDHC_RevA_SendCommandAsync(mxc_sdhc_reva_regs_t * sdhc,mxc_sdhc_cmd_cfg_t * sd_cmd_cfg)221 int MXC_SDHC_RevA_SendCommandAsync(mxc_sdhc_reva_regs_t *sdhc, mxc_sdhc_cmd_cfg_t *sd_cmd_cfg)
222 {
223 int err;
224
225 if ((err = MXC_SDHC_TransSetup(sdhc, sd_cmd_cfg)) != E_NO_ERROR) {
226 return err;
227 }
228
229 sdhc_callback = sd_cmd_cfg->callback;
230
231 if (sd_cmd_cfg->direction == MXC_SDHC_DIRECTION_CFG) {
232 sdhc->int_signal = MXC_F_SDHC_REVA_INT_SIGNAL_CMD_COMP;
233 } else {
234 sdhc->int_signal = MXC_F_SDHC_REVA_INT_SIGNAL_TRANS_COMP;
235 }
236
237 /* Start transfer */
238 sdhc->cmd = sd_cmd_cfg->command;
239
240 return E_NO_ERROR;
241 }
242
243 /* ************************************************************************** */
MXC_SDHC_RevA_Handler(mxc_sdhc_reva_regs_t * sdhc)244 void MXC_SDHC_RevA_Handler(mxc_sdhc_reva_regs_t *sdhc)
245 {
246 int signal = sdhc->int_signal;
247 int flag = MXC_SDHC_GetFlags() & signal;
248
249 // Need to check if there is anything to do in case this function is called
250 // in a polling fashion instead of from the interrupt handler.
251 if (!flag) {
252 return;
253 }
254
255 // Command complete interrupt
256 if ((signal & MXC_F_SDHC_REVA_INT_SIGNAL_CMD_COMP) &&
257 (flag & MXC_F_SDHC_REVA_INT_STAT_CMD_COMP)) {
258 MXC_SDHC_ClearFlags(MXC_F_SDHC_REVA_INT_STAT_CMD_COMP);
259 sdhc->int_signal &= ~MXC_F_SDHC_REVA_INT_SIGNAL_CMD_COMP;
260 MXC_SDHC_FreeCallback(E_NO_ERROR);
261 return;
262 }
263
264 // Transfer complete interrupt
265 if ((signal & MXC_F_SDHC_REVA_INT_SIGNAL_TRANS_COMP) &&
266 (flag & MXC_F_SDHC_REVA_INT_STAT_TRANS_COMP)) {
267 MXC_SDHC_ClearFlags(MXC_F_SDHC_REVA_INT_STAT_TRANS_COMP);
268 sdhc->int_signal &= ~MXC_F_SDHC_REVA_INT_SIGNAL_TRANS_COMP;
269 MXC_SDHC_FreeCallback(E_NO_ERROR);
270 return;
271 }
272
273 MXC_SDHC_ClearFlags(flag);
274 sdhc->int_signal = 0;
275 MXC_SDHC_FreeCallback(E_UNKNOWN);
276 }
277
278 /* ************************************************************************** */
MXC_SDHC_RevA_ClearFlags(mxc_sdhc_reva_regs_t * sdhc,uint32_t mask)279 void MXC_SDHC_RevA_ClearFlags(mxc_sdhc_reva_regs_t *sdhc, uint32_t mask)
280 {
281 sdhc->int_stat = mask;
282 }
283
284 /* ************************************************************************** */
MXC_SDHC_RevA_GetFlags(mxc_sdhc_reva_regs_t * sdhc)285 unsigned MXC_SDHC_RevA_GetFlags(mxc_sdhc_reva_regs_t *sdhc)
286 {
287 return sdhc->int_stat;
288 }
289
290 /* ************************************************************************** */
MXC_SDHC_RevA_Card_Inserted(mxc_sdhc_reva_regs_t * sdhc)291 int MXC_SDHC_RevA_Card_Inserted(mxc_sdhc_reva_regs_t *sdhc)
292 {
293 unsigned int detect, inserted, stable;
294
295 detect = !!(sdhc->present & MXC_F_SDHC_REVA_PRESENT_CARD_DETECT);
296 inserted = !!(sdhc->present & MXC_F_SDHC_REVA_PRESENT_CARD_INSERTED);
297 stable = !!(sdhc->present & MXC_F_SDHC_REVA_PRESENT_CARD_STATE);
298
299 return (detect & inserted & stable);
300 }
301
302 /* ************************************************************************** */
MXC_SDHC_RevA_Reset(mxc_sdhc_reva_regs_t * sdhc)303 void MXC_SDHC_RevA_Reset(mxc_sdhc_reva_regs_t *sdhc)
304 {
305 sdhc->sw_reset = MXC_F_SDHC_REVA_SW_RESET_RESET_ALL;
306
307 /* Reset takes non-zero time, so wait for completion */
308 while (sdhc->sw_reset & MXC_F_SDHC_REVA_SW_RESET_RESET_ALL) {}
309 }
310
311 /* ************************************************************************** */
MXC_SDHC_RevA_Reset_CMD_DAT(mxc_sdhc_reva_regs_t * sdhc)312 void MXC_SDHC_RevA_Reset_CMD_DAT(mxc_sdhc_reva_regs_t *sdhc)
313 {
314 sdhc->sw_reset = MXC_F_SDHC_REVA_SW_RESET_RESET_CMD | MXC_F_SDHC_REVA_SW_RESET_RESET_DAT;
315
316 /* Reset takes non-zero time, so wait for completion */
317 while (sdhc->sw_reset &
318 (MXC_F_SDHC_REVA_SW_RESET_RESET_CMD | MXC_F_SDHC_REVA_SW_RESET_RESET_DAT)) {}
319 }
320
321 /* ************************************************************************** */
MXC_SDHC_RevA_Card_Busy(mxc_sdhc_reva_regs_t * sdhc)322 int MXC_SDHC_RevA_Card_Busy(mxc_sdhc_reva_regs_t *sdhc)
323 {
324 /* Response type 1b uses the DAT[0] line low to indicate busy */
325 return (!((sdhc->present >> MXC_F_SDHC_REVA_PRESENT_DAT_SIGNAL_LEVEL_POS) & 1));
326 }
327
328 /* ************************************************************************** */
MXC_SDHC_RevA_Get_Host_Cn_1(mxc_sdhc_reva_regs_t * sdhc)329 unsigned int MXC_SDHC_RevA_Get_Host_Cn_1(mxc_sdhc_reva_regs_t *sdhc)
330 {
331 return sdhc->host_cn_1;
332 }
333
334 /* ************************************************************************** */
MXC_SDHC_RevA_Get_Response32(mxc_sdhc_reva_regs_t * sdhc)335 uint32_t MXC_SDHC_RevA_Get_Response32(mxc_sdhc_reva_regs_t *sdhc)
336 {
337 return sdhc->resp[0];
338 }
339
340 /* ************************************************************************** */
MXC_SDHC_RevA_Get_Response32_Auto(mxc_sdhc_reva_regs_t * sdhc)341 uint32_t MXC_SDHC_RevA_Get_Response32_Auto(mxc_sdhc_reva_regs_t *sdhc)
342 {
343 /* The response for auto commands get set at idx 3 */
344 return sdhc->resp[3];
345 }
346
347 /* ************************************************************************** */
MXC_SDHC_RevA_Get_Response128(mxc_sdhc_reva_regs_t * sdhc,unsigned char * response)348 void MXC_SDHC_RevA_Get_Response128(mxc_sdhc_reva_regs_t *sdhc, unsigned char *response)
349 {
350 uint32_t tmp;
351
352 tmp = sdhc->resp[0];
353 response[0] = tmp;
354 response[1] = tmp >> 8;
355 response[2] = tmp >> 16;
356 response[3] = tmp >> 24;
357
358 tmp = sdhc->resp[1];
359 response[4] = tmp;
360 response[5] = tmp >> 8;
361 response[6] = tmp >> 16;
362 response[7] = tmp >> 24;
363
364 tmp = sdhc->resp[2];
365 response[8] = tmp;
366 response[9] = tmp >> 8;
367 response[10] = tmp >> 16;
368 response[11] = tmp >> 24;
369
370 tmp = sdhc->resp[3];
371 response[12] = tmp;
372 response[13] = tmp >> 8;
373 response[14] = tmp >> 16;
374 response[15] = tmp >> 24;
375 }
376
377 /* ************************************************************************** */
MXC_SDHC_FreeCallback(int error)378 static void MXC_SDHC_FreeCallback(int error)
379 {
380 /* Save the request so the callback can be NULLed out and still be called. */
381 mxc_sdhc_callback_fn temp_callback = sdhc_callback;
382
383 sdhc_callback = NULL;
384
385 temp_callback(error);
386 }
387