hc
2024-12-19 9370bb92b2d16684ee45cf24e879c93c509162da
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
 */
 
#include <linux/dmaengine.h>
#include <crypto/scatterwalk.h>
 
#include "dma.h"
 
int qce_dma_request(struct device *dev, struct qce_dma_data *dma)
{
   int ret;
 
   dma->txchan = dma_request_chan(dev, "tx");
   if (IS_ERR(dma->txchan))
       return PTR_ERR(dma->txchan);
 
   dma->rxchan = dma_request_chan(dev, "rx");
   if (IS_ERR(dma->rxchan)) {
       ret = PTR_ERR(dma->rxchan);
       goto error_rx;
   }
 
   dma->result_buf = kmalloc(QCE_RESULT_BUF_SZ + QCE_IGNORE_BUF_SZ,
                 GFP_KERNEL);
   if (!dma->result_buf) {
       ret = -ENOMEM;
       goto error_nomem;
   }
 
   dma->ignore_buf = dma->result_buf + QCE_RESULT_BUF_SZ;
 
   return 0;
error_nomem:
   dma_release_channel(dma->rxchan);
error_rx:
   dma_release_channel(dma->txchan);
   return ret;
}
 
void qce_dma_release(struct qce_dma_data *dma)
{
   dma_release_channel(dma->txchan);
   dma_release_channel(dma->rxchan);
   kfree(dma->result_buf);
}
 
struct scatterlist *
qce_sgtable_add(struct sg_table *sgt, struct scatterlist *new_sgl,
       unsigned int max_len)
{
   struct scatterlist *sg = sgt->sgl, *sg_last = NULL;
   unsigned int new_len;
 
   while (sg) {
       if (!sg_page(sg))
           break;
       sg = sg_next(sg);
   }
 
   if (!sg)
       return ERR_PTR(-EINVAL);
 
   while (new_sgl && sg && max_len) {
       new_len = new_sgl->length > max_len ? max_len : new_sgl->length;
       sg_set_page(sg, sg_page(new_sgl), new_len, new_sgl->offset);
       sg_last = sg;
       sg = sg_next(sg);
       new_sgl = sg_next(new_sgl);
       max_len -= new_len;
   }
 
   return sg_last;
}
 
static int qce_dma_prep_sg(struct dma_chan *chan, struct scatterlist *sg,
              int nents, unsigned long flags,
              enum dma_transfer_direction dir,
              dma_async_tx_callback cb, void *cb_param)
{
   struct dma_async_tx_descriptor *desc;
   dma_cookie_t cookie;
 
   if (!sg || !nents)
       return -EINVAL;
 
   desc = dmaengine_prep_slave_sg(chan, sg, nents, dir, flags);
   if (!desc)
       return -EINVAL;
 
   desc->callback = cb;
   desc->callback_param = cb_param;
   cookie = dmaengine_submit(desc);
 
   return dma_submit_error(cookie);
}
 
int qce_dma_prep_sgs(struct qce_dma_data *dma, struct scatterlist *rx_sg,
            int rx_nents, struct scatterlist *tx_sg, int tx_nents,
            dma_async_tx_callback cb, void *cb_param)
{
   struct dma_chan *rxchan = dma->rxchan;
   struct dma_chan *txchan = dma->txchan;
   unsigned long flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
   int ret;
 
   ret = qce_dma_prep_sg(rxchan, rx_sg, rx_nents, flags, DMA_MEM_TO_DEV,
                NULL, NULL);
   if (ret)
       return ret;
 
   return qce_dma_prep_sg(txchan, tx_sg, tx_nents, flags, DMA_DEV_TO_MEM,
                  cb, cb_param);
}
 
void qce_dma_issue_pending(struct qce_dma_data *dma)
{
   dma_async_issue_pending(dma->rxchan);
   dma_async_issue_pending(dma->txchan);
}
 
int qce_dma_terminate_all(struct qce_dma_data *dma)
{
   int ret;
 
   ret = dmaengine_terminate_all(dma->rxchan);
   return ret ?: dmaengine_terminate_all(dma->txchan);
}