1 /**
2   ******************************************************************************
3   * @file    stm32l5xx_hal_mmc_ex.c
4   * @author  MCD Application Team
5   * @brief   MMC card Extended HAL module driver.
6   *          This file provides firmware functions to manage the following
7   *          functionalities of the Secure Digital (MMC) peripheral:
8   *           + Extended features functions
9   *
10   ******************************************************************************
11   * @attention
12   *
13   * Copyright (c) 2019 STMicroelectronics.
14   * All rights reserved.
15   *
16   * This software is licensed under terms that can be found in the LICENSE file
17   * in the root directory of this software component.
18   * If no LICENSE file comes with this software, it is provided AS-IS.
19   *
20   ******************************************************************************
21   @verbatim
22   ==============================================================================
23                         ##### How to use this driver #####
24   ==============================================================================
25   [..]
26    The MMC Extension HAL driver can be used as follows:
27    (+) Configure Buffer0 and Buffer1 start address and Buffer size using HAL_MMCEx_ConfigDMAMultiBuffer() function.
28 
29    (+) Start Read and Write for multibuffer mode using HAL_MMCEx_ReadBlocksDMAMultiBuffer() and
30        HAL_MMCEx_WriteBlocksDMAMultiBuffer() functions.
31 
32   @endverbatim
33   ******************************************************************************
34   */
35 
36 /* Includes ------------------------------------------------------------------*/
37 #include "stm32l5xx_hal.h"
38 
39 /** @addtogroup STM32L5xx_HAL_Driver
40   * @{
41   */
42 
43 /** @defgroup MMCEx MMCEx
44   * @brief MMC Extended HAL module driver
45   * @{
46   */
47 
48 #if defined (SDMMC1) || defined (SDMMC2)
49 #ifdef HAL_MMC_MODULE_ENABLED
50 
51 /* Private typedef -----------------------------------------------------------*/
52 /* Private define ------------------------------------------------------------*/
53 /* Private macro -------------------------------------------------------------*/
54 /* Private variables ---------------------------------------------------------*/
55 /* Private function prototypes -----------------------------------------------*/
56 /* Private functions ---------------------------------------------------------*/
57 /* Exported functions --------------------------------------------------------*/
58 /** @addtogroup MMCEx_Exported_Functions
59   * @{
60   */
61 
62 
63 
64 /** @addtogroup MMCEx_Exported_Functions_Group1
65   *  @brief   Multibuffer functions
66   *
67 @verbatim
68   ==============================================================================
69           ##### Multibuffer functions #####
70   ==============================================================================
71   [..]
72     This section provides functions allowing to configure the multibuffer mode and start read and write
73     multibuffer mode for MMC HAL driver.
74 
75 @endverbatim
76   * @{
77   */
78 
79 /**
80   * @brief  Configure DMA Dual Buffer mode. The Data transfer is managed by an Internal DMA.
81   * @param  hmmc: MMC handle
82   * @param  pDataBuffer0: Pointer to the buffer0 that will contain/receive the transferred data
83   * @param  pDataBuffer1: Pointer to the buffer1 that will contain/receive the transferred data
84   * @param  BufferSize: Size of Buffer0 in Blocks. Buffer0 and Buffer1 must have the same size.
85   * @retval HAL status
86   */
HAL_MMCEx_ConfigDMAMultiBuffer(MMC_HandleTypeDef * hmmc,uint32_t * pDataBuffer0,uint32_t * pDataBuffer1,uint32_t BufferSize)87 HAL_StatusTypeDef HAL_MMCEx_ConfigDMAMultiBuffer(MMC_HandleTypeDef *hmmc, uint32_t *pDataBuffer0,
88                                                  uint32_t *pDataBuffer1, uint32_t BufferSize)
89 {
90   if (hmmc->State == HAL_MMC_STATE_READY)
91   {
92     hmmc->Instance->IDMABASE0 = (uint32_t) pDataBuffer0 ;
93     hmmc->Instance->IDMABASE1 = (uint32_t) pDataBuffer1 ;
94     hmmc->Instance->IDMABSIZE = (uint32_t)(MMC_BLOCKSIZE * BufferSize);
95 
96     return HAL_OK;
97   }
98   else
99   {
100     return HAL_BUSY;
101   }
102 }
103 
104 /**
105   * @brief  Reads block(s) from a specified address in a card. The received Data will be stored in Buffer0 and Buffer1.
106   *         Buffer0, Buffer1 and BufferSize need to be configured by function HAL_MMCEx_ConfigDMAMultiBuffer before
107   *         call this function.
108   * @param  hmmc: MMC handle
109   * @param  BlockAdd: Block Address from where data is to be read
110   * @param  NumberOfBlocks: Total number of blocks to read
111   * @retval HAL status
112   */
HAL_MMCEx_ReadBlocksDMAMultiBuffer(MMC_HandleTypeDef * hmmc,uint32_t BlockAdd,uint32_t NumberOfBlocks)113 HAL_StatusTypeDef HAL_MMCEx_ReadBlocksDMAMultiBuffer(MMC_HandleTypeDef *hmmc, uint32_t BlockAdd,
114                                                      uint32_t NumberOfBlocks)
115 {
116   SDMMC_DataInitTypeDef config;
117   uint32_t DmaBase0_reg;
118   uint32_t DmaBase1_reg;
119   uint32_t errorstate;
120   uint32_t add = BlockAdd;
121 
122   if (hmmc->State == HAL_MMC_STATE_READY)
123   {
124     if ((BlockAdd + NumberOfBlocks) > (hmmc->MmcCard.LogBlockNbr))
125     {
126       hmmc->ErrorCode |= HAL_MMC_ERROR_ADDR_OUT_OF_RANGE;
127       return HAL_ERROR;
128     }
129 
130     /* Check the case of 4kB blocks (field DATA SECTOR SIZE of extended CSD register) */
131     if (((hmmc->Ext_CSD[(MMC_EXT_CSD_DATA_SEC_SIZE_INDEX / 4)] >> MMC_EXT_CSD_DATA_SEC_SIZE_POS) & 0x000000FFU) != 0x0U)
132     {
133       if ((NumberOfBlocks % 8U) != 0U)
134       {
135         /* The number of blocks should be a multiple of 8 sectors of 512 bytes = 4 KBytes */
136         hmmc->ErrorCode |= HAL_MMC_ERROR_BLOCK_LEN_ERR;
137         return HAL_ERROR;
138       }
139 
140       if ((BlockAdd % 8U) != 0U)
141       {
142         /* The address should be aligned to 8 (corresponding to 4 KBytes blocks) */
143         hmmc->ErrorCode |= HAL_MMC_ERROR_ADDR_MISALIGNED;
144         return HAL_ERROR;
145       }
146     }
147 
148     DmaBase0_reg = hmmc->Instance->IDMABASE0;
149     DmaBase1_reg = hmmc->Instance->IDMABASE1;
150 
151     if ((hmmc->Instance->IDMABSIZE == 0U) || (DmaBase0_reg == 0U) || (DmaBase1_reg == 0U))
152     {
153       hmmc->ErrorCode = HAL_MMC_ERROR_ADDR_OUT_OF_RANGE;
154       return HAL_ERROR;
155     }
156 
157     /* Initialize data control register */
158     hmmc->Instance->DCTRL = 0;
159 
160     hmmc->ErrorCode = HAL_MMC_ERROR_NONE;
161     hmmc->State = HAL_MMC_STATE_BUSY;
162 
163     if ((hmmc->MmcCard.CardType) != MMC_HIGH_CAPACITY_CARD)
164     {
165       add *= 512U;
166     }
167 
168     /* Configure the MMC DPSM (Data Path State Machine) */
169     config.DataTimeOut   = SDMMC_DATATIMEOUT;
170     config.DataLength    = MMC_BLOCKSIZE * NumberOfBlocks;
171     config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B;
172     config.TransferDir   = SDMMC_TRANSFER_DIR_TO_SDMMC;
173     config.TransferMode  = SDMMC_TRANSFER_MODE_BLOCK;
174     config.DPSM          = SDMMC_DPSM_DISABLE;
175     (void)SDMMC_ConfigData(hmmc->Instance, &config);
176 
177     hmmc->Instance->DCTRL |= SDMMC_DCTRL_FIFORST;
178 
179     __SDMMC_CMDTRANS_ENABLE(hmmc->Instance);
180 
181     hmmc->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0;
182 
183     /* Read Blocks in DMA mode */
184     hmmc->Context = (MMC_CONTEXT_READ_MULTIPLE_BLOCK | MMC_CONTEXT_DMA);
185 
186     /* Read Multi Block command */
187     errorstate = SDMMC_CmdReadMultiBlock(hmmc->Instance, add);
188     if (errorstate != HAL_MMC_ERROR_NONE)
189     {
190       hmmc->State = HAL_MMC_STATE_READY;
191       hmmc->ErrorCode |= errorstate;
192       return HAL_ERROR;
193     }
194 
195     __HAL_MMC_ENABLE_IT(hmmc, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_RXOVERR | SDMMC_IT_DATAEND |
196                                SDMMC_FLAG_IDMATE | SDMMC_FLAG_IDMABTC));
197 
198     return HAL_OK;
199   }
200   else
201   {
202     return HAL_BUSY;
203   }
204 
205 }
206 
207 /**
208   * @brief  Write block(s) to a specified address in a card. The transferred Data are stored in Buffer0 and Buffer1.
209   *         Buffer0, Buffer1 and BufferSize need to be configured by function HAL_MMCEx_ConfigDMAMultiBuffer before
210   *         call this function.
211   * @param  hmmc: MMC handle
212   * @param  BlockAdd: Block Address from where data is to be read
213   * @param  NumberOfBlocks: Total number of blocks to read
214   * @retval HAL status
215   */
HAL_MMCEx_WriteBlocksDMAMultiBuffer(MMC_HandleTypeDef * hmmc,uint32_t BlockAdd,uint32_t NumberOfBlocks)216 HAL_StatusTypeDef HAL_MMCEx_WriteBlocksDMAMultiBuffer(MMC_HandleTypeDef *hmmc, uint32_t BlockAdd,
217                                                       uint32_t NumberOfBlocks)
218 {
219   SDMMC_DataInitTypeDef config;
220   uint32_t errorstate;
221   uint32_t DmaBase0_reg;
222   uint32_t DmaBase1_reg;
223   uint32_t add = BlockAdd;
224 
225   if (hmmc->State == HAL_MMC_STATE_READY)
226   {
227     if ((BlockAdd + NumberOfBlocks) > (hmmc->MmcCard.LogBlockNbr))
228     {
229       hmmc->ErrorCode |= HAL_MMC_ERROR_ADDR_OUT_OF_RANGE;
230       return HAL_ERROR;
231     }
232 
233     /* Check the case of 4kB blocks (field DATA SECTOR SIZE of extended CSD register) */
234     if (((hmmc->Ext_CSD[(MMC_EXT_CSD_DATA_SEC_SIZE_INDEX / 4)] >> MMC_EXT_CSD_DATA_SEC_SIZE_POS) & 0x000000FFU) != 0x0U)
235     {
236       if ((NumberOfBlocks % 8U) != 0U)
237       {
238         /* The number of blocks should be a multiple of 8 sectors of 512 bytes = 4 KBytes */
239         hmmc->ErrorCode |= HAL_MMC_ERROR_BLOCK_LEN_ERR;
240         return HAL_ERROR;
241       }
242 
243       if ((BlockAdd % 8U) != 0U)
244       {
245         /* The address should be aligned to 8 (corresponding to 4 KBytes blocks) */
246         hmmc->ErrorCode |= HAL_MMC_ERROR_ADDR_MISALIGNED;
247         return HAL_ERROR;
248       }
249     }
250 
251     DmaBase0_reg = hmmc->Instance->IDMABASE0;
252     DmaBase1_reg = hmmc->Instance->IDMABASE1;
253 
254     if ((hmmc->Instance->IDMABSIZE == 0U) || (DmaBase0_reg == 0U) || (DmaBase1_reg == 0U))
255     {
256       hmmc->ErrorCode = HAL_MMC_ERROR_ADDR_OUT_OF_RANGE;
257       return HAL_ERROR;
258     }
259 
260     /* Initialize data control register */
261     hmmc->Instance->DCTRL = 0;
262 
263     hmmc->ErrorCode = HAL_MMC_ERROR_NONE;
264 
265     hmmc->State = HAL_MMC_STATE_BUSY;
266 
267     if ((hmmc->MmcCard.CardType) != MMC_HIGH_CAPACITY_CARD)
268     {
269       add *= 512U;
270     }
271 
272     /* Configure the MMC DPSM (Data Path State Machine) */
273     config.DataTimeOut   = SDMMC_DATATIMEOUT;
274     config.DataLength    = MMC_BLOCKSIZE * NumberOfBlocks;
275     config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B;
276     config.TransferDir   = SDMMC_TRANSFER_DIR_TO_CARD;
277     config.TransferMode  = SDMMC_TRANSFER_MODE_BLOCK;
278     config.DPSM          = SDMMC_DPSM_DISABLE;
279     (void)SDMMC_ConfigData(hmmc->Instance, &config);
280 
281     __SDMMC_CMDTRANS_ENABLE(hmmc->Instance);
282 
283     hmmc->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0;
284 
285     /* Write Blocks in DMA mode */
286     hmmc->Context = (MMC_CONTEXT_WRITE_MULTIPLE_BLOCK | MMC_CONTEXT_DMA);
287 
288     /* Write Multi Block command */
289     errorstate = SDMMC_CmdWriteMultiBlock(hmmc->Instance, add);
290     if (errorstate != HAL_MMC_ERROR_NONE)
291     {
292       hmmc->State = HAL_MMC_STATE_READY;
293       hmmc->ErrorCode |= errorstate;
294       return HAL_ERROR;
295     }
296 
297     __HAL_MMC_ENABLE_IT(hmmc, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_TXUNDERR | SDMMC_IT_DATAEND |
298                                SDMMC_FLAG_IDMATE | SDMMC_FLAG_IDMABTC));
299 
300     return HAL_OK;
301   }
302   else
303   {
304     return HAL_BUSY;
305   }
306 }
307 
308 
309 /**
310   * @brief  Change the DMA Buffer0 or Buffer1 address on the fly.
311   * @param  hmmc:           pointer to a MMC_HandleTypeDef structure.
312   * @param  Buffer:        the buffer to be changed, This parameter can be one of
313   *                        the following values: MMC_DMA_BUFFER0 or MMC_DMA_BUFFER1
314   * @param  pDataBuffer:   The new address
315   * @note   The BUFFER0 address can be changed only when the current transfer use
316   *         BUFFER1 and the BUFFER1 address can be changed only when the current
317   *         transfer use BUFFER0.
318   * @retval HAL status
319   */
HAL_MMCEx_ChangeDMABuffer(MMC_HandleTypeDef * hmmc,HAL_MMCEx_DMABuffer_MemoryTypeDef Buffer,uint32_t * pDataBuffer)320 HAL_StatusTypeDef HAL_MMCEx_ChangeDMABuffer(MMC_HandleTypeDef *hmmc, HAL_MMCEx_DMABuffer_MemoryTypeDef Buffer,
321                                             uint32_t *pDataBuffer)
322 {
323   if (Buffer == MMC_DMA_BUFFER0)
324   {
325     /* change the buffer0 address */
326     hmmc->Instance->IDMABASE0 = (uint32_t)pDataBuffer;
327   }
328   else
329   {
330     /* change the memory1 address */
331     hmmc->Instance->IDMABASE1 = (uint32_t)pDataBuffer;
332   }
333 
334   return HAL_OK;
335 }
336 
337 
338 /**
339   * @}
340   */
341 
342 /**
343   * @}
344   */
345 
346 #endif /* HAL_MMC_MODULE_ENABLED */
347 #endif /* SDMMC1 || SDMMC2 */
348 
349 /**
350   * @}
351   */
352 
353 /**
354   * @}
355   */
356