This patch switches dmtimer(4) over to use the clockintr subsystem. This is v2. v1 is here:
https://marc.info/?l=openbsd-tech&m=167060320326851&w=2 This revision fixes a bug in dmtimer_rearm() that kept the kernel from booting with the v1 patch: This revision also switches stathz from 128 to 100 and profhz from 1024 to 1000 to align dmtimer(4) with other drivers and platforms. I am looking for a tester to build a release atop kernel with this patch and then upgrade from the resulting bsd.rd. This will demonstrate that the patched system is stable enough to self-host. This is the last armv7 driver that needs testing before we can switch armv7 over to clockintr. Index: arm/include/_types.h =================================================================== RCS file: /cvs/src/sys/arch/arm/include/_types.h,v retrieving revision 1.19 diff -u -p -r1.19 _types.h --- arm/include/_types.h 5 Mar 2018 01:15:25 -0000 1.19 +++ arm/include/_types.h 11 Jan 2023 01:05:39 -0000 @@ -35,6 +35,8 @@ #ifndef _ARM__TYPES_H_ #define _ARM__TYPES_H_ +#define __HAVE_CLOCKINTR + #if defined(_KERNEL) typedef struct label_t { long val[11]; Index: arm/include/cpu.h =================================================================== RCS file: /cvs/src/sys/arch/arm/include/cpu.h,v retrieving revision 1.61 diff -u -p -r1.61 cpu.h --- arm/include/cpu.h 6 Jul 2021 09:34:06 -0000 1.61 +++ arm/include/cpu.h 11 Jan 2023 01:05:39 -0000 @@ -149,6 +149,7 @@ void arm32_vector_init(vaddr_t, int); * Per-CPU information. For now we assume one CPU. */ +#include <sys/clockintr.h> #include <sys/device.h> #include <sys/sched.h> #include <sys/srp.h> @@ -198,7 +199,7 @@ struct cpu_info { #ifdef GPROF struct gmonparam *ci_gmon; #endif - + struct clockintr_queue ci_queue; char ci_panicbuf[512]; }; Index: armv7/omap/dmtimer.c =================================================================== RCS file: /cvs/src/sys/arch/armv7/omap/dmtimer.c,v retrieving revision 1.15 diff -u -p -r1.15 dmtimer.c --- armv7/omap/dmtimer.c 21 Feb 2022 10:57:58 -0000 1.15 +++ armv7/omap/dmtimer.c 11 Jan 2023 01:05:39 -0000 @@ -25,8 +25,10 @@ #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> +#include <sys/clockintr.h> #include <sys/evcount.h> #include <sys/device.h> +#include <sys/stdint.h> #include <sys/timetc.h> #include <machine/bus.h> #include <armv7/armv7/armv7var.h> @@ -95,11 +97,9 @@ #define TIMER_FREQUENCY 32768 /* 32kHz is used, selectable */ #define MAX_TIMERS 2 -static struct evcount clk_count; -static struct evcount stat_count; - void dmtimer_attach(struct device *parent, struct device *self, void *args); int dmtimer_intr(void *frame); +void dmtimer_reset_tisr(void); void dmtimer_wait(int reg); void dmtimer_cpu_initclocks(void); void dmtimer_delay(u_int); @@ -117,6 +117,14 @@ static struct timecounter dmtimer_timeco .tc_priv = NULL, }; +void dmtimer_rearm(void *, uint64_t); +void dmtimer_trigger(void *); + +struct intrclock dmtimer_intrclock = { + .ic_rearm = dmtimer_rearm, + .ic_trigger = dmtimer_trigger +}; + bus_space_handle_t dmtimer_ioh0; int dmtimer_irq = 0; @@ -126,13 +134,8 @@ struct dmtimer_softc { bus_space_handle_t sc_ioh[MAX_TIMERS]; u_int32_t sc_irq; u_int32_t sc_ticks_per_second; - u_int32_t sc_ticks_per_intr; - u_int32_t sc_ticks_err_cnt; - u_int32_t sc_ticks_err_sum; - u_int32_t sc_statvar; - u_int32_t sc_statmin; - u_int32_t sc_nexttickevent; - u_int32_t sc_nextstatevent; + u_int64_t sc_nsec_cycle_ratio; + u_int64_t sc_nsec_max; }; const struct cfattach dmtimer_ca = { @@ -208,82 +211,11 @@ dmtimer_attach(struct device *parent, st printf(" rev %d.%d\n", (rev & DM_TIDR_MAJOR) >> 8, rev & DM_TIDR_MINOR); } -/* - * See comment in arm/xscale/i80321_clock.c - * - * Counter is count up, but with autoreload timers it is not possible - * to detect how many interrupts passed while interrupts were blocked. - * Also it is not possible to atomically add to the register. - * - * To work around this two timers are used, one is used as a reference - * clock without reload, however we just disable the interrupt it - * could generate. - * - * Internally this keeps track of when the next timer should fire - * and based on that time and the current value of the reference - * clock a number is written into the timer count register to schedule - * the next event. - */ - int dmtimer_intr(void *frame) { - struct dmtimer_softc *sc = dmtimer_cd.cd_devs[1]; - u_int32_t now, r, nextevent; - int32_t duration; - - now = bus_space_read_4(sc->sc_iot, sc->sc_ioh[1], DM_TCRR); - - while ((int32_t) (sc->sc_nexttickevent - now) <= 0) { - sc->sc_nexttickevent += sc->sc_ticks_per_intr; - sc->sc_ticks_err_sum += sc->sc_ticks_err_cnt; - - while (sc->sc_ticks_err_sum > hz) { - sc->sc_nexttickevent += 1; - sc->sc_ticks_err_sum -= hz; - } - - clk_count.ec_count++; - hardclock(frame); - } - - while ((int32_t) (sc->sc_nextstatevent - now) <= 0) { - do { - r = random() & (sc->sc_statvar - 1); - } while (r == 0); /* random == 0 not allowed */ - sc->sc_nextstatevent += sc->sc_statmin + r; - stat_count.ec_count++; - statclock(frame); - } - if ((sc->sc_nexttickevent - now) < (sc->sc_nextstatevent - now)) - nextevent = sc->sc_nexttickevent; - else - nextevent = sc->sc_nextstatevent; - - duration = nextevent - - bus_space_read_4(sc->sc_iot, sc->sc_ioh[1], DM_TCRR); - - if (duration <= 0) - duration = 1; /* trigger immediately. */ - - if (duration > sc->sc_ticks_per_intr + 1) { - printf("%s: time lost!\n", __func__); - /* - * If interrupts are blocked too long, like during - * the root prompt or ddb, the timer can roll over, - * this will allow the system to continue to run - * even if time is lost. - */ - duration = sc->sc_ticks_per_intr; - sc->sc_nexttickevent = now; - sc->sc_nextstatevent = now; - } - - bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TISR, - bus_space_read_4(sc->sc_iot, sc->sc_ioh[0], DM_TISR)); - bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TCRR, -duration); - dmtimer_wait(DM_TWPS_ALL); - + dmtimer_reset_tisr(); /* clear pending interrupts */ + clockintr_dispatch(frame); return 1; } @@ -298,37 +230,28 @@ dmtimer_cpu_initclocks(void) { struct dmtimer_softc *sc = dmtimer_cd.cd_devs[1]; - stathz = 128; - profhz = 1024; + stathz = 100; + profhz = 1000; + clockintr_init(CL_RNDSTAT); sc->sc_ticks_per_second = TIMER_FREQUENCY; /* 32768 */ - - setstatclockrate(stathz); - - sc->sc_ticks_per_intr = sc->sc_ticks_per_second / hz; - sc->sc_ticks_err_cnt = sc->sc_ticks_per_second % hz; - sc->sc_ticks_err_sum = 0; + sc->sc_nsec_cycle_ratio = + sc->sc_ticks_per_second * (1ULL << 32) / 1000000000; + sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio; + dmtimer_intrclock.ic_cookie = sc; /* establish interrupts */ arm_intr_establish(sc->sc_irq, IPL_CLOCK, dmtimer_intr, NULL, "tick"); /* setup timer 0 */ - bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TLDR, 0); - - sc->sc_nexttickevent = sc->sc_nextstatevent = bus_space_read_4(sc->sc_iot, - sc->sc_ioh[1], DM_TCRR) + sc->sc_ticks_per_intr; - bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TIER, DM_TIER_OVF_EN); bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TWER, DM_TWER_OVF_EN); - bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TISR, /*clear interrupt flags */ - bus_space_read_4(sc->sc_iot, sc->sc_ioh[0], DM_TISR)); - bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TCRR, -sc->sc_ticks_per_intr); - dmtimer_wait(DM_TWPS_ALL); - bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TCLR, /* autoreload and start */ - DM_TCLR_AR | DM_TCLR_ST); - dmtimer_wait(DM_TWPS_ALL); + + /* start the clock interrupt cycle */ + clockintr_cpu_init(&dmtimer_intrclock); + clockintr_trigger(); } void @@ -339,6 +262,19 @@ dmtimer_wait(int reg) ; } +/* + * Clear all interrupt status bits. + */ +void +dmtimer_reset_tisr(void) +{ + struct dmtimer_softc *sc = dmtimer_cd.cd_devs[1]; + u_int32_t tisr; + + tisr = bus_space_read_4(sc->sc_iot, sc->sc_ioh[0], DM_TISR); + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TISR, tisr); +} + void dmtimer_delay(u_int usecs) { @@ -382,27 +318,7 @@ dmtimer_delay(u_int usecs) void dmtimer_setstatclockrate(int newhz) { - struct dmtimer_softc *sc = dmtimer_cd.cd_devs[1]; - int minint, statint; - int s; - - s = splclock(); - - statint = sc->sc_ticks_per_second / newhz; - /* calculate largest 2^n which is smaller than just over half statint */ - sc->sc_statvar = 0x40000000; /* really big power of two */ - minint = statint / 2 + 100; - while (sc->sc_statvar > minint) - sc->sc_statvar >>= 1; - - sc->sc_statmin = statint - (sc->sc_statvar >> 1); - - splx(s); - - /* - * XXX this allows the next stat timer to occur then it switches - * to the new frequency. Rather than switching instantly. - */ + clockintr_setstatclockrate(newhz); } @@ -412,4 +328,38 @@ dmtimer_get_timecount(struct timecounter struct dmtimer_softc *sc = dmtimer_timecounter.tc_priv; return bus_space_read_4(sc->sc_iot, sc->sc_ioh[1], DM_TCRR); +} + +void +dmtimer_rearm(void *cookie, uint64_t nsecs) +{ + struct dmtimer_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; + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TCRR, + UINT32_MAX - cycles); + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TCLR, DM_TCLR_ST); + dmtimer_wait(DM_TWPS_ALL); +} + +void +dmtimer_trigger(void *cookie) +{ + struct dmtimer_softc *sc = cookie; + + /* stop timer */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TCLR, 0); + + dmtimer_reset_tisr(); /* clear pending interrupts */ + + /* set shortest possible timeout */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TCRR, UINT32_MAX); + dmtimer_wait(DM_TWPS_ALL); + + /* start timer */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh[0], DM_TCLR, DM_TCLR_ST); + dmtimer_wait(DM_TWPS_ALL); }