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