123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (C) STMicroelectronics SA 2017
- * Author: Fabien Dessenne <[email protected]>
- */
- #include <linux/bitrev.h>
- #include <linux/clk.h>
- #include <linux/crc32.h>
- #include <linux/crc32poly.h>
- #include <linux/io.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/mod_devicetable.h>
- #include <linux/platform_device.h>
- #include <linux/pm_runtime.h>
- #include <crypto/internal/hash.h>
- #include <asm/unaligned.h>
- #define DRIVER_NAME "stm32-crc32"
- #define CHKSUM_DIGEST_SIZE 4
- #define CHKSUM_BLOCK_SIZE 1
- /* Registers */
- #define CRC_DR 0x00000000
- #define CRC_CR 0x00000008
- #define CRC_INIT 0x00000010
- #define CRC_POL 0x00000014
- /* Registers values */
- #define CRC_CR_RESET BIT(0)
- #define CRC_CR_REV_IN_WORD (BIT(6) | BIT(5))
- #define CRC_CR_REV_IN_BYTE BIT(5)
- #define CRC_CR_REV_OUT BIT(7)
- #define CRC32C_INIT_DEFAULT 0xFFFFFFFF
- #define CRC_AUTOSUSPEND_DELAY 50
- static unsigned int burst_size;
- module_param(burst_size, uint, 0644);
- MODULE_PARM_DESC(burst_size, "Select burst byte size (0 unlimited)");
- struct stm32_crc {
- struct list_head list;
- struct device *dev;
- void __iomem *regs;
- struct clk *clk;
- spinlock_t lock;
- };
- struct stm32_crc_list {
- struct list_head dev_list;
- spinlock_t lock; /* protect dev_list */
- };
- static struct stm32_crc_list crc_list = {
- .dev_list = LIST_HEAD_INIT(crc_list.dev_list),
- .lock = __SPIN_LOCK_UNLOCKED(crc_list.lock),
- };
- struct stm32_crc_ctx {
- u32 key;
- u32 poly;
- };
- struct stm32_crc_desc_ctx {
- u32 partial; /* crc32c: partial in first 4 bytes of that struct */
- };
- static int stm32_crc32_cra_init(struct crypto_tfm *tfm)
- {
- struct stm32_crc_ctx *mctx = crypto_tfm_ctx(tfm);
- mctx->key = 0;
- mctx->poly = CRC32_POLY_LE;
- return 0;
- }
- static int stm32_crc32c_cra_init(struct crypto_tfm *tfm)
- {
- struct stm32_crc_ctx *mctx = crypto_tfm_ctx(tfm);
- mctx->key = CRC32C_INIT_DEFAULT;
- mctx->poly = CRC32C_POLY_LE;
- return 0;
- }
- static int stm32_crc_setkey(struct crypto_shash *tfm, const u8 *key,
- unsigned int keylen)
- {
- struct stm32_crc_ctx *mctx = crypto_shash_ctx(tfm);
- if (keylen != sizeof(u32))
- return -EINVAL;
- mctx->key = get_unaligned_le32(key);
- return 0;
- }
- static struct stm32_crc *stm32_crc_get_next_crc(void)
- {
- struct stm32_crc *crc;
- spin_lock_bh(&crc_list.lock);
- crc = list_first_entry(&crc_list.dev_list, struct stm32_crc, list);
- if (crc)
- list_move_tail(&crc->list, &crc_list.dev_list);
- spin_unlock_bh(&crc_list.lock);
- return crc;
- }
- static int stm32_crc_init(struct shash_desc *desc)
- {
- struct stm32_crc_desc_ctx *ctx = shash_desc_ctx(desc);
- struct stm32_crc_ctx *mctx = crypto_shash_ctx(desc->tfm);
- struct stm32_crc *crc;
- unsigned long flags;
- crc = stm32_crc_get_next_crc();
- if (!crc)
- return -ENODEV;
- pm_runtime_get_sync(crc->dev);
- spin_lock_irqsave(&crc->lock, flags);
- /* Reset, set key, poly and configure in bit reverse mode */
- writel_relaxed(bitrev32(mctx->key), crc->regs + CRC_INIT);
- writel_relaxed(bitrev32(mctx->poly), crc->regs + CRC_POL);
- writel_relaxed(CRC_CR_RESET | CRC_CR_REV_IN_WORD | CRC_CR_REV_OUT,
- crc->regs + CRC_CR);
- /* Store partial result */
- ctx->partial = readl_relaxed(crc->regs + CRC_DR);
- spin_unlock_irqrestore(&crc->lock, flags);
- pm_runtime_mark_last_busy(crc->dev);
- pm_runtime_put_autosuspend(crc->dev);
- return 0;
- }
- static int burst_update(struct shash_desc *desc, const u8 *d8,
- size_t length)
- {
- struct stm32_crc_desc_ctx *ctx = shash_desc_ctx(desc);
- struct stm32_crc_ctx *mctx = crypto_shash_ctx(desc->tfm);
- struct stm32_crc *crc;
- crc = stm32_crc_get_next_crc();
- if (!crc)
- return -ENODEV;
- pm_runtime_get_sync(crc->dev);
- if (!spin_trylock(&crc->lock)) {
- /* Hardware is busy, calculate crc32 by software */
- if (mctx->poly == CRC32_POLY_LE)
- ctx->partial = crc32_le(ctx->partial, d8, length);
- else
- ctx->partial = __crc32c_le(ctx->partial, d8, length);
- goto pm_out;
- }
- /*
- * Restore previously calculated CRC for this context as init value
- * Restore polynomial configuration
- * Configure in register for word input data,
- * Configure out register in reversed bit mode data.
- */
- writel_relaxed(bitrev32(ctx->partial), crc->regs + CRC_INIT);
- writel_relaxed(bitrev32(mctx->poly), crc->regs + CRC_POL);
- writel_relaxed(CRC_CR_RESET | CRC_CR_REV_IN_WORD | CRC_CR_REV_OUT,
- crc->regs + CRC_CR);
- if (d8 != PTR_ALIGN(d8, sizeof(u32))) {
- /* Configure for byte data */
- writel_relaxed(CRC_CR_REV_IN_BYTE | CRC_CR_REV_OUT,
- crc->regs + CRC_CR);
- while (d8 != PTR_ALIGN(d8, sizeof(u32)) && length) {
- writeb_relaxed(*d8++, crc->regs + CRC_DR);
- length--;
- }
- /* Configure for word data */
- writel_relaxed(CRC_CR_REV_IN_WORD | CRC_CR_REV_OUT,
- crc->regs + CRC_CR);
- }
- for (; length >= sizeof(u32); d8 += sizeof(u32), length -= sizeof(u32))
- writel_relaxed(*((u32 *)d8), crc->regs + CRC_DR);
- if (length) {
- /* Configure for byte data */
- writel_relaxed(CRC_CR_REV_IN_BYTE | CRC_CR_REV_OUT,
- crc->regs + CRC_CR);
- while (length--)
- writeb_relaxed(*d8++, crc->regs + CRC_DR);
- }
- /* Store partial result */
- ctx->partial = readl_relaxed(crc->regs + CRC_DR);
- spin_unlock(&crc->lock);
- pm_out:
- pm_runtime_mark_last_busy(crc->dev);
- pm_runtime_put_autosuspend(crc->dev);
- return 0;
- }
- static int stm32_crc_update(struct shash_desc *desc, const u8 *d8,
- unsigned int length)
- {
- const unsigned int burst_sz = burst_size;
- unsigned int rem_sz;
- const u8 *cur;
- size_t size;
- int ret;
- if (!burst_sz)
- return burst_update(desc, d8, length);
- /* Digest first bytes not 32bit aligned at first pass in the loop */
- size = min_t(size_t, length, burst_sz + (size_t)d8 -
- ALIGN_DOWN((size_t)d8, sizeof(u32)));
- for (rem_sz = length, cur = d8; rem_sz;
- rem_sz -= size, cur += size, size = min(rem_sz, burst_sz)) {
- ret = burst_update(desc, cur, size);
- if (ret)
- return ret;
- }
- return 0;
- }
- static int stm32_crc_final(struct shash_desc *desc, u8 *out)
- {
- struct stm32_crc_desc_ctx *ctx = shash_desc_ctx(desc);
- struct stm32_crc_ctx *mctx = crypto_shash_ctx(desc->tfm);
- /* Send computed CRC */
- put_unaligned_le32(mctx->poly == CRC32C_POLY_LE ?
- ~ctx->partial : ctx->partial, out);
- return 0;
- }
- static int stm32_crc_finup(struct shash_desc *desc, const u8 *data,
- unsigned int length, u8 *out)
- {
- return stm32_crc_update(desc, data, length) ?:
- stm32_crc_final(desc, out);
- }
- static int stm32_crc_digest(struct shash_desc *desc, const u8 *data,
- unsigned int length, u8 *out)
- {
- return stm32_crc_init(desc) ?: stm32_crc_finup(desc, data, length, out);
- }
- static unsigned int refcnt;
- static DEFINE_MUTEX(refcnt_lock);
- static struct shash_alg algs[] = {
- /* CRC-32 */
- {
- .setkey = stm32_crc_setkey,
- .init = stm32_crc_init,
- .update = stm32_crc_update,
- .final = stm32_crc_final,
- .finup = stm32_crc_finup,
- .digest = stm32_crc_digest,
- .descsize = sizeof(struct stm32_crc_desc_ctx),
- .digestsize = CHKSUM_DIGEST_SIZE,
- .base = {
- .cra_name = "crc32",
- .cra_driver_name = "stm32-crc32-crc32",
- .cra_priority = 200,
- .cra_flags = CRYPTO_ALG_OPTIONAL_KEY,
- .cra_blocksize = CHKSUM_BLOCK_SIZE,
- .cra_alignmask = 3,
- .cra_ctxsize = sizeof(struct stm32_crc_ctx),
- .cra_module = THIS_MODULE,
- .cra_init = stm32_crc32_cra_init,
- }
- },
- /* CRC-32Castagnoli */
- {
- .setkey = stm32_crc_setkey,
- .init = stm32_crc_init,
- .update = stm32_crc_update,
- .final = stm32_crc_final,
- .finup = stm32_crc_finup,
- .digest = stm32_crc_digest,
- .descsize = sizeof(struct stm32_crc_desc_ctx),
- .digestsize = CHKSUM_DIGEST_SIZE,
- .base = {
- .cra_name = "crc32c",
- .cra_driver_name = "stm32-crc32-crc32c",
- .cra_priority = 200,
- .cra_flags = CRYPTO_ALG_OPTIONAL_KEY,
- .cra_blocksize = CHKSUM_BLOCK_SIZE,
- .cra_alignmask = 3,
- .cra_ctxsize = sizeof(struct stm32_crc_ctx),
- .cra_module = THIS_MODULE,
- .cra_init = stm32_crc32c_cra_init,
- }
- }
- };
- static int stm32_crc_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct stm32_crc *crc;
- int ret;
- crc = devm_kzalloc(dev, sizeof(*crc), GFP_KERNEL);
- if (!crc)
- return -ENOMEM;
- crc->dev = dev;
- crc->regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(crc->regs)) {
- dev_err(dev, "Cannot map CRC IO\n");
- return PTR_ERR(crc->regs);
- }
- crc->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(crc->clk)) {
- dev_err(dev, "Could not get clock\n");
- return PTR_ERR(crc->clk);
- }
- ret = clk_prepare_enable(crc->clk);
- if (ret) {
- dev_err(crc->dev, "Failed to enable clock\n");
- return ret;
- }
- pm_runtime_set_autosuspend_delay(dev, CRC_AUTOSUSPEND_DELAY);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_get_noresume(dev);
- pm_runtime_set_active(dev);
- pm_runtime_irq_safe(dev);
- pm_runtime_enable(dev);
- spin_lock_init(&crc->lock);
- platform_set_drvdata(pdev, crc);
- spin_lock(&crc_list.lock);
- list_add(&crc->list, &crc_list.dev_list);
- spin_unlock(&crc_list.lock);
- mutex_lock(&refcnt_lock);
- if (!refcnt) {
- ret = crypto_register_shashes(algs, ARRAY_SIZE(algs));
- if (ret) {
- mutex_unlock(&refcnt_lock);
- dev_err(dev, "Failed to register\n");
- clk_disable_unprepare(crc->clk);
- return ret;
- }
- }
- refcnt++;
- mutex_unlock(&refcnt_lock);
- dev_info(dev, "Initialized\n");
- pm_runtime_put_sync(dev);
- return 0;
- }
- static int stm32_crc_remove(struct platform_device *pdev)
- {
- struct stm32_crc *crc = platform_get_drvdata(pdev);
- int ret = pm_runtime_get_sync(crc->dev);
- if (ret < 0) {
- pm_runtime_put_noidle(crc->dev);
- return ret;
- }
- spin_lock(&crc_list.lock);
- list_del(&crc->list);
- spin_unlock(&crc_list.lock);
- mutex_lock(&refcnt_lock);
- if (!--refcnt)
- crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
- mutex_unlock(&refcnt_lock);
- pm_runtime_disable(crc->dev);
- pm_runtime_put_noidle(crc->dev);
- clk_disable_unprepare(crc->clk);
- return 0;
- }
- static int __maybe_unused stm32_crc_suspend(struct device *dev)
- {
- struct stm32_crc *crc = dev_get_drvdata(dev);
- int ret;
- ret = pm_runtime_force_suspend(dev);
- if (ret)
- return ret;
- clk_unprepare(crc->clk);
- return 0;
- }
- static int __maybe_unused stm32_crc_resume(struct device *dev)
- {
- struct stm32_crc *crc = dev_get_drvdata(dev);
- int ret;
- ret = clk_prepare(crc->clk);
- if (ret) {
- dev_err(crc->dev, "Failed to prepare clock\n");
- return ret;
- }
- return pm_runtime_force_resume(dev);
- }
- static int __maybe_unused stm32_crc_runtime_suspend(struct device *dev)
- {
- struct stm32_crc *crc = dev_get_drvdata(dev);
- clk_disable(crc->clk);
- return 0;
- }
- static int __maybe_unused stm32_crc_runtime_resume(struct device *dev)
- {
- struct stm32_crc *crc = dev_get_drvdata(dev);
- int ret;
- ret = clk_enable(crc->clk);
- if (ret) {
- dev_err(crc->dev, "Failed to enable clock\n");
- return ret;
- }
- return 0;
- }
- static const struct dev_pm_ops stm32_crc_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(stm32_crc_suspend,
- stm32_crc_resume)
- SET_RUNTIME_PM_OPS(stm32_crc_runtime_suspend,
- stm32_crc_runtime_resume, NULL)
- };
- static const struct of_device_id stm32_dt_ids[] = {
- { .compatible = "st,stm32f7-crc", },
- {},
- };
- MODULE_DEVICE_TABLE(of, stm32_dt_ids);
- static struct platform_driver stm32_crc_driver = {
- .probe = stm32_crc_probe,
- .remove = stm32_crc_remove,
- .driver = {
- .name = DRIVER_NAME,
- .pm = &stm32_crc_pm_ops,
- .of_match_table = stm32_dt_ids,
- },
- };
- module_platform_driver(stm32_crc_driver);
- MODULE_AUTHOR("Fabien Dessenne <[email protected]>");
- MODULE_DESCRIPTION("STMicrolectronics STM32 CRC32 hardware driver");
- MODULE_LICENSE("GPL");
|