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 | |
| 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')
| -rw-r--r-- | drivers/soc/fsl/qbman/Kconfig | 23 | ||||
| -rw-r--r-- | drivers/soc/fsl/qbman/Makefile | 5 | ||||
| -rw-r--r-- | drivers/soc/fsl/qbman/qman_test.c | 62 | ||||
| -rw-r--r-- | drivers/soc/fsl/qbman/qman_test.h | 36 | ||||
| -rw-r--r-- | drivers/soc/fsl/qbman/qman_test_api.c | 252 | ||||
| -rw-r--r-- | drivers/soc/fsl/qbman/qman_test_stash.c | 617 |
6 files changed, 995 insertions, 0 deletions
diff --git a/drivers/soc/fsl/qbman/Kconfig b/drivers/soc/fsl/qbman/Kconfig index e6da6a2f396d..757033c0586c 100644 --- a/drivers/soc/fsl/qbman/Kconfig +++ b/drivers/soc/fsl/qbman/Kconfig | |||
| @@ -41,4 +41,27 @@ config FSL_BMAN_TEST_API | |||
| 41 | high-level API testing with them (whichever portal(s) are affine | 41 | high-level API testing with them (whichever portal(s) are affine |
| 42 | to the cpu(s) the test executes on). | 42 | to the cpu(s) the test executes on). |
| 43 | 43 | ||
| 44 | config FSL_QMAN_TEST | ||
| 45 | tristate "QMan self-tests" | ||
| 46 | help | ||
| 47 | Compile self-test code for QMan. | ||
| 48 | |||
| 49 | config FSL_QMAN_TEST_API | ||
| 50 | bool "QMan high-level self-test" | ||
| 51 | depends on FSL_QMAN_TEST | ||
| 52 | default y | ||
| 53 | help | ||
| 54 | This requires the presence of cpu-affine portals, and performs | ||
| 55 | high-level API testing with them (whichever portal(s) are affine to | ||
| 56 | the cpu(s) the test executes on). | ||
| 57 | |||
| 58 | config FSL_QMAN_TEST_STASH | ||
| 59 | bool "QMan 'hot potato' data-stashing self-test" | ||
| 60 | depends on FSL_QMAN_TEST | ||
| 61 | default y | ||
| 62 | help | ||
| 63 | This performs a "hot potato" style test enqueuing/dequeuing a frame | ||
| 64 | across a series of FQs scheduled to different portals (and cpus), with | ||
| 65 | DQRR, data and context stashing always on. | ||
| 66 | |||
| 44 | endif # FSL_DPAA | 67 | endif # FSL_DPAA |
diff --git a/drivers/soc/fsl/qbman/Makefile b/drivers/soc/fsl/qbman/Makefile index 714dd97484b3..7ae199f1664e 100644 --- a/drivers/soc/fsl/qbman/Makefile +++ b/drivers/soc/fsl/qbman/Makefile | |||
| @@ -5,3 +5,8 @@ obj-$(CONFIG_FSL_DPAA) += bman_ccsr.o qman_ccsr.o \ | |||
| 5 | obj-$(CONFIG_FSL_BMAN_TEST) += bman-test.o | 5 | obj-$(CONFIG_FSL_BMAN_TEST) += bman-test.o |
| 6 | bman-test-y = bman_test.o | 6 | bman-test-y = bman_test.o |
| 7 | bman-test-$(CONFIG_FSL_BMAN_TEST_API) += bman_test_api.o | 7 | bman-test-$(CONFIG_FSL_BMAN_TEST_API) += bman_test_api.o |
| 8 | |||
| 9 | obj-$(CONFIG_FSL_QMAN_TEST) += qman-test.o | ||
| 10 | qman-test-y = qman_test.o | ||
| 11 | qman-test-$(CONFIG_FSL_QMAN_TEST_API) += qman_test_api.o | ||
| 12 | qman-test-$(CONFIG_FSL_QMAN_TEST_STASH) += qman_test_stash.o | ||
diff --git a/drivers/soc/fsl/qbman/qman_test.c b/drivers/soc/fsl/qbman/qman_test.c new file mode 100644 index 000000000000..18f7f0202fa7 --- /dev/null +++ b/drivers/soc/fsl/qbman/qman_test.c | |||
| @@ -0,0 +1,62 @@ | |||
| 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 | MODULE_AUTHOR("Geoff Thorpe"); | ||
| 34 | MODULE_LICENSE("Dual BSD/GPL"); | ||
| 35 | MODULE_DESCRIPTION("QMan testing"); | ||
| 36 | |||
| 37 | static int test_init(void) | ||
| 38 | { | ||
| 39 | int loop = 1; | ||
| 40 | int err = 0; | ||
| 41 | |||
| 42 | while (loop--) { | ||
| 43 | #ifdef CONFIG_FSL_QMAN_TEST_STASH | ||
| 44 | err = qman_test_stash(); | ||
| 45 | if (err) | ||
| 46 | break; | ||
| 47 | #endif | ||
| 48 | #ifdef CONFIG_FSL_QMAN_TEST_API | ||
| 49 | err = qman_test_api(); | ||
| 50 | if (err) | ||
| 51 | break; | ||
| 52 | #endif | ||
| 53 | } | ||
| 54 | return err; | ||
| 55 | } | ||
| 56 | |||
| 57 | static void test_exit(void) | ||
| 58 | { | ||
| 59 | } | ||
| 60 | |||
| 61 | module_init(test_init); | ||
| 62 | module_exit(test_exit); | ||
diff --git a/drivers/soc/fsl/qbman/qman_test.h b/drivers/soc/fsl/qbman/qman_test.h new file mode 100644 index 000000000000..d5f8cb2260dc --- /dev/null +++ b/drivers/soc/fsl/qbman/qman_test.h | |||
| @@ -0,0 +1,36 @@ | |||
| 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_priv.h" | ||
| 32 | |||
| 33 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 34 | |||
| 35 | int qman_test_stash(void); | ||
| 36 | int qman_test_api(void); | ||
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 | } | ||
diff --git a/drivers/soc/fsl/qbman/qman_test_stash.c b/drivers/soc/fsl/qbman/qman_test_stash.c new file mode 100644 index 000000000000..43cf66ba42f5 --- /dev/null +++ b/drivers/soc/fsl/qbman/qman_test_stash.c | |||
| @@ -0,0 +1,617 @@ | |||
| 1 | /* Copyright 2009 - 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 | #include <linux/dma-mapping.h> | ||
| 34 | #include <linux/delay.h> | ||
| 35 | |||
| 36 | /* | ||
| 37 | * Algorithm: | ||
| 38 | * | ||
| 39 | * Each cpu will have HP_PER_CPU "handlers" set up, each of which incorporates | ||
| 40 | * an rx/tx pair of FQ objects (both of which are stashed on dequeue). The | ||
| 41 | * organisation of FQIDs is such that the HP_PER_CPU*NUM_CPUS handlers will | ||
| 42 | * shuttle a "hot potato" frame around them such that every forwarding action | ||
| 43 | * moves it from one cpu to another. (The use of more than one handler per cpu | ||
| 44 | * is to allow enough handlers/FQs to truly test the significance of caching - | ||
| 45 | * ie. when cache-expiries are occurring.) | ||
| 46 | * | ||
| 47 | * The "hot potato" frame content will be HP_NUM_WORDS*4 bytes in size, and the | ||
| 48 | * first and last words of the frame data will undergo a transformation step on | ||
| 49 | * each forwarding action. To achieve this, each handler will be assigned a | ||
| 50 | * 32-bit "mixer", that is produced using a 32-bit LFSR. When a frame is | ||
| 51 | * received by a handler, the mixer of the expected sender is XOR'd into all | ||
| 52 | * words of the entire frame, which is then validated against the original | ||
| 53 | * values. Then, before forwarding, the entire frame is XOR'd with the mixer of | ||
| 54 | * the current handler. Apart from validating that the frame is taking the | ||
| 55 | * expected path, this also provides some quasi-realistic overheads to each | ||
| 56 | * forwarding action - dereferencing *all* the frame data, computation, and | ||
| 57 | * conditional branching. There is a "special" handler designated to act as the | ||
| 58 | * instigator of the test by creating an enqueuing the "hot potato" frame, and | ||
| 59 | * to determine when the test has completed by counting HP_LOOPS iterations. | ||
| 60 | * | ||
| 61 | * Init phases: | ||
| 62 | * | ||
| 63 | * 1. prepare each cpu's 'hp_cpu' struct using on_each_cpu(,,1) and link them | ||
| 64 | * into 'hp_cpu_list'. Specifically, set processor_id, allocate HP_PER_CPU | ||
| 65 | * handlers and link-list them (but do no other handler setup). | ||
| 66 | * | ||
| 67 | * 2. scan over 'hp_cpu_list' HP_PER_CPU times, the first time sets each | ||
| 68 | * hp_cpu's 'iterator' to point to its first handler. With each loop, | ||
| 69 | * allocate rx/tx FQIDs and mixer values to the hp_cpu's iterator handler | ||
| 70 | * and advance the iterator for the next loop. This includes a final fixup, | ||
| 71 | * which connects the last handler to the first (and which is why phase 2 | ||
| 72 | * and 3 are separate). | ||
| 73 | * | ||
| 74 | * 3. scan over 'hp_cpu_list' HP_PER_CPU times, the first time sets each | ||
| 75 | * hp_cpu's 'iterator' to point to its first handler. With each loop, | ||
| 76 | * initialise FQ objects and advance the iterator for the next loop. | ||
| 77 | * Moreover, do this initialisation on the cpu it applies to so that Rx FQ | ||
| 78 | * initialisation targets the correct cpu. | ||
| 79 | */ | ||
| 80 | |||
| 81 | /* | ||
| 82 | * helper to run something on all cpus (can't use on_each_cpu(), as that invokes | ||
| 83 | * the fn from irq context, which is too restrictive). | ||
| 84 | */ | ||
| 85 | struct bstrap { | ||
| 86 | int (*fn)(void); | ||
| 87 | atomic_t started; | ||
| 88 | }; | ||
| 89 | static int bstrap_fn(void *bs) | ||
| 90 | { | ||
| 91 | struct bstrap *bstrap = bs; | ||
| 92 | int err; | ||
| 93 | |||
| 94 | atomic_inc(&bstrap->started); | ||
| 95 | err = bstrap->fn(); | ||
| 96 | if (err) | ||
| 97 | return err; | ||
| 98 | while (!kthread_should_stop()) | ||
| 99 | msleep(20); | ||
| 100 | return 0; | ||
| 101 | } | ||
| 102 | static int on_all_cpus(int (*fn)(void)) | ||
| 103 | { | ||
| 104 | int cpu; | ||
| 105 | |||
| 106 | for_each_cpu(cpu, cpu_online_mask) { | ||
| 107 | struct bstrap bstrap = { | ||
| 108 | .fn = fn, | ||
| 109 | .started = ATOMIC_INIT(0) | ||
| 110 | }; | ||
| 111 | struct task_struct *k = kthread_create(bstrap_fn, &bstrap, | ||
| 112 | "hotpotato%d", cpu); | ||
| 113 | int ret; | ||
| 114 | |||
| 115 | if (IS_ERR(k)) | ||
| 116 | return -ENOMEM; | ||
| 117 | kthread_bind(k, cpu); | ||
| 118 | wake_up_process(k); | ||
| 119 | /* | ||
| 120 | * If we call kthread_stop() before the "wake up" has had an | ||
| 121 | * effect, then the thread may exit with -EINTR without ever | ||
| 122 | * running the function. So poll until it's started before | ||
| 123 | * requesting it to stop. | ||
| 124 | */ | ||
| 125 | while (!atomic_read(&bstrap.started)) | ||
| 126 | msleep(20); | ||
| 127 | ret = kthread_stop(k); | ||
| 128 | if (ret) | ||
| 129 | return ret; | ||
| 130 | } | ||
| 131 | return 0; | ||
| 132 | } | ||
| 133 | |||
| 134 | struct hp_handler { | ||
| 135 | |||
| 136 | /* The following data is stashed when 'rx' is dequeued; */ | ||
| 137 | /* -------------- */ | ||
| 138 | /* The Rx FQ, dequeues of which will stash the entire hp_handler */ | ||
| 139 | struct qman_fq rx; | ||
| 140 | /* The Tx FQ we should forward to */ | ||
| 141 | struct qman_fq tx; | ||
| 142 | /* The value we XOR post-dequeue, prior to validating */ | ||
| 143 | u32 rx_mixer; | ||
| 144 | /* The value we XOR pre-enqueue, after validating */ | ||
| 145 | u32 tx_mixer; | ||
| 146 | /* what the hotpotato address should be on dequeue */ | ||
| 147 | dma_addr_t addr; | ||
| 148 | u32 *frame_ptr; | ||
| 149 | |||
| 150 | /* The following data isn't (necessarily) stashed on dequeue; */ | ||
| 151 | /* -------------- */ | ||
| 152 | u32 fqid_rx, fqid_tx; | ||
| 153 | /* list node for linking us into 'hp_cpu' */ | ||
| 154 | struct list_head node; | ||
| 155 | /* Just to check ... */ | ||
| 156 | unsigned int processor_id; | ||
| 157 | } ____cacheline_aligned; | ||
| 158 | |||
| 159 | struct hp_cpu { | ||
| 160 | /* identify the cpu we run on; */ | ||
| 161 | unsigned int processor_id; | ||
| 162 | /* root node for the per-cpu list of handlers */ | ||
| 163 | struct list_head handlers; | ||
| 164 | /* list node for linking us into 'hp_cpu_list' */ | ||
| 165 | struct list_head node; | ||
| 166 | /* | ||
| 167 | * when repeatedly scanning 'hp_list', each time linking the n'th | ||
| 168 | * handlers together, this is used as per-cpu iterator state | ||
| 169 | */ | ||
| 170 | struct hp_handler *iterator; | ||
| 171 | }; | ||
| 172 | |||
| 173 | /* Each cpu has one of these */ | ||
| 174 | static DEFINE_PER_CPU(struct hp_cpu, hp_cpus); | ||
| 175 | |||
| 176 | /* links together the hp_cpu structs, in first-come first-serve order. */ | ||
| 177 | static LIST_HEAD(hp_cpu_list); | ||
| 178 | static spinlock_t hp_lock = __SPIN_LOCK_UNLOCKED(hp_lock); | ||
| 179 | |||
| 180 | static unsigned int hp_cpu_list_length; | ||
| 181 | |||
| 182 | /* the "special" handler, that starts and terminates the test. */ | ||
| 183 | static struct hp_handler *special_handler; | ||
| 184 | static int loop_counter; | ||
| 185 | |||
| 186 | /* handlers are allocated out of this, so they're properly aligned. */ | ||
| 187 | static struct kmem_cache *hp_handler_slab; | ||
| 188 | |||
| 189 | /* this is the frame data */ | ||
| 190 | static void *__frame_ptr; | ||
| 191 | static u32 *frame_ptr; | ||
| 192 | static dma_addr_t frame_dma; | ||
| 193 | |||
| 194 | /* the main function waits on this */ | ||
| 195 | static DECLARE_WAIT_QUEUE_HEAD(queue); | ||
| 196 | |||
| 197 | #define HP_PER_CPU 2 | ||
| 198 | #define HP_LOOPS 8 | ||
| 199 | /* 80 bytes, like a small ethernet frame, and bleeds into a second cacheline */ | ||
| 200 | #define HP_NUM_WORDS 80 | ||
| 201 | /* First word of the LFSR-based frame data */ | ||
| 202 | #define HP_FIRST_WORD 0xabbaf00d | ||
| 203 | |||
| 204 | static inline u32 do_lfsr(u32 prev) | ||
| 205 | { | ||
| 206 | return (prev >> 1) ^ (-(prev & 1u) & 0xd0000001u); | ||
| 207 | } | ||
| 208 | |||
| 209 | static int allocate_frame_data(void) | ||
| 210 | { | ||
| 211 | u32 lfsr = HP_FIRST_WORD; | ||
| 212 | int loop; | ||
| 213 | struct platform_device *pdev = platform_device_alloc("foobar", -1); | ||
| 214 | |||
| 215 | if (!pdev) { | ||
| 216 | pr_crit("platform_device_alloc() failed"); | ||
| 217 | return -EIO; | ||
| 218 | } | ||
| 219 | if (platform_device_add(pdev)) { | ||
| 220 | pr_crit("platform_device_add() failed"); | ||
| 221 | return -EIO; | ||
| 222 | } | ||
| 223 | __frame_ptr = kmalloc(4 * HP_NUM_WORDS, GFP_KERNEL); | ||
| 224 | if (!__frame_ptr) | ||
| 225 | return -ENOMEM; | ||
| 226 | |||
| 227 | frame_ptr = PTR_ALIGN(__frame_ptr, 64); | ||
| 228 | for (loop = 0; loop < HP_NUM_WORDS; loop++) { | ||
| 229 | frame_ptr[loop] = lfsr; | ||
| 230 | lfsr = do_lfsr(lfsr); | ||
| 231 | } | ||
| 232 | frame_dma = dma_map_single(&pdev->dev, frame_ptr, 4 * HP_NUM_WORDS, | ||
| 233 | DMA_BIDIRECTIONAL); | ||
| 234 | platform_device_del(pdev); | ||
| 235 | platform_device_put(pdev); | ||
| 236 | return 0; | ||
| 237 | } | ||
| 238 | |||
| 239 | static void deallocate_frame_data(void) | ||
| 240 | { | ||
| 241 | kfree(__frame_ptr); | ||
| 242 | } | ||
| 243 | |||
| 244 | static inline int process_frame_data(struct hp_handler *handler, | ||
| 245 | const struct qm_fd *fd) | ||
| 246 | { | ||
| 247 | u32 *p = handler->frame_ptr; | ||
| 248 | u32 lfsr = HP_FIRST_WORD; | ||
| 249 | int loop; | ||
| 250 | |||
| 251 | if (qm_fd_addr_get64(fd) != handler->addr) { | ||
| 252 | pr_crit("bad frame address"); | ||
| 253 | return -EIO; | ||
| 254 | } | ||
| 255 | for (loop = 0; loop < HP_NUM_WORDS; loop++, p++) { | ||
| 256 | *p ^= handler->rx_mixer; | ||
| 257 | if (*p != lfsr) { | ||
| 258 | pr_crit("corrupt frame data"); | ||
| 259 | return -EIO; | ||
| 260 | } | ||
| 261 | *p ^= handler->tx_mixer; | ||
| 262 | lfsr = do_lfsr(lfsr); | ||
| 263 | } | ||
| 264 | return 0; | ||
| 265 | } | ||
| 266 | |||
| 267 | static enum qman_cb_dqrr_result normal_dqrr(struct qman_portal *portal, | ||
| 268 | struct qman_fq *fq, | ||
| 269 | const struct qm_dqrr_entry *dqrr) | ||
| 270 | { | ||
| 271 | struct hp_handler *handler = (struct hp_handler *)fq; | ||
| 272 | |||
| 273 | if (process_frame_data(handler, &dqrr->fd)) { | ||
| 274 | WARN_ON(1); | ||
| 275 | goto skip; | ||
| 276 | } | ||
| 277 | if (qman_enqueue(&handler->tx, &dqrr->fd)) { | ||
| 278 | pr_crit("qman_enqueue() failed"); | ||
| 279 | WARN_ON(1); | ||
| 280 | } | ||
| 281 | skip: | ||
| 282 | return qman_cb_dqrr_consume; | ||
| 283 | } | ||
| 284 | |||
| 285 | static enum qman_cb_dqrr_result special_dqrr(struct qman_portal *portal, | ||
| 286 | struct qman_fq *fq, | ||
| 287 | const struct qm_dqrr_entry *dqrr) | ||
| 288 | { | ||
| 289 | struct hp_handler *handler = (struct hp_handler *)fq; | ||
| 290 | |||
| 291 | process_frame_data(handler, &dqrr->fd); | ||
| 292 | if (++loop_counter < HP_LOOPS) { | ||
| 293 | if (qman_enqueue(&handler->tx, &dqrr->fd)) { | ||
| 294 | pr_crit("qman_enqueue() failed"); | ||
| 295 | WARN_ON(1); | ||
| 296 | goto skip; | ||
| 297 | } | ||
| 298 | } else { | ||
| 299 | pr_info("Received final (%dth) frame\n", loop_counter); | ||
| 300 | wake_up(&queue); | ||
| 301 | } | ||
| 302 | skip: | ||
| 303 | return qman_cb_dqrr_consume; | ||
| 304 | } | ||
| 305 | |||
| 306 | static int create_per_cpu_handlers(void) | ||
| 307 | { | ||
| 308 | struct hp_handler *handler; | ||
| 309 | int loop; | ||
| 310 | struct hp_cpu *hp_cpu = this_cpu_ptr(&hp_cpus); | ||
| 311 | |||
| 312 | hp_cpu->processor_id = smp_processor_id(); | ||
| 313 | spin_lock(&hp_lock); | ||
| 314 | list_add_tail(&hp_cpu->node, &hp_cpu_list); | ||
| 315 | hp_cpu_list_length++; | ||
| 316 | spin_unlock(&hp_lock); | ||
| 317 | INIT_LIST_HEAD(&hp_cpu->handlers); | ||
| 318 | for (loop = 0; loop < HP_PER_CPU; loop++) { | ||
| 319 | handler = kmem_cache_alloc(hp_handler_slab, GFP_KERNEL); | ||
| 320 | if (!handler) { | ||
| 321 | pr_crit("kmem_cache_alloc() failed"); | ||
| 322 | WARN_ON(1); | ||
| 323 | return -EIO; | ||
| 324 | } | ||
| 325 | handler->processor_id = hp_cpu->processor_id; | ||
| 326 | handler->addr = frame_dma; | ||
| 327 | handler->frame_ptr = frame_ptr; | ||
| 328 | list_add_tail(&handler->node, &hp_cpu->handlers); | ||
| 329 | } | ||
| 330 | return 0; | ||
| 331 | } | ||
| 332 | |||
| 333 | static int destroy_per_cpu_handlers(void) | ||
| 334 | { | ||
| 335 | struct list_head *loop, *tmp; | ||
| 336 | struct hp_cpu *hp_cpu = this_cpu_ptr(&hp_cpus); | ||
| 337 | |||
| 338 | spin_lock(&hp_lock); | ||
| 339 | list_del(&hp_cpu->node); | ||
| 340 | spin_unlock(&hp_lock); | ||
| 341 | list_for_each_safe(loop, tmp, &hp_cpu->handlers) { | ||
| 342 | u32 flags = 0; | ||
| 343 | struct hp_handler *handler = list_entry(loop, struct hp_handler, | ||
| 344 | node); | ||
| 345 | if (qman_retire_fq(&handler->rx, &flags) || | ||
| 346 | (flags & QMAN_FQ_STATE_BLOCKOOS)) { | ||
| 347 | pr_crit("qman_retire_fq(rx) failed, flags: %x", flags); | ||
| 348 | WARN_ON(1); | ||
| 349 | return -EIO; | ||
| 350 | } | ||
| 351 | if (qman_oos_fq(&handler->rx)) { | ||
| 352 | pr_crit("qman_oos_fq(rx) failed"); | ||
| 353 | WARN_ON(1); | ||
| 354 | return -EIO; | ||
| 355 | } | ||
| 356 | qman_destroy_fq(&handler->rx); | ||
| 357 | qman_destroy_fq(&handler->tx); | ||
| 358 | qman_release_fqid(handler->fqid_rx); | ||
| 359 | list_del(&handler->node); | ||
| 360 | kmem_cache_free(hp_handler_slab, handler); | ||
| 361 | } | ||
| 362 | return 0; | ||
| 363 | } | ||
| 364 | |||
| 365 | static inline u8 num_cachelines(u32 offset) | ||
| 366 | { | ||
| 367 | u8 res = (offset + (L1_CACHE_BYTES - 1)) | ||
| 368 | / (L1_CACHE_BYTES); | ||
| 369 | if (res > 3) | ||
| 370 | return 3; | ||
| 371 | return res; | ||
| 372 | } | ||
| 373 | #define STASH_DATA_CL \ | ||
| 374 | num_cachelines(HP_NUM_WORDS * 4) | ||
| 375 | #define STASH_CTX_CL \ | ||
| 376 | num_cachelines(offsetof(struct hp_handler, fqid_rx)) | ||
| 377 | |||
| 378 | static int init_handler(void *h) | ||
| 379 | { | ||
| 380 | struct qm_mcc_initfq opts; | ||
| 381 | struct hp_handler *handler = h; | ||
| 382 | int err; | ||
| 383 | |||
| 384 | if (handler->processor_id != smp_processor_id()) { | ||
| 385 | err = -EIO; | ||
| 386 | goto failed; | ||
| 387 | } | ||
| 388 | /* Set up rx */ | ||
| 389 | memset(&handler->rx, 0, sizeof(handler->rx)); | ||
| 390 | if (handler == special_handler) | ||
| 391 | handler->rx.cb.dqrr = special_dqrr; | ||
| 392 | else | ||
| 393 | handler->rx.cb.dqrr = normal_dqrr; | ||
| 394 | err = qman_create_fq(handler->fqid_rx, 0, &handler->rx); | ||
| 395 | if (err) { | ||
| 396 | pr_crit("qman_create_fq(rx) failed"); | ||
| 397 | goto failed; | ||
| 398 | } | ||
| 399 | memset(&opts, 0, sizeof(opts)); | ||
| 400 | opts.we_mask = QM_INITFQ_WE_FQCTRL | QM_INITFQ_WE_CONTEXTA; | ||
| 401 | opts.fqd.fq_ctrl = QM_FQCTRL_CTXASTASHING; | ||
| 402 | qm_fqd_set_stashing(&opts.fqd, 0, STASH_DATA_CL, STASH_CTX_CL); | ||
| 403 | err = qman_init_fq(&handler->rx, QMAN_INITFQ_FLAG_SCHED | | ||
| 404 | QMAN_INITFQ_FLAG_LOCAL, &opts); | ||
| 405 | if (err) { | ||
| 406 | pr_crit("qman_init_fq(rx) failed"); | ||
| 407 | goto failed; | ||
| 408 | } | ||
| 409 | /* Set up tx */ | ||
| 410 | memset(&handler->tx, 0, sizeof(handler->tx)); | ||
| 411 | err = qman_create_fq(handler->fqid_tx, QMAN_FQ_FLAG_NO_MODIFY, | ||
| 412 | &handler->tx); | ||
| 413 | if (err) { | ||
| 414 | pr_crit("qman_create_fq(tx) failed"); | ||
| 415 | goto failed; | ||
| 416 | } | ||
| 417 | |||
| 418 | return 0; | ||
| 419 | failed: | ||
| 420 | return err; | ||
| 421 | } | ||
| 422 | |||
| 423 | static void init_handler_cb(void *h) | ||
| 424 | { | ||
| 425 | if (init_handler(h)) | ||
| 426 | WARN_ON(1); | ||
| 427 | } | ||
| 428 | |||
| 429 | static int init_phase2(void) | ||
| 430 | { | ||
| 431 | int loop; | ||
| 432 | u32 fqid = 0; | ||
| 433 | u32 lfsr = 0xdeadbeef; | ||
| 434 | struct hp_cpu *hp_cpu; | ||
| 435 | struct hp_handler *handler; | ||
| 436 | |||
| 437 | for (loop = 0; loop < HP_PER_CPU; loop++) { | ||
| 438 | list_for_each_entry(hp_cpu, &hp_cpu_list, node) { | ||
| 439 | int err; | ||
| 440 | |||
| 441 | if (!loop) | ||
| 442 | hp_cpu->iterator = list_first_entry( | ||
| 443 | &hp_cpu->handlers, | ||
| 444 | struct hp_handler, node); | ||
| 445 | else | ||
| 446 | hp_cpu->iterator = list_entry( | ||
| 447 | hp_cpu->iterator->node.next, | ||
| 448 | struct hp_handler, node); | ||
| 449 | /* Rx FQID is the previous handler's Tx FQID */ | ||
| 450 | hp_cpu->iterator->fqid_rx = fqid; | ||
| 451 | /* Allocate new FQID for Tx */ | ||
| 452 | err = qman_alloc_fqid(&fqid); | ||
| 453 | if (err) { | ||
| 454 | pr_crit("qman_alloc_fqid() failed"); | ||
| 455 | return err; | ||
| 456 | } | ||
| 457 | hp_cpu->iterator->fqid_tx = fqid; | ||
| 458 | /* Rx mixer is the previous handler's Tx mixer */ | ||
| 459 | hp_cpu->iterator->rx_mixer = lfsr; | ||
| 460 | /* Get new mixer for Tx */ | ||
| 461 | lfsr = do_lfsr(lfsr); | ||
| 462 | hp_cpu->iterator->tx_mixer = lfsr; | ||
| 463 | } | ||
| 464 | } | ||
| 465 | /* Fix up the first handler (fqid_rx==0, rx_mixer=0xdeadbeef) */ | ||
| 466 | hp_cpu = list_first_entry(&hp_cpu_list, struct hp_cpu, node); | ||
| 467 | handler = list_first_entry(&hp_cpu->handlers, struct hp_handler, node); | ||
| 468 | if (handler->fqid_rx != 0 || handler->rx_mixer != 0xdeadbeef) | ||
| 469 | return 1; | ||
| 470 | handler->fqid_rx = fqid; | ||
| 471 | handler->rx_mixer = lfsr; | ||
| 472 | /* and tag it as our "special" handler */ | ||
| 473 | special_handler = handler; | ||
| 474 | return 0; | ||
| 475 | } | ||
| 476 | |||
| 477 | static int init_phase3(void) | ||
| 478 | { | ||
| 479 | int loop, err; | ||
| 480 | struct hp_cpu *hp_cpu; | ||
| 481 | |||
| 482 | for (loop = 0; loop < HP_PER_CPU; loop++) { | ||
| 483 | list_for_each_entry(hp_cpu, &hp_cpu_list, node) { | ||
| 484 | if (!loop) | ||
| 485 | hp_cpu->iterator = list_first_entry( | ||
| 486 | &hp_cpu->handlers, | ||
| 487 | struct hp_handler, node); | ||
| 488 | else | ||
| 489 | hp_cpu->iterator = list_entry( | ||
| 490 | hp_cpu->iterator->node.next, | ||
| 491 | struct hp_handler, node); | ||
| 492 | preempt_disable(); | ||
| 493 | if (hp_cpu->processor_id == smp_processor_id()) { | ||
| 494 | err = init_handler(hp_cpu->iterator); | ||
| 495 | if (err) | ||
| 496 | return err; | ||
| 497 | } else { | ||
| 498 | smp_call_function_single(hp_cpu->processor_id, | ||
| 499 | init_handler_cb, hp_cpu->iterator, 1); | ||
| 500 | } | ||
| 501 | preempt_enable(); | ||
| 502 | } | ||
| 503 | } | ||
| 504 | return 0; | ||
| 505 | } | ||
| 506 | |||
| 507 | static int send_first_frame(void *ignore) | ||
| 508 | { | ||
| 509 | u32 *p = special_handler->frame_ptr; | ||
| 510 | u32 lfsr = HP_FIRST_WORD; | ||
| 511 | int loop, err; | ||
| 512 | struct qm_fd fd; | ||
| 513 | |||
| 514 | if (special_handler->processor_id != smp_processor_id()) { | ||
| 515 | err = -EIO; | ||
| 516 | goto failed; | ||
| 517 | } | ||
| 518 | memset(&fd, 0, sizeof(fd)); | ||
| 519 | qm_fd_addr_set64(&fd, special_handler->addr); | ||
| 520 | qm_fd_set_contig_big(&fd, HP_NUM_WORDS * 4); | ||
| 521 | for (loop = 0; loop < HP_NUM_WORDS; loop++, p++) { | ||
| 522 | if (*p != lfsr) { | ||
| 523 | err = -EIO; | ||
| 524 | pr_crit("corrupt frame data"); | ||
| 525 | goto failed; | ||
| 526 | } | ||
| 527 | *p ^= special_handler->tx_mixer; | ||
| 528 | lfsr = do_lfsr(lfsr); | ||
| 529 | } | ||
| 530 | pr_info("Sending first frame\n"); | ||
| 531 | err = qman_enqueue(&special_handler->tx, &fd); | ||
| 532 | if (err) { | ||
| 533 | pr_crit("qman_enqueue() failed"); | ||
| 534 | goto failed; | ||
| 535 | } | ||
| 536 | |||
| 537 | return 0; | ||
| 538 | failed: | ||
| 539 | return err; | ||
| 540 | } | ||
| 541 | |||
| 542 | static void send_first_frame_cb(void *ignore) | ||
| 543 | { | ||
| 544 | if (send_first_frame(NULL)) | ||
| 545 | WARN_ON(1); | ||
| 546 | } | ||
| 547 | |||
| 548 | int qman_test_stash(void) | ||
| 549 | { | ||
| 550 | int err; | ||
| 551 | |||
| 552 | if (cpumask_weight(cpu_online_mask) < 2) { | ||
| 553 | pr_info("%s(): skip - only 1 CPU\n", __func__); | ||
| 554 | return 0; | ||
| 555 | } | ||
| 556 | |||
| 557 | pr_info("%s(): Starting\n", __func__); | ||
| 558 | |||
| 559 | hp_cpu_list_length = 0; | ||
| 560 | loop_counter = 0; | ||
| 561 | hp_handler_slab = kmem_cache_create("hp_handler_slab", | ||
| 562 | sizeof(struct hp_handler), L1_CACHE_BYTES, | ||
| 563 | SLAB_HWCACHE_ALIGN, NULL); | ||
| 564 | if (!hp_handler_slab) { | ||
| 565 | err = -EIO; | ||
| 566 | pr_crit("kmem_cache_create() failed"); | ||
| 567 | goto failed; | ||
| 568 | } | ||
| 569 | |||
| 570 | err = allocate_frame_data(); | ||
| 571 | if (err) | ||
| 572 | goto failed; | ||
| 573 | |||
| 574 | /* Init phase 1 */ | ||
| 575 | pr_info("Creating %d handlers per cpu...\n", HP_PER_CPU); | ||
| 576 | if (on_all_cpus(create_per_cpu_handlers)) { | ||
| 577 | err = -EIO; | ||
| 578 | pr_crit("on_each_cpu() failed"); | ||
| 579 | goto failed; | ||
| 580 | } | ||
| 581 | pr_info("Number of cpus: %d, total of %d handlers\n", | ||
| 582 | hp_cpu_list_length, hp_cpu_list_length * HP_PER_CPU); | ||
| 583 | |||
| 584 | err = init_phase2(); | ||
| 585 | if (err) | ||
| 586 | goto failed; | ||
| 587 | |||
| 588 | err = init_phase3(); | ||
| 589 | if (err) | ||
| 590 | goto failed; | ||
| 591 | |||
| 592 | preempt_disable(); | ||
| 593 | if (special_handler->processor_id == smp_processor_id()) { | ||
| 594 | err = send_first_frame(NULL); | ||
| 595 | if (err) | ||
| 596 | goto failed; | ||
| 597 | } else { | ||
| 598 | smp_call_function_single(special_handler->processor_id, | ||
| 599 | send_first_frame_cb, NULL, 1); | ||
| 600 | } | ||
| 601 | preempt_enable(); | ||
| 602 | |||
| 603 | wait_event(queue, loop_counter == HP_LOOPS); | ||
| 604 | deallocate_frame_data(); | ||
| 605 | if (on_all_cpus(destroy_per_cpu_handlers)) { | ||
| 606 | err = -EIO; | ||
| 607 | pr_crit("on_each_cpu() failed"); | ||
| 608 | goto failed; | ||
| 609 | } | ||
| 610 | kmem_cache_destroy(hp_handler_slab); | ||
| 611 | pr_info("%s(): Finished\n", __func__); | ||
| 612 | |||
| 613 | return 0; | ||
| 614 | failed: | ||
| 615 | WARN_ON(1); | ||
| 616 | return err; | ||
| 617 | } | ||
