SAMA5: Fix bugs in timer/counter interrupts and one-shot timer
This commit is contained in:
parent
6324df44e8
commit
320707fdfa
@ -69,6 +69,8 @@
|
|||||||
#ifdef CONFIG_SAMA5_TC_DEBUG
|
#ifdef CONFIG_SAMA5_TC_DEBUG
|
||||||
# define tcdbg dbg
|
# define tcdbg dbg
|
||||||
# define tcvdbg vdbg
|
# define tcvdbg vdbg
|
||||||
|
# define tcdbg lldbg
|
||||||
|
# define tcvdbg llvdbg
|
||||||
# define tclldbg lldbg
|
# define tclldbg lldbg
|
||||||
# define tcllvdbg llvdbg
|
# define tcllvdbg llvdbg
|
||||||
#else
|
#else
|
||||||
|
@ -102,32 +102,44 @@
|
|||||||
* level up.
|
* level up.
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
* handle - The handle that represents the timer state
|
* tch - The handle that represents the timer state
|
||||||
* arg - An opaque argument provided when the interrupt was registered
|
* arg - An opaque argument provided when the interrupt was registered
|
||||||
* sr - The value of the timer interrupt status register at the time
|
* sr - The value of the timer interrupt status register at the time
|
||||||
* that the interrupt occurred.
|
* that the interrupt occurred.
|
||||||
*
|
*
|
||||||
* Returned Value:
|
* Returned Value:
|
||||||
* None
|
* 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;
|
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);
|
DEBUGASSERT(oneshot && oneshot->handler);
|
||||||
|
|
||||||
/* The clock was stopped, but not disabled when the RC match occurred.
|
/* The clock was stopped, but not disabled when the RC match occurred.
|
||||||
* Disable the TC now and disable any further interrupts.
|
* Disable the TC now and disable any further interrupts.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
sam_tc_attach(oneshot->handler, NULL, NULL, 0);
|
sam_tc_attach(oneshot->tch, NULL, NULL, 0);
|
||||||
sam_tc_stop(oneshot->handle);
|
sam_tc_stop(oneshot->tch);
|
||||||
|
|
||||||
/* Forward the event */
|
/* The timer is no longer running */
|
||||||
|
|
||||||
oneshot->running = false;
|
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_ASWTRG_NONE | TC_CMR_BCPB_NONE | TC_CMR_BCPC_NONE |
|
||||||
TC_CMR_BEEVT_NONE | TC_CMR_BSWTRG_NONE);
|
TC_CMR_BEEVT_NONE | TC_CMR_BSWTRG_NONE);
|
||||||
|
|
||||||
oneshot->handle = sam_tc_allocate(chan, cmr);
|
oneshot->tch = sam_tc_allocate(chan, cmr);
|
||||||
if (!oneshot->handle)
|
if (!oneshot->tch)
|
||||||
{
|
{
|
||||||
tcdbg("ERROR: Failed to allocate timer channel %d\n", chan);
|
tcdbg("ERROR: Failed to allocate timer channel %d\n", chan);
|
||||||
return -EBUSY;
|
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);
|
(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
|
/* We configured the counter to run with an LSB of the specified
|
||||||
* resolution. We now must need need to set RC to the number
|
* resolution. We now must need need to set RC to the number
|
||||||
* of resolution units corresponding to the requested delay.
|
* 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);
|
usec = (uint64_t)ts->tv_sec * 1000000 + (uint64_t)(ts->tv_nsec / 1000);
|
||||||
regval = usec / oneshot->resolution;
|
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);
|
DEBUGASSERT(regval <= UINT32_MAX);
|
||||||
|
|
||||||
/* Set up to receive the callback when the interrupt occurs */
|
/* 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);
|
TC_INT_CPCS);
|
||||||
|
|
||||||
/* Set RC so that an event will be triggered when TC_CV register counts
|
/* Set RC so that an event will be triggered when TC_CV register counts
|
||||||
* up to RC.
|
* up to RC.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
sam_tc_setregister(oneshot->handle, TC_REGC, regval);
|
sam_tc_setregister(oneshot->tch, TC_REGC, (uint32_t)regval);
|
||||||
|
|
||||||
/* Start the counter */
|
/* Start the counter */
|
||||||
|
|
||||||
sam_tc_start(oneshot->handle);
|
sam_tc_start(oneshot->tch);
|
||||||
|
|
||||||
/* Enable interrupts. We should get the callback when the interrupt
|
/* Enable interrupts. We should get the callback when the interrupt
|
||||||
* occurs.
|
* occurs.
|
||||||
@ -311,16 +329,21 @@ int sam_oneshot_start(struct sam_oneshot_s *oneshot, oneshot_handler_t handler,
|
|||||||
* Description:
|
* Description:
|
||||||
* Cancel the oneshot timer and return the time remaining on the timer.
|
* 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:
|
* Input Parameters:
|
||||||
* oneshot Caller allocated instance of the oneshot state structure. This
|
* oneshot Caller allocated instance of the oneshot state structure. This
|
||||||
* structure must have been previously initialized via a call to
|
* structure must have been previously initialized via a call to
|
||||||
* sam_oneshot_initialize();
|
* sam_oneshot_initialize();
|
||||||
* ts The location in which to return the time remaining on the
|
* 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:
|
* Returned Value:
|
||||||
* Zero (OK) is returned on success; a negated errno value is returned
|
* Zero (OK) is returned on success. A call to up_timer_cancel() when
|
||||||
* on failure.
|
* 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 usec;
|
||||||
uint32_t sec;
|
uint32_t sec;
|
||||||
|
|
||||||
/* Get the timer counter and rc registers and stop the counter. If the
|
/* Was the timer running? */
|
||||||
* 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?
|
|
||||||
*/
|
|
||||||
|
|
||||||
flags = irqsave();
|
flags = irqsave();
|
||||||
count = sam_tc_getcounter(oneshot->handle);
|
if (!oneshot->running)
|
||||||
rc = sam_tc_getregister(oneshot->handle, TC_REGC);
|
{
|
||||||
|
/* 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.
|
/* 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.
|
* clock will be disabled, so I am hoping not.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DEBUGASSERT(count > 0 || (sam_tc_getpending(oneshot->handle) & TC_INT_CPCS) == 0);
|
DEBUGASSERT(count > 0 || (sam_tc_getpending(oneshot->tch) & TC_INT_CPCS) == 0);
|
||||||
sam_tc_attach(oneshot->handle, NULL, NULL, 0);
|
sam_tc_attach(oneshot->tch, NULL, NULL, 0);
|
||||||
sam_tc_stop(oneshot->handle);
|
sam_tc_stop(oneshot->tch);
|
||||||
|
|
||||||
oneshot->running = false;
|
oneshot->running = false;
|
||||||
|
oneshot->handler = NULL;
|
||||||
|
oneshot->arg = NULL;
|
||||||
irqrestore(flags);
|
irqrestore(flags);
|
||||||
|
|
||||||
/* The total time remaining is the difference */
|
/* The total time remaining is the difference */
|
||||||
|
@ -73,13 +73,15 @@ typedef void (*oneshot_handler_t)(void *arg);
|
|||||||
|
|
||||||
struct sam_oneshot_s
|
struct sam_oneshot_s
|
||||||
{
|
{
|
||||||
uint8_t chan; /* The timer/counter in use */
|
uint8_t chan; /* The timer/counter in use */
|
||||||
bool running; /* True: the timer is running */
|
volatile bool running; /* True: the timer is running */
|
||||||
uint16_t resolution; /* Timer resolution in microseconds */
|
uint16_t resolution; /* Timer resolution in microseconds */
|
||||||
uint32_t divisor; /* TC divisor derived from resolution */
|
uint32_t divisor; /* TC divisor derived from resolution */
|
||||||
TC_HANDLE handle; /* Handle returned by sam_tc_initialize() */
|
TC_HANDLE tch; /* Handle returned by
|
||||||
oneshot_handler_t handler; /* Oneshot expiration callback */
|
* sam_tc_initialize() */
|
||||||
void *arg; /* The argument that will accompany the callback */
|
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:
|
* Description:
|
||||||
* Cancel the oneshot timer and return the time remaining on the timer.
|
* 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:
|
* Input Parameters:
|
||||||
* oneshot Caller allocated instance of the oneshot state structure. This
|
* oneshot Caller allocated instance of the oneshot state structure. This
|
||||||
* structure must have been previously initialized via a call to
|
* structure must have been previously initialized via a call to
|
||||||
* sam_oneshot_initialize();
|
* sam_oneshot_initialize();
|
||||||
* ts The location in which to return the time remaining on the
|
* 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:
|
* Returned Value:
|
||||||
* Zero (OK) is returned on success; a negated errno value is returned
|
* Zero (OK) is returned on success. A call to up_timer_cancel() when
|
||||||
* on failure.
|
* 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 */
|
/* Remember the old interrupt handler and set the new handler */
|
||||||
|
|
||||||
flags = irqsave();
|
flags = irqsave();
|
||||||
oldhandler = handler;
|
oldhandler = chan->handler;
|
||||||
chan->handler = handler;
|
chan->handler = handler;
|
||||||
|
|
||||||
/* Don't enable interrupt if we are detaching no matter what the caller
|
/* 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;
|
mask = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chan->arg = arg;
|
||||||
|
|
||||||
/* Now enable interrupt as requested */
|
/* Now enable interrupt as requested */
|
||||||
|
|
||||||
sam_chan_putreg(chan, SAM_TC_IDR_OFFSET, TC_INT_ALL & ~mask);
|
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
|
/* Timer interrupt callback. When a timer interrupt expires, the client will
|
||||||
* receive:
|
* receive:
|
||||||
*
|
*
|
||||||
* handle - The handle that represents the timer state
|
* tch - The handle that represents the timer state
|
||||||
* arg - An opaque argument provided when the interrupt was registered
|
* arg - An opaque argument provided when the interrupt was registered
|
||||||
* sr - The value of the timer interrupt status register at the time
|
* sr - The value of the timer interrupt status register at the time
|
||||||
* that the interrupt occurred.
|
* 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
|
* Public Data
|
||||||
|
@ -151,6 +151,8 @@
|
|||||||
#ifdef CONFIG_SAMA5_TC_DEBUG
|
#ifdef CONFIG_SAMA5_TC_DEBUG
|
||||||
# define tcdbg dbg
|
# define tcdbg dbg
|
||||||
# define tcvdbg vdbg
|
# define tcvdbg vdbg
|
||||||
|
# define tcdbg lldbg
|
||||||
|
# define tcvdbg llvdbg
|
||||||
# define tclldbg lldbg
|
# define tclldbg lldbg
|
||||||
# define tcllvdbg llvdbg
|
# define tcllvdbg llvdbg
|
||||||
#else
|
#else
|
||||||
@ -204,6 +206,7 @@ struct sam_tickless_s g_tickless;
|
|||||||
|
|
||||||
static void sam_oneshot_handler(void *arg)
|
static void sam_oneshot_handler(void *arg)
|
||||||
{
|
{
|
||||||
|
tcllvdbg("Expired...\n");
|
||||||
sched_timer_expiration();
|
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
|
* that up_timer_start() and the remaining time of zero should be
|
||||||
* returned.
|
* 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.
|
* Provided by platform-specific code and called from the RTOS base code.
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
@ -322,8 +328,9 @@ int up_timer_gettime(FAR struct timespec *ts)
|
|||||||
* if the timer is not active.
|
* if the timer is not active.
|
||||||
*
|
*
|
||||||
* Returned Value:
|
* Returned Value:
|
||||||
* Zero (OK) is returned on success; a negated errno value is returned on
|
* Zero (OK) is returned on success. A call to up_timer_cancel() when
|
||||||
* any failure.
|
* the timer is not active should also return success; a negated errno
|
||||||
|
* value is returned on any failure.
|
||||||
*
|
*
|
||||||
* Assumptions:
|
* Assumptions:
|
||||||
* May be called from interrupt level handling or from the normal tasking
|
* May be called from interrupt level handling or from the normal tasking
|
||||||
|
@ -435,8 +435,8 @@ static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer,
|
|||||||
*
|
*
|
||||||
* OXTABS - primarily a full-screen terminal optimisation
|
* OXTABS - primarily a full-screen terminal optimisation
|
||||||
* ONOEOT - Unix interoperability hack
|
* ONOEOT - Unix interoperability hack
|
||||||
* OLCUC - Not specified by Posix
|
* OLCUC - Not specified by POSIX
|
||||||
* ONOCR - low-speed interactive optimisation
|
* ONOCR - low-speed interactive optimisation
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,7 +447,6 @@ static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer,
|
|||||||
{
|
{
|
||||||
ret = uart_putxmitchar(dev, '\r', oktoblock);
|
ret = uart_putxmitchar(dev, '\r', oktoblock);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Put the character into the transmit buffer */
|
/* Put the character into the transmit buffer */
|
||||||
|
@ -1042,6 +1042,9 @@ int up_timer_gettime(FAR struct timespec *ts);
|
|||||||
* that up_timer_start() and the remaining time of zero should be
|
* that up_timer_start() and the remaining time of zero should be
|
||||||
* returned.
|
* 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.
|
* Provided by platform-specific code and called from the RTOS base code.
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
@ -1049,8 +1052,9 @@ int up_timer_gettime(FAR struct timespec *ts);
|
|||||||
* if the timer is not active.
|
* if the timer is not active.
|
||||||
*
|
*
|
||||||
* Returned Value:
|
* Returned Value:
|
||||||
* Zero (OK) is returned on success; a negated errno value is returned on
|
* Zero (OK) is returned on success. A call to up_timer_cancel() when
|
||||||
* any failure.
|
* the timer is not active should also return success; a negated errno
|
||||||
|
* value is returned on any failure.
|
||||||
*
|
*
|
||||||
* Assumptions:
|
* Assumptions:
|
||||||
* May be called from interrupt level handling or from the normal tasking
|
* May be called from interrupt level handling or from the normal tasking
|
||||||
|
@ -73,8 +73,8 @@ static void rawoutstream_putc(FAR struct lib_outstream_s *this, int ch)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* The only expected error is EINTR, meaning that the write operation
|
/* The only expected error is EINTR, meaning that the write operation
|
||||||
* was awakened by a signal. Zero would not be a valid return value
|
* was awakened by a signal. Zero or values > 1 would not be valid
|
||||||
* from write().
|
* return values from write().
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DEBUGASSERT(nwritten < 0);
|
DEBUGASSERT(nwritten < 0);
|
||||||
|
Loading…
Reference in New Issue
Block a user