[RFC 1/5] MIPS: Add support for the Lexra LX5280 CPU

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.che3@xxxxxxxxx>
Cc: Ralf Baechle <ralf@xxxxxxxxxxxxxx>
Cc: Paul Burton <paul.burton@xxxxxxxx>
Cc: James Hogan <jhogan@xxxxxxxxxx>
Cc: linux-mips@xxxxxxxxxxxxxx
Cc: linux-kernel@xxxxxxxxxxxxxxx
---
 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



[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux