Merge tag 'riscv-for-linus-4.19-mw0' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 19 Aug 2018 16:56:38 +0000 (09:56 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 19 Aug 2018 16:56:38 +0000 (09:56 -0700)
Pull RISC-V updates from Palmer Dabbelt:
 "This contains some major improvements to the RISC-V port, including
  the necessary interrupt controller and timer support to actually make
  it to userspace. Support for three devices has been added:

   - the ISA-mandated timers on RISC-V systems.

   - the ISA-mandated first-level interrupt controller on RISC-V
     systems, which is handled as part of our core arch code because
     it's very small and tightly tied to the ISA.

   - SiFive's platform-level interrupt controller, which talks to the
     actual devices.

  In addition to these new devices, there are a handful of cleanups all
  over the RISC-V tree:

   - build fixes for various configurations:
      * A fix to the vDSO build's makefile so it respects CFLAGS.
      * The addition of __lshrti3, a libgcc derived function necessary
        for some 32-bit configurations.
      * !SMP && PERF_EVENTS

   - Cleanups to the arch code to remove the remnants of old versions of
     the drivers that were just properly submitted.
      * Some dead code from the timer driver, most of which wasn't ever
        even compiled.
      * Cleanups of some interrupt #defines, which are now local to the
        interrupt handling code.

   - Fixes to ptrace(), which while not being sufficient to fully make
     GDB work are at least sufficient to get simple GDB tasks to work.

   - Early printk support via RISC-V's architecturally mandated SBI
     console device.

   - A fix to our early debug trap handler to ensure it's always
     aligned.

  These patches have all been through a fairly extensive review process,
  but as this enables a whole pile of functionality (ie, userspace) I'm
  confident we'll need to submit a few more patches. The only concrete
  issues I know about are the sys_riscv_flush_icache patches, but as I
  managed to screw those up on Friday I figured it'd be best to let them
  bake another week.

  This tag boots a Fedora root filesystem on QEMU's master branch for
  me, and before this morning's rebase (from 4.18-rc8 to 4.18) it booted
  on the HiFive Unleashed.

  Thanks to Christoph Hellwig and the other guys at WD for getting the
  new drivers in shape!"

* tag 'riscv-for-linus-4.19-mw0' of git://git.kernel.org/pub/scm/linux/kernel/git/palmer/riscv-linux:
  dt-bindings: interrupt-controller: SiFive Plaform Level Interrupt Controller
  dt-bindings: interrupt-controller: RISC-V local interrupt controller
  RISC-V: Fix !CONFIG_SMP compilation error
  irqchip: add a SiFive PLIC driver
  RISC-V: Add the directive for alignment of stvec's value
  clocksource: new RISC-V SBI timer driver
  RISC-V: implement low-level interrupt handling
  RISC-V: add a definition for the SIE SEIE bit
  RISC-V: remove INTERRUPT_CAUSE_* defines from asm/irq.h
  RISC-V: simplify software interrupt / IPI code
  RISC-V: remove timer leftovers
  RISC-V: Add early printk support via the SBI console
  RISC-V: Don't increment sepc after breakpoint.
  RISC-V: implement __lshrti3.
  RISC-V: Use KBUILD_CFLAGS instead of KCFLAGS when building the vDSO

27 files changed:
Documentation/devicetree/bindings/interrupt-controller/riscv,cpu-intc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.txt [new file with mode: 0644]
arch/riscv/Makefile
arch/riscv/configs/defconfig
arch/riscv/include/asm/csr.h
arch/riscv/include/asm/irq.h
arch/riscv/include/asm/perf_event.h
arch/riscv/include/asm/smp.h
arch/riscv/kernel/entry.S
arch/riscv/kernel/head.S
arch/riscv/kernel/irq.c
arch/riscv/kernel/perf_event.c
arch/riscv/kernel/setup.c
arch/riscv/kernel/smp.c
arch/riscv/kernel/smpboot.c
arch/riscv/kernel/time.c
arch/riscv/kernel/traps.c
arch/riscv/kernel/vdso/Makefile
arch/riscv/lib/Makefile
arch/riscv/lib/tishift.S [new file with mode: 0644]
drivers/clocksource/Kconfig
drivers/clocksource/Makefile
drivers/clocksource/riscv_timer.c [new file with mode: 0644]
drivers/irqchip/Kconfig
drivers/irqchip/Makefile
drivers/irqchip/irq-sifive-plic.c [new file with mode: 0644]
include/linux/cpuhotplug.h

diff --git a/Documentation/devicetree/bindings/interrupt-controller/riscv,cpu-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/riscv,cpu-intc.txt
new file mode 100644 (file)
index 0000000..b0a8af5
--- /dev/null
@@ -0,0 +1,44 @@
+RISC-V Hart-Level Interrupt Controller (HLIC)
+---------------------------------------------
+
+RISC-V cores include Control Status Registers (CSRs) which are local to each
+CPU core (HART in RISC-V terminology) and can be read or written by software.
+Some of these CSRs are used to control local interrupts connected to the core.
+Every interrupt is ultimately routed through a hart's HLIC before it
+interrupts that hart.
+
+The RISC-V supervisor ISA manual specifies three interrupt sources that are
+attached to every HLIC: software interrupts, the timer interrupt, and external
+interrupts.  Software interrupts are used to send IPIs between cores.  The
+timer interrupt comes from an architecturally mandated real-time timer that is
+controller via Supervisor Binary Interface (SBI) calls and CSR reads.  External
+interrupts connect all other device interrupts to the HLIC, which are routed
+via the platform-level interrupt controller (PLIC).
+
+All RISC-V systems that conform to the supervisor ISA specification are
+required to have a HLIC with these three interrupt sources present.  Since the
+interrupt map is defined by the ISA it's not listed in the HLIC's device tree
+entry, though external interrupt controllers (like the PLIC, for example) will
+need to define how their interrupts map to the relevant HLICs.  This means
+a PLIC interrupt property will typically list the HLICs for all present HARTs
+in the system.
+
+Required properties:
+- compatible : "riscv,cpu-intc"
+- #interrupt-cells : should be <1>
+- interrupt-controller : Identifies the node as an interrupt controller
+
+Furthermore, this interrupt-controller MUST be embedded inside the cpu
+definition of the hart whose CSRs control these local interrupts.
+
+An example device tree entry for a HLIC is show below.
+
+       cpu1: cpu@1 {
+               compatible = "riscv";
+               ...
+               cpu1-intc: interrupt-controller {
+                       #interrupt-cells = <1>;
+                       compatible = "riscv,cpu-intc", "sifive,fu540-c000-cpu-intc";
+                       interrupt-controller;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.txt b/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.txt
new file mode 100644 (file)
index 0000000..6adf7a6
--- /dev/null
@@ -0,0 +1,58 @@
+SiFive Platform-Level Interrupt Controller (PLIC)
+-------------------------------------------------
+
+SiFive SOCs include an implementation of the Platform-Level Interrupt Controller
+(PLIC) high-level specification in the RISC-V Privileged Architecture
+specification.  The PLIC connects all external interrupts in the system to all
+hart contexts in the system, via the external interrupt source in each hart.
+
+A hart context is a privilege mode in a hardware execution thread.  For example,
+in an 4 core system with 2-way SMT, you have 8 harts and probably at least two
+privilege modes per hart; machine mode and supervisor mode.
+
+Each interrupt can be enabled on per-context basis.  Any context can claim
+a pending enabled interrupt and then release it once it has been handled.
+
+Each interrupt has a configurable priority.  Higher priority interrupts are
+serviced first.  Each context can specify a priority threshold. Interrupts
+with priority below this threshold will not cause the PLIC to raise its
+interrupt line leading to the context.
+
+While the PLIC supports both edge-triggered and level-triggered interrupts,
+interrupt handlers are oblivious to this distinction and therefore it is not
+specified in the PLIC device-tree binding.
+
+While the RISC-V ISA doesn't specify a memory layout for the PLIC, the
+"sifive,plic-1.0.0" device is a concrete implementation of the PLIC that
+contains a specific memory layout, which is documented in chapter 8 of the
+SiFive U5 Coreplex Series Manual <https://static.dev.sifive.com/U54-MC-RVCoreIP.pdf>.
+
+Required properties:
+- compatible : "sifive,plic-1.0.0" and a string identifying the actual
+  detailed implementation in case that specific bugs need to be worked around.
+- #address-cells : should be <0> or more.
+- #interrupt-cells : should be <1> or more.
+- interrupt-controller : Identifies the node as an interrupt controller.
+- reg : Should contain 1 register range (address and length).
+- interrupts-extended : Specifies which contexts are connected to the PLIC,
+  with "-1" specifying that a context is not present.  Each node pointed
+  to should be a riscv,cpu-intc node, which has a riscv node as parent.
+- riscv,ndev: Specifies how many external interrupts are supported by
+  this controller.
+
+Example:
+
+       plic: interrupt-controller@c000000 {
+               #address-cells = <0>;
+               #interrupt-cells = <1>;
+               compatible = "sifive,plic-1.0.0", "sifive,fu540-c000-plic";
+               interrupt-controller;
+               interrupts-extended = <
+                       &cpu0-intc 11
+                       &cpu1-intc 11 &cpu1-intc 9
+                       &cpu2-intc 11 &cpu2-intc 9
+                       &cpu3-intc 11 &cpu3-intc 9
+                       &cpu4-intc 11 &cpu4-intc 9>;
+               reg = <0xc000000 0x4000000>;
+               riscv,ndev = <10>;
+       };
index 2627e4813edf68c22b06aac206d615d11600b6c0..9ddd88bb30b724c4683cd52a4195be604b1a57a4 100644 (file)
@@ -25,6 +25,9 @@ ifeq ($(CONFIG_ARCH_RV64I),y)
 
        KBUILD_CFLAGS += -mabi=lp64
        KBUILD_AFLAGS += -mabi=lp64
+       
+       KBUILD_CFLAGS   += $(call cc-ifversion, -ge, 0500, -DCONFIG_ARCH_SUPPORTS_INT128)
+
        KBUILD_MARCH = rv64im
        LDFLAGS += -melf64lriscv
 else
index 07326466871b0d74fb0c43f219bc49058f663468..36473d7dbaac4a602096414e20d2cad2ed296358 100644 (file)
@@ -76,3 +76,4 @@ CONFIG_ROOT_NFS=y
 CONFIG_CRYPTO_USER_API_HASH=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
+CONFIG_SIFIVE_PLIC=y
index 421fa35857988e2c203c086e2dfb5aaab628491b..28a0d1cb374c8c25ce4032da874b974712ca3129 100644 (file)
@@ -54,6 +54,7 @@
 /* Interrupt Enable and Interrupt Pending flags */
 #define SIE_SSIE _AC(0x00000002, UL) /* Software Interrupt Enable */
 #define SIE_STIE _AC(0x00000020, UL) /* Timer Interrupt Enable */
+#define SIE_SEIE _AC(0x00000200, UL) /* External Interrupt Enable */
 
 #define EXC_INST_MISALIGNED     0
 #define EXC_INST_ACCESS         1
index 4dee9d4c13c08143ef034918ac4812752a25bcf0..996b6fbe17a61dc3ee149087def49de1108d399c 100644 (file)
 
 #define NR_IRQS         0
 
-#define INTERRUPT_CAUSE_SOFTWARE    1
-#define INTERRUPT_CAUSE_TIMER       5
-#define INTERRUPT_CAUSE_EXTERNAL    9
-
 void riscv_timer_interrupt(void);
+void riscv_software_interrupt(void);
 
 #include <asm-generic/irq.h>
 
index 0e638a0c3febbaf23e522ae2d7f5f17527c981e4..aefbfaa6a78156f2aeb66f9cda26a25cbb249e21 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <linux/perf_event.h>
 #include <linux/ptrace.h>
+#include <linux/interrupt.h>
 
 #define RISCV_BASE_COUNTERS    2
 
index 85e4220839b0ab505f09e4eb6c715b75ae278031..36016845461dc8f3788891f32455736b56ffd7f3 100644 (file)
@@ -24,9 +24,6 @@
 
 #ifdef CONFIG_SMP
 
-/* SMP initialization hook for setup_arch */
-void __init init_clockevent(void);
-
 /* SMP initialization hook for setup_arch */
 void __init setup_smp(void);
 
@@ -44,9 +41,6 @@ void arch_send_call_function_single_ipi(int cpu);
  */
 #define raw_smp_processor_id() (*((int*)((char*)get_current() + TASK_TI_CPU)))
 
-/* Interprocessor interrupt handler */
-irqreturn_t handle_ipi(void);
-
 #endif /* CONFIG_SMP */
 
 #endif /* _ASM_RISCV_SMP_H */
index 9aaf6c98677192ddacbcb2745dd55c8f630b3330..fa2c08e3c05e6ee74ea0258b62cdf6580f98a385 100644 (file)
@@ -168,8 +168,8 @@ ENTRY(handle_exception)
 
        /* Handle interrupts */
        move a0, sp /* pt_regs */
-       REG_L a1, handle_arch_irq
-       jr a1
+       move a1, s4 /* scause */
+       tail do_IRQ
 1:
        /* Exceptions run with interrupts enabled */
        csrs sstatus, SR_SIE
index 6e07ed37bbff794bb52664670b42c10c59810e01..c4d2c63f9a2927396f2d47c23e35a9fdedcd21f9 100644 (file)
@@ -94,6 +94,7 @@ relocate:
        or a0, a0, a1
        sfence.vma
        csrw sptbr, a0
+.align 2
 1:
        /* Set trap vector to spin forever to help debug */
        la a0, .Lsecondary_park
@@ -143,6 +144,7 @@ relocate:
        tail smp_callin
 #endif
 
+.align 2
 .Lsecondary_park:
        /* We lack SMP support or have too many harts, so park this hart */
        wfi
index 7bcdaed15703be6d8f141a8582abd024dc966fec..0cfac48a1272d98f95cd0fcc6ffaa536d862b408 100644 (file)
@@ -1,21 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (C) 2012 Regents of the University of California
  * Copyright (C) 2017 SiFive
- *
- *   This program is free software; you can redistribute it and/or
- *   modify it under the terms of the GNU General Public License
- *   as published by the Free Software Foundation, version 2.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
+ * Copyright (C) 2018 Christoph Hellwig
  */
 
 #include <linux/interrupt.h>
 #include <linux/irqchip.h>
 #include <linux/irqdomain.h>
 
+/*
+ * Possible interrupt causes:
+ */
+#define INTERRUPT_CAUSE_SOFTWARE    1
+#define INTERRUPT_CAUSE_TIMER       5
+#define INTERRUPT_CAUSE_EXTERNAL    9
+
+/*
+ * 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))
+
+asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs, unsigned long cause)
+{
+       struct pt_regs *old_regs = set_irq_regs(regs);
+
+       irq_enter();
+       switch (cause & ~INTERRUPT_CAUSE_FLAG) {
+       case INTERRUPT_CAUSE_TIMER:
+               riscv_timer_interrupt();
+               break;
+#ifdef CONFIG_SMP
+       case INTERRUPT_CAUSE_SOFTWARE:
+               /*
+                * We only use software interrupts to pass IPIs, so if a non-SMP
+                * system gets one, then we don't know what to do.
+                */
+               riscv_software_interrupt();
+               break;
+#endif
+       case INTERRUPT_CAUSE_EXTERNAL:
+               handle_arch_irq(regs);
+               break;
+       default:
+               panic("unexpected interrupt cause");
+       }
+       irq_exit();
+
+       set_irq_regs(old_regs);
+}
+
 void __init init_IRQ(void)
 {
        irqchip_init();
index b0e10c4e9f7745beea96cac0c96d20a6b738a77c..a243fae1c1dbb8600942c6fd83205558603b5f6b 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/mutex.h>
 #include <linux/bitmap.h>
 #include <linux/irq.h>
-#include <linux/interrupt.h>
 #include <linux/perf_event.h>
 #include <linux/atomic.h>
 #include <linux/of.h>
index f0d2070866d49b170da74ae0e20e779f55a02199..db20dc630e7efbadeb8ebeb497d5685a3cbad682 100644 (file)
 #include <asm/tlbflush.h>
 #include <asm/thread_info.h>
 
+#ifdef CONFIG_EARLY_PRINTK
+static void sbi_console_write(struct console *co, const char *buf,
+                             unsigned int n)
+{
+       int i;
+
+       for (i = 0; i < n; ++i) {
+               if (buf[i] == '\n')
+                       sbi_console_putchar('\r');
+               sbi_console_putchar(buf[i]);
+       }
+}
+
+struct console riscv_sbi_early_console_dev __initdata = {
+       .name   = "early",
+       .write  = sbi_console_write,
+       .flags  = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,
+       .index  = -1
+};
+#endif
+
 #ifdef CONFIG_DUMMY_CONSOLE
 struct screen_info screen_info = {
        .orig_video_lines       = 30,
@@ -195,6 +216,12 @@ static void __init setup_bootmem(void)
 
 void __init setup_arch(char **cmdline_p)
 {
+#if defined(CONFIG_EARLY_PRINTK)
+       if (likely(early_console == NULL)) {
+               early_console = &riscv_sbi_early_console_dev;
+               register_console(early_console);
+       }
+#endif
        *cmdline_p = boot_command_line;
 
        parse_early_param();
index 6d3962435720d1ee7fe84bf74d50ffbe16d49219..906fe21ea21bf3f10db2e460cfe2b4fbd21ac790 100644 (file)
@@ -45,7 +45,7 @@ int setup_profiling_timer(unsigned int multiplier)
        return -EINVAL;
 }
 
-irqreturn_t handle_ipi(void)
+void riscv_software_interrupt(void)
 {
        unsigned long *pending_ipis = &ipi_data[smp_processor_id()].bits;
 
@@ -60,7 +60,7 @@ irqreturn_t handle_ipi(void)
 
                ops = xchg(pending_ipis, 0);
                if (ops == 0)
-                       return IRQ_HANDLED;
+                       return;
 
                if (ops & (1 << IPI_RESCHEDULE))
                        scheduler_ipi();
@@ -73,8 +73,6 @@ irqreturn_t handle_ipi(void)
                /* Order data access and bit testing. */
                mb();
        }
-
-       return IRQ_HANDLED;
 }
 
 static void
index f741458c5a3f0731fa9c016bcc1499fefc1227d5..56abab6a9812457072f4948a353fd11b40f3cd2a 100644 (file)
@@ -104,7 +104,6 @@ asmlinkage void __init smp_callin(void)
        current->active_mm = mm;
 
        trap_init();
-       init_clockevent();
        notify_cpu_starting(smp_processor_id());
        set_cpu_online(smp_processor_id(), 1);
        local_flush_tlb_all();
index 2463fcca719eb2e461ab8f1972b8b057cb05b0a0..1911c8f6b8a69668e7e787d8832fcfa2389a218a 100644 (file)
  */
 
 #include <linux/clocksource.h>
-#include <linux/clockchips.h>
 #include <linux/delay.h>
-
-#ifdef CONFIG_RISCV_TIMER
-#include <linux/timer_riscv.h>
-#endif
-
 #include <asm/sbi.h>
 
 unsigned long riscv_timebase;
 
-DECLARE_PER_CPU(struct clock_event_device, riscv_clock_event);
-
-void riscv_timer_interrupt(void)
-{
-#ifdef CONFIG_RISCV_TIMER
-       /*
-        * FIXME: This needs to be cleaned up along with the rest of the IRQ
-        * handling cleanup.  See irq.c for more details.
-        */
-       struct clock_event_device *evdev = this_cpu_ptr(&riscv_clock_event);
-
-       evdev->event_handler(evdev);
-#endif
-}
-
-void __init init_clockevent(void)
-{
-       timer_probe();
-       csr_set(sie, SIE_STIE);
-}
-
 void __init time_init(void)
 {
        struct device_node *cpu;
@@ -56,6 +29,5 @@ void __init time_init(void)
        riscv_timebase = prop;
 
        lpj_fine = riscv_timebase / HZ;
-
-       init_clockevent();
+       timer_probe();
 }
index 81a1952015a6556d3428f6e3ab3b541d7f371bfa..24a9333dda2cb9bb407d3c4ee1f3954714d30f01 100644 (file)
@@ -138,7 +138,6 @@ asmlinkage void do_trap_break(struct pt_regs *regs)
 #endif /* CONFIG_GENERIC_BUG */
 
        force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)(regs->sepc), current);
-       regs->sepc += 0x4;
 }
 
 #ifdef CONFIG_GENERIC_BUG
index f6561b783b619282c014ac62230b2a029c4ef226..eed1c137f6183a1d4f4f8303df8a0e8c12cf1f2b 100644 (file)
@@ -52,8 +52,8 @@ $(obj)/%.so: $(obj)/%.so.dbg FORCE
 # Add -lgcc so rv32 gets static muldi3 and lshrdi3 definitions.
 # Make sure only to export the intended __vdso_xxx symbol offsets.
 quiet_cmd_vdsold = VDSOLD  $@
-      cmd_vdsold = $(CC) $(KCFLAGS) $(call cc-option, -no-pie) -nostdlib $(SYSCFLAGS_$(@F)) \
-                           -Wl,-T,$(filter-out FORCE,$^) -o $@.tmp -lgcc && \
+      cmd_vdsold = $(CC) $(KBUILD_CFLAGS) $(call cc-option, -no-pie) -nostdlib -nostartfiles $(SYSCFLAGS_$(@F)) \
+                           -Wl,-T,$(filter-out FORCE,$^) -o $@.tmp && \
                    $(CROSS_COMPILE)objcopy \
                            $(patsubst %, -G __vdso_%, $(vdso-syms)) $@.tmp $@
 
index 596c2ca40d6342569191a99c75ca62821cc8f828..445ec84f9a4796e90ca80d882300d24ff770b435 100644 (file)
@@ -2,5 +2,6 @@ lib-y   += delay.o
 lib-y  += memcpy.o
 lib-y  += memset.o
 lib-y  += uaccess.o
+lib-y  += tishift.o
 
 lib-$(CONFIG_32BIT) += udivdi3.o
diff --git a/arch/riscv/lib/tishift.S b/arch/riscv/lib/tishift.S
new file mode 100644 (file)
index 0000000..69abb12
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 Free Software Foundation, Inc.
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+  .globl __lshrti3
+__lshrti3:
+  beqz  a2, .L1
+  li    a5,64
+  sub   a5,a5,a2
+  addi  sp,sp,-16
+  sext.w a4,a5
+  blez  a5, .L2
+  sext.w a2,a2
+  sll   a4,a1,a4
+  srl   a0,a0,a2
+  srl   a1,a1,a2
+  or    a0,a0,a4
+  sd    a1,8(sp)
+  sd    a0,0(sp)
+  ld    a0,0(sp)
+  ld    a1,8(sp)
+  addi  sp,sp,16
+  ret
+.L1:
+  ret
+.L2:
+  negw  a4,a4
+  srl   a1,a1,a4
+  sd    a1,0(sp)
+  sd    zero,8(sp)
+  ld    a0,0(sp)
+  ld    a1,8(sp)
+  addi  sp,sp,16
+  ret
index dec0dd88ec15fdf8ca81e01a0a9692572f1cef21..a11f4ba98b05c57d08b211ac933f93fcf7cb4616 100644 (file)
@@ -609,4 +609,15 @@ config ATCPIT100_TIMER
        help
          This option enables support for the Andestech ATCPIT100 timers.
 
+config RISCV_TIMER
+       bool "Timer for the RISC-V platform"
+       depends on RISCV
+       default y
+       select TIMER_PROBE
+       select TIMER_OF
+       help
+         This enables the per-hart timer built into all RISC-V systems, which
+         is accessed via both the SBI and the rdcycle instruction.  This is
+         required for all RISC-V systems.
+
 endmenu
index c070cc7992e9c84d45216565e7f399d54f12cfcd..db51b2427e8a64e9bfe20bbb41a9363069b101f4 100644 (file)
@@ -78,3 +78,4 @@ obj-$(CONFIG_H8300_TPU)                       += h8300_tpu.o
 obj-$(CONFIG_CLKSRC_ST_LPC)            += clksrc_st_lpc.o
 obj-$(CONFIG_X86_NUMACHIP)             += numachip.o
 obj-$(CONFIG_ATCPIT100_TIMER)          += timer-atcpit100.o
+obj-$(CONFIG_RISCV_TIMER)              += riscv_timer.o
diff --git a/drivers/clocksource/riscv_timer.c b/drivers/clocksource/riscv_timer.c
new file mode 100644 (file)
index 0000000..4e8b347
--- /dev/null
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ * Copyright (C) 2017 SiFive
+ */
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/cpu.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <asm/sbi.h>
+
+/*
+ * All RISC-V systems have a timer attached to every hart.  These timers can be
+ * read by the 'rdcycle' pseudo instruction, and can use the SBI to setup
+ * events.  In order to abstract the architecture-specific timer reading and
+ * setting functions away from the clock event insertion code, we provide
+ * function pointers to the clockevent subsystem that perform two basic
+ * operations: rdtime() reads the timer on the current CPU, and
+ * next_event(delta) sets the next timer event to 'delta' cycles in the future.
+ * As the timers are inherently a per-cpu resource, these callbacks perform
+ * operations on the current hart.  There is guaranteed to be exactly one timer
+ * per hart on all RISC-V systems.
+ */
+
+static int riscv_clock_next_event(unsigned long delta,
+               struct clock_event_device *ce)
+{
+       csr_set(sie, SIE_STIE);
+       sbi_set_timer(get_cycles64() + delta);
+       return 0;
+}
+
+static DEFINE_PER_CPU(struct clock_event_device, riscv_clock_event) = {
+       .name                   = "riscv_timer_clockevent",
+       .features               = CLOCK_EVT_FEAT_ONESHOT,
+       .rating                 = 100,
+       .set_next_event         = riscv_clock_next_event,
+};
+
+/*
+ * It is guaranteed that all the timers across all the harts are synchronized
+ * within one tick of each other, so while this could technically go
+ * backwards when hopping between CPUs, practically it won't happen.
+ */
+static unsigned long long riscv_clocksource_rdtime(struct clocksource *cs)
+{
+       return get_cycles64();
+}
+
+static DEFINE_PER_CPU(struct clocksource, riscv_clocksource) = {
+       .name           = "riscv_clocksource",
+       .rating         = 300,
+       .mask           = CLOCKSOURCE_MASK(BITS_PER_LONG),
+       .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
+       .read           = riscv_clocksource_rdtime,
+};
+
+static int riscv_timer_starting_cpu(unsigned int cpu)
+{
+       struct clock_event_device *ce = per_cpu_ptr(&riscv_clock_event, cpu);
+
+       ce->cpumask = cpumask_of(cpu);
+       clockevents_config_and_register(ce, riscv_timebase, 100, 0x7fffffff);
+
+       csr_set(sie, SIE_STIE);
+       return 0;
+}
+
+static int riscv_timer_dying_cpu(unsigned int cpu)
+{
+       csr_clear(sie, SIE_STIE);
+       return 0;
+}
+
+/* called directly from the low-level interrupt handler */
+void riscv_timer_interrupt(void)
+{
+       struct clock_event_device *evdev = this_cpu_ptr(&riscv_clock_event);
+
+       csr_clear(sie, SIE_STIE);
+       evdev->event_handler(evdev);
+}
+
+static int __init riscv_timer_init_dt(struct device_node *n)
+{
+       int cpu_id = riscv_of_processor_hart(n), error;
+       struct clocksource *cs;
+
+       if (cpu_id != smp_processor_id())
+               return 0;
+
+       cs = per_cpu_ptr(&riscv_clocksource, cpu_id);
+       clocksource_register_hz(cs, riscv_timebase);
+
+       error = cpuhp_setup_state(CPUHP_AP_RISCV_TIMER_STARTING,
+                        "clockevents/riscv/timer:starting",
+                        riscv_timer_starting_cpu, riscv_timer_dying_cpu);
+       if (error)
+               pr_err("RISCV timer register failed [%d] for cpu = [%d]\n",
+                      error, cpu_id);
+       return error;
+}
+
+TIMER_OF_DECLARE(riscv_timer, "riscv", riscv_timer_init_dt);
index d564d21245c5c3aa7ccafc39dc0964170e1de6e0..383e7b70221d21fd1197ffd1191828f3f76d204c 100644 (file)
@@ -372,3 +372,15 @@ config QCOM_PDC
          IRQs for Qualcomm Technologies Inc (QTI) mobile chips.
 
 endmenu
+
+config SIFIVE_PLIC
+       bool "SiFive Platform-Level Interrupt Controller"
+       depends on RISCV
+       help
+          This enables support for the PLIC chip found in SiFive (and
+          potentially other) RISC-V systems.  The PLIC controls devices
+          interrupts and connects them to each core's local interrupt
+          controller.  Aside from timer and software interrupts, all other
+          interrupt sources are subordinate to the PLIC.
+
+          If you don't know what to do here, say Y.
index 15f268f646bf28676563ac81c3a1637f8ff73edd..fbd1ec8070efa89e5439321f410af09579e76b1e 100644 (file)
@@ -87,3 +87,4 @@ obj-$(CONFIG_MESON_IRQ_GPIO)          += irq-meson-gpio.o
 obj-$(CONFIG_GOLDFISH_PIC)             += irq-goldfish-pic.o
 obj-$(CONFIG_NDS32)                    += irq-ativic32.o
 obj-$(CONFIG_QCOM_PDC)                 += qcom-pdc.o
+obj-$(CONFIG_SIFIVE_PLIC)              += irq-sifive-plic.o
diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c
new file mode 100644 (file)
index 0000000..532e9d6
--- /dev/null
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017 SiFive
+ * Copyright (C) 2018 Christoph Hellwig
+ */
+#define pr_fmt(fmt) "plic: " fmt
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/*
+ * This driver implements a version of the RISC-V PLIC with the actual layout
+ * specified in chapter 8 of the SiFive U5 Coreplex Series Manual:
+ *
+ *     https://static.dev.sifive.com/U54-MC-RVCoreIP.pdf
+ *
+ * The largest number supported by devices marked as 'sifive,plic-1.0.0', is
+ * 1024, of which device 0 is defined as non-existent by the RISC-V Privileged
+ * Spec.
+ */
+
+#define MAX_DEVICES                    1024
+#define MAX_CONTEXTS                   15872
+
+/*
+ * Each interrupt source has a priority register associated with it.
+ * We always hardwire it to one in Linux.
+ */
+#define PRIORITY_BASE                  0
+#define     PRIORITY_PER_ID            4
+
+/*
+ * Each hart context has a vector of interrupt enable bits associated with it.
+ * There's one bit for each interrupt source.
+ */
+#define ENABLE_BASE                    0x2000
+#define     ENABLE_PER_HART            0x80
+
+/*
+ * Each hart context has a set of control registers associated with it.  Right
+ * now there's only two: a source priority threshold over which the hart will
+ * take an interrupt, and a register to claim interrupts.
+ */
+#define CONTEXT_BASE                   0x200000
+#define     CONTEXT_PER_HART           0x1000
+#define     CONTEXT_THRESHOLD          0x00
+#define     CONTEXT_CLAIM              0x04
+
+static void __iomem *plic_regs;
+
+struct plic_handler {
+       bool                    present;
+       int                     ctxid;
+};
+static DEFINE_PER_CPU(struct plic_handler, plic_handlers);
+
+static inline void __iomem *plic_hart_offset(int ctxid)
+{
+       return plic_regs + CONTEXT_BASE + ctxid * CONTEXT_PER_HART;
+}
+
+static inline u32 __iomem *plic_enable_base(int ctxid)
+{
+       return plic_regs + ENABLE_BASE + ctxid * ENABLE_PER_HART;
+}
+
+/*
+ * Protect mask operations on the registers given that we can't assume that
+ * atomic memory operations work on them.
+ */
+static DEFINE_RAW_SPINLOCK(plic_toggle_lock);
+
+static inline void plic_toggle(int ctxid, int hwirq, int enable)
+{
+       u32 __iomem *reg = plic_enable_base(ctxid) + (hwirq / 32);
+       u32 hwirq_mask = 1 << (hwirq % 32);
+
+       raw_spin_lock(&plic_toggle_lock);
+       if (enable)
+               writel(readl(reg) | hwirq_mask, reg);
+       else
+               writel(readl(reg) & ~hwirq_mask, reg);
+       raw_spin_unlock(&plic_toggle_lock);
+}
+
+static inline void plic_irq_toggle(struct irq_data *d, int enable)
+{
+       int cpu;
+
+       writel(enable, plic_regs + PRIORITY_BASE + d->hwirq * PRIORITY_PER_ID);
+       for_each_cpu(cpu, irq_data_get_affinity_mask(d)) {
+               struct plic_handler *handler = per_cpu_ptr(&plic_handlers, cpu);
+
+               if (handler->present)
+                       plic_toggle(handler->ctxid, d->hwirq, enable);
+       }
+}
+
+static void plic_irq_enable(struct irq_data *d)
+{
+       plic_irq_toggle(d, 1);
+}
+
+static void plic_irq_disable(struct irq_data *d)
+{
+       plic_irq_toggle(d, 0);
+}
+
+static struct irq_chip plic_chip = {
+       .name           = "SiFive PLIC",
+       /*
+        * There is no need to mask/unmask PLIC interrupts.  They are "masked"
+        * by reading claim and "unmasked" when writing it back.
+        */
+       .irq_enable     = plic_irq_enable,
+       .irq_disable    = plic_irq_disable,
+};
+
+static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq,
+                             irq_hw_number_t hwirq)
+{
+       irq_set_chip_and_handler(irq, &plic_chip, handle_simple_irq);
+       irq_set_chip_data(irq, NULL);
+       irq_set_noprobe(irq);
+       return 0;
+}
+
+static const struct irq_domain_ops plic_irqdomain_ops = {
+       .map            = plic_irqdomain_map,
+       .xlate          = irq_domain_xlate_onecell,
+};
+
+static struct irq_domain *plic_irqdomain;
+
+/*
+ * Handling an interrupt is a two-step process: first you claim the interrupt
+ * by reading the claim register, then you complete the interrupt by writing
+ * that source ID back to the same claim register.  This automatically enables
+ * and disables the interrupt, so there's nothing else to do.
+ */
+static void plic_handle_irq(struct pt_regs *regs)
+{
+       struct plic_handler *handler = this_cpu_ptr(&plic_handlers);
+       void __iomem *claim = plic_hart_offset(handler->ctxid) + CONTEXT_CLAIM;
+       irq_hw_number_t hwirq;
+
+       WARN_ON_ONCE(!handler->present);
+
+       csr_clear(sie, SIE_SEIE);
+       while ((hwirq = readl(claim))) {
+               int irq = irq_find_mapping(plic_irqdomain, hwirq);
+
+               if (unlikely(irq <= 0))
+                       pr_warn_ratelimited("can't find mapping for hwirq %lu\n",
+                                       hwirq);
+               else
+                       generic_handle_irq(irq);
+               writel(hwirq, claim);
+       }
+       csr_set(sie, SIE_SEIE);
+}
+
+/*
+ * Walk up the DT tree until we find an active RISC-V core (HART) node and
+ * extract the cpuid from it.
+ */
+static int plic_find_hart_id(struct device_node *node)
+{
+       for (; node; node = node->parent) {
+               if (of_device_is_compatible(node, "riscv"))
+                       return riscv_of_processor_hart(node);
+       }
+
+       return -1;
+}
+
+static int __init plic_init(struct device_node *node,
+               struct device_node *parent)
+{
+       int error = 0, nr_handlers, nr_mapped = 0, i;
+       u32 nr_irqs;
+
+       if (plic_regs) {
+               pr_warn("PLIC already present.\n");
+               return -ENXIO;
+       }
+
+       plic_regs = of_iomap(node, 0);
+       if (WARN_ON(!plic_regs))
+               return -EIO;
+
+       error = -EINVAL;
+       of_property_read_u32(node, "riscv,ndev", &nr_irqs);
+       if (WARN_ON(!nr_irqs))
+               goto out_iounmap;
+
+       nr_handlers = of_irq_count(node);
+       if (WARN_ON(!nr_handlers))
+               goto out_iounmap;
+       if (WARN_ON(nr_handlers < num_possible_cpus()))
+               goto out_iounmap;
+
+       error = -ENOMEM;
+       plic_irqdomain = irq_domain_add_linear(node, nr_irqs + 1,
+                       &plic_irqdomain_ops, NULL);
+       if (WARN_ON(!plic_irqdomain))
+               goto out_iounmap;
+
+       for (i = 0; i < nr_handlers; i++) {
+               struct of_phandle_args parent;
+               struct plic_handler *handler;
+               irq_hw_number_t hwirq;
+               int cpu;
+
+               if (of_irq_parse_one(node, i, &parent)) {
+                       pr_err("failed to parse parent for context %d.\n", i);
+                       continue;
+               }
+
+               /* skip context holes */
+               if (parent.args[0] == -1)
+                       continue;
+
+               cpu = plic_find_hart_id(parent.np);
+               if (cpu < 0) {
+                       pr_warn("failed to parse hart ID for context %d.\n", i);
+                       continue;
+               }
+
+               handler = per_cpu_ptr(&plic_handlers, cpu);
+               handler->present = true;
+               handler->ctxid = i;
+
+               /* priority must be > threshold to trigger an interrupt */
+               writel(0, plic_hart_offset(i) + CONTEXT_THRESHOLD);
+               for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
+                       plic_toggle(i, hwirq, 0);
+               nr_mapped++;
+       }
+
+       pr_info("mapped %d interrupts to %d (out of %d) handlers.\n",
+               nr_irqs, nr_mapped, nr_handlers);
+       set_handle_irq(plic_handle_irq);
+       return 0;
+
+out_iounmap:
+       iounmap(plic_regs);
+       return error;
+}
+
+IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init);
+IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */
index 4cf06a64bc02f2e954328fa01aad7eaeefc10e96..c49843c4d031bfd289ed26b4617e5bc5592578b2 100644 (file)
@@ -125,6 +125,7 @@ enum cpuhp_state {
        CPUHP_AP_MARCO_TIMER_STARTING,
        CPUHP_AP_MIPS_GIC_TIMER_STARTING,
        CPUHP_AP_ARC_TIMER_STARTING,
+       CPUHP_AP_RISCV_TIMER_STARTING,
        CPUHP_AP_KVM_STARTING,
        CPUHP_AP_KVM_ARM_VGIC_INIT_STARTING,
        CPUHP_AP_KVM_ARM_VGIC_STARTING,