mvsoctmr.c revision 1.1.2.2 1 /* $NetBSD: mvsoctmr.c,v 1.1.2.2 2010/10/09 03:31:40 yamt 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.2.2 2010/10/09 03:31:40 yamt 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