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