summaryrefslogtreecommitdiff
path: root/arch/x86/lib/div64.c
blob: a8a3d36f51130efed4973d0ba8fca35bbd4f2e0c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// SPDX-License-Identifier: BSD-3-Clause
/*
 * This file is copied from the coreboot repository as part of
 * the libpayload project:
 *
 * Copyright 2014 Google Inc.
 */

#include <common.h>
#include <asm/types.h>
#include <linux/stddef.h>

union overlay64 {
	u64 longw;
	struct {
		u32 lower;
		u32 higher;
	} words;
};

u64 __ashldi3(u64 num, unsigned int shift)
{
	union overlay64 output;

	output.longw = num;
	if (shift >= 32) {
		output.words.higher = output.words.lower << (shift - 32);
		output.words.lower = 0;
	} else {
		if (!shift)
			return num;
		output.words.higher = (output.words.higher << shift) |
			(output.words.lower >> (32 - shift));
		output.words.lower = output.words.lower << shift;
	}
	return output.longw;
}

u64 __lshrdi3(u64 num, unsigned int shift)
{
	union overlay64 output;

	output.longw = num;
	if (shift >= 32) {
		output.words.lower = output.words.higher >> (shift - 32);
		output.words.higher = 0;
	} else {
		if (!shift)
			return num;
		output.words.lower = output.words.lower >> shift |
			(output.words.higher << (32 - shift));
		output.words.higher = output.words.higher >> shift;
	}
	return output.longw;
}

#define MAX_32BIT_UINT ((((u64)1) << 32) - 1)

static u64 _64bit_divide(u64 dividend, u64 divider, u64 *rem_p)
{
	u64 result = 0;

	/*
	 * If divider is zero - let the rest of the system care about the
	 * exception.
	 */
	if (!divider)
		return 1 / (u32)divider;

	/* As an optimization, let's not use 64 bit division unless we must. */
	if (dividend <= MAX_32BIT_UINT) {
		if (divider > MAX_32BIT_UINT) {
			result = 0;
			if (rem_p)
				*rem_p = divider;
		} else {
			result = (u32)dividend / (u32)divider;
			if (rem_p)
				*rem_p = (u32)dividend % (u32)divider;
		}
		return result;
	}

	while (divider <= dividend) {
		u64 locald = divider;
		u64 limit = __lshrdi3(dividend, 1);
		int shifts = 0;

		while (locald <= limit) {
			shifts++;
			locald = locald + locald;
		}
		result |= __ashldi3(1, shifts);
		dividend -= locald;
	}

	if (rem_p)
		*rem_p = dividend;

	return result;
}

u64 __udivdi3(u64 num, u64 den)
{
	return _64bit_divide(num, den, NULL);
}

u64 __umoddi3(u64 num, u64 den)
{
	u64 v = 0;

	_64bit_divide(num, den, &v);
	return v;
}