sunxi_timer.c revision 1.3
1/* $NetBSD: sunxi_timer.c,v 1.3 2017/12/16 20:04:38 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.3 2017/12/16 20:04:38 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/* 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 74static const char * const compatible[] = { 75 "allwinner,sun4i-a10-timer", 76 NULL 77}; 78 79struct sunxi_timer_softc { 80 device_t sc_dev; 81 bus_space_tag_t sc_bst; 82 bus_space_handle_t sc_bsh; 83 int sc_phandle; 84 struct clk *sc_clk; 85 86 struct timecounter sc_tc; 87}; 88 89#define TIMER_READ(sc, reg) \ 90 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 91#define TIMER_WRITE(sc, reg, val) \ 92 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 93 94static struct sunxi_timer_softc *timer_softc; 95 96static int 97sunxi_timer_intr(void *arg) 98{ 99 struct sunxi_timer_softc * const sc = timer_softc; 100 struct clockframe *frame = arg; 101 uint32_t stas; 102 103 stas = TIMER_READ(sc, TMR_IRQ_STAS_REG); 104 if (stas == 0) 105 return 0; 106 TIMER_WRITE(sc, TMR_IRQ_STAS_REG, stas); 107 108 if ((stas & TMR_IRQ_STAS_PEND(0)) != 0) 109 hardclock(frame); 110 111 return 1; 112} 113 114static void 115sunxi_timer_cpu_initclocks(void) 116{ 117 struct sunxi_timer_softc * const sc = timer_softc; 118 char intrstr[128]; 119 void *ih; 120 121 KASSERT(sc != NULL); 122 123 if (!fdtbus_intr_str(sc->sc_phandle, 0, intrstr, sizeof(intrstr))) 124 panic("%s: failed to decode interrupt", __func__); 125 126 ih = fdtbus_intr_establish(sc->sc_phandle, 0, IPL_CLOCK, 127 FDT_INTR_MPSAFE, sunxi_timer_intr, NULL); 128 if (ih == NULL) 129 panic("%s: failed to establish timer interrupt", __func__); 130 131 aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr); 132 133 /* Enable Timer 0 IRQ */ 134 const uint32_t irq_en = TIMER_READ(sc, TMR_IRQ_EN_REG); 135 TIMER_WRITE(sc, TMR_IRQ_EN_REG, irq_en | TMR_IRQ_EN(0)); 136} 137 138static u_int 139sunxi_timer_get_timecount(struct timecounter *tc) 140{ 141 struct sunxi_timer_softc * const sc = tc->tc_priv; 142 143 /* Timer current value is a 32-bit down counter. */ 144 return ~TIMER_READ(sc, TMR2_CURNT_VALUE_REG); 145} 146 147static int 148sunxi_timer_match(device_t parent, cfdata_t cf, void *aux) 149{ 150 struct fdt_attach_args * const faa = aux; 151 152 return of_match_compatible(faa->faa_phandle, compatible); 153} 154 155static void 156sunxi_timer_attach(device_t parent, device_t self, void *aux) 157{ 158 struct sunxi_timer_softc * const sc = device_private(self); 159 struct fdt_attach_args * const faa = aux; 160 struct timecounter *tc = &sc->sc_tc; 161 const int phandle = faa->faa_phandle; 162 bus_addr_t addr; 163 bus_size_t size; 164 165 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 166 aprint_error(": couldn't get registers\n"); 167 return; 168 } 169 170 if ((sc->sc_clk = fdtbus_clock_get_index(phandle, 0)) == NULL) { 171 aprint_error(": couldn't get clock\n"); 172 return; 173 } 174 175 sc->sc_dev = self; 176 sc->sc_phandle = phandle; 177 sc->sc_bst = faa->faa_bst; 178 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 179 aprint_error(": couldn't map registers\n"); 180 return; 181 } 182 183 aprint_naive("\n"); 184 aprint_normal(": Timer\n"); 185 186 const u_int rate = clk_get_rate(sc->sc_clk); 187 188 /* Disable IRQs and all timers */ 189 TIMER_WRITE(sc, TMR_IRQ_EN_REG, 0); 190 TIMER_WRITE(sc, TMR_IRQ_STAS_REG, TIMER_READ(sc, TMR_IRQ_STAS_REG)); 191 /* Enable Timer 0 (hardclock) */ 192 TIMER_WRITE(sc, TMR0_INTV_VALUE_REG, rate / hz); 193 TIMER_WRITE(sc, TMR0_CTRL_REG, 194 __SHIFTIN(TMR0_CTRL_CLK_SRC_OSC24M, TMR0_CTRL_CLK_SRC) | 195 TMR0_CTRL_RELOAD | TMR0_CTRL_EN); 196 /* Enable Timer 2 (timecounter) */ 197 TIMER_WRITE(sc, TMR2_INTV_VALUE_REG, ~0u); 198 TIMER_WRITE(sc, TMR2_CTRL_REG, 199 __SHIFTIN(TMR2_CTRL_CLK_SRC_OSC24M, TMR2_CTRL_CLK_SRC) | 200 TMR2_CTRL_RELOAD | TMR2_CTRL_EN); 201 202 /* Timecounter setup */ 203 tc->tc_get_timecount = sunxi_timer_get_timecount; 204 tc->tc_counter_mask = ~0u, 205 tc->tc_frequency = clk_get_rate(sc->sc_clk); 206 tc->tc_name = "Timer 2"; 207 tc->tc_quality = 200; 208 tc->tc_priv = sc; 209 tc_init(tc); 210 211 /* Use this as the OS timer in UP configurations */ 212 if (!arm_has_mpext_p) { 213 timer_softc = sc; 214 arm_fdt_timer_register(sunxi_timer_cpu_initclocks); 215 } 216} 217 218CFATTACH_DECL_NEW(sunxi_timer, sizeof(struct sunxi_timer_softc), 219 sunxi_timer_match, sunxi_timer_attach, NULL, NULL); 220