in fsl/qbman/qman.c [2618:2823]
int qman_shutdown_fq(u32 fqid)
{
struct qman_portal *p, *channel_portal;
struct device *dev;
union qm_mc_command *mcc;
union qm_mc_result *mcr;
int orl_empty, drain = 0, ret = 0;
u32 channel, res;
u8 state;
p = get_affine_portal();
dev = p->config->dev;
/* Determine the state of the FQID */
mcc = qm_mc_start(&p->p);
qm_fqid_set(&mcc->fq, fqid);
qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ_NP);
if (!qm_mc_result_timeout(&p->p, &mcr)) {
dev_err(dev, "QUERYFQ_NP timeout\n");
ret = -ETIMEDOUT;
goto out;
}
DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ_NP);
state = mcr->queryfq_np.state & QM_MCR_NP_STATE_MASK;
if (state == QM_MCR_NP_STATE_OOS)
goto out; /* Already OOS, no need to do anymore checks */
/* Query which channel the FQ is using */
mcc = qm_mc_start(&p->p);
qm_fqid_set(&mcc->fq, fqid);
qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ);
if (!qm_mc_result_timeout(&p->p, &mcr)) {
dev_err(dev, "QUERYFQ timeout\n");
ret = -ETIMEDOUT;
goto out;
}
DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ);
/* Need to store these since the MCR gets reused */
channel = qm_fqd_get_chan(&mcr->queryfq.fqd);
qm_fqd_get_wq(&mcr->queryfq.fqd);
if (channel < qm_channel_pool1) {
channel_portal = get_portal_for_channel(channel);
if (channel_portal == NULL) {
dev_err(dev, "Can't find portal for dedicated channel 0x%x\n",
channel);
ret = -EIO;
goto out;
}
} else
channel_portal = p;
switch (state) {
case QM_MCR_NP_STATE_TEN_SCHED:
case QM_MCR_NP_STATE_TRU_SCHED:
case QM_MCR_NP_STATE_ACTIVE:
case QM_MCR_NP_STATE_PARKED:
orl_empty = 0;
mcc = qm_mc_start(&channel_portal->p);
qm_fqid_set(&mcc->fq, fqid);
qm_mc_commit(&channel_portal->p, QM_MCC_VERB_ALTER_RETIRE);
if (!qm_mc_result_timeout(&channel_portal->p, &mcr)) {
dev_err(dev, "ALTER_RETIRE timeout\n");
ret = -ETIMEDOUT;
goto out;
}
DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
QM_MCR_VERB_ALTER_RETIRE);
res = mcr->result; /* Make a copy as we reuse MCR below */
if (res == QM_MCR_RESULT_OK)
drain_mr_fqrni(&channel_portal->p);
if (res == QM_MCR_RESULT_PENDING) {
/*
* Need to wait for the FQRN in the message ring, which
* will only occur once the FQ has been drained. In
* order for the FQ to drain the portal needs to be set
* to dequeue from the channel the FQ is scheduled on
*/
int found_fqrn = 0;
/* Flag that we need to drain FQ */
drain = 1;
if (channel >= qm_channel_pool1 &&
channel < qm_channel_pool1 + 15) {
/* Pool channel, enable the bit in the portal */
} else if (channel < qm_channel_pool1) {
/* Dedicated channel */
} else {
dev_err(dev, "Can't recover FQ 0x%x, ch: 0x%x",
fqid, channel);
ret = -EBUSY;
goto out;
}
/* Set the sdqcr to drain this channel */
if (channel < qm_channel_pool1)
qm_dqrr_sdqcr_set(&channel_portal->p,
QM_SDQCR_TYPE_ACTIVE |
QM_SDQCR_CHANNELS_DEDICATED);
else
qm_dqrr_sdqcr_set(&channel_portal->p,
QM_SDQCR_TYPE_ACTIVE |
QM_SDQCR_CHANNELS_POOL_CONV
(channel));
do {
/* Keep draining DQRR while checking the MR*/
qm_dqrr_drain_nomatch(&channel_portal->p);
/* Process message ring too */
found_fqrn = qm_mr_drain(&channel_portal->p,
FQRN);
cpu_relax();
} while (!found_fqrn);
/* Restore SDQCR */
qm_dqrr_sdqcr_set(&channel_portal->p,
channel_portal->sdqcr);
}
if (res != QM_MCR_RESULT_OK &&
res != QM_MCR_RESULT_PENDING) {
dev_err(dev, "retire_fq failed: FQ 0x%x, res=0x%x\n",
fqid, res);
ret = -EIO;
goto out;
}
if (!(mcr->alterfq.fqs & QM_MCR_FQS_ORLPRESENT)) {
/*
* ORL had no entries, no need to wait until the
* ERNs come in
*/
orl_empty = 1;
}
/*
* Retirement succeeded, check to see if FQ needs
* to be drained
*/
if (drain || mcr->alterfq.fqs & QM_MCR_FQS_NOTEMPTY) {
/* FQ is Not Empty, drain using volatile DQ commands */
do {
u32 vdqcr = fqid | QM_VDQCR_NUMFRAMES_SET(3);
qm_dqrr_vdqcr_set(&p->p, vdqcr);
/*
* Wait for a dequeue and process the dequeues,
* making sure to empty the ring completely
*/
} while (!qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY));
}
while (!orl_empty) {
/* Wait for the ORL to have been completely drained */
orl_empty = qm_mr_drain(&p->p, FQRL);
cpu_relax();
}
mcc = qm_mc_start(&p->p);
qm_fqid_set(&mcc->fq, fqid);
qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_OOS);
if (!qm_mc_result_timeout(&p->p, &mcr)) {
ret = -ETIMEDOUT;
goto out;
}
DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
QM_MCR_VERB_ALTER_OOS);
if (mcr->result != QM_MCR_RESULT_OK) {
dev_err(dev, "OOS after drain fail: FQ 0x%x (0x%x)\n",
fqid, mcr->result);
ret = -EIO;
goto out;
}
break;
case QM_MCR_NP_STATE_RETIRED:
/* Send OOS Command */
mcc = qm_mc_start(&p->p);
qm_fqid_set(&mcc->fq, fqid);
qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_OOS);
if (!qm_mc_result_timeout(&p->p, &mcr)) {
ret = -ETIMEDOUT;
goto out;
}
DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
QM_MCR_VERB_ALTER_OOS);
if (mcr->result != QM_MCR_RESULT_OK) {
dev_err(dev, "OOS fail: FQ 0x%x (0x%x)\n",
fqid, mcr->result);
ret = -EIO;
goto out;
}
break;
case QM_MCR_NP_STATE_OOS:
/* Done */
break;
default:
ret = -EIO;
}
out:
put_affine_portal();
return ret;
}