1*5dfdc49fSandvar /* $NetBSD: iq80310_timer.c,v 1.24 2024/07/20 20:36:33 andvar Exp $ */ 2acf9a688Sthorpej 3acf9a688Sthorpej /* 4727b4699Sthorpej * Copyright (c) 2001, 2002 Wasabi Systems, Inc. 5acf9a688Sthorpej * All rights reserved. 6acf9a688Sthorpej * 7acf9a688Sthorpej * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8acf9a688Sthorpej * 9acf9a688Sthorpej * Redistribution and use in source and binary forms, with or without 10acf9a688Sthorpej * modification, are permitted provided that the following conditions 11acf9a688Sthorpej * are met: 12acf9a688Sthorpej * 1. Redistributions of source code must retain the above copyright 13acf9a688Sthorpej * notice, this list of conditions and the following disclaimer. 14acf9a688Sthorpej * 2. Redistributions in binary form must reproduce the above copyright 15acf9a688Sthorpej * notice, this list of conditions and the following disclaimer in the 16acf9a688Sthorpej * documentation and/or other materials provided with the distribution. 17acf9a688Sthorpej * 3. All advertising materials mentioning features or use of this software 18acf9a688Sthorpej * must display the following acknowledgement: 19acf9a688Sthorpej * This product includes software developed for the NetBSD Project by 20acf9a688Sthorpej * Wasabi Systems, Inc. 21acf9a688Sthorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22acf9a688Sthorpej * or promote products derived from this software without specific prior 23acf9a688Sthorpej * written permission. 24acf9a688Sthorpej * 25acf9a688Sthorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26acf9a688Sthorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27acf9a688Sthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28acf9a688Sthorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29acf9a688Sthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30acf9a688Sthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31acf9a688Sthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32acf9a688Sthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33acf9a688Sthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34acf9a688Sthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35acf9a688Sthorpej * POSSIBILITY OF SUCH DAMAGE. 36acf9a688Sthorpej */ 37acf9a688Sthorpej 38acf9a688Sthorpej /* 39acf9a688Sthorpej * Timer/clock support for the Intel IQ80310. 40acf9a688Sthorpej * 41acf9a688Sthorpej * The IQ80310 has a 22-bit reloadable timer implemented in the CPLD. 42acf9a688Sthorpej * We use it to provide a hardclock interrupt. There is no RTC on 43acf9a688Sthorpej * the IQ80310. 44acf9a688Sthorpej * 45acf9a688Sthorpej * The timer uses the SPCI clock. The timer uses the 33MHz clock by 46acf9a688Sthorpej * reading the SPCI_66EN signal and dividing the clock if necessary. 47acf9a688Sthorpej */ 48acf9a688Sthorpej 4908716eaeSlukem #include <sys/cdefs.h> 50*5dfdc49fSandvar __KERNEL_RCSID(0, "$NetBSD: iq80310_timer.c,v 1.24 2024/07/20 20:36:33 andvar Exp $"); 5108716eaeSlukem 52acf9a688Sthorpej #include <sys/param.h> 53acf9a688Sthorpej #include <sys/systm.h> 54acf9a688Sthorpej #include <sys/kernel.h> 554e56cdd2Sjoerg #include <sys/atomic.h> 56acf9a688Sthorpej #include <sys/time.h> 574e56cdd2Sjoerg #include <sys/timetc.h> 58acf9a688Sthorpej 59ca601a77Sthorpej #include <dev/clock_subr.h> 60ca601a77Sthorpej 61fea15f47Sdyoung #include <sys/bus.h> 6296959902Sthorpej #include <arm/cpufunc.h> 63acf9a688Sthorpej 64acf9a688Sthorpej #include <evbarm/iq80310/iq80310reg.h> 65acf9a688Sthorpej #include <evbarm/iq80310/iq80310var.h> 66acf9a688Sthorpej #include <evbarm/iq80310/obiovar.h> 67acf9a688Sthorpej 68727b4699Sthorpej /* 69727b4699Sthorpej * Some IQ80310-based designs have fewer bits in the timer counter. 70727b4699Sthorpej * Deal with them here. 71727b4699Sthorpej */ 72727b4699Sthorpej #if defined(IOP310_TEAMASA_NPWR) 73a9e4c1a9Sthorpej #define COUNTER_MASK 0x0007ffff 74727b4699Sthorpej #else /* Default to stock IQ80310 */ 75a9e4c1a9Sthorpej #define COUNTER_MASK 0x003fffff 76727b4699Sthorpej #endif /* list of IQ80310-based designs */ 77727b4699Sthorpej 78acf9a688Sthorpej #define COUNTS_PER_SEC 33000000 /* 33MHz */ 79acf9a688Sthorpej #define COUNTS_PER_USEC (COUNTS_PER_SEC / 1000000) 80acf9a688Sthorpej 81acf9a688Sthorpej static void *clock_ih; 82acf9a688Sthorpej 83acf9a688Sthorpej static uint32_t counts_per_hz; 84acf9a688Sthorpej 854e56cdd2Sjoerg static u_int iq80310_get_timecount(struct timecounter *); 864e56cdd2Sjoerg 874e56cdd2Sjoerg static struct timecounter iq80310_timecounter = { 88482eef70Srin .tc_get_timecount = iq80310_get_timecount, 89482eef70Srin .tc_counter_mask = 0xffffffff, 90482eef70Srin .tc_frequency = COUNTS_PER_SEC, 91482eef70Srin .tc_name = "iq80310", 92482eef70Srin .tc_quality = 100, 934e56cdd2Sjoerg }; 944e56cdd2Sjoerg 954e56cdd2Sjoerg static volatile uint32_t iq80310_base; 964e56cdd2Sjoerg 97acf9a688Sthorpej int clockhandler(void *); 98acf9a688Sthorpej 995f1c88d7Sperry static inline void 100acf9a688Sthorpej timer_enable(uint8_t bit) 101acf9a688Sthorpej { 102acf9a688Sthorpej 1030ea59754Sthorpej CPLD_WRITE(IQ80310_TIMER_ENABLE, 1040ea59754Sthorpej CPLD_READ(IQ80310_TIMER_ENABLE) | bit); 105acf9a688Sthorpej } 106acf9a688Sthorpej 1075f1c88d7Sperry static inline void 108acf9a688Sthorpej timer_disable(uint8_t bit) 109acf9a688Sthorpej { 110acf9a688Sthorpej 1110ea59754Sthorpej CPLD_WRITE(IQ80310_TIMER_ENABLE, 1120ea59754Sthorpej CPLD_READ(IQ80310_TIMER_ENABLE) & ~bit); 113acf9a688Sthorpej } 114acf9a688Sthorpej 1155f1c88d7Sperry static inline uint32_t 116acf9a688Sthorpej timer_read(void) 117acf9a688Sthorpej { 118f30c8426Sthorpej uint32_t rv; 119a9e4c1a9Sthorpej uint8_t la0, la1, la2, la3; 120acf9a688Sthorpej 121acf9a688Sthorpej /* 122acf9a688Sthorpej * First read latches count. 123acf9a688Sthorpej * 124*5dfdc49fSandvar * From RedBoot: hardware bug that causes invalid counts to be 125acf9a688Sthorpej * latched. The loop appears to work around the problem. 126acf9a688Sthorpej */ 127acf9a688Sthorpej do { 128a9e4c1a9Sthorpej la0 = CPLD_READ(IQ80310_TIMER_LA0); 129a9e4c1a9Sthorpej } while (la0 == 0); 130a9e4c1a9Sthorpej la1 = CPLD_READ(IQ80310_TIMER_LA1); 131a9e4c1a9Sthorpej la2 = CPLD_READ(IQ80310_TIMER_LA2); 132a9e4c1a9Sthorpej la3 = CPLD_READ(IQ80310_TIMER_LA3); 133acf9a688Sthorpej 134a9e4c1a9Sthorpej rv = ((la0 & 0x40) >> 1) | (la0 & 0x1f); 135a9e4c1a9Sthorpej rv |= (((la1 & 0x40) >> 1) | (la1 & 0x1f)) << 6; 136a9e4c1a9Sthorpej rv |= (((la2 & 0x40) >> 1) | (la2 & 0x1f)) << 12; 137a9e4c1a9Sthorpej rv |= (la3 & 0x0f) << 18; 138acf9a688Sthorpej 139f30c8426Sthorpej return (rv); 140acf9a688Sthorpej } 141acf9a688Sthorpej 1425f1c88d7Sperry static inline void 143acf9a688Sthorpej timer_write(uint32_t x) 144acf9a688Sthorpej { 145acf9a688Sthorpej 146727b4699Sthorpej KASSERT((x & COUNTER_MASK) == x); 147727b4699Sthorpej 1480ea59754Sthorpej CPLD_WRITE(IQ80310_TIMER_LA0, x & 0xff); 1490ea59754Sthorpej CPLD_WRITE(IQ80310_TIMER_LA1, (x >> 8) & 0xff); 1500ea59754Sthorpej CPLD_WRITE(IQ80310_TIMER_LA2, (x >> 16) & 0x3f); 151acf9a688Sthorpej } 152acf9a688Sthorpej 153acf9a688Sthorpej /* 154acf9a688Sthorpej * iq80310_calibrate_delay: 155acf9a688Sthorpej * 156acf9a688Sthorpej * Calibrate the delay loop. 157acf9a688Sthorpej */ 158acf9a688Sthorpej void 159acf9a688Sthorpej iq80310_calibrate_delay(void) 160acf9a688Sthorpej { 161acf9a688Sthorpej 162acf9a688Sthorpej /* 163acf9a688Sthorpej * We'll use the CPLD timer for delay(), as well. We go 164acf9a688Sthorpej * ahead and start it up now, just don't enable interrupts 165acf9a688Sthorpej * until cpu_initclocks(). 166acf9a688Sthorpej * 167acf9a688Sthorpej * Just use hz=100 for now -- we'll adjust it, if necessary, 168acf9a688Sthorpej * in cpu_initclocks(). 169acf9a688Sthorpej */ 170acf9a688Sthorpej counts_per_hz = COUNTS_PER_SEC / 100; 171acf9a688Sthorpej 172acf9a688Sthorpej timer_disable(TIMER_ENABLE_INTEN); 173acf9a688Sthorpej timer_disable(TIMER_ENABLE_EN); 174acf9a688Sthorpej 175acf9a688Sthorpej timer_write(counts_per_hz); 176acf9a688Sthorpej 177acf9a688Sthorpej timer_enable(TIMER_ENABLE_EN); 178acf9a688Sthorpej } 179acf9a688Sthorpej 180acf9a688Sthorpej /* 181acf9a688Sthorpej * cpu_initclocks: 182acf9a688Sthorpej * 183acf9a688Sthorpej * Initialize the clock and get them going. 184acf9a688Sthorpej */ 185acf9a688Sthorpej void 186acf9a688Sthorpej cpu_initclocks(void) 187acf9a688Sthorpej { 188acf9a688Sthorpej u_int oldirqstate; 189acf9a688Sthorpej 190acf9a688Sthorpej if (hz < 50 || COUNTS_PER_SEC % hz) { 191acf9a688Sthorpej printf("Cannot get %d Hz clock; using 100 Hz\n", hz); 192acf9a688Sthorpej hz = 100; 193d8415403Sthorpej } 194acf9a688Sthorpej 195acf9a688Sthorpej /* 196acf9a688Sthorpej * We only have one timer available; stathz and profhz are 197d8415403Sthorpej * always left as 0 (the upper-layer clock code deals with 198d8415403Sthorpej * this situation). 199acf9a688Sthorpej */ 200acf9a688Sthorpej if (stathz != 0) 201d8415403Sthorpej printf("Cannot get %d Hz statclock\n", stathz); 202d8415403Sthorpej stathz = 0; 203acf9a688Sthorpej 204acf9a688Sthorpej if (profhz != 0) 205d8415403Sthorpej printf("Cannot get %d Hz profclock\n", profhz); 206d8415403Sthorpej profhz = 0; 207acf9a688Sthorpej 208acf9a688Sthorpej /* Report the clock frequency. */ 209acf9a688Sthorpej printf("clock: hz=%d stathz=%d profhz=%d\n", hz, stathz, profhz); 210acf9a688Sthorpej 211acf9a688Sthorpej /* Hook up the clock interrupt handler. */ 212acf9a688Sthorpej clock_ih = iq80310_intr_establish(XINT3_IRQ(XINT3_TIMER), IPL_CLOCK, 213acf9a688Sthorpej clockhandler, NULL); 214acf9a688Sthorpej if (clock_ih == NULL) 215acf9a688Sthorpej panic("cpu_initclocks: unable to register timer interrupt"); 216acf9a688Sthorpej 217acf9a688Sthorpej /* Set up the new clock parameters. */ 218acf9a688Sthorpej oldirqstate = disable_interrupts(I32_bit); 219acf9a688Sthorpej 220acf9a688Sthorpej timer_disable(TIMER_ENABLE_EN); 221acf9a688Sthorpej 222acf9a688Sthorpej counts_per_hz = COUNTS_PER_SEC / hz; 223acf9a688Sthorpej timer_write(counts_per_hz); 224acf9a688Sthorpej 225acf9a688Sthorpej timer_enable(TIMER_ENABLE_INTEN); 226acf9a688Sthorpej timer_enable(TIMER_ENABLE_EN); 227acf9a688Sthorpej 228acf9a688Sthorpej restore_interrupts(oldirqstate); 2294e56cdd2Sjoerg 2304e56cdd2Sjoerg tc_init(&iq80310_timecounter); 231acf9a688Sthorpej } 232acf9a688Sthorpej 233acf9a688Sthorpej /* 234acf9a688Sthorpej * setstatclockrate: 235acf9a688Sthorpej * 236acf9a688Sthorpej * Set the rate of the statistics clock. 237acf9a688Sthorpej * 238acf9a688Sthorpej * We assume that hz is either stathz or profhz, and that neither 239acf9a688Sthorpej * will change after being set by cpu_initclocks(). We could 240acf9a688Sthorpej * recalculate the intervals here, but that would be a pain. 241acf9a688Sthorpej */ 242acf9a688Sthorpej void 243fb96dc83She setstatclockrate(int newhz) 244acf9a688Sthorpej { 245acf9a688Sthorpej 246acf9a688Sthorpej /* 247acf9a688Sthorpej * Nothing to do, here; we can't change the statclock 248acf9a688Sthorpej * rate on the IQ80310. 249acf9a688Sthorpej */ 250acf9a688Sthorpej } 251acf9a688Sthorpej 2524e56cdd2Sjoerg static u_int 2534e56cdd2Sjoerg iq80310_get_timecount(struct timecounter *tc) 254acf9a688Sthorpej { 2554e56cdd2Sjoerg u_int oldirqstate, base, counter; 256acf9a688Sthorpej 257acf9a688Sthorpej oldirqstate = disable_interrupts(I32_bit); 2584e56cdd2Sjoerg base = iq80310_base; 2594e56cdd2Sjoerg counter = timer_read(); 260acf9a688Sthorpej restore_interrupts(oldirqstate); 2614e56cdd2Sjoerg 2624e56cdd2Sjoerg return base + counter; 263acf9a688Sthorpej } 264acf9a688Sthorpej 265acf9a688Sthorpej /* 266acf9a688Sthorpej * delay: 267acf9a688Sthorpej * 268acf9a688Sthorpej * Delay for at least N microseconds. 269acf9a688Sthorpej */ 270acf9a688Sthorpej void 271acf9a688Sthorpej delay(u_int n) 272acf9a688Sthorpej { 273acf9a688Sthorpej uint32_t cur, last, delta, usecs; 274acf9a688Sthorpej 275acf9a688Sthorpej /* 276acf9a688Sthorpej * This works by polling the timer and counting the 277acf9a688Sthorpej * number of microseconds that go by. 278acf9a688Sthorpej */ 279acf9a688Sthorpej last = timer_read(); 280acf9a688Sthorpej delta = usecs = 0; 281acf9a688Sthorpej 282f30c8426Sthorpej while (n > usecs) { 283acf9a688Sthorpej cur = timer_read(); 284acf9a688Sthorpej 285acf9a688Sthorpej /* Check to see if the timer has wrapped around. */ 286acf9a688Sthorpej if (cur < last) 287f30c8426Sthorpej delta += ((counts_per_hz - last) + cur); 288acf9a688Sthorpej else 289f30c8426Sthorpej delta += (cur - last); 290acf9a688Sthorpej 291acf9a688Sthorpej last = cur; 292acf9a688Sthorpej 293acf9a688Sthorpej if (delta >= COUNTS_PER_USEC) { 294acf9a688Sthorpej usecs += delta / COUNTS_PER_USEC; 295acf9a688Sthorpej delta %= COUNTS_PER_USEC; 296acf9a688Sthorpej } 297acf9a688Sthorpej } 298acf9a688Sthorpej } 299acf9a688Sthorpej 300acf9a688Sthorpej /* 301acf9a688Sthorpej * clockhandler: 302acf9a688Sthorpej * 303acf9a688Sthorpej * Handle the hardclock interrupt. 304acf9a688Sthorpej */ 305acf9a688Sthorpej int 306acf9a688Sthorpej clockhandler(void *arg) 307acf9a688Sthorpej { 308acf9a688Sthorpej struct clockframe *frame = arg; 309acf9a688Sthorpej 310acf9a688Sthorpej timer_disable(TIMER_ENABLE_INTEN); 311acf9a688Sthorpej timer_enable(TIMER_ENABLE_INTEN); 312acf9a688Sthorpej 3134e56cdd2Sjoerg atomic_add_32(&iq80310_base, counts_per_hz); 3144e56cdd2Sjoerg 315acf9a688Sthorpej hardclock(frame); 316acf9a688Sthorpej 317755369ecSthorpej /* 318755369ecSthorpej * Don't run the snake on IOP310-based systems that 319755369ecSthorpej * don't have the 7-segment display. 320755369ecSthorpej */ 321755369ecSthorpej #if !defined(IOP310_TEAMASA_NPWR) 322755369ecSthorpej { 323755369ecSthorpej static int snakefreq; 324755369ecSthorpej 325a7cfcd87Sthorpej if ((snakefreq++ & 15) == 0) 326a7cfcd87Sthorpej iq80310_7seg_snake(); 327755369ecSthorpej } 328755369ecSthorpej #endif 329a7cfcd87Sthorpej 330acf9a688Sthorpej return (1); 331acf9a688Sthorpej } 332