Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/crypto/hisilicon/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 139 kB image not shown  

Quelle  qm.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 HiSilicon Limited. */
#include <asm/page.h>
#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/dma-mapping.h>
#include <linux/idr.h>
#include <linux/io.h>
#include <linux/irqreturn.h>
#include <linux/log2.h>
#include <linux/pm_runtime.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/uacce.h>
#include <linux/uaccess.h>
#include <uapi/misc/uacce/hisi_qm.h>
#include <linux/hisi_acc_qm.h>
#include "qm_common.h"

/* eq/aeq irq enable */
#define QM_VF_AEQ_INT_SOURCE  0x0
#define QM_VF_AEQ_INT_MASK  0x4
#define QM_VF_EQ_INT_SOURCE  0x8
#define QM_VF_EQ_INT_MASK  0xc

#define QM_IRQ_VECTOR_MASK  GENMASK(15, 0)
#define QM_IRQ_TYPE_MASK  GENMASK(15, 0)
#define QM_IRQ_TYPE_SHIFT  16
#define QM_ABN_IRQ_TYPE_MASK  GENMASK(7, 0)

/* mailbox */
#define QM_MB_PING_ALL_VFS  0xffff
#define QM_MB_STATUS_MASK  GENMASK(12, 9)

/* sqc shift */
#define QM_SQ_HOP_NUM_SHIFT  0
#define QM_SQ_PAGE_SIZE_SHIFT  4
#define QM_SQ_BUF_SIZE_SHIFT  8
#define QM_SQ_SQE_SIZE_SHIFT  12
#define QM_SQ_PRIORITY_SHIFT  0
#define QM_SQ_ORDERS_SHIFT  4
#define QM_SQ_TYPE_SHIFT  8
#define QM_QC_PASID_ENABLE  0x1
#define QM_QC_PASID_ENABLE_SHIFT 7

#define QM_SQ_TYPE_MASK   GENMASK(3, 0)
#define QM_SQ_TAIL_IDX(sqc)  ((le16_to_cpu((sqc).w11) >> 6) & 0x1)
#define QM_SQC_DISABLE_QP  (1U << 6)
#define QM_XQC_RANDOM_DATA  0xaaaa

/* cqc shift */
#define QM_CQ_HOP_NUM_SHIFT  0
#define QM_CQ_PAGE_SIZE_SHIFT  4
#define QM_CQ_BUF_SIZE_SHIFT  8
#define QM_CQ_CQE_SIZE_SHIFT  12
#define QM_CQ_PHASE_SHIFT  0
#define QM_CQ_FLAG_SHIFT  1

#define QM_CQE_PHASE(cqe)  (le16_to_cpu((cqe)->w7) & 0x1)
#define QM_QC_CQE_SIZE   4
#define QM_CQ_TAIL_IDX(cqc)  ((le16_to_cpu((cqc).w11) >> 6) & 0x1)

/* eqc shift */
#define QM_EQE_AEQE_SIZE  (2UL << 12)
#define QM_EQC_PHASE_SHIFT  16

#define QM_EQE_PHASE(eqe)  ((le32_to_cpu((eqe)->dw0) >> 16) & 0x1)
#define QM_EQE_CQN_MASK   GENMASK(15, 0)

#define QM_AEQE_PHASE(aeqe)  ((le32_to_cpu((aeqe)->dw0) >> 16) & 0x1)
#define QM_AEQE_TYPE_SHIFT  17
#define QM_AEQE_TYPE_MASK  0xf
#define QM_AEQE_CQN_MASK  GENMASK(15, 0)
#define QM_CQ_OVERFLOW   0
#define QM_EQ_OVERFLOW   1
#define QM_CQE_ERROR   2

#define QM_XQ_DEPTH_SHIFT  16
#define QM_XQ_DEPTH_MASK  GENMASK(15, 0)

#define QM_DOORBELL_CMD_SQ  0
#define QM_DOORBELL_CMD_CQ  1
#define QM_DOORBELL_CMD_EQ  2
#define QM_DOORBELL_CMD_AEQ  3

#define QM_DOORBELL_BASE_V1  0x340
#define QM_DB_CMD_SHIFT_V1  16
#define QM_DB_INDEX_SHIFT_V1  32
#define QM_DB_PRIORITY_SHIFT_V1  48
#define QM_PAGE_SIZE   0x0034
#define QM_QP_DB_INTERVAL  0x10000
#define QM_DB_TIMEOUT_CFG  0x100074
#define QM_DB_TIMEOUT_SET  0x1fffff

#define QM_MEM_START_INIT  0x100040
#define QM_MEM_INIT_DONE  0x100044
#define QM_VFT_CFG_RDY   0x10006c
#define QM_VFT_CFG_OP_WR  0x100058
#define QM_VFT_CFG_TYPE   0x10005c
#define QM_VFT_CFG   0x100060
#define QM_VFT_CFG_OP_ENABLE  0x100054
#define QM_PM_CTRL   0x100148
#define QM_IDLE_DISABLE   BIT(9)

#define QM_SUB_VERSION_ID  0x210

#define QM_VFT_CFG_DATA_L  0x100064
#define QM_VFT_CFG_DATA_H  0x100068
#define QM_SQC_VFT_BUF_SIZE  (7ULL << 8)
#define QM_SQC_VFT_SQC_SIZE  (5ULL << 12)
#define QM_SQC_VFT_INDEX_NUMBER  (1ULL << 16)
#define QM_SQC_VFT_START_SQN_SHIFT 28
#define QM_SQC_VFT_VALID  (1ULL << 44)
#define QM_SQC_VFT_SQN_SHIFT  45
#define QM_CQC_VFT_BUF_SIZE  (7ULL << 8)
#define QM_CQC_VFT_SQC_SIZE  (5ULL << 12)
#define QM_CQC_VFT_INDEX_NUMBER  (1ULL << 16)
#define QM_CQC_VFT_VALID  (1ULL << 28)

#define QM_SQC_VFT_BASE_SHIFT_V2 28
#define QM_SQC_VFT_BASE_MASK_V2  GENMASK(15, 0)
#define QM_SQC_VFT_NUM_SHIFT_V2  45
#define QM_SQC_VFT_NUM_MASK_V2  GENMASK(9, 0)
#define QM_MAX_QC_TYPE                  2

#define QM_ABNORMAL_INT_SOURCE  0x100000
#define QM_ABNORMAL_INT_MASK  0x100004
#define QM_ABNORMAL_INT_MASK_VALUE 0x7fff
#define QM_ABNORMAL_INT_STATUS  0x100008
#define QM_ABNORMAL_INT_SET  0x10000c
#define QM_ABNORMAL_INF00  0x100010
#define QM_FIFO_OVERFLOW_TYPE  0xc0
#define QM_FIFO_OVERFLOW_TYPE_SHIFT 6
#define QM_FIFO_OVERFLOW_VF  0x3f
#define QM_FIFO_OVERFLOW_QP_SHIFT 16
#define QM_ABNORMAL_INF01  0x100014
#define QM_DB_TIMEOUT_TYPE  0xc0
#define QM_DB_TIMEOUT_TYPE_SHIFT 6
#define QM_DB_TIMEOUT_VF  0x3f
#define QM_DB_TIMEOUT_QP_SHIFT  16
#define QM_ABNORMAL_INF02  0x100018
#define QM_AXI_POISON_ERR  BIT(22)
#define QM_RAS_CE_ENABLE  0x1000ec
#define QM_RAS_FE_ENABLE  0x1000f0
#define QM_RAS_NFE_ENABLE  0x1000f4
#define QM_RAS_CE_THRESHOLD  0x1000f8
#define QM_RAS_CE_TIMES_PER_IRQ  1
#define QM_OOO_SHUTDOWN_SEL  0x1040f8
#define QM_AXI_RRESP_ERR  BIT(0)
#define QM_ECC_MBIT   BIT(2)
#define QM_DB_TIMEOUT   BIT(10)
#define QM_OF_FIFO_OF   BIT(11)

#define QM_RESET_WAIT_TIMEOUT  400
#define QM_PEH_VENDOR_ID  0x1000d8
#define ACC_VENDOR_ID_VALUE  0x5a5a
#define QM_PEH_DFX_INFO0  0x1000fc
#define QM_PEH_DFX_INFO1  0x100100
#define QM_PEH_DFX_MASK   (BIT(0) | BIT(2))
#define QM_PEH_MSI_FINISH_MASK  GENMASK(19, 16)
#define ACC_PEH_SRIOV_CTRL_VF_MSE_SHIFT 3
#define ACC_PEH_MSI_DISABLE  GENMASK(31, 0)
#define ACC_MASTER_GLOBAL_CTRL_SHUTDOWN 0x1
#define ACC_MASTER_TRANS_RETURN_RW 3
#define ACC_MASTER_TRANS_RETURN  0x300150
#define ACC_MASTER_GLOBAL_CTRL  0x300000
#define ACC_AM_CFG_PORT_WR_EN  0x30001c
#define QM_RAS_NFE_MBIT_DISABLE  ~QM_ECC_MBIT
#define ACC_AM_ROB_ECC_INT_STS  0x300104
#define ACC_ROB_ECC_ERR_MULTPL  BIT(1)
#define QM_MSI_CAP_ENABLE  BIT(16)

/* interfunction communication */
#define QM_IFC_READY_STATUS  0x100128
#define QM_IFC_INT_SET_P  0x100130
#define QM_IFC_INT_CFG   0x100134
#define QM_IFC_INT_SOURCE_P  0x100138
#define QM_IFC_INT_SOURCE_V  0x0020
#define QM_IFC_INT_MASK   0x0024
#define QM_IFC_INT_STATUS  0x0028
#define QM_IFC_INT_SET_V  0x002C
#define QM_PF2VF_PF_W   0x104700
#define QM_VF2PF_PF_R   0x104800
#define QM_VF2PF_VF_W   0x320
#define QM_PF2VF_VF_R   0x380
#define QM_IFC_SEND_ALL_VFS  GENMASK(6, 0)
#define QM_IFC_INT_SOURCE_CLR  GENMASK(63, 0)
#define QM_IFC_INT_SOURCE_MASK  BIT(0)
#define QM_IFC_INT_DISABLE  BIT(0)
#define QM_IFC_INT_STATUS_MASK  BIT(0)
#define QM_IFC_INT_SET_MASK  BIT(0)
#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_VF2PF_REG_SIZE  4
#define QM_IFC_CMD_MASK   GENMASK(31, 0)
#define QM_IFC_DATA_SHIFT  32
#define QM_VF_RESET_WAIT_TIMEOUT_US    \
 (QM_VF_RESET_WAIT_US * QM_VF_RESET_WAIT_CNT)

#define POLL_PERIOD   10
#define POLL_TIMEOUT   1000
#define WAIT_PERIOD_US_MAX  200
#define WAIT_PERIOD_US_MIN  100
#define MAX_WAIT_COUNTS   1000
#define QM_CACHE_WB_START  0x204
#define QM_CACHE_WB_DONE  0x208
#define QM_FUNC_CAPS_REG  0x3100
#define QM_CAPBILITY_VERSION  GENMASK(7, 0)

#define PCI_BAR_2   2
#define PCI_BAR_4   4
#define QMC_ALIGN(sz)   ALIGN(sz, 32)

#define QM_DBG_READ_LEN  256
#define QM_PCI_COMMAND_INVALID  ~0
#define QM_RESET_STOP_TX_OFFSET  1
#define QM_RESET_STOP_RX_OFFSET  2

#define WAIT_PERIOD   20
#define REMOVE_WAIT_DELAY  10

#define QM_QOS_PARAM_NUM  2
#define QM_QOS_MAX_VAL   1000
#define QM_QOS_RATE   100
#define QM_QOS_EXPAND_RATE  1000
#define QM_SHAPER_CIR_B_MASK  GENMASK(7, 0)
#define QM_SHAPER_CIR_U_MASK  GENMASK(10, 8)
#define QM_SHAPER_CIR_S_MASK  GENMASK(14, 11)
#define QM_SHAPER_FACTOR_CIR_U_SHIFT 8
#define QM_SHAPER_FACTOR_CIR_S_SHIFT 11
#define QM_SHAPER_FACTOR_CBS_B_SHIFT 15
#define QM_SHAPER_FACTOR_CBS_S_SHIFT 19
#define QM_SHAPER_CBS_B   1
#define QM_SHAPER_VFT_OFFSET  6
#define QM_QOS_MIN_ERROR_RATE  5
#define QM_SHAPER_MIN_CBS_S  8
#define QM_QOS_TICK   0x300U
#define QM_QOS_DIVISOR_CLK  0x1f40U
#define QM_QOS_MAX_CIR_B  200
#define QM_QOS_MIN_CIR_B  100
#define QM_QOS_MAX_CIR_U  6
#define QM_AUTOSUSPEND_DELAY  3000

 /* abnormal status value for stopping queue */
#define QM_STOP_QUEUE_FAIL  1
#define QM_DUMP_SQC_FAIL  3
#define QM_DUMP_CQC_FAIL  4
#define QM_FINISH_WAIT   5

#define QM_MK_CQC_DW3_V1(hop_num, pg_sz, buf_sz, cqe_sz) \
 (((hop_num) << QM_CQ_HOP_NUM_SHIFT) | \
 ((pg_sz) << QM_CQ_PAGE_SIZE_SHIFT) | \
 ((buf_sz) << QM_CQ_BUF_SIZE_SHIFT) | \
 ((cqe_sz) << QM_CQ_CQE_SIZE_SHIFT))

#define QM_MK_CQC_DW3_V2(cqe_sz, cq_depth) \
 ((((u32)cq_depth) - 1) | ((cqe_sz) << QM_CQ_CQE_SIZE_SHIFT))

#define QM_MK_SQC_W13(priority, orders, alg_type) \
 (((priority) << QM_SQ_PRIORITY_SHIFT) | \
 ((orders) << QM_SQ_ORDERS_SHIFT) | \
 (((alg_type) & QM_SQ_TYPE_MASK) << QM_SQ_TYPE_SHIFT))

#define QM_MK_SQC_DW3_V1(hop_num, pg_sz, buf_sz, sqe_sz) \
 (((hop_num) << QM_SQ_HOP_NUM_SHIFT) | \
 ((pg_sz) << QM_SQ_PAGE_SIZE_SHIFT) | \
 ((buf_sz) << QM_SQ_BUF_SIZE_SHIFT) | \
 ((u32)ilog2(sqe_sz) << QM_SQ_SQE_SIZE_SHIFT))

#define QM_MK_SQC_DW3_V2(sqe_sz, sq_depth) \
 ((((u32)sq_depth) - 1) | ((u32)ilog2(sqe_sz) << QM_SQ_SQE_SIZE_SHIFT))

enum vft_type {
 SQC_VFT = 0,
 CQC_VFT,
 SHAPER_VFT,
};

enum qm_alg_type {
 ALG_TYPE_0,
 ALG_TYPE_1,
};

enum qm_ifc_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,
 QM_PF_SET_QOS,
 QM_VF_GET_QOS,
};

enum qm_basic_type {
 QM_TOTAL_QP_NUM_CAP = 0x0,
 QM_FUNC_MAX_QP_CAP,
 QM_XEQ_DEPTH_CAP,
 QM_QP_DEPTH_CAP,
 QM_EQ_IRQ_TYPE_CAP,
 QM_AEQ_IRQ_TYPE_CAP,
 QM_ABN_IRQ_TYPE_CAP,
 QM_PF2VF_IRQ_TYPE_CAP,
 QM_PF_IRQ_NUM_CAP,
 QM_VF_IRQ_NUM_CAP,
};

enum qm_cap_table_type {
 QM_CAP_VF  = 0x0,
 QM_AEQE_NUM,
 QM_SCQE_NUM,
 QM_EQ_IRQ,
 QM_AEQ_IRQ,
 QM_ABNORMAL_IRQ,
 QM_MB_IRQ,
 MAX_IRQ_NUM,
 EXT_BAR_INDEX,
};

static const struct hisi_qm_cap_query_info qm_cap_query_info[] = {
 {QM_CAP_VF, "QM_CAP_VF ", 0x3100, 0x0, 0x0, 0x6F01},
 {QM_AEQE_NUM, "QM_AEQE_NUM ", 0x3104, 0x800, 0x4000800, 0x4000800},
 {QM_SCQE_NUM, "QM_SCQE_NUM ",
      0x3108, 0x4000400, 0x4000400, 0x4000400},
 {QM_EQ_IRQ, "QM_EQ_IRQ ", 0x310c, 0x10000, 0x10000, 0x10000},
 {QM_AEQ_IRQ, "QM_AEQ_IRQ ", 0x3110, 0x0, 0x10001, 0x10001},
 {QM_ABNORMAL_IRQ, "QM_ABNORMAL_IRQ ", 0x3114, 0x0, 0x10003, 0x10003},
 {QM_MB_IRQ, "QM_MB_IRQ ", 0x3118, 0x0, 0x0, 0x10002},
 {MAX_IRQ_NUM, "MAX_IRQ_NUM ", 0x311c, 0x10001, 0x40002, 0x40003},
 {EXT_BAR_INDEX, "EXT_BAR_INDEX ", 0x3120, 0x0, 0x0, 0x14},
};

static const struct hisi_qm_cap_info qm_cap_info_comm[] = {
 {QM_SUPPORT_DB_ISOLATION, 0x30,   0, BIT(0),  0x0, 0x0, 0x0},
 {QM_SUPPORT_FUNC_QOS,     0x3100, 0, BIT(8),  0x0, 0x0, 0x1},
 {QM_SUPPORT_STOP_QP,      0x3100, 0, BIT(9),  0x0, 0x0, 0x1},
 {QM_SUPPORT_STOP_FUNC,     0x3100, 0, BIT(10), 0x0, 0x0, 0x1},
 {QM_SUPPORT_MB_COMMAND,   0x3100, 0, BIT(11), 0x0, 0x0, 0x1},
 {QM_SUPPORT_SVA_PREFETCH, 0x3100, 0, BIT(14), 0x0, 0x0, 0x1},
 {QM_SUPPORT_DAE,          0x3100, 0, BIT(15), 0x0, 0x0, 0x0},
};

static const struct hisi_qm_cap_info qm_cap_info_pf[] = {
 {QM_SUPPORT_RPM, 0x3100, 0, BIT(13), 0x0, 0x0, 0x1},
};

static const struct hisi_qm_cap_info qm_cap_info_vf[] = {
 {QM_SUPPORT_RPM, 0x3100, 0, BIT(12), 0x0, 0x0, 0x0},
};

static const struct hisi_qm_cap_info qm_basic_info[] = {
 {QM_TOTAL_QP_NUM_CAP,   0x100158, 0,  GENMASK(10, 0), 0x1000,    0x400,     0x400},
 {QM_FUNC_MAX_QP_CAP,    0x100158, 11, GENMASK(10, 0), 0x1000,    0x400,     0x400},
 {QM_XEQ_DEPTH_CAP,      0x3104,   0,  GENMASK(31, 0), 0x800,     0x4000800, 0x4000800},
 {QM_QP_DEPTH_CAP,       0x3108,   0,  GENMASK(31, 0), 0x4000400, 0x4000400, 0x4000400},
 {QM_EQ_IRQ_TYPE_CAP,    0x310c,   0,  GENMASK(31, 0), 0x10000,   0x10000,   0x10000},
 {QM_AEQ_IRQ_TYPE_CAP,   0x3110,   0,  GENMASK(31, 0), 0x0,       0x10001,   0x10001},
 {QM_ABN_IRQ_TYPE_CAP,   0x3114,   0,  GENMASK(31, 0), 0x0,       0x10003,   0x10003},
 {QM_PF2VF_IRQ_TYPE_CAP, 0x3118,   0,  GENMASK(31, 0), 0x0,       0x0,       0x10002},
 {QM_PF_IRQ_NUM_CAP,     0x311c,   16, GENMASK(15, 0), 0x1,       0x4,       0x4},
 {QM_VF_IRQ_NUM_CAP,     0x311c,   0,  GENMASK(15, 0), 0x1,       0x2,       0x3},
};

struct qm_mailbox {
 __le16 w0;
 __le16 queue_num;
 __le32 base_l;
 __le32 base_h;
 __le32 rsvd;
};

struct qm_doorbell {
 __le16 queue_num;
 __le16 cmd;
 __le16 index;
 __le16 priority;
};

struct hisi_qm_resource {
 struct hisi_qm *qm;
 int distance;
 struct list_head list;
};

/**
 * struct qm_hw_err - Structure describing the device errors
 * @list: hardware error list
 * @timestamp: timestamp when the error occurred
 */

struct qm_hw_err {
 struct list_head list;
 unsigned long long timestamp;
};

struct hisi_qm_hw_ops {
 int (*get_vft)(struct hisi_qm *qm, u32 *base, u32 *number);
 void (*qm_db)(struct hisi_qm *qm, u16 qn,
        u8 cmd, u16 index, u8 priority);
 int (*debug_init)(struct hisi_qm *qm);
 void (*hw_error_init)(struct hisi_qm *qm);
 void (*hw_error_uninit)(struct hisi_qm *qm);
 enum acc_err_result (*hw_error_handle)(struct hisi_qm *qm);
 int (*set_msi)(struct hisi_qm *qm, bool set);

 /* (u64)msg = (u32)data << 32 | (enum qm_ifc_cmd)cmd */
 int (*set_ifc_begin)(struct hisi_qm *qm, enum qm_ifc_cmd cmd, u32 data, u32 fun_num);
 void (*set_ifc_end)(struct hisi_qm *qm);
 int (*get_ifc)(struct hisi_qm *qm, enum qm_ifc_cmd *cmd, u32 *data, u32 fun_num);
};

struct hisi_qm_hw_error {
 u32 int_msk;
 const char *msg;
};

static const struct hisi_qm_hw_error qm_hw_error[] = {
 { .int_msk = BIT(0), .msg = "qm_axi_rresp" },
 { .int_msk = BIT(1), .msg = "qm_axi_bresp" },
 { .int_msk = BIT(2), .msg = "qm_ecc_mbit" },
 { .int_msk = BIT(3), .msg = "qm_ecc_1bit" },
 { .int_msk = BIT(4), .msg = "qm_acc_get_task_timeout" },
 { .int_msk = BIT(5), .msg = "qm_acc_do_task_timeout" },
 { .int_msk = BIT(6), .msg = "qm_acc_wb_not_ready_timeout" },
 { .int_msk = BIT(7), .msg = "qm_sq_cq_vf_invalid" },
 { .int_msk = BIT(8), .msg = "qm_cq_vf_invalid" },
 { .int_msk = BIT(9), .msg = "qm_sq_vf_invalid" },
 { .int_msk = BIT(10), .msg = "qm_db_timeout" },
 { .int_msk = BIT(11), .msg = "qm_of_fifo_of" },
 { .int_msk = BIT(12), .msg = "qm_db_random_invalid" },
 { .int_msk = BIT(13), .msg = "qm_mailbox_timeout" },
 { .int_msk = BIT(14), .msg = "qm_flr_timeout" },
};

static const char * const qm_db_timeout[] = {
 "sq""cq""eq""aeq",
};

static const char * const qm_fifo_overflow[] = {
 "cq""eq""aeq",
};

struct qm_typical_qos_table {
 u32 start;
 u32 end;
 u32 val;
};

/* the qos step is 100 */
static struct qm_typical_qos_table shaper_cir_s[] = {
 {100, 100, 4},
 {200, 200, 3},
 {300, 500, 2},
 {600, 1000, 1},
 {1100, 100000, 0},
};

static struct qm_typical_qos_table shaper_cbs_s[] = {
 {100, 200, 9},
 {300, 500, 11},
 {600, 1000, 12},
 {1100, 10000, 16},
 {10100, 25000, 17},
 {25100, 50000, 18},
 {50100, 100000, 19}
};

static void qm_irqs_unregister(struct hisi_qm *qm);
static int qm_reset_device(struct hisi_qm *qm);
int hisi_qm_q_num_set(const char *val, const struct kernel_param *kp,
        unsigned int device)
{
 struct pci_dev *pdev;
 u32 n, q_num;
 int ret;

 if (!val)
  return -EINVAL;

 pdev = pci_get_device(PCI_VENDOR_ID_HUAWEI, device, NULL);
 if (!pdev) {
  q_num = min_t(u32, QM_QNUM_V1, QM_QNUM_V2);
  pr_info("No device found currently, suppose queue number is %u\n",
   q_num);
 } else {
  if (pdev->revision == QM_HW_V1)
   q_num = QM_QNUM_V1;
  else
   q_num = QM_QNUM_V2;

  pci_dev_put(pdev);
 }

 ret = kstrtou32(val, 10, &n);
 if (ret || n < QM_MIN_QNUM || n > q_num)
  return -EINVAL;

 return param_set_int(val, kp);
}
EXPORT_SYMBOL_GPL(hisi_qm_q_num_set);

static u32 qm_get_hw_error_status(struct hisi_qm *qm)
{
 return readl(qm->io_base + QM_ABNORMAL_INT_STATUS);
}

static u32 qm_get_dev_err_status(struct hisi_qm *qm)
{
 return qm->err_ini->get_dev_hw_err_status(qm);
}

/* Check if the error causes the master ooo block */
static bool 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)
  return false;

 err_status = qm_get_hw_error_status(pf_qm);
 if (err_status & pf_qm->err_info.qm_shutdown_mask)
  return true;

 if (pf_qm->err_ini->dev_is_abnormal)
  return pf_qm->err_ini->dev_is_abnormal(pf_qm);

 return false;
}

static int 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;
 }

 return 0;
}

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));

 /*
 * 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)
{
 struct pci_dev *pdev = qm->pdev;
 struct hisi_qm *pf_qm = pci_get_drvdata(pci_physfn(pdev));

 if (qm->ver < QM_HW_V3)
  clear_bit(QM_RESETTING, &pf_qm->misc_ctl);

 clear_bit(QM_RESETTING, &qm->misc_ctl);
}

static void qm_mb_pre_init(struct qm_mailbox *mailbox, u8 cmd,
      u64 base, u16 queue, bool op)
{
 mailbox->w0 = cpu_to_le16((cmd) |
  ((op) ? 0x1 << QM_MB_OP_SHIFT : 0) |
  (0x1 << QM_MB_BUSY_SHIFT));
 mailbox->queue_num = cpu_to_le16(queue);
 mailbox->base_l = cpu_to_le32(lower_32_bits(base));
 mailbox->base_h = cpu_to_le32(upper_32_bits(base));
 mailbox->rsvd = 0;
}

/* return 0 mailbox ready, -ETIMEDOUT hardware timeout */
int hisi_qm_wait_mb_ready(struct hisi_qm *qm)
{
 u32 val;

 return readl_relaxed_poll_timeout(qm->io_base + QM_MB_CMD_SEND_BASE,
       val, !((val >> QM_MB_BUSY_SHIFT) &
       0x1), POLL_PERIOD, POLL_TIMEOUT);
}
EXPORT_SYMBOL_GPL(hisi_qm_wait_mb_ready);

/* 128 bit should be written to hardware at one time to trigger a mailbox */
static void qm_mb_write(struct hisi_qm *qm, const void *src)
{
 void __iomem *fun_base = qm->io_base + QM_MB_CMD_SEND_BASE;

#if IS_ENABLED(CONFIG_ARM64)
 unsigned long tmp0 = 0, tmp1 = 0;
#endif

 if (!IS_ENABLED(CONFIG_ARM64)) {
  memcpy_toio(fun_base, src, 16);
  dma_wmb();
  return;
 }

#if IS_ENABLED(CONFIG_ARM64)
 asm volatile("ldp %0, %1, %3\n"
       "stp %0, %1, %2\n"
       "dmb oshst\n"
       : "=&r" (tmp0),
         "=&r" (tmp1),
         "+Q" (*((char __iomem *)fun_base))
       : "Q" (*((char *)src))
       : "memory");
#endif
}

static int qm_mb_nolock(struct hisi_qm *qm, struct qm_mailbox *mailbox)
{
 int ret;
 u32 val;

 if (unlikely(hisi_qm_wait_mb_ready(qm))) {
  dev_err(&qm->pdev->dev, "QM mailbox is busy to start!\n");
  ret = -EBUSY;
  goto mb_busy;
 }

 qm_mb_write(qm, mailbox);

 if (unlikely(hisi_qm_wait_mb_ready(qm))) {
  dev_err(&qm->pdev->dev, "QM mailbox operation timeout!\n");
  ret = -ETIMEDOUT;
  goto mb_busy;
 }

 val = readl(qm->io_base + QM_MB_CMD_SEND_BASE);
 if (val & QM_MB_STATUS_MASK) {
  dev_err(&qm->pdev->dev, "QM mailbox operation failed!\n");
  ret = -EIO;
  goto mb_busy;
 }

 return 0;

mb_busy:
 atomic64_inc(&qm->debug.dfx.mb_err_cnt);
 return ret;
}

int hisi_qm_mb(struct hisi_qm *qm, u8 cmd, dma_addr_t dma_addr, u16 queue,
        bool op)
{
 struct qm_mailbox mailbox;
 int ret;

 qm_mb_pre_init(&mailbox, cmd, dma_addr, queue, op);

 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;

 switch (cmd) {
 case QM_MB_CMD_SQC:
  size = sizeof(struct qm_sqc);
  tmp_xqc = qm->xqc_buf.sqc;
  xqc_dma = qm->xqc_buf.sqc_dma;
  break;
 case QM_MB_CMD_CQC:
  size = sizeof(struct qm_cqc);
  tmp_xqc = qm->xqc_buf.cqc;
  xqc_dma = qm->xqc_buf.cqc_dma;
  break;
 case QM_MB_CMD_EQC:
  size = sizeof(struct qm_eqc);
  tmp_xqc = qm->xqc_buf.eqc;
  xqc_dma = qm->xqc_buf.eqc_dma;
  break;
 case QM_MB_CMD_AEQC:
  size = sizeof(struct qm_aeqc);
  tmp_xqc = qm->xqc_buf.aeqc;
  xqc_dma = qm->xqc_buf.aeqc_dma;
  break;
 default:
  dev_err(&qm->pdev->dev, "unknown mailbox cmd %u\n", cmd);
  return -EINVAL;
 }

 /* 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);

 mutex_unlock(&qm->mailbox_lock);

 return ret;
}

static void qm_db_v1(struct hisi_qm *qm, u16 qn, u8 cmd, u16 index, u8 priority)
{
 u64 doorbell;

 doorbell = qn | ((u64)cmd << QM_DB_CMD_SHIFT_V1) |
     ((u64)index << QM_DB_INDEX_SHIFT_V1)  |
     ((u64)priority << QM_DB_PRIORITY_SHIFT_V1);

 writeq(doorbell, qm->io_base + QM_DOORBELL_BASE_V1);
}

static void qm_db_v2(struct hisi_qm *qm, u16 qn, u8 cmd, u16 index, u8 priority)
{
 void __iomem *io_base = qm->io_base;
 u16 randata = 0;
 u64 doorbell;

 if (cmd == QM_DOORBELL_CMD_SQ || cmd == QM_DOORBELL_CMD_CQ)
  io_base = qm->db_io_base + (u64)qn * qm->db_interval +
     QM_DOORBELL_SQ_CQ_BASE_V2;
 else
  io_base += QM_DOORBELL_EQ_AEQ_BASE_V2;

 doorbell = qn | ((u64)cmd << QM_DB_CMD_SHIFT_V2) |
     ((u64)randata << QM_DB_RAND_SHIFT_V2) |
     ((u64)index << QM_DB_INDEX_SHIFT_V2) |
     ((u64)priority << QM_DB_PRIORITY_SHIFT_V2);

 writeq(doorbell, io_base);
}

static void qm_db(struct hisi_qm *qm, u16 qn, u8 cmd, u16 index, u8 priority)
{
 dev_dbg(&qm->pdev->dev, "QM doorbell request: qn=%u, cmd=%u, index=%u\n",
  qn, cmd, index);

 qm->ops->qm_db(qm, qn, cmd, index, priority);
}

static void qm_disable_clock_gate(struct hisi_qm *qm)
{
 u32 val;

 /* if qm enables clock gating in Kunpeng930, qos will be inaccurate. */
 if (qm->ver < QM_HW_V3)
  return;

 val = readl(qm->io_base + QM_PM_CTRL);
 val |= QM_IDLE_DISABLE;
 writel(val, qm->io_base +  QM_PM_CTRL);
}

static int qm_dev_mem_reset(struct hisi_qm *qm)
{
 u32 val;

 writel(0x1, qm->io_base + QM_MEM_START_INIT);
 return readl_relaxed_poll_timeout(qm->io_base + QM_MEM_INIT_DONE, val,
       val & BIT(0), POLL_PERIOD,
       POLL_TIMEOUT);
}

/**
 * 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,
   const struct 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;

  val = readl(qm->io_base + info_table[index].offset);
  return (val >> info_table[index].shift) & info_table[index].mask;
 }
}
EXPORT_SYMBOL_GPL(hisi_qm_get_hw_info);

u32 hisi_qm_get_cap_value(struct hisi_qm *qm,
   const struct hisi_qm_cap_query_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;

  val = readl(qm->io_base + info_table[index].offset);
  return val;
 }
}
EXPORT_SYMBOL_GPL(hisi_qm_get_cap_value);

static void qm_get_xqc_depth(struct hisi_qm *qm, u16 *low_bits,
        u16 *high_bits, enum qm_basic_type type)
{
 u32 depth;

 depth = hisi_qm_get_hw_info(qm, qm_basic_info, type, qm->cap_ver);
 *low_bits = depth & QM_XQ_DEPTH_MASK;
 *high_bits = (depth >> QM_XQ_DEPTH_SHIFT) & QM_XQ_DEPTH_MASK;
}

int hisi_qm_set_algs(struct hisi_qm *qm, u64 alg_msk, const struct qm_dev_alg *dev_algs,
       u32 dev_algs_size)
{
 struct device *dev = &qm->pdev->dev;
 char *algs, *ptr;
 int i;

 if (!qm->uacce)
  return 0;

 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);

 ptr = strrchr(algs, '\n');
 if (ptr)
  *ptr = '\0';

 qm->uacce->algs = algs;

 return 0;
}
EXPORT_SYMBOL_GPL(hisi_qm_set_algs);

static u32 qm_get_irq_num(struct hisi_qm *qm)
{
 if (qm->fun_type == QM_HW_PF)
  return hisi_qm_get_hw_info(qm, qm_basic_info, QM_PF_IRQ_NUM_CAP, qm->cap_ver);

 return hisi_qm_get_hw_info(qm, qm_basic_info, QM_VF_IRQ_NUM_CAP, qm->cap_ver);
}

static int qm_pm_get_sync(struct hisi_qm *qm)
{
 struct device *dev = &qm->pdev->dev;
 int ret;

 if (!test_bit(QM_SUPPORT_RPM, &qm->caps))
  return 0;

 ret = pm_runtime_resume_and_get(dev);
 if (ret < 0) {
  dev_err(dev, "failed to get_sync(%d).\n", ret);
  return ret;
 }

 return 0;
}

static void qm_pm_put_sync(struct hisi_qm *qm)
{
 struct device *dev = &qm->pdev->dev;

 if (!test_bit(QM_SUPPORT_RPM, &qm->caps))
  return;

 pm_runtime_put_autosuspend(dev);
}

static void qm_cq_head_update(struct hisi_qp *qp)
{
 if (qp->qp_status.cq_head == qp->cq_depth - 1) {
  qp->qp_status.cqc_phase = !qp->qp_status.cqc_phase;
  qp->qp_status.cq_head = 0;
 } else {
  qp->qp_status.cq_head++;
 }
}

static void qm_poll_req_cb(struct hisi_qp *qp)
{
 struct qm_cqe *cqe = qp->cqe + qp->qp_status.cq_head;
 struct hisi_qm *qm = qp->qm;

 while (QM_CQE_PHASE(cqe) == qp->qp_status.cqc_phase) {
  dma_rmb();
  qp->req_cb(qp, qp->sqe + qm->sqe_size *
      le16_to_cpu(cqe->sq_head));
  qm_cq_head_update(qp);
  cqe = qp->cqe + qp->qp_status.cq_head;
  qm_db(qm, qp->qp_id, QM_DOORBELL_CMD_CQ,
        qp->qp_status.cq_head, 0);
  atomic_dec(&qp->qp_status.used);

  cond_resched();
 }

 /* set c_flag */
 qm_db(qm, qp->qp_id, QM_DOORBELL_CMD_CQ, qp->qp_status.cq_head, 1);
}

static void qm_work_process(struct work_struct *work)
{
 struct hisi_qm_poll_data *poll_data =
  container_of(work, struct hisi_qm_poll_data, work);
 struct hisi_qm *qm = poll_data->qm;
 u16 eqe_num = poll_data->eqe_num;
 struct hisi_qp *qp;
 int i;

 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 (qp->event_cb) {
   qp->event_cb(qp);
   continue;
  }

  if (likely(qp->req_cb))
   qm_poll_req_cb(qp);
 }
}

static void qm_get_complete_eqe_num(struct hisi_qm *qm)
{
 struct qm_eqe *eqe = qm->eqe + qm->status.eq_head;
 struct hisi_qm_poll_data *poll_data = NULL;
 u16 eq_depth = qm->eq_depth;
 u16 cqn, eqe_num = 0;

 if (QM_EQE_PHASE(eqe) != qm->status.eqc_phase) {
  atomic64_inc(&qm->debug.dfx.err_irq_cnt);
  qm_db(qm, 0, QM_DOORBELL_CMD_EQ, qm->status.eq_head, 0);
  return;
 }

 cqn = le32_to_cpu(eqe->dw0) & QM_EQE_CQN_MASK;
 if (unlikely(cqn >= qm->qp_num))
  return;
 poll_data = &qm->poll_data[cqn];

 while (QM_EQE_PHASE(eqe) == qm->status.eqc_phase) {
  cqn = le32_to_cpu(eqe->dw0) & QM_EQE_CQN_MASK;
  poll_data->qp_finish_id[eqe_num] = cqn;
  eqe_num++;

  if (qm->status.eq_head == eq_depth - 1) {
   qm->status.eqc_phase = !qm->status.eqc_phase;
   eqe = qm->eqe;
   qm->status.eq_head = 0;
  } else {
   eqe++;
   qm->status.eq_head++;
  }

  if (eqe_num == (eq_depth >> 1) - 1)
   break;
 }

 poll_data->eqe_num = eqe_num;
 queue_work(qm->wq, &poll_data->work);
 qm_db(qm, 0, QM_DOORBELL_CMD_EQ, qm->status.eq_head, 0);
}

static irqreturn_t qm_eq_irq(int irq, void *data)
{
 struct hisi_qm *qm = data;

 /* Get qp id of completed tasks and re-enable the interrupt */
 qm_get_complete_eqe_num(qm);

 return IRQ_HANDLED;
}

static irqreturn_t qm_mb_cmd_irq(int irq, void *data)
{
 struct hisi_qm *qm = data;
 u32 val;

 val = readl(qm->io_base + QM_IFC_INT_STATUS);
 val &= QM_IFC_INT_STATUS_MASK;
 if (!val)
  return IRQ_NONE;

 if (test_bit(QM_DRIVER_REMOVING, &qm->misc_ctl)) {
  dev_warn(&qm->pdev->dev, "Driver is down, message cannot be processed!\n");
  return IRQ_HANDLED;
 }

 schedule_work(&qm->cmd_process);

 return IRQ_HANDLED;
}

static void qm_set_qp_disable(struct hisi_qp *qp, int offset)
{
 u32 *addr;

 if (qp->is_in_kernel)
  return;

 addr = (u32 *)(qp->qdma.va + qp->qdma.size) - offset;
 *addr = 1;

 /* make sure setup is completed */
 smp_wmb();
}

static void qm_disable_qp(struct hisi_qm *qm, u32 qp_id)
{
 struct hisi_qp *qp = &qm->qp_array[qp_id];

 qm_set_qp_disable(qp, QM_RESET_STOP_TX_OFFSET);
 hisi_qm_stop_qp(qp);
 qm_set_qp_disable(qp, QM_RESET_STOP_RX_OFFSET);
}

static void qm_reset_function(struct hisi_qm *qm)
{
 struct device *dev = &qm->pdev->dev;
 int ret;

 if (qm_check_dev_error(qm))
  return;

 ret = qm_reset_prepare_ready(qm);
 if (ret) {
  dev_err(dev, "reset function not ready\n");
  return;
 }

 ret = hisi_qm_stop(qm, QM_DOWN);
 if (ret) {
  dev_err(dev, "failed to stop qm when reset function\n");
  goto clear_bit;
 }

 ret = hisi_qm_start(qm);
 if (ret)
  dev_err(dev, "failed to start qm when reset function\n");

clear_bit:
 qm_reset_bit_clear(qm);
}

static irqreturn_t qm_aeq_thread(int irq, void *data)
{
 struct hisi_qm *qm = data;
 struct qm_aeqe *aeqe = qm->aeqe + qm->status.aeq_head;
 u16 aeq_depth = qm->aeq_depth;
 u32 type, qp_id;

 atomic64_inc(&qm->debug.dfx.aeq_irq_cnt);

 while (QM_AEQE_PHASE(aeqe) == qm->status.aeqc_phase) {
  type = (le32_to_cpu(aeqe->dw0) >> QM_AEQE_TYPE_SHIFT) &
   QM_AEQE_TYPE_MASK;
  qp_id = le32_to_cpu(aeqe->dw0) & QM_AEQE_CQN_MASK;

  switch (type) {
  case QM_EQ_OVERFLOW:
   dev_err(&qm->pdev->dev, "eq overflow, reset function\n");
   qm_reset_function(qm);
   return IRQ_HANDLED;
  case QM_CQ_OVERFLOW:
   dev_err(&qm->pdev->dev, "cq overflow, stop qp(%u)\n",
    qp_id);
   fallthrough;
  case QM_CQE_ERROR:
   qm_disable_qp(qm, qp_id);
   break;
  default:
   dev_err(&qm->pdev->dev, "unknown error type %u\n",
    type);
   break;
  }

  if (qm->status.aeq_head == aeq_depth - 1) {
   qm->status.aeqc_phase = !qm->status.aeqc_phase;
   aeqe = qm->aeqe;
   qm->status.aeq_head = 0;
  } else {
   aeqe++;
   qm->status.aeq_head++;
  }
 }

 qm_db(qm, 0, QM_DOORBELL_CMD_AEQ, qm->status.aeq_head, 0);

 return IRQ_HANDLED;
}

static void qm_init_qp_status(struct hisi_qp *qp)
{
 struct hisi_qp_status *qp_status = &qp->qp_status;

 qp_status->sq_tail = 0;
 qp_status->cq_head = 0;
 qp_status->cqc_phase = true;
 atomic_set(&qp_status->used, 0);
}

static void qm_init_prefetch(struct hisi_qm *qm)
{
 struct device *dev = &qm->pdev->dev;
 u32 page_type = 0x0;

 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;
 }

 return 0;
}

static int qm_get_shaper_para(u32 ir, struct qm_shaper_factor *factor)
{
 u32 cir_b, cir_u, cir_s, ir_calc;
 u32 error_rate;

 factor->cbs_s = acc_shaper_calc_cbs_s(ir);
 cir_s = acc_shaper_calc_cir_s(ir);

 for (cir_b = QM_QOS_MIN_CIR_B; cir_b <= QM_QOS_MAX_CIR_B; cir_b++) {
  for (cir_u = 0; cir_u <= QM_QOS_MAX_CIR_U; cir_u++) {
   ir_calc = acc_shaper_para_calc(cir_b, cir_u, cir_s);

   error_rate = QM_QOS_EXPAND_RATE * (u32)abs(ir_calc - ir) / ir;
   if (error_rate <= QM_QOS_MIN_ERROR_RATE) {
    factor->cir_b = cir_b;
    factor->cir_u = cir_u;
    factor->cir_s = cir_s;
    return 0;
   }
  }
 }

 return -EINVAL;
}

static void qm_vft_data_cfg(struct hisi_qm *qm, enum vft_type type, u32 base,
       u32 number, struct qm_shaper_factor *factor)
{
 u64 tmp = 0;

 if (number > 0) {
  switch (type) {
  case SQC_VFT:
   if (qm->ver == QM_HW_V1) {
    tmp = QM_SQC_VFT_BUF_SIZE |
          QM_SQC_VFT_SQC_SIZE |
          QM_SQC_VFT_INDEX_NUMBER |
          QM_SQC_VFT_VALID  |
          (u64)base << QM_SQC_VFT_START_SQN_SHIFT;
   } else {
    tmp = (u64)base << QM_SQC_VFT_START_SQN_SHIFT |
          QM_SQC_VFT_VALID |
          (u64)(number - 1) << QM_SQC_VFT_SQN_SHIFT;
   }
   break;
  case CQC_VFT:
   if (qm->ver == QM_HW_V1) {
    tmp = QM_CQC_VFT_BUF_SIZE |
          QM_CQC_VFT_SQC_SIZE |
          QM_CQC_VFT_INDEX_NUMBER |
          QM_CQC_VFT_VALID;
   } else {
    tmp = QM_CQC_VFT_VALID;
   }
   break;
  case SHAPER_VFT:
   if (factor) {
    tmp = factor->cir_b |
    (factor->cir_u << QM_SHAPER_FACTOR_CIR_U_SHIFT) |
    (factor->cir_s << QM_SHAPER_FACTOR_CIR_S_SHIFT) |
    (QM_SHAPER_CBS_B << QM_SHAPER_FACTOR_CBS_B_SHIFT) |
    (factor->cbs_s << QM_SHAPER_FACTOR_CBS_S_SHIFT);
   }
   break;
  }
 }

 writel(lower_32_bits(tmp), qm->io_base + QM_VFT_CFG_DATA_L);
 writel(upper_32_bits(tmp), qm->io_base + QM_VFT_CFG_DATA_H);
}

static int qm_set_vft_common(struct hisi_qm *qm, enum vft_type type,
        u32 fun_num, u32 base, u32 number)
{
 struct qm_shaper_factor *factor = NULL;
 unsigned int val;
 int ret;

 if (type == SHAPER_VFT && test_bit(QM_SUPPORT_FUNC_QOS, &qm->caps))
  factor = &qm->factor[fun_num];

 ret = readl_relaxed_poll_timeout(qm->io_base + QM_VFT_CFG_RDY, val,
      val & BIT(0), POLL_PERIOD,
      POLL_TIMEOUT);
 if (ret)
  return ret;

 writel(0x0, qm->io_base + QM_VFT_CFG_OP_WR);
 writel(type, qm->io_base + QM_VFT_CFG_TYPE);
 if (type == SHAPER_VFT)
  fun_num |= base << QM_SHAPER_VFT_OFFSET;

 writel(fun_num, qm->io_base + QM_VFT_CFG);

 qm_vft_data_cfg(qm, type, base, number, factor);

 writel(0x0, qm->io_base + QM_VFT_CFG_RDY);
 writel(0x1, qm->io_base + QM_VFT_CFG_OP_ENABLE);

 return readl_relaxed_poll_timeout(qm->io_base + QM_VFT_CFG_RDY, val,
       val & BIT(0), POLL_PERIOD,
       POLL_TIMEOUT);
}

static int qm_shaper_init_vft(struct hisi_qm *qm, u32 fun_num)
{
 u32 qos = qm->factor[fun_num].func_qos;
 int ret, i;

 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() */
static int 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);

 return ret;
}

static int qm_get_vft_v2(struct hisi_qm *qm, u32 *base, u32 *number)
{
 u64 sqc_vft;
 int ret;

 ret = hisi_qm_mb(qm, QM_MB_CMD_SQC_VFT_V2, 0, 0, 1);
 if (ret)
  return ret;

 sqc_vft = readl(qm->io_base + QM_MB_CMD_DATA_ADDR_L) |
    ((u64)readl(qm->io_base + QM_MB_CMD_DATA_ADDR_H) << 32);
 *base = QM_SQC_VFT_BASE_MASK_V2 & (sqc_vft >> QM_SQC_VFT_BASE_SHIFT_V2);
 *number = (QM_SQC_VFT_NUM_MASK_V2 &
     (sqc_vft >> QM_SQC_VFT_NUM_SHIFT_V2)) + 1;

 return 0;
}

static void qm_hw_error_init_v1(struct hisi_qm *qm)
{
 writel(QM_ABNORMAL_INT_MASK_VALUE, qm->io_base + QM_ABNORMAL_INT_MASK);
}

static void qm_hw_error_cfg(struct hisi_qm *qm)
{
 struct hisi_qm_err_info *err_info = &qm->err_info;

 qm->error_mask = err_info->nfe | err_info->ce | err_info->fe;
 /* clear QM hw residual error source */
 writel(qm->error_mask, qm->io_base + QM_ABNORMAL_INT_SOURCE);

 /* configure error type */
 writel(err_info->ce, qm->io_base + QM_RAS_CE_ENABLE);
 writel(QM_RAS_CE_TIMES_PER_IRQ, qm->io_base + QM_RAS_CE_THRESHOLD);
 writel(err_info->nfe, qm->io_base + QM_RAS_NFE_ENABLE);
 writel(err_info->fe, qm->io_base + QM_RAS_FE_ENABLE);
}

static void qm_hw_error_init_v2(struct hisi_qm *qm)
{
 u32 irq_unmask;

 qm_hw_error_cfg(qm);

 irq_unmask = ~qm->error_mask;
 irq_unmask &= readl(qm->io_base + QM_ABNORMAL_INT_MASK);
 writel(irq_unmask, qm->io_base + QM_ABNORMAL_INT_MASK);
}

static void qm_hw_error_uninit_v2(struct hisi_qm *qm)
{
 u32 irq_mask = qm->error_mask;

 irq_mask |= readl(qm->io_base + QM_ABNORMAL_INT_MASK);
 writel(irq_mask, qm->io_base + QM_ABNORMAL_INT_MASK);
}

static void qm_hw_error_init_v3(struct hisi_qm *qm)
{
 u32 irq_unmask;

 qm_hw_error_cfg(qm);

 /* enable close master ooo when hardware error happened */
 writel(qm->err_info.qm_shutdown_mask, qm->io_base + QM_OOO_SHUTDOWN_SEL);

 irq_unmask = ~qm->error_mask;
 irq_unmask &= readl(qm->io_base + QM_ABNORMAL_INT_MASK);
 writel(irq_unmask, qm->io_base + QM_ABNORMAL_INT_MASK);
}

static void qm_hw_error_uninit_v3(struct hisi_qm *qm)
{
 u32 irq_mask = qm->error_mask;

 irq_mask |= readl(qm->io_base + QM_ABNORMAL_INT_MASK);
 writel(irq_mask, qm->io_base + QM_ABNORMAL_INT_MASK);

 /* disable close master ooo when hardware error happened */
 writel(0x0, qm->io_base + QM_OOO_SHUTDOWN_SEL);
}

static void qm_log_hw_error(struct hisi_qm *qm, u32 error_status)
{
 const struct hisi_qm_hw_error *err;
 struct device *dev = &qm->pdev->dev;
 u32 reg_val, type, vf_num, qp_id;
 int i;

 for (i = 0; i < ARRAY_SIZE(qm_hw_error); i++) {
  err = &qm_hw_error[i];
  if (!(err->int_msk & error_status))
   continue;

  dev_err(dev, "%s [error status=0x%x] found\n",
   err->msg, err->int_msk);

  if (err->int_msk & QM_DB_TIMEOUT) {
   reg_val = readl(qm->io_base + QM_ABNORMAL_INF01);
   type = (reg_val & QM_DB_TIMEOUT_TYPE) >>
          QM_DB_TIMEOUT_TYPE_SHIFT;
   vf_num = reg_val & QM_DB_TIMEOUT_VF;
   qp_id = reg_val >> QM_DB_TIMEOUT_QP_SHIFT;
   dev_err(dev, "qm %s doorbell timeout in function %u qp %u\n",
    qm_db_timeout[type], vf_num, qp_id);
  } else if (err->int_msk & QM_OF_FIFO_OF) {
   reg_val = readl(qm->io_base + QM_ABNORMAL_INF00);
   type = (reg_val & QM_FIFO_OVERFLOW_TYPE) >>
          QM_FIFO_OVERFLOW_TYPE_SHIFT;
   vf_num = reg_val & QM_FIFO_OVERFLOW_VF;
   qp_id = reg_val >> QM_FIFO_OVERFLOW_QP_SHIFT;
   if (type < ARRAY_SIZE(qm_fifo_overflow))
    dev_err(dev, "qm %s fifo overflow in function %u qp %u\n",
     qm_fifo_overflow[type], vf_num, qp_id);
   else
    dev_err(dev, "unknown error type\n");
  } else if (err->int_msk & QM_AXI_RRESP_ERR) {
   reg_val = readl(qm->io_base + QM_ABNORMAL_INF02);
   if (reg_val & QM_AXI_POISON_ERR)
    dev_err(dev, "qm axi poison error happened\n");
  }
 }
}

static enum acc_err_result qm_hw_error_handle_v2(struct hisi_qm *qm)
{
 u32 error_status;

 error_status = qm_get_hw_error_status(qm);
 if (error_status & qm->error_mask) {
  if (error_status & QM_ECC_MBIT)
   qm->err_status.is_qm_ecc_mbit = true;

  qm_log_hw_error(qm, error_status);
  if (error_status & qm->err_info.qm_reset_mask) {
   /* Disable the same error reporting until device is recovered. */
   writel(qm->err_info.nfe & (~error_status),
          qm->io_base + QM_RAS_NFE_ENABLE);
   return ACC_ERR_NEED_RESET;
  }

  /* Clear error source if not need reset. */
  writel(error_status, qm->io_base + QM_ABNORMAL_INT_SOURCE);
  writel(qm->err_info.nfe, qm->io_base + QM_RAS_NFE_ENABLE);
  writel(qm->err_info.ce, qm->io_base + QM_RAS_CE_ENABLE);
 }

 return ACC_ERR_RECOVERED;
}

static int qm_get_mb_cmd(struct hisi_qm *qm, u64 *msg, u16 fun_num)
{
 struct qm_mailbox mailbox;
 int ret;

 qm_mb_pre_init(&mailbox, QM_MB_CMD_DST, 0, fun_num, 0);
 mutex_lock(&qm->mailbox_lock);
 ret = qm_mb_nolock(qm, &mailbox);
 if (ret)
  goto err_unlock;

 *msg = readl(qm->io_base + QM_MB_CMD_DATA_ADDR_L) |
    ((u64)readl(qm->io_base + QM_MB_CMD_DATA_ADDR_H) << 32);

err_unlock:
 mutex_unlock(&qm->mailbox_lock);
 return ret;
}

static void qm_clear_cmd_interrupt(struct hisi_qm *qm, u64 vf_mask)
{
 u32 val;

 if (qm->fun_type == QM_HW_PF)
  writeq(vf_mask, qm->io_base + QM_IFC_INT_SOURCE_P);

 val = readl(qm->io_base + QM_IFC_INT_SOURCE_V);
 val |= QM_IFC_INT_SOURCE_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;
 enum qm_ifc_cmd cmd;
 int ret;

 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;
 }
}

static int 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);

 return ret;
}

static void qm_trigger_vf_interrupt(struct hisi_qm *qm, u32 fun_num)
{
 u32 val;

 val = readl(qm->io_base + QM_IFC_INT_CFG);
 val &= ~QM_IFC_SEND_ALL_VFS;
 val |= fun_num;
 writel(val, qm->io_base + QM_IFC_INT_CFG);

 val = readl(qm->io_base + QM_IFC_INT_SET_P);
 val |= QM_IFC_INT_SET_MASK;
 writel(val, qm->io_base + QM_IFC_INT_SET_P);
}

static void qm_trigger_pf_interrupt(struct hisi_qm *qm)
{
 u32 val;

 val = readl(qm->io_base + QM_IFC_INT_SET_V);
 val |= QM_IFC_INT_SET_MASK;
 writel(val, qm->io_base + QM_IFC_INT_SET_V);
}

static int qm_ping_single_vf(struct hisi_qm *qm, enum qm_ifc_cmd cmd, u32 data, u32 fun_num)
{
 struct device *dev = &qm->pdev->dev;
 int cnt = 0;
 u64 val;
 int ret;

 ret = qm->ops->set_ifc_begin(qm, cmd, data, fun_num);
 if (ret) {
  dev_err(dev, "failed to send command to vf(%u)!\n", fun_num);
  goto err_unlock;
 }

 qm_trigger_vf_interrupt(qm, fun_num);
 while (true) {
  msleep(QM_WAIT_DST_ACK);
  val = readq(qm->io_base + QM_IFC_READY_STATUS);
  /* if VF respond, PF notifies VF successfully. */
  if (!(val & BIT(fun_num)))
   goto err_unlock;

  if (++cnt > QM_MAX_PF_WAIT_COUNT) {
   dev_err(dev, "failed to get response from VF(%u)!\n", fun_num);
   ret = -ETIMEDOUT;
   break;
  }
 }

err_unlock:
 qm->ops->set_ifc_end(qm);
 return ret;
}

static int 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;
}

static int 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;
  }
 }

unlock:
 qm->ops->set_ifc_end(qm);

 return ret;
}

static int qm_drain_qm(struct hisi_qm *qm)
{
 return hisi_qm_mb(qm, QM_MB_CMD_FLUSH_QM, 0, 0, 0);
}

static int qm_stop_qp(struct hisi_qp *qp)
{
 return hisi_qm_mb(qp->qm, QM_MB_CMD_STOP_QP, 0, qp->qp_id, 0);
}

static int qm_set_msi(struct hisi_qm *qm, bool set)
{
 struct pci_dev *pdev = qm->pdev;

 if (set) {
  pci_write_config_dword(pdev, pdev->msi_cap + PCI_MSI_MASK_64,
           0);
 } else {
  pci_write_config_dword(pdev, pdev->msi_cap + PCI_MSI_MASK_64,
           ACC_PEH_MSI_DISABLE);
  if (qm->err_status.is_qm_ecc_mbit ||
      qm->err_status.is_dev_ecc_mbit)
   return 0;

  mdelay(1);
  if (readl(qm->io_base + QM_PEH_DFX_INFO0))
   return -EFAULT;
 }

 return 0;
}

static void qm_wait_msi_finish(struct hisi_qm *qm)
{
 struct pci_dev *pdev = qm->pdev;
 u32 cmd = ~0;
 int cnt = 0;
 u32 val;
 int ret;

 while (true) {
  pci_read_config_dword(pdev, pdev->msi_cap +
          PCI_MSI_PENDING_64, &cmd);
  if (!cmd)
   break;

  if (++cnt > MAX_WAIT_COUNTS) {
   pci_warn(pdev, "failed to empty MSI PENDING!\n");
   break;
  }

  udelay(1);
 }

 ret = readl_relaxed_poll_timeout(qm->io_base + QM_PEH_DFX_INFO0,
      val, !(val & QM_PEH_DFX_MASK),
      POLL_PERIOD, POLL_TIMEOUT);
 if (ret)
  pci_warn(pdev, "failed to empty PEH MSI!\n");

 ret = readl_relaxed_poll_timeout(qm->io_base + QM_PEH_DFX_INFO1,
      val, !(val & QM_PEH_MSI_FINISH_MASK),
      POLL_PERIOD, POLL_TIMEOUT);
 if (ret)
  pci_warn(pdev, "failed to finish MSI operation!\n");
}

static int qm_set_msi_v3(struct hisi_qm *qm, bool set)
{
 struct pci_dev *pdev = qm->pdev;
 int ret = -ETIMEDOUT;
 u32 cmd, i;

 pci_read_config_dword(pdev, pdev->msi_cap, &cmd);
 if (set)
  cmd |= QM_MSI_CAP_ENABLE;
 else
  cmd &= ~QM_MSI_CAP_ENABLE;

 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;

   udelay(1);
  }
 } else {
  udelay(WAIT_PERIOD_US_MIN);
  qm_wait_msi_finish(qm);
  ret = 0;
 }

 return ret;
}

static int qm_set_ifc_begin_v3(struct hisi_qm *qm, enum qm_ifc_cmd cmd, u32 data, u32 fun_num)
{
 struct qm_mailbox mailbox;
 u64 msg;

 msg = cmd | (u64)data << QM_IFC_DATA_SHIFT;

 qm_mb_pre_init(&mailbox, QM_MB_CMD_SRC, msg, fun_num, 0);
 mutex_lock(&qm->mailbox_lock);
 return qm_mb_nolock(qm, &mailbox);
}

static void qm_set_ifc_end_v3(struct hisi_qm *qm)
{
 mutex_unlock(&qm->mailbox_lock);
}

static int qm_get_ifc_v3(struct hisi_qm *qm, enum qm_ifc_cmd *cmd, u32 *data, u32 fun_num)
{
 u64 msg;
 int ret;

 ret = qm_get_mb_cmd(qm, &msg, fun_num);
 if (ret)
  return ret;

 *cmd = msg & QM_IFC_CMD_MASK;

 if (data)
  *data = msg >> QM_IFC_DATA_SHIFT;

 return 0;
}

static int qm_set_ifc_begin_v4(struct hisi_qm *qm, enum qm_ifc_cmd cmd, u32 data, u32 fun_num)
{
 uintptr_t offset;
 u64 msg;

 if (qm->fun_type == QM_HW_PF)
  offset = QM_PF2VF_PF_W;
 else
  offset = QM_VF2PF_VF_W;

 msg = cmd | (u64)data << QM_IFC_DATA_SHIFT;

 mutex_lock(&qm->ifc_lock);
 writeq(msg, qm->io_base + offset);

 return 0;
}

static void qm_set_ifc_end_v4(struct hisi_qm *qm)
{
 mutex_unlock(&qm->ifc_lock);
}

static u64 qm_get_ifc_pf(struct hisi_qm *qm, u32 fun_num)
{
 uintptr_t offset;

 offset = QM_VF2PF_PF_R + QM_VF2PF_REG_SIZE * fun_num;

 return (u64)readl(qm->io_base + offset);
}

static u64 qm_get_ifc_vf(struct hisi_qm *qm)
{
 return readq(qm->io_base + QM_PF2VF_VF_R);
}

static int qm_get_ifc_v4(struct hisi_qm *qm, enum qm_ifc_cmd *cmd, u32 *data, u32 fun_num)
{
 u64 msg;

 if (qm->fun_type == QM_HW_PF)
  msg = qm_get_ifc_pf(qm, fun_num);
 else
  msg = qm_get_ifc_vf(qm);

 *cmd = msg & QM_IFC_CMD_MASK;

 if (data)
  *data = msg >> QM_IFC_DATA_SHIFT;

 return 0;
}

static const struct hisi_qm_hw_ops qm_hw_ops_v1 = {
 .qm_db = qm_db_v1,
 .hw_error_init = qm_hw_error_init_v1,
 .set_msi = qm_set_msi,
};

static const struct hisi_qm_hw_ops qm_hw_ops_v2 = {
 .get_vft = qm_get_vft_v2,
 .qm_db = qm_db_v2,
 .hw_error_init = qm_hw_error_init_v2,
 .hw_error_uninit = qm_hw_error_uninit_v2,
 .hw_error_handle = qm_hw_error_handle_v2,
 .set_msi = qm_set_msi,
};

static const struct hisi_qm_hw_ops qm_hw_ops_v3 = {
 .get_vft = qm_get_vft_v2,
 .qm_db = qm_db_v2,
 .hw_error_init = qm_hw_error_init_v3,
 .hw_error_uninit = qm_hw_error_uninit_v3,
 .hw_error_handle = qm_hw_error_handle_v2,
 .set_msi = qm_set_msi_v3,
 .set_ifc_begin = qm_set_ifc_begin_v3,
 .set_ifc_end = qm_set_ifc_end_v3,
 .get_ifc = qm_get_ifc_v3,
};

static const struct hisi_qm_hw_ops qm_hw_ops_v4 = {
 .get_vft = qm_get_vft_v2,
 .qm_db = qm_db_v2,
 .hw_error_init = qm_hw_error_init_v3,
 .hw_error_uninit = qm_hw_error_uninit_v3,
 .hw_error_handle = qm_hw_error_handle_v2,
 .set_msi = qm_set_msi_v3,
 .set_ifc_begin = qm_set_ifc_begin_v4,
 .set_ifc_end = qm_set_ifc_end_v4,
 .get_ifc = qm_get_ifc_v4,
};

static void *qm_get_avail_sqe(struct hisi_qp *qp)
{
 struct hisi_qp_status *qp_status = &qp->qp_status;
 u16 sq_tail = qp_status->sq_tail;

 if (unlikely(atomic_read(&qp->qp_status.used) == qp->sq_depth - 1))
  return NULL;

 return qp->sqe + sq_tail * qp->qm->sqe_size;
}

static void hisi_qm_unset_hw_reset(struct hisi_qp *qp)
{
 u64 *addr;

 /* Use last 64 bits of DUS to reset status. */
 addr = (u64 *)(qp->qdma.va + qp->qdma.size) - QM_RESET_STOP_TX_OFFSET;
 *addr = 0;
}

static struct hisi_qp *qm_create_qp_nolock(struct hisi_qm *qm, u8 alg_type)
{
 struct device *dev = &qm->pdev->dev;
 struct hisi_qp *qp;
 int qp_id;

 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);
 }

 qp = &qm->qp_array[qp_id];
 hisi_qm_unset_hw_reset(qp);
 memset(qp->cqe, 0, sizeof(struct qm_cqe) * qp->cq_depth);

 qp->event_cb = NULL;
 qp->req_cb = NULL;
 qp->qp_id = qp_id;
 qp->alg_type = alg_type;
 qp->is_in_kernel = true;
 qm->qp_in_used++;

 return qp;
}

/**
 * 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.
 */

static struct 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);

 down_write(&qm->qps_lock);
 qp = qm_create_qp_nolock(qm, alg_type);
 up_write(&qm->qps_lock);

 if (IS_ERR(qp))
  qm_pm_put_sync(qm);

 return qp;
}

/**
 * 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.
 */

static void hisi_qm_release_qp(struct hisi_qp *qp)
{
 struct hisi_qm *qm = qp->qm;

 down_write(&qm->qps_lock);

 qm->qp_in_used--;
 idr_remove(&qm->qp_idr, qp->qp_id);

 up_write(&qm->qps_lock);

 qm_pm_put_sync(qm);
}

static int qm_sq_ctx_cfg(struct hisi_qp *qp, int qp_id, u32 pasid)
{
 struct hisi_qm *qm = qp->qm;
 enum qm_hw_ver ver = qm->ver;
 struct qm_sqc sqc = {0};

 if (ver == QM_HW_V1) {
  sqc.dw3 = cpu_to_le32(QM_MK_SQC_DW3_V1(0, 0, 0, qm->sqe_size));
  sqc.w8 = cpu_to_le16(qp->sq_depth - 1);
 } else {
  sqc.dw3 = cpu_to_le32(QM_MK_SQC_DW3_V2(qm->sqe_size, qp->sq_depth));
  sqc.w8 = 0; /* rand_qc */
 }
 sqc.w13 = cpu_to_le16(QM_MK_SQC_W13(0, 1, qp->alg_type));
 sqc.base_l = cpu_to_le32(lower_32_bits(qp->sqe_dma));
 sqc.base_h = cpu_to_le32(upper_32_bits(qp->sqe_dma));
 sqc.cq_num = cpu_to_le16(qp_id);
 sqc.pasid = cpu_to_le16(pasid);

 if (ver >= QM_HW_V3 && qm->use_sva && !qp->is_in_kernel)
  sqc.w11 = cpu_to_le16(QM_QC_PASID_ENABLE <<
          QM_QC_PASID_ENABLE_SHIFT);

 return qm_set_and_get_xqc(qm, QM_MB_CMD_SQC, &sqc, qp_id, 0);
}

static int qm_cq_ctx_cfg(struct hisi_qp *qp, int qp_id, u32 pasid)
{
 struct hisi_qm *qm = qp->qm;
 enum qm_hw_ver ver = qm->ver;
 struct qm_cqc cqc = {0};

 if (ver == QM_HW_V1) {
  cqc.dw3 = cpu_to_le32(QM_MK_CQC_DW3_V1(0, 0, 0, QM_QC_CQE_SIZE));
  cqc.w8 = cpu_to_le16(qp->cq_depth - 1);
 } else {
  cqc.dw3 = cpu_to_le32(QM_MK_CQC_DW3_V2(QM_QC_CQE_SIZE, qp->cq_depth));
  cqc.w8 = 0; /* rand_qc */
 }
 /*
 * Enable request finishing interrupts defaultly.
 * So, there will be some interrupts until disabling
 * this.
 */

 cqc.dw6 = cpu_to_le32(1 << QM_CQ_PHASE_SHIFT | 1 << QM_CQ_FLAG_SHIFT);
 cqc.base_l = cpu_to_le32(lower_32_bits(qp->cqe_dma));
 cqc.base_h = cpu_to_le32(upper_32_bits(qp->cqe_dma));
 cqc.pasid = cpu_to_le16(pasid);

 if (ver >= QM_HW_V3 && qm->use_sva && !qp->is_in_kernel)
  cqc.w11 = cpu_to_le16(QM_QC_PASID_ENABLE);

 return qm_set_and_get_xqc(qm, QM_MB_CMD_CQC, &cqc, qp_id, 0);
}

static int qm_qp_ctx_cfg(struct hisi_qp *qp, int qp_id, u32 pasid)
{
 int ret;

 qm_init_qp_status(qp);

 ret = qm_sq_ctx_cfg(qp, qp_id, pasid);
 if (ret)
  return ret;

 return qm_cq_ctx_cfg(qp, qp_id, pasid);
}

static int qm_start_qp_nolock(struct hisi_qp *qp, unsigned long arg)
{
 struct hisi_qm *qm = qp->qm;
 struct device *dev = &qm->pdev->dev;
 int qp_id = qp->qp_id;
 u32 pasid = arg;
 int ret;

 if (atomic_read(&qm->status.flags) == QM_STOP) {
  dev_info_ratelimited(dev, "failed to start qp as qm is stop!\n");
  return -EPERM;
 }

 ret = qm_qp_ctx_cfg(qp, qp_id, pasid);
 if (ret)
  return ret;

 atomic_set(&qp->qp_status.flags, QP_START);
 dev_dbg(dev, "queue %d started\n", qp_id);

 return 0;
}

/**
 * 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, unsigned long 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);

 return ret;
}
EXPORT_SYMBOL_GPL(hisi_qm_start_qp);

/**
 * qp_stop_fail_cb() - call request cb.
 * @qp: stopped failed qp.
 *
 * Callback function should be called whether task completed or not.
 */

static void qp_stop_fail_cb(struct hisi_qp *qp)
{
 int qp_used = atomic_read(&qp->qp_status.used);
 u16 cur_tail = qp->qp_status.sq_tail;
 u16 sq_depth = qp->sq_depth;
 u16 cur_head = (cur_tail + sq_depth - qp_used) % sq_depth;
 struct hisi_qm *qm = qp->qm;
 u16 pos;
 int i;

 for (i = 0; i < qp_used; i++) {
  pos = (i + cur_head) % sq_depth;
  qp->req_cb(qp, qp->sqe + (u32)(qm->sqe_size * pos));
  atomic_dec(&qp->qp_status.used);
 }
}

static int qm_wait_qp_empty(struct hisi_qm *qm, u32 *state, u32 qp_id)
{
 struct device *dev = &qm->pdev->dev;
 struct qm_sqc sqc;
 struct qm_cqc cqc;
 int ret, i = 0;

 while (++i) {
  ret = qm_set_and_get_xqc(qm, QM_MB_CMD_SQC, &sqc, qp_id, 1);
  if (ret) {
   dev_err_ratelimited(dev, "Failed to dump sqc!\n");
   *state = QM_DUMP_SQC_FAIL;
   return ret;
  }

  ret = qm_set_and_get_xqc(qm, QM_MB_CMD_CQC, &cqc, qp_id, 1);
  if (ret) {
   dev_err_ratelimited(dev, "Failed to dump cqc!\n");
   *state = QM_DUMP_CQC_FAIL;
   return ret;
  }

  if ((sqc.tail == cqc.tail) &&
      (QM_SQ_TAIL_IDX(sqc) == QM_CQ_TAIL_IDX(cqc)))
   break;

  if (i == MAX_WAIT_COUNTS) {
   dev_err(dev, "Fail to empty queue %u!\n", qp_id);
   *state = QM_STOP_QUEUE_FAIL;
   return -ETIMEDOUT;
  }

  usleep_range(WAIT_PERIOD_US_MIN, WAIT_PERIOD_US_MAX);
 }

 return 0;
}

/**
 * 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.
 */

static int 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;

 return ret;
}

static void qm_stop_qp_nolock(struct hisi_qp *qp)
{
 struct hisi_qm *qm = qp->qm;
 struct device *dev = &qm->pdev->dev;
 int ret;

 /*
 * 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, const void *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;
 }

 if (!sqe)
  return -EBUSY;

 memcpy(sqe, msg, qp->qm->sqe_size);

 qm_db(qp->qm, qp->qp_id, QM_DOORBELL_CMD_SQ, sq_tail_next, 0);
 atomic_inc(&qp->qp_status.used);
 qp_status->sq_tail = sq_tail_next;

 return 0;
}
EXPORT_SYMBOL_GPL(hisi_qp_send);

static void hisi_qm_cache_wb(struct hisi_qm *qm)
{
 unsigned int val;

 if (qm->ver == QM_HW_V1)
  return;

 writel(0x1, qm->io_base + QM_CACHE_WB_START);
 if (readl_relaxed_poll_timeout(qm->io_base + QM_CACHE_WB_DONE,
           val, val & BIT(0), POLL_PERIOD,
           POLL_TIMEOUT))
  dev_err(&qm->pdev->dev, "QM writeback sqc cache fail!\n");
}

static void qm_qp_event_notifier(struct hisi_qp *qp)
{
 wake_up_interruptible(&qp->uacce_q->wait);
}

 /* This function returns free number of qp in qm. */
static int 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;
}

static void 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);
}

static int hisi_qm_uacce_get_queue(struct uacce_device *uacce,
       unsigned long arg,
       struct uacce_queue *q)
{
 struct hisi_qm *qm = uacce->priv;
 struct hisi_qp *qp;
 u8 alg_type = 0;

 qp = hisi_qm_create_qp(qm, alg_type);
 if (IS_ERR(qp))
  return PTR_ERR(qp);

 q->priv = qp;
 q->uacce = uacce;
 qp->uacce_q = q;
 qp->event_cb = qm_qp_event_notifier;
 qp->pasid = arg;
 qp->is_in_kernel = false;

 return 0;
}

static void hisi_qm_uacce_put_queue(struct uacce_queue *q)
{
 struct hisi_qp *qp = q->priv;

 hisi_qm_release_qp(qp);
}

/* map sq/cq/doorbell to user space */
static int hisi_qm_uacce_mmap(struct uacce_queue *q,
         struct vm_area_struct *vma,
         struct uacce_qfile_region *qfr)
{
 struct hisi_qp *qp = q->priv;
 struct hisi_qm *qm = qp->qm;
 resource_size_t phys_base = qm->db_phys_base +
        qp->qp_id * qm->db_interval;
 size_t sz = vma->vm_end - vma->vm_start;
 struct pci_dev *pdev = qm->pdev;
 struct device *dev = &pdev->dev;
 unsigned long vm_pgoff;
 int ret;

 switch (qfr->type) {
 case UACCE_QFRT_MMIO:
  if (qm->ver == QM_HW_V1) {
   if (sz > PAGE_SIZE * QM_DOORBELL_PAGE_NR)
    return -EINVAL;
  } else if (!test_bit(QM_SUPPORT_DB_ISOLATION, &qm->caps)) {
   if (sz > PAGE_SIZE * (QM_DOORBELL_PAGE_NR +
       QM_DOORBELL_SQ_CQ_BASE_V2 / PAGE_SIZE))
    return -EINVAL;
  } else {
   if (sz > qm->db_interval)
    return -EINVAL;
  }

  vm_flags_set(vma, VM_IO);

  return remap_pfn_range(vma, vma->vm_start,
           phys_base >> PAGE_SHIFT,
           sz, pgprot_noncached(vma->vm_page_prot));
 case UACCE_QFRT_DUS:
  if (sz != qp->qdma.size)
   return -EINVAL;

  /*
 * dma_mmap_coherent() requires vm_pgoff as 0
 * restore vm_pfoff to initial value for mmap()
 */

  vm_pgoff = vma->vm_pgoff;
  vma->vm_pgoff = 0;
  ret = dma_mmap_coherent(dev, vma, qp->qdma.va,
     qp->qdma.dma, sz);
  vma->vm_pgoff = vm_pgoff;
  return ret;

 default:
  return -EINVAL;
 }
}

static int hisi_qm_uacce_start_queue(struct uacce_queue *q)
{
 struct hisi_qp *qp = q->priv;

 return hisi_qm_start_qp(qp, qp->pasid);
}

static void hisi_qm_uacce_stop_queue(struct uacce_queue *q)
{
 struct hisi_qp *qp = q->priv;
 struct hisi_qm *qm = qp->qm;
 struct qm_dev_dfx *dev_dfx = &qm->debug.dev_dfx;
 u32 i = 0;

 hisi_qm_stop_qp(qp);

 if (!dev_dfx->dev_timeout || !dev_dfx->dev_state)
  return;

 /*
 * After the queue fails to be stopped,
 * wait for a period of time before releasing the queue.
 */

 while (++i) {
  msleep(WAIT_PERIOD);

  /* Since dev_timeout maybe modified, check i >= dev_timeout */
  if (i >= dev_dfx->dev_timeout) {
   dev_err(&qm->pdev->dev, "Stop q %u timeout, state %u\n",
          qp->qp_id, dev_dfx->dev_state);
   dev_dfx->dev_state = QM_FINISH_WAIT;
   break;
  }
 }
}

static int hisi_qm_is_q_updated(struct uacce_queue *q)
{
 struct hisi_qp *qp = q->priv;
 struct qm_cqe *cqe = qp->cqe + qp->qp_status.cq_head;
 int updated = 0;

 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;
 }

 return updated;
}

static void qm_set_sqctype(struct uacce_queue *q, u16 type)
{
 struct hisi_qm *qm = q->uacce->priv;
 struct hisi_qp *qp = q->priv;

 down_write(&qm->qps_lock);
 qp->alg_type = type;
 up_write(&qm->qps_lock);
}

static long hisi_qm_uacce_ioctl(struct uacce_queue *q, unsigned int cmd,
    unsigned long arg)
{
 struct hisi_qp *qp = q->priv;
 struct hisi_qp_info qp_info;
 struct hisi_qp_ctx qp_ctx;

 if (cmd == UACCE_CMD_QM_SET_QP_CTX) {
  if (copy_from_user(&qp_ctx, (void __user *)arg,
       sizeof(struct hisi_qp_ctx)))
   return -EFAULT;

  if (qp_ctx.qc_type > QM_MAX_QC_TYPE)
   return -EINVAL;

  qm_set_sqctype(q, qp_ctx.qc_type);
  qp_ctx.id = qp->qp_id;

  if (copy_to_user((void __user *)arg, &qp_ctx,
     sizeof(struct hisi_qp_ctx)))
   return -EFAULT;

  return 0;
 } else if (cmd == UACCE_CMD_QM_SET_QP_INFO) {
  if (copy_from_user(&qp_info, (void __user *)arg,
       sizeof(struct hisi_qp_info)))
   return -EFAULT;

  qp_info.sqe_size = qp->qm->sqe_size;
  qp_info.sq_depth = qp->sq_depth;
  qp_info.cq_depth = qp->cq_depth;

  if (copy_to_user((void __user *)arg, &qp_info,
      sizeof(struct hisi_qp_info)))
   return -EFAULT;

  return 0;
 }

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=86 G=91

¤ Dauer der Verarbeitung: 0.22 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.