sa11x0_ost.c revision 1.23.2.1 1 /* $NetBSD: sa11x0_ost.c,v 1.23.2.1 2009/06/20 07:20:01 yamt 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.23.2.1 2009/06/20 07:20:01 yamt 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 <machine/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 #error ost needs to dynamically configure the frequency
84 #elif defined(CPU_XSCALE_PXA270)
85 #define TIMER_FREQUENCY 3250000 /* PXA270 uses 3.25MHz */
86 #else
87 #define TIMER_FREQUENCY 3686400 /* 3.6864MHz */
88 #endif
89
90 #ifndef STATHZ
91 #define STATHZ 64
92 #endif
93
94 CFATTACH_DECL_NEW(saost, sizeof(struct saost_softc),
95 saost_match, saost_attach, NULL, NULL);
96
97 static int
98 saost_match(device_t parent, cfdata_t match, void *aux)
99 {
100
101 return 1;
102 }
103
104 static void
105 saost_attach(device_t parent, device_t self, void *aux)
106 {
107 struct saost_softc *sc = device_private(self);
108 struct sa11x0_attach_args *sa = aux;
109
110 aprint_normal("\n");
111
112 sc->sc_dev = self;
113 sc->sc_iot = sa->sa_iot;
114
115 saost_sc = sc;
116
117 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
118 &sc->sc_ioh))
119 panic("%s: Cannot map registers", device_xname(self));
120
121 /* disable all channel and clear interrupt status */
122 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_IR, 0);
123 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 0xf);
124
125 aprint_normal_dev(self, "SA-11x0 OS Timer\n");
126 }
127
128 static int
129 clockintr(void *arg)
130 {
131 struct saost_softc *sc = saost_sc;
132 struct clockframe *frame = arg;
133 uint32_t oscr, nextmatch, oldmatch;
134 int s;
135
136 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 1);
137
138 /* schedule next clock intr */
139 oldmatch = sc->sc_clock_count;
140 nextmatch = oldmatch + TIMER_FREQUENCY / hz;
141
142 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0, nextmatch);
143 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
144
145 if ((nextmatch > oldmatch &&
146 (oscr > nextmatch || oscr < oldmatch)) ||
147 (nextmatch < oldmatch && oscr > nextmatch && oscr < oldmatch)) {
148 /*
149 * we couldn't set the matching register in time.
150 * just set it to some value so that next interrupt happens.
151 * XXX is it possible to compensate lost interrupts?
152 */
153
154 s = splhigh();
155 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
156 nextmatch = oscr + 10;
157 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0, nextmatch);
158 splx(s);
159 }
160
161 sc->sc_clock_count = nextmatch;
162 hardclock(frame);
163
164 return 1;
165 }
166
167 static int
168 statintr(void *arg)
169 {
170 struct saost_softc *sc = saost_sc;
171 struct clockframe *frame = arg;
172 uint32_t oscr, nextmatch, oldmatch;
173 int s;
174
175 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 2);
176
177 /* schedule next clock intr */
178 oldmatch = sc->sc_statclock_count;
179 nextmatch = oldmatch + sc->sc_statclock_step;
180
181 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, nextmatch);
182 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
183
184 if ((nextmatch > oldmatch &&
185 (oscr > nextmatch || oscr < oldmatch)) ||
186 (nextmatch < oldmatch && oscr > nextmatch && oscr < oldmatch)) {
187 /*
188 * we couldn't set the matching register in time.
189 * just set it to some value so that next interrupt happens.
190 * XXX is it possible to compensate lost interrupts?
191 */
192
193 s = splhigh();
194 oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
195 nextmatch = oscr + 10;
196 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, nextmatch);
197 splx(s);
198 }
199
200 sc->sc_statclock_count = nextmatch;
201 statclock(frame);
202
203 return 1;
204 }
205
206 void
207 setstatclockrate(int schz)
208 {
209 struct saost_softc *sc = saost_sc;
210 uint32_t count;
211
212 sc->sc_statclock_step = TIMER_FREQUENCY / schz;
213 count = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
214 count += sc->sc_statclock_step;
215 sc->sc_statclock_count = count;
216 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, count);
217 }
218
219 void
220 cpu_initclocks(void)
221 {
222 struct saost_softc *sc = saost_sc;
223
224 stathz = STATHZ;
225 profhz = stathz;
226 sc->sc_statclock_step = TIMER_FREQUENCY / stathz;
227
228 aprint_normal("clock: hz=%d stathz=%d\n", hz, stathz);
229
230 /* Use the channels 0 and 1 for hardclock and statclock, respectively */
231 sc->sc_clock_count = TIMER_FREQUENCY / hz;
232 sc->sc_statclock_count = TIMER_FREQUENCY / stathz;
233
234 sa11x0_intr_establish(0, 26, 1, IPL_CLOCK, clockintr, 0);
235 sa11x0_intr_establish(0, 27, 1, IPL_CLOCK, statintr, 0);
236
237 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 0xf);
238 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_IR, 3);
239 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0,
240 sc->sc_clock_count);
241 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1,
242 sc->sc_statclock_count);
243
244 /* Zero the counter value */
245 bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_CR, 0);
246
247 saost_tc_init();
248 }
249
250 static u_int
251 saost_tc_get_timecount(struct timecounter *tc)
252 {
253 return (u_int)gettick();
254 }
255
256 static void
257 saost_tc_init(void)
258 {
259 static struct timecounter saost_tc = {
260 .tc_get_timecount = saost_tc_get_timecount,
261 .tc_frequency = TIMER_FREQUENCY,
262 .tc_counter_mask = ~0,
263 .tc_name = "saost_count",
264 .tc_quality = 100,
265 };
266
267 tc_init(&saost_tc);
268 }
269
270 static uint32_t
271 gettick(void)
272 {
273 struct saost_softc *sc = saost_sc;
274 uint32_t counter;
275 u_int saved_ints;
276
277 saved_ints = disable_interrupts(I32_bit);
278 counter = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
279 restore_interrupts(saved_ints);
280
281 return counter;
282 }
283
284 void
285 delay(u_int usecs)
286 {
287 uint32_t xtick, otick, delta;
288 int csec, usec;
289
290 csec = usecs / 10000;
291 usec = usecs % 10000;
292
293 usecs = (TIMER_FREQUENCY / 100) * csec
294 + (TIMER_FREQUENCY / 100) * usec / 10000;
295
296 if (saost_sc == NULL) {
297 volatile int k;
298 int j;
299 /* clock isn't initialized yet */
300 for (; usecs > 0; usecs--)
301 for (j = 100; j > 0; j--, k--)
302 continue;
303 return;
304 }
305
306 otick = gettick();
307
308 while (1) {
309 xtick = gettick();
310 delta = xtick - otick;
311 if (delta > usecs)
312 break;
313 usecs -= delta;
314 otick = xtick;
315 }
316 }
317