On Allwinner D1, the SBI call to schedule timer interrupts doesn't work.
authorkettenis <kettenis@openbsd.org>
Sat, 27 Jan 2024 12:05:40 +0000 (12:05 +0000)
committerkettenis <kettenis@openbsd.org>
Sat, 27 Jan 2024 12:05:40 +0000 (12:05 +0000)
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
sys/arch/riscv64/dev/sxitimer.c [new file with mode: 0644]
sys/arch/riscv64/include/cpu.h
sys/arch/riscv64/riscv64/clock.c

index eb15831..cf1518b 100644 (file)
@@ -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 (file)
index 0000000..2496b07
--- /dev/null
@@ -0,0 +1,178 @@
+/*     $OpenBSD: sxitimer.c,v 1.1 2024/01/27 12:05:40 kettenis Exp $   */
+/*
+ * Copyright (c) 2024 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/clockintr.h>
+#include <sys/device.h>
+#include <sys/sensors.h>
+
+#include <machine/intr.h>
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_clock.h>
+#include <dev/ofw/fdt.h>
+
+/* 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);
+}
index 282cece..be8f96c 100644 (file)
@@ -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 <mlarkin@openbsd.org>
@@ -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 *);
 
index d0574f5..f977529 100644 (file)
@@ -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 <kettenis@openbsd.org>
@@ -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)
 {