diff options
author | Maciej W. Rozycki <macro@linux-mips.org> | 2006-08-23 09:26:50 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2007-09-14 14:08:43 -0400 |
commit | 8df5beac2aa15b18a912ab585e1b86e748eda9ad (patch) | |
tree | 9c1d6621a58ed7804cd68da2f5d8bbd6dd30de7b /arch/mips/mm | |
parent | 48d480b0bde794781fcae9501fb043c1bac0e523 (diff) |
[MIPS] Workaround for 4Kc machine check exception
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/mm')
-rw-r--r-- | arch/mips/mm/tlbex.c | 26 |
1 files changed, 25 insertions, 1 deletions
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 9cb39644b6f1..6c425b052442 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c | |||
@@ -58,6 +58,21 @@ static __init int __maybe_unused r10000_llsc_war(void) | |||
58 | } | 58 | } |
59 | 59 | ||
60 | /* | 60 | /* |
61 | * Found by experiment: At least some revisions of the 4kc throw under | ||
62 | * some circumstances a machine check exception, triggered by invalid | ||
63 | * values in the index register. Delaying the tlbp instruction until | ||
64 | * after the next branch, plus adding an additional nop in front of | ||
65 | * tlbwi/tlbwr avoids the invalid index register values. Nobody knows | ||
66 | * why; it's not an issue caused by the core RTL. | ||
67 | * | ||
68 | */ | ||
69 | static __init int __attribute__((unused)) m4kc_tlbp_war(void) | ||
70 | { | ||
71 | return (current_cpu_data.processor_id & 0xffff00) == | ||
72 | (PRID_COMP_MIPS | PRID_IMP_4KC); | ||
73 | } | ||
74 | |||
75 | /* | ||
61 | * A little micro-assembler, intended for TLB refill handler | 76 | * A little micro-assembler, intended for TLB refill handler |
62 | * synthesizing. It is intentionally kept simple, does only support | 77 | * synthesizing. It is intentionally kept simple, does only support |
63 | * a subset of instructions, and does not try to hide pipeline effects | 78 | * a subset of instructions, and does not try to hide pipeline effects |
@@ -894,6 +909,8 @@ static __init void build_tlb_write_entry(u32 **p, struct label **l, | |||
894 | case CPU_20KC: | 909 | case CPU_20KC: |
895 | case CPU_25KF: | 910 | case CPU_25KF: |
896 | case CPU_LOONGSON2: | 911 | case CPU_LOONGSON2: |
912 | if (m4kc_tlbp_war()) | ||
913 | i_nop(p); | ||
897 | tlbw(p); | 914 | tlbw(p); |
898 | break; | 915 | break; |
899 | 916 | ||
@@ -1705,7 +1722,8 @@ build_r4000_tlbchange_handler_head(u32 **p, struct label **l, | |||
1705 | l_smp_pgtable_change(l, *p); | 1722 | l_smp_pgtable_change(l, *p); |
1706 | # endif | 1723 | # endif |
1707 | iPTE_LW(p, l, pte, ptr); /* get even pte */ | 1724 | iPTE_LW(p, l, pte, ptr); /* get even pte */ |
1708 | build_tlb_probe_entry(p); | 1725 | if (!m4kc_tlbp_war()) |
1726 | build_tlb_probe_entry(p); | ||
1709 | } | 1727 | } |
1710 | 1728 | ||
1711 | static void __init | 1729 | static void __init |
@@ -1747,6 +1765,8 @@ static void __init build_r4000_tlb_load_handler(void) | |||
1747 | 1765 | ||
1748 | build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1); | 1766 | build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1); |
1749 | build_pte_present(&p, &l, &r, K0, K1, label_nopage_tlbl); | 1767 | build_pte_present(&p, &l, &r, K0, K1, label_nopage_tlbl); |
1768 | if (m4kc_tlbp_war()) | ||
1769 | build_tlb_probe_entry(&p); | ||
1750 | build_make_valid(&p, &r, K0, K1); | 1770 | build_make_valid(&p, &r, K0, K1); |
1751 | build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1); | 1771 | build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1); |
1752 | 1772 | ||
@@ -1781,6 +1801,8 @@ static void __init build_r4000_tlb_store_handler(void) | |||
1781 | 1801 | ||
1782 | build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1); | 1802 | build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1); |
1783 | build_pte_writable(&p, &l, &r, K0, K1, label_nopage_tlbs); | 1803 | build_pte_writable(&p, &l, &r, K0, K1, label_nopage_tlbs); |
1804 | if (m4kc_tlbp_war()) | ||
1805 | build_tlb_probe_entry(&p); | ||
1784 | build_make_write(&p, &r, K0, K1); | 1806 | build_make_write(&p, &r, K0, K1); |
1785 | build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1); | 1807 | build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1); |
1786 | 1808 | ||
@@ -1815,6 +1837,8 @@ static void __init build_r4000_tlb_modify_handler(void) | |||
1815 | 1837 | ||
1816 | build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1); | 1838 | build_r4000_tlbchange_handler_head(&p, &l, &r, K0, K1); |
1817 | build_pte_modifiable(&p, &l, &r, K0, K1, label_nopage_tlbm); | 1839 | build_pte_modifiable(&p, &l, &r, K0, K1, label_nopage_tlbm); |
1840 | if (m4kc_tlbp_war()) | ||
1841 | build_tlb_probe_entry(&p); | ||
1818 | /* Present and writable bits set, set accessed and dirty bits. */ | 1842 | /* Present and writable bits set, set accessed and dirty bits. */ |
1819 | build_make_write(&p, &r, K0, K1); | 1843 | build_make_write(&p, &r, K0, K1); |
1820 | build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1); | 1844 | build_r4000_tlbchange_handler_tail(&p, &l, &r, K0, K1); |