diff --git a/arch/arm/src/sama5/sam_pck.c b/arch/arm/src/sama5/sam_pck.c index f089c4381d4..11202c5d565 100644 --- a/arch/arm/src/sama5/sam_pck.c +++ b/arch/arm/src/sama5/sam_pck.c @@ -163,13 +163,13 @@ uint32_t sam_pck_configure(enum pckid_e pckid, enum pckid_clksrc_e clksrc, { pres = 1; } - else if (pres > (PMC_PCK_PRES_MASK + 1)) + else if (pres > 256) { - pres = PMC_PCK_PRES_MASK + 1; + pres = 256; } regval |= PMC_PCK_PRES(pres - 1); - actual = frequency / pres; + actual = clkin / pres; #else /* The the larger smallest divisor that does not exceed the requested @@ -226,29 +226,29 @@ uint32_t sam_pck_configure(enum pckid_e pckid, enum pckid_clksrc_e clksrc, switch (pckid) { -#ifdef PIO_PMC_PCK0 case PCK0: putreg32(PMC_PCK0, SAM_PMC_SCDR); +#ifdef PIO_PMC_PCK0 (void)sam_configpio(PIO_PMC_PCK0); +#endif putreg32(regval, SAM_PMC_PCK0); break; -#endif -#ifdef PIO_PMC_PCK1 case PCK1: putreg32(PMC_PCK1, SAM_PMC_SCDR); +#ifdef PIO_PMC_PCK1 (void)sam_configpio(PIO_PMC_PCK1); +#endif putreg32(regval, SAM_PMC_PCK1); break; -#endif -#ifdef PIO_PMC_PCK2 case PCK2: putreg32(PMC_PCK2, SAM_PMC_SCDR); +#ifdef PIO_PMC_PCK2 (void)sam_configpio(PIO_PMC_PCK2); +#endif putreg32(regval, SAM_PMC_PCK2); break; -#endif default: return -EINVAL; diff --git a/arch/arm/src/samv7/chip/sam_pmc.h b/arch/arm/src/samv7/chip/sam_pmc.h index ce3af485f76..ac4f570cbe8 100644 --- a/arch/arm/src/samv7/chip/sam_pmc.h +++ b/arch/arm/src/samv7/chip/sam_pmc.h @@ -303,7 +303,7 @@ # define PMC_PCK_CSS_MCK (4 << PMC_PCK_CSS_SHIFT) /* Master Clock */ #define PMC_PCK_PRES_SHIFT (4) /* Bits 4-11: Programmable Clock Prescaler */ #define PMC_PCK_PRES_MASK (0xff << PMC_PCK_PRES_SHIFT) -# define PMC_PCK_PRES(n) ((uint32_t)((n)-1) << PMC_PCK_PRES_SHIFT) /* n=1..256 */ +# define PMC_PCK_PRES(n) ((uint32_t)(n) << PMC_PCK_PRES_SHIFT) /* n=0..255 */ /* PMC Interrupt Enable Register, PMC Interrupt Disable Register, PMC Status Register, * and PMC Interrupt Mask Register common bit-field definitions diff --git a/arch/arm/src/samv7/sam_freerun.c b/arch/arm/src/samv7/sam_freerun.c index e30f94555c9..5edc15c5a48 100644 --- a/arch/arm/src/samv7/sam_freerun.c +++ b/arch/arm/src/samv7/sam_freerun.c @@ -120,7 +120,7 @@ int sam_freerun_initialize(struct sam_freerun_s *freerun, int chan, uint16_t resolution) { uint32_t frequency; - uint32_t divisor; + uint32_t actual; uint32_t cmr; int ret; @@ -133,20 +133,20 @@ int sam_freerun_initialize(struct sam_freerun_s *freerun, int chan, /* The pre-calculate values to use when we start the timer */ - ret = sam_tc_divisor(frequency, &divisor, &cmr); + ret = sam_tc_clockselect(frequency, &cmr, &actual); if (ret < 0) { - tcdbg("ERROR: sam_tc_divisor failed: %d\n", ret); + tcdbg("ERROR: sam_tc_clockselect failed: %d\n", ret); return ret; } - tcvdbg("frequency=%lu, divisor=%u, cmr=%08lx\n", - (unsigned long)frequency, (unsigned long)divisor, + tcvdbg("frequency=%lu, actual=%lu, cmr=%08lx\n", + (unsigned long)frequency, (unsigned long)actual, (unsigned long)cmr); /* Allocate the timer/counter and select its mode of operation * - * TC_CMR_TCCLKS - Returned by sam_tc_divisor + * 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=0 - Don't stop the clock on an RC compare event diff --git a/arch/arm/src/samv7/sam_mcan.c b/arch/arm/src/samv7/sam_mcan.c index 0402a0fb49f..03f962385ac 100644 --- a/arch/arm/src/samv7/sam_mcan.c +++ b/arch/arm/src/samv7/sam_mcan.c @@ -3720,7 +3720,7 @@ FAR struct can_dev_s *sam_mcan_initialize(int port) * use PCK5 to derive bit rate. */ - regval = PMC_PCK_PRES(CONFIG_SAMV7_MCAN_CLKSRC_PRESCALER) | SAMV7_MCAN_CLKSRC; + regval = PMC_PCK_PRES(CONFIG_SAMV7_MCAN_CLKSRC_PRESCALER - 1) | SAMV7_MCAN_CLKSRC; putreg32(regval, SAM_PMC_PCK5); /* Enable PCK5 */ diff --git a/arch/arm/src/samv7/sam_oneshot.c b/arch/arm/src/samv7/sam_oneshot.c index 679c1344685..8d2ff8789b8 100644 --- a/arch/arm/src/samv7/sam_oneshot.c +++ b/arch/arm/src/samv7/sam_oneshot.c @@ -160,7 +160,7 @@ int sam_oneshot_initialize(struct sam_oneshot_s *oneshot, int chan, uint16_t resolution) { uint32_t frequency; - uint32_t divisor; + uint32_t actual; uint32_t cmr; int ret; @@ -173,20 +173,20 @@ int sam_oneshot_initialize(struct sam_oneshot_s *oneshot, int chan, /* The pre-calculate values to use when we start the timer */ - ret = sam_tc_divisor(frequency, &divisor, &cmr); + ret = sam_tc_clockselect(frequency, &cmr, &actual); if (ret < 0) { - tcdbg("ERROR: sam_tc_divisor failed: %d\n", ret); + tcdbg("ERROR: sam_tc_clockselect failed: %d\n", ret); return ret; } - tcvdbg("frequency=%lu, divisor=%lu, cmr=%08lx\n", - (unsigned long)frequency, (unsigned long)divisor, + tcvdbg("frequency=%lu, actual=%lu, cmr=%08lx\n", + (unsigned long)frequency, (unsigned long)actual, (unsigned long)cmr); /* Allocate the timer/counter and select its mode of operation * - * TC_CMR_TCCLKS - Returned by sam_tc_divisor + * 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 diff --git a/arch/arm/src/samv7/sam_pck.c b/arch/arm/src/samv7/sam_pck.c index c72ee139682..c4dc27d0295 100644 --- a/arch/arm/src/samv7/sam_pck.c +++ b/arch/arm/src/samv7/sam_pck.c @@ -139,13 +139,13 @@ uint32_t sam_pck_configure(enum pckid_e pckid, enum pckid_clksrc_e clksrc, { pres = 1; } - else if (pres > (PMC_PCK_PRES_MASK + 1)) + else if (pres > 256) { - pres = PMC_PCK_PRES_MASK + 1; + pres = 256; } regval |= PMC_PCK_PRES(pres - 1); - actual = frequency / pres; + actual = clkin / pres; /* Disable the programmable clock, configure the PCK output pin, then set * the selected configuration. @@ -153,61 +153,61 @@ uint32_t sam_pck_configure(enum pckid_e pckid, enum pckid_clksrc_e clksrc, switch (pckid) { -#ifdef GPIO_PMC_PCK0 case PCK0: putreg32(PMC_PCK0, SAM_PMC_SCDR); +#ifdef GPIO_PMC_PCK0 (void)cam_configgpio(GPIO_PMC_PCK0); +#endif putreg32(regval, SAM_PMC_PCK0); break; -#endif -#ifdef GPIO_PMC_PCK1 case PCK1: putreg32(PMC_PCK1, SAM_PMC_SCDR); +#ifdef GPIO_PMC_PCK1 (void)cam_configgpio(GPIO_PMC_PCK1); +#endif putreg32(regval, SAM_PMC_PCK1); break; -#endif -#ifdef GPIO_PMC_PCK2 case PCK2: putreg32(PMC_PCK2, SAM_PMC_SCDR); +#ifdef GPIO_PMC_PCK2 (void)cam_configgpio(GPIO_PMC_PCK2); +#endif putreg32(regval, SAM_PMC_PCK2); break; -#endif -#ifdef GPIO_PMC_PCK3 case PCK3: putreg32(PMC_PCK3, SAM_PMC_SCDR); +#ifdef GPIO_PMC_PCK3 (void)cam_configgpio(GPIO_PMC_PCK3); +#endif putreg32(regval, SAM_PMC_PCK3); break; -#endif -#ifdef GPIO_PMC_PCK4 case PCK4: putreg32(PMC_PCK4, SAM_PMC_SCDR); +#ifdef GPIO_PMC_PCK4 (void)cam_configgpio(GPIO_PMC_PCK4); +#endif putreg32(regval, SAM_PMC_PCK4); break; -#endif -#ifdef GPIO_PMC_PCK5 case PCK5: putreg32(PMC_PCK5, SAM_PMC_SCDR); +#ifdef GPIO_PMC_PCK5 (void)cam_configgpio(GPIO_PMC_PCK5); +#endif putreg32(regval, SAM_PMC_PCK5); break; -#endif -#ifdef GPIO_PMC_PCK6 case PCK6: putreg32(PMC_PCK6, SAM_PMC_SCDR); +#ifdef GPIO_PMC_PCK6 (void)cam_configgpio(GPIO_PMC_PCK6); +#endif putreg32(regval, SAM_PMC_PCK6); break; -#endif default: return -EINVAL; diff --git a/arch/arm/src/samv7/sam_tc.c b/arch/arm/src/samv7/sam_tc.c index 72ef965144d..b1e12759ddd 100644 --- a/arch/arm/src/samv7/sam_tc.c +++ b/arch/arm/src/samv7/sam_tc.c @@ -66,6 +66,7 @@ #include "chip/sam_pinmap.h" #include "chip/sam_pmc.h" #include "sam_gpio.h" +#include "sam_pck.h" #include "sam_tc.h" #if defined(CONFIG_SAMV7_TC0) || defined(CONFIG_SAMV7_TC1) || \ @@ -139,6 +140,14 @@ struct sam_tc_s #endif }; +/* Type of the MCK divider lookup table */ + +struct mck_divsrc_s +{ + uint8_t log2; /* Log2 of the divider */ + uint8_t tcclks; /* CMR TCCLCKS setting */ +}; + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -197,8 +206,10 @@ static int sam_tc11_interrupt(int irq, void *context); /* Initialization ***********************************************************/ -static int sam_tc_freqdiv_lookup(uint32_t ftcin, int ndx); -static uint32_t sam_tc_divfreq_lookup(uint32_t ftcin, int ndx); +static uint32_t sam_tc_mckfreq_lookup(uint32_t ftcin, int ndx); +static inline uint32_t sam_tc_tcclks_lookup(int ndx); +static int sam_tc_mcksrc(uint32_t frequency, uint32_t *tcclks, + uint32_t *actual); static inline struct sam_chan_s *sam_tc_initialize(int channel); /**************************************************************************** @@ -546,17 +557,18 @@ static struct sam_tc_s g_tc901; /* TC frequency data. This table provides the frequency for each selection of TCCLK */ -#define TC_NDIVIDERS 4 -#define TC_NDIVOPTIONS 5 +#define TC_NDIVIDERS 3 +#define TC_NDIVOPTIONS 4 /* This is the list of divider values: divider = (1 << value) */ -static const uint8_t g_log2divider[TC_NDIVIDERS] = +static struct mck_divsrc_s g_log2divider[TC_NDIVOPTIONS] = { - 1, /* TIMER_CLOCK1 -> PCK6 REVISIT! Was MCK/2 */ - 3, /* TIMER_CLOCK2 -> MCK/8 */ - 5, /* TIMER_CLOCK3 -> MCK/32 */ - 7 /* TIMER_CLOCK4 -> MCK/128 */ + /* TIMER_CLOCK1(0) -> PCK6 */ + {3, 1}, /* TIMER_CLOCK2(1) -> MCK/8 */ + {5, 2}, /* TIMER_CLOCK3(2) -> MCK/32 */ + {7, 3}, /* TIMER_CLOCK4(3) -> MCK/128 */ + {0, 4}, /* TIMER_CLOCK5(4) -> SLCK (No MCK divider) */ }; /* TC register lookup used by sam_tc_setregister */ @@ -948,46 +960,12 @@ static int sam_tc11_interrupt(int irq, void *context) ****************************************************************************/ /**************************************************************************** - * 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) -{ - /* The final option is to use the SLOW clock */ - - if (ndx >= TC_NDIVIDERS) - { - /* Not really a divider. In this case, the board is actually driven - * by the 32.768KHz slow clock. This returns a value that looks like - * correct divider if MCK were the input. - */ - - return ftcin / BOARD_SLOWCLK_FREQUENCY; - } - else - { - return 1 << g_log2divider[ndx]; - } -} - -/**************************************************************************** - * Name: sam_tc_divfreq_lookup + * Name: sam_tc_mckfreq_lookup * * Description: * Given the TC input frequency (Ftcin) and a divider index, return the - * value of the divided frequency + * value of the divided frequency. The slow clock source is treated as + * though it were a divided down MCK frequency. * * Input Parameters: * ftcin - TC input frequency @@ -998,7 +976,7 @@ static int sam_tc_freqdiv_lookup(uint32_t ftcin, int ndx) * ****************************************************************************/ -static uint32_t sam_tc_divfreq_lookup(uint32_t ftcin, int ndx) +static uint32_t sam_tc_mckfreq_lookup(uint32_t ftcin, int ndx) { /* The final option is to use the SLOW clock */ @@ -1008,10 +986,113 @@ static uint32_t sam_tc_divfreq_lookup(uint32_t ftcin, int ndx) } else { - return ftcin >> g_log2divider[ndx]; + return ftcin >> g_log2divider[ndx].log2; } } +/**************************************************************************** + * Name: sam_tc_tcclks_lookup + * + * Description: + * Given the TC input frequency (Ftcin) and a divider index, return the + * value of the divided frequency. The slow clock source is treated as + * though it were a divided down MCK frequency. + * + * Input Parameters: + * ftcin - TC input frequency + * ndx - Divider index + * + * Returned Value: + * The divided frequency value + * + ****************************************************************************/ + +static inline uint32_t sam_tc_tcclks_lookup(int ndx) +{ + unsigned int index = g_log2divider[ndx].tcclks; + return TC_CMR_TCCLKS(index); +} + +/**************************************************************************** + * Name: sam_tc_mcksrc + * + * 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 / div) + * + * 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. + * tcclks TCCLKS field value for divisor. + * actual The actual freqency of the MCK + * + * Returned Value: + * Zero (OK) if a proper divisor has been found, otherwise a negated errno + * value indicating the nature of the failure. + * + ****************************************************************************/ + +static int sam_tc_mcksrc(uint32_t frequency, uint32_t *tcclks, + uint32_t *actual) +{ + uint32_t fselect; + uint32_t fnext; + int ndx = 0; + + tcvdbg("frequency=%d\n", frequency); + + /* Satisfy lower bound. That is, the value of the divider such that: + * + * frequency >= (tc_input_frequency * 65536) / divider. + */ + + for (; ndx < TC_NDIVIDERS; ndx++) + { + fselect = sam_tc_mckfreq_lookup(BOARD_MCK_FREQUENCY, ndx); + if (frequency >= (fselect >> 16)) + { + break; + } + } + + if (ndx >= TC_NDIVIDERS) + { + /* If no divisor can be found, return -ERANGE */ + + tcdbg("Lower bound search failed\n"); + return -ERANGE; + } + + /* 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; ndx++) + { + fnext = sam_tc_mckfreq_lookup(BOARD_MCK_FREQUENCY, ndx + 1); + if (frequency > fnext) + { + break; + } + + fselect = fnext; + } + + /* Return the actual frequency and the TCCLKS selection */ + + *actual = fselect; + *tcclks = sam_tc_tcclks_lookup(ndx); + return OK; +} + /**************************************************************************** * Name: sam_tc_initialize * @@ -1538,11 +1619,24 @@ uint32_t sam_tc_divfreq(TC_HANDLE handle) /* And use the TCCLKS index to calculate the timer counter frequency */ - return sam_tc_divfreq_lookup(BOARD_MCK_FREQUENCY, tcclks); + if (tcclks == 0) + { + /* The tcclks value of 0 corresponds to PCK6 */ + + return sam_pck_frequency(PCK6); + } + else + { + /* Values of tcclks in the range {1,5} correspond to the divided + * down MCK or to the slow clock. + */ + + return sam_tc_mckfreq_lookup(BOARD_MCK_FREQUENCY, tcclks - 1); + } } /**************************************************************************** - * Name: sam_tc_divisor + * Name: sam_tc_clockselect * * Description: * Finds the best MCK divisor given the timer frequency and MCK. The @@ -1557,8 +1651,8 @@ uint32_t sam_tc_divfreq(TC_HANDLE handle) * * Input Parameters: * frequency Desired timer frequency. - * div Divisor value. * tcclks TCCLKS field value for divisor. + * actual The actual freqency of the MCK * * Returned Value: * Zero (OK) if a proper divisor has been found, otherwise a negated errno @@ -1566,67 +1660,99 @@ uint32_t sam_tc_divfreq(TC_HANDLE handle) * ****************************************************************************/ -int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks) +int sam_tc_clockselect(uint32_t frequency, uint32_t *tcclks, + uint32_t *actual) { - int ndx = 0; + uint32_t mck_actual; + uint32_t mck_tcclks; + uint32_t mck_error; + int ret; - tcvdbg("frequency=%d\n", frequency); + /* Try to satisfy the requested frequency with the MCK or slow clock */ - /* On other chips, TCCLCKS==0 corresponded to MCK/2. But for the SAMV7, - * this is PCK6. That will need to be handled differently. - */ - -#warning REVISIT: PCK6 clock source not yet supported - ndx++; - - /* Satisfy lower bound. That is, the value of the divider such that: - * - * frequency >= (tc_input_frequency * 65536) / divider. - */ - - while (frequency < (sam_tc_divfreq_lookup(BOARD_MCK_FREQUENCY, ndx) >> 16)) + ret = sam_tc_mcksrc(frequency, &mck_tcclks, &mck_actual); + if (ret < 0) { - if (++ndx > TC_NDIVOPTIONS) - { - /* If no divisor can be found, return -ERANGE */ + mck_error = UINT32_MAX; + } + else + { + /* Get the absolute value of the frequency error */ - tcdbg("Lower bound search failed\n"); - return -ERANGE; + if (mck_actual > frequency) + { + mck_error = mck_actual - frequency; + } + else + { + mck_error = frequency - mck_actual; } } - /* Try to maximize DIV while still satisfying upper bound. That the - * value of the divider such that: - * - * frequency < tc_input_frequency / divider. - */ + /* See if we do better with PCK6 */ - for (; ndx < (TC_NDIVOPTIONS-1); ndx++) + if (sam_pck_isenabled(PCK6)) { - if (frequency > sam_tc_divfreq_lookup(BOARD_MCK_FREQUENCY, ndx + 1)) + uint32_t pck6_actual; + uint32_t pck6_error; + + /* Get the absolute value of the frequency error */ + + pck6_actual = sam_pck_frequency(PCK6); + if (pck6_actual > frequency) { - break; + pck6_error = pck6_actual - frequency; + } + else + { + pck6_error = frequency - pck6_actual; + } + + /* Return the PCK6 selection if the error is smaller */ + + if (pck6_error < mck_error) + { + /* Return the PCK selection */ + + if (actual) + { + tcvdbg("return actual=%lu\n", (unsigned long)fselect); + *actual = pck6_actual; + } + + /* Return the TCCLKS selection */ + + if (tcclks) + { + tcvdbg("return tcclks=%08lx\n", (unsigned long)TC_CMR_TCCLKS_PCK6); + *tcclks = TC_CMR_TCCLKS_PCK6; + } + + /* Return success */ + + return OK; } } - /* Return the divider value */ + /* Return the MCK/slow clock selection */ - if (div) + if (actual) { - uint32_t value = sam_tc_freqdiv_lookup(BOARD_MCK_FREQUENCY, ndx); - tcvdbg("return div=%lu\n", (unsigned long)value); - *div = value; + tcvdbg("return actual=%lu\n", (unsigned long)mck_actual); + *actual = mck_actual; } /* Return the TCCLKS selection */ if (tcclks) { - tcvdbg("return tcclks=%08lx\n", (unsigned long)TC_CMR_TCCLKS(ndx)); - *tcclks = TC_CMR_TCCLKS(ndx); + tcvdbg("return tcclks=%08lx\n", (unsigned long)mck_tcclks); + *tcclks = mck_tcclks; } - return OK; + /* Return success */ + + return ret; } #endif /* CONFIG_SAMV7_TC0 || CONFIG_SAMV7_TC1 || CONFIG_SAMV7_TC2 || CONFIG_SAMV7_TC3 */ diff --git a/arch/arm/src/samv7/sam_tc.h b/arch/arm/src/samv7/sam_tc.h index 421b0835cad..5dda45ece99 100644 --- a/arch/arm/src/samv7/sam_tc.h +++ b/arch/arm/src/samv7/sam_tc.h @@ -331,7 +331,7 @@ uint32_t sam_tc_infreq(void); uint32_t sam_tc_divfreq(TC_HANDLE handle); /**************************************************************************** - * Name: sam_tc_divisor + * Name: sam_tc_clockselect * * Description: * Finds the best MCK divisor given the timer frequency and MCK. The @@ -346,8 +346,8 @@ uint32_t sam_tc_divfreq(TC_HANDLE handle); * * Input Parameters: * frequency Desired timer frequency. - * div Divisor value. * tcclks TCCLKS field value for divisor. + * actual The actual freqency of the MCK * * Returned Value: * Zero (OK) if a proper divisor has been found, otherwise a negated errno @@ -355,7 +355,8 @@ uint32_t sam_tc_divfreq(TC_HANDLE handle); * ****************************************************************************/ -int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks); +int sam_tc_clockselect(uint32_t frequency, uint32_t *tcclks, + uint32_t *actual); #undef EXTERN #ifdef __cplusplus