Commit b0bb1269 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'riscv-for-linus-5.2-mw2' of...

Merge tag 'riscv-for-linus-5.2-mw2' of git://git.kernel.org/pub/scm/linux/kernel/git/palmer/riscv-linux

Pull RISC-V updates from Palmer Dabbelt:
 "This contains an assortment of RISC-V related patches that I'd like to
  target for the 5.2 merge window. Most of the patches are cleanups, but
  there are a handful of user-visible changes:

   - The nosmp and nr_cpus command-line arguments are now supported,
     which work like normal.

   - The SBI console no longer installs itself as a preferred console,
     we rely on standard mechanisms (/chosen, command-line, hueristics)
     instead.

   - sfence_remove_sfence_vma{,_asid} now pass their arguments along to
     the SBI call.

   - Modules now support BUG().

   - A missing sfence.vma during boot has been added. This bug only
     manifests during boot.

   - The arch/riscv support for SiFive's L2 cache controller has been
     merged, which should un-block the EDAC framework work.

  I've only tested this on QEMU again, as I didn't have time to get
  things running on the Unleashed. The latest master from this morning
  merges in cleanly and passes the tests as well"

* tag 'riscv-for-linus-5.2-mw2' of git://git.kernel.org/pub/scm/linux/kernel/git/palmer/riscv-linux: (31 commits)
  riscv: fix locking violation in page fault handler
  RISC-V: sifive_l2_cache: Add L2 cache controller driver for SiFive SoCs
  RISC-V: Add DT documentation for SiFive L2 Cache Controller
  RISC-V: Avoid using invalid intermediate translations
  riscv: Support BUG() in kernel module
  riscv: Add the support for c.ebreak check in is_valid_bugaddr()
  riscv: support trap-based WARN()
  riscv: fix sbi_remote_sfence_vma{,_asid}.
  riscv: move switch_mm to its own file
  riscv: move flush_icache_{all,mm} to cacheflush.c
  tty: Don't force RISCV SBI console as preferred console
  RISC-V: Access CSRs using CSR numbers
  RISC-V: Add interrupt related SCAUSE defines in asm/csr.h
  RISC-V: Use tabs to align macro values in asm/csr.h
  RISC-V: Fix minor checkpatch issues.
  RISC-V: Support nr_cpus command line option.
  RISC-V: Implement nosmp commandline option.
  RISC-V: Add RISC-V specific arch_match_cpu_phys_id
  riscv: vdso: drop unnecessary cc-ldoption
  riscv: call pm_power_off from machine_halt / machine_power_off
  ...
parents 72cf0b07 8fef9900
SiFive L2 Cache Controller
--------------------------
The SiFive Level 2 Cache Controller is used to provide access to fast copies
of memory for masters in a Core Complex. The Level 2 Cache Controller also
acts as directory-based coherency manager.
All the properties in ePAPR/DeviceTree specification applies for this platform
Required Properties:
--------------------
- compatible: Should be "sifive,fu540-c000-ccache" and "cache"
- cache-block-size: Specifies the block size in bytes of the cache.
Should be 64
- cache-level: Should be set to 2 for a level 2 cache
- cache-sets: Specifies the number of associativity sets of the cache.
Should be 1024
- cache-size: Specifies the size in bytes of the cache. Should be 2097152
- cache-unified: Specifies the cache is a unified cache
- interrupts: Must contain 3 entries (DirError, DataError and DataFail signals)
- reg: Physical base address and size of L2 cache controller registers map
Optional Properties:
--------------------
- next-level-cache: phandle to the next level cache if present.
- memory-region: reference to the reserved-memory for the L2 Loosely Integrated
Memory region. The reserved memory node should be defined as per the bindings
in reserved-memory.txt
Example:
cache-controller@2010000 {
compatible = "sifive,fu540-c000-ccache", "cache";
cache-block-size = <64>;
cache-level = <2>;
cache-sets = <1024>;
cache-size = <2097152>;
cache-unified;
interrupt-parent = <&plic0>;
interrupts = <1 2 3>;
reg = <0x0 0x2010000 0x0 0x1000>;
next-level-cache = <&L25 &L40 &L36>;
memory-region = <&l2_lim>;
};
...@@ -27,7 +27,7 @@ config RISCV ...@@ -27,7 +27,7 @@ config RISCV
select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNCPY_FROM_USER
select GENERIC_STRNLEN_USER select GENERIC_STRNLEN_USER
select GENERIC_SMP_IDLE_THREAD select GENERIC_SMP_IDLE_THREAD
select GENERIC_ATOMIC64 if !64BIT || !RISCV_ISA_A select GENERIC_ATOMIC64 if !64BIT
select HAVE_ARCH_AUDITSYSCALL select HAVE_ARCH_AUDITSYSCALL
select HAVE_MEMBLOCK_NODE_MAP select HAVE_MEMBLOCK_NODE_MAP
select HAVE_DMA_CONTIGUOUS select HAVE_DMA_CONTIGUOUS
...@@ -35,7 +35,6 @@ config RISCV ...@@ -35,7 +35,6 @@ config RISCV
select HAVE_PERF_EVENTS select HAVE_PERF_EVENTS
select HAVE_SYSCALL_TRACEPOINTS select HAVE_SYSCALL_TRACEPOINTS
select IRQ_DOMAIN select IRQ_DOMAIN
select RISCV_ISA_A if SMP
select SPARSE_IRQ select SPARSE_IRQ
select SYSCTL_EXCEPTION_TRACE select SYSCTL_EXCEPTION_TRACE
select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRACEHOOK
...@@ -195,9 +194,6 @@ config RISCV_ISA_C ...@@ -195,9 +194,6 @@ config RISCV_ISA_C
If you don't know what to do here, say Y. If you don't know what to do here, say Y.
config RISCV_ISA_A
def_bool y
menu "supported PMU type" menu "supported PMU type"
depends on PERF_EVENTS depends on PERF_EVENTS
......
...@@ -39,9 +39,8 @@ endif ...@@ -39,9 +39,8 @@ endif
KBUILD_CFLAGS += -Wall KBUILD_CFLAGS += -Wall
# ISA string setting # ISA string setting
riscv-march-$(CONFIG_ARCH_RV32I) := rv32im riscv-march-$(CONFIG_ARCH_RV32I) := rv32ima
riscv-march-$(CONFIG_ARCH_RV64I) := rv64im riscv-march-$(CONFIG_ARCH_RV64I) := rv64ima
riscv-march-$(CONFIG_RISCV_ISA_A) := $(riscv-march-y)a
riscv-march-$(CONFIG_FPU) := $(riscv-march-y)fd riscv-march-$(CONFIG_FPU) := $(riscv-march-y)fd
riscv-march-$(CONFIG_RISCV_ISA_C) := $(riscv-march-y)c riscv-march-$(CONFIG_RISCV_ISA_C) := $(riscv-march-y)c
KBUILD_CFLAGS += -march=$(subst fd,,$(riscv-march-y)) KBUILD_CFLAGS += -march=$(subst fd,,$(riscv-march-y))
......
...@@ -4,6 +4,7 @@ generic-y += compat.h ...@@ -4,6 +4,7 @@ generic-y += compat.h
generic-y += cputime.h generic-y += cputime.h
generic-y += device.h generic-y += device.h
generic-y += div64.h generic-y += div64.h
generic-y += extable.h
generic-y += dma.h generic-y += dma.h
generic-y += dma-contiguous.h generic-y += dma-contiguous.h
generic-y += dma-mapping.h generic-y += dma-mapping.h
......
...@@ -21,7 +21,12 @@ ...@@ -21,7 +21,12 @@
#include <asm/asm.h> #include <asm/asm.h>
#ifdef CONFIG_GENERIC_BUG #ifdef CONFIG_GENERIC_BUG
#define __BUG_INSN _AC(0x00100073, UL) /* ebreak */ #define __INSN_LENGTH_MASK _UL(0x3)
#define __INSN_LENGTH_32 _UL(0x3)
#define __COMPRESSED_INSN_MASK _UL(0xffff)
#define __BUG_INSN_32 _UL(0x00100073) /* ebreak */
#define __BUG_INSN_16 _UL(0x9002) /* c.ebreak */
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
typedef u32 bug_insn_t; typedef u32 bug_insn_t;
...@@ -38,38 +43,46 @@ typedef u32 bug_insn_t; ...@@ -38,38 +43,46 @@ typedef u32 bug_insn_t;
#define __BUG_ENTRY \ #define __BUG_ENTRY \
__BUG_ENTRY_ADDR "\n\t" \ __BUG_ENTRY_ADDR "\n\t" \
__BUG_ENTRY_FILE "\n\t" \ __BUG_ENTRY_FILE "\n\t" \
RISCV_SHORT " %1" RISCV_SHORT " %1\n\t" \
RISCV_SHORT " %2"
#else #else
#define __BUG_ENTRY \ #define __BUG_ENTRY \
__BUG_ENTRY_ADDR __BUG_ENTRY_ADDR "\n\t" \
RISCV_SHORT " %2"
#endif #endif
#define BUG() \ #define __BUG_FLAGS(flags) \
do { \ do { \
__asm__ __volatile__ ( \ __asm__ __volatile__ ( \
"1:\n\t" \ "1:\n\t" \
"ebreak\n" \ "ebreak\n" \
".pushsection __bug_table,\"a\"\n\t" \ ".pushsection __bug_table,\"aw\"\n\t" \
"2:\n\t" \ "2:\n\t" \
__BUG_ENTRY "\n\t" \ __BUG_ENTRY "\n\t" \
".org 2b + %2\n\t" \ ".org 2b + %3\n\t" \
".popsection" \ ".popsection" \
: \ : \
: "i" (__FILE__), "i" (__LINE__), \ : "i" (__FILE__), "i" (__LINE__), \
"i" (sizeof(struct bug_entry))); \ "i" (flags), \
unreachable(); \ "i" (sizeof(struct bug_entry))); \
} while (0) } while (0)
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
#else /* CONFIG_GENERIC_BUG */ #else /* CONFIG_GENERIC_BUG */
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#define BUG() \ #define __BUG_FLAGS(flags) do { \
do { \
__asm__ __volatile__ ("ebreak\n"); \ __asm__ __volatile__ ("ebreak\n"); \
unreachable(); \
} while (0) } while (0)
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
#endif /* CONFIG_GENERIC_BUG */ #endif /* CONFIG_GENERIC_BUG */
#define BUG() do { \
__BUG_FLAGS(0); \
unreachable(); \
} while (0)
#define __WARN_FLAGS(flags) __BUG_FLAGS(BUGFLAG_WARNING|(flags))
#define HAVE_ARCH_BUG #define HAVE_ARCH_BUG
#include <asm-generic/bug.h> #include <asm-generic/bug.h>
......
...@@ -47,7 +47,7 @@ static inline void flush_dcache_page(struct page *page) ...@@ -47,7 +47,7 @@ static inline void flush_dcache_page(struct page *page)
#else /* CONFIG_SMP */ #else /* CONFIG_SMP */
#define flush_icache_all() sbi_remote_fence_i(NULL) void flush_icache_all(void);
void flush_icache_mm(struct mm_struct *mm, bool local); void flush_icache_mm(struct mm_struct *mm, bool local);
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
......
...@@ -14,64 +14,95 @@ ...@@ -14,64 +14,95 @@
#ifndef _ASM_RISCV_CSR_H #ifndef _ASM_RISCV_CSR_H
#define _ASM_RISCV_CSR_H #define _ASM_RISCV_CSR_H
#include <asm/asm.h>
#include <linux/const.h> #include <linux/const.h>
/* Status register flags */ /* Status register flags */
#define SR_SIE _AC(0x00000002, UL) /* Supervisor Interrupt Enable */ #define SR_SIE _AC(0x00000002, UL) /* Supervisor Interrupt Enable */
#define SR_SPIE _AC(0x00000020, UL) /* Previous Supervisor IE */ #define SR_SPIE _AC(0x00000020, UL) /* Previous Supervisor IE */
#define SR_SPP _AC(0x00000100, UL) /* Previously Supervisor */ #define SR_SPP _AC(0x00000100, UL) /* Previously Supervisor */
#define SR_SUM _AC(0x00040000, UL) /* Supervisor may access User Memory */ #define SR_SUM _AC(0x00040000, UL) /* Supervisor User Memory Access */
#define SR_FS _AC(0x00006000, UL) /* Floating-point Status */ #define SR_FS _AC(0x00006000, UL) /* Floating-point Status */
#define SR_FS_OFF _AC(0x00000000, UL) #define SR_FS_OFF _AC(0x00000000, UL)
#define SR_FS_INITIAL _AC(0x00002000, UL) #define SR_FS_INITIAL _AC(0x00002000, UL)
#define SR_FS_CLEAN _AC(0x00004000, UL) #define SR_FS_CLEAN _AC(0x00004000, UL)
#define SR_FS_DIRTY _AC(0x00006000, UL) #define SR_FS_DIRTY _AC(0x00006000, UL)
#define SR_XS _AC(0x00018000, UL) /* Extension Status */ #define SR_XS _AC(0x00018000, UL) /* Extension Status */
#define SR_XS_OFF _AC(0x00000000, UL) #define SR_XS_OFF _AC(0x00000000, UL)
#define SR_XS_INITIAL _AC(0x00008000, UL) #define SR_XS_INITIAL _AC(0x00008000, UL)
#define SR_XS_CLEAN _AC(0x00010000, UL) #define SR_XS_CLEAN _AC(0x00010000, UL)
#define SR_XS_DIRTY _AC(0x00018000, UL) #define SR_XS_DIRTY _AC(0x00018000, UL)
#ifndef CONFIG_64BIT #ifndef CONFIG_64BIT
#define SR_SD _AC(0x80000000, UL) /* FS/XS dirty */ #define SR_SD _AC(0x80000000, UL) /* FS/XS dirty */
#else #else
#define SR_SD _AC(0x8000000000000000, UL) /* FS/XS dirty */ #define SR_SD _AC(0x8000000000000000, UL) /* FS/XS dirty */
#endif #endif
/* SATP flags */ /* SATP flags */
#if __riscv_xlen == 32 #ifndef CONFIG_64BIT
#define SATP_PPN _AC(0x003FFFFF, UL) #define SATP_PPN _AC(0x003FFFFF, UL)
#define SATP_MODE_32 _AC(0x80000000, UL) #define SATP_MODE_32 _AC(0x80000000, UL)
#define SATP_MODE SATP_MODE_32 #define SATP_MODE SATP_MODE_32
#else #else
#define SATP_PPN _AC(0x00000FFFFFFFFFFF, UL) #define SATP_PPN _AC(0x00000FFFFFFFFFFF, UL)
#define SATP_MODE_39 _AC(0x8000000000000000, UL) #define SATP_MODE_39 _AC(0x8000000000000000, UL)
#define SATP_MODE SATP_MODE_39 #define SATP_MODE SATP_MODE_39
#endif #endif
/* Interrupt Enable and Interrupt Pending flags */ /* SCAUSE */
#define SIE_SSIE _AC(0x00000002, UL) /* Software Interrupt Enable */ #define SCAUSE_IRQ_FLAG (_AC(1, UL) << (__riscv_xlen - 1))
#define SIE_STIE _AC(0x00000020, UL) /* Timer Interrupt Enable */
#define SIE_SEIE _AC(0x00000200, UL) /* External Interrupt Enable */ #define IRQ_U_SOFT 0
#define IRQ_S_SOFT 1
#define EXC_INST_MISALIGNED 0 #define IRQ_M_SOFT 3
#define EXC_INST_ACCESS 1 #define IRQ_U_TIMER 4
#define EXC_BREAKPOINT 3 #define IRQ_S_TIMER 5
#define EXC_LOAD_ACCESS 5 #define IRQ_M_TIMER 7
#define EXC_STORE_ACCESS 7 #define IRQ_U_EXT 8
#define EXC_SYSCALL 8 #define IRQ_S_EXT 9
#define EXC_INST_PAGE_FAULT 12 #define IRQ_M_EXT 11
#define EXC_LOAD_PAGE_FAULT 13
#define EXC_STORE_PAGE_FAULT 15 #define EXC_INST_MISALIGNED 0
#define EXC_INST_ACCESS 1
#define EXC_BREAKPOINT 3
#define EXC_LOAD_ACCESS 5
#define EXC_STORE_ACCESS 7
#define EXC_SYSCALL 8
#define EXC_INST_PAGE_FAULT 12
#define EXC_LOAD_PAGE_FAULT 13
#define EXC_STORE_PAGE_FAULT 15
/* SIE (Interrupt Enable) and SIP (Interrupt Pending) flags */
#define SIE_SSIE (_AC(0x1, UL) << IRQ_S_SOFT)
#define SIE_STIE (_AC(0x1, UL) << IRQ_S_TIMER)
#define SIE_SEIE (_AC(0x1, UL) << IRQ_S_EXT)
#define CSR_CYCLE 0xc00
#define CSR_TIME 0xc01
#define CSR_INSTRET 0xc02
#define CSR_SSTATUS 0x100
#define CSR_SIE 0x104
#define CSR_STVEC 0x105
#define CSR_SCOUNTEREN 0x106
#define CSR_SSCRATCH 0x140
#define CSR_SEPC 0x141
#define CSR_SCAUSE 0x142
#define CSR_STVAL 0x143
#define CSR_SIP 0x144
#define CSR_SATP 0x180
#define CSR_CYCLEH 0xc80
#define CSR_TIMEH 0xc81
#define CSR_INSTRETH 0xc82
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#define csr_swap(csr, val) \ #define csr_swap(csr, val) \
({ \ ({ \
unsigned long __v = (unsigned long)(val); \ unsigned long __v = (unsigned long)(val); \
__asm__ __volatile__ ("csrrw %0, " #csr ", %1" \ __asm__ __volatile__ ("csrrw %0, " __ASM_STR(csr) ", %1"\
: "=r" (__v) : "rK" (__v) \ : "=r" (__v) : "rK" (__v) \
: "memory"); \ : "memory"); \
__v; \ __v; \
...@@ -80,7 +111,7 @@ ...@@ -80,7 +111,7 @@
#define csr_read(csr) \ #define csr_read(csr) \
({ \ ({ \
register unsigned long __v; \ register unsigned long __v; \
__asm__ __volatile__ ("csrr %0, " #csr \ __asm__ __volatile__ ("csrr %0, " __ASM_STR(csr) \
: "=r" (__v) : \ : "=r" (__v) : \
: "memory"); \ : "memory"); \
__v; \ __v; \
...@@ -89,7 +120,7 @@ ...@@ -89,7 +120,7 @@
#define csr_write(csr, val) \ #define csr_write(csr, val) \
({ \ ({ \
unsigned long __v = (unsigned long)(val); \ unsigned long __v = (unsigned long)(val); \
__asm__ __volatile__ ("csrw " #csr ", %0" \ __asm__ __volatile__ ("csrw " __ASM_STR(csr) ", %0" \
: : "rK" (__v) \ : : "rK" (__v) \
: "memory"); \ : "memory"); \
}) })
...@@ -97,7 +128,7 @@ ...@@ -97,7 +128,7 @@
#define csr_read_set(csr, val) \ #define csr_read_set(csr, val) \
({ \ ({ \
unsigned long __v = (unsigned long)(val); \ unsigned long __v = (unsigned long)(val); \
__asm__ __volatile__ ("csrrs %0, " #csr ", %1" \ __asm__ __volatile__ ("csrrs %0, " __ASM_STR(csr) ", %1"\
: "=r" (__v) : "rK" (__v) \ : "=r" (__v) : "rK" (__v) \
: "memory"); \ : "memory"); \
__v; \ __v; \
...@@ -106,7 +137,7 @@ ...@@ -106,7 +137,7 @@
#define csr_set(csr, val) \ #define csr_set(csr, val) \
({ \ ({ \
unsigned long __v = (unsigned long)(val); \ unsigned long __v = (unsigned long)(val); \
__asm__ __volatile__ ("csrs " #csr ", %0" \ __asm__ __volatile__ ("csrs " __ASM_STR(csr) ", %0" \
: : "rK" (__v) \ : : "rK" (__v) \
: "memory"); \ : "memory"); \
}) })
...@@ -114,7 +145,7 @@ ...@@ -114,7 +145,7 @@
#define csr_read_clear(csr, val) \ #define csr_read_clear(csr, val) \
({ \ ({ \
unsigned long __v = (unsigned long)(val); \ unsigned long __v = (unsigned long)(val); \
__asm__ __volatile__ ("csrrc %0, " #csr ", %1" \ __asm__ __volatile__ ("csrrc %0, " __ASM_STR(csr) ", %1"\
: "=r" (__v) : "rK" (__v) \ : "=r" (__v) : "rK" (__v) \
: "memory"); \ : "memory"); \
__v; \ __v; \
...@@ -123,7 +154,7 @@ ...@@ -123,7 +154,7 @@
#define csr_clear(csr, val) \ #define csr_clear(csr, val) \
({ \ ({ \
unsigned long __v = (unsigned long)(val); \ unsigned long __v = (unsigned long)(val); \
__asm__ __volatile__ ("csrc " #csr ", %0" \ __asm__ __volatile__ ("csrc " __ASM_STR(csr) ", %0" \
: : "rK" (__v) \ : : "rK" (__v) \
: "memory"); \ : "memory"); \
}) })
......
...@@ -27,13 +27,7 @@ ...@@ -27,13 +27,7 @@
#define ELF_CLASS ELFCLASS32 #define ELF_CLASS ELFCLASS32
#endif #endif
#if defined(__LITTLE_ENDIAN)
#define ELF_DATA ELFDATA2LSB #define ELF_DATA ELFDATA2LSB
#elif defined(__BIG_ENDIAN)
#define ELF_DATA ELFDATA2MSB
#else
#error "Unknown endianness"
#endif
/* /*
* This is used to ensure we don't load something for the wrong architecture. * This is used to ensure we don't load something for the wrong architecture.
......
...@@ -7,18 +7,6 @@ ...@@ -7,18 +7,6 @@
#ifndef _ASM_FUTEX_H #ifndef _ASM_FUTEX_H
#define _ASM_FUTEX_H #define _ASM_FUTEX_H
#ifndef CONFIG_RISCV_ISA_A
/*
* Use the generic interrupt disabling versions if the A extension
* is not supported.
*/
#ifdef CONFIG_SMP
#error "Can't support generic futex calls without A extension on SMP"
#endif
#include <asm-generic/futex.h>
#else /* CONFIG_RISCV_ISA_A */
#include <linux/futex.h> #include <linux/futex.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/errno.h> #include <linux/errno.h>
...@@ -124,5 +112,4 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, ...@@ -124,5 +112,4 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
return ret; return ret;
} }
#endif /* CONFIG_RISCV_ISA_A */
#endif /* _ASM_FUTEX_H */ #endif /* _ASM_FUTEX_H */
...@@ -21,25 +21,25 @@ ...@@ -21,25 +21,25 @@
/* read interrupt enabled status */ /* read interrupt enabled status */
static inline unsigned long arch_local_save_flags(void) static inline unsigned long arch_local_save_flags(void)
{ {
return csr_read(sstatus); return csr_read(CSR_SSTATUS);
} }
/* unconditionally enable interrupts */ /* unconditionally enable interrupts */
static inline void arch_local_irq_enable(void) static inline void arch_local_irq_enable(void)
{ {
csr_set(sstatus, SR_SIE); csr_set(CSR_SSTATUS, SR_SIE);
} }
/* unconditionally disable interrupts */ /* unconditionally disable interrupts */
static inline void arch_local_irq_disable(void) static inline void arch_local_irq_disable(void)
{ {
csr_clear(sstatus, SR_SIE); csr_clear(CSR_SSTATUS, SR_SIE);
} }
/* get status and disable interrupts */ /* get status and disable interrupts */
static inline unsigned long arch_local_irq_save(void) static inline unsigned long arch_local_irq_save(void)
{ {
return csr_read_clear(sstatus, SR_SIE); return csr_read_clear(CSR_SSTATUS, SR_SIE);
} }
/* test flags */ /* test flags */
...@@ -57,7 +57,7 @@ static inline int arch_irqs_disabled(void) ...@@ -57,7 +57,7 @@ static inline int arch_irqs_disabled(void)
/* set interrupt enabled status */ /* set interrupt enabled status */
static inline void arch_local_irq_restore(unsigned long flags) static inline void arch_local_irq_restore(unsigned long flags)
{ {
csr_set(sstatus, flags & SR_SIE); csr_set(CSR_SSTATUS, flags & SR_SIE);
} }
#endif /* _ASM_RISCV_IRQFLAGS_H */ #endif /* _ASM_RISCV_IRQFLAGS_H */
...@@ -20,8 +20,6 @@ ...@@ -20,8 +20,6 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <asm/tlbflush.h>
#include <asm/cacheflush.h>
static inline void enter_lazy_tlb(struct mm_struct *mm, static inline void enter_lazy_tlb(struct mm_struct *mm,
struct task_struct *task) struct task_struct *task)
...@@ -39,61 +37,8 @@ static inline void destroy_context(struct mm_struct *mm) ...@@ -39,61 +37,8 @@ static inline void destroy_context(struct mm_struct *mm)
{ {
} }
/* void switch_mm(struct mm_struct *prev, struct mm_struct *next,
* When necessary, performs a deferred icache flush for the given MM context, struct task_struct *task);
* on the local CPU. RISC-V has no direct mechanism for instruction cache
* shoot downs, so instead we send an IPI that informs the remote harts they
* need to flush their local instruction caches. To avoid pathologically slow
* behavior in a common case (a bunch of single-hart processes on a many-hart
* machine, ie 'make -j') we avoid the IPIs for harts that are not currently
* executing a MM context and instead schedule a deferred local instruction
* cache flush to be performed before execution resumes on each hart. This
* actually performs that local instruction cache flush, which implicitly only
* refers to the current hart.
*/
static inline void flush_icache_deferred(struct mm_struct *mm)
{
#ifdef CONFIG_SMP
unsigned int cpu = smp_processor_id();
cpumask_t *mask = &mm->context.icache_stale_mask;
if (cpumask_test_cpu(cpu, mask)) {
cpumask_clear_cpu(cpu, mask);
/*
* Ensure the remote hart's writes are visible to this hart.
* This pairs with a barrier in flush_icache_mm.
*/
smp_mb();
local_flush_icache_all();
}
#endif
}
static inline void switch_mm(struct mm_struct *prev,
struct mm_struct *next, struct task_struct *task)
{
if (likely(prev != next)) {
/*
* Mark the current MM context as inactive, and the next as
* active. This is at least used by the icache flushing
* routines in order to determine who should
*/
unsigned int cpu = smp_processor_id();
cpumask_clear_cpu(cpu, mm_cpumask(prev));
cpumask_set_cpu(cpu, mm_cpumask(next));
/*
* Use the old spbtr name instead of using the current satp
* name to support binutils 2.29 which doesn't know about the
* privileged ISA 1.10 yet.
*/
csr_write(sptbr, virt_to_pfn(next->pgd) | SATP_MODE);
local_flush_tlb_all();
flush_icache_deferred(next);
}
}
static inline void activate_mm(struct mm_struct *prev, static inline void activate_mm(struct mm_struct *prev,
struct mm_struct *next) struct mm_struct *next)
......
...@@ -70,47 +70,38 @@ struct pt_regs { ...@@ -70,47 +70,38 @@ struct pt_regs {
/* Helpers for working with the instruction pointer */ /* Helpers for working with the instruction pointer */
#define GET_IP(regs) ((regs)->sepc)
#define SET_IP(regs, val) (GET_IP(regs) = (val))
static inline unsigned long instruction_pointer(struct pt_regs *regs) static inline unsigned long instruction_pointer(struct pt_regs *regs)
{ {
return GET_IP(regs); return regs->sepc;
} }
static inline void instruction_pointer_set(struct pt_regs *regs, static inline void instruction_pointer_set(struct pt_regs *regs,
unsigned long val) unsigned long val)
{ {
SET_IP(regs, val); regs->sepc = val;
} }
#define profile_pc(regs) instruction_pointer(regs) #define profile_pc(regs) instruction_pointer(regs)
/* Helpers for working with the user stack pointer */ /* Helpers for working with the user stack pointer */
#define GET_USP(regs) ((regs)->sp)
#define SET_USP(regs, val) (GET_USP(regs) = (val))
static inline unsigned long user_stack_pointer(struct pt_regs *regs) static inline unsigned long user_stack_pointer(struct pt_regs *regs)
{ {
return GET_USP(regs); return regs->sp;
} }
static inline void user_stack_pointer_set(struct pt_regs *regs, static inline void user_stack_pointer_set(struct pt_regs *regs,
unsigned long val) unsigned long val)
{ {
SET_USP(regs, val); regs->sp = val;
} }
/* Helpers for working with the frame pointer */ /* Helpers for working with the frame pointer */
#define GET_FP(regs) ((regs)->s0)
#define SET_FP(regs, val) (GET_FP(regs) = (val))
static inline unsigned long frame_pointer(struct pt_regs *regs) static inline unsigned long frame_pointer(struct pt_regs *regs)
{ {
return GET_FP(regs); return regs->s0;
} }
static inline void frame_pointer_set(struct pt_regs *regs, static inline void frame_pointer_set(struct pt_regs *regs,
unsigned long val) unsigned long val)
{ {
SET_FP(regs, val); regs->s0 = val;
} }
static inline unsigned long regs_return_value(struct pt_regs *regs) static inline unsigned long regs_return_value(struct pt_regs *regs)
......
...@@ -26,22 +26,27 @@ ...@@ -26,22 +26,27 @@
#define SBI_REMOTE_SFENCE_VMA_ASID 7 #define SBI_REMOTE_SFENCE_VMA_ASID 7
#define SBI_SHUTDOWN 8 #define SBI_SHUTDOWN 8
#define SBI_CALL(which, arg0, arg1, arg2) ({ \ #define SBI_CALL(which, arg0, arg1, arg2, arg3) ({ \
register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); \ register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); \
register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \ register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \
register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \ register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \
register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3); \
register uintptr_t a7 asm ("a7") = (uintptr_t)(which); \ register uintptr_t a7 asm ("a7") = (uintptr_t)(which); \
asm volatile ("ecall" \ asm volatile ("ecall" \
: "+r" (a0) \ : "+r" (a0) \
: "r" (a1), "r" (a2), "r" (a7) \ : "r" (a1), "r" (a2), "r" (a3), "r" (a7) \
: "memory"); \ : "memory"); \
a0; \ a0; \
}) })
/* Lazy implementations until SBI is finalized */ /* Lazy implementations until SBI is finalized */
#define SBI_CALL_0(which) SBI_CALL(which, 0, 0, 0) #define SBI_CALL_0(which) SBI_CALL(which, 0, 0, 0, 0)
#define SBI_CALL_1(which, arg0) SBI_CALL(which, arg0, 0, 0) #define SBI_CALL_1(which, arg0) SBI_CALL(which, arg0, 0, 0, 0)
#define SBI_CALL_2(which, arg0, arg1) SBI_CALL(which, arg0, arg1, 0) #define SBI_CALL_2(which, arg0, arg1) SBI_CALL(which, arg0, arg1, 0, 0)
#define SBI_CALL_3(which, arg0, arg1, arg2) \
SBI_CALL(which, arg0, arg1, arg2, 0)
#define SBI_CALL_4(which, arg0, arg1, arg2, arg3) \
SBI_CALL(which, arg0, arg1, arg2, arg3)
static inline void sbi_console_putchar(int ch) static inline void sbi_console_putchar(int ch)
{ {
...@@ -86,7 +91,7 @@ static inline void sbi_remote_sfence_vma(const unsigned long *hart_mask, ...@@ -86,7 +91,7 @@ static inline void sbi_remote_sfence_vma(const unsigned long *hart_mask,
unsigned long start, unsigned long start,
unsigned long size) unsigned long size)
{ {
SBI_CALL_1(SBI_REMOTE_SFENCE_VMA, hart_mask); SBI_CALL_3(SBI_REMOTE_SFENCE_VMA, hart_mask, start, size);
} }
static inline void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask, static inline void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,
...@@ -94,7 +99,7 @@ static inline void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask, ...@@ -94,7 +99,7 @@ static inline void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,
unsigned long size, unsigned long size,
unsigned long asid) unsigned long asid)
{ {
SBI_CALL_1(SBI_REMOTE_SFENCE_VMA_ASID, hart_mask); SBI_CALL_4(SBI_REMOTE_SFENCE_VMA_ASID, hart_mask, start, size, asid);
} }
#endif #endif
/* SPDX-License-Identifier: GPL-2.0 */
/*
* SiFive L2 Cache Controller header file
*
*/
#ifndef _ASM_RISCV_SIFIVE_L2_CACHE_H
#define _ASM_RISCV_SIFIVE_L2_CACHE_H
extern int register_sifive_l2_error_notifier(struct notifier_block *nb);
extern int unregister_sifive_l2_error_notifier(struct notifier_block *nb);
#define SIFIVE_L2_ERR_TYPE_CE 0
#define SIFIVE_L2_ERR_TYPE_UE 1
#endif /* _ASM_RISCV_SIFIVE_L2_CACHE_H */
...@@ -28,7 +28,9 @@ ...@@ -28,7 +28,9 @@
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/csr.h> #include <asm/csr.h>
typedef unsigned long mm_segment_t; typedef struct {
unsigned long seg;
} mm_segment_t;
/* /*
* low level task data that entry.S needs immediate access to * low level task data that entry.S needs immediate access to
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/thread_info.h> #include <linux/thread_info.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/extable.h>
#include <asm/asm.h> #include <asm/asm.h>
#define __enable_user_access() \ #define __enable_user_access() \
...@@ -38,8 +39,10 @@ ...@@ -38,8 +39,10 @@
* For historical reasons, these macros are grossly misnamed. * For historical reasons, these macros are grossly misnamed.
*/ */
#define KERNEL_DS (~0UL) #define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
#define USER_DS (TASK_SIZE)
#define KERNEL_DS MAKE_MM_SEG(~0UL)
#define USER_DS MAKE_MM_SEG(TASK_SIZE)
#define get_fs() (current_thread_info()->addr_limit) #define get_fs() (current_thread_info()->addr_limit)
...@@ -48,9 +51,9 @@ static inline void set_fs(mm_segment_t fs) ...@@ -48,9 +51,9 @@ static inline void set_fs(mm_segment_t fs)
current_thread_info()->addr_limit = fs; current_thread_info()->addr_limit = fs;
} }
#define segment_eq(a, b) ((a) == (b)) #define segment_eq(a, b) ((a).seg == (b).seg)
#define user_addr_max() (get_fs()) #define user_addr_max() (get_fs().seg)
/** /**
...@@ -82,7 +85,7 @@ static inline int __access_ok(unsigned long addr, unsigned long size) ...@@ -82,7 +85,7 @@ static inline int __access_ok(unsigned long addr, unsigned long size)
{ {
const mm_segment_t fs = get_fs(); const mm_segment_t fs = get_fs();
return (size <= fs) && (addr <= (fs - size)); return size <= fs.seg && addr <= fs.seg - size;
} }
/* /*
...@@ -98,21 +101,8 @@ static inline int __access_ok(unsigned long addr, unsigned long size) ...@@ -98,21 +101,8 @@ static inline int __access_ok(unsigned long addr, unsigned long size)
* on our cache or tlb entries. * on our cache or tlb entries.
*/ */
struct exception_table_entry {
unsigned long insn, fixup;
};
extern int fixup_exception(struct pt_regs *state);
#if defined(__LITTLE_ENDIAN)
#define __MSW 1
#define __LSW 0 #define __LSW 0
#elif defined(__BIG_ENDIAN) #define __MSW 1
#define __MSW 0
#define __LSW 1
#else
#error "Unknown endianness"
#endif
/* /*
* The "__xxx" versions of the user access functions do not verify the address * The "__xxx" versions of the user access functions do not verify the address
......
...@@ -312,9 +312,6 @@ void asm_offsets(void) ...@@ -312,9 +312,6 @@ void asm_offsets(void)
- offsetof(struct task_struct, thread.fstate.f[0]) - offsetof(struct task_struct, thread.fstate.f[0])
); );
/* The assembler needs access to THREAD_SIZE as well. */
DEFINE(ASM_THREAD_SIZE, THREAD_SIZE);
/* /*
* We allocate a pt_regs on the stack when entering the kernel. This * We allocate a pt_regs on the stack when entering the kernel. This
* ensures the alignment is sane. * ensures the alignment is sane.
......
...@@ -136,8 +136,7 @@ static void c_stop(struct seq_file *m, void *v) ...@@ -136,8 +136,7 @@ static void c_stop(struct seq_file *m, void *v)
static int c_show(struct seq_file *m, void *v) static int c_show(struct seq_file *m, void *v)
{ {
unsigned long cpu_id = (unsigned long)v - 1; unsigned long cpu_id = (unsigned long)v - 1;
struct device_node *node = of_get_cpu_node(cpuid_to_hartid_map(cpu_id), struct device_node *node = of_get_cpu_node(cpu_id, NULL);
NULL);
const char *compat, *isa, *mmu; const char *compat, *isa, *mmu;
seq_printf(m, "processor\t: %lu\n", cpu_id); seq_printf(m, "processor\t: %lu\n", cpu_id);
......
...@@ -37,11 +37,11 @@ ...@@ -37,11 +37,11 @@
* the kernel thread pointer. If we came from the kernel, sscratch * the kernel thread pointer. If we came from the kernel, sscratch
* will contain 0, and we should continue on the current TP. * will contain 0, and we should continue on the current TP.
*/ */
csrrw tp, sscratch, tp csrrw tp, CSR_SSCRATCH, tp
bnez tp, _save_context bnez tp, _save_context
_restore_kernel_tpsp: _restore_kernel_tpsp:
csrr tp, sscratch csrr tp, CSR_SSCRATCH
REG_S sp, TASK_TI_KERNEL_SP(tp) REG_S sp, TASK_TI_KERNEL_SP(tp)
_save_context: _save_context:
REG_S sp, TASK_TI_USER_SP(tp) REG_S sp, TASK_TI_USER_SP(tp)
...@@ -87,11 +87,11 @@ _save_context: ...@@ -87,11 +87,11 @@ _save_context:
li t0, SR_SUM | SR_FS li t0, SR_SUM | SR_FS
REG_L s0, TASK_TI_USER_SP(tp) REG_L s0, TASK_TI_USER_SP(tp)
csrrc s1, sstatus, t0 csrrc s1, CSR_SSTATUS, t0
csrr s2, sepc csrr s2, CSR_SEPC
csrr s3, sbadaddr csrr s3, CSR_STVAL
csrr s4, scause csrr s4, CSR_SCAUSE
csrr s5, sscratch csrr s5, CSR_SSCRATCH
REG_S s0, PT_SP(sp) REG_S s0, PT_SP(sp)
REG_S s1, PT_SSTATUS(sp) REG_S s1, PT_SSTATUS(sp)
REG_S s2, PT_SEPC(sp) REG_S s2, PT_SEPC(sp)
...@@ -107,8 +107,8 @@ _save_context: ...@@ -107,8 +107,8 @@ _save_context:
.macro RESTORE_ALL .macro RESTORE_ALL
REG_L a0, PT_SSTATUS(sp) REG_L a0, PT_SSTATUS(sp)
REG_L a2, PT_SEPC(sp) REG_L a2, PT_SEPC(sp)
csrw sstatus, a0 csrw CSR_SSTATUS, a0
csrw sepc, a2 csrw CSR_SEPC, a2
REG_L x1, PT_RA(sp) REG_L x1, PT_RA(sp)
REG_L x3, PT_GP(sp) REG_L x3, PT_GP(sp)
...@@ -155,7 +155,7 @@ ENTRY(handle_exception) ...@@ -155,7 +155,7 @@ ENTRY(handle_exception)
* Set sscratch register to 0, so that if a recursive exception * Set sscratch register to 0, so that if a recursive exception
* occurs, the exception vector knows it came from the kernel * occurs, the exception vector knows it came from the kernel
*/ */
csrw sscratch, x0 csrw CSR_SSCRATCH, x0
/* Load the global pointer */ /* Load the global pointer */
.option push .option push
...@@ -248,7 +248,7 @@ resume_userspace: ...@@ -248,7 +248,7 @@ resume_userspace:
* Save TP into sscratch, so we can find the kernel data structures * Save TP into sscratch, so we can find the kernel data structures
* again. * again.
*/ */
csrw sscratch, tp csrw CSR_SSCRATCH, tp
restore_all: restore_all:
RESTORE_ALL RESTORE_ALL
......
...@@ -23,7 +23,8 @@ ...@@ -23,7 +23,8 @@
__INIT __INIT
ENTRY(_start) ENTRY(_start)
/* Mask all interrupts */ /* Mask all interrupts */
csrw sie, zero csrw CSR_SIE, zero
csrw CSR_SIP, zero
/* Load the global pointer */ /* Load the global pointer */
.option push .option push
...@@ -68,14 +69,10 @@ clear_bss_done: ...@@ -68,14 +69,10 @@ clear_bss_done:
/* Restore C environment */ /* Restore C environment */
la tp, init_task la tp, init_task
sw zero, TASK_TI_CPU(tp) sw zero, TASK_TI_CPU(tp)
la sp, init_thread_union + THREAD_SIZE
la sp, init_thread_union
li a0, ASM_THREAD_SIZE
add sp, sp, a0
/* Start the kernel */ /* Start the kernel */
mv a0, s0 mv a0, s1
mv a1, s1
call parse_dtb call parse_dtb
tail start_kernel tail start_kernel
...@@ -89,7 +86,7 @@ relocate: ...@@ -89,7 +86,7 @@ relocate:
/* Point stvec to virtual address of intruction after satp write */ /* Point stvec to virtual address of intruction after satp write */
la a0, 1f la a0, 1f
add a0, a0, a1 add a0, a0, a1
csrw stvec, a0 csrw CSR_STVEC, a0
/* Compute satp for kernel page tables, but don't load it yet */ /* Compute satp for kernel page tables, but don't load it yet */
la a2, swapper_pg_dir la a2, swapper_pg_dir
...@@ -99,18 +96,20 @@ relocate: ...@@ -99,18 +96,20 @@ relocate:
/* /*
* Load trampoline page directory, which will cause us to trap to * Load trampoline page directory, which will cause us to trap to
* stvec if VA != PA, or simply fall through if VA == PA * stvec if VA != PA, or simply fall through if VA == PA. We need a
* full fence here because setup_vm() just wrote these PTEs and we need
* to ensure the new translations are in use.
*/ */
la a0, trampoline_pg_dir la a0, trampoline_pg_dir
srl a0, a0, PAGE_SHIFT srl a0, a0, PAGE_SHIFT
or a0, a0, a1 or a0, a0, a1
sfence.vma sfence.vma
csrw sptbr, a0 csrw CSR_SATP, a0
.align 2 .align 2
1: 1:
/* Set trap vector to spin forever to help debug */ /* Set trap vector to spin forever to help debug */
la a0, .Lsecondary_park la a0, .Lsecondary_park
csrw stvec, a0 csrw CSR_STVEC, a0
/* Reload the global pointer */ /* Reload the global pointer */
.option push .option push
...@@ -118,8 +117,14 @@ relocate: ...@@ -118,8 +117,14 @@ relocate:
la gp, __global_pointer$ la gp, __global_pointer$
.option pop .option pop
/* Switch to kernel page tables */ /*
csrw sptbr, a2 * Switch to kernel page tables. A full fence is necessary in order to
* avoid using the trampoline translations, which are only correct for
* the first superpage. Fetching the fence is guarnteed to work
* because that first superpage is translated the same way.
*/
csrw CSR_SATP, a2
sfence.vma
ret ret
...@@ -130,7 +135,7 @@ relocate: ...@@ -130,7 +135,7 @@ relocate:
/* Set trap vector to spin forever to help debug */ /* Set trap vector to spin forever to help debug */
la a3, .Lsecondary_park la a3, .Lsecondary_park
csrw stvec, a3 csrw CSR_STVEC, a3
slli a3, a0, LGREG slli a3, a0, LGREG
la a1, __cpu_up_stack_pointer la a1, __cpu_up_stack_pointer
......
...@@ -14,17 +14,9 @@ ...@@ -14,17 +14,9 @@
/* /*
* Possible interrupt causes: * Possible interrupt causes:
*/ */
#define INTERRUPT_CAUSE_SOFTWARE 1 #define INTERRUPT_CAUSE_SOFTWARE IRQ_S_SOFT
#define INTERRUPT_CAUSE_TIMER 5 #define INTERRUPT_CAUSE_TIMER IRQ_S_TIMER
#define INTERRUPT_CAUSE_EXTERNAL 9 #define INTERRUPT_CAUSE_EXTERNAL IRQ_S_EXT
/*
* The high order bit of the trap cause register is always set for
* interrupts, which allows us to differentiate them from exceptions
* quickly. The INTERRUPT_CAUSE_* macros don't contain that bit, so we
* need to mask it off.
*/
#define INTERRUPT_CAUSE_FLAG (1UL << (__riscv_xlen - 1))
int arch_show_interrupts(struct seq_file *p, int prec) int arch_show_interrupts(struct seq_file *p, int prec)
{ {
...@@ -37,7 +29,7 @@ asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs) ...@@ -37,7 +29,7 @@ asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs)
struct pt_regs *old_regs = set_irq_regs(regs); struct pt_regs *old_regs = set_irq_regs(regs);
irq_enter(); irq_enter();
switch (regs->scause & ~INTERRUPT_CAUSE_FLAG) { switch (regs->scause & ~SCAUSE_IRQ_FLAG) {
case INTERRUPT_CAUSE_TIMER: case INTERRUPT_CAUSE_TIMER:
riscv_timer_interrupt(); riscv_timer_interrupt();
break; break;
...@@ -54,7 +46,8 @@ asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs) ...@@ -54,7 +46,8 @@ asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs)
handle_arch_irq(regs); handle_arch_irq(regs);
break; break;
default: default:
panic("unexpected interrupt cause"); pr_alert("unexpected interrupt cause 0x%lx", regs->scause);
BUG();
} }
irq_exit(); irq_exit();
......
...@@ -185,10 +185,10 @@ static inline u64 read_counter(int idx) ...@@ -185,10 +185,10 @@ static inline u64 read_counter(int idx)
switch (idx) { switch (idx) {
case RISCV_PMU_CYCLE: case RISCV_PMU_CYCLE:
val = csr_read(cycle); val = csr_read(CSR_CYCLE);
break; break;
case RISCV_PMU_INSTRET: case RISCV_PMU_INSTRET:
val = csr_read(instret); val = csr_read(CSR_INSTRET);
break; break;
default: default:
WARN_ON_ONCE(idx < 0 || idx > RISCV_MAX_COUNTERS); WARN_ON_ONCE(idx < 0 || idx > RISCV_MAX_COUNTERS);
......
...@@ -12,11 +12,15 @@ ...@@ -12,11 +12,15 @@
*/ */
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/export.h>
#include <asm/sbi.h> #include <asm/sbi.h>
void (*pm_power_off)(void) = machine_power_off; static void default_power_off(void)
EXPORT_SYMBOL(pm_power_off); {
sbi_shutdown();
while (1);
}
void (*pm_power_off)(void) = default_power_off;
void machine_restart(char *cmd) void machine_restart(char *cmd)
{ {
...@@ -26,11 +30,10 @@ void machine_restart(char *cmd) ...@@ -26,11 +30,10 @@ void machine_restart(char *cmd)
void machine_halt(void) void machine_halt(void)
{ {
machine_power_off(); pm_power_off();
} }
void machine_power_off(void) void machine_power_off(void)
{ {
sbi_shutdown(); pm_power_off();
while (1);
} }
...@@ -52,9 +52,11 @@ struct screen_info screen_info = { ...@@ -52,9 +52,11 @@ struct screen_info screen_info = {
atomic_t hart_lottery; atomic_t hart_lottery;
unsigned long boot_cpu_hartid; unsigned long boot_cpu_hartid;
void __init parse_dtb(unsigned int hartid, void *dtb) void __init parse_dtb(phys_addr_t dtb_phys)
{ {
if (early_init_dt_scan(__va(dtb))) void *dtb = __va(dtb_phys);
if (early_init_dt_scan(dtb))
return; return;
pr_err("No DTB passed to the kernel\n"); pr_err("No DTB passed to the kernel\n");
......
...@@ -234,6 +234,9 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) ...@@ -234,6 +234,9 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
/* Are we from a system call? */ /* Are we from a system call? */
if (regs->scause == EXC_SYSCALL) { if (regs->scause == EXC_SYSCALL) {
/* Avoid additional syscall restarting via ret_from_exception */
regs->scause = -1UL;
/* If so, check system call restarting.. */ /* If so, check system call restarting.. */
switch (regs->a0) { switch (regs->a0) {
case -ERESTART_RESTARTBLOCK: case -ERESTART_RESTARTBLOCK:
...@@ -272,6 +275,9 @@ static void do_signal(struct pt_regs *regs) ...@@ -272,6 +275,9 @@ static void do_signal(struct pt_regs *regs)
/* Did we come from a system call? */ /* Did we come from a system call? */
if (regs->scause == EXC_SYSCALL) { if (regs->scause == EXC_SYSCALL) {
/* Avoid additional syscall restarting via ret_from_exception */
regs->scause = -1UL;
/* Restart the system call - no handlers present */ /* Restart the system call - no handlers present */
switch (regs->a0) { switch (regs->a0) {
case -ERESTARTNOHAND: case -ERESTARTNOHAND:
......
...@@ -42,7 +42,7 @@ unsigned long __cpuid_to_hartid_map[NR_CPUS] = { ...@@ -42,7 +42,7 @@ unsigned long __cpuid_to_hartid_map[NR_CPUS] = {
void __init smp_setup_processor_id(void) void __init smp_setup_processor_id(void)
{ {
cpuid_to_hartid_map(0) = boot_cpu_hartid; cpuid_to_hartid_map(0) = boot_cpu_hartid;
} }
/* A collection of single bit ipi messages. */ /* A collection of single bit ipi messages. */
...@@ -53,7 +53,7 @@ static struct { ...@@ -53,7 +53,7 @@ static struct {
int riscv_hartid_to_cpuid(int hartid) int riscv_hartid_to_cpuid(int hartid)
{ {
int i = -1; int i;
for (i = 0; i < NR_CPUS; i++) for (i = 0; i < NR_CPUS; i++)
if (cpuid_to_hartid_map(i) == hartid) if (cpuid_to_hartid_map(i) == hartid)
...@@ -70,6 +70,12 @@ void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out) ...@@ -70,6 +70,12 @@ void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out)
for_each_cpu(cpu, in) for_each_cpu(cpu, in)
cpumask_set_cpu(cpuid_to_hartid_map(cpu), out); cpumask_set_cpu(cpuid_to_hartid_map(cpu), out);
} }
bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
{
return phys_id == cpuid_to_hartid_map(cpu);
}
/* Unsupported */ /* Unsupported */
int setup_profiling_timer(unsigned int multiplier) int setup_profiling_timer(unsigned int multiplier)
{ {
...@@ -89,7 +95,7 @@ void riscv_software_interrupt(void) ...@@ -89,7 +95,7 @@ void riscv_software_interrupt(void)
unsigned long *stats = ipi_data[smp_processor_id()].stats; unsigned long *stats = ipi_data[smp_processor_id()].stats;
/* Clear pending IPI */ /* Clear pending IPI */
csr_clear(sip, SIE_SSIE); csr_clear(CSR_SIP, SIE_SSIE);
while (true) { while (true) {
unsigned long ops; unsigned long ops;
...@@ -199,52 +205,3 @@ void smp_send_reschedule(int cpu) ...@@ -199,52 +205,3 @@ void smp_send_reschedule(int cpu)
send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE); send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE);
} }
/*
* Performs an icache flush for the given MM context. RISC-V has no direct
* mechanism for instruction cache shoot downs, so instead we send an IPI that
* informs the remote harts they need to flush their local instruction caches.
* To avoid pathologically slow behavior in a common case (a bunch of
* single-hart processes on a many-hart machine, ie 'make -j') we avoid the
* IPIs for harts that are not currently executing a MM context and instead
* schedule a deferred local instruction cache flush to be performed before
* execution resumes on each hart.
*/
void flush_icache_mm(struct mm_struct *mm, bool local)
{
unsigned int cpu;
cpumask_t others, hmask, *mask;
preempt_disable();
/* Mark every hart's icache as needing a flush for this MM. */
mask = &mm->context.icache_stale_mask;
cpumask_setall(mask);
/* Flush this hart's I$ now, and mark it as flushed. */
cpu = smp_processor_id();
cpumask_clear_cpu(cpu, mask);
local_flush_icache_all();
/*
* Flush the I$ of other harts concurrently executing, and mark them as
* flushed.
*/
cpumask_andnot(&others, mm_cpumask(mm), cpumask_of(cpu));
local |= cpumask_empty(&others);
if (mm != current->active_mm || !local) {
cpumask_clear(&hmask);
riscv_cpuid_to_hartid_mask(&others, &hmask);
sbi_remote_fence_i(hmask.bits);
} else {
/*
* It's assumed that at least one strongly ordered operation is
* performed on this hart between setting a hart's cpumask bit
* and scheduling this MM context on that hart. Sending an SBI
* remote message will do this, but in the case where no
* messages are sent we still need to order this hart's writes
* with flush_icache_deferred().
*/
smp_mb();
}
preempt_enable();
}
...@@ -47,6 +47,17 @@ void __init smp_prepare_boot_cpu(void) ...@@ -47,6 +47,17 @@ void __init smp_prepare_boot_cpu(void)
void __init smp_prepare_cpus(unsigned int max_cpus) void __init smp_prepare_cpus(unsigned int max_cpus)
{ {
int cpuid;
/* This covers non-smp usecase mandated by "nosmp" option */
if (max_cpus == 0)
return;
for_each_possible_cpu(cpuid) {
if (cpuid == smp_processor_id())
continue;
set_cpu_present(cpuid, true);
}
} }
void __init setup_smp(void) void __init setup_smp(void)
...@@ -73,12 +84,19 @@ void __init setup_smp(void) ...@@ -73,12 +84,19 @@ void __init setup_smp(void)
} }
cpuid_to_hartid_map(cpuid) = hart; cpuid_to_hartid_map(cpuid) = hart;
set_cpu_possible(cpuid, true);
set_cpu_present(cpuid, true);
cpuid++; cpuid++;
} }
BUG_ON(!found_boot_cpu); BUG_ON(!found_boot_cpu);
if (cpuid > nr_cpu_ids)
pr_warn("Total number of cpus [%d] is greater than nr_cpus option value [%d]\n",
cpuid, nr_cpu_ids);
for (cpuid = 1; cpuid < nr_cpu_ids; cpuid++) {
if (cpuid_to_hartid_map(cpuid) != INVALID_HARTID)
set_cpu_possible(cpuid, true);
}
} }
int __cpu_up(unsigned int cpu, struct task_struct *tidle) int __cpu_up(unsigned int cpu, struct task_struct *tidle)
......
...@@ -33,9 +33,9 @@ static void notrace walk_stackframe(struct task_struct *task, ...@@ -33,9 +33,9 @@ static void notrace walk_stackframe(struct task_struct *task,
unsigned long fp, sp, pc; unsigned long fp, sp, pc;
if (regs) { if (regs) {
fp = GET_FP(regs); fp = frame_pointer(regs);
sp = GET_USP(regs); sp = user_stack_pointer(regs);
pc = GET_IP(regs); pc = instruction_pointer(regs);
} else if (task == NULL || task == current) { } else if (task == NULL || task == current) {
const register unsigned long current_sp __asm__ ("sp"); const register unsigned long current_sp __asm__ ("sp");
fp = (unsigned long)__builtin_frame_address(0); fp = (unsigned long)__builtin_frame_address(0);
...@@ -64,12 +64,8 @@ static void notrace walk_stackframe(struct task_struct *task, ...@@ -64,12 +64,8 @@ static void notrace walk_stackframe(struct task_struct *task,
frame = (struct stackframe *)fp - 1; frame = (struct stackframe *)fp - 1;
sp = fp; sp = fp;
fp = frame->fp; fp = frame->fp;
#ifdef HAVE_FUNCTION_GRAPH_RET_ADDR_PTR
pc = ftrace_graph_ret_addr(current, NULL, frame->ra, pc = ftrace_graph_ret_addr(current, NULL, frame->ra,
(unsigned long *)(fp - 8)); (unsigned long *)(fp - 8));
#else
pc = frame->ra - 0x4;
#endif
} }
} }
...@@ -82,8 +78,8 @@ static void notrace walk_stackframe(struct task_struct *task, ...@@ -82,8 +78,8 @@ static void notrace walk_stackframe(struct task_struct *task,
unsigned long *ksp; unsigned long *ksp;
if (regs) { if (regs) {
sp = GET_USP(regs); sp = user_stack_pointer(regs);
pc = GET_IP(regs); pc = instruction_pointer(regs);
} else if (task == NULL || task == current) { } else if (task == NULL || task == current) {
const register unsigned long current_sp __asm__ ("sp"); const register unsigned long current_sp __asm__ ("sp");
sp = current_sp; sp = current_sp;
......
...@@ -70,7 +70,7 @@ void do_trap(struct pt_regs *regs, int signo, int code, ...@@ -70,7 +70,7 @@ void do_trap(struct pt_regs *regs, int signo, int code,
&& printk_ratelimit()) { && printk_ratelimit()) {
pr_info("%s[%d]: unhandled signal %d code 0x%x at 0x" REG_FMT, pr_info("%s[%d]: unhandled signal %d code 0x%x at 0x" REG_FMT,
tsk->comm, task_pid_nr(tsk), signo, code, addr); tsk->comm, task_pid_nr(tsk), signo, code, addr);
print_vma_addr(KERN_CONT " in ", GET_IP(regs)); print_vma_addr(KERN_CONT " in ", instruction_pointer(regs));
pr_cont("\n"); pr_cont("\n");
show_regs(regs); show_regs(regs);
} }
...@@ -118,6 +118,17 @@ DO_ERROR_INFO(do_trap_ecall_s, ...@@ -118,6 +118,17 @@ DO_ERROR_INFO(do_trap_ecall_s,
DO_ERROR_INFO(do_trap_ecall_m, DO_ERROR_INFO(do_trap_ecall_m,
SIGILL, ILL_ILLTRP, "environment call from M-mode"); SIGILL, ILL_ILLTRP, "environment call from M-mode");
#ifdef CONFIG_GENERIC_BUG
static inline unsigned long get_break_insn_length(unsigned long pc)
{
bug_insn_t insn;
if (probe_kernel_address((bug_insn_t *)pc, insn))
return 0;
return (((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_32) ? 4UL : 2UL);
}
#endif /* CONFIG_GENERIC_BUG */
asmlinkage void do_trap_break(struct pt_regs *regs) asmlinkage void do_trap_break(struct pt_regs *regs)
{ {
#ifdef CONFIG_GENERIC_BUG #ifdef CONFIG_GENERIC_BUG
...@@ -129,8 +140,8 @@ asmlinkage void do_trap_break(struct pt_regs *regs) ...@@ -129,8 +140,8 @@ asmlinkage void do_trap_break(struct pt_regs *regs)
case BUG_TRAP_TYPE_NONE: case BUG_TRAP_TYPE_NONE:
break; break;
case BUG_TRAP_TYPE_WARN: case BUG_TRAP_TYPE_WARN:
regs->sepc += sizeof(bug_insn_t); regs->sepc += get_break_insn_length(regs->sepc);
return; break;
case BUG_TRAP_TYPE_BUG: case BUG_TRAP_TYPE_BUG:
die(regs, "Kernel BUG"); die(regs, "Kernel BUG");
} }
...@@ -145,11 +156,14 @@ int is_valid_bugaddr(unsigned long pc) ...@@ -145,11 +156,14 @@ int is_valid_bugaddr(unsigned long pc)
{ {
bug_insn_t insn; bug_insn_t insn;
if (pc < PAGE_OFFSET) if (pc < VMALLOC_START)
return 0; return 0;
if (probe_kernel_address((bug_insn_t *)pc, insn)) if (probe_kernel_address((bug_insn_t *)pc, insn))
return 0; return 0;
return (insn == __BUG_INSN); if ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_32)
return (insn == __BUG_INSN_32);
else
return ((insn & __COMPRESSED_INSN_MASK) == __BUG_INSN_16);
} }
#endif /* CONFIG_GENERIC_BUG */ #endif /* CONFIG_GENERIC_BUG */
...@@ -159,9 +173,9 @@ void __init trap_init(void) ...@@ -159,9 +173,9 @@ void __init trap_init(void)
* Set sup0 scratch register to 0, indicating to exception vector * Set sup0 scratch register to 0, indicating to exception vector
* that we are presently executing in the kernel * that we are presently executing in the kernel
*/ */
csr_write(sscratch, 0); csr_write(CSR_SSCRATCH, 0);
/* Set the exception vector address */ /* Set the exception vector address */
csr_write(stvec, &handle_exception); csr_write(CSR_STVEC, &handle_exception);
/* Enable all interrupts */ /* Enable all interrupts */
csr_write(sie, -1); csr_write(CSR_SIE, -1);
} }
...@@ -36,7 +36,7 @@ $(obj)/vdso.so.dbg: $(src)/vdso.lds $(obj-vdso) FORCE ...@@ -36,7 +36,7 @@ $(obj)/vdso.so.dbg: $(src)/vdso.lds $(obj-vdso) FORCE
# these symbols in the kernel code rather than hand-coded addresses. # these symbols in the kernel code rather than hand-coded addresses.
SYSCFLAGS_vdso.so.dbg = -shared -s -Wl,-soname=linux-vdso.so.1 \ SYSCFLAGS_vdso.so.dbg = -shared -s -Wl,-soname=linux-vdso.so.1 \
$(call cc-ldoption, -Wl$(comma)--hash-style=both) -Wl,--hash-style=both
$(obj)/vdso-dummy.o: $(src)/vdso.lds $(obj)/rt_sigreturn.o FORCE $(obj)/vdso-dummy.o: $(src)/vdso.lds $(obj)/rt_sigreturn.o FORCE
$(call if_changed,vdsold) $(call if_changed,vdsold)
......
...@@ -9,3 +9,5 @@ obj-y += fault.o ...@@ -9,3 +9,5 @@ obj-y += fault.o
obj-y += extable.o obj-y += extable.o
obj-y += ioremap.o obj-y += ioremap.o
obj-y += cacheflush.o obj-y += cacheflush.o
obj-y += context.o
obj-y += sifive_l2_cache.o
...@@ -14,6 +14,67 @@ ...@@ -14,6 +14,67 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#ifdef CONFIG_SMP
#include <asm/sbi.h>
void flush_icache_all(void)
{
sbi_remote_fence_i(NULL);
}
/*
* Performs an icache flush for the given MM context. RISC-V has no direct
* mechanism for instruction cache shoot downs, so instead we send an IPI that
* informs the remote harts they need to flush their local instruction caches.
* To avoid pathologically slow behavior in a common case (a bunch of
* single-hart processes on a many-hart machine, ie 'make -j') we avoid the
* IPIs for harts that are not currently executing a MM context and instead
* schedule a deferred local instruction cache flush to be performed before
* execution resumes on each hart.
*/
void flush_icache_mm(struct mm_struct *mm, bool local)
{
unsigned int cpu;
cpumask_t others, hmask, *mask;
preempt_disable();
/* Mark every hart's icache as needing a flush for this MM. */
mask = &mm->context.icache_stale_mask;
cpumask_setall(mask);
/* Flush this hart's I$ now, and mark it as flushed. */
cpu = smp_processor_id();
cpumask_clear_cpu(cpu, mask);
local_flush_icache_all();
/*
* Flush the I$ of other harts concurrently executing, and mark them as
* flushed.
*/
cpumask_andnot(&others, mm_cpumask(mm), cpumask_of(cpu));
local |= cpumask_empty(&others);
if (mm != current->active_mm || !local) {
cpumask_clear(&hmask);
riscv_cpuid_to_hartid_mask(&others, &hmask);
sbi_remote_fence_i(hmask.bits);
} else {
/*
* It's assumed that at least one strongly ordered operation is
* performed on this hart between setting a hart's cpumask bit
* and scheduling this MM context on that hart. Sending an SBI
* remote message will do this, but in the case where no
* messages are sent we still need to order this hart's writes
* with flush_icache_deferred().
*/
smp_mb();
}
preempt_enable();
}
#endif /* CONFIG_SMP */
void flush_icache_pte(pte_t pte) void flush_icache_pte(pte_t pte)
{ {
struct page *page = pte_page(pte); struct page *page = pte_page(pte);
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2012 Regents of the University of California
* Copyright (C) 2017 SiFive
*/
#include <linux/mm.h>
#include <asm/tlbflush.h>
#include <asm/cacheflush.h>
/*
* When necessary, performs a deferred icache flush for the given MM context,
* on the local CPU. RISC-V has no direct mechanism for instruction cache
* shoot downs, so instead we send an IPI that informs the remote harts they
* need to flush their local instruction caches. To avoid pathologically slow
* behavior in a common case (a bunch of single-hart processes on a many-hart
* machine, ie 'make -j') we avoid the IPIs for harts that are not currently
* executing a MM context and instead schedule a deferred local instruction
* cache flush to be performed before execution resumes on each hart. This
* actually performs that local instruction cache flush, which implicitly only
* refers to the current hart.
*/
static inline void flush_icache_deferred(struct mm_struct *mm)
{
#ifdef CONFIG_SMP
unsigned int cpu = smp_processor_id();
cpumask_t *mask = &mm->context.icache_stale_mask;
if (cpumask_test_cpu(cpu, mask)) {
cpumask_clear_cpu(cpu, mask);
/*
* Ensure the remote hart's writes are visible to this hart.
* This pairs with a barrier in flush_icache_mm.
*/
smp_mb();
local_flush_icache_all();
}
#endif
}
void switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *task)
{
unsigned int cpu;
if (unlikely(prev == next))
return;
/*
* Mark the current MM context as inactive, and the next as
* active. This is at least used by the icache flushing
* routines in order to determine who should be flushed.
*/
cpu = smp_processor_id();
cpumask_clear_cpu(cpu, mm_cpumask(prev));
cpumask_set_cpu(cpu, mm_cpumask(next));
/*
* Use the old spbtr name instead of using the current satp
* name to support binutils 2.29 which doesn't know about the
* privileged ISA 1.10 yet.
*/
csr_write(sptbr, virt_to_pfn(next->pgd) | SATP_MODE);
local_flush_tlb_all();
flush_icache_deferred(next);
}
...@@ -229,8 +229,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs) ...@@ -229,8 +229,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
pte_t *pte_k; pte_t *pte_k;
int index; int index;
/* User mode accesses just cause a SIGSEGV */
if (user_mode(regs)) if (user_mode(regs))
goto bad_area; return do_trap(regs, SIGSEGV, code, addr, tsk);
/* /*
* Synchronize this task's top level page-table * Synchronize this task's top level page-table
...@@ -239,13 +240,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs) ...@@ -239,13 +240,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs)
* Do _not_ use "tsk->active_mm->pgd" here. * Do _not_ use "tsk->active_mm->pgd" here.
* We might be inside an interrupt in the middle * We might be inside an interrupt in the middle
* of a task switch. * of a task switch.
*
* Note: Use the old spbtr name instead of using the current
* satp name to support binutils 2.29 which doesn't know about
* the privileged ISA 1.10 yet.
*/ */
index = pgd_index(addr); index = pgd_index(addr);
pgd = (pgd_t *)pfn_to_virt(csr_read(sptbr)) + index; pgd = (pgd_t *)pfn_to_virt(csr_read(CSR_SATP)) + index;
pgd_k = init_mm.pgd + index; pgd_k = init_mm.pgd + index;
if (!pgd_present(*pgd_k)) if (!pgd_present(*pgd_k))
......
// SPDX-License-Identifier: GPL-2.0
/*
* SiFive L2 cache controller Driver
*
* Copyright (C) 2018-2019 SiFive, Inc.
*
*/
#include <linux/debugfs.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <asm/sifive_l2_cache.h>
#define SIFIVE_L2_DIRECCFIX_LOW 0x100
#define SIFIVE_L2_DIRECCFIX_HIGH 0x104
#define SIFIVE_L2_DIRECCFIX_COUNT 0x108
#define SIFIVE_L2_DATECCFIX_LOW 0x140
#define SIFIVE_L2_DATECCFIX_HIGH 0x144
#define SIFIVE_L2_DATECCFIX_COUNT 0x148
#define SIFIVE_L2_DATECCFAIL_LOW 0x160
#define SIFIVE_L2_DATECCFAIL_HIGH 0x164
#define SIFIVE_L2_DATECCFAIL_COUNT 0x168
#define SIFIVE_L2_CONFIG 0x00
#define SIFIVE_L2_WAYENABLE 0x08
#define SIFIVE_L2_ECCINJECTERR 0x40
#define SIFIVE_L2_MAX_ECCINTR 3
static void __iomem *l2_base;
static int g_irq[SIFIVE_L2_MAX_ECCINTR];
enum {
DIR_CORR = 0,
DATA_CORR,
DATA_UNCORR,
};
#ifdef CONFIG_DEBUG_FS
static struct dentry *sifive_test;
static ssize_t l2_write(struct file *file, const char __user *data,
size_t count, loff_t *ppos)
{
unsigned int val;
if (kstrtouint_from_user(data, count, 0, &val))
return -EINVAL;
if ((val >= 0 && val < 0xFF) || (val >= 0x10000 && val < 0x100FF))
writel(val, l2_base + SIFIVE_L2_ECCINJECTERR);
else
return -EINVAL;
return count;
}
static const struct file_operations l2_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.write = l2_write
};
static void setup_sifive_debug(void)
{
sifive_test = debugfs_create_dir("sifive_l2_cache", NULL);
debugfs_create_file("sifive_debug_inject_error", 0200,
sifive_test, NULL, &l2_fops);
}
#endif
static void l2_config_read(void)
{
u32 regval, val;
regval = readl(l2_base + SIFIVE_L2_CONFIG);
val = regval & 0xFF;
pr_info("L2CACHE: No. of Banks in the cache: %d\n", val);
val = (regval & 0xFF00) >> 8;
pr_info("L2CACHE: No. of ways per bank: %d\n", val);
val = (regval & 0xFF0000) >> 16;
pr_info("L2CACHE: Sets per bank: %llu\n", (uint64_t)1 << val);
val = (regval & 0xFF000000) >> 24;
pr_info("L2CACHE: Bytes per cache block: %llu\n", (uint64_t)1 << val);
regval = readl(l2_base + SIFIVE_L2_WAYENABLE);
pr_info("L2CACHE: Index of the largest way enabled: %d\n", regval);
}
static const struct of_device_id sifive_l2_ids[] = {
{ .compatible = "sifive,fu540-c000-ccache" },
{ /* end of table */ },
};
static ATOMIC_NOTIFIER_HEAD(l2_err_chain);
int register_sifive_l2_error_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_register(&l2_err_chain, nb);
}
EXPORT_SYMBOL_GPL(register_sifive_l2_error_notifier);
int unregister_sifive_l2_error_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_unregister(&l2_err_chain, nb);
}
EXPORT_SYMBOL_GPL(unregister_sifive_l2_error_notifier);
static irqreturn_t l2_int_handler(int irq, void *device)
{
unsigned int regval, add_h, add_l;
if (irq == g_irq[DIR_CORR]) {
add_h = readl(l2_base + SIFIVE_L2_DIRECCFIX_HIGH);
add_l = readl(l2_base + SIFIVE_L2_DIRECCFIX_LOW);
pr_err("L2CACHE: DirError @ 0x%08X.%08X\n", add_h, add_l);
regval = readl(l2_base + SIFIVE_L2_DIRECCFIX_COUNT);
atomic_notifier_call_chain(&l2_err_chain, SIFIVE_L2_ERR_TYPE_CE,
"DirECCFix");
}
if (irq == g_irq[DATA_CORR]) {
add_h = readl(l2_base + SIFIVE_L2_DATECCFIX_HIGH);
add_l = readl(l2_base + SIFIVE_L2_DATECCFIX_LOW);
pr_err("L2CACHE: DataError @ 0x%08X.%08X\n", add_h, add_l);
regval = readl(l2_base + SIFIVE_L2_DATECCFIX_COUNT);
atomic_notifier_call_chain(&l2_err_chain, SIFIVE_L2_ERR_TYPE_CE,
"DatECCFix");
}
if (irq == g_irq[DATA_UNCORR]) {
add_h = readl(l2_base + SIFIVE_L2_DATECCFAIL_HIGH);
add_l = readl(l2_base + SIFIVE_L2_DATECCFAIL_LOW);
pr_err("L2CACHE: DataFail @ 0x%08X.%08X\n", add_h, add_l);
regval = readl(l2_base + SIFIVE_L2_DATECCFAIL_COUNT);
atomic_notifier_call_chain(&l2_err_chain, SIFIVE_L2_ERR_TYPE_UE,
"DatECCFail");
}
return IRQ_HANDLED;
}
int __init sifive_l2_init(void)
{
struct device_node *np;
struct resource res;
int i, rc;
np = of_find_matching_node(NULL, sifive_l2_ids);
if (!np)
return -ENODEV;
if (of_address_to_resource(np, 0, &res))
return -ENODEV;
l2_base = ioremap(res.start, resource_size(&res));
if (!l2_base)
return -ENOMEM;
for (i = 0; i < SIFIVE_L2_MAX_ECCINTR; i++) {
g_irq[i] = irq_of_parse_and_map(np, i);
rc = request_irq(g_irq[i], l2_int_handler, 0, "l2_ecc", NULL);
if (rc) {
pr_err("L2CACHE: Could not request IRQ %d\n", g_irq[i]);
return rc;
}
}
l2_config_read();
#ifdef CONFIG_DEBUG_FS
setup_sifive_debug();
#endif
return 0;
}
device_initcall(sifive_l2_init);
...@@ -53,7 +53,6 @@ device_initcall(hvc_sbi_init); ...@@ -53,7 +53,6 @@ device_initcall(hvc_sbi_init);
static int __init hvc_sbi_console_init(void) static int __init hvc_sbi_console_init(void)
{ {
hvc_instantiate(0, 0, &hvc_sbi_ops); hvc_instantiate(0, 0, &hvc_sbi_ops);
add_preferred_console("hvc", 0, NULL);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment