timer.c revision 245453
1/*- 2 * Copyright (c) 2012 Ganbold Tsagaankhuu <ganbold@gmail.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/arm/allwinner/timer.c 245453 2013-01-15 09:31:13Z ganbold $"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/bus.h> 33#include <sys/kernel.h> 34#include <sys/module.h> 35#include <sys/malloc.h> 36#include <sys/rman.h> 37#include <sys/timeet.h> 38#include <sys/timetc.h> 39#include <sys/watchdog.h> 40#include <machine/bus.h> 41#include <machine/cpu.h> 42#include <machine/frame.h> 43#include <machine/intr.h> 44 45#include <dev/fdt/fdt_common.h> 46#include <dev/ofw/openfirm.h> 47#include <dev/ofw/ofw_bus.h> 48#include <dev/ofw/ofw_bus_subr.h> 49 50#include <machine/bus.h> 51#include <machine/fdt.h> 52 53#include <sys/kdb.h> 54 55/** 56 * Timer registers addr 57 * 58 */ 59#define SW_TIMER_IRQ_EN_REG 0x00 60#define SW_TIMER_IRQ_STA_REG 0x04 61#define SW_TIMER0_CTRL_REG 0x10 62#define SW_TIMER0_INT_VALUE_REG 0x14 63#define SW_TIMER0_CUR_VALUE_REG 0x18 64 65#define SYS_TIMER_SCAL 16 /* timer clock source pre-divsion */ 66#define SYS_TIMER_CLKSRC 24000000 /* timer clock source */ 67#define TMR_INTER_VAL SYS_TIMER_CLKSRC/(SYS_TIMER_SCAL * 1000) 68 69#define CLOCK_TICK_RATE TMR_INTER_VAL 70#define INITIAL_TIMECOUNTER (0xffffffff) 71 72struct a10_timer_softc { 73 device_t sc_dev; 74 struct resource *res[2]; 75 bus_space_tag_t sc_bst; 76 bus_space_handle_t sc_bsh; 77 void *sc_ih; /* interrupt handler */ 78 uint32_t sc_period; 79 uint32_t clkfreq; 80 struct eventtimer et; 81}; 82 83int a10_timer_get_timerfreq(struct a10_timer_softc *); 84 85#define timer_read_4(sc, reg) \ 86 bus_space_read_4(sc->sc_bst, sc->sc_bsh, reg) 87#define timer_write_4(sc, reg, val) \ 88 bus_space_write_4(sc->sc_bst, sc->sc_bsh, reg, val) 89 90static u_int a10_timer_get_timecount(struct timecounter *); 91static int a10_timer_timer_start(struct eventtimer *, 92 struct bintime *, struct bintime *); 93static int a10_timer_timer_stop(struct eventtimer *); 94 95static int a10_timer_initialized = 0; 96static int a10_timer_intr(void *); 97static int a10_timer_probe(device_t); 98static int a10_timer_attach(device_t); 99 100static struct timecounter a10_timer_timecounter = { 101 .tc_name = "a10_timer timer0", 102 .tc_get_timecount = a10_timer_get_timecount, 103 .tc_counter_mask = ~0u, 104 .tc_frequency = 0, 105 .tc_quality = 1000, 106}; 107 108struct a10_timer_softc *a10_timer_sc = NULL; 109 110static struct resource_spec a10_timer_spec[] = { 111 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 112 { SYS_RES_IRQ, 0, RF_ACTIVE }, 113 { -1, 0 } 114}; 115 116static int 117a10_timer_probe(device_t dev) 118{ 119 120 if (!ofw_bus_is_compatible(dev, "a10,timers")) 121 return (ENXIO); 122 123 device_set_desc(dev, "Allwinner A10 timer"); 124 return (BUS_PROBE_DEFAULT); 125} 126 127static int 128a10_timer_attach(device_t dev) 129{ 130 struct a10_timer_softc *sc; 131 int err; 132 uint32_t val; 133 uint32_t freq; 134 135 sc = device_get_softc(dev); 136 137 if (bus_alloc_resources(dev, a10_timer_spec, sc->res)) { 138 device_printf(dev, "could not allocate resources\n"); 139 return (ENXIO); 140 } 141 142 sc->sc_dev = dev; 143 sc->sc_bst = rman_get_bustag(sc->res[0]); 144 sc->sc_bsh = rman_get_bushandle(sc->res[0]); 145 146 /* set interval */ 147 timer_write_4(sc, SW_TIMER0_INT_VALUE_REG, TMR_INTER_VAL); 148 149 /* set clock source to HOSC, 16 pre-division */ 150 val = timer_read_4(sc, SW_TIMER0_CTRL_REG); 151 val &= ~(0x07<<4); 152 val &= ~(0x03<<2); 153 val |= (4<<4) | (1<<2); 154 timer_write_4(sc, SW_TIMER0_CTRL_REG, val); 155 156 /* set mode to auto reload */ 157 val = timer_read_4(sc, SW_TIMER0_CTRL_REG); 158 val |= (1<<1); 159 timer_write_4(sc, SW_TIMER0_CTRL_REG, val); 160 161 /* Enable timer0 */ 162 val = timer_read_4(sc, SW_TIMER_IRQ_EN_REG); 163 val |= (1<<0); 164 timer_write_4(sc, SW_TIMER_IRQ_EN_REG, val); 165 166 /* Setup and enable the timer interrupt */ 167 err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_CLK, a10_timer_intr, 168 NULL, sc, &sc->sc_ih); 169 if (err != 0) { 170 bus_release_resources(dev, a10_timer_spec, sc->res); 171 device_printf(dev, "Unable to setup the clock irq handler, " 172 "err = %d\n", err); 173 return (ENXIO); 174 } 175 freq = SYS_TIMER_CLKSRC; 176 177 /* Set desired frequency in event timer and timecounter */ 178 sc->et.et_frequency = (uint64_t)freq; 179 sc->clkfreq = (uint64_t)freq; 180 sc->et.et_name = "a10_timer Eventtimer"; 181 sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC; 182 sc->et.et_quality = 1000; 183 sc->et.et_min_period.sec = 0; 184 sc->et.et_min_period.frac = 185 ((0x00000002LLU << 32) / sc->et.et_frequency) << 32; 186 sc->et.et_max_period.sec = 0xfffffff0U / sc->et.et_frequency; 187 sc->et.et_max_period.frac = 188 ((0xfffffffeLLU << 32) / sc->et.et_frequency) << 32; 189 sc->et.et_start = a10_timer_timer_start; 190 sc->et.et_stop = a10_timer_timer_stop; 191 sc->et.et_priv = sc; 192 et_register(&sc->et); 193 194 if (device_get_unit(dev) == 0) 195 a10_timer_sc = sc; 196 197 a10_timer_timecounter.tc_frequency = (uint64_t)freq; 198 tc_init(&a10_timer_timecounter); 199 200 printf("clock: hz=%d stathz = %d\n", hz, stathz); 201 202 device_printf(sc->sc_dev, "timer clock frequency %d\n", sc->clkfreq); 203 204 a10_timer_initialized = 1; 205 206 return (0); 207} 208 209static int 210a10_timer_timer_start(struct eventtimer *et, struct bintime *first, 211 struct bintime *period) 212{ 213 struct a10_timer_softc *sc; 214 uint32_t clo, count; 215 216 sc = (struct a10_timer_softc *)et->et_priv; 217 218 if (first != NULL) { 219 count = (sc->et.et_frequency * (first->frac >> 32)) >> 32; 220 if (first->sec != 0) 221 count += sc->et.et_frequency * first->sec; 222 223 /* clear */ 224 timer_write_4(sc, SW_TIMER0_CUR_VALUE_REG, 0); 225 clo = timer_read_4(sc, SW_TIMER0_CUR_VALUE_REG); 226 clo += count; 227 timer_write_4(sc, SW_TIMER0_CUR_VALUE_REG, clo); 228 229 return (0); 230 } 231 232 return (EINVAL); 233} 234 235static int 236a10_timer_timer_stop(struct eventtimer *et) 237{ 238 struct a10_timer_softc *sc; 239 uint32_t val; 240 241 sc = (struct a10_timer_softc *)et->et_priv; 242 243 /* clear */ 244 timer_write_4(sc, SW_TIMER0_CUR_VALUE_REG, 0); 245 246 /* disable */ 247 val = timer_read_4(sc, SW_TIMER0_CTRL_REG); 248 val &= ~(1<<0); /* Disable timer0 */ 249 timer_write_4(sc, SW_TIMER0_CTRL_REG, val); 250 251 sc->sc_period = 0; 252 253 return (0); 254} 255 256int 257a10_timer_get_timerfreq(struct a10_timer_softc *sc) 258{ 259 260 return (sc->clkfreq); 261} 262 263void 264cpu_initclocks(void) 265{ 266 cpu_initclocks_bsp(); 267} 268 269static int 270a10_timer_intr(void *arg) 271{ 272 struct a10_timer_softc *sc; 273 274 sc = (struct a10_timer_softc *)arg; 275 276 if (sc->et.et_active) 277 sc->et.et_event_cb(&sc->et, sc->et.et_arg); 278 279 /* pending */ 280 timer_write_4(sc, SW_TIMER_IRQ_STA_REG, 0x1); 281 282 return (FILTER_HANDLED); 283} 284 285u_int 286a10_timer_get_timecount(struct timecounter *tc) 287{ 288 289 if (a10_timer_sc == NULL) 290 return (0); 291 292 return (timer_read_4(a10_timer_sc, SW_TIMER0_CUR_VALUE_REG)); 293} 294 295static device_method_t a10_timer_methods[] = { 296 DEVMETHOD(device_probe, a10_timer_probe), 297 DEVMETHOD(device_attach, a10_timer_attach), 298 299 DEVMETHOD_END 300}; 301 302static driver_t a10_timer_driver = { 303 "a10_timer", 304 a10_timer_methods, 305 sizeof(struct a10_timer_softc), 306}; 307 308static devclass_t a10_timer_devclass; 309 310DRIVER_MODULE(a10_timer, simplebus, a10_timer_driver, a10_timer_devclass, 0, 0); 311 312void 313DELAY(int usec) 314{ 315 uint32_t counter; 316 uint32_t val, val_temp; 317 int32_t nticks; 318 319 /* Timer is not initialized yet */ 320 if (!a10_timer_initialized) { 321 for (; usec > 0; usec--) 322 for (counter = 200; counter > 0; counter--) 323 /* Prevent optimizing out the loop */ 324 cpufunc_nullop(); 325 return; 326 } 327 328 val = timer_read_4(a10_timer_sc, SW_TIMER0_CUR_VALUE_REG); 329 nticks = ((a10_timer_sc->clkfreq / 1000000 + 1) * usec); 330 331 while (nticks > 0) { 332 val_temp = timer_read_4(a10_timer_sc, SW_TIMER0_CUR_VALUE_REG); 333 if (val > val_temp) 334 nticks -= (val - val_temp); 335 else 336 nticks -= (val + (INITIAL_TIMECOUNTER - val_temp)); 337 338 val = val_temp; 339 } 340} 341 342