summaryrefslogtreecommitdiff
path: root/drivers/dma/imx-sdma.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma/imx-sdma.c')
-rw-r--r--drivers/dma/imx-sdma.c108
1 files changed, 87 insertions, 21 deletions
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 70c0aa931ddf..8535018ee7a2 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -14,6 +14,7 @@
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
@@ -35,7 +36,7 @@
#include <linux/workqueue.h>
#include <asm/irq.h>
-#include <linux/platform_data/dma-imx.h>
+#include <linux/dma/imx-dma.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
@@ -73,6 +74,7 @@
#define SDMA_CHNENBL0_IMX35 0x200
#define SDMA_CHNENBL0_IMX31 0x080
#define SDMA_CHNPRI_0 0x100
+#define SDMA_DONE0_CONFIG 0x1000
/*
* Buffer descriptor status values.
@@ -180,6 +182,12 @@
BIT(DMA_MEM_TO_DEV) | \
BIT(DMA_DEV_TO_DEV))
+#define SDMA_WATERMARK_LEVEL_N_FIFOS GENMASK(15, 12)
+#define SDMA_WATERMARK_LEVEL_SW_DONE BIT(23)
+
+#define SDMA_DONE0_CONFIG_DONE_SEL BIT(7)
+#define SDMA_DONE0_CONFIG_DONE_DIS BIT(6)
+
/**
* struct sdma_script_start_addrs - SDMA script start pointers
*
@@ -198,12 +206,12 @@ struct sdma_script_start_addrs {
s32 per_2_firi_addr;
s32 mcu_2_firi_addr;
s32 uart_2_per_addr;
- s32 uart_2_mcu_ram_addr;
+ s32 uart_2_mcu_addr;
s32 per_2_app_addr;
s32 mcu_2_app_addr;
s32 per_2_per_addr;
s32 uartsh_2_per_addr;
- s32 uartsh_2_mcu_ram_addr;
+ s32 uartsh_2_mcu_addr;
s32 per_2_shp_addr;
s32 mcu_2_shp_addr;
s32 ata_2_mcu_addr;
@@ -232,8 +240,8 @@ struct sdma_script_start_addrs {
s32 mcu_2_ecspi_addr;
s32 mcu_2_sai_addr;
s32 sai_2_mcu_addr;
- s32 uart_2_mcu_addr;
- s32 uartsh_2_mcu_addr;
+ s32 uart_2_mcu_rom_addr;
+ s32 uartsh_2_mcu_rom_addr;
/* End of v3 array */
s32 mcu_2_zqspi_addr;
/* End of v4 array */
@@ -441,6 +449,9 @@ struct sdma_channel {
struct work_struct terminate_worker;
struct list_head terminated;
bool is_ram_script;
+ unsigned int n_fifos_src;
+ unsigned int n_fifos_dst;
+ bool sw_done;
};
#define IMX_DMA_SG_LOOP BIT(0)
@@ -778,6 +789,14 @@ static void sdma_event_enable(struct sdma_channel *sdmac, unsigned int event)
val = readl_relaxed(sdma->regs + chnenbl);
__set_bit(channel, &val);
writel_relaxed(val, sdma->regs + chnenbl);
+
+ /* Set SDMA_DONEx_CONFIG is sw_done enabled */
+ if (sdmac->sw_done) {
+ val = readl_relaxed(sdma->regs + SDMA_DONE0_CONFIG);
+ val |= SDMA_DONE0_CONFIG_DONE_SEL;
+ val &= ~SDMA_DONE0_CONFIG_DONE_DIS;
+ writel_relaxed(val, sdma->regs + SDMA_DONE0_CONFIG);
+ }
}
static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event)
@@ -940,7 +959,7 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id)
/*
* sets the pc of SDMA script according to the peripheral type
*/
-static void sdma_get_pc(struct sdma_channel *sdmac,
+static int sdma_get_pc(struct sdma_channel *sdmac,
enum sdma_peripheral_type peripheral_type)
{
struct sdma_engine *sdma = sdmac->sdma;
@@ -1038,14 +1057,22 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
case IMX_DMATYPE_IPU_MEMORY:
emi_2_per = sdma->script_addrs->ext_mem_2_ipu_addr;
break;
- default:
+ case IMX_DMATYPE_MULTI_SAI:
+ per_2_emi = sdma->script_addrs->sai_2_mcu_addr;
+ emi_2_per = sdma->script_addrs->mcu_2_sai_addr;
break;
+ default:
+ dev_err(sdma->dev, "Unsupported transfer type %d\n",
+ peripheral_type);
+ return -EINVAL;
}
sdmac->pc_from_device = per_2_emi;
sdmac->pc_to_device = emi_2_per;
sdmac->device_to_device = per_2_per;
sdmac->pc_to_pc = emi_2_emi;
+
+ return 0;
}
static int sdma_load_context(struct sdma_channel *sdmac)
@@ -1210,9 +1237,26 @@ static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac)
sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_CONT;
}
+static void sdma_set_watermarklevel_for_sais(struct sdma_channel *sdmac)
+{
+ unsigned int n_fifos;
+
+ if (sdmac->sw_done)
+ sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_SW_DONE;
+
+ if (sdmac->direction == DMA_DEV_TO_MEM)
+ n_fifos = sdmac->n_fifos_src;
+ else
+ n_fifos = sdmac->n_fifos_dst;
+
+ sdmac->watermark_level |=
+ FIELD_PREP(SDMA_WATERMARK_LEVEL_N_FIFOS, n_fifos);
+}
+
static int sdma_config_channel(struct dma_chan *chan)
{
struct sdma_channel *sdmac = to_sdma_chan(chan);
+ int ret;
sdma_disable_channel(chan);
@@ -1233,7 +1277,9 @@ static int sdma_config_channel(struct dma_chan *chan)
break;
}
- sdma_get_pc(sdmac, sdmac->peripheral_type);
+ ret = sdma_get_pc(sdmac, sdmac->peripheral_type);
+ if (ret)
+ return ret;
if ((sdmac->peripheral_type != IMX_DMATYPE_MEMORY) &&
(sdmac->peripheral_type != IMX_DMATYPE_DSP)) {
@@ -1243,6 +1289,10 @@ static int sdma_config_channel(struct dma_chan *chan)
sdmac->peripheral_type == IMX_DMATYPE_ASRC)
sdma_set_watermarklevel_for_p2p(sdmac);
} else {
+ if (sdmac->peripheral_type ==
+ IMX_DMATYPE_MULTI_SAI)
+ sdma_set_watermarklevel_for_sais(sdmac);
+
__set_bit(sdmac->event_id0, sdmac->event_mask);
}
@@ -1349,7 +1399,9 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan)
mem_data.dma_request2 = 0;
data = &mem_data;
- sdma_get_pc(sdmac, IMX_DMATYPE_MEMORY);
+ ret = sdma_get_pc(sdmac, IMX_DMATYPE_MEMORY);
+ if (ret)
+ return ret;
}
switch (data->priority) {
@@ -1698,9 +1750,23 @@ static int sdma_config(struct dma_chan *chan,
struct dma_slave_config *dmaengine_cfg)
{
struct sdma_channel *sdmac = to_sdma_chan(chan);
+ struct sdma_engine *sdma = sdmac->sdma;
memcpy(&sdmac->slave_config, dmaengine_cfg, sizeof(*dmaengine_cfg));
+ if (dmaengine_cfg->peripheral_config) {
+ struct sdma_peripheral_config *sdmacfg = dmaengine_cfg->peripheral_config;
+ if (dmaengine_cfg->peripheral_size != sizeof(struct sdma_peripheral_config)) {
+ dev_err(sdma->dev, "Invalid peripheral size %zu, expected %zu\n",
+ dmaengine_cfg->peripheral_size,
+ sizeof(struct sdma_peripheral_config));
+ return -EINVAL;
+ }
+ sdmac->n_fifos_src = sdmacfg->n_fifos_src;
+ sdmac->n_fifos_dst = sdmacfg->n_fifos_dst;
+ sdmac->sw_done = sdmacfg->sw_done;
+ }
+
/* Set ENBLn earlier to make sure dma request triggered after that */
if (sdmac->event_id0 >= sdmac->sdma->drvdata->num_events)
return -EINVAL;
@@ -1796,17 +1862,17 @@ static void sdma_add_scripts(struct sdma_engine *sdma,
saddr_arr[i] = addr_arr[i];
/*
- * get uart_2_mcu_addr/uartsh_2_mcu_addr rom script specially because
- * they are now replaced by uart_2_mcu_ram_addr/uartsh_2_mcu_ram_addr
- * to be compatible with legacy freescale/nxp sdma firmware, and they
- * are located in the bottom part of sdma_script_start_addrs which are
- * beyond the SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1.
+ * For compatibility with NXP internal legacy kernel before 4.19 which
+ * is based on uart ram script and mainline kernel based on uart rom
+ * script, both uart ram/rom scripts are present in newer sdma
+ * firmware. Use the rom versions if they are present (V3 or newer).
*/
- if (addr->uart_2_mcu_addr)
- sdma->script_addrs->uart_2_mcu_addr = addr->uart_2_mcu_addr;
- if (addr->uartsh_2_mcu_addr)
- sdma->script_addrs->uartsh_2_mcu_addr = addr->uartsh_2_mcu_addr;
-
+ if (sdma->script_number >= SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3) {
+ if (addr->uart_2_mcu_rom_addr)
+ sdma->script_addrs->uart_2_mcu_addr = addr->uart_2_mcu_rom_addr;
+ if (addr->uartsh_2_mcu_rom_addr)
+ sdma->script_addrs->uartsh_2_mcu_addr = addr->uartsh_2_mcu_rom_addr;
+ }
}
static void sdma_load_firmware(const struct firmware *fw, void *context)
@@ -1885,7 +1951,7 @@ static int sdma_event_remap(struct sdma_engine *sdma)
u32 reg, val, shift, num_map, i;
int ret = 0;
- if (IS_ERR(np) || IS_ERR(gpr_np))
+ if (IS_ERR(np) || !gpr_np)
goto out;
event_remap = of_find_property(np, propname, NULL);
@@ -1933,7 +1999,7 @@ static int sdma_event_remap(struct sdma_engine *sdma)
}
out:
- if (!IS_ERR(gpr_np))
+ if (gpr_np)
of_node_put(gpr_np);
return ret;