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