1 /* $NetBSD: sunxi_timer.c,v 1.2 2017/08/25 21:52:01 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: sunxi_timer.c,v 1.2 2017/08/25 21:52:01 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/bus.h> 35 #include <sys/device.h> 36 #include <sys/intr.h> 37 #include <sys/systm.h> 38 #include <sys/timetc.h> 39 40 #include <arm/locore.h> 41 42 #include <dev/fdt/fdtvar.h> 43 44 #include <arm/fdt/arm_fdtvar.h> 45 46 #define TMR_IRQ_EN_REG 0x00 47 #define TMR_IRQ_EN(n) __BIT(n) 48 #define TMR_IRQ_STAS_REG 0x04 49 #define TMR_IRQ_STAS_PEND(n) __BIT(n) 50 #define TMR0_CTRL_REG 0x10 51 #define TMR0_CTRL_MODE __BIT(7) 52 #define TMR0_CTRL_CLK_PRESCALE __BITS(6,4) 53 #define TMR0_CTRL_CLK_SRC __BITS(3,2) 54 #define TMR0_CTRL_CLK_SRC_OSC24M 1 55 #define TMR0_CTRL_CLK_SRC_PLL6_6 2 56 #define TMR0_CTRL_RELOAD __BIT(1) 57 #define TMR0_CTRL_EN __BIT(0) 58 #define TMR0_INTV_VALUE_REG 0x14 59 #define TMR0_CURNT_VALUE_REG 0x18 60 #define COUNTER64_CTRL_REG 0xa0 61 #define COUNTER64_CTRL_CLK_SRC_SEL __BIT(2) 62 #define COUNTER64_CTRL_RLATCH_EN __BIT(1) 63 #define COUNTER64_CTRL_CLR_EN __BIT(0) 64 #define COUNTER64_LOW_REG 0xa4 65 #define COUNTER64_HI_REG 0xa8 66 67 static const char * const compatible[] = { 68 "allwinner,sun4i-a10-timer", 69 NULL 70 }; 71 72 struct sunxi_timer_softc { 73 device_t sc_dev; 74 bus_space_tag_t sc_bst; 75 bus_space_handle_t sc_bsh; 76 int sc_phandle; 77 struct clk *sc_clk; 78 79 struct timecounter sc_tc; 80 }; 81 82 #define TIMER_READ(sc, reg) \ 83 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 84 #define TIMER_WRITE(sc, reg, val) \ 85 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 86 87 static struct sunxi_timer_softc *timer_softc; 88 89 static int 90 sunxi_timer_intr(void *arg) 91 { 92 struct sunxi_timer_softc * const sc = timer_softc; 93 struct clockframe *frame = arg; 94 uint32_t stas; 95 96 stas = TIMER_READ(sc, TMR_IRQ_STAS_REG); 97 if (stas == 0) 98 return 0; 99 TIMER_WRITE(sc, TMR_IRQ_STAS_REG, stas); 100 101 if ((stas & TMR_IRQ_STAS_PEND(0)) != 0) 102 hardclock(frame); 103 104 return 1; 105 } 106 107 static void 108 sunxi_timer_cpu_initclocks(void) 109 { 110 struct sunxi_timer_softc * const sc = timer_softc; 111 char intrstr[128]; 112 void *ih; 113 114 KASSERT(sc != NULL); 115 116 if (!fdtbus_intr_str(sc->sc_phandle, 0, intrstr, sizeof(intrstr))) 117 panic("%s: failed to decode interrupt", __func__); 118 119 ih = fdtbus_intr_establish(sc->sc_phandle, 0, IPL_CLOCK, 120 FDT_INTR_MPSAFE, sunxi_timer_intr, NULL); 121 if (ih == NULL) 122 panic("%s: failed to establish timer interrupt", __func__); 123 124 aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr); 125 126 /* Enable Timer 0 IRQ */ 127 const uint32_t irq_en = TIMER_READ(sc, TMR_IRQ_EN_REG); 128 TIMER_WRITE(sc, TMR_IRQ_EN_REG, irq_en | TMR_IRQ_EN(0)); 129 } 130 131 static u_int 132 sunxi_timer_get_timecount(struct timecounter *tc) 133 { 134 struct sunxi_timer_softc * const sc = tc->tc_priv; 135 uint32_t val; 136 137 /* Enable read latch and wait for it to clear */ 138 val = TIMER_READ(sc, COUNTER64_CTRL_REG); 139 val |= COUNTER64_CTRL_RLATCH_EN; 140 TIMER_WRITE(sc, COUNTER64_CTRL_REG, val); 141 do { 142 val = TIMER_READ(sc, COUNTER64_CTRL_REG); 143 } while (val & COUNTER64_CTRL_RLATCH_EN); 144 145 return TIMER_READ(sc, COUNTER64_LOW_REG); 146 } 147 148 static int 149 sunxi_timer_match(device_t parent, cfdata_t cf, void *aux) 150 { 151 struct fdt_attach_args * const faa = aux; 152 153 return of_match_compatible(faa->faa_phandle, compatible); 154 } 155 156 static void 157 sunxi_timer_attach(device_t parent, device_t self, void *aux) 158 { 159 struct sunxi_timer_softc * const sc = device_private(self); 160 struct fdt_attach_args * const faa = aux; 161 struct timecounter *tc = &sc->sc_tc; 162 const int phandle = faa->faa_phandle; 163 bus_addr_t addr; 164 bus_size_t size; 165 uint32_t val; 166 167 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 168 aprint_error(": couldn't get registers\n"); 169 return; 170 } 171 172 if ((sc->sc_clk = fdtbus_clock_get_index(phandle, 0)) == NULL) { 173 aprint_error(": couldn't get clock\n"); 174 return; 175 } 176 177 sc->sc_dev = self; 178 sc->sc_phandle = phandle; 179 sc->sc_bst = faa->faa_bst; 180 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 181 aprint_error(": couldn't map registers\n"); 182 return; 183 } 184 185 aprint_naive("\n"); 186 aprint_normal(": Timer\n"); 187 188 const u_int rate = clk_get_rate(sc->sc_clk); 189 190 /* Disable IRQs and all timers */ 191 TIMER_WRITE(sc, TMR_IRQ_EN_REG, 0); 192 TIMER_WRITE(sc, TMR_IRQ_STAS_REG, TIMER_READ(sc, TMR_IRQ_STAS_REG)); 193 /* Enable Timer 0 */ 194 TIMER_WRITE(sc, TMR0_INTV_VALUE_REG, rate / hz); 195 TIMER_WRITE(sc, TMR0_CTRL_REG, 196 __SHIFTIN(TMR0_CTRL_CLK_SRC_OSC24M, TMR0_CTRL_CLK_SRC) | 197 TMR0_CTRL_RELOAD | TMR0_CTRL_EN); 198 199 /* Set 64-bit counter source to OSC24M */ 200 val = TIMER_READ(sc, COUNTER64_CTRL_REG); 201 val &= ~COUNTER64_CTRL_CLK_SRC_SEL; 202 TIMER_WRITE(sc, COUNTER64_CTRL_REG, val); 203 204 /* Timecounter setup */ 205 tc->tc_get_timecount = sunxi_timer_get_timecount; 206 tc->tc_counter_mask = ~0u, 207 tc->tc_frequency = clk_get_rate(sc->sc_clk); 208 tc->tc_name = "CNT64"; 209 tc->tc_quality = arm_has_mpext_p ? -1 : 200; 210 tc->tc_priv = sc; 211 tc_init(tc); 212 213 /* Use this as the OS timer in UP configurations */ 214 if (!arm_has_mpext_p) { 215 timer_softc = sc; 216 arm_fdt_timer_register(sunxi_timer_cpu_initclocks); 217 } 218 } 219 220 CFATTACH_DECL_NEW(sunxi_timer, sizeof(struct sunxi_timer_softc), 221 sunxi_timer_match, sunxi_timer_attach, NULL, NULL); 222