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