diff options
Diffstat (limited to 'arch/mips/mm/tlbex.c')
-rw-r--r-- | arch/mips/mm/tlbex.c | 30 |
1 files changed, 27 insertions, 3 deletions
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 4ec0964b8394..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 |
@@ -78,7 +93,7 @@ enum fields | |||
78 | SET = 0x200 | 93 | SET = 0x200 |
79 | }; | 94 | }; |
80 | 95 | ||
81 | #define OP_MASK 0x2f | 96 | #define OP_MASK 0x3f |
82 | #define OP_SH 26 | 97 | #define OP_SH 26 |
83 | #define RS_MASK 0x1f | 98 | #define RS_MASK 0x1f |
84 | #define RS_SH 21 | 99 | #define RS_SH 21 |
@@ -92,7 +107,7 @@ enum fields | |||
92 | #define IMM_SH 0 | 107 | #define IMM_SH 0 |
93 | #define JIMM_MASK 0x3ffffff | 108 | #define JIMM_MASK 0x3ffffff |
94 | #define JIMM_SH 0 | 109 | #define JIMM_SH 0 |
95 | #define FUNC_MASK 0x2f | 110 | #define FUNC_MASK 0x3f |
96 | #define FUNC_SH 0 | 111 | #define FUNC_SH 0 |
97 | #define SET_MASK 0x7 | 112 | #define SET_MASK 0x7 |
98 | #define SET_SH 0 | 113 | #define SET_SH 0 |
@@ -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); |