]>
Commit | Line | Data |
---|---|---|
af873fce | 1 | // SPDX-License-Identifier: GPL-2.0-only |
b51dbe90 FD |
2 | /* |
3 | * Copyright (C) STMicroelectronics SA 2017 | |
4 | * Author: Fabien Dessenne <fabien.dessenne@st.com> | |
b51dbe90 FD |
5 | */ |
6 | ||
7 | #include <linux/bitrev.h> | |
8 | #include <linux/clk.h> | |
f7aee878 | 9 | #include <linux/crc32poly.h> |
b51dbe90 | 10 | #include <linux/module.h> |
ac316725 | 11 | #include <linux/mod_devicetable.h> |
b51dbe90 | 12 | #include <linux/platform_device.h> |
73463ade | 13 | #include <linux/pm_runtime.h> |
b51dbe90 FD |
14 | |
15 | #include <crypto/internal/hash.h> | |
16 | ||
17 | #include <asm/unaligned.h> | |
18 | ||
19 | #define DRIVER_NAME "stm32-crc32" | |
20 | #define CHKSUM_DIGEST_SIZE 4 | |
21 | #define CHKSUM_BLOCK_SIZE 1 | |
22 | ||
23 | /* Registers */ | |
24 | #define CRC_DR 0x00000000 | |
25 | #define CRC_CR 0x00000008 | |
26 | #define CRC_INIT 0x00000010 | |
27 | #define CRC_POL 0x00000014 | |
28 | ||
29 | /* Registers values */ | |
30 | #define CRC_CR_RESET BIT(0) | |
49c2c082 NT |
31 | #define CRC_CR_REV_IN_WORD (BIT(6) | BIT(5)) |
32 | #define CRC_CR_REV_IN_BYTE BIT(5) | |
33 | #define CRC_CR_REV_OUT BIT(7) | |
a8cc3128 | 34 | #define CRC32C_INIT_DEFAULT 0xFFFFFFFF |
b51dbe90 | 35 | |
73463ade | 36 | #define CRC_AUTOSUSPEND_DELAY 50 |
37 | ||
7795c0ba NT |
38 | static unsigned int burst_size; |
39 | module_param(burst_size, uint, 0644); | |
40 | MODULE_PARM_DESC(burst_size, "Select burst byte size (0 unlimited)"); | |
41 | ||
b51dbe90 FD |
42 | struct stm32_crc { |
43 | struct list_head list; | |
44 | struct device *dev; | |
45 | void __iomem *regs; | |
46 | struct clk *clk; | |
7795c0ba | 47 | spinlock_t lock; |
b51dbe90 FD |
48 | }; |
49 | ||
50 | struct stm32_crc_list { | |
51 | struct list_head dev_list; | |
52 | spinlock_t lock; /* protect dev_list */ | |
53 | }; | |
54 | ||
55 | static struct stm32_crc_list crc_list = { | |
56 | .dev_list = LIST_HEAD_INIT(crc_list.dev_list), | |
57 | .lock = __SPIN_LOCK_UNLOCKED(crc_list.lock), | |
58 | }; | |
59 | ||
60 | struct stm32_crc_ctx { | |
61 | u32 key; | |
62 | u32 poly; | |
63 | }; | |
64 | ||
65 | struct stm32_crc_desc_ctx { | |
66 | u32 partial; /* crc32c: partial in first 4 bytes of that struct */ | |
b51dbe90 FD |
67 | }; |
68 | ||
69 | static int stm32_crc32_cra_init(struct crypto_tfm *tfm) | |
70 | { | |
71 | struct stm32_crc_ctx *mctx = crypto_tfm_ctx(tfm); | |
72 | ||
a8cc3128 | 73 | mctx->key = 0; |
f7aee878 | 74 | mctx->poly = CRC32_POLY_LE; |
b51dbe90 FD |
75 | return 0; |
76 | } | |
77 | ||
78 | static int stm32_crc32c_cra_init(struct crypto_tfm *tfm) | |
79 | { | |
80 | struct stm32_crc_ctx *mctx = crypto_tfm_ctx(tfm); | |
81 | ||
a8cc3128 | 82 | mctx->key = CRC32C_INIT_DEFAULT; |
f7aee878 | 83 | mctx->poly = CRC32C_POLY_LE; |
b51dbe90 FD |
84 | return 0; |
85 | } | |
86 | ||
87 | static int stm32_crc_setkey(struct crypto_shash *tfm, const u8 *key, | |
88 | unsigned int keylen) | |
89 | { | |
90 | struct stm32_crc_ctx *mctx = crypto_shash_ctx(tfm); | |
91 | ||
674f368a | 92 | if (keylen != sizeof(u32)) |
b51dbe90 | 93 | return -EINVAL; |
b51dbe90 FD |
94 | |
95 | mctx->key = get_unaligned_le32(key); | |
96 | return 0; | |
97 | } | |
98 | ||
10b89c43 | 99 | static struct stm32_crc *stm32_crc_get_next_crc(void) |
b51dbe90 | 100 | { |
b51dbe90 FD |
101 | struct stm32_crc *crc; |
102 | ||
103 | spin_lock_bh(&crc_list.lock); | |
49c2c082 | 104 | crc = list_first_entry(&crc_list.dev_list, struct stm32_crc, list); |
10b89c43 NT |
105 | if (crc) |
106 | list_move_tail(&crc->list, &crc_list.dev_list); | |
b51dbe90 FD |
107 | spin_unlock_bh(&crc_list.lock); |
108 | ||
10b89c43 NT |
109 | return crc; |
110 | } | |
111 | ||
112 | static int stm32_crc_init(struct shash_desc *desc) | |
113 | { | |
114 | struct stm32_crc_desc_ctx *ctx = shash_desc_ctx(desc); | |
115 | struct stm32_crc_ctx *mctx = crypto_shash_ctx(desc->tfm); | |
116 | struct stm32_crc *crc; | |
7795c0ba | 117 | unsigned long flags; |
10b89c43 NT |
118 | |
119 | crc = stm32_crc_get_next_crc(); | |
120 | if (!crc) | |
121 | return -ENODEV; | |
122 | ||
49c2c082 | 123 | pm_runtime_get_sync(crc->dev); |
73463ade | 124 | |
7795c0ba NT |
125 | spin_lock_irqsave(&crc->lock, flags); |
126 | ||
b51dbe90 | 127 | /* Reset, set key, poly and configure in bit reverse mode */ |
49c2c082 NT |
128 | writel_relaxed(bitrev32(mctx->key), crc->regs + CRC_INIT); |
129 | writel_relaxed(bitrev32(mctx->poly), crc->regs + CRC_POL); | |
130 | writel_relaxed(CRC_CR_RESET | CRC_CR_REV_IN_WORD | CRC_CR_REV_OUT, | |
131 | crc->regs + CRC_CR); | |
b51dbe90 FD |
132 | |
133 | /* Store partial result */ | |
49c2c082 | 134 | ctx->partial = readl_relaxed(crc->regs + CRC_DR); |
b51dbe90 | 135 | |
7795c0ba NT |
136 | spin_unlock_irqrestore(&crc->lock, flags); |
137 | ||
49c2c082 NT |
138 | pm_runtime_mark_last_busy(crc->dev); |
139 | pm_runtime_put_autosuspend(crc->dev); | |
73463ade | 140 | |
b51dbe90 FD |
141 | return 0; |
142 | } | |
143 | ||
7795c0ba NT |
144 | static int burst_update(struct shash_desc *desc, const u8 *d8, |
145 | size_t length) | |
b51dbe90 FD |
146 | { |
147 | struct stm32_crc_desc_ctx *ctx = shash_desc_ctx(desc); | |
49c2c082 NT |
148 | struct stm32_crc_ctx *mctx = crypto_shash_ctx(desc->tfm); |
149 | struct stm32_crc *crc; | |
7795c0ba | 150 | unsigned long flags; |
49c2c082 | 151 | |
10b89c43 NT |
152 | crc = stm32_crc_get_next_crc(); |
153 | if (!crc) | |
154 | return -ENODEV; | |
b51dbe90 | 155 | |
73463ade | 156 | pm_runtime_get_sync(crc->dev); |
157 | ||
7795c0ba NT |
158 | spin_lock_irqsave(&crc->lock, flags); |
159 | ||
49c2c082 NT |
160 | /* |
161 | * Restore previously calculated CRC for this context as init value | |
162 | * Restore polynomial configuration | |
163 | * Configure in register for word input data, | |
164 | * Configure out register in reversed bit mode data. | |
165 | */ | |
166 | writel_relaxed(bitrev32(ctx->partial), crc->regs + CRC_INIT); | |
167 | writel_relaxed(bitrev32(mctx->poly), crc->regs + CRC_POL); | |
168 | writel_relaxed(CRC_CR_RESET | CRC_CR_REV_IN_WORD | CRC_CR_REV_OUT, | |
169 | crc->regs + CRC_CR); | |
170 | ||
171 | if (d8 != PTR_ALIGN(d8, sizeof(u32))) { | |
172 | /* Configure for byte data */ | |
173 | writel_relaxed(CRC_CR_REV_IN_BYTE | CRC_CR_REV_OUT, | |
174 | crc->regs + CRC_CR); | |
175 | while (d8 != PTR_ALIGN(d8, sizeof(u32)) && length) { | |
176 | writeb_relaxed(*d8++, crc->regs + CRC_DR); | |
b51dbe90 FD |
177 | length--; |
178 | } | |
49c2c082 NT |
179 | /* Configure for word data */ |
180 | writel_relaxed(CRC_CR_REV_IN_WORD | CRC_CR_REV_OUT, | |
181 | crc->regs + CRC_CR); | |
b51dbe90 FD |
182 | } |
183 | ||
49c2c082 NT |
184 | for (; length >= sizeof(u32); d8 += sizeof(u32), length -= sizeof(u32)) |
185 | writel_relaxed(*((u32 *)d8), crc->regs + CRC_DR); | |
186 | ||
187 | if (length) { | |
188 | /* Configure for byte data */ | |
189 | writel_relaxed(CRC_CR_REV_IN_BYTE | CRC_CR_REV_OUT, | |
190 | crc->regs + CRC_CR); | |
191 | while (length--) | |
192 | writeb_relaxed(*d8++, crc->regs + CRC_DR); | |
193 | } | |
b51dbe90 FD |
194 | |
195 | /* Store partial result */ | |
39177519 | 196 | ctx->partial = readl_relaxed(crc->regs + CRC_DR); |
b51dbe90 | 197 | |
7795c0ba NT |
198 | spin_unlock_irqrestore(&crc->lock, flags); |
199 | ||
73463ade | 200 | pm_runtime_mark_last_busy(crc->dev); |
201 | pm_runtime_put_autosuspend(crc->dev); | |
202 | ||
b51dbe90 FD |
203 | return 0; |
204 | } | |
205 | ||
7795c0ba NT |
206 | static int stm32_crc_update(struct shash_desc *desc, const u8 *d8, |
207 | unsigned int length) | |
208 | { | |
209 | const unsigned int burst_sz = burst_size; | |
210 | unsigned int rem_sz; | |
211 | const u8 *cur; | |
212 | size_t size; | |
213 | int ret; | |
214 | ||
215 | if (!burst_sz) | |
216 | return burst_update(desc, d8, length); | |
217 | ||
218 | /* Digest first bytes not 32bit aligned at first pass in the loop */ | |
219 | size = min(length, | |
220 | burst_sz + (unsigned int)d8 - ALIGN_DOWN((unsigned int)d8, | |
221 | sizeof(u32))); | |
222 | for (rem_sz = length, cur = d8; rem_sz; | |
223 | rem_sz -= size, cur += size, size = min(rem_sz, burst_sz)) { | |
224 | ret = burst_update(desc, cur, size); | |
225 | if (ret) | |
226 | return ret; | |
227 | } | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
b51dbe90 FD |
232 | static int stm32_crc_final(struct shash_desc *desc, u8 *out) |
233 | { | |
234 | struct stm32_crc_desc_ctx *ctx = shash_desc_ctx(desc); | |
235 | struct stm32_crc_ctx *mctx = crypto_shash_ctx(desc->tfm); | |
236 | ||
237 | /* Send computed CRC */ | |
f7aee878 | 238 | put_unaligned_le32(mctx->poly == CRC32C_POLY_LE ? |
b51dbe90 FD |
239 | ~ctx->partial : ctx->partial, out); |
240 | ||
241 | return 0; | |
242 | } | |
243 | ||
244 | static int stm32_crc_finup(struct shash_desc *desc, const u8 *data, | |
245 | unsigned int length, u8 *out) | |
246 | { | |
247 | return stm32_crc_update(desc, data, length) ?: | |
248 | stm32_crc_final(desc, out); | |
249 | } | |
250 | ||
251 | static int stm32_crc_digest(struct shash_desc *desc, const u8 *data, | |
252 | unsigned int length, u8 *out) | |
253 | { | |
254 | return stm32_crc_init(desc) ?: stm32_crc_finup(desc, data, length, out); | |
255 | } | |
256 | ||
10b89c43 NT |
257 | static unsigned int refcnt; |
258 | static DEFINE_MUTEX(refcnt_lock); | |
b51dbe90 FD |
259 | static struct shash_alg algs[] = { |
260 | /* CRC-32 */ | |
261 | { | |
262 | .setkey = stm32_crc_setkey, | |
263 | .init = stm32_crc_init, | |
264 | .update = stm32_crc_update, | |
265 | .final = stm32_crc_final, | |
266 | .finup = stm32_crc_finup, | |
267 | .digest = stm32_crc_digest, | |
268 | .descsize = sizeof(struct stm32_crc_desc_ctx), | |
269 | .digestsize = CHKSUM_DIGEST_SIZE, | |
270 | .base = { | |
271 | .cra_name = "crc32", | |
272 | .cra_driver_name = DRIVER_NAME, | |
273 | .cra_priority = 200, | |
a208fa8f | 274 | .cra_flags = CRYPTO_ALG_OPTIONAL_KEY, |
b51dbe90 FD |
275 | .cra_blocksize = CHKSUM_BLOCK_SIZE, |
276 | .cra_alignmask = 3, | |
277 | .cra_ctxsize = sizeof(struct stm32_crc_ctx), | |
278 | .cra_module = THIS_MODULE, | |
279 | .cra_init = stm32_crc32_cra_init, | |
280 | } | |
281 | }, | |
282 | /* CRC-32Castagnoli */ | |
283 | { | |
284 | .setkey = stm32_crc_setkey, | |
285 | .init = stm32_crc_init, | |
286 | .update = stm32_crc_update, | |
287 | .final = stm32_crc_final, | |
288 | .finup = stm32_crc_finup, | |
289 | .digest = stm32_crc_digest, | |
290 | .descsize = sizeof(struct stm32_crc_desc_ctx), | |
291 | .digestsize = CHKSUM_DIGEST_SIZE, | |
292 | .base = { | |
293 | .cra_name = "crc32c", | |
294 | .cra_driver_name = DRIVER_NAME, | |
295 | .cra_priority = 200, | |
a208fa8f | 296 | .cra_flags = CRYPTO_ALG_OPTIONAL_KEY, |
b51dbe90 FD |
297 | .cra_blocksize = CHKSUM_BLOCK_SIZE, |
298 | .cra_alignmask = 3, | |
299 | .cra_ctxsize = sizeof(struct stm32_crc_ctx), | |
300 | .cra_module = THIS_MODULE, | |
301 | .cra_init = stm32_crc32c_cra_init, | |
302 | } | |
303 | } | |
304 | }; | |
305 | ||
306 | static int stm32_crc_probe(struct platform_device *pdev) | |
307 | { | |
308 | struct device *dev = &pdev->dev; | |
309 | struct stm32_crc *crc; | |
b51dbe90 FD |
310 | int ret; |
311 | ||
312 | crc = devm_kzalloc(dev, sizeof(*crc), GFP_KERNEL); | |
313 | if (!crc) | |
314 | return -ENOMEM; | |
315 | ||
316 | crc->dev = dev; | |
317 | ||
473b4d99 | 318 | crc->regs = devm_platform_ioremap_resource(pdev, 0); |
b51dbe90 FD |
319 | if (IS_ERR(crc->regs)) { |
320 | dev_err(dev, "Cannot map CRC IO\n"); | |
321 | return PTR_ERR(crc->regs); | |
322 | } | |
323 | ||
324 | crc->clk = devm_clk_get(dev, NULL); | |
325 | if (IS_ERR(crc->clk)) { | |
326 | dev_err(dev, "Could not get clock\n"); | |
327 | return PTR_ERR(crc->clk); | |
328 | } | |
329 | ||
330 | ret = clk_prepare_enable(crc->clk); | |
331 | if (ret) { | |
332 | dev_err(crc->dev, "Failed to enable clock\n"); | |
333 | return ret; | |
334 | } | |
335 | ||
73463ade | 336 | pm_runtime_set_autosuspend_delay(dev, CRC_AUTOSUSPEND_DELAY); |
337 | pm_runtime_use_autosuspend(dev); | |
338 | ||
339 | pm_runtime_get_noresume(dev); | |
340 | pm_runtime_set_active(dev); | |
100f84be | 341 | pm_runtime_irq_safe(dev); |
73463ade | 342 | pm_runtime_enable(dev); |
343 | ||
7795c0ba NT |
344 | spin_lock_init(&crc->lock); |
345 | ||
b51dbe90 FD |
346 | platform_set_drvdata(pdev, crc); |
347 | ||
348 | spin_lock(&crc_list.lock); | |
349 | list_add(&crc->list, &crc_list.dev_list); | |
350 | spin_unlock(&crc_list.lock); | |
351 | ||
10b89c43 NT |
352 | mutex_lock(&refcnt_lock); |
353 | if (!refcnt) { | |
354 | ret = crypto_register_shashes(algs, ARRAY_SIZE(algs)); | |
355 | if (ret) { | |
356 | mutex_unlock(&refcnt_lock); | |
357 | dev_err(dev, "Failed to register\n"); | |
358 | clk_disable_unprepare(crc->clk); | |
359 | return ret; | |
360 | } | |
b51dbe90 | 361 | } |
10b89c43 NT |
362 | refcnt++; |
363 | mutex_unlock(&refcnt_lock); | |
b51dbe90 FD |
364 | |
365 | dev_info(dev, "Initialized\n"); | |
366 | ||
73463ade | 367 | pm_runtime_put_sync(dev); |
368 | ||
b51dbe90 FD |
369 | return 0; |
370 | } | |
371 | ||
372 | static int stm32_crc_remove(struct platform_device *pdev) | |
373 | { | |
374 | struct stm32_crc *crc = platform_get_drvdata(pdev); | |
73463ade | 375 | int ret = pm_runtime_get_sync(crc->dev); |
376 | ||
377 | if (ret < 0) | |
378 | return ret; | |
b51dbe90 FD |
379 | |
380 | spin_lock(&crc_list.lock); | |
381 | list_del(&crc->list); | |
382 | spin_unlock(&crc_list.lock); | |
383 | ||
10b89c43 NT |
384 | mutex_lock(&refcnt_lock); |
385 | if (!--refcnt) | |
386 | crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); | |
387 | mutex_unlock(&refcnt_lock); | |
b51dbe90 | 388 | |
73463ade | 389 | pm_runtime_disable(crc->dev); |
390 | pm_runtime_put_noidle(crc->dev); | |
391 | ||
b51dbe90 FD |
392 | clk_disable_unprepare(crc->clk); |
393 | ||
394 | return 0; | |
395 | } | |
396 | ||
100f84be | 397 | static int __maybe_unused stm32_crc_suspend(struct device *dev) |
73463ade | 398 | { |
399 | struct stm32_crc *crc = dev_get_drvdata(dev); | |
100f84be | 400 | int ret; |
73463ade | 401 | |
100f84be NT |
402 | ret = pm_runtime_force_suspend(dev); |
403 | if (ret) | |
404 | return ret; | |
405 | ||
406 | clk_unprepare(crc->clk); | |
73463ade | 407 | |
408 | return 0; | |
409 | } | |
410 | ||
100f84be | 411 | static int __maybe_unused stm32_crc_resume(struct device *dev) |
73463ade | 412 | { |
413 | struct stm32_crc *crc = dev_get_drvdata(dev); | |
414 | int ret; | |
415 | ||
100f84be | 416 | ret = clk_prepare(crc->clk); |
73463ade | 417 | if (ret) { |
100f84be NT |
418 | dev_err(crc->dev, "Failed to prepare clock\n"); |
419 | return ret; | |
420 | } | |
421 | ||
422 | return pm_runtime_force_resume(dev); | |
423 | } | |
424 | ||
425 | static int __maybe_unused stm32_crc_runtime_suspend(struct device *dev) | |
426 | { | |
427 | struct stm32_crc *crc = dev_get_drvdata(dev); | |
428 | ||
429 | clk_disable(crc->clk); | |
430 | ||
431 | return 0; | |
432 | } | |
433 | ||
434 | static int __maybe_unused stm32_crc_runtime_resume(struct device *dev) | |
435 | { | |
436 | struct stm32_crc *crc = dev_get_drvdata(dev); | |
437 | int ret; | |
438 | ||
439 | ret = clk_enable(crc->clk); | |
440 | if (ret) { | |
441 | dev_err(crc->dev, "Failed to enable clock\n"); | |
73463ade | 442 | return ret; |
443 | } | |
444 | ||
445 | return 0; | |
446 | } | |
73463ade | 447 | |
448 | static const struct dev_pm_ops stm32_crc_pm_ops = { | |
100f84be NT |
449 | SET_SYSTEM_SLEEP_PM_OPS(stm32_crc_suspend, |
450 | stm32_crc_resume) | |
73463ade | 451 | SET_RUNTIME_PM_OPS(stm32_crc_runtime_suspend, |
452 | stm32_crc_runtime_resume, NULL) | |
453 | }; | |
454 | ||
b51dbe90 FD |
455 | static const struct of_device_id stm32_dt_ids[] = { |
456 | { .compatible = "st,stm32f7-crc", }, | |
457 | {}, | |
458 | }; | |
929562b1 | 459 | MODULE_DEVICE_TABLE(of, stm32_dt_ids); |
b51dbe90 FD |
460 | |
461 | static struct platform_driver stm32_crc_driver = { | |
462 | .probe = stm32_crc_probe, | |
463 | .remove = stm32_crc_remove, | |
464 | .driver = { | |
465 | .name = DRIVER_NAME, | |
73463ade | 466 | .pm = &stm32_crc_pm_ops, |
b51dbe90 FD |
467 | .of_match_table = stm32_dt_ids, |
468 | }, | |
469 | }; | |
470 | ||
471 | module_platform_driver(stm32_crc_driver); | |
472 | ||
473 | MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>"); | |
474 | MODULE_DESCRIPTION("STMicrolectronics STM32 CRC32 hardware driver"); | |
475 | MODULE_LICENSE("GPL"); |