/* Check if the error causes the master ooo block */ staticbool qm_check_dev_error(struct hisi_qm *qm)
{ struct hisi_qm *pf_qm = pci_get_drvdata(pci_physfn(qm->pdev));
u32 err_status;
if (pf_qm->fun_type == QM_HW_VF) returnfalse;
err_status = qm_get_hw_error_status(pf_qm); if (err_status & pf_qm->err_info.qm_shutdown_mask) returntrue;
if (pf_qm->err_ini->dev_is_abnormal) return pf_qm->err_ini->dev_is_abnormal(pf_qm);
returnfalse;
}
staticint qm_wait_reset_finish(struct hisi_qm *qm)
{ int delay = 0;
/* All reset requests need to be queued for processing */ while (test_and_set_bit(QM_RESETTING, &qm->misc_ctl)) {
msleep(++delay); if (delay > QM_RESET_WAIT_TIMEOUT) return -EBUSY;
}
/* 128 bit should be written to hardware at one time to trigger a mailbox */ staticvoid qm_mb_write(struct hisi_qm *qm, constvoid *src)
{ void __iomem *fun_base = qm->io_base + QM_MB_CMD_SEND_BASE;
mutex_lock(&qm->mailbox_lock);
ret = qm_mb_nolock(qm, &mailbox);
mutex_unlock(&qm->mailbox_lock);
return ret;
}
EXPORT_SYMBOL_GPL(hisi_qm_mb);
/* op 0: set xqc information to hardware, 1: get xqc information from hardware. */ int qm_set_and_get_xqc(struct hisi_qm *qm, u8 cmd, void *xqc, u32 qp_id, bool op)
{ struct qm_mailbox mailbox;
dma_addr_t xqc_dma; void *tmp_xqc;
size_t size; int ret;
/* Setting xqc will fail if master OOO is blocked. */ if (qm_check_dev_error(qm)) {
dev_err(&qm->pdev->dev, "failed to send mailbox since qm is stop!\n"); return -EIO;
}
mutex_lock(&qm->mailbox_lock); if (!op)
memcpy(tmp_xqc, xqc, size);
qm_mb_pre_init(&mailbox, cmd, xqc_dma, qp_id, op);
ret = qm_mb_nolock(qm, &mailbox); if (!ret && op)
memcpy(xqc, tmp_xqc, size);
/** * hisi_qm_get_hw_info() - Get device information. * @qm: The qm which want to get information. * @info_table: Array for storing device information. * @index: Index in info_table. * @is_read: Whether read from reg, 0: not support read from reg. * * This function returns device information the caller needs.
*/
u32 hisi_qm_get_hw_info(struct hisi_qm *qm, conststruct hisi_qm_cap_info *info_table,
u32 index, bool is_read)
{
u32 val;
switch (qm->ver) { case QM_HW_V1: return info_table[index].v1_val; case QM_HW_V2: return info_table[index].v2_val; default: if (!is_read) return info_table[index].v3_val;
if (dev_algs_size >= QM_DEV_ALG_MAX_LEN) {
dev_err(dev, "algs size %u is equal or larger than %d.\n",
dev_algs_size, QM_DEV_ALG_MAX_LEN); return -EINVAL;
}
algs = devm_kzalloc(dev, QM_DEV_ALG_MAX_LEN, GFP_KERNEL); if (!algs) return -ENOMEM;
for (i = 0; i < dev_algs_size; i++) if (alg_msk & dev_algs[i].alg_msk)
strcat(algs, dev_algs[i].alg);
for (i = eqe_num - 1; i >= 0; i--) {
qp = &qm->qp_array[poll_data->qp_finish_id[i]]; if (unlikely(atomic_read(&qp->qp_status.flags) == QP_STOP)) continue;
if (!test_bit(QM_SUPPORT_SVA_PREFETCH, &qm->caps)) return;
switch (PAGE_SIZE) { case SZ_4K:
page_type = 0x0; break; case SZ_16K:
page_type = 0x1; break; case SZ_64K:
page_type = 0x2; break; default:
dev_err(dev, "system page size is not support: %lu, default set to 4KB",
PAGE_SIZE);
}
writel(page_type, qm->io_base + QM_PAGE_SIZE);
}
/* * acc_shaper_para_calc() Get the IR value by the qos formula, the return value * is the expected qos calculated. * the formula: * IR = X Mbps if ir = 1 means IR = 100 Mbps, if ir = 10000 means = 10Gbps * * IR_b * (2 ^ IR_u) * 8000 * IR(Mbps) = ------------------------- * Tick * (2 ^ IR_s)
*/ static u32 acc_shaper_para_calc(u64 cir_b, u64 cir_u, u64 cir_s)
{ return ((cir_b * QM_QOS_DIVISOR_CLK) * (1 << cir_u)) /
(QM_QOS_TICK * (1 << cir_s));
}
static u32 acc_shaper_calc_cbs_s(u32 ir)
{ int table_size = ARRAY_SIZE(shaper_cbs_s); int i;
for (i = 0; i < table_size; i++) { if (ir >= shaper_cbs_s[i].start && ir <= shaper_cbs_s[i].end) return shaper_cbs_s[i].val;
}
return QM_SHAPER_MIN_CBS_S;
}
static u32 acc_shaper_calc_cir_s(u32 ir)
{ int table_size = ARRAY_SIZE(shaper_cir_s); int i;
for (i = 0; i < table_size; i++) { if (ir >= shaper_cir_s[i].start && ir <= shaper_cir_s[i].end) return shaper_cir_s[i].val;
}
ret = qm_get_shaper_para(qos * QM_QOS_RATE, &qm->factor[fun_num]); if (ret) {
dev_err(&qm->pdev->dev, "failed to calculate shaper parameter!\n"); return ret;
}
writel(qm->type_rate, qm->io_base + QM_SHAPER_CFG); for (i = ALG_TYPE_0; i <= ALG_TYPE_1; i++) { /* The base number of queue reuse for different alg type */
ret = qm_set_vft_common(qm, SHAPER_VFT, fun_num, i, 1); if (ret) return ret;
}
return 0;
}
/* The config should be conducted after qm_dev_mem_reset() */ staticint qm_set_sqc_cqc_vft(struct hisi_qm *qm, u32 fun_num, u32 base,
u32 number)
{ int ret, i;
for (i = SQC_VFT; i <= CQC_VFT; i++) {
ret = qm_set_vft_common(qm, i, fun_num, base, number); if (ret) return ret;
}
/* init default shaper qos val */ if (test_bit(QM_SUPPORT_FUNC_QOS, &qm->caps)) {
ret = qm_shaper_init_vft(qm, fun_num); if (ret) goto back_sqc_cqc;
}
return 0;
back_sqc_cqc: for (i = SQC_VFT; i <= CQC_VFT; i++)
qm_set_vft_common(qm, i, fun_num, 0, 0);
ret = qm->ops->get_ifc(qm, &cmd, NULL, vf_id); if (ret) {
dev_err(dev, "failed to get command from VF(%u)!\n", vf_id); return;
}
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 command(0x%x) sent by VF(%u)!\n", cmd, vf_id); break;
}
}
staticint qm_wait_vf_prepare_finish(struct hisi_qm *qm)
{ 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 || !test_bit(QM_SUPPORT_MB_COMMAND, &qm->caps)) 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);
staticint qm_ping_all_vfs(struct hisi_qm *qm, enum qm_ifc_cmd cmd)
{ struct device *dev = &qm->pdev->dev;
u32 vfs_num = qm->vfs_num;
u64 val = 0; int cnt = 0; int ret;
u32 i;
ret = qm->ops->set_ifc_begin(qm, cmd, 0, QM_MB_PING_ALL_VFS); if (ret) {
dev_err(dev, "failed to send command(0x%x) to all vfs!\n", cmd);
qm->ops->set_ifc_end(qm); return ret;
}
qm_trigger_vf_interrupt(qm, QM_IFC_SEND_ALL_VFS); while (true) {
msleep(QM_WAIT_DST_ACK);
val = readq(qm->io_base + QM_IFC_READY_STATUS); /* If all VFs acked, PF notifies VFs successfully. */ if (!(val & GENMASK(vfs_num, 1))) {
qm->ops->set_ifc_end(qm); return 0;
}
if (++cnt > QM_MAX_PF_WAIT_COUNT) break;
}
qm->ops->set_ifc_end(qm);
/* Check which vf respond timeout. */ for (i = 1; i <= vfs_num; i++) { if (val & BIT(i))
dev_err(dev, "failed to get response from VF(%u)!\n", i);
}
return -ETIMEDOUT;
}
staticint qm_ping_pf(struct hisi_qm *qm, enum qm_ifc_cmd cmd)
{ int cnt = 0;
u32 val; int ret;
ret = qm->ops->set_ifc_begin(qm, cmd, 0, 0); if (ret) {
dev_err(&qm->pdev->dev, "failed to send command(0x%x) to PF!\n", cmd); goto unlock;
}
qm_trigger_pf_interrupt(qm); /* Waiting for PF response */ while (true) {
msleep(QM_WAIT_DST_ACK);
val = readl(qm->io_base + QM_IFC_INT_SET_V); if (!(val & QM_IFC_INT_STATUS_MASK)) break;
if (++cnt > QM_MAX_VF_WAIT_COUNT) {
ret = -ETIMEDOUT; break;
}
}
pci_write_config_dword(pdev, pdev->msi_cap, cmd); if (set) { for (i = 0; i < MAX_WAIT_COUNTS; i++) {
pci_read_config_dword(pdev, pdev->msi_cap, &cmd); if (cmd & QM_MSI_CAP_ENABLE) return 0;
if (atomic_read(&qm->status.flags) == QM_STOP) {
dev_info_ratelimited(dev, "failed to create qp as qm is stop!\n"); return ERR_PTR(-EPERM);
}
if (qm->qp_in_used == qm->qp_num) {
dev_info_ratelimited(dev, "All %u queues of QM are busy!\n",
qm->qp_num);
atomic64_inc(&qm->debug.dfx.create_qp_err_cnt); return ERR_PTR(-EBUSY);
}
qp_id = idr_alloc_cyclic(&qm->qp_idr, NULL, 0, qm->qp_num, GFP_ATOMIC); if (qp_id < 0) {
dev_info_ratelimited(dev, "All %u queues of QM are busy!\n",
qm->qp_num);
atomic64_inc(&qm->debug.dfx.create_qp_err_cnt); return ERR_PTR(-EBUSY);
}
/** * hisi_qm_create_qp() - Create a queue pair from qm. * @qm: The qm we create a qp from. * @alg_type: Accelerator specific algorithm type in sqc. * * Return created qp, negative error code if failed.
*/ staticstruct hisi_qp *hisi_qm_create_qp(struct hisi_qm *qm, u8 alg_type)
{ struct hisi_qp *qp; int ret;
ret = qm_pm_get_sync(qm); if (ret) return ERR_PTR(ret);
/** * hisi_qm_release_qp() - Release a qp back to its qm. * @qp: The qp we want to release. * * This function releases the resource of a qp.
*/ staticvoid hisi_qm_release_qp(struct hisi_qp *qp)
{ struct hisi_qm *qm = qp->qm;
/** * hisi_qm_start_qp() - Start a qp into running. * @qp: The qp we want to start to run. * @arg: Accelerator specific argument. * * After this function, qp can receive request from user. Return 0 if * successful, negative error code if failed.
*/ int hisi_qm_start_qp(struct hisi_qp *qp, unsignedlong arg)
{ struct hisi_qm *qm = qp->qm; int ret;
down_write(&qm->qps_lock);
ret = qm_start_qp_nolock(qp, arg);
up_write(&qm->qps_lock);
/** * qm_drain_qp() - Drain a qp. * @qp: The qp we want to drain. * * If the device does not support stopping queue by sending mailbox, * determine whether the queue is cleared by judging the tail pointers of * sq and cq.
*/ staticint qm_drain_qp(struct hisi_qp *qp)
{ struct hisi_qm *qm = qp->qm;
u32 state = 0; int ret;
/* No need to judge if master OOO is blocked. */ if (qm_check_dev_error(qm)) return 0;
/* HW V3 supports drain qp by device */ if (test_bit(QM_SUPPORT_STOP_QP, &qm->caps)) {
ret = qm_stop_qp(qp); if (ret) {
dev_err(&qm->pdev->dev, "Failed to stop qp!\n");
state = QM_STOP_QUEUE_FAIL; goto set_dev_state;
} return ret;
}
ret = qm_wait_qp_empty(qm, &state, qp->qp_id); if (ret) goto set_dev_state;
return 0;
set_dev_state: if (qm->debug.dev_dfx.dev_timeout)
qm->debug.dev_dfx.dev_state = state;
/* * It is allowed to stop and release qp when reset, If the qp is * stopped when reset but still want to be released then, the * is_resetting flag should be set negative so that this qp will not * be restarted after reset.
*/ if (atomic_read(&qp->qp_status.flags) != QP_START) {
qp->is_resetting = false; return;
}
atomic_set(&qp->qp_status.flags, QP_STOP);
/* V3 supports direct stop function when FLR prepare */ if (qm->ver < QM_HW_V3 || qm->status.stop_reason == QM_NORMAL) {
ret = qm_drain_qp(qp); if (ret)
dev_err(dev, "Failed to drain out data for stopping qp(%u)!\n", qp->qp_id);
}
flush_workqueue(qm->wq); if (unlikely(qp->is_resetting && atomic_read(&qp->qp_status.used)))
qp_stop_fail_cb(qp);
dev_dbg(dev, "stop queue %u!", qp->qp_id);
}
/** * hisi_qm_stop_qp() - Stop a qp in qm. * @qp: The qp we want to stop. * * This function is reverse of hisi_qm_start_qp.
*/ void hisi_qm_stop_qp(struct hisi_qp *qp)
{
down_write(&qp->qm->qps_lock);
qm_stop_qp_nolock(qp);
up_write(&qp->qm->qps_lock);
}
EXPORT_SYMBOL_GPL(hisi_qm_stop_qp);
/** * hisi_qp_send() - Queue up a task in the hardware queue. * @qp: The qp in which to put the message. * @msg: The message. * * This function will return -EBUSY if qp is currently full, and -EAGAIN * if qp related qm is resetting. * * Note: This function may run with qm_irq_thread and ACC reset at same time. * It has no race with qm_irq_thread. However, during hisi_qp_send, ACC * reset may happen, we have no lock here considering performance. This * causes current qm_db sending fail or can not receive sended sqe. QM * sync/async receive function should handle the error sqe. ACC reset * done function should clear used sqe to 0.
*/ int hisi_qp_send(struct hisi_qp *qp, constvoid *msg)
{ struct hisi_qp_status *qp_status = &qp->qp_status;
u16 sq_tail = qp_status->sq_tail;
u16 sq_tail_next = (sq_tail + 1) % qp->sq_depth; void *sqe = qm_get_avail_sqe(qp);
if (unlikely(atomic_read(&qp->qp_status.flags) == QP_STOP ||
atomic_read(&qp->qm->status.flags) == QM_STOP ||
qp->is_resetting)) {
dev_info_ratelimited(&qp->qm->pdev->dev, "QP is stopped or resetting\n"); return -EAGAIN;
}
/* This function returns free number of qp in qm. */ staticint hisi_qm_get_available_instances(struct uacce_device *uacce)
{ struct hisi_qm *qm = uacce->priv; int ret;
down_read(&qm->qps_lock);
ret = qm->qp_num - qm->qp_in_used;
up_read(&qm->qps_lock);
return ret;
}
staticvoid hisi_qm_set_hw_reset(struct hisi_qm *qm, int offset)
{ int i;
for (i = 0; i < qm->qp_num; i++)
qm_set_qp_disable(&qm->qp_array[i], offset);
}
while (QM_CQE_PHASE(cqe) == qp->qp_status.cqc_phase) { /* make sure to read data from memory */
dma_rmb();
qm_cq_head_update(qp);
cqe = qp->cqe + qp->qp_status.cq_head;
updated = 1;
}
if (copy_to_user((void __user *)arg, &qp_info, sizeof(struct hisi_qp_info))) return -EFAULT;
return 0;
}
return -EINVAL;
}
/** * qm_hw_err_isolate() - Try to set the isolation status of the uacce device * according to user's configuration of error threshold. * @qm: the uacce device
*/ staticint qm_hw_err_isolate(struct hisi_qm *qm)
{ struct qm_hw_err *err, *tmp, *hw_err; struct qm_err_isolate *isolate;
u32 count = 0;
isolate = &qm->isolate_data;
#define SECONDS_PER_HOUR 3600
/* All the hw errs are processed by PF driver */ if (qm->uacce->is_vf || isolate->is_isolate || !isolate->err_threshold) return 0;
hw_err = kzalloc(sizeof(*hw_err), GFP_KERNEL); if (!hw_err) return -ENOMEM;
/* * Time-stamp every slot AER error. Then check the AER error log when the * next device AER error occurred. if the device slot AER error count exceeds * the setting error threshold in one hour, the isolated state will be set * to true. And the AER error logs that exceed one hour will be cleared.
*/
mutex_lock(&isolate->isolate_lock);
hw_err->timestamp = jiffies;
list_for_each_entry_safe(err, tmp, &isolate->qm_hw_errs, list) { if ((hw_err->timestamp - err->timestamp) / HZ >
SECONDS_PER_HOUR) {
list_del(&err->list);
kfree(err);
} else {
count++;
}
}
list_add(&hw_err->list, &isolate->qm_hw_errs);
mutex_unlock(&isolate->isolate_lock);
if (count >= isolate->err_threshold)
isolate->is_isolate = true;
/** * qm_frozen() - Try to froze QM to cut continuous queue request. If * there is user on the QM, return failure without doing anything. * @qm: The qm needed to be fronzen. * * This function frozes QM, then we can do SRIOV disabling.
*/ staticint qm_frozen(struct hisi_qm *qm)
{ if (test_bit(QM_DRIVER_REMOVING, &qm->misc_ctl)) return 0;
staticint qm_try_frozen_vfs(struct pci_dev *pdev, struct hisi_qm_list *qm_list)
{ struct hisi_qm *qm, *vf_qm; struct pci_dev *dev; int ret = 0;
if (!qm_list || !pdev) return -EINVAL;
/* Try to frozen all the VFs as disable SRIOV */
mutex_lock(&qm_list->lock);
list_for_each_entry(qm, &qm_list->list, list) {
dev = qm->pdev; if (dev == pdev) continue; if (pci_physfn(dev) == pdev) {
vf_qm = pci_get_drvdata(dev);
ret = qm_frozen(vf_qm); if (ret) goto frozen_fail;
}
}
frozen_fail:
mutex_unlock(&qm_list->lock);
return ret;
}
/** * hisi_qm_wait_task_finish() - Wait until the task is finished * when removing the driver. * @qm: The qm needed to wait for the task to finish. * @qm_list: The list of all available devices.
*/ void hisi_qm_wait_task_finish(struct hisi_qm *qm, struct hisi_qm_list *qm_list)
{ while (qm_frozen(qm) ||
((qm->fun_type == QM_HW_PF) &&
qm_try_frozen_vfs(qm->pdev, qm_list))) {
msleep(WAIT_PERIOD);
}
while (test_bit(QM_RST_SCHED, &qm->misc_ctl) ||
test_bit(QM_RESETTING, &qm->misc_ctl))
msleep(WAIT_PERIOD);
if (test_bit(QM_SUPPORT_MB_COMMAND, &qm->caps))
flush_work(&qm->cmd_process);
staticvoid hisi_qp_memory_uninit(struct hisi_qm *qm, int num)
{ struct device *dev = &qm->pdev->dev; struct qm_dma *qdma; int i;
for (i = num - 1; i >= 0; i--) {
qdma = &qm->qp_array[i].qdma;
dma_free_coherent(dev, qdma->size, qdma->va, qdma->dma);
kfree(qm->poll_data[i].qp_finish_id);
}
kfree(qm->poll_data);
kfree(qm->qp_array);
}
staticint hisi_qp_memory_init(struct hisi_qm *qm, size_t dma_size, int id,
u16 sq_depth, u16 cq_depth)
{ struct device *dev = &qm->pdev->dev;
size_t off = qm->sqe_size * sq_depth; struct hisi_qp *qp; int ret = -ENOMEM;
qm->poll_data[id].qp_finish_id = kcalloc(qm->qp_num, sizeof(u16),
GFP_KERNEL); if (!qm->poll_data[id].qp_finish_id) return -ENOMEM;
pci_set_drvdata(pdev, qm);
mutex_init(&qm->mailbox_lock);
mutex_init(&qm->ifc_lock);
init_rwsem(&qm->qps_lock);
qm->qp_in_used = 0; if (test_bit(QM_SUPPORT_RPM, &qm->caps)) { if (!acpi_device_power_manageable(ACPI_COMPANION(&pdev->dev)))
dev_info(&pdev->dev, "_PS0 and _PR0 are not defined");
}
}
/** * hisi_qm_get_vft() - Get vft from a qm. * @qm: The qm we want to get its vft. * @base: The base number of queue in vft. * @number: The number of queues in vft. * * We can allocate multiple queues to a qm by configuring virtual function * table. We get related configures by this function. Normally, we call this * function in VF driver to get the queue information. * * qm hw v1 does not support this interface.
*/ staticint hisi_qm_get_vft(struct hisi_qm *qm, u32 *base, u32 *number)
{ if (!base || !number) return -EINVAL;
if (!qm->ops->get_vft) {
dev_err(&qm->pdev->dev, "Don't support vft read!\n"); return -EINVAL;
}
return qm->ops->get_vft(qm, base, number);
}
/** * hisi_qm_set_vft() - Set vft to a qm. * @qm: The qm we want to set its vft. * @fun_num: The function number. * @base: The base number of queue in vft. * @number: The number of queues in vft. * * This function is alway called in PF driver, it is used to assign queues * among PF and VFs. * * Assign queues A~B to PF: hisi_qm_set_vft(qm, 0, A, B - A + 1) * Assign queues A~B to VF: hisi_qm_set_vft(qm, 2, A, B - A + 1) * (VF function number 0x2)
*/ staticint hisi_qm_set_vft(struct hisi_qm *qm, u32 fun_num, u32 base,
u32 number)
{
u32 max_q_num = qm->ctrl_qp_num;
if (base >= max_q_num || number > max_q_num ||
(base + number) > max_q_num) return -EINVAL;
/** * hisi_qm_start() - start qm * @qm: The qm to be started. * * This function starts a qm, then we can allocate qp from this qm.
*/ int hisi_qm_start(struct hisi_qm *qm)
{ struct device *dev = &qm->pdev->dev; int ret = 0;
down_write(&qm->qps_lock);
dev_dbg(dev, "qm start with %u queue pairs\n", qm->qp_num);
if (!qm->qp_num) {
dev_err(dev, "qp_num should not be 0\n");
ret = -EINVAL; goto err_unlock;
}
ret = __hisi_qm_start(qm); if (ret) goto err_unlock;
/* Stop started qps in reset flow */ staticvoid qm_stop_started_qp(struct hisi_qm *qm)
{ struct hisi_qp *qp; int i;
for (i = 0; i < qm->qp_num; i++) {
qp = &qm->qp_array[i]; if (atomic_read(&qp->qp_status.flags) == QP_START) {
qp->is_resetting = true;
qm_stop_qp_nolock(qp);
}
}
}
/** * qm_invalid_queues() - invalid all queues in use. * @qm: The qm in which the queues will be invalidated. * * This function invalid all queues in use. If the doorbell command is sent * to device in user space after the device is reset, the device discards * the doorbell command.
*/ staticvoid qm_invalid_queues(struct hisi_qm *qm)
{ struct hisi_qp *qp; struct qm_sqc *sqc; struct qm_cqc *cqc; int i;
/* * Normal stop queues is no longer used and does not need to be * invalid queues.
*/ if (qm->status.stop_reason == QM_NORMAL) return;
if (qm->status.stop_reason == QM_DOWN)
hisi_qm_cache_wb(qm);
for (i = 0; i < qm->qp_num; i++) {
qp = &qm->qp_array[i]; if (!qp->is_resetting) continue;
/* Modify random data and set sqc close bit to invalid queue. */
sqc = qm->sqc + i;
cqc = qm->cqc + i;
sqc->w8 = cpu_to_le16(QM_XQC_RANDOM_DATA);
sqc->w13 = cpu_to_le16(QM_SQC_DISABLE_QP);
cqc->w8 = cpu_to_le16(QM_XQC_RANDOM_DATA); if (qp->is_in_kernel)
memset(qp->qdma.va, 0, qp->qdma.size);
}
}
/** * hisi_qm_stop() - Stop a qm. * @qm: The qm which will be stopped. * @r: The reason to stop qm. * * This function stops qm and its qps, then qm can not accept request. * Related resources are not released at this state, we can use hisi_qm_start * to let qm start again.
*/ int hisi_qm_stop(struct hisi_qm *qm, enum qm_stop_reason r)
{ struct device *dev = &qm->pdev->dev; int ret = 0;
down_write(&qm->qps_lock);
if (atomic_read(&qm->status.flags) == QM_STOP) goto err_unlock;
/* Stop all the request sending at first. */
atomic_set(&qm->status.flags, QM_STOP);
qm->status.stop_reason = r;
if (qm->status.stop_reason != QM_NORMAL) {
hisi_qm_set_hw_reset(qm, QM_RESET_STOP_TX_OFFSET); /* * When performing soft reset, the hardware will no longer * do tasks, and the tasks in the device will be flushed * out directly since the master ooo is closed.
*/ if (test_bit(QM_SUPPORT_STOP_FUNC, &qm->caps) &&
r != QM_SOFT_RESET) {
ret = qm_drain_qm(qm); if (ret) {
dev_err(dev, "failed to drain qm!\n"); goto err_unlock;
}
}
qm_disable_eq_aeq_interrupts(qm); if (qm->fun_type == QM_HW_PF) {
ret = hisi_qm_set_vft(qm, 0, 0, 0); if (ret < 0) {
dev_err(dev, "Failed to set vft!\n");
ret = -EBUSY; goto err_unlock;
}
}
staticenum acc_err_result qm_hw_error_handle(struct hisi_qm *qm)
{ if (!qm->ops->hw_error_handle) {
dev_err(&qm->pdev->dev, "QM doesn't support hw error report!\n"); return ACC_ERR_NONE;
}
return qm->ops->hw_error_handle(qm);
}
/** * hisi_qm_dev_err_init() - Initialize device error configuration. * @qm: The qm for which we want to do error initialization. * * Initialize QM and device error related configuration.
*/ void hisi_qm_dev_err_init(struct hisi_qm *qm)
{ if (qm->fun_type == QM_HW_VF) return;
qm_hw_error_init(qm);
if (!qm->err_ini->hw_err_enable) {
dev_err(&qm->pdev->dev, "Device doesn't support hw error init!\n"); return;
}
qm->err_ini->hw_err_enable(qm);
}
EXPORT_SYMBOL_GPL(hisi_qm_dev_err_init);
/** * hisi_qm_dev_err_uninit() - Uninitialize device error configuration. * @qm: The qm for which we want to do error uninitialization. * * Uninitialize QM and device error related configuration.
*/ void hisi_qm_dev_err_uninit(struct hisi_qm *qm)
{ if (qm->fun_type == QM_HW_VF) return;
/** * hisi_qm_free_qps() - free multiple queue pairs. * @qps: The queue pairs need to be freed. * @qp_num: The num of queue pairs.
*/ void hisi_qm_free_qps(struct hisi_qp **qps, int qp_num)
{ int i;
if (!qps || qp_num <= 0) return;
for (i = qp_num - 1; i >= 0; i--)
hisi_qm_release_qp(qps[i]);
}
EXPORT_SYMBOL_GPL(hisi_qm_free_qps);
list_for_each_entry(qm, &qm_list->list, list) {
dev = &qm->pdev->dev;
dev_node = dev_to_node(dev); if (dev_node < 0)
dev_node = 0;
res = kzalloc(sizeof(*res), GFP_KERNEL); if (!res) return -ENOMEM;
res->qm = qm;
res->distance = node_distance(dev_node, node);
n = head;
list_for_each_entry(tmp, head, list) { if (res->distance < tmp->distance) {
n = &tmp->list; break;
}
}
list_add_tail(&res->list, n);
}
return 0;
}
/** * hisi_qm_alloc_qps_node() - Create multiple queue pairs. * @qm_list: The list of all available devices. * @qp_num: The number of queue pairs need created. * @alg_type: The algorithm type. * @node: The numa node. * @qps: The queue pairs need created. * * This function will sort all available device according to numa distance. * Then try to create all queue pairs from one device, if all devices do * not meet the requirements will return error.
*/ int hisi_qm_alloc_qps_node(struct hisi_qm_list *qm_list, int qp_num,
u8 alg_type, int node, struct hisi_qp **qps)
{ struct hisi_qm_resource *tmp; int ret = -ENODEV;
LIST_HEAD(head); int i;
if (!qps || !qm_list || qp_num <= 0) return -EINVAL;
mutex_lock(&qm_list->lock); if (hisi_qm_sort_devices(node, &head, qm_list)) {
mutex_unlock(&qm_list->lock); goto err;
}
list_for_each_entry(tmp, &head, list) { for (i = 0; i < qp_num; i++) {
qps[i] = hisi_qm_create_qp(tmp->qm, alg_type); if (IS_ERR(qps[i])) {
hisi_qm_free_qps(qps, i); break;
}
}
if (i == qp_num) {
ret = 0; break;
}
}
mutex_unlock(&qm_list->lock); if (ret)
pr_info("Failed to create qps, node[%d], alg[%u], qp[%d]!\n",
node, alg_type, qp_num);
/* * When disabling SR-IOV, clear the configuration of each VF in the hardware * sequentially. Failure to clear a single VF should not affect the clearing * operation of other VFs.
*/ for (i = 1; i <= qm->vfs_num; i++)
(void)hisi_qm_set_vft(qm, i, 0, 0);
total_vfs = pci_sriov_get_totalvfs(qm->pdev); if (fun_index > total_vfs) return -EINVAL;
qm->factor[fun_index].func_qos = qos;
ret = qm_get_shaper_para(ir, &qm->factor[fun_index]); if (ret) {
dev_err(dev, "failed to calculate shaper parameter!\n"); return -EINVAL;
}
for (i = ALG_TYPE_0; i <= ALG_TYPE_1; i++) { /* The base number of queue reuse for different alg type */
ret = qm_set_vft_common(qm, SHAPER_VFT, fun_index, i, 1); if (ret) {
dev_err(dev, "type: %d, failed to set shaper vft!\n", i); return -EINVAL;
}
}
qos = qm_get_shaper_vft_qos(qm, fun_num); if (!qos) {
dev_err(dev, "function(%u) failed to get qos by PF!\n", fun_num); return;
}
ret = qm_ping_single_vf(qm, QM_PF_SET_QOS, qos, fun_num); if (ret)
dev_err(dev, "failed to send command(0x%x) to VF(%u)!\n", QM_PF_SET_QOS, fun_num);
}
staticint qm_vf_read_qos(struct hisi_qm *qm)
{ int cnt = 0; int ret = -EINVAL;
/* reset mailbox qos val */
qm->mb_qos = 0;
/* vf ping pf to get function qos */
ret = qm_ping_pf(qm, QM_VF_GET_QOS); if (ret) {
pci_err(qm->pdev, "failed to send cmd to PF to get qos!\n"); return ret;
}
while (true) {
msleep(QM_WAIT_DST_ACK); if (qm->mb_qos) break;
ret = hisi_qm_get_dfx_access(qm); if (ret) return ret;
/* Mailbox and reset cannot be operated at the same time */ if (test_and_set_bit(QM_RESETTING, &qm->misc_ctl)) {
pci_err(qm->pdev, "dev resetting, read alg qos failed!\n");
ret = -EAGAIN; goto err_put_dfx_access;
}
if (qm->fun_type == QM_HW_PF) {
ir = qm_get_shaper_vft_qos(qm, 0);
} else {
ret = qm_vf_read_qos(qm); if (ret) goto err_get_status;
ir = qm->mb_qos;
}
qos_val = ir / QM_QOS_RATE;
ret = scnprintf(tbuf, QM_DBG_READ_LEN, "%u\n", qos_val);
ret = simple_read_from_buffer(buf, count, pos, tbuf, ret);
ret = sscanf(buf, "%s %s", tbuf_bdf, val_buf); if (ret != QM_QOS_PARAM_NUM) return -EINVAL;
ret = kstrtoul(val_buf, 10, val); if (ret || *val == 0 || *val > QM_QOS_MAX_VAL) {
pci_err(qm->pdev, "input qos value is error, please set 1~1000!\n"); return -EINVAL;
}
dev = bus_find_device_by_name(bus_type, NULL, tbuf_bdf); if (!dev) {
pci_err(qm->pdev, "input pci bdf number is error!\n"); return -ENODEV;
}
pdev = container_of(dev, struct pci_dev, dev); if (pci_physfn(pdev) != qm->pdev) {
pci_err(qm->pdev, "the pdev input does not match the pf!\n");
put_device(dev); return -EINVAL;
}
len = simple_write_to_buffer(tbuf, QM_DBG_READ_LEN - 1, pos, buf, count); if (len < 0) return len;
tbuf[len] = '\0';
ret = qm_get_qos_value(qm, tbuf, &val, &fun_index); if (ret) return ret;
/* Mailbox and reset cannot be operated at the same time */ if (test_and_set_bit(QM_RESETTING, &qm->misc_ctl)) {
pci_err(qm->pdev, "dev resetting, write alg qos failed!\n"); return -EAGAIN;
}
ret = qm_pm_get_sync(qm); if (ret) {
ret = -EINVAL; goto err_get_status;
}
ret = qm_func_shaper_enable(qm, fun_index, val); if (ret) {
pci_err(qm->pdev, "failed to enable function shaper!\n");
ret = -EINVAL; goto err_put_sync;
}
pci_info(qm->pdev, "the qos value of function%u is set to %lu.\n",
fun_index, val);
ret = count;
/** * hisi_qm_set_algqos_init() - Initialize function qos debugfs files. * @qm: The qm for which we want to add debugfs files. * * Create function qos debugfs files, VF ping PF to get function qos.
*/ void hisi_qm_set_algqos_init(struct hisi_qm *qm)
{ if (qm->fun_type == QM_HW_PF)
debugfs_create_file("alg_qos", 0644, qm->debug.debug_root,
qm, &qm_algqos_fops); elseif (test_bit(QM_SUPPORT_MB_COMMAND, &qm->caps))
debugfs_create_file("alg_qos", 0444, qm->debug.debug_root,
qm, &qm_algqos_fops);
}
staticvoid hisi_qm_init_vf_qos(struct hisi_qm *qm, int total_func)
{ int i;
for (i = 1; i <= total_func; i++)
qm->factor[i].func_qos = QM_QOS_MAX_VAL;
}
/** * hisi_qm_sriov_enable() - enable virtual functions * @pdev: the PCIe device * @max_vfs: the number of virtual functions to enable * * Returns the number of enabled VFs. If there are VFs enabled already or * max_vfs is more than the total number of device can be enabled, returns * failure.
*/ int hisi_qm_sriov_enable(struct pci_dev *pdev, int max_vfs)
{ struct hisi_qm *qm = pci_get_drvdata(pdev); int pre_existing_vfs, num_vfs, total_vfs, ret;
/** * hisi_qm_sriov_disable - disable virtual functions * @pdev: the PCI device. * @is_frozen: true when all the VFs are frozen. * * Return failure if there are VFs assigned already or VF is in used.
*/ int hisi_qm_sriov_disable(struct pci_dev *pdev, bool is_frozen)
{ struct hisi_qm *qm = pci_get_drvdata(pdev);
if (pci_vfs_assigned(pdev)) {
pci_err(pdev, "Failed to disable VFs as VFs are assigned!\n"); return -EPERM;
}
/* While VF is in used, SRIOV cannot be disabled. */ if (!is_frozen && qm_try_frozen_vfs(pdev, qm->qm_list)) {
pci_err(pdev, "Task is using its VF!\n"); return -EBUSY;
}
/** * hisi_qm_sriov_configure - configure the number of VFs * @pdev: The PCI device * @num_vfs: The number of VFs need enabled * * Enable SR-IOV according to num_vfs, 0 means disable.
*/ int hisi_qm_sriov_configure(struct pci_dev *pdev, int num_vfs)
{ if (num_vfs == 0) return hisi_qm_sriov_disable(pdev, false); else return hisi_qm_sriov_enable(pdev, num_vfs);
}
EXPORT_SYMBOL_GPL(hisi_qm_sriov_configure);
staticenum acc_err_result qm_dev_err_handle(struct hisi_qm *qm)
{ if (!qm->err_ini->get_err_result) {
dev_err(&qm->pdev->dev, "Device doesn't support reset!\n"); return ACC_ERR_NONE;
}
/** * hisi_qm_dev_err_detected() - Get device and qm error status then log it. * @pdev: The PCI device which need report error. * @state: The connectivity between CPU and device. * * We register this function into PCIe AER handlers, It will report device or * qm hardware error status when error occur.
*/
pci_ers_result_t hisi_qm_dev_err_detected(struct pci_dev *pdev,
pci_channel_state_t state)
{ struct hisi_qm *qm = pci_get_drvdata(pdev); enum acc_err_result ret;
writel(ACC_VENDOR_ID_VALUE, qm->io_base + QM_PEH_VENDOR_ID);
ret = readl_relaxed_poll_timeout(qm->io_base + QM_PEH_VENDOR_ID, val,
(val == ACC_VENDOR_ID_VALUE),
POLL_PERIOD, POLL_TIMEOUT); if (ret) {
dev_err(&pdev->dev, "Fails to read QM reg!\n"); return ret;
}
writel(PCI_VENDOR_ID_HUAWEI, qm->io_base + QM_PEH_VENDOR_ID);
ret = readl_relaxed_poll_timeout(qm->io_base + QM_PEH_VENDOR_ID, val,
(val == PCI_VENDOR_ID_HUAWEI),
POLL_PERIOD, POLL_TIMEOUT); if (ret)
dev_err(&pdev->dev, "Fails to read QM reg in the second time!\n");
pci_write_config_word(pdev, PCI_COMMAND, cmd); for (i = 0; i < MAX_WAIT_COUNTS; i++) {
pci_read_config_word(pdev, PCI_COMMAND, &cmd); if (set == ((cmd & PCI_COMMAND_MEMORY) >> 1)) return 0;
udelay(1);
}
return -ETIMEDOUT;
}
staticint qm_set_vf_mse(struct hisi_qm *qm, bool set)
{ struct pci_dev *pdev = qm->pdev;
u16 sriov_ctrl; int pos; int i;
/* * Since function qm_set_vf_mse is called only after SRIOV is enabled, * pci_find_ext_capability cannot return 0, pos does not need to be * checked.
*/
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
pci_read_config_word(pdev, pos + PCI_SRIOV_CTRL, &sriov_ctrl); if (set)
sriov_ctrl |= PCI_SRIOV_CTRL_MSE; else
sriov_ctrl &= ~PCI_SRIOV_CTRL_MSE;
pci_write_config_word(pdev, pos + PCI_SRIOV_CTRL, sriov_ctrl);
for (i = 0; i < MAX_WAIT_COUNTS; i++) {
pci_read_config_word(pdev, pos + PCI_SRIOV_CTRL, &sriov_ctrl); if (set == (sriov_ctrl & PCI_SRIOV_CTRL_MSE) >>
ACC_PEH_SRIOV_CTRL_VF_MSE_SHIFT) return 0;
/* Kunpeng930 supports to notify VFs to stop before PF reset */ if (test_bit(QM_SUPPORT_MB_COMMAND, &qm->caps)) {
ret = qm_ping_all_vfs(qm, cmd); if (ret)
pci_err(pdev, "failed to send command 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);
}
if (qm->err_ini->set_priv_status) {
ret = qm->err_ini->set_priv_status(qm); if (ret) return ret;
}
ret = qm_reset_prepare_ready(qm); if (ret) {
pci_err(pdev, "Controller reset not ready!\n"); return ret;
}
qm_dev_ecc_mbit_handle(qm);
/* 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");
ret = hisi_qm_stop(qm, QM_SOFT_RESET); if (ret) {
pci_err(pdev, "Fails to stop QM!\n");
qm_reset_bit_clear(qm); return ret;
}
if (qm->use_sva) {
ret = qm_hw_err_isolate(qm); if (ret)
pci_err(pdev, "failed to isolate hw err!\n");
}
ret = qm_wait_vf_prepare_finish(qm); if (ret)
pci_err(pdev, "failed to stop by vfs in soft reset!\n");
clear_bit(QM_RST_SCHED, &qm->misc_ctl);
return 0;
}
staticint qm_master_ooo_check(struct hisi_qm *qm)
{
u32 val; int ret;
/* Check the ooo register of the device before resetting the device. */
writel(ACC_MASTER_GLOBAL_CTRL_SHUTDOWN, qm->io_base + ACC_MASTER_GLOBAL_CTRL);
ret = readl_relaxed_poll_timeout(qm->io_base + ACC_MASTER_TRANS_RETURN,
val, (val == ACC_MASTER_TRANS_RETURN_RW),
POLL_PERIOD, POLL_TIMEOUT); if (ret)
pci_warn(qm->pdev, "Bus lock! Please reset system.\n");
ret = qm_vf_q_assign(qm, qm->vfs_num); if (ret) {
pci_err(pdev, "failed to assign VFs, ret = %d.\n", ret); return ret;
}
/* Kunpeng930 supports to notify VFs to start after PF reset. */ if (test_bit(QM_SUPPORT_MB_COMMAND, &qm->caps)) {
ret = qm_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);
}
if (!qm->err_status.is_qm_ecc_mbit &&
!qm->err_status.is_dev_ecc_mbit) return;
/* temporarily close the OOO port used for PEH to write out MSI */
value = readl(qm->io_base + ACC_AM_CFG_PORT_WR_EN);
writel(value & ~qm->err_info.msi_wr_port,
qm->io_base + ACC_AM_CFG_PORT_WR_EN);
/* clear dev ecc 2bit error source if having */
value = qm_get_dev_err_status(qm) & qm->err_info.ecc_2bits_mask; if (value && qm->err_ini->clear_dev_hw_err_status)
qm->err_ini->clear_dev_hw_err_status(qm, value);
if (!qm->err_status.is_qm_ecc_mbit &&
!qm->err_status.is_dev_ecc_mbit) return;
/* open the OOO port for PEH to write out MSI */
value = readl(qm->io_base + ACC_AM_CFG_PORT_WR_EN);
value |= qm->err_info.msi_wr_port;
writel(value, qm->io_base + ACC_AM_CFG_PORT_WR_EN);
/* if resetting fails, isolate the device */ if (qm->use_sva)
qm->isolate_data.is_isolate = true; return ret;
}
/** * hisi_qm_dev_slot_reset() - slot reset * @pdev: the PCIe device * * This function offers QM relate PCIe device reset interface. Drivers which * use QM can use this function as slot_reset in its struct pci_error_handlers.
*/
pci_ers_result_t hisi_qm_dev_slot_reset(struct pci_dev *pdev)
{ struct hisi_qm *qm = pci_get_drvdata(pdev); int ret;
if (pdev->is_virtfn) return PCI_ERS_RESULT_RECOVERED;
/* * Check whether there is an ECC mbit error, If it occurs, need to * wait for soft reset to fix it.
*/ while (qm_check_dev_error(qm)) {
msleep(++delay); if (delay > QM_RESET_WAIT_TIMEOUT) return;
}
ret = qm_reset_prepare_ready(qm); if (ret) {
pci_err(pdev, "FLR not ready!\n"); return;
}
/* 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_DOWN); if (ret)
pci_err(pdev, "failed to stop vfs by pf in FLR.\n");
ret = hisi_qm_stop(qm, QM_DOWN); if (ret) {
pci_err(pdev, "Failed to stop QM, ret = %d.\n", ret);
hisi_qm_set_hw_reset(qm, QM_RESET_STOP_TX_OFFSET);
hisi_qm_set_hw_reset(qm, QM_RESET_STOP_RX_OFFSET); return;
}
ret = qm_wait_vf_prepare_finish(qm); if (ret)
pci_err(pdev, "failed to stop by vfs in FLR!\n");
atomic64_inc(&qm->debug.dfx.abnormal_irq_cnt);
ret = qm_process_dev_error(qm); if (ret == ACC_ERR_NEED_RESET &&
!test_bit(QM_DRIVER_REMOVING, &qm->misc_ctl) &&
!test_and_set_bit(QM_RST_SCHED, &qm->misc_ctl))
schedule_work(&qm->rst_work);
return IRQ_HANDLED;
}
/** * hisi_qm_dev_shutdown() - Shutdown device. * @pdev: The device will be shutdown. * * This function will stop qm when OS shutdown or rebooting.
*/ void hisi_qm_dev_shutdown(struct pci_dev *pdev)
{ struct hisi_qm *qm = pci_get_drvdata(pdev); int ret;
ret = hisi_qm_stop(qm, QM_DOWN); if (ret)
dev_err(&pdev->dev, "Fail to stop qm in shutdown!\n");
}
EXPORT_SYMBOL_GPL(hisi_qm_dev_shutdown);
/* 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->ops->get_ifc(qm, &cmd, NULL, 0);
qm_clear_cmd_interrupt(qm, 0); if (ret) {
dev_err(dev, "failed to get command from PF in reset done!\n"); return ret;
}
if (cmd != QM_PF_RESET_DONE) {
dev_err(dev, "the command(0x%x) is not reset done!\n", cmd);
ret = -EINVAL;
}
/* * Get the msg from source by sending mailbox. Whether message is got * successfully, destination needs to ack source by clearing the interrupt.
*/
ret = qm->ops->get_ifc(qm, &cmd, &data, fun_num);
qm_clear_cmd_interrupt(qm, BIT(fun_num)); if (ret) {
dev_err(dev, "failed to get command from source!\n"); return;
}
switch (cmd) { case QM_PF_FLR_PREPARE:
qm_pf_reset_vf_process(qm, QM_DOWN); break; case QM_PF_SRST_PREPARE:
qm_pf_reset_vf_process(qm, QM_SOFT_RESET); break; case QM_VF_GET_QOS:
qm_vf_get_qos(qm, fun_num); break; case QM_PF_SET_QOS:
qm->mb_qos = data; break; default:
dev_err(dev, "unsupported command(0x%x) sent by function(%u)!\n", cmd, fun_num); break;
}
}
if (qm->fun_type == QM_HW_PF) {
val = readq(qm->io_base + QM_IFC_INT_SOURCE_P); if (!val) return;
for (i = 1; i <= vfs_num; i++) { if (val & BIT(i))
qm_handle_cmd_msg(qm, i);
}
return;
}
qm_handle_cmd_msg(qm, 0);
}
/** * hisi_qm_alg_register() - Register alg to crypto. * @qm: The qm needs add. * @qm_list: The qm list. * @guard: Guard of qp_num. * * Register algorithm to crypto when the function is satisfy guard.
*/ int hisi_qm_alg_register(struct hisi_qm *qm, struct hisi_qm_list *qm_list, int guard)
{ struct device *dev = &qm->pdev->dev;
if (qm->ver <= QM_HW_V2 && qm->use_sva) {
dev_info(dev, "HW V2 not both use uacce sva mode and hardware crypto algs.\n"); return 0;
}
if (qm->qp_num < guard) {
dev_info(dev, "qp_num is less than task need.\n"); return 0;
}
/** * hisi_qm_alg_unregister() - Unregister alg from crypto. * @qm: The qm needs delete. * @qm_list: The qm list. * @guard: Guard of qp_num. * * Unregister algorithm from crypto when the last function is satisfy guard.
*/ void hisi_qm_alg_unregister(struct hisi_qm *qm, struct hisi_qm_list *qm_list, int guard)
{ if (qm->ver <= QM_HW_V2 && qm->use_sva) return;
val = qm->cap_tables.qm_cap_table[QM_ABNORMAL_IRQ].cap_val; if (!((val >> QM_IRQ_TYPE_SHIFT) & QM_ABN_IRQ_TYPE_MASK)) return 0;
irq_vector = val & QM_IRQ_VECTOR_MASK;
/* For VF, this is a reserved interrupt in V3 version. */ if (qm->fun_type == QM_HW_VF) { if (qm->ver < QM_HW_V3) return 0;
ret = request_irq(pci_irq_vector(pdev, irq_vector), qm_rsvd_irq,
IRQF_NO_AUTOEN, qm->dev_name, qm); if (ret) {
dev_err(&pdev->dev, "failed to request reserved irq, ret = %d!\n", ret); return ret;
} return 0;
}
ret = request_irq(pci_irq_vector(pdev, irq_vector), qm_abnormal_irq, 0, qm->dev_name, qm); if (ret)
dev_err(&qm->pdev->dev, "failed to request abnormal irq, ret = %d!\n", ret);
val = qm->cap_tables.qm_cap_table[QM_MB_IRQ].cap_val; if (!((val >> QM_IRQ_TYPE_SHIFT) & QM_IRQ_TYPE_MASK)) return 0;
irq_vector = val & QM_IRQ_VECTOR_MASK;
ret = request_irq(pci_irq_vector(pdev, irq_vector), qm_mb_cmd_irq, 0, qm->dev_name, qm); if (ret)
dev_err(&pdev->dev, "failed to request function communication irq, ret = %d", ret);
val = qm->cap_tables.qm_cap_table[QM_EQ_IRQ].cap_val; if (!((val >> QM_IRQ_TYPE_SHIFT) & QM_IRQ_TYPE_MASK)) return 0;
irq_vector = val & QM_IRQ_VECTOR_MASK;
ret = request_irq(pci_irq_vector(pdev, irq_vector), qm_eq_irq, 0, qm->dev_name, qm); if (ret)
dev_err(&pdev->dev, "failed to request eq irq, ret = %d", ret);
/* VF's qp_num assigned by PF in v2, and VF can get qp_num by vft. */ if (qm->fun_type == QM_HW_VF) { if (qm->ver != QM_HW_V1) /* v2 starts to support get vft by mailbox */ return hisi_qm_get_vft(qm, &qm->qp_base, &qm->qp_num);
if (test_bit(QM_MODULE_PARAM, &qm->misc_ctl)) { /* Check whether the set qp number is valid */
dev_err(dev, "qp num(%u) is more than max qp num(%u)!\n",
qm->qp_num, qm->max_qp_num); return -EINVAL;
}
dev_info(dev, "Default qp num(%u) is too big, reset it to Function's max qp num(%u)!\n",
qm->qp_num, qm->max_qp_num);
qm->qp_num = qm->max_qp_num;
qm->debug.curr_qm_qp_num = qm->qp_num;
/* Doorbell isolate register is a independent register. */
val = hisi_qm_get_hw_info(qm, qm_cap_info_comm, QM_SUPPORT_DB_ISOLATION, true); if (val)
set_bit(QM_SUPPORT_DB_ISOLATION, &qm->caps);
if (qm->ver >= QM_HW_V3) {
val = readl(qm->io_base + QM_FUNC_CAPS_REG);
qm->cap_ver = val & QM_CAPBILITY_VERSION;
}
/* Get PF/VF common capbility */ for (i = 1; i < ARRAY_SIZE(qm_cap_info_comm); i++) {
val = hisi_qm_get_hw_info(qm, qm_cap_info_comm, i, qm->cap_ver); if (val)
set_bit(qm_cap_info_comm[i].type, &qm->caps);
}
/* Get PF/VF different capbility */ for (i = 0; i < size; i++) {
val = hisi_qm_get_hw_info(qm, cap_info, i, qm->cap_ver); if (val)
set_bit(cap_info[i].type, &qm->caps);
}
/* Fetch and save the value of qm capability registers */ return qm_pre_store_caps(qm);
}
/* one more page for device or qp statuses */
qp_dma_size = qm->sqe_size * sq_depth + sizeof(struct qm_cqe) * cq_depth;
qp_dma_size = PAGE_ALIGN(qp_dma_size) + PAGE_SIZE; for (i = 0; i < qm->qp_num; i++) {
qm->poll_data[i].qm = qm;
ret = hisi_qp_memory_init(qm, qp_dma_size, i, sq_depth, cq_depth); if (ret) goto err_init_qp_mem;
/** * hisi_qm_init() - Initialize configures about qm. * @qm: The qm needing init. * * This function init qm, then we can call hisi_qm_start to put qm into work.
*/ int hisi_qm_init(struct hisi_qm *qm)
{ struct pci_dev *pdev = qm->pdev; struct device *dev = &pdev->dev; int ret;
ret = hisi_qm_pci_init(qm); if (ret) return ret;
ret = qm_irqs_register(qm); if (ret) goto err_pci_init;
if (qm->fun_type == QM_HW_PF) { /* Set the doorbell timeout to QM_DB_TIMEOUT_CFG ns. */
writel(QM_DB_TIMEOUT_SET, qm->io_base + QM_DB_TIMEOUT_CFG);
qm_disable_clock_gate(qm);
ret = qm_dev_mem_reset(qm); if (ret) {
dev_err(dev, "failed to reset device memory\n"); goto err_irq_register;
}
}
if (qm->mode == UACCE_MODE_SVA) {
ret = qm_alloc_uacce(qm); if (ret < 0)
dev_warn(dev, "fail to alloc uacce (%d)\n", ret);
}
ret = hisi_qm_memory_init(qm); if (ret) goto err_alloc_uacce;
ret = hisi_qm_init_work(qm); if (ret) goto err_free_qm_memory;
/** * hisi_qm_get_dfx_access() - Try to get dfx access. * @qm: pointer to accelerator device. * * Try to get dfx access, then user can get message. * * If device is in suspended, return failure, otherwise * bump up the runtime PM usage counter.
*/ int hisi_qm_get_dfx_access(struct hisi_qm *qm)
{ struct device *dev = &qm->pdev->dev;
if (pm_runtime_suspended(dev)) {
dev_info(dev, "can not read/write - device in suspended.\n"); return -EAGAIN;
}
ret = qm_set_pf_mse(qm, true); if (ret) {
pci_err(pdev, "failed to enable MSE after resuming!\n"); return ret;
}
ret = qm->ops->set_msi(qm, true); if (ret) {
pci_err(pdev, "failed to enable MSI after resuming!\n"); return ret;
}
ret = qm_dev_hw_init(qm); if (ret) {
pci_err(pdev, "failed to init device after resuming\n"); return ret;
}
qm_cmd_init(qm);
hisi_qm_dev_err_init(qm); /* Set the doorbell timeout to QM_DB_TIMEOUT_CFG ns. */
writel(QM_DB_TIMEOUT_SET, qm->io_base + QM_DB_TIMEOUT_CFG);
qm_disable_clock_gate(qm);
ret = qm_dev_mem_reset(qm); if (ret)
pci_err(pdev, "failed to reset device memory\n");
return ret;
}
/** * hisi_qm_suspend() - Runtime suspend of given device. * @dev: device to suspend. * * Function that suspend the device.
*/ int hisi_qm_suspend(struct device *dev)
{ struct pci_dev *pdev = to_pci_dev(dev); struct hisi_qm *qm = pci_get_drvdata(pdev); int ret;
pci_info(pdev, "entering suspended state\n");
ret = hisi_qm_stop(qm, QM_NORMAL); if (ret) {
pci_err(pdev, "failed to stop qm(%d)\n", ret); return ret;
}
ret = qm_prepare_for_suspend(qm); if (ret)
pci_err(pdev, "failed to prepare suspended(%d)\n", ret);
return ret;
}
EXPORT_SYMBOL_GPL(hisi_qm_suspend);
/** * hisi_qm_resume() - Runtime resume of given device. * @dev: device to resume. * * Function that resume the device.
*/ int hisi_qm_resume(struct device *dev)
{ struct pci_dev *pdev = to_pci_dev(dev); struct hisi_qm *qm = pci_get_drvdata(pdev); int ret;
pci_info(pdev, "resuming from suspend state\n");
ret = qm_rebuild_for_resume(qm); if (ret) {
pci_err(pdev, "failed to rebuild resume(%d)\n", ret); return ret;
}
ret = hisi_qm_start(qm); if (ret) { if (qm_check_dev_error(qm)) {
pci_info(pdev, "failed to start qm due to device error, device will be reset!\n"); return 0;
}
pci_err(pdev, "failed to start qm(%d)!\n", ret);
}
return ret;
}
EXPORT_SYMBOL_GPL(hisi_qm_resume);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Zhou Wang <wangzhou1@hisilicon.com>");
MODULE_DESCRIPTION("HiSilicon Accelerator queue manager driver");
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.109 Sekunden
(vorverarbeitet am 2026-04-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.