xref: /netbsd-src/sys/arch/arm/marvell/mvsoctmr.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: mvsoctmr.c,v 1.1 2010/10/03 05:49:24 kiyohara Exp $	*/
2 /*
3  * Copyright (c) 2007, 2008 KIYOHARA Takashi
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 #include <sys/cdefs.h>
28 __KERNEL_RCSID(0, "$NetBSD: mvsoctmr.c,v 1.1 2010/10/03 05:49:24 kiyohara Exp $");
29 
30 #include <sys/param.h>
31 #include <sys/atomic.h>
32 #include <sys/bus.h>
33 #include <sys/device.h>
34 #include <sys/errno.h>
35 #include <sys/kernel.h>
36 #include <sys/time.h>
37 #include <sys/timetc.h>
38 #include <sys/systm.h>
39 
40 #include <machine/intr.h>
41 
42 #include <arm/cpufunc.h>
43 
44 #include <arm/marvell/mvsocreg.h>
45 #include <arm/marvell/mvsocvar.h>
46 #include <arm/marvell/mvsoctmrreg.h>
47 
48 #include <dev/marvell/marvellvar.h>
49 
50 
51 struct mvsoctmr_softc {
52 	device_t sc_dev;
53 
54 	bus_space_tag_t sc_iot;
55 	bus_space_handle_t sc_ioh;
56 };
57 
58 
59 static int mvsoctmr_match(device_t, struct cfdata *, void *);
60 static void mvsoctmr_attach(device_t, device_t, void *);
61 
62 static int clockhandler(void *);
63 static int statclockhandler(void *);
64 
65 static u_int mvsoctmr_get_timecount(struct timecounter *);
66 
67 static void mvsoctmr_cntl(struct mvsoctmr_softc *, int, u_int, int, int);
68 
69 #ifndef STATHZ
70 #define STATHZ	64
71 #endif
72 
73 static struct mvsoctmr_softc *mvsoctmr_sc;
74 static uint32_t clock_ticks, statclock_ticks;
75 static struct timecounter mvsoctmr_timecounter = {
76 	mvsoctmr_get_timecount,	/* get_timecount */
77 	0,			/* no poll_pps */
78 	~0u,			/* counter_mask */
79 	0,			/* frequency  (set by cpu_initclocks()) */
80 	"mvsoctmr",		/* name */
81 	100,			/* quality */
82 	NULL,			/* prev */
83 	NULL,			/* next */
84 };
85 static volatile uint32_t mvsoctmr_base;
86 
87 CFATTACH_DECL_NEW(mvsoctmr, sizeof(struct mvsoctmr_softc),
88     mvsoctmr_match, mvsoctmr_attach, NULL, NULL);
89 
90 
91 /* ARGSUSED */
92 static int
93 mvsoctmr_match(device_t parent, struct cfdata *match, void *aux)
94 {
95 	struct marvell_attach_args *mva = aux;
96 
97 	if (strcmp(mva->mva_name, match->cf_name) != 0)
98 		return 0;
99 	if (mva->mva_offset == MVA_OFFSET_DEFAULT)
100 		return 0;
101 
102 	mva->mva_size = MVSOCTMR_SIZE;
103 	return 1;
104 }
105 
106 /* ARGSUSED */
107 static void
108 mvsoctmr_attach(device_t parent, device_t self, void *aux)
109 {
110         struct mvsoctmr_softc *sc = device_private(self);
111 	struct marvell_attach_args *mva = aux;
112 
113 	aprint_naive("\n");
114 	aprint_normal(": Marvell SoC Timer\n");
115 
116 	if (mvsoctmr_sc == NULL)
117 		mvsoctmr_sc = sc;
118 
119 	sc->sc_dev = self;
120 	sc->sc_iot = mva->mva_iot;
121 	if (bus_space_subregion(mva->mva_iot, mva->mva_ioh,
122 	    mva->mva_offset, mva->mva_size, &sc->sc_ioh))
123 		panic("%s: Cannot map registers", device_xname(self));
124 }
125 
126 /*
127  * clockhandler:
128  *
129  *	Handle the hardclock interrupt.
130  */
131 static int
132 clockhandler(void *arg)
133 {
134 	struct clockframe *frame = arg;
135 
136 	atomic_add_32(&mvsoctmr_base, clock_ticks);
137 
138 	hardclock(frame);
139 
140 	return 1;
141 }
142 
143 /*
144  * statclockhandler:
145  *
146  *	Handle the statclock interrupt.
147  */
148 static int
149 statclockhandler(void *arg)
150 {
151 	struct clockframe *frame = arg;
152 
153 	statclock(frame);
154 
155 	return 1;
156 }
157 
158 
159 /*
160  * setstatclockrate:
161  *
162  *	Set the rate of the statistics clock.
163  *
164  *	We assume that hz is either stathz or profhz, and that neither
165  *	will change after being set by cpu_initclocks().  We could
166  *	recalculate the intervals here, but that would be a pain.
167  */
168 /* ARGSUSED */
169 void
170 setstatclockrate(int newhz)
171 {
172 	struct mvsoctmr_softc *sc = mvsoctmr_sc;
173 	const int en = 1, autoen = 1;
174 
175 	statclock_ticks = mvTclk / newhz;
176 
177 	mvsoctmr_cntl(sc, MVSOCTMR_TIMER1, statclock_ticks, en, autoen);
178 }
179 
180 /*
181  * cpu_initclocks:
182  *
183  *	Initialize the clock and get them going.
184  */
185 void
186 cpu_initclocks()
187 {
188 	struct mvsoctmr_softc *sc;
189 	void *clock_ih;
190 	const int en = 1, autoen = 1;
191 
192 	sc = mvsoctmr_sc;
193 	if (sc == NULL)
194 		panic("cpu_initclocks: mvsoctmr not found");
195 
196 	stathz = profhz = STATHZ;
197 	mvsoctmr_timecounter.tc_frequency = mvTclk;
198 	clock_ticks = mvTclk / hz;
199 
200 	mvsoctmr_cntl(sc, MVSOCTMR_TIMER0, clock_ticks, en, autoen);
201 
202 	clock_ih = mvsoc_bridge_intr_establish(MVSOC_MLMB_MLMBI_CPUTIMER0INTREQ,
203 	    IPL_CLOCK, clockhandler, NULL);
204 	if (clock_ih == NULL)
205 		panic("cpu_initclocks: unable to register timer interrupt");
206 
207 	if (stathz) {
208 		setstatclockrate(stathz);
209 		clock_ih = mvsoc_bridge_intr_establish(
210 		    MVSOC_MLMB_MLMBI_CPUTIMER1INTREQ, IPL_HIGH,
211 		    statclockhandler, NULL);
212 		if (clock_ih == NULL)
213 			panic("cpu_initclocks:"
214 			    " unable to register statclock timer interrupt");
215 	}
216 
217 	tc_init(&mvsoctmr_timecounter);
218 }
219 
220 void
221 delay(unsigned int n)
222 {
223 	struct mvsoctmr_softc *sc;
224 	unsigned int cur_tick, initial_tick;
225 	int remaining;
226 
227 	sc = mvsoctmr_sc;
228 #ifdef DEBUG
229 	if (sc == NULL) {
230 		printf("%s: called before start mvsoctmr\n", __func__);
231 		return;
232 	}
233 #endif
234 
235 	/*
236 	 * Read the counter first, so that the rest of the setup overhead is
237 	 * counted.
238 	 */
239 	initial_tick = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
240 	    MVSOCTMR_TIMER(MVSOCTMR_TIMER0));
241 
242 	if (n <= UINT_MAX / mvTclk) {
243 		/*
244 		 * For unsigned arithmetic, division can be replaced with
245 		 * multiplication with the inverse and a shift.
246 		 */
247 		remaining = n * mvTclk / 1000000;
248 	} else {
249 		/*
250 		 * This is a very long delay.
251 		 * Being slow here doesn't matter.
252 		 */
253 		remaining = (unsigned long long) n * mvTclk / 1000000;
254 	}
255 
256 	while (remaining > 0) {
257 		cur_tick = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
258 		    MVSOCTMR_TIMER(MVSOCTMR_TIMER0));
259 		if (cur_tick > initial_tick)
260 			remaining -= clock_ticks - cur_tick + initial_tick;
261 		else
262 			remaining -= (initial_tick - cur_tick);
263 		initial_tick = cur_tick;
264 	}
265 }
266 
267 static u_int
268 mvsoctmr_get_timecount(struct timecounter *tc)
269 {
270 	struct mvsoctmr_softc *sc = mvsoctmr_sc;
271 	uint32_t counter, base;
272 	u_int intrstat;
273 
274 	intrstat = disable_interrupts(I32_bit);
275 	base = mvsoctmr_base;
276 	counter = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
277 	    MVSOCTMR_TIMER(MVSOCTMR_TIMER0));
278 	restore_interrupts(intrstat);
279 
280 	return base - counter;
281 }
282 
283 
284 static void
285 mvsoctmr_cntl(struct mvsoctmr_softc *sc, int num, u_int ticks, int en,
286 	      int autoen)
287 {
288 	uint32_t ctrl;
289 
290 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_RELOAD(num),
291 	    ticks);
292 
293 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_TIMER(num), ticks);
294 
295 	ctrl = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_CTCR);
296 	if (en)
297 		ctrl |= MVSOCTMR_CTCR_CPUTIMEREN(num);
298 	else
299 		ctrl &= ~MVSOCTMR_CTCR_CPUTIMEREN(num);
300 	if (autoen)
301 		ctrl |= MVSOCTMR_CTCR_CPUTIMERAUTO(num);
302 	else
303 		ctrl &= ~MVSOCTMR_CTCR_CPUTIMERAUTO(num);
304 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSOCTMR_CTCR, ctrl);
305 }
306