Skip to content

Commit

Permalink
Merge tag 'riscv-for-linus-4.19-mw0' of git://git.kernel.org/pub/scm/…
Browse files Browse the repository at this point in the history
…linux/kernel/git/palmer/riscv-linux

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
  • Loading branch information
torvalds committed Aug 19, 2018
2 parents 1d0926e + 627672c commit 1009aa1
Show file tree
Hide file tree
Showing 27 changed files with 625 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -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;
};
};
Original file line number Diff line number Diff line change
@@ -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>;
};
3 changes: 3 additions & 0 deletions arch/riscv/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions arch/riscv/configs/defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions arch/riscv/include/asm/csr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 1 addition & 4 deletions arch/riscv/include/asm/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@

#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>

Expand Down
1 change: 1 addition & 0 deletions arch/riscv/include/asm/perf_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include <linux/perf_event.h>
#include <linux/ptrace.h>
#include <linux/interrupt.h>

#define RISCV_BASE_COUNTERS 2

Expand Down
6 changes: 0 additions & 6 deletions arch/riscv/include/asm/smp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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 */
4 changes: 2 additions & 2 deletions arch/riscv/kernel/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions arch/riscv/kernel/head.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
55 changes: 46 additions & 9 deletions arch/riscv/kernel/irq.c
Original file line number Diff line number Diff line change
@@ -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();
Expand Down
1 change: 0 additions & 1 deletion arch/riscv/kernel/perf_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
27 changes: 27 additions & 0 deletions arch/riscv/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,27 @@
#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,
Expand Down Expand Up @@ -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();
Expand Down
6 changes: 2 additions & 4 deletions arch/riscv/kernel/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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();
Expand All @@ -73,8 +73,6 @@ irqreturn_t handle_ipi(void)
/* Order data access and bit testing. */
mb();
}

return IRQ_HANDLED;
}

static void
Expand Down
1 change: 0 additions & 1 deletion arch/riscv/kernel/smpboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
30 changes: 1 addition & 29 deletions arch/riscv/kernel/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,11 @@
*/

#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;
Expand All @@ -56,6 +29,5 @@ void __init time_init(void)
riscv_timebase = prop;

lpj_fine = riscv_timebase / HZ;

init_clockevent();
timer_probe();
}
Loading

0 comments on commit 1009aa1

Please sign in to comment.