1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * crc32-mips.c - CRC32 and CRC32C using optional MIPSr6 instructions 4 * 5 * Module based on arm64/crypto/crc32-arm.c 6 * 7 * Copyright (C) 2014 Linaro Ltd <yazen.ghannam@linaro.org> 8 * Copyright (C) 2018 MIPS Tech, LLC 9 */ 10 11 #include <linux/cpufeature.h> 12 #include <linux/crc32.h> 13 #include <linux/init.h> 14 #include <linux/kernel.h> 15 #include <linux/module.h> 16 #include <asm/mipsregs.h> 17 #include <linux/unaligned.h> 18 19 enum crc_op_size { 20 b, h, w, d, 21 }; 22 23 enum crc_type { 24 crc32, 25 crc32c, 26 }; 27 28 #ifndef TOOLCHAIN_SUPPORTS_CRC 29 #define _ASM_SET_CRC(OP, SZ, TYPE) \ 30 _ASM_MACRO_3R(OP, rt, rs, rt2, \ 31 ".ifnc \\rt, \\rt2\n\t" \ 32 ".error \"invalid operands \\\"" #OP " \\rt,\\rs,\\rt2\\\"\"\n\t" \ 33 ".endif\n\t" \ 34 _ASM_INSN_IF_MIPS(0x7c00000f | (__rt << 16) | (__rs << 21) | \ 35 ((SZ) << 6) | ((TYPE) << 8)) \ 36 _ASM_INSN32_IF_MM(0x00000030 | (__rs << 16) | (__rt << 21) | \ 37 ((SZ) << 14) | ((TYPE) << 3))) 38 #define _ASM_UNSET_CRC(op, SZ, TYPE) ".purgem " #op "\n\t" 39 #else /* !TOOLCHAIN_SUPPORTS_CRC */ 40 #define _ASM_SET_CRC(op, SZ, TYPE) ".set\tcrc\n\t" 41 #define _ASM_UNSET_CRC(op, SZ, TYPE) 42 #endif 43 44 #define __CRC32(crc, value, op, SZ, TYPE) \ 45 do { \ 46 __asm__ __volatile__( \ 47 ".set push\n\t" \ 48 _ASM_SET_CRC(op, SZ, TYPE) \ 49 #op " %0, %1, %0\n\t" \ 50 _ASM_UNSET_CRC(op, SZ, TYPE) \ 51 ".set pop" \ 52 : "+r" (crc) \ 53 : "r" (value)); \ 54 } while (0) 55 56 #define _CRC32_crc32b(crc, value) __CRC32(crc, value, crc32b, 0, 0) 57 #define _CRC32_crc32h(crc, value) __CRC32(crc, value, crc32h, 1, 0) 58 #define _CRC32_crc32w(crc, value) __CRC32(crc, value, crc32w, 2, 0) 59 #define _CRC32_crc32d(crc, value) __CRC32(crc, value, crc32d, 3, 0) 60 #define _CRC32_crc32cb(crc, value) __CRC32(crc, value, crc32cb, 0, 1) 61 #define _CRC32_crc32ch(crc, value) __CRC32(crc, value, crc32ch, 1, 1) 62 #define _CRC32_crc32cw(crc, value) __CRC32(crc, value, crc32cw, 2, 1) 63 #define _CRC32_crc32cd(crc, value) __CRC32(crc, value, crc32cd, 3, 1) 64 65 #define _CRC32(crc, value, size, op) \ 66 _CRC32_##op##size(crc, value) 67 68 #define CRC32(crc, value, size) \ 69 _CRC32(crc, value, size, crc32) 70 71 #define CRC32C(crc, value, size) \ 72 _CRC32(crc, value, size, crc32c) 73 74 static DEFINE_STATIC_KEY_FALSE(have_crc32); 75 76 u32 crc32_le_arch(u32 crc, const u8 *p, size_t len) 77 { 78 if (!static_branch_likely(&have_crc32)) 79 return crc32_le_base(crc, p, len); 80 81 if (IS_ENABLED(CONFIG_64BIT)) { 82 for (; len >= sizeof(u64); p += sizeof(u64), len -= sizeof(u64)) { 83 u64 value = get_unaligned_le64(p); 84 85 CRC32(crc, value, d); 86 } 87 88 if (len & sizeof(u32)) { 89 u32 value = get_unaligned_le32(p); 90 91 CRC32(crc, value, w); 92 p += sizeof(u32); 93 } 94 } else { 95 for (; len >= sizeof(u32); len -= sizeof(u32)) { 96 u32 value = get_unaligned_le32(p); 97 98 CRC32(crc, value, w); 99 p += sizeof(u32); 100 } 101 } 102 103 if (len & sizeof(u16)) { 104 u16 value = get_unaligned_le16(p); 105 106 CRC32(crc, value, h); 107 p += sizeof(u16); 108 } 109 110 if (len & sizeof(u8)) { 111 u8 value = *p++; 112 113 CRC32(crc, value, b); 114 } 115 116 return crc; 117 } 118 EXPORT_SYMBOL(crc32_le_arch); 119 120 u32 crc32c_le_arch(u32 crc, const u8 *p, size_t len) 121 { 122 if (!static_branch_likely(&have_crc32)) 123 return crc32c_le_base(crc, p, len); 124 125 if (IS_ENABLED(CONFIG_64BIT)) { 126 for (; len >= sizeof(u64); p += sizeof(u64), len -= sizeof(u64)) { 127 u64 value = get_unaligned_le64(p); 128 129 CRC32C(crc, value, d); 130 } 131 132 if (len & sizeof(u32)) { 133 u32 value = get_unaligned_le32(p); 134 135 CRC32C(crc, value, w); 136 p += sizeof(u32); 137 } 138 } else { 139 for (; len >= sizeof(u32); len -= sizeof(u32)) { 140 u32 value = get_unaligned_le32(p); 141 142 CRC32C(crc, value, w); 143 p += sizeof(u32); 144 } 145 } 146 147 if (len & sizeof(u16)) { 148 u16 value = get_unaligned_le16(p); 149 150 CRC32C(crc, value, h); 151 p += sizeof(u16); 152 } 153 154 if (len & sizeof(u8)) { 155 u8 value = *p++; 156 157 CRC32C(crc, value, b); 158 } 159 return crc; 160 } 161 EXPORT_SYMBOL(crc32c_le_arch); 162 163 u32 crc32_be_arch(u32 crc, const u8 *p, size_t len) 164 { 165 return crc32_be_base(crc, p, len); 166 } 167 EXPORT_SYMBOL(crc32_be_arch); 168 169 static int __init crc32_mips_init(void) 170 { 171 if (cpu_have_feature(cpu_feature(MIPS_CRC32))) 172 static_branch_enable(&have_crc32); 173 return 0; 174 } 175 arch_initcall(crc32_mips_init); 176 177 static void __exit crc32_mips_exit(void) 178 { 179 } 180 module_exit(crc32_mips_exit); 181 182 u32 crc32_optimizations(void) 183 { 184 if (static_key_enabled(&have_crc32)) 185 return CRC32_LE_OPTIMIZATION | CRC32C_OPTIMIZATION; 186 return 0; 187 } 188 EXPORT_SYMBOL(crc32_optimizations); 189 190 MODULE_AUTHOR("Marcin Nowakowski <marcin.nowakowski@mips.com"); 191 MODULE_DESCRIPTION("CRC32 and CRC32C using optional MIPS instructions"); 192 MODULE_LICENSE("GPL v2"); 193