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