1 /* $OpenBSD: sxitimer.c,v 1.2 2024/10/17 01:57:18 jsg 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 23 #include <machine/intr.h> 24 #include <machine/bus.h> 25 #include <machine/fdt.h> 26 27 #include <dev/ofw/openfirm.h> 28 #include <dev/ofw/ofw_clock.h> 29 #include <dev/ofw/fdt.h> 30 31 /* Registers */ 32 #define TMR_IRQ_EN 0x0000 33 #define TMR1_IRQ_EN (1 << 1) 34 #define TMR0_IRQ_EN (1 << 0) 35 #define TMR_IRQ_STA 0x0004 36 #define TMR1_IRQ_PEND (1 << 1) 37 #define TMR0_IRQ_PEND (1 << 0) 38 #define TMR0_CTRL 0x0010 39 #define TMR0_MODE_SINGLE (1 << 7) 40 #define TMR0_CLK_PRES_1 (0 << 4) 41 #define TMR0_CLK_SRC_OSC24M (1 << 2) 42 #define TMR0_RELOAD (1 << 1) 43 #define TMR0_EN (1 << 0) 44 #define TMR0_INTV_VALUE 0x0014 45 46 #define HREAD4(sc, reg) \ 47 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 48 #define HWRITE4(sc, reg, val) \ 49 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 50 51 struct sxitimer_softc { 52 struct device sc_dev; 53 bus_space_tag_t sc_iot; 54 bus_space_handle_t sc_ioh; 55 56 uint32_t sc_ticks_per_second; 57 uint64_t sc_nsec_cycle_ratio; 58 uint64_t sc_nsec_max; 59 void *sc_ih; 60 }; 61 62 int sxitimer_match(struct device *, void *, void *); 63 void sxitimer_attach(struct device *, struct device *, void *); 64 65 const struct cfattach sxitimer_ca = { 66 sizeof (struct sxitimer_softc), sxitimer_match, sxitimer_attach 67 }; 68 69 struct cfdriver sxitimer_cd = { 70 NULL, "sxitimer", DV_DULL 71 }; 72 73 void sxitimer_startclock(void); 74 int sxitimer_intr(void *); 75 void sxitimer_rearm(void *, uint64_t); 76 void sxitimer_trigger(void *); 77 78 struct intrclock sxitimer_intrclock = { 79 .ic_rearm = sxitimer_rearm, 80 .ic_trigger = sxitimer_trigger 81 }; 82 83 int 84 sxitimer_match(struct device *parent, void *match, void *aux) 85 { 86 struct fdt_attach_args *faa = aux; 87 88 return OF_is_compatible(faa->fa_node, "allwinner,sun20i-d1-timer"); 89 } 90 91 void 92 sxitimer_attach(struct device *parent, struct device *self, void *aux) 93 { 94 struct sxitimer_softc *sc = (struct sxitimer_softc *)self; 95 struct fdt_attach_args *faa = aux; 96 97 if (faa->fa_nreg < 1) { 98 printf(": no registers\n"); 99 return; 100 } 101 102 sc->sc_iot = faa->fa_iot; 103 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 104 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 105 printf(": can't map registers\n"); 106 return; 107 } 108 109 HWRITE4(sc, TMR_IRQ_EN, 0); 110 111 sc->sc_ticks_per_second = clock_get_frequency(faa->fa_node, NULL); 112 sc->sc_nsec_cycle_ratio = 113 sc->sc_ticks_per_second * (1ULL << 32) / 1000000000; 114 sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio; 115 116 sxitimer_intrclock.ic_cookie = sc; 117 cpu_startclock_fcn = sxitimer_startclock; 118 119 sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, 0, IPL_CLOCK, 120 sxitimer_intr, NULL, sc->sc_dev.dv_xname); 121 if (sc->sc_ih == NULL) { 122 printf("can't establish interrupt\n"); 123 return; 124 } 125 126 HWRITE4(sc, TMR0_INTV_VALUE, 0); 127 HWRITE4(sc, TMR0_CTRL, TMR0_MODE_SINGLE | TMR0_CLK_PRES_1 | 128 TMR0_CLK_SRC_OSC24M | TMR0_RELOAD | TMR0_EN); 129 HWRITE4(sc, TMR_IRQ_STA, TMR0_IRQ_PEND); 130 HWRITE4(sc, TMR_IRQ_EN, TMR0_IRQ_EN); 131 132 printf(": %u kHz\n", sc->sc_ticks_per_second / 1000); 133 } 134 135 void 136 sxitimer_startclock(void) 137 { 138 clockintr_cpu_init(&sxitimer_intrclock); 139 clockintr_trigger(); 140 } 141 142 int 143 sxitimer_intr(void *frame) 144 { 145 struct sxitimer_softc *sc = sxitimer_intrclock.ic_cookie; 146 147 HWRITE4(sc, TMR_IRQ_STA, TMR0_IRQ_PEND); 148 return clockintr_dispatch(frame); 149 } 150 151 void 152 sxitimer_rearm(void *cookie, uint64_t nsecs) 153 { 154 struct sxitimer_softc *sc = cookie; 155 uint32_t cycles; 156 157 if (nsecs > sc->sc_nsec_max) 158 nsecs = sc->sc_nsec_max; 159 cycles = (nsecs * sc->sc_nsec_cycle_ratio) >> 32; 160 if (cycles > UINT32_MAX) 161 cycles = UINT32_MAX; 162 if (cycles < 1) 163 cycles = 1; 164 HWRITE4(sc, TMR0_INTV_VALUE, cycles); 165 HWRITE4(sc, TMR0_CTRL, TMR0_MODE_SINGLE | TMR0_CLK_PRES_1 | 166 TMR0_CLK_SRC_OSC24M | TMR0_RELOAD | TMR0_EN); 167 } 168 169 void 170 sxitimer_trigger(void *cookie) 171 { 172 struct sxitimer_softc *sc = cookie; 173 174 HWRITE4(sc, TMR0_INTV_VALUE, 1); 175 HWRITE4(sc, TMR0_CTRL, TMR0_MODE_SINGLE | TMR0_CLK_PRES_1 | 176 TMR0_CLK_SRC_OSC24M | TMR0_RELOAD | TMR0_EN); 177 } 178