Home | History | Annotate | Line # | Download | only in ingenic
clock.c revision 1.4
      1 /*	$NetBSD: clock.c,v 1.4 2014/12/26 17:43:32 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.4 2014/12/26 17:43:32 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 struct clockframe cf;
     48 
     49 static u_int
     50 ingenic_count_read(struct timecounter *tc)
     51 {
     52 	return readreg(JZ_OST_CNT_LO);
     53 }
     54 
     55 void
     56 cpu_initclocks(void)
     57 {
     58 	struct cpu_info * const ci = curcpu();
     59 	uint32_t cnt;
     60 
     61 	static struct timecounter tc =  {
     62 		ingenic_count_read,		/* get_timecount */
     63 		0,				/* no poll_pps */
     64 		~0u,				/* counter_mask */
     65 		12000000,			/* frequency */
     66 		"Ingenic OS timer",		/* name */
     67 		100,				/* quality */
     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 #endif
    136 }
    137 
    138 /* shamelessly stolen from mips3_clock.c */
    139 void
    140 delay(int n)
    141 {
    142 	u_long divisor_delay;
    143 	uint32_t cur, last, delta, usecs;
    144 
    145 	last = readreg(JZ_OST_CNT_LO);
    146 	delta = usecs = 0;
    147 
    148 	divisor_delay = curcpu()->ci_divisor_delay;
    149 	if (divisor_delay == 0) {
    150 		/*
    151 		 * Frequency values in curcpu() are not initialized.
    152 		 * Assume faster frequency since longer delays are harmless.
    153 		 * Note CPU_MIPS_DOUBLE_COUNT is ignored here.
    154 		 */
    155 #define FAST_FREQ	(300 * 1000 * 1000)	/* fast enough? */
    156 		divisor_delay = FAST_FREQ / (1000 * 1000);
    157 	}
    158 	while (n > usecs) {
    159 		cur = readreg(JZ_OST_CNT_LO);
    160 
    161 		/*
    162 		 * We setup the OS timer to always counts upto UINT32_MAX,
    163 		 * so no need to check wrapped around case.
    164 		 */
    165 		delta += (cur - last);
    166 
    167 		last = cur;
    168 
    169 		while (delta >= divisor_delay) {
    170 			/*
    171 			 * delta is not so larger than divisor_delay here,
    172 			 * and using DIV/DIVU ops could be much slower.
    173 			 * (though longer delay may be harmless)
    174 			 */
    175 			usecs++;
    176 			delta -= divisor_delay;
    177 		}
    178 	}
    179 }
    180 
    181 void
    182 setstatclockrate(int r)
    183 {
    184 	/* we could just use another timer channel here */
    185 }
    186 
    187 #ifdef INGENIC_CLOCK_DEBUG
    188 int cnt = 99;
    189 #endif
    190 
    191 void
    192 ingenic_clockintr(uint32_t id)
    193 {
    194 	struct cpu_info * const ci = curcpu();
    195 #ifdef USE_OST
    196 	uint32_t new_cnt;
    197 #endif
    198 	ci->ci_ev_count_compare.ev_count++;
    199 
    200 	/* clear flags */
    201 	writereg(JZ_TC_TFCR, TFR_OSTFLAG);
    202 
    203 	KASSERT((ci->ci_cycles_per_hz & ~(0xffffffff)) == 0);
    204 	ci->ci_next_cp0_clk_intr += (uint32_t)(ci->ci_cycles_per_hz & 0xffffffff);
    205 #ifdef USE_OST
    206 	writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
    207 
    208 	/* Check for lost clock interrupts */
    209 	new_cnt = readreg(JZ_OST_CNT_LO);
    210 
    211 	/*
    212 	 * Missed one or more clock interrupts, so let's start
    213 	 * counting again from the current value.
    214 	 */
    215 	if ((ci->ci_next_cp0_clk_intr - new_cnt) & 0x80000000) {
    216 
    217 		ci->ci_next_cp0_clk_intr = new_cnt + curcpu()->ci_cycles_per_hz;
    218 		writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
    219 		curcpu()->ci_ev_count_compare_missed.ev_count++;
    220 	}
    221 	writereg(JZ_TC_TFCR, TFR_OSTFLAG);
    222 #else
    223 	writereg(JZ_TC_TFCR, TFR_FFLAG5);
    224 #endif
    225 
    226 #ifdef INGENIC_CLOCK_DEBUG
    227 	cnt++;
    228 	if (cnt == 100) {
    229 		cnt = 0;
    230 		ingenic_puts("+");
    231 	}
    232 #endif
    233 	hardclock(&cf);
    234 }
    235