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
19__wsum do_csum(const unsigned char *buff, int len)
20{
21	int odd, count;
22	unsigned long result = 0;
23
24	if (len <= 0)
25		goto out;
26	odd = 1 & (unsigned long) buff;
27	if (odd) {
28		result = (*buff << 8);
29		len--;
30		buff++;
31	}
32	count = len >> 1;		/* nr of 16-bit words.. */
33	if (count) {
34		if (2 & (unsigned long) buff) {
35			result += *(const unsigned short *)buff;
36			count--;
37			len -= 2;
38			buff += 2;
39		}
40		count >>= 1;		/* nr of 32-bit words.. */
41		if (count) {
42#ifdef __tilegx__
43			if (4 & (unsigned long) buff) {
44				unsigned int w = *(const unsigned int *)buff;
45				result = __insn_v2sadau(result, w, 0);
46				count--;
47				len -= 4;
48				buff += 4;
49			}
50			count >>= 1;		/* nr of 64-bit words.. */
51#endif
52
53			/*
54			 * This algorithm could wrap around for very
55			 * large buffers, but those should be impossible.
56			 */
57			BUG_ON(count >= 65530);
58
59			while (count) {
60				unsigned long w = *(const unsigned long *)buff;
61				count--;
62				buff += sizeof(w);
63#ifdef __tilegx__
64				result = __insn_v2sadau(result, w, 0);
65#else
66				result = __insn_sadah_u(result, w, 0);
67#endif
68			}
69#ifdef __tilegx__
70			if (len & 4) {
71				unsigned int w = *(const unsigned int *)buff;
72				result = __insn_v2sadau(result, w, 0);
73				buff += 4;
74			}
75#endif
76		}
77		if (len & 2) {
78			result += *(const unsigned short *) buff;
79			buff += 2;
80		}
81	}
82	if (len & 1)
83		result += *buff;
84	result = csum_long(result);
85	if (odd)
86		result = swab16(result);
87out:
88	return result;
89}
90