sunxi_timer.c revision 1.6
1/* $NetBSD: sunxi_timer.c,v 1.6 2019/03/27 16:38:49 tnn 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.6 2019/03/27 16:38:49 tnn 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/* Timer 0 registers */ 47#define TMR_IRQ_EN_REG 0x00 48#define TMR_IRQ_EN(n) __BIT(n) 49#define TMR_IRQ_STAS_REG 0x04 50#define TMR_IRQ_STAS_PEND(n) __BIT(n) 51#define TMR0_CTRL_REG 0x10 52#define TMR0_CTRL_MODE __BIT(7) 53#define TMR0_CTRL_CLK_PRESCALE __BITS(6,4) 54#define TMR0_CTRL_CLK_SRC __BITS(3,2) 55#define TMR0_CTRL_CLK_SRC_OSC24M 1 56#define TMR0_CTRL_CLK_SRC_PLL6_6 2 57#define TMR0_CTRL_RELOAD __BIT(1) 58#define TMR0_CTRL_EN __BIT(0) 59#define TMR0_INTV_VALUE_REG 0x14 60#define TMR0_CURNT_VALUE_REG 0x18 61 62/* Timer 1 is used for delay() */ 63 64/* Timer 2 registers */ 65#define TMR2_CTRL_REG 0x30 66#define TMR2_CTRL_MODE __BIT(7) 67#define TMR2_CTRL_CLK_SRC __BITS(3,2) 68#define TMR2_CTRL_CLK_SRC_OSC24M 1 69#define TMR2_CTRL_RELOAD __BIT(1) 70#define TMR2_CTRL_EN __BIT(0) 71#define TMR2_INTV_VALUE_REG 0x34 72#define TMR2_CURNT_VALUE_REG 0x38 73 74/* Timer 4 registers */ 75#define TMR4_CTRL_REG 0x50 76#define TMR4_CTRL_RELOAD __BIT(1) 77#define TMR4_CTRL_EN __BIT(0) 78#define TMR4_INTV_VALUE_REG 0x54 79#define TMR4_CURNT_VALUE_REG 0x58 80 81/* Control registers */ 82#define AVS_CNT_CTL_REG 0x80 83#define AVS_CNT0_REG 0x84 84#define AVS_CNT1_REG 0x88 85#define AVS_CNT_DIV_REG 0x8c 86#define WDOG_CTRL_REG 0x90 87#define WDOG_MODE_REG 0x94 88#define LOSC_CTRL_REG 0x100 89#define LOSC_CTRL_KEY_FIELD __BITS(31,16) 90#define LOSC_CTRL_KEY_FIELD_V 0x16aa 91#define LOSC_CTRL_OSC32K_AUTO_SWT_EN __BIT(14) 92#define LOSC_CTRL_OSC32K_SEL __BIT(0) 93 94static const char * const compatible[] = { 95 "allwinner,sun4i-a10-timer", 96 NULL 97}; 98 99struct sunxi_timer_softc { 100 device_t sc_dev; 101 bus_space_tag_t sc_bst; 102 bus_space_handle_t sc_bsh; 103 int sc_phandle; 104 struct clk *sc_clk; 105 106 struct timecounter sc_tc; 107 struct timecounter sc_tc_losc; 108}; 109 110#define TIMER_READ(sc, reg) \ 111 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 112#define TIMER_WRITE(sc, reg, val) \ 113 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 114 115static struct sunxi_timer_softc *timer_softc; 116 117static int 118sunxi_timer_intr(void *arg) 119{ 120 struct sunxi_timer_softc * const sc = timer_softc; 121 struct clockframe *frame = arg; 122 uint32_t stas; 123 124 stas = TIMER_READ(sc, TMR_IRQ_STAS_REG); 125 if (stas == 0) 126 return 0; 127 TIMER_WRITE(sc, TMR_IRQ_STAS_REG, stas); 128 129 if ((stas & TMR_IRQ_STAS_PEND(0)) != 0) 130 hardclock(frame); 131 132 return 1; 133} 134 135static void 136sunxi_timer_cpu_initclocks(void) 137{ 138 struct sunxi_timer_softc * const sc = timer_softc; 139 char intrstr[128]; 140 void *ih; 141 142 KASSERT(sc != NULL); 143 144 if (!fdtbus_intr_str(sc->sc_phandle, 0, intrstr, sizeof(intrstr))) 145 panic("%s: failed to decode interrupt", __func__); 146 147 ih = fdtbus_intr_establish(sc->sc_phandle, 0, IPL_CLOCK, 148 FDT_INTR_MPSAFE, sunxi_timer_intr, NULL); 149 if (ih == NULL) 150 panic("%s: failed to establish timer interrupt", __func__); 151 152 aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr); 153 154 /* Enable Timer 0 IRQ */ 155 const uint32_t irq_en = TIMER_READ(sc, TMR_IRQ_EN_REG); 156 TIMER_WRITE(sc, TMR_IRQ_EN_REG, irq_en | TMR_IRQ_EN(0)); 157} 158 159static u_int 160sunxi_timer_get_timecount(struct timecounter *tc) 161{ 162 struct sunxi_timer_softc * const sc = tc->tc_priv; 163 164 /* Timer current value is a 32-bit down counter. */ 165 return ~TIMER_READ(sc, TMR2_CURNT_VALUE_REG); 166} 167 168static u_int 169sunxi_timer_get_timecount_losc(struct timecounter *tc) 170{ 171 struct sunxi_timer_softc * const sc = tc->tc_priv; 172 173 return ~TIMER_READ(sc, TMR4_CURNT_VALUE_REG); 174} 175 176static int 177sunxi_timer_match(device_t parent, cfdata_t cf, void *aux) 178{ 179 struct fdt_attach_args * const faa = aux; 180 181 return of_match_compatible(faa->faa_phandle, compatible); 182} 183 184static void 185sunxi_timer_attach(device_t parent, device_t self, void *aux) 186{ 187 struct sunxi_timer_softc * const sc = device_private(self); 188 struct fdt_attach_args * const faa = aux; 189 struct timecounter *tc = &sc->sc_tc; 190 struct timecounter *tc_losc = &sc->sc_tc_losc; 191 const int phandle = faa->faa_phandle; 192 bus_addr_t addr; 193 bus_size_t size; 194 u_int ticks; 195 u_int reg; 196 197 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 198 aprint_error(": couldn't get registers\n"); 199 return; 200 } 201 202 if ((sc->sc_clk = fdtbus_clock_get_index(phandle, 0)) == NULL) { 203 aprint_error(": couldn't get clock\n"); 204 return; 205 } 206 207 sc->sc_dev = self; 208 sc->sc_phandle = phandle; 209 sc->sc_bst = faa->faa_bst; 210 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 211 aprint_error(": couldn't map registers\n"); 212 return; 213 } 214 215 aprint_naive("\n"); 216 aprint_normal(": Timer\n"); 217 218 const u_int rate = clk_get_rate(sc->sc_clk); 219 220 /* Disable IRQs and all timers */ 221 TIMER_WRITE(sc, TMR_IRQ_EN_REG, 0); 222 TIMER_WRITE(sc, TMR_IRQ_STAS_REG, TIMER_READ(sc, TMR_IRQ_STAS_REG)); 223 /* Enable Timer 0 (hardclock) */ 224 TIMER_WRITE(sc, TMR0_INTV_VALUE_REG, rate / hz); 225 TIMER_WRITE(sc, TMR0_CTRL_REG, 226 __SHIFTIN(TMR0_CTRL_CLK_SRC_OSC24M, TMR0_CTRL_CLK_SRC) | 227 TMR0_CTRL_RELOAD | TMR0_CTRL_EN); 228 /* Enable Timer 2 (timecounter) */ 229 TIMER_WRITE(sc, TMR2_INTV_VALUE_REG, ~0u); 230 TIMER_WRITE(sc, TMR2_CTRL_REG, 231 __SHIFTIN(TMR2_CTRL_CLK_SRC_OSC24M, TMR2_CTRL_CLK_SRC) | 232 TMR2_CTRL_RELOAD | TMR2_CTRL_EN); 233 /* Enable Timer 4 (timecounter for LOSC) */ 234 TIMER_WRITE(sc, TMR4_INTV_VALUE_REG, ~0u); 235 TIMER_WRITE(sc, TMR4_CTRL_REG, TMR4_CTRL_RELOAD | TMR4_CTRL_EN); 236 237 /* Timecounter setup */ 238 tc->tc_get_timecount = sunxi_timer_get_timecount; 239 tc->tc_counter_mask = ~0u, 240 tc->tc_frequency = rate; 241 tc->tc_name = "Timer 2"; 242 tc->tc_quality = 200; 243 tc->tc_priv = sc; 244 tc_init(tc); 245 tc_losc->tc_get_timecount = sunxi_timer_get_timecount_losc; 246 tc_losc->tc_counter_mask = ~0u; 247 tc_losc->tc_frequency = 32768; 248 tc_losc->tc_name = "LOSC"; 249 tc_losc->tc_quality = 150; 250 tc_losc->tc_priv = sc; 251 /* 252 * LOSC is optional to implement in hardware. 253 * Make sure it ticks before registering it. 254 */ 255 reg = __SHIFTIN(LOSC_CTRL_KEY_FIELD_V, LOSC_CTRL_KEY_FIELD) | 256 LOSC_CTRL_OSC32K_AUTO_SWT_EN | 257 LOSC_CTRL_OSC32K_SEL; 258 TIMER_WRITE(sc, LOSC_CTRL_REG, reg); 259 ticks = sunxi_timer_get_timecount_losc(tc_losc); 260 delay(100); 261 if (ticks != sunxi_timer_get_timecount_losc(tc_losc)) 262 tc_init(tc_losc); 263 else 264 TIMER_WRITE(sc, LOSC_CTRL_REG, reg & ~LOSC_CTRL_OSC32K_SEL); 265 266 /* Use this as the OS timer in UP configurations */ 267 if (!arm_has_mpext_p) { 268 timer_softc = sc; 269 arm_fdt_timer_register(sunxi_timer_cpu_initclocks); 270 } 271} 272 273CFATTACH_DECL_NEW(sunxi_timer, sizeof(struct sunxi_timer_softc), 274 sunxi_timer_match, sunxi_timer_attach, NULL, NULL); 275