Skip to content

Commit 3698507

Browse files
JordanYatescfriedt
authored andcommitted
disk: sdmmc: support L4 series with shared DMA channel
Update the driver to support DMA operations on L4 series devices, with a shared DMA channel. Split channels do not work on these chips, since there is no dedicated TX and RX channels on the DMA, so configuring two channels with SDMMC as the peripheral results in a non-functional configuration. Fixes zephyrproject-rtos#91216. Signed-off-by: Jordan Yates <[email protected]>
1 parent 792b7e3 commit 3698507

File tree

2 files changed

+93
-11
lines changed

2 files changed

+93
-11
lines changed

drivers/disk/sdmmc_stm32.c

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ LOG_MODULE_REGISTER(stm32_sdmmc, CONFIG_SDMMC_LOG_LEVEL);
2626
#include <zephyr/drivers/dma.h>
2727
#include <zephyr/drivers/dma/dma_stm32.h>
2828
#include <stm32_ll_dma.h>
29+
/* Uses a shared DMA channel for read & write */
30+
#define STM32_SDMMC_USE_DMA_SHARED DT_INST_DMAS_HAS_NAME(0, txrx)
2931
#endif
3032

3133
#ifndef MMC_TypeDef
@@ -87,11 +89,16 @@ struct stm32_sdmmc_priv {
8789
const struct reset_dt_spec reset;
8890

8991
#if STM32_SDMMC_USE_DMA
92+
#if STM32_SDMMC_USE_DMA_SHARED
93+
struct sdmmc_dma_stream dma_txrx;
94+
DMA_HandleTypeDef dma_txrx_handle;
95+
#else
9096
struct sdmmc_dma_stream dma_rx;
9197
struct sdmmc_dma_stream dma_tx;
9298
DMA_HandleTypeDef dma_tx_handle;
9399
DMA_HandleTypeDef dma_rx_handle;
94100
#endif
101+
#endif /* STM32_SDMMC_USE_DMA */
95102
};
96103

97104
#ifdef CONFIG_SDMMC_STM32_HWFC
@@ -208,24 +215,45 @@ static int stm32_sdmmc_configure_dma(DMA_HandleTypeDef *handle, struct sdmmc_dma
208215

209216
dma->cfg.user_data = handle;
210217

218+
/* Reserve the channel in the DMA subsystem, even though we use the HAL API.
219+
* See the usage of STM32_DMA_HAL_OVERRIDE.
220+
*/
211221
ret = dma_config(dma->dev, dma->channel, &dma->cfg);
212222
if (ret != 0) {
213223
LOG_ERR("Failed to conig");
214224
return ret;
215225
}
216226

227+
#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_dma_v1)
217228
handle->Instance = __LL_DMA_GET_STREAM_INSTANCE(dma->reg, dma->channel_nb);
218229
handle->Init.Channel = dma->cfg.dma_slot * DMA_CHANNEL_1;
219230
handle->Init.PeriphInc = DMA_PINC_DISABLE;
220231
handle->Init.MemInc = DMA_MINC_ENABLE;
221232
handle->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
222233
handle->Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
223234
handle->Init.Mode = DMA_PFCTRL;
224-
handle->Init.Priority = table_priority[dma->cfg.channel_priority],
235+
handle->Init.Priority = table_priority[dma->cfg.channel_priority];
225236
handle->Init.FIFOMode = DMA_FIFOMODE_ENABLE;
226237
handle->Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
227238
handle->Init.MemBurst = DMA_MBURST_INC4;
228239
handle->Init.PeriphBurst = DMA_PBURST_INC4;
240+
#else
241+
uint32_t channel_id = dma->channel_nb - STM32_DMA_STREAM_OFFSET;
242+
243+
BUILD_ASSERT(STM32_SDMMC_USE_DMA_SHARED == 1, "Only txrx is supported on this family");
244+
/* handle->Init.Direction is not initialised here on purpose.
245+
* Since the channel is reused for both directions, the direction is
246+
* configured before each read/write call.
247+
*/
248+
handle->Instance = __LL_DMA_GET_CHANNEL_INSTANCE(dma->reg, channel_id);
249+
handle->Init.Request = dma->cfg.dma_slot;
250+
handle->Init.PeriphInc = DMA_PINC_DISABLE;
251+
handle->Init.MemInc = DMA_MINC_ENABLE;
252+
handle->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
253+
handle->Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
254+
handle->Init.Mode = DMA_NORMAL;
255+
handle->Init.Priority = table_priority[dma->cfg.channel_priority];
256+
#endif
229257

230258
return ret;
231259
}
@@ -236,6 +264,15 @@ static int stm32_sdmmc_dma_init(struct stm32_sdmmc_priv *priv)
236264

237265
LOG_DBG("using dma");
238266

267+
#if STM32_SDMMC_USE_DMA_SHARED
268+
err = stm32_sdmmc_configure_dma(&priv->dma_txrx_handle, &priv->dma_txrx);
269+
if (err) {
270+
LOG_ERR("failed to init shared DMA");
271+
return err;
272+
}
273+
__HAL_LINKDMA(&priv->hsd, hdmatx, priv->dma_txrx_handle);
274+
__HAL_LINKDMA(&priv->hsd, hdmarx, priv->dma_txrx_handle);
275+
#else
239276
err = stm32_sdmmc_configure_dma(&priv->dma_tx_handle, &priv->dma_tx);
240277
if (err) {
241278
LOG_ERR("failed to init tx dma");
@@ -251,29 +288,39 @@ static int stm32_sdmmc_dma_init(struct stm32_sdmmc_priv *priv)
251288
}
252289
__HAL_LINKDMA(&priv->hsd, hdmarx, priv->dma_rx_handle);
253290
HAL_DMA_Init(&priv->dma_rx_handle);
291+
#endif /* STM32_SDMMC_USE_DMA_SHARED */
254292

255293
return err;
256294
}
257295

258296
static int stm32_sdmmc_dma_deinit(struct stm32_sdmmc_priv *priv)
259297
{
260298
int ret;
299+
300+
/* Since we use STM32_DMA_HAL_OVERRIDE, the only purpose of dma_stop
301+
* is to notify the DMA subsystem that the channel is no longer in use.
302+
* Calling this before or after HAL_DMA_DeInit makes no difference.
303+
* There is no possibility of runtime failures apart from providing an
304+
* invalid channel ID, which is already validated by the setup.
305+
*/
306+
#if STM32_SDMMC_USE_DMA_SHARED
307+
struct sdmmc_dma_stream *dma_txrx = &priv->dma_txrx;
308+
309+
ret = dma_stop(dma_txrx->dev, dma_txrx->channel);
310+
__ASSERT(ret == 0, "Shared DMA channel index corrupted");
311+
#else
261312
struct sdmmc_dma_stream *dma_tx = &priv->dma_tx;
262313
struct sdmmc_dma_stream *dma_rx = &priv->dma_rx;
263314

264315
ret = dma_stop(dma_tx->dev, dma_tx->channel);
316+
__ASSERT(ret == 0, "TX DMA channel index corrupted");
265317
HAL_DMA_DeInit(&priv->dma_tx_handle);
266-
if (ret != 0) {
267-
LOG_ERR("Failed to stop tx DMA transmission");
268-
return ret;
269-
}
318+
270319
ret = dma_stop(dma_rx->dev, dma_rx->channel);
320+
__ASSERT(ret == 0, "RX DMA channel index corrupted");
271321
HAL_DMA_DeInit(&priv->dma_rx_handle);
272-
if (ret != 0) {
273-
LOG_ERR("Failed to stop rx DMA transmission");
274-
return ret;
275-
}
276-
return ret;
322+
#endif
323+
return 0;
277324
}
278325

279326
#endif
@@ -401,6 +448,15 @@ static int stm32_sdmmc_access_read(struct disk_info *disk, uint8_t *data_buf,
401448

402449
k_sem_take(&priv->thread_lock, K_FOREVER);
403450

451+
#if STM32_SDMMC_USE_DMA_SHARED
452+
/* Initialise the shared DMA channel for the current direction */
453+
priv->dma_txrx_handle.Init.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
454+
if (HAL_DMA_Init(&priv->dma_txrx_handle) != HAL_OK) {
455+
err = -EIO;
456+
goto end;
457+
}
458+
#endif
459+
404460
err = stm32_sdmmc_read_blocks(&priv->hsd, data_buf, start_sector, num_sector);
405461
if (err != HAL_OK) {
406462
LOG_ERR("sd read block failed %d", err);
@@ -410,6 +466,10 @@ static int stm32_sdmmc_access_read(struct disk_info *disk, uint8_t *data_buf,
410466

411467
k_sem_take(&priv->sync, K_FOREVER);
412468

469+
#if STM32_SDMMC_USE_DMA_SHARED
470+
HAL_DMA_DeInit(&priv->dma_txrx_handle);
471+
#endif
472+
413473
if (priv->status != DISK_STATUS_OK) {
414474
LOG_ERR("sd read error %d", priv->status);
415475
err = -EIO;
@@ -457,6 +517,15 @@ static int stm32_sdmmc_access_write(struct disk_info *disk,
457517

458518
k_sem_take(&priv->thread_lock, K_FOREVER);
459519

520+
#if STM32_SDMMC_USE_DMA_SHARED
521+
/* Initialise the shared DMA channel for the current direction */
522+
priv->dma_txrx_handle.Init.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
523+
if (HAL_DMA_Init(&priv->dma_txrx_handle) != HAL_OK) {
524+
err = -EIO;
525+
goto end;
526+
}
527+
#endif
528+
460529
err = stm32_sdmmc_write_blocks(&priv->hsd, (uint8_t *)data_buf, start_sector, num_sector);
461530
if (err != HAL_OK) {
462531
LOG_ERR("sd write block failed %d", err);
@@ -466,6 +535,10 @@ static int stm32_sdmmc_access_write(struct disk_info *disk,
466535

467536
k_sem_take(&priv->sync, K_FOREVER);
468537

538+
#if STM32_SDMMC_USE_DMA_SHARED
539+
HAL_DMA_DeInit(&priv->dma_txrx_handle);
540+
#endif
541+
469542
if (priv->status != DISK_STATUS_OK) {
470543
LOG_ERR("sd write error %d", priv->status);
471544
err = -EIO;
@@ -813,8 +886,12 @@ static struct stm32_sdmmc_priv stm32_sdmmc_priv_1 = {
813886
.pclken = pclken_sdmmc,
814887
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
815888
.reset = RESET_DT_SPEC_INST_GET(0),
889+
#if STM32_SDMMC_USE_DMA_SHARED
890+
SDMMC_DMA_CHANNEL(txrx, TXRX)
891+
#else
816892
SDMMC_DMA_CHANNEL(rx, RX)
817893
SDMMC_DMA_CHANNEL(tx, TX)
894+
#endif
818895
};
819896

820897
DEVICE_DT_INST_DEFINE(0, disk_stm32_sdmmc_init, NULL,

dts/bindings/mmc/st,stm32-sdmmc.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,15 @@ properties:
7070
7171
For example dmas for TX/RX on SDMMC
7272
dmas = <&dma2 4 6 0x30000 0x00>, <&dma2 4 3 0x30000 0x00>;
73+
Alternatively, for a shared TX/RX DMA channels on a STM32L4
74+
dmas = <&dma2 5 7 STM32_DMA_PRIORITY_HIGH>;
7375
7476
dma-names:
7577
description: |
76-
DMA channel name. If DMA should be used, expected value is "tx", "rx".
78+
DMA channel name. If DMA should be used, expected value is either "tx", "rx"
79+
or a single "txrx" for the shared channel configuration
7780
7881
For example
7982
dma-names = "tx", "rx";
83+
or
84+
dma-names = "txrx";

0 commit comments

Comments
 (0)