1 /*- 2 * Copyright (c) 2012, 2013 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Oleksandr Rybalko under sponsorship 6 * from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/bus.h> 36 #include <sys/kernel.h> 37 #include <sys/module.h> 38 #include <sys/malloc.h> 39 #include <sys/rman.h> 40 #include <sys/timeet.h> 41 #include <sys/timetc.h> 42 #include <sys/watchdog.h> 43 #include <machine/bus.h> 44 #include <machine/cpu.h> 45 #include <machine/frame.h> 46 #include <machine/intr.h> 47 48 #include <machine/fdt.h> 49 #include <dev/fdt/fdt_common.h> 50 #include <dev/ofw/openfirm.h> 51 #include <dev/ofw/ofw_bus.h> 52 #include <dev/ofw/ofw_bus_subr.h> 53 54 #include <arm/freescale/imx/imx_gptvar.h> 55 #include <arm/freescale/imx/imx_gptreg.h> 56 57 #include <sys/kdb.h> 58 #include <arm/freescale/imx/imx51_ccmvar.h> 59 60 #define MIN_PERIOD 100LLU 61 62 #define WRITE4(_sc, _r, _v) \ 63 bus_space_write_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r), (_v)) 64 #define READ4(_sc, _r) \ 65 bus_space_read_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r)) 66 #define SET4(_sc, _r, _m) \ 67 WRITE4((_sc), (_r), READ4((_sc), (_r)) | (_m)) 68 #define CLEAR4(_sc, _r, _m) \ 69 WRITE4((_sc), (_r), READ4((_sc), (_r)) & ~(_m)) 70 71 static u_int imx_gpt_get_timecount(struct timecounter *); 72 static int imx_gpt_timer_start(struct eventtimer *, sbintime_t, 73 sbintime_t); 74 static int imx_gpt_timer_stop(struct eventtimer *); 75 76 static int imx_gpt_intr(void *); 77 static int imx_gpt_probe(device_t); 78 static int imx_gpt_attach(device_t); 79 80 static struct timecounter imx_gpt_timecounter = { 81 .tc_name = "i.MX GPT Timecounter", 82 .tc_get_timecount = imx_gpt_get_timecount, 83 .tc_counter_mask = ~0u, 84 .tc_frequency = 0, 85 .tc_quality = 500, 86 }; 87 88 struct imx_gpt_softc *imx_gpt_sc = NULL; 89 static volatile int imx_gpt_delay_count = 300; 90 91 static struct resource_spec imx_gpt_spec[] = { 92 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 93 { SYS_RES_IRQ, 0, RF_ACTIVE }, 94 { -1, 0 } 95 }; 96 97 static int 98 imx_gpt_probe(device_t dev) 99 { 100 101 if (!ofw_bus_is_compatible(dev, "fsl,imx51-gpt")) 102 return (ENXIO); 103 104 device_set_desc(dev, "Freescale i.MXxxx GPT timer"); 105 return (BUS_PROBE_DEFAULT); 106 } 107 108 static int 109 imx_gpt_attach(device_t dev) 110 { 111 struct imx_gpt_softc *sc; 112 int err; 113 114 sc = device_get_softc(dev); 115 116 if (bus_alloc_resources(dev, imx_gpt_spec, sc->res)) { 117 device_printf(dev, "could not allocate resources\n"); 118 return (ENXIO); 119 } 120 121 sc->sc_dev = dev; 122 sc->sc_clksrc = GPT_CR_CLKSRC_IPG; 123 sc->sc_iot = rman_get_bustag(sc->res[0]); 124 sc->sc_ioh = rman_get_bushandle(sc->res[0]); 125 126 switch (sc->sc_clksrc) { 127 case GPT_CR_CLKSRC_NONE: 128 device_printf(dev, "can't run timer without clock source\n"); 129 return (EINVAL); 130 case GPT_CR_CLKSRC_EXT: 131 device_printf(dev, "Not implemented. Geve me the way to get " 132 "external clock source frequency\n"); 133 return (EINVAL); 134 case GPT_CR_CLKSRC_32K: 135 sc->clkfreq = 32768; 136 break; 137 case GPT_CR_CLKSRC_IPG_HIGH: 138 sc->clkfreq = imx51_get_clock(IMX51CLK_IPG_CLK_ROOT) * 2; 139 break; 140 default: 141 sc->clkfreq = imx51_get_clock(IMX51CLK_IPG_CLK_ROOT); 142 } 143 device_printf(dev, "Run on %dKHz clock.\n", sc->clkfreq / 1000); 144 145 /* Reset */ 146 WRITE4(sc, IMX_GPT_CR, GPT_CR_SWR); 147 /* Enable and setup counters */ 148 WRITE4(sc, IMX_GPT_CR, 149 GPT_CR_CLKSRC_IPG | /* Use IPG clock */ 150 GPT_CR_FRR | /* Just count (FreeRunner mode) */ 151 GPT_CR_STOPEN | /* Run in STOP mode */ 152 GPT_CR_WAITEN | /* Run in WAIT mode */ 153 GPT_CR_DBGEN); /* Run in DEBUG mode */ 154 155 /* Disable interrupts */ 156 WRITE4(sc, IMX_GPT_IR, 0); 157 158 /* Tick every 10us */ 159 /* XXX: must be calculated from clock source frequency */ 160 WRITE4(sc, IMX_GPT_PR, 665); 161 /* Use 100 KHz */ 162 sc->clkfreq = 100000; 163 164 /* Setup and enable the timer interrupt */ 165 err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_CLK, imx_gpt_intr, 166 NULL, sc, &sc->sc_ih); 167 if (err != 0) { 168 bus_release_resources(dev, imx_gpt_spec, sc->res); 169 device_printf(dev, "Unable to setup the clock irq handler, " 170 "err = %d\n", err); 171 return (ENXIO); 172 } 173 174 sc->et.et_name = "i.MXxxx GPT Eventtimer"; 175 sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC; 176 sc->et.et_quality = 1000; 177 sc->et.et_frequency = sc->clkfreq; 178 sc->et.et_min_period = (MIN_PERIOD << 32) / sc->et.et_frequency; 179 sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; 180 sc->et.et_start = imx_gpt_timer_start; 181 sc->et.et_stop = imx_gpt_timer_stop; 182 sc->et.et_priv = sc; 183 et_register(&sc->et); 184 185 /* Disable interrupts */ 186 WRITE4(sc, IMX_GPT_IR, 0); 187 /* ACK any panding interrupts */ 188 WRITE4(sc, IMX_GPT_SR, (GPT_IR_ROV << 1) - 1); 189 190 if (device_get_unit(dev) == 0) 191 imx_gpt_sc = sc; 192 193 imx_gpt_timecounter.tc_frequency = sc->clkfreq; 194 tc_init(&imx_gpt_timecounter); 195 196 printf("clock: hz=%d stathz = %d\n", hz, stathz); 197 198 device_printf(sc->sc_dev, "timer clock frequency %d\n", sc->clkfreq); 199 200 imx_gpt_delay_count = imx51_get_clock(IMX51CLK_ARM_ROOT) / 4000000; 201 202 SET4(sc, IMX_GPT_CR, GPT_CR_EN); 203 204 return (0); 205 } 206 207 static int 208 imx_gpt_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 209 { 210 struct imx_gpt_softc *sc; 211 uint32_t ticks; 212 213 sc = (struct imx_gpt_softc *)et->et_priv; 214 215 if (period != 0) { 216 sc->sc_period = ((uint32_t)et->et_frequency * period) >> 32; 217 /* Set expected value */ 218 WRITE4(sc, IMX_GPT_OCR2, READ4(sc, IMX_GPT_CNT) + sc->sc_period); 219 /* Enable compare register 2 Interrupt */ 220 SET4(sc, IMX_GPT_IR, GPT_IR_OF2); 221 } else if (first != 0) { 222 ticks = ((uint32_t)et->et_frequency * first) >> 32; 223 224 /* 225 * TODO: setupt second compare reg with time which will save 226 * us in case correct one lost, f.e. if period to short and 227 * setup done later than counter reach target value. 228 */ 229 /* Do not disturb, otherwise event will be lost */ 230 spinlock_enter(); 231 /* Set expected value */ 232 WRITE4(sc, IMX_GPT_OCR1, READ4(sc, IMX_GPT_CNT) + ticks); 233 /* Enable compare register 1 Interrupt */ 234 SET4(sc, IMX_GPT_IR, GPT_IR_OF1); 235 /* Now everybody can relax */ 236 spinlock_exit(); 237 238 return (0); 239 } 240 241 return (EINVAL); 242 } 243 244 static int 245 imx_gpt_timer_stop(struct eventtimer *et) 246 { 247 struct imx_gpt_softc *sc; 248 249 sc = (struct imx_gpt_softc *)et->et_priv; 250 251 /* Disable OF2 Interrupt */ 252 CLEAR4(sc, IMX_GPT_IR, GPT_IR_OF2); 253 WRITE4(sc, IMX_GPT_SR, GPT_IR_OF2); 254 sc->sc_period = 0; 255 256 return (0); 257 } 258 259 int 260 imx_gpt_get_timerfreq(struct imx_gpt_softc *sc) 261 { 262 263 return (sc->clkfreq); 264 } 265 266 void 267 cpu_initclocks(void) 268 { 269 270 if (!imx_gpt_sc) { 271 panic("%s: driver has not been initialized!", __func__); 272 } 273 274 cpu_initclocks_bsp(); 275 276 /* Switch to DELAY using counter */ 277 imx_gpt_delay_count = 0; 278 device_printf(imx_gpt_sc->sc_dev, 279 "switch DELAY to use H/W counter\n"); 280 } 281 282 static int 283 imx_gpt_intr(void *arg) 284 { 285 struct imx_gpt_softc *sc; 286 uint32_t status; 287 288 sc = (struct imx_gpt_softc *)arg; 289 290 /* Sometime we not get staus bit when interrupt arrive. Cache? */ 291 while (!(status = READ4(sc, IMX_GPT_SR))) 292 ; 293 294 if (status & GPT_IR_OF1) { 295 if (sc->et.et_active) { 296 sc->et.et_event_cb(&sc->et, sc->et.et_arg); 297 } 298 } 299 if (status & GPT_IR_OF2) { 300 if (sc->et.et_active) { 301 sc->et.et_event_cb(&sc->et, sc->et.et_arg); 302 /* Set expected value */ 303 WRITE4(sc, IMX_GPT_OCR2, READ4(sc, IMX_GPT_CNT) + 304 sc->sc_period); 305 } 306 } 307 308 /* ACK */ 309 WRITE4(sc, IMX_GPT_SR, status); 310 311 return (FILTER_HANDLED); 312 } 313 314 u_int 315 imx_gpt_get_timecount(struct timecounter *tc) 316 { 317 318 if (imx_gpt_sc == NULL) 319 return (0); 320 321 return (READ4(imx_gpt_sc, IMX_GPT_CNT)); 322 } 323 324 static device_method_t imx_gpt_methods[] = { 325 DEVMETHOD(device_probe, imx_gpt_probe), 326 DEVMETHOD(device_attach, imx_gpt_attach), 327 328 DEVMETHOD_END 329 }; 330 331 static driver_t imx_gpt_driver = { 332 "imx_gpt", 333 imx_gpt_methods, 334 sizeof(struct imx_gpt_softc), 335 }; 336 337 static devclass_t imx_gpt_devclass; 338 339 EARLY_DRIVER_MODULE(imx_gpt, simplebus, imx_gpt_driver, imx_gpt_devclass, 0, 340 0, BUS_PASS_TIMER); 341 342 void 343 DELAY(int usec) 344 { 345 int32_t counts; 346 uint32_t last; 347 348 /* 349 * Check the timers are setup, if not just use a for loop for the 350 * meantime. 351 */ 352 if (imx_gpt_delay_count) { 353 for (; usec > 0; usec--) 354 for (counts = imx_gpt_delay_count; counts > 0; 355 counts--) 356 /* Prevent optimizing out the loop */ 357 cpufunc_nullop(); 358 return; 359 } 360 361 /* At least 1 count */ 362 usec = MAX(1, usec / 100); 363 364 last = READ4(imx_gpt_sc, IMX_GPT_CNT) + usec; 365 while (READ4(imx_gpt_sc, IMX_GPT_CNT) < last) { 366 /* Prevent optimizing out the loop */ 367 cpufunc_nullop(); 368 } 369 /* TODO: use interrupt on OCR2 */ 370 } 371