xref: /openbmc/u-boot/arch/x86/lib/div64.c (revision 3ba98ed8)
1 // SPDX-License-Identifier: BSD-3-Clause
2 /*
3  * This file is copied from the coreboot repository as part of
4  * the libpayload project:
5  *
6  * Copyright 2014 Google Inc.
7  */
8 
9 #include <common.h>
10 
11 union overlay64 {
12 	u64 longw;
13 	struct {
14 		u32 lower;
15 		u32 higher;
16 	} words;
17 };
18 
19 u64 __ashldi3(u64 num, unsigned int shift)
20 {
21 	union overlay64 output;
22 
23 	output.longw = num;
24 	if (shift >= 32) {
25 		output.words.higher = output.words.lower << (shift - 32);
26 		output.words.lower = 0;
27 	} else {
28 		if (!shift)
29 			return num;
30 		output.words.higher = (output.words.higher << shift) |
31 			(output.words.lower >> (32 - shift));
32 		output.words.lower = output.words.lower << shift;
33 	}
34 	return output.longw;
35 }
36 
37 u64 __lshrdi3(u64 num, unsigned int shift)
38 {
39 	union overlay64 output;
40 
41 	output.longw = num;
42 	if (shift >= 32) {
43 		output.words.lower = output.words.higher >> (shift - 32);
44 		output.words.higher = 0;
45 	} else {
46 		if (!shift)
47 			return num;
48 		output.words.lower = output.words.lower >> shift |
49 			(output.words.higher << (32 - shift));
50 		output.words.higher = output.words.higher >> shift;
51 	}
52 	return output.longw;
53 }
54 
55 #define MAX_32BIT_UINT ((((u64)1) << 32) - 1)
56 
57 static u64 _64bit_divide(u64 dividend, u64 divider, u64 *rem_p)
58 {
59 	u64 result = 0;
60 
61 	/*
62 	 * If divider is zero - let the rest of the system care about the
63 	 * exception.
64 	 */
65 	if (!divider)
66 		return 1 / (u32)divider;
67 
68 	/* As an optimization, let's not use 64 bit division unless we must. */
69 	if (dividend <= MAX_32BIT_UINT) {
70 		if (divider > MAX_32BIT_UINT) {
71 			result = 0;
72 			if (rem_p)
73 				*rem_p = divider;
74 		} else {
75 			result = (u32)dividend / (u32)divider;
76 			if (rem_p)
77 				*rem_p = (u32)dividend % (u32)divider;
78 		}
79 		return result;
80 	}
81 
82 	while (divider <= dividend) {
83 		u64 locald = divider;
84 		u64 limit = __lshrdi3(dividend, 1);
85 		int shifts = 0;
86 
87 		while (locald <= limit) {
88 			shifts++;
89 			locald = locald + locald;
90 		}
91 		result |= __ashldi3(1, shifts);
92 		dividend -= locald;
93 	}
94 
95 	if (rem_p)
96 		*rem_p = dividend;
97 
98 	return result;
99 }
100 
101 u64 __udivdi3(u64 num, u64 den)
102 {
103 	return _64bit_divide(num, den, NULL);
104 }
105 
106 u64 __umoddi3(u64 num, u64 den)
107 {
108 	u64 v = 0;
109 
110 	_64bit_divide(num, den, &v);
111 	return v;
112 }
113