diff --git a/arch/arm/src/sama5/Kconfig b/arch/arm/src/sama5/Kconfig index 8da6191b2c..d1b947cf7c 100644 --- a/arch/arm/src/sama5/Kconfig +++ b/arch/arm/src/sama5/Kconfig @@ -2070,6 +2070,7 @@ config SAMA5_ADC_ADTRG config SAMA5_ADC_TIOATRIG bool "TC0 ouput A trigger" + depends on SAMA5_TC0 ---help--- A-to-D Conversion is initiated the A output from one of Timer/Counter 0 channels. @@ -2100,12 +2101,20 @@ endchoice # ADTRG edge if SAMA5_ADC_TIOATRIG +config SAMA5_ADC_TIOAFREQ + int "ADC sampling frequency" + default 1 + ---help--- + This setting provides the rate at which the timer will driver ADC + sampling. + choice prompt "TC0 channel" default SAMA5_ADC_TIOA0TRIG config SAMA5_ADC_TIOA0TRIG bool "TC0 Channel 0 Output A" + select SAMA5_TC0_TIOA0 ---help--- A-to-D conversion is triggered by the TC0 channel 0 output A signal. This output must be enabled independently in the Timer/Counter @@ -2113,6 +2122,7 @@ config SAMA5_ADC_TIOA0TRIG config SAMA5_ADC_TIOA1TRIG bool "TC0 Channel 1 Output A" + select SAMA5_TC0_TIOA1 ---help--- A-to-D conversion is triggered by the TC0 channel 1 output A signal. This output must be enabled independently in the Timer/Counter @@ -2120,6 +2130,7 @@ config SAMA5_ADC_TIOA1TRIG config SAMA5_ADC_TIOA2TRIG bool "TC0 Channel 2 Output A" + select SAMA5_TC0_TIOA2 ---help--- A-to-D conversion is triggered by the TC0 channel 2 output A signal. This output must be enabled independently in the Timer/Counter @@ -2161,6 +2172,92 @@ config SAMA5_ADC_REGDEBUG endmenu # ADC Configuration +if SAMA5_TC0 || SAMA5_TC1 +menu "Timer/counter Configuration" + +if SAMA5_TC0 + +config SAMA5_TC0_CLK0 + bool "Enable TC0 channel 0 clock input pin" + default n + +config SAMA5_TC0_TIOA0 + bool "Enable TC0 channel 0 ouput A" + default n + +config SAMA5_TC0_TIOB0 + bool "Enable TC0 channel 0 ouput B" + default n + +config SAMA5_TC0_CLK1 + bool "Enable TC0 channel 1 clock input pin" + default n + +config SAMA5_TC0_TIOA1 + bool "Enable TC0 channel 1 ouput A" + default n + +config SAMA5_TC0_TIOB1 + bool "Enable TC0 channel 1 ouput B" + default n + +config SAMA5_TC0_CLK2 + bool "Enable TC0 channel 2 clock input pin" + default n + +config SAMA5_TC0_TIOA2 + bool "Enable TC0 channel 2 ouput A" + default n + +config SAMA5_TC0_TIOB2 + bool "Enable TC0 channel 2 ouput B" + default n + +endif # SAMA5_TC0 + +if SAMA5_TC1 + +config SAMA5_TC1_CLK3 + bool "Enable TC1 channel 3 clock input pin" + default n + +config SAMA5_TC1_TIOA3 + bool "Enable TC1 channel 3 ouput A" + default n + +config SAMA5_TC1_TIOB3 + bool "Enable TC1 channel 3 ouput B" + default n + +config SAMA5_TC1_CLK4 + bool "Enable TC1 channel 4 clock input pin" + default n + +config SAMA5_TC1_TIOA4 + bool "Enable TC1 channel 4 ouput A" + default n + +config SAMA5_TC1_TIOB4 + bool "Enable TC1 channel 4 ouput B" + default n + +config SAMA5_TC1_CLK5 + bool "Enable TC1 channel 5 clock input pin" + default n + +config SAMA5_TC1_TIOA5 + bool "Enable TC1 channel 5 ouput A" + default n + +config SAMA5_TC1_TIOB5 + bool "Enable TC1 channel 5 ouput B" + default n + +endif # SAMA5_TC1 + +endmenu # Timer/counter Configuration +endif # SAMA5_TC0 || SAMA5_TC1 + menu "Touchscreen configuration" config SAMA5_TSD diff --git a/arch/arm/src/sama5/chip/sam_tc.h b/arch/arm/src/sama5/chip/sam_tc.h index bf8fc18176..fcbb194c7e 100644 --- a/arch/arm/src/sama5/chip/sam_tc.h +++ b/arch/arm/src/sama5/chip/sam_tc.h @@ -46,6 +46,10 @@ /************************************************************************************ * Pre-processor Definitions ************************************************************************************/ + +#define SAM_TC_NCHANNELS 3 /* Number of channels per TC peripheral */ +#define SAM_TC_MAXPERCLK 66000000 /* Maximum peripheral input clock frequency */ + /* TC Register Offsets **************************************************************/ #define SAM_TC_CHAN_OFFSET(n) ((n) << 6) /* Channel n offset */ diff --git a/arch/arm/src/sama5/sam_adc.c b/arch/arm/src/sama5/sam_adc.c index c9d47daeab..2201f71b59 100644 --- a/arch/arm/src/sama5/sam_adc.c +++ b/arch/arm/src/sama5/sam_adc.c @@ -71,6 +71,7 @@ #include "chip/sam_pmc.h" #include "sam_periphclks.h" #include "sam_dmac.h" +#include "sam_tc.h" #include "sam_tsd.h" #include "sam_adc.h" @@ -379,6 +380,9 @@ struct sam_adc_s #ifdef CONFIG_SAMA5_ADC_DMA DMA_HANDLE dma; /* Handle for DMA channel */ #endif +#ifdef CONFIG_SAMA5_ADC_TIOATRIG + TC_HANDLE tc; /* Handle for the timer channel */ +#endif /* DMA sample data buffer */ @@ -435,6 +439,10 @@ static int sam_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg); /* Initialization/Configuration */ +#ifdef CONFIG_SAMA5_ADC_TIOATRIG +static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency, + int channel, boot tioa); +#endif static void sam_adc_trigger(struct sam_adc_s *priv); static void sam_adc_autocalibrate(struct sam_adc_s *priv); static void sam_adc_offset(struct sam_adc_s *priv); @@ -878,6 +886,12 @@ static void sam_adc_reset(struct adc_dev_s *dev) dma_stop(priv->dma); + /* Stop an release any timer */ + +#ifdef CONFIG_SAMA5_ADC_TIOATRIG + sam_adc_freetimer(priv); +#endif + /* Disable all EOC interrupts */ sam_adc_putreg(priv, SAM_ADC_IDR, ADC_INT_EOCALL); @@ -1067,6 +1081,85 @@ static int sam_adc_ioctl(struct adc_dev_s *dev, int cmd, unsigned long arg) * Initialization/Configuration ****************************************************************************/ +/**************************************************************************** + * Name: sam_adc_settimer + * + * Description: + * Configure a timer to trigger the sampling periodically + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_ADC_TIOATRIG +static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency, + int channel) +{ + uint32_t div; + uint32_t tcclks; + + /* Configure TC for a 1Hz frequency and trigger on RC compare. */ + + ret = sam_tc_divisor(frequency, &div, &tcclks); + if (ret < 0) + { + adbg("ERROR: sam_tc_divisor failed: %d\n", ret); + return ret; + } + + /* Set the timer/counter waveform mode the the clock input slected by + * sam_tc_divisor() + */ + + mode = ((tcclks << TC_CMR_TCCLKS_SHIFT) | /* Use selected TCCLKS value */ + TC_CMR_WAVSEL_UPRC | /* UP mode w/ trigger on RC Compare */ + TC_CMR_WAVE | /* Wave mode */ + TC_CMR_ACPA_CLEAR | /* RA Compare Effect on TIOA: Clear */ + TC_CMR_ACPC_SET); /* RC effect on TIOA: Set + + /* Now allocate and configure the channel */ + + priv->tc = sam_tc_allocate(channel, mode); + if (!priv->tc) + { + adbg("ERROR: Failed to allocate channel %d mode %08x\n", channel, mode); + return -EINVAL; + } + + /* Set up TC_RA and TC_RC */ + + sam_tc_setregister(priv->tc, TC_REGA, div / 2); + sam_tc_setregister(priv->tc, TC_REGC, div); + + /* And start the timer */ + + sam_tc_start(priv->tc); +} +#endif + +/**************************************************************************** + * Name: sam_adc_settimer + * + * Description: + * Configure a timer to trigger the sampling periodically + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_ADC_TIOATRIG +static void sam_adc_settimer(struct sam_adc_s *priv, uint32_t frequency, + int channel) +{ + /* Is a timer allocated? */ + + if (priv->tc) + { + /* Yes.. stop it and free it */ + + sam_tc_stop(priv->tc); + sam_tc_free(priv->tc); + priv->tc = NULL; + } +} +#endif + /**************************************************************************** * Name: sam_adc_trigger * @@ -1119,6 +1212,18 @@ static void sam_adc_trigger(struct sam_adc_s *priv) sam_adc_putreg(priv, SAM_ADC_TRGR, regval); #elif defined(CONFIG_SAMA5_ADC_TIOATRIG) + /* Start the timer */ + +#if defined(CONFIG_SAMA5_ADC_TIOA0TRIG) + sam_adc_settimer(priv, CONFIG_SAMA5_ADC_TIOAFREQ, TC_CHAN0); +#elif defined(CONFIG_SAMA5_ADC_TIOA1TRIG) + sam_adc_settimer(priv, CONFIG_SAMA5_ADC_TIOAFREQ, TC_CHAN1); +#elif defined(CONFIG_SAMA5_ADC_TIOA2TRIG) + sam_adc_settimer(priv, CONFIG_SAMA5_ADC_TIOAFREQ, TC_CHAN2); +#else +# error Timer/counter for trigger not defined +#endif + /* Configure to trigger using Timer/counter 0, channel 1, 2, or 3. * NOTE: This trigger option depends on having properly configuer * timer/counter 0 to provide this output. That is done independently diff --git a/arch/arm/src/sama5/sam_can.c b/arch/arm/src/sama5/sam_can.c index 7ce3a15bb9..5bd302fb0e 100644 --- a/arch/arm/src/sama5/sam_can.c +++ b/arch/arm/src/sama5/sam_can.c @@ -116,7 +116,7 @@ # define CAN_FREQUENCY (BOARD_MCK_FREQUENCY >> 3) # define CAN_PCR_DIV PMC_PCR_DIV8 #else -# error Cannot realize ADC input frequency +# error Cannot realize CAN input frequency #endif /* Debug ********************************************************************/ diff --git a/arch/arm/src/sama5/sam_tc.c b/arch/arm/src/sama5/sam_tc.c index 4007cdcfdb..7e2e93d725 100644 --- a/arch/arm/src/sama5/sam_tc.c +++ b/arch/arm/src/sama5/sam_tc.c @@ -57,15 +57,63 @@ #include #include +#include + #include "up_arch.h" #include "sam_periphclks.h" +#include "chip/sam_pmc.h" +#include "sam_pio.h" #include "sam_tc.h" #if defined(CONFIG_SAMA5_TC0) || defined(CONFIG_SAMA5_TC1) +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Clocking */ + +#if BOARD_MCK_FREQUENCY <= SAM_TC_MAXPERCLK +# define TC_FREQUENCY BOARD_MCK_FREQUENCY +# define TC_PCR_DIV PMC_PCR_DIV1 +#elif (BOARD_MCK_FREQUENCY >> 1) <= SAM_TC_MAXPERCLK +# define TC_FREQUENCY (BOARD_MCK_FREQUENCY >> 1) +# define TC_PCR_DIV PMC_PCR_DIV2 +#elif (BOARD_MCK_FREQUENCY >> 2) <= SAM_TC_MAXPERCLK +# define TC_FREQUENCY (BOARD_MCK_FREQUENCY >> 2) +# define TC_PCR_DIV PMC_PCR_DIV4 +#elif (BOARD_MCK_FREQUENCY >> 3) <= SAM_TC_MAXPERCLK +# define TC_FREQUENCY (BOARD_MCK_FREQUENCY >> 3) +# define TC_PCR_DIV PMC_PCR_DIV8 +#else +# error Cannot realize TC input frequency +#endif + /**************************************************************************** * Private Types ****************************************************************************/ +/* This structure describes the static configuration of a TC channel */ + +struct sam_chconfig_s +{ + uintptr_t base; /* Channel register base address */ + pio_pinset_t clkset; /* CLK input PIO configuration */ + pio_pinset_t tioaset; /* Output A PIO configuration */ + pio_pinset_t tiobset; /* Output B PIO configuration */ +}; + +/* This structure describes the static configuration of a TC */ + +struct sam_tcconfig_s +{ + uintptr_t base; /* TC register base address */ + uint8_t pid; /* Peripheral ID */ + uint8_t chfirst; /* First channel number */ + + /* Channels */ + + struct sam_chconfig_s channel[3]; +}; /* This structure describes one timer counter channel */ @@ -134,14 +182,187 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel); /**************************************************************************** * Private Data ****************************************************************************/ +/* Static timer configuration */ + +#ifdef CONFIG_SAMA5_TC0 +static const struct sam_tcconfig_s g_tc012config = +{ + .base = SAM_TC012_VBASE, + .pid = SAM_PID_TC0, + .chfirst = 0, + .channel = + { + { + SAM_TC012_CHAN_BASE(0), +#ifdef CONFIG_SAMA5_TC0_CLK0 + .clkset = PIO_TC0_CLK, +#else + .clkset = 0, +#endif +#ifdef CONFIG_SAMA5_TC0_TIOA0 + .tioaset = PIO_TC0_IOA, +#else + .tioaset = 0, +#endif +#ifdef CONFIG_SAMA5_TC0_TIOB0 + .tiobset = PIO_TC0_IOB, +#else + .tiobset = 0, +#endif + }, + { + SAM_TC012_CHAN_BASE(1), +#ifdef CONFIG_SAMA5_TC0_CLK1 + .clkset = PIO_TC1_CLK, +#else + .clkset = 0, +#endif +#ifdef CONFIG_SAMA5_TC0_TIOA1 + .tioaset = PIO_TC1_IOA, +#else + .tioaset = 0, +#endif +#ifdef CONFIG_SAMA5_TC0_TIOB1 + .tiobset = PIO_TC1_IOB, +#else + .tiobset = 0, +#endif + }, + { + SAM_TC012_CHAN_BASE(2), +#ifdef CONFIG_SAMA5_TC0_CLK2 + .clkset = PIO_TC2_CLK, +#else + .clkset = 0, +#endif +#ifdef CONFIG_SAMA5_TC0_TIOA2 + .tioaset = PIO_TC2_IOA, +#else + .tioaset = 0, +#endif +#ifdef CONFIG_SAMA5_TC0_TIOB2 + .tiobset = PIO_TC2_IOB, +#else + .tiobset = 0, +#endif + }, + }, +}; +#endif + +#ifdef CONFIG_SAMA5_TC1 +static const struct sam_tcconfig_s g_tc345config = +{ + .base = SAM_TC345_VBASE, + .pid = SAM_PID_TC1, + .chfirst = 3, + .channel = + { + { + SAM_TC345_CHAN_BASE(3), +#ifdef CONFIG_SAMA5_TC1_CLK3 + .clkset = PIO_TC3_CLK, +#else + .clkset = 0, +#endif +#ifdef CONFIG_SAMA5_TC1_TIOA3 + .tioaset = PIO_TC3_IOA, +#else + .tioaset = 0, +#endif +#ifdef CONFIG_SAMA5_TC1_TIOB3 + .tiobset = PIO_TC3_IOB, +#else + .tiobset = 0, +#endif + }, + { + SAM_TC345_CHAN_BASE(4), +#ifdef CONFIG_SAMA5_TC1_CLK4 + .clkset = PIO_TC4_CLK, +#else + .clkset = 0, +#endif +#ifdef CONFIG_SAMA5_TC1_TIOA4 + .tioaset = PIO_TC4_IOA, +#else + .tioaset = 0, +#endif +#ifdef CONFIG_SAMA5_TC1_TIOB4 + .tiobset = PIO_TC4_IOB, +#else + .tiobset = 0, +#endif + }, + { + SAM_TC345_CHAN_BASE(5), +#ifdef CONFIG_SAMA5_TC1_CLK5 + .clkset = PIO_TC5_CLK, +#else + .clkset = 0, +#endif +#ifdef CONFIG_SAMA5_TC1_TIOA5 + .tioaset = PIO_TC5_IOA, +#else + .tioaset = 0, +#endif +#ifdef CONFIG_SAMA5_TC1_TIOB5 + .tiobset = PIO_TC5_IOB, +#else + .tiobset = 0, +#endif + }, + }, +}; +#endif + +/* Timer/counter state */ #ifdef CONFIG_SAMA5_TC0 static struct sam_tc_s g_tc012; #endif + #ifdef CONFIG_SAMA5_TC1 static struct sam_tc_s g_tc345; #endif +/* TC frequency data. This table provides the frequency for each selection of TCCLK */ + +#define TC_NDIVIDERS 5 + +/* This is the list of divider values */ + +static const uint16_t g_divider[TC_NDIVIDERS] = +{ + 2, /* TIMER_CLOCK1 -> div2 */ + 8, /* TIMER_CLOCK2 -> div8 */ + 32, /* TIMER_CLOCK3 -> div32 */ + 128, /* TIMER_CLOCK4 -> div128 */ + TC_FREQUENCY / 32768 /* TIMER_CLOCK5 -> slow clock (not really a divider) */ +}; + +/* This is the list of divided down frequencies */ + +static const uint32_t g_divfreq[TC_NDIVIDERS] = +{ + TC_FREQUENCY / 2, /* TIMER_CLOCK1 -> div2 */ + TC_FREQUENCY / 8, /* TIMER_CLOCK2 -> div8 */ + TC_FREQUENCY / 32, /* TIMER_CLOCK3 -> div32 */ + TC_FREQUENCY / 128, /* TIMER_CLOCK4 -> div128 */ + 32768 /* TIMER_CLOCK5 -> slow clock */ +}; + +/* TC register lookup used by sam_tc_setregister */ + +#define TC_NREGISTERS 3 + +static const uint8_t g_regoffset[TC_NREGISTERS] = +{ + SAM_TC_RA_OFFSET, /* Register A */ + SAM_TC_RB_OFFSET, /* Register B */ + SAM_TC_RC_OFFSET /* Register C */ +}; + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -352,14 +573,13 @@ static inline void sam_chan_putreg(struct sam_chan_s *chan, unsigned int offset, static inline struct sam_chan_s *sam_tc_initialize(int channel) { - static struct sam_tc_s *tc; - static struct sam_chan_s *chan; + FAR struct sam_tc_s *tc; + FAR const struct sam_tcconfig_s *tcconfig; + FAR struct sam_chan_s *chan; + FAR const struct sam_chconfig_s *chconfig; irqstate_t flags; - uintptr_t tcbase; - uintptr_t chbase; - int chfirst; - int chndx; - int pid; + uint32_t regval; + uint8_t ch; int i; /* Select the timer/counter and get the index associated with the @@ -369,34 +589,16 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel) #ifdef CONFIG_SAMA5_TC0 if (channel >= 0 && channel < 3) { - tc = &g_tc012; - chndx = channel; - - /* These are only needed in the case where we need to initialize the - * timer/counter. - */ - - chfirst = 0; - tcbase = SAM_TC012_VBASE; - chbase = SAM_TC012_CHAN_BASE(channel); - pid = SAM_PID_TC0; + tc = &g_tc012; + tcconfig = &g_tc012config; } else #endif #ifdef CONFIG_SAMA5_TC1 if (channel >= 3 && channel < 5) { - tc = &g_tc345; - chndx = channel - 3; - - /* These are only needed in the case where we need to initialize the - * timer/counter. - */ - - chfirst = 3; - tcbase = SAM_TC345_VBASE; - chbase = SAM_TC345_CHAN_BASE(channel) - pid = SAM_PID_TC0; + tc = &g_tc345; + tcconfig = &g_tc345config; } else #endif @@ -417,21 +619,53 @@ 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 = tcbase; - tc->pid = pid; + tc->base = tcconfig->base; + tc->pid = tcconfig->pid; /* Initialize the channels */ - for (i = 0; i < 3; i++) + for (i = 0, ch = tcconfig->chfirst; i < SAM_TC_NCHANNELS; i++) { + /* Initialize the channel data structure */ + chan = &tc->channel[i]; - chan->base = chbase; - chan->chan = chfirst++; + chconfig = &tcconfig->channel[i]; + + chan->base = chconfig->base; + chan->chan = ch++; + + /* Configure channel input/output pins */ + + if (chconfig->clkset) + { + /* Configure clock input pin */ + + sam_configpio(chconfig->clkset); + } + + if (chconfig->tioaset) + { + /* Configure output A pin */ + + sam_configpio(chconfig->tioaset); + } + + if (chconfig->tiobset) + { + /* Configure output B pin */ + + sam_configpio(chconfig->tiobset); + } } + /* Set the maximum TC peripheral clock frequency */ + + regval = PMC_PCR_PID(tcconfig->pid) | PMC_PCR_CMD | TC_PCR_DIV | PMC_PCR_EN; + putreg32(regval, SAM_PMC_PCR); + /* Enable clocking to the timer counter */ - sam_enableperiph0(pid); + sam_enableperiph0(tcconfig->pid); /* Now the channel is initialized */ @@ -445,7 +679,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel) /* Get the requested channel structure */ - chan = &tc->channel[chndx]; + chan = &tc->channel[channel - tcconfig->chfirst]; /* Is it available? */ @@ -484,7 +718,7 @@ static inline struct sam_chan_s *sam_tc_initialize(int channel) * ****************************************************************************/ -TCHANDLE sam_tc_allocate(int channel, int mode) +TC_HANDLE sam_tc_allocate(int channel, int mode) { struct sam_chan_s *chan; @@ -514,7 +748,7 @@ TCHANDLE sam_tc_allocate(int channel, int mode) /* Return an opaque reference to the channel */ - return (TCHANDLE)chan; + return (TC_HANDLE)chan; } /**************************************************************************** @@ -531,7 +765,7 @@ TCHANDLE sam_tc_allocate(int channel, int mode) * ****************************************************************************/ -void sam_tc_free(TCHANDLE handle) +void sam_tc_free(TC_HANDLE handle) { struct sam_chan_s *chan = (struct sam_chan_s *)handle; DEBUGASSERT(chan && chan->inuse); @@ -559,7 +793,7 @@ void sam_tc_free(TCHANDLE handle) * ****************************************************************************/ -void sam_tc_start(TCHANDLE handle) +void sam_tc_start(TC_HANDLE handle) { struct sam_chan_s *chan = (struct sam_chan_s *)handle; @@ -580,7 +814,7 @@ void sam_tc_start(TCHANDLE handle) * ****************************************************************************/ -void sam_tc_stop(TCHANDLE handle) +void sam_tc_stop(TC_HANDLE handle) { struct sam_chan_s *chan = (struct sam_chan_s *)handle; @@ -588,6 +822,53 @@ void sam_tc_stop(TCHANDLE handle) sam_chan_putreg(chan, SAM_TC_CCR_OFFSET, TC_CCR_CLKDIS); } +/**************************************************************************** + * Name: sam_tc_setregister + * + * Description: + * Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual + * setting in the regsiter will be the TC input frequency divided by + * the provided divider (which should derive from the divider returned + * by sam_tc_divider). + * + * + * Input Parameters: + * handle Channel handle previously allocated by sam_tc_allocate() + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div) +{ + struct sam_chan_s *chan = (struct sam_chan_s *)handle; + DEBUGASSERT(reg < TC_NREGISTERS); + + sam_chan_putreg(chan, g_regoffset[reg], TC_FREQUENCY / div); +} + +/**************************************************************************** + * Name: sam_tc_frequency + * + * Description: + * Return the timer input frequency, that is, the MCK frequency divided + * down so that the timer/counter is driven within its maximum frequency. + * This value needed for + * + * Input Parameters: + * None + * + * Returned Value: + * The timer input frequency. + * + ****************************************************************************/ + +uint32_t sam_tc_frequency(void) +{ + return TC_FREQUENCY; +} + /**************************************************************************** * Name: sam_tc_divisor * @@ -595,17 +876,17 @@ void sam_tc_stop(TCHANDLE handle) * Finds the best MCK divisor given the timer frequency and MCK. The * result is guaranteed to satisfy the following equation: * - * (MCK / (DIV * 65536)) <= freq <= (MCK / DIV) + * (Ftc / (div * 65536)) <= freq <= (Ftc / dev) * - * with DIV being the highest possible value. + * where: + * freq - the desitred frequency + * Ftc - The timer/counter input frequency + * div - With DIV being the highest possible value. * * Input Parameters: - * * frequency Desired timer frequency. - * mck Master clock frequency. * div Divisor value. * tcclks TCCLKS field value for divisor. - * boardmck Board clock frequency. * * Returned Value: * Zero (OK) if a proper divisor has been found, otherwise a negated errno @@ -613,46 +894,41 @@ void sam_tc_stop(TCHANDLE handle) * ****************************************************************************/ -uint32_t sam_tc_divisor(uint32_t frequency, uint32_t mck, uint32_t *div, - uint32_t *tcclks, uint32_t boardmck) +int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks) { - const uint32_t adivisors[5] = { 2, 8, 32, 128, boardmck / 32768 }; int ndx = 0; /* Satisfy lower bound */ - while (frequency < ((mck / adivisors[ndx]) / 65536)) + while (frequency < (g_divfreq[ndx] >> 16)) { - ndx++; - - /* If no divisor can be found, return -ERANGE */ - - if (ndx == (sizeof(adivisors)/sizeof(adivisors[0]))) + if (++ndx > TC_NDIVIDERS) { + /* If no divisor can be found, return -ERANGE */ + return -ERANGE; } } - /* Try to maximize DIV while satisfying upper bound */ + /* Try to maximize DIV while still satisfying upper bound */ - while (ndx < 4) + for (; ndx < (TC_NDIVIDERS-1); ndx++) { - - if (frequency > (mck / adivisors[ndx + 1])) + if (frequency > g_divfreq[ndx + 1]) { break; } - - ndx++; } - /* Store results */ + /* Return the divider value */ if (div) { - *div = adivisors[ndx]; + *div = g_divider[ndx]; } + /* REturn the TCCLKS selection */ + if (tcclks) { *tcclks = ndx; diff --git a/arch/arm/src/sama5/sam_tc.h b/arch/arm/src/sama5/sam_tc.h index d73f3e0d16..97e7b0c34b 100644 --- a/arch/arm/src/sama5/sam_tc.h +++ b/arch/arm/src/sama5/sam_tc.h @@ -62,11 +62,17 @@ #define TC_CHAN4 4 #define TC_CHAN5 5 +/* Register identifier used with sam_tc_setregister */ + +#define TC_REGA 0 +#define TC_REGB 1 +#define TC_REGC 2 + /**************************************************************************** * Public Types ****************************************************************************/ -typedef void *TCHANDLE; +typedef void *TC_HANDLE; /**************************************************************************** * Public Data @@ -104,7 +110,7 @@ extern "C" * ****************************************************************************/ -TCHANDLE sam_tc_allocate(int channel, int mode); +TC_HANDLE sam_tc_allocate(int channel, int mode); /**************************************************************************** * Name: sam_tc_free @@ -120,7 +126,7 @@ TCHANDLE sam_tc_allocate(int channel, int mode); * ****************************************************************************/ -void sam_tc_free(TCHANDLE handle); +void sam_tc_free(TC_HANDLE handle); /**************************************************************************** * Name: sam_tc_start @@ -136,7 +142,7 @@ void sam_tc_free(TCHANDLE handle); * ****************************************************************************/ -void sam_tc_start(TCHANDLE handle); +void sam_tc_start(TC_HANDLE handle); /**************************************************************************** * Name: sam_tc_stop @@ -151,7 +157,44 @@ void sam_tc_start(TCHANDLE handle); * ****************************************************************************/ -void sam_tc_stop(TCHANDLE handle); +void sam_tc_stop(TC_HANDLE handle); + +/**************************************************************************** + * Name: sam_tc_setregister + * + * Description: + * Set TC_RA, TC_RB, or TC_RB using the provided divisor. The actual + * setting in the regsiter will be the TC input frequency divided by + * the provided divider (which should derive from the divider returned + * by sam_tc_divider). + * + * + * Input Parameters: + * handle Channel handle previously allocated by sam_tc_allocate() + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sam_tc_setregister(TC_HANDLE handle, int reg, unsigned int div); + +/**************************************************************************** + * Name: sam_tc_frequency + * + * Description: + * Return the timer input frequency, 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_frequency(void); /**************************************************************************** * Name: sam_tc_divisor @@ -160,17 +203,17 @@ void sam_tc_stop(TCHANDLE handle); * Finds the best MCK divisor given the timer frequency and MCK. The * result is guaranteed to satisfy the following equation: * - * (MCK / (DIV * 65536)) <= freq <= (MCK / DIV) + * (Ftc / (div * 65536)) <= freq <= (Ftc / dev) * - * with DIV being the highest possible value. + * where: + * freq - the desitred frequency + * Ftc - The timer/counter input frequency + * div - With DIV being the highest possible value. * * Input Parameters: - * * frequency Desired timer frequency. - * mck Master clock frequency. * div Divisor value. * tcclks TCCLKS field value for divisor. - * boardmck Board clock frequency. * * Returned Value: * Zero (OK) if a proper divisor has been found, otherwise a negated errno @@ -178,8 +221,7 @@ void sam_tc_stop(TCHANDLE handle); * ****************************************************************************/ -uint32_t sam_tc_divisor(uint32_t frequency, uint32_t mck, uint32_t *div, - uint32_t *tcclks, uint32_t boardmck); +int sam_tc_divisor(uint32_t frequency, uint32_t *div, uint32_t *tcclks); #undef EXTERN #ifdef __cplusplus