summaryrefslogtreecommitdiff
path: root/drivers/crypto/hisilicon/zip/dae_main.c
blob: 6f22e4c36e494f7500c1ac0633f8f3feeacbdc45 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2024 HiSilicon Limited. */

#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/uacce.h>
#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);
}