o Add driver for PLIC (Platform-Level Interrupt Controller) device.

o Convert interrupt machdep support to use INTRNG code.

Sponsored by:	DARPA, AFRL
This commit is contained in:
br 2018-06-12 17:45:15 +00:00
parent f0d2af516b
commit ef85338e60
12 changed files with 354 additions and 139 deletions

View File

@ -9,8 +9,11 @@ dev/ofw/ofw_cpu.c optional fdt
dev/uart/uart_cpu_fdt.c optional uart fdt
dev/xilinx/axi_quad_spi.c optional xilinx_spi
kern/kern_clocksource.c standard
kern/msi_if.m standard
kern/pic_if.m standard
kern/subr_devmap.c standard
kern/subr_dummy_vdso_tc.c standard
kern/subr_intr.c standard
libkern/bcmp.c standard
libkern/bcopy.c standard
libkern/ffs.c standard
@ -44,6 +47,7 @@ riscv/riscv/mp_machdep.c optional smp
riscv/riscv/mem.c standard
riscv/riscv/nexus.c standard
riscv/riscv/ofw_machdep.c optional fdt
riscv/riscv/plic.c standard
riscv/riscv/pmap.c standard
riscv/riscv/riscv_console.c optional rcons
riscv/riscv/soc.c standard

View File

@ -2,3 +2,4 @@
RISCV opt_global.h
FPE opt_global.h
INTRNG opt_global.h

View File

@ -76,6 +76,7 @@ options RACCT # Resource accounting framework
options RACCT_DEFAULT_TO_DISABLED # Set kern.racct.enable=0 by default
options RCTL # Resource limits
options SMP
options INTRNG
# RISC-V SBI console
device rcons

View File

@ -37,11 +37,19 @@
#ifndef _MACHINE_INTR_MACHDEP_H_
#define _MACHINE_INTR_MACHDEP_H_
#define RISCV_NIRQ 1024
#ifndef NIRQ
#define NIRQ RISCV_NIRQ
#endif
#ifdef INTRNG
#include <sys/intr.h>
#endif
struct trapframe;
void riscv_init_interrupts(void);
int riscv_teardown_intr(void *);
int riscv_config_intr(u_int, enum intr_trigger, enum intr_polarity);
int riscv_setup_intr(const char *, driver_filter_t *, driver_intr_t *,
void *, int, int, void **);
void riscv_cpu_intr(struct trapframe *);
@ -69,12 +77,7 @@ enum {
IRQ_EXTERNAL_SUPERVISOR,
IRQ_EXTERNAL_HYPERVISOR,
IRQ_EXTERNAL_MACHINE,
#if 0
/* lowRISC TODO */
IRQ_COP, /* lowRISC only */
IRQ_UART, /* lowRISC only */
#endif
NIRQS
INTC_NIRQS
};
#endif /* !_MACHINE_INTR_MACHDEP_H_ */

View File

@ -137,6 +137,8 @@
#define SIE_SSIE (1 << 1)
#define SIE_UTIE (1 << 4)
#define SIE_STIE (1 << 5)
#define SIE_UEIE (1 << 8)
#define SIE_SEIE (1 << 9)
#define MIP_SEIP (1 << 9)

View File

@ -46,6 +46,8 @@
#define IPI_STOP_HARD (1 << 4)
#define IPI_HARDCLOCK (1 << 5)
#define INTR_IPI_COUNT 1
void ipi_all_but_self(u_int ipi);
void ipi_cpu(int cpu, u_int ipi);
void ipi_selected(cpuset_t cpus, u_int ipi);

View File

@ -38,11 +38,13 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/cpuset.h>
#include <sys/interrupt.h>
#include <sys/smp.h>
#include <sys/vmmeter.h>
#include <machine/bus.h>
#include <machine/clock.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
@ -50,44 +52,22 @@ __FBSDID("$FreeBSD$");
#include <machine/intr.h>
#include <machine/sbi.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#ifdef SMP
#include <machine/smp.h>
#endif
u_long intrcnt[NIRQS];
size_t sintrcnt = sizeof(intrcnt);
void intr_irq_handler(struct trapframe *tf);
char intrnames[NIRQS * (MAXCOMLEN + 1) * 2];
size_t sintrnames = sizeof(intrnames);
struct intc_irqsrc {
struct intr_irqsrc isrc;
u_int irq;
};
static struct intr_event *intr_events[NIRQS];
static riscv_intrcnt_t riscv_intr_counters[NIRQS];
static int intrcnt_index;
riscv_intrcnt_t
riscv_intrcnt_create(const char* name)
{
riscv_intrcnt_t counter;
counter = &intrcnt[intrcnt_index++];
riscv_intrcnt_setname(counter, name);
return (counter);
}
void
riscv_intrcnt_setname(riscv_intrcnt_t counter, const char *name)
{
int i;
i = (counter - intrcnt);
KASSERT(counter != NULL, ("riscv_intrcnt_setname: NULL counter"));
snprintf(intrnames + (MAXCOMLEN + 1) * i,
MAXCOMLEN + 1, "%-*s", MAXCOMLEN, name);
}
struct intc_irqsrc isrcs[INTC_NIRQS];
static void
riscv_mask_irq(void *source)
@ -102,15 +82,10 @@ riscv_mask_irq(void *source)
break;
case IRQ_SOFTWARE_USER:
csr_clear(sie, SIE_USIE);
break;
case IRQ_SOFTWARE_SUPERVISOR:
csr_clear(sie, SIE_SSIE);
break;
#if 0
/* lowRISC TODO */
case IRQ_UART:
machine_command(ECALL_IO_IRQ_MASK, 0);
break;
#endif
default:
panic("Unknown irq %d\n", irq);
}
@ -133,60 +108,37 @@ riscv_unmask_irq(void *source)
case IRQ_SOFTWARE_SUPERVISOR:
csr_set(sie, SIE_SSIE);
break;
#if 0
/* lowRISC TODO */
case IRQ_UART:
machine_command(ECALL_IO_IRQ_MASK, 1);
break;
#endif
default:
panic("Unknown irq %d\n", irq);
}
}
void
riscv_init_interrupts(void)
{
char name[MAXCOMLEN + 1];
int i;
for (i = 0; i < NIRQS; i++) {
snprintf(name, MAXCOMLEN + 1, "int%d:", i);
riscv_intr_counters[i] = riscv_intrcnt_create(name);
}
}
int
riscv_setup_intr(const char *name, driver_filter_t *filt,
void (*handler)(void*), void *arg, int irq, int flags, void **cookiep)
{
struct intr_event *event;
struct intr_irqsrc *isrc;
int error;
if (irq < 0 || irq >= NIRQS)
if (irq < 0 || irq >= INTC_NIRQS)
panic("%s: unknown intr %d", __func__, irq);
event = intr_events[irq];
if (event == NULL) {
error = intr_event_create(&event, (void *)(uintptr_t)irq, 0,
irq, riscv_mask_irq, riscv_unmask_irq,
NULL, NULL, "int%d", irq);
isrc = &isrcs[irq].isrc;
if (isrc->isrc_event == NULL) {
error = intr_event_create(&isrc->isrc_event, isrc, 0, irq,
riscv_mask_irq, riscv_unmask_irq, NULL, NULL, "int%d", irq);
if (error)
return (error);
intr_events[irq] = event;
riscv_unmask_irq((void*)(uintptr_t)irq);
}
error = intr_event_add_handler(event, name, filt, handler, arg,
intr_priority(flags), flags, cookiep);
error = intr_event_add_handler(isrc->isrc_event, name,
filt, handler, arg, intr_priority(flags), flags, cookiep);
if (error) {
printf("Failed to setup intr: %d\n", irq);
return (error);
}
riscv_intrcnt_setname(riscv_intr_counters[irq],
event->ie_fullname);
return (0);
}
@ -199,19 +151,10 @@ riscv_teardown_intr(void *ih)
return (0);
}
int
riscv_config_intr(u_int irq, enum intr_trigger trig, enum intr_polarity pol)
{
/* There is no configuration for interrupts */
return (0);
}
void
riscv_cpu_intr(struct trapframe *frame)
{
struct intr_event *event;
struct intr_irqsrc *isrc;
int active_irq;
critical_enter();
@ -222,26 +165,20 @@ riscv_cpu_intr(struct trapframe *frame)
active_irq = (frame->tf_scause & EXCP_MASK);
switch (active_irq) {
#if 0
/* lowRISC TODO */
case IRQ_UART:
#endif
case IRQ_SOFTWARE_USER:
case IRQ_SOFTWARE_SUPERVISOR:
case IRQ_TIMER_SUPERVISOR:
event = intr_events[active_irq];
/* Update counters */
atomic_add_long(riscv_intr_counters[active_irq], 1);
VM_CNT_INC(v_intr);
isrc = &isrcs[active_irq].isrc;
if (intr_isrc_dispatch(isrc, frame) != 0)
printf("stray interrupt %d\n", active_irq);
break;
case IRQ_EXTERNAL_SUPERVISOR:
intr_irq_handler(frame);
break;
default:
event = NULL;
break;
}
if (!event || TAILQ_EMPTY(&event->ie_handlers) ||
(intr_event_handle(event, frame) != 0))
printf("stray interrupt %d\n", active_irq);
critical_exit();
}
@ -320,5 +257,22 @@ ipi_selected(cpuset_t cpus, u_int ipi)
}
sbi_send_ipi(&mask);
}
#endif
/* Interrupt machdep initialization routine. */
static void
intc_init(void *dummy __unused)
{
int error;
int i;
for (i = 0; i < INTC_NIRQS; i++) {
isrcs[i].irq = i;
error = intr_isrc_register(&isrcs[i].isrc, NULL,
0, "intc,%u", i);
if (error != 0)
printf("Can't register interrupt %d\n", i);
}
}
SYSINIT(intc_init, SI_SUB_INTR, SI_ORDER_MIDDLE, intc_init, NULL);

View File

@ -872,8 +872,6 @@ initriscv(struct riscv_bootparams *rvbp)
init_param2(physmem);
kdb_init();
riscv_init_interrupts();
early_boot = 0;
}

View File

@ -256,6 +256,9 @@ init_secondary(uint64_t cpu)
/* Enable interrupts */
intr_enable();
/* Enable external (PLIC) interrupts */
csr_set(sie, SIE_SEIE);
mtx_lock_spin(&ap_boot_mtx);
atomic_add_rel_32(&smp_cpus, 1);

View File

@ -38,6 +38,7 @@
* ISA code but it's easier to do it here for now), I/O port addresses,
* and I/O memory address space.
*/
#include "opt_platform.h"
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
@ -48,22 +49,18 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <sys/interrupt.h>
#include <machine/vmparam.h>
#include <machine/pcb.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <machine/intr.h>
#include "opt_platform.h"
#include <dev/fdt/fdt_common.h>
#ifdef FDT
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ofw/openfirm.h>
#include "ofw_bus_if.h"
#endif
extern struct bus_space memmap_bus;
@ -265,7 +262,7 @@ nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
enum intr_polarity pol)
{
return (riscv_config_intr(irq, trig, pol));
return (EOPNOTSUPP);
}
static int
@ -282,8 +279,7 @@ nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
if (error)
return (error);
error = riscv_setup_intr(device_get_nameunit(child), filt, intr,
arg, rman_get_start(res), flags, cookiep);
error = intr_setup_irq(child, res, filt, intr, arg, flags, cookiep);
return (error);
}
@ -292,7 +288,7 @@ static int
nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
{
return (riscv_teardown_intr(ih));
return (intr_teardown_irq(child, r, ih));
}
static int
@ -321,7 +317,14 @@ nexus_activate_resource(device_t bus, device_t child, int type, int rid,
rman_set_bustag(r, &memmap_bus);
rman_set_virtual(r, (void *)vaddr);
rman_set_bushandle(r, vaddr);
} else if (type == SYS_RES_IRQ) {
err = intr_activate_irq(child, r);
if (err != 0) {
rman_deactivate_resource(r);
return (err);
}
}
return (0);
}
@ -375,16 +378,17 @@ static int
nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells,
pcell_t *intr)
{
int irq;
struct intr_map_data_fdt *fdt_data;
size_t len;
u_int irq;
if (icells == 3) {
irq = intr[1];
if (intr[0] == 0)
irq += 32; /* SPI */
else
irq += 16; /* PPI */
} else
irq = intr[0];
len = sizeof(*fdt_data) + icells * sizeof(pcell_t);
fdt_data = (struct intr_map_data_fdt *)intr_alloc_map_data(
INTR_MAP_DATA_FDT, len, M_WAITOK | M_ZERO);
fdt_data->iparent = iparent;
fdt_data->ncells = icells;
memcpy(fdt_data->cells, intr, icells * sizeof(pcell_t));
irq = intr_map_irq(NULL, iparent, (struct intr_map_data *)fdt_data);
return (irq);
}

254
sys/riscv/riscv/plic.c Normal file
View File

@ -0,0 +1,254 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2018 Ruslan Bukin <br@bsdpad.com>
* All rights reserved.
*
* This software was developed by SRI International and the University of
* Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
* ("CTSRD"), as part of the DARPA CRASH research programme.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/ktr.h>
#include <sys/module.h>
#include <sys/proc.h>
#include <sys/rman.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "pic_if.h"
#define PLIC_NIRQS 32
#define PLIC_PRIORITY(n) (0x000000 + (n) * 0x4)
#define PLIC_ENABLE(n, h) (0x002000 + (h) * 0x80 + (n) / 32)
#define PLIC_THRESHOLD(h) (0x200000 + (h) * 0x1000 + 0x0)
#define PLIC_CLAIM(h) (0x200000 + (h) * 0x1000 + 0x4)
struct plic_irqsrc {
struct intr_irqsrc isrc;
u_int irq;
};
struct plic_softc {
device_t dev;
struct resource * intc_res;
struct plic_irqsrc isrcs[PLIC_NIRQS];
};
#define RD4(sc, reg) \
bus_read_4(sc->intc_res, (reg))
#define WR4(sc, reg, val) \
bus_write_4(sc->intc_res, (reg), (val))
static inline void
plic_irq_dispatch(struct plic_softc *sc, u_int irq,
struct trapframe *tf)
{
struct plic_irqsrc *src;
src = &sc->isrcs[irq];
if (intr_isrc_dispatch(&src->isrc, tf) != 0)
device_printf(sc->dev, "Stray irq %u detected\n", irq);
}
static int
plic_intr(void *arg)
{
struct plic_softc *sc;
struct trapframe *tf;
uint32_t pending;
uint32_t cpu;
sc = arg;
cpu = PCPU_GET(cpuid);
pending = RD4(sc, PLIC_CLAIM(cpu));
if (pending) {
tf = curthread->td_intr_frame;
plic_irq_dispatch(sc, pending, tf);
WR4(sc, PLIC_CLAIM(cpu), pending);
}
return (FILTER_HANDLED);
}
static void
plic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
{
struct plic_softc *sc;
struct plic_irqsrc *src;
uint32_t reg;
uint32_t cpu;
sc = device_get_softc(dev);
src = (struct plic_irqsrc *)isrc;
cpu = PCPU_GET(cpuid);
reg = RD4(sc, PLIC_ENABLE(src->irq, cpu));
reg &= ~(1 << (src->irq % 32));
WR4(sc, PLIC_ENABLE(src->irq, cpu), reg);
}
static void
plic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
{
struct plic_softc *sc;
struct plic_irqsrc *src;
uint32_t reg;
uint32_t cpu;
sc = device_get_softc(dev);
src = (struct plic_irqsrc *)isrc;
WR4(sc, PLIC_PRIORITY(src->irq), 1);
cpu = PCPU_GET(cpuid);
reg = RD4(sc, PLIC_ENABLE(src->irq, cpu));
reg |= (1 << (src->irq % 32));
WR4(sc, PLIC_ENABLE(src->irq, cpu), reg);
}
static int
plic_map_intr(device_t dev, struct intr_map_data *data,
struct intr_irqsrc **isrcp)
{
struct intr_map_data_fdt *daf;
struct plic_softc *sc;
sc = device_get_softc(dev);
if (data->type != INTR_MAP_DATA_FDT)
return (ENOTSUP);
daf = (struct intr_map_data_fdt *)data;
if (daf->ncells != 1 || daf->cells[0] >= PLIC_NIRQS)
return (EINVAL);
*isrcp = &sc->isrcs[daf->cells[0]].isrc;
return (0);
}
static int
plic_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "riscv,plic0"))
return (ENXIO);
device_set_desc(dev, "RISC-V PLIC");
return (BUS_PROBE_DEFAULT);
}
static int
plic_attach(device_t dev)
{
struct plic_irqsrc *isrcs;
struct plic_softc *sc;
struct intr_pic *pic;
uint32_t irq;
const char *name;
phandle_t xref;
uint32_t cpu;
int error;
int rid;
sc = device_get_softc(dev);
sc->dev = dev;
/* Request memory resources */
rid = 0;
sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
if (sc->intc_res == NULL) {
device_printf(dev,
"Error: could not allocate memory resources\n");
return (ENXIO);
}
isrcs = sc->isrcs;
name = device_get_nameunit(sc->dev);
cpu = PCPU_GET(cpuid);
for (irq = 0; irq < PLIC_NIRQS; irq++) {
isrcs[irq].irq = irq;
error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
0, "%s,%u", name, irq);
if (error != 0)
return (error);
WR4(sc, PLIC_ENABLE(irq, cpu), 0);
}
WR4(sc, PLIC_THRESHOLD(cpu), 0);
xref = OF_xref_from_node(ofw_bus_get_node(sc->dev));
pic = intr_pic_register(sc->dev, xref);
if (pic == NULL)
return (ENXIO);
csr_set(sie, SIE_SEIE);
return (intr_pic_claim_root(sc->dev, xref, plic_intr, sc, 0));
}
static device_method_t plic_methods[] = {
DEVMETHOD(device_probe, plic_probe),
DEVMETHOD(device_attach, plic_attach),
DEVMETHOD(pic_disable_intr, plic_disable_intr),
DEVMETHOD(pic_enable_intr, plic_enable_intr),
DEVMETHOD(pic_map_intr, plic_map_intr),
DEVMETHOD_END
};
static driver_t plic_driver = {
"plic",
plic_methods,
sizeof(struct plic_softc),
};
static devclass_t plic_devclass;
EARLY_DRIVER_MODULE(plic, simplebus, plic_driver, plic_devclass,
0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);

View File

@ -70,8 +70,6 @@ struct riscv_timer_softc {
void *ih;
uint32_t clkfreq;
struct eventtimer et;
int intr_rid;
struct resource *intr_res;
};
static struct riscv_timer_softc *riscv_timer_sc = NULL;
@ -188,18 +186,9 @@ riscv_timer_attach(device_t dev)
riscv_timer_sc = sc;
sc->intr_rid = 0;
sc->intr_res = bus_alloc_resource(dev,
SYS_RES_IRQ, &sc->intr_rid, IRQ_TIMER_SUPERVISOR,
IRQ_TIMER_SUPERVISOR, 1, RF_ACTIVE);
if (sc->intr_res == NULL) {
device_printf(dev, "failed to allocate irq\n");
return (ENXIO);
}
/* Setup IRQs handler */
error = bus_setup_intr(dev, sc->intr_res, INTR_TYPE_CLK,
riscv_timer_intr, NULL, sc, &sc->ih);
error = riscv_setup_intr(device_get_nameunit(dev), riscv_timer_intr,
NULL, sc, IRQ_TIMER_SUPERVISOR, INTR_TYPE_CLK, &sc->ih);
if (error) {
device_printf(dev, "Unable to alloc int resource.\n");
return (ENXIO);