* arch/arm/src/samd5e5/sam_tc.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <debug.h>
#include <string.h>
#include <semaphore.h>
#include <assert.h>
#include <nuttx/arch.h>
#include <nuttx/wdog.h>
#include <nuttx/clock.h>
#include <nuttx/mutex.h>
#include <nuttx/semaphore.h>
#include <nuttx/spinlock.h>
#include "arm_internal.h"
#include "sam_gclk.h"
#include "sam_periphclks.h"
#include "sam_port.h"
#include "sam_tc.h"
#include "hardware/sam_pac.h"
#include <arch/board/board.h>
#ifdef CONFIG_SAMD5E5_TC
* Pre-processor Definitions
****************************************************************************/
* Private Types
****************************************************************************/
* Private Types
****************************************************************************/
* Private Data
****************************************************************************/
#ifdef CONFIG_SAMD5E5_TC0
static const struct tc_attr_s g_tc0attr =
{
.tc = 0,
.irq = SAM_IRQ_TC0,
.coregen = BOARD_TC0_GCLKGEN,
.cc0 = BOARD_TC0_PINMAP_CC0,
.cc1 = BOARD_TC0_PINMAP_CC1,
.srcfreq = BOARD_TC0_FREQUENCY,
.base = SAM_TC0_BASE,
};
static struct sam_tc_dev_s g_tc0 =
{
.attr = &g_tc0attr,
.lock = NXMUTEX_INITIALIZER,
.waitsem = SEM_INITIALIZER(0),
};
#endif
#ifdef CONFIG_SAMD5E5_TC1
static const struct tc_attr_s g_tc1attr =
{
.tc = 1,
.irq = SAM_IRQ_TC1,
.coregen = BOARD_TC1_GCLKGEN,
.cc0 = BOARD_TC1_PINMAP_CC0,
.cc1 = BOARD_TC1_PINMAP_CC1,
.srcfreq = BOARD_TC1_FREQUENCY,
.base = SAM_TC1_BASE,
};
static struct sam_tc_dev_s g_tc1 =
{
.attr = &g_tc0attr,
.lock = NXMUTEX_INITIALIZER,
.waitsem = SEM_INITIALIZER(0),
};
#endif
#ifdef CONFIG_SAMD5E5_TC2
static const struct tc_attr_s g_tc2attr =
{
.tc = 2,
.irq = SAM_IRQ_TC2,
.coregen = BOARD_TC2_GCLKGEN,
.cc0 = BOARD_TC2_PINMAP_CC0,
.cc1 = BOARD_TC2_PINMAP_CC1,
.srcfreq = BOARD_TC2_FREQUENCY,
.base = SAM_TC2_BASE,
};
static struct sam_tc_dev_s g_tc2 =
{
.attr = &g_tc0attr,
.lock = NXMUTEX_INITIALIZER,
.waitsem = SEM_INITIALIZER(0),
};
#endif
#ifdef CONFIG_SAMD5E5_TC3
static const struct tc_attr_s g_tc3attr =
{
.tc = 3,
.irq = SAM_IRQ_TC3,
.coregen = BOARD_TC3_GCLKGEN,
.cc0 = BOARD_TC3_PINMAP_CC0,
.cc1 = BOARD_TC3_PINMAP_CC1,
.srcfreq = BOARD_TC3_FREQUENCY,
.base = SAM_TC3_BASE,
};
static struct sam_tc_dev_s g_tc3 =
{
.attr = &g_tc0attr,
.lock = NXMUTEX_INITIALIZER,
.waitsem = SEM_INITIALIZER(0),
};
#endif
#ifdef CONFIG_SAMD5E5_TC4
static const struct tc_attr_s g_tc4attr =
{
.tc = 4,
.irq = SAM_IRQ_TC4,
.coregen = BOARD_TC4_GCLKGEN,
.cc0 = BOARD_TC4_PINMAP_CC0,
.cc1 = BOARD_TC4_PINMAP_CC1,
.srcfreq = BOARD_TC4_FREQUENCY,
.base = SAM_TC4_BASE,
};
static struct sam_tc_dev_s g_tc4 =
{
.attr = &g_tc0attr,
.lock = NXMUTEX_INITIALIZER,
.waitsem = SEM_INITIALIZER(0),
};
#endif
#ifdef CONFIG_SAMD5E5_TC5
static const struct tc_attr_s g_tc5attr =
{
.tc = 5,
.irq = SAM_IRQ_TC5,
.coregen = BOARD_TC5_GCLKGEN,
.cc0 = BOARD_TC5_PINMAP_CC0,
.cc1 = BOARD_TC5_PINMAP_CC1,
.srcfreq = BOARD_TC5_FREQUENCY,
.base = SAM_TC5_BASE,
};
static struct sam_tc_dev_s g_tc5 =
{
.attr = &g_tc0attr,
.lock = NXMUTEX_INITIALIZER,
.waitsem = SEM_INITIALIZER(0),
};
#endif
static const uint8_t g_corclk_channel[SAMD5E5_NTC] =
{
GCLK_CHAN_TC0
#if SAMD5E5_NTC > 1
, GCLK_CHAN_TC1
#endif
#if SAMD5E5_NTC > 2
, GCLK_CHAN_TC2
#endif
#if SAMD5E5_NTC > 3
, GCLK_CHAN_TC3
#endif
#if SAMD5E5_NTC > 4
, GCLK_CHAN_TC4
#endif
#if SAMD5E5_NTC > 5
, GCLK_CHAN_TC5
#endif
};
* This table provides the frequency for each selection of TCCLK
*/
#define TC_NDIVIDERS 8
static const uint8_t g_log2divider[TC_NDIVIDERS] =
{
0,
1,
2,
3,
4,
6,
8,
10
};
#define TC_NREGISTERS 2
static const uint8_t g_regoffset[TC_NREGISTERS] =
{
SAM_TC_COUNT32_CC0_OFFSET,
SAM_TC_COUNT32_CC1_OFFSET,
};
* Private Function Prototypes
****************************************************************************/
void tc_bridge_enable(int tc);
void sam_tc_dumpregs(struct sam_tc_dev_s *priv);
void sam_tc_setperiod(struct sam_tc_dev_s *priv);
static uint32_t sam_tc_divfreq_lookup(uint32_t ftcin, int ndx);
* Private Functions
****************************************************************************/
* Private Functions
****************************************************************************/
* Name: tc_enable
*
* Description:
* Enable the Main Clock for Tc. (MCLK_APBxMASK)
*
****************************************************************************/
void tc_bridge_enable(int tc)
{
DEBUGASSERT((unsigned)tc < SAMD5E5_NTC);
switch (tc)
{
case 0:
sam_apb_tc0_enableperiph();
break;
case 1:
sam_apb_tc1_enableperiph();
break;
case 2:
sam_apb_tc2_enableperiph();
break;
case 3:
sam_apb_tc3_enableperiph();
break;
case 4:
sam_apb_tc4_enableperiph();
break;
case 5:
sam_apb_tc5_enableperiph();
break;
default:
break;
}
}
* Name: tc_wait_synchronization
*
* Description:
* Wait until the TC reports that it is synchronized.
*
****************************************************************************/
static void tc_wait_synchronization(struct sam_tc_dev_s *priv)
{
while ((getreg32(priv->attr->base + SAM_TC_SYNCBUSY_OFFSET) & 0x7) != 0);
}
* Name: tc_coreclk_configure
*
* Description:
* Configure the TC source clock. PCHCTRL[id]
*
* One generic clock is used by the TC: GCLK_TCx.
*
****************************************************************************/
void tc_coreclk_configure(int tc, int coregen, bool wrlock)
{
uint8_t corechan;
DEBUGASSERT((unsigned)tc < SAMD5E5_NTC);
corechan = g_corclk_channel[tc];
sam_gclk_chan_enable(corechan, coregen, wrlock);
}
* Name: 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 tc_interrupt(int irq, void *context, void *arg)
{
struct sam_tc_dev_s *priv = (struct sam_tc_dev_s *)arg;
uint8_t flags;
flags = getreg8(priv->attr->base + SAM_TC_INTFLAG_OFFSET);
if (flags & TC_INTFLAG_OVF)
{
tmrinfo("Overflow Interrupt Flag\n");
putreg8(TC_INTFLAG_OVF, priv->attr->base + SAM_TC_INTFLAG_OFFSET);
}
if (flags & TC_INTFLAG_ERR)
{
tmrinfo("Error Interrupt Flag\n");
putreg8(TC_INTFLAG_ERR, priv->attr->base + SAM_TC_INTFLAG_OFFSET);
}
if (flags & TC_INTFLAG_MC0)
{
tmrinfo("MC0 Interrupt Flag\n");
putreg8(TC_INTFLAG_MC0, priv->attr->base + SAM_TC_INTFLAG_OFFSET);
}
if (flags & TC_INTFLAG_MC1)
{
tmrinfo("MC1 Interrupt Flag\n");
putreg8(TC_INTFLAG_MC1, priv->attr->base + SAM_TC_INTFLAG_OFFSET);
}
if (priv->handler)
{
priv->handler(priv, priv->arg, flags);
}
return OK;
}
* Name: sam_tc_freqdiv_lookup
*
* Description:
* Given the TC input frequency (Ftcin) and a divider index, return the
* value of the Ftcin divider.
*
* Input Parameters:
* ftcin - TC input frequency
* ndx - Divider index
*
* Returned Value:
* The Ftcin input divider value
*
****************************************************************************/
static int sam_tc_freqdiv_lookup(uint32_t ftcin, int ndx)
{
if (ndx >= TC_NDIVIDERS)
{
* by the 32.768KHz slow clock. This returns a value that looks like
* correct divider if MCK were the input.
*/
return ftcin / BOARD_OSC32K_FREQUENCY;
}
else
{
return 1 << g_log2divider[ndx];
}
}
* Name: sam_tc_divfreq_lookup
*
* Description:
* Given the TC input frequency (Ftcin) and a divider index, return the
* value of the divided frequency
*
* Input Parameters:
* ftcin - TC input frequency
* ndx - Divider index
*
* Returned Value:
* The divided frequency value
*
****************************************************************************/
static uint32_t sam_tc_divfreq_lookup(uint32_t ftcin, int ndx)
{
if (ndx >= TC_NDIVIDERS)
{
return BOARD_OSC32K_FREQUENCY;
}
else
{
return ftcin >> g_log2divider[ndx];
}
}
* Name: sam_tc_initialize
*
* Description:
* There is no global, one-time initialization of timer/counter data
* structures. Rather, this function is called each time that a channel
* is allocated and, if the channel has not been initialized, it will be
* initialized then.
*
* Input Parameters:
* channel TC channel number (see TC_CHANx definitions)
*
* 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 inline struct sam_tc_dev_s *sam_tc_initialize(int tc)
{
struct sam_tc_dev_s *priv;
#ifdef CONFIG_SAMD5E5_TC0
if (tc == 0)
{
priv = &g_tc0;
}
else
#endif
#ifdef CONFIG_SAMD5E5_TC1
if (tc == 1)
{
priv = &g_tc1;
}
else
#endif
#ifdef CONFIG_SAMD5E5_TC2
if (tc == 2)
{
priv = &g_tc2;
}
else
#endif
#ifdef CONFIG_SAMD5E5_TC3
if (tc == 3)
{
priv = &g_tc3;
}
else
#endif
#ifdef CONFIG_SAMD5E5_TC4
if (tc == 4)
{
priv = &g_tc4;
}
else
#endif
#ifdef CONFIG_SAMD5E5_TC5
if (tc == 5)
{
priv = &g_tc5;
}
else
#endif
{
tmrerr("ERROR: Unsupported TC%d\n", tc);
return NULL;
}
return priv;
}
* Public Functions
****************************************************************************/
* Name: sam_tc_allocate
*
* Description:
* Configures a Timer Counter to operate in the given mode. The timer is
* stopped after configuration and must be restarted with sam_tc_start().
* All the interrupts of the timer are also disabled.
*
* Input Parameters:
* channel TC channel number
* mode Operating mode (TC_CMR value).
*
* Returned Value:
* On success, a non-NULL handle value is returned. This handle may be
* used with subsequent timer/counter interfaces to manage the timer. A
* NULL handle value is returned on a failure.
*
****************************************************************************/
TC_HANDLE sam_tc_allocate(int tc, int frequency)
{
struct sam_tc_dev_s *priv;
int ret;
uint32_t divisor;
uint32_t ctrla;
uint32_t regval;
irqstate_t flags;
* access to the requested tc.
*/
tmrinfo("tc=%d frequency=%d\n", tc, frequency);
priv = sam_tc_initialize(tc);
if (priv)
{
ret = sam_tc_divisor(priv, frequency, &divisor, &ctrla);
if (ret < 0)
{
tmrerr("ERROR: sam_tc_divisor failed: %d\n", ret);
return NULL;
}
tmrinfo("frequency=%lu, divisor=%lu, ctrla=0x%08lx\n",
(unsigned long)frequency, (unsigned long)divisor,
(unsigned long)ctrla);
flags = enter_critical_section();
ret = irq_attach(priv->attr->irq, tc_interrupt, priv);
if (ret < 0)
{
tmrerr("ERROR: TC%d failed to attach irq %d\n",
tc, priv->attr->irq);
leave_critical_section(flags);
return NULL;
}
tmrinfo("TC%d attached irq %d\n", tc, priv->attr->irq);
tc_bridge_enable(priv->attr->tc);
tc_bridge_enable(priv->attr->tc +1);
tc_coreclk_configure(priv->attr->tc, priv->attr->coregen, false);
regval = getreg32(priv->attr->base + SAM_TC_CTRLA_OFFSET);
if (regval & TC_CTRLA_ENABLE)
{
tmrerr("ERROR: Cannot initialize TC! It's already initialized!\n");
leave_critical_section(flags);
return NULL;
}
regval = getreg32(priv->attr->base + SAM_TC_CTRLA_OFFSET);
if (regval & TC_CTRLA_SWRST)
{
tmrerr("ERROR: Module is in RESET process!\n");
leave_critical_section(flags);
return NULL;
}
putreg32(ctrla | TC_CTRLA_MODE_COUNT32,
priv->attr->base + SAM_TC_CTRLA_OFFSET);
tc_wait_synchronization(priv);
up_enable_irq(priv->attr->irq);
tmrinfo("enable TC%d irq=%d\n", tc, priv->attr->irq);
nxmutex_lock(&priv->lock);
if (priv->inuse)
{
tmrerr("ERROR: TC%d is in-use\n", priv->attr->tc);
nxmutex_unlock(&priv->lock);
leave_critical_section(flags);
return NULL;
}
priv->inuse = true;
priv->initialized = true;
leave_critical_section(flags);
nxmutex_unlock(&priv->lock);
}
tmrinfo("Returning %p\n", priv);
return (TC_HANDLE)priv;
}
* Name: sam_tc_free
*
* Description:
* Release the handle previously allocated by sam_tc_allocate().
*
* Input Parameters:
* handle Channel handle previously allocated by sam_tc_allocate()
*
* Returned Value:
* None
*
****************************************************************************/
void sam_tc_free(TC_HANDLE handle)
{
struct sam_tc_dev_s *priv = (struct sam_tc_dev_s *)handle;
tmrinfo("Freeing %p inuse=%d\n", priv, priv->inuse);
DEBUGASSERT(priv && priv->inuse);
* is stopped and disabled.
*/
sam_tc_detach(handle);
sam_tc_stop(handle);
priv->inuse = false;
}
* Name: sam_tc_start
*
* Description:
* Reset and Start the TC Channel. Enables the timer clock and performs a
* software reset to start the counting.
*
* Input Parameters:
* handle Channel handle previously allocated by sam_tc_allocate()
*
* Returned Value:
*
****************************************************************************/
void sam_tc_start(TC_HANDLE handle)
{
struct sam_tc_dev_s *priv = (struct sam_tc_dev_s *)handle;
uint32_t regval;
tmrinfo("Starting TC%d inuse=%d COUNT=%" PRId32 "\n",
priv->attr->tc, priv->inuse, sam_tc_getcounter(priv));
DEBUGASSERT(priv && priv->inuse);
putreg32(0, priv->attr->base + SAM_TC_COUNT_OFFSET);
putreg8(TC_INTFLAG_ALL, priv->attr->base + SAM_TC_INTFLAG_OFFSET);
regval = getreg32(priv->attr->base + SAM_TC_CTRLA_OFFSET);
regval |= TC_CTRLA_ENABLE;
putreg32(regval, priv->attr->base + SAM_TC_CTRLA_OFFSET);
tc_wait_synchronization(priv);
}
* Name: sam_tc_stop
*
* Description:
* Stop TC Channel. Disables the timer clock, stopping the counting.
*
* Input Parameters:
* handle Channel handle previously allocated by sam_tc_allocate()
*
* Returned Value:
*
****************************************************************************/
void sam_tc_stop(TC_HANDLE handle)
{
struct sam_tc_dev_s *priv = (struct sam_tc_dev_s *)handle;
uint32_t regval;
tmrinfo("Stopping TC%d inuse=%d\n", priv->attr->tc, priv->inuse);
DEBUGASSERT(priv && priv->inuse);
regval = getreg32(priv->attr->base + SAM_TC_CTRLA_OFFSET);
regval &= ~TC_CTRLA_ENABLE;
putreg32(regval, priv->attr->base + SAM_TC_CTRLA_OFFSET);
tc_wait_synchronization(priv);
}
* 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_tc_dev_s *priv = (struct sam_tc_dev_s *)handle;
tc_handler_t oldhandler;
irqstate_t flags;
DEBUGASSERT(priv);
flags = enter_critical_section();
oldhandler = priv->handler;
priv->handler = handler;
* says.
*/
if (!handler)
{
arg = NULL;
mask = 0;
}
priv->arg = arg;
putreg8(TC_INTFLAG_ALL, priv->attr->base + SAM_TC_INTENCLR_OFFSET);
putreg8(mask, priv->attr->base + SAM_TC_INTENSET_OFFSET);
if (mask & TC_INTFLAG_MC0)
putreg8(TC_CTRLBSET_ONESHOT, priv->attr->base + SAM_TC_CTRLBSET_OFFSET);
leave_critical_section(flags);
return oldhandler;
}
* Name: sam_tc_getpending
*
* Description:
* Return the current contents of the interrupt status register, clearing
* all pending interrupts.
*
* Input Parameters:
* handle The handle that represents the timer state
*
* Returned Value:
* The value of the channel interrupt status register.
*
****************************************************************************/
uint8_t sam_tc_getpending(TC_HANDLE handle)
{
struct sam_tc_dev_s *priv = (struct sam_tc_dev_s *)handle;
DEBUGASSERT(priv);
return getreg8(priv->attr->base + SAM_TC_INTFLAG_OFFSET);
}
* Name: sam_tc_setregister
*
* Description:
* Set TC_REGA, TC_REGB, or TC_REGC register.
*
* Input Parameters:
* handle Channel handle previously allocated by sam_tc_allocate()
* regid One of {TC_REGA, TC_REGB, or TC_REGC}
* regval Then value to set in the register
*
* Returned Value:
* None
*
****************************************************************************/
void sam_tc_setregister(TC_HANDLE handle, int regid, uint32_t regval)
{
struct sam_tc_dev_s *priv = (struct sam_tc_dev_s *)handle;
DEBUGASSERT(priv && regid < TC_NREGISTERS);
tmrinfo("TC%d: Set register CC%d to 0x%08lx\n",
priv->attr->tc, regid, (unsigned long)regval);
putreg32(regval, priv->attr->base + g_regoffset[regid]);
}
* Name: sam_tc_getregister
*
* Description:
* Get the current value of the channel register.
*
* Input Parameters:
* handle Channel handle previously allocated by sam_tc_allocate()
* regid One of {TC_REGA, TC_REGB, or TC_REGC}
*
* Returned Value:
* The value of the specified register.
*
****************************************************************************/
uint32_t sam_tc_getregister(TC_HANDLE handle, int regid)
{
struct sam_tc_dev_s *priv = (struct sam_tc_dev_s *)handle;
DEBUGASSERT(priv && regid < TC_NREGISTERS);
return getreg32(priv->attr->base + g_regoffset[regid]);
}
* Name: sam_tc_getcounter
*
* Description:
* Return the current value of the timer counter register
*
* Input Parameters:
* handle Channel handle previously allocated by sam_tc_allocate()
*
* Returned Value:
* The current value of the timer counter register for this channel.
*
****************************************************************************/
uint32_t sam_tc_getcounter(TC_HANDLE handle)
{
struct sam_tc_dev_s *priv = (struct sam_tc_dev_s *)handle;
DEBUGASSERT(priv);
putreg8(TC_CTRLBSET_CMD_READSYNC,
priv->attr->base + SAM_TC_CTRLBSET_OFFSET);
return getreg32(priv->attr->base + SAM_TC_COUNT_OFFSET);
}
* Name: sam_tc_infreq
*
* Description:
* Return the timer input frequency (Ftcin), that is, the MCK frequency
* divided down so that the timer/counter is driven within its maximum
* frequency.
*
* Input Parameters:
* None
*
* Returned Value:
* The timer input frequency.
*
****************************************************************************/
uint32_t sam_tc_infreq(void)
{
return BOARD_GCLK3_FREQUENCY;
}
* Name: sam_tc_divfreq
*
* Description:
* Return the divided timer input frequency that is currently driving the
* the timer counter.
*
* Input Parameters:
* handle Channel handle previously allocated by sam_tc_allocate()
*
* Returned Value:
* The timer counter frequency.
*
****************************************************************************/
uint32_t sam_tc_divfreq(TC_HANDLE handle)
{
struct sam_tc_dev_s *priv = (struct sam_tc_dev_s *)handle;
uint32_t ftcin = priv->attr->srcfreq;
uint32_t regval;
int tcclks;
DEBUGASSERT(priv);
* and extract the PRESCALER index.
*/
regval = getreg32(priv->attr->base + SAM_TC_CTRLA_OFFSET);
tcclks = (regval & TC_CTRLA_PRESCALER_MASK) >> TC_CTRLA_PRESCALER_SHIFT;
return sam_tc_divfreq_lookup(ftcin, tcclks);
}
* Name: sam_tc_divisor
*
* Description:
* Finds the best MCK divisor given the timer frequency and MCK. The
* result is guaranteed to satisfy the following equation:
*
* (Ftcin / (div * 65536)) <= freq <= (Ftcin / dev)
*
* where:
* freq - the desired frequency
* Ftcin - The timer/counter input frequency
* div - With DIV being the highest possible value.
*
* Input Parameters:
* frequency Desired timer frequency.
* div Divisor value.
* tcclks TCCLKS field value for divisor.
*
* Returned Value:
* Zero (OK) if a proper divisor has been found, otherwise a negated
* errno value indicating the nature of the failure.
*
****************************************************************************/
int sam_tc_divisor(struct sam_tc_dev_s *tc, uint32_t frequency,
uint32_t *div, uint32_t *tcclks)
{
uint32_t ftcin = tc->attr->srcfreq;
int ndx = 0;
tmrinfo("frequency=%" PRId32 "\n", frequency);
*
* frequency >= (tc_input_frequency * 65536) / divider.
*/
while (frequency < (sam_tc_divfreq_lookup(ftcin, ndx) >> 16))
{
if (++ndx > TC_NDIVIDERS)
{
tmrerr("ERROR: Lower bound search failed\n");
return -ERANGE;
}
}
* That the value of the divider such that:
*
* frequency < tc_input_frequency / divider.
*/
for (; ndx < (TC_NDIVIDERS - 1); ndx++)
{
if (frequency > sam_tc_divfreq_lookup(ftcin, ndx + 1))
{
break;
}
}
if (div)
{
uint32_t value = sam_tc_freqdiv_lookup(ftcin, ndx);
tmrinfo("return div=%lu\n", (unsigned long)value);
*div = value;
}
if (tcclks)
{
tmrinfo("return tcclks=0x%08lx\n",
(unsigned long)TC_CTRLA_PRESCALER(ndx));
*tcclks = TC_CTRLA_PRESCALER(ndx);
}
return OK;
}
uint32_t sam_tc_getctrla(TC_HANDLE handle)
{
struct sam_tc_dev_s *priv = (struct sam_tc_dev_s *)handle;
DEBUGASSERT(priv);
return getreg32(priv->attr->base + SAM_TC_CTRLA_OFFSET);
}
#endif