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