1 /*
2  * Copyright 2010 Tilera Corporation. All Rights Reserved.
3  *
4  *   This program is free software; you can redistribute it and/or
5  *   modify it under the terms of the GNU General Public License
6  *   as published by the Free Software Foundation, version 2.
7  *
8  *   This program is distributed in the hope that it will be useful, but
9  *   WITHOUT ANY WARRANTY; without even the implied warranty of
10  *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
11  *   NON INFRINGEMENT.  See the GNU General Public License for
12  *   more details.
13  * Support code for the main lib/checksum.c.
14  */
15 
16 #include <net/checksum.h>
17 #include <linux/module.h>
18 
longto16(unsigned long x)19 static inline unsigned int longto16(unsigned long x)
20 {
21 	unsigned long ret;
22 #ifdef __tilegx__
23 	ret = __insn_v2sadu(x, 0);
24 	ret = __insn_v2sadu(ret, 0);
25 #else
26 	ret = __insn_sadh_u(x, 0);
27 	ret = __insn_sadh_u(ret, 0);
28 #endif
29 	return ret;
30 }
31 
do_csum(const unsigned char * buff,int len)32 __wsum do_csum(const unsigned char *buff, int len)
33 {
34 	int odd, count;
35 	unsigned long result = 0;
36 
37 	if (len <= 0)
38 		goto out;
39 	odd = 1 & (unsigned long) buff;
40 	if (odd) {
41 		result = (*buff << 8);
42 		len--;
43 		buff++;
44 	}
45 	count = len >> 1;		/* nr of 16-bit words.. */
46 	if (count) {
47 		if (2 & (unsigned long) buff) {
48 			result += *(const unsigned short *)buff;
49 			count--;
50 			len -= 2;
51 			buff += 2;
52 		}
53 		count >>= 1;		/* nr of 32-bit words.. */
54 		if (count) {
55 #ifdef __tilegx__
56 			if (4 & (unsigned long) buff) {
57 				unsigned int w = *(const unsigned int *)buff;
58 				result = __insn_v2sadau(result, w, 0);
59 				count--;
60 				len -= 4;
61 				buff += 4;
62 			}
63 			count >>= 1;		/* nr of 64-bit words.. */
64 #endif
65 
66 			/*
67 			 * This algorithm could wrap around for very
68 			 * large buffers, but those should be impossible.
69 			 */
70 			BUG_ON(count >= 65530);
71 
72 			while (count) {
73 				unsigned long w = *(const unsigned long *)buff;
74 				count--;
75 				buff += sizeof(w);
76 #ifdef __tilegx__
77 				result = __insn_v2sadau(result, w, 0);
78 #else
79 				result = __insn_sadah_u(result, w, 0);
80 #endif
81 			}
82 #ifdef __tilegx__
83 			if (len & 4) {
84 				unsigned int w = *(const unsigned int *)buff;
85 				result = __insn_v2sadau(result, w, 0);
86 				buff += 4;
87 			}
88 #endif
89 		}
90 		if (len & 2) {
91 			result += *(const unsigned short *) buff;
92 			buff += 2;
93 		}
94 	}
95 	if (len & 1)
96 		result += *buff;
97 	result = longto16(result);
98 	if (odd)
99 		result = swab16(result);
100 out:
101 	return result;
102 }
103