xref: /netbsd-src/sys/arch/evbmips/ingenic/clock.c (revision 200d779b75dbeafa7bc01fd0f60bc61185f6967b)
1 /*	$NetBSD: clock.c,v 1.6 2015/06/30 04:10:10 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.6 2015/06/30 04:10:10 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 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 
198 	/* clear flags */
199 	writereg(JZ_TC_TFCR, TFR_OSTFLAG);
200 
201 	KASSERT((ci->ci_cycles_per_hz & ~(0xffffffff)) == 0);
202 	ci->ci_next_cp0_clk_intr += (uint32_t)(ci->ci_cycles_per_hz & 0xffffffff);
203 #ifdef USE_OST
204 	writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
205 
206 	/* Check for lost clock interrupts */
207 	new_cnt = readreg(JZ_OST_CNT_LO);
208 
209 	/*
210 	 * Missed one or more clock interrupts, so let's start
211 	 * counting again from the current value.
212 	 */
213 	if ((ci->ci_next_cp0_clk_intr - new_cnt) & 0x80000000) {
214 
215 		ci->ci_next_cp0_clk_intr = new_cnt + curcpu()->ci_cycles_per_hz;
216 		writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
217 		curcpu()->ci_ev_count_compare_missed.ev_count++;
218 	}
219 	writereg(JZ_TC_TFCR, TFR_OSTFLAG);
220 #else
221 	writereg(JZ_TC_TFCR, TFR_FFLAG5);
222 #endif
223 
224 #ifdef INGENIC_CLOCK_DEBUG
225 	cnt++;
226 	if (cnt == 100) {
227 		cnt = 0;
228 		ingenic_puts("+");
229 	}
230 #endif
231 	hardclock(&cf);
232 }
233