Commit 760fe22c authored by Weili Qian's avatar Weili Qian Committed by Herbert Xu

crypto: hisilicon/qm - update reset flow

This patch updates the reset flow based on PF/VF communications. VFs
will be stopped after receiving reset message from PF, and wait for
reset finish to restart VFs.
Signed-off-by: default avatarWeili Qian <qianweili@huawei.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 3cd53a27
......@@ -201,7 +201,10 @@
#define QM_WAIT_DST_ACK 10
#define QM_MAX_PF_WAIT_COUNT 10
#define QM_MAX_VF_WAIT_COUNT 40
#define QM_VF_RESET_WAIT_US 20000
#define QM_VF_RESET_WAIT_CNT 3000
#define QM_VF_RESET_WAIT_TIMEOUT_US \
(QM_VF_RESET_WAIT_US * QM_VF_RESET_WAIT_CNT)
#define QM_DFX_MB_CNT_VF 0x104010
#define QM_DFX_DB_CNT_VF 0x104020
......@@ -285,6 +288,16 @@ enum acc_err_result {
ACC_ERR_RECOVERED,
};
enum qm_mb_cmd {
QM_PF_FLR_PREPARE = 0x01,
QM_PF_SRST_PREPARE,
QM_PF_RESET_DONE,
QM_VF_PREPARE_DONE,
QM_VF_PREPARE_FAIL,
QM_VF_START_DONE,
QM_VF_START_FAIL,
};
struct qm_cqe {
__le32 rsvd0;
__le16 cmd_id;
......@@ -1890,9 +1903,74 @@ static void qm_clear_cmd_interrupt(struct hisi_qm *qm, u64 vf_mask)
writel(val, qm->io_base + QM_IFC_INT_SOURCE_V);
}
static void qm_handle_vf_msg(struct hisi_qm *qm, u32 vf_id)
{
struct device *dev = &qm->pdev->dev;
u32 cmd;
u64 msg;
int ret;
ret = qm_get_mb_cmd(qm, &msg, vf_id);
if (ret) {
dev_err(dev, "failed to get msg from VF(%u)!\n", vf_id);
return;
}
cmd = msg & QM_MB_CMD_DATA_MASK;
switch (cmd) {
case QM_VF_PREPARE_FAIL:
dev_err(dev, "failed to stop VF(%u)!\n", vf_id);
break;
case QM_VF_START_FAIL:
dev_err(dev, "failed to start VF(%u)!\n", vf_id);
break;
case QM_VF_PREPARE_DONE:
case QM_VF_START_DONE:
break;
default:
dev_err(dev, "unsupported cmd %u sent by VF(%u)!\n", cmd, vf_id);
break;
}
}
static int qm_wait_vf_prepare_finish(struct hisi_qm *qm)
{
return 0;
struct device *dev = &qm->pdev->dev;
u32 vfs_num = qm->vfs_num;
int cnt = 0;
int ret = 0;
u64 val;
u32 i;
if (!qm->vfs_num || qm->ver < QM_HW_V3)
return 0;
while (true) {
val = readq(qm->io_base + QM_IFC_INT_SOURCE_P);
/* All VFs send command to PF, break */
if ((val & GENMASK(vfs_num, 1)) == GENMASK(vfs_num, 1))
break;
if (++cnt > QM_MAX_PF_WAIT_COUNT) {
ret = -EBUSY;
break;
}
msleep(QM_WAIT_DST_ACK);
}
/* PF check VFs msg */
for (i = 1; i <= vfs_num; i++) {
if (val & BIT(i))
qm_handle_vf_msg(qm, i);
else
dev_err(dev, "VF(%u) not ping PF!\n", i);
}
/* PF clear interrupt to ack VFs */
qm_clear_cmd_interrupt(qm, val);
return ret;
}
static void qm_trigger_vf_interrupt(struct hisi_qm *qm, u32 fun_num)
......@@ -4038,7 +4116,8 @@ static int qm_vf_reset_prepare(struct hisi_qm *qm,
return ret;
}
static int qm_try_stop_vfs(struct hisi_qm *qm, enum qm_stop_reason stop_reason)
static int qm_try_stop_vfs(struct hisi_qm *qm, u64 cmd,
enum qm_stop_reason stop_reason)
{
struct pci_dev *pdev = qm->pdev;
int ret;
......@@ -4046,9 +4125,16 @@ static int qm_try_stop_vfs(struct hisi_qm *qm, enum qm_stop_reason stop_reason)
if (!qm->vfs_num)
return 0;
ret = qm_vf_reset_prepare(qm, stop_reason);
if (ret)
pci_err(pdev, "failed to prepare reset, ret = %d.\n", ret);
/* Kunpeng930 supports to notify VFs to stop before PF reset */
if (qm->ops->ping_all_vfs) {
ret = qm->ops->ping_all_vfs(qm, cmd);
if (ret)
pci_err(pdev, "failed to send cmd to all VFs before PF reset!\n");
} else {
ret = qm_vf_reset_prepare(qm, stop_reason);
if (ret)
pci_err(pdev, "failed to prepare reset, ret = %d.\n", ret);
}
return ret;
}
......@@ -4072,7 +4158,14 @@ static int qm_reset_prepare_ready(struct hisi_qm *qm)
struct pci_dev *pdev = qm->pdev;
struct hisi_qm *pf_qm = pci_get_drvdata(pci_physfn(pdev));
return qm_wait_reset_finish(pf_qm);
/*
* PF and VF on host doesnot support resetting at the
* same time on Kunpeng920.
*/
if (qm->ver < QM_HW_V3)
return qm_wait_reset_finish(pf_qm);
return qm_wait_reset_finish(qm);
}
static void qm_reset_bit_clear(struct hisi_qm *qm)
......@@ -4080,7 +4173,10 @@ static void qm_reset_bit_clear(struct hisi_qm *qm)
struct pci_dev *pdev = qm->pdev;
struct hisi_qm *pf_qm = pci_get_drvdata(pci_physfn(pdev));
clear_bit(QM_RESETTING, &pf_qm->misc_ctl);
if (qm->ver < QM_HW_V3)
clear_bit(QM_RESETTING, &pf_qm->misc_ctl);
clear_bit(QM_RESETTING, &qm->misc_ctl);
}
static int qm_controller_reset_prepare(struct hisi_qm *qm)
......@@ -4094,7 +4190,11 @@ static int qm_controller_reset_prepare(struct hisi_qm *qm)
return ret;
}
ret = qm_try_stop_vfs(qm, QM_SOFT_RESET);
/* PF obtains the information of VF by querying the register. */
qm_cmd_uninit(qm);
/* Whether VFs stop successfully, soft reset will continue. */
ret = qm_try_stop_vfs(qm, QM_PF_SRST_PREPARE, QM_SOFT_RESET);
if (ret)
pci_err(pdev, "failed to stop vfs by pf in soft reset.\n");
......@@ -4243,7 +4343,7 @@ static int qm_vf_reset_done(struct hisi_qm *qm)
return ret;
}
static int qm_try_start_vfs(struct hisi_qm *qm)
static int qm_try_start_vfs(struct hisi_qm *qm, enum qm_mb_cmd cmd)
{
struct pci_dev *pdev = qm->pdev;
int ret;
......@@ -4257,9 +4357,16 @@ static int qm_try_start_vfs(struct hisi_qm *qm)
return ret;
}
ret = qm_vf_reset_done(qm);
if (ret)
pci_warn(pdev, "failed to start vfs, ret = %d.\n", ret);
/* Kunpeng930 supports to notify VFs to start after PF reset. */
if (qm->ops->ping_all_vfs) {
ret = qm->ops->ping_all_vfs(qm, cmd);
if (ret)
pci_warn(pdev, "failed to send cmd to all VFs after PF reset!\n");
} else {
ret = qm_vf_reset_done(qm);
if (ret)
pci_warn(pdev, "failed to start vfs, ret = %d.\n", ret);
}
return ret;
}
......@@ -4363,7 +4470,7 @@ static int qm_controller_reset_done(struct hisi_qm *qm)
return ret;
}
ret = qm_try_start_vfs(qm);
ret = qm_try_start_vfs(qm, QM_PF_RESET_DONE);
if (ret)
pci_err(pdev, "failed to start vfs by pf in soft reset.\n");
......@@ -4371,6 +4478,7 @@ static int qm_controller_reset_done(struct hisi_qm *qm)
if (ret)
pci_err(pdev, "failed to start by vfs in soft reset!\n");
qm_cmd_init(qm);
qm_restart_done(qm);
qm_reset_bit_clear(qm);
......@@ -4462,7 +4570,11 @@ void hisi_qm_reset_prepare(struct pci_dev *pdev)
return;
}
ret = qm_try_stop_vfs(qm, QM_SOFT_RESET);
/* PF obtains the information of VF by querying the register. */
if (qm->fun_type == QM_HW_PF)
qm_cmd_uninit(qm);
ret = qm_try_stop_vfs(qm, QM_PF_FLR_PREPARE, QM_FLR);
if (ret)
pci_err(pdev, "failed to stop vfs by pf in FLR.\n");
......@@ -4517,7 +4629,7 @@ void hisi_qm_reset_done(struct pci_dev *pdev)
goto flr_done;
}
ret = qm_try_start_vfs(qm);
ret = qm_try_start_vfs(qm, QM_PF_RESET_DONE);
if (ret)
pci_err(pdev, "failed to start vfs by pf in FLR.\n");
......@@ -4526,6 +4638,9 @@ void hisi_qm_reset_done(struct pci_dev *pdev)
pci_err(pdev, "failed to start by vfs in FLR!\n");
flr_done:
if (qm->fun_type == QM_HW_PF)
qm_cmd_init(qm);
if (qm_flr_reset_complete(pdev))
pci_info(pdev, "FLR reset complete\n");
......@@ -4621,12 +4736,128 @@ static void hisi_qm_controller_reset(struct work_struct *rst_work)
}
static void qm_pf_reset_vf_prepare(struct hisi_qm *qm,
enum qm_stop_reason stop_reason)
{
enum qm_mb_cmd cmd = QM_VF_PREPARE_DONE;
struct pci_dev *pdev = qm->pdev;
int ret;
ret = qm_reset_prepare_ready(qm);
if (ret) {
dev_err(&pdev->dev, "reset prepare not ready!\n");
atomic_set(&qm->status.flags, QM_STOP);
cmd = QM_VF_PREPARE_FAIL;
goto err_prepare;
}
ret = hisi_qm_stop(qm, stop_reason);
if (ret) {
dev_err(&pdev->dev, "failed to stop QM, ret = %d.\n", ret);
atomic_set(&qm->status.flags, QM_STOP);
cmd = QM_VF_PREPARE_FAIL;
goto err_prepare;
}
err_prepare:
pci_save_state(pdev);
ret = qm->ops->ping_pf(qm, cmd);
if (ret)
dev_warn(&pdev->dev, "PF responds timeout in reset prepare!\n");
}
static void qm_pf_reset_vf_done(struct hisi_qm *qm)
{
enum qm_mb_cmd cmd = QM_VF_START_DONE;
struct pci_dev *pdev = qm->pdev;
int ret;
pci_restore_state(pdev);
ret = hisi_qm_start(qm);
if (ret) {
dev_err(&pdev->dev, "failed to start QM, ret = %d.\n", ret);
cmd = QM_VF_START_FAIL;
}
ret = qm->ops->ping_pf(qm, cmd);
if (ret)
dev_warn(&pdev->dev, "PF responds timeout in reset done!\n");
qm_reset_bit_clear(qm);
}
static int qm_wait_pf_reset_finish(struct hisi_qm *qm)
{
struct device *dev = &qm->pdev->dev;
u32 val, cmd;
u64 msg;
int ret;
/* Wait for reset to finish */
ret = readl_relaxed_poll_timeout(qm->io_base + QM_IFC_INT_SOURCE_V, val,
val == BIT(0), QM_VF_RESET_WAIT_US,
QM_VF_RESET_WAIT_TIMEOUT_US);
/* hardware completion status should be available by this time */
if (ret) {
dev_err(dev, "couldn't get reset done status from PF, timeout!\n");
return -ETIMEDOUT;
}
/*
* Whether message is got successfully,
* VF needs to ack PF by clearing the interrupt.
*/
ret = qm_get_mb_cmd(qm, &msg, 0);
qm_clear_cmd_interrupt(qm, 0);
if (ret) {
dev_err(dev, "failed to get msg from PF in reset done!\n");
return ret;
}
cmd = msg & QM_MB_CMD_DATA_MASK;
if (cmd != QM_PF_RESET_DONE) {
dev_err(dev, "the cmd(%u) is not reset done!\n", cmd);
ret = -EINVAL;
}
return ret;
}
static void qm_pf_reset_vf_process(struct hisi_qm *qm,
enum qm_stop_reason stop_reason)
{
struct device *dev = &qm->pdev->dev;
int ret;
dev_info(dev, "device reset start...\n");
/* The message is obtained by querying the register during resetting */
qm_cmd_uninit(qm);
qm_pf_reset_vf_prepare(qm, stop_reason);
ret = qm_wait_pf_reset_finish(qm);
if (ret)
goto err_get_status;
qm_pf_reset_vf_done(qm);
qm_cmd_init(qm);
dev_info(dev, "device reset done.\n");
return;
err_get_status:
qm_cmd_init(qm);
qm_reset_bit_clear(qm);
}
static void qm_cmd_process(struct work_struct *cmd_process)
{
struct hisi_qm *qm = container_of(cmd_process,
struct hisi_qm, cmd_process);
struct device *dev = &qm->pdev->dev;
u64 msg;
u32 cmd;
int ret;
/*
......@@ -4635,9 +4866,23 @@ static void qm_cmd_process(struct work_struct *cmd_process)
*/
ret = qm_get_mb_cmd(qm, &msg, 0);
qm_clear_cmd_interrupt(qm, 0);
if (ret)
if (ret) {
dev_err(dev, "failed to get msg from source!\n");
return;
}
cmd = msg & QM_MB_CMD_DATA_MASK;
switch (cmd) {
case QM_PF_FLR_PREPARE:
qm_pf_reset_vf_process(qm, QM_FLR);
break;
case QM_PF_SRST_PREPARE:
qm_pf_reset_vf_process(qm, QM_SOFT_RESET);
break;
default:
dev_err(dev, "unsupported cmd %u sent by PF!\n", cmd);
break;
}
}
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment