--- /dev/null
+/* ----------------------------------------------------------------------------\r
+ * SAM Software Package License\r
+ * ----------------------------------------------------------------------------\r
+ * Copyright (c) 2015, Atmel Corporation\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ * - Redistributions of source code must retain the above copyright notice,\r
+ * this list of conditions and the disclaimer below.\r
+ *\r
+ * Atmel's name may not be used to endorse or promote products derived from\r
+ * this software without specific prior written permission.\r
+ *\r
+ * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR\r
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE\r
+ * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,\r
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\r
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * ----------------------------------------------------------------------------\r
+ */\r
+\r
+/** \addtogroup tc_module\r
+ * \section Purpose\r
+ * The TC driver provides the Interface to configure the Timer Counter (TC).\r
+ *\r
+ * \section Usage\r
+ * <ul>\r
+ * <li> Optionally, use tc_find_mck_divisor() to let the program find the best\r
+ * TCCLKS field value automatically.</li>\r
+ * <li> Configure a Timer Counter in the desired mode using tc_configure().</li>\r
+ * <li> Start or stop the timer clock using tc_start() and tc_stop().</li>\r
+ * </li>\r
+ * </ul>\r
+ * For more accurate information, please look at the TC section of the Datasheet.\r
+ *\r
+ * Related files :\n\r
+ * \ref tc.c\n\r
+ * \ref tc.h.\n\r
+*/\r
+\r
+/**\r
+* \file\r
+*\r
+* \section Purpose\r
+*\r
+* Interface for configuring and using Timer Counter (TC) peripherals.\r
+*\r
+* \section Usage\r
+* -# Optionally, use tc_find_mck_divisor() to let the program find the best\r
+* TCCLKS field value automatically.\r
+* -# Configure a Timer Counter in the desired mode using tc_configure().\r
+* -# Start or stop the timer clock using tc_start() and tc_stop().\r
+*/\r
+\r
+/**\r
+ * \file\r
+ *\r
+ * Implementation of Timer Counter (TC).\r
+ *\r
+ */\r
+\r
+/*------------------------------------------------------------------------------\r
+ * Headers\r
+ *------------------------------------------------------------------------------*/\r
+\r
+#include "chip.h"\r
+#include "peripherals/tc.h"\r
+#include "peripherals/pmc.h"\r
+\r
+#include <assert.h>\r
+\r
+/*------------------------------------------------------------------------------\r
+ * Global functions\r
+ *------------------------------------------------------------------------------*/\r
+\r
+/**\r
+ * \brief Configures a Timer Counter Channel\r
+ *\r
+ * Configures a Timer Counter to operate in the given mode. Timer is stopped\r
+ * after configuration and must be restarted with tc_start(). All the\r
+ * interrupts of the timer are also disabled.\r
+ *\r
+ * \param pTc Pointer to a Tc instance.\r
+ * \param channel Channel number.\r
+ * \param mode Operating mode (TC_CMR value).\r
+ */\r
+void tc_configure(Tc * pTc, uint32_t channel, uint32_t mode)\r
+{\r
+ volatile TcChannel *pTcCh;\r
+\r
+ assert(channel <\r
+ (sizeof (pTc->TC_CHANNEL) / sizeof (pTc->TC_CHANNEL[0])));\r
+ pTcCh = pTc->TC_CHANNEL + channel;\r
+\r
+ /* Disable TC clock */\r
+ pTcCh->TC_CCR = TC_CCR_CLKDIS;\r
+\r
+ /* Disable interrupts */\r
+ pTcCh->TC_IDR = 0xFFFFFFFF;\r
+\r
+ /* Clear status register */\r
+ pTcCh->TC_SR;\r
+\r
+ /* Set mode */\r
+ pTcCh->TC_CMR = mode;\r
+}\r
+\r
+/**\r
+ * \brief Reset and Start the TC Channel\r
+ *\r
+ * Enables the timer clock and performs a software reset to start the counting.\r
+ *\r
+ * \param pTc Pointer to a Tc instance.\r
+ * \param channel Channel number.\r
+ */\r
+void tc_start(Tc * pTc, uint32_t channel)\r
+{\r
+ volatile TcChannel *pTcCh;\r
+\r
+ assert(channel <\r
+ (sizeof (pTc->TC_CHANNEL) / sizeof (pTc->TC_CHANNEL[0])));\r
+\r
+ pTcCh = pTc->TC_CHANNEL + channel;\r
+ pTcCh->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;\r
+ pTcCh->TC_IER = TC_IER_COVFS;\r
+}\r
+\r
+/**\r
+ * \brief Stop TC Channel\r
+ *\r
+ * Disables the timer clock, stopping the counting.\r
+ *\r
+ * \param pTc Pointer to a Tc instance.\r
+ * \param channel Channel number.\r
+ */\r
+void tc_stop(Tc * pTc, uint32_t channel)\r
+{\r
+ volatile TcChannel *pTcCh;\r
+\r
+ assert(channel <\r
+ (sizeof (pTc->TC_CHANNEL) / sizeof (pTc->TC_CHANNEL[0])));\r
+\r
+ pTcCh = pTc->TC_CHANNEL + channel;\r
+ pTcCh->TC_CCR = TC_CCR_CLKDIS;\r
+ pTcCh->TC_IDR = TC_IER_COVFS;\r
+}\r
+\r
+/**\r
+ * \brief Enables TC channel interrupts\r
+ *\r
+ * \param tc Pointer to Tc instance\r
+ * \param channel Channel number\r
+ * \param mask mask of interrupts to enable\r
+ */\r
+void tc_enable_it(Tc* tc, uint32_t channel, uint32_t mask)\r
+{\r
+ assert(channel < (sizeof (tc->TC_CHANNEL) / sizeof (tc->TC_CHANNEL[0])));\r
+ tc->TC_CHANNEL[channel].TC_IER = mask;\r
+}\r
+\r
+/**\r
+ * \brief Find best MCK divisor\r
+ *\r
+ * Finds the best MCK divisor given the timer frequency and MCK. The result\r
+ * is guaranteed to satisfy the following equation:\r
+ * \code\r
+ * (MCK / (DIV * 65536)) <= freq <= (MCK / DIV)\r
+ * \endcode\r
+ * with DIV being the highest possible value.\r
+ *\r
+ * \param freq Desired timer freq.\r
+ * \param div Divisor value.\r
+ * \param tc_clks TCCLKS field value for divisor.\r
+ *\r
+ * \return 1 if a proper divisor has been found, otherwise 0.\r
+ */\r
+uint32_t tc_find_mck_divisor (uint32_t freq, uint32_t * div,\r
+ uint32_t * tc_clks)\r
+{\r
+ const uint32_t periph_clock = pmc_get_peripheral_clock(ID_TC0);\r
+ const uint32_t available_freqs[5] = {periph_clock >> 1, periph_clock >> 3, periph_clock >> 5, periph_clock >> 7, 32768};\r
+\r
+ int i = 0;\r
+ for (i = 0; i < 5; ++i)\r
+ {\r
+ uint32_t tmp = freq << 1;\r
+ if (tmp > available_freqs[i])\r
+ break;\r
+ }\r
+\r
+ i = (i == 5 ? i-1 : i);\r
+\r
+ /* Store results */\r
+ if (div) {\r
+ *div = periph_clock / available_freqs[i];\r
+ }\r
+ if (tc_clks) {\r
+ *tc_clks = i;\r
+ }\r
+\r
+ return 1;\r
+}\r
+\r
+uint32_t tc_get_status(Tc* tc, uint32_t channel_num)\r
+{\r
+ return tc->TC_CHANNEL[channel_num].TC_SR;\r
+}\r
+\r
+\r
+void tc_trigger_on_freq(Tc* tc, uint32_t channel_num, uint32_t freq)\r
+{\r
+ uint32_t div = 0;\r
+ uint32_t tcclks = 0;\r
+ uint32_t tc_id = get_tc_id_from_addr(tc);\r
+ TcChannel* channel = &tc->TC_CHANNEL[channel_num];\r
+\r
+ tc_find_mck_divisor(freq, &div, &tcclks);\r
+ tc_configure(tc, channel_num, tcclks | TC_CMR_CPCTRG);\r
+ channel->TC_RC = (pmc_get_peripheral_clock(tc_id) / div) / freq;\r
+}\r