1 /* $OpenBSD: sxitimer.c,v 1.1 2024/01/27 12:05:40 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2024 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/clockintr.h> 21 #include <sys/device.h> 22 #include <sys/sensors.h> 23 24 #include <machine/intr.h> 25 #include <machine/bus.h> 26 #include <machine/fdt.h> 27 28 #include <dev/ofw/openfirm.h> 29 #include <dev/ofw/ofw_clock.h> 30 #include <dev/ofw/fdt.h> 31 32 /* Registers */ 33 #define TMR_IRQ_EN 0x0000 34 #define TMR1_IRQ_EN (1 << 1) 35 #define TMR0_IRQ_EN (1 << 0) 36 #define TMR_IRQ_STA 0x0004 37 #define TMR1_IRQ_PEND (1 << 1) 38 #define TMR0_IRQ_PEND (1 << 0) 39 #define TMR0_CTRL 0x0010 40 #define TMR0_MODE_SINGLE (1 << 7) 41 #define TMR0_CLK_PRES_1 (0 << 4) 42 #define TMR0_CLK_SRC_OSC24M (1 << 2) 43 #define TMR0_RELOAD (1 << 1) 44 #define TMR0_EN (1 << 0) 45 #define TMR0_INTV_VALUE 0x0014 46 47 #define HREAD4(sc, reg) \ 48 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 49 #define HWRITE4(sc, reg, val) \ 50 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 51 52 struct sxitimer_softc { 53 struct device sc_dev; 54 bus_space_tag_t sc_iot; 55 bus_space_handle_t sc_ioh; 56 57 uint32_t sc_ticks_per_second; 58 uint64_t sc_nsec_cycle_ratio; 59 uint64_t sc_nsec_max; 60 void *sc_ih; 61 }; 62 63 int sxitimer_match(struct device *, void *, void *); 64 void sxitimer_attach(struct device *, struct device *, void *); 65 66 const struct cfattach sxitimer_ca = { 67 sizeof (struct sxitimer_softc), sxitimer_match, sxitimer_attach 68 }; 69 70 struct cfdriver sxitimer_cd = { 71 NULL, "sxitimer", DV_DULL 72 }; 73 74 void sxitimer_startclock(void); 75 int sxitimer_intr(void *); 76 void sxitimer_rearm(void *, uint64_t); 77 void sxitimer_trigger(void *); 78 79 struct intrclock sxitimer_intrclock = { 80 .ic_rearm = sxitimer_rearm, 81 .ic_trigger = sxitimer_trigger 82 }; 83 84 int 85 sxitimer_match(struct device *parent, void *match, void *aux) 86 { 87 struct fdt_attach_args *faa = aux; 88 89 return OF_is_compatible(faa->fa_node, "allwinner,sun20i-d1-timer"); 90 } 91 92 void 93 sxitimer_attach(struct device *parent, struct device *self, void *aux) 94 { 95 struct sxitimer_softc *sc = (struct sxitimer_softc *)self; 96 struct fdt_attach_args *faa = aux; 97 98 if (faa->fa_nreg < 1) { 99 printf(": no registers\n"); 100 return; 101 } 102 103 sc->sc_iot = faa->fa_iot; 104 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 105 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 106 printf(": can't map registers\n"); 107 return; 108 } 109 110 HWRITE4(sc, TMR_IRQ_EN, 0); 111 112 sc->sc_ticks_per_second = clock_get_frequency(faa->fa_node, NULL); 113 sc->sc_nsec_cycle_ratio = 114 sc->sc_ticks_per_second * (1ULL << 32) / 1000000000; 115 sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio; 116 117 sxitimer_intrclock.ic_cookie = sc; 118 cpu_startclock_fcn = sxitimer_startclock; 119 120 sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, 0, IPL_CLOCK, 121 sxitimer_intr, NULL, sc->sc_dev.dv_xname); 122 if (sc->sc_ih == NULL) { 123 printf("can't establish interrupt\n"); 124 return; 125 } 126 127 HWRITE4(sc, TMR0_INTV_VALUE, 0); 128 HWRITE4(sc, TMR0_CTRL, TMR0_MODE_SINGLE | TMR0_CLK_PRES_1 | 129 TMR0_CLK_SRC_OSC24M | TMR0_RELOAD | TMR0_EN); 130 HWRITE4(sc, TMR_IRQ_STA, TMR0_IRQ_PEND); 131 HWRITE4(sc, TMR_IRQ_EN, TMR0_IRQ_EN); 132 133 printf(": %u kHz\n", sc->sc_ticks_per_second / 1000); 134 } 135 136 void 137 sxitimer_startclock(void) 138 { 139 clockintr_cpu_init(&sxitimer_intrclock); 140 clockintr_trigger(); 141 } 142 143 int 144 sxitimer_intr(void *frame) 145 { 146 struct sxitimer_softc *sc = sxitimer_intrclock.ic_cookie; 147 148 HWRITE4(sc, TMR_IRQ_STA, TMR0_IRQ_PEND); 149 return clockintr_dispatch(frame); 150 } 151 152 void 153 sxitimer_rearm(void *cookie, uint64_t nsecs) 154 { 155 struct sxitimer_softc *sc = cookie; 156 uint32_t cycles; 157 158 if (nsecs > sc->sc_nsec_max) 159 nsecs = sc->sc_nsec_max; 160 cycles = (nsecs * sc->sc_nsec_cycle_ratio) >> 32; 161 if (cycles > UINT32_MAX) 162 cycles = UINT32_MAX; 163 if (cycles < 1) 164 cycles = 1; 165 HWRITE4(sc, TMR0_INTV_VALUE, cycles); 166 HWRITE4(sc, TMR0_CTRL, TMR0_MODE_SINGLE | TMR0_CLK_PRES_1 | 167 TMR0_CLK_SRC_OSC24M | TMR0_RELOAD | TMR0_EN); 168 } 169 170 void 171 sxitimer_trigger(void *cookie) 172 { 173 struct sxitimer_softc *sc = cookie; 174 175 HWRITE4(sc, TMR0_INTV_VALUE, 1); 176 HWRITE4(sc, TMR0_CTRL, TMR0_MODE_SINGLE | TMR0_CLK_PRES_1 | 177 TMR0_CLK_SRC_OSC24M | TMR0_RELOAD | TMR0_EN); 178 } 179