SAMA5 T/C: Can now handle non-constant BOARD_MCK_FREQUENCY. Also now supports methods to attach user interrupt handlers
This commit is contained in:
parent
5d143578b0
commit
c7662e3f92
@ -423,6 +423,7 @@
|
|||||||
# define SAMA5_HAVE_PMC_PCR_DIV 1 /* Supports conditional compilation */
|
# define SAMA5_HAVE_PMC_PCR_DIV 1 /* Supports conditional compilation */
|
||||||
# define PMC_PCR_DIV_SHIFT (16) /* Bits 16-17: Divisor Value */
|
# define PMC_PCR_DIV_SHIFT (16) /* Bits 16-17: Divisor Value */
|
||||||
# define PMC_PCR_DIV_MASK (3 << PMC_PCR_DIV_SHIFT)
|
# define PMC_PCR_DIV_MASK (3 << PMC_PCR_DIV_SHIFT)
|
||||||
|
# define PMC_PCR_DIV(n) ((uint32_t)(n) << PMC_PCR_DIV_SHIFT)
|
||||||
# define PMC_PCR_DIV1 (0 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK */
|
# define PMC_PCR_DIV1 (0 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK */
|
||||||
# define PMC_PCR_DIV2 (1 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/2 */
|
# define PMC_PCR_DIV2 (1 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/2 */
|
||||||
# define PMC_PCR_DIV4 (2 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/4 */
|
# define PMC_PCR_DIV4 (2 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/4 */
|
||||||
|
@ -58,6 +58,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
|
|
||||||
|
#include <nuttx/arch.h>
|
||||||
#include <arch/board/board.h>
|
#include <arch/board/board.h>
|
||||||
|
|
||||||
#include "up_arch.h"
|
#include "up_arch.h"
|
||||||
@ -74,24 +75,6 @@
|
|||||||
* Pre-processor Definitions
|
* Pre-processor Definitions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
/* Clocking */
|
|
||||||
|
|
||||||
#if BOARD_MCK_FREQUENCY <= SAM_TC_MAXPERCLK
|
|
||||||
# define TC_FREQUENCY BOARD_MCK_FREQUENCY
|
|
||||||
# define TC_PCR_DIV PMC_PCR_DIV1
|
|
||||||
#elif (BOARD_MCK_FREQUENCY >> 1) <= SAM_TC_MAXPERCLK
|
|
||||||
# define TC_FREQUENCY (BOARD_MCK_FREQUENCY >> 1)
|
|
||||||
# define TC_PCR_DIV PMC_PCR_DIV2
|
|
||||||
#elif (BOARD_MCK_FREQUENCY >> 2) <= SAM_TC_MAXPERCLK
|
|
||||||
# define TC_FREQUENCY (BOARD_MCK_FREQUENCY >> 2)
|
|
||||||
# define TC_PCR_DIV PMC_PCR_DIV4
|
|
||||||
#elif (BOARD_MCK_FREQUENCY >> 3) <= SAM_TC_MAXPERCLK
|
|
||||||
# define TC_FREQUENCY (BOARD_MCK_FREQUENCY >> 3)
|
|
||||||
# define TC_PCR_DIV PMC_PCR_DIV8
|
|
||||||
#else
|
|
||||||
# error Cannot realize TC input frequency
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Timer debug is enabled if any timer client is enabled */
|
/* Timer debug is enabled if any timer client is enabled */
|
||||||
|
|
||||||
#ifndef CONFIG_DEBUG
|
#ifndef CONFIG_DEBUG
|
||||||
@ -146,17 +129,19 @@ struct sam_chan_s
|
|||||||
{
|
{
|
||||||
struct sam_tc_s *tc; /* Parent timer/counter */
|
struct sam_tc_s *tc; /* Parent timer/counter */
|
||||||
uintptr_t base; /* Channel register base address */
|
uintptr_t base; /* Channel register base address */
|
||||||
|
tc_handler_t handler; /* Attached interrupt handler */
|
||||||
|
void *arg; /* Interrupt handler argument */
|
||||||
uint8_t chan; /* Channel number (0, 1, or 2 OR 3, 4, or 5) */
|
uint8_t chan; /* Channel number (0, 1, or 2 OR 3, 4, or 5) */
|
||||||
bool inuse; /* True: channel is in use */
|
bool inuse; /* True: channel is in use */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This structure describes on timer/counter */
|
/* This structure describes one timer/counter */
|
||||||
|
|
||||||
struct sam_tc_s
|
struct sam_tc_s
|
||||||
{
|
{
|
||||||
sem_t exclsem; /* Assures mutually exclusive access to TC */
|
sem_t exclsem; /* Assures mutually exclusive access to TC */
|
||||||
uintptr_t base; /* Register base address */
|
uintptr_t base; /* Register base address */
|
||||||
uint8_t pid; /* Peripheral ID */
|
uint8_t pid; /* Peripheral ID/irq number */
|
||||||
uint8_t tc; /* Timer/channel number (0 or 1) */
|
uint8_t tc; /* Timer/channel number (0 or 1) */
|
||||||
bool initialized; /* True: Timer data has been initialized */
|
bool initialized; /* True: Timer data has been initialized */
|
||||||
|
|
||||||
@ -202,8 +187,26 @@ static inline uint32_t sam_chan_getreg(struct sam_chan_s *chan,
|
|||||||
static inline void sam_chan_putreg(struct sam_chan_s *chan,
|
static inline void sam_chan_putreg(struct sam_chan_s *chan,
|
||||||
unsigned int offset, uint32_t regval);
|
unsigned int offset, uint32_t regval);
|
||||||
|
|
||||||
|
/* Interrupt Handling *******************************************************/
|
||||||
|
|
||||||
|
static int sam_tc_interrupt(struct sam_tc_s *tc);
|
||||||
|
#ifdef CONFIG_SAMA5_TC0
|
||||||
|
static int sam_tc012_interrupt(int irq, void *context);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_TC1
|
||||||
|
static int sam_tc345_interrupt(int irq, void *context);
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SAMA5_TC2
|
||||||
|
static int sam_tc678_interrupt(int irq, void *context);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Initialization ***********************************************************/
|
/* Initialization ***********************************************************/
|
||||||
|
|
||||||
|
#ifdef SAMA5_HAVE_PMC_PCR_DIV
|
||||||
|
static int sam_tc_mckdivider(uint32_t mck);
|
||||||
|
#endif
|
||||||
|
static int sam_tc_freqdiv(uint32_t ftc, int ndx);
|
||||||
|
static uint32_t sam_tc_divfreq(uint32_t ftc, int ndx);
|
||||||
static inline struct sam_chan_s *sam_tc_initialize(int channel);
|
static inline struct sam_chan_s *sam_tc_initialize(int channel);
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@ -437,28 +440,17 @@ static struct sam_tc_s g_tc678;
|
|||||||
|
|
||||||
/* TC frequency data. This table provides the frequency for each selection of TCCLK */
|
/* TC frequency data. This table provides the frequency for each selection of TCCLK */
|
||||||
|
|
||||||
#define TC_NDIVIDERS 5
|
#define TC_NDIVIDERS 4
|
||||||
|
#define TC_NDIVOPTIONS 5
|
||||||
|
|
||||||
/* This is the list of divider values */
|
/* This is the list of divider values: divider = (1 << value) */
|
||||||
|
|
||||||
static const uint16_t g_divider[TC_NDIVIDERS] =
|
static const uint8_t g_log2divider[TC_NDIVIDERS] =
|
||||||
{
|
{
|
||||||
2, /* TIMER_CLOCK1 -> div2 */
|
1, /* TIMER_CLOCK1 -> div2 */
|
||||||
8, /* TIMER_CLOCK2 -> div8 */
|
3, /* TIMER_CLOCK2 -> div8 */
|
||||||
32, /* TIMER_CLOCK3 -> div32 */
|
5, /* TIMER_CLOCK3 -> div32 */
|
||||||
128, /* TIMER_CLOCK4 -> div128 */
|
7 /* TIMER_CLOCK4 -> div128 */
|
||||||
TC_FREQUENCY / 32768 /* TIMER_CLOCK5 -> slow clock (not really a divider) */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This is the list of divided down frequencies */
|
|
||||||
|
|
||||||
static const uint32_t g_divfreq[TC_NDIVIDERS] =
|
|
||||||
{
|
|
||||||
TC_FREQUENCY / 2, /* TIMER_CLOCK1 -> div2 */
|
|
||||||
TC_FREQUENCY / 8, /* TIMER_CLOCK2 -> div8 */
|
|
||||||
TC_FREQUENCY / 32, /* TIMER_CLOCK3 -> div32 */
|
|
||||||
TC_FREQUENCY / 128, /* TIMER_CLOCK4 -> div128 */
|
|
||||||
32768 /* TIMER_CLOCK5 -> slow clock */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* TC register lookup used by sam_tc_setregister */
|
/* TC register lookup used by sam_tc_setregister */
|
||||||
@ -699,9 +691,217 @@ static inline void sam_chan_putreg(struct sam_chan_s *chan, unsigned int offset,
|
|||||||
putreg32(regval, regaddr);
|
putreg32(regval, regaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Interrupt Handling
|
||||||
|
****************************************************************************/
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_tc_interrupt
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Common timer channel interrupt handling.
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* tc Timer status instance
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* A pointer to the initialized timer channel structure associated with tc
|
||||||
|
* and channel. NULL is returned on any failure.
|
||||||
|
*
|
||||||
|
* On successful return, the caller holds the tc exclusive access semaphore.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static int sam_tc_interrupt(struct sam_tc_s *tc)
|
||||||
|
{
|
||||||
|
struct sam_chan_s *chan;
|
||||||
|
uint32_t sr;
|
||||||
|
uint32_t imr;
|
||||||
|
uint32_t pending;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Process interrupts on each channel */
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
/* Get the handy channel reference */
|
||||||
|
|
||||||
|
chan = &tc->channel[i];
|
||||||
|
|
||||||
|
/* Get the interrupt status for this channel */
|
||||||
|
|
||||||
|
sr = sam_chan_getreg(chan, SAM_TC_SR_OFFSET);
|
||||||
|
imr = sam_chan_getreg(chan, SAM_TC_IMR_OFFSET);
|
||||||
|
pending = sr & imr;
|
||||||
|
|
||||||
|
/* Are there any pending interrupts for this channel? */
|
||||||
|
|
||||||
|
if (pending)
|
||||||
|
{
|
||||||
|
/* Yes... if we have pending interrupts then interrupts must be
|
||||||
|
* enabled and we must have a handler attached.
|
||||||
|
*/
|
||||||
|
|
||||||
|
DEBUGASSERT(chan->handler);
|
||||||
|
if (chan->handler)
|
||||||
|
{
|
||||||
|
/* Execute the callback */
|
||||||
|
|
||||||
|
chan->handler(chan, chan->arg, sr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Should never happen */
|
||||||
|
|
||||||
|
sam_chan_putreg(chan, SAM_TC_IDR_OFFSET, TC_INT_ALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_tcABC_interrupt
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Timer block interrupt handlers
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* chan TC channel structure
|
||||||
|
* sr The status register value that generated the interrupt
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* A pointer to the initialized timer channel structure associated with tc
|
||||||
|
* and channel. NULL is returned on any failure.
|
||||||
|
*
|
||||||
|
* On successful return, the caller holds the tc exclusive access semaphore.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_SAMA5_TC0
|
||||||
|
static int sam_tc012_interrupt(int irq, void *context)
|
||||||
|
{
|
||||||
|
return sam_tc_interrupt(&g_tc012);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SAMA5_TC1
|
||||||
|
static int sam_tc345_interrupt(int irq, void *context)
|
||||||
|
{
|
||||||
|
return sam_tc_interrupt(&g_tc345);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SAMA5_TC2
|
||||||
|
static int sam_tc678_interrupt(int irq, void *context)
|
||||||
|
{
|
||||||
|
return sam_tc_interrupt(&g_tc678);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Initialization
|
* Initialization
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_tc_mckdivider
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Return the TC clock input divider value. One of n=0..3 corresponding
|
||||||
|
* to divider values of {1, 2, 4, 8}.
|
||||||
|
*
|
||||||
|
* NOTE: The SAMA5D4 has no clock input divider
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* mck - The MCK frequency to be divider.
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* Log2 of the TC clock divider.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef SAMA5_HAVE_PMC_PCR_DIV
|
||||||
|
static int sam_tc_mckdivider(uint32_t mck)
|
||||||
|
{
|
||||||
|
if (mck <= SAM_TC_MAXPERCLK)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if ((mck >> 1) <= SAM_TC_MAXPERCLK)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if ((mck >> 2) <= SAM_TC_MAXPERCLK)
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
else /* if ((mck >> 3) <= SAM_TC_MAXPERCLK) */
|
||||||
|
{
|
||||||
|
DEBUGASSERT((mck >> 3) <= SAM_TC_MAXPERCLK);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_tc_freqdiv
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Given the TC input frequency (Ftc) and a divider index, return the value of
|
||||||
|
* the Ftc divider.
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* ftc - TC input frequency
|
||||||
|
* ndx - Divider index
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* The ftc input divider value
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static int sam_tc_freqdiv(uint32_t ftc, int ndx)
|
||||||
|
{
|
||||||
|
/* The final option is to use the SLOW clock */
|
||||||
|
|
||||||
|
if (ndx >= TC_NDIVIDERS)
|
||||||
|
{
|
||||||
|
return ftc / BOARD_SLOWCLK_FREQUENCY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1 << g_log2divider[ndx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_tc_divfreq
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Given the TC input frequency (Ftc) and a divider index, return the value of
|
||||||
|
* the divided frequency
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* ftc - TC input frequency
|
||||||
|
* ndx - Divider index
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* The divided frequency value
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static uint32_t sam_tc_divfreq(uint32_t ftc, int ndx)
|
||||||
|
{
|
||||||
|
/* The final option is to use the SLOW clock */
|
||||||
|
|
||||||
|
if (ndx >= TC_NDIVIDERS)
|
||||||
|
{
|
||||||
|
return BOARD_SLOWCLK_FREQUENCY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ftc >> g_log2divider[ndx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: sam_tc_initialize
|
* Name: sam_tc_initialize
|
||||||
*
|
*
|
||||||
@ -724,11 +924,12 @@ static inline void sam_chan_putreg(struct sam_chan_s *chan, unsigned int offset,
|
|||||||
|
|
||||||
static inline struct sam_chan_s *sam_tc_initialize(int channel)
|
static inline struct sam_chan_s *sam_tc_initialize(int channel)
|
||||||
{
|
{
|
||||||
FAR struct sam_tc_s *tc;
|
struct sam_tc_s *tc;
|
||||||
FAR const struct sam_tcconfig_s *tcconfig;
|
const struct sam_tcconfig_s *tcconfig;
|
||||||
FAR struct sam_chan_s *chan;
|
struct sam_chan_s *chan;
|
||||||
FAR const struct sam_chconfig_s *chconfig;
|
const struct sam_chconfig_s *chconfig;
|
||||||
irqstate_t flags;
|
irqstate_t flags;
|
||||||
|
xcpt_t handler;
|
||||||
uint32_t regval;
|
uint32_t regval;
|
||||||
uint8_t ch;
|
uint8_t ch;
|
||||||
int i;
|
int i;
|
||||||
@ -742,6 +943,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
|
|||||||
{
|
{
|
||||||
tc = &g_tc012;
|
tc = &g_tc012;
|
||||||
tcconfig = &g_tc012config;
|
tcconfig = &g_tc012config;
|
||||||
|
handler = sam_tc012_interrupt;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
@ -750,6 +952,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
|
|||||||
{
|
{
|
||||||
tc = &g_tc345;
|
tc = &g_tc345;
|
||||||
tcconfig = &g_tc345config;
|
tcconfig = &g_tc345config;
|
||||||
|
handler = sam_tc345_interrupt;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
@ -758,6 +961,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
|
|||||||
{
|
{
|
||||||
tc = &g_tc678;
|
tc = &g_tc678;
|
||||||
tcconfig = &g_tc678config;
|
tcconfig = &g_tc678config;
|
||||||
|
handler = sam_tc678_interrupt;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
@ -820,17 +1024,34 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
|
|||||||
|
|
||||||
sam_configpio(chconfig->tiobset);
|
sam_configpio(chconfig->tiobset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Disable and clear all channel interrupts */
|
||||||
|
|
||||||
|
sam_chan_putreg(chan, SAM_TC_IDR_OFFSET, TC_INT_ALL);
|
||||||
|
(void)sam_chan_getreg(chan, SAM_TC_SR_OFFSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the maximum TC peripheral clock frequency */
|
/* Set the maximum TC peripheral clock frequency */
|
||||||
|
|
||||||
regval = PMC_PCR_PID(tcconfig->pid) | PMC_PCR_CMD | TC_PCR_DIV | PMC_PCR_EN;
|
regval = PMC_PCR_PID(tcconfig->pid) | PMC_PCR_CMD | PMC_PCR_EN;
|
||||||
|
|
||||||
|
#ifdef SAMA5_HAVE_PMC_PCR_DIV
|
||||||
|
/* Set the MCK divider (if any) */
|
||||||
|
|
||||||
|
regval |= PMC_PCR_DIV(sam_tc_mckdivider(BOARD_MCK_FREQUENCY));
|
||||||
|
#endif
|
||||||
|
|
||||||
putreg32(regval, SAM_PMC_PCR);
|
putreg32(regval, SAM_PMC_PCR);
|
||||||
|
|
||||||
/* Enable clocking to the timer counter */
|
/* Enable clocking to the timer counter */
|
||||||
|
|
||||||
sam_enableperiph0(tcconfig->pid);
|
sam_enableperiph0(tcconfig->pid);
|
||||||
|
|
||||||
|
/* Attach the timer interrupt handler and enable the timer interrupts */
|
||||||
|
|
||||||
|
(void)irq_attach(tc->pid, handler);
|
||||||
|
up_enable_irq(tc->pid);
|
||||||
|
|
||||||
/* Now the channel is initialized */
|
/* Now the channel is initialized */
|
||||||
|
|
||||||
tc->initialized = true;
|
tc->initialized = true;
|
||||||
@ -980,6 +1201,60 @@ void sam_tc_start(TC_HANDLE handle)
|
|||||||
sam_regdump(chan, "Started");
|
sam_regdump(chan, "Started");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_tc_attach
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Attach or detach an interrupt handler to the timer interrupt. The
|
||||||
|
* interrupt is detached if the handler argument is NULL.
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* handle The handle that represents the timer state
|
||||||
|
* handler The interrupt handler that will be invoked when the interrupt
|
||||||
|
* condition occurs
|
||||||
|
* arg An opaque argument that will be provided when the interrupt
|
||||||
|
* handler callback is executed.
|
||||||
|
* mask The value of the timer interrupt mask register that defines
|
||||||
|
* which interrupts should be disabled.
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
tc_handler_t sam_tc_attach(TC_HANDLE handle, tc_handler_t handler,
|
||||||
|
void *arg, uint32_t mask)
|
||||||
|
{
|
||||||
|
struct sam_chan_s *chan = (struct sam_chan_s *)handle;
|
||||||
|
tc_handler_t oldhandler;
|
||||||
|
irqstate_t flags;
|
||||||
|
|
||||||
|
DEBUGASSERT(chan);
|
||||||
|
|
||||||
|
/* Remember the old interrupt handler and set the new handler */
|
||||||
|
|
||||||
|
flags = irqsave();
|
||||||
|
oldhandler = handler;
|
||||||
|
chan->handler = handler;
|
||||||
|
|
||||||
|
/* Don't enable interrupt if we are detaching no matter what the caller
|
||||||
|
* says.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!handler)
|
||||||
|
{
|
||||||
|
arg = NULL;
|
||||||
|
mask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now enable interrupt as requested */
|
||||||
|
|
||||||
|
sam_chan_putreg(chan, SAM_TC_IDR_OFFSET, TC_INT_ALL & ~mask);
|
||||||
|
sam_chan_putreg(chan, SAM_TC_IER_OFFSET, TC_INT_ALL & mask);
|
||||||
|
irqrestore(flags);
|
||||||
|
|
||||||
|
return oldhandler;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: sam_tc_stop
|
* Name: sam_tc_stop
|
||||||
*
|
*
|
||||||
@ -1009,9 +1284,9 @@ void sam_tc_stop(TC_HANDLE handle)
|
|||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual
|
* Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual
|
||||||
* setting in the register will be the TC input frequency divided by
|
* setting in the register will be the TC input frequency (Ftc) divided by
|
||||||
* the provided divider (which should derive from the divider returned
|
* the provided divider (which should derive from the divider returned
|
||||||
* by sam_tc_divider).
|
* by sam_tc_divisor).
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
* handle Channel handle previously allocated by sam_tc_allocate()
|
* handle Channel handle previously allocated by sam_tc_allocate()
|
||||||
@ -1024,13 +1299,15 @@ void sam_tc_stop(TC_HANDLE handle)
|
|||||||
void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div)
|
void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div)
|
||||||
{
|
{
|
||||||
struct sam_chan_s *chan = (struct sam_chan_s *)handle;
|
struct sam_chan_s *chan = (struct sam_chan_s *)handle;
|
||||||
|
uint32_t frequency;
|
||||||
uint32_t regval;
|
uint32_t regval;
|
||||||
|
|
||||||
DEBUGASSERT(reg < TC_NREGISTERS);
|
DEBUGASSERT(reg < TC_NREGISTERS);
|
||||||
|
|
||||||
regval = TC_FREQUENCY / div;
|
frequency = sam_tc_frequency();
|
||||||
|
regval = frequency / div;
|
||||||
tcvdbg("Channel %d: Set register %d to %d / %d = %d\n",
|
tcvdbg("Channel %d: Set register %d to %d / %d = %d\n",
|
||||||
chan->chan, reg, TC_FREQUENCY, div, (unsigned int)regval);
|
chan->chan, reg, frequency, div, (unsigned int)regval);
|
||||||
|
|
||||||
sam_chan_putreg(chan, g_regoffset[reg], regval);
|
sam_chan_putreg(chan, g_regoffset[reg], regval);
|
||||||
sam_regdump(chan, "Set register");
|
sam_regdump(chan, "Set register");
|
||||||
@ -1040,8 +1317,9 @@ void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div)
|
|||||||
* Name: sam_tc_frequency
|
* Name: sam_tc_frequency
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Return the timer input frequency, that is, the MCK frequency divided
|
* Return the timer input frequency (Ftc), that is, the MCK frequency
|
||||||
* down so that the timer/counter is driven within its maximum frequency.
|
* divided down so that the timer/counter is driven within its maximum
|
||||||
|
* frequency.
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
* None
|
* None
|
||||||
@ -1053,7 +1331,13 @@ void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div)
|
|||||||
|
|
||||||
uint32_t sam_tc_frequency(void)
|
uint32_t sam_tc_frequency(void)
|
||||||
{
|
{
|
||||||
return TC_FREQUENCY;
|
#ifdef SAMA5_HAVE_PMC_PCR_DIV
|
||||||
|
uint32_t mck = BOARD_MCK_FREQUENCY;
|
||||||
|
int shift = sam_tc_mckdivider(mck);
|
||||||
|
return mck >> shift;
|
||||||
|
#else
|
||||||
|
return BOARD_MCK_FREQUENCY;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@ -1066,7 +1350,7 @@ uint32_t sam_tc_frequency(void)
|
|||||||
* (Ftc / (div * 65536)) <= freq <= (Ftc / dev)
|
* (Ftc / (div * 65536)) <= freq <= (Ftc / dev)
|
||||||
*
|
*
|
||||||
* where:
|
* where:
|
||||||
* freq - the desitred frequency
|
* freq - the desired frequency
|
||||||
* Ftc - The timer/counter input frequency
|
* Ftc - The timer/counter input frequency
|
||||||
* div - With DIV being the highest possible value.
|
* div - With DIV being the highest possible value.
|
||||||
*
|
*
|
||||||
@ -1083,15 +1367,19 @@ uint32_t sam_tc_frequency(void)
|
|||||||
|
|
||||||
int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks)
|
int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks)
|
||||||
{
|
{
|
||||||
|
uint32_t ftc = sam_tc_frequency();
|
||||||
int ndx = 0;
|
int ndx = 0;
|
||||||
|
|
||||||
tcvdbg("frequency=%d\n", frequency);
|
tcvdbg("frequency=%d\n", frequency);
|
||||||
|
|
||||||
/* Satisfy lower bound */
|
/* Satisfy lower bound. That is, the value of the divider such that:
|
||||||
|
*
|
||||||
|
* frequency >= tc_input_frequency / divider.
|
||||||
|
*/
|
||||||
|
|
||||||
while (frequency < (g_divfreq[ndx] >> 16))
|
while (frequency < (sam_tc_divfreq(ftc, ndx) >> 16))
|
||||||
{
|
{
|
||||||
if (++ndx > TC_NDIVIDERS)
|
if (++ndx > TC_NDIVOPTIONS)
|
||||||
{
|
{
|
||||||
/* If no divisor can be found, return -ERANGE */
|
/* If no divisor can be found, return -ERANGE */
|
||||||
|
|
||||||
@ -1100,11 +1388,15 @@ int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to maximize DIV while still satisfying upper bound */
|
/* Try to maximize DIV while still satisfying upper bound. That the
|
||||||
|
* value of the divider such that:
|
||||||
|
*
|
||||||
|
* frequency < tc_input_frequency / divider.
|
||||||
|
*/
|
||||||
|
|
||||||
for (; ndx < (TC_NDIVIDERS-1); ndx++)
|
for (; ndx < (TC_NDIVOPTIONS-1); ndx++)
|
||||||
{
|
{
|
||||||
if (frequency > g_divfreq[ndx + 1])
|
if (frequency > sam_tc_divfreq(ftc, ndx + 1))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1114,11 +1406,12 @@ int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks)
|
|||||||
|
|
||||||
if (div)
|
if (div)
|
||||||
{
|
{
|
||||||
tcvdbg("return div=%d\n", g_divider[ndx]);
|
uint32_t value = sam_tc_freqdiv(ftc, ndx);
|
||||||
*div = g_divider[ndx];
|
tcvdbg("return div=%lu\n", (unsigned long)value);
|
||||||
|
*div = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* REturn the TCCLKS selection */
|
/* Return the TCCLKS selection */
|
||||||
|
|
||||||
if (tcclks)
|
if (tcclks)
|
||||||
{
|
{
|
||||||
|
@ -55,12 +55,15 @@
|
|||||||
|
|
||||||
/* The timer/counter and channel arguments to sam_tc_allocate() */
|
/* The timer/counter and channel arguments to sam_tc_allocate() */
|
||||||
|
|
||||||
#define TC_CHAN0 0
|
#define TC_CHAN0 0 /* TC0 */
|
||||||
#define TC_CHAN1 1
|
#define TC_CHAN1 1
|
||||||
#define TC_CHAN2 2
|
#define TC_CHAN2 2
|
||||||
#define TC_CHAN3 3
|
#define TC_CHAN3 3 /* TC1 */
|
||||||
#define TC_CHAN4 4
|
#define TC_CHAN4 4
|
||||||
#define TC_CHAN5 5
|
#define TC_CHAN5 5
|
||||||
|
#define TC_CHAN6 6 /* TC2 */
|
||||||
|
#define TC_CHAN7 7
|
||||||
|
#define TC_CHAN8 8
|
||||||
|
|
||||||
/* Register identifier used with sam_tc_setregister */
|
/* Register identifier used with sam_tc_setregister */
|
||||||
|
|
||||||
@ -71,9 +74,21 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Public Types
|
* Public Types
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
/* An opaque handle used to represent a timer channel */
|
||||||
|
|
||||||
typedef void *TC_HANDLE;
|
typedef void *TC_HANDLE;
|
||||||
|
|
||||||
|
/* Timer interrupt callback. When a timer interrup 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef void (*tc_handler_t)(TC_HANDLE handle, void *arg, uint32_t sr);
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Public Data
|
* Public Data
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@ -144,6 +159,33 @@ void sam_tc_free(TC_HANDLE handle);
|
|||||||
|
|
||||||
void sam_tc_start(TC_HANDLE handle);
|
void sam_tc_start(TC_HANDLE handle);
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: sam_tc_attach/sam_tc_detach
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Attach or detach an interrupt handler to the timer interrupt. The
|
||||||
|
* interrupt is detached if the handler argument is NULL.
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* handle The handle that represents the timer state
|
||||||
|
* handler The interrupt handler that will be invoked when the interrupt
|
||||||
|
* condition occurs
|
||||||
|
* arg An opaque argument that will be provided when the interrupt
|
||||||
|
* handler callback is executed. Ignored if handler is NULL.
|
||||||
|
* mask The value of the timer interrupt mask register that defines
|
||||||
|
* which interrupts should be disabled. Ignored if handler is
|
||||||
|
* NULL.
|
||||||
|
*
|
||||||
|
* Returned Value:
|
||||||
|
* The address of the previous handler, if any.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
tc_handler_t sam_tc_attach(TC_HANDLE handle, tc_handler_t handler,
|
||||||
|
void *arg, uint32_t mask);
|
||||||
|
|
||||||
|
#define sam_tc_detach(h) sam_tc_attach(h, NULL, NULL, 0)
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: sam_tc_stop
|
* Name: sam_tc_stop
|
||||||
*
|
*
|
||||||
@ -166,7 +208,7 @@ void sam_tc_stop(TC_HANDLE handle);
|
|||||||
* Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual
|
* Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual
|
||||||
* setting in the register will be the TC input frequency divided by
|
* setting in the register will be the TC input frequency divided by
|
||||||
* the provided divider (which should derive from the divider returned
|
* the provided divider (which should derive from the divider returned
|
||||||
* by sam_tc_divider).
|
* by sam_tc_divisor).
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Input Parameters:
|
* Input Parameters:
|
||||||
@ -206,7 +248,7 @@ uint32_t sam_tc_frequency(void);
|
|||||||
* (Ftc / (div * 65536)) <= freq <= (Ftc / dev)
|
* (Ftc / (div * 65536)) <= freq <= (Ftc / dev)
|
||||||
*
|
*
|
||||||
* where:
|
* where:
|
||||||
* freq - the desitred frequency
|
* freq - the desired frequency
|
||||||
* Ftc - The timer/counter input frequency
|
* Ftc - The timer/counter input frequency
|
||||||
* div - With DIV being the highest possible value.
|
* div - With DIV being the highest possible value.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user