SAMV7: Revise timer driver. SAMA5 has one pid/interrupt per timer/counter; SAMV7 has a pid/interrupt per channel

This commit is contained in:
Gregory Nutt 2015-12-02 14:09:34 -06:00
parent 192772b7ee
commit 9a54702d99
3 changed files with 211 additions and 129 deletions

View File

@ -419,6 +419,11 @@
# define TC_CMR_TCCLKS_TCLK3 (2 << TC_CMR_TCCLKS_SHIFT) /* TIMER_CLOCK3 Clock selected */
# define TC_CMR_TCCLKS_TCLK4 (3 << TC_CMR_TCCLKS_SHIFT) /* TIMER_CLOCK4 Clock selected */
# define TC_CMR_TCCLKS_TCLK5 (4 << TC_CMR_TCCLKS_SHIFT) /* TIMER_CLOCK5 Clock selected */
# define TC_CMR_TCCLKS_PCK6 TC_CMR_TCCLKS_TCLK1 /* TIMER_CLOCK1 is PCK6 */
# define TC_CMR_TCCLKS_MCK8 TC_CMR_TCCLKS_TCLK2 /* TIMER_CLOCK2 is MCK/8 */
# define TC_CMR_TCCLKS_MCK32 TC_CMR_TCCLKS_TCLK3 /* TIMER_CLOCK3 is MCK/32 */
# define TC_CMR_TCCLKS_MCK128 TC_CMR_TCCLKS_TCLK4 /* TIMER_CLOCK4 is MCK/128 */
# define TC_CMR_TCCLKS_SLCK TC_CMR_TCCLKS_TCLK5 /* TIMER_CLOCK5 is SLCK */
# define TC_CMR_TCCLKS_XC0 (5 << TC_CMR_TCCLKS_SHIFT) /* XC0 Clock selected */
# define TC_CMR_TCCLKS_XC1 (6 << TC_CMR_TCCLKS_SHIFT) /* XC1 Clock selected */
# define TC_CMR_TCCLKS_XC2 (7 << TC_CMR_TCCLKS_SHIFT) /* XC2 Clock selected */

View File

@ -186,7 +186,7 @@ int sam_oneshot_initialize(struct sam_oneshot_s *oneshot, int chan,
/* Allocate the timer/counter and select its mode of operation
*
* CMR_TCCLKS - Returned by sam_tc_divisor
* TC_CMR_TCCLKS - Returned by sam_tc_divisor
* TC_CMR_CLKI=0 - Not inverted
* TC_CMR_BURST_NONE - Not gated by an external signal
* TC_CMR_CPCSTOP=1 - Stop the clock on an RC compare event

View File

@ -83,6 +83,8 @@
struct sam_chconfig_s
{
uintptr_t base; /* Channel register base address */
uint8_t pid; /* Peripheral ID */
xcpt_t handler; /* Timer interrupt handler */
gpio_pinset_t clkset; /* CLK input PIO configuration */
gpio_pinset_t tioaset; /* Output A PIO configuration */
gpio_pinset_t tiobset; /* Output B PIO configuration */
@ -93,9 +95,8 @@ struct sam_chconfig_s
struct sam_tcconfig_s
{
uintptr_t base; /* TC register base address */
uint8_t pid; /* Peripheral ID */
uint8_t chfirst; /* First channel number */
uint8_t tc; /* Timer/counter number */
uint8_t chfirst; /* First channel number */
/* Channels */
@ -109,9 +110,9 @@ struct sam_chan_s
{
struct sam_tc_s *tc; /* Parent timer/counter */
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) */
tc_handler_t handler; /* User timer interrupt handler */
void *arg; /* User interrupt handler argument */
uint8_t chan; /* Channel number (0, 1, 2, ... 11} */
bool inuse; /* True: channel is in use */
};
@ -121,9 +122,8 @@ struct sam_tc_s
{
sem_t exclsem; /* Assures mutually exclusive access to TC */
uintptr_t base; /* Register base address */
uint8_t pid; /* Peripheral ID/irq number */
uint8_t tc; /* Timer/channel number (0 or 1) */
bool initialized; /* True: Timer data has been initialized */
uint8_t tc; /* Timer/channel number {0, 1, 2, 3} */
bool initialized; /* True: Timer/counter has been initialized */
/* Channels */
@ -169,18 +169,30 @@ static inline void sam_chan_putreg(struct sam_chan_s *chan,
/* Interrupt Handling *******************************************************/
static int sam_tc_interrupt(struct sam_tc_s *tc);
static int sam_tc_interrupt(struct sam_tc_s *tc, struct sam_chan_s *chan);
#ifdef CONFIG_SAMV7_TC0
static int sam_tc012_interrupt(int irq, void *context);
static int sam_tc0_interrupt(int irq, void *context);
static int sam_tc1_interrupt(int irq, void *context);
static int sam_tc2_interrupt(int irq, void *context);
#endif
#ifdef CONFIG_SAMV7_TC1
static int sam_tc345_interrupt(int irq, void *context);
static int sam_tc3_interrupt(int irq, void *context);
static int sam_tc4_interrupt(int irq, void *context);
static int sam_tc5_interrupt(int irq, void *context);
#endif
#ifdef CONFIG_SAMV7_TC2
static int sam_tc678_interrupt(int irq, void *context);
static int sam_tc6_interrupt(int irq, void *context);
static int sam_tc7_interrupt(int irq, void *context);
static int sam_tc8_interrupt(int irq, void *context);
#endif
#ifdef CONFIG_SAMV7_TC3
static int sam_tc901_interrupt(int irq, void *context);
static int sam_tc9_interrupt(int irq, void *context);
static int sam_tc10_interrupt(int irq, void *context);
static int sam_tc11_interrupt(int irq, void *context);
#endif
/* Initialization ***********************************************************/
@ -198,18 +210,20 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel);
static const struct sam_tcconfig_s g_tc012config =
{
.base = SAM_TC012_BASE,
.pid = SAM_PID_TC0,
.chfirst = 0,
.tc = 0,
.chfirst = 0,
.channel =
{
[0] =
{
.base = SAM_TC012_CHAN_BASE(0),
.base = SAM_TC012_CHAN_BASE(0),
.handler = sam_tc0_interrupt,
.pid = SAM_PID_TC0,
#ifdef CONFIG_SAMV7_TC0_CLK0
.clkset = PIO_TC0_CLK,
.clkset = PIO_TC0_CLK,
#else
.clkset = 0,
.clkset = 0,
#endif
#ifdef CONFIG_SAMV7_TC0_TIOA0
.tioaset = PIO_TC0_IOA,
@ -225,6 +239,9 @@ static const struct sam_tcconfig_s g_tc012config =
[1] =
{
.base = SAM_TC012_CHAN_BASE(1),
.handler = sam_tc1_interrupt,
.pid = SAM_PID_TC1,
#ifdef CONFIG_SAMV7_TC0_CLK1
.clkset = PIO_TC1_CLK,
#else
@ -244,6 +261,8 @@ static const struct sam_tcconfig_s g_tc012config =
[2] =
{
.base = SAM_TC012_CHAN_BASE(2),
.handler = sam_tc2_interrupt,
#ifdef CONFIG_SAMV7_TC0_CLK2
.clkset = PIO_TC2_CLK,
#else
@ -268,14 +287,16 @@ static const struct sam_tcconfig_s g_tc012config =
static const struct sam_tcconfig_s g_tc345config =
{
.base = SAM_TC345_BASE,
.pid = SAM_PID_TC1,
.chfirst = 3,
.tc = 1,
.chfirst = 3,
.channel =
{
[0] =
{
.base = SAM_TC345_CHAN_BASE(3),
.handler = sam_tc3_interrupt,
.pid = SAM_PID_TC3,
#ifdef CONFIG_SAMV7_TC1_CLK3
.clkset = PIO_TC3_CLK,
#else
@ -295,6 +316,9 @@ static const struct sam_tcconfig_s g_tc345config =
[1] =
{
.base = SAM_TC345_CHAN_BASE(4),
.handler = sam_tc4_interrupt,
.pid = SAM_PID_TC4,
#ifdef CONFIG_SAMV7_TC1_CLK4
.clkset = PIO_TC4_CLK,
#else
@ -314,6 +338,9 @@ static const struct sam_tcconfig_s g_tc345config =
[2] =
{
.base = SAM_TC345_CHAN_BASE(5),
.handler = sam_tc5_interrupt,
.pid = SAM_PID_TC5,
#ifdef CONFIG_SAMV7_TC1_CLK5
.clkset = PIO_TC5_CLK,
#else
@ -338,14 +365,16 @@ static const struct sam_tcconfig_s g_tc345config =
static const struct sam_tcconfig_s g_tc678config =
{
.base = SAM_TC678_BASE,
.pid = SAM_PID_TC2,
.chfirst = 6,
.tc = 2,
.chfirst = 6,
.channel =
{
[0] =
{
.base = SAM_TC678_CHAN_BASE(6),
.handler = sam_tc6_interrupt,
.pid = SAM_PID_TC6,
#ifdef CONFIG_SAMV7_TC2_CLK6
.clkset = PIO_TC6_CLK,
#else
@ -365,6 +394,9 @@ static const struct sam_tcconfig_s g_tc678config =
[1] =
{
.base = SAM_TC678_CHAN_BASE(7),
.handler = sam_tc7_interrupt,
.pid = SAM_PID_TC7,
#ifdef CONFIG_SAMV7_TC2_CLK7
.clkset = PIO_TC7_CLK,
#else
@ -384,6 +416,9 @@ static const struct sam_tcconfig_s g_tc678config =
[2] =
{
.base = SAM_TC345_CHAN_BASE(8),
.handler = sam_tc8_interrupt,
.pid = SAM_PID_TC8,
#ifdef CONFIG_SAMV7_TC2_CLK8
.clkset = PIO_TC8_CLK,
#else
@ -408,14 +443,16 @@ static const struct sam_tcconfig_s g_tc678config =
static const struct sam_tcconfig_s g_tc901config =
{
.base = SAM_TC901_BASE,
.pid = SAM_PID_TC3,
.chfirst = 9,
.tc = 3,
.chfirst = 9,
.channel =
{
[0] =
{
.base = SAM_TC901_CHAN_BASE(9),
.handler = sam_tc9_interrupt,
.pid = SAM_PID_TC9,
#ifdef CONFIG_SAMV7_TC2_CLK9
.clkset = PIO_TC9_CLK,
#else
@ -435,6 +472,9 @@ static const struct sam_tcconfig_s g_tc901config =
[1] =
{
.base = SAM_TC901_CHAN_BASE(10),
.handler = sam_tc10_interrupt,
.pid = SAM_PID_TC10,
#ifdef CONFIG_SAMV7_TC2_CLK10
.clkset = PIO_TC10_CLK,
#else
@ -454,6 +494,9 @@ static const struct sam_tcconfig_s g_tc901config =
[2] =
{
.base = SAM_TC345_CHAN_BASE(11),
.handler = sam_tc11_interrupt,
.pid = SAM_PID_TC11,
#ifdef CONFIG_SAMV7_TC2_CLK11
.clkset = PIO_TC11_CLK,
#else
@ -755,59 +798,46 @@ static inline void sam_chan_putreg(struct sam_chan_s *chan, unsigned int offset,
* Common timer channel interrupt handling.
*
* Input Parameters:
* tc Timer status instance
* tc Timer/counter state instance
* chan Channel state 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.
* OK on success; a negated errno value on failure
*
****************************************************************************/
static int sam_tc_interrupt(struct sam_tc_s *tc)
static int sam_tc_interrupt(struct sam_tc_s *tc, struct sam_chan_s *chan)
{
struct sam_chan_s *chan;
uint32_t sr;
uint32_t imr;
uint32_t pending;
int i;
/* Process interrupts on each channel */
/* Get the interrupt status for this channel */
for (i = 0; i < 3; i++)
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)
{
/* Get the handy channel reference */
/* Yes... if we have pending interrupts then interrupts must be
* enabled and we must have a handler attached.
*/
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)
DEBUGASSERT(chan->handler);
if (chan->handler)
{
/* Yes... if we have pending interrupts then interrupts must be
* enabled and we must have a handler attached.
*/
/* Execute the callback */
DEBUGASSERT(chan->handler);
if (chan->handler)
{
/* Execute the callback */
chan->handler(chan, chan->arg, sr);
}
else
{
/* Should never happen */
chan->handler(chan, chan->arg, sr);
}
else
{
/* Should never happen */
sam_chan_putreg(chan, SAM_TC_IDR_OFFSET, TC_INT_ALL);
}
sam_chan_putreg(chan, SAM_TC_IDR_OFFSET, TC_INT_ALL);
}
}
@ -833,30 +863,70 @@ static int sam_tc_interrupt(struct sam_tc_s *tc)
****************************************************************************/
#ifdef CONFIG_SAMV7_TC0
static int sam_tc012_interrupt(int irq, void *context)
static int sam_tc0_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc012);
return sam_tc_interrupt(&g_tc012, &g_tc012.channel[0]);
}
static int sam_tc1_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc012, &g_tc012.channel[1]);
}
static int sam_tc2_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc012, &g_tc012.channel[2]);
}
#endif
#ifdef CONFIG_SAMV7_TC1
static int sam_tc345_interrupt(int irq, void *context)
static int sam_tc3_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc345);
return sam_tc_interrupt(&g_tc345, g_tc345.channel[0]);
}
static int sam_tc4_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc345, g_tc345.channel[1]);
}
static int sam_tc5_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc345, g_tc345.channel[2]);
}
#endif
#ifdef CONFIG_SAMV7_TC2
static int sam_tc678_interrupt(int irq, void *context)
static int sam_tc6_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc678);
return sam_tc_interrupt(&g_tc678, g_tc678.channel[0]);
}
static int sam_tc7_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc678, g_tc678.channel[1]);
}
static int sam_tc8_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc678, g_tc678.channel[2]);
}
#endif
#ifdef CONFIG_SAMV7_TC3
static int sam_tc901_interrupt(int irq, void *context)
static int sam_tc9_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc901);
return sam_tc_interrupt(&g_tc901, g_tc901.channel[0]);
}
static int sam_tc10_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc901, g_tc901.channel[1]);
}
static int sam_tc11_interrupt(int irq, void *context)
{
return sam_tc_interrupt(&g_tc901, g_tc901.channel[2]);
}
#endif
@ -956,10 +1026,10 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
struct sam_chan_s *chan;
const struct sam_chconfig_s *chconfig;
irqstate_t flags;
xcpt_t handler;
uint32_t regval;
uint8_t tcno;
uint8_t chndx;
uint8_t ch;
int i;
/* Select the timer/counter and get the index associated with the
* channel.
@ -970,7 +1040,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
{
tc = &g_tc012;
tcconfig = &g_tc012config;
handler = sam_tc012_interrupt;
tcno = 0;
}
else
#endif
@ -979,7 +1049,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
{
tc = &g_tc345;
tcconfig = &g_tc345config;
handler = sam_tc345_interrupt;
tcno = 1;
}
else
#endif
@ -988,7 +1058,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
{
tc = &g_tc678;
tcconfig = &g_tc678config;
handler = sam_tc678_interrupt;
tcno = 2;
}
else
#endif
@ -997,7 +1067,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
{
tc = &g_tc901;
tcconfig = &g_tc901config;
handler = sam_tc901_interrupt;
tcno = 3;
}
else
#endif
@ -1008,7 +1078,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
return NULL;
}
/* Has the timer counter been initialized. We have to be careful here
/* Has the timer/counter been initialized. We have to be careful here
* because there is no semaphore protection.
*/
@ -1020,68 +1090,28 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
memset(tc, 0, sizeof(struct sam_tc_s));
sem_init(&tc->exclsem, 0, 1);
tc->base = tcconfig->base;
tc->tc = channel < 3 ? 0 : 1;
tc->pid = tcconfig->pid;
tc->tc = tcno;
/* Initialize the channels */
for (i = 0, ch = tcconfig->chfirst; i < SAM_TC_NCHANNELS; i++)
for (chndx = 0, ch = tcconfig->chfirst; chndx < SAM_TC_NCHANNELS; chndx++)
{
tcdbg("Initializing TC%d channel %d\n", tcconfig->tc, ch);
/* Initialize the channel data structure */
chan = &tc->channel[i];
chconfig = &tcconfig->channel[i];
chan = &tc->channel[chndx];
chconfig = &tcconfig->channel[chndx];
chan->tc = tc;
chan->base = chconfig->base;
chan->tc = tc;
chan->chan = ch++;
/* Configure channel input/output pins */
if (chconfig->clkset)
{
/* Configure clock input pin */
sam_configgpio(chconfig->clkset);
}
if (chconfig->tioaset)
{
/* Configure output A pin */
sam_configgpio(chconfig->tioaset);
}
if (chconfig->tiobset)
{
/* Configure output B pin */
sam_configgpio(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 */
regval = PMC_PCR_PID(tcconfig->pid) | PMC_PCR_CMD | PMC_PCR_EN;
putreg32(regval, SAM_PMC_PCR);
/* Enable clocking to the timer counter */
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 timer/counter is initialized */
tc->initialized = true;
}
@ -1091,21 +1121,68 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel)
sam_takesem(tc);
irqrestore(flags);
/* Get the requested channel structure */
/* Is the requested channel already in-use? */
chan = &tc->channel[channel - tcconfig->chfirst];
/* Is it available? */
chndx = channel - tcconfig->chfirst;
chan = &tc->channel[chndx];
if (chan->inuse)
{
/* No.. return a failure */
/* Yes.. return a failure */
tcdbg("Channel %d is in-used\n", channel);
tcdbg("Channel %d is in-use\n", channel);
sam_givesem(tc);
return NULL;
}
chconfig = &tcconfig->channel[chndx];
/* Configure channel input/output pins */
if (chconfig->clkset)
{
/* Configure clock input pin */
sam_configgpio(chconfig->clkset);
}
if (chconfig->tioaset)
{
/* Configure output A pin */
sam_configgpio(chconfig->tioaset);
}
if (chconfig->tiobset)
{
/* Configure output B pin */
sam_configgpio(chconfig->tiobset);
}
/* Enable clocking to the timer counter */
if (chconfig->pid < 32)
{
sam_enableperiph0(chconfig->pid);
}
else
{
sam_enableperiph1(chconfig->pid);
}
/* Set the maximum TC peripheral clock frequency.
* REVISIT: This is from the SAMA5. Does it apply here?
*/
regval = PMC_PCR_PID(chconfig->pid) | PMC_PCR_CMD | PMC_PCR_EN;
putreg32(regval, SAM_PMC_PCR);
/* Attach the timer interrupt handler and enable the timer interrupts */
(void)irq_attach(chconfig->pid, chconfig->handler);
up_enable_irq(chconfig->pid);
/* Mark the channel "inuse" */
chan->inuse = true;