| /* | |
| * (C) Copyright 2000 | |
| * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | |
| * | |
| * SPDX-License-Identifier: GPL-2.0+ | |
| */ | |
| #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 const char usage[] = "\n[q, b, e, ?] "; | |
| int timer (int argc, char * const 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); | |
| puts(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"); | |
| } | |
| puts(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); | |
| } |