You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2022/03/15 19:20:10 UTC
[incubator-nuttx] branch master updated: arch/arm/samv7/sam_tc: implement timer driver support
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 9858291 arch/arm/samv7/sam_tc: implement timer driver support
9858291 is described below
commit 985829190e89a895163dab8ada1dc760c61e773b
Author: Petro Karashchenko <pe...@gmail.com>
AuthorDate: Mon Mar 14 22:59:00 2022 +0100
arch/arm/samv7/sam_tc: implement timer driver support
Signed-off-by: Petro Karashchenko <pe...@gmail.com>
---
arch/arm/src/samv7/Make.defs | 3 +
arch/arm/src/samv7/sam_clockconfig.c | 2 +-
arch/arm/src/samv7/sam_freerun.c | 2 +-
arch/arm/src/samv7/sam_oneshot.c | 2 +-
arch/arm/src/samv7/sam_tc.c | 28 +
arch/arm/src/samv7/sam_tc.h | 17 +
arch/arm/src/samv7/sam_tc_lowerhalf.c | 678 ++++++++++++++++++++++++
arch/arm/src/samv7/sam_tc_lowerhalf.h | 55 ++
boards/arm/samv7/same70-qmtech/src/sam_boot.c | 12 +-
boards/arm/samv7/same70-xplained/src/sam_boot.c | 12 +-
boards/arm/samv7/samv71-xult/src/sam_boot.c | 12 +-
11 files changed, 817 insertions(+), 6 deletions(-)
diff --git a/arch/arm/src/samv7/Make.defs b/arch/arm/src/samv7/Make.defs
index 5906bc6..1c7f8b6 100644
--- a/arch/arm/src/samv7/Make.defs
+++ b/arch/arm/src/samv7/Make.defs
@@ -142,6 +142,9 @@ endif
ifeq ($(CONFIG_SAMV7_HAVE_TC),y)
CHIP_CSRCS += sam_tc.c
+ifeq ($(CONFIG_TIMER),y)
+CHIP_CSRCS += sam_tc_lowerhalf.c
+endif
ifeq ($(CONFIG_SAMV7_ONESHOT),y)
CHIP_CSRCS += sam_oneshot.c sam_oneshot_lowerhalf.c
endif
diff --git a/arch/arm/src/samv7/sam_clockconfig.c b/arch/arm/src/samv7/sam_clockconfig.c
index 5380e3c..529c0cf 100644
--- a/arch/arm/src/samv7/sam_clockconfig.c
+++ b/arch/arm/src/samv7/sam_clockconfig.c
@@ -124,7 +124,7 @@ static inline void sam_supcsetup(void)
if ((getreg32(SAM_SUPC_SR) & SUPC_SR_OSCSEL) == 0)
{
- uint32_t delay;
+ volatile uint32_t delay;
putreg32((SUPC_CR_XTALSEL | SUPR_CR_KEY), SAM_SUPC_CR);
for (delay = 0;
diff --git a/arch/arm/src/samv7/sam_freerun.c b/arch/arm/src/samv7/sam_freerun.c
index b3a026a..3d4b3d9 100644
--- a/arch/arm/src/samv7/sam_freerun.c
+++ b/arch/arm/src/samv7/sam_freerun.c
@@ -130,7 +130,7 @@ int sam_freerun_initialize(struct sam_freerun_s *freerun, int chan,
/* Get the TC frequency the corresponds to the requested resolution */
- frequency = USEC_PER_SEC / (uint32_t)resolution;
+ frequency = USEC_PER_SEC / resolution;
/* The pre-calculate values to use when we start the timer */
diff --git a/arch/arm/src/samv7/sam_oneshot.c b/arch/arm/src/samv7/sam_oneshot.c
index 5190387..3473319 100644
--- a/arch/arm/src/samv7/sam_oneshot.c
+++ b/arch/arm/src/samv7/sam_oneshot.c
@@ -158,7 +158,7 @@ int sam_oneshot_initialize(struct sam_oneshot_s *oneshot, int chan,
/* Get the TC frequency the corresponds to the requested resolution */
- frequency = USEC_PER_SEC / (uint32_t)resolution;
+ frequency = USEC_PER_SEC / resolution;
/* The pre-calculate values to use when we start the timer */
diff --git a/arch/arm/src/samv7/sam_tc.c b/arch/arm/src/samv7/sam_tc.c
index b148188..e88bec3 100644
--- a/arch/arm/src/samv7/sam_tc.c
+++ b/arch/arm/src/samv7/sam_tc.c
@@ -1427,6 +1427,34 @@ uint32_t sam_tc_getpending(TC_HANDLE handle)
}
/****************************************************************************
+ * Name: sam_tc_settcclks
+ *
+ * Description:
+ * Set the value of TCCLKS clock selection in TC_CMR register
+ *
+ * Input Parameters:
+ * handle The handle that represents the timer state
+ * tcclks The clock selection value to set
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void sam_tc_settcclks(TC_HANDLE handle, uint32_t tcclks)
+{
+ struct sam_chan_s *chan = (struct sam_chan_s *)handle;
+ uint32_t regval;
+
+ DEBUGASSERT(chan);
+
+ regval = sam_chan_getreg(chan, SAM_TC_CMR_OFFSET);
+ regval &= ~TC_CMR_TCCLKS_MASK;
+ regval |= tcclks;
+ sam_chan_putreg(chan, SAM_TC_CMR_OFFSET, regval);
+}
+
+/****************************************************************************
* Name: sam_tc_setregister
*
* Description:
diff --git a/arch/arm/src/samv7/sam_tc.h b/arch/arm/src/samv7/sam_tc.h
index 9ffb69f..2517b3d 100644
--- a/arch/arm/src/samv7/sam_tc.h
+++ b/arch/arm/src/samv7/sam_tc.h
@@ -220,6 +220,23 @@ tc_handler_t sam_tc_attach(TC_HANDLE handle, tc_handler_t handler,
uint32_t sam_tc_getpending(TC_HANDLE handle);
/****************************************************************************
+ * Name: sam_tc_settcclks
+ *
+ * Description:
+ * Set the value of TCCLKS clock selection in TC_CMR register
+ *
+ * Input Parameters:
+ * handle The handle that represents the timer state
+ * tcclks The clock selection value to set
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void sam_tc_settcclks(TC_HANDLE handle, uint32_t tcclks);
+
+/****************************************************************************
* Name: sam_tc_setregister
*
* Description:
diff --git a/arch/arm/src/samv7/sam_tc_lowerhalf.c b/arch/arm/src/samv7/sam_tc_lowerhalf.c
new file mode 100644
index 0000000..7f743e0
--- /dev/null
+++ b/arch/arm/src/samv7/sam_tc_lowerhalf.c
@@ -0,0 +1,678 @@
+/****************************************************************************
+ * arch/arm/src/samv7/sam_tc_lowerhalf.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 <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/timers/timer.h>
+
+#include <arch/board/board.h>
+
+#include "sam_tc.h"
+
+#if defined(CONFIG_TIMER) && \
+ (defined(CONFIG_SAMV7_TC0) || defined(CONFIG_SAMV7_TC1) || \
+ defined(CONFIG_SAMV7_TC2) || defined(CONFIG_SAMV7_TC3))
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure provides the private representation of the "lower-half"
+ * driver state structure. This structure must be cast-compatible with the
+ * timer_lowerhalf_s structure.
+ */
+
+struct sam_lowerhalf_s
+{
+ FAR const struct timer_ops_s *ops; /* Lower half operations */
+ TC_HANDLE tch; /* Handle returned by sam_tc_initialize() */
+ tccb_t callback; /* Current user interrupt callback */
+ FAR void *arg; /* Argument passed to upper half callback */
+ bool started; /* True: Timer has been started */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void sam_timer_handler(TC_HANDLE tch, void *arg, uint32_t sr);
+
+/* "Lower half" driver methods **********************************************/
+
+static int sam_start(FAR struct timer_lowerhalf_s *lower);
+static int sam_stop(FAR struct timer_lowerhalf_s *lower);
+static int sam_getstatus(FAR struct timer_lowerhalf_s *lower,
+ FAR struct timer_status_s *status);
+static int sam_settimeout(FAR struct timer_lowerhalf_s *lower,
+ uint32_t timeout);
+static void sam_setcallback(FAR struct timer_lowerhalf_s *lower,
+ tccb_t callback, FAR void *arg);
+static int sam_maxtimeout(FAR struct timer_lowerhalf_s *lower,
+ FAR uint32_t *maxtimeout);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* "Lower half" driver methods */
+
+static const struct timer_ops_s g_timer_ops =
+{
+ .start = sam_start,
+ .stop = sam_stop,
+ .getstatus = sam_getstatus,
+ .settimeout = sam_settimeout,
+ .setcallback = sam_setcallback,
+ .ioctl = NULL,
+ .maxtimeout = sam_maxtimeout
+};
+
+#ifdef CONFIG_SAMV7_TC0
+static struct sam_lowerhalf_s g_tc0_lowerhalf =
+{
+ .ops = &g_timer_ops
+};
+
+static struct sam_lowerhalf_s g_tc1_lowerhalf =
+{
+ .ops = &g_timer_ops
+};
+
+static struct sam_lowerhalf_s g_tc2_lowerhalf =
+{
+ .ops = &g_timer_ops
+};
+#endif
+
+#ifdef CONFIG_SAMV7_TC1
+static struct sam_lowerhalf_s g_tc3_lowerhalf =
+{
+ .ops = &g_timer_ops
+};
+
+static struct sam_lowerhalf_s g_tc4_lowerhalf =
+{
+ .ops = &g_timer_ops
+};
+
+static struct sam_lowerhalf_s g_tc5_lowerhalf =
+{
+ .ops = &g_timer_ops
+};
+#endif
+
+#ifdef CONFIG_SAMV7_TC2
+static struct sam_lowerhalf_s g_tc6_lowerhalf =
+{
+ .ops = &g_timer_ops
+};
+
+static struct sam_lowerhalf_s g_tc7_lowerhalf =
+{
+ .ops = &g_timer_ops
+};
+
+static struct sam_lowerhalf_s g_tc8_lowerhalf =
+{
+ .ops = &g_timer_ops
+};
+#endif
+
+#ifdef CONFIG_SAMV7_TC3
+static struct sam_lowerhalf_s g_tc9_lowerhalf =
+{
+ .ops = &g_timer_ops
+};
+
+static struct sam_lowerhalf_s g_tc10_lowerhalf =
+{
+ .ops = &g_timer_ops
+};
+
+static struct sam_lowerhalf_s g_tc11_lowerhalf =
+{
+ .ops = &g_timer_ops
+};
+#endif
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sam_timer_handler
+ *
+ * Description:
+ * timer interrupt handler
+ *
+ * Input Parameters:
+ *
+ * Returned Value:
+ *
+ ****************************************************************************/
+
+static void sam_timer_handler(TC_HANDLE tch, void *arg, uint32_t sr)
+{
+ FAR struct sam_lowerhalf_s *lower = (struct sam_lowerhalf_s *)arg;
+ uint32_t next_interval_us = 0;
+
+ if (lower->callback(&next_interval_us, lower->arg))
+ {
+ if (next_interval_us > 0)
+ {
+ sam_settimeout((struct timer_lowerhalf_s *)lower,
+ next_interval_us);
+ }
+
+ /* Start the counter */
+
+ sam_tc_start(tch);
+ }
+ else
+ {
+ sam_stop((struct timer_lowerhalf_s *)lower);
+ }
+}
+
+/****************************************************************************
+ * Name: sam_start
+ *
+ * Description:
+ * Start the timer, resetting the time to the current timeout,
+ *
+ * Input Parameters:
+ * lower - A pointer the publicly visible representation of the
+ * "lower-half" driver state structure.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int sam_start(FAR struct timer_lowerhalf_s *lower)
+{
+ FAR struct sam_lowerhalf_s *priv = (FAR struct sam_lowerhalf_s *)lower;
+ int ret = -EBUSY; /* EBUSY indicates that the timer was already running */
+ irqstate_t flags;
+
+ flags = enter_critical_section();
+
+ if (!priv->started)
+ {
+ if (priv->callback != NULL)
+ {
+ /* Set up to receive the callback when the interrupt occurs */
+
+ sam_tc_attach(priv->tch, sam_timer_handler, priv, TC_INT_CPCS);
+
+ /* Start the counter */
+
+ sam_tc_start(priv->tch);
+ }
+
+ priv->started = true;
+ ret = OK;
+ }
+
+ leave_critical_section(flags);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: sam_stop
+ *
+ * Description:
+ * Stop the timer
+ *
+ * Input Parameters:
+ * lower - A pointer the publicly visible representation of the
+ * "lower-half" driver state structure.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int sam_stop(struct timer_lowerhalf_s *lower)
+{
+ struct sam_lowerhalf_s *priv = (struct sam_lowerhalf_s *)lower;
+ int ret = -ENODEV; /* ENODEV indicates that the timer was not running */
+ irqstate_t flags;
+
+ flags = enter_critical_section();
+
+ if (priv->started)
+ {
+ sam_tc_stop(priv->tch);
+ sam_tc_detach(priv->tch);
+ priv->started = false;
+ ret = OK;
+ }
+
+ leave_critical_section(flags);
+
+ return ret;
+}
+
+/****************************************************************************
+ * Name: sam_getstatus
+ *
+ * Description:
+ * get timer status
+ *
+ * Input Parameters:
+ * lower - A pointer the publicly visible representation of the
+ * "lower- half" driver state structure.
+ * status - The location to return the status information.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int sam_getstatus(FAR struct timer_lowerhalf_s *lower,
+ FAR struct timer_status_s *status)
+{
+ FAR struct sam_lowerhalf_s *priv = (FAR struct sam_lowerhalf_s *)lower;
+ uint32_t frequency;
+ uint16_t period;
+ uint16_t current;
+
+ DEBUGASSERT(priv);
+
+ /* Return the status bit */
+
+ status->flags = 0;
+ if (priv->started)
+ {
+ status->flags |= TCFLAGS_ACTIVE;
+ }
+
+ if (priv->callback)
+ {
+ status->flags |= TCFLAGS_HANDLER;
+ }
+
+ frequency = sam_tc_divfreq(priv->tch);
+ period = sam_tc_getregister(priv->tch, TC_REGC);
+ current = sam_tc_getcounter(priv->tch);
+
+ /* Get timeout */
+
+ status->timeout = (1000000llu * period) / frequency;
+
+ /* Get the time remaining until the timer expires (in microseconds) */
+
+ status->timeleft = (1000000llu * (period - current)) / frequency;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: sam_settimeout
+ *
+ * Description:
+ * Set a new timeout value (and reset the timer)
+ *
+ * Input Parameters:
+ * lower - A pointer the publicly visible representation of the "lower-
+ * half" driver state structure.
+ * timeout - The new timeout value in microseconds.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int sam_settimeout(FAR struct timer_lowerhalf_s *lower,
+ uint32_t timeout)
+{
+ FAR struct sam_lowerhalf_s *priv = (FAR struct sam_lowerhalf_s *)lower;
+ uint64_t maxtimeout;
+ uint64_t regval;
+ uint32_t desired;
+ uint32_t actual;
+ uint32_t tcclks = 0;
+
+ if (priv->started)
+ {
+ return -EPERM;
+ }
+
+ desired = USEC_PER_SEC;
+ actual = desired;
+ maxtimeout = (1000000llu * 0xffff) / actual;
+
+ while ((timeout > maxtimeout) && (desired > 0))
+ {
+ desired /= 10;
+
+ if (sam_tc_clockselect(desired, &tcclks, &actual) < 0)
+ {
+ break;
+ }
+
+ maxtimeout = (1000000llu * 0xffff) / actual;
+ }
+
+ /* Can this timeout be represented? */
+
+ if (timeout < 1 || timeout > maxtimeout)
+ {
+ tmrerr("ERROR: Cannot represent timeout=%" PRIu32 " > %" PRIu64 "\n",
+ timeout, maxtimeout);
+ return -ERANGE;
+ }
+
+ if (actual != sam_tc_divfreq(priv->tch))
+ {
+ sam_tc_settcclks(priv->tch, tcclks);
+ }
+
+ /* Get the timer counter frequency and determine the number of counts
+ * needed to achieve the requested delay.
+ *
+ * frequency = ticks / second
+ * ticks = seconds * frequency
+ * = (usecs * frequency) / USEC_PER_SEC;
+ */
+
+ regval = (timeout * (uint64_t)sam_tc_divfreq(priv->tch)) / USEC_PER_SEC;
+
+ tmrinfo("timeout=%" PRIu32 " regval=%08" PRIx64 "\n", timeout, regval);
+ DEBUGASSERT(regval <= UINT16_MAX);
+
+ /* Set RC so that an event will be triggered when TC_CV register counts
+ * up to RC.
+ */
+
+ sam_tc_setregister(priv->tch, TC_REGC, regval);
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: sam_setcallback
+ *
+ * Description:
+ * Call this user provided timeout callback.
+ *
+ * Input Parameters:
+ * lower - A pointer the publicly visible representation of the
+ * "lower-half" driver state structure.
+ * callback - The new timer expiration function pointer. If this
+ * function pointer is NULL, then the reset-on-expiration
+ * behavior is restored,
+ * arg - Argument that will be provided in the callback.
+ *
+ * Returned Value:
+ * The previous timer expiration function pointer or NULL is there was
+ * no previous function pointer.
+ *
+ ****************************************************************************/
+
+static void sam_setcallback(FAR struct timer_lowerhalf_s *lower,
+ tccb_t callback, FAR void *arg)
+{
+ FAR struct sam_lowerhalf_s *priv = (FAR struct sam_lowerhalf_s *)lower;
+
+ irqstate_t flags = enter_critical_section();
+
+ /* Save the new callback */
+
+ priv->callback = callback;
+ priv->arg = arg;
+
+ if (callback != NULL && priv->started)
+ {
+ sam_tc_attach(priv->tch, sam_timer_handler, priv, TC_INT_CPCS);
+ }
+ else
+ {
+ sam_tc_detach(priv->tch);
+ }
+
+ leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: sam_maxtimeout
+ *
+ * Description:
+ * Get the maximum supported timeout value
+ *
+ * Input Parameters:
+ * lower A pointer the publicly visible representation of the
+ * "lower-half" driver state structure.
+ * maxtimeout The max value in microseconds will be written here.
+ *
+ * Returned Value:
+ * Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int sam_maxtimeout(FAR struct timer_lowerhalf_s *lower,
+ FAR uint32_t *maxtimeout)
+{
+ uint64_t bigusec;
+ uint32_t frequency = USEC_PER_SEC;
+ int ret;
+
+ ret = sam_tc_clockselect(BOARD_SLOWCLK_FREQUENCY, NULL, &frequency);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ bigusec = (1000000ull * 0xffff) / frequency;
+ if (bigusec > UINT32_MAX)
+ {
+ *maxtimeout = UINT32_MAX;
+ }
+ else
+ {
+ *maxtimeout = bigusec;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sam_timer_initialize
+ *
+ * Description:
+ * Bind the configuration timer to a timer lower half instance and
+ * register the timer drivers at 'devpath'
+ *
+ * Input Parameters:
+ * devpath The full path to the timer device. This should be of the
+ * form /dev/timer0.
+ * chan Timer counter channel to be used. See the TC_CHAN*
+ * definitions in arch/arm/src/samv7/sam_tc.h.
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success; A negated errno value is returned
+ * to indicate the nature of any failure.
+ *
+ ****************************************************************************/
+
+int sam_timer_initialize(FAR const char *devpath, int chan)
+{
+ FAR struct sam_lowerhalf_s *lower;
+ uint32_t actual;
+ uint32_t cmr;
+ int ret;
+
+ switch (chan)
+ {
+#ifdef CONFIG_SAMV7_TC0
+ case TC_CHAN0:
+ lower = &g_tc0_lowerhalf;
+ break;
+
+ case TC_CHAN1:
+ lower = &g_tc1_lowerhalf;
+ break;
+
+ case TC_CHAN2:
+ lower = &g_tc2_lowerhalf;
+ break;
+#endif
+#ifdef CONFIG_SAMV7_TC1
+ case TC_CHAN3:
+ lower = &g_tc3_lowerhalf;
+ break;
+
+ case TC_CHAN4:
+ lower = &g_tc4_lowerhalf;
+ break;
+
+ case TC_CHAN5:
+ lower = &g_tc5_lowerhalf;
+ break;
+#endif
+#ifdef CONFIG_SAMV7_TC2
+ case TC_CHAN6:
+ lower = &g_tc6_lowerhalf;
+ break;
+
+ case TC_CHAN7:
+ lower = &g_tc7_lowerhalf;
+ break;
+
+ case TC_CHAN8:
+ lower = &g_tc8_lowerhalf;
+ break;
+#endif
+#ifdef CONFIG_SAMV7_TC3
+ case TC_CHAN9:
+ lower = &g_tc9_lowerhalf;
+ break;
+
+ case TC_CHAN10:
+ lower = &g_tc10_lowerhalf;
+ break;
+
+ case TC_CHAN11:
+ lower = &g_tc11_lowerhalf;
+ break;
+#endif
+ default:
+ return -ENODEV;
+ }
+
+ /* The pre-calculate values to use when we start the timer */
+
+ ret = sam_tc_clockselect(USEC_PER_SEC, &cmr, &actual);
+ if (ret < 0)
+ {
+ tmrerr("ERROR: sam_tc_clockselect failed: %d\n", ret);
+ return ret;
+ }
+
+ tmrinfo("actual=%" PRIu32 ", cmr=%08" PRIx32 "\n", actual, cmr);
+
+ /* Initialize the elements of lower half state structure */
+
+ lower->started = false;
+ lower->callback = NULL;
+
+ /* Allocate the timer/counter and select its mode of operation
+ *
+ * TC_CMR_TCCLKS - Returned by sam_tc_clockselect
+ * 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
+ * TC_CMR_CPCDIS=0 - Don't disable the clock on an RC compare event
+ * TC_CMR_EEVTEDG_NONE - No external events (and, hence, no edges
+ * TC_CMR_EEVT_TIOB - ???? REVISIT
+ * TC_CMR_ENET=0 - External event trigger disabled
+ * TC_CMR_WAVSEL_UPRC - TC_CV is incremented from 0 to the value of RC,
+ * then automatically reset on a RC Compare
+ * TC_CMR_WAVE - Waveform mode
+ * TC_CMR_ACPA_NONE - RA compare has no effect on TIOA
+ * TC_CMR_ACPC_NONE - RC compare has no effect on TIOA
+ * TC_CMR_AEEVT_NONE - No external event effect on TIOA
+ * TC_CMR_ASWTRG_NONE - No software trigger effect on TIOA
+ * TC_CMR_BCPB_NONE - RB compare has no effect on TIOB
+ * TC_CMR_BCPC_NONE - RC compare has no effect on TIOB
+ * TC_CMR_BEEVT_NONE - No external event effect on TIOB
+ * TC_CMR_BSWTRG_NONE - No software trigger effect on TIOB
+ */
+
+ cmr |= (TC_CMR_BURST_NONE | TC_CMR_CPCSTOP | TC_CMR_EEVTEDG_NONE |
+ TC_CMR_EEVT_TIOB | TC_CMR_WAVSEL_UPRC | TC_CMR_WAVE |
+ TC_CMR_ACPA_NONE | TC_CMR_ACPC_NONE | TC_CMR_AEEVT_NONE |
+ TC_CMR_ASWTRG_NONE | TC_CMR_BCPB_NONE | TC_CMR_BCPC_NONE |
+ TC_CMR_BEEVT_NONE | TC_CMR_BSWTRG_NONE);
+
+ lower->tch = sam_tc_allocate(chan, cmr);
+
+ if (lower->tch == NULL)
+ {
+ return -EINVAL;
+ }
+
+ /* Register the timer driver as /dev/timerX. The returned value from
+ * timer_register is a handle that could be used with timer_unregister().
+ * REVISIT: The returned handle is discard here.
+ */
+
+ FAR void *drvr = timer_register(devpath,
+ (FAR struct timer_lowerhalf_s *)lower);
+ if (drvr == NULL)
+ {
+ /* The actual cause of the failure may have been a failure to allocate
+ * perhaps a failure to register the timer driver (such as if the
+ * 'depath' were not unique). We know here but we return EEXIST to
+ * indicate the failure (implying the non-unique devpath).
+ */
+
+ sam_tc_free(lower->tch);
+ return -EEXIST;
+ }
+
+ return OK;
+}
+
+#endif /* CONFIG_TIMER */
diff --git a/arch/arm/src/samv7/sam_tc_lowerhalf.h b/arch/arm/src/samv7/sam_tc_lowerhalf.h
new file mode 100644
index 0000000..8f9fab7
--- /dev/null
+++ b/arch/arm/src/samv7/sam_tc_lowerhalf.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+ * arch/arm/src/samv7/sam_tc_lowerhalf.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_SAMV7_SAM_TC_LOWERHALF_H
+#define __ARCH_ARM_SRC_SAMV7_SAM_TC_LOWERHALF_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sam_timer_initialize
+ *
+ * Description:
+ * Bind the configuration timer to a timer lower half instance and
+ * register the timer drivers at 'devpath'
+ *
+ * Input Parameters:
+ * devpath The full path to the timer device. This should be of the
+ * form /dev/timer0.
+ * chan Timer counter channel to be used. See the TC_CHAN*
+ * definitions in arch/arm/src/samv7/sam_tc.h.
+ *
+ * Returned Value:
+ * Zero (OK) is returned on success; A negated errno value is returned
+ * to indicate the nature of any failure.
+ *
+ ****************************************************************************/
+
+int sam_timer_initialize(FAR const char *devpath, int chan);
+
+#endif /* __ARCH_ARM_SRC_SAMV7_SAM_TC_LOWERHALF_H */
diff --git a/boards/arm/samv7/same70-qmtech/src/sam_boot.c b/boards/arm/samv7/same70-qmtech/src/sam_boot.c
index 40104bf..917b448 100644
--- a/boards/arm/samv7/same70-qmtech/src/sam_boot.c
+++ b/boards/arm/samv7/same70-qmtech/src/sam_boot.c
@@ -55,7 +55,7 @@
void sam_boardinitialize(void)
{
-#ifdef CONFIG_SCHED_TICKLESS
+#if defined(CONFIG_SCHED_TICKLESS) || defined(CONFIG_TIMER)
uint32_t frequency;
uint32_t actual;
@@ -72,6 +72,16 @@ void sam_boardinitialize(void)
*/
frequency = USEC_PER_SEC / CONFIG_USEC_PER_TICK;
+
+#if defined(CONFIG_TIMER)
+ /* Timer driver needs at least microseconds resolution */
+
+ if (frequency < USEC_PER_SEC)
+ {
+ frequency = USEC_PER_SEC;
+ }
+#endif
+
DEBUGASSERT(frequency >= (BOARD_MAINOSC_FREQUENCY / 256));
actual = sam_pck_configure(PCK6, PCKSRC_MAINCK, frequency);
diff --git a/boards/arm/samv7/same70-xplained/src/sam_boot.c b/boards/arm/samv7/same70-xplained/src/sam_boot.c
index 9d78285..13338a4 100644
--- a/boards/arm/samv7/same70-xplained/src/sam_boot.c
+++ b/boards/arm/samv7/same70-xplained/src/sam_boot.c
@@ -55,7 +55,7 @@
void sam_boardinitialize(void)
{
-#ifdef CONFIG_SCHED_TICKLESS
+#if defined(CONFIG_SCHED_TICKLESS) || defined(CONFIG_TIMER)
uint32_t frequency;
uint32_t actual;
@@ -72,6 +72,16 @@ void sam_boardinitialize(void)
*/
frequency = USEC_PER_SEC / CONFIG_USEC_PER_TICK;
+
+#if defined(CONFIG_TIMER)
+ /* Timer driver needs at least microseconds resolution */
+
+ if (frequency < USEC_PER_SEC)
+ {
+ frequency = USEC_PER_SEC;
+ }
+#endif
+
DEBUGASSERT(frequency >= (BOARD_MAINOSC_FREQUENCY / 256));
actual = sam_pck_configure(PCK6, PCKSRC_MAINCK, frequency);
diff --git a/boards/arm/samv7/samv71-xult/src/sam_boot.c b/boards/arm/samv7/samv71-xult/src/sam_boot.c
index d835189..9ceb7d0 100644
--- a/boards/arm/samv7/samv71-xult/src/sam_boot.c
+++ b/boards/arm/samv7/samv71-xult/src/sam_boot.c
@@ -55,7 +55,7 @@
void sam_boardinitialize(void)
{
-#ifdef CONFIG_SCHED_TICKLESS
+#if defined(CONFIG_SCHED_TICKLESS) || defined(CONFIG_TIMER)
uint32_t frequency;
uint32_t actual;
@@ -72,6 +72,16 @@ void sam_boardinitialize(void)
*/
frequency = USEC_PER_SEC / CONFIG_USEC_PER_TICK;
+
+#if defined(CONFIG_TIMER)
+ /* Timer driver needs at least microseconds resolution */
+
+ if (frequency < USEC_PER_SEC)
+ {
+ frequency = USEC_PER_SEC;
+ }
+#endif
+
DEBUGASSERT(frequency >= (BOARD_MAINOSC_FREQUENCY / 256));
actual = sam_pck_configure(PCK6, PCKSRC_MAINCK, frequency);