Home | History | Annotate | Line # | Download | only in ingenic
clock.c revision 1.5
      1 /*	$NetBSD: clock.c,v 1.5 2014/12/31 15:25:08 martin 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.5 2014/12/31 15:25:08 martin 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 	ci->ci_ev_count_compare.ev_count++;
    198 
    199 	/* clear flags */
    200 	writereg(JZ_TC_TFCR, TFR_OSTFLAG);
    201 
    202 	KASSERT((ci->ci_cycles_per_hz & ~(0xffffffff)) == 0);
    203 	ci->ci_next_cp0_clk_intr += (uint32_t)(ci->ci_cycles_per_hz & 0xffffffff);
    204 #ifdef USE_OST
    205 	writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
    206 
    207 	/* Check for lost clock interrupts */
    208 	new_cnt = readreg(JZ_OST_CNT_LO);
    209 
    210 	/*
    211 	 * Missed one or more clock interrupts, so let's start
    212 	 * counting again from the current value.
    213 	 */
    214 	if ((ci->ci_next_cp0_clk_intr - new_cnt) & 0x80000000) {
    215 
    216 		ci->ci_next_cp0_clk_intr = new_cnt + curcpu()->ci_cycles_per_hz;
    217 		writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
    218 		curcpu()->ci_ev_count_compare_missed.ev_count++;
    219 	}
    220 	writereg(JZ_TC_TFCR, TFR_OSTFLAG);
    221 #else
    222 	writereg(JZ_TC_TFCR, TFR_FFLAG5);
    223 #endif
    224 
    225 #ifdef INGENIC_CLOCK_DEBUG
    226 	cnt++;
    227 	if (cnt == 100) {
    228 		cnt = 0;
    229 		ingenic_puts("+");
    230 	}
    231 #endif
    232 	hardclock(&cf);
    233 }
    234