162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * copy offload engine support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright © 2006, Intel Corporation.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *      Dan Williams <dan.j.williams@intel.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *      with architecture considerations by:
1062306a36Sopenharmony_ci *      Neil Brown <neilb@suse.de>
1162306a36Sopenharmony_ci *      Jeff Garzik <jeff@garzik.org>
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/highmem.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/mm.h>
1762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1862306a36Sopenharmony_ci#include <linux/async_tx.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/**
2162306a36Sopenharmony_ci * async_memcpy - attempt to copy memory with a dma engine.
2262306a36Sopenharmony_ci * @dest: destination page
2362306a36Sopenharmony_ci * @src: src page
2462306a36Sopenharmony_ci * @dest_offset: offset into 'dest' to start transaction
2562306a36Sopenharmony_ci * @src_offset: offset into 'src' to start transaction
2662306a36Sopenharmony_ci * @len: length in bytes
2762306a36Sopenharmony_ci * @submit: submission / completion modifiers
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * honored flags: ASYNC_TX_ACK
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_cistruct dma_async_tx_descriptor *
3262306a36Sopenharmony_ciasync_memcpy(struct page *dest, struct page *src, unsigned int dest_offset,
3362306a36Sopenharmony_ci	     unsigned int src_offset, size_t len,
3462306a36Sopenharmony_ci	     struct async_submit_ctl *submit)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct dma_chan *chan = async_tx_find_channel(submit, DMA_MEMCPY,
3762306a36Sopenharmony_ci						      &dest, 1, &src, 1, len);
3862306a36Sopenharmony_ci	struct dma_device *device = chan ? chan->device : NULL;
3962306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx = NULL;
4062306a36Sopenharmony_ci	struct dmaengine_unmap_data *unmap = NULL;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (device)
4362306a36Sopenharmony_ci		unmap = dmaengine_get_unmap_data(device->dev, 2, GFP_NOWAIT);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (unmap && is_dma_copy_aligned(device, src_offset, dest_offset, len)) {
4662306a36Sopenharmony_ci		unsigned long dma_prep_flags = 0;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci		if (submit->cb_fn)
4962306a36Sopenharmony_ci			dma_prep_flags |= DMA_PREP_INTERRUPT;
5062306a36Sopenharmony_ci		if (submit->flags & ASYNC_TX_FENCE)
5162306a36Sopenharmony_ci			dma_prep_flags |= DMA_PREP_FENCE;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci		unmap->to_cnt = 1;
5462306a36Sopenharmony_ci		unmap->addr[0] = dma_map_page(device->dev, src, src_offset, len,
5562306a36Sopenharmony_ci					      DMA_TO_DEVICE);
5662306a36Sopenharmony_ci		unmap->from_cnt = 1;
5762306a36Sopenharmony_ci		unmap->addr[1] = dma_map_page(device->dev, dest, dest_offset, len,
5862306a36Sopenharmony_ci					      DMA_FROM_DEVICE);
5962306a36Sopenharmony_ci		unmap->len = len;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		tx = device->device_prep_dma_memcpy(chan, unmap->addr[1],
6262306a36Sopenharmony_ci						    unmap->addr[0], len,
6362306a36Sopenharmony_ci						    dma_prep_flags);
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (tx) {
6762306a36Sopenharmony_ci		pr_debug("%s: (async) len: %zu\n", __func__, len);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		dma_set_unmap(tx, unmap);
7062306a36Sopenharmony_ci		async_tx_submit(chan, tx, submit);
7162306a36Sopenharmony_ci	} else {
7262306a36Sopenharmony_ci		void *dest_buf, *src_buf;
7362306a36Sopenharmony_ci		pr_debug("%s: (sync) len: %zu\n", __func__, len);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci		/* wait for any prerequisite operations */
7662306a36Sopenharmony_ci		async_tx_quiesce(&submit->depend_tx);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		dest_buf = kmap_atomic(dest) + dest_offset;
7962306a36Sopenharmony_ci		src_buf = kmap_atomic(src) + src_offset;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		memcpy(dest_buf, src_buf, len);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		kunmap_atomic(src_buf);
8462306a36Sopenharmony_ci		kunmap_atomic(dest_buf);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		async_tx_sync_epilog(submit);
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	dmaengine_unmap_put(unmap);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return tx;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(async_memcpy);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation");
9662306a36Sopenharmony_ciMODULE_DESCRIPTION("asynchronous memcpy api");
9762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
98