From eda78419691a0f30e20eb4a2878e56aa339650a3 Mon Sep 17 00:00:00 2001 From: kettenis Date: Sat, 27 Jan 2024 12:05:40 +0000 Subject: [PATCH] On Allwinner D1, the SBI call to schedule timer interrupts doesn't work. Instead we have to use one of the timers integerated on the SoC that triggers an external interrupt. Add the appropriate driver and change the MD clock code to hook it up. ok cheloha@, jca@ --- sys/arch/riscv64/conf/files.riscv64 | 7 +- sys/arch/riscv64/dev/sxitimer.c | 178 ++++++++++++++++++++++++++++ sys/arch/riscv64/include/cpu.h | 4 +- sys/arch/riscv64/riscv64/clock.c | 28 +++-- 4 files changed, 206 insertions(+), 11 deletions(-) create mode 100644 sys/arch/riscv64/dev/sxitimer.c diff --git a/sys/arch/riscv64/conf/files.riscv64 b/sys/arch/riscv64/conf/files.riscv64 index eb15831ea4e..cf1518b3dfc 100644 --- a/sys/arch/riscv64/conf/files.riscv64 +++ b/sys/arch/riscv64/conf/files.riscv64 @@ -1,4 +1,4 @@ -# $OpenBSD: files.riscv64,v 1.28 2024/01/01 18:25:50 kettenis Exp $ +# $OpenBSD: files.riscv64,v 1.29 2024/01/27 12:05:40 kettenis Exp $ # Standard stanzas config(8) can't run without maxpartitions 16 @@ -81,6 +81,11 @@ device plic attach plic at fdt file arch/riscv64/dev/plic.c plic +# Allwinner timer +device sxitimer +attach sxitimer at fdt +file arch/riscv64/dev/sxitimer.c sxitimer + # PolarFire SoC MSS clock controller device mpfclock attach mpfclock at fdt diff --git a/sys/arch/riscv64/dev/sxitimer.c b/sys/arch/riscv64/dev/sxitimer.c new file mode 100644 index 00000000000..2496b07a550 --- /dev/null +++ b/sys/arch/riscv64/dev/sxitimer.c @@ -0,0 +1,178 @@ +/* $OpenBSD: sxitimer.c,v 1.1 2024/01/27 12:05:40 kettenis Exp $ */ +/* + * Copyright (c) 2024 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* Registers */ +#define TMR_IRQ_EN 0x0000 +#define TMR1_IRQ_EN (1 << 1) +#define TMR0_IRQ_EN (1 << 0) +#define TMR_IRQ_STA 0x0004 +#define TMR1_IRQ_PEND (1 << 1) +#define TMR0_IRQ_PEND (1 << 0) +#define TMR0_CTRL 0x0010 +#define TMR0_MODE_SINGLE (1 << 7) +#define TMR0_CLK_PRES_1 (0 << 4) +#define TMR0_CLK_SRC_OSC24M (1 << 2) +#define TMR0_RELOAD (1 << 1) +#define TMR0_EN (1 << 0) +#define TMR0_INTV_VALUE 0x0014 + +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) + +struct sxitimer_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + uint32_t sc_ticks_per_second; + uint64_t sc_nsec_cycle_ratio; + uint64_t sc_nsec_max; + void *sc_ih; +}; + +int sxitimer_match(struct device *, void *, void *); +void sxitimer_attach(struct device *, struct device *, void *); + +const struct cfattach sxitimer_ca = { + sizeof (struct sxitimer_softc), sxitimer_match, sxitimer_attach +}; + +struct cfdriver sxitimer_cd = { + NULL, "sxitimer", DV_DULL +}; + +void sxitimer_startclock(void); +int sxitimer_intr(void *); +void sxitimer_rearm(void *, uint64_t); +void sxitimer_trigger(void *); + +struct intrclock sxitimer_intrclock = { + .ic_rearm = sxitimer_rearm, + .ic_trigger = sxitimer_trigger +}; + +int +sxitimer_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return OF_is_compatible(faa->fa_node, "allwinner,sun20i-d1-timer"); +} + +void +sxitimer_attach(struct device *parent, struct device *self, void *aux) +{ + struct sxitimer_softc *sc = (struct sxitimer_softc *)self; + struct fdt_attach_args *faa = aux; + + if (faa->fa_nreg < 1) { + printf(": no registers\n"); + return; + } + + sc->sc_iot = faa->fa_iot; + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, + faa->fa_reg[0].size, 0, &sc->sc_ioh)) { + printf(": can't map registers\n"); + return; + } + + HWRITE4(sc, TMR_IRQ_EN, 0); + + sc->sc_ticks_per_second = clock_get_frequency(faa->fa_node, NULL); + sc->sc_nsec_cycle_ratio = + sc->sc_ticks_per_second * (1ULL << 32) / 1000000000; + sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio; + + sxitimer_intrclock.ic_cookie = sc; + cpu_startclock_fcn = sxitimer_startclock; + + sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, 0, IPL_CLOCK, + sxitimer_intr, NULL, sc->sc_dev.dv_xname); + if (sc->sc_ih == NULL) { + printf("can't establish interrupt\n"); + return; + } + + HWRITE4(sc, TMR0_INTV_VALUE, 0); + HWRITE4(sc, TMR0_CTRL, TMR0_MODE_SINGLE | TMR0_CLK_PRES_1 | + TMR0_CLK_SRC_OSC24M | TMR0_RELOAD | TMR0_EN); + HWRITE4(sc, TMR_IRQ_STA, TMR0_IRQ_PEND); + HWRITE4(sc, TMR_IRQ_EN, TMR0_IRQ_EN); + + printf(": %u kHz\n", sc->sc_ticks_per_second / 1000); +} + +void +sxitimer_startclock(void) +{ + clockintr_cpu_init(&sxitimer_intrclock); + clockintr_trigger(); +} + +int +sxitimer_intr(void *frame) +{ + struct sxitimer_softc *sc = sxitimer_intrclock.ic_cookie; + + HWRITE4(sc, TMR_IRQ_STA, TMR0_IRQ_PEND); + return clockintr_dispatch(frame); +} + +void +sxitimer_rearm(void *cookie, uint64_t nsecs) +{ + struct sxitimer_softc *sc = cookie; + uint32_t cycles; + + if (nsecs > sc->sc_nsec_max) + nsecs = sc->sc_nsec_max; + cycles = (nsecs * sc->sc_nsec_cycle_ratio) >> 32; + if (cycles > UINT32_MAX) + cycles = UINT32_MAX; + if (cycles < 1) + cycles = 1; + HWRITE4(sc, TMR0_INTV_VALUE, cycles); + HWRITE4(sc, TMR0_CTRL, TMR0_MODE_SINGLE | TMR0_CLK_PRES_1 | + TMR0_CLK_SRC_OSC24M | TMR0_RELOAD | TMR0_EN); +} + +void +sxitimer_trigger(void *cookie) +{ + struct sxitimer_softc *sc = cookie; + + HWRITE4(sc, TMR0_INTV_VALUE, 1); + HWRITE4(sc, TMR0_CTRL, TMR0_MODE_SINGLE | TMR0_CLK_PRES_1 | + TMR0_CLK_SRC_OSC24M | TMR0_RELOAD | TMR0_EN); +} diff --git a/sys/arch/riscv64/include/cpu.h b/sys/arch/riscv64/include/cpu.h index 282cece0448..be8f96cc38a 100644 --- a/sys/arch/riscv64/include/cpu.h +++ b/sys/arch/riscv64/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.20 2024/01/24 19:23:39 cheloha Exp $ */ +/* $OpenBSD: cpu.h,v 1.21 2024/01/27 12:05:40 kettenis Exp $ */ /* * Copyright (c) 2019 Mike Larkin @@ -267,6 +267,8 @@ intr_restore(u_long s) void delay (unsigned); #define DELAY(x) delay(x) +extern void (*cpu_startclock_fcn)(void); + void fpu_save(struct proc *, struct trapframe *); void fpu_load(struct proc *); diff --git a/sys/arch/riscv64/riscv64/clock.c b/sys/arch/riscv64/riscv64/clock.c index d0574f534b0..f9775297737 100644 --- a/sys/arch/riscv64/riscv64/clock.c +++ b/sys/arch/riscv64/riscv64/clock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clock.c,v 1.13 2023/09/17 14:50:51 cheloha Exp $ */ +/* $OpenBSD: clock.c,v 1.14 2024/01/27 12:05:40 kettenis Exp $ */ /* * Copyright (c) 2020 Mark Kettenis @@ -36,6 +36,7 @@ uint64_t timer_nsec_cycle_ratio; struct evcount clock_count; +void timer_startclock(void); void timer_rearm(void *, uint64_t); void timer_trigger(void *); @@ -56,6 +57,7 @@ static struct timecounter tb_timecounter = { .tc_user = TC_TB, }; +void (*cpu_startclock_fcn)(void) = timer_startclock; int clock_intr(void *); void @@ -87,28 +89,36 @@ cpu_initclocks(void) tb_timecounter.tc_frequency = tb_freq; tc_init(&tb_timecounter); - timer_nsec_cycle_ratio = tb_freq * (1ULL << 32) / 1000000000; - timer_nsec_max = UINT64_MAX / timer_nsec_cycle_ratio; - stathz = hz; profhz = stathz * 10; statclock_is_randomized = 1; - riscv_intc_intr_establish(IRQ_TIMER_SUPERVISOR, 0, - clock_intr, NULL, NULL); + if (cpu_startclock_fcn == timer_startclock) { + timer_nsec_cycle_ratio = tb_freq * (1ULL << 32) / 1000000000; + timer_nsec_max = UINT64_MAX / timer_nsec_cycle_ratio; + + riscv_intc_intr_establish(IRQ_TIMER_SUPERVISOR, 0, + clock_intr, NULL, NULL); - evcount_attach(&clock_count, "clock", NULL); - evcount_percpu(&clock_count); + evcount_attach(&clock_count, "clock", NULL); + evcount_percpu(&clock_count); + } } void -cpu_startclock(void) +timer_startclock(void) { clockintr_cpu_init(&timer_intrclock); clockintr_trigger(); csr_set(sie, SIE_STIE); } +void +cpu_startclock(void) +{ + cpu_startclock_fcn(); +} + int clock_intr(void *frame) { -- 2.20.1