Commit 2996148a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'dmaengine-4.18-rc1' of git://git.infradead.org/users/vkoul/slave-dma

Pull dmaengine updates from Vinod Koul:

 - updates to sprd, bam_dma, stm drivers

 - remove VLAs in dmatest

 - move TI drivers to their own subdir

 - switch to SPDX tags for ima/mxs dma drivers

 - simplify getting .drvdata on bunch of drivers by Wolfram Sang

* tag 'dmaengine-4.18-rc1' of git://git.infradead.org/users/vkoul/slave-dma: (32 commits)
  dmaengine: sprd: Add Spreadtrum DMA configuration
  dmaengine: sprd: Optimize the sprd_dma_prep_dma_memcpy()
  dmaengine: imx-dma: Switch to SPDX identifier
  dmaengine: mxs-dma: Switch to SPDX identifier
  dmaengine: imx-sdma: Switch to SPDX identifier
  dmaengine: usb-dmac: Document R8A7799{0,5} bindings
  dmaengine: qcom: bam_dma: fix some doc warnings.
  dmaengine: qcom: bam_dma: fix invalid assignment warning
  dmaengine: sprd: fix an NULL vs IS_ERR() bug
  dmaengine: sprd: Use devm_ioremap_resource() to map memory
  dmaengine: sprd: Fix potential NULL dereference in sprd_dma_probe()
  dmaengine: pl330: flush before wait, and add dev burst support.
  dmaengine: axi-dmac: Request IRQ with IRQF_SHARED
  dmaengine: stm32-mdma: fix spelling mistake: "avalaible" -> "available"
  dmaengine: rcar-dmac: Document R-Car D3 bindings
  dmaengine: sprd: Move DMA request mode and interrupt type into head file
  dmaengine: sprd: Define the DMA data width type
  dmaengine: sprd: Define the DMA transfer step type
  dmaengine: ti: New directory for Texas Instruments DMA drivers
  dmaengine: shdmac: Change platform check to CONFIG_ARCH_RENESAS
  ...
parents 18f18376 67f31971
......@@ -29,6 +29,7 @@ Required Properties:
- "renesas,dmac-r8a77965" (R-Car M3-N)
- "renesas,dmac-r8a77970" (R-Car V3M)
- "renesas,dmac-r8a77980" (R-Car V3H)
- "renesas,dmac-r8a77995" (R-Car D3)
- reg: base address and length of the registers block for the DMAC
......
......@@ -12,6 +12,8 @@ Required Properties:
- "renesas,r8a7795-usb-dmac" (R-Car H3)
- "renesas,r8a7796-usb-dmac" (R-Car M3-W)
- "renesas,r8a77965-usb-dmac" (R-Car M3-N)
- "renesas,r8a77990-usb-dmac" (R-Car E3)
- "renesas,r8a77995-usb-dmac" (R-Car D3)
- reg: base address and length of the registers block for the DMAC
- interrupts: interrupt specifiers for the DMAC, one for each entry in
interrupt-names.
......
......@@ -11790,6 +11790,14 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/rkuo/linux-hexagon-kernel.g
S: Supported
F: arch/hexagon/
QUALCOMM HIDMA DRIVER
M: Sinan Kaya <okaya@codeaurora.org>
L: linux-arm-kernel@lists.infradead.org
L: linux-arm-msm@vger.kernel.org
L: dmaengine@vger.kernel.org
S: Supported
F: drivers/dma/qcom/hidma*
QUALCOMM IOMMU
M: Rob Clark <robdclark@gmail.com>
L: iommu@lists.linux-foundation.org
......
......@@ -151,13 +151,6 @@ config DMA_JZ4780
If you have a board based on such a SoC and wish to use DMA for
devices which can use the DMA controller, say Y or M here.
config DMA_OMAP
tristate "OMAP DMA support"
depends on ARCH_OMAP || COMPILE_TEST
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
select TI_DMA_CROSSBAR if (SOC_DRA7XX || COMPILE_TEST)
config DMA_SA11X0
tristate "SA-11x0 DMA support"
depends on ARCH_SA1100 || COMPILE_TEST
......@@ -574,28 +567,6 @@ config TIMB_DMA
help
Enable support for the Timberdale FPGA DMA engine.
config TI_CPPI41
tristate "CPPI 4.1 DMA support"
depends on (ARCH_OMAP || ARCH_DAVINCI_DA8XX)
select DMA_ENGINE
help
The Communications Port Programming Interface (CPPI) 4.1 DMA engine
is currently used by the USB driver on AM335x and DA8xx platforms.
config TI_DMA_CROSSBAR
bool
config TI_EDMA
bool "TI EDMA support"
depends on ARCH_DAVINCI || ARCH_OMAP || ARCH_KEYSTONE || COMPILE_TEST
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
select TI_DMA_CROSSBAR if (ARCH_OMAP || COMPILE_TEST)
default n
help
Enable support for the TI EDMA controller. This DMA
engine is found on TI DaVinci and AM33xx parts.
config XGENE_DMA
tristate "APM X-Gene DMA support"
depends on ARCH_XGENE || COMPILE_TEST
......@@ -653,6 +624,8 @@ source "drivers/dma/hsu/Kconfig"
source "drivers/dma/sh/Kconfig"
source "drivers/dma/ti/Kconfig"
# clients
comment "DMA Clients"
depends on DMA_ENGINE
......
......@@ -24,7 +24,6 @@ obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o
obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o
obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
obj-$(CONFIG_DMA_JZ4780) += dma-jz4780.o
obj-$(CONFIG_DMA_OMAP) += omap-dma.o
obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
obj-$(CONFIG_DMA_SUN4I) += sun4i-dma.o
obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o
......@@ -69,13 +68,11 @@ obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
obj-$(CONFIG_TEGRA20_APB_DMA) += tegra20-apb-dma.o
obj-$(CONFIG_TEGRA210_ADMA) += tegra210-adma.o
obj-$(CONFIG_TIMB_DMA) += timb_dma.o
obj-$(CONFIG_TI_CPPI41) += cppi41.o
obj-$(CONFIG_TI_DMA_CROSSBAR) += ti-dma-crossbar.o
obj-$(CONFIG_TI_EDMA) += edma.o
obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
obj-$(CONFIG_ZX_DMA) += zx_dma.o
obj-$(CONFIG_ST_FDMA) += st_fdma.o
obj-y += mediatek/
obj-y += qcom/
obj-y += ti/
obj-y += xilinx/
......@@ -2041,8 +2041,7 @@ static void at_dma_shutdown(struct platform_device *pdev)
static int at_dma_prepare(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct at_dma *atdma = platform_get_drvdata(pdev);
struct at_dma *atdma = dev_get_drvdata(dev);
struct dma_chan *chan, *_chan;
list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels,
......@@ -2076,8 +2075,7 @@ static void atc_suspend_cyclic(struct at_dma_chan *atchan)
static int at_dma_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct at_dma *atdma = platform_get_drvdata(pdev);
struct at_dma *atdma = dev_get_drvdata(dev);
struct dma_chan *chan, *_chan;
/* preserve data */
......@@ -2118,8 +2116,7 @@ static void atc_resume_cyclic(struct at_dma_chan *atchan)
static int at_dma_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct at_dma *atdma = platform_get_drvdata(pdev);
struct at_dma *atdma = dev_get_drvdata(dev);
struct dma_chan *chan, *_chan;
/* bring back DMA controller */
......
......@@ -1833,8 +1833,7 @@ static void at_xdmac_free_chan_resources(struct dma_chan *chan)
#ifdef CONFIG_PM
static int atmel_xdmac_prepare(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct at_xdmac *atxdmac = platform_get_drvdata(pdev);
struct at_xdmac *atxdmac = dev_get_drvdata(dev);
struct dma_chan *chan, *_chan;
list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) {
......@@ -1853,8 +1852,7 @@ static int atmel_xdmac_prepare(struct device *dev)
#ifdef CONFIG_PM_SLEEP
static int atmel_xdmac_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct at_xdmac *atxdmac = platform_get_drvdata(pdev);
struct at_xdmac *atxdmac = dev_get_drvdata(dev);
struct dma_chan *chan, *_chan;
list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) {
......@@ -1878,8 +1876,7 @@ static int atmel_xdmac_suspend(struct device *dev)
static int atmel_xdmac_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct at_xdmac *atxdmac = platform_get_drvdata(pdev);
struct at_xdmac *atxdmac = dev_get_drvdata(dev);
struct at_xdmac_chan *atchan;
struct dma_chan *chan, *_chan;
int i;
......
......@@ -687,7 +687,7 @@ static int axi_dmac_probe(struct platform_device *pdev)
if (ret)
goto err_unregister_device;
ret = request_irq(dmac->irq, axi_dmac_interrupt_handler, 0,
ret = request_irq(dmac->irq, axi_dmac_interrupt_handler, IRQF_SHARED,
dev_name(&pdev->dev), dmac);
if (ret)
goto err_unregister_of;
......
......@@ -468,6 +468,8 @@ static int dmatest_func(void *data)
unsigned long long total_len = 0;
u8 align = 0;
bool is_memset = false;
dma_addr_t *srcs;
dma_addr_t *dma_pq;
set_freezable();
......@@ -551,6 +553,14 @@ static int dmatest_func(void *data)
set_user_nice(current, 10);
srcs = kcalloc(src_cnt, sizeof(dma_addr_t), GFP_KERNEL);
if (!srcs)
goto err_dstbuf;
dma_pq = kcalloc(dst_cnt, sizeof(dma_addr_t), GFP_KERNEL);
if (!dma_pq)
goto err_srcs_array;
/*
* src and dst buffers are freed by ourselves below
*/
......@@ -561,7 +571,6 @@ static int dmatest_func(void *data)
&& !(params->iterations && total_tests >= params->iterations)) {
struct dma_async_tx_descriptor *tx = NULL;
struct dmaengine_unmap_data *um;
dma_addr_t srcs[src_cnt];
dma_addr_t *dsts;
unsigned int src_off, dst_off, len;
......@@ -676,8 +685,6 @@ static int dmatest_func(void *data)
srcs, src_cnt,
len, flags);
else if (thread->type == DMA_PQ) {
dma_addr_t dma_pq[dst_cnt];
for (i = 0; i < dst_cnt; i++)
dma_pq[i] = dsts[i] + dst_off;
tx = dev->device_prep_dma_pq(chan, dma_pq, srcs,
......@@ -779,6 +786,9 @@ static int dmatest_func(void *data)
runtime = ktime_to_us(ktime);
ret = 0;
kfree(dma_pq);
err_srcs_array:
kfree(srcs);
err_dstbuf:
for (i = 0; thread->udsts[i]; i++)
kfree(thread->udsts[i]);
......
......@@ -293,8 +293,7 @@ MODULE_DEVICE_TABLE(acpi, dw_dma_acpi_id_table);
static int dw_suspend_late(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct dw_dma_chip *chip = platform_get_drvdata(pdev);
struct dw_dma_chip *chip = dev_get_drvdata(dev);
dw_dma_disable(chip);
clk_disable_unprepare(chip->clk);
......@@ -304,8 +303,7 @@ static int dw_suspend_late(struct device *dev)
static int dw_resume_early(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct dw_dma_chip *chip = platform_get_drvdata(pdev);
struct dw_dma_chip *chip = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(chip->clk);
......
......@@ -1328,8 +1328,7 @@ static int fsldma_of_remove(struct platform_device *op)
#ifdef CONFIG_PM
static int fsldma_suspend_late(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct fsldma_device *fdev = platform_get_drvdata(pdev);
struct fsldma_device *fdev = dev_get_drvdata(dev);
struct fsldma_chan *chan;
int i;
......@@ -1360,8 +1359,7 @@ static int fsldma_suspend_late(struct device *dev)
static int fsldma_resume_early(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct fsldma_device *fdev = platform_get_drvdata(pdev);
struct fsldma_device *fdev = dev_get_drvdata(dev);
struct fsldma_chan *chan;
u32 mode;
int i;
......
......@@ -670,8 +670,7 @@ static int idma64_platform_remove(struct platform_device *pdev)
static int idma64_pm_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct idma64_chip *chip = platform_get_drvdata(pdev);
struct idma64_chip *chip = dev_get_drvdata(dev);
idma64_off(chip->idma64);
return 0;
......@@ -679,8 +678,7 @@ static int idma64_pm_suspend(struct device *dev)
static int idma64_pm_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct idma64_chip *chip = platform_get_drvdata(pdev);
struct idma64_chip *chip = dev_get_drvdata(dev);
idma64_on(chip->idma64);
return 0;
......
/*
* drivers/dma/imx-dma.c
*
* This file contains a driver for the Freescale i.MX DMA engine
* found on i.MX1/21/27
*
* Copyright 2010 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
* Copyright 2012 Javier Martin, Vista Silicon <javier.martin@vista-silicon.com>
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
// SPDX-License-Identifier: GPL-2.0+
//
// drivers/dma/imx-dma.c
//
// This file contains a driver for the Freescale i.MX DMA engine
// found on i.MX1/21/27
//
// Copyright 2010 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
// Copyright 2012 Javier Martin, Vista Silicon <javier.martin@vista-silicon.com>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/types.h>
......
/*
* drivers/dma/imx-sdma.c
*
* This file contains a driver for the Freescale Smart DMA engine
*
* Copyright 2010 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
*
* Based on code from Freescale:
*
* Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
// SPDX-License-Identifier: GPL-2.0+
//
// drivers/dma/imx-sdma.c
//
// This file contains a driver for the Freescale Smart DMA engine
//
// Copyright 2010 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
//
// Based on code from Freescale:
//
// Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
#include <linux/init.h>
#include <linux/iopoll.h>
......
/*
* Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
*
* Refer to drivers/dma/imx-sdma.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
// SPDX-License-Identifier: GPL-2.0
//
// Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
//
// Refer to drivers/dma/imx-sdma.c
#include <linux/init.h>
#include <linux/types.h>
......
......@@ -27,6 +27,7 @@
#include <linux/of_dma.h>
#include <linux/err.h>
#include <linux/pm_runtime.h>
#include <linux/bug.h>
#include "dmaengine.h"
#define PL330_MAX_CHAN 8
......@@ -1094,51 +1095,96 @@ static inline int _ldst_memtomem(unsigned dry_run, u8 buf[],
return off;
}
static inline int _ldst_devtomem(struct pl330_dmac *pl330, unsigned dry_run,
u8 buf[], const struct _xfer_spec *pxs,
int cyc)
static u32 _emit_load(unsigned int dry_run, u8 buf[],
enum pl330_cond cond, enum dma_transfer_direction direction,
u8 peri)
{
int off = 0;
enum pl330_cond cond;
if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)
cond = BURST;
else
cond = SINGLE;
switch (direction) {
case DMA_MEM_TO_MEM:
/* fall through */
case DMA_MEM_TO_DEV:
off += _emit_LD(dry_run, &buf[off], cond);
break;
while (cyc--) {
off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri);
off += _emit_LDP(dry_run, &buf[off], cond, pxs->desc->peri);
off += _emit_ST(dry_run, &buf[off], ALWAYS);
case DMA_DEV_TO_MEM:
if (cond == ALWAYS) {
off += _emit_LDP(dry_run, &buf[off], SINGLE,
peri);
off += _emit_LDP(dry_run, &buf[off], BURST,
peri);
} else {
off += _emit_LDP(dry_run, &buf[off], cond,
peri);
}
break;
if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP))
off += _emit_FLUSHP(dry_run, &buf[off],
pxs->desc->peri);
default:
/* this code should be unreachable */
WARN_ON(1);
break;
}
return off;
}
static inline int _ldst_memtodev(struct pl330_dmac *pl330,
static inline u32 _emit_store(unsigned int dry_run, u8 buf[],
enum pl330_cond cond, enum dma_transfer_direction direction,
u8 peri)
{
int off = 0;
switch (direction) {
case DMA_MEM_TO_MEM:
/* fall through */
case DMA_DEV_TO_MEM:
off += _emit_ST(dry_run, &buf[off], cond);
break;
case DMA_MEM_TO_DEV:
if (cond == ALWAYS) {
off += _emit_STP(dry_run, &buf[off], SINGLE,
peri);
off += _emit_STP(dry_run, &buf[off], BURST,
peri);
} else {
off += _emit_STP(dry_run, &buf[off], cond,
peri);
}
break;
default:
/* this code should be unreachable */
WARN_ON(1);
break;
}
return off;
}
static inline int _ldst_peripheral(struct pl330_dmac *pl330,
unsigned dry_run, u8 buf[],
const struct _xfer_spec *pxs, int cyc)
const struct _xfer_spec *pxs, int cyc,
enum pl330_cond cond)
{
int off = 0;
enum pl330_cond cond;
if (pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)
cond = BURST;
else
cond = SINGLE;
/*
* do FLUSHP at beginning to clear any stale dma requests before the
* first WFP.
*/
if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP))
off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri);
while (cyc--) {
off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri);
off += _emit_LD(dry_run, &buf[off], ALWAYS);
off += _emit_STP(dry_run, &buf[off], cond, pxs->desc->peri);
if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP))
off += _emit_FLUSHP(dry_run, &buf[off],
pxs->desc->peri);
off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype,
pxs->desc->peri);
off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype,
pxs->desc->peri);
}
return off;
......@@ -1148,19 +1194,65 @@ static int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[],
const struct _xfer_spec *pxs, int cyc)
{
int off = 0;
enum pl330_cond cond = BRST_LEN(pxs->ccr) > 1 ? BURST : SINGLE;
switch (pxs->desc->rqtype) {
case DMA_MEM_TO_DEV:
off += _ldst_memtodev(pl330, dry_run, &buf[off], pxs, cyc);
break;
/* fall through */
case DMA_DEV_TO_MEM:
off += _ldst_devtomem(pl330, dry_run, &buf[off], pxs, cyc);
off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs, cyc,
cond);
break;
case DMA_MEM_TO_MEM:
off += _ldst_memtomem(dry_run, &buf[off], pxs, cyc);
break;
default:
/* this code should be unreachable */
WARN_ON(1);
break;
}
return off;
}
/*
* transfer dregs with single transfers to peripheral, or a reduced size burst
* for mem-to-mem.
*/
static int _dregs(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[],
const struct _xfer_spec *pxs, int transfer_length)
{
int off = 0;
int dregs_ccr;
if (transfer_length == 0)
return off;
switch (pxs->desc->rqtype) {
case DMA_MEM_TO_DEV:
/* fall through */
case DMA_DEV_TO_MEM:
off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs,
transfer_length, SINGLE);
break;
case DMA_MEM_TO_MEM:
dregs_ccr = pxs->ccr;
dregs_ccr &= ~((0xf << CC_SRCBRSTLEN_SHFT) |
(0xf << CC_DSTBRSTLEN_SHFT));
dregs_ccr |= (((transfer_length - 1) & 0xf) <<
CC_SRCBRSTLEN_SHFT);
dregs_ccr |= (((transfer_length - 1) & 0xf) <<
CC_DSTBRSTLEN_SHFT);
off += _emit_MOV(dry_run, &buf[off], CCR, dregs_ccr);
off += _ldst_memtomem(dry_run, &buf[off], pxs, 1);
break;
default:
off += 0x40000000; /* Scare off the Client */
/* this code should be unreachable */
WARN_ON(1);
break;
}
......@@ -1256,6 +1348,8 @@ static inline int _setup_loops(struct pl330_dmac *pl330,
struct pl330_xfer *x = &pxs->desc->px;
u32 ccr = pxs->ccr;
unsigned long c, bursts = BYTE_TO_BURST(x->bytes, ccr);
int num_dregs = (x->bytes - BURST_TO_BYTE(bursts, ccr)) /
BRST_SIZE(ccr);
int off = 0;
while (bursts) {
......@@ -1263,6 +1357,7 @@ static inline int _setup_loops(struct pl330_dmac *pl330,
off += _loop(pl330, dry_run, &buf[off], &c, pxs);
bursts -= c;
}
off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs);
return off;
}
......@@ -1294,7 +1389,6 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run,
struct _xfer_spec *pxs)
{
struct _pl330_req *req = &thrd->req[index];
struct pl330_xfer *x;
u8 *buf = req->mc_cpu;
int off = 0;
......@@ -1303,11 +1397,6 @@ static int _setup_req(struct pl330_dmac *pl330, unsigned dry_run,
/* DMAMOV CCR, ccr */
off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr);
x = &pxs->desc->px;
/* Error if xfer length is not aligned at burst size */
if (x->bytes % (BRST_SIZE(pxs->ccr) * BRST_LEN(pxs->ccr)))
return -EINVAL;
off += _setup_xfer(pl330, dry_run, &buf[off], pxs);
/* DMASEV peripheral/event */
......@@ -1365,6 +1454,20 @@ static int pl330_submit_req(struct pl330_thread *thrd,
u32 ccr;
int ret = 0;
switch (desc->rqtype) {
case DMA_MEM_TO_DEV:
break;
case DMA_DEV_TO_MEM:
break;
case DMA_MEM_TO_MEM:
break;
default:
return -ENOTSUPP;
}
if (pl330->state == DYING
|| pl330->dmac_tbd.reset_chan & (1 << thrd->id)) {
dev_info(thrd->dmac->ddma.dev, "%s:%d\n",
......@@ -2106,6 +2209,18 @@ static bool pl330_prep_slave_fifo(struct dma_pl330_chan *pch,
return true;
}
static int fixup_burst_len(int max_burst_len, int quirks)
{
if (quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)
return 1;
else if (max_burst_len > PL330_MAX_BURST)
return PL330_MAX_BURST;
else if (max_burst_len < 1)
return 1;
else
return max_burst_len;
}
static int pl330_config(struct dma_chan *chan,
struct dma_slave_config *slave_config)
{
......@@ -2117,15 +2232,15 @@ static int pl330_config(struct dma_chan *chan,
pch->fifo_addr = slave_config->dst_addr;
if (slave_config->dst_addr_width)
pch->burst_sz = __ffs(slave_config->dst_addr_width);
if (slave_config->dst_maxburst)
pch->burst_len = slave_config->dst_maxburst;
pch->burst_len = fixup_burst_len(slave_config->dst_maxburst,
pch->dmac->quirks);
} else if (slave_config->direction == DMA_DEV_TO_MEM) {
if (slave_config->src_addr)
pch->fifo_addr = slave_config->src_addr;
if (slave_config->src_addr_width)
pch->burst_sz = __ffs(slave_config->src_addr_width);
if (slave_config->src_maxburst)
pch->burst_len = slave_config->src_maxburst;
pch->burst_len = fixup_burst_len(slave_config->src_maxburst,
pch->dmac->quirks);
}
return 0;
......@@ -2519,14 +2634,8 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
burst_len >>= desc->rqcfg.brst_size;
/* src/dst_burst_len can't be more than 16 */
if (burst_len > 16)
burst_len = 16;
while (burst_len > 1) {
if (!(len % (burst_len << desc->rqcfg.brst_size)))
break;
burst_len--;
}
if (burst_len > PL330_MAX_BURST)
burst_len = PL330_MAX_BURST;
return burst_len;
}
......@@ -2598,7 +2707,7 @@ static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
desc->rqtype = direction;
desc->rqcfg.brst_size = pch->burst_sz;
desc->rqcfg.brst_len = 1;
desc->rqcfg.brst_len = pch->burst_len;
desc->bytes_requested = period_len;
fill_px(&desc->px, dst, src, period_len);
......@@ -2743,7 +2852,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
}
desc->rqcfg.brst_size = pch->burst_sz;
desc->rqcfg.brst_len = 1;
desc->rqcfg.brst_len = pch->burst_len;
desc->rqtype = direction;
desc->bytes_requested = sg_dma_len(sg);
}
......
......@@ -451,6 +451,7 @@ static void bam_reset_channel(struct bam_chan *bchan)
/**
* bam_chan_init_hw - Initialize channel hardware
* @bchan: bam channel
* @dir: DMA transfer direction
*
* This function resets and initializes the BAM channel
*/
......@@ -673,7 +674,7 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan,
remainder = 0;
}
async_desc->length += desc->size;
async_desc->length += le16_to_cpu(desc->size);
desc++;
} while (remainder > 0);
}
......@@ -687,7 +688,7 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan,
/**
* bam_dma_terminate_all - terminate all transactions on a channel
* @bchan: bam dma channel
* @chan: bam dma channel
*
* Dequeues and frees all transactions
* No callbacks are done
......@@ -918,7 +919,8 @@ static enum dma_status bam_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
continue;
for (i = 0; i < async_desc->num_desc; i++)
residue += async_desc->curr_desc[i].size;
residue += le16_to_cpu(
async_desc->curr_desc[i].size);
}
}
......@@ -958,7 +960,7 @@ static void bam_apply_new_config(struct bam_chan *bchan,
/**
* bam_start_dma - start next transaction
* @bchan - bam dma channel
* @bchan: bam dma channel
*/
static void bam_start_dma(struct bam_chan *bchan)
{
......
......@@ -616,8 +616,7 @@ static irqreturn_t hidma_chirq_handler_msi(int chirq, void *arg)
static ssize_t hidma_show_values(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct hidma_dev *mdev = platform_get_drvdata(pdev);
struct hidma_dev *mdev = dev_get_drvdata(dev);
buf[0] = 0;
......
......@@ -107,8 +107,7 @@ static struct hidma_mgmt_fileinfo hidma_mgmt_files[] = {
static ssize_t show_values(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct hidma_mgmt_dev *mdev = platform_get_drvdata(pdev);
struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
unsigned int i;
buf[0] = 0;
......@@ -125,8 +124,7 @@ static ssize_t show_values(struct device *dev, struct device_attribute *attr,
static ssize_t set_values(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct hidma_mgmt_dev *mdev = platform_get_drvdata(pdev);
struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
unsigned long tmp;
unsigned int i;
int rc;
......
......@@ -443,7 +443,6 @@ static bool sh_dmae_reset(struct sh_dmae_device *shdev)
return ret;
}
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
static irqreturn_t sh_dmae_err(int irq, void *data)
{
struct sh_dmae_device *shdev = data;
......@@ -454,7 +453,6 @@ static irqreturn_t sh_dmae_err(int irq, void *data)
sh_dmae_reset(shdev);
return IRQ_HANDLED;
}
#endif
static bool sh_dmae_desc_completed(struct shdma_chan *schan,
struct shdma_desc *sdesc)
......@@ -686,11 +684,8 @@ static int sh_dmae_probe(struct platform_device *pdev)
const struct sh_dmae_pdata *pdata;
unsigned long chan_flag[SH_DMAE_MAX_CHANNELS] = {};
int chan_irq[SH_DMAE_MAX_CHANNELS];
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
unsigned long irqflags = 0;
int errirq;
#endif
int err, i, irq_cnt = 0, irqres = 0, irq_cap = 0;
int err, errirq, i, irq_cnt = 0, irqres = 0, irq_cap = 0;
struct sh_dmae_device *shdev;
struct dma_device *dma_dev;
struct resource *chan, *dmars, *errirq_res, *chanirq_res;
......@@ -792,33 +787,32 @@ static int sh_dmae_probe(struct platform_device *pdev)
if (err)
goto rst_err;
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (IS_ENABLED(CONFIG_CPU_SH4) || IS_ENABLED(CONFIG_ARCH_RENESAS)) {
chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
if (!chanirq_res)
chanirq_res = errirq_res;
else
irqres++;
if (!chanirq_res)
chanirq_res = errirq_res;
else
irqres++;
if (chanirq_res == errirq_res ||
(errirq_res->flags & IORESOURCE_BITS) == IORESOURCE_IRQ_SHAREABLE)
irqflags = IRQF_SHARED;
if (chanirq_res == errirq_res ||
(errirq_res->flags & IORESOURCE_BITS) == IORESOURCE_IRQ_SHAREABLE)
irqflags = IRQF_SHARED;
errirq = errirq_res->start;
errirq = errirq_res->start;
err = devm_request_irq(&pdev->dev, errirq, sh_dmae_err, irqflags,
"DMAC Address Error", shdev);
if (err) {
dev_err(&pdev->dev,
"DMA failed requesting irq #%d, error %d\n",
errirq, err);
goto eirq_err;
err = devm_request_irq(&pdev->dev, errirq, sh_dmae_err,
irqflags, "DMAC Address Error", shdev);
if (err) {
dev_err(&pdev->dev,
"DMA failed requesting irq #%d, error %d\n",
errirq, err);
goto eirq_err;
}
} else {
chanirq_res = errirq_res;
}
#else
chanirq_res = errirq_res;
#endif /* CONFIG_CPU_SH4 || CONFIG_ARCH_SHMOBILE */
if (chanirq_res->start == chanirq_res->end &&
!platform_get_resource(pdev, IORESOURCE_IRQ, 1)) {
/* Special case - all multiplexed */
......@@ -884,9 +878,7 @@ static int sh_dmae_probe(struct platform_device *pdev)
chan_probe_err:
sh_dmae_chan_remove(shdev);
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
eirq_err:
#endif
rst_err:
spin_lock_irq(&sh_dmae_lock);
list_del_rcu(&shdev->node);
......
......@@ -6,6 +6,7 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/dma/sprd-dma.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/interrupt.h>
......@@ -116,57 +117,21 @@
#define SPRD_DMA_SRC_TRSF_STEP_OFFSET 0
#define SPRD_DMA_TRSF_STEP_MASK GENMASK(15, 0)
/* define the DMA transfer step type */
#define SPRD_DMA_NONE_STEP 0
#define SPRD_DMA_BYTE_STEP 1
#define SPRD_DMA_SHORT_STEP 2
#define SPRD_DMA_WORD_STEP 4
#define SPRD_DMA_DWORD_STEP 8
#define SPRD_DMA_SOFTWARE_UID 0
/*
* enum sprd_dma_req_mode: define the DMA request mode
* @SPRD_DMA_FRAG_REQ: fragment request mode
* @SPRD_DMA_BLK_REQ: block request mode
* @SPRD_DMA_TRANS_REQ: transaction request mode
* @SPRD_DMA_LIST_REQ: link-list request mode
*
* We have 4 types request mode: fragment mode, block mode, transaction mode
* and linklist mode. One transaction can contain several blocks, one block can
* contain several fragments. Link-list mode means we can save several DMA
* configuration into one reserved memory, then DMA can fetch each DMA
* configuration automatically to start transfer.
*/
enum sprd_dma_req_mode {
SPRD_DMA_FRAG_REQ,
SPRD_DMA_BLK_REQ,
SPRD_DMA_TRANS_REQ,
SPRD_DMA_LIST_REQ,
};
/*
* enum sprd_dma_int_type: define the DMA interrupt type
* @SPRD_DMA_NO_INT: do not need generate DMA interrupts.
* @SPRD_DMA_FRAG_INT: fragment done interrupt when one fragment request
* is done.
* @SPRD_DMA_BLK_INT: block done interrupt when one block request is done.
* @SPRD_DMA_BLK_FRAG_INT: block and fragment interrupt when one fragment
* or one block request is done.
* @SPRD_DMA_TRANS_INT: tansaction done interrupt when one transaction
* request is done.
* @SPRD_DMA_TRANS_FRAG_INT: transaction and fragment interrupt when one
* transaction request or fragment request is done.
* @SPRD_DMA_TRANS_BLK_INT: transaction and block interrupt when one
* transaction request or block request is done.
* @SPRD_DMA_LIST_INT: link-list done interrupt when one link-list request
* is done.
* @SPRD_DMA_CFGERR_INT: configure error interrupt when configuration is
* incorrect.
*/
enum sprd_dma_int_type {
SPRD_DMA_NO_INT,
SPRD_DMA_FRAG_INT,
SPRD_DMA_BLK_INT,
SPRD_DMA_BLK_FRAG_INT,
SPRD_DMA_TRANS_INT,
SPRD_DMA_TRANS_FRAG_INT,
SPRD_DMA_TRANS_BLK_INT,
SPRD_DMA_LIST_INT,
SPRD_DMA_CFGERR_INT,
/* dma data width values */
enum sprd_dma_datawidth {
SPRD_DMA_DATAWIDTH_1_BYTE,
SPRD_DMA_DATAWIDTH_2_BYTES,
SPRD_DMA_DATAWIDTH_4_BYTES,
SPRD_DMA_DATAWIDTH_8_BYTES,
};
/* dma channel hardware configuration */
......@@ -199,6 +164,7 @@ struct sprd_dma_desc {
struct sprd_dma_chn {
struct virt_dma_chan vc;
void __iomem *chn_base;
struct dma_slave_config slave_cfg;
u32 chn_num;
u32 dev_id;
struct sprd_dma_desc *cur_desc;
......@@ -587,52 +553,97 @@ static void sprd_dma_issue_pending(struct dma_chan *chan)
spin_unlock_irqrestore(&schan->vc.lock, flags);
}
static int sprd_dma_config(struct dma_chan *chan, struct sprd_dma_desc *sdesc,
dma_addr_t dest, dma_addr_t src, size_t len)
static int sprd_dma_get_datawidth(enum dma_slave_buswidth buswidth)
{
switch (buswidth) {
case DMA_SLAVE_BUSWIDTH_1_BYTE:
case DMA_SLAVE_BUSWIDTH_2_BYTES:
case DMA_SLAVE_BUSWIDTH_4_BYTES:
case DMA_SLAVE_BUSWIDTH_8_BYTES:
return ffs(buswidth) - 1;
default:
return -EINVAL;
}
}
static int sprd_dma_get_step(enum dma_slave_buswidth buswidth)
{
switch (buswidth) {
case DMA_SLAVE_BUSWIDTH_1_BYTE:
case DMA_SLAVE_BUSWIDTH_2_BYTES:
case DMA_SLAVE_BUSWIDTH_4_BYTES:
case DMA_SLAVE_BUSWIDTH_8_BYTES:
return buswidth;
default:
return -EINVAL;
}
}
static int sprd_dma_fill_desc(struct dma_chan *chan,
struct sprd_dma_desc *sdesc,
dma_addr_t src, dma_addr_t dst, u32 len,
enum dma_transfer_direction dir,
unsigned long flags,
struct dma_slave_config *slave_cfg)
{
struct sprd_dma_dev *sdev = to_sprd_dma_dev(chan);
struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
struct sprd_dma_chn_hw *hw = &sdesc->chn_hw;
u32 datawidth, src_step, des_step, fragment_len;
u32 block_len, req_mode, irq_mode, transcation_len;
u32 fix_mode = 0, fix_en = 0;
if (IS_ALIGNED(len, 4)) {
datawidth = 2;
src_step = 4;
des_step = 4;
} else if (IS_ALIGNED(len, 2)) {
datawidth = 1;
src_step = 2;
des_step = 2;
u32 req_mode = (flags >> SPRD_DMA_REQ_SHIFT) & SPRD_DMA_REQ_MODE_MASK;
u32 int_mode = flags & SPRD_DMA_INT_MASK;
int src_datawidth, dst_datawidth, src_step, dst_step;
u32 temp, fix_mode = 0, fix_en = 0;
if (dir == DMA_MEM_TO_DEV) {
src_step = sprd_dma_get_step(slave_cfg->src_addr_width);
if (src_step < 0) {
dev_err(sdev->dma_dev.dev, "invalid source step\n");
return src_step;
}
dst_step = SPRD_DMA_NONE_STEP;
} else {
datawidth = 0;
src_step = 1;
des_step = 1;
dst_step = sprd_dma_get_step(slave_cfg->dst_addr_width);
if (dst_step < 0) {
dev_err(sdev->dma_dev.dev, "invalid destination step\n");
return dst_step;
}
src_step = SPRD_DMA_NONE_STEP;
}
fragment_len = SPRD_DMA_MEMCPY_MIN_SIZE;
if (len <= SPRD_DMA_BLK_LEN_MASK) {
block_len = len;
transcation_len = 0;
req_mode = SPRD_DMA_BLK_REQ;
irq_mode = SPRD_DMA_BLK_INT;
} else {
block_len = SPRD_DMA_MEMCPY_MIN_SIZE;
transcation_len = len;
req_mode = SPRD_DMA_TRANS_REQ;
irq_mode = SPRD_DMA_TRANS_INT;
src_datawidth = sprd_dma_get_datawidth(slave_cfg->src_addr_width);
if (src_datawidth < 0) {
dev_err(sdev->dma_dev.dev, "invalid source datawidth\n");
return src_datawidth;
}
dst_datawidth = sprd_dma_get_datawidth(slave_cfg->dst_addr_width);
if (dst_datawidth < 0) {
dev_err(sdev->dma_dev.dev, "invalid destination datawidth\n");
return dst_datawidth;
}
if (slave_cfg->slave_id)
schan->dev_id = slave_cfg->slave_id;
hw->cfg = SPRD_DMA_DONOT_WAIT_BDONE << SPRD_DMA_WAIT_BDONE_OFFSET;
hw->wrap_ptr = (u32)((src >> SPRD_DMA_HIGH_ADDR_OFFSET) &
SPRD_DMA_HIGH_ADDR_MASK);
hw->wrap_to = (u32)((dest >> SPRD_DMA_HIGH_ADDR_OFFSET) &
SPRD_DMA_HIGH_ADDR_MASK);
hw->src_addr = (u32)(src & SPRD_DMA_LOW_ADDR_MASK);
hw->des_addr = (u32)(dest & SPRD_DMA_LOW_ADDR_MASK);
/*
* wrap_ptr and wrap_to will save the high 4 bits source address and
* destination address.
*/
hw->wrap_ptr = (src >> SPRD_DMA_HIGH_ADDR_OFFSET) & SPRD_DMA_HIGH_ADDR_MASK;
hw->wrap_to = (dst >> SPRD_DMA_HIGH_ADDR_OFFSET) & SPRD_DMA_HIGH_ADDR_MASK;
hw->src_addr = src & SPRD_DMA_LOW_ADDR_MASK;
hw->des_addr = dst & SPRD_DMA_LOW_ADDR_MASK;
if ((src_step != 0 && des_step != 0) || (src_step | des_step) == 0) {
/*
* If the src step and dst step both are 0 or both are not 0, that means
* we can not enable the fix mode. If one is 0 and another one is not,
* we can enable the fix mode.
*/
if ((src_step != 0 && dst_step != 0) || (src_step | dst_step) == 0) {
fix_en = 0;
} else {
fix_en = 1;
......@@ -642,87 +653,119 @@ static int sprd_dma_config(struct dma_chan *chan, struct sprd_dma_desc *sdesc,
fix_mode = 0;
}
hw->frg_len = datawidth << SPRD_DMA_SRC_DATAWIDTH_OFFSET |
datawidth << SPRD_DMA_DES_DATAWIDTH_OFFSET |
req_mode << SPRD_DMA_REQ_MODE_OFFSET |
fix_mode << SPRD_DMA_FIX_SEL_OFFSET |
fix_en << SPRD_DMA_FIX_EN_OFFSET |
(fragment_len & SPRD_DMA_FRG_LEN_MASK);
hw->blk_len = block_len & SPRD_DMA_BLK_LEN_MASK;
hw->intc = SPRD_DMA_CFG_ERR_INT_EN;
switch (irq_mode) {
case SPRD_DMA_NO_INT:
break;
case SPRD_DMA_FRAG_INT:
hw->intc |= SPRD_DMA_FRAG_INT_EN;
break;
hw->intc = int_mode | SPRD_DMA_CFG_ERR_INT_EN;
case SPRD_DMA_BLK_INT:
hw->intc |= SPRD_DMA_BLK_INT_EN;
break;
temp = src_datawidth << SPRD_DMA_SRC_DATAWIDTH_OFFSET;
temp |= dst_datawidth << SPRD_DMA_DES_DATAWIDTH_OFFSET;
temp |= req_mode << SPRD_DMA_REQ_MODE_OFFSET;
temp |= fix_mode << SPRD_DMA_FIX_SEL_OFFSET;
temp |= fix_en << SPRD_DMA_FIX_EN_OFFSET;
temp |= slave_cfg->src_maxburst & SPRD_DMA_FRG_LEN_MASK;
hw->frg_len = temp;
case SPRD_DMA_BLK_FRAG_INT:
hw->intc |= SPRD_DMA_BLK_INT_EN | SPRD_DMA_FRAG_INT_EN;
break;
hw->blk_len = len & SPRD_DMA_BLK_LEN_MASK;
hw->trsc_len = len & SPRD_DMA_TRSC_LEN_MASK;
case SPRD_DMA_TRANS_INT:
hw->intc |= SPRD_DMA_TRANS_INT_EN;
break;
temp = (dst_step & SPRD_DMA_TRSF_STEP_MASK) << SPRD_DMA_DEST_TRSF_STEP_OFFSET;
temp |= (src_step & SPRD_DMA_TRSF_STEP_MASK) << SPRD_DMA_SRC_TRSF_STEP_OFFSET;
hw->trsf_step = temp;
case SPRD_DMA_TRANS_FRAG_INT:
hw->intc |= SPRD_DMA_TRANS_INT_EN | SPRD_DMA_FRAG_INT_EN;
break;
hw->frg_step = 0;
hw->src_blk_step = 0;
hw->des_blk_step = 0;
return 0;
}
case SPRD_DMA_TRANS_BLK_INT:
hw->intc |= SPRD_DMA_TRANS_INT_EN | SPRD_DMA_BLK_INT_EN;
break;
static struct dma_async_tx_descriptor *
sprd_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
size_t len, unsigned long flags)
{
struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
struct sprd_dma_desc *sdesc;
struct sprd_dma_chn_hw *hw;
enum sprd_dma_datawidth datawidth;
u32 step, temp;
case SPRD_DMA_LIST_INT:
hw->intc |= SPRD_DMA_LIST_INT_EN;
break;
sdesc = kzalloc(sizeof(*sdesc), GFP_NOWAIT);
if (!sdesc)
return NULL;
case SPRD_DMA_CFGERR_INT:
hw->intc |= SPRD_DMA_CFG_ERR_INT_EN;
break;
hw = &sdesc->chn_hw;
default:
dev_err(sdev->dma_dev.dev, "invalid irq mode\n");
return -EINVAL;
hw->cfg = SPRD_DMA_DONOT_WAIT_BDONE << SPRD_DMA_WAIT_BDONE_OFFSET;
hw->intc = SPRD_DMA_TRANS_INT | SPRD_DMA_CFG_ERR_INT_EN;
hw->src_addr = src & SPRD_DMA_LOW_ADDR_MASK;
hw->des_addr = dest & SPRD_DMA_LOW_ADDR_MASK;
hw->wrap_ptr = (src >> SPRD_DMA_HIGH_ADDR_OFFSET) &
SPRD_DMA_HIGH_ADDR_MASK;
hw->wrap_to = (dest >> SPRD_DMA_HIGH_ADDR_OFFSET) &
SPRD_DMA_HIGH_ADDR_MASK;
if (IS_ALIGNED(len, 8)) {
datawidth = SPRD_DMA_DATAWIDTH_8_BYTES;
step = SPRD_DMA_DWORD_STEP;
} else if (IS_ALIGNED(len, 4)) {
datawidth = SPRD_DMA_DATAWIDTH_4_BYTES;
step = SPRD_DMA_WORD_STEP;
} else if (IS_ALIGNED(len, 2)) {
datawidth = SPRD_DMA_DATAWIDTH_2_BYTES;
step = SPRD_DMA_SHORT_STEP;
} else {
datawidth = SPRD_DMA_DATAWIDTH_1_BYTE;
step = SPRD_DMA_BYTE_STEP;
}
if (transcation_len == 0)
hw->trsc_len = block_len & SPRD_DMA_TRSC_LEN_MASK;
else
hw->trsc_len = transcation_len & SPRD_DMA_TRSC_LEN_MASK;
temp = datawidth << SPRD_DMA_SRC_DATAWIDTH_OFFSET;
temp |= datawidth << SPRD_DMA_DES_DATAWIDTH_OFFSET;
temp |= SPRD_DMA_TRANS_REQ << SPRD_DMA_REQ_MODE_OFFSET;
temp |= len & SPRD_DMA_FRG_LEN_MASK;
hw->frg_len = temp;
hw->trsf_step = (des_step & SPRD_DMA_TRSF_STEP_MASK) <<
SPRD_DMA_DEST_TRSF_STEP_OFFSET |
(src_step & SPRD_DMA_TRSF_STEP_MASK) <<
SPRD_DMA_SRC_TRSF_STEP_OFFSET;
hw->blk_len = len & SPRD_DMA_BLK_LEN_MASK;
hw->trsc_len = len & SPRD_DMA_TRSC_LEN_MASK;
hw->frg_step = 0;
hw->src_blk_step = 0;
hw->des_blk_step = 0;
hw->src_blk_step = 0;
return 0;
temp = (step & SPRD_DMA_TRSF_STEP_MASK) << SPRD_DMA_DEST_TRSF_STEP_OFFSET;
temp |= (step & SPRD_DMA_TRSF_STEP_MASK) << SPRD_DMA_SRC_TRSF_STEP_OFFSET;
hw->trsf_step = temp;
return vchan_tx_prep(&schan->vc, &sdesc->vd, flags);
}
static struct dma_async_tx_descriptor *
sprd_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
size_t len, unsigned long flags)
sprd_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sglen, enum dma_transfer_direction dir,
unsigned long flags, void *context)
{
struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
struct dma_slave_config *slave_cfg = &schan->slave_cfg;
dma_addr_t src = 0, dst = 0;
struct sprd_dma_desc *sdesc;
int ret;
struct scatterlist *sg;
u32 len = 0;
int ret, i;
/* TODO: now we only support one sg for each DMA configuration. */
if (!is_slave_direction(dir) || sglen > 1)
return NULL;
sdesc = kzalloc(sizeof(*sdesc), GFP_NOWAIT);
if (!sdesc)
return NULL;
ret = sprd_dma_config(chan, sdesc, dest, src, len);
for_each_sg(sgl, sg, sglen, i) {
len = sg_dma_len(sg);
if (dir == DMA_MEM_TO_DEV) {
src = sg_dma_address(sg);
dst = slave_cfg->dst_addr;
} else {
src = slave_cfg->src_addr;
dst = sg_dma_address(sg);
}
}
ret = sprd_dma_fill_desc(chan, sdesc, src, dst, len, dir, flags,
slave_cfg);
if (ret) {
kfree(sdesc);
return NULL;
......@@ -731,6 +774,19 @@ sprd_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
return vchan_tx_prep(&schan->vc, &sdesc->vd, flags);
}
static int sprd_dma_slave_config(struct dma_chan *chan,
struct dma_slave_config *config)
{
struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
struct dma_slave_config *slave_cfg = &schan->slave_cfg;
if (!is_slave_direction(config->direction))
return -EINVAL;
memcpy(slave_cfg, config, sizeof(*config));
return 0;
}
static int sprd_dma_pause(struct dma_chan *chan)
{
struct sprd_dma_chn *schan = to_sprd_dma_chan(chan);
......@@ -842,10 +898,9 @@ static int sprd_dma_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sdev->glb_base = devm_ioremap_nocache(&pdev->dev, res->start,
resource_size(res));
if (!sdev->glb_base)
return -ENOMEM;
sdev->glb_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(sdev->glb_base))
return PTR_ERR(sdev->glb_base);
dma_cap_set(DMA_MEMCPY, sdev->dma_dev.cap_mask);
sdev->total_chns = chn_count;
......@@ -858,6 +913,8 @@ static int sprd_dma_probe(struct platform_device *pdev)
sdev->dma_dev.device_tx_status = sprd_dma_tx_status;
sdev->dma_dev.device_issue_pending = sprd_dma_issue_pending;
sdev->dma_dev.device_prep_dma_memcpy = sprd_dma_prep_dma_memcpy;
sdev->dma_dev.device_prep_slave_sg = sprd_dma_prep_slave_sg;
sdev->dma_dev.device_config = sprd_dma_slave_config;
sdev->dma_dev.device_pause = sprd_dma_pause;
sdev->dma_dev.device_resume = sprd_dma_resume;
sdev->dma_dev.device_terminate_all = sprd_dma_terminate_all;
......
......@@ -2889,8 +2889,7 @@ static int __init d40_dmaengine_init(struct d40_base *base,
#ifdef CONFIG_PM_SLEEP
static int dma40_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct d40_base *base = platform_get_drvdata(pdev);
struct d40_base *base = dev_get_drvdata(dev);
int ret;
ret = pm_runtime_force_suspend(dev);
......@@ -2904,8 +2903,7 @@ static int dma40_suspend(struct device *dev)
static int dma40_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct d40_base *base = platform_get_drvdata(pdev);
struct d40_base *base = dev_get_drvdata(dev);
int ret = 0;
if (base->lcpa_regulator) {
......@@ -2970,8 +2968,7 @@ static void d40_save_restore_registers(struct d40_base *base, bool save)
static int dma40_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct d40_base *base = platform_get_drvdata(pdev);
struct d40_base *base = dev_get_drvdata(dev);
d40_save_restore_registers(base, true);
......@@ -2985,8 +2982,7 @@ static int dma40_runtime_suspend(struct device *dev)
static int dma40_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct d40_base *base = platform_get_drvdata(pdev);
struct d40_base *base = dev_get_drvdata(dev);
d40_save_restore_registers(base, false);
......
......@@ -252,13 +252,17 @@ struct stm32_mdma_hwdesc {
u32 cmdr;
} __aligned(64);
struct stm32_mdma_desc_node {
struct stm32_mdma_hwdesc *hwdesc;
dma_addr_t hwdesc_phys;
};
struct stm32_mdma_desc {
struct virt_dma_desc vdesc;
u32 ccr;
struct stm32_mdma_hwdesc *hwdesc;
dma_addr_t hwdesc_phys;
bool cyclic;
u32 count;
struct stm32_mdma_desc_node node[];
};
struct stm32_mdma_chan {
......@@ -344,30 +348,42 @@ static struct stm32_mdma_desc *stm32_mdma_alloc_desc(
struct stm32_mdma_chan *chan, u32 count)
{
struct stm32_mdma_desc *desc;
int i;
desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
desc = kzalloc(offsetof(typeof(*desc), node[count]), GFP_NOWAIT);
if (!desc)
return NULL;
desc->hwdesc = dma_pool_alloc(chan->desc_pool, GFP_NOWAIT,
&desc->hwdesc_phys);
if (!desc->hwdesc) {
dev_err(chan2dev(chan), "Failed to allocate descriptor\n");
kfree(desc);
return NULL;
for (i = 0; i < count; i++) {
desc->node[i].hwdesc =
dma_pool_alloc(chan->desc_pool, GFP_NOWAIT,
&desc->node[i].hwdesc_phys);
if (!desc->node[i].hwdesc)
goto err;
}
desc->count = count;
return desc;
err:
dev_err(chan2dev(chan), "Failed to allocate descriptor\n");
while (--i >= 0)
dma_pool_free(chan->desc_pool, desc->node[i].hwdesc,
desc->node[i].hwdesc_phys);
kfree(desc);
return NULL;
}
static void stm32_mdma_desc_free(struct virt_dma_desc *vdesc)
{
struct stm32_mdma_desc *desc = to_stm32_mdma_desc(vdesc);
struct stm32_mdma_chan *chan = to_stm32_mdma_chan(vdesc->tx.chan);
int i;
dma_pool_free(chan->desc_pool, desc->hwdesc, desc->hwdesc_phys);
for (i = 0; i < desc->count; i++)
dma_pool_free(chan->desc_pool, desc->node[i].hwdesc,
desc->node[i].hwdesc_phys);
kfree(desc);
}
......@@ -410,13 +426,10 @@ static enum dma_slave_buswidth stm32_mdma_get_max_width(dma_addr_t addr,
static u32 stm32_mdma_get_best_burst(u32 buf_len, u32 tlen, u32 max_burst,
enum dma_slave_buswidth width)
{
u32 best_burst = max_burst;
u32 burst_len = best_burst * width;
u32 best_burst;
while ((burst_len > 0) && (tlen % burst_len)) {
best_burst = best_burst >> 1;
burst_len = best_burst * width;
}
best_burst = min((u32)1 << __ffs(tlen | buf_len),
max_burst * width) / width;
return (best_burst > 0) ? best_burst : 1;
}
......@@ -669,18 +682,18 @@ static int stm32_mdma_set_xfer_param(struct stm32_mdma_chan *chan,
}
static void stm32_mdma_dump_hwdesc(struct stm32_mdma_chan *chan,
struct stm32_mdma_hwdesc *hwdesc)
struct stm32_mdma_desc_node *node)
{
dev_dbg(chan2dev(chan), "hwdesc: 0x%p\n", hwdesc);
dev_dbg(chan2dev(chan), "CTCR: 0x%08x\n", hwdesc->ctcr);
dev_dbg(chan2dev(chan), "CBNDTR: 0x%08x\n", hwdesc->cbndtr);
dev_dbg(chan2dev(chan), "CSAR: 0x%08x\n", hwdesc->csar);
dev_dbg(chan2dev(chan), "CDAR: 0x%08x\n", hwdesc->cdar);
dev_dbg(chan2dev(chan), "CBRUR: 0x%08x\n", hwdesc->cbrur);
dev_dbg(chan2dev(chan), "CLAR: 0x%08x\n", hwdesc->clar);
dev_dbg(chan2dev(chan), "CTBR: 0x%08x\n", hwdesc->ctbr);
dev_dbg(chan2dev(chan), "CMAR: 0x%08x\n", hwdesc->cmar);
dev_dbg(chan2dev(chan), "CMDR: 0x%08x\n\n", hwdesc->cmdr);
dev_dbg(chan2dev(chan), "hwdesc: %pad\n", &node->hwdesc_phys);
dev_dbg(chan2dev(chan), "CTCR: 0x%08x\n", node->hwdesc->ctcr);
dev_dbg(chan2dev(chan), "CBNDTR: 0x%08x\n", node->hwdesc->cbndtr);
dev_dbg(chan2dev(chan), "CSAR: 0x%08x\n", node->hwdesc->csar);
dev_dbg(chan2dev(chan), "CDAR: 0x%08x\n", node->hwdesc->cdar);
dev_dbg(chan2dev(chan), "CBRUR: 0x%08x\n", node->hwdesc->cbrur);
dev_dbg(chan2dev(chan), "CLAR: 0x%08x\n", node->hwdesc->clar);
dev_dbg(chan2dev(chan), "CTBR: 0x%08x\n", node->hwdesc->ctbr);
dev_dbg(chan2dev(chan), "CMAR: 0x%08x\n", node->hwdesc->cmar);
dev_dbg(chan2dev(chan), "CMDR: 0x%08x\n\n", node->hwdesc->cmdr);
}
static void stm32_mdma_setup_hwdesc(struct stm32_mdma_chan *chan,
......@@ -694,7 +707,7 @@ static void stm32_mdma_setup_hwdesc(struct stm32_mdma_chan *chan,
struct stm32_mdma_hwdesc *hwdesc;
u32 next = count + 1;
hwdesc = &desc->hwdesc[count];
hwdesc = desc->node[count].hwdesc;
hwdesc->ctcr = ctcr;
hwdesc->cbndtr &= ~(STM32_MDMA_CBNDTR_BRC_MK |
STM32_MDMA_CBNDTR_BRDUM |
......@@ -704,19 +717,20 @@ static void stm32_mdma_setup_hwdesc(struct stm32_mdma_chan *chan,
hwdesc->csar = src_addr;
hwdesc->cdar = dst_addr;
hwdesc->cbrur = 0;
hwdesc->clar = desc->hwdesc_phys + next * sizeof(*hwdesc);
hwdesc->ctbr = ctbr;
hwdesc->cmar = config->mask_addr;
hwdesc->cmdr = config->mask_data;
if (is_last) {
if (is_cyclic)
hwdesc->clar = desc->hwdesc_phys;
hwdesc->clar = desc->node[0].hwdesc_phys;
else
hwdesc->clar = 0;
} else {
hwdesc->clar = desc->node[next].hwdesc_phys;
}
stm32_mdma_dump_hwdesc(chan, hwdesc);
stm32_mdma_dump_hwdesc(chan, &desc->node[count]);
}
static int stm32_mdma_setup_xfer(struct stm32_mdma_chan *chan,
......@@ -780,7 +794,7 @@ stm32_mdma_prep_slave_sg(struct dma_chan *c, struct scatterlist *sgl,
{
struct stm32_mdma_chan *chan = to_stm32_mdma_chan(c);
struct stm32_mdma_desc *desc;
int ret;
int i, ret;
/*
* Once DMA is in setup cyclic mode the channel we cannot assign this
......@@ -806,7 +820,9 @@ stm32_mdma_prep_slave_sg(struct dma_chan *c, struct scatterlist *sgl,
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
xfer_setup_err:
dma_pool_free(chan->desc_pool, &desc->hwdesc, desc->hwdesc_phys);
for (i = 0; i < desc->count; i++)
dma_pool_free(chan->desc_pool, desc->node[i].hwdesc,
desc->node[i].hwdesc_phys);
kfree(desc);
return NULL;
}
......@@ -895,7 +911,9 @@ stm32_mdma_prep_dma_cyclic(struct dma_chan *c, dma_addr_t buf_addr,
return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
xfer_setup_err:
dma_pool_free(chan->desc_pool, &desc->hwdesc, desc->hwdesc_phys);
for (i = 0; i < desc->count; i++)
dma_pool_free(chan->desc_pool, desc->node[i].hwdesc,
desc->node[i].hwdesc_phys);
kfree(desc);
return NULL;
}
......@@ -1009,7 +1027,7 @@ stm32_mdma_prep_dma_memcpy(struct dma_chan *c, dma_addr_t dest, dma_addr_t src,
ctcr |= STM32_MDMA_CTCR_PKE;
/* Prepare hardware descriptor */
hwdesc = desc->hwdesc;
hwdesc = desc->node[0].hwdesc;
hwdesc->ctcr = ctcr;
hwdesc->cbndtr = cbndtr;
hwdesc->csar = src;
......@@ -1020,7 +1038,7 @@ stm32_mdma_prep_dma_memcpy(struct dma_chan *c, dma_addr_t dest, dma_addr_t src,
hwdesc->cmar = 0;
hwdesc->cmdr = 0;
stm32_mdma_dump_hwdesc(chan, hwdesc);
stm32_mdma_dump_hwdesc(chan, &desc->node[0]);
} else {
/* Setup a LLI transfer */
ctcr |= STM32_MDMA_CTCR_TRGM(STM32_MDMA_LINKED_LIST) |
......@@ -1120,7 +1138,7 @@ static void stm32_mdma_start_transfer(struct stm32_mdma_chan *chan)
}
chan->desc = to_stm32_mdma_desc(vdesc);
hwdesc = chan->desc->hwdesc;
hwdesc = chan->desc->node[0].hwdesc;
chan->curr_hwdesc = 0;
stm32_mdma_write(dmadev, STM32_MDMA_CCR(id), chan->desc->ccr);
......@@ -1198,7 +1216,7 @@ static int stm32_mdma_resume(struct dma_chan *c)
unsigned long flags;
u32 status, reg;
hwdesc = &chan->desc->hwdesc[chan->curr_hwdesc];
hwdesc = chan->desc->node[chan->curr_hwdesc].hwdesc;
spin_lock_irqsave(&chan->vchan.lock, flags);
......@@ -1268,13 +1286,13 @@ static size_t stm32_mdma_desc_residue(struct stm32_mdma_chan *chan,
u32 curr_hwdesc)
{
struct stm32_mdma_device *dmadev = stm32_mdma_get_dev(chan);
struct stm32_mdma_hwdesc *hwdesc = desc->node[0].hwdesc;
u32 cbndtr, residue, modulo, burst_size;
int i;
residue = 0;
for (i = curr_hwdesc + 1; i < desc->count; i++) {
struct stm32_mdma_hwdesc *hwdesc = &desc->hwdesc[i];
hwdesc = desc->node[i].hwdesc;
residue += STM32_MDMA_CBNDTR_BNDT(hwdesc->cbndtr);
}
cbndtr = stm32_mdma_read(dmadev, STM32_MDMA_CBNDTR(chan->id));
......@@ -1503,7 +1521,7 @@ static struct dma_chan *stm32_mdma_of_xlate(struct of_phandle_args *dma_spec,
c = dma_get_any_slave_channel(&dmadev->ddev);
if (!c) {
dev_err(mdma2dev(dmadev), "No more channel avalaible\n");
dev_err(mdma2dev(dmadev), "No more channels available\n");
return NULL;
}
......
#
# Texas Instruments DMA drivers
#
config TI_CPPI41
tristate "Texas Instruments CPPI 4.1 DMA support"
depends on (ARCH_OMAP || ARCH_DAVINCI_DA8XX)
select DMA_ENGINE
help
The Communications Port Programming Interface (CPPI) 4.1 DMA engine
is currently used by the USB driver on AM335x and DA8xx platforms.
config TI_EDMA
tristate "Texas Instruments EDMA support"
depends on ARCH_DAVINCI || ARCH_OMAP || ARCH_KEYSTONE || COMPILE_TEST
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
select TI_DMA_CROSSBAR if (ARCH_OMAP || COMPILE_TEST)
default y
help
Enable support for the TI EDMA (Enhanced DMA) controller. This DMA
engine is found on TI DaVinci, AM33xx, AM43xx, DRA7xx and Keystone 2
parts.
config DMA_OMAP
tristate "Texas Instruments sDMA (omap-dma) support"
depends on ARCH_OMAP || COMPILE_TEST
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
select TI_DMA_CROSSBAR if (SOC_DRA7XX || COMPILE_TEST)
default y
help
Enable support for the TI sDMA (System DMA or DMA4) controller. This
DMA engine is found on OMAP and DRA7xx parts.
config TI_DMA_CROSSBAR
bool
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TI_CPPI41) += cppi41.o
obj-$(CONFIG_TI_EDMA) += edma.o
obj-$(CONFIG_DMA_OMAP) += omap-dma.o
obj-$(CONFIG_TI_DMA_CROSSBAR) += dma-crossbar.o
......@@ -11,7 +11,7 @@
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/pm_runtime.h>
#include "dmaengine.h"
#include "../dmaengine.h"
#define DESC_TYPE 27
#define DESC_TYPE_HOST 0x10
......
......@@ -33,8 +33,8 @@
#include <linux/platform_data/edma.h>
#include "dmaengine.h"
#include "virt-dma.h"
#include "../dmaengine.h"
#include "../virt-dma.h"
/* Offsets matching "struct edmacc_param" */
#define PARM_OPT 0x00
......
......@@ -21,7 +21,7 @@
#include <linux/of_dma.h>
#include <linux/of_device.h>
#include "virt-dma.h"
#include "../virt-dma.h"
#define OMAP_SDMA_REQUESTS 127
#define OMAP_SDMA_CHANNELS 32
......
......@@ -1244,8 +1244,7 @@ static void txx9dmac_shutdown(struct platform_device *pdev)
static int txx9dmac_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct txx9dmac_dev *ddev = platform_get_drvdata(pdev);
struct txx9dmac_dev *ddev = dev_get_drvdata(dev);
txx9dmac_off(ddev);
return 0;
......@@ -1253,9 +1252,8 @@ static int txx9dmac_suspend_noirq(struct device *dev)
static int txx9dmac_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct txx9dmac_dev *ddev = platform_get_drvdata(pdev);
struct txx9dmac_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct txx9dmac_dev *ddev = dev_get_drvdata(dev);
struct txx9dmac_platform_data *pdata = dev_get_platdata(dev);
u32 mcr;
mcr = TXX9_DMA_MCR_MSTEN | MCR_LE;
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _SPRD_DMA_H_
#define _SPRD_DMA_H_
#define SPRD_DMA_REQ_SHIFT 16
#define SPRD_DMA_FLAGS(req_mode, int_type) \
((req_mode) << SPRD_DMA_REQ_SHIFT | (int_type))
/*
* enum sprd_dma_req_mode: define the DMA request mode
* @SPRD_DMA_FRAG_REQ: fragment request mode
* @SPRD_DMA_BLK_REQ: block request mode
* @SPRD_DMA_TRANS_REQ: transaction request mode
* @SPRD_DMA_LIST_REQ: link-list request mode
*
* We have 4 types request mode: fragment mode, block mode, transaction mode
* and linklist mode. One transaction can contain several blocks, one block can
* contain several fragments. Link-list mode means we can save several DMA
* configuration into one reserved memory, then DMA can fetch each DMA
* configuration automatically to start transfer.
*/
enum sprd_dma_req_mode {
SPRD_DMA_FRAG_REQ,
SPRD_DMA_BLK_REQ,
SPRD_DMA_TRANS_REQ,
SPRD_DMA_LIST_REQ,
};
/*
* enum sprd_dma_int_type: define the DMA interrupt type
* @SPRD_DMA_NO_INT: do not need generate DMA interrupts.
* @SPRD_DMA_FRAG_INT: fragment done interrupt when one fragment request
* is done.
* @SPRD_DMA_BLK_INT: block done interrupt when one block request is done.
* @SPRD_DMA_BLK_FRAG_INT: block and fragment interrupt when one fragment
* or one block request is done.
* @SPRD_DMA_TRANS_INT: tansaction done interrupt when one transaction
* request is done.
* @SPRD_DMA_TRANS_FRAG_INT: transaction and fragment interrupt when one
* transaction request or fragment request is done.
* @SPRD_DMA_TRANS_BLK_INT: transaction and block interrupt when one
* transaction request or block request is done.
* @SPRD_DMA_LIST_INT: link-list done interrupt when one link-list request
* is done.
* @SPRD_DMA_CFGERR_INT: configure error interrupt when configuration is
* incorrect.
*/
enum sprd_dma_int_type {
SPRD_DMA_NO_INT,
SPRD_DMA_FRAG_INT,
SPRD_DMA_BLK_INT,
SPRD_DMA_BLK_FRAG_INT,
SPRD_DMA_TRANS_INT,
SPRD_DMA_TRANS_FRAG_INT,
SPRD_DMA_TRANS_BLK_INT,
SPRD_DMA_LIST_INT,
SPRD_DMA_CFGERR_INT,
};
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment