aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2006-09-28 10:55:39 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2006-09-28 10:55:39 -0400
commitd9f7a745d55527d0d41684b22506a86c4381f7f1 (patch)
treeea8870ef06c3723ad59b78aac97bfe8152894c72
parent1fce518e8e7de62597c823d6d795cafc694e7910 (diff)
[S390] __div64_32 for 31 bit.
The clocksource infrastructure introduced with commit ad596171ed635c51a9eef829187af100cbf8dcf7 broke 31 bit s390. The reason is that the do_div() primitive for 31 bit always had a restriction: it could only divide an unsigned 64 bit integer by an unsigned 31 bit integer. The clocksource code now uses do_div() with a base value that has the most significant bit set. The result is that clock->cycle_interval has a funny value which causes the linux time to jump around like mad. The solution is "obvious": implement a proper __div64_32 function for 31 bit s390. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r--arch/s390/Kconfig4
-rw-r--r--arch/s390/lib/Makefile1
-rw-r--r--arch/s390/lib/div64.c151
-rw-r--r--include/asm-s390/div64.h48
4 files changed, 156 insertions, 48 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index b216ca659cdf..b6b42f9f0d52 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -51,6 +51,10 @@ config 64BIT
51 Select this option if you have a 64 bit IBM zSeries machine 51 Select this option if you have a 64 bit IBM zSeries machine
52 and want to use the 64 bit addressing mode. 52 and want to use the 64 bit addressing mode.
53 53
54config 32BIT
55 bool
56 default y if !64BIT
57
54config SMP 58config SMP
55 bool "Symmetric multi-processing support" 59 bool "Symmetric multi-processing support"
56 ---help--- 60 ---help---
diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile
index c42ffedfdb49..b0cfa6c4883d 100644
--- a/arch/s390/lib/Makefile
+++ b/arch/s390/lib/Makefile
@@ -5,5 +5,6 @@
5EXTRA_AFLAGS := -traditional 5EXTRA_AFLAGS := -traditional
6 6
7lib-y += delay.o string.o uaccess_std.o 7lib-y += delay.o string.o uaccess_std.o
8lib-$(CONFIG_32BIT) += div64.o
8lib-$(CONFIG_64BIT) += uaccess_mvcos.o 9lib-$(CONFIG_64BIT) += uaccess_mvcos.o
9lib-$(CONFIG_SMP) += spinlock.o 10lib-$(CONFIG_SMP) += spinlock.o
diff --git a/arch/s390/lib/div64.c b/arch/s390/lib/div64.c
new file mode 100644
index 000000000000..0481f3424a13
--- /dev/null
+++ b/arch/s390/lib/div64.c
@@ -0,0 +1,151 @@
1/*
2 * arch/s390/lib/div64.c
3 *
4 * __div64_32 implementation for 31 bit.
5 *
6 * Copyright (C) IBM Corp. 2006
7 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
8 */
9
10#include <linux/types.h>
11#include <linux/module.h>
12
13#ifdef CONFIG_MARCH_G5
14
15/*
16 * Function to divide an unsigned 64 bit integer by an unsigned
17 * 31 bit integer using signed 64/32 bit division.
18 */
19static uint32_t __div64_31(uint64_t *n, uint32_t base)
20{
21 register uint32_t reg2 asm("2");
22 register uint32_t reg3 asm("3");
23 uint32_t *words = (uint32_t *) n;
24 uint32_t tmp;
25
26 /* Special case base==1, remainder = 0, quotient = n */
27 if (base == 1)
28 return 0;
29 /*
30 * Special case base==0 will cause a fixed point divide exception
31 * on the dr instruction and may not happen anyway. For the
32 * following calculation we can assume base > 1. The first
33 * signed 64 / 32 bit division with an upper half of 0 will
34 * give the correct upper half of the 64 bit quotient.
35 */
36 reg2 = 0UL;
37 reg3 = words[0];
38 asm volatile(
39 " dr %0,%2\n"
40 : "+d" (reg2), "+d" (reg3) : "d" (base) : "cc" );
41 words[0] = reg3;
42 reg3 = words[1];
43 /*
44 * To get the lower half of the 64 bit quotient and the 32 bit
45 * remainder we have to use a little trick. Since we only have
46 * a signed division the quotient can get too big. To avoid this
47 * the 64 bit dividend is halved, then the signed division will
48 * work. Afterwards the quotient and the remainder are doubled.
49 * If the last bit of the dividend has been one the remainder
50 * is increased by one then checked against the base. If the
51 * remainder has overflown subtract base and increase the
52 * quotient. Simple, no ?
53 */
54 asm volatile(
55 " nr %2,%1\n"
56 " srdl %0,1\n"
57 " dr %0,%3\n"
58 " alr %0,%0\n"
59 " alr %1,%1\n"
60 " alr %0,%2\n"
61 " clr %0,%3\n"
62 " jl 0f\n"
63 " slr %0,%3\n"
64 " alr %1,%2\n"
65 "0:\n"
66 : "+d" (reg2), "+d" (reg3), "=d" (tmp)
67 : "d" (base), "2" (1UL) : "cc" );
68 words[1] = reg3;
69 return reg2;
70}
71
72/*
73 * Function to divide an unsigned 64 bit integer by an unsigned
74 * 32 bit integer using the unsigned 64/31 bit division.
75 */
76uint32_t __div64_32(uint64_t *n, uint32_t base)
77{
78 uint32_t r;
79
80 /*
81 * If the most significant bit of base is set, divide n by
82 * (base/2). That allows to use 64/31 bit division and gives a
83 * good approximation of the result: n = (base/2)*q + r. The
84 * result needs to be corrected with two simple transformations.
85 * If base is already < 2^31-1 __div64_31 can be used directly.
86 */
87 r = __div64_31(n, ((signed) base < 0) ? (base/2) : base);
88 if ((signed) base < 0) {
89 uint64_t q = *n;
90 /*
91 * First transformation:
92 * n = (base/2)*q + r
93 * = ((base/2)*2)*(q/2) + ((q&1) ? (base/2) : 0) + r
94 * Since r < (base/2), r + (base/2) < base.
95 * With q1 = (q/2) and r1 = r + ((q&1) ? (base/2) : 0)
96 * n = ((base/2)*2)*q1 + r1 with r1 < base.
97 */
98 if (q & 1)
99 r += base/2;
100 q >>= 1;
101 /*
102 * Second transformation. ((base/2)*2) could have lost the
103 * last bit.
104 * n = ((base/2)*2)*q1 + r1
105 * = base*q1 - ((base&1) ? q1 : 0) + r1
106 */
107 if (base & 1) {
108 int64_t rx = r - q;
109 /*
110 * base is >= 2^31. The worst case for the while
111 * loop is n=2^64-1 base=2^31+1. That gives a
112 * maximum for q=(2^64-1)/2^31 = 0x1ffffffff. Since
113 * base >= 2^31 the loop is finished after a maximum
114 * of three iterations.
115 */
116 while (rx < 0) {
117 rx += base;
118 q--;
119 }
120 r = rx;
121 }
122 *n = q;
123 }
124 return r;
125}
126
127#else /* MARCH_G5 */
128
129uint32_t __div64_32(uint64_t *n, uint32_t base)
130{
131 register uint32_t reg2 asm("2");
132 register uint32_t reg3 asm("3");
133 uint32_t *words = (uint32_t *) n;
134
135 reg2 = 0UL;
136 reg3 = words[0];
137 asm volatile(
138 " dlr %0,%2\n"
139 : "+d" (reg2), "+d" (reg3) : "d" (base) : "cc" );
140 words[0] = reg3;
141 reg3 = words[1];
142 asm volatile(
143 " dlr %0,%2\n"
144 : "+d" (reg2), "+d" (reg3) : "d" (base) : "cc" );
145 words[1] = reg3;
146 return reg2;
147}
148
149#endif /* MARCH_G5 */
150
151EXPORT_SYMBOL(__div64_32);
diff --git a/include/asm-s390/div64.h b/include/asm-s390/div64.h
index af098dc3cf59..6cd978cefb28 100644
--- a/include/asm-s390/div64.h
+++ b/include/asm-s390/div64.h
@@ -1,49 +1 @@
1#ifndef __S390_DIV64
2#define __S390_DIV64
3
4#ifndef __s390x__
5
6/* for do_div "base" needs to be smaller than 2^31-1 */
7#define do_div(n, base) ({ \
8 unsigned long long __n = (n); \
9 unsigned long __r; \
10 \
11 asm (" slr 0,0\n" \
12 " l 1,%1\n" \
13 " srdl 0,1\n" \
14 " dr 0,%2\n" \
15 " alr 1,1\n" \
16 " alr 0,0\n" \
17 " lhi 2,1\n" \
18 " n 2,%1\n" \
19 " alr 0,2\n" \
20 " clr 0,%2\n" \
21 " jl 0f\n" \
22 " slr 0,%2\n" \
23 " ahi 1,1\n" \
24 "0: st 1,%1\n" \
25 " l 1,4+%1\n" \
26 " srdl 0,1\n" \
27 " dr 0,%2\n" \
28 " alr 1,1\n" \
29 " alr 0,0\n" \
30 " lhi 2,1\n" \
31 " n 2,4+%1\n" \
32 " alr 0,2\n" \
33 " clr 0,%2\n" \
34 " jl 1f\n" \
35 " slr 0,%2\n" \
36 " ahi 1,1\n" \
37 "1: st 1,4+%1\n" \
38 " lr %0,0" \
39 : "=d" (__r), "=m" (__n) \
40 : "d" (base), "m" (__n) : "0", "1", "2", "cc" ); \
41 (n) = (__n); \
42 __r; \
43})
44
45#else /* __s390x__ */
46#include <asm-generic/div64.h> #include <asm-generic/div64.h>
47#endif /* __s390x__ */
48
49#endif