xref: /openbmc/u-boot/drivers/crypto/aspeed_hace.c (revision e31caa5a8948aba7fb2d84fc870754290e970fe7)
1*e31caa5aSChia-Wei Wang // SPDX-License-Identifier: GPL-2.0-or-later
2*e31caa5aSChia-Wei Wang /*
3*e31caa5aSChia-Wei Wang  * (C) Copyright ASPEED Technology Inc.
4*e31caa5aSChia-Wei Wang  * Copyright 2021 IBM Corp.
5*e31caa5aSChia-Wei Wang  */
6*e31caa5aSChia-Wei Wang #include <common.h>
7*e31caa5aSChia-Wei Wang #include <clk.h>
8*e31caa5aSChia-Wei Wang 
9*e31caa5aSChia-Wei Wang #include <log.h>
10*e31caa5aSChia-Wei Wang #include <asm/io.h>
11*e31caa5aSChia-Wei Wang #include <malloc.h>
12*e31caa5aSChia-Wei Wang #include <hash.h>
13*e31caa5aSChia-Wei Wang 
14*e31caa5aSChia-Wei Wang #include <dm/device.h>
15*e31caa5aSChia-Wei Wang #include <dm/fdtaddr.h>
16*e31caa5aSChia-Wei Wang 
17*e31caa5aSChia-Wei Wang #include <linux/bitops.h>
18*e31caa5aSChia-Wei Wang #include <linux/delay.h>
19*e31caa5aSChia-Wei Wang #include <linux/kernel.h>
20*e31caa5aSChia-Wei Wang #include <linux/iopoll.h>
21*e31caa5aSChia-Wei Wang 
22*e31caa5aSChia-Wei Wang #define ASPEED_HACE_STS			0x1C
23*e31caa5aSChia-Wei Wang #define  HACE_RSA_ISR			BIT(13)
24*e31caa5aSChia-Wei Wang #define  HACE_CRYPTO_ISR		BIT(12)
25*e31caa5aSChia-Wei Wang #define  HACE_HASH_ISR			BIT(9)
26*e31caa5aSChia-Wei Wang #define  HACE_RSA_BUSY			BIT(2)
27*e31caa5aSChia-Wei Wang #define  HACE_CRYPTO_BUSY		BIT(1)
28*e31caa5aSChia-Wei Wang #define  HACE_HASH_BUSY			BIT(0)
29*e31caa5aSChia-Wei Wang #define ASPEED_HACE_HASH_SRC		0x20
30*e31caa5aSChia-Wei Wang #define ASPEED_HACE_HASH_DIGEST_BUFF	0x24
31*e31caa5aSChia-Wei Wang #define ASPEED_HACE_HASH_KEY_BUFF	0x28
32*e31caa5aSChia-Wei Wang #define ASPEED_HACE_HASH_DATA_LEN	0x2C
33*e31caa5aSChia-Wei Wang #define  HACE_SG_LAST			BIT(31)
34*e31caa5aSChia-Wei Wang #define ASPEED_HACE_HASH_CMD		0x30
35*e31caa5aSChia-Wei Wang #define  HACE_SHA_BE_EN			BIT(3)
36*e31caa5aSChia-Wei Wang #define  HACE_MD5_LE_EN			BIT(2)
37*e31caa5aSChia-Wei Wang #define  HACE_ALGO_MD5			0
38*e31caa5aSChia-Wei Wang #define  HACE_ALGO_SHA1			BIT(5)
39*e31caa5aSChia-Wei Wang #define  HACE_ALGO_SHA224		BIT(6)
40*e31caa5aSChia-Wei Wang #define  HACE_ALGO_SHA256		(BIT(4) | BIT(6))
41*e31caa5aSChia-Wei Wang #define  HACE_ALGO_SHA512		(BIT(5) | BIT(6))
42*e31caa5aSChia-Wei Wang #define  HACE_ALGO_SHA384		(BIT(5) | BIT(6) | BIT(10))
43*e31caa5aSChia-Wei Wang #define  HACE_SG_EN			BIT(18)
44*e31caa5aSChia-Wei Wang 
45*e31caa5aSChia-Wei Wang #define ASPEED_MAX_SG			32
46*e31caa5aSChia-Wei Wang 
47*e31caa5aSChia-Wei Wang struct aspeed_sg {
48*e31caa5aSChia-Wei Wang 	u32 len;
49*e31caa5aSChia-Wei Wang 	u32 addr;
50*e31caa5aSChia-Wei Wang };
51*e31caa5aSChia-Wei Wang 
52*e31caa5aSChia-Wei Wang struct aspeed_hash_ctx {
53*e31caa5aSChia-Wei Wang 	u32 method;
54*e31caa5aSChia-Wei Wang 	u32 digest_size;
55*e31caa5aSChia-Wei Wang 	u32 len;
56*e31caa5aSChia-Wei Wang 	u32 count;
57*e31caa5aSChia-Wei Wang 	struct aspeed_sg list[ASPEED_MAX_SG]; /* Must be 8 byte aligned */
58*e31caa5aSChia-Wei Wang };
59*e31caa5aSChia-Wei Wang 
60*e31caa5aSChia-Wei Wang struct aspeed_hace {
61*e31caa5aSChia-Wei Wang 	struct clk clk;
62*e31caa5aSChia-Wei Wang };
63*e31caa5aSChia-Wei Wang 
64*e31caa5aSChia-Wei Wang static phys_addr_t base;
65*e31caa5aSChia-Wei Wang 
66*e31caa5aSChia-Wei Wang static int aspeed_hace_wait_completion(u32 reg, u32 flag, int timeout_us)
67*e31caa5aSChia-Wei Wang {
68*e31caa5aSChia-Wei Wang 	u32 val;
69*e31caa5aSChia-Wei Wang 
70*e31caa5aSChia-Wei Wang 	return readl_poll_timeout(reg, val, (val & flag) == flag, timeout_us);
71*e31caa5aSChia-Wei Wang }
72*e31caa5aSChia-Wei Wang 
73*e31caa5aSChia-Wei Wang static int digest_object(const void *src, unsigned int length, void *digest,
74*e31caa5aSChia-Wei Wang 			 u32 method)
75*e31caa5aSChia-Wei Wang {
76*e31caa5aSChia-Wei Wang 	if (!((u32)src & BIT(31))) {
77*e31caa5aSChia-Wei Wang 		debug("HACE src out of bounds: can only copy from SDRAM\n");
78*e31caa5aSChia-Wei Wang 		return -EINVAL;
79*e31caa5aSChia-Wei Wang 	}
80*e31caa5aSChia-Wei Wang 
81*e31caa5aSChia-Wei Wang 	if ((u32)digest & 0x7) {
82*e31caa5aSChia-Wei Wang 		debug("HACE dest alignment incorrect: %p\n", digest);
83*e31caa5aSChia-Wei Wang 		return -EINVAL;
84*e31caa5aSChia-Wei Wang 	}
85*e31caa5aSChia-Wei Wang 
86*e31caa5aSChia-Wei Wang 	if (readl(base + ASPEED_HACE_STS) & HACE_HASH_BUSY) {
87*e31caa5aSChia-Wei Wang 		debug("HACE error: engine busy\n");
88*e31caa5aSChia-Wei Wang 		return -EBUSY;
89*e31caa5aSChia-Wei Wang 	}
90*e31caa5aSChia-Wei Wang 
91*e31caa5aSChia-Wei Wang 	/* Clear pending completion status */
92*e31caa5aSChia-Wei Wang 	writel(HACE_HASH_ISR, base + ASPEED_HACE_STS);
93*e31caa5aSChia-Wei Wang 
94*e31caa5aSChia-Wei Wang 	writel((u32)src, base + ASPEED_HACE_HASH_SRC);
95*e31caa5aSChia-Wei Wang 	writel((u32)digest, base + ASPEED_HACE_HASH_DIGEST_BUFF);
96*e31caa5aSChia-Wei Wang 	writel(length, base + ASPEED_HACE_HASH_DATA_LEN);
97*e31caa5aSChia-Wei Wang 	writel(HACE_SHA_BE_EN | method, base + ASPEED_HACE_HASH_CMD);
98*e31caa5aSChia-Wei Wang 
99*e31caa5aSChia-Wei Wang 	/* SHA512 hashing appears to have a througput of about 12MB/s */
100*e31caa5aSChia-Wei Wang 	return aspeed_hace_wait_completion(base + ASPEED_HACE_STS,
101*e31caa5aSChia-Wei Wang 			HACE_HASH_ISR,
102*e31caa5aSChia-Wei Wang 			1000 + (length >> 3));
103*e31caa5aSChia-Wei Wang }
104*e31caa5aSChia-Wei Wang 
105*e31caa5aSChia-Wei Wang void hw_sha1(const unsigned char *pbuf, unsigned int buf_len,
106*e31caa5aSChia-Wei Wang 	     unsigned char *pout, unsigned int chunk_size)
107*e31caa5aSChia-Wei Wang {
108*e31caa5aSChia-Wei Wang 	int rc;
109*e31caa5aSChia-Wei Wang 
110*e31caa5aSChia-Wei Wang 	rc = digest_object(pbuf, buf_len, pout, HACE_ALGO_SHA1);
111*e31caa5aSChia-Wei Wang 	if (rc)
112*e31caa5aSChia-Wei Wang 		debug("HACE failure: %d\n", rc);
113*e31caa5aSChia-Wei Wang }
114*e31caa5aSChia-Wei Wang 
115*e31caa5aSChia-Wei Wang void hw_sha256(const unsigned char *pbuf, unsigned int buf_len,
116*e31caa5aSChia-Wei Wang 	       unsigned char *pout, unsigned int chunk_size)
117*e31caa5aSChia-Wei Wang {
118*e31caa5aSChia-Wei Wang 	int rc;
119*e31caa5aSChia-Wei Wang 
120*e31caa5aSChia-Wei Wang 	rc = digest_object(pbuf, buf_len, pout, HACE_ALGO_SHA256);
121*e31caa5aSChia-Wei Wang 	if (rc)
122*e31caa5aSChia-Wei Wang 		debug("HACE failure: %d\n", rc);
123*e31caa5aSChia-Wei Wang }
124*e31caa5aSChia-Wei Wang 
125*e31caa5aSChia-Wei Wang void hw_sha512(const unsigned char *pbuf, unsigned int buf_len,
126*e31caa5aSChia-Wei Wang 	       unsigned char *pout, unsigned int chunk_size)
127*e31caa5aSChia-Wei Wang {
128*e31caa5aSChia-Wei Wang 	int rc;
129*e31caa5aSChia-Wei Wang 
130*e31caa5aSChia-Wei Wang 	rc = digest_object(pbuf, buf_len, pout, HACE_ALGO_SHA512);
131*e31caa5aSChia-Wei Wang 	if (rc)
132*e31caa5aSChia-Wei Wang 		debug("HACE failure: %d\n", rc);
133*e31caa5aSChia-Wei Wang }
134*e31caa5aSChia-Wei Wang 
135*e31caa5aSChia-Wei Wang #if IS_ENABLED(CONFIG_SHA_PROG_HW_ACCEL)
136*e31caa5aSChia-Wei Wang int hw_sha_init(struct hash_algo *algo, void **ctxp)
137*e31caa5aSChia-Wei Wang {
138*e31caa5aSChia-Wei Wang 	struct aspeed_hash_ctx *ctx;
139*e31caa5aSChia-Wei Wang 	u32 method;
140*e31caa5aSChia-Wei Wang 
141*e31caa5aSChia-Wei Wang 	if (!strcmp(algo->name, "sha1"))
142*e31caa5aSChia-Wei Wang 		method = HACE_ALGO_SHA1;
143*e31caa5aSChia-Wei Wang 	else if (!strcmp(algo->name, "sha256"))
144*e31caa5aSChia-Wei Wang 		method = HACE_ALGO_SHA256;
145*e31caa5aSChia-Wei Wang 	else if (!strcmp(algo->name, "sha512"))
146*e31caa5aSChia-Wei Wang 		method = HACE_ALGO_SHA512;
147*e31caa5aSChia-Wei Wang 	else
148*e31caa5aSChia-Wei Wang 		return -ENOTSUPP;
149*e31caa5aSChia-Wei Wang 
150*e31caa5aSChia-Wei Wang 	ctx = memalign(8, sizeof(*ctx));
151*e31caa5aSChia-Wei Wang 	memset(ctx, '\0', sizeof(*ctx));
152*e31caa5aSChia-Wei Wang 
153*e31caa5aSChia-Wei Wang 	if (!ctx) {
154*e31caa5aSChia-Wei Wang 		debug("HACE error: Cannot allocate memory for context\n");
155*e31caa5aSChia-Wei Wang 		return -ENOMEM;
156*e31caa5aSChia-Wei Wang 	}
157*e31caa5aSChia-Wei Wang 
158*e31caa5aSChia-Wei Wang 	if (((uintptr_t)ctx->list & 0x3) != 0) {
159*e31caa5aSChia-Wei Wang 		printf("HACE error: Invalid alignment for input data\n");
160*e31caa5aSChia-Wei Wang 		return -EINVAL;
161*e31caa5aSChia-Wei Wang 	}
162*e31caa5aSChia-Wei Wang 
163*e31caa5aSChia-Wei Wang 	ctx->method = method | HACE_SG_EN;
164*e31caa5aSChia-Wei Wang 	ctx->digest_size = algo->digest_size;
165*e31caa5aSChia-Wei Wang 	*ctxp = ctx;
166*e31caa5aSChia-Wei Wang 
167*e31caa5aSChia-Wei Wang 	return 0;
168*e31caa5aSChia-Wei Wang }
169*e31caa5aSChia-Wei Wang 
170*e31caa5aSChia-Wei Wang int hw_sha_update(struct hash_algo *algo, void *hash_ctx, const void *buf,
171*e31caa5aSChia-Wei Wang 		  unsigned int size, int is_last)
172*e31caa5aSChia-Wei Wang {
173*e31caa5aSChia-Wei Wang 	struct aspeed_hash_ctx *ctx = hash_ctx;
174*e31caa5aSChia-Wei Wang 	struct aspeed_sg *sg = &ctx->list[ctx->count];
175*e31caa5aSChia-Wei Wang 
176*e31caa5aSChia-Wei Wang 	if (ctx->count >= ARRAY_SIZE(ctx->list)) {
177*e31caa5aSChia-Wei Wang 		debug("HACE error: Reached maximum number of hash segments\n");
178*e31caa5aSChia-Wei Wang 		free(ctx);
179*e31caa5aSChia-Wei Wang 		return -EINVAL;
180*e31caa5aSChia-Wei Wang 	}
181*e31caa5aSChia-Wei Wang 
182*e31caa5aSChia-Wei Wang 	sg->addr = (u32)buf;
183*e31caa5aSChia-Wei Wang 	sg->len = size;
184*e31caa5aSChia-Wei Wang 	if (is_last)
185*e31caa5aSChia-Wei Wang 		sg->len |= HACE_SG_LAST;
186*e31caa5aSChia-Wei Wang 
187*e31caa5aSChia-Wei Wang 	ctx->count++;
188*e31caa5aSChia-Wei Wang 	ctx->len += size;
189*e31caa5aSChia-Wei Wang 
190*e31caa5aSChia-Wei Wang 	return 0;
191*e31caa5aSChia-Wei Wang }
192*e31caa5aSChia-Wei Wang 
193*e31caa5aSChia-Wei Wang int hw_sha_finish(struct hash_algo *algo, void *hash_ctx, void *dest_buf, int size)
194*e31caa5aSChia-Wei Wang {
195*e31caa5aSChia-Wei Wang 	struct aspeed_hash_ctx *ctx = hash_ctx;
196*e31caa5aSChia-Wei Wang 	int rc;
197*e31caa5aSChia-Wei Wang 
198*e31caa5aSChia-Wei Wang 	if (size < ctx->digest_size) {
199*e31caa5aSChia-Wei Wang 		debug("HACE error: insufficient size on destination buffer\n");
200*e31caa5aSChia-Wei Wang 		free(ctx);
201*e31caa5aSChia-Wei Wang 		return -EINVAL;
202*e31caa5aSChia-Wei Wang 	}
203*e31caa5aSChia-Wei Wang 
204*e31caa5aSChia-Wei Wang 	rc = digest_object(ctx->list, ctx->len, dest_buf, ctx->method);
205*e31caa5aSChia-Wei Wang 	if (rc)
206*e31caa5aSChia-Wei Wang 		debug("HACE Scatter-Gather failure\n");
207*e31caa5aSChia-Wei Wang 
208*e31caa5aSChia-Wei Wang 	free(ctx);
209*e31caa5aSChia-Wei Wang 
210*e31caa5aSChia-Wei Wang 	return rc;
211*e31caa5aSChia-Wei Wang }
212*e31caa5aSChia-Wei Wang #endif
213*e31caa5aSChia-Wei Wang 
214*e31caa5aSChia-Wei Wang static int aspeed_hace_probe(struct udevice *dev)
215*e31caa5aSChia-Wei Wang {
216*e31caa5aSChia-Wei Wang 	struct aspeed_hace *hace = dev_get_priv(dev);
217*e31caa5aSChia-Wei Wang 	int ret;
218*e31caa5aSChia-Wei Wang 
219*e31caa5aSChia-Wei Wang 	ret = clk_get_by_index(dev, 0, &hace->clk);
220*e31caa5aSChia-Wei Wang 	if (ret < 0) {
221*e31caa5aSChia-Wei Wang 		debug("Can't get clock for %s: %d\n", dev->name, ret);
222*e31caa5aSChia-Wei Wang 		return ret;
223*e31caa5aSChia-Wei Wang 	}
224*e31caa5aSChia-Wei Wang 
225*e31caa5aSChia-Wei Wang 	ret = clk_enable(&hace->clk);
226*e31caa5aSChia-Wei Wang 	if (ret) {
227*e31caa5aSChia-Wei Wang 		debug("Failed to enable fsi clock (%d)\n", ret);
228*e31caa5aSChia-Wei Wang 		return ret;
229*e31caa5aSChia-Wei Wang 	}
230*e31caa5aSChia-Wei Wang 
231*e31caa5aSChia-Wei Wang 	/* As the crypto code does not pass us any driver state */
232*e31caa5aSChia-Wei Wang 	base = devfdt_get_addr(dev);
233*e31caa5aSChia-Wei Wang 
234*e31caa5aSChia-Wei Wang 	return ret;
235*e31caa5aSChia-Wei Wang }
236*e31caa5aSChia-Wei Wang 
237*e31caa5aSChia-Wei Wang static int aspeed_hace_remove(struct udevice *dev)
238*e31caa5aSChia-Wei Wang {
239*e31caa5aSChia-Wei Wang 	struct aspeed_hace *hace = dev_get_priv(dev);
240*e31caa5aSChia-Wei Wang 
241*e31caa5aSChia-Wei Wang 	clk_disable(&hace->clk);
242*e31caa5aSChia-Wei Wang 
243*e31caa5aSChia-Wei Wang 	return 0;
244*e31caa5aSChia-Wei Wang }
245*e31caa5aSChia-Wei Wang 
246*e31caa5aSChia-Wei Wang static const struct udevice_id aspeed_hace_ids[] = {
247*e31caa5aSChia-Wei Wang 	{ .compatible = "aspeed,ast2600-hace" },
248*e31caa5aSChia-Wei Wang 	{ }
249*e31caa5aSChia-Wei Wang };
250*e31caa5aSChia-Wei Wang 
251*e31caa5aSChia-Wei Wang U_BOOT_DRIVER(aspeed_hace) = {
252*e31caa5aSChia-Wei Wang 	.name		= "aspeed_hace",
253*e31caa5aSChia-Wei Wang 	.id		= UCLASS_MISC,
254*e31caa5aSChia-Wei Wang 	.of_match	= aspeed_hace_ids,
255*e31caa5aSChia-Wei Wang 	.probe		= aspeed_hace_probe,
256*e31caa5aSChia-Wei Wang 	.remove		= aspeed_hace_remove,
257*e31caa5aSChia-Wei Wang 	.priv_auto_alloc_size = sizeof(struct aspeed_hace),
258*e31caa5aSChia-Wei Wang 	.flags  = DM_FLAG_PRE_RELOC,
259*e31caa5aSChia-Wei Wang };
260