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 52struct 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 63int sxitimer_match(struct device *, void *, void *); 64void sxitimer_attach(struct device *, struct device *, void *); 65 66const struct cfattach sxitimer_ca = { 67 sizeof (struct sxitimer_softc), sxitimer_match, sxitimer_attach 68}; 69 70struct cfdriver sxitimer_cd = { 71 NULL, "sxitimer", DV_DULL 72}; 73 74void sxitimer_startclock(void); 75int sxitimer_intr(void *); 76void sxitimer_rearm(void *, uint64_t); 77void sxitimer_trigger(void *); 78 79struct intrclock sxitimer_intrclock = { 80 .ic_rearm = sxitimer_rearm, 81 .ic_trigger = sxitimer_trigger 82}; 83 84int 85sxitimer_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 92void 93sxitimer_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 136void 137sxitimer_startclock(void) 138{ 139 clockintr_cpu_init(&sxitimer_intrclock); 140 clockintr_trigger(); 141} 142 143int 144sxitimer_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 152void 153sxitimer_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 170void 171sxitimer_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