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 release_sdk_4_4_0-3c5977e664 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 .card_busy = am_hal_sdhc_card_busy,
196 };
197
198 static am_hal_card_host_t g_sdhc_card_host = {
199 .pHandle = NULL,
200 .bInited = false,
201 .eBusVoltage = AM_HAL_HOST_BUS_VOLTAGE_1_8,
202 .ui32Module = 0x0,
203 .ops = &g_sdhc_host_ops,
204 };
205
206
207 //
208 // SD/MMC/SDIO host instances
209 //
210 static am_hal_card_host_t *g_CardHosts[AM_HAL_CARD_HOST_NUM] = {
211 &g_sdhc_card_host,
212
213 };
214
215 //
216 // Public functions
217 //
218
219 //
220 // Initialize the card host instance and return it if it can be initialized
221 // successfully.
222 //
am_hal_get_card_host(am_hal_host_inst_index_e eIndex,bool bReInit)223 am_hal_card_host_t *am_hal_get_card_host(am_hal_host_inst_index_e eIndex, bool bReInit)
224 {
225 am_hal_card_host_t *pHost;
226
227 if (eIndex >= AM_HAL_CARD_HOST_NUM)
228 {
229 return NULL;
230 }
231
232 pHost = g_CardHosts[eIndex];
233
234 if (pHost->bInited && !bReInit)
235 {
236 return pHost;
237 }
238
239 if (pHost->bInited)
240 {
241 pHost->ops->deinit(pHost->pHandle);
242 }
243
244 if (pHost->ops->init(pHost) != AM_HAL_STATUS_SUCCESS)
245 {
246 return NULL;
247 }
248
249 pHost->bInited = true;
250
251 return pHost;
252 }
253
254 //
255 // Set the default transfer mode of the SDHC host controller. if command's transfer type is not
256 // specified, it will be used.
257 //
am_hal_card_host_set_xfer_mode(am_hal_card_host_t * pHost,am_hal_host_xfer_mode_e eXferMode)258 void am_hal_card_host_set_xfer_mode(am_hal_card_host_t *pHost, am_hal_host_xfer_mode_e eXferMode)
259 {
260 pHost->eXferMode = eXferMode;
261 }
262
am_hal_card_host_set_txrx_delay(am_hal_card_host_t * pHost,uint8_t ui8TxRxDelays[2])263 void am_hal_card_host_set_txrx_delay(am_hal_card_host_t *pHost, uint8_t ui8TxRxDelays[2])
264 {
265 if (pHost != NULL && pHost->bInited && pHost->ops->set_txrx_delay)
266 {
267 pHost->ops->set_txrx_delay(pHost->pHandle, ui8TxRxDelays);
268 }
269 }
270
271 //*****************************************************************************
272 //
273 // End Doxygen group.
274 //! @}
275 //
276 //*****************************************************************************
277