1 //*****************************************************************************
2 //
3 //! @file am_hal_card_host.c
4 //!
5 //! @brief Functions for interfacing with the SDHC or SPI SD/MMC/SDIO card host.
6 //!
7 //! @addtogroup card_host_4p Card Host for SD/MMC/eMMC/SDIO
8 //! @ingroup apollo4p_hal
9 //! @{
10 //
11 //*****************************************************************************
12
13 //*****************************************************************************
14 //
15 // Copyright (c) 2023, Ambiq Micro, Inc.
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are met:
20 //
21 // 1. Redistributions of source code must retain the above copyright notice,
22 // this list of conditions and the following disclaimer.
23 //
24 // 2. Redistributions in binary form must reproduce the above copyright
25 // notice, this list of conditions and the following disclaimer in the
26 // documentation and/or other materials provided with the distribution.
27 //
28 // 3. Neither the name of the copyright holder nor the names of its
29 // contributors may be used to endorse or promote products derived from this
30 // software without specific prior written permission.
31 //
32 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
33 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
36 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 // POSSIBILITY OF SUCH DAMAGE.
43 //
44 // This is part of revision stable-7da8bae71f of the AmbiqSuite Development Package.
45 //
46 //*****************************************************************************
47
48 #include <stdint.h>
49 #include <stdbool.h>
50
51 #include "am_mcu_apollo.h"
52 #include "am_util_stdio.h"
53 #include "am_util_debug.h"
54
55 //
56 // Internal MACROs
57 //
58 #define AM_HAL_CARD_HOST_DEBUG(fmt, ...) am_util_debug_printf("[CARD HOST] line %04d - "fmt, __LINE__, ##__VA_ARGS__)
59
60 //
61 // Internal functions
62 //
63
64 //*****************************************************************************
65 //
66 // Initialize the SDHC as a Host
67 // Sets up the power, clock, and capabilities
68 //
69 //*****************************************************************************
am_hal_sdhc_host_init(am_hal_card_host_t * pHost)70 static uint32_t am_hal_sdhc_host_init(am_hal_card_host_t *pHost)
71 {
72 //
73 // Must initialize the SDHC state firstly
74 //
75 if (am_hal_sdhc_initialize(pHost->ui32Module, &pHost->pHandle) != AM_HAL_STATUS_SUCCESS)
76 {
77 return AM_HAL_STATUS_FAIL;
78 }
79
80 //
81 // Must power the SDHC peripheral firstly
82 //
83 if (am_hal_sdhc_power_control(pHost->pHandle, AM_HAL_SYSCTRL_WAKE, false) != AM_HAL_STATUS_SUCCESS)
84 {
85 return AM_HAL_STATUS_FAIL;
86 }
87
88 //
89 // Enable the clock to SDHC peripheral
90 //
91 if (am_hal_sdhc_enable(pHost->pHandle) != AM_HAL_STATUS_SUCCESS)
92 {
93 return AM_HAL_STATUS_FAIL;
94 }
95
96 //
97 // Get the capabilities and version information
98 // save them in the card host for later use
99 //
100 if (am_hal_sdhc_setup_host(pHost->pHandle, pHost) != AM_HAL_STATUS_SUCCESS)
101 {
102 return AM_HAL_STATUS_FAIL;
103 }
104
105 //
106 // Set the 1.8V as the default bus voltage
107 //
108 if (am_hal_sdhc_set_bus_voltage(pHost->pHandle, pHost->eBusVoltage) != AM_HAL_STATUS_SUCCESS)
109 {
110 return AM_HAL_STATUS_FAIL;
111 }
112
113 //
114 // Use the lowest frequency as the default clock speed
115 //
116 if (am_hal_sdhc_set_bus_clock(pHost->pHandle, pHost->ui32MinClock) != AM_HAL_STATUS_SUCCESS)
117 {
118 return AM_HAL_STATUS_FAIL;
119 }
120
121 return AM_HAL_STATUS_SUCCESS;
122 }
123
124 //*****************************************************************************
125 //
126 // Denitializes the SDHC as a Host
127 // Disables the SDHC and power capabilities
128 //
129 //*****************************************************************************
am_hal_sdhc_host_deinit(void * pHandle)130 static uint32_t am_hal_sdhc_host_deinit(void *pHandle)
131 {
132 if (am_hal_sdhc_disable(pHandle) != AM_HAL_STATUS_SUCCESS)
133 {
134 return AM_HAL_STATUS_FAIL;
135 }
136
137 if (am_hal_sdhc_power_control(pHandle, AM_HAL_SYSCTRL_NORMALSLEEP, false) != AM_HAL_STATUS_SUCCESS)
138 {
139 return AM_HAL_STATUS_FAIL;
140 }
141
142 if (am_hal_sdhc_deinitialize(pHandle) != AM_HAL_STATUS_SUCCESS)
143 {
144 return AM_HAL_STATUS_FAIL;
145 }
146
147 return AM_HAL_STATUS_SUCCESS;
148 }
149
150 //*****************************************************************************
151 //
152 // A wrapper for the retries of the am_hal_sdhc_execute_cmd function
153 //
154 //*****************************************************************************
am_hal_sdhc_host_execute_cmd(void * pHandle,am_hal_card_cmd_t * pCmd,am_hal_card_cmd_data_t * pCmdData)155 static uint32_t am_hal_sdhc_host_execute_cmd(void *pHandle, am_hal_card_cmd_t *pCmd, am_hal_card_cmd_data_t *pCmdData)
156 {
157 uint32_t ui32Status;
158 uint8_t ui8Retries = 3;
159
160 while (ui8Retries--)
161 {
162 ui32Status = am_hal_sdhc_execute_cmd(pHandle, pCmd, pCmdData);
163 if ((ui32Status & 0xFFFF) == AM_HAL_STATUS_SUCCESS)
164 {
165 return ui32Status;
166 }
167 }
168
169 AM_HAL_CARD_HOST_DEBUG("Failed to send the %d command after retry three times\n", pCmd->ui8Idx);
170
171 return ui32Status;
172 }
173
174 //*****************************************************************************
175 //
176 // A wrapper for the am_hal_sdhc_power_control function
177 //
178 //*****************************************************************************
am_hal_card_host_power_control(void * pHandle,bool bOnOff)179 static uint32_t am_hal_card_host_power_control(void *pHandle, bool bOnOff)
180 {
181 return am_hal_sdhc_power_control(pHandle, bOnOff ? AM_HAL_SYSCTRL_WAKE : AM_HAL_SYSCTRL_NORMALSLEEP, true);
182 }
183
184 static am_hal_card_host_ops_t g_sdhc_host_ops = {
185 .init = am_hal_sdhc_host_init,
186 .deinit = am_hal_sdhc_host_deinit,
187 .pwr_ctrl = am_hal_card_host_power_control,
188 .execute_cmd = am_hal_sdhc_host_execute_cmd,
189 .set_bus_voltage = am_hal_sdhc_set_bus_voltage,
190 .set_bus_width = am_hal_sdhc_set_bus_width,
191 .set_bus_clock = am_hal_sdhc_set_bus_clock,
192 .set_uhs_mode = am_hal_sdhc_set_uhs_mode,
193 .set_txrx_delay = am_hal_sdhc_set_txrx_delay,
194 .get_cd = am_hal_sdhc_get_cd,
195 .get_wr_protect = am_hal_sdhc_get_wr_protect,
196 .card_busy = am_hal_sdhc_card_busy,
197 };
198
199 static am_hal_card_host_t g_sdhc_card_host = {
200 .pHandle = NULL,
201 .bInited = false,
202 .eBusVoltage = AM_HAL_HOST_BUS_VOLTAGE_1_8,
203 .ui32Module = 0x0,
204 .ops = &g_sdhc_host_ops,
205 };
206
207
208 //
209 // SD/MMC/SDIO host instances
210 //
211 static am_hal_card_host_t *g_CardHosts[AM_HAL_CARD_HOST_NUM] = {
212 &g_sdhc_card_host,
213
214 };
215
216 //
217 // Public functions
218 //
219
220 //
221 // Initialize the card host instance and return it if it can be initialized
222 // successfully.
223 //
am_hal_get_card_host(am_hal_host_inst_index_e eIndex,bool bReInit)224 am_hal_card_host_t *am_hal_get_card_host(am_hal_host_inst_index_e eIndex, bool bReInit)
225 {
226 am_hal_card_host_t *pHost;
227
228 if (eIndex >= AM_HAL_CARD_HOST_NUM)
229 {
230 return NULL;
231 }
232
233 pHost = g_CardHosts[eIndex];
234
235 if (pHost->bInited && !bReInit)
236 {
237 return pHost;
238 }
239
240 if (pHost->bInited)
241 {
242 pHost->ops->deinit(pHost->pHandle);
243 }
244
245 if (pHost->ops->init(pHost) != AM_HAL_STATUS_SUCCESS)
246 {
247 return NULL;
248 }
249
250 pHost->bInited = true;
251
252 return pHost;
253 }
254
255 //
256 // Set the default transfer mode of the SDHC host controller. if command's transfer type is not
257 // specified, it will be used.
258 //
am_hal_card_host_set_xfer_mode(am_hal_card_host_t * pHost,am_hal_host_xfer_mode_e eXferMode)259 void am_hal_card_host_set_xfer_mode(am_hal_card_host_t *pHost, am_hal_host_xfer_mode_e eXferMode)
260 {
261 pHost->eXferMode = eXferMode;
262 }
263
am_hal_card_host_set_txrx_delay(am_hal_card_host_t * pHost,uint8_t ui8TxRxDelays[2])264 void am_hal_card_host_set_txrx_delay(am_hal_card_host_t *pHost, uint8_t ui8TxRxDelays[2])
265 {
266 if (pHost != NULL && pHost->bInited && pHost->ops->set_txrx_delay)
267 {
268 pHost->ops->set_txrx_delay(pHost->pHandle, ui8TxRxDelays);
269 }
270 }
271
272 //*****************************************************************************
273 //
274 // End Doxygen group.
275 //! @}
276 //
277 //*****************************************************************************
278