sa11x0_ost.c revision 1.29 1 /* $NetBSD: sa11x0_ost.c,v 1.29 2011/07/01 20:31:39 dyoung Exp $ */
2
3 /*
4 * Copyright (c) 1997 Mark Brinicombe.
5 * Copyright (c) 1997 Causality Limited.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by IWAMOTO Toshihiro and Ichiro FUKUHARA.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: sa11x0_ost.c,v 1.29 2011/07/01 20:31:39 dyoung Exp $");
42
43 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/time.h>
48 #include <sys/timetc.h>
49 #include <sys/device.h>
50
51 #include <sys/bus.h>
52 #include <machine/intr.h>
53
54 #include <arm/cpufunc.h>
55
56 #include <arm/sa11x0/sa11x0_reg.h>
57 #include <arm/sa11x0/sa11x0_var.h>
58 #include <arm/sa11x0/sa11x0_ostreg.h>
59
60 static int saost_match(device_t, cfdata_t, void *);
61 static void saost_attach(device_t, device_t, void *);
62
63 static void saost_tc_init(void);
64
65 static uint32_t gettick(void);
66 static int clockintr(void *);
67 static int statintr(void *);
68
69 struct saost_softc {
70 device_t sc_dev;
71
72 bus_space_tag_t sc_iot;
73 bus_space_handle_t sc_ioh;
74
75 uint32_t sc_clock_count;
76 uint32_t sc_statclock_count;
77 uint32_t sc_statclock_step;
78 };
79
80 static struct saost_softc *saost_sc = NULL;
81
82 #if defined(CPU_XSCALE_PXA270) && defined(CPU_XSCALE_PXA250)
83 #include <arm/xscale/pxa2x0cpu.h>
84 static uint32_t freq;
85 #define TIMER_FREQUENCY freq
86 #elif defined(CPU_XSCALE_PXA270)
87 #define TIMER_FREQUENCY 3250000 /* PXA270 uses 3.25MHz */
88 #else
89 #define TIMER_FREQUENCY 3686400 /* 3.6864MHz */
90 #endif
91
92 #ifndef STATHZ
93 #define STATHZ 64
94 #endif
95
96 CFATTACH_DECL_NEW(saost, sizeof(struct saost_softc),
97 saost_match, saost_attach, NULL, NULL);
98
99 static int
100 saost_match(device_t parent, cfdata_t match, void *aux)
101 {
102 struct sa11x0_attach_args *sa = aux;
103
104 if (strcmp(sa->sa_name, match->cf_name) != 0)
105 return 0;
106 return 1;
107 }
108
109 static void
110 saost_attach(device_t parent, device_t self, void *aux)
111 {
112 struct saost_softc *sc = device_private(self);
113 struct sa11x0_attach_args *sa = aux;
114
115 aprint_normal("\n");
116
117 sc->sc_dev = self;
118 sc->sc_iot = sa->sa_iot;
119
120 saost_sc = sc;
121
122 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
123 &sc->sc_ioh))
124 panic("%s: Cannot map registers", device_xname(self));
125
126 /* disable all channel and clear interrupt status */
127 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_IR, 0);
128 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 0xf);
129
130 aprint_normal_dev(self, "SA-11x0 OS Timer\n");
131 }
132
133 static int
134 clockintr(void *arg)
135 {
136 struct saost_softc *sc = saost_sc;
137 struct clockframe *frame = arg;
138 uint32_t oscr, nextmatch, oldmatch;
139 int s;
140
141 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 1);
142
143 /* schedule next clock intr */
144 oldmatch = sc->sc_clock_count;
145 nextmatch = oldmatch + TIMER_FREQUENCY / hz;
146
147 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0, nextmatch);
148 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
149
150 if ((nextmatch > oldmatch &&
151 (oscr > nextmatch || oscr < oldmatch)) ||
152 (nextmatch < oldmatch && oscr > nextmatch && oscr < oldmatch)) {
153 /*
154 * we couldn't set the matching register in time.
155 * just set it to some value so that next interrupt happens.
156 * XXX is it possible to compensate lost interrupts?
157 */
158
159 s = splhigh();
160 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
161 nextmatch = oscr + 10;
162 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0, nextmatch);
163 splx(s);
164 }
165
166 sc->sc_clock_count = nextmatch;
167 hardclock(frame);
168
169 return 1;
170 }
171
172 static int
173 statintr(void *arg)
174 {
175 struct saost_softc *sc = saost_sc;
176 struct clockframe *frame = arg;
177 uint32_t oscr, nextmatch, oldmatch;
178 int s;
179
180 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 2);
181
182 /* schedule next clock intr */
183 oldmatch = sc->sc_statclock_count;
184 nextmatch = oldmatch + sc->sc_statclock_step;
185
186 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, nextmatch);
187 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
188
189 if ((nextmatch > oldmatch &&
190 (oscr > nextmatch || oscr < oldmatch)) ||
191 (nextmatch < oldmatch && oscr > nextmatch && oscr < oldmatch)) {
192 /*
193 * we couldn't set the matching register in time.
194 * just set it to some value so that next interrupt happens.
195 * XXX is it possible to compensate lost interrupts?
196 */
197
198 s = splhigh();
199 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
200 nextmatch = oscr + 10;
201 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, nextmatch);
202 splx(s);
203 }
204
205 sc->sc_statclock_count = nextmatch;
206 statclock(frame);
207
208 return 1;
209 }
210
211 void
212 setstatclockrate(int schz)
213 {
214 struct saost_softc *sc = saost_sc;
215 uint32_t count;
216
217 sc->sc_statclock_step = TIMER_FREQUENCY / schz;
218 count = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
219 count += sc->sc_statclock_step;
220 sc->sc_statclock_count = count;
221 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, count);
222 }
223
224 void
225 cpu_initclocks(void)
226 {
227 struct saost_softc *sc = saost_sc;
228
229 stathz = STATHZ;
230 profhz = stathz;
231 #if defined(CPU_XSCALE_PXA270) && defined(CPU_XSCALE_PXA250)
232 TIMER_FREQUENCY = (CPU_IS_PXA250) ? 3686400 : 3250000;
233 #endif
234 sc->sc_statclock_step = TIMER_FREQUENCY / stathz;
235
236 aprint_normal("clock: hz=%d stathz=%d\n", hz, stathz);
237
238 /* Use the channels 0 and 1 for hardclock and statclock, respectively */
239 sc->sc_clock_count = TIMER_FREQUENCY / hz;
240 sc->sc_statclock_count = TIMER_FREQUENCY / stathz;
241
242 sa11x0_intr_establish(0, 26, 1, IPL_CLOCK, clockintr, 0);
243 sa11x0_intr_establish(0, 27, 1, IPL_CLOCK, statintr, 0);
244
245 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 0xf);
246 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_IR, 3);
247 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0,
248 sc->sc_clock_count);
249 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1,
250 sc->sc_statclock_count);
251
252 /* Zero the counter value */
253 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_CR, 0);
254
255 saost_tc_init();
256 }
257
258 static u_int
259 saost_tc_get_timecount(struct timecounter *tc)
260 {
261 return (u_int)gettick();
262 }
263
264 static void
265 saost_tc_init(void)
266 {
267 static struct timecounter saost_tc = {
268 .tc_get_timecount = saost_tc_get_timecount,
269 .tc_counter_mask = ~0,
270 .tc_name = "saost_count",
271 #if !(defined(CPU_XSCALE_PXA270) && defined(CPU_XSCALE_PXA250))
272 .tc_frequency = TIMER_FREQUENCY,
273 #endif
274 .tc_quality = 100,
275 };
276
277 #if defined(CPU_XSCALE_PXA270) && defined(CPU_XSCALE_PXA250)
278 saost_tc.tc_frequency = TIMER_FREQUENCY,
279 #endif
280 tc_init(&saost_tc);
281 }
282
283 static uint32_t
284 gettick(void)
285 {
286 struct saost_softc *sc = saost_sc;
287 uint32_t counter;
288 u_int saved_ints;
289
290 saved_ints = disable_interrupts(I32_bit);
291 counter = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
292 restore_interrupts(saved_ints);
293
294 return counter;
295 }
296
297 void
298 delay(u_int usecs)
299 {
300 uint32_t xtick, otick, delta;
301 int csec, usec;
302
303 csec = usecs / 10000;
304 usec = usecs % 10000;
305
306 usecs = (TIMER_FREQUENCY / 100) * csec
307 + (TIMER_FREQUENCY / 100) * usec / 10000;
308
309 if (saost_sc == NULL) {
310 volatile int k;
311 int j;
312 /* clock isn't initialized yet */
313 for (; usecs > 0; usecs--)
314 for (j = 100; j > 0; j--, k--)
315 continue;
316 return;
317 }
318
319 otick = gettick();
320
321 while (1) {
322 xtick = gettick();
323 delta = xtick - otick;
324 if (delta > usecs)
325 break;
326 usecs -= delta;
327 otick = xtick;
328 }
329 }
330