clock.c revision 1.4 1 1.4 macallan /* $NetBSD: clock.c,v 1.4 2014/12/26 17:43:32 macallan 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.4 macallan __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.4 2014/12/26 17:43:32 macallan Exp $");
31 1.1 macallan
32 1.1 macallan #include <sys/param.h>
33 1.1 macallan #include <sys/cpu.h>
34 1.1 macallan #include <sys/device.h>
35 1.1 macallan #include <sys/kernel.h>
36 1.1 macallan #include <sys/systm.h>
37 1.2 macallan #include <sys/timetc.h>
38 1.2 macallan
39 1.1 macallan #include <mips/ingenic/ingenic_regs.h>
40 1.1 macallan
41 1.3 macallan #include "opt_ingenic.h"
42 1.3 macallan
43 1.2 macallan extern void ingenic_puts(const char *);
44 1.2 macallan
45 1.2 macallan void ingenic_clockintr(uint32_t);
46 1.2 macallan
47 1.2 macallan struct clockframe cf;
48 1.2 macallan
49 1.2 macallan static u_int
50 1.2 macallan ingenic_count_read(struct timecounter *tc)
51 1.2 macallan {
52 1.2 macallan return readreg(JZ_OST_CNT_LO);
53 1.2 macallan }
54 1.2 macallan
55 1.1 macallan void
56 1.1 macallan cpu_initclocks(void)
57 1.1 macallan {
58 1.2 macallan struct cpu_info * const ci = curcpu();
59 1.2 macallan uint32_t cnt;
60 1.1 macallan
61 1.2 macallan static struct timecounter tc = {
62 1.2 macallan ingenic_count_read, /* get_timecount */
63 1.2 macallan 0, /* no poll_pps */
64 1.2 macallan ~0u, /* counter_mask */
65 1.2 macallan 12000000, /* frequency */
66 1.2 macallan "Ingenic OS timer", /* name */
67 1.2 macallan 100, /* quality */
68 1.2 macallan };
69 1.2 macallan
70 1.2 macallan curcpu()->ci_cctr_freq = tc.tc_frequency;
71 1.2 macallan
72 1.2 macallan tc_init(&tc);
73 1.2 macallan
74 1.2 macallan printf("starting timer interrupt...\n");
75 1.2 macallan /* start the timer interrupt */
76 1.2 macallan cnt = readreg(JZ_OST_CNT_LO);
77 1.2 macallan ci->ci_next_cp0_clk_intr = cnt + ci->ci_cycles_per_hz;
78 1.4 macallan writereg(JZ_TC_TFCR, TFR_OSTFLAG);
79 1.2 macallan writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
80 1.2 macallan /*
81 1.2 macallan * XXX
82 1.2 macallan * We can use OST or one of the regular timers to generate the 100hz
83 1.2 macallan * interrupt. OST interrupts need to be rescheduled every time and by
84 1.2 macallan * only one core, the regular timer can be programmed to fire every
85 1.2 macallan * 10ms without rescheduling and we'd still use the OST as time base.
86 1.2 macallan * OST is supposed to fire on INT2 although I haven't been able to get
87 1.2 macallan * that to work yet ( all I get is INT0 which is for hardware interrupts
88 1.2 macallan * in general )
89 1.2 macallan * So if we can get OST to fire on INT2 we can just block INT0 on core1
90 1.2 macallan * and have a timer interrupt on both cores, if not the regular timer
91 1.2 macallan * would be more convenient but we'd have to shoot an IPI to core1 on
92 1.2 macallan * every tick.
93 1.2 macallan * For now, use OST and hope we'll figure out how to make it fire on
94 1.2 macallan * INT2.
95 1.2 macallan */
96 1.4 macallan #ifdef USE_OST
97 1.2 macallan writereg(JZ_TC_TMCR, TFR_OSTFLAG);
98 1.2 macallan #else
99 1.2 macallan writereg(JZ_TC_TECR, TESR_TCST5); /* disable timer 5 */
100 1.2 macallan writereg(JZ_TC_TCNT(5), 0);
101 1.2 macallan writereg(JZ_TC_TDFR(5), 30000); /* 10ms at 48MHz / 16 */
102 1.2 macallan writereg(JZ_TC_TDHR(5), 60000); /* not reached */
103 1.2 macallan writereg(JZ_TC_TCSR(5), TCSR_EXT_EN| TCSR_DIV_16);
104 1.2 macallan writereg(JZ_TC_TMCR, TFR_FFLAG5);
105 1.2 macallan writereg(JZ_TC_TFCR, TFR_FFLAG5);
106 1.2 macallan writereg(JZ_TC_TESR, TESR_TCST5); /* enable timer 5 */
107 1.2 macallan #endif
108 1.3 macallan
109 1.3 macallan #ifdef INGENIC_CLOCK_DEBUG
110 1.2 macallan printf("INTC %08x %08x\n", readreg(JZ_ICSR0), readreg(JZ_ICSR1));
111 1.4 macallan printf("ICMR0 %08x\n", readreg(JZ_ICMR0));
112 1.4 macallan #endif
113 1.2 macallan writereg(JZ_ICMCR0, 0x0c000000); /* TCU2, OST */
114 1.2 macallan spl0();
115 1.3 macallan #ifdef INGENIC_CLOCK_DEBUG
116 1.2 macallan printf("TFR: %08x\n", readreg(JZ_TC_TFR));
117 1.2 macallan printf("TMR: %08x\n", readreg(JZ_TC_TMR));
118 1.2 macallan printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5)));
119 1.2 macallan printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0));
120 1.2 macallan printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0));
121 1.2 macallan delay(100000);
122 1.2 macallan printf("TFR: %08x\n", readreg(JZ_TC_TFR));
123 1.2 macallan printf("TMR: %08x\n", readreg(JZ_TC_TMR));
124 1.2 macallan printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5)));
125 1.2 macallan printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0));
126 1.2 macallan printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0));
127 1.2 macallan printf("TFR: %08x\n", readreg(JZ_TC_TFR));
128 1.2 macallan printf("TMR: %08x\n", readreg(JZ_TC_TMR));
129 1.2 macallan printf("cnt5: %08x\n", readreg(JZ_TC_TCNT(5)));
130 1.2 macallan printf("CR: %08x\n", MFC0(MIPS_COP_0_CAUSE, 0));
131 1.2 macallan printf("SR: %08x\n", MFC0(MIPS_COP_0_STATUS, 0));
132 1.2 macallan
133 1.2 macallan printf("INTC %08x %08x\n", readreg(JZ_ICSR0), readreg(JZ_ICSR1));
134 1.2 macallan delay(3000000);
135 1.3 macallan #endif
136 1.1 macallan }
137 1.1 macallan
138 1.1 macallan /* shamelessly stolen from mips3_clock.c */
139 1.1 macallan void
140 1.1 macallan delay(int n)
141 1.1 macallan {
142 1.1 macallan u_long divisor_delay;
143 1.1 macallan uint32_t cur, last, delta, usecs;
144 1.1 macallan
145 1.1 macallan last = readreg(JZ_OST_CNT_LO);
146 1.1 macallan delta = usecs = 0;
147 1.1 macallan
148 1.1 macallan divisor_delay = curcpu()->ci_divisor_delay;
149 1.1 macallan if (divisor_delay == 0) {
150 1.1 macallan /*
151 1.1 macallan * Frequency values in curcpu() are not initialized.
152 1.1 macallan * Assume faster frequency since longer delays are harmless.
153 1.1 macallan * Note CPU_MIPS_DOUBLE_COUNT is ignored here.
154 1.1 macallan */
155 1.1 macallan #define FAST_FREQ (300 * 1000 * 1000) /* fast enough? */
156 1.1 macallan divisor_delay = FAST_FREQ / (1000 * 1000);
157 1.1 macallan }
158 1.1 macallan while (n > usecs) {
159 1.1 macallan cur = readreg(JZ_OST_CNT_LO);
160 1.1 macallan
161 1.1 macallan /*
162 1.1 macallan * We setup the OS timer to always counts upto UINT32_MAX,
163 1.1 macallan * so no need to check wrapped around case.
164 1.1 macallan */
165 1.1 macallan delta += (cur - last);
166 1.1 macallan
167 1.1 macallan last = cur;
168 1.1 macallan
169 1.1 macallan while (delta >= divisor_delay) {
170 1.1 macallan /*
171 1.1 macallan * delta is not so larger than divisor_delay here,
172 1.1 macallan * and using DIV/DIVU ops could be much slower.
173 1.1 macallan * (though longer delay may be harmless)
174 1.1 macallan */
175 1.1 macallan usecs++;
176 1.1 macallan delta -= divisor_delay;
177 1.1 macallan }
178 1.1 macallan }
179 1.1 macallan }
180 1.1 macallan
181 1.1 macallan void
182 1.1 macallan setstatclockrate(int r)
183 1.1 macallan {
184 1.2 macallan /* we could just use another timer channel here */
185 1.2 macallan }
186 1.2 macallan
187 1.3 macallan #ifdef INGENIC_CLOCK_DEBUG
188 1.2 macallan int cnt = 99;
189 1.3 macallan #endif
190 1.2 macallan
191 1.2 macallan void
192 1.2 macallan ingenic_clockintr(uint32_t id)
193 1.2 macallan {
194 1.2 macallan struct cpu_info * const ci = curcpu();
195 1.4 macallan #ifdef USE_OST
196 1.2 macallan uint32_t new_cnt;
197 1.4 macallan #endif
198 1.2 macallan ci->ci_ev_count_compare.ev_count++;
199 1.2 macallan
200 1.2 macallan /* clear flags */
201 1.2 macallan writereg(JZ_TC_TFCR, TFR_OSTFLAG);
202 1.2 macallan
203 1.2 macallan KASSERT((ci->ci_cycles_per_hz & ~(0xffffffff)) == 0);
204 1.2 macallan ci->ci_next_cp0_clk_intr += (uint32_t)(ci->ci_cycles_per_hz & 0xffffffff);
205 1.4 macallan #ifdef USE_OST
206 1.2 macallan writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
207 1.2 macallan
208 1.2 macallan /* Check for lost clock interrupts */
209 1.2 macallan new_cnt = readreg(JZ_OST_CNT_LO);
210 1.2 macallan
211 1.2 macallan /*
212 1.2 macallan * Missed one or more clock interrupts, so let's start
213 1.2 macallan * counting again from the current value.
214 1.2 macallan */
215 1.2 macallan if ((ci->ci_next_cp0_clk_intr - new_cnt) & 0x80000000) {
216 1.2 macallan
217 1.2 macallan ci->ci_next_cp0_clk_intr = new_cnt + curcpu()->ci_cycles_per_hz;
218 1.2 macallan writereg(JZ_OST_DATA, ci->ci_next_cp0_clk_intr);
219 1.2 macallan curcpu()->ci_ev_count_compare_missed.ev_count++;
220 1.2 macallan }
221 1.4 macallan writereg(JZ_TC_TFCR, TFR_OSTFLAG);
222 1.4 macallan #else
223 1.4 macallan writereg(JZ_TC_TFCR, TFR_FFLAG5);
224 1.4 macallan #endif
225 1.2 macallan
226 1.3 macallan #ifdef INGENIC_CLOCK_DEBUG
227 1.2 macallan cnt++;
228 1.2 macallan if (cnt == 100) {
229 1.2 macallan cnt = 0;
230 1.2 macallan ingenic_puts("+");
231 1.2 macallan }
232 1.3 macallan #endif
233 1.2 macallan hardclock(&cf);
234 1.1 macallan }
235