1 /*
2  * (C) Copyright 2000
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
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>
27 #include <exports.h>
28 
29 DECLARE_GLOBAL_DATA_PTR;
30 
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 */
get_msr(void)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 
set_msr(unsigned long msr)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 
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 
timer(int argc,char * argv[])118 int timer (int argc, char *argv[])
119 {
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;
124 	int running;
125 
126 	app_startup(argv);
127 
128 	/* Pointer to CPM Timer structure */
129 	cpmtimerp = &((immap_t *) gd->bd->bi_immr_base)->im_cpmtimer;
130 
131 	printf ("TIMERS=0x%x\n", (unsigned) cpmtimerp);
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 
171 	printf ("Using timer %d\n"
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 
189 	printf (usage);
190 	running = 0;
191 	while ((c = getc()) != 'q') {
192 	    if (c == 'b') {
193 
194 		setPeriod (hwp, TIMER_PERIOD);	/* Set period and start ticking */
195 
196 		/* Install interrupt handler (enable timer in CIMR) */
197 		install_hdlr (hwp->cpm_vec, timer_handler, hwp);
198 
199 		printf ("Enabling timer\n");
200 
201 		/* enable timer */
202 		*hwp->tgcrp |= (CPMT_GCR_RST << TID_TIMER_ID);
203 		running = 1;
204 
205 #ifdef	DEBUG
206 		printf ("tgcr=0x%x, tmr=0x%x, trr=0x%x,"
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 
214 		printf ("Stopping timer\n");
215 
216 		*hwp->tgcrp &= ~(CPMT_GCR_MASK << TID_TIMER_ID);
217 		running = 0;
218 
219 #ifdef	DEBUG
220 		printf ("tgcr=0x%x, tmr=0x%x, trr=0x%x,"
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 */
227 		free_hdlr (hwp->cpm_vec);
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 
235 		printf ("\ntgcr=0x%x, tmr=0x%x, trr=0x%x,"
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
241 		printf ("SIUMCR=0x%08lx, SYPCR=0x%08lx,"
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 
249 		printf ("CIMR=0x%08lx, CICR=0x%08lx, CIPR=0x%08lx\n",
250 			cpm_icp->cpic_cimr,
251 			cpm_icp->cpic_cicr,
252 			cpm_icp->cpic_cipr
253 			);
254 #endif
255 	    } else {
256 		printf ("\nEnter: q - quit, b - start timer, e - stop timer, ? - get status\n");
257 	    }
258 	    printf (usage);
259 	}
260 	if (running) {
261 		printf ("Stopping timer\n");
262 		*hwp->tgcrp &= ~(CPMT_GCR_MASK << TID_TIMER_ID);
263 		free_hdlr (hwp->cpm_vec);
264 	}
265 
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 
setPeriod(tid_8xx_cpmtimer_t * hwp,ulong interval)274 void setPeriod (tid_8xx_cpmtimer_t *hwp, ulong interval)
275 {
276 	unsigned short prescaler;
277 	unsigned long ticks;
278 
279 	printf ("Set interval %ld us\n", interval);
280 
281 	/* Warn if requesting longer period than possible */
282 	if (interval > CPMT_MAX_INTERVAL) {
283 		printf ("Truncate interval %ld to maximum (%d)\n",
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
308 	printf ("clock/%d, prescale factor %d, reference %ld, ticks %ld\n",
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
326 	printf ("tgcr=0x%x, tmr=0x%x, trr=0x%x,"
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
timer_handler(void * arg)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 */
345 	printf (".");
346 
347 	/* clear all possible events: Ref. and Cap. */
348 	*hwp->terp = (CPMT_EVENT_CAP | CPMT_EVENT_REF);
349 }
350