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