162306a36Sopenharmony_ci/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
462306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met:
562306a36Sopenharmony_ci *     * Redistributions of source code must retain the above copyright
662306a36Sopenharmony_ci *	 notice, this list of conditions and the following disclaimer.
762306a36Sopenharmony_ci *     * Redistributions in binary form must reproduce the above copyright
862306a36Sopenharmony_ci *	 notice, this list of conditions and the following disclaimer in the
962306a36Sopenharmony_ci *	 documentation and/or other materials provided with the distribution.
1062306a36Sopenharmony_ci *     * Neither the name of Freescale Semiconductor nor the
1162306a36Sopenharmony_ci *	 names of its contributors may be used to endorse or promote products
1262306a36Sopenharmony_ci *	 derived from this software without specific prior written permission.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * ALTERNATIVELY, this software may be distributed under the terms of the
1562306a36Sopenharmony_ci * GNU General Public License ("GPL") as published by the Free Software
1662306a36Sopenharmony_ci * Foundation, either version 2 of that License or (at your option) any
1762306a36Sopenharmony_ci * later version.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
2062306a36Sopenharmony_ci * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2162306a36Sopenharmony_ci * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2262306a36Sopenharmony_ci * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
2362306a36Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2462306a36Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2562306a36Sopenharmony_ci * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2662306a36Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2762306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2862306a36Sopenharmony_ci * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "qman_test.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define CGR_ID		27
3462306a36Sopenharmony_ci#define POOL_ID		2
3562306a36Sopenharmony_ci#define FQ_FLAGS	QMAN_FQ_FLAG_DYNAMIC_FQID
3662306a36Sopenharmony_ci#define NUM_ENQUEUES	10
3762306a36Sopenharmony_ci#define NUM_PARTIAL	4
3862306a36Sopenharmony_ci#define PORTAL_SDQCR	(QM_SDQCR_SOURCE_CHANNELS | \
3962306a36Sopenharmony_ci			QM_SDQCR_TYPE_PRIO_QOS | \
4062306a36Sopenharmony_ci			QM_SDQCR_TOKEN_SET(0x98) | \
4162306a36Sopenharmony_ci			QM_SDQCR_CHANNELS_DEDICATED | \
4262306a36Sopenharmony_ci			QM_SDQCR_CHANNELS_POOL(POOL_ID))
4362306a36Sopenharmony_ci#define PORTAL_OPAQUE	((void *)0xf00dbeef)
4462306a36Sopenharmony_ci#define VDQCR_FLAGS	(QMAN_VOLATILE_FLAG_WAIT | QMAN_VOLATILE_FLAG_FINISH)
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *,
4762306a36Sopenharmony_ci					struct qman_fq *,
4862306a36Sopenharmony_ci					const struct qm_dqrr_entry *,
4962306a36Sopenharmony_ci					bool sched_napi);
5062306a36Sopenharmony_cistatic void cb_ern(struct qman_portal *, struct qman_fq *,
5162306a36Sopenharmony_ci		   const union qm_mr_entry *);
5262306a36Sopenharmony_cistatic void cb_fqs(struct qman_portal *, struct qman_fq *,
5362306a36Sopenharmony_ci		   const union qm_mr_entry *);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic struct qm_fd fd, fd_dq;
5662306a36Sopenharmony_cistatic struct qman_fq fq_base = {
5762306a36Sopenharmony_ci	.cb.dqrr = cb_dqrr,
5862306a36Sopenharmony_ci	.cb.ern = cb_ern,
5962306a36Sopenharmony_ci	.cb.fqs = cb_fqs
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(waitqueue);
6262306a36Sopenharmony_cistatic int retire_complete, sdqcr_complete;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Helpers for initialising and "incrementing" a frame descriptor */
6562306a36Sopenharmony_cistatic void fd_init(struct qm_fd *fd)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	qm_fd_addr_set64(fd, 0xabdeadbeefLLU);
6862306a36Sopenharmony_ci	qm_fd_set_contig_big(fd, 0x0000ffff);
6962306a36Sopenharmony_ci	fd->cmd = cpu_to_be32(0xfeedf00d);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void fd_inc(struct qm_fd *fd)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	u64 t = qm_fd_addr_get64(fd);
7562306a36Sopenharmony_ci	int z = t >> 40;
7662306a36Sopenharmony_ci	unsigned int len, off;
7762306a36Sopenharmony_ci	enum qm_fd_format fmt;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	t <<= 1;
8062306a36Sopenharmony_ci	if (z)
8162306a36Sopenharmony_ci		t |= 1;
8262306a36Sopenharmony_ci	qm_fd_addr_set64(fd, t);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	fmt = qm_fd_get_format(fd);
8562306a36Sopenharmony_ci	off = qm_fd_get_offset(fd);
8662306a36Sopenharmony_ci	len = qm_fd_get_length(fd);
8762306a36Sopenharmony_ci	len--;
8862306a36Sopenharmony_ci	qm_fd_set_param(fd, fmt, off, len);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	be32_add_cpu(&fd->cmd, 1);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/* The only part of the 'fd' we can't memcmp() is the ppid */
9462306a36Sopenharmony_cistatic bool fd_neq(const struct qm_fd *a, const struct qm_fd *b)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	bool neq = qm_fd_addr_get64(a) != qm_fd_addr_get64(b);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	neq |= qm_fd_get_format(a) != qm_fd_get_format(b);
9962306a36Sopenharmony_ci	neq |= a->cfg != b->cfg;
10062306a36Sopenharmony_ci	neq |= a->cmd != b->cmd;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return neq;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* test */
10662306a36Sopenharmony_cistatic int do_enqueues(struct qman_fq *fq)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	unsigned int loop;
10962306a36Sopenharmony_ci	int err = 0;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	for (loop = 0; loop < NUM_ENQUEUES; loop++) {
11262306a36Sopenharmony_ci		if (qman_enqueue(fq, &fd)) {
11362306a36Sopenharmony_ci			pr_crit("qman_enqueue() failed\n");
11462306a36Sopenharmony_ci			err = -EIO;
11562306a36Sopenharmony_ci		}
11662306a36Sopenharmony_ci		fd_inc(&fd);
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return err;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ciint qman_test_api(void)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	unsigned int flags, frmcnt;
12562306a36Sopenharmony_ci	int err;
12662306a36Sopenharmony_ci	struct qman_fq *fq = &fq_base;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	pr_info("%s(): Starting\n", __func__);
12962306a36Sopenharmony_ci	fd_init(&fd);
13062306a36Sopenharmony_ci	fd_init(&fd_dq);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* Initialise (parked) FQ */
13362306a36Sopenharmony_ci	err = qman_create_fq(0, FQ_FLAGS, fq);
13462306a36Sopenharmony_ci	if (err) {
13562306a36Sopenharmony_ci		pr_crit("qman_create_fq() failed\n");
13662306a36Sopenharmony_ci		goto failed;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	err = qman_init_fq(fq, QMAN_INITFQ_FLAG_LOCAL, NULL);
13962306a36Sopenharmony_ci	if (err) {
14062306a36Sopenharmony_ci		pr_crit("qman_init_fq() failed\n");
14162306a36Sopenharmony_ci		goto failed;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	/* Do enqueues + VDQCR, twice. (Parked FQ) */
14462306a36Sopenharmony_ci	err = do_enqueues(fq);
14562306a36Sopenharmony_ci	if (err)
14662306a36Sopenharmony_ci		goto failed;
14762306a36Sopenharmony_ci	pr_info("VDQCR (till-empty);\n");
14862306a36Sopenharmony_ci	frmcnt = QM_VDQCR_NUMFRAMES_TILLEMPTY;
14962306a36Sopenharmony_ci	err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt);
15062306a36Sopenharmony_ci	if (err) {
15162306a36Sopenharmony_ci		pr_crit("qman_volatile_dequeue() failed\n");
15262306a36Sopenharmony_ci		goto failed;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci	err = do_enqueues(fq);
15562306a36Sopenharmony_ci	if (err)
15662306a36Sopenharmony_ci		goto failed;
15762306a36Sopenharmony_ci	pr_info("VDQCR (%d of %d);\n", NUM_PARTIAL, NUM_ENQUEUES);
15862306a36Sopenharmony_ci	frmcnt = QM_VDQCR_NUMFRAMES_SET(NUM_PARTIAL);
15962306a36Sopenharmony_ci	err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt);
16062306a36Sopenharmony_ci	if (err) {
16162306a36Sopenharmony_ci		pr_crit("qman_volatile_dequeue() failed\n");
16262306a36Sopenharmony_ci		goto failed;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci	pr_info("VDQCR (%d of %d);\n", NUM_ENQUEUES - NUM_PARTIAL,
16562306a36Sopenharmony_ci		NUM_ENQUEUES);
16662306a36Sopenharmony_ci	frmcnt = QM_VDQCR_NUMFRAMES_SET(NUM_ENQUEUES - NUM_PARTIAL);
16762306a36Sopenharmony_ci	err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt);
16862306a36Sopenharmony_ci	if (err) {
16962306a36Sopenharmony_ci		pr_err("qman_volatile_dequeue() failed\n");
17062306a36Sopenharmony_ci		goto failed;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	err = do_enqueues(fq);
17462306a36Sopenharmony_ci	if (err)
17562306a36Sopenharmony_ci		goto failed;
17662306a36Sopenharmony_ci	pr_info("scheduled dequeue (till-empty)\n");
17762306a36Sopenharmony_ci	err = qman_schedule_fq(fq);
17862306a36Sopenharmony_ci	if (err) {
17962306a36Sopenharmony_ci		pr_crit("qman_schedule_fq() failed\n");
18062306a36Sopenharmony_ci		goto failed;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	wait_event(waitqueue, sdqcr_complete);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* Retire and OOS the FQ */
18562306a36Sopenharmony_ci	err = qman_retire_fq(fq, &flags);
18662306a36Sopenharmony_ci	if (err < 0) {
18762306a36Sopenharmony_ci		pr_crit("qman_retire_fq() failed\n");
18862306a36Sopenharmony_ci		goto failed;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci	wait_event(waitqueue, retire_complete);
19162306a36Sopenharmony_ci	if (flags & QMAN_FQ_STATE_BLOCKOOS) {
19262306a36Sopenharmony_ci		err = -EIO;
19362306a36Sopenharmony_ci		pr_crit("leaking frames\n");
19462306a36Sopenharmony_ci		goto failed;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci	err = qman_oos_fq(fq);
19762306a36Sopenharmony_ci	if (err) {
19862306a36Sopenharmony_ci		pr_crit("qman_oos_fq() failed\n");
19962306a36Sopenharmony_ci		goto failed;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci	qman_destroy_fq(fq);
20262306a36Sopenharmony_ci	pr_info("%s(): Finished\n", __func__);
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cifailed:
20662306a36Sopenharmony_ci	WARN_ON(1);
20762306a36Sopenharmony_ci	return err;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *p,
21162306a36Sopenharmony_ci					struct qman_fq *fq,
21262306a36Sopenharmony_ci					const struct qm_dqrr_entry *dq,
21362306a36Sopenharmony_ci					bool sched_napi)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	if (WARN_ON(fd_neq(&fd_dq, &dq->fd))) {
21662306a36Sopenharmony_ci		pr_err("BADNESS: dequeued frame doesn't match;\n");
21762306a36Sopenharmony_ci		return qman_cb_dqrr_consume;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci	fd_inc(&fd_dq);
22062306a36Sopenharmony_ci	if (!(dq->stat & QM_DQRR_STAT_UNSCHEDULED) && !fd_neq(&fd_dq, &fd)) {
22162306a36Sopenharmony_ci		sdqcr_complete = 1;
22262306a36Sopenharmony_ci		wake_up(&waitqueue);
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci	return qman_cb_dqrr_consume;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void cb_ern(struct qman_portal *p, struct qman_fq *fq,
22862306a36Sopenharmony_ci		   const union qm_mr_entry *msg)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	pr_crit("cb_ern() unimplemented");
23162306a36Sopenharmony_ci	WARN_ON(1);
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic void cb_fqs(struct qman_portal *p, struct qman_fq *fq,
23562306a36Sopenharmony_ci		   const union qm_mr_entry *msg)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	u8 verb = (msg->verb & QM_MR_VERB_TYPE_MASK);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if ((verb != QM_MR_VERB_FQRN) && (verb != QM_MR_VERB_FQRNI)) {
24062306a36Sopenharmony_ci		pr_crit("unexpected FQS message");
24162306a36Sopenharmony_ci		WARN_ON(1);
24262306a36Sopenharmony_ci		return;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci	pr_info("Retirement message received\n");
24562306a36Sopenharmony_ci	retire_complete = 1;
24662306a36Sopenharmony_ci	wake_up(&waitqueue);
24762306a36Sopenharmony_ci}
248