1 From patchwork Tue Nov 8 14:59:01 2011
2 Content-Type: text/plain; charset="utf-8"
4 Content-Transfer-Encoding: 7bit
5 Subject: MIPS: Kernel hangs occasionally during boot.
6 Date: Tue, 08 Nov 2011 13:59:01 -0000
7 From: Al Cooper <alcooperx@gmail.com>
9 Message-Id: <1320764341-4275-1-git-send-email-alcooperx@gmail.com>
10 To: ralf@linux-mips.org, linux-mips@linux-mips.org,
11 linux-kernel@vger.kernel.org
12 Cc: "Al Cooper" <alcooperx@gmail.com>
14 The Kernel hangs occasionally during boot after
15 "Calibrating delay loop..". This is caused by the
16 c0_compare_int_usable() routine in cevt-r4k.c returning false which
17 causes the system to disable the timer and hang later. The false
18 return happens because the routine is using a series of four calls to
19 irq_disable_hazard() as a delay while it waits for the timer changes
20 to propagate to the cp0 cause register. On newer MIPS cores, like the 74K,
21 the series of irq_disable_hazard() calls turn into ehb instructions and
22 can take as little as a few clock ticks for all 4 instructions. This
23 is not enough of a delay, so the routine thinks the timer is not working.
24 This fix uses up to a max number of cycle counter ticks for the delay
25 and uses back_to_back_c0_hazard() instead of irq_disable_hazard() to
26 handle the hazard condition between cp0 writes and cp0 reads.
28 Signed-off-by: Al Cooper <alcooperx@gmail.com>
31 arch/mips/kernel/cevt-r4k.c | 38 +++++++++++++++++++-------------------
32 1 files changed, 19 insertions(+), 19 deletions(-)
34 --- a/arch/mips/kernel/cevt-r4k.c
35 +++ b/arch/mips/kernel/cevt-r4k.c
36 @@ -103,19 +103,10 @@ static int c0_compare_int_pending(void)
39 * Compare interrupt can be routed and latched outside the core,
40 - * so a single execution hazard barrier may not be enough to give
41 - * it time to clear as seen in the Cause register. 4 time the
42 - * pipeline depth seems reasonably conservative, and empirically
43 - * works better in configurations with high CPU/bus clock ratios.
44 + * so wait up to worst case number of cycle counter ticks for timer interrupt
45 + * changes to propagate to the cause register.
48 -#define compare_change_hazard() \
50 - irq_disable_hazard(); \
51 - irq_disable_hazard(); \
52 - irq_disable_hazard(); \
53 - irq_disable_hazard(); \
55 +#define COMPARE_INT_SEEN_TICKS 50
57 int c0_compare_int_usable(void)
59 @@ -126,8 +117,12 @@ int c0_compare_int_usable(void)
60 * IP7 already pending? Try to clear it by acking the timer.
62 if (c0_compare_int_pending()) {
63 - write_c0_compare(read_c0_count());
64 - compare_change_hazard();
65 + cnt = read_c0_count();
66 + write_c0_compare(cnt);
67 + back_to_back_c0_hazard();
68 + while (read_c0_count() < (cnt + COMPARE_INT_SEEN_TICKS))
69 + if (!c0_compare_int_pending())
71 if (c0_compare_int_pending())
74 @@ -136,7 +131,7 @@ int c0_compare_int_usable(void)
75 cnt = read_c0_count();
77 write_c0_compare(cnt);
78 - compare_change_hazard();
79 + back_to_back_c0_hazard();
80 if ((int)(read_c0_count() - cnt) < 0)
82 /* increase delta if the timer was already expired */
83 @@ -145,12 +140,17 @@ int c0_compare_int_usable(void)
84 while ((int)(read_c0_count() - cnt) <= 0)
85 ; /* Wait for expiry */
87 - compare_change_hazard();
88 + while (read_c0_count() < (cnt + COMPARE_INT_SEEN_TICKS))
89 + if (c0_compare_int_pending())
91 if (!c0_compare_int_pending())
94 - write_c0_compare(read_c0_count());
95 - compare_change_hazard();
96 + cnt = read_c0_count();
97 + write_c0_compare(cnt);
98 + back_to_back_c0_hazard();
99 + while (read_c0_count() < (cnt + COMPARE_INT_SEEN_TICKS))
100 + if (!c0_compare_int_pending())
102 if (c0_compare_int_pending())