1*72f51a4fSEric Biggers // SPDX-License-Identifier: GPL-2.0
2*72f51a4fSEric Biggers /*
3*72f51a4fSEric Biggers * CRC32 and CRC32C using LoongArch crc* instructions
4*72f51a4fSEric Biggers *
5*72f51a4fSEric Biggers * Module based on mips/crypto/crc32-mips.c
6*72f51a4fSEric Biggers *
7*72f51a4fSEric Biggers * Copyright (C) 2014 Linaro Ltd <yazen.ghannam@linaro.org>
8*72f51a4fSEric Biggers * Copyright (C) 2018 MIPS Tech, LLC
9*72f51a4fSEric Biggers * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
10*72f51a4fSEric Biggers */
11*72f51a4fSEric Biggers
12*72f51a4fSEric Biggers #include <asm/cpu-features.h>
13*72f51a4fSEric Biggers #include <linux/crc32.h>
14*72f51a4fSEric Biggers #include <linux/module.h>
15*72f51a4fSEric Biggers #include <linux/unaligned.h>
16*72f51a4fSEric Biggers
17*72f51a4fSEric Biggers #define _CRC32(crc, value, size, type) \
18*72f51a4fSEric Biggers do { \
19*72f51a4fSEric Biggers __asm__ __volatile__( \
20*72f51a4fSEric Biggers #type ".w." #size ".w" " %0, %1, %0\n\t"\
21*72f51a4fSEric Biggers : "+r" (crc) \
22*72f51a4fSEric Biggers : "r" (value) \
23*72f51a4fSEric Biggers : "memory"); \
24*72f51a4fSEric Biggers } while (0)
25*72f51a4fSEric Biggers
26*72f51a4fSEric Biggers #define CRC32(crc, value, size) _CRC32(crc, value, size, crc)
27*72f51a4fSEric Biggers #define CRC32C(crc, value, size) _CRC32(crc, value, size, crcc)
28*72f51a4fSEric Biggers
29*72f51a4fSEric Biggers static DEFINE_STATIC_KEY_FALSE(have_crc32);
30*72f51a4fSEric Biggers
crc32_le_arch(u32 crc,const u8 * p,size_t len)31*72f51a4fSEric Biggers u32 crc32_le_arch(u32 crc, const u8 *p, size_t len)
32*72f51a4fSEric Biggers {
33*72f51a4fSEric Biggers if (!static_branch_likely(&have_crc32))
34*72f51a4fSEric Biggers return crc32_le_base(crc, p, len);
35*72f51a4fSEric Biggers
36*72f51a4fSEric Biggers while (len >= sizeof(u64)) {
37*72f51a4fSEric Biggers u64 value = get_unaligned_le64(p);
38*72f51a4fSEric Biggers
39*72f51a4fSEric Biggers CRC32(crc, value, d);
40*72f51a4fSEric Biggers p += sizeof(u64);
41*72f51a4fSEric Biggers len -= sizeof(u64);
42*72f51a4fSEric Biggers }
43*72f51a4fSEric Biggers
44*72f51a4fSEric Biggers if (len & sizeof(u32)) {
45*72f51a4fSEric Biggers u32 value = get_unaligned_le32(p);
46*72f51a4fSEric Biggers
47*72f51a4fSEric Biggers CRC32(crc, value, w);
48*72f51a4fSEric Biggers p += sizeof(u32);
49*72f51a4fSEric Biggers }
50*72f51a4fSEric Biggers
51*72f51a4fSEric Biggers if (len & sizeof(u16)) {
52*72f51a4fSEric Biggers u16 value = get_unaligned_le16(p);
53*72f51a4fSEric Biggers
54*72f51a4fSEric Biggers CRC32(crc, value, h);
55*72f51a4fSEric Biggers p += sizeof(u16);
56*72f51a4fSEric Biggers }
57*72f51a4fSEric Biggers
58*72f51a4fSEric Biggers if (len & sizeof(u8)) {
59*72f51a4fSEric Biggers u8 value = *p++;
60*72f51a4fSEric Biggers
61*72f51a4fSEric Biggers CRC32(crc, value, b);
62*72f51a4fSEric Biggers }
63*72f51a4fSEric Biggers
64*72f51a4fSEric Biggers return crc;
65*72f51a4fSEric Biggers }
66*72f51a4fSEric Biggers EXPORT_SYMBOL(crc32_le_arch);
67*72f51a4fSEric Biggers
crc32c_le_arch(u32 crc,const u8 * p,size_t len)68*72f51a4fSEric Biggers u32 crc32c_le_arch(u32 crc, const u8 *p, size_t len)
69*72f51a4fSEric Biggers {
70*72f51a4fSEric Biggers if (!static_branch_likely(&have_crc32))
71*72f51a4fSEric Biggers return crc32c_le_base(crc, p, len);
72*72f51a4fSEric Biggers
73*72f51a4fSEric Biggers while (len >= sizeof(u64)) {
74*72f51a4fSEric Biggers u64 value = get_unaligned_le64(p);
75*72f51a4fSEric Biggers
76*72f51a4fSEric Biggers CRC32C(crc, value, d);
77*72f51a4fSEric Biggers p += sizeof(u64);
78*72f51a4fSEric Biggers len -= sizeof(u64);
79*72f51a4fSEric Biggers }
80*72f51a4fSEric Biggers
81*72f51a4fSEric Biggers if (len & sizeof(u32)) {
82*72f51a4fSEric Biggers u32 value = get_unaligned_le32(p);
83*72f51a4fSEric Biggers
84*72f51a4fSEric Biggers CRC32C(crc, value, w);
85*72f51a4fSEric Biggers p += sizeof(u32);
86*72f51a4fSEric Biggers }
87*72f51a4fSEric Biggers
88*72f51a4fSEric Biggers if (len & sizeof(u16)) {
89*72f51a4fSEric Biggers u16 value = get_unaligned_le16(p);
90*72f51a4fSEric Biggers
91*72f51a4fSEric Biggers CRC32C(crc, value, h);
92*72f51a4fSEric Biggers p += sizeof(u16);
93*72f51a4fSEric Biggers }
94*72f51a4fSEric Biggers
95*72f51a4fSEric Biggers if (len & sizeof(u8)) {
96*72f51a4fSEric Biggers u8 value = *p++;
97*72f51a4fSEric Biggers
98*72f51a4fSEric Biggers CRC32C(crc, value, b);
99*72f51a4fSEric Biggers }
100*72f51a4fSEric Biggers
101*72f51a4fSEric Biggers return crc;
102*72f51a4fSEric Biggers }
103*72f51a4fSEric Biggers EXPORT_SYMBOL(crc32c_le_arch);
104*72f51a4fSEric Biggers
crc32_be_arch(u32 crc,const u8 * p,size_t len)105*72f51a4fSEric Biggers u32 crc32_be_arch(u32 crc, const u8 *p, size_t len)
106*72f51a4fSEric Biggers {
107*72f51a4fSEric Biggers return crc32_be_base(crc, p, len);
108*72f51a4fSEric Biggers }
109*72f51a4fSEric Biggers EXPORT_SYMBOL(crc32_be_arch);
110*72f51a4fSEric Biggers
crc32_loongarch_init(void)111*72f51a4fSEric Biggers static int __init crc32_loongarch_init(void)
112*72f51a4fSEric Biggers {
113*72f51a4fSEric Biggers if (cpu_has_crc32)
114*72f51a4fSEric Biggers static_branch_enable(&have_crc32);
115*72f51a4fSEric Biggers return 0;
116*72f51a4fSEric Biggers }
117*72f51a4fSEric Biggers arch_initcall(crc32_loongarch_init);
118*72f51a4fSEric Biggers
crc32_loongarch_exit(void)119*72f51a4fSEric Biggers static void __exit crc32_loongarch_exit(void)
120*72f51a4fSEric Biggers {
121*72f51a4fSEric Biggers }
122*72f51a4fSEric Biggers module_exit(crc32_loongarch_exit);
123*72f51a4fSEric Biggers
crc32_optimizations(void)124*72f51a4fSEric Biggers u32 crc32_optimizations(void)
125*72f51a4fSEric Biggers {
126*72f51a4fSEric Biggers if (static_key_enabled(&have_crc32))
127*72f51a4fSEric Biggers return CRC32_LE_OPTIMIZATION | CRC32C_OPTIMIZATION;
128*72f51a4fSEric Biggers return 0;
129*72f51a4fSEric Biggers }
130*72f51a4fSEric Biggers EXPORT_SYMBOL(crc32_optimizations);
131*72f51a4fSEric Biggers
132*72f51a4fSEric Biggers MODULE_AUTHOR("Min Zhou <zhoumin@loongson.cn>");
133*72f51a4fSEric Biggers MODULE_AUTHOR("Huacai Chen <chenhuacai@loongson.cn>");
134*72f51a4fSEric Biggers MODULE_DESCRIPTION("CRC32 and CRC32C using LoongArch crc* instructions");
135*72f51a4fSEric Biggers MODULE_LICENSE("GPL v2");
136