xref: /netbsd-src/sys/arch/arm/sa11x0/sa11x0_ost.c (revision 11a6dbe72840351315e0652b2fc6663628c84cad)
1 /*	$NetBSD: sa11x0_ost.c,v 1.24 2008/04/28 20:23:14 martin 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  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: sa11x0_ost.c,v 1.24 2008/04/28 20:23:14 martin Exp $");
35 
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/time.h>
41 #include <sys/timetc.h>
42 #include <sys/device.h>
43 
44 #include <machine/bus.h>
45 #include <machine/intr.h>
46 
47 #include <arm/cpufunc.h>
48 
49 #include <arm/sa11x0/sa11x0_reg.h>
50 #include <arm/sa11x0/sa11x0_var.h>
51 #include <arm/sa11x0/sa11x0_ostreg.h>
52 
53 static int	saost_match(struct device *, struct cfdata *, void *);
54 static void	saost_attach(struct device *, struct device *, void *);
55 
56 static void	saost_tc_init(void);
57 
58 static uint32_t	gettick(void);
59 static int	clockintr(void *);
60 static int	statintr(void *);
61 
62 struct saost_softc {
63 	struct device		sc_dev;
64 
65 	bus_space_tag_t		sc_iot;
66 	bus_space_handle_t	sc_ioh;
67 
68 	uint32_t		sc_clock_count;
69 	uint32_t		sc_statclock_count;
70 	uint32_t		sc_statclock_step;
71 };
72 
73 static struct saost_softc *saost_sc = NULL;
74 
75 #if defined(CPU_XSCALE_PXA270) && defined(CPU_XSCALE_PXA250)
76 #error ost needs to dynamically configure the frequency
77 #elif defined(CPU_XSCALE_PXA270)
78 #define TIMER_FREQUENCY         3250000         /* PXA270 uses 3.25MHz */
79 #else
80 #define TIMER_FREQUENCY         3686400         /* 3.6864MHz */
81 #endif
82 
83 #ifndef STATHZ
84 #define STATHZ	64
85 #endif
86 
87 CFATTACH_DECL(saost, sizeof(struct saost_softc),
88     saost_match, saost_attach, NULL, NULL);
89 
90 static int
91 saost_match(struct device *parent, struct cfdata *match, void *aux)
92 {
93 
94 	return 1;
95 }
96 
97 static void
98 saost_attach(struct device *parent, struct device *self, void *aux)
99 {
100 	struct saost_softc *sc = (struct saost_softc *)self;
101 	struct sa11x0_attach_args *sa = aux;
102 
103 	printf("\n");
104 
105 	sc->sc_iot = sa->sa_iot;
106 
107 	saost_sc = sc;
108 
109 	if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size, 0,
110 	    &sc->sc_ioh))
111 		panic("%s: Cannot map registers", self->dv_xname);
112 
113 	/* disable all channel and clear interrupt status */
114 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_IR, 0);
115 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 0xf);
116 
117 	printf("%s: SA-11x0 OS Timer\n", sc->sc_dev.dv_xname);
118 }
119 
120 static int
121 clockintr(void *arg)
122 {
123 	struct saost_softc *sc = saost_sc;
124 	struct clockframe *frame = arg;
125 	uint32_t oscr, nextmatch, oldmatch;
126 	int s;
127 
128 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 1);
129 
130 	/* schedule next clock intr */
131 	oldmatch = sc->sc_clock_count;
132 	nextmatch = oldmatch + TIMER_FREQUENCY / hz;
133 
134 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0, nextmatch);
135 	oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
136 
137 	if ((nextmatch > oldmatch &&
138 	     (oscr > nextmatch || oscr < oldmatch)) ||
139 	    (nextmatch < oldmatch && oscr > nextmatch && oscr < oldmatch)) {
140 		/*
141 		 * we couldn't set the matching register in time.
142 		 * just set it to some value so that next interrupt happens.
143 		 * XXX is it possible to compensate lost interrupts?
144 		 */
145 
146 		s = splhigh();
147 		oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
148 		nextmatch = oscr + 10;
149 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0, nextmatch);
150 		splx(s);
151 	}
152 
153 	sc->sc_clock_count = nextmatch;
154 	hardclock(frame);
155 
156 	return 1;
157 }
158 
159 static int
160 statintr(void *arg)
161 {
162 	struct saost_softc *sc = saost_sc;
163 	struct clockframe *frame = arg;
164 	uint32_t oscr, nextmatch, oldmatch;
165 	int s;
166 
167 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 2);
168 
169 	/* schedule next clock intr */
170 	oldmatch = sc->sc_statclock_count;
171 	nextmatch = oldmatch + sc->sc_statclock_step;
172 
173 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, nextmatch);
174 	oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
175 
176 	if ((nextmatch > oldmatch &&
177 	     (oscr > nextmatch || oscr < oldmatch)) ||
178 	    (nextmatch < oldmatch && oscr > nextmatch && oscr < oldmatch)) {
179 		/*
180 		 * we couldn't set the matching register in time.
181 		 * just set it to some value so that next interrupt happens.
182 		 * XXX is it possible to compensate lost interrupts?
183 		 */
184 
185 		s = splhigh();
186 		oscr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
187 		nextmatch = oscr + 10;
188 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, nextmatch);
189 		splx(s);
190 	}
191 
192 	sc->sc_statclock_count = nextmatch;
193 	statclock(frame);
194 
195 	return 1;
196 }
197 
198 void
199 setstatclockrate(int schz)
200 {
201 	struct saost_softc *sc = saost_sc;
202 	uint32_t count;
203 
204 	sc->sc_statclock_step = TIMER_FREQUENCY / schz;
205 	count = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
206 	count += sc->sc_statclock_step;
207 	sc->sc_statclock_count = count;
208 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1, count);
209 }
210 
211 void
212 cpu_initclocks(void)
213 {
214 	struct saost_softc *sc = saost_sc;
215 
216 	stathz = STATHZ;
217 	profhz = stathz;
218 	sc->sc_statclock_step = TIMER_FREQUENCY / stathz;
219 
220 	printf("clock: hz=%d stathz=%d\n", hz, stathz);
221 
222 	/* Use the channels 0 and 1 for hardclock and statclock, respectively */
223 	sc->sc_clock_count = TIMER_FREQUENCY / hz;
224 	sc->sc_statclock_count = TIMER_FREQUENCY / stathz;
225 
226 	sa11x0_intr_establish(0, 26, 1, IPL_CLOCK, clockintr, 0);
227 	sa11x0_intr_establish(0, 27, 1, IPL_CLOCK, statintr, 0);
228 
229 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_SR, 0xf);
230 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_IR, 3);
231 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR0,
232 			  sc->sc_clock_count);
233 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_MR1,
234 			  sc->sc_statclock_count);
235 
236 	/* Zero the counter value */
237 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, SAOST_CR, 0);
238 
239 	saost_tc_init();
240 }
241 
242 static u_int
243 saost_tc_get_timecount(struct timecounter *tc)
244 {
245 	return (u_int)gettick();
246 }
247 
248 static void
249 saost_tc_init(void)
250 {
251 	static struct timecounter saost_tc = {
252 		.tc_get_timecount = saost_tc_get_timecount,
253 		.tc_frequency = TIMER_FREQUENCY,
254 		.tc_counter_mask = ~0,
255 		.tc_name = "saost_count",
256 		.tc_quality = 100,
257 	};
258 
259 	tc_init(&saost_tc);
260 }
261 
262 static uint32_t
263 gettick(void)
264 {
265 	struct saost_softc *sc = saost_sc;
266 	uint32_t counter;
267 	u_int saved_ints;
268 
269 	saved_ints = disable_interrupts(I32_bit);
270 	counter = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SAOST_CR);
271 	restore_interrupts(saved_ints);
272 
273 	return counter;
274 }
275 
276 void
277 delay(u_int usecs)
278 {
279 	uint32_t xtick, otick, delta;
280 	int csec, usec;
281 
282 	csec = usecs / 10000;
283 	usec = usecs % 10000;
284 
285 	usecs = (TIMER_FREQUENCY / 100) * csec
286 	    + (TIMER_FREQUENCY / 100) * usec / 10000;
287 
288 	if (saost_sc == NULL) {
289 		volatile int k;
290 		int j;
291 		/* clock isn't initialized yet */
292 		for (; usecs > 0; usecs--)
293 			for (j = 100; j > 0; j--, k--)
294 				continue;
295 		return;
296 	}
297 
298 	otick = gettick();
299 
300 	while (1) {
301 		xtick = gettick();
302 		delta = xtick - otick;
303 		if (delta > usecs)
304 			break;
305 		usecs -= delta;
306 		otick = xtick;
307 	}
308 }
309