sa11x0_ost.c revision 1.18 1 /* $NetBSD: sa11x0_ost.c,v 1.18 2006/06/27 13:58:08 peter 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.18 2006/06/27 13:58:08 peter 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/device.h>
49
50 #include <machine/bus.h>
51 #include <machine/intr.h>
52
53 #include <arm/cpufunc.h>
54
55 #include <arm/arm32/katelib.h>
56
57 #include <arm/sa11x0/sa11x0_reg.h>
58 #include <arm/sa11x0/sa11x0_var.h>
59 #include <arm/sa11x0/sa11x0_ostreg.h>
60
61 static int saost_match(struct device *, struct cfdata *, void *);
62 static void saost_attach(struct device *, struct device *, void *);
63
64 int gettick(void);
65 static int clockintr(void *);
66 static int statintr(void *);
67 void rtcinit(void);
68
69 struct saost_softc {
70 struct device sc_dev;
71 bus_addr_t sc_baseaddr;
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 #define TIMER_FREQUENCY 3686400 /* 3.6864MHz */
83 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY/1000000)
84
85 #ifndef STATHZ
86 #define STATHZ 64
87 #endif
88
89 CFATTACH_DECL(saost, sizeof(struct saost_softc),
90 saost_match, saost_attach, NULL, NULL);
91
92 static int
93 saost_match(struct device *parent, struct cfdata *match, void *aux)
94 {
95
96 return 1;
97 }
98
99 void
100 saost_attach(struct device *parent, struct device *self, void *aux)
101 {
102 struct saost_softc *sc = (struct saost_softc*)self;
103 struct sa11x0_attach_args *sa = aux;
104
105 printf("\n");
106
107 sc->sc_iot = sa->sa_iot;
108 sc->sc_baseaddr = sa->sa_addr;
109
110 saost_sc = sc;
111
112 if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
113 &sc->sc_ioh))
114 panic("%s: Cannot map registers", self->dv_xname);
115
116 /* disable all channel and clear interrupt status */
117 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh, SAOST_IR, 0);
118 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh, SAOST_SR, 0xf);
119
120 printf("%s: SA-11x0 OS Timer\n", sc->sc_dev.dv_xname);
121 }
122
123 static int
124 clockintr(void *arg)
125 {
126 struct clockframe *frame = arg;
127 uint32_t oscr, nextmatch, oldmatch;
128 int s;
129
130 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh,
131 SAOST_SR, 1);
132
133 /* schedule next clock intr */
134 oldmatch = saost_sc->sc_clock_count;
135 nextmatch = oldmatch + TIMER_FREQUENCY / hz;
136
137 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh, SAOST_MR0,
138 nextmatch);
139 oscr = bus_space_read_4(saost_sc->sc_iot, saost_sc->sc_ioh,
140 SAOST_CR);
141
142 if ((nextmatch > oldmatch &&
143 (oscr > nextmatch || oscr < oldmatch)) ||
144 (nextmatch < oldmatch && oscr > nextmatch && oscr < oldmatch)) {
145 /*
146 * we couldn't set the matching register in time.
147 * just set it to some value so that next interrupt happens.
148 * XXX is it possible to compensate lost interrupts?
149 */
150
151 s = splhigh();
152 oscr = bus_space_read_4(saost_sc->sc_iot, saost_sc->sc_ioh,
153 SAOST_CR);
154 nextmatch = oscr + 10;
155 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh,
156 SAOST_MR0, nextmatch);
157 splx(s);
158 }
159
160 saost_sc->sc_clock_count = nextmatch;
161 hardclock(frame);
162
163 return 1;
164 }
165
166 static int
167 statintr(void *arg)
168 {
169 struct clockframe *frame = arg;
170 uint32_t oscr, nextmatch, oldmatch;
171 int s;
172
173 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh,
174 SAOST_SR, 2);
175
176 /* schedule next clock intr */
177 oldmatch = saost_sc->sc_statclock_count;
178 nextmatch = oldmatch + saost_sc->sc_statclock_step;
179
180 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh, SAOST_MR1,
181 nextmatch);
182 oscr = bus_space_read_4(saost_sc->sc_iot, saost_sc->sc_ioh,
183 SAOST_CR);
184
185 if ((nextmatch > oldmatch &&
186 (oscr > nextmatch || oscr < oldmatch)) ||
187 (nextmatch < oldmatch && oscr > nextmatch && oscr < oldmatch)) {
188 /*
189 * we couldn't set the matching register in time.
190 * just set it to some value so that next interrupt happens.
191 * XXX is it possible to compensate lost interrupts?
192 */
193
194 s = splhigh();
195 oscr = bus_space_read_4(saost_sc->sc_iot, saost_sc->sc_ioh,
196 SAOST_CR);
197 nextmatch = oscr + 10;
198 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh,
199 SAOST_MR1, nextmatch);
200 splx(s);
201 }
202
203 saost_sc->sc_statclock_count = nextmatch;
204 statclock(frame);
205
206 return 1;
207 }
208
209
210 void
211 setstatclockrate(int schz)
212 {
213 uint32_t count;
214
215 saost_sc->sc_statclock_step = TIMER_FREQUENCY / schz;
216 count = bus_space_read_4(saost_sc->sc_iot, saost_sc->sc_ioh, SAOST_CR);
217 count += saost_sc->sc_statclock_step;
218 saost_sc->sc_statclock_count = count;
219 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh,
220 SAOST_MR1, count);
221 }
222
223 void
224 cpu_initclocks(void)
225 {
226 stathz = STATHZ;
227 profhz = stathz;
228 saost_sc->sc_statclock_step = TIMER_FREQUENCY / stathz;
229
230 printf("clock: hz=%d stathz=%d\n", hz, stathz);
231
232 /* Use the channels 0 and 1 for hardclock and statclock, respectively */
233 saost_sc->sc_clock_count = TIMER_FREQUENCY / hz;
234 saost_sc->sc_statclock_count = TIMER_FREQUENCY / stathz;
235
236 sa11x0_intr_establish(0, 26, 1, IPL_CLOCK, clockintr, 0);
237 sa11x0_intr_establish(0, 27, 1, IPL_CLOCK, statintr, 0);
238
239 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh, SAOST_SR, 0xf);
240 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh, SAOST_IR, 3);
241 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh, SAOST_MR0,
242 saost_sc->sc_clock_count);
243 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh, SAOST_MR1,
244 saost_sc->sc_statclock_count);
245
246 /* Zero the counter value */
247 bus_space_write_4(saost_sc->sc_iot, saost_sc->sc_ioh, SAOST_CR, 0);
248 }
249
250 int
251 gettick(void)
252 {
253 int counter;
254 u_int savedints;
255 savedints = disable_interrupts(I32_bit);
256
257 counter = bus_space_read_4(saost_sc->sc_iot, saost_sc->sc_ioh,
258 SAOST_CR);
259
260 restore_interrupts(savedints);
261 return counter;
262 }
263
264 void
265 microtime(struct timeval *tvp)
266 {
267 int s, tm, deltatm;
268 static struct timeval lasttime;
269
270 if (saost_sc == NULL) {
271 tvp->tv_sec = 0;
272 tvp->tv_usec = 0;
273 return;
274 }
275
276 s = splhigh();
277 tm = bus_space_read_4(saost_sc->sc_iot, saost_sc->sc_ioh,
278 SAOST_CR);
279
280 deltatm = saost_sc->sc_clock_count - tm;
281
282 #ifdef OST_DEBUG
283 printf("deltatm = %d\n",deltatm);
284 #endif
285
286 *tvp = time;
287 tvp->tv_usec++; /* XXX */
288 while (tvp->tv_usec >= 1000000) {
289 tvp->tv_sec++;
290 tvp->tv_usec -= 1000000;
291 }
292
293 if (tvp->tv_sec == lasttime.tv_sec &&
294 tvp->tv_usec <= lasttime.tv_usec &&
295 (tvp->tv_usec = lasttime.tv_usec + 1) >= 1000000)
296 {
297 tvp->tv_sec++;
298 tvp->tv_usec -= 1000000;
299 }
300 lasttime = *tvp;
301 splx(s);
302 }
303
304 void
305 delay(u_int usecs)
306 {
307 uint32_t xtick, otick, delta;
308 int j, csec, usec;
309
310 csec = usecs / 10000;
311 usec = usecs % 10000;
312
313 usecs = (TIMER_FREQUENCY / 100) * csec
314 + (TIMER_FREQUENCY / 100) * usec / 10000;
315
316 if (!saost_sc) {
317 /* clock isn't initialized yet */
318 for (; usecs > 0; usecs--)
319 for (j = 100; j > 0; j--)
320 continue;
321 return;
322 }
323
324 otick = gettick();
325
326 while (1) {
327 for (j = 100; j > 0; j--)
328 continue;
329 xtick = gettick();
330 delta = xtick - otick;
331 if (delta > usecs)
332 break;
333 usecs -= delta;
334 otick = xtick;
335 }
336 }
337
338 void
339 resettodr(void)
340 {
341 }
342
343 void
344 inittodr(time_t base)
345 {
346 time.tv_sec = base;
347 time.tv_usec = 0;
348 }
349