timer.c 8.9 KB

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