clock.c revision 1.10 1 1.10 skrll /* $NetBSD: clock.c,v 1.10 2017/05/21 06:49:12 skrll Exp $ */
2 1.1 macallan
3 1.1 macallan /*-
4 1.1 macallan * Copyright (c) 2014 Michael Lorenz
5 1.1 macallan * All rights reserved.
6 1.1 macallan *
7 1.1 macallan * Redistribution and use in source and binary forms, with or without
8 1.1 macallan * modification, are permitted provided that the following conditions
9 1.1 macallan * are met:
10 1.1 macallan * 1. Redistributions of source code must retain the above copyright
11 1.1 macallan * notice, this list of conditions and the following disclaimer.
12 1.1 macallan * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 macallan * notice, this list of conditions and the following disclaimer in the
14 1.1 macallan * documentation and/or other materials provided with the distribution.
15 1.1 macallan *
16 1.1 macallan * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 macallan * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 macallan * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 macallan * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 macallan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 macallan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 macallan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 macallan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 macallan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 macallan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 macallan * POSSIBILITY OF SUCH DAMAGE.
27 1.1 macallan */
28 1.1 macallan
29 1.1 macallan #include <sys/cdefs.h>
30 1.10 skrll __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.10 2017/05/21 06:49:12 skrll Exp $");
31 1.7 macallan
32 1.7 macallan #include "opt_multiprocessor.h"
33 1.1 macallan
34 1.1 macallan #include <sys/param.h>
35 1.1 macallan #include <sys/cpu.h>
36 1.1 macallan #include <sys/device.h>
37 1.1 macallan #include <sys/kernel.h>
38 1.1 macallan #include <sys/systm.h>
39 1.2 macallan #include <sys/timetc.h>
40 1.2 macallan
41 1.10 skrll #include <mips/ingenic/ingenic_var.h>
42 1.1 macallan #include <mips/ingenic/ingenic_regs.h>
43 1.1 macallan
44 1.3 macallan #include "opt_ingenic.h"
45 1.3 macallan
46 1.2 macallan extern void ingenic_puts(const char *);
47 1.2 macallan
48 1.8 skrll void ingenic_clockintr(struct clockframe *);
49 1.2 macallan
50 1.2 macallan static u_int
51 1.2 macallan ingenic_count_read(struct timecounter *tc)
52 1.2 macallan {
53 1.2 macallan return readreg(JZ_OST_CNT_LO);
54 1.2 macallan }
55 1.2 macallan
56 1.1 macallan void
57 1.1 macallan cpu_initclocks(void)
58 1.1 macallan {
59 1.2 macallan struct cpu_info * const ci = curcpu();
60 1.2 macallan uint32_t cnt;
61 1.1 macallan
62 1.2 macallan static struct timecounter tc = {
63 1.2 macallan ingenic_count_read, /* get_timecount */
64 1.2 macallan 0, /* no poll_pps */
65 1.2 macallan ~0u, /* counter_mask */
66 1.2 macallan 12000000, /* frequency */
67 1.2 macallan "Ingenic OS timer", /* name */
68 1.2 macallan 100, /* quality */
69 1.2 macallan };
70 1.2 macallan
71 1.2 macallan curcpu()->ci_cctr_freq = tc.tc_frequency;
72 1.2 macallan
73 1.2 macallan tc_init(&tc);
74 1.2 macallan
75 1.2 macallan printf("starting timer interrupt...\n");
76 1.2 macallan /* start the timer interrupt */
77 1.2 macallan cnt = readreg(JZ_OST_CNT_LO);
78 1.2 macallan ci->ci_next_cp0_clk_intr = cnt + ci->ci_cycles_per_hz;
79 1.4 macallan writereg(JZ_TC_TFCR, TFR_OSTFLAG);
80 1.2 macallan writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
81 1.2 macallan /*
82 1.2 macallan * XXX
83 1.2 macallan * We can use OST or one of the regular timers to generate the 100hz
84 1.2 macallan * interrupt. OST interrupts need to be rescheduled every time and by
85 1.2 macallan * only one core, the regular timer can be programmed to fire every
86 1.2 macallan * 10ms without rescheduling and we'd still use the OST as time base.
87 1.2 macallan * OST is supposed to fire on INT2 although I haven't been able to get
88 1.2 macallan * that to work yet ( all I get is INT0 which is for hardware interrupts
89 1.2 macallan * in general )
90 1.2 macallan * So if we can get OST to fire on INT2 we can just block INT0 on core1
91 1.9 skrll * and have a timer interrupt on both cores, if not the regular timer
92 1.2 macallan * would be more convenient but we'd have to shoot an IPI to core1 on
93 1.2 macallan * every tick.
94 1.2 macallan * For now, use OST and hope we'll figure out how to make it fire on
95 1.2 macallan * INT2.
96 1.2 macallan */
97 1.4 macallan #ifdef USE_OST
98 1.2 macallan writereg(JZ_TC_TMCR, TFR_OSTFLAG);
99 1.2 macallan #else
100 1.2 macallan writereg(JZ_TC_TECR, TESR_TCST5); /* disable timer 5 */
101 1.2 macallan writereg(JZ_TC_TCNT(5), 0);
102 1.2 macallan writereg(JZ_TC_TDFR(5), 30000); /* 10ms at 48MHz / 16 */
103 1.2 macallan writereg(JZ_TC_TDHR(5), 60000); /* not reached */
104 1.2 macallan writereg(JZ_TC_TCSR(5), TCSR_EXT_EN| TCSR_DIV_16);
105 1.2 macallan writereg(JZ_TC_TMCR, TFR_FFLAG5);
106 1.2 macallan writereg(JZ_TC_TFCR, TFR_FFLAG5);
107 1.2 macallan writereg(JZ_TC_TESR, TESR_TCST5); /* enable timer 5 */
108 1.2 macallan #endif
109 1.3 macallan
110 1.3 macallan #ifdef INGENIC_CLOCK_DEBUG
111 1.2 macallan printf("INTC %08x %08x\n", readreg(JZ_ICSR0), readreg(JZ_ICSR1));
112 1.4 macallan printf("ICMR0 %08x\n", readreg(JZ_ICMR0));
113 1.4 macallan #endif
114 1.2 macallan writereg(JZ_ICMCR0, 0x0c000000); /* TCU2, OST */
115 1.2 macallan spl0();
116 1.3 macallan #ifdef INGENIC_CLOCK_DEBUG
117 1.2 macallan printf("TFR: %08x\n", readreg(JZ_TC_TFR));
118 1.2 macallan printf("TMR: %08x\n", readreg(JZ_TC_TMR));
119 1.2 macallan printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5)));
120 1.2 macallan printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0));
121 1.2 macallan printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0));
122 1.2 macallan delay(100000);
123 1.2 macallan printf("TFR: %08x\n", readreg(JZ_TC_TFR));
124 1.2 macallan printf("TMR: %08x\n", readreg(JZ_TC_TMR));
125 1.2 macallan printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5)));
126 1.2 macallan printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0));
127 1.2 macallan printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0));
128 1.2 macallan printf("TFR: %08x\n", readreg(JZ_TC_TFR));
129 1.2 macallan printf("TMR: %08x\n", readreg(JZ_TC_TMR));
130 1.2 macallan printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5)));
131 1.2 macallan printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0));
132 1.2 macallan printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0));
133 1.9 skrll
134 1.2 macallan printf("INTC %08x %08x\n", readreg(JZ_ICSR0), readreg(JZ_ICSR1));
135 1.2 macallan delay(3000000);
136 1.7 macallan printf("%s %d\n", __func__, MFC0(12, 3));
137 1.7 macallan printf("%s %08x\n", __func__, MFC0(12, 4));
138 1.3 macallan #endif
139 1.1 macallan }
140 1.1 macallan
141 1.1 macallan /* shamelessly stolen from mips3_clock.c */
142 1.1 macallan void
143 1.1 macallan delay(int n)
144 1.1 macallan {
145 1.1 macallan u_long divisor_delay;
146 1.1 macallan uint32_t cur, last, delta, usecs;
147 1.1 macallan
148 1.1 macallan last = readreg(JZ_OST_CNT_LO);
149 1.1 macallan delta = usecs = 0;
150 1.1 macallan
151 1.1 macallan divisor_delay = curcpu()->ci_divisor_delay;
152 1.1 macallan if (divisor_delay == 0) {
153 1.1 macallan /*
154 1.1 macallan * Frequency values in curcpu() are not initialized.
155 1.1 macallan * Assume faster frequency since longer delays are harmless.
156 1.1 macallan * Note CPU_MIPS_DOUBLE_COUNT is ignored here.
157 1.1 macallan */
158 1.1 macallan #define FAST_FREQ (300 * 1000 * 1000) /* fast enough? */
159 1.1 macallan divisor_delay = FAST_FREQ / (1000 * 1000);
160 1.1 macallan }
161 1.1 macallan while (n > usecs) {
162 1.1 macallan cur = readreg(JZ_OST_CNT_LO);
163 1.1 macallan
164 1.1 macallan /*
165 1.1 macallan * We setup the OS timer to always counts upto UINT32_MAX,
166 1.1 macallan * so no need to check wrapped around case.
167 1.1 macallan */
168 1.1 macallan delta += (cur - last);
169 1.1 macallan
170 1.1 macallan last = cur;
171 1.1 macallan
172 1.1 macallan while (delta >= divisor_delay) {
173 1.1 macallan /*
174 1.1 macallan * delta is not so larger than divisor_delay here,
175 1.1 macallan * and using DIV/DIVU ops could be much slower.
176 1.1 macallan * (though longer delay may be harmless)
177 1.1 macallan */
178 1.1 macallan usecs++;
179 1.1 macallan delta -= divisor_delay;
180 1.1 macallan }
181 1.1 macallan }
182 1.1 macallan }
183 1.1 macallan
184 1.1 macallan void
185 1.1 macallan setstatclockrate(int r)
186 1.1 macallan {
187 1.2 macallan /* we could just use another timer channel here */
188 1.2 macallan }
189 1.2 macallan
190 1.3 macallan #ifdef INGENIC_CLOCK_DEBUG
191 1.2 macallan int cnt = 99;
192 1.3 macallan #endif
193 1.2 macallan
194 1.2 macallan void
195 1.8 skrll ingenic_clockintr(struct clockframe *cf)
196 1.2 macallan {
197 1.7 macallan int s = splsched();
198 1.2 macallan struct cpu_info * const ci = curcpu();
199 1.4 macallan #ifdef USE_OST
200 1.2 macallan uint32_t new_cnt;
201 1.4 macallan #endif
202 1.2 macallan
203 1.2 macallan /* clear flags */
204 1.2 macallan writereg(JZ_TC_TFCR, TFR_OSTFLAG);
205 1.2 macallan
206 1.2 macallan ci->ci_next_cp0_clk_intr += (uint32_t)(ci->ci_cycles_per_hz & 0xffffffff);
207 1.4 macallan #ifdef USE_OST
208 1.2 macallan writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
209 1.2 macallan
210 1.2 macallan /* Check for lost clock interrupts */
211 1.2 macallan new_cnt = readreg(JZ_OST_CNT_LO);
212 1.2 macallan
213 1.9 skrll /*
214 1.9 skrll * Missed one or more clock interrupts, so let's start
215 1.2 macallan * counting again from the current value.
216 1.2 macallan */
217 1.2 macallan if ((ci->ci_next_cp0_clk_intr - new_cnt) & 0x80000000) {
218 1.2 macallan
219 1.2 macallan ci->ci_next_cp0_clk_intr = new_cnt + curcpu()->ci_cycles_per_hz;
220 1.2 macallan writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
221 1.2 macallan curcpu()->ci_ev_count_compare_missed.ev_count++;
222 1.2 macallan }
223 1.4 macallan writereg(JZ_TC_TFCR, TFR_OSTFLAG);
224 1.4 macallan #else
225 1.4 macallan writereg(JZ_TC_TFCR, TFR_FFLAG5);
226 1.4 macallan #endif
227 1.2 macallan
228 1.3 macallan #ifdef INGENIC_CLOCK_DEBUG
229 1.2 macallan cnt++;
230 1.2 macallan if (cnt == 100) {
231 1.2 macallan cnt = 0;
232 1.2 macallan ingenic_puts("+");
233 1.2 macallan }
234 1.3 macallan #endif
235 1.7 macallan #ifdef MULTIPROCESSOR
236 1.7 macallan /*
237 1.7 macallan * XXX
238 1.7 macallan * needs to take the IPI lock and ping all online CPUs, not just core 1
239 1.7 macallan */
240 1.10 skrll mips_cp0_corembox_write(1, 1 << IPI_CLOCK);
241 1.7 macallan #endif
242 1.8 skrll hardclock(cf);
243 1.7 macallan splx(s);
244 1.1 macallan }
245