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