18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 78c2ecf20Sopenharmony_ci#include <crypto/scatterwalk.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "dma.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ciint qce_dma_request(struct device *dev, struct qce_dma_data *dma) 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci int ret; 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci dma->txchan = dma_request_chan(dev, "tx"); 168c2ecf20Sopenharmony_ci if (IS_ERR(dma->txchan)) 178c2ecf20Sopenharmony_ci return PTR_ERR(dma->txchan); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci dma->rxchan = dma_request_chan(dev, "rx"); 208c2ecf20Sopenharmony_ci if (IS_ERR(dma->rxchan)) { 218c2ecf20Sopenharmony_ci ret = PTR_ERR(dma->rxchan); 228c2ecf20Sopenharmony_ci goto error_rx; 238c2ecf20Sopenharmony_ci } 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci dma->result_buf = kmalloc(QCE_RESULT_BUF_SZ + QCE_IGNORE_BUF_SZ, 268c2ecf20Sopenharmony_ci GFP_KERNEL); 278c2ecf20Sopenharmony_ci if (!dma->result_buf) { 288c2ecf20Sopenharmony_ci ret = -ENOMEM; 298c2ecf20Sopenharmony_ci goto error_nomem; 308c2ecf20Sopenharmony_ci } 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci dma->ignore_buf = dma->result_buf + QCE_RESULT_BUF_SZ; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci return 0; 358c2ecf20Sopenharmony_cierror_nomem: 368c2ecf20Sopenharmony_ci dma_release_channel(dma->rxchan); 378c2ecf20Sopenharmony_cierror_rx: 388c2ecf20Sopenharmony_ci dma_release_channel(dma->txchan); 398c2ecf20Sopenharmony_ci return ret; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_civoid qce_dma_release(struct qce_dma_data *dma) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci dma_release_channel(dma->txchan); 458c2ecf20Sopenharmony_ci dma_release_channel(dma->rxchan); 468c2ecf20Sopenharmony_ci kfree(dma->result_buf); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct scatterlist * 508c2ecf20Sopenharmony_ciqce_sgtable_add(struct sg_table *sgt, struct scatterlist *new_sgl, 518c2ecf20Sopenharmony_ci unsigned int max_len) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct scatterlist *sg = sgt->sgl, *sg_last = NULL; 548c2ecf20Sopenharmony_ci unsigned int new_len; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci while (sg) { 578c2ecf20Sopenharmony_ci if (!sg_page(sg)) 588c2ecf20Sopenharmony_ci break; 598c2ecf20Sopenharmony_ci sg = sg_next(sg); 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (!sg) 638c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci while (new_sgl && sg && max_len) { 668c2ecf20Sopenharmony_ci new_len = new_sgl->length > max_len ? max_len : new_sgl->length; 678c2ecf20Sopenharmony_ci sg_set_page(sg, sg_page(new_sgl), new_len, new_sgl->offset); 688c2ecf20Sopenharmony_ci sg_last = sg; 698c2ecf20Sopenharmony_ci sg = sg_next(sg); 708c2ecf20Sopenharmony_ci new_sgl = sg_next(new_sgl); 718c2ecf20Sopenharmony_ci max_len -= new_len; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return sg_last; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int qce_dma_prep_sg(struct dma_chan *chan, struct scatterlist *sg, 788c2ecf20Sopenharmony_ci int nents, unsigned long flags, 798c2ecf20Sopenharmony_ci enum dma_transfer_direction dir, 808c2ecf20Sopenharmony_ci dma_async_tx_callback cb, void *cb_param) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *desc; 838c2ecf20Sopenharmony_ci dma_cookie_t cookie; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (!sg || !nents) 868c2ecf20Sopenharmony_ci return -EINVAL; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci desc = dmaengine_prep_slave_sg(chan, sg, nents, dir, flags); 898c2ecf20Sopenharmony_ci if (!desc) 908c2ecf20Sopenharmony_ci return -EINVAL; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci desc->callback = cb; 938c2ecf20Sopenharmony_ci desc->callback_param = cb_param; 948c2ecf20Sopenharmony_ci cookie = dmaengine_submit(desc); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return dma_submit_error(cookie); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ciint qce_dma_prep_sgs(struct qce_dma_data *dma, struct scatterlist *rx_sg, 1008c2ecf20Sopenharmony_ci int rx_nents, struct scatterlist *tx_sg, int tx_nents, 1018c2ecf20Sopenharmony_ci dma_async_tx_callback cb, void *cb_param) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct dma_chan *rxchan = dma->rxchan; 1048c2ecf20Sopenharmony_ci struct dma_chan *txchan = dma->txchan; 1058c2ecf20Sopenharmony_ci unsigned long flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; 1068c2ecf20Sopenharmony_ci int ret; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ret = qce_dma_prep_sg(rxchan, rx_sg, rx_nents, flags, DMA_MEM_TO_DEV, 1098c2ecf20Sopenharmony_ci NULL, NULL); 1108c2ecf20Sopenharmony_ci if (ret) 1118c2ecf20Sopenharmony_ci return ret; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return qce_dma_prep_sg(txchan, tx_sg, tx_nents, flags, DMA_DEV_TO_MEM, 1148c2ecf20Sopenharmony_ci cb, cb_param); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_civoid qce_dma_issue_pending(struct qce_dma_data *dma) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci dma_async_issue_pending(dma->rxchan); 1208c2ecf20Sopenharmony_ci dma_async_issue_pending(dma->txchan); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ciint qce_dma_terminate_all(struct qce_dma_data *dma) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci int ret; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci ret = dmaengine_terminate_all(dma->rxchan); 1288c2ecf20Sopenharmony_ci return ret ?: dmaengine_terminate_all(dma->txchan); 1298c2ecf20Sopenharmony_ci} 130