diff --git a/Documentation/dmaengine/provider.txt b/Documentation/dmaengine/provider.txt index e33bc1c8ed2c..bfadbfdf13ed 100644 --- a/Documentation/dmaengine/provider.txt +++ b/Documentation/dmaengine/provider.txt @@ -395,6 +395,13 @@ where to put them) when DMA_CTRL_REUSE is already set - Terminating the channel + * DMA_PREP_CMD + - If set, the client driver tells DMA controller that passed data in DMA + API is command data. + - Interpretation of command data is DMA controller specific. It can be + used for issuing commands to other peripherals/register reads/register + writes for which the descriptor should be in different format from + normal data descriptors. General Design Notes -------------------- diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c index 03c4eb3fd314..6d89fb6a6a92 100644 --- a/drivers/dma/qcom/bam_dma.c +++ b/drivers/dma/qcom/bam_dma.c @@ -65,6 +65,7 @@ struct bam_desc_hw { #define DESC_FLAG_EOT BIT(14) #define DESC_FLAG_EOB BIT(13) #define DESC_FLAG_NWD BIT(12) +#define DESC_FLAG_CMD BIT(11) struct bam_async_desc { struct virt_dma_desc vd; @@ -645,6 +646,9 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan, unsigned int curr_offset = 0; do { + if (flags & DMA_PREP_CMD) + desc->flags |= cpu_to_le16(DESC_FLAG_CMD); + desc->addr = cpu_to_le32(sg_dma_address(sg) + curr_offset); @@ -960,7 +964,7 @@ static void bam_start_dma(struct bam_chan *bchan) /* set any special flags on the last descriptor */ if (async_desc->num_desc == async_desc->xfer_len) - desc[async_desc->xfer_len - 1].flags = + desc[async_desc->xfer_len - 1].flags |= cpu_to_le16(async_desc->flags); else desc[async_desc->xfer_len - 1].flags |= diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c index 34fb6afd229b..e3669850aef4 100644 --- a/drivers/dma/qcom/hidma.c +++ b/drivers/dma/qcom/hidma.c @@ -411,7 +411,40 @@ hidma_prep_dma_memcpy(struct dma_chan *dmach, dma_addr_t dest, dma_addr_t src, return NULL; hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch, - src, dest, len, flags); + src, dest, len, flags, + HIDMA_TRE_MEMCPY); + + /* Place descriptor in prepared list */ + spin_lock_irqsave(&mchan->lock, irqflags); + list_add_tail(&mdesc->node, &mchan->prepared); + spin_unlock_irqrestore(&mchan->lock, irqflags); + + return &mdesc->desc; +} + +static struct dma_async_tx_descriptor * +hidma_prep_dma_memset(struct dma_chan *dmach, dma_addr_t dest, int value, + size_t len, unsigned long flags) +{ + struct hidma_chan *mchan = to_hidma_chan(dmach); + struct hidma_desc *mdesc = NULL; + struct hidma_dev *mdma = mchan->dmadev; + unsigned long irqflags; + + /* Get free descriptor */ + spin_lock_irqsave(&mchan->lock, irqflags); + if (!list_empty(&mchan->free)) { + mdesc = list_first_entry(&mchan->free, struct hidma_desc, node); + list_del(&mdesc->node); + } + spin_unlock_irqrestore(&mchan->lock, irqflags); + + if (!mdesc) + return NULL; + + hidma_ll_set_transfer_params(mdma->lldev, mdesc->tre_ch, + value, dest, len, flags, + HIDMA_TRE_MEMSET); /* Place descriptor in prepared list */ spin_lock_irqsave(&mchan->lock, irqflags); @@ -776,6 +809,7 @@ static int hidma_probe(struct platform_device *pdev) pm_runtime_get_sync(dmadev->ddev.dev); dma_cap_set(DMA_MEMCPY, dmadev->ddev.cap_mask); + dma_cap_set(DMA_MEMSET, dmadev->ddev.cap_mask); if (WARN_ON(!pdev->dev.dma_mask)) { rc = -ENXIO; goto dmafree; @@ -786,6 +820,7 @@ static int hidma_probe(struct platform_device *pdev) dmadev->dev_trca = trca; dmadev->trca_resource = trca_resource; dmadev->ddev.device_prep_dma_memcpy = hidma_prep_dma_memcpy; + dmadev->ddev.device_prep_dma_memset = hidma_prep_dma_memset; dmadev->ddev.device_alloc_chan_resources = hidma_alloc_chan_resources; dmadev->ddev.device_free_chan_resources = hidma_free_chan_resources; dmadev->ddev.device_tx_status = hidma_tx_status; diff --git a/drivers/dma/qcom/hidma.h b/drivers/dma/qcom/hidma.h index 41e0aa283828..5f9966e82c0b 100644 --- a/drivers/dma/qcom/hidma.h +++ b/drivers/dma/qcom/hidma.h @@ -28,6 +28,11 @@ #define HIDMA_TRE_DEST_LOW_IDX 4 #define HIDMA_TRE_DEST_HI_IDX 5 +enum tre_type { + HIDMA_TRE_MEMCPY = 3, + HIDMA_TRE_MEMSET = 4, +}; + struct hidma_tre { atomic_t allocated; /* if this channel is allocated */ bool queued; /* flag whether this is pending */ @@ -150,7 +155,7 @@ void hidma_ll_start(struct hidma_lldev *llhndl); int hidma_ll_disable(struct hidma_lldev *lldev); int hidma_ll_enable(struct hidma_lldev *llhndl); void hidma_ll_set_transfer_params(struct hidma_lldev *llhndl, u32 tre_ch, - dma_addr_t src, dma_addr_t dest, u32 len, u32 flags); + dma_addr_t src, dma_addr_t dest, u32 len, u32 flags, u32 txntype); void hidma_ll_setup_irq(struct hidma_lldev *lldev, bool msi); int hidma_ll_setup(struct hidma_lldev *lldev); struct hidma_lldev *hidma_ll_init(struct device *dev, u32 max_channels, diff --git a/drivers/dma/qcom/hidma_ll.c b/drivers/dma/qcom/hidma_ll.c index 1530a661518d..4999e266b2de 100644 --- a/drivers/dma/qcom/hidma_ll.c +++ b/drivers/dma/qcom/hidma_ll.c @@ -105,10 +105,6 @@ enum ch_state { HIDMA_CH_STOPPED = 4, }; -enum tre_type { - HIDMA_TRE_MEMCPY = 3, -}; - enum err_code { HIDMA_EVRE_STATUS_COMPLETE = 1, HIDMA_EVRE_STATUS_ERROR = 4, @@ -174,8 +170,7 @@ int hidma_ll_request(struct hidma_lldev *lldev, u32 sig, const char *dev_name, tre->err_info = 0; tre->lldev = lldev; tre_local = &tre->tre_local[0]; - tre_local[HIDMA_TRE_CFG_IDX] = HIDMA_TRE_MEMCPY; - tre_local[HIDMA_TRE_CFG_IDX] |= (lldev->chidx & 0xFF) << 8; + tre_local[HIDMA_TRE_CFG_IDX] = (lldev->chidx & 0xFF) << 8; tre_local[HIDMA_TRE_CFG_IDX] |= BIT(16); /* set IEOB */ *tre_ch = i; if (callback) @@ -607,7 +602,7 @@ int hidma_ll_disable(struct hidma_lldev *lldev) void hidma_ll_set_transfer_params(struct hidma_lldev *lldev, u32 tre_ch, dma_addr_t src, dma_addr_t dest, u32 len, - u32 flags) + u32 flags, u32 txntype) { struct hidma_tre *tre; u32 *tre_local; @@ -626,6 +621,8 @@ void hidma_ll_set_transfer_params(struct hidma_lldev *lldev, u32 tre_ch, } tre_local = &tre->tre_local[0]; + tre_local[HIDMA_TRE_CFG_IDX] &= ~GENMASK(7, 0); + tre_local[HIDMA_TRE_CFG_IDX] |= txntype; tre_local[HIDMA_TRE_LEN_IDX] = len; tre_local[HIDMA_TRE_SRC_LOW_IDX] = lower_32_bits(src); tre_local[HIDMA_TRE_SRC_HI_IDX] = upper_32_bits(src); diff --git a/drivers/dma/qcom/hidma_mgmt.c b/drivers/dma/qcom/hidma_mgmt.c index 5a0991bc4787..7335e2eb9b72 100644 --- a/drivers/dma/qcom/hidma_mgmt.c +++ b/drivers/dma/qcom/hidma_mgmt.c @@ -28,7 +28,7 @@ #include "hidma_mgmt.h" -#define HIDMA_QOS_N_OFFSET 0x300 +#define HIDMA_QOS_N_OFFSET 0x700 #define HIDMA_CFG_OFFSET 0x400 #define HIDMA_MAX_BUS_REQ_LEN_OFFSET 0x41C #define HIDMA_MAX_XACTIONS_OFFSET 0x420 @@ -227,7 +227,8 @@ static int hidma_mgmt_probe(struct platform_device *pdev) goto out; } - if (max_write_request) { + if (max_write_request && + (max_write_request != mgmtdev->max_write_request)) { dev_info(&pdev->dev, "overriding max-write-burst-bytes: %d\n", max_write_request); mgmtdev->max_write_request = max_write_request; @@ -240,7 +241,8 @@ static int hidma_mgmt_probe(struct platform_device *pdev) dev_err(&pdev->dev, "max-read-burst-bytes missing\n"); goto out; } - if (max_read_request) { + if (max_read_request && + (max_read_request != mgmtdev->max_read_request)) { dev_info(&pdev->dev, "overriding max-read-burst-bytes: %d\n", max_read_request); mgmtdev->max_read_request = max_read_request; @@ -253,7 +255,8 @@ static int hidma_mgmt_probe(struct platform_device *pdev) dev_err(&pdev->dev, "max-write-transactions missing\n"); goto out; } - if (max_wr_xactions) { + if (max_wr_xactions && + (max_wr_xactions != mgmtdev->max_wr_xactions)) { dev_info(&pdev->dev, "overriding max-write-transactions: %d\n", max_wr_xactions); mgmtdev->max_wr_xactions = max_wr_xactions; @@ -266,7 +269,8 @@ static int hidma_mgmt_probe(struct platform_device *pdev) dev_err(&pdev->dev, "max-read-transactions missing\n"); goto out; } - if (max_rd_xactions) { + if (max_rd_xactions && + (max_rd_xactions != mgmtdev->max_rd_xactions)) { dev_info(&pdev->dev, "overriding max-read-transactions: %d\n", max_rd_xactions); mgmtdev->max_rd_xactions = max_rd_xactions; @@ -354,7 +358,7 @@ static int __init hidma_mgmt_of_populate_channels(struct device_node *np) struct platform_device_info pdevinfo; struct of_phandle_args out_irq; struct device_node *child; - struct resource *res; + struct resource *res = NULL; const __be32 *cell; int ret = 0, size, i, num; u64 addr, addr_size; diff --git a/include/linux/dma/qcom_bam_dma.h b/include/linux/dma/qcom_bam_dma.h new file mode 100644 index 000000000000..077d43a358e5 --- /dev/null +++ b/include/linux/dma/qcom_bam_dma.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _QCOM_BAM_DMA_H +#define _QCOM_BAM_DMA_H + +#include + +/* + * This data type corresponds to the native Command Element + * supported by BAM DMA Engine. + * + * @cmd_and_addr - upper 8 bits command and lower 24 bits register address. + * @data - for write command: content to be written into peripheral register. + * for read command: dest addr to write peripheral register value. + * @mask - register mask. + * @reserved - for future usage. + * + */ +struct bam_cmd_element { + __le32 cmd_and_addr; + __le32 data; + __le32 mask; + __le32 reserved; +}; + +/* + * This enum indicates the command type in a command element + */ +enum bam_command_type { + BAM_WRITE_COMMAND = 0, + BAM_READ_COMMAND, +}; + +/* + * prep_bam_ce_le32 - Wrapper function to prepare a single BAM command + * element with the data already in le32 format. + * + * @bam_ce: bam command element + * @addr: target address + * @cmd: BAM command + * @data: actual data for write and dest addr for read in le32 + */ +static inline void +bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr, + enum bam_command_type cmd, __le32 data) +{ + bam_ce->cmd_and_addr = + cpu_to_le32((addr & 0xffffff) | ((cmd & 0xff) << 24)); + bam_ce->data = data; + bam_ce->mask = cpu_to_le32(0xffffffff); +} + +/* + * bam_prep_ce - Wrapper function to prepare a single BAM command element + * with the data. + * + * @bam_ce: BAM command element + * @addr: target address + * @cmd: BAM command + * @data: actual data for write and dest addr for read + */ +static inline void +bam_prep_ce(struct bam_cmd_element *bam_ce, u32 addr, + enum bam_command_type cmd, u32 data) +{ + bam_prep_ce_le32(bam_ce, addr, cmd, cpu_to_le32(data)); +} +#endif diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 533680860865..dd4de1d40166 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -186,6 +186,9 @@ struct dma_interleaved_template { * on the result of this operation * @DMA_CTRL_REUSE: client can reuse the descriptor and submit again till * cleared or freed + * @DMA_PREP_CMD: tell the driver that the data passed to DMA API is command + * data and the descriptor should be in different format from normal + * data descriptors. */ enum dma_ctrl_flags { DMA_PREP_INTERRUPT = (1 << 0), @@ -195,6 +198,7 @@ enum dma_ctrl_flags { DMA_PREP_CONTINUE = (1 << 4), DMA_PREP_FENCE = (1 << 5), DMA_CTRL_REUSE = (1 << 6), + DMA_PREP_CMD = (1 << 7), }; /**