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