1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2024 HiSilicon Limited. */
3
4 #include <linux/bitops.h>
5 #include <linux/io.h>
6 #include <linux/uacce.h>
7 #include "zip.h"
8
9 /* memory */
10 #define DAE_MEM_START_OFFSET 0x331040
11 #define DAE_MEM_DONE_OFFSET 0x331044
12 #define DAE_MEM_START_MASK 0x1
13 #define DAE_MEM_DONE_MASK 0x1
14 #define DAE_REG_RD_INTVRL_US 10
15 #define DAE_REG_RD_TMOUT_US USEC_PER_SEC
16
17 #define DAE_ALG_NAME "hashagg"
18
19 /* error */
20 #define DAE_AXI_CFG_OFFSET 0x331000
21 #define DAE_AXI_SHUTDOWN_MASK (BIT(0) | BIT(5))
22 #define DAE_ERR_SOURCE_OFFSET 0x331C84
23 #define DAE_ERR_STATUS_OFFSET 0x331C88
24 #define DAE_ERR_CE_OFFSET 0x331CA0
25 #define DAE_ERR_CE_MASK BIT(3)
26 #define DAE_ERR_NFE_OFFSET 0x331CA4
27 #define DAE_ERR_NFE_MASK 0x17
28 #define DAE_ERR_FE_OFFSET 0x331CA8
29 #define DAE_ERR_FE_MASK 0
30 #define DAE_ECC_MBIT_MASK BIT(2)
31 #define DAE_ECC_INFO_OFFSET 0x33400C
32 #define DAE_ERR_SHUTDOWN_OFFSET 0x331CAC
33 #define DAE_ERR_SHUTDOWN_MASK 0x17
34 #define DAE_ERR_ENABLE_OFFSET 0x331C80
35 #define DAE_ERR_ENABLE_MASK (DAE_ERR_FE_MASK | DAE_ERR_NFE_MASK | DAE_ERR_CE_MASK)
36 #define DAE_AM_CTRL_GLOBAL_OFFSET 0x330000
37 #define DAE_AM_RETURN_OFFSET 0x330150
38 #define DAE_AM_RETURN_MASK 0x3
39 #define DAE_AXI_CFG_OFFSET 0x331000
40 #define DAE_AXI_SHUTDOWN_EN_MASK (BIT(0) | BIT(5))
41
42 struct hisi_dae_hw_error {
43 u32 int_msk;
44 const char *msg;
45 };
46
47 static const struct hisi_dae_hw_error dae_hw_error[] = {
48 { .int_msk = BIT(0), .msg = "dae_axi_bus_err" },
49 { .int_msk = BIT(1), .msg = "dae_axi_poison_err" },
50 { .int_msk = BIT(2), .msg = "dae_ecc_2bit_err" },
51 { .int_msk = BIT(3), .msg = "dae_ecc_1bit_err" },
52 { .int_msk = BIT(4), .msg = "dae_fsm_hbeat_err" },
53 };
54
dae_is_support(struct hisi_qm * qm)55 static inline bool dae_is_support(struct hisi_qm *qm)
56 {
57 if (test_bit(QM_SUPPORT_DAE, &qm->caps))
58 return true;
59
60 return false;
61 }
62
hisi_dae_set_user_domain(struct hisi_qm * qm)63 int hisi_dae_set_user_domain(struct hisi_qm *qm)
64 {
65 u32 val;
66 int ret;
67
68 if (!dae_is_support(qm))
69 return 0;
70
71 val = readl(qm->io_base + DAE_MEM_START_OFFSET);
72 val |= DAE_MEM_START_MASK;
73 writel(val, qm->io_base + DAE_MEM_START_OFFSET);
74 ret = readl_relaxed_poll_timeout(qm->io_base + DAE_MEM_DONE_OFFSET, val,
75 val & DAE_MEM_DONE_MASK,
76 DAE_REG_RD_INTVRL_US, DAE_REG_RD_TMOUT_US);
77 if (ret)
78 pci_err(qm->pdev, "failed to init dae memory!\n");
79
80 return ret;
81 }
82
hisi_dae_set_alg(struct hisi_qm * qm)83 int hisi_dae_set_alg(struct hisi_qm *qm)
84 {
85 size_t len;
86
87 if (!dae_is_support(qm))
88 return 0;
89
90 if (!qm->uacce)
91 return 0;
92
93 len = strlen(qm->uacce->algs);
94 /* A line break may be required */
95 if (len + strlen(DAE_ALG_NAME) + 1 >= QM_DEV_ALG_MAX_LEN) {
96 pci_err(qm->pdev, "algorithm name is too long!\n");
97 return -EINVAL;
98 }
99
100 if (len)
101 strcat((char *)qm->uacce->algs, "\n");
102
103 strcat((char *)qm->uacce->algs, DAE_ALG_NAME);
104
105 return 0;
106 }
107
hisi_dae_master_ooo_ctrl(struct hisi_qm * qm,bool enable)108 static void hisi_dae_master_ooo_ctrl(struct hisi_qm *qm, bool enable)
109 {
110 u32 axi_val, err_val;
111
112 axi_val = readl(qm->io_base + DAE_AXI_CFG_OFFSET);
113 if (enable) {
114 axi_val |= DAE_AXI_SHUTDOWN_MASK;
115 err_val = DAE_ERR_SHUTDOWN_MASK;
116 } else {
117 axi_val &= ~DAE_AXI_SHUTDOWN_MASK;
118 err_val = 0;
119 }
120
121 writel(axi_val, qm->io_base + DAE_AXI_CFG_OFFSET);
122 writel(err_val, qm->io_base + DAE_ERR_SHUTDOWN_OFFSET);
123 }
124
hisi_dae_hw_error_enable(struct hisi_qm * qm)125 void hisi_dae_hw_error_enable(struct hisi_qm *qm)
126 {
127 if (!dae_is_support(qm))
128 return;
129
130 /* clear dae hw error source if having */
131 writel(DAE_ERR_ENABLE_MASK, qm->io_base + DAE_ERR_SOURCE_OFFSET);
132
133 /* configure error type */
134 writel(DAE_ERR_CE_MASK, qm->io_base + DAE_ERR_CE_OFFSET);
135 writel(DAE_ERR_NFE_MASK, qm->io_base + DAE_ERR_NFE_OFFSET);
136 writel(DAE_ERR_FE_MASK, qm->io_base + DAE_ERR_FE_OFFSET);
137
138 hisi_dae_master_ooo_ctrl(qm, true);
139
140 /* enable dae hw error interrupts */
141 writel(DAE_ERR_ENABLE_MASK, qm->io_base + DAE_ERR_ENABLE_OFFSET);
142 }
143
hisi_dae_hw_error_disable(struct hisi_qm * qm)144 void hisi_dae_hw_error_disable(struct hisi_qm *qm)
145 {
146 if (!dae_is_support(qm))
147 return;
148
149 writel(0, qm->io_base + DAE_ERR_ENABLE_OFFSET);
150 hisi_dae_master_ooo_ctrl(qm, false);
151 }
152
hisi_dae_get_hw_err_status(struct hisi_qm * qm)153 static u32 hisi_dae_get_hw_err_status(struct hisi_qm *qm)
154 {
155 return readl(qm->io_base + DAE_ERR_STATUS_OFFSET);
156 }
157
hisi_dae_clear_hw_err_status(struct hisi_qm * qm,u32 err_sts)158 static void hisi_dae_clear_hw_err_status(struct hisi_qm *qm, u32 err_sts)
159 {
160 if (!dae_is_support(qm))
161 return;
162
163 writel(err_sts, qm->io_base + DAE_ERR_SOURCE_OFFSET);
164 }
165
hisi_dae_disable_error_report(struct hisi_qm * qm,u32 err_type)166 static void hisi_dae_disable_error_report(struct hisi_qm *qm, u32 err_type)
167 {
168 writel(DAE_ERR_NFE_MASK & (~err_type), qm->io_base + DAE_ERR_NFE_OFFSET);
169 }
170
hisi_dae_log_hw_error(struct hisi_qm * qm,u32 err_type)171 static void hisi_dae_log_hw_error(struct hisi_qm *qm, u32 err_type)
172 {
173 const struct hisi_dae_hw_error *err = dae_hw_error;
174 struct device *dev = &qm->pdev->dev;
175 u32 ecc_info;
176 size_t i;
177
178 for (i = 0; i < ARRAY_SIZE(dae_hw_error); i++) {
179 err = &dae_hw_error[i];
180 if (!(err->int_msk & err_type))
181 continue;
182
183 dev_err(dev, "%s [error status=0x%x] found\n",
184 err->msg, err->int_msk);
185
186 if (err->int_msk & DAE_ECC_MBIT_MASK) {
187 ecc_info = readl(qm->io_base + DAE_ECC_INFO_OFFSET);
188 dev_err(dev, "dae multi ecc sram info 0x%x\n", ecc_info);
189 }
190 }
191 }
192
hisi_dae_get_err_result(struct hisi_qm * qm)193 enum acc_err_result hisi_dae_get_err_result(struct hisi_qm *qm)
194 {
195 u32 err_status;
196
197 if (!dae_is_support(qm))
198 return ACC_ERR_NONE;
199
200 err_status = hisi_dae_get_hw_err_status(qm);
201 if (!err_status)
202 return ACC_ERR_NONE;
203
204 hisi_dae_log_hw_error(qm, err_status);
205
206 if (err_status & DAE_ERR_NFE_MASK) {
207 /* Disable the same error reporting until device is recovered. */
208 hisi_dae_disable_error_report(qm, err_status);
209 return ACC_ERR_NEED_RESET;
210 }
211 hisi_dae_clear_hw_err_status(qm, err_status);
212
213 return ACC_ERR_RECOVERED;
214 }
215
hisi_dae_dev_is_abnormal(struct hisi_qm * qm)216 bool hisi_dae_dev_is_abnormal(struct hisi_qm *qm)
217 {
218 u32 err_status;
219
220 if (!dae_is_support(qm))
221 return false;
222
223 err_status = hisi_dae_get_hw_err_status(qm);
224 if (err_status & DAE_ERR_NFE_MASK)
225 return true;
226
227 return false;
228 }
229
hisi_dae_close_axi_master_ooo(struct hisi_qm * qm)230 int hisi_dae_close_axi_master_ooo(struct hisi_qm *qm)
231 {
232 u32 val;
233 int ret;
234
235 if (!dae_is_support(qm))
236 return 0;
237
238 val = readl(qm->io_base + DAE_AM_CTRL_GLOBAL_OFFSET);
239 val |= BIT(0);
240 writel(val, qm->io_base + DAE_AM_CTRL_GLOBAL_OFFSET);
241
242 ret = readl_relaxed_poll_timeout(qm->io_base + DAE_AM_RETURN_OFFSET,
243 val, (val == DAE_AM_RETURN_MASK),
244 DAE_REG_RD_INTVRL_US, DAE_REG_RD_TMOUT_US);
245 if (ret)
246 dev_err(&qm->pdev->dev, "failed to close dae axi ooo!\n");
247
248 return ret;
249 }
250
hisi_dae_open_axi_master_ooo(struct hisi_qm * qm)251 void hisi_dae_open_axi_master_ooo(struct hisi_qm *qm)
252 {
253 u32 val;
254
255 if (!dae_is_support(qm))
256 return;
257
258 val = readl(qm->io_base + DAE_AXI_CFG_OFFSET);
259
260 writel(val & ~DAE_AXI_SHUTDOWN_EN_MASK, qm->io_base + DAE_AXI_CFG_OFFSET);
261 writel(val | DAE_AXI_SHUTDOWN_EN_MASK, qm->io_base + DAE_AXI_CFG_OFFSET);
262 }
263