Home | History | Annotate | Line # | Download | only in ingenic
      1 /*	$NetBSD: clock.c,v 1.11 2020/05/29 12:30:40 rin 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.11 2020/05/29 12:30:40 rin Exp $");
     31 
     32 #include "opt_multiprocessor.h"
     33 
     34 #include <sys/param.h>
     35 #include <sys/cpu.h>
     36 #include <sys/device.h>
     37 #include <sys/kernel.h>
     38 #include <sys/systm.h>
     39 #include <sys/timetc.h>
     40 
     41 #include <mips/ingenic/ingenic_var.h>
     42 #include <mips/ingenic/ingenic_regs.h>
     43 
     44 #include "opt_ingenic.h"
     45 
     46 extern void ingenic_puts(const char *);
     47 
     48 void ingenic_clockintr(struct clockframe *);
     49 
     50 static u_int
     51 ingenic_count_read(struct timecounter *tc)
     52 {
     53 	return readreg(JZ_OST_CNT_LO);
     54 }
     55 
     56 void
     57 cpu_initclocks(void)
     58 {
     59 	struct cpu_info * const ci = curcpu();
     60 	uint32_t cnt;
     61 
     62 	static struct timecounter tc =  {
     63 		.tc_get_timecount = ingenic_count_read,
     64 		.tc_counter_mask = ~0u,
     65 		.tc_frequency = 12000000,
     66 		.tc_name = "Ingenic OS timer",
     67 		.tc_quality = 100,
     68 	};
     69 
     70 	curcpu()->ci_cctr_freq = tc.tc_frequency;
     71 
     72 	tc_init(&tc);
     73 
     74 	printf("starting timer interrupt...\n");
     75 	/* start the timer interrupt */
     76 	cnt = readreg(JZ_OST_CNT_LO);
     77 	ci->ci_next_cp0_clk_intr = cnt + ci->ci_cycles_per_hz;
     78 	writereg(JZ_TC_TFCR, TFR_OSTFLAG);
     79 	writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
     80 	/*
     81 	 * XXX
     82 	 * We can use OST or one of the regular timers to generate the 100hz
     83 	 * interrupt. OST interrupts need to be rescheduled every time and by
     84 	 * only one core, the regular timer can be programmed to fire every
     85 	 * 10ms without rescheduling and we'd still use the OST as time base.
     86 	 * OST is supposed to fire on INT2 although I haven't been able to get
     87 	 * that to work yet ( all I get is INT0 which is for hardware interrupts
     88 	 * in general )
     89 	 * So if we can get OST to fire on INT2 we can just block INT0 on core1
     90 	 * and have a timer interrupt on both cores, if not the regular timer
     91 	 * would be more convenient but we'd have to shoot an IPI to core1 on
     92 	 * every tick.
     93 	 * For now, use OST and hope we'll figure out how to make it fire on
     94 	 * INT2.
     95 	 */
     96 #ifdef USE_OST
     97 	writereg(JZ_TC_TMCR, TFR_OSTFLAG);
     98 #else
     99 	writereg(JZ_TC_TECR, TESR_TCST5);	/* disable timer 5 */
    100 	writereg(JZ_TC_TCNT(5), 0);
    101 	writereg(JZ_TC_TDFR(5), 30000);	/* 10ms at 48MHz / 16 */
    102 	writereg(JZ_TC_TDHR(5), 60000);	/* not reached */
    103 	writereg(JZ_TC_TCSR(5), TCSR_EXT_EN| TCSR_DIV_16);
    104 	writereg(JZ_TC_TMCR, TFR_FFLAG5);
    105 	writereg(JZ_TC_TFCR, TFR_FFLAG5);
    106 	writereg(JZ_TC_TESR, TESR_TCST5);	/* enable timer 5 */
    107 #endif
    108 
    109 #ifdef INGENIC_CLOCK_DEBUG
    110 	printf("INTC %08x %08x\n", readreg(JZ_ICSR0), readreg(JZ_ICSR1));
    111 	printf("ICMR0 %08x\n", readreg(JZ_ICMR0));
    112 #endif
    113 	writereg(JZ_ICMCR0, 0x0c000000); /* TCU2, OST */
    114 	spl0();
    115 #ifdef INGENIC_CLOCK_DEBUG
    116 	printf("TFR: %08x\n", readreg(JZ_TC_TFR));
    117 	printf("TMR: %08x\n", readreg(JZ_TC_TMR));
    118 	printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5)));
    119 	printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0));
    120 	printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0));
    121 	delay(100000);
    122 	printf("TFR: %08x\n", readreg(JZ_TC_TFR));
    123 	printf("TMR: %08x\n", readreg(JZ_TC_TMR));
    124 	printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5)));
    125 	printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0));
    126 	printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0));
    127 	printf("TFR: %08x\n", readreg(JZ_TC_TFR));
    128 	printf("TMR: %08x\n", readreg(JZ_TC_TMR));
    129 	printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5)));
    130 	printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0));
    131 	printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0));
    132 
    133 	printf("INTC %08x %08x\n", readreg(JZ_ICSR0), readreg(JZ_ICSR1));
    134 	delay(3000000);
    135 	printf("%s %d\n", __func__, MFC0(12, 3));
    136 	printf("%s %08x\n", __func__, MFC0(12, 4));
    137 #endif
    138 }
    139 
    140 /* shamelessly stolen from mips3_clock.c */
    141 void
    142 delay(int n)
    143 {
    144 	u_long divisor_delay;
    145 	uint32_t cur, last, delta, usecs;
    146 
    147 	last = readreg(JZ_OST_CNT_LO);
    148 	delta = usecs = 0;
    149 
    150 	divisor_delay = curcpu()->ci_divisor_delay;
    151 	if (divisor_delay == 0) {
    152 		/*
    153 		 * Frequency values in curcpu() are not initialized.
    154 		 * Assume faster frequency since longer delays are harmless.
    155 		 * Note CPU_MIPS_DOUBLE_COUNT is ignored here.
    156 		 */
    157 #define FAST_FREQ	(300 * 1000 * 1000)	/* fast enough? */
    158 		divisor_delay = FAST_FREQ / (1000 * 1000);
    159 	}
    160 	while (n > usecs) {
    161 		cur = readreg(JZ_OST_CNT_LO);
    162 
    163 		/*
    164 		 * We setup the OS timer to always counts upto UINT32_MAX,
    165 		 * so no need to check wrapped around case.
    166 		 */
    167 		delta += (cur - last);
    168 
    169 		last = cur;
    170 
    171 		while (delta >= divisor_delay) {
    172 			/*
    173 			 * delta is not so larger than divisor_delay here,
    174 			 * and using DIV/DIVU ops could be much slower.
    175 			 * (though longer delay may be harmless)
    176 			 */
    177 			usecs++;
    178 			delta -= divisor_delay;
    179 		}
    180 	}
    181 }
    182 
    183 void
    184 setstatclockrate(int r)
    185 {
    186 	/* we could just use another timer channel here */
    187 }
    188 
    189 #ifdef INGENIC_CLOCK_DEBUG
    190 int cnt = 99;
    191 #endif
    192 
    193 void
    194 ingenic_clockintr(struct clockframe *cf)
    195 {
    196 	int s = splsched();
    197 	struct cpu_info * const ci = curcpu();
    198 #ifdef USE_OST
    199 	uint32_t new_cnt;
    200 #endif
    201 
    202 	/* clear flags */
    203 	writereg(JZ_TC_TFCR, TFR_OSTFLAG);
    204 
    205 	ci->ci_next_cp0_clk_intr += (uint32_t)(ci->ci_cycles_per_hz & 0xffffffff);
    206 #ifdef USE_OST
    207 	writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
    208 
    209 	/* Check for lost clock interrupts */
    210 	new_cnt = readreg(JZ_OST_CNT_LO);
    211 
    212 	/*
    213 	 * Missed one or more clock interrupts, so let's start
    214 	 * counting again from the current value.
    215 	 */
    216 	if ((ci->ci_next_cp0_clk_intr - new_cnt) & 0x80000000) {
    217 
    218 		ci->ci_next_cp0_clk_intr = new_cnt + curcpu()->ci_cycles_per_hz;
    219 		writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
    220 		curcpu()->ci_ev_count_compare_missed.ev_count++;
    221 	}
    222 	writereg(JZ_TC_TFCR, TFR_OSTFLAG);
    223 #else
    224 	writereg(JZ_TC_TFCR, TFR_FFLAG5);
    225 #endif
    226 
    227 #ifdef INGENIC_CLOCK_DEBUG
    228 	cnt++;
    229 	if (cnt == 100) {
    230 		cnt = 0;
    231 		ingenic_puts("+");
    232 	}
    233 #endif
    234 #ifdef MULTIPROCESSOR
    235 	/*
    236 	 * XXX
    237 	 * needs to take the IPI lock and ping all online CPUs, not just core 1
    238 	 */
    239 	mips_cp0_corembox_write(1, 1 << IPI_CLOCK);
    240 #endif
    241 	hardclock(cf);
    242 	splx(s);
    243 }
    244