1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright 2024 ASPEED Technology Inc.
4  */
5 #include <asm/io.h>
6 #include <config.h>
7 #include <dm.h>
8 #include <linux/bitfield.h>
9 #include <linux/bitops.h>
10 #include <linux/iopoll.h>
11 #include <malloc.h>
12 #include <u-boot/hash.h>
13 #include <watchdog.h>
14 
15 /* SHA register offsets */
16 #define CPTRA_SHA_LOCK			0x00
17 #define CPTRA_SHA_USER			0x04
18 #define CPTRA_SHA_MODE			0x08
19 #define   CPTRA_SHA_MODE_ENDIAN		BIT(2)
20 #define   CPTRA_SHA_MODE_SEL		GENMASK(1, 0)
21 #define CPTRA_SHA_DLEN			0x10
22 #define CPTRA_SHA_DATAIN		0x14
23 #define CPTRA_SHA_EXEC			0x18
24 #define CPTRA_SHA_STS			0x1c
25 #define   CPTRA_SHA_STS_SOC_LOCK	BIT(1)
26 #define   CPTRA_SHA_STS_VLD		BIT(0)
27 #define CPTRA_SHA_DIGEST(n)		(0x20 + ((n) << 2))
28 #define CPTRA_SHA_CTRL			0x60
29 #define   CPTRA_SHA_CTRL_ZEROIZE	BIT(0)
30 
31 enum cptra_sha_modes {
32 	CPTRA_SHA384_STREAM,
33 	CPTRA_SHA512_STREAM,
34 };
35 
36 struct cptra_sha_ctx {
37 	enum HASH_ALGO algo;
38 	uint32_t dgst_len;
39 };
40 
41 struct cptra_sha {
42 	void *regs;
43 };
44 
cptra_sha_init(struct udevice * dev,enum HASH_ALGO algo,void ** ctxp)45 static int cptra_sha_init(struct udevice *dev, enum HASH_ALGO algo, void **ctxp)
46 {
47 	struct cptra_sha_ctx *cs_ctx;
48 	struct cptra_sha *cs;
49 	uint32_t mode;
50 	uint32_t reg;
51 	int rc;
52 
53 	cs_ctx = malloc(sizeof(struct cptra_sha_ctx));
54 	if (!cs_ctx)
55 		return -ENOMEM;
56 
57 	memset(cs_ctx, 0, sizeof(struct cptra_sha_ctx));
58 
59 	cs_ctx->algo = algo;
60 
61 	switch (algo) {
62 	case HASH_ALGO_SHA384:
63 		mode = CPTRA_SHA384_STREAM;
64 		cs_ctx->dgst_len = 48;
65 		break;
66 	case HASH_ALGO_SHA512:
67 		mode = CPTRA_SHA512_STREAM;
68 		cs_ctx->dgst_len = 64;
69 		break;
70 	default:
71 		rc = -EINVAL;
72 		goto free_n_out;
73 	};
74 
75 	cs = dev_get_priv(dev);
76 
77 	/* get CPTRA SHA lock */
78 	if (readl_poll_timeout(cs->regs + CPTRA_SHA_LOCK, reg, reg == 0, 1000000))
79 		return -EBUSY;
80 
81 	/* zero clear SHA */
82 	writel(CPTRA_SHA_CTRL_ZEROIZE, cs->regs + CPTRA_SHA_CTRL);
83 
84 	/* zero clear length */
85 	writel(0x0, cs->regs + CPTRA_SHA_DLEN);
86 
87 	/* set SHA mode */
88 	reg = readl(cs->regs + CPTRA_SHA_MODE);
89 	reg &= ~(CPTRA_SHA_MODE_SEL);
90 	reg |= FIELD_PREP(CPTRA_SHA_MODE_SEL, mode);
91 	writel(reg, cs->regs + CPTRA_SHA_MODE);
92 
93 	*ctxp = cs_ctx;
94 
95 	return 0;
96 
97 free_n_out:
98 	free(cs_ctx);
99 
100 	return rc;
101 }
102 
cptra_sha_update(struct udevice * dev,void * ctx,const void * ibuf,uint32_t ilen)103 static int cptra_sha_update(struct udevice *dev, void *ctx, const void *ibuf, uint32_t ilen)
104 {
105 	struct cptra_sha *cs;
106 	uint32_t din_be;
107 	uint32_t dlen_sum;
108 	uint8_t *p8;
109 	uint32_t i;
110 
111 	cs = dev_get_priv(dev);
112 
113 	/* update length */
114 	dlen_sum = readl(cs->regs + CPTRA_SHA_DLEN) + ilen;
115 	writel(dlen_sum, cs->regs + CPTRA_SHA_DLEN);
116 
117 	din_be = 0;
118 	for (i = 0, p8 = (uint8_t *)ibuf; i < ilen; ++i) {
119 		if (i && (i % sizeof(din_be) == 0)) {
120 			writel(din_be, cs->regs + CPTRA_SHA_DATAIN);
121 			din_be = 0;
122 		}
123 
124 		din_be <<= 8;
125 		din_be |= p8[i];
126 	}
127 
128 	if (i % sizeof(din_be))
129 		din_be <<= (8 * (sizeof(din_be) - (i % sizeof(din_be))));
130 
131 	writel(din_be, cs->regs + CPTRA_SHA_DATAIN);
132 
133 	return 0;
134 }
135 
cptra_sha_finish(struct udevice * dev,void * ctx,void * obuf)136 static int cptra_sha_finish(struct udevice *dev, void *ctx, void *obuf)
137 {
138 	struct cptra_sha_ctx *cs_ctx;
139 	struct cptra_sha *cs;
140 	uint32_t i, *p32;
141 	uint32_t sts;
142 
143 	cs = dev_get_priv(dev);
144 	cs_ctx = (struct cptra_sha_ctx *)ctx;
145 
146 	/* trigger SHA calculation */
147 	writel(0x1, cs->regs + CPTRA_SHA_EXEC);
148 
149 	/* wait for completion */
150 	while (1) {
151 		sts = readl(cs->regs + CPTRA_SHA_STS);
152 		if (sts & CPTRA_SHA_STS_VLD)
153 			break;
154 	}
155 
156 	/* get the SHA digest in big-endian */
157 	p32 = (uint32_t *)obuf;
158 	for (i = 0; i < (cs_ctx->dgst_len / sizeof(*p32)); ++i, p32++)
159 		*p32 = be32_to_cpu(readl(cs->regs + CPTRA_SHA_DIGEST(i)));
160 
161 	/* release CPTRA SHA lock */
162 	writel(0x1, cs->regs + CPTRA_SHA_LOCK);
163 
164 	free(cs_ctx);
165 
166 	return 0;
167 }
168 
cptra_sha_digest_wd(struct udevice * dev,enum HASH_ALGO algo,const void * ibuf,const uint32_t ilen,void * obuf,uint32_t chunk_sz)169 static int cptra_sha_digest_wd(struct udevice *dev, enum HASH_ALGO algo,
170 			       const void *ibuf, const uint32_t ilen,
171 			       void *obuf, uint32_t chunk_sz)
172 {
173 	const void *cur, *end;
174 	uint32_t chunk;
175 	void *ctx;
176 	int rc;
177 
178 	rc = cptra_sha_init(dev, algo, &ctx);
179 	if (rc)
180 		return rc;
181 
182 	if (IS_ENABLED(CONFIG_HW_WATCHDOG) || CONFIG_IS_ENABLED(WATCHDOG)) {
183 		cur = ibuf;
184 		end = ibuf + ilen;
185 
186 		while (cur < end) {
187 			chunk = end - cur;
188 			if (chunk > chunk_sz)
189 				chunk = chunk_sz;
190 
191 			rc = cptra_sha_update(dev, ctx, cur, chunk);
192 			if (rc)
193 				return rc;
194 
195 			cur += chunk;
196 			schedule();
197 		}
198 	} else {
199 		rc = cptra_sha_update(dev, ctx, ibuf, ilen);
200 		if (rc)
201 			return rc;
202 	}
203 
204 	rc = cptra_sha_finish(dev, ctx, obuf);
205 	if (rc)
206 		return rc;
207 
208 	return 0;
209 }
210 
cptra_sha_digest(struct udevice * dev,enum HASH_ALGO algo,const void * ibuf,const uint32_t ilen,void * obuf)211 static int cptra_sha_digest(struct udevice *dev, enum HASH_ALGO algo,
212 			    const void *ibuf, const uint32_t ilen, void *obuf)
213 {
214 	/* re-use the watchdog version with input length as the chunk_sz */
215 	return cptra_sha_digest_wd(dev, algo, ibuf, ilen, obuf, ilen);
216 }
217 
cptra_sha_probe(struct udevice * dev)218 static int cptra_sha_probe(struct udevice *dev)
219 {
220 	struct cptra_sha *cs = dev_get_priv(dev);
221 
222 	cs->regs = (void *)devfdt_get_addr(dev);
223 	if (cs->regs == (void *)FDT_ADDR_T_NONE) {
224 		debug("cannot map Caliptra SHA ACC registers\n");
225 		return -ENODEV;
226 	}
227 
228 	return 0;
229 }
230 
cptra_sha_remove(struct udevice * dev)231 static int cptra_sha_remove(struct udevice *dev)
232 {
233 	return 0;
234 }
235 
236 static const struct hash_ops cptra_sha_ops = {
237 	.hash_init = cptra_sha_init,
238 	.hash_update = cptra_sha_update,
239 	.hash_finish = cptra_sha_finish,
240 	.hash_digest_wd = cptra_sha_digest_wd,
241 	.hash_digest = cptra_sha_digest,
242 };
243 
244 static const struct udevice_id cptra_sha_ids[] = {
245 	{ .compatible = "aspeed,ast2700-cptra-sha" },
246 	{ }
247 };
248 
249 U_BOOT_DRIVER(aspeed_cptra_sha) = {
250 	.name = "aspeed_cptra_sha",
251 	.id = UCLASS_HASH,
252 	.of_match = cptra_sha_ids,
253 	.ops = &cptra_sha_ops,
254 	.probe = cptra_sha_probe,
255 	.remove	= cptra_sha_remove,
256 	.priv_auto = sizeof(struct cptra_sha),
257 	.flags = DM_FLAG_PRE_RELOC,
258 };
259