SAMA5: Fix bugs in timer/counter interrupts and one-shot timer
This commit is contained in:
parent
9cb0b680ac
commit
42b1bcdf33
@ -69,6 +69,8 @@
|
||||
#ifdef CONFIG_SAMA5_TC_DEBUG
|
||||
# define tcdbg dbg
|
||||
# define tcvdbg vdbg
|
||||
# define tcdbg lldbg
|
||||
# define tcvdbg llvdbg
|
||||
# define tclldbg lldbg
|
||||
# define tcllvdbg llvdbg
|
||||
#else
|
||||
|
@ -102,32 +102,44 @@
|
||||
* level up.
|
||||
*
|
||||
* Input Parameters:
|
||||
* handle - The handle that represents the timer state
|
||||
* arg - An opaque argument provided when the interrupt was registered
|
||||
* sr - The value of the timer interrupt status register at the time
|
||||
* that the interrupt occurred.
|
||||
* tch - The handle that represents the timer state
|
||||
* arg - An opaque argument provided when the interrupt was registered
|
||||
* sr - The value of the timer interrupt status register at the time
|
||||
* that the interrupt occurred.
|
||||
*
|
||||
* Returned Value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void sam_oneshot_handler(TC_HANDLE handle, void *arg, uint32_t sr)
|
||||
static void sam_oneshot_handler(TC_HANDLE tch, void *arg, uint32_t sr)
|
||||
{
|
||||
struct sam_oneshot_s *oneshot = (struct sam_oneshot_s *)arg;
|
||||
oneshot_handler_t oneshot_handler;
|
||||
void *oneshot_arg;
|
||||
|
||||
tcllvdbg("Expired...\n");
|
||||
DEBUGASSERT(oneshot && oneshot->handler);
|
||||
|
||||
/* The clock was stopped, but not disabled when the RC match occurred.
|
||||
* Disable the TC now and disable any further interrupts.
|
||||
*/
|
||||
|
||||
sam_tc_attach(oneshot->handler, NULL, NULL, 0);
|
||||
sam_tc_stop(oneshot->handle);
|
||||
sam_tc_attach(oneshot->tch, NULL, NULL, 0);
|
||||
sam_tc_stop(oneshot->tch);
|
||||
|
||||
/* Forward the event */
|
||||
/* The timer is no longer running */
|
||||
|
||||
oneshot->running = false;
|
||||
oneshot->handler(oneshot->arg);
|
||||
|
||||
/* Forward the event, clearing out any vestiges */
|
||||
|
||||
oneshot_handler = oneshot->handler;
|
||||
oneshot->handler = NULL;
|
||||
oneshot_arg = oneshot->arg;
|
||||
oneshot->arg = NULL;
|
||||
|
||||
oneshot_handler(oneshot_arg);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -210,8 +222,8 @@ int sam_oneshot_initialize(struct sam_oneshot_s *oneshot, int chan,
|
||||
TC_CMR_ASWTRG_NONE | TC_CMR_BCPB_NONE | TC_CMR_BCPC_NONE |
|
||||
TC_CMR_BEEVT_NONE | TC_CMR_BSWTRG_NONE);
|
||||
|
||||
oneshot->handle = sam_tc_allocate(chan, cmr);
|
||||
if (!oneshot->handle)
|
||||
oneshot->tch = sam_tc_allocate(chan, cmr);
|
||||
if (!oneshot->tch)
|
||||
{
|
||||
tcdbg("ERROR: Failed to allocate timer channel %d\n", chan);
|
||||
return -EBUSY;
|
||||
@ -270,6 +282,11 @@ int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler,
|
||||
(void)sam_oneshot_cancel(oneshot, NULL);
|
||||
}
|
||||
|
||||
/* Save the new handler and its argument */
|
||||
|
||||
oneshot->handler = handler;
|
||||
oneshot->arg = arg;
|
||||
|
||||
/* We configured the counter to run with an LSB of the specified
|
||||
* resolution. We now must need need to set RC to the number
|
||||
* of resolution units corresponding to the requested delay.
|
||||
@ -278,23 +295,24 @@ int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler,
|
||||
usec = (uint64_t)ts->tv_sec * 1000000 + (uint64_t)(ts->tv_nsec / 1000);
|
||||
regval = usec / oneshot->resolution;
|
||||
|
||||
tcdbg("usec=%llu regval=%08llx\n", usec, regval);
|
||||
tcdbg("usec=%lu regval=%08lx\n",
|
||||
(unsigned long)usec, (unsigned long)regval);
|
||||
DEBUGASSERT(regval <= UINT32_MAX);
|
||||
|
||||
/* Set up to receive the callback when the interrupt occurs */
|
||||
|
||||
(void)sam_tc_attach(oneshot->handle, sam_oneshot_handler, oneshot,
|
||||
(void)sam_tc_attach(oneshot->tch, sam_oneshot_handler, oneshot,
|
||||
TC_INT_CPCS);
|
||||
|
||||
/* Set RC so that an event will be triggered when TC_CV register counts
|
||||
* up to RC.
|
||||
*/
|
||||
|
||||
sam_tc_setregister(oneshot->handle, TC_REGC, regval);
|
||||
sam_tc_setregister(oneshot->tch, TC_REGC, (uint32_t)regval);
|
||||
|
||||
/* Start the counter */
|
||||
|
||||
sam_tc_start(oneshot->handle);
|
||||
sam_tc_start(oneshot->tch);
|
||||
|
||||
/* Enable interrupts. We should get the callback when the interrupt
|
||||
* occurs.
|
||||
@ -311,16 +329,21 @@ int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler,
|
||||
* Description:
|
||||
* Cancel the oneshot timer and return the time remaining on the timer.
|
||||
*
|
||||
* NOTE: This function may execute at a high rate with no timer running (as
|
||||
* when pre-emption is enabled and disabled).
|
||||
*
|
||||
* Input Parameters:
|
||||
* oneshot Caller allocated instance of the oneshot state structure. This
|
||||
* structure must have been previously initialized via a call to
|
||||
* sam_oneshot_initialize();
|
||||
* ts The location in which to return the time remaining on the
|
||||
* oneshot timer.
|
||||
* oneshot timer. A time of zero is returned if the timer is
|
||||
* not running.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated errno value is returned
|
||||
* on failure.
|
||||
* Zero (OK) is returned on success. A call to up_timer_cancel() when
|
||||
* the timer is not active should also return success; a negated errno
|
||||
* value is returned on any failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
@ -332,16 +355,35 @@ int sam_oneshot_cancel(struct sam_oneshot_s *oneshot, struct timespec *ts)
|
||||
uint32_t usec;
|
||||
uint32_t sec;
|
||||
|
||||
/* Get the timer counter and rc registers and stop the counter. If the
|
||||
* counter expires while we are doing this, the counter clock will be
|
||||
* stopped, but the clock will not be disabled.
|
||||
*
|
||||
* REVISIT: Will the counter value be reset to zero?
|
||||
*/
|
||||
/* Was the timer running? */
|
||||
|
||||
flags = irqsave();
|
||||
count = sam_tc_getcounter(oneshot->handle);
|
||||
rc = sam_tc_getregister(oneshot->handle, TC_REGC);
|
||||
if (!oneshot->running)
|
||||
{
|
||||
/* No.. Just return zero timer remaining and successful cancellation.
|
||||
* This function may execute at a high rate with no timer running
|
||||
* (as when pre-emption is enabled and disabled).
|
||||
*/
|
||||
|
||||
ts->tv_sec = 0;
|
||||
ts->tv_nsec = 0;
|
||||
irqrestore(flags);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Yes.. Get the timer counter and rc registers and stop the counter. If
|
||||
* the counter expires while we are doing this, the counter clock will be
|
||||
* stopped, but the clock will not be disabled.
|
||||
*
|
||||
* NOTE: This is not documented, but I have observed in this case that the
|
||||
* counter register freezes at a value equal to the RC register. The
|
||||
* following logic depends on this fact.
|
||||
*/
|
||||
|
||||
tcvdbg("Canceling...\n");
|
||||
|
||||
count = sam_tc_getcounter(oneshot->tch);
|
||||
rc = sam_tc_getregister(oneshot->tch, TC_REGC);
|
||||
|
||||
/* Now we can disable the interrupt and stop the timer.
|
||||
*
|
||||
@ -350,11 +392,13 @@ int sam_oneshot_cancel(struct sam_oneshot_s *oneshot, struct timespec *ts)
|
||||
* clock will be disabled, so I am hoping not.
|
||||
*/
|
||||
|
||||
DEBUGASSERT(count > 0 || (sam_tc_getpending(oneshot->handle) & TC_INT_CPCS) == 0);
|
||||
sam_tc_attach(oneshot->handle, NULL, NULL, 0);
|
||||
sam_tc_stop(oneshot->handle);
|
||||
DEBUGASSERT(count > 0 || (sam_tc_getpending(oneshot->tch) & TC_INT_CPCS) == 0);
|
||||
sam_tc_attach(oneshot->tch, NULL, NULL, 0);
|
||||
sam_tc_stop(oneshot->tch);
|
||||
|
||||
oneshot->running = false;
|
||||
oneshot->handler = NULL;
|
||||
oneshot->arg = NULL;
|
||||
irqrestore(flags);
|
||||
|
||||
/* The total time remaining is the difference */
|
||||
|
@ -73,13 +73,15 @@ typedef void (*oneshot_handler_t)(void *arg);
|
||||
|
||||
struct sam_oneshot_s
|
||||
{
|
||||
uint8_t chan; /* The timer/counter in use */
|
||||
bool running; /* True: the timer is running */
|
||||
uint16_t resolution; /* Timer resolution in microseconds */
|
||||
uint32_t divisor; /* TC divisor derived from resolution */
|
||||
TC_HANDLE handle; /* Handle returned by sam_tc_initialize() */
|
||||
oneshot_handler_t handler; /* Oneshot expiration callback */
|
||||
void *arg; /* The argument that will accompany the callback */
|
||||
uint8_t chan; /* The timer/counter in use */
|
||||
volatile bool running; /* True: the timer is running */
|
||||
uint16_t resolution; /* Timer resolution in microseconds */
|
||||
uint32_t divisor; /* TC divisor derived from resolution */
|
||||
TC_HANDLE tch; /* Handle returned by
|
||||
* sam_tc_initialize() */
|
||||
volatile oneshot_handler_t handler; /* Oneshot expiration callback */
|
||||
volatile void *arg; /* The argument that will accompany
|
||||
* the callback */
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
@ -151,16 +153,21 @@ int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler,
|
||||
* Description:
|
||||
* Cancel the oneshot timer and return the time remaining on the timer.
|
||||
*
|
||||
* NOTE: This function may execute at a high rate with no timer running (as
|
||||
* when pre-emption is enabled and disabled).
|
||||
*
|
||||
* Input Parameters:
|
||||
* oneshot Caller allocated instance of the oneshot state structure. This
|
||||
* structure must have been previously initialized via a call to
|
||||
* sam_oneshot_initialize();
|
||||
* ts The location in which to return the time remaining on the
|
||||
* oneshot timer.
|
||||
* oneshot timer. A time of zero is returned if the timer is
|
||||
* not running.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated errno value is returned
|
||||
* on failure.
|
||||
* Zero (OK) is returned on success. A call to up_timer_cancel() when
|
||||
* the timer is not active should also return success; a negated errno
|
||||
* value is returned on any failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
@ -1264,8 +1264,8 @@ tc_handler_t sam_tc_attach(TC_HANDLE handle, tc_handler_t handler,
|
||||
|
||||
/* Remember the old interrupt handler and set the new handler */
|
||||
|
||||
flags = irqsave();
|
||||
oldhandler = handler;
|
||||
flags = irqsave();
|
||||
oldhandler = chan->handler;
|
||||
chan->handler = handler;
|
||||
|
||||
/* Don't enable interrupt if we are detaching no matter what the caller
|
||||
@ -1278,6 +1278,8 @@ tc_handler_t sam_tc_attach(TC_HANDLE handle, tc_handler_t handler,
|
||||
mask = 0;
|
||||
}
|
||||
|
||||
chan->arg = arg;
|
||||
|
||||
/* Now enable interrupt as requested */
|
||||
|
||||
sam_chan_putreg(chan, SAM_TC_IDR_OFFSET, TC_INT_ALL & ~mask);
|
||||
|
@ -81,13 +81,13 @@ typedef void *TC_HANDLE;
|
||||
/* Timer interrupt callback. When a timer interrupt expires, the client will
|
||||
* receive:
|
||||
*
|
||||
* handle - The handle that represents the timer state
|
||||
* arg - An opaque argument provided when the interrupt was registered
|
||||
* sr - The value of the timer interrupt status register at the time
|
||||
* that the interrupt occurred.
|
||||
* tch - The handle that represents the timer state
|
||||
* arg - An opaque argument provided when the interrupt was registered
|
||||
* sr - The value of the timer interrupt status register at the time
|
||||
* that the interrupt occurred.
|
||||
*/
|
||||
|
||||
typedef void (*tc_handler_t)(TC_HANDLE handle, void *arg, uint32_t sr);
|
||||
typedef void (*tc_handler_t)(TC_HANDLE tch, void *arg, uint32_t sr);
|
||||
|
||||
/****************************************************************************
|
||||
* Public Data
|
||||
|
@ -151,6 +151,8 @@
|
||||
#ifdef CONFIG_SAMA5_TC_DEBUG
|
||||
# define tcdbg dbg
|
||||
# define tcvdbg vdbg
|
||||
# define tcdbg lldbg
|
||||
# define tcvdbg llvdbg
|
||||
# define tclldbg lldbg
|
||||
# define tcllvdbg llvdbg
|
||||
#else
|
||||
@ -204,6 +206,7 @@ struct sam_tickless_s g_tickless;
|
||||
|
||||
static void sam_oneshot_handler(void *arg)
|
||||
{
|
||||
tcllvdbg("Expired...\n");
|
||||
sched_timer_expiration();
|
||||
}
|
||||
|
||||
@ -315,6 +318,9 @@ int up_timer_gettime(FAR struct timespec *ts)
|
||||
* that up_timer_start() and the remaining time of zero should be
|
||||
* returned.
|
||||
*
|
||||
* NOTE: This function may execute at a high rate with no timer running (as
|
||||
* when pre-emption is enabled and disabled).
|
||||
*
|
||||
* Provided by platform-specific code and called from the RTOS base code.
|
||||
*
|
||||
* Input Parameters:
|
||||
@ -322,8 +328,9 @@ int up_timer_gettime(FAR struct timespec *ts)
|
||||
* if the timer is not active.
|
||||
*
|
||||
* Returned Value:
|
||||
* Zero (OK) is returned on success; a negated errno value is returned on
|
||||
* any failure.
|
||||
* Zero (OK) is returned on success. A call to up_timer_cancel() when
|
||||
* the timer is not active should also return success; a negated errno
|
||||
* value is returned on any failure.
|
||||
*
|
||||
* Assumptions:
|
||||
* May be called from interrupt level handling or from the normal tasking
|
||||
|
Loading…
Reference in New Issue
Block a user