The Lexra LX5280 CPU [1][2] implements the MIPS-I ISA,
without unaligned load/store instructions (lwl, lwr, swl, swr).
The programming model of this CPU is very similar
to the R3000 programming model, with a few differences.

The Realtek RTL8186 SoC has this CPU, so this patch is required
for future RTL8186 SoC support.

The LX5280 CPU has no documented TLB unit (only SMMU, a simple MMU unit
which is not enough for usual Linux).
However, the RTL8186 SoC does include a TLB unit with the CPU
(programmed like R3000 TLB).

So this patch adds support *only* for LX5280s that have a TLB unit.

This patch includes:
- Adding Kconfig entries for LX5280
- Adding CPU_LX5280 to the cpu_type_enum
- Passing -march=lx5280 to the compiler
- Using existing R3000 code/behavior where possible
- Wait instruction support (for better idle power consuption)
- RDHWR instruction emulation from the page fault handler
  (more details in a code comment)

[1] https://www.linux-mips.org/wiki/Lexra
[2] https://wikidevi.com/wiki/Lexra_LX5280

Signed-off-by: Yasha Cherikovsky <yasha.c...@gmail.com>
Cc: Ralf Baechle <r...@linux-mips.org>
Cc: Paul Burton <paul.bur...@mips.com>
Cc: James Hogan <jho...@kernel.org>
Cc: linux-m...@linux-mips.org
Cc: linux-kernel@vger.kernel.org
---
 arch/mips/Kconfig                    |  30 +++-
 arch/mips/Makefile                   |   1 +
 arch/mips/include/asm/cpu-features.h |   3 +
 arch/mips/include/asm/cpu-type.h     |   4 +
 arch/mips/include/asm/cpu.h          |   9 +
 arch/mips/include/asm/isadep.h       |   3 +-
 arch/mips/include/asm/mipsregs.h     |  10 ++
 arch/mips/include/asm/module.h       |   2 +
 arch/mips/include/asm/pgtable-32.h   |   7 +-
 arch/mips/include/asm/pgtable-bits.h |   9 +-
 arch/mips/include/asm/pgtable.h      |   6 +-
 arch/mips/include/asm/stackframe.h   |   9 +-
 arch/mips/include/asm/traps.h        |   2 +
 arch/mips/kernel/Makefile            |   2 +
 arch/mips/kernel/cpu-probe.c         |   6 +
 arch/mips/kernel/entry.S             |   3 +-
 arch/mips/kernel/genex.S             |   6 +-
 arch/mips/kernel/idle.c              |  10 ++
 arch/mips/kernel/process.c           |   3 +-
 arch/mips/kernel/traps.c             |  42 +++++
 arch/mips/lib/Makefile               |   1 +
 arch/mips/mm/Makefile                |   1 +
 arch/mips/mm/c-lx5280.c              | 251 +++++++++++++++++++++++++++
 arch/mips/mm/cache.c                 |   6 +
 arch/mips/mm/fault.c                 |   4 +
 arch/mips/mm/tlbex.c                 |   1 +
 26 files changed, 408 insertions(+), 23 deletions(-)
 create mode 100644 arch/mips/mm/c-lx5280.c

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index a6b0391996ea..bbeabd6b0a80 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1558,6 +1558,17 @@ config CPU_R3000
          might be a safe bet.  If the resulting kernel does not work,
          try to recompile with R3000.
 
+config CPU_LX5280
+       bool "LX5280"
+       depends on SYS_HAS_CPU_LX5280
+       select CPU_SUPPORTS_32BIT_KERNEL
+       help
+         Choose this option to build a kernel for the Lexra LX5280 CPU.
+         Lexra LX5280 implements the MIPS-I instruction set, without
+         unaligned load and store instructions (lwl, lwr, swl, swr).
+         Only LX5280 CPUs with a TLB unit are supported.
+
+
 config CPU_TX39XX
        bool "R39XX"
        depends on SYS_HAS_CPU_TX39XX
@@ -1939,6 +1950,9 @@ config SYS_HAS_CPU_R3000
 config SYS_HAS_CPU_TX39XX
        bool
 
+config SYS_HAS_CPU_LX5280
+       bool
+
 config SYS_HAS_CPU_VR41XX
        bool
 
@@ -2169,7 +2183,7 @@ config PAGE_SIZE_8KB
 
 config PAGE_SIZE_16KB
        bool "16kB"
-       depends on !CPU_R3000 && !CPU_TX39XX
+       depends on !CPU_R3000 && !CPU_TX39XX && !CPU_LX5280
        help
          Using 16kB page size will result in higher performance kernel at
          the price of higher memory consumption.  This option is available on
@@ -2188,7 +2202,7 @@ config PAGE_SIZE_32KB
 
 config PAGE_SIZE_64KB
        bool "64kB"
-       depends on !CPU_R3000 && !CPU_TX39XX
+       depends on !CPU_R3000 && !CPU_TX39XX && !CPU_LX5280
        help
          Using 64kB page size will result in higher performance kernel at
          the price of higher memory consumption.  This option is available on
@@ -2256,15 +2270,15 @@ config CPU_HAS_PREFETCH
 
 config CPU_GENERIC_DUMP_TLB
        bool
-       default y if !(CPU_R3000 || CPU_R8000 || CPU_TX39XX)
+       default y if !(CPU_R3000 || CPU_R8000 || CPU_TX39XX || CPU_LX5280)
 
 config CPU_R4K_FPU
        bool
-       default y if !(CPU_R3000 || CPU_TX39XX)
+       default y if !(CPU_R3000 || CPU_TX39XX || CPU_LX5280)
 
 config CPU_R4K_CACHE_TLB
        bool
-       default y if !(CPU_R3000 || CPU_R8000 || CPU_SB1 || CPU_TX39XX || 
CPU_CAVIUM_OCTEON)
+       default y if !(CPU_R3000 || CPU_R8000 || CPU_SB1 || CPU_TX39XX || 
CPU_CAVIUM_OCTEON || CPU_LX5280)
 
 config MIPS_MT_SMP
        bool "MIPS MT SMP support (1 TC on each available VPE)"
@@ -2501,7 +2515,7 @@ config CPU_MIPSR2_IRQ_EI
 
 config CPU_HAS_SYNC
        bool
-       depends on !CPU_R3000
+       depends on !(CPU_R3000 || CPU_LX5280)
        default y
 
 #
@@ -2519,14 +2533,14 @@ config CPU_R4400_WORKAROUNDS
 
 config MIPS_ASID_SHIFT
        int
-       default 6 if CPU_R3000 || CPU_TX39XX
+       default 6 if CPU_R3000 || CPU_TX39XX || CPU_LX5280
        default 4 if CPU_R8000
        default 0
 
 config MIPS_ASID_BITS
        int
        default 0 if MIPS_ASID_BITS_VARIABLE
-       default 6 if CPU_R3000 || CPU_TX39XX
+       default 6 if CPU_R3000 || CPU_TX39XX || CPU_LX5280
        default 8
 
 config MIPS_ASID_BITS_VARIABLE
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index e2122cca4ae2..293403f38ffe 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -151,6 +151,7 @@ cflags-y += -fno-stack-check
 #
 cflags-$(CONFIG_CPU_R3000)     += -march=r3000
 cflags-$(CONFIG_CPU_TX39XX)    += -march=r3900
+cflags-$(CONFIG_CPU_LX5280)    += -march=lx5280
 cflags-$(CONFIG_CPU_R4300)     += -march=r4300 -Wa,--trap
 cflags-$(CONFIG_CPU_VR41XX)    += -march=r4100 -Wa,--trap
 cflags-$(CONFIG_CPU_R4X00)     += -march=r4600 -Wa,--trap
diff --git a/arch/mips/include/asm/cpu-features.h 
b/arch/mips/include/asm/cpu-features.h
index 9cdb4e4ce258..118e0ff4b54a 100644
--- a/arch/mips/include/asm/cpu-features.h
+++ b/arch/mips/include/asm/cpu-features.h
@@ -75,6 +75,9 @@
 #ifndef cpu_has_octeon_cache
 #define cpu_has_octeon_cache   0
 #endif
+#ifndef cpu_has_lx5280_cache
+#define cpu_has_lx5280_cache   (cpu_data[0].options & MIPS_CPU_LX5280_CACHE)
+#endif
 /* Don't override `cpu_has_fpu' to 1 or the "nofpu" option won't work.  */
 #ifndef cpu_has_fpu
 #define cpu_has_fpu            (current_cpu_data.options & MIPS_CPU_FPU)
diff --git a/arch/mips/include/asm/cpu-type.h b/arch/mips/include/asm/cpu-type.h
index a45af3de075d..bd837232196d 100644
--- a/arch/mips/include/asm/cpu-type.h
+++ b/arch/mips/include/asm/cpu-type.h
@@ -105,6 +105,10 @@ static inline int __pure __get_cpu_type(const int cpu_type)
        case CPU_TX3927:
 #endif
 
+#ifdef CONFIG_SYS_HAS_CPU_LX5280
+       case CPU_LX5280:
+#endif
+
 #ifdef CONFIG_SYS_HAS_CPU_VR41XX
        case CPU_VR41XX:
        case CPU_VR4111:
diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h
index 5b9d02ef4f60..970a263b52d9 100644
--- a/arch/mips/include/asm/cpu.h
+++ b/arch/mips/include/asm/cpu.h
@@ -90,6 +90,7 @@
 #define PRID_IMP_R5432         0x5400
 #define PRID_IMP_R5500         0x5500
 #define PRID_IMP_LOONGSON_64   0x6300  /* Loongson-2/3 */
+#define PRID_IMP_LX5280                0xC600
 
 #define PRID_IMP_UNKNOWN       0xff00
 
@@ -306,6 +307,11 @@ enum cpu_type_enum {
         */
        CPU_TX3912, CPU_TX3922, CPU_TX3927,
 
+       /*
+        * Lexra processors
+        */
+       CPU_LX5280,
+
        /*
         * MIPS32 class processors
         */
@@ -420,6 +426,9 @@ enum cpu_type_enum {
                                MBIT_ULL(55)    /* CPU shares FTLB entries with 
another */
 #define MIPS_CPU_MT_PER_TC_PERF_COUNTERS \
                                MBIT_ULL(56)    /* CPU has perf counters 
implemented per TC (MIPSMT ASE) */
+#define MIPS_CPU_LX5280_CACHE \
+                               MBIT_ULL(57)
+
 
 /*
  * CPU ASE encodings
diff --git a/arch/mips/include/asm/isadep.h b/arch/mips/include/asm/isadep.h
index d1683202399b..e725bec9a8ab 100644
--- a/arch/mips/include/asm/isadep.h
+++ b/arch/mips/include/asm/isadep.h
@@ -10,7 +10,8 @@
 #ifndef __ASM_ISADEP_H
 #define __ASM_ISADEP_H
 
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
+#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       defined(CONFIG_CPU_LX5280)
 /*
  * R2000 or R3000
  */
diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h
index ae461d91cd1f..d3d025d08f3d 100644
--- a/arch/mips/include/asm/mipsregs.h
+++ b/arch/mips/include/asm/mipsregs.h
@@ -81,6 +81,7 @@
 #define CP0_WATCHLO $18
 #define CP0_WATCHHI $19
 #define CP0_XCONTEXT $20
+#define CP0_LX5280_CCTL $20
 #define CP0_FRAMEMASK $21
 #define CP0_DIAGNOSTIC $22
 #define CP0_DEBUG $23
@@ -562,6 +563,12 @@
 #define MIPS_CONF_AT           (_ULCAST_(3) << 13)
 #define MIPS_CONF_M            (_ULCAST_(1) << 31)
 
+/* Bits specific to the Lexra LX5280 CPU. */
+#define LX5280_CCTL_DINVAL             (_ULCAST_(1) << 0)
+#define LX5280_CCTL_IINVAL             (_ULCAST_(1) << 1)
+#define LX5280_CCTL_IMEMFILL           (_ULCAST_(1) << 4)
+#define LX5280_CCTL_IMEMOFF            (_ULCAST_(1) << 5)
+
 /*
  * Bits in the MIPS32/64 PRA coprocessor 0 config registers 1 and above.
  */
@@ -1725,6 +1732,9 @@ do {                                                      
                \
 #define read_c0_xcontext()     __read_ulong_c0_register($20, 0)
 #define write_c0_xcontext(val) __write_ulong_c0_register($20, 0, val)
 
+#define read_c0_lx5280_cctl()  __read_ulong_c0_register($20, 0)
+#define write_c0_lx5280_cctl(val)      __write_ulong_c0_register($20, 0, val)
+
 #define read_c0_intcontrol()   __read_32bit_c0_ctrl_register($20)
 #define write_c0_intcontrol(val) __write_32bit_c0_ctrl_register($20, val)
 
diff --git a/arch/mips/include/asm/module.h b/arch/mips/include/asm/module.h
index 6dc0b21b8acd..8547620e20b1 100644
--- a/arch/mips/include/asm/module.h
+++ b/arch/mips/include/asm/module.h
@@ -137,6 +137,8 @@ search_module_dbetables(unsigned long addr)
 #define MODULE_PROC_FAMILY "XLR "
 #elif defined CONFIG_CPU_XLP
 #define MODULE_PROC_FAMILY "XLP "
+#elif defined CONFIG_CPU_LX5280
+#define MODULE_PROC_FAMILY "LX5280 "
 #else
 #error MODULE_PROC_FAMILY undefined for your processor configuration
 #endif
diff --git a/arch/mips/include/asm/pgtable-32.h 
b/arch/mips/include/asm/pgtable-32.h
index 74afe8c76bdd..54000c0dc56e 100644
--- a/arch/mips/include/asm/pgtable-32.h
+++ b/arch/mips/include/asm/pgtable-32.h
@@ -175,7 +175,8 @@ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t 
prot)
        ((pte_t *)page_address(pmd_page(*(dir))) + __pte_offset(address))
 #define pte_unmap(pte) ((void)(pte))
 
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
+#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       defined(CONFIG_CPU_LX5280)
 
 /* Swap entries must have VALID bit cleared. */
 #define __swp_type(x)                  (((x).val >> 10) & 0x1f)
@@ -220,6 +221,8 @@ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t 
prot)
 
 #endif /* defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32) */
 
-#endif /* defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) */
+#endif /* defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       * defined(CONFIG_CPU_LX5280)
+       */
 
 #endif /* _ASM_PGTABLE_32_H */
diff --git a/arch/mips/include/asm/pgtable-bits.h 
b/arch/mips/include/asm/pgtable-bits.h
index f88a48cd68b2..75bb141f308d 100644
--- a/arch/mips/include/asm/pgtable-bits.h
+++ b/arch/mips/include/asm/pgtable-bits.h
@@ -80,7 +80,8 @@ enum pgtable_bits {
        _PAGE_MODIFIED_SHIFT,
 };
 
-#elif defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
+#elif defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       defined(CONFIG_CPU_LX5280)
 
 /* Page table bits used for r3k systems */
 enum pgtable_bits {
@@ -146,7 +147,8 @@ enum pgtable_bits {
 #define _PAGE_GLOBAL           (1 << _PAGE_GLOBAL_SHIFT)
 #define _PAGE_VALID            (1 << _PAGE_VALID_SHIFT)
 #define _PAGE_DIRTY            (1 << _PAGE_DIRTY_SHIFT)
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
+#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       defined(CONFIG_CPU_LX5280)
 # define _CACHE_UNCACHED       (1 << _CACHE_UNCACHED_SHIFT)
 # define _CACHE_MASK           _CACHE_UNCACHED
 # define _PFN_SHIFT            PAGE_SHIFT
@@ -204,7 +206,8 @@ static inline uint64_t pte_to_entrylo(unsigned long pte_val)
 /*
  * Cache attributes
  */
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
+#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       defined(CONFIG_CPU_LX5280)
 
 #define _CACHE_CACHABLE_NONCOHERENT 0
 #define _CACHE_UNCACHED_ACCELERATED _CACHE_UNCACHED
diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h
index 129e0328367f..688ac35441ab 100644
--- a/arch/mips/include/asm/pgtable.h
+++ b/arch/mips/include/asm/pgtable.h
@@ -197,7 +197,8 @@ static inline void pte_clear(struct mm_struct *mm, unsigned 
long addr, pte_t *pt
 static inline void set_pte(pte_t *ptep, pte_t pteval)
 {
        *ptep = pteval;
-#if !defined(CONFIG_CPU_R3000) && !defined(CONFIG_CPU_TX39XX)
+#if !defined(CONFIG_CPU_R3000) && !defined(CONFIG_CPU_TX39XX) && \
+       !defined(CONFIG_CPU_LX5280)
        if (pte_val(pteval) & _PAGE_GLOBAL) {
                pte_t *buddy = ptep_buddy(ptep);
                /*
@@ -256,7 +257,8 @@ static inline void set_pte(pte_t *ptep, pte_t pteval)
 static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t 
*ptep)
 {
        htw_stop();
-#if !defined(CONFIG_CPU_R3000) && !defined(CONFIG_CPU_TX39XX)
+#if !defined(CONFIG_CPU_R3000) && !defined(CONFIG_CPU_TX39XX) && \
+       !defined(CONFIG_CPU_LX5280)
        /* Preserve global status for the pair */
        if (pte_val(*ptep_buddy(ptep)) & _PAGE_GLOBAL)
                set_pte_at(mm, addr, ptep, __pte(_PAGE_GLOBAL));
diff --git a/arch/mips/include/asm/stackframe.h 
b/arch/mips/include/asm/stackframe.h
index 2161357cc68f..698e635f7afc 100644
--- a/arch/mips/include/asm/stackframe.h
+++ b/arch/mips/include/asm/stackframe.h
@@ -42,7 +42,8 @@
        cfi_restore \reg \offset \docfi
        .endm
 
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
+#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       defined(CONFIG_CPU_LX5280)
 #define STATMASK 0x3f
 #else
 #define STATMASK 0x1f
@@ -349,7 +350,8 @@
                cfi_ld  sp, PT_R29, \docfi
                .endm
 
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
+#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       defined(CONFIG_CPU_LX5280)
 
                .macro  RESTORE_SOME docfi=0
                .set    push
@@ -477,7 +479,8 @@
                .macro  KMODE
                mfc0    t0, CP0_STATUS
                li      t1, ST0_CU0 | (STATMASK & ~1)
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
+#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       defined(CONFIG_CPU_LX5280)
                andi    t2, t0, ST0_IEP
                srl     t2, 2
                or      t0, t2
diff --git a/arch/mips/include/asm/traps.h b/arch/mips/include/asm/traps.h
index f41cf3ee82a7..e611a3d0ac99 100644
--- a/arch/mips/include/asm/traps.h
+++ b/arch/mips/include/asm/traps.h
@@ -39,4 +39,6 @@ extern int register_nmi_notifier(struct notifier_block *nb);
        register_nmi_notifier(&fn##_nb);                                \
 })
 
+int simulate_rdhwr_in_page_fault(struct pt_regs *regs);
+
 #endif /* _ASM_TRAPS_H */
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index f10e1e15e1c6..7fe3f3d4d4b4 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -39,12 +39,14 @@ obj-$(CONFIG_FUNCTION_TRACER)       += mcount.o ftrace.o
 sw-y                           := r4k_switch.o
 sw-$(CONFIG_CPU_R3000)         := r2300_switch.o
 sw-$(CONFIG_CPU_TX39XX)                := r2300_switch.o
+sw-$(CONFIG_CPU_LX5280)                := r2300_switch.o
 sw-$(CONFIG_CPU_CAVIUM_OCTEON) := octeon_switch.o
 obj-y                          += $(sw-y)
 
 obj-$(CONFIG_CPU_R4K_FPU)      += r4k_fpu.o
 obj-$(CONFIG_CPU_R3000)                += r2300_fpu.o
 obj-$(CONFIG_CPU_TX39XX)       += r2300_fpu.o
+obj-$(CONFIG_CPU_LX5280)       += r2300_fpu.o
 
 obj-$(CONFIG_SMP)              += smp.o
 obj-$(CONFIG_SMP_UP)           += smp-up.o
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index b2509c19cfb5..d7e39635fbe7 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -1516,6 +1516,12 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips 
*c, unsigned int cpu)
                        break;
                }
 
+               break;
+       case PRID_IMP_LX5280:
+               c->cputype = CPU_LX5280;
+               __cpu_name[cpu] = "Lexra LX5280";
+               c->options = MIPS_CPU_TLB | MIPS_CPU_LX5280_CACHE;
+               c->tlbsize = 16; // TODO Lexra: RTL8186 only. use dt?
                break;
        }
 }
diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S
index d7de8adcfcc8..ab4cb33020c5 100644
--- a/arch/mips/kernel/entry.S
+++ b/arch/mips/kernel/entry.S
@@ -102,7 +102,8 @@ restore_partial:            # restore partial frame
        SAVE_AT
        SAVE_TEMP
        LONG_L  v0, PT_STATUS(sp)
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
+#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       defined(CONFIG_CPU_LX5280)
        and     v0, ST0_IEP
 #else
        and     v0, ST0_IE
diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S
index 37b9383eacd3..7d789e20e6ea 100644
--- a/arch/mips/kernel/genex.S
+++ b/arch/mips/kernel/genex.S
@@ -165,7 +165,8 @@ NESTED(handle_int, PT_SIZE, sp)
        .set    push
        .set    noat
        mfc0    k0, CP0_STATUS
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
+#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       defined(CONFIG_CPU_LX5280)
        and     k0, ST0_IEP
        bnez    k0, 1f
 
@@ -584,7 +585,8 @@ isrdhwr:
        get_saved_sp    /* k1 := current_thread_info */
        .set    noreorder
        MFC0    k0, CP0_EPC
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
+#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       defined(CONFIG_CPU_LX5280)
        ori     k1, _THREAD_MASK
        xori    k1, _THREAD_MASK
        LONG_L  v1, TI_TP_VALUE(k1)
diff --git a/arch/mips/kernel/idle.c b/arch/mips/kernel/idle.c
index 7c246b69c545..336d1498a175 100644
--- a/arch/mips/kernel/idle.c
+++ b/arch/mips/kernel/idle.c
@@ -115,6 +115,13 @@ static void au1k_wait(void)
        : : "r" (au1k_wait), "r" (c0status));
 }
 
+static void lx5280_wait(void)
+{
+       /* Execute LX5280 'sleep' instruction */
+       asm volatile(".word 0x42000038");
+       local_irq_enable();
+}
+
 static int __initdata nowait;
 
 static int __init wait_disable(char *s)
@@ -249,6 +256,9 @@ void __init check_wait(void)
                   cpu_wait = r4k_wait;
                 */
                break;
+       case CPU_LX5280:
+               cpu_wait = lx5280_wait;
+               break;
        default:
                break;
        }
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 9670e70139fd..2f92f203dcd0 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -138,7 +138,8 @@ int copy_thread_tls(unsigned long clone_flags, unsigned 
long usp,
                p->thread.reg17 = kthread_arg;
                p->thread.reg29 = childksp;
                p->thread.reg31 = (unsigned long) ret_from_kernel_thread;
-#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
+#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX) || \
+       defined(CONFIG_CPU_LX5280)
                status = (status & ~(ST0_KUP | ST0_IEP | ST0_IEC)) |
                         ((status & (ST0_KUC | ST0_IEC)) << 2);
 #else
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 8d505a21396e..dda828c4e955 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -686,6 +686,48 @@ static int simulate_rdhwr_mm(struct pt_regs *regs, 
unsigned int opcode)
        return -1;
 }
 
+/*
+ * When most MIPS CPUs hit 'rdwhr' instruction, they raise a 'reserved
+ * instruction' exception if the instruction is unsupported.
+ * This is not the case with the LX5280 CPU, on which
+ * 'rdhwr' instruction raises a page fault.
+ * So for LX5280, we must do the 'rdhwr' simulation in the
+ * page fault handler.
+ *
+ * Returns 0 on successful simulation.
+ * Register state is not affected on failure.
+ */
+int simulate_rdhwr_in_page_fault(struct pt_regs *regs)
+{
+#ifdef CONFIG_CPU_LX5280
+       unsigned long old_epc = regs->cp0_epc;
+       unsigned long old31 = regs->regs[31];
+       unsigned int opcode = 0;
+       unsigned int __user *epc;
+
+       if (get_isa16_mode(regs->cp0_epc))
+               goto err;
+
+       epc = (unsigned int __user *)exception_epc(regs);
+       if (unlikely(get_user(opcode, epc)))
+               goto err;
+
+       if (unlikely(compute_return_epc(regs) < 0))
+               goto err;
+
+       if (!simulate_rdhwr_normal(regs, opcode))
+               return 0;  /* Success */
+
+err:
+       regs->cp0_epc = old_epc;                /* Undo skip-over.  */
+       regs->regs[31] = old31;
+
+       return -1;
+#else /* !CONFIG_CPU_LX5280 */
+       return -1;
+#endif
+}
+
 static int simulate_sync(struct pt_regs *regs, unsigned int opcode)
 {
        if ((opcode & OPCODE) == SPEC0 && (opcode & FUNC) == SYNC) {
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index 6537e022ef62..7d44d11ed9bc 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -14,6 +14,7 @@ lib-$(CONFIG_GENERIC_CSUM)    := $(filter-out csum_partial.o, 
$(lib-y))
 obj-$(CONFIG_CPU_GENERIC_DUMP_TLB) += dump_tlb.o
 obj-$(CONFIG_CPU_R3000)                += r3k_dump_tlb.o
 obj-$(CONFIG_CPU_TX39XX)       += r3k_dump_tlb.o
+obj-$(CONFIG_CPU_LX5280)       += r3k_dump_tlb.o
 
 # libgcc-style stuff needed in the kernel
 obj-y += bswapsi.o bswapdi.o multi3.o
diff --git a/arch/mips/mm/Makefile b/arch/mips/mm/Makefile
index c463bdad45c7..1b3e55c26012 100644
--- a/arch/mips/mm/Makefile
+++ b/arch/mips/mm/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_HUGETLB_PAGE)    += hugetlbpage.o
 
 obj-$(CONFIG_CPU_R4K_CACHE_TLB) += c-r4k.o cex-gen.o tlb-r4k.o
 obj-$(CONFIG_CPU_R3000)                += c-r3k.o tlb-r3k.o
+obj-$(CONFIG_CPU_LX5280)       += c-lx5280.o tlb-r3k.o
 obj-$(CONFIG_CPU_R8000)                += c-r4k.o cex-gen.o tlb-r8k.o
 obj-$(CONFIG_CPU_SB1)          += c-r4k.o cerr-sb1.o cex-sb1.o tlb-r4k.o
 obj-$(CONFIG_CPU_TX39XX)       += c-tx39.o tlb-r3k.o
diff --git a/arch/mips/mm/c-lx5280.c b/arch/mips/mm/c-lx5280.c
new file mode 100644
index 000000000000..c974be564906
--- /dev/null
+++ b/arch/mips/mm/c-lx5280.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * c-lx5280.c: Lexra LX5280 CPU cache code.
+ *
+ * Copyright (C) 2018 Yasha Cherikovsky
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/mm.h>
+#include <linux/of.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/mmu_context.h>
+#include <asm/isadep.h>
+#include <asm/io.h>
+#include <asm/bootinfo.h>
+#include <asm/cpu.h>
+
+static unsigned int icache_size, dcache_size;          /* Size in bytes */
+static unsigned int icache_lsize, dcache_lsize;                /* Size in 
bytes */
+
+
+#define _nop() \
+do { \
+       __asm__ __volatile__("nop"); \
+} while (0)
+
+
+static inline void __lx5280_flush_dcache_internal(void)
+{
+       unsigned long cctl;
+
+       cctl = read_c0_lx5280_cctl();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       write_c0_lx5280_cctl(cctl & (~LX5280_CCTL_DINVAL));
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       write_c0_lx5280_cctl(cctl | (LX5280_CCTL_DINVAL));
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+}
+
+static inline void __lx5280_flush_icache_internal(void)
+{
+       unsigned long cctl;
+
+       cctl = read_c0_lx5280_cctl();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       write_c0_lx5280_cctl(cctl & (~LX5280_CCTL_IINVAL));
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       write_c0_lx5280_cctl(cctl | (LX5280_CCTL_IINVAL));
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+       _nop();
+}
+
+static void __lx5280_flush_icache(void)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       __lx5280_flush_icache_internal();
+       local_irq_restore(flags);
+}
+
+static void __lx5280_flush_dcache(void)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       __lx5280_flush_dcache_internal();
+       local_irq_restore(flags);
+}
+
+static void lx5280_flush_icache_range(unsigned long start, unsigned long end)
+{
+       __lx5280_flush_icache();
+}
+
+static void lx5280_flush_dcache_range(unsigned long start, unsigned long end)
+{
+       __lx5280_flush_dcache();
+}
+
+static void lx5280___flush_cache_all(void)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       __lx5280_flush_dcache_internal();
+       __lx5280_flush_icache_internal();
+       local_irq_restore(flags);
+}
+
+static void lx5280_flush_cache_all(void)
+{
+       lx5280___flush_cache_all();
+}
+
+static void lx5280_flush_cache_mm(struct mm_struct *mm)
+{
+       lx5280_flush_cache_all();
+}
+
+static void lx5280_flush_cache_range(struct vm_area_struct *vma,
+                                 unsigned long start, unsigned long end)
+{
+       lx5280_flush_cache_all();
+}
+
+static void lx5280_flush_cache_page(struct vm_area_struct *vma,
+                                unsigned long addr, unsigned long pfn)
+{
+       unsigned long kaddr = KSEG0ADDR(pfn << PAGE_SHIFT);
+       struct mm_struct *mm = vma->vm_mm;
+       pgd_t *pgdp;
+       pud_t *pudp;
+       pmd_t *pmdp;
+       pte_t *ptep;
+
+       pr_debug("cpage[%08lx,%08lx]\n",
+                cpu_context(smp_processor_id(), mm), addr);
+
+       /* No ASID => no such page in the cache.  */
+       if (cpu_context(smp_processor_id(), mm) == 0)
+               return;
+
+       pgdp = pgd_offset(mm, addr);
+       pudp = pud_offset(pgdp, addr);
+       pmdp = pmd_offset(pudp, addr);
+       ptep = pte_offset(pmdp, addr);
+
+       /* Invalid => no such page in the cache.  */
+       if (!(pte_val(*ptep) & _PAGE_PRESENT))
+               return;
+
+       lx5280_flush_dcache_range(kaddr, kaddr + PAGE_SIZE);
+       lx5280_flush_icache_range(kaddr, kaddr + PAGE_SIZE);
+}
+
+static void local_lx5280_flush_data_cache_page(void *addr)
+{
+       __lx5280_flush_dcache();
+}
+
+static void lx5280_flush_data_cache_page(unsigned long addr)
+{
+       __lx5280_flush_dcache();
+}
+
+static void lx5280_flush_cache_sigtramp(unsigned long addr)
+{
+       lx5280_flush_cache_all();
+}
+
+static void lx5280_flush_kernel_vmap_range(unsigned long vaddr, int size)
+{
+       lx5280_flush_cache_all();
+}
+
+static void lx5280_dma_cache_wback_inv(unsigned long start, unsigned long size)
+{
+       lx5280_flush_dcache_range(start, start + size);
+}
+
+static u32 of_property_read_u32_or_panic(struct device_node *np,
+               const char *propname)
+{
+       u32 out_value;
+
+       if (of_property_read_u32(np, propname, &out_value))
+               panic("Unable to get %s from devicetree", propname);
+       return out_value;
+}
+
+void lx5280_cache_init(void)
+{
+       extern void build_clear_page(void);
+       extern void build_copy_page(void);
+       struct device_node *np;
+
+       np = of_get_cpu_node(0, NULL);
+       if (!np)
+               panic("Unable to find cpu node in devicetree");
+
+       dcache_size = of_property_read_u32_or_panic(np, "d-cache-size");
+       icache_size = of_property_read_u32_or_panic(np, "i-cache-size");
+       dcache_lsize = of_property_read_u32_or_panic(np, "d-cache-line-size");
+       icache_lsize = of_property_read_u32_or_panic(np, "i-cache-line-size");
+
+       of_node_put(np);
+
+       current_cpu_data.dcache.linesz = dcache_lsize;
+       current_cpu_data.icache.linesz = icache_lsize;
+
+       flush_cache_all = lx5280_flush_cache_all;
+       __flush_cache_all = lx5280___flush_cache_all;
+       flush_cache_mm = lx5280_flush_cache_mm;
+       flush_cache_range = lx5280_flush_cache_range;
+       flush_cache_page = lx5280_flush_cache_page;
+       flush_icache_range = lx5280_flush_icache_range;
+       local_flush_icache_range = lx5280_flush_icache_range;
+       __flush_icache_user_range = lx5280_flush_icache_range;
+       __local_flush_icache_user_range = lx5280_flush_icache_range;
+
+       __flush_kernel_vmap_range = lx5280_flush_kernel_vmap_range;
+
+       flush_cache_sigtramp = lx5280_flush_cache_sigtramp;
+       local_flush_data_cache_page = local_lx5280_flush_data_cache_page;
+       flush_data_cache_page = lx5280_flush_data_cache_page;
+
+       _dma_cache_wback_inv = lx5280_dma_cache_wback_inv;
+       _dma_cache_wback = lx5280_dma_cache_wback_inv;
+       _dma_cache_inv = lx5280_dma_cache_wback_inv;
+
+       pr_info("Primary instruction cache %dkB, linesize %d bytes.\n",
+               icache_size >> 10, icache_lsize);
+       pr_info("Primary data cache %dkB, linesize %d bytes.\n",
+               dcache_size >> 10, dcache_lsize);
+
+       build_clear_page();
+       build_copy_page();
+}
diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
index 0d3c656feba0..873e62cfc821 100644
--- a/arch/mips/mm/cache.c
+++ b/arch/mips/mm/cache.c
@@ -234,6 +234,12 @@ void cpu_cache_init(void)
                octeon_cache_init();
        }
 
+       if (cpu_has_lx5280_cache) {
+               extern void __weak lx5280_cache_init(void);
+
+               lx5280_cache_init();
+       }
+
        setup_protection_map();
 }
 
diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c
index 5f71f2b903b7..159fd009f5bd 100644
--- a/arch/mips/mm/fault.c
+++ b/arch/mips/mm/fault.c
@@ -26,6 +26,7 @@
 #include <asm/mmu_context.h>
 #include <asm/ptrace.h>
 #include <asm/highmem.h>               /* For VMALLOC_END */
+#include <asm/traps.h>
 #include <linux/kdebug.h>
 
 int show_unhandled_signals = 1;
@@ -204,6 +205,9 @@ static void __kprobes __do_page_fault(struct pt_regs *regs, 
unsigned long write,
 bad_area_nosemaphore:
        /* User mode accesses just cause a SIGSEGV */
        if (user_mode(regs)) {
+               if (!simulate_rdhwr_in_page_fault(regs))
+                       return;
+
                tsk->thread.cp0_badvaddr = address;
                tsk->thread.error_code = write;
                if (show_unhandled_signals &&
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 79b9f2ad3ff5..95e745795724 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -2616,6 +2616,7 @@ void build_tlb_refill_handler(void)
        case CPU_TX3912:
        case CPU_TX3922:
        case CPU_TX3927:
+       case CPU_LX5280:
 #ifndef CONFIG_MIPS_PGD_C0_CONTEXT
                if (cpu_has_local_ebase)
                        build_r3000_tlb_refill_handler();
-- 
2.19.0

Reply via email to