]>
Commit | Line | Data |
---|---|---|
3b285da6 WD |
1 | /* |
2 | * (C) Copyright 2000 | |
3 | * Wolfgang Denk, DENX Software Engineering, [email protected]. | |
4 | * | |
5 | * See file CREDITS for list of people who contributed to this | |
6 | * project. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License as | |
10 | * published by the Free Software Foundation; either version 2 of | |
11 | * the License, or (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
21 | * MA 02111-1307 USA | |
22 | */ | |
23 | ||
24 | #include <common.h> | |
25 | #include <commproc.h> | |
26 | #include <mpc8xx_irq.h> | |
27b207fd | 27 | #include <exports.h> |
3b285da6 | 28 | |
d87080b7 WD |
29 | DECLARE_GLOBAL_DATA_PTR; |
30 | ||
3b285da6 WD |
31 | #undef DEBUG |
32 | ||
33 | #define TIMER_PERIOD 1000000 /* 1 second clock */ | |
34 | ||
35 | static void timer_handler (void *arg); | |
36 | ||
37 | ||
38 | /* Access functions for the Machine State Register */ | |
39 | static __inline__ unsigned long get_msr(void) | |
40 | { | |
41 | unsigned long msr; | |
42 | ||
43 | asm volatile("mfmsr %0" : "=r" (msr) :); | |
44 | return msr; | |
45 | } | |
46 | ||
47 | static __inline__ void set_msr(unsigned long msr) | |
48 | { | |
49 | asm volatile("mtmsr %0" : : "r" (msr)); | |
50 | } | |
51 | ||
52 | /* | |
53 | * Definitions to access the CPM Timer registers | |
54 | * See 8xx_immap.h for Internal Memory Map layout, | |
55 | * and commproc.h for CPM Interrupt vectors (aka "IRQ"s) | |
56 | */ | |
57 | ||
58 | typedef struct tid_8xx_cpmtimer_s { | |
59 | int cpm_vec; /* CPM Interrupt Vector for this timer */ | |
60 | ushort *tgcrp; /* Pointer to Timer Global Config Reg. */ | |
61 | ushort *tmrp; /* Pointer to Timer Mode Register */ | |
62 | ushort *trrp; /* Pointer to Timer Reference Register */ | |
63 | ushort *tcrp; /* Pointer to Timer Capture Register */ | |
64 | ushort *tcnp; /* Pointer to Timer Counter Register */ | |
65 | ushort *terp; /* Pointer to Timer Event Register */ | |
66 | } tid_8xx_cpmtimer_t; | |
67 | ||
68 | #ifndef CLOCKRATE | |
69 | # define CLOCKRATE 64 | |
70 | #endif | |
71 | ||
72 | #define CPMT_CLOCK_DIV 16 | |
73 | #define CPMT_MAX_PRESCALER 256 | |
74 | #define CPMT_MAX_REFERENCE 65535 /* max. unsigned short */ | |
75 | ||
76 | #define CPMT_MAX_TICKS (CPMT_MAX_REFERENCE * CPMT_MAX_PRESCALER) | |
77 | #define CPMT_MAX_TICKS_WITH_DIV (CPMT_MAX_REFERENCE * CPMT_MAX_PRESCALER * CPMT_CLOCK_DIV) | |
78 | #define CPMT_MAX_INTERVAL (CPMT_MAX_TICKS_WITH_DIV / CLOCKRATE) | |
79 | ||
80 | /* For now: always use max. prescaler value */ | |
81 | #define CPMT_PRESCALER (CPMT_MAX_PRESCALER) | |
82 | ||
83 | /* CPM Timer Event Register Bits */ | |
84 | #define CPMT_EVENT_CAP 0x0001 /* Capture Event */ | |
85 | #define CPMT_EVENT_REF 0x0002 /* Reference Counter Event */ | |
86 | ||
87 | /* CPM Timer Global Config Register */ | |
88 | #define CPMT_GCR_RST 0x0001 /* Reset Timer */ | |
89 | #define CPMT_GCR_STP 0x0002 /* Stop Timer */ | |
90 | #define CPMT_GCR_FRZ 0x0004 /* Freeze Timer */ | |
91 | #define CPMT_GCR_GM_CAS 0x0008 /* Gate Mode / Cascade Timers */ | |
92 | #define CPMT_GCR_MASK (CPMT_GCR_RST|CPMT_GCR_STP|CPMT_GCR_FRZ|CPMT_GCR_GM_CAS) | |
93 | ||
94 | /* CPM Timer Mode register */ | |
95 | #define CPMT_MR_GE 0x0001 /* Gate Enable */ | |
96 | #define CPMT_MR_ICLK_CASC 0x0000 /* Clock internally cascaded */ | |
97 | #define CPMT_MR_ICLK_CLK 0x0002 /* Clock = system clock */ | |
98 | #define CPMT_MR_ICLK_CLKDIV 0x0004 /* Clock = system clock / 16 */ | |
99 | #define CPMT_MR_ICLK_TIN 0x0006 /* Clock = TINx signal */ | |
100 | #define CPMT_MR_FRR 0x0008 /* Free Run / Restart */ | |
101 | #define CPMT_MR_ORI 0x0010 /* Out. Reference Interrupt En. */ | |
102 | #define CPMT_MR_OM 0x0020 /* Output Mode */ | |
103 | #define CPMT_MR_CE_DIS 0x0000 /* Capture/Interrupt disabled */ | |
104 | #define CPMT_MR_CE_RISE 0x0040 /* Capt./Interr. on rising TIN */ | |
105 | #define CPMT_MR_CE_FALL 0x0080 /* Capt./Interr. on falling TIN */ | |
106 | #define CPMT_MR_CE_ANY 0x00C0 /* Capt./Interr. on any TIN edge*/ | |
107 | ||
108 | ||
3b285da6 WD |
109 | /* |
110 | * which CPM timer to use - index starts at 0 (= timer 1) | |
111 | */ | |
112 | #define TID_TIMER_ID 0 /* use CPM timer 1 */ | |
113 | ||
114 | void setPeriod (tid_8xx_cpmtimer_t *hwp, ulong interval); | |
115 | ||
116 | static char *usage = "\n[q, b, e, ?] "; | |
117 | ||
118 | int timer (int argc, char *argv[]) | |
119 | { | |
3b285da6 WD |
120 | cpmtimer8xx_t *cpmtimerp; /* Pointer to the CPM Timer structure */ |
121 | tid_8xx_cpmtimer_t hw; | |
122 | tid_8xx_cpmtimer_t *hwp = &hw; | |
123 | int c; | |
7c7a23bd | 124 | int running; |
3b285da6 | 125 | |
27b207fd WD |
126 | app_startup(argv); |
127 | ||
3b285da6 WD |
128 | /* Pointer to CPM Timer structure */ |
129 | cpmtimerp = &((immap_t *) gd->bd->bi_immr_base)->im_cpmtimer; | |
130 | ||
27b207fd | 131 | printf ("TIMERS=0x%x\n", (unsigned) cpmtimerp); |
3b285da6 WD |
132 | |
133 | /* Initialize pointers depending on which timer we use */ | |
134 | switch (TID_TIMER_ID) { | |
135 | case 0: | |
136 | hwp->tmrp = &(cpmtimerp->cpmt_tmr1); | |
137 | hwp->trrp = &(cpmtimerp->cpmt_trr1); | |
138 | hwp->tcrp = &(cpmtimerp->cpmt_tcr1); | |
139 | hwp->tcnp = &(cpmtimerp->cpmt_tcn1); | |
140 | hwp->terp = &(cpmtimerp->cpmt_ter1); | |
141 | hwp->cpm_vec = CPMVEC_TIMER1; | |
142 | break; | |
143 | case 1: | |
144 | hwp->tmrp = &(cpmtimerp->cpmt_tmr2); | |
145 | hwp->trrp = &(cpmtimerp->cpmt_trr2); | |
146 | hwp->tcrp = &(cpmtimerp->cpmt_tcr2); | |
147 | hwp->tcnp = &(cpmtimerp->cpmt_tcn2); | |
148 | hwp->terp = &(cpmtimerp->cpmt_ter2); | |
149 | hwp->cpm_vec = CPMVEC_TIMER2; | |
150 | break; | |
151 | case 2: | |
152 | hwp->tmrp = &(cpmtimerp->cpmt_tmr3); | |
153 | hwp->trrp = &(cpmtimerp->cpmt_trr3); | |
154 | hwp->tcrp = &(cpmtimerp->cpmt_tcr3); | |
155 | hwp->tcnp = &(cpmtimerp->cpmt_tcn3); | |
156 | hwp->terp = &(cpmtimerp->cpmt_ter3); | |
157 | hwp->cpm_vec = CPMVEC_TIMER3; | |
158 | break; | |
159 | case 3: | |
160 | hwp->tmrp = &(cpmtimerp->cpmt_tmr4); | |
161 | hwp->trrp = &(cpmtimerp->cpmt_trr4); | |
162 | hwp->tcrp = &(cpmtimerp->cpmt_tcr4); | |
163 | hwp->tcnp = &(cpmtimerp->cpmt_tcn4); | |
164 | hwp->terp = &(cpmtimerp->cpmt_ter4); | |
165 | hwp->cpm_vec = CPMVEC_TIMER4; | |
166 | break; | |
167 | } | |
168 | ||
169 | hwp->tgcrp = &cpmtimerp->cpmt_tgcr; | |
170 | ||
27b207fd | 171 | printf ("Using timer %d\n" |
3b285da6 WD |
172 | "tgcr @ 0x%x, tmr @ 0x%x, trr @ 0x%x," |
173 | " tcr @ 0x%x, tcn @ 0x%x, ter @ 0x%x\n", | |
174 | TID_TIMER_ID + 1, | |
175 | (unsigned) hwp->tgcrp, | |
176 | (unsigned) hwp->tmrp, | |
177 | (unsigned) hwp->trrp, | |
178 | (unsigned) hwp->tcrp, | |
179 | (unsigned) hwp->tcnp, | |
180 | (unsigned) hwp->terp | |
181 | ); | |
182 | ||
183 | /* reset timer */ | |
184 | *hwp->tgcrp &= ~(CPMT_GCR_MASK << TID_TIMER_ID); | |
185 | ||
186 | /* clear all events */ | |
187 | *hwp->terp = (CPMT_EVENT_CAP | CPMT_EVENT_REF); | |
188 | ||
27b207fd | 189 | printf (usage); |
7c7a23bd | 190 | running = 0; |
27b207fd | 191 | while ((c = getc()) != 'q') { |
3b285da6 WD |
192 | if (c == 'b') { |
193 | ||
194 | setPeriod (hwp, TIMER_PERIOD); /* Set period and start ticking */ | |
195 | ||
196 | /* Install interrupt handler (enable timer in CIMR) */ | |
27b207fd | 197 | install_hdlr (hwp->cpm_vec, timer_handler, hwp); |
3b285da6 | 198 | |
27b207fd | 199 | printf ("Enabling timer\n"); |
3b285da6 WD |
200 | |
201 | /* enable timer */ | |
202 | *hwp->tgcrp |= (CPMT_GCR_RST << TID_TIMER_ID); | |
7c7a23bd | 203 | running = 1; |
3b285da6 WD |
204 | |
205 | #ifdef DEBUG | |
27b207fd | 206 | printf ("tgcr=0x%x, tmr=0x%x, trr=0x%x," |
3b285da6 WD |
207 | " tcr=0x%x, tcn=0x%x, ter=0x%x\n", |
208 | *hwp->tgcrp, *hwp->tmrp, *hwp->trrp, | |
209 | *hwp->tcrp, *hwp->tcnp, *hwp->terp | |
210 | ); | |
211 | #endif | |
212 | } else if (c == 'e') { | |
213 | ||
27b207fd | 214 | printf ("Stopping timer\n"); |
3b285da6 WD |
215 | |
216 | *hwp->tgcrp &= ~(CPMT_GCR_MASK << TID_TIMER_ID); | |
7c7a23bd | 217 | running = 0; |
3b285da6 WD |
218 | |
219 | #ifdef DEBUG | |
27b207fd | 220 | printf ("tgcr=0x%x, tmr=0x%x, trr=0x%x," |
3b285da6 WD |
221 | " tcr=0x%x, tcn=0x%x, ter=0x%x\n", |
222 | *hwp->tgcrp, *hwp->tmrp, *hwp->trrp, | |
223 | *hwp->tcrp, *hwp->tcnp, *hwp->terp | |
224 | ); | |
225 | #endif | |
226 | /* Uninstall interrupt handler */ | |
27b207fd | 227 | free_hdlr (hwp->cpm_vec); |
3b285da6 WD |
228 | |
229 | } else if (c == '?') { | |
230 | #ifdef DEBUG | |
231 | cpic8xx_t *cpm_icp = &((immap_t *) gd->bd->bi_immr_base)->im_cpic; | |
232 | sysconf8xx_t *siup = &((immap_t *) gd->bd->bi_immr_base)->im_siu_conf; | |
233 | #endif | |
234 | ||
27b207fd | 235 | printf ("\ntgcr=0x%x, tmr=0x%x, trr=0x%x," |
3b285da6 WD |
236 | " tcr=0x%x, tcn=0x%x, ter=0x%x\n", |
237 | *hwp->tgcrp, *hwp->tmrp, *hwp->trrp, | |
238 | *hwp->tcrp, *hwp->tcnp, *hwp->terp | |
239 | ); | |
240 | #ifdef DEBUG | |
27b207fd | 241 | printf ("SIUMCR=0x%08lx, SYPCR=0x%08lx," |
3b285da6 WD |
242 | " SIMASK=0x%08lx, SIPEND=0x%08lx\n", |
243 | siup->sc_siumcr, | |
244 | siup->sc_sypcr, | |
245 | siup->sc_simask, | |
246 | siup->sc_sipend | |
247 | ); | |
248 | ||
27b207fd | 249 | printf ("CIMR=0x%08lx, CICR=0x%08lx, CIPR=0x%08lx\n", |
3b285da6 WD |
250 | cpm_icp->cpic_cimr, |
251 | cpm_icp->cpic_cicr, | |
252 | cpm_icp->cpic_cipr | |
253 | ); | |
254 | #endif | |
255 | } else { | |
27b207fd | 256 | printf ("\nEnter: q - quit, b - start timer, e - stop timer, ? - get status\n"); |
3b285da6 | 257 | } |
27b207fd | 258 | printf (usage); |
3b285da6 | 259 | } |
7c7a23bd | 260 | if (running) { |
27b207fd | 261 | printf ("Stopping timer\n"); |
7c7a23bd | 262 | *hwp->tgcrp &= ~(CPMT_GCR_MASK << TID_TIMER_ID); |
27b207fd | 263 | free_hdlr (hwp->cpm_vec); |
7c7a23bd WD |
264 | } |
265 | ||
3b285da6 WD |
266 | return (0); |
267 | } | |
268 | ||
269 | ||
270 | /* Set period in microseconds and start. | |
271 | * Truncate to maximum period if more than this is requested - but warn about it. | |
272 | */ | |
273 | ||
274 | void setPeriod (tid_8xx_cpmtimer_t *hwp, ulong interval) | |
275 | { | |
276 | unsigned short prescaler; | |
277 | unsigned long ticks; | |
278 | ||
27b207fd | 279 | printf ("Set interval %ld us\n", interval); |
3b285da6 WD |
280 | |
281 | /* Warn if requesting longer period than possible */ | |
282 | if (interval > CPMT_MAX_INTERVAL) { | |
27b207fd | 283 | printf ("Truncate interval %ld to maximum (%d)\n", |
3b285da6 WD |
284 | interval, CPMT_MAX_INTERVAL); |
285 | interval = CPMT_MAX_INTERVAL; | |
286 | } | |
287 | /* | |
288 | * Check if we want to use clock divider: | |
289 | * Since the reference counter can be incremented only in integer steps, | |
290 | * we try to keep it as big as possible to allow the resulting period to be | |
291 | * as precise as possible. | |
292 | */ | |
293 | /* prescaler, enable interrupt, restart after ref count is reached */ | |
294 | prescaler = (ushort) ((CPMT_PRESCALER - 1) << 8) | | |
295 | CPMT_MR_ORI | | |
296 | CPMT_MR_FRR; | |
297 | ||
298 | ticks = ((ulong) CLOCKRATE * interval); | |
299 | ||
300 | if (ticks > CPMT_MAX_TICKS) { | |
301 | ticks /= CPMT_CLOCK_DIV; | |
302 | prescaler |= CPMT_MR_ICLK_CLKDIV; /* use system clock divided by 16 */ | |
303 | } else { | |
304 | prescaler |= CPMT_MR_ICLK_CLK; /* use system clock without divider */ | |
305 | } | |
306 | ||
307 | #ifdef DEBUG | |
27b207fd | 308 | printf ("clock/%d, prescale factor %d, reference %ld, ticks %ld\n", |
3b285da6 WD |
309 | (ticks > CPMT_MAX_TICKS) ? CPMT_CLOCK_DIV : 1, |
310 | CPMT_PRESCALER, | |
311 | (ticks / CPMT_PRESCALER), | |
312 | ticks | |
313 | ); | |
314 | #endif | |
315 | ||
316 | /* set prescaler register */ | |
317 | *hwp->tmrp = prescaler; | |
318 | ||
319 | /* clear timer counter */ | |
320 | *hwp->tcnp = 0; | |
321 | ||
322 | /* set reference register */ | |
323 | *hwp->trrp = (unsigned short) (ticks / CPMT_PRESCALER); | |
324 | ||
325 | #ifdef DEBUG | |
27b207fd | 326 | printf ("tgcr=0x%x, tmr=0x%x, trr=0x%x," |
3b285da6 WD |
327 | " tcr=0x%x, tcn=0x%x, ter=0x%x\n", |
328 | *hwp->tgcrp, *hwp->tmrp, *hwp->trrp, | |
329 | *hwp->tcrp, *hwp->tcnp, *hwp->terp | |
330 | ); | |
331 | #endif | |
332 | } | |
333 | ||
334 | /* | |
335 | * Handler for CPMVEC_TIMER1 interrupt | |
336 | */ | |
337 | static | |
338 | void timer_handler (void *arg) | |
339 | { | |
340 | tid_8xx_cpmtimer_t *hwp = (tid_8xx_cpmtimer_t *)arg; | |
341 | ||
342 | /* printf ("** TER1=%04x ** ", *hwp->terp); */ | |
343 | ||
344 | /* just for demonstration */ | |
27b207fd | 345 | printf ("."); |
3b285da6 WD |
346 | |
347 | /* clear all possible events: Ref. and Cap. */ | |
348 | *hwp->terp = (CPMT_EVENT_CAP | CPMT_EVENT_REF); | |
349 | } |