1 | /* Copyright 2008 - 2016 Freescale Semiconductor, Inc. |
2 | * |
3 | * Redistribution and use in source and binary forms, with or without |
4 | * modification, are permitted provided that the following conditions are met: |
5 | * * Redistributions of source code must retain the above copyright |
6 | * notice, this list of conditions and the following disclaimer. |
7 | * * Redistributions in binary form must reproduce the above copyright |
8 | * notice, this list of conditions and the following disclaimer in the |
9 | * documentation and/or other materials provided with the distribution. |
10 | * * Neither the name of Freescale Semiconductor nor the |
11 | * names of its contributors may be used to endorse or promote products |
12 | * derived from this software without specific prior written permission. |
13 | * |
14 | * ALTERNATIVELY, this software may be distributed under the terms of the |
15 | * GNU General Public License ("GPL") as published by the Free Software |
16 | * Foundation, either version 2 of that License or (at your option) any |
17 | * later version. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY |
20 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
22 | * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | */ |
30 | |
31 | #include "qman_test.h" |
32 | |
33 | #define CGR_ID 27 |
34 | #define POOL_ID 2 |
35 | #define FQ_FLAGS QMAN_FQ_FLAG_DYNAMIC_FQID |
36 | #define NUM_ENQUEUES 10 |
37 | #define NUM_PARTIAL 4 |
38 | #define PORTAL_SDQCR (QM_SDQCR_SOURCE_CHANNELS | \ |
39 | QM_SDQCR_TYPE_PRIO_QOS | \ |
40 | QM_SDQCR_TOKEN_SET(0x98) | \ |
41 | QM_SDQCR_CHANNELS_DEDICATED | \ |
42 | QM_SDQCR_CHANNELS_POOL(POOL_ID)) |
43 | #define PORTAL_OPAQUE ((void *)0xf00dbeef) |
44 | #define VDQCR_FLAGS (QMAN_VOLATILE_FLAG_WAIT | QMAN_VOLATILE_FLAG_FINISH) |
45 | |
46 | static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *, |
47 | struct qman_fq *, |
48 | const struct qm_dqrr_entry *, |
49 | bool sched_napi); |
50 | static void cb_ern(struct qman_portal *, struct qman_fq *, |
51 | const union qm_mr_entry *); |
52 | static void cb_fqs(struct qman_portal *, struct qman_fq *, |
53 | const union qm_mr_entry *); |
54 | |
55 | static struct qm_fd fd, fd_dq; |
56 | static struct qman_fq fq_base = { |
57 | .cb.dqrr = cb_dqrr, |
58 | .cb.ern = cb_ern, |
59 | .cb.fqs = cb_fqs |
60 | }; |
61 | static DECLARE_WAIT_QUEUE_HEAD(waitqueue); |
62 | static int retire_complete, sdqcr_complete; |
63 | |
64 | /* Helpers for initialising and "incrementing" a frame descriptor */ |
65 | static void fd_init(struct qm_fd *fd) |
66 | { |
67 | qm_fd_addr_set64(fd, addr: 0xabdeadbeefLLU); |
68 | qm_fd_set_contig_big(fd, 0x0000ffff); |
69 | fd->cmd = cpu_to_be32(0xfeedf00d); |
70 | } |
71 | |
72 | static void fd_inc(struct qm_fd *fd) |
73 | { |
74 | u64 t = qm_fd_addr_get64(fd); |
75 | int z = t >> 40; |
76 | unsigned int len, off; |
77 | enum qm_fd_format fmt; |
78 | |
79 | t <<= 1; |
80 | if (z) |
81 | t |= 1; |
82 | qm_fd_addr_set64(fd, addr: t); |
83 | |
84 | fmt = qm_fd_get_format(fd); |
85 | off = qm_fd_get_offset(fd); |
86 | len = qm_fd_get_length(fd); |
87 | len--; |
88 | qm_fd_set_param(fd, fmt, off, len); |
89 | |
90 | be32_add_cpu(var: &fd->cmd, val: 1); |
91 | } |
92 | |
93 | /* The only part of the 'fd' we can't memcmp() is the ppid */ |
94 | static bool fd_neq(const struct qm_fd *a, const struct qm_fd *b) |
95 | { |
96 | bool neq = qm_fd_addr_get64(fd: a) != qm_fd_addr_get64(fd: b); |
97 | |
98 | neq |= qm_fd_get_format(fd: a) != qm_fd_get_format(fd: b); |
99 | neq |= a->cfg != b->cfg; |
100 | neq |= a->cmd != b->cmd; |
101 | |
102 | return neq; |
103 | } |
104 | |
105 | /* test */ |
106 | static int do_enqueues(struct qman_fq *fq) |
107 | { |
108 | unsigned int loop; |
109 | int err = 0; |
110 | |
111 | for (loop = 0; loop < NUM_ENQUEUES; loop++) { |
112 | if (qman_enqueue(fq, fd: &fd)) { |
113 | pr_crit("qman_enqueue() failed\n" ); |
114 | err = -EIO; |
115 | } |
116 | fd_inc(fd: &fd); |
117 | } |
118 | |
119 | return err; |
120 | } |
121 | |
122 | int qman_test_api(void) |
123 | { |
124 | unsigned int flags, frmcnt; |
125 | int err; |
126 | struct qman_fq *fq = &fq_base; |
127 | |
128 | pr_info("%s(): Starting\n" , __func__); |
129 | fd_init(fd: &fd); |
130 | fd_init(fd: &fd_dq); |
131 | |
132 | /* Initialise (parked) FQ */ |
133 | err = qman_create_fq(fqid: 0, FQ_FLAGS, fq); |
134 | if (err) { |
135 | pr_crit("qman_create_fq() failed\n" ); |
136 | goto failed; |
137 | } |
138 | err = qman_init_fq(fq, QMAN_INITFQ_FLAG_LOCAL, NULL); |
139 | if (err) { |
140 | pr_crit("qman_init_fq() failed\n" ); |
141 | goto failed; |
142 | } |
143 | /* Do enqueues + VDQCR, twice. (Parked FQ) */ |
144 | err = do_enqueues(fq); |
145 | if (err) |
146 | goto failed; |
147 | pr_info("VDQCR (till-empty);\n" ); |
148 | frmcnt = QM_VDQCR_NUMFRAMES_TILLEMPTY; |
149 | err = qman_volatile_dequeue(fq, VDQCR_FLAGS, vdqcr: frmcnt); |
150 | if (err) { |
151 | pr_crit("qman_volatile_dequeue() failed\n" ); |
152 | goto failed; |
153 | } |
154 | err = do_enqueues(fq); |
155 | if (err) |
156 | goto failed; |
157 | pr_info("VDQCR (%d of %d);\n" , NUM_PARTIAL, NUM_ENQUEUES); |
158 | frmcnt = QM_VDQCR_NUMFRAMES_SET(NUM_PARTIAL); |
159 | err = qman_volatile_dequeue(fq, VDQCR_FLAGS, vdqcr: frmcnt); |
160 | if (err) { |
161 | pr_crit("qman_volatile_dequeue() failed\n" ); |
162 | goto failed; |
163 | } |
164 | pr_info("VDQCR (%d of %d);\n" , NUM_ENQUEUES - NUM_PARTIAL, |
165 | NUM_ENQUEUES); |
166 | frmcnt = QM_VDQCR_NUMFRAMES_SET(NUM_ENQUEUES - NUM_PARTIAL); |
167 | err = qman_volatile_dequeue(fq, VDQCR_FLAGS, vdqcr: frmcnt); |
168 | if (err) { |
169 | pr_err("qman_volatile_dequeue() failed\n" ); |
170 | goto failed; |
171 | } |
172 | |
173 | err = do_enqueues(fq); |
174 | if (err) |
175 | goto failed; |
176 | pr_info("scheduled dequeue (till-empty)\n" ); |
177 | err = qman_schedule_fq(fq); |
178 | if (err) { |
179 | pr_crit("qman_schedule_fq() failed\n" ); |
180 | goto failed; |
181 | } |
182 | wait_event(waitqueue, sdqcr_complete); |
183 | |
184 | /* Retire and OOS the FQ */ |
185 | err = qman_retire_fq(fq, flags: &flags); |
186 | if (err < 0) { |
187 | pr_crit("qman_retire_fq() failed\n" ); |
188 | goto failed; |
189 | } |
190 | wait_event(waitqueue, retire_complete); |
191 | if (flags & QMAN_FQ_STATE_BLOCKOOS) { |
192 | err = -EIO; |
193 | pr_crit("leaking frames\n" ); |
194 | goto failed; |
195 | } |
196 | err = qman_oos_fq(fq); |
197 | if (err) { |
198 | pr_crit("qman_oos_fq() failed\n" ); |
199 | goto failed; |
200 | } |
201 | qman_destroy_fq(fq); |
202 | pr_info("%s(): Finished\n" , __func__); |
203 | return 0; |
204 | |
205 | failed: |
206 | WARN_ON(1); |
207 | return err; |
208 | } |
209 | |
210 | static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *p, |
211 | struct qman_fq *fq, |
212 | const struct qm_dqrr_entry *dq, |
213 | bool sched_napi) |
214 | { |
215 | if (WARN_ON(fd_neq(&fd_dq, &dq->fd))) { |
216 | pr_err("BADNESS: dequeued frame doesn't match;\n" ); |
217 | return qman_cb_dqrr_consume; |
218 | } |
219 | fd_inc(fd: &fd_dq); |
220 | if (!(dq->stat & QM_DQRR_STAT_UNSCHEDULED) && !fd_neq(a: &fd_dq, b: &fd)) { |
221 | sdqcr_complete = 1; |
222 | wake_up(&waitqueue); |
223 | } |
224 | return qman_cb_dqrr_consume; |
225 | } |
226 | |
227 | static void cb_ern(struct qman_portal *p, struct qman_fq *fq, |
228 | const union qm_mr_entry *msg) |
229 | { |
230 | pr_crit("cb_ern() unimplemented" ); |
231 | WARN_ON(1); |
232 | } |
233 | |
234 | static void cb_fqs(struct qman_portal *p, struct qman_fq *fq, |
235 | const union qm_mr_entry *msg) |
236 | { |
237 | u8 verb = (msg->verb & QM_MR_VERB_TYPE_MASK); |
238 | |
239 | if ((verb != QM_MR_VERB_FQRN) && (verb != QM_MR_VERB_FQRNI)) { |
240 | pr_crit("unexpected FQS message" ); |
241 | WARN_ON(1); |
242 | return; |
243 | } |
244 | pr_info("Retirement message received\n" ); |
245 | retire_complete = 1; |
246 | wake_up(&waitqueue); |
247 | } |
248 | |