1 1.11 rin /* $NetBSD: clock.c,v 1.11 2020/05/29 12:30:40 rin Exp $ */ 2 1.1 macallan 3 1.1 macallan /*- 4 1.1 macallan * Copyright (c) 2014 Michael Lorenz 5 1.1 macallan * All rights reserved. 6 1.1 macallan * 7 1.1 macallan * Redistribution and use in source and binary forms, with or without 8 1.1 macallan * modification, are permitted provided that the following conditions 9 1.1 macallan * are met: 10 1.1 macallan * 1. Redistributions of source code must retain the above copyright 11 1.1 macallan * notice, this list of conditions and the following disclaimer. 12 1.1 macallan * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 macallan * notice, this list of conditions and the following disclaimer in the 14 1.1 macallan * documentation and/or other materials provided with the distribution. 15 1.1 macallan * 16 1.1 macallan * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 macallan * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 macallan * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 macallan * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 macallan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 macallan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 macallan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 macallan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 macallan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 macallan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 macallan * POSSIBILITY OF SUCH DAMAGE. 27 1.1 macallan */ 28 1.1 macallan 29 1.1 macallan #include <sys/cdefs.h> 30 1.11 rin __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.11 2020/05/29 12:30:40 rin Exp $"); 31 1.7 macallan 32 1.7 macallan #include "opt_multiprocessor.h" 33 1.1 macallan 34 1.1 macallan #include <sys/param.h> 35 1.1 macallan #include <sys/cpu.h> 36 1.1 macallan #include <sys/device.h> 37 1.1 macallan #include <sys/kernel.h> 38 1.1 macallan #include <sys/systm.h> 39 1.2 macallan #include <sys/timetc.h> 40 1.2 macallan 41 1.10 skrll #include <mips/ingenic/ingenic_var.h> 42 1.1 macallan #include <mips/ingenic/ingenic_regs.h> 43 1.1 macallan 44 1.3 macallan #include "opt_ingenic.h" 45 1.3 macallan 46 1.2 macallan extern void ingenic_puts(const char *); 47 1.2 macallan 48 1.8 skrll void ingenic_clockintr(struct clockframe *); 49 1.2 macallan 50 1.2 macallan static u_int 51 1.2 macallan ingenic_count_read(struct timecounter *tc) 52 1.2 macallan { 53 1.2 macallan return readreg(JZ_OST_CNT_LO); 54 1.2 macallan } 55 1.2 macallan 56 1.1 macallan void 57 1.1 macallan cpu_initclocks(void) 58 1.1 macallan { 59 1.2 macallan struct cpu_info * const ci = curcpu(); 60 1.2 macallan uint32_t cnt; 61 1.1 macallan 62 1.2 macallan static struct timecounter tc = { 63 1.11 rin .tc_get_timecount = ingenic_count_read, 64 1.11 rin .tc_counter_mask = ~0u, 65 1.11 rin .tc_frequency = 12000000, 66 1.11 rin .tc_name = "Ingenic OS timer", 67 1.11 rin .tc_quality = 100, 68 1.2 macallan }; 69 1.2 macallan 70 1.2 macallan curcpu()->ci_cctr_freq = tc.tc_frequency; 71 1.2 macallan 72 1.2 macallan tc_init(&tc); 73 1.2 macallan 74 1.2 macallan printf("starting timer interrupt...\n"); 75 1.2 macallan /* start the timer interrupt */ 76 1.2 macallan cnt = readreg(JZ_OST_CNT_LO); 77 1.2 macallan ci->ci_next_cp0_clk_intr = cnt + ci->ci_cycles_per_hz; 78 1.4 macallan writereg(JZ_TC_TFCR, TFR_OSTFLAG); 79 1.2 macallan writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr); 80 1.2 macallan /* 81 1.2 macallan * XXX 82 1.2 macallan * We can use OST or one of the regular timers to generate the 100hz 83 1.2 macallan * interrupt. OST interrupts need to be rescheduled every time and by 84 1.2 macallan * only one core, the regular timer can be programmed to fire every 85 1.2 macallan * 10ms without rescheduling and we'd still use the OST as time base. 86 1.2 macallan * OST is supposed to fire on INT2 although I haven't been able to get 87 1.2 macallan * that to work yet ( all I get is INT0 which is for hardware interrupts 88 1.2 macallan * in general ) 89 1.2 macallan * So if we can get OST to fire on INT2 we can just block INT0 on core1 90 1.9 skrll * and have a timer interrupt on both cores, if not the regular timer 91 1.2 macallan * would be more convenient but we'd have to shoot an IPI to core1 on 92 1.2 macallan * every tick. 93 1.2 macallan * For now, use OST and hope we'll figure out how to make it fire on 94 1.2 macallan * INT2. 95 1.2 macallan */ 96 1.4 macallan #ifdef USE_OST 97 1.2 macallan writereg(JZ_TC_TMCR, TFR_OSTFLAG); 98 1.2 macallan #else 99 1.2 macallan writereg(JZ_TC_TECR, TESR_TCST5); /* disable timer 5 */ 100 1.2 macallan writereg(JZ_TC_TCNT(5), 0); 101 1.2 macallan writereg(JZ_TC_TDFR(5), 30000); /* 10ms at 48MHz / 16 */ 102 1.2 macallan writereg(JZ_TC_TDHR(5), 60000); /* not reached */ 103 1.2 macallan writereg(JZ_TC_TCSR(5), TCSR_EXT_EN| TCSR_DIV_16); 104 1.2 macallan writereg(JZ_TC_TMCR, TFR_FFLAG5); 105 1.2 macallan writereg(JZ_TC_TFCR, TFR_FFLAG5); 106 1.2 macallan writereg(JZ_TC_TESR, TESR_TCST5); /* enable timer 5 */ 107 1.2 macallan #endif 108 1.3 macallan 109 1.3 macallan #ifdef INGENIC_CLOCK_DEBUG 110 1.2 macallan printf("INTC %08x %08x\n", readreg(JZ_ICSR0), readreg(JZ_ICSR1)); 111 1.4 macallan printf("ICMR0 %08x\n", readreg(JZ_ICMR0)); 112 1.4 macallan #endif 113 1.2 macallan writereg(JZ_ICMCR0, 0x0c000000); /* TCU2, OST */ 114 1.2 macallan spl0(); 115 1.3 macallan #ifdef INGENIC_CLOCK_DEBUG 116 1.2 macallan printf("TFR: %08x\n", readreg(JZ_TC_TFR)); 117 1.2 macallan printf("TMR: %08x\n", readreg(JZ_TC_TMR)); 118 1.2 macallan printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5))); 119 1.2 macallan printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0)); 120 1.2 macallan printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0)); 121 1.2 macallan delay(100000); 122 1.2 macallan printf("TFR: %08x\n", readreg(JZ_TC_TFR)); 123 1.2 macallan printf("TMR: %08x\n", readreg(JZ_TC_TMR)); 124 1.2 macallan printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5))); 125 1.2 macallan printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0)); 126 1.2 macallan printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0)); 127 1.2 macallan printf("TFR: %08x\n", readreg(JZ_TC_TFR)); 128 1.2 macallan printf("TMR: %08x\n", readreg(JZ_TC_TMR)); 129 1.2 macallan printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5))); 130 1.2 macallan printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0)); 131 1.2 macallan printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0)); 132 1.9 skrll 133 1.2 macallan printf("INTC %08x %08x\n", readreg(JZ_ICSR0), readreg(JZ_ICSR1)); 134 1.2 macallan delay(3000000); 135 1.7 macallan printf("%s %d\n", __func__, MFC0(12, 3)); 136 1.7 macallan printf("%s %08x\n", __func__, MFC0(12, 4)); 137 1.3 macallan #endif 138 1.1 macallan } 139 1.1 macallan 140 1.1 macallan /* shamelessly stolen from mips3_clock.c */ 141 1.1 macallan void 142 1.1 macallan delay(int n) 143 1.1 macallan { 144 1.1 macallan u_long divisor_delay; 145 1.1 macallan uint32_t cur, last, delta, usecs; 146 1.1 macallan 147 1.1 macallan last = readreg(JZ_OST_CNT_LO); 148 1.1 macallan delta = usecs = 0; 149 1.1 macallan 150 1.1 macallan divisor_delay = curcpu()->ci_divisor_delay; 151 1.1 macallan if (divisor_delay == 0) { 152 1.1 macallan /* 153 1.1 macallan * Frequency values in curcpu() are not initialized. 154 1.1 macallan * Assume faster frequency since longer delays are harmless. 155 1.1 macallan * Note CPU_MIPS_DOUBLE_COUNT is ignored here. 156 1.1 macallan */ 157 1.1 macallan #define FAST_FREQ (300 * 1000 * 1000) /* fast enough? */ 158 1.1 macallan divisor_delay = FAST_FREQ / (1000 * 1000); 159 1.1 macallan } 160 1.1 macallan while (n > usecs) { 161 1.1 macallan cur = readreg(JZ_OST_CNT_LO); 162 1.1 macallan 163 1.1 macallan /* 164 1.1 macallan * We setup the OS timer to always counts upto UINT32_MAX, 165 1.1 macallan * so no need to check wrapped around case. 166 1.1 macallan */ 167 1.1 macallan delta += (cur - last); 168 1.1 macallan 169 1.1 macallan last = cur; 170 1.1 macallan 171 1.1 macallan while (delta >= divisor_delay) { 172 1.1 macallan /* 173 1.1 macallan * delta is not so larger than divisor_delay here, 174 1.1 macallan * and using DIV/DIVU ops could be much slower. 175 1.1 macallan * (though longer delay may be harmless) 176 1.1 macallan */ 177 1.1 macallan usecs++; 178 1.1 macallan delta -= divisor_delay; 179 1.1 macallan } 180 1.1 macallan } 181 1.1 macallan } 182 1.1 macallan 183 1.1 macallan void 184 1.1 macallan setstatclockrate(int r) 185 1.1 macallan { 186 1.2 macallan /* we could just use another timer channel here */ 187 1.2 macallan } 188 1.2 macallan 189 1.3 macallan #ifdef INGENIC_CLOCK_DEBUG 190 1.2 macallan int cnt = 99; 191 1.3 macallan #endif 192 1.2 macallan 193 1.2 macallan void 194 1.8 skrll ingenic_clockintr(struct clockframe *cf) 195 1.2 macallan { 196 1.7 macallan int s = splsched(); 197 1.2 macallan struct cpu_info * const ci = curcpu(); 198 1.4 macallan #ifdef USE_OST 199 1.2 macallan uint32_t new_cnt; 200 1.4 macallan #endif 201 1.2 macallan 202 1.2 macallan /* clear flags */ 203 1.2 macallan writereg(JZ_TC_TFCR, TFR_OSTFLAG); 204 1.2 macallan 205 1.2 macallan ci->ci_next_cp0_clk_intr += (uint32_t)(ci->ci_cycles_per_hz & 0xffffffff); 206 1.4 macallan #ifdef USE_OST 207 1.2 macallan writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr); 208 1.2 macallan 209 1.2 macallan /* Check for lost clock interrupts */ 210 1.2 macallan new_cnt = readreg(JZ_OST_CNT_LO); 211 1.2 macallan 212 1.9 skrll /* 213 1.9 skrll * Missed one or more clock interrupts, so let's start 214 1.2 macallan * counting again from the current value. 215 1.2 macallan */ 216 1.2 macallan if ((ci->ci_next_cp0_clk_intr - new_cnt) & 0x80000000) { 217 1.2 macallan 218 1.2 macallan ci->ci_next_cp0_clk_intr = new_cnt + curcpu()->ci_cycles_per_hz; 219 1.2 macallan writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr); 220 1.2 macallan curcpu()->ci_ev_count_compare_missed.ev_count++; 221 1.2 macallan } 222 1.4 macallan writereg(JZ_TC_TFCR, TFR_OSTFLAG); 223 1.4 macallan #else 224 1.4 macallan writereg(JZ_TC_TFCR, TFR_FFLAG5); 225 1.4 macallan #endif 226 1.2 macallan 227 1.3 macallan #ifdef INGENIC_CLOCK_DEBUG 228 1.2 macallan cnt++; 229 1.2 macallan if (cnt == 100) { 230 1.2 macallan cnt = 0; 231 1.2 macallan ingenic_puts("+"); 232 1.2 macallan } 233 1.3 macallan #endif 234 1.7 macallan #ifdef MULTIPROCESSOR 235 1.7 macallan /* 236 1.7 macallan * XXX 237 1.7 macallan * needs to take the IPI lock and ping all online CPUs, not just core 1 238 1.7 macallan */ 239 1.10 skrll mips_cp0_corembox_write(1, 1 << IPI_CLOCK); 240 1.7 macallan #endif 241 1.8 skrll hardclock(cf); 242 1.7 macallan splx(s); 243 1.1 macallan } 244