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: Weili Qian <qianweili@huawei.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
parent
3cd53a27c2
commit
760fe22cf5
@ -201,7 +201,10 @@
|
|||||||
#define QM_WAIT_DST_ACK 10
|
#define QM_WAIT_DST_ACK 10
|
||||||
#define QM_MAX_PF_WAIT_COUNT 10
|
#define QM_MAX_PF_WAIT_COUNT 10
|
||||||
#define QM_MAX_VF_WAIT_COUNT 40
|
#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_MB_CNT_VF 0x104010
|
||||||
#define QM_DFX_DB_CNT_VF 0x104020
|
#define QM_DFX_DB_CNT_VF 0x104020
|
||||||
@ -285,6 +288,16 @@ enum acc_err_result {
|
|||||||
ACC_ERR_RECOVERED,
|
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 {
|
struct qm_cqe {
|
||||||
__le32 rsvd0;
|
__le32 rsvd0;
|
||||||
__le16 cmd_id;
|
__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);
|
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)
|
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)
|
static void qm_trigger_vf_interrupt(struct hisi_qm *qm, u32 fun_num)
|
||||||
@ -4038,7 +4116,8 @@ stop_fail:
|
|||||||
return ret;
|
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;
|
struct pci_dev *pdev = qm->pdev;
|
||||||
int ret;
|
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)
|
if (!qm->vfs_num)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = qm_vf_reset_prepare(qm, stop_reason);
|
/* Kunpeng930 supports to notify VFs to stop before PF reset */
|
||||||
if (ret)
|
if (qm->ops->ping_all_vfs) {
|
||||||
pci_err(pdev, "failed to prepare reset, ret = %d.\n", ret);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -4072,7 +4158,14 @@ static int qm_reset_prepare_ready(struct hisi_qm *qm)
|
|||||||
struct pci_dev *pdev = qm->pdev;
|
struct pci_dev *pdev = qm->pdev;
|
||||||
struct hisi_qm *pf_qm = pci_get_drvdata(pci_physfn(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)
|
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 pci_dev *pdev = qm->pdev;
|
||||||
struct hisi_qm *pf_qm = pci_get_drvdata(pci_physfn(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)
|
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;
|
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)
|
if (ret)
|
||||||
pci_err(pdev, "failed to stop vfs by pf in soft reset.\n");
|
pci_err(pdev, "failed to stop vfs by pf in soft reset.\n");
|
||||||
|
|
||||||
@ -4243,7 +4343,7 @@ restart_fail:
|
|||||||
return ret;
|
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;
|
struct pci_dev *pdev = qm->pdev;
|
||||||
int ret;
|
int ret;
|
||||||
@ -4257,9 +4357,16 @@ static int qm_try_start_vfs(struct hisi_qm *qm)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qm_vf_reset_done(qm);
|
/* Kunpeng930 supports to notify VFs to start after PF reset. */
|
||||||
if (ret)
|
if (qm->ops->ping_all_vfs) {
|
||||||
pci_warn(pdev, "failed to start vfs, ret = %d.\n", ret);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -4363,7 +4470,7 @@ static int qm_controller_reset_done(struct hisi_qm *qm)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qm_try_start_vfs(qm);
|
ret = qm_try_start_vfs(qm, QM_PF_RESET_DONE);
|
||||||
if (ret)
|
if (ret)
|
||||||
pci_err(pdev, "failed to start vfs by pf in soft reset.\n");
|
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)
|
if (ret)
|
||||||
pci_err(pdev, "failed to start by vfs in soft reset!\n");
|
pci_err(pdev, "failed to start by vfs in soft reset!\n");
|
||||||
|
|
||||||
|
qm_cmd_init(qm);
|
||||||
qm_restart_done(qm);
|
qm_restart_done(qm);
|
||||||
|
|
||||||
qm_reset_bit_clear(qm);
|
qm_reset_bit_clear(qm);
|
||||||
@ -4462,7 +4570,11 @@ void hisi_qm_reset_prepare(struct pci_dev *pdev)
|
|||||||
return;
|
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)
|
if (ret)
|
||||||
pci_err(pdev, "failed to stop vfs by pf in FLR.\n");
|
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;
|
goto flr_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qm_try_start_vfs(qm);
|
ret = qm_try_start_vfs(qm, QM_PF_RESET_DONE);
|
||||||
if (ret)
|
if (ret)
|
||||||
pci_err(pdev, "failed to start vfs by pf in FLR.\n");
|
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");
|
pci_err(pdev, "failed to start by vfs in FLR!\n");
|
||||||
|
|
||||||
flr_done:
|
flr_done:
|
||||||
|
if (qm->fun_type == QM_HW_PF)
|
||||||
|
qm_cmd_init(qm);
|
||||||
|
|
||||||
if (qm_flr_reset_complete(pdev))
|
if (qm_flr_reset_complete(pdev))
|
||||||
pci_info(pdev, "FLR reset complete\n");
|
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)
|
static void qm_cmd_process(struct work_struct *cmd_process)
|
||||||
{
|
{
|
||||||
struct hisi_qm *qm = container_of(cmd_process,
|
struct hisi_qm *qm = container_of(cmd_process,
|
||||||
struct hisi_qm, cmd_process);
|
struct hisi_qm, cmd_process);
|
||||||
struct device *dev = &qm->pdev->dev;
|
struct device *dev = &qm->pdev->dev;
|
||||||
u64 msg;
|
u64 msg;
|
||||||
|
u32 cmd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4635,9 +4866,23 @@ static void qm_cmd_process(struct work_struct *cmd_process)
|
|||||||
*/
|
*/
|
||||||
ret = qm_get_mb_cmd(qm, &msg, 0);
|
ret = qm_get_mb_cmd(qm, &msg, 0);
|
||||||
qm_clear_cmd_interrupt(qm, 0);
|
qm_clear_cmd_interrupt(qm, 0);
|
||||||
if (ret)
|
if (ret) {
|
||||||
dev_err(dev, "failed to get msg from source!\n");
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user