diff options
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 @@ +/* Copyright 2008 - 2016 Freescale Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *     * Redistributions of source code must retain the above copyright + *	 notice, this list of conditions and the following disclaimer. + *     * Redistributions in binary form must reproduce the above copyright + *	 notice, this list of conditions and the following disclaimer in the + *	 documentation and/or other materials provided with the distribution. + *     * Neither the name of Freescale Semiconductor nor the + *	 names of its contributors may be used to endorse or promote products + *	 derived from this software without specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "qman_test.h" + +#define CGR_ID		27 +#define POOL_ID		2 +#define FQ_FLAGS	QMAN_FQ_FLAG_DYNAMIC_FQID +#define NUM_ENQUEUES	10 +#define NUM_PARTIAL	4 +#define PORTAL_SDQCR	(QM_SDQCR_SOURCE_CHANNELS | \ +			QM_SDQCR_TYPE_PRIO_QOS | \ +			QM_SDQCR_TOKEN_SET(0x98) | \ +			QM_SDQCR_CHANNELS_DEDICATED | \ +			QM_SDQCR_CHANNELS_POOL(POOL_ID)) +#define PORTAL_OPAQUE	((void *)0xf00dbeef) +#define VDQCR_FLAGS	(QMAN_VOLATILE_FLAG_WAIT | QMAN_VOLATILE_FLAG_FINISH) + +static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *, +					struct qman_fq *, +					const struct qm_dqrr_entry *); +static void cb_ern(struct qman_portal *, struct qman_fq *, +		   const union qm_mr_entry *); +static void cb_fqs(struct qman_portal *, struct qman_fq *, +		   const union qm_mr_entry *); + +static struct qm_fd fd, fd_dq; +static struct qman_fq fq_base = { +	.cb.dqrr = cb_dqrr, +	.cb.ern = cb_ern, +	.cb.fqs = cb_fqs +}; +static DECLARE_WAIT_QUEUE_HEAD(waitqueue); +static int retire_complete, sdqcr_complete; + +/* Helpers for initialising and "incrementing" a frame descriptor */ +static void fd_init(struct qm_fd *fd) +{ +	qm_fd_addr_set64(fd, 0xabdeadbeefLLU); +	qm_fd_set_contig_big(fd, 0x0000ffff); +	fd->cmd = 0xfeedf00d; +} + +static void fd_inc(struct qm_fd *fd) +{ +	u64 t = qm_fd_addr_get64(fd); +	int z = t >> 40; +	unsigned int len, off; +	enum qm_fd_format fmt; + +	t <<= 1; +	if (z) +		t |= 1; +	qm_fd_addr_set64(fd, t); + +	fmt = qm_fd_get_format(fd); +	off = qm_fd_get_offset(fd); +	len = qm_fd_get_length(fd); +	len--; +	qm_fd_set_param(fd, fmt, off, len); + +	fd->cmd++; +} + +/* The only part of the 'fd' we can't memcmp() is the ppid */ +static int fd_cmp(const struct qm_fd *a, const struct qm_fd *b) +{ +	int r = (qm_fd_addr_get64(a) == qm_fd_addr_get64(b)) ? 0 : -1; + +	if (!r) { +		enum qm_fd_format fmt_a, fmt_b; + +		fmt_a = qm_fd_get_format(a); +		fmt_b = qm_fd_get_format(b); +		r = fmt_a - fmt_b; +	} +	if (!r) +		r = a->cfg - b->cfg; +	if (!r) +		r = a->cmd - b->cmd; +	return r; +} + +/* test */ +static int do_enqueues(struct qman_fq *fq) +{ +	unsigned int loop; +	int err = 0; + +	for (loop = 0; loop < NUM_ENQUEUES; loop++) { +		if (qman_enqueue(fq, &fd)) { +			pr_crit("qman_enqueue() failed\n"); +			err = -EIO; +		} +		fd_inc(&fd); +	} + +	return err; +} + +int qman_test_api(void) +{ +	unsigned int flags, frmcnt; +	int err; +	struct qman_fq *fq = &fq_base; + +	pr_info("%s(): Starting\n", __func__); +	fd_init(&fd); +	fd_init(&fd_dq); + +	/* Initialise (parked) FQ */ +	err = qman_create_fq(0, FQ_FLAGS, fq); +	if (err) { +		pr_crit("qman_create_fq() failed\n"); +		goto failed; +	} +	err = qman_init_fq(fq, QMAN_INITFQ_FLAG_LOCAL, NULL); +	if (err) { +		pr_crit("qman_init_fq() failed\n"); +		goto failed; +	} +	/* Do enqueues + VDQCR, twice. (Parked FQ) */ +	err = do_enqueues(fq); +	if (err) +		goto failed; +	pr_info("VDQCR (till-empty);\n"); +	frmcnt = QM_VDQCR_NUMFRAMES_TILLEMPTY; +	err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt); +	if (err) { +		pr_crit("qman_volatile_dequeue() failed\n"); +		goto failed; +	} +	err = do_enqueues(fq); +	if (err) +		goto failed; +	pr_info("VDQCR (%d of %d);\n", NUM_PARTIAL, NUM_ENQUEUES); +	frmcnt = QM_VDQCR_NUMFRAMES_SET(NUM_PARTIAL); +	err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt); +	if (err) { +		pr_crit("qman_volatile_dequeue() failed\n"); +		goto failed; +	} +	pr_info("VDQCR (%d of %d);\n", NUM_ENQUEUES - NUM_PARTIAL, +		NUM_ENQUEUES); +	frmcnt = QM_VDQCR_NUMFRAMES_SET(NUM_ENQUEUES - NUM_PARTIAL); +	err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt); +	if (err) { +		pr_err("qman_volatile_dequeue() failed\n"); +		goto failed; +	} + +	err = do_enqueues(fq); +	if (err) +		goto failed; +	pr_info("scheduled dequeue (till-empty)\n"); +	err = qman_schedule_fq(fq); +	if (err) { +		pr_crit("qman_schedule_fq() failed\n"); +		goto failed; +	} +	wait_event(waitqueue, sdqcr_complete); + +	/* Retire and OOS the FQ */ +	err = qman_retire_fq(fq, &flags); +	if (err < 0) { +		pr_crit("qman_retire_fq() failed\n"); +		goto failed; +	} +	wait_event(waitqueue, retire_complete); +	if (flags & QMAN_FQ_STATE_BLOCKOOS) { +		err = -EIO; +		pr_crit("leaking frames\n"); +		goto failed; +	} +	err = qman_oos_fq(fq); +	if (err) { +		pr_crit("qman_oos_fq() failed\n"); +		goto failed; +	} +	qman_destroy_fq(fq); +	pr_info("%s(): Finished\n", __func__); +	return 0; + +failed: +	WARN_ON(1); +	return err; +} + +static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *p, +					struct qman_fq *fq, +					const struct qm_dqrr_entry *dq) +{ +	if (WARN_ON(fd_cmp(&fd_dq, &dq->fd))) { +		pr_err("BADNESS: dequeued frame doesn't match;\n"); +		return qman_cb_dqrr_consume; +	} +	fd_inc(&fd_dq); +	if (!(dq->stat & QM_DQRR_STAT_UNSCHEDULED) && !fd_cmp(&fd_dq, &fd)) { +		sdqcr_complete = 1; +		wake_up(&waitqueue); +	} +	return qman_cb_dqrr_consume; +} + +static void cb_ern(struct qman_portal *p, struct qman_fq *fq, +		   const union qm_mr_entry *msg) +{ +	pr_crit("cb_ern() unimplemented"); +	WARN_ON(1); +} + +static void cb_fqs(struct qman_portal *p, struct qman_fq *fq, +		   const union qm_mr_entry *msg) +{ +	u8 verb = (msg->verb & QM_MR_VERB_TYPE_MASK); + +	if ((verb != QM_MR_VERB_FQRN) && (verb != QM_MR_VERB_FQRNI)) { +		pr_crit("unexpected FQS message"); +		WARN_ON(1); +		return; +	} +	pr_info("Retirement message received\n"); +	retire_complete = 1; +	wake_up(&waitqueue); +}  |