216 lines
5.6 KiB
C
216 lines
5.6 KiB
C
/*
|
|
* Copyright (c) 2020 STMicroelectronics
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @brief Common part of DMAMUX drivers for stm32.
|
|
* @note api functions named dmamux_stm32_
|
|
* are calling the dma_stm32 corresponding function
|
|
* implemented in dma_stm32.c
|
|
*/
|
|
|
|
#include <soc.h>
|
|
#include <init.h>
|
|
#include <drivers/dma.h>
|
|
#include <drivers/clock_control.h>
|
|
#include <drivers/clock_control/stm32_clock_control.h>
|
|
|
|
#include "dmamux_stm32.h"
|
|
#include "dma_stm32.h"
|
|
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(dmamux_stm32, CONFIG_DMA_LOG_LEVEL);
|
|
|
|
#define DT_DRV_COMPAT st_stm32_dmamux
|
|
|
|
int dmamux_stm32_configure(struct device *dev, uint32_t id,
|
|
struct dma_config *config)
|
|
{
|
|
/* device is the dmamux, id is the dmamux channel from 0 */
|
|
struct dmamux_stm32_data *data = dev->driver_data;
|
|
const struct dmamux_stm32_config *dev_config =
|
|
dev->config_info;
|
|
|
|
/*
|
|
* request line ID for this mux channel is stored
|
|
* in the dma_slot parameter
|
|
*/
|
|
int request_id = config->dma_slot;
|
|
|
|
if (request_id >= dev_config->req_nb + dev_config->gen_nb) {
|
|
LOG_ERR("request ID %d is too big.", request_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* check if this channel is valid */
|
|
if (id >= dev_config->channel_nb) {
|
|
LOG_ERR("channel ID %d is too big.", id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Also configures the corresponding dma channel
|
|
* instance is given by the dev_dma
|
|
* stream is given by the index i
|
|
* config is directly this dma_config
|
|
*/
|
|
|
|
/*
|
|
* This dmamux channel 'id' is now used for this peripheral request
|
|
* It gives this mux request ID to the dma through the config.dma_slot
|
|
*/
|
|
if (dma_stm32_configure(data->mux_channels[id].dev_dma,
|
|
data->mux_channels[id].dma_id, config) != 0) {
|
|
LOG_ERR("cannot configure the dmamux.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* set the Request Line ID to this dmamux channel i */
|
|
DMAMUX_Channel_TypeDef *dmamux =
|
|
(DMAMUX_Channel_TypeDef *)dev_config->base;
|
|
|
|
LL_DMAMUX_SetRequestID(dmamux, id, request_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dmamux_stm32_start(struct device *dev, uint32_t id)
|
|
{
|
|
const struct dmamux_stm32_config *dev_config =
|
|
dev->config_info;
|
|
struct dmamux_stm32_data *data = dev->driver_data;
|
|
|
|
/* check if this channel is valid */
|
|
if (id >= dev_config->channel_nb) {
|
|
LOG_ERR("channel ID %d is too big.", id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (dma_stm32_start(data->mux_channels[id].dev_dma,
|
|
data->mux_channels[id].dma_id) != 0) {
|
|
LOG_ERR("cannot start the dmamux channel %d.", id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dmamux_stm32_stop(struct device *dev, uint32_t id)
|
|
{
|
|
const struct dmamux_stm32_config *dev_config =
|
|
dev->config_info;
|
|
struct dmamux_stm32_data *data = dev->driver_data;
|
|
|
|
/* check if this channel is valid */
|
|
if (id >= dev_config->channel_nb) {
|
|
LOG_ERR("channel ID %d is too big.", id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (dma_stm32_stop(data->mux_channels[id].dev_dma,
|
|
data->mux_channels[id].dma_id) != 0) {
|
|
LOG_ERR("cannot stop the dmamux channel %d.", id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dmamux_stm32_reload(struct device *dev, uint32_t id,
|
|
uint32_t src, uint32_t dst, size_t size)
|
|
{
|
|
const struct dmamux_stm32_config *dev_config =
|
|
dev->config_info;
|
|
struct dmamux_stm32_data *data = dev->driver_data;
|
|
|
|
/* check if this channel is valid */
|
|
if (id >= dev_config->channel_nb) {
|
|
LOG_ERR("channel ID %d is too big.", id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (dma_stm32_reload(data->mux_channels[id].dev_dma,
|
|
data->mux_channels[id].dma_id,
|
|
src, dst, size) != 0) {
|
|
LOG_ERR("cannot reload the dmamux channel %d.", id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dmamux_stm32_init(struct device *dev)
|
|
{
|
|
struct dmamux_stm32_data *data = dev->driver_data;
|
|
const struct dmamux_stm32_config *config =
|
|
dev->config_info;
|
|
struct device *clk =
|
|
device_get_binding(STM32_CLOCK_CONTROL_NAME);
|
|
|
|
if (clock_control_on(clk,
|
|
(clock_control_subsys_t *) &config->pclken) != 0) {
|
|
LOG_ERR("clock op failed\n");
|
|
return -EIO;
|
|
}
|
|
|
|
int size_stream =
|
|
sizeof(struct dmamux_stm32_channel) * config->channel_nb;
|
|
data->mux_channels = k_malloc(size_stream);
|
|
if (!data->mux_channels) {
|
|
LOG_ERR("HEAP_MEM_POOL_SIZE is too small");
|
|
return -ENOMEM;
|
|
}
|
|
for (int i = 0; i < config->channel_nb; i++) {
|
|
/*
|
|
* associates the dmamux channel
|
|
* to the corresponding dma stream
|
|
*/
|
|
if (i < config->channel_nb / 2) {
|
|
data->mux_channels[i].dev_dma =
|
|
device_get_binding((const char *)"DMA_1");
|
|
/* dma 1 channels from 1 to N */
|
|
data->mux_channels[i].dma_id = i + 1;
|
|
} else {
|
|
data->mux_channels[i].dev_dma =
|
|
device_get_binding((const char *)"DMA_2");
|
|
data->mux_channels[i].dma_id =
|
|
/* dma 2 channels from 1 to N */
|
|
i - config->channel_nb / 2 + 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dma_driver_api dma_funcs = {
|
|
.reload = dmamux_stm32_reload,
|
|
.config = dmamux_stm32_configure,
|
|
.start = dmamux_stm32_start,
|
|
.stop = dmamux_stm32_stop,
|
|
};
|
|
|
|
#define DMAMUX_INIT(index) \
|
|
\
|
|
const struct dmamux_stm32_config dmamux_stm32_config_##index = {\
|
|
.pclken = { .bus = DT_INST_CLOCKS_CELL(index, bus), \
|
|
.enr = DT_INST_CLOCKS_CELL(index, bits) }, \
|
|
.base = DT_INST_REG_ADDR(index), \
|
|
.channel_nb = DT_INST_PROP(index, dma_channels), \
|
|
.gen_nb = DT_INST_PROP(index, dma_generators), \
|
|
.req_nb = DT_INST_PROP(index, dma_requests), \
|
|
}; \
|
|
\
|
|
static struct dmamux_stm32_data dmamux_stm32_data_##index = { \
|
|
.mux_channels = NULL, \
|
|
}; \
|
|
\
|
|
DEVICE_AND_API_INIT(dmamux_##index, DT_INST_LABEL(index), \
|
|
&dmamux_stm32_init, \
|
|
&dmamux_stm32_data_##index, &dmamux_stm32_config_##index,\
|
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,\
|
|
&dma_funcs);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(DMAMUX_INIT)
|