]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
mips/crc32: expose CRC32 functions through lib
authorEric Biggers <ebiggers@google.com>
Mon, 2 Dec 2024 01:08:32 +0000 (17:08 -0800)
committerEric Biggers <ebiggers@google.com>
Mon, 2 Dec 2024 01:23:01 +0000 (17:23 -0800)
Move the mips CRC32 assembly code into the lib directory and wire it up
to the library interface.  This allows it to be used without going
through the crypto API.  It remains usable via the crypto API too via
the shash algorithms that use the library interface.  Thus all the
arch-specific "shash" code becomes unnecessary and is removed.

Note: to see the diff from arch/mips/crypto/crc32-mips.c to
arch/mips/lib/crc32-mips.c, view this commit with 'git show -M10'.

Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20241202010844.144356-8-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
arch/mips/Kconfig
arch/mips/configs/eyeq5_defconfig
arch/mips/configs/eyeq6_defconfig
arch/mips/configs/generic/32r6.config
arch/mips/configs/generic/64r6.config
arch/mips/crypto/Kconfig
arch/mips/crypto/Makefile
arch/mips/crypto/crc32-mips.c [deleted file]
arch/mips/lib/Makefile
arch/mips/lib/crc32-mips.c [new file with mode: 0644]

index 467b10f4361aeb7aad0121f334eaa5d23351010c..a33f05e1ad6d31fc8e2bf16c0d4dc588f0110cc6 100644 (file)
@@ -1995,11 +1995,11 @@ config CPU_MIPSR5
 config CPU_MIPSR6
        bool
        default y if CPU_MIPS32_R6 || CPU_MIPS64_R6
+       select ARCH_HAS_CRC32
        select CPU_HAS_RIXI
        select CPU_HAS_DIEI if !CPU_DIEI_BROKEN
        select HAVE_ARCH_BITREVERSE
        select MIPS_ASID_BITS_VARIABLE
-       select MIPS_CRC_SUPPORT
        select MIPS_SPRAM
 
 config TARGET_ISA_REV
@@ -2475,9 +2475,6 @@ config MIPS_ASID_BITS
 config MIPS_ASID_BITS_VARIABLE
        bool
 
-config MIPS_CRC_SUPPORT
-       bool
-
 # R4600 erratum.  Due to the lack of errata information the exact
 # technical details aren't known.  I've experimentally found that disabling
 # interrupts during indexed I-cache flushes seems to be sufficient to deal
index ae9a09b16e40b07151ab4cc7d004bbabb4dd5405..ff7af5dc6d9d353cd84a5532c6f1de43c6983dfd 100644 (file)
@@ -99,7 +99,6 @@ CONFIG_NFS_V4=y
 CONFIG_NFS_V4_1=y
 CONFIG_NFS_V4_2=y
 CONFIG_ROOT_NFS=y
-CONFIG_CRYPTO_CRC32_MIPS=y
 CONFIG_FRAME_WARN=1024
 CONFIG_DEBUG_FS=y
 # CONFIG_RCU_TRACE is not set
index 6597d5e88b335cceb34c499acdabdc1193246e94..0afbb45a78e8e948c713914fccbe0f988797bc0b 100644 (file)
@@ -102,7 +102,6 @@ CONFIG_NFS_V4=y
 CONFIG_NFS_V4_1=y
 CONFIG_NFS_V4_2=y
 CONFIG_ROOT_NFS=y
-CONFIG_CRYPTO_CRC32_MIPS=y
 CONFIG_FRAME_WARN=1024
 CONFIG_DEBUG_FS=y
 # CONFIG_RCU_TRACE is not set
index 1a5d5ea4ab2b5139ef841b7af9038f965909b3bd..ca606e71f4d02611f2b1989013035c1c364d8610 100644 (file)
@@ -1,4 +1,2 @@
 CONFIG_CPU_MIPS32_R6=y
 CONFIG_HIGHMEM=y
-
-CONFIG_CRYPTO_CRC32_MIPS=y
index 63b4e95f303ded99b556d9c5f9b8ca38df5f7618..23a3009149570584a112900966b7a40c58e79553 100644 (file)
@@ -4,5 +4,4 @@ CONFIG_MIPS32_O32=y
 CONFIG_MIPS32_N32=y
 
 CONFIG_CPU_HAS_MSA=y
-CONFIG_CRYPTO_CRC32_MIPS=y
 CONFIG_VIRTUALIZATION=y
index 9003a5c1e879fa78e8d264db1a236824cde53aa8..7decd40c4e204f5acabc1691f58afbee9f404cfd 100644 (file)
@@ -2,15 +2,6 @@
 
 menu "Accelerated Cryptographic Algorithms for CPU (mips)"
 
-config CRYPTO_CRC32_MIPS
-       tristate "CRC32c and CRC32"
-       depends on MIPS_CRC_SUPPORT
-       select CRYPTO_HASH
-       help
-         CRC32c and CRC32 CRC algorithms
-
-         Architecture: mips
-
 config CRYPTO_POLY1305_MIPS
        tristate "Hash functions: Poly1305"
        depends on MIPS
index 5e4105cccf9fa9dde2b3f495b89156e3700c04de..fddc8828141238f291ec013821a63e105e56bb5f 100644 (file)
@@ -3,8 +3,6 @@
 # Makefile for MIPS crypto files..
 #
 
-obj-$(CONFIG_CRYPTO_CRC32_MIPS) += crc32-mips.o
-
 obj-$(CONFIG_CRYPTO_CHACHA_MIPS) += chacha-mips.o
 chacha-mips-y := chacha-core.o chacha-glue.o
 AFLAGS_chacha-core.o += -O2 # needed to fill branch delay slots
diff --git a/arch/mips/crypto/crc32-mips.c b/arch/mips/crypto/crc32-mips.c
deleted file mode 100644 (file)
index 90eacf0..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * crc32-mips.c - CRC32 and CRC32C using optional MIPSr6 instructions
- *
- * Module based on arm64/crypto/crc32-arm.c
- *
- * Copyright (C) 2014 Linaro Ltd <yazen.ghannam@linaro.org>
- * Copyright (C) 2018 MIPS Tech, LLC
- */
-
-#include <linux/cpufeature.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <asm/mipsregs.h>
-#include <linux/unaligned.h>
-
-#include <crypto/internal/hash.h>
-
-enum crc_op_size {
-       b, h, w, d,
-};
-
-enum crc_type {
-       crc32,
-       crc32c,
-};
-
-#ifndef TOOLCHAIN_SUPPORTS_CRC
-#define _ASM_SET_CRC(OP, SZ, TYPE)                                       \
-_ASM_MACRO_3R(OP, rt, rs, rt2,                                           \
-       ".ifnc  \\rt, \\rt2\n\t"                                          \
-       ".error \"invalid operands \\\"" #OP " \\rt,\\rs,\\rt2\\\"\"\n\t" \
-       ".endif\n\t"                                                      \
-       _ASM_INSN_IF_MIPS(0x7c00000f | (__rt << 16) | (__rs << 21) |      \
-                         ((SZ) <<  6) | ((TYPE) << 8))                   \
-       _ASM_INSN32_IF_MM(0x00000030 | (__rs << 16) | (__rt << 21) |      \
-                         ((SZ) << 14) | ((TYPE) << 3)))
-#define _ASM_UNSET_CRC(op, SZ, TYPE) ".purgem " #op "\n\t"
-#else /* !TOOLCHAIN_SUPPORTS_CRC */
-#define _ASM_SET_CRC(op, SZ, TYPE) ".set\tcrc\n\t"
-#define _ASM_UNSET_CRC(op, SZ, TYPE)
-#endif
-
-#define __CRC32(crc, value, op, SZ, TYPE)              \
-do {                                                   \
-       __asm__ __volatile__(                           \
-               ".set   push\n\t"                       \
-               _ASM_SET_CRC(op, SZ, TYPE)              \
-               #op "   %0, %1, %0\n\t"                 \
-               _ASM_UNSET_CRC(op, SZ, TYPE)            \
-               ".set   pop"                            \
-               : "+r" (crc)                            \
-               : "r" (value));                         \
-} while (0)
-
-#define _CRC32_crc32b(crc, value)      __CRC32(crc, value, crc32b, 0, 0)
-#define _CRC32_crc32h(crc, value)      __CRC32(crc, value, crc32h, 1, 0)
-#define _CRC32_crc32w(crc, value)      __CRC32(crc, value, crc32w, 2, 0)
-#define _CRC32_crc32d(crc, value)      __CRC32(crc, value, crc32d, 3, 0)
-#define _CRC32_crc32cb(crc, value)     __CRC32(crc, value, crc32cb, 0, 1)
-#define _CRC32_crc32ch(crc, value)     __CRC32(crc, value, crc32ch, 1, 1)
-#define _CRC32_crc32cw(crc, value)     __CRC32(crc, value, crc32cw, 2, 1)
-#define _CRC32_crc32cd(crc, value)     __CRC32(crc, value, crc32cd, 3, 1)
-
-#define _CRC32(crc, value, size, op) \
-       _CRC32_##op##size(crc, value)
-
-#define CRC32(crc, value, size) \
-       _CRC32(crc, value, size, crc32)
-
-#define CRC32C(crc, value, size) \
-       _CRC32(crc, value, size, crc32c)
-
-static u32 crc32_mips_le_hw(u32 crc_, const u8 *p, unsigned int len)
-{
-       u32 crc = crc_;
-
-       if (IS_ENABLED(CONFIG_64BIT)) {
-               for (; len >= sizeof(u64); p += sizeof(u64), len -= sizeof(u64)) {
-                       u64 value = get_unaligned_le64(p);
-
-                       CRC32(crc, value, d);
-               }
-
-               if (len & sizeof(u32)) {
-                       u32 value = get_unaligned_le32(p);
-
-                       CRC32(crc, value, w);
-                       p += sizeof(u32);
-               }
-       } else {
-               for (; len >= sizeof(u32); len -= sizeof(u32)) {
-                       u32 value = get_unaligned_le32(p);
-
-                       CRC32(crc, value, w);
-                       p += sizeof(u32);
-               }
-       }
-
-       if (len & sizeof(u16)) {
-               u16 value = get_unaligned_le16(p);
-
-               CRC32(crc, value, h);
-               p += sizeof(u16);
-       }
-
-       if (len & sizeof(u8)) {
-               u8 value = *p++;
-
-               CRC32(crc, value, b);
-       }
-
-       return crc;
-}
-
-static u32 crc32c_mips_le_hw(u32 crc_, const u8 *p, unsigned int len)
-{
-       u32 crc = crc_;
-
-       if (IS_ENABLED(CONFIG_64BIT)) {
-               for (; len >= sizeof(u64); p += sizeof(u64), len -= sizeof(u64)) {
-                       u64 value = get_unaligned_le64(p);
-
-                       CRC32C(crc, value, d);
-               }
-
-               if (len & sizeof(u32)) {
-                       u32 value = get_unaligned_le32(p);
-
-                       CRC32C(crc, value, w);
-                       p += sizeof(u32);
-               }
-       } else {
-               for (; len >= sizeof(u32); len -= sizeof(u32)) {
-                       u32 value = get_unaligned_le32(p);
-
-                       CRC32C(crc, value, w);
-                       p += sizeof(u32);
-               }
-       }
-
-       if (len & sizeof(u16)) {
-               u16 value = get_unaligned_le16(p);
-
-               CRC32C(crc, value, h);
-               p += sizeof(u16);
-       }
-
-       if (len & sizeof(u8)) {
-               u8 value = *p++;
-
-               CRC32C(crc, value, b);
-       }
-       return crc;
-}
-
-#define CHKSUM_BLOCK_SIZE      1
-#define CHKSUM_DIGEST_SIZE     4
-
-struct chksum_ctx {
-       u32 key;
-};
-
-struct chksum_desc_ctx {
-       u32 crc;
-};
-
-static int chksum_init(struct shash_desc *desc)
-{
-       struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
-       struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
-
-       ctx->crc = mctx->key;
-
-       return 0;
-}
-
-/*
- * Setting the seed allows arbitrary accumulators and flexible XOR policy
- * If your algorithm starts with ~0, then XOR with ~0 before you set
- * the seed.
- */
-static int chksum_setkey(struct crypto_shash *tfm, const u8 *key,
-                        unsigned int keylen)
-{
-       struct chksum_ctx *mctx = crypto_shash_ctx(tfm);
-
-       if (keylen != sizeof(mctx->key))
-               return -EINVAL;
-       mctx->key = get_unaligned_le32(key);
-       return 0;
-}
-
-static int chksum_update(struct shash_desc *desc, const u8 *data,
-                        unsigned int length)
-{
-       struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
-
-       ctx->crc = crc32_mips_le_hw(ctx->crc, data, length);
-       return 0;
-}
-
-static int chksumc_update(struct shash_desc *desc, const u8 *data,
-                        unsigned int length)
-{
-       struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
-
-       ctx->crc = crc32c_mips_le_hw(ctx->crc, data, length);
-       return 0;
-}
-
-static int chksum_final(struct shash_desc *desc, u8 *out)
-{
-       struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
-
-       put_unaligned_le32(ctx->crc, out);
-       return 0;
-}
-
-static int chksumc_final(struct shash_desc *desc, u8 *out)
-{
-       struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
-
-       put_unaligned_le32(~ctx->crc, out);
-       return 0;
-}
-
-static int __chksum_finup(u32 crc, const u8 *data, unsigned int len, u8 *out)
-{
-       put_unaligned_le32(crc32_mips_le_hw(crc, data, len), out);
-       return 0;
-}
-
-static int __chksumc_finup(u32 crc, const u8 *data, unsigned int len, u8 *out)
-{
-       put_unaligned_le32(~crc32c_mips_le_hw(crc, data, len), out);
-       return 0;
-}
-
-static int chksum_finup(struct shash_desc *desc, const u8 *data,
-                       unsigned int len, u8 *out)
-{
-       struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
-
-       return __chksum_finup(ctx->crc, data, len, out);
-}
-
-static int chksumc_finup(struct shash_desc *desc, const u8 *data,
-                       unsigned int len, u8 *out)
-{
-       struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
-
-       return __chksumc_finup(ctx->crc, data, len, out);
-}
-
-static int chksum_digest(struct shash_desc *desc, const u8 *data,
-                        unsigned int length, u8 *out)
-{
-       struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
-
-       return __chksum_finup(mctx->key, data, length, out);
-}
-
-static int chksumc_digest(struct shash_desc *desc, const u8 *data,
-                        unsigned int length, u8 *out)
-{
-       struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
-
-       return __chksumc_finup(mctx->key, data, length, out);
-}
-
-static int chksum_cra_init(struct crypto_tfm *tfm)
-{
-       struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
-
-       mctx->key = ~0;
-       return 0;
-}
-
-static struct shash_alg crc32_alg = {
-       .digestsize             =       CHKSUM_DIGEST_SIZE,
-       .setkey                 =       chksum_setkey,
-       .init                   =       chksum_init,
-       .update                 =       chksum_update,
-       .final                  =       chksum_final,
-       .finup                  =       chksum_finup,
-       .digest                 =       chksum_digest,
-       .descsize               =       sizeof(struct chksum_desc_ctx),
-       .base                   =       {
-               .cra_name               =       "crc32",
-               .cra_driver_name        =       "crc32-mips-hw",
-               .cra_priority           =       300,
-               .cra_flags              =       CRYPTO_ALG_OPTIONAL_KEY,
-               .cra_blocksize          =       CHKSUM_BLOCK_SIZE,
-               .cra_ctxsize            =       sizeof(struct chksum_ctx),
-               .cra_module             =       THIS_MODULE,
-               .cra_init               =       chksum_cra_init,
-       }
-};
-
-static struct shash_alg crc32c_alg = {
-       .digestsize             =       CHKSUM_DIGEST_SIZE,
-       .setkey                 =       chksum_setkey,
-       .init                   =       chksum_init,
-       .update                 =       chksumc_update,
-       .final                  =       chksumc_final,
-       .finup                  =       chksumc_finup,
-       .digest                 =       chksumc_digest,
-       .descsize               =       sizeof(struct chksum_desc_ctx),
-       .base                   =       {
-               .cra_name               =       "crc32c",
-               .cra_driver_name        =       "crc32c-mips-hw",
-               .cra_priority           =       300,
-               .cra_flags              =       CRYPTO_ALG_OPTIONAL_KEY,
-               .cra_blocksize          =       CHKSUM_BLOCK_SIZE,
-               .cra_ctxsize            =       sizeof(struct chksum_ctx),
-               .cra_module             =       THIS_MODULE,
-               .cra_init               =       chksum_cra_init,
-       }
-};
-
-static int __init crc32_mod_init(void)
-{
-       int err;
-
-       err = crypto_register_shash(&crc32_alg);
-
-       if (err)
-               return err;
-
-       err = crypto_register_shash(&crc32c_alg);
-
-       if (err) {
-               crypto_unregister_shash(&crc32_alg);
-               return err;
-       }
-
-       return 0;
-}
-
-static void __exit crc32_mod_exit(void)
-{
-       crypto_unregister_shash(&crc32_alg);
-       crypto_unregister_shash(&crc32c_alg);
-}
-
-MODULE_AUTHOR("Marcin Nowakowski <marcin.nowakowski@mips.com");
-MODULE_DESCRIPTION("CRC32 and CRC32C using optional MIPS instructions");
-MODULE_LICENSE("GPL v2");
-
-module_cpu_feature_match(MIPS_CRC32, crc32_mod_init);
-module_exit(crc32_mod_exit);
index 5d5b993cbc2bff88a0b9903c4843c64295e7bdb9..9c024e6d5e54c057c3dd196f7a6f7fc6eb8feda9 100644 (file)
@@ -14,5 +14,7 @@ lib-$(CONFIG_GENERIC_CSUM)    := $(filter-out csum_partial.o, $(lib-y))
 obj-$(CONFIG_CPU_GENERIC_DUMP_TLB) += dump_tlb.o
 obj-$(CONFIG_CPU_R3000)                += r3k_dump_tlb.o
 
+obj-$(CONFIG_CRC32_ARCH)       += crc32-mips.o
+
 # libgcc-style stuff needed in the kernel
 obj-y += bswapsi.o bswapdi.o multi3.o
diff --git a/arch/mips/lib/crc32-mips.c b/arch/mips/lib/crc32-mips.c
new file mode 100644 (file)
index 0000000..083e5d6
--- /dev/null
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * crc32-mips.c - CRC32 and CRC32C using optional MIPSr6 instructions
+ *
+ * Module based on arm64/crypto/crc32-arm.c
+ *
+ * Copyright (C) 2014 Linaro Ltd <yazen.ghannam@linaro.org>
+ * Copyright (C) 2018 MIPS Tech, LLC
+ */
+
+#include <linux/cpufeature.h>
+#include <linux/crc32.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/mipsregs.h>
+#include <linux/unaligned.h>
+
+enum crc_op_size {
+       b, h, w, d,
+};
+
+enum crc_type {
+       crc32,
+       crc32c,
+};
+
+#ifndef TOOLCHAIN_SUPPORTS_CRC
+#define _ASM_SET_CRC(OP, SZ, TYPE)                                       \
+_ASM_MACRO_3R(OP, rt, rs, rt2,                                           \
+       ".ifnc  \\rt, \\rt2\n\t"                                          \
+       ".error \"invalid operands \\\"" #OP " \\rt,\\rs,\\rt2\\\"\"\n\t" \
+       ".endif\n\t"                                                      \
+       _ASM_INSN_IF_MIPS(0x7c00000f | (__rt << 16) | (__rs << 21) |      \
+                         ((SZ) <<  6) | ((TYPE) << 8))                   \
+       _ASM_INSN32_IF_MM(0x00000030 | (__rs << 16) | (__rt << 21) |      \
+                         ((SZ) << 14) | ((TYPE) << 3)))
+#define _ASM_UNSET_CRC(op, SZ, TYPE) ".purgem " #op "\n\t"
+#else /* !TOOLCHAIN_SUPPORTS_CRC */
+#define _ASM_SET_CRC(op, SZ, TYPE) ".set\tcrc\n\t"
+#define _ASM_UNSET_CRC(op, SZ, TYPE)
+#endif
+
+#define __CRC32(crc, value, op, SZ, TYPE)              \
+do {                                                   \
+       __asm__ __volatile__(                           \
+               ".set   push\n\t"                       \
+               _ASM_SET_CRC(op, SZ, TYPE)              \
+               #op "   %0, %1, %0\n\t"                 \
+               _ASM_UNSET_CRC(op, SZ, TYPE)            \
+               ".set   pop"                            \
+               : "+r" (crc)                            \
+               : "r" (value));                         \
+} while (0)
+
+#define _CRC32_crc32b(crc, value)      __CRC32(crc, value, crc32b, 0, 0)
+#define _CRC32_crc32h(crc, value)      __CRC32(crc, value, crc32h, 1, 0)
+#define _CRC32_crc32w(crc, value)      __CRC32(crc, value, crc32w, 2, 0)
+#define _CRC32_crc32d(crc, value)      __CRC32(crc, value, crc32d, 3, 0)
+#define _CRC32_crc32cb(crc, value)     __CRC32(crc, value, crc32cb, 0, 1)
+#define _CRC32_crc32ch(crc, value)     __CRC32(crc, value, crc32ch, 1, 1)
+#define _CRC32_crc32cw(crc, value)     __CRC32(crc, value, crc32cw, 2, 1)
+#define _CRC32_crc32cd(crc, value)     __CRC32(crc, value, crc32cd, 3, 1)
+
+#define _CRC32(crc, value, size, op) \
+       _CRC32_##op##size(crc, value)
+
+#define CRC32(crc, value, size) \
+       _CRC32(crc, value, size, crc32)
+
+#define CRC32C(crc, value, size) \
+       _CRC32(crc, value, size, crc32c)
+
+static DEFINE_STATIC_KEY_FALSE(have_crc32);
+
+u32 crc32_le_arch(u32 crc, const u8 *p, size_t len)
+{
+       if (!static_branch_likely(&have_crc32))
+               return crc32_le_base(crc, p, len);
+
+       if (IS_ENABLED(CONFIG_64BIT)) {
+               for (; len >= sizeof(u64); p += sizeof(u64), len -= sizeof(u64)) {
+                       u64 value = get_unaligned_le64(p);
+
+                       CRC32(crc, value, d);
+               }
+
+               if (len & sizeof(u32)) {
+                       u32 value = get_unaligned_le32(p);
+
+                       CRC32(crc, value, w);
+                       p += sizeof(u32);
+               }
+       } else {
+               for (; len >= sizeof(u32); len -= sizeof(u32)) {
+                       u32 value = get_unaligned_le32(p);
+
+                       CRC32(crc, value, w);
+                       p += sizeof(u32);
+               }
+       }
+
+       if (len & sizeof(u16)) {
+               u16 value = get_unaligned_le16(p);
+
+               CRC32(crc, value, h);
+               p += sizeof(u16);
+       }
+
+       if (len & sizeof(u8)) {
+               u8 value = *p++;
+
+               CRC32(crc, value, b);
+       }
+
+       return crc;
+}
+EXPORT_SYMBOL(crc32_le_arch);
+
+u32 crc32c_le_arch(u32 crc, const u8 *p, size_t len)
+{
+       if (!static_branch_likely(&have_crc32))
+               return crc32c_le_base(crc, p, len);
+
+       if (IS_ENABLED(CONFIG_64BIT)) {
+               for (; len >= sizeof(u64); p += sizeof(u64), len -= sizeof(u64)) {
+                       u64 value = get_unaligned_le64(p);
+
+                       CRC32C(crc, value, d);
+               }
+
+               if (len & sizeof(u32)) {
+                       u32 value = get_unaligned_le32(p);
+
+                       CRC32C(crc, value, w);
+                       p += sizeof(u32);
+               }
+       } else {
+               for (; len >= sizeof(u32); len -= sizeof(u32)) {
+                       u32 value = get_unaligned_le32(p);
+
+                       CRC32C(crc, value, w);
+                       p += sizeof(u32);
+               }
+       }
+
+       if (len & sizeof(u16)) {
+               u16 value = get_unaligned_le16(p);
+
+               CRC32C(crc, value, h);
+               p += sizeof(u16);
+       }
+
+       if (len & sizeof(u8)) {
+               u8 value = *p++;
+
+               CRC32C(crc, value, b);
+       }
+       return crc;
+}
+EXPORT_SYMBOL(crc32c_le_arch);
+
+u32 crc32_be_arch(u32 crc, const u8 *p, size_t len)
+{
+       return crc32_be_base(crc, p, len);
+}
+EXPORT_SYMBOL(crc32_be_arch);
+
+static int __init crc32_mips_init(void)
+{
+       if (cpu_have_feature(cpu_feature(MIPS_CRC32)))
+               static_branch_enable(&have_crc32);
+       return 0;
+}
+arch_initcall(crc32_mips_init);
+
+static void __exit crc32_mips_exit(void)
+{
+}
+module_exit(crc32_mips_exit);
+
+u32 crc32_optimizations(void)
+{
+       if (static_key_enabled(&have_crc32))
+               return CRC32_LE_OPTIMIZATION | CRC32C_OPTIMIZATION;
+       return 0;
+}
+EXPORT_SYMBOL(crc32_optimizations);
+
+MODULE_AUTHOR("Marcin Nowakowski <marcin.nowakowski@mips.com");
+MODULE_DESCRIPTION("CRC32 and CRC32C using optional MIPS instructions");
+MODULE_LICENSE("GPL v2");