1/*- 2 * Copyright 2013-2015 Alexander Kabaev <kan@FreeBSD.org> 3 * Copyright 2013-2015 John Wehle <john@feith.com> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/bus.h> 35#include <sys/kernel.h> 36#include <sys/module.h> 37#include <sys/malloc.h> 38#include <sys/rman.h> 39#include <sys/timetc.h> 40#include <sys/timeet.h> 41 42#include <machine/bus.h> 43#include <machine/cpu.h> 44#include <machine/hwfunc.h> 45 46#include <dev/extres/clk/clk.h> 47 48#include <dev/fdt/fdt_common.h> 49#include <dev/ofw/ofw_bus.h> 50#include <dev/ofw/ofw_bus_subr.h> 51 52#include <mips/ingenic/jz4780_regs.h> 53 54struct jz4780_timer_softc { 55 device_t dev; 56 struct resource * res[4]; 57 void * ih_cookie; 58 struct eventtimer et; 59 struct timecounter tc; 60}; 61 62static struct resource_spec jz4780_timer_spec[] = { 63 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 64 { SYS_RES_IRQ, 0, RF_ACTIVE }, /* OST */ 65 { SYS_RES_IRQ, 1, RF_ACTIVE }, /* TC5 */ 66 { SYS_RES_IRQ, 2, RF_ACTIVE }, /* TC0-4,6 */ 67 { -1, 0 } 68}; 69 70/* 71 * devclass_get_device / device_get_softc could be used 72 * to dynamically locate this, however the timers are a 73 * required device which can't be unloaded so there's 74 * no need for the overhead. 75 */ 76static struct jz4780_timer_softc *jz4780_timer_sc = NULL; 77 78#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) 79#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) 80 81static unsigned 82jz4780_get_timecount(struct timecounter *tc) 83{ 84 struct jz4780_timer_softc *sc = 85 (struct jz4780_timer_softc *)tc->tc_priv; 86 87 return CSR_READ_4(sc, JZ_OST_CNT_LO); 88} 89 90static int 91jz4780_hardclock(void *arg) 92{ 93 struct jz4780_timer_softc *sc = (struct jz4780_timer_softc *)arg; 94 95 CSR_WRITE_4(sc, JZ_TC_TFCR, TFR_FFLAG5); 96 CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST5); 97 98 if (sc->et.et_active) 99 sc->et.et_event_cb(&sc->et, sc->et.et_arg); 100 101 return (FILTER_HANDLED); 102} 103 104static int 105jz4780_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 106{ 107 struct jz4780_timer_softc *sc = 108 (struct jz4780_timer_softc *)et->et_priv; 109 uint32_t ticks; 110 111 ticks = (first * et->et_frequency) / SBT_1S; 112 if (ticks == 0) 113 return (EINVAL); 114 115 CSR_WRITE_4(sc, JZ_TC_TDFR(5), ticks); 116 CSR_WRITE_4(sc, JZ_TC_TCNT(5), 0); 117 CSR_WRITE_4(sc, JZ_TC_TESR, TESR_TCST5); 118 119 return (0); 120} 121 122static int 123jz4780_timer_stop(struct eventtimer *et) 124{ 125 struct jz4780_timer_softc *sc = 126 (struct jz4780_timer_softc *)et->et_priv; 127 128 CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST5); 129 return (0); 130} 131 132static int 133jz4780_timer_probe(device_t dev) 134{ 135 136 if (!ofw_bus_status_okay(dev)) 137 return (ENXIO); 138 139 if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-tcu")) 140 return (ENXIO); 141 142 device_set_desc(dev, "Ingenic JZ4780 timer"); 143 144 return (BUS_PROBE_DEFAULT); 145} 146 147static int 148jz4780_timer_attach(device_t dev) 149{ 150 struct jz4780_timer_softc *sc = device_get_softc(dev); 151 pcell_t counter_freq; 152 clk_t clk; 153 154 /* There should be exactly one instance. */ 155 if (jz4780_timer_sc != NULL) 156 return (ENXIO); 157 158 sc->dev = dev; 159 160 if (bus_alloc_resources(dev, jz4780_timer_spec, sc->res)) { 161 device_printf(dev, "can not allocate resources for device\n"); 162 return (ENXIO); 163 } 164 165 counter_freq = 0; 166 if (clk_get_by_name(dev, "ext", &clk) == 0) { 167 uint64_t clk_freq; 168 169 if (clk_get_freq(clk, &clk_freq) == 0) 170 counter_freq = (uint32_t)clk_freq / 16; 171 clk_release(clk); 172 } 173 if (counter_freq == 0) { 174 device_printf(dev, "unable to determine ext clock frequency\n"); 175 /* Hardcode value we 'know' is correct */ 176 counter_freq = 48000000 / 16; 177 } 178 179 /* 180 * Disable the timers, select the input for each timer, 181 * clear and then start OST. 182 */ 183 184 /* Stop OST, if it happens to be running */ 185 CSR_WRITE_4(sc, JZ_TC_TECR, TESR_OST); 186 /* Stop all other channels as well */ 187 CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST0 | TESR_TCST1 | TESR_TCST2 | 188 TESR_TCST3 | TESR_TCST4 | TESR_TCST5 | TESR_TCST6 | TESR_TCST7); 189 /* Clear detect mask flags */ 190 CSR_WRITE_4(sc, JZ_TC_TFCR, 0xFFFFFFFF); 191 /* Mask all interrupts */ 192 CSR_WRITE_4(sc, JZ_TC_TMSR, 0xFFFFFFFF); 193 194 /* Init counter with known data */ 195 CSR_WRITE_4(sc, JZ_OST_CTRL, 0); 196 CSR_WRITE_4(sc, JZ_OST_CNT_LO, 0); 197 CSR_WRITE_4(sc, JZ_OST_CNT_HI, 0); 198 CSR_WRITE_4(sc, JZ_OST_DATA, 0xffffffff); 199 200 /* Configure counter for external clock */ 201 CSR_WRITE_4(sc, JZ_OST_CTRL, OSTC_EXT_EN | OSTC_MODE | OSTC_DIV_16); 202 203 /* Start the counter again */ 204 CSR_WRITE_4(sc, JZ_TC_TESR, TESR_OST); 205 206 /* Configure TCU channel 5 similarly to OST and leave it disabled */ 207 CSR_WRITE_4(sc, JZ_TC_TCSR(5), TCSR_EXT_EN | TCSR_DIV_16); 208 CSR_WRITE_4(sc, JZ_TC_TMCR, TMR_FMASK(5)); 209 210 if (bus_setup_intr(dev, sc->res[2], INTR_TYPE_CLK, 211 jz4780_hardclock, NULL, sc, &sc->ih_cookie)) { 212 device_printf(dev, "could not setup interrupt handler\n"); 213 bus_release_resources(dev, jz4780_timer_spec, sc->res); 214 return (ENXIO); 215 } 216 217 sc->et.et_name = "JZ4780 TCU5"; 218 sc->et.et_flags = ET_FLAGS_ONESHOT; 219 sc->et.et_frequency = counter_freq; 220 sc->et.et_quality = 1000; 221 sc->et.et_min_period = (0x00000002LLU * SBT_1S) / sc->et.et_frequency; 222 sc->et.et_max_period = (0x0000fffeLLU * SBT_1S) / sc->et.et_frequency; 223 sc->et.et_start = jz4780_timer_start; 224 sc->et.et_stop = jz4780_timer_stop; 225 sc->et.et_priv = sc; 226 227 et_register(&sc->et); 228 229 sc->tc.tc_get_timecount = jz4780_get_timecount; 230 sc->tc.tc_name = "JZ4780 OST"; 231 sc->tc.tc_frequency = counter_freq; 232 sc->tc.tc_counter_mask = ~0u; 233 sc->tc.tc_quality = 1000; 234 sc->tc.tc_priv = sc; 235 236 tc_init(&sc->tc); 237 238 /* Now when tc is initialized, allow DELAY to find it */ 239 jz4780_timer_sc = sc; 240 241 return (0); 242} 243 244static int 245jz4780_timer_detach(device_t dev) 246{ 247 248 return (EBUSY); 249} 250 251static device_method_t jz4780_timer_methods[] = { 252 /* Device interface */ 253 DEVMETHOD(device_probe, jz4780_timer_probe), 254 DEVMETHOD(device_attach, jz4780_timer_attach), 255 DEVMETHOD(device_detach, jz4780_timer_detach), 256 257 DEVMETHOD_END 258}; 259 260static driver_t jz4780_timer_driver = { 261 "timer", 262 jz4780_timer_methods, 263 sizeof(struct jz4780_timer_softc), 264}; 265 266static devclass_t jz4780_timer_devclass; 267 268EARLY_DRIVER_MODULE(timer, simplebus, jz4780_timer_driver, 269 jz4780_timer_devclass, 0, 0, BUS_PASS_TIMER); 270 271void 272DELAY(int usec) 273{ 274 uint32_t counter; 275 uint32_t delta, now, previous, remaining; 276 277 /* Timer has not yet been initialized */ 278 if (jz4780_timer_sc == NULL) { 279 for (; usec > 0; usec--) 280 for (counter = 200; counter > 0; counter--) { 281 /* Prevent gcc from optimizing out the loop */ 282 mips_rd_cause(); 283 } 284 return; 285 } 286 TSENTER(); 287 288 /* 289 * Some of the other timers in the source tree do this calculation as: 290 * 291 * usec * ((sc->tc.tc_frequency / 1000000) + 1) 292 * 293 * which gives a fairly pessimistic result when tc_frequency is an exact 294 * multiple of 1000000. Given the data type and typical values for 295 * tc_frequency adding 999999 shouldn't overflow. 296 */ 297 remaining = usec * ((jz4780_timer_sc->tc.tc_frequency + 999999) / 298 1000000); 299 300 /* 301 * We add one since the first iteration may catch the counter just 302 * as it is changing. 303 */ 304 remaining += 1; 305 306 previous = jz4780_get_timecount(&jz4780_timer_sc->tc); 307 308 for ( ; ; ) { 309 now = jz4780_get_timecount(&jz4780_timer_sc->tc); 310 311 /* 312 * If the timer has rolled over, then we have the case: 313 * 314 * if (previous > now) { 315 * delta = (0 - previous) + now 316 * } 317 * 318 * which is really no different then the normal case. 319 * Both cases are simply: 320 * 321 * delta = now - previous. 322 */ 323 delta = now - previous; 324 325 if (delta >= remaining) 326 break; 327 328 previous = now; 329 remaining -= delta; 330 } 331 TSEXIT(); 332} 333 334void 335platform_initclocks(void) 336{ 337 338} 339 340