1 /* $NetBSD: mvsoctmr.c,v 1.9 2013/05/01 12:45:31 rkujawa Exp $ */ 2 /* 3 * Copyright (c) 2007, 2008 KIYOHARA Takashi 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 ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 #include <sys/cdefs.h> 28 __KERNEL_RCSID(0, "$NetBSD: mvsoctmr.c,v 1.9 2013/05/01 12:45:31 rkujawa Exp $"); 29 30 #include "opt_ddb.h" 31 #include "opt_mvsoc.h" 32 33 #include <sys/param.h> 34 #include <sys/atomic.h> 35 #include <sys/bus.h> 36 #include <sys/device.h> 37 #include <sys/errno.h> 38 #include <sys/kernel.h> 39 #include <sys/time.h> 40 #include <sys/timetc.h> 41 #include <sys/systm.h> 42 #include <sys/wdog.h> 43 44 #include <machine/intr.h> 45 46 #include <arm/cpufunc.h> 47 48 #include <arm/marvell/mvsocreg.h> 49 #include <arm/marvell/mvsocvar.h> 50 #include <arm/marvell/mvsoctmrreg.h> 51 52 #include <dev/marvell/marvellvar.h> 53 54 #include <dev/sysmon/sysmonvar.h> 55 56 #ifdef DDB 57 #include <machine/db_machdep.h> 58 #include <ddb/db_extern.h> 59 #endif 60 61 62 struct mvsoctmr_softc { 63 device_t sc_dev; 64 65 struct sysmon_wdog sc_wdog; 66 uint32_t sc_wdog_period; 67 uint32_t sc_wdog_armed; 68 69 bus_space_tag_t sc_iot; 70 bus_space_handle_t sc_ioh; 71 #if defined(ARMADAXP) 72 int sc_irq; 73 #endif 74 }; 75 76 77 static int mvsoctmr_match(device_t, struct cfdata *, void *); 78 static void mvsoctmr_attach(device_t, device_t, void *); 79 80 static int clockhandler(void *); 81 82 static u_int mvsoctmr_get_timecount(struct timecounter *); 83 84 static void mvsoctmr_cntl(struct mvsoctmr_softc *, int, u_int, int, int); 85 86 static int mvsoctmr_wdog_tickle(struct sysmon_wdog *); 87 static int mvsoctmr_wdog_setmode(struct sysmon_wdog *); 88 89 #ifdef DDB 90 static void mvsoctmr_wdog_ddb_trap(int); 91 #endif 92 93 #define MVSOC_WDOG_MAX_PERIOD (0xffffffff / mvTclk) 94 95 static struct mvsoctmr_softc *mvsoctmr_sc; 96 static struct timecounter mvsoctmr_timecounter = { 97 mvsoctmr_get_timecount, /* get_timecount */ 98 0, /* no poll_pps */ 99 ~0u, /* counter_mask */ 100 0, /* frequency (set by cpu_initclocks()) */ 101 "mvsoctmr", /* name */ 102 100, /* quality */ 103 NULL, /* prev */ 104 NULL, /* next */ 105 }; 106 107 CFATTACH_DECL_NEW(mvsoctmr, sizeof(struct mvsoctmr_softc), 108 mvsoctmr_match, mvsoctmr_attach, NULL, NULL); 109 110 111 /* ARGSUSED */ 112 static int 113 mvsoctmr_match(device_t parent, struct cfdata *match, void *aux) 114 { 115 struct marvell_attach_args *mva = aux; 116 117 if (strcmp(mva->mva_name, match->cf_name) != 0) 118 return 0; 119 if (mva->mva_offset == MVA_OFFSET_DEFAULT) 120 return 0; 121 122 mva->mva_size = MVSOCTMR_SIZE; 123 return 1; 124 } 125 126 /* ARGSUSED */ 127 static void 128 mvsoctmr_attach(device_t parent, device_t self, void *aux) 129 { 130 struct mvsoctmr_softc *sc = device_private(self); 131 struct marvell_attach_args *mva = aux; 132 uint32_t rstoutn; 133 134 aprint_naive("\n"); 135 aprint_normal(": Marvell SoC Timer\n"); 136 137 if (mvsoctmr_sc == NULL) 138 mvsoctmr_sc = sc; 139 140 #if defined(ARMADAXP) 141 sc->sc_irq = mva->mva_irq; 142 #endif 143 144 sc->sc_dev = self; 145 sc->sc_iot = mva->mva_iot; 146 if (bus_space_subregion(mva->mva_iot, mva->mva_ioh, 147 mva->mva_offset, mva->mva_size, &sc->sc_ioh)) 148 panic("%s: Cannot map registers", device_xname(self)); 149 150 mvsoctmr_timecounter.tc_name = device_xname(self); 151 mvsoctmr_cntl(sc, MVSOCTMR_TIMER1, 0xffffffff, 1, 1); 152 153 /* 154 * stop watchdog timer, enable watchdog timer resets 155 */ 156 mvsoctmr_cntl(sc, MVSOCTMR_WATCHDOG, 0xffffffff, 0, 0); 157 write_mlmbreg(MVSOC_MLMB_MLMBICR, 158 ~(1<<MVSOC_MLMB_MLMBI_CPUWDTIMERINTREQ)); 159 rstoutn = read_mlmbreg(MVSOC_MLMB_RSTOUTNMASKR); 160 write_mlmbreg(MVSOC_MLMB_RSTOUTNMASKR, 161 rstoutn | MVSOC_MLMB_RSTOUTNMASKR_WDRSTOUTEN); 162 163 #ifdef DDB 164 db_trap_callback = mvsoctmr_wdog_ddb_trap; 165 #endif 166 167 sc->sc_wdog.smw_name = device_xname(self); 168 sc->sc_wdog.smw_cookie = sc; 169 sc->sc_wdog.smw_setmode = mvsoctmr_wdog_setmode; 170 sc->sc_wdog.smw_tickle = mvsoctmr_wdog_tickle; 171 sc->sc_wdog.smw_period = MVSOC_WDOG_MAX_PERIOD; 172 173 if (sysmon_wdog_register(&sc->sc_wdog) != 0) 174 aprint_error_dev(self, 175 "unable to register watchdog with sysmon\n"); 176 } 177 178 /* 179 * clockhandler: 180 * 181 * Handle the hardclock interrupt. 182 */ 183 static int 184 clockhandler(void *arg) 185 { 186 struct clockframe *frame = arg; 187 188 #if defined(ARMADAXP) 189 /* Acknowledge all timers-related interrupts */ 190 bus_space_write_4(mvsoctmr_sc->sc_iot, mvsoctmr_sc->sc_ioh, 191 MVSOCTMR_TESR, 0x0); 192 #endif 193 194 hardclock(frame); 195 196 return 1; 197 } 198 199 /* 200 * setstatclockrate: 201 * 202 * Set the rate of the statistics clock. 203 */ 204 /* ARGSUSED */ 205 void 206 setstatclockrate(int newhz) 207 { 208 } 209 210 /* 211 * cpu_initclocks: 212 * 213 * Initialize the clock and get them going. 214 */ 215 void 216 cpu_initclocks(void) 217 { 218 struct mvsoctmr_softc *sc; 219 void *clock_ih; 220 const int en = 1, autoen = 1; 221 uint32_t timer0_tval; 222 223 sc = mvsoctmr_sc; 224 if (sc == NULL) 225 panic("cpu_initclocks: mvsoctmr not found"); 226 227 mvsoctmr_timecounter.tc_priv = sc; 228 229 #if defined(ARMADAXP) 230 /* We set global timer and counter to 25 MHz mode */ 231 mvsoctmr_timecounter.tc_frequency = 25000000; 232 #else 233 mvsoctmr_timecounter.tc_frequency = mvTclk; 234 #endif 235 236 timer0_tval = (mvsoctmr_timecounter.tc_frequency * 2) / (u_long) hz; 237 timer0_tval = (timer0_tval / 2) + (timer0_tval & 1); 238 239 mvsoctmr_cntl(sc, MVSOCTMR_TIMER0, timer0_tval, en, autoen); 240 mvsoctmr_cntl(sc, MVSOCTMR_TIMER1, 0xffffffff, en, autoen); 241 242 #if defined(ARMADAXP) 243 /* 244 * Establishing timer interrupts is slightly different for Armada XP 245 * than for other supported SoCs from Marvell. 246 * Timer interrupt is no different from any other interrupt in Armada 247 * XP, so we use generic marvell_intr_establish(). 248 */ 249 clock_ih = marvell_intr_establish(sc->sc_irq, IPL_CLOCK, 250 clockhandler, NULL); 251 #else 252 clock_ih = mvsoc_bridge_intr_establish(MVSOC_MLMB_MLMBI_CPUTIMER0INTREQ, 253 IPL_CLOCK, clockhandler, NULL); 254 #endif 255 if (clock_ih == NULL) 256 panic("cpu_initclocks: unable to register timer interrupt"); 257 258 tc_init(&mvsoctmr_timecounter); 259 } 260 261 void 262 delay(unsigned int n) 263 { 264 struct mvsoctmr_softc *sc; 265 unsigned int cur_tick, initial_tick; 266 int remaining; 267 268 sc = mvsoctmr_sc; 269 #ifdef DEBUG 270 if (sc == NULL) { 271 printf("%s: called before start mvsoctmr\n", __func__); 272 return; 273 } 274 #endif 275 276 /* 277 * Read the counter first, so that the rest of the setup overhead is 278 * counted. 279 */ 280 initial_tick = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 281 MVSOCTMR_TIMER(MVSOCTMR_TIMER1)); 282 283 if (n <= UINT_MAX / mvTclk) { 284 /* 285 * For unsigned arithmetic, division can be replaced with 286 * multiplication with the inverse and a shift. 287 */ 288 remaining = n * mvTclk / 1000000; 289 } else { 290 /* 291 * This is a very long delay. 292 * Being slow here doesn't matter. 293 */ 294 remaining = (unsigned long long) n * mvTclk / 1000000; 295 } 296 297 while (remaining > 0) { 298 cur_tick = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 299 MVSOCTMR_TIMER(MVSOCTMR_TIMER1)); 300 if (cur_tick > initial_tick) 301 remaining -= 0xffffffff - cur_tick + initial_tick; 302 else 303 remaining -= (initial_tick - cur_tick); 304 initial_tick = cur_tick; 305 } 306 } 307 308 static u_int 309 mvsoctmr_get_timecount(struct timecounter *tc) 310 { 311 struct mvsoctmr_softc *sc = tc->tc_priv; 312 313 return 0xffffffff - bus_space_read_4(sc->sc_iot, sc->sc_ioh, 314 MVSOCTMR_TIMER(MVSOCTMR_TIMER1)); 315 } 316 317 static void 318 mvsoctmr_cntl(struct mvsoctmr_softc *sc, int num, u_int ticks, int en, 319 int autoen) 320 { 321 uint32_t ctrl; 322 323 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_RELOAD(num), 324 ticks); 325 326 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_TIMER(num), ticks); 327 328 ctrl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_CTCR); 329 if (en) 330 ctrl |= MVSOCTMR_CTCR_CPUTIMEREN(num); 331 else 332 ctrl &= ~MVSOCTMR_CTCR_CPUTIMEREN(num); 333 if (autoen) 334 ctrl |= MVSOCTMR_CTCR_CPUTIMERAUTO(num); 335 else 336 ctrl &= ~MVSOCTMR_CTCR_CPUTIMERAUTO(num); 337 #if defined(ARMADAXP) 338 /* Set timer and counter to 25MHz mode */ 339 ctrl |= MVSOCTMR_CTCR_25MHZEN(num); 340 #endif 341 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_CTCR, ctrl); 342 } 343 344 static int 345 mvsoctmr_wdog_setmode(struct sysmon_wdog *smw) 346 { 347 struct mvsoctmr_softc *sc = smw->smw_cookie; 348 349 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 350 sc->sc_wdog_armed = 0; 351 mvsoctmr_cntl(sc, MVSOCTMR_WATCHDOG, 0xffffffff, 0, 0); 352 } else { 353 sc->sc_wdog_armed = 1; 354 if (smw->smw_period == WDOG_PERIOD_DEFAULT) 355 smw->smw_period = MVSOC_WDOG_MAX_PERIOD; 356 else if (smw->smw_period > MVSOC_WDOG_MAX_PERIOD || 357 smw->smw_period <= 0) 358 return (EOPNOTSUPP); 359 sc->sc_wdog_period = smw->smw_period * mvTclk; 360 mvsoctmr_cntl(sc, MVSOCTMR_WATCHDOG, sc->sc_wdog_period, 1, 0); 361 } 362 363 return (0); 364 } 365 366 static int 367 mvsoctmr_wdog_tickle(struct sysmon_wdog *smw) 368 { 369 struct mvsoctmr_softc *sc = smw->smw_cookie; 370 371 mvsoctmr_cntl(sc, MVSOCTMR_WATCHDOG, sc->sc_wdog_period, 1, 0); 372 373 return (0); 374 } 375 376 #ifdef DDB 377 static void 378 mvsoctmr_wdog_ddb_trap(int enter) 379 { 380 struct mvsoctmr_softc *sc = mvsoctmr_sc; 381 382 if (sc == NULL) 383 return; 384 385 if (sc->sc_wdog_armed) { 386 if (enter) 387 mvsoctmr_cntl(sc, MVSOCTMR_WATCHDOG, 0xffffffff, 0, 0); 388 else 389 mvsoctmr_cntl(sc, MVSOCTMR_WATCHDOG, 390 sc->sc_wdog_period, 1, 0); 391 } 392 } 393 #endif 394