diff options
author | Claudiu Manoil <claudiu.manoil@nxp.com> | 2016-09-22 11:04:11 -0400 |
---|---|---|
committer | Scott Wood <oss@buserror.net> | 2016-09-25 03:39:00 -0400 |
commit | de7756233994b48cfc4d948904380cd0453a0063 (patch) | |
tree | 55469a0e43833662e500f1efa3380e02e41a877f /drivers/soc/fsl/qbman/qman_test_api.c | |
parent | 97e0d385b13998980252ff63123d8ebc4138db0a (diff) |
soc/qman: Add self-test for QMan driver
Add self tests for the DPAA 1.x Queue Manager driver. The tests
ensure that the driver can properly enqueue and dequeue to/from
frame queues using the QMan portal infrastructure.
Signed-off-by: Roy Pledge <roy.pledge@nxp.com>
Signed-off-by: Claudiu Manoil <claudiu.manoil@nxp.com>
Signed-off-by: Scott Wood <oss@buserror.net>
Diffstat (limited to 'drivers/soc/fsl/qbman/qman_test_api.c')
-rw-r--r-- | drivers/soc/fsl/qbman/qman_test_api.c | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/drivers/soc/fsl/qbman/qman_test_api.c b/drivers/soc/fsl/qbman/qman_test_api.c new file mode 100644 index 000000000000..6880ff17f45e --- /dev/null +++ b/drivers/soc/fsl/qbman/qman_test_api.c | |||
@@ -0,0 +1,252 @@ | |||
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 | static void cb_ern(struct qman_portal *, struct qman_fq *, | ||
50 | const union qm_mr_entry *); | ||
51 | static void cb_fqs(struct qman_portal *, struct qman_fq *, | ||
52 | const union qm_mr_entry *); | ||
53 | |||
54 | static struct qm_fd fd, fd_dq; | ||
55 | static struct qman_fq fq_base = { | ||
56 | .cb.dqrr = cb_dqrr, | ||
57 | .cb.ern = cb_ern, | ||
58 | .cb.fqs = cb_fqs | ||
59 | }; | ||
60 | static DECLARE_WAIT_QUEUE_HEAD(waitqueue); | ||
61 | static int retire_complete, sdqcr_complete; | ||
62 | |||
63 | /* Helpers for initialising and "incrementing" a frame descriptor */ | ||
64 | static void fd_init(struct qm_fd *fd) | ||
65 | { | ||
66 | qm_fd_addr_set64(fd, 0xabdeadbeefLLU); | ||
67 | qm_fd_set_contig_big(fd, 0x0000ffff); | ||
68 | fd->cmd = 0xfeedf00d; | ||
69 | } | ||
70 | |||
71 | static void fd_inc(struct qm_fd *fd) | ||
72 | { | ||
73 | u64 t = qm_fd_addr_get64(fd); | ||
74 | int z = t >> 40; | ||
75 | unsigned int len, off; | ||
76 | enum qm_fd_format fmt; | ||
77 | |||
78 | t <<= 1; | ||
79 | if (z) | ||
80 | t |= 1; | ||
81 | qm_fd_addr_set64(fd, t); | ||
82 | |||
83 | fmt = qm_fd_get_format(fd); | ||
84 | off = qm_fd_get_offset(fd); | ||
85 | len = qm_fd_get_length(fd); | ||
86 | len--; | ||
87 | qm_fd_set_param(fd, fmt, off, len); | ||
88 | |||
89 | fd->cmd++; | ||
90 | } | ||
91 | |||
92 | /* The only part of the 'fd' we can't memcmp() is the ppid */ | ||
93 | static int fd_cmp(const struct qm_fd *a, const struct qm_fd *b) | ||
94 | { | ||
95 | int r = (qm_fd_addr_get64(a) == qm_fd_addr_get64(b)) ? 0 : -1; | ||
96 | |||
97 | if (!r) { | ||
98 | enum qm_fd_format fmt_a, fmt_b; | ||
99 | |||
100 | fmt_a = qm_fd_get_format(a); | ||
101 | fmt_b = qm_fd_get_format(b); | ||
102 | r = fmt_a - fmt_b; | ||
103 | } | ||
104 | if (!r) | ||
105 | r = a->cfg - b->cfg; | ||
106 | if (!r) | ||
107 | r = a->cmd - b->cmd; | ||
108 | return r; | ||
109 | } | ||
110 | |||
111 | /* test */ | ||
112 | static int do_enqueues(struct qman_fq *fq) | ||
113 | { | ||
114 | unsigned int loop; | ||
115 | int err = 0; | ||
116 | |||
117 | for (loop = 0; loop < NUM_ENQUEUES; loop++) { | ||
118 | if (qman_enqueue(fq, &fd)) { | ||
119 | pr_crit("qman_enqueue() failed\n"); | ||
120 | err = -EIO; | ||
121 | } | ||
122 | fd_inc(&fd); | ||
123 | } | ||
124 | |||
125 | return err; | ||
126 | } | ||
127 | |||
128 | int qman_test_api(void) | ||
129 | { | ||
130 | unsigned int flags, frmcnt; | ||
131 | int err; | ||
132 | struct qman_fq *fq = &fq_base; | ||
133 | |||
134 | pr_info("%s(): Starting\n", __func__); | ||
135 | fd_init(&fd); | ||
136 | fd_init(&fd_dq); | ||
137 | |||
138 | /* Initialise (parked) FQ */ | ||
139 | err = qman_create_fq(0, FQ_FLAGS, fq); | ||
140 | if (err) { | ||
141 | pr_crit("qman_create_fq() failed\n"); | ||
142 | goto failed; | ||
143 | } | ||
144 | err = qman_init_fq(fq, QMAN_INITFQ_FLAG_LOCAL, NULL); | ||
145 | if (err) { | ||
146 | pr_crit("qman_init_fq() failed\n"); | ||
147 | goto failed; | ||
148 | } | ||
149 | /* Do enqueues + VDQCR, twice. (Parked FQ) */ | ||
150 | err = do_enqueues(fq); | ||
151 | if (err) | ||
152 | goto failed; | ||
153 | pr_info("VDQCR (till-empty);\n"); | ||
154 | frmcnt = QM_VDQCR_NUMFRAMES_TILLEMPTY; | ||
155 | err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt); | ||
156 | if (err) { | ||
157 | pr_crit("qman_volatile_dequeue() failed\n"); | ||
158 | goto failed; | ||
159 | } | ||
160 | err = do_enqueues(fq); | ||
161 | if (err) | ||
162 | goto failed; | ||
163 | pr_info("VDQCR (%d of %d);\n", NUM_PARTIAL, NUM_ENQUEUES); | ||
164 | frmcnt = QM_VDQCR_NUMFRAMES_SET(NUM_PARTIAL); | ||
165 | err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt); | ||
166 | if (err) { | ||
167 | pr_crit("qman_volatile_dequeue() failed\n"); | ||
168 | goto failed; | ||
169 | } | ||
170 | pr_info("VDQCR (%d of %d);\n", NUM_ENQUEUES - NUM_PARTIAL, | ||
171 | NUM_ENQUEUES); | ||
172 | frmcnt = QM_VDQCR_NUMFRAMES_SET(NUM_ENQUEUES - NUM_PARTIAL); | ||
173 | err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt); | ||
174 | if (err) { | ||
175 | pr_err("qman_volatile_dequeue() failed\n"); | ||
176 | goto failed; | ||
177 | } | ||
178 | |||
179 | err = do_enqueues(fq); | ||
180 | if (err) | ||
181 | goto failed; | ||
182 | pr_info("scheduled dequeue (till-empty)\n"); | ||
183 | err = qman_schedule_fq(fq); | ||
184 | if (err) { | ||
185 | pr_crit("qman_schedule_fq() failed\n"); | ||
186 | goto failed; | ||
187 | } | ||
188 | wait_event(waitqueue, sdqcr_complete); | ||
189 | |||
190 | /* Retire and OOS the FQ */ | ||
191 | err = qman_retire_fq(fq, &flags); | ||
192 | if (err < 0) { | ||
193 | pr_crit("qman_retire_fq() failed\n"); | ||
194 | goto failed; | ||
195 | } | ||
196 | wait_event(waitqueue, retire_complete); | ||
197 | if (flags & QMAN_FQ_STATE_BLOCKOOS) { | ||
198 | err = -EIO; | ||
199 | pr_crit("leaking frames\n"); | ||
200 | goto failed; | ||
201 | } | ||
202 | err = qman_oos_fq(fq); | ||
203 | if (err) { | ||
204 | pr_crit("qman_oos_fq() failed\n"); | ||
205 | goto failed; | ||
206 | } | ||
207 | qman_destroy_fq(fq); | ||
208 | pr_info("%s(): Finished\n", __func__); | ||
209 | return 0; | ||
210 | |||
211 | failed: | ||
212 | WARN_ON(1); | ||
213 | return err; | ||
214 | } | ||
215 | |||
216 | static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *p, | ||
217 | struct qman_fq *fq, | ||
218 | const struct qm_dqrr_entry *dq) | ||
219 | { | ||
220 | if (WARN_ON(fd_cmp(&fd_dq, &dq->fd))) { | ||
221 | pr_err("BADNESS: dequeued frame doesn't match;\n"); | ||
222 | return qman_cb_dqrr_consume; | ||
223 | } | ||
224 | fd_inc(&fd_dq); | ||
225 | if (!(dq->stat & QM_DQRR_STAT_UNSCHEDULED) && !fd_cmp(&fd_dq, &fd)) { | ||
226 | sdqcr_complete = 1; | ||
227 | wake_up(&waitqueue); | ||
228 | } | ||
229 | return qman_cb_dqrr_consume; | ||
230 | } | ||
231 | |||
232 | static void cb_ern(struct qman_portal *p, struct qman_fq *fq, | ||
233 | const union qm_mr_entry *msg) | ||
234 | { | ||
235 | pr_crit("cb_ern() unimplemented"); | ||
236 | WARN_ON(1); | ||
237 | } | ||
238 | |||
239 | static void cb_fqs(struct qman_portal *p, struct qman_fq *fq, | ||
240 | const union qm_mr_entry *msg) | ||
241 | { | ||
242 | u8 verb = (msg->verb & QM_MR_VERB_TYPE_MASK); | ||
243 | |||
244 | if ((verb != QM_MR_VERB_FQRN) && (verb != QM_MR_VERB_FQRNI)) { | ||
245 | pr_crit("unexpected FQS message"); | ||
246 | WARN_ON(1); | ||
247 | return; | ||
248 | } | ||
249 | pr_info("Retirement message received\n"); | ||
250 | retire_complete = 1; | ||
251 | wake_up(&waitqueue); | ||
252 | } | ||