mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-14 06:24:53 +08:00
e8ee6c8cb6
DW DMA IP-core provides a way to synthesize the DMA controller with channels having different parameters like maximum burst-length, multi-block support, maximum data width, etc. Those parameters both explicitly and implicitly affect the channels performance. Since DMA slave devices might be very demanding to the DMA performance, let's provide a functionality for the slaves to be assigned with DW DMA channels, which performance according to the platform engineer fulfill their requirements. After this patch is applied it can be done by passing the mask of suitable DMA-channels either directly in the dw_dma_slave structure instance or as a fifth cell of the DMA DT-property. If mask is zero or not provided, then there is no limitation on the channels allocation. For instance Baikal-T1 SoC is equipped with a DW DMAC engine, which first two channels are synthesized with max burst length of 16, while the rest of the channels have been created with max-burst-len=4. It would seem that the first two channels must be faster than the others and should be more preferable for the time-critical DMA slave devices. In practice it turned out that the situation is quite the opposite. The channels with max-burst-len=4 demonstrated a better performance than the channels with max-burst-len=16 even when they both had been initialized with the same settings. The performance drop of the first two DMA-channels made them unsuitable for the DW APB SSI slave device. No matter what settings they are configured with, full-duplex SPI transfers occasionally experience the Rx FIFO overflow. It means that the DMA-engine doesn't keep up with incoming data pace even though the SPI-bus is enabled with speed of 25MHz while the DW DMA controller is clocked with 50MHz signal. There is no such problem has been noticed for the channels synthesized with max-burst-len=4. Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru> Link: https://lore.kernel.org/r/20200731200826.9292-6-Sergey.Semin@baikalelectronics.ru Signed-off-by: Vinod Koul <vkoul@kernel.org>
140 lines
3.6 KiB
C
140 lines
3.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Platform driver for the Synopsys DesignWare DMA Controller
|
|
*
|
|
* Copyright (C) 2007-2008 Atmel Corporation
|
|
* Copyright (C) 2010-2011 ST Microelectronics
|
|
* Copyright (C) 2013 Intel Corporation
|
|
*/
|
|
|
|
#include <linux/of.h>
|
|
#include <linux/of_dma.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include "internal.h"
|
|
|
|
static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec,
|
|
struct of_dma *ofdma)
|
|
{
|
|
struct dw_dma *dw = ofdma->of_dma_data;
|
|
struct dw_dma_slave slave = {
|
|
.dma_dev = dw->dma.dev,
|
|
};
|
|
dma_cap_mask_t cap;
|
|
|
|
if (dma_spec->args_count < 3 || dma_spec->args_count > 4)
|
|
return NULL;
|
|
|
|
slave.src_id = dma_spec->args[0];
|
|
slave.dst_id = dma_spec->args[0];
|
|
slave.m_master = dma_spec->args[1];
|
|
slave.p_master = dma_spec->args[2];
|
|
if (dma_spec->args_count >= 4)
|
|
slave.channels = dma_spec->args[3];
|
|
|
|
if (WARN_ON(slave.src_id >= DW_DMA_MAX_NR_REQUESTS ||
|
|
slave.dst_id >= DW_DMA_MAX_NR_REQUESTS ||
|
|
slave.m_master >= dw->pdata->nr_masters ||
|
|
slave.p_master >= dw->pdata->nr_masters ||
|
|
slave.channels >= BIT(dw->pdata->nr_channels)))
|
|
return NULL;
|
|
|
|
dma_cap_zero(cap);
|
|
dma_cap_set(DMA_SLAVE, cap);
|
|
|
|
/* TODO: there should be a simpler way to do this */
|
|
return dma_request_channel(cap, dw_dma_filter, &slave);
|
|
}
|
|
|
|
struct dw_dma_platform_data *dw_dma_parse_dt(struct platform_device *pdev)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct dw_dma_platform_data *pdata;
|
|
u32 tmp, arr[DW_DMA_MAX_NR_MASTERS], mb[DW_DMA_MAX_NR_CHANNELS];
|
|
u32 nr_masters;
|
|
u32 nr_channels;
|
|
|
|
if (!np) {
|
|
dev_err(&pdev->dev, "Missing DT data\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (of_property_read_u32(np, "dma-masters", &nr_masters))
|
|
return NULL;
|
|
if (nr_masters < 1 || nr_masters > DW_DMA_MAX_NR_MASTERS)
|
|
return NULL;
|
|
|
|
if (of_property_read_u32(np, "dma-channels", &nr_channels))
|
|
return NULL;
|
|
if (nr_channels > DW_DMA_MAX_NR_CHANNELS)
|
|
return NULL;
|
|
|
|
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
|
if (!pdata)
|
|
return NULL;
|
|
|
|
pdata->nr_masters = nr_masters;
|
|
pdata->nr_channels = nr_channels;
|
|
|
|
if (!of_property_read_u32(np, "chan_allocation_order", &tmp))
|
|
pdata->chan_allocation_order = (unsigned char)tmp;
|
|
|
|
if (!of_property_read_u32(np, "chan_priority", &tmp))
|
|
pdata->chan_priority = tmp;
|
|
|
|
if (!of_property_read_u32(np, "block_size", &tmp))
|
|
pdata->block_size = tmp;
|
|
|
|
if (!of_property_read_u32_array(np, "data-width", arr, nr_masters)) {
|
|
for (tmp = 0; tmp < nr_masters; tmp++)
|
|
pdata->data_width[tmp] = arr[tmp];
|
|
} else if (!of_property_read_u32_array(np, "data_width", arr, nr_masters)) {
|
|
for (tmp = 0; tmp < nr_masters; tmp++)
|
|
pdata->data_width[tmp] = BIT(arr[tmp] & 0x07);
|
|
}
|
|
|
|
if (!of_property_read_u32_array(np, "multi-block", mb, nr_channels)) {
|
|
for (tmp = 0; tmp < nr_channels; tmp++)
|
|
pdata->multi_block[tmp] = mb[tmp];
|
|
} else {
|
|
for (tmp = 0; tmp < nr_channels; tmp++)
|
|
pdata->multi_block[tmp] = 1;
|
|
}
|
|
|
|
if (of_property_read_u32_array(np, "snps,max-burst-len", pdata->max_burst,
|
|
nr_channels)) {
|
|
memset32(pdata->max_burst, DW_DMA_MAX_BURST, nr_channels);
|
|
}
|
|
|
|
if (!of_property_read_u32(np, "snps,dma-protection-control", &tmp)) {
|
|
if (tmp > CHAN_PROTCTL_MASK)
|
|
return NULL;
|
|
pdata->protctl = tmp;
|
|
}
|
|
|
|
return pdata;
|
|
}
|
|
|
|
void dw_dma_of_controller_register(struct dw_dma *dw)
|
|
{
|
|
struct device *dev = dw->dma.dev;
|
|
int ret;
|
|
|
|
if (!dev->of_node)
|
|
return;
|
|
|
|
ret = of_dma_controller_register(dev->of_node, dw_dma_of_xlate, dw);
|
|
if (ret)
|
|
dev_err(dev, "could not register of_dma_controller\n");
|
|
}
|
|
|
|
void dw_dma_of_controller_free(struct dw_dma *dw)
|
|
{
|
|
struct device *dev = dw->dma.dev;
|
|
|
|
if (!dev->of_node)
|
|
return;
|
|
|
|
of_dma_controller_free(dev->of_node);
|
|
}
|