diff options
Diffstat (limited to 'examples/standalone/timer.c')
| -rw-r--r-- | examples/standalone/timer.c | 349 | 
1 files changed, 349 insertions, 0 deletions
| diff --git a/examples/standalone/timer.c b/examples/standalone/timer.c new file mode 100644 index 000000000..6628b21de --- /dev/null +++ b/examples/standalone/timer.c @@ -0,0 +1,349 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <commproc.h> +#include <mpc8xx_irq.h> +#include <exports.h> + +DECLARE_GLOBAL_DATA_PTR; + +#undef	DEBUG + +#define	TIMER_PERIOD	1000000		/* 1 second clock */ + +static void timer_handler (void *arg); + + +/* Access functions for the Machine State Register */ +static __inline__ unsigned long get_msr(void) +{ +    unsigned long msr; + +    asm volatile("mfmsr %0" : "=r" (msr) :); +    return msr; +} + +static __inline__ void set_msr(unsigned long msr) +{ +    asm volatile("mtmsr %0" : : "r" (msr)); +} + +/* + * Definitions to access the CPM Timer registers + * See 8xx_immap.h for Internal Memory Map layout, + * and commproc.h for CPM Interrupt vectors (aka "IRQ"s) + */ + +typedef struct tid_8xx_cpmtimer_s { +  int		 cpm_vec;	/* CPM Interrupt Vector for this timer	*/ +  ushort	*tgcrp;		/* Pointer to Timer Global Config Reg.	*/ +  ushort	*tmrp;		/* Pointer to Timer Mode Register	*/ +  ushort	*trrp;		/* Pointer to Timer Reference Register	*/ +  ushort	*tcrp;		/* Pointer to Timer Capture Register	*/ +  ushort	*tcnp;		/* Pointer to Timer Counter Register	*/ +  ushort	*terp;		/* Pointer to Timer Event Register	*/ +} tid_8xx_cpmtimer_t; + +#ifndef CLOCKRATE +#  define CLOCKRATE 64 +#endif + +#define	CPMT_CLOCK_DIV		16 +#define	CPMT_MAX_PRESCALER	256 +#define CPMT_MAX_REFERENCE	65535	/* max. unsigned short */ + +#define	CPMT_MAX_TICKS		(CPMT_MAX_REFERENCE * CPMT_MAX_PRESCALER) +#define	CPMT_MAX_TICKS_WITH_DIV	(CPMT_MAX_REFERENCE * CPMT_MAX_PRESCALER * CPMT_CLOCK_DIV) +#define	CPMT_MAX_INTERVAL	(CPMT_MAX_TICKS_WITH_DIV / CLOCKRATE) + +/* For now: always use max. prescaler value */ +#define	CPMT_PRESCALER		(CPMT_MAX_PRESCALER) + +/* CPM Timer Event Register Bits */ +#define	CPMT_EVENT_CAP		0x0001	/* Capture Event		*/ +#define	CPMT_EVENT_REF		0x0002	/* Reference Counter Event	*/ + +/* CPM Timer Global Config Register */ +#define	CPMT_GCR_RST		0x0001	/* Reset  Timer			*/ +#define	CPMT_GCR_STP		0x0002	/* Stop   Timer			*/ +#define	CPMT_GCR_FRZ		0x0004	/* Freeze Timer			*/ +#define	CPMT_GCR_GM_CAS		0x0008	/* Gate Mode / Cascade Timers	*/ +#define	CPMT_GCR_MASK		(CPMT_GCR_RST|CPMT_GCR_STP|CPMT_GCR_FRZ|CPMT_GCR_GM_CAS) + +/* CPM Timer Mode register */ +#define	CPMT_MR_GE		0x0001	/* Gate Enable			*/ +#define	CPMT_MR_ICLK_CASC	0x0000	/* Clock internally cascaded	*/ +#define	CPMT_MR_ICLK_CLK	0x0002	/* Clock = system clock		*/ +#define	CPMT_MR_ICLK_CLKDIV	0x0004	/* Clock = system clock / 16	*/ +#define	CPMT_MR_ICLK_TIN	0x0006	/* Clock = TINx signal		*/ +#define	CPMT_MR_FRR		0x0008	/* Free Run / Restart		*/ +#define	CPMT_MR_ORI		0x0010	/* Out. Reference Interrupt En.	*/ +#define	CPMT_MR_OM		0x0020	/* Output Mode			*/ +#define	CPMT_MR_CE_DIS		0x0000	/* Capture/Interrupt disabled	*/ +#define	CPMT_MR_CE_RISE		0x0040	/* Capt./Interr. on rising  TIN	*/ +#define CPMT_MR_CE_FALL		0x0080	/* Capt./Interr. on falling TIN	*/ +#define	CPMT_MR_CE_ANY		0x00C0	/* Capt./Interr. on any TIN edge*/ + + +/* + * which CPM timer to use - index starts at 0 (= timer 1) + */ +#define	TID_TIMER_ID	0	/* use CPM timer 1		*/ + +void setPeriod (tid_8xx_cpmtimer_t *hwp, ulong interval); + +static char *usage = "\n[q, b, e, ?] "; + +int timer (int argc, char *argv[]) +{ +	cpmtimer8xx_t *cpmtimerp;	/* Pointer to the CPM Timer structure   */ +	tid_8xx_cpmtimer_t hw; +	tid_8xx_cpmtimer_t *hwp = &hw; +	int c; +	int running; + +	app_startup(argv); + +	/* Pointer to CPM Timer structure */ +	cpmtimerp = &((immap_t *) gd->bd->bi_immr_base)->im_cpmtimer; + +	printf ("TIMERS=0x%x\n", (unsigned) cpmtimerp); + +	/* Initialize pointers depending on which timer we use */ +	switch (TID_TIMER_ID) { +	case 0: +		hwp->tmrp = &(cpmtimerp->cpmt_tmr1); +		hwp->trrp = &(cpmtimerp->cpmt_trr1); +		hwp->tcrp = &(cpmtimerp->cpmt_tcr1); +		hwp->tcnp = &(cpmtimerp->cpmt_tcn1); +		hwp->terp = &(cpmtimerp->cpmt_ter1); +		hwp->cpm_vec = CPMVEC_TIMER1; +		break; +	case 1: +		hwp->tmrp = &(cpmtimerp->cpmt_tmr2); +		hwp->trrp = &(cpmtimerp->cpmt_trr2); +		hwp->tcrp = &(cpmtimerp->cpmt_tcr2); +		hwp->tcnp = &(cpmtimerp->cpmt_tcn2); +		hwp->terp = &(cpmtimerp->cpmt_ter2); +		hwp->cpm_vec = CPMVEC_TIMER2; +		break; +	case 2: +		hwp->tmrp = &(cpmtimerp->cpmt_tmr3); +		hwp->trrp = &(cpmtimerp->cpmt_trr3); +		hwp->tcrp = &(cpmtimerp->cpmt_tcr3); +		hwp->tcnp = &(cpmtimerp->cpmt_tcn3); +		hwp->terp = &(cpmtimerp->cpmt_ter3); +		hwp->cpm_vec = CPMVEC_TIMER3; +		break; +	case 3: +		hwp->tmrp = &(cpmtimerp->cpmt_tmr4); +		hwp->trrp = &(cpmtimerp->cpmt_trr4); +		hwp->tcrp = &(cpmtimerp->cpmt_tcr4); +		hwp->tcnp = &(cpmtimerp->cpmt_tcn4); +		hwp->terp = &(cpmtimerp->cpmt_ter4); +		hwp->cpm_vec = CPMVEC_TIMER4; +		break; +	} + +	hwp->tgcrp = &cpmtimerp->cpmt_tgcr; + +	printf ("Using timer %d\n" +			"tgcr @ 0x%x, tmr @ 0x%x, trr @ 0x%x," +			" tcr @ 0x%x, tcn @ 0x%x, ter @ 0x%x\n", +			TID_TIMER_ID + 1, +			(unsigned) hwp->tgcrp, +			(unsigned) hwp->tmrp, +			(unsigned) hwp->trrp, +			(unsigned) hwp->tcrp, +			(unsigned) hwp->tcnp, +			(unsigned) hwp->terp +			); + +	/* reset timer    */ +	*hwp->tgcrp &= ~(CPMT_GCR_MASK << TID_TIMER_ID); + +	/* clear all events */ +	*hwp->terp = (CPMT_EVENT_CAP | CPMT_EVENT_REF); + +	printf (usage); +	running = 0; +	while ((c = getc()) != 'q') { +	    if (c == 'b') { + +		setPeriod (hwp, TIMER_PERIOD);	/* Set period and start ticking */ + +		/* Install interrupt handler (enable timer in CIMR) */ +		install_hdlr (hwp->cpm_vec, timer_handler, hwp); + +		printf ("Enabling timer\n"); + +		/* enable timer */ +		*hwp->tgcrp |= (CPMT_GCR_RST << TID_TIMER_ID); +		running = 1; + +#ifdef	DEBUG +		printf ("tgcr=0x%x, tmr=0x%x, trr=0x%x," +			" tcr=0x%x, tcn=0x%x, ter=0x%x\n", +				*hwp->tgcrp, *hwp->tmrp, *hwp->trrp, +				*hwp->tcrp,  *hwp->tcnp, *hwp->terp +				); +#endif +	    } else if (c == 'e') { + +		printf ("Stopping timer\n"); + +		*hwp->tgcrp &= ~(CPMT_GCR_MASK << TID_TIMER_ID); +		running = 0; + +#ifdef	DEBUG +		printf ("tgcr=0x%x, tmr=0x%x, trr=0x%x," +			" tcr=0x%x, tcn=0x%x, ter=0x%x\n", +				*hwp->tgcrp, *hwp->tmrp, *hwp->trrp, +				*hwp->tcrp,  *hwp->tcnp, *hwp->terp +			); +#endif +		/* Uninstall interrupt handler */ +		free_hdlr (hwp->cpm_vec); + +	    } else if (c == '?') { +#ifdef	DEBUG +		cpic8xx_t *cpm_icp = &((immap_t *) gd->bd->bi_immr_base)->im_cpic; +		sysconf8xx_t *siup = &((immap_t *) gd->bd->bi_immr_base)->im_siu_conf; +#endif + +		printf ("\ntgcr=0x%x, tmr=0x%x, trr=0x%x," +			" tcr=0x%x, tcn=0x%x, ter=0x%x\n", +				*hwp->tgcrp, *hwp->tmrp, *hwp->trrp, +				*hwp->tcrp,  *hwp->tcnp, *hwp->terp +			); +#ifdef	DEBUG +		printf ("SIUMCR=0x%08lx, SYPCR=0x%08lx," +			" SIMASK=0x%08lx, SIPEND=0x%08lx\n", +				siup->sc_siumcr, +				siup->sc_sypcr, +				siup->sc_simask, +				siup->sc_sipend +			); + +		printf ("CIMR=0x%08lx, CICR=0x%08lx, CIPR=0x%08lx\n", +			cpm_icp->cpic_cimr, +			cpm_icp->cpic_cicr, +			cpm_icp->cpic_cipr +			); +#endif +	    } else { +		printf ("\nEnter: q - quit, b - start timer, e - stop timer, ? - get status\n"); +	    } +	    printf (usage); +	} +	if (running) { +		printf ("Stopping timer\n"); +		*hwp->tgcrp &= ~(CPMT_GCR_MASK << TID_TIMER_ID); +		free_hdlr (hwp->cpm_vec); +	} + +	return (0); +} + + +/* Set period in microseconds and start. + * Truncate to maximum period if more than this is requested - but warn about it. + */ + +void setPeriod (tid_8xx_cpmtimer_t *hwp, ulong interval) +{ +	unsigned short prescaler; +	unsigned long ticks; + +	printf ("Set interval %ld us\n", interval); + +	/* Warn if requesting longer period than possible */ +	if (interval > CPMT_MAX_INTERVAL) { +		printf ("Truncate interval %ld to maximum (%d)\n", +				interval, CPMT_MAX_INTERVAL); +		interval = CPMT_MAX_INTERVAL; +	} +	/* +	 * Check if we want to use clock divider: +	 * Since the reference counter can be incremented only in integer steps, +	 * we try to keep it as big as possible to allow the resulting period to be +	 * as precise as possible. +	 */ +	/* prescaler, enable interrupt, restart after ref count is reached */ +	prescaler = (ushort) ((CPMT_PRESCALER - 1) << 8) | +			CPMT_MR_ORI | +			CPMT_MR_FRR; + +	ticks = ((ulong) CLOCKRATE * interval); + +	if (ticks > CPMT_MAX_TICKS) { +		ticks /= CPMT_CLOCK_DIV; +		prescaler |= CPMT_MR_ICLK_CLKDIV;	/* use system clock divided by 16 */ +	} else { +		prescaler |= CPMT_MR_ICLK_CLK;	/* use system clock without divider */ +	} + +#ifdef	DEBUG +	printf ("clock/%d, prescale factor %d, reference %ld, ticks %ld\n", +			(ticks > CPMT_MAX_TICKS) ? CPMT_CLOCK_DIV : 1, +			CPMT_PRESCALER, +			(ticks / CPMT_PRESCALER), +			ticks +			); +#endif + +	/* set prescaler register */ +	*hwp->tmrp = prescaler; + +	/* clear timer counter */ +	*hwp->tcnp = 0; + +	/* set reference register */ +	*hwp->trrp = (unsigned short) (ticks / CPMT_PRESCALER); + +#ifdef	DEBUG +	printf ("tgcr=0x%x, tmr=0x%x, trr=0x%x," +		" tcr=0x%x, tcn=0x%x, ter=0x%x\n", +			*hwp->tgcrp, *hwp->tmrp, *hwp->trrp, +			*hwp->tcrp,  *hwp->tcnp, *hwp->terp +		); +#endif +} + +/* + * Handler for CPMVEC_TIMER1 interrupt + */ +static +void timer_handler (void *arg) +{ +	tid_8xx_cpmtimer_t *hwp = (tid_8xx_cpmtimer_t *)arg; + +	/* printf ("** TER1=%04x ** ", *hwp->terp); */ + +	/* just for demonstration */ +	printf ("."); + +	/* clear all possible events: Ref. and Cap. */ +	*hwp->terp = (CPMT_EVENT_CAP | CPMT_EVENT_REF); +} |