// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2024 HiSilicon Limited. */ #include #include #include #include "zip.h" /* memory */ #define DAE_MEM_START_OFFSET 0x331040 #define DAE_MEM_DONE_OFFSET 0x331044 #define DAE_MEM_START_MASK 0x1 #define DAE_MEM_DONE_MASK 0x1 #define DAE_REG_RD_INTVRL_US 10 #define DAE_REG_RD_TMOUT_US USEC_PER_SEC #define DAE_ALG_NAME "hashagg" /* error */ #define DAE_AXI_CFG_OFFSET 0x331000 #define DAE_AXI_SHUTDOWN_MASK (BIT(0) | BIT(5)) #define DAE_ERR_SOURCE_OFFSET 0x331C84 #define DAE_ERR_STATUS_OFFSET 0x331C88 #define DAE_ERR_CE_OFFSET 0x331CA0 #define DAE_ERR_CE_MASK BIT(3) #define DAE_ERR_NFE_OFFSET 0x331CA4 #define DAE_ERR_NFE_MASK 0x17 #define DAE_ERR_FE_OFFSET 0x331CA8 #define DAE_ERR_FE_MASK 0 #define DAE_ECC_MBIT_MASK BIT(2) #define DAE_ECC_INFO_OFFSET 0x33400C #define DAE_ERR_SHUTDOWN_OFFSET 0x331CAC #define DAE_ERR_SHUTDOWN_MASK 0x17 #define DAE_ERR_ENABLE_OFFSET 0x331C80 #define DAE_ERR_ENABLE_MASK (DAE_ERR_FE_MASK | DAE_ERR_NFE_MASK | DAE_ERR_CE_MASK) #define DAE_AM_CTRL_GLOBAL_OFFSET 0x330000 #define DAE_AM_RETURN_OFFSET 0x330150 #define DAE_AM_RETURN_MASK 0x3 #define DAE_AXI_CFG_OFFSET 0x331000 #define DAE_AXI_SHUTDOWN_EN_MASK (BIT(0) | BIT(5)) struct hisi_dae_hw_error { u32 int_msk; const char *msg; }; static const struct hisi_dae_hw_error dae_hw_error[] = { { .int_msk = BIT(0), .msg = "dae_axi_bus_err" }, { .int_msk = BIT(1), .msg = "dae_axi_poison_err" }, { .int_msk = BIT(2), .msg = "dae_ecc_2bit_err" }, { .int_msk = BIT(3), .msg = "dae_ecc_1bit_err" }, { .int_msk = BIT(4), .msg = "dae_fsm_hbeat_err" }, }; static inline bool dae_is_support(struct hisi_qm *qm) { if (test_bit(QM_SUPPORT_DAE, &qm->caps)) return true; return false; } int hisi_dae_set_user_domain(struct hisi_qm *qm) { u32 val; int ret; if (!dae_is_support(qm)) return 0; val = readl(qm->io_base + DAE_MEM_START_OFFSET); val |= DAE_MEM_START_MASK; writel(val, qm->io_base + DAE_MEM_START_OFFSET); ret = readl_relaxed_poll_timeout(qm->io_base + DAE_MEM_DONE_OFFSET, val, val & DAE_MEM_DONE_MASK, DAE_REG_RD_INTVRL_US, DAE_REG_RD_TMOUT_US); if (ret) pci_err(qm->pdev, "failed to init dae memory!\n"); return ret; } int hisi_dae_set_alg(struct hisi_qm *qm) { size_t len; if (!dae_is_support(qm)) return 0; if (!qm->uacce) return 0; len = strlen(qm->uacce->algs); /* A line break may be required */ if (len + strlen(DAE_ALG_NAME) + 1 >= QM_DEV_ALG_MAX_LEN) { pci_err(qm->pdev, "algorithm name is too long!\n"); return -EINVAL; } if (len) strcat((char *)qm->uacce->algs, "\n"); strcat((char *)qm->uacce->algs, DAE_ALG_NAME); return 0; } static void hisi_dae_master_ooo_ctrl(struct hisi_qm *qm, bool enable) { u32 axi_val, err_val; axi_val = readl(qm->io_base + DAE_AXI_CFG_OFFSET); if (enable) { axi_val |= DAE_AXI_SHUTDOWN_MASK; err_val = DAE_ERR_SHUTDOWN_MASK; } else { axi_val &= ~DAE_AXI_SHUTDOWN_MASK; err_val = 0; } writel(axi_val, qm->io_base + DAE_AXI_CFG_OFFSET); writel(err_val, qm->io_base + DAE_ERR_SHUTDOWN_OFFSET); } void hisi_dae_hw_error_enable(struct hisi_qm *qm) { if (!dae_is_support(qm)) return; /* clear dae hw error source if having */ writel(DAE_ERR_ENABLE_MASK, qm->io_base + DAE_ERR_SOURCE_OFFSET); /* configure error type */ writel(DAE_ERR_CE_MASK, qm->io_base + DAE_ERR_CE_OFFSET); writel(DAE_ERR_NFE_MASK, qm->io_base + DAE_ERR_NFE_OFFSET); writel(DAE_ERR_FE_MASK, qm->io_base + DAE_ERR_FE_OFFSET); hisi_dae_master_ooo_ctrl(qm, true); /* enable dae hw error interrupts */ writel(DAE_ERR_ENABLE_MASK, qm->io_base + DAE_ERR_ENABLE_OFFSET); } void hisi_dae_hw_error_disable(struct hisi_qm *qm) { if (!dae_is_support(qm)) return; writel(0, qm->io_base + DAE_ERR_ENABLE_OFFSET); hisi_dae_master_ooo_ctrl(qm, false); } static u32 hisi_dae_get_hw_err_status(struct hisi_qm *qm) { return readl(qm->io_base + DAE_ERR_STATUS_OFFSET); } static void hisi_dae_clear_hw_err_status(struct hisi_qm *qm, u32 err_sts) { if (!dae_is_support(qm)) return; writel(err_sts, qm->io_base + DAE_ERR_SOURCE_OFFSET); } static void hisi_dae_disable_error_report(struct hisi_qm *qm, u32 err_type) { writel(DAE_ERR_NFE_MASK & (~err_type), qm->io_base + DAE_ERR_NFE_OFFSET); } static void hisi_dae_log_hw_error(struct hisi_qm *qm, u32 err_type) { const struct hisi_dae_hw_error *err = dae_hw_error; struct device *dev = &qm->pdev->dev; u32 ecc_info; size_t i; for (i = 0; i < ARRAY_SIZE(dae_hw_error); i++) { err = &dae_hw_error[i]; if (!(err->int_msk & err_type)) continue; dev_err(dev, "%s [error status=0x%x] found\n", err->msg, err->int_msk); if (err->int_msk & DAE_ECC_MBIT_MASK) { ecc_info = readl(qm->io_base + DAE_ECC_INFO_OFFSET); dev_err(dev, "dae multi ecc sram info 0x%x\n", ecc_info); } } } enum acc_err_result hisi_dae_get_err_result(struct hisi_qm *qm) { u32 err_status; if (!dae_is_support(qm)) return ACC_ERR_NONE; err_status = hisi_dae_get_hw_err_status(qm); if (!err_status) return ACC_ERR_NONE; hisi_dae_log_hw_error(qm, err_status); if (err_status & DAE_ERR_NFE_MASK) { /* Disable the same error reporting until device is recovered. */ hisi_dae_disable_error_report(qm, err_status); return ACC_ERR_NEED_RESET; } hisi_dae_clear_hw_err_status(qm, err_status); return ACC_ERR_RECOVERED; } bool hisi_dae_dev_is_abnormal(struct hisi_qm *qm) { u32 err_status; if (!dae_is_support(qm)) return false; err_status = hisi_dae_get_hw_err_status(qm); if (err_status & DAE_ERR_NFE_MASK) return true; return false; } int hisi_dae_close_axi_master_ooo(struct hisi_qm *qm) { u32 val; int ret; if (!dae_is_support(qm)) return 0; val = readl(qm->io_base + DAE_AM_CTRL_GLOBAL_OFFSET); val |= BIT(0); writel(val, qm->io_base + DAE_AM_CTRL_GLOBAL_OFFSET); ret = readl_relaxed_poll_timeout(qm->io_base + DAE_AM_RETURN_OFFSET, val, (val == DAE_AM_RETURN_MASK), DAE_REG_RD_INTVRL_US, DAE_REG_RD_TMOUT_US); if (ret) dev_err(&qm->pdev->dev, "failed to close dae axi ooo!\n"); return ret; } void hisi_dae_open_axi_master_ooo(struct hisi_qm *qm) { u32 val; if (!dae_is_support(qm)) return; val = readl(qm->io_base + DAE_AXI_CFG_OFFSET); writel(val & ~DAE_AXI_SHUTDOWN_EN_MASK, qm->io_base + DAE_AXI_CFG_OFFSET); writel(val | DAE_AXI_SHUTDOWN_EN_MASK, qm->io_base + DAE_AXI_CFG_OFFSET); }