1/* $NetBSD: mvsoctmr.c,v 1.15 2020/05/29 12:30:39 rin Exp $ */ 2/* 3 * Copyright (c) 2007, 2008, 2010 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.15 2020/05/29 12:30:39 rin 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/marvellreg.h> 53#include <dev/marvell/marvellvar.h> 54 55#include <dev/sysmon/sysmonvar.h> 56 57#ifdef DDB 58#include <machine/db_machdep.h> 59#include <ddb/db_extern.h> 60#endif 61 62 63struct mvsoctmr_softc { 64 device_t sc_dev; 65 66 struct sysmon_wdog sc_wdog; 67 uint32_t sc_wdog_period; 68 uint32_t sc_wdog_armed; 69 70 bus_space_tag_t sc_iot; 71 bus_space_handle_t sc_ioh; 72 int sc_irq; 73 74#define TMR_FLAGS_NOBRIDGE (1 << 0) 75#define TMR_FLAGS_25MHZ (1 << 1) 76#define TMR_FLAGS_SYSCLK (1 << 2) 77 int sc_flags; 78}; 79 80 81static int mvsoctmr_match(device_t, struct cfdata *, void *); 82static void mvsoctmr_attach(device_t, device_t, void *); 83 84static int clockhandler(void *); 85 86static u_int mvsoctmr_get_timecount(struct timecounter *); 87 88static void mvsoctmr_cntl(struct mvsoctmr_softc *, int, u_int, int, int); 89 90static int mvsoctmr_wdog_tickle(struct sysmon_wdog *); 91static int mvsoctmr_wdog_setmode(struct sysmon_wdog *); 92 93#ifdef DDB 94static void mvsoctmr_wdog_ddb_trap(int); 95#endif 96 97static int mvsoctmr_freq; 98 99#define MVSOC_WDOG_MAX_PERIOD (0xffffffff / mvsoctmr_freq) 100 101static struct mvsoctmr_softc *mvsoctmr_sc; 102static struct timecounter mvsoctmr_timecounter = { 103 .tc_get_timecount = mvsoctmr_get_timecount, 104 .tc_counter_mask = ~0u, 105 .tc_name = "mvsoctmr", 106 .tc_quality = 100, 107}; 108 109CFATTACH_DECL_NEW(mvsoctmr, sizeof(struct mvsoctmr_softc), 110 mvsoctmr_match, mvsoctmr_attach, NULL, NULL); 111 112 113/* ARGSUSED */ 114static int 115mvsoctmr_match(device_t parent, struct cfdata *match, void *aux) 116{ 117 struct marvell_attach_args *mva = aux; 118 119 if (strcmp(mva->mva_name, match->cf_name) != 0) 120 return 0; 121 if (mva->mva_offset == MVA_OFFSET_DEFAULT || 122 mva->mva_irq == MVA_IRQ_DEFAULT) 123 return 0; 124 125 mva->mva_size = MVSOCTMR_SIZE; 126 return 1; 127} 128 129/* ARGSUSED */ 130static void 131mvsoctmr_attach(device_t parent, device_t self, void *aux) 132{ 133 struct mvsoctmr_softc *sc = device_private(self); 134 struct marvell_attach_args *mva = aux; 135 uint32_t rstoutn; 136 137 aprint_naive("\n"); 138 aprint_normal(": Marvell SoC Timer\n"); 139 140 if (mvsoctmr_sc == NULL) 141 mvsoctmr_sc = sc; 142 143 sc->sc_dev = self; 144 sc->sc_iot = mva->mva_iot; 145 if (bus_space_subregion(mva->mva_iot, mva->mva_ioh, 146 mva->mva_offset, mva->mva_size, &sc->sc_ioh)) 147 panic("%s: Cannot map registers", device_xname(self)); 148 sc->sc_irq = mva->mva_irq; 149 150 switch (mva->mva_model) { 151 case MARVELL_ARMADAXP_MV78130: 152 case MARVELL_ARMADAXP_MV78160: 153 case MARVELL_ARMADAXP_MV78230: 154 case MARVELL_ARMADAXP_MV78260: 155 case MARVELL_ARMADAXP_MV78460: 156 sc->sc_flags = TMR_FLAGS_25MHZ | TMR_FLAGS_NOBRIDGE; 157 break; 158 case MARVELL_ARMADA370_MV6707: 159 case MARVELL_ARMADA370_MV6710: 160 case MARVELL_ARMADA370_MV6W11: 161 sc->sc_flags = TMR_FLAGS_NOBRIDGE | TMR_FLAGS_SYSCLK; 162 break; 163 } 164 165 mvsoctmr_timecounter.tc_name = device_xname(self); 166 mvsoctmr_cntl(sc, MVSOCTMR_TIMER1, 0xffffffff, 1, 1); 167 168 /* 169 * stop watchdog timer, enable watchdog timer resets 170 */ 171 mvsoctmr_cntl(sc, MVSOCTMR_WATCHDOG, 0xffffffff, 0, 0); 172 write_mlmbreg(MVSOC_MLMB_MLMBICR, 173 ~(1<<MVSOC_MLMB_MLMBI_CPUWDTIMERINTREQ)); 174 rstoutn = read_mlmbreg(MVSOC_MLMB_RSTOUTNMASKR); 175 write_mlmbreg(MVSOC_MLMB_RSTOUTNMASKR, 176 rstoutn | MVSOC_MLMB_RSTOUTNMASKR_WDRSTOUTEN); 177 178#ifdef DDB 179 db_trap_callback = mvsoctmr_wdog_ddb_trap; 180#endif 181 182 if (sc->sc_flags & TMR_FLAGS_25MHZ) 183 /* We set global timer and counter to 25 MHz mode */ 184 mvsoctmr_freq = 25000000; 185 else if (sc->sc_flags & TMR_FLAGS_SYSCLK) 186 mvsoctmr_freq = mvSysclk; 187 else 188 mvsoctmr_freq = mvTclk; 189 190 sc->sc_wdog.smw_name = device_xname(self); 191 sc->sc_wdog.smw_cookie = sc; 192 sc->sc_wdog.smw_setmode = mvsoctmr_wdog_setmode; 193 sc->sc_wdog.smw_tickle = mvsoctmr_wdog_tickle; 194 sc->sc_wdog.smw_period = MVSOC_WDOG_MAX_PERIOD; 195 196 if (sysmon_wdog_register(&sc->sc_wdog) != 0) 197 aprint_error_dev(self, 198 "unable to register watchdog with sysmon\n"); 199} 200 201/* 202 * clockhandler: 203 * 204 * Handle the hardclock interrupt. 205 */ 206static int 207clockhandler(void *arg) 208{ 209 struct clockframe *frame = arg; 210 211#if defined(ARMADAXP) 212 KASSERT(mvsoctmr_sc != NULL); 213 214 if (mvsoctmr_sc->sc_flags & TMR_FLAGS_NOBRIDGE) 215 /* Acknowledge all timers-related interrupts */ 216 bus_space_write_4(mvsoctmr_sc->sc_iot, mvsoctmr_sc->sc_ioh, 217 MVSOCTMR_TESR, 0x0); 218#endif 219 220 hardclock(frame); 221 222 return 1; 223} 224 225/* 226 * setstatclockrate: 227 * 228 * Set the rate of the statistics clock. 229 */ 230/* ARGSUSED */ 231void 232setstatclockrate(int newhz) 233{ 234} 235 236/* 237 * cpu_initclocks: 238 * 239 * Initialize the clock and get them going. 240 */ 241void 242cpu_initclocks(void) 243{ 244 struct mvsoctmr_softc *sc; 245 void *clock_ih; 246 const int en = 1, autoen = 1; 247 uint32_t timer0_tval; 248 249 sc = mvsoctmr_sc; 250 if (sc == NULL) 251 panic("cpu_initclocks: mvsoctmr not found"); 252 253 mvsoctmr_timecounter.tc_priv = sc; 254 mvsoctmr_timecounter.tc_frequency = mvsoctmr_freq; 255 256 timer0_tval = (mvsoctmr_freq * 2) / (u_long) hz; 257 timer0_tval = (timer0_tval / 2) + (timer0_tval & 1); 258 259 mvsoctmr_cntl(sc, MVSOCTMR_TIMER0, timer0_tval, en, autoen); 260 mvsoctmr_cntl(sc, MVSOCTMR_TIMER1, 0xffffffff, en, autoen); 261 262 if (sc->sc_flags & TMR_FLAGS_NOBRIDGE) { 263 /* 264 * Establishing timer interrupts is slightly different for 265 * Armada XP than for other supported SoCs from Marvell. 266 * Timer interrupt is no different from any other interrupt 267 * in Armada XP, so we use generic marvell_intr_establish(). 268 */ 269 clock_ih = marvell_intr_establish(sc->sc_irq, IPL_CLOCK, 270 clockhandler, NULL); 271 } else 272 clock_ih = mvsoc_bridge_intr_establish( 273 MVSOC_MLMB_MLMBI_CPUTIMER0INTREQ, IPL_CLOCK, clockhandler, 274 NULL); 275 if (clock_ih == NULL) 276 panic("cpu_initclocks: unable to register timer interrupt"); 277 278 tc_init(&mvsoctmr_timecounter); 279} 280 281void 282delay(unsigned int n) 283{ 284 struct mvsoctmr_softc *sc; 285 unsigned int cur_tick, initial_tick; 286 int remaining; 287 288 sc = mvsoctmr_sc; 289#ifdef DEBUG 290 if (sc == NULL) { 291 printf("%s: called before start mvsoctmr\n", __func__); 292 return; 293 } 294#endif 295 296 /* 297 * Read the counter first, so that the rest of the setup overhead is 298 * counted. 299 */ 300 initial_tick = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 301 MVSOCTMR_TIMER(MVSOCTMR_TIMER1)); 302 303 if (n <= UINT_MAX / mvsoctmr_freq) { 304 /* 305 * For unsigned arithmetic, division can be replaced with 306 * multiplication with the inverse and a shift. 307 */ 308 remaining = n * mvsoctmr_freq / 1000000; 309 } else { 310 /* 311 * This is a very long delay. 312 * Being slow here doesn't matter. 313 */ 314 remaining = (unsigned long long) n * mvsoctmr_freq / 1000000; 315 } 316 317 while (remaining > 0) { 318 cur_tick = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 319 MVSOCTMR_TIMER(MVSOCTMR_TIMER1)); 320 if (cur_tick > initial_tick) 321 remaining -= 0xffffffff - cur_tick + initial_tick; 322 else 323 remaining -= (initial_tick - cur_tick); 324 initial_tick = cur_tick; 325 } 326} 327 328static u_int 329mvsoctmr_get_timecount(struct timecounter *tc) 330{ 331 struct mvsoctmr_softc *sc = tc->tc_priv; 332 333 return 0xffffffff - bus_space_read_4(sc->sc_iot, sc->sc_ioh, 334 MVSOCTMR_TIMER(MVSOCTMR_TIMER1)); 335} 336 337static void 338mvsoctmr_cntl(struct mvsoctmr_softc *sc, int num, u_int ticks, int en, 339 int autoen) 340{ 341 uint32_t ctrl; 342 343 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_RELOAD(num), ticks); 344 345 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_TIMER(num), ticks); 346 347 ctrl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_CTCR); 348 if (en) 349 ctrl |= MVSOCTMR_CTCR_CPUTIMEREN(num); 350 else 351 ctrl &= ~MVSOCTMR_CTCR_CPUTIMEREN(num); 352 if (autoen) 353 ctrl |= MVSOCTMR_CTCR_CPUTIMERAUTO(num); 354 else 355 ctrl &= ~MVSOCTMR_CTCR_CPUTIMERAUTO(num); 356 if (sc->sc_flags & TMR_FLAGS_25MHZ) 357 /* Set timer and counter to 25MHz mode */ 358 ctrl |= MVSOCTMR_CTCR_25MHZEN(num); 359 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_CTCR, ctrl); 360} 361 362static int 363mvsoctmr_wdog_setmode(struct sysmon_wdog *smw) 364{ 365 struct mvsoctmr_softc *sc = smw->smw_cookie; 366 367 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 368 sc->sc_wdog_armed = 0; 369 mvsoctmr_cntl(sc, MVSOCTMR_WATCHDOG, 0xffffffff, 0, 0); 370 } else { 371 sc->sc_wdog_armed = 1; 372 if (smw->smw_period == WDOG_PERIOD_DEFAULT) 373 smw->smw_period = MVSOC_WDOG_MAX_PERIOD; 374 else if (smw->smw_period > MVSOC_WDOG_MAX_PERIOD || 375 smw->smw_period <= 0) 376 return (EOPNOTSUPP); 377 sc->sc_wdog_period = smw->smw_period * mvsoctmr_freq; 378 mvsoctmr_cntl(sc, MVSOCTMR_WATCHDOG, sc->sc_wdog_period, 1, 0); 379 } 380 381 return (0); 382} 383 384static int 385mvsoctmr_wdog_tickle(struct sysmon_wdog *smw) 386{ 387 struct mvsoctmr_softc *sc = smw->smw_cookie; 388 389 mvsoctmr_cntl(sc, MVSOCTMR_WATCHDOG, sc->sc_wdog_period, 1, 0); 390 391 return (0); 392} 393 394#ifdef DDB 395static void 396mvsoctmr_wdog_ddb_trap(int enter) 397{ 398 struct mvsoctmr_softc *sc = mvsoctmr_sc; 399 400 if (sc == NULL) 401 return; 402 403 if (sc->sc_wdog_armed) { 404 if (enter) 405 mvsoctmr_cntl(sc, MVSOCTMR_WATCHDOG, 0xffffffff, 0, 0); 406 else 407 mvsoctmr_cntl(sc, MVSOCTMR_WATCHDOG, 408 sc->sc_wdog_period, 1, 0); 409 } 410} 411#endif 412