summaryrefslogtreecommitdiffstats
path: root/nuttx/arch/arm/src/str71x/str71x_timerisr.c
blob: 8f41b929127efc9af3c903432acb2dd342f7e4e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/****************************************************************************
 * arch/arm/src/str71x/str71x_timerisr.c
 *
 *   Copyright (C) 2007-2009 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <stdint.h>
#include <time.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <arch/board/board.h>

#include "chip.h"
#include "up_arch.h"
#include "clock_internal.h"
#include "up_internal.h"

#include "str71x_internal.h"

/****************************************************************************
 * Pre-procesor Definitions
 ****************************************************************************/

/* Configuration */

#ifndef CONFIG_TIM_PRI
#  define CONFIG_TIM_PRI 1
#elif CONFIG_TIM_PRI <= 1 || CONFIG_TIM_PRI > 15
#  error "CONFIG_TIM_PRI is out of range"
#endif

/* The desired timer interrupt frequency is provided by the definition
 * CLK_TCK (see include/time.h).  CLK_TCK defines the desired number of
 * system clock ticks per second.  That value is a user configurable setting
 * that defaults to 100 (100 ticks per second = 10 MS interval).
 *
 * The best accuracy would be obtained by using the largest value in the
 * the output compare register (OCAR), i.e., 0xffff = 65,535:
 */

#define MAX_OCAR    65535

 /* In this case, the desired, maximum clocking would be MAX_TIM0CLK.  For
  * example if CLK_TCK is the default of 100Hz, then the ideal clocking for
  * timer0 would be 6,553,500 */

#define MAX_TIM0CLK (MAX_OCAR * CLK_TCK)

 /* The best divider then would be the one that reduces PCLK2 to MAX_TIM0CLK.
  * Note that the following calculation forces an integer divisor to the next
  * integer above the optimal.  So, for example, if MAX_TIM0CLK is 6,553,500
  * and PCLK2 is 32MHz, then ideal PCLK2_DIVIDER would be 4.88 but 5 is used
  * instead.  The value 5 would give an actual TIM0CLK of 6,400,000, less
  * than the maximum.
  */

#if STR71X_PCLK2 > MAX_TIM0CLK
#  define PCLK2_DIVIDER (((STR71X_PCLK2) + (MAX_TIM0CLK+1)) / MAX_TIM0CLK)
#else
#  define PCLK2_DIVIDER (1)
#endif

#if PCLK2_DIVIDER > 255
#  error "PCLK2 is too fast for any divisor"
#endif

  /* Then we can get the actual OCAR value from the selected divider value.
   * For example, if PCLK2 is 32MHz and PCLK2_DIVIDER is 5, then the actual
   * TIM0CLK would 6,4000,000 and the final OCAR_VALUE would be 64,000.
   */

#define ACTUAL_TIM0CLK (STR71X_PCLK2 / PCLK2_DIVIDER)
#define OCAR_VALUE     (ACTUAL_TIM0CLK / CLK_TCK)

#if OCAR_VALUE > 65535
#  error "PCLK2 is too fast for the configured CLK_TCK"
#endif

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/****************************************************************************
 * Global Functions
 ****************************************************************************/

/****************************************************************************
 * Function:  up_timerisr
 *
 * Description:
 *   The timer ISR will perform a variety of services for various portions
 *   of the systems.
 *
 ****************************************************************************/

int up_timerisr(int irq, uint32_t *regs)
{
  uint16_t ocar;

  /* Clear all the output compare A interrupt status bit */

  putreg16(~STR71X_TIMERSR_OCFA, STR71X_TIMER0_SR);

  /* Set up for the next compare match.  We could either reset
   * the OCAR and CNTR to restart, or simply update the OCAR as
   * follows to that the match occurs later without resetting:
   */

  ocar = getreg16(STR71X_TIMER0_OCAR);
  ocar += OCAR_VALUE;
  putreg16(ocar, STR71X_TIMER0_OCAR);

  /* Process timer interrupt */

  sched_process_timer();
  return 0;
}

/****************************************************************************
 * Function:  up_timerinit
 *
 * Description:
 *   This function is called during start-up to initialize
 *   the timer interrupt.
 *
 ****************************************************************************/

void up_timerinit(void)
{
  irqstate_t flags;

  /* Make sure that timer0 is disabled */

  flags = irqsave();
  putreg16(0x0000, STR71X_TIMER0_CR1);
  putreg16(0x0000, STR71X_TIMER0_CR2);
  putreg16(0x0000, STR71X_TIMER0_SR);

 /* Configure TIM0 so that it is clocked by the internal APB2 frequency (PCLK2)
  * divided by the above prescaler value (1) -- versus an external Clock.
  * -- Nothing to do because  STR71X_TIMERCR1_ECKEN is already cleared.
  *
  * Select a divisor to reduce the frequency of clocking.  This must be
  * done so that the entire timer interval can fit in the 16-bit OCAR register.
  * (see the discussion above).
  */

  putreg16(STR71X_TIMERCR2_OCAIE | (PCLK2_DIVIDER - 1), STR71X_TIMER0_CR2);

  /* Start The TIM0 Counter and enable the output comparison A */

  putreg16(STR71X_TIMERCR1_EN | STR71X_TIMERCR1_OCAE, STR71X_TIMER0_CR1);

  /* Setup output compare A for desired interrupt frequency.  Note that
   * the OCAE and OCBE bits are cleared and the pins are available for other
   * functions.
   */

  putreg16(OCAR_VALUE, STR71X_TIMER0_OCAR);
  putreg16(0xfffc, STR71X_TIMER0_CNTR);

  /* Set the timer interrupt priority */

  up_prioritize_irq(STR71X_IRQ_SYSTIMER, CONFIG_TIM_PRI);

  /* Attach the timer interrupt vector */

  (void)irq_attach(STR71X_IRQ_SYSTIMER, (xcpt_t)up_timerisr);

  /* And enable the timer interrupt */

  up_enable_irq(STR71X_IRQ_SYSTIMER);
  irqrestore(flags);
}