Home | History | Annotate | Line # | Download | only in ingenic
clock.c revision 1.2
      1 /*	$NetBSD: clock.c,v 1.2 2014/12/06 14:24:58 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.2 2014/12/06 14:24:58 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 extern void ingenic_puts(const char *);
     42 
     43 void ingenic_clockintr(uint32_t);
     44 
     45 struct clockframe cf;
     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_OST_DATA, ci->ci_next_cp0_clk_intr);
     77 	writereg(JZ_TC_TFCR, TFR_OSTFLAG);
     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 #if 1
     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 	printf("INTC %08x %08x\n", readreg(JZ_ICSR0), readreg(JZ_ICSR1));
    107 	writereg(JZ_ICMCR0, 0x0c000000); /* TCU2, OST */
    108 	spl0();
    109 	printf("TFR: %08x\n", readreg(JZ_TC_TFR));
    110 	printf("TMR: %08x\n", readreg(JZ_TC_TMR));
    111 	printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5)));
    112 	printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0));
    113 	printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0));
    114 	delay(100000);
    115 	printf("TFR: %08x\n", readreg(JZ_TC_TFR));
    116 	printf("TMR: %08x\n", readreg(JZ_TC_TMR));
    117 	printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5)));
    118 	printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0));
    119 	printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0));
    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 
    126 	printf("INTC %08x %08x\n", readreg(JZ_ICSR0), readreg(JZ_ICSR1));
    127 	delay(3000000);
    128 }
    129 
    130 /* shamelessly stolen from mips3_clock.c */
    131 void
    132 delay(int n)
    133 {
    134 	u_long divisor_delay;
    135 	uint32_t cur, last, delta, usecs;
    136 
    137 	last = readreg(JZ_OST_CNT_LO);
    138 	delta = usecs = 0;
    139 
    140 	divisor_delay = curcpu()->ci_divisor_delay;
    141 	if (divisor_delay == 0) {
    142 		/*
    143 		 * Frequency values in curcpu() are not initialized.
    144 		 * Assume faster frequency since longer delays are harmless.
    145 		 * Note CPU_MIPS_DOUBLE_COUNT is ignored here.
    146 		 */
    147 #define FAST_FREQ	(300 * 1000 * 1000)	/* fast enough? */
    148 		divisor_delay = FAST_FREQ / (1000 * 1000);
    149 	}
    150 	while (n > usecs) {
    151 		cur = readreg(JZ_OST_CNT_LO);
    152 
    153 		/*
    154 		 * We setup the OS timer to always counts upto UINT32_MAX,
    155 		 * so no need to check wrapped around case.
    156 		 */
    157 		delta += (cur - last);
    158 
    159 		last = cur;
    160 
    161 		while (delta >= divisor_delay) {
    162 			/*
    163 			 * delta is not so larger than divisor_delay here,
    164 			 * and using DIV/DIVU ops could be much slower.
    165 			 * (though longer delay may be harmless)
    166 			 */
    167 			usecs++;
    168 			delta -= divisor_delay;
    169 		}
    170 	}
    171 }
    172 
    173 void
    174 setstatclockrate(int r)
    175 {
    176 	/* we could just use another timer channel here */
    177 }
    178 
    179 int cnt = 99;
    180 
    181 
    182 void
    183 ingenic_clockintr(uint32_t id)
    184 {
    185 	struct cpu_info * const ci = curcpu();
    186 	uint32_t new_cnt;
    187 
    188 	ci->ci_ev_count_compare.ev_count++;
    189 
    190 	/* clear flags */
    191 	writereg(JZ_TC_TFCR, TFR_OSTFLAG);
    192 
    193 	KASSERT((ci->ci_cycles_per_hz & ~(0xffffffff)) == 0);
    194 	ci->ci_next_cp0_clk_intr += (uint32_t)(ci->ci_cycles_per_hz & 0xffffffff);
    195 	writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
    196 
    197 	/* Check for lost clock interrupts */
    198 	new_cnt = readreg(JZ_OST_CNT_LO);
    199 
    200 	/*
    201 	 * Missed one or more clock interrupts, so let's start
    202 	 * counting again from the current value.
    203 	 */
    204 	if ((ci->ci_next_cp0_clk_intr - new_cnt) & 0x80000000) {
    205 
    206 		ci->ci_next_cp0_clk_intr = new_cnt + curcpu()->ci_cycles_per_hz;
    207 		writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
    208 		curcpu()->ci_ev_count_compare_missed.ev_count++;
    209 	}
    210 
    211 	cnt++;
    212 	if (cnt == 100) {
    213 		cnt = 0;
    214 		ingenic_puts("+");
    215 	}
    216 
    217 	hardclock(&cf);
    218 }
    219