diff --git a/arch/arm/src/efm32/Kconfig b/arch/arm/src/efm32/Kconfig index 6684acf9baf..0b90826e5ed 100644 --- a/arch/arm/src/efm32/Kconfig +++ b/arch/arm/src/efm32/Kconfig @@ -132,6 +132,11 @@ config EFM32_RMU bool "Reset Management Unit (RMU) " default n +config EFM32_FLASHPROG + bool "Enable Erase/Write flash function (MSC) " + default n + select ARCH_HAVE_RAMFUNCS + config EFM32_RMU_DEBUG bool "Reset Management Unit (RMU) DEBUG " default n @@ -145,6 +150,10 @@ config EFM32_I2C1 bool "I2C1" default n +config EFM32_BITBAND + bool "BITBAND" + default n + config EFM32_USART0 bool "USART0" default n diff --git a/arch/arm/src/efm32/Make.defs b/arch/arm/src/efm32/Make.defs index e40e240d79c..609ac46d14e 100644 --- a/arch/arm/src/efm32/Make.defs +++ b/arch/arm/src/efm32/Make.defs @@ -105,6 +105,14 @@ endif CHIP_CSRCS = efm32_start.c efm32_clockconfig.c efm32_irq.c efm32_timerisr.c CHIP_CSRCS += efm32_gpio.c efm32_lowputc.c efm32_timer.c efm32_i2c.c +ifeq ($(CONFIG_EFM32_FLASHPROG),y) +CHIP_CSRCS += efm32_flash.c +endif + +ifeq ($(CONFIG_EFM32_BITBAND),y) +CHIP_CSRCS += efm32_bitband.c +endif + ifneq ($(CONFIG_ARCH_IDLE_CUSTOM),y) CHIP_CSRCS += efm32_idle.c endif diff --git a/arch/arm/src/efm32/efm32_adc.c b/arch/arm/src/efm32/efm32_adc.c new file mode 100644 index 00000000000..b670c3923e8 --- /dev/null +++ b/arch/arm/src/efm32/efm32_adc.c @@ -0,0 +1,1256 @@ +/**************************************************************************** + * arch/arm/src/efm32/efm32_adc.c + * + * Copyright (C) 2014 Bouteville Pierre-Noel. All rights reserved. + * Authors: Bouteville Pierre-Noel + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "up_internal.h" +#include "up_arch.h" + +#include "chip.h" +#include "efm32.h" +#include "efm32_adc.h" + +/* ADC "upper half" support must be enabled */ + +#ifdef CONFIG_ADC + +/* Some ADC peripheral must be enabled */ + +#if defined(CONFIG_EFM32_ADC1) + +/* This implementation is for the STM32 F1, F2, and F4 only */ + +#if defined(CONFIG_EFM32_EFM32GG) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* ADC interrupts ***********************************************************/ + +/* The maximum number of channels that can be sampled. If dma support is + * not enabled, then only a single channel can be sampled. Otherwise, + * data overruns would occur. + */ + +#ifdef CONFIG_ADC_DMA +# define ADC_MAX_SAMPLES 16 +# warning "not tested !" +#else +# define ADC_MAX_SAMPLES 1 +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure describes the state of one ADC block */ + +struct efm32_dev_s +{ + uint8_t irq; /* Interrupt generated by this ADC block */ + uint8_t nchannels; /* Number of channels */ + uint8_t current; /* Current ADC channel being converted */ + xcpt_t isr; /* Interrupt handler for this ADC block */ + uint32_t base; /* Base address of registers unique to this ADC block */ + uint8_t chanlist[ADC_MAX_SAMPLES]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* ADC Register access */ + +static uint32_t adc_getreg( struct stm32_dev_s *priv, int offset); +static void adc_putreg( struct stm32_dev_s *priv, int offset, uint32_t value); +static void adc_hw_reset(struct stm32_dev_s *priv, bool reset); + +/* ADC Interrupt Handler */ + +static int adc_interrupt(FAR struct adc_dev_s *dev); + +/* ADC Driver Methods */ + +static void adc_reset(FAR struct adc_dev_s *dev); +static int adc_setup(FAR struct adc_dev_s *dev); +static void adc_shutdown(FAR struct adc_dev_s *dev); +static void adc_rxint(FAR struct adc_dev_s *dev, bool enable); +static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg); +static void adc_enable(FAR struct stm32_dev_s *priv, bool enable); + +#ifdef ADC_HAVE_TIMER +static void adc_timstart(FAR struct stm32_dev_s *priv, bool enable); +static int adc_timinit(FAR struct stm32_dev_s *priv); +#endif + +#if defined(CONFIG_EFM32_EFM32GG) +static void adc_startconv(FAR struct stm32_dev_s *priv, bool enable); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* ADC interface operations */ + +static const struct adc_ops_s g_adcops = +{ + .ao_reset = adc_reset, + .ao_setup = adc_setup, + .ao_shutdown = adc_shutdown, + .ao_rxint = adc_rxint, + .ao_ioctl = adc_ioctl, +}; + +/* ADC1 state */ + +#ifdef CONFIG_EFM32_ADC1 +static struct efm32_dev_s g_adcpriv1 = +{ + .irq = EFM32_IRQ_ADC0, + .isr = adc_interrupt, + .base = STM32_ADC1_BASE, +}; + +static struct adc_dev_s g_adcdev1 = +{ + .ad_ops = &g_adcops, + .ad_priv= &g_adcpriv1, +}; +#endif + + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: adc_getreg + * + * Description: + * Read the value of an ADC register. + * + * Input Parameters: + * priv - A reference to the ADC block status + * offset - The offset to the register to read + * + * Returned Value: + * + ****************************************************************************/ + +static uint32_t adc_getreg(struct stm32_dev_s *priv, int offset) +{ + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: adc_getreg + * + * Description: + * Read the value of an ADC register. + * + * Input Parameters: + * priv - A reference to the ADC block status + * offset - The offset to the register to read + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_putreg(struct stm32_dev_s *priv, int offset, uint32_t value) +{ + putreg32(value, priv->base + offset); +} + + +/***************************************************************************//** + * @brief + * Load SCAN calibrate register with predefined values for a certain + * reference. + * + * @details + * During production, calibration values are made and stored in the device + * information page for known references. Notice that for external references, + * calibration values must be determined explicitly, and this function + * will not modify the calibration register. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] ref + * Reference to load calibrated values for. No values are loaded for + * external references. + ******************************************************************************/ +static void ADC_CalibrateLoadScan(ADC_TypeDef *adc, ADC_Ref_TypeDef ref) +{ + uint32_t cal; + + /* Load proper calibration data depending on selected reference */ + /* NOTE: We use ...SCAN... defines below, they are the same as */ + /* similar ...SINGLE... defines. */ + switch (ref) + { + case adcRef1V25: + cal = adc->CAL & ~(_ADC_CAL_SCANOFFSET_MASK | _ADC_CAL_SCANGAIN_MASK); + cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_1V25_GAIN_MASK) >> + _DEVINFO_ADC0CAL0_1V25_GAIN_SHIFT) << _ADC_CAL_SCANGAIN_SHIFT; + cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_1V25_OFFSET_MASK) >> + _DEVINFO_ADC0CAL0_1V25_OFFSET_SHIFT) << _ADC_CAL_SCANOFFSET_SHIFT; + adc->CAL = cal; + break; + + case adcRef2V5: + cal = adc->CAL & ~(_ADC_CAL_SCANOFFSET_MASK | _ADC_CAL_SCANGAIN_MASK); + cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_2V5_GAIN_MASK) >> + _DEVINFO_ADC0CAL0_2V5_GAIN_SHIFT) << _ADC_CAL_SCANGAIN_SHIFT; + cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_2V5_OFFSET_MASK) >> + _DEVINFO_ADC0CAL0_2V5_OFFSET_SHIFT) << _ADC_CAL_SCANOFFSET_SHIFT; + adc->CAL = cal; + break; + + case adcRefVDD: + cal = adc->CAL & ~(_ADC_CAL_SCANOFFSET_MASK | _ADC_CAL_SCANGAIN_MASK); + cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_VDD_GAIN_MASK) >> + _DEVINFO_ADC0CAL1_VDD_GAIN_SHIFT) << _ADC_CAL_SCANGAIN_SHIFT; + cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_VDD_OFFSET_MASK) >> + _DEVINFO_ADC0CAL1_VDD_OFFSET_SHIFT) << _ADC_CAL_SCANOFFSET_SHIFT; + adc->CAL = cal; + break; + + case adcRef5VDIFF: + cal = adc->CAL & ~(_ADC_CAL_SCANOFFSET_MASK | _ADC_CAL_SCANGAIN_MASK); + cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_5VDIFF_GAIN_MASK) >> + _DEVINFO_ADC0CAL1_5VDIFF_GAIN_SHIFT) << _ADC_CAL_SCANGAIN_SHIFT; + cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_5VDIFF_OFFSET_MASK) >> + _DEVINFO_ADC0CAL1_5VDIFF_OFFSET_SHIFT) << _ADC_CAL_SCANOFFSET_SHIFT; + adc->CAL = cal; + break; + + case adcRef2xVDD: + /* Gain value not of relevance for this reference, leave as is */ + cal = adc->CAL & ~_ADC_CAL_SCANOFFSET_MASK; + cal |= ((DEVINFO->ADC0CAL2 & _DEVINFO_ADC0CAL2_2XVDDVSS_OFFSET_MASK) >> + _DEVINFO_ADC0CAL2_2XVDDVSS_OFFSET_SHIFT) << _ADC_CAL_SCANOFFSET_SHIFT; + adc->CAL = cal; + break; + + /* For external references, the calibration must be determined for the */ + /* specific application and set explicitly. */ + default: + break; + } +} + +/***************************************************************************//** + * @brief + * Load SINGLE calibrate register with predefined values for a certain + * reference. + * + * @details + * During production, calibration values are made and stored in the device + * information page for known references. Notice that for external references, + * calibration values must be determined explicitly, and this function + * will not modify the calibration register. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] ref + * Reference to load calibrated values for. No values are loaded for + * external references. + ******************************************************************************/ +static void ADC_CalibrateLoadSingle(ADC_TypeDef *adc, ADC_Ref_TypeDef ref) +{ + uint32_t cal; + + /* Load proper calibration data depending on selected reference */ + /* NOTE: We use ...SCAN... defines below, they are the same as */ + /* similar ...SINGLE... defines. */ + switch (ref) + { + case adcRef1V25: + cal = adc->CAL & ~(_ADC_CAL_SINGLEOFFSET_MASK | _ADC_CAL_SINGLEGAIN_MASK); + cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_1V25_GAIN_MASK) >> + _DEVINFO_ADC0CAL0_1V25_GAIN_SHIFT) << _ADC_CAL_SINGLEGAIN_SHIFT; + cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_1V25_OFFSET_MASK) >> + _DEVINFO_ADC0CAL0_1V25_OFFSET_SHIFT) << _ADC_CAL_SINGLEOFFSET_SHIFT; + adc->CAL = cal; + break; + + case adcRef2V5: + cal = adc->CAL & ~(_ADC_CAL_SINGLEOFFSET_MASK | _ADC_CAL_SINGLEGAIN_MASK); + cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_2V5_GAIN_MASK) >> + _DEVINFO_ADC0CAL0_2V5_GAIN_SHIFT) << _ADC_CAL_SINGLEGAIN_SHIFT; + cal |= ((DEVINFO->ADC0CAL0 & _DEVINFO_ADC0CAL0_2V5_OFFSET_MASK) >> + _DEVINFO_ADC0CAL0_2V5_OFFSET_SHIFT) << _ADC_CAL_SINGLEOFFSET_SHIFT; + adc->CAL = cal; + break; + + case adcRefVDD: + cal = adc->CAL & ~(_ADC_CAL_SINGLEOFFSET_MASK | _ADC_CAL_SINGLEGAIN_MASK); + cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_VDD_GAIN_MASK) >> + _DEVINFO_ADC0CAL1_VDD_GAIN_SHIFT) << _ADC_CAL_SINGLEGAIN_SHIFT; + cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_VDD_OFFSET_MASK) >> + _DEVINFO_ADC0CAL1_VDD_OFFSET_SHIFT) << _ADC_CAL_SINGLEOFFSET_SHIFT; + adc->CAL = cal; + break; + + case adcRef5VDIFF: + cal = adc->CAL & ~(_ADC_CAL_SINGLEOFFSET_MASK | _ADC_CAL_SINGLEGAIN_MASK); + cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_5VDIFF_GAIN_MASK) >> + _DEVINFO_ADC0CAL1_5VDIFF_GAIN_SHIFT) << _ADC_CAL_SINGLEGAIN_SHIFT; + cal |= ((DEVINFO->ADC0CAL1 & _DEVINFO_ADC0CAL1_5VDIFF_OFFSET_MASK) >> + _DEVINFO_ADC0CAL1_5VDIFF_OFFSET_SHIFT) << _ADC_CAL_SINGLEOFFSET_SHIFT; + adc->CAL = cal; + break; + + case adcRef2xVDD: + /* Gain value not of relevance for this reference, leave as is */ + cal = adc->CAL & ~_ADC_CAL_SINGLEOFFSET_MASK; + cal |= ((DEVINFO->ADC0CAL2 & _DEVINFO_ADC0CAL2_2XVDDVSS_OFFSET_MASK) >> + _DEVINFO_ADC0CAL2_2XVDDVSS_OFFSET_SHIFT) << _ADC_CAL_SINGLEOFFSET_SHIFT; + adc->CAL = cal; + break; + + /* For external references, the calibration must be determined for the */ + /* specific application and set explicitly. */ + default: + break; + } +} + +/** @endcond */ + +/******************************************************************************* + ************************** GLOBAL FUNCTIONS ******************************* + ******************************************************************************/ + +/***************************************************************************//** + * @brief + * Initialize ADC. + * + * @details + * Initializes common parts for both single conversion and scan sequence. + * In addition, single and/or scan control configuration must be done, please + * refer to ADC_InitSingle() and ADC_InitScan() respectively. + * + * @note + * This function will stop any ongoing conversion. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] init + * Pointer to ADC initialization structure. + ******************************************************************************/ +void ADC_Init(ADC_TypeDef *adc, const ADC_Init_TypeDef *init) +{ + uint32_t tmp; + + EFM_ASSERT(ADC_REF_VALID(adc)); + + /* Make sure conversion is not in progress */ + adc->CMD = ADC_CMD_SINGLESTOP | ADC_CMD_SCANSTOP; + + tmp = ((uint32_t)(init->ovsRateSel) << _ADC_CTRL_OVSRSEL_SHIFT) | + (((uint32_t)(init->timebase) << _ADC_CTRL_TIMEBASE_SHIFT) & _ADC_CTRL_TIMEBASE_MASK) | + (((uint32_t)(init->prescale) << _ADC_CTRL_PRESC_SHIFT) & _ADC_CTRL_PRESC_MASK) | + ((uint32_t)(init->lpfMode) << _ADC_CTRL_LPFMODE_SHIFT) | + ((uint32_t)(init->warmUpMode) << _ADC_CTRL_WARMUPMODE_SHIFT); + + if (init->tailgate) + { + tmp |= ADC_CTRL_TAILGATE; + } + + adc->CTRL = tmp; +} + + +/***************************************************************************//** + * @brief + * Initialize ADC scan sequence. + * + * @details + * Please refer to ADC_Start() for starting scan sequence. + * + * When selecting an external reference, the gain and offset calibration + * must be set explicitly (CAL register). For other references, the + * calibration is updated with values defined during manufacturing. + * + * @note + * This function will stop any ongoing scan sequence. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] init + * Pointer to ADC initialization structure. + ******************************************************************************/ +void ADC_InitScan(ADC_TypeDef *adc, const ADC_InitScan_TypeDef *init) +{ + uint32_t tmp; + + EFM_ASSERT(ADC_REF_VALID(adc)); + + /* Make sure scan sequence is not in progress */ + adc->CMD = ADC_CMD_SCANSTOP; + + /* Load proper calibration data depending on selected reference */ + ADC_CalibrateLoadScan(adc, init->reference); + + tmp = ((uint32_t)(init->prsSel) << _ADC_SCANCTRL_PRSSEL_SHIFT) | + ((uint32_t)(init->acqTime) << _ADC_SCANCTRL_AT_SHIFT) | + ((uint32_t)(init->reference) << _ADC_SCANCTRL_REF_SHIFT) | + init->input | + ((uint32_t)(init->resolution) << _ADC_SCANCTRL_RES_SHIFT); + + if (init->prsEnable) + { + tmp |= ADC_SCANCTRL_PRSEN; + } + + if (init->leftAdjust) + { + tmp |= ADC_SCANCTRL_ADJ_LEFT; + } + + if (init->diff) + { + tmp |= ADC_SCANCTRL_DIFF; + } + + if (init->rep) + { + tmp |= ADC_SCANCTRL_REP; + } + + adc->SCANCTRL = tmp; +} + + +/***************************************************************************//** + * @brief + * Initialize single ADC sample conversion. + * + * @details + * Please refer to ADC_Start() for starting single conversion. + * + * When selecting an external reference, the gain and offset calibration + * must be set explicitly (CAL register). For other references, the + * calibration is updated with values defined during manufacturing. + * + * @note + * This function will stop any ongoing single conversion. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + * + * @param[in] init + * Pointer to ADC initialization structure. + ******************************************************************************/ +void ADC_InitSingle(ADC_TypeDef *adc, const ADC_InitSingle_TypeDef *init) +{ + uint32_t tmp; + + EFM_ASSERT(ADC_REF_VALID(adc)); + + /* Make sure single conversion is not in progress */ + adc->CMD = ADC_CMD_SINGLESTOP; + + /* Load proper calibration data depending on selected reference */ + ADC_CalibrateLoadSingle(adc, init->reference); + + tmp = ((uint32_t)(init->prsSel) << _ADC_SINGLECTRL_PRSSEL_SHIFT) | + ((uint32_t)(init->acqTime) << _ADC_SINGLECTRL_AT_SHIFT) | + ((uint32_t)(init->reference) << _ADC_SINGLECTRL_REF_SHIFT) | + ((uint32_t)(init->input) << _ADC_SINGLECTRL_INPUTSEL_SHIFT) | + ((uint32_t)(init->resolution) << _ADC_SINGLECTRL_RES_SHIFT); + + if (init->prsEnable) + { + tmp |= ADC_SINGLECTRL_PRSEN; + } + + if (init->leftAdjust) + { + tmp |= ADC_SINGLECTRL_ADJ_LEFT; + } + + if (init->diff) + { + tmp |= ADC_SINGLECTRL_DIFF; + } + + if (init->rep) + { + tmp |= ADC_SINGLECTRL_REP; + } + + adc->SINGLECTRL = tmp; +} + + +/***************************************************************************//** + * @brief + * Calculate prescaler value used to determine ADC clock. + * + * @details + * The ADC clock is given by: HFPERCLK / (prescale + 1). + * + * @param[in] adcFreq ADC frequency wanted. The frequency will automatically + * be adjusted to be within valid range according to reference manual. + * + * @param[in] hfperFreq Frequency in Hz of reference HFPER clock. Set to 0 to + * use currently defined HFPER clock setting. + * + * @return + * Prescaler value to use for ADC in order to achieve a clock value + * <= @p adcFreq. + ******************************************************************************/ +uint8_t ADC_PrescaleCalc(uint32_t adcFreq, uint32_t hfperFreq) +{ + uint32_t ret; + + /* Make sure selected ADC clock is within valid range */ + if (adcFreq > ADC_MAX_CLOCK) + { + adcFreq = ADC_MAX_CLOCK; + } + else if (adcFreq < ADC_MIN_CLOCK) + { + adcFreq = ADC_MIN_CLOCK; + } + + /* Use current HFPER frequency? */ + if (!hfperFreq) + { + hfperFreq = CMU_ClockFreqGet(cmuClock_HFPER); + } + + ret = (hfperFreq + adcFreq - 1) / adcFreq; + if (ret) + { + ret--; + } + + return (uint8_t)ret; +} + + +/***************************************************************************//** + * @brief + * Reset ADC to same state as after a HW reset. + * + * @note + * The ROUTE register is NOT reset by this function, in order to allow for + * centralized setup of this feature. + * + * @param[in] adc + * Pointer to ADC peripheral register block. + ******************************************************************************/ +void ADC_Reset(ADC_TypeDef *adc) +{ + /* Stop conversions, before resetting other registers. */ + adc->CMD = ADC_CMD_SINGLESTOP | ADC_CMD_SCANSTOP; + adc->SINGLECTRL = _ADC_SINGLECTRL_RESETVALUE; + adc->SCANCTRL = _ADC_SCANCTRL_RESETVALUE; + adc->CTRL = _ADC_CTRL_RESETVALUE; + adc->IEN = _ADC_IEN_RESETVALUE; + adc->IFC = _ADC_IFC_MASK; + adc->BIASPROG = _ADC_BIASPROG_RESETVALUE; + + /* Load calibration values for the 1V25 internal reference. */ + ADC_CalibrateLoadSingle(adc, adcRef1V25); + ADC_CalibrateLoadScan(adc, adcRef1V25); + + /* Do not reset route register, setting should be done independently */ +} + + +/***************************************************************************//** + * @brief + * Calculate timebase value in order to get a timebase providing at least 1us. + * + * @param[in] hfperFreq Frequency in Hz of reference HFPER clock. Set to 0 to + * use currently defined HFPER clock setting. + * + * @return + * Timebase value to use for ADC in order to achieve at least 1 us. + ******************************************************************************/ +uint8_t ADC_TimebaseCalc(uint32_t hfperFreq) +{ + if (!hfperFreq) + { + hfperFreq = CMU_ClockFreqGet(cmuClock_HFPER); + + /* Just in case, make sure we get non-zero freq for below calculation */ + if (!hfperFreq) + { + hfperFreq = 1; + } + } +#if defined(_EFM32_GIANT_FAMILY) || defined(_EFM32_WONDER_FAMILY) + /* Handle errata on Giant Gecko, max TIMEBASE is 5 bits wide or max 0x1F */ + /* cycles. This will give a warmp up time of e.g. 0.645us, not the */ + /* required 1us when operating at 48MHz. One must also increase acqTime */ + /* to compensate for the missing clock cycles, adding up to 1us in total.*/ + /* See reference manual for details. */ + if( hfperFreq > 32000000 ) + { + hfperFreq = 32000000; + } +#endif + /* Determine number of HFPERCLK cycle >= 1us */ + hfperFreq += 999999; + hfperFreq /= 1000000; + + /* Return timebase value (N+1 format) */ + return (uint8_t)(hfperFreq - 1); +} + + +/** @} (end addtogroup ADC) */ +/** @} (end addtogroup EM_Library) */ +#endif /* defined(ADC_COUNT) && (ADC_COUNT > 0) */ + +/**************************************************************************** + * Name: adc_tim_dumpregs + * + * Description: + * Dump all timer registers. + * + * Input parameters: + * priv - A reference to the ADC block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef ADC_HAVE_TIMER +static void adc_tim_dumpregs(struct stm32_dev_s *priv, FAR const char *msg) +{ +#if defined(CONFIG_DEBUG_ANALOG) && defined(CONFIG_DEBUG_VERBOSE) + avdbg("%s:\n", msg); + avdbg(" CR1: %04x CR2: %04x SMCR: %04x DIER: %04x\n", + tim_getreg(priv, STM32_GTIM_CR1_OFFSET), + tim_getreg(priv, STM32_GTIM_CR2_OFFSET), + tim_getreg(priv, STM32_GTIM_SMCR_OFFSET), + tim_getreg(priv, STM32_GTIM_DIER_OFFSET)); + avdbg(" SR: %04x EGR: 0000 CCMR1: %04x CCMR2: %04x\n", + tim_getreg(priv, STM32_GTIM_SR_OFFSET), + tim_getreg(priv, STM32_GTIM_CCMR1_OFFSET), + tim_getreg(priv, STM32_GTIM_CCMR2_OFFSET)); + avdbg(" CCER: %04x CNT: %04x PSC: %04x ARR: %04x\n", + tim_getreg(priv, STM32_GTIM_CCER_OFFSET), + tim_getreg(priv, STM32_GTIM_CNT_OFFSET), + tim_getreg(priv, STM32_GTIM_PSC_OFFSET), + tim_getreg(priv, STM32_GTIM_ARR_OFFSET)); + avdbg(" CCR1: %04x CCR2: %04x CCR3: %04x CCR4: %04x\n", + tim_getreg(priv, STM32_GTIM_CCR1_OFFSET), + tim_getreg(priv, STM32_GTIM_CCR2_OFFSET), + tim_getreg(priv, STM32_GTIM_CCR3_OFFSET), + tim_getreg(priv, STM32_GTIM_CCR4_OFFSET)); + + if (priv->tbase == STM32_TIM1_BASE || priv->tbase == STM32_TIM8_BASE) + { + avdbg(" RCR: %04x BDTR: %04x DCR: %04x DMAR: %04x\n", + tim_getreg(priv, STM32_ATIM_RCR_OFFSET), + tim_getreg(priv, STM32_ATIM_BDTR_OFFSET), + tim_getreg(priv, STM32_ATIM_DCR_OFFSET), + tim_getreg(priv, STM32_ATIM_DMAR_OFFSET)); + } + else + { + avdbg(" DCR: %04x DMAR: %04x\n", + tim_getreg(priv, STM32_GTIM_DCR_OFFSET), + tim_getreg(priv, STM32_GTIM_DMAR_OFFSET)); + } +#endif +} +#endif + + +/**************************************************************************** + * Name: adc_startconv + * + * Description: + * Start (or stop) the ADC conversion process in DMA mode + * + * Input Parameters: + * priv - A reference to the ADC block status + * enable - True: Start conversion + * + * Returned Value: + * + ****************************************************************************/ + +#if defined(CONFIG_EFM32_EFM32GG) +static void adc_startconv(struct stm32_dev_s *priv, bool enable) +{ + uint32_t regval; + + avdbg("enable: %d\n", enable); + + regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); + if (enable) + { + /* Start conversion of regular channles */ + + regval |= ADC_CR2_SWSTART; + } + else + { + /* Disable the conversion of regular channels */ + + regval &= ~ADC_CR2_SWSTART; + } + adc_putreg(priv, STM32_ADC_CR2_OFFSET,regval); +} +#endif + +/**************************************************************************** + * Name: adc_reset + * + * Description: + * Deinitializes the ADCx peripheral registers to their default + * reset values. It could set all the ADCs configured. + * + * Input Parameters: + * regaddr - The register to read + * reset - Condition, set or reset + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_hw_reset(struct stm32_dev_s *priv, bool reset) +{ + irqstate_t flags; + uint32_t regval; + uint32_t adcbit; + + /* Pick the appropriate bit in the APB2 reset register */ + + + /* Disable interrupts. This is necessary because the APB2RTSR register + * is used by several different drivers. + */ + + flags = irqsave(); + + /* Set or clear the selected bit in the APB2 reset register */ + + regval = getreg32(STM32_RCC_APB2RSTR); + if (reset) + { + /* Enable ADC reset state */ + + regval |= adcbit; + } + else + { + /* Release ADC from reset state */ + + regval &= ~adcbit; + } + putreg32(regval, STM32_RCC_APB2RSTR); + irqrestore(flags); +} + +/******************************************************************************* + * Name: adc_enable + * + * Description : Enables or disables the specified ADC peripheral. + * Also, starts a conversion when the ADC is not + * triggered by timers + * + * Input Parameters: + * + * enable - true: enable ADC conversion + * false: disable ADC conversion + * + * Returned Value: + * + *******************************************************************************/ + +static void adc_enable(FAR struct stm32_dev_s *priv, bool enable) +{ + uint32_t regval; + + avdbg("enable: %d\n", enable); + + regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); + if (enable) + { + regval |= ADC_CR2_ADON; + } + else + { + regval &= ~ADC_CR2_ADON; + } + adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); +} + +/**************************************************************************** + * Name: adc_reset + * + * Description: + * Reset the ADC device. Called early to initialize the hardware. This + * is called, before adc_setup() and on error conditions. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_reset(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + irqstate_t flags; + uint32_t regval; + int offset; + int i; +#ifdef ADC_HAVE_TIMER + int ret; +#endif + + avdbg("intf: ADC%d\n", priv->intf); + flags = irqsave(); + + /* Enable ADC reset state */ + + adc_hw_reset(priv, true); + + /* Release ADC from reset state */ + + adc_hw_reset(priv, false); + + /* Initialize the ADC data structures */ + + /* Initialize the watchdog high threshold register */ + + adc_putreg(priv, STM32_ADC_HTR_OFFSET, 0x00000fff); + + /* Initialize the watchdog low threshold register */ + + adc_putreg(priv, STM32_ADC_LTR_OFFSET, 0x00000000); + + /* Initialize the same sample time for each ADC 55.5 cycles + * + * During sample cycles channel selection bits must remain unchanged. + * + * 000: 1.5 cycles + * 001: 7.5 cycles + * 010: 13.5 cycles + * 011: 28.5 cycles + * 100: 41.5 cycles + * 101: 55.5 cycles + * 110: 71.5 cycles + * 111: 239.5 cycles + */ + + adc_putreg(priv, STM32_ADC_SMPR1_OFFSET, 0x00b6db6d); + adc_putreg(priv, STM32_ADC_SMPR2_OFFSET, 0x00b6db6d); + + /* ADC CR1 Configuration */ + + regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET); + + /* Initialize the Analog watchdog enable */ + + regval |= ADC_CR1_AWDEN; + regval |= (priv->chanlist[0] << ADC_CR1_AWDCH_SHIFT); + + /* Enable interrupt flags */ + + regval |= ADC_CR1_ALLINTS; + + adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval); + + /* ADC CR2 Configuration */ + + regval = adc_getreg(priv, STM32_ADC_CR2_OFFSET); + + /* Clear CONT, continuous mode disable */ + + regval &= ~ADC_CR2_CONT; + + /* Set ALIGN (Right = 0) */ + + regval &= ~ADC_CR2_ALIGN; + + adc_putreg(priv, STM32_ADC_CR2_OFFSET, regval); + + /* Configuration of the channel conversions */ + + regval = adc_getreg(priv, STM32_ADC_SQR3_OFFSET) & ADC_SQR3_RESERVED; + for (i = 0, offset = 0; i < priv->nchannels && i < 6; i++, offset += 5) + { + regval |= (uint32_t)priv->chanlist[i] << offset; + } + adc_putreg(priv, STM32_ADC_SQR3_OFFSET, regval); + + regval = adc_getreg(priv, STM32_ADC_SQR2_OFFSET) & ADC_SQR2_RESERVED; + for (i = 6, offset = 0; i < priv->nchannels && i < 12; i++, offset += 5) + { + regval |= (uint32_t)priv->chanlist[i] << offset; + } + adc_putreg(priv, STM32_ADC_SQR2_OFFSET, regval); + + regval = adc_getreg(priv, STM32_ADC_SQR1_OFFSET) & ADC_SQR1_RESERVED; + for (i = 12, offset = 0; i < priv->nchannels && i < 16; i++, offset += 5) + { + regval |= (uint32_t)priv->chanlist[i] << offset; + } + + /* ADC CCR configuration */ + + regval = getreg32(STM32_ADC_CCR); + regval &= ~(ADC_CCR_MULTI_MASK | ADC_CCR_DELAY_MASK | ADC_CCR_DDS | ADC_CCR_DMA_MASK | + ADC_CCR_ADCPRE_MASK | ADC_CCR_VBATE | ADC_CCR_TSVREFE); + regval |= (ADC_CCR_MULTI_NONE | ADC_CCR_DMA_DISABLED | ADC_CCR_ADCPRE_DIV2); + putreg32(regval, STM32_ADC_CCR); + + /* Set the number of conversions */ + + DEBUGASSERT(priv->nchannels <= ADC_MAX_SAMPLES); + + regval |= (((uint32_t)priv->nchannels-1) << ADC_SQR1_L_SHIFT); + adc_putreg(priv, STM32_ADC_SQR1_OFFSET, regval); + + /* Set the channel index of the first conversion */ + + priv->current = 0; + + /* Set ADON to wake up the ADC from Power Down state. */ + + adc_enable(priv, true); + + adc_startconv(priv, true); + + irqrestore(flags); + + avdbg("SR: 0x%08x CR1: 0x%08x CR2: 0x%08x\n", + adc_getreg(priv, STM32_ADC_SR_OFFSET), + adc_getreg(priv, STM32_ADC_CR1_OFFSET), + adc_getreg(priv, STM32_ADC_CR2_OFFSET)); + avdbg("SQR1: 0x%08x SQR2: 0x%08x SQR3: 0x%08x\n", + adc_getreg(priv, STM32_ADC_SQR1_OFFSET), + adc_getreg(priv, STM32_ADC_SQR2_OFFSET), + adc_getreg(priv, STM32_ADC_SQR3_OFFSET)); +} + +/**************************************************************************** + * Name: adc_setup + * + * Description: + * Configure the ADC. This method is called the first time that the ADC + * device is opened. This will occur when the port is first opened. + * This setup includes configuring and attaching ADC interrupts. Interrupts + * are all disabled upon return. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int adc_setup(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + int ret; + + /* Attach the ADC interrupt */ + + ret = irq_attach(priv->irq, priv->isr); + if (ret == OK) + { + /* Make sure that the ADC device is in the powered up, reset state */ + + adc_reset(dev); + + /* Enable the ADC interrupt */ + + avdbg("Enable the ADC interrupt: irq=%d\n", priv->irq); + up_enable_irq(priv->irq); + } + + return ret; +} + +/**************************************************************************** + * Name: adc_shutdown + * + * Description: + * Disable the ADC. This method is called when the ADC device is closed. + * This method reverses the operation the setup method. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_shutdown(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + + /* Disable ADC interrupts and detach the ADC interrupt handler */ + + up_disable_irq(priv->irq); + irq_detach(priv->irq); + + /* Disable and reset the ADC module */ + + adc_hw_reset(priv, true); +} + +/**************************************************************************** + * Name: adc_rxint + * + * Description: + * Call to enable or disable RX interrupts. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_rxint(FAR struct adc_dev_s *dev, bool enable) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + uint32_t regval; + + avdbg("intf: %d enable: %d\n", priv->intf, enable); + + regval = adc_getreg(priv, STM32_ADC_CR1_OFFSET); + if (enable) + { + /* Enable the end-of-conversion ADC and analog watchdog interrupts */ + + regval |= ADC_CR1_ALLINTS; + } + else + { + /* Disable all ADC interrupts */ + + regval &= ~ADC_CR1_ALLINTS; + } + adc_putreg(priv, STM32_ADC_CR1_OFFSET, regval); +} + +/**************************************************************************** + * Name: adc_ioctl + * + * Description: + * All ioctl calls will be routed through this method. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg) +{ + return -ENOTTY; +} + +/**************************************************************************** + * Name: adc_interrupt + * + * Description: + * Common ADC interrupt handler. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int adc_interrupt(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + uint32_t adcsr; + int32_t value; + + /* Identifies the interruption AWD, OVR or EOC */ + + adcsr = adc_getreg(priv, STM32_ADC_SR_OFFSET); + if ((adcsr & ADC_SR_AWD) != 0) + { + alldbg("WARNING: Analog Watchdog, Value converted out of range!\n"); + } + + /* EOC: End of conversion */ + + if ((adcsr & ADC_SR_EOC) != 0) + { + /* Read the converted value and clear EOC bit + * (It is cleared by reading the ADC_DR) + */ + + value = adc_getreg(priv, STM32_ADC_DR_OFFSET); + value &= ADC_DR_DATA_MASK; + + /* Give the ADC data to the ADC driver. adc_receive accepts 3 parameters: + * + * 1) The first is the ADC device instance for this ADC block. + * 2) The second is the channel number for the data, and + * 3) The third is the converted data for the channel. + */ + + adc_receive(dev, priv->chanlist[priv->current], value); + + /* Set the channel number of the next channel that will complete conversion */ + + priv->current++; + + if (priv->current >= priv->nchannels) + { + /* Restart the conversion sequence from the beginning */ + + priv->current = 0; + } + } + + return OK; +} + + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_adcinitialize + * + * Description: + * Initialize the ADC. + * + * The logic is, save nchannels : # of channels (conversions) in ADC_SQR1_L + * Then, take the chanlist array and store it in the SQR Regs, + * chanlist[0] -> ADC_SQR3_SQ1 + * chanlist[1] -> ADC_SQR3_SQ2 + * ... + * chanlist[15]-> ADC_SQR1_SQ16 + * + * up to + * chanlist[nchannels] + * + * Input Parameters: + * intf - Could be {1,2,3} for ADC1, ADC2, or ADC3 + * chanlist - The list of channels + * nchannels - Number of channels + * + * Returned Value: + * Valid ADC device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +struct adc_dev_s *stm32_adcinitialize(int intf, const uint8_t *chanlist, int nchannels) +{ + FAR struct adc_dev_s *dev; + FAR struct stm32_dev_s *priv; + + avdbg("intf: %d nchannels: %d\n", intf, nchannels); + +#ifdef CONFIG_STM32_ADC1 + if (intf == 1) + { + avdbg("ADC1 Selected\n"); + dev = &g_adcdev1; + } + else +#endif +#ifdef CONFIG_STM32_ADC2 + if (intf == 2) + { + avdbg("ADC2 Selected\n"); + dev = &g_adcdev2; + } + else +#endif +#ifdef CONFIG_STM32_ADC3 + if (intf == 3) + { + avdbg("ADC3 Selected\n"); + dev = &g_adcdev3; + } + else +#endif + { + adbg("No ADC interface defined\n"); + return NULL; + } + + /* Configure the selected ADC */ + + priv = dev->ad_priv; + + DEBUGASSERT(nchannels <= ADC_MAX_SAMPLES); + priv->nchannels = nchannels; + + memcpy(priv->chanlist, chanlist, nchannels); + return dev; +} + +#endif /* CONFIG_STM32_STM32F10XX || CONFIG_STM32_STM32F20XX || CONFIG_STM32_STM32F40XX */ +#endif /* CONFIG_STM32_ADC || CONFIG_STM32_ADC2 || CONFIG_STM32_ADC3 */ +#endif /* CONFIG_ADC */ diff --git a/arch/arm/src/efm32/efm32_adc.h b/arch/arm/src/efm32/efm32_adc.h new file mode 100644 index 00000000000..f9e26294e38 --- /dev/null +++ b/arch/arm/src/efm32/efm32_adc.h @@ -0,0 +1,607 @@ +/************************************************************************************ + * arch/arm/src/stm32/stm32_adc.h + * + * Copyright (C) 2009, 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32_STM32_ADC_H +#define __ARCH_ARM_SRC_STM32_STM32_ADC_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include "chip.h" + +#if defined(CONFIG_STM32_STM32F30XX) +# include "chip/stm32f30xxx_adc.h" +#else +# include "chip/stm32_adc.h" +#endif + +#include + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* Configuration ********************************************************************/ +/* Timer devices may be used for different purposes. One special purpose is to + * control periodic ADC sampling. If CONFIG_STM32_TIMn is defined then + * CONFIG_STM32_TIMn_ADC must also be defined to indicate that timer "n" is intended + * to be used for that purpose. + */ + +/* For the STM32 F1 line, timers 1-4 may be used. For STM32 F4 line, timers 1-5 and + * 8 may be used. + */ + +#ifndef CONFIG_STM32_TIM1 +# undef CONFIG_STM32_TIM1_ADC +# undef CONFIG_STM32_TIM1_ADC1 +# undef CONFIG_STM32_TIM1_ADC2 +# undef CONFIG_STM32_TIM1_ADC3 +#endif +#ifndef CONFIG_STM32_TIM2 +# undef CONFIG_STM32_TIM2_ADC +# undef CONFIG_STM32_TIM2_ADC1 +# undef CONFIG_STM32_TIM2_ADC2 +# undef CONFIG_STM32_TIM2_ADC3 +#endif +#ifndef CONFIG_STM32_TIM3 +# undef CONFIG_STM32_TIM3_ADC +# undef CONFIG_STM32_TIM3_ADC1 +# undef CONFIG_STM32_TIM3_ADC2 +# undef CONFIG_STM32_TIM3_ADC3 +#endif +#ifndef CONFIG_STM32_TIM4 +# undef CONFIG_STM32_TIM4_ADC +# undef CONFIG_STM32_TIM4_ADC1 +# undef CONFIG_STM32_TIM4_ADC2 +# undef CONFIG_STM32_TIM4_ADC3 +#endif + +#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F40XX) +# ifndef CONFIG_STM32_TIM5 +# undef CONFIG_STM32_TIM5_ADC +# undef CONFIG_STM32_TIM5_ADC1 +# undef CONFIG_STM32_TIM5_ADC2 +# undef CONFIG_STM32_TIM5_ADC3 +# endif +# ifndef CONFIG_STM32_TIM8 +# undef CONFIG_STM32_TIM8_ADC +# undef CONFIG_STM32_TIM8_ADC1 +# undef CONFIG_STM32_TIM8_ADC2 +# undef CONFIG_STM32_TIM8_ADC3 +# endif +#else +# undef CONFIG_STM32_TIM5_ADC +# undef CONFIG_STM32_TIM5_ADC1 +# undef CONFIG_STM32_TIM5_ADC2 +# undef CONFIG_STM32_TIM5_ADC3 +# undef CONFIG_STM32_TIM8_ADC +# undef CONFIG_STM32_TIM8_ADC1 +# undef CONFIG_STM32_TIM8_ADC2 +# undef CONFIG_STM32_TIM8_ADC3 +#endif + +/* Timers 6, 7, and 10-14 are not used with the ADC by any supported family */ + +#undef CONFIG_STM32_TIM6_ADC +#undef CONFIG_STM32_TIM6_ADC1 +#undef CONFIG_STM32_TIM6_ADC2 +#undef CONFIG_STM32_TIM6_ADC3 +#undef CONFIG_STM32_TIM7_ADC +#undef CONFIG_STM32_TIM7_ADC1 +#undef CONFIG_STM32_TIM7_ADC2 +#undef CONFIG_STM32_TIM7_ADC3 +#undef CONFIG_STM32_TIM9_ADC +#undef CONFIG_STM32_TIM9_ADC1 +#undef CONFIG_STM32_TIM9_ADC2 +#undef CONFIG_STM32_TIM9_ADC3 +#undef CONFIG_STM32_TIM10_ADC +#undef CONFIG_STM32_TIM10_ADC1 +#undef CONFIG_STM32_TIM10_ADC2 +#undef CONFIG_STM32_TIM10_ADC3 +#undef CONFIG_STM32_TIM11_ADC +#undef CONFIG_STM32_TIM11_ADC1 +#undef CONFIG_STM32_TIM11_ADC2 +#undef CONFIG_STM32_TIM11_ADC3 +#undef CONFIG_STM32_TIM12_ADC +#undef CONFIG_STM32_TIM12_ADC1 +#undef CONFIG_STM32_TIM12_ADC2 +#undef CONFIG_STM32_TIM12_ADC3 +#undef CONFIG_STM32_TIM13_ADC +#undef CONFIG_STM32_TIM13_ADC1 +#undef CONFIG_STM32_TIM13_ADC2 +#undef CONFIG_STM32_TIM13_ADC3 +#undef CONFIG_STM32_TIM14_ADC +#undef CONFIG_STM32_TIM14_ADC1 +#undef CONFIG_STM32_TIM14_ADC2 +#undef CONFIG_STM32_TIM14_ADC3 + +/* Up to 3 ADC interfaces are supported */ + +#if STM32_NADC < 3 +# undef CONFIG_STM32_ADC3 +#endif + +#if STM32_NADC < 2 +# undef CONFIG_STM32_ADC2 +#endif + +#if STM32_NADC < 1 +# undef CONFIG_STM32_ADC1 +#endif + +#if defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2) || defined(CONFIG_STM32_ADC3) + +/* DMA support is not yet implemented for this driver */ + +#ifdef CONFIG_ADC_DMA +# warning "DMA is not supported by the current driver" +#endif + +/* Timer configuration: If a timer trigger is specified, then get information + * about the timer. + */ + +#if defined(CONFIG_STM32_TIM1_ADC1) +# define ADC1_HAVE_TIMER 1 +# define ADC1_TIMER_BASE STM32_TIM1_BASE +# define ADC1_TIMER_PCLK_FREQUENCY STM32_APB2_TIM1_CLKIN +#elif defined(CONFIG_STM32_TIM2_ADC1) +# define ADC1_HAVE_TIMER 1 +# define ADC1_TIMER_BASE STM32_TIM2_BASE +# define ADC1_TIMER_PCLK_FREQUENCY STM32_APB1_TIM2_CLKIN +#elif defined(CONFIG_STM32_TIM3_ADC1) +# define ADC1_HAVE_TIMER 1 +# define ADC1_TIMER_BASE STM32_TIM3_BASE +# define ADC1_TIMER_PCLK_FREQUENCY STM32_APB1_TIM3_CLKIN +#elif defined(CONFIG_STM32_TIM4_ADC1) +# define ADC1_HAVE_TIMER 1 +# define ADC1_TIMER_BASE STM32_TIM4_BASE +# define ADC1_TIMER_PCLK_FREQUENCY STM32_APB1_TIM4_CLKIN +#elif defined(CONFIG_STM32_TIM5_ADC1) +# define ADC1_HAVE_TIMER 1 +# define ADC1_TIMER_BASE STM32_TIM5_BASE +# define ADC1_TIMER_PCLK_FREQUENCY STM32_APB1_TIM5_CLKIN +#elif defined(CONFIG_STM32_TIM8_ADC1) +# define ADC1_HAVE_TIMER 1 +# define ADC1_TIMER_BASE STM32_TIM8_BASE +# define ADC1_TIMER_PCLK_FREQUENCY STM32_APB2_TIM8_CLKIN +#else +# undef ADC1_HAVE_TIMER +#endif + +#ifdef ADC1_HAVE_TIMER +# ifndef CONFIG_STM32_ADC1_SAMPLE_FREQUENCY +# error "CONFIG_STM32_ADC1_SAMPLE_FREQUENCY not defined" +# endif +# ifndef CONFIG_STM32_ADC1_TIMTRIG +# error "CONFIG_STM32_ADC1_TIMTRIG not defined" +# warning "Values 0:CC1 1:CC2 2:CC3 3:CC4 4:TRGO" +# endif +#endif + +#if defined(CONFIG_STM32_TIM1_ADC2) +# define ADC2_HAVE_TIMER 1 +# define ADC2_TIMER_BASE STM32_TIM1_BASE +# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB2_TIM1_CLKIN +#elif defined(CONFIG_STM32_TIM2_ADC2) +# define ADC2_HAVE_TIMER 1 +# define ADC2_TIMER_BASE STM32_TIM2_BASE +# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM2_CLKIN +#elif defined(CONFIG_STM32_TIM3_ADC2) +# define ADC2_HAVE_TIMER 1 +# define ADC2_TIMER_BASE STM32_TIM3_BASE +# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM3_CLKIN +#elif defined(CONFIG_STM32_TIM4_ADC2) +# define ADC2_HAVE_TIMER 1 +# define ADC2_TIMER_BASE STM32_TIM4_BASE +# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM4_CLKIN +#elif defined(CONFIG_STM32_TIM5_ADC2) +# define ADC2_HAVE_TIMER 1 +# define ADC2_TIMER_BASE STM32_TIM5_BASE +# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM5_CLKIN +#elif defined(CONFIG_STM32_TIM8_ADC2) +# define ADC2_HAVE_TIMER 1 +# define ADC2_TIMER_BASE STM32_TIM8_BASE +# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB2_TIM8_CLKIN +#else +# undef ADC2_HAVE_TIMER +#endif + +#ifdef ADC2_HAVE_TIMER +# ifndef CONFIG_STM32_ADC2_SAMPLE_FREQUENCY +# error "CONFIG_STM32_ADC2_SAMPLE_FREQUENCY not defined" +# endif +# ifndef CONFIG_STM32_ADC2_TIMTRIG +# error "CONFIG_STM32_ADC2_TIMTRIG not defined" +# warning "Values 0:CC1 1:CC2 2:CC3 3:CC4 4:TRGO" +# endif +#endif + +#if defined(CONFIG_STM32_TIM1_ADC3) +# define ADC3_HAVE_TIMER 1 +# define ADC3_TIMER_BASE STM32_TIM1_BASE +# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB2_TIM1_CLKIN +#elif defined(CONFIG_STM32_TIM2_ADC3) +# define ADC3_HAVE_TIMER 1 +# define ADC3_TIMER_BASE STM32_TIM2_BASE +# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM2_CLKIN +#elif defined(CONFIG_STM32_TIM3_ADC3) +# define ADC3_HAVE_TIMER 1 +# define ADC3_TIMER_BASE STM32_TIM3_BASE +# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM3_CLKIN +#elif defined(CONFIG_STM32_TIM4_ADC3) +# define ADC3_HAVE_TIMER 1 +# define ADC3_TIMER_BASE STM32_TIM4_BASE +# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM4_CLKIN +#elif defined(CONFIG_STM32_TIM5_ADC3) +# define ADC3_HAVE_TIMER 1 +# define ADC3_TIMER_BASE STM32_TIM5_BASE +# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM5_CLKIN +#elif defined(CONFIG_STM32_TIM8_ADC3) +# define ADC3_HAVE_TIMER 1 +# define ADC3_TIMER_BASE STM32_TIM8_BASE +# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB2_TIM8_CLKIN +#else +# undef ADC3_HAVE_TIMER +#endif + +#ifdef ADC3_HAVE_TIMER +# ifndef CONFIG_STM32_ADC3_SAMPLE_FREQUENCY +# error "CONFIG_STM32_ADC3_SAMPLE_FREQUENCY not defined" +# endif +# ifndef CONFIG_STM32_ADC3_TIMTRIG +# error "CONFIG_STM32_ADC3_TIMTRIG not defined" +# warning "Values 0:CC1 1:CC2 2:CC3 3:CC4 4:TRGO" +# endif +#endif + +#if defined(ADC1_HAVE_TIMER) || defined(ADC2_HAVE_TIMER) || defined(ADC3_HAVE_TIMER) +# define ADC_HAVE_TIMER 1 +# if defined(CONFIG_STM32_STM32F10XX) && !defined(CONFIG_STM32_FORCEPOWER) +# warning "CONFIG_STM32_FORCEPOWER must be defined to enable the timer(s)" +# endif +#else +# undef ADC_HAVE_TIMER +#endif + +/* NOTE: The following assumes that all possible combinations of timers and + * values are support EXTSEL. That is not so and it varies from one STM32 to another. + * But this (wrong) assumptions keeps the logic as simple as possible. If un + * unsupported combination is used, an error will show up later during compilation + * although it may be difficult to track it back to this simplification. + */ + +#if defined(CONFIG_STM32_TIM1_ADC1) +# if CONFIG_STM32_ADC1_TIMTRIG == 0 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T1CC1 +# elif CONFIG_STM32_ADC1_TIMTRIG == 1 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T1CC2 +# elif CONFIG_STM32_ADC1_TIMTRIG == 2 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T1CC3 +# elif CONFIG_STM32_ADC1_TIMTRIG == 3 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T1CC4 +# elif CONFIG_STM32_ADC1_TIMTRIG == 4 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T1TRGO +# else +# error "CONFIG_STM32_ADC1_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM2_ADC1) +# if CONFIG_STM32_ADC1_TIMTRIG == 0 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T2CC1 +# elif CONFIG_STM32_ADC1_TIMTRIG == 1 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T2CC2 +# elif CONFIG_STM32_ADC1_TIMTRIG == 2 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T2CC3 +# elif CONFIG_STM32_ADC1_TIMTRIG == 3 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T2CC4 +# elif CONFIG_STM32_ADC1_TIMTRIG == 4 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T2TRGO +# else +# error "CONFIG_STM32_ADC1_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM3_ADC1) +# if CONFIG_STM32_ADC1_TIMTRIG == 0 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T3CC1 +# elif CONFIG_STM32_ADC1_TIMTRIG == 1 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T3CC2 +# elif CONFIG_STM32_ADC1_TIMTRIG == 2 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T3CC3 +# elif CONFIG_STM32_ADC1_TIMTRIG == 3 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T3CC4 +# elif CONFIG_STM32_ADC1_TIMTRIG == 4 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T3TRGO +# else +# error "CONFIG_STM32_ADC1_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM4_ADC1) +# if CONFIG_STM32_ADC1_TIMTRIG == 0 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T4CC1 +# elif CONFIG_STM32_ADC1_TIMTRIG == 1 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T4CC2 +# elif CONFIG_STM32_ADC1_TIMTRIG == 2 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T4CC3 +# elif CONFIG_STM32_ADC1_TIMTRIG == 3 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T4CC4 +# elif CONFIG_STM32_ADC1_TIMTRIG == 4 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T4TRGO +# else +# error "CONFIG_STM32_ADC1_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM5_ADC1) +# if CONFIG_STM32_ADC1_TIMTRIG == 0 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T5CC1 +# elif CONFIG_STM32_ADC1_TIMTRIG == 1 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T5CC2 +# elif CONFIG_STM32_ADC1_TIMTRIG == 2 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T5CC3 +# elif CONFIG_STM32_ADC1_TIMTRIG == 3 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T5CC4 +# elif CONFIG_STM32_ADC1_TIMTRIG == 4 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T5TRGO +# else +# error "CONFIG_STM32_ADC1_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM8_ADC1) +# if CONFIG_STM32_ADC1_TIMTRIG == 0 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T8CC1 +# elif CONFIG_STM32_ADC1_TIMTRIG == 1 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T8CC2 +# elif CONFIG_STM32_ADC1_TIMTRIG == 2 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T8CC3 +# elif CONFIG_STM32_ADC1_TIMTRIG == 3 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T8CC4 +# elif CONFIG_STM32_ADC1_TIMTRIG == 4 +# define ADC1_EXTSEL_VALUE ADC_CR2_EXTSEL_T8TRGO +# else +# error "CONFIG_STM32_ADC1_TIMTRIG is out of range" +# endif +#endif + +#if defined(CONFIG_STM32_TIM1_ADC2) +# if CONFIG_STM32_ADC2_TIMTRIG == 0 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T1CC1 +# elif CONFIG_STM32_ADC2_TIMTRIG == 1 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T1CC2 +# elif CONFIG_STM32_ADC2_TIMTRIG == 2 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T1CC3 +# elif CONFIG_STM32_ADC2_TIMTRIG == 3 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T1CC4 +# elif CONFIG_STM32_ADC2_TIMTRIG == 4 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T1TRGO +# else +# error "CONFIG_STM32_ADC2_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM2_ADC2) +# if CONFIG_STM32_ADC2_TIMTRIG == 0 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T2CC1 +# elif CONFIG_STM32_ADC2_TIMTRIG == 1 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T2CC2 +# elif CONFIG_STM32_ADC2_TIMTRIG == 2 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T2CC3 +# elif CONFIG_STM32_ADC2_TIMTRIG == 3 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T2CC4 +# elif CONFIG_STM32_ADC2_TIMTRIG == 4 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T2TRGO +# else +# error "CONFIG_STM32_ADC2_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM3_ADC2) +# if CONFIG_STM32_ADC2_TIMTRIG == 0 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T3CC1 +# elif CONFIG_STM32_ADC2_TIMTRIG == 1 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T3CC2 +# elif CONFIG_STM32_ADC2_TIMTRIG == 2 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T3CC3 +# elif CONFIG_STM32_ADC2_TIMTRIG == 3 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T3CC4 +# elif CONFIG_STM32_ADC2_TIMTRIG == 4 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T3TRGO +# else +# error "CONFIG_STM32_ADC2_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM4_ADC2) +# if CONFIG_STM32_ADC2_TIMTRIG == 0 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T4CC1 +# elif CONFIG_STM32_ADC2_TIMTRIG == 1 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T4CC2 +# elif CONFIG_STM32_ADC2_TIMTRIG == 2 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T4CC3 +# elif CONFIG_STM32_ADC2_TIMTRIG == 3 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T4CC4 +# elif CONFIG_STM32_ADC2_TIMTRIG == 4 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T4TRGO +# else +# error "CONFIG_STM32_ADC2_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM5_ADC2) +# if CONFIG_STM32_ADC2_TIMTRIG == 0 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T5CC1 +# elif CONFIG_STM32_ADC2_TIMTRIG == 1 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T5CC2 +# elif CONFIG_STM32_ADC2_TIMTRIG == 2 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T5CC3 +# elif CONFIG_STM32_ADC2_TIMTRIG == 3 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T5CC4 +# elif CONFIG_STM32_ADC2_TIMTRIG == 4 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T5TRGO +# else +# error "CONFIG_STM32_ADC2_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM8_ADC2) +# if CONFIG_STM32_ADC2_TIMTRIG == 0 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T8CC1 +# elif CONFIG_STM32_ADC2_TIMTRIG == 1 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T8CC2 +# elif CONFIG_STM32_ADC2_TIMTRIG == 2 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T8CC3 +# elif CONFIG_STM32_ADC2_TIMTRIG == 3 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T8CC4 +# elif CONFIG_STM32_ADC2_TIMTRIG == 4 +# define ADC2_EXTSEL_VALUE ADC_CR2_EXTSEL_T8TRGO +# else +# error "CONFIG_STM32_ADC2_TIMTRIG is out of range" +# endif +#endif + +#if defined(CONFIG_STM32_TIM1_ADC3) +# if CONFIG_STM32_ADC3_TIMTRIG == 0 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T1CC1 +# elif CONFIG_STM32_ADC3_TIMTRIG == 1 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T1CC2 +# elif CONFIG_STM32_ADC3_TIMTRIG == 2 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T1CC3 +# elif CONFIG_STM32_ADC3_TIMTRIG == 3 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T1CC4 +# elif CONFIG_STM32_ADC3_TIMTRIG == 4 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T1TRGO +# else +# error "CONFIG_STM32_ADC3_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM2_ADC3) +# if CONFIG_STM32_ADC3_TIMTRIG == 0 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T2CC1 +# elif CONFIG_STM32_ADC3_TIMTRIG == 1 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T2CC2 +# elif CONFIG_STM32_ADC3_TIMTRIG == 2 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T2CC3 +# elif CONFIG_STM32_ADC3_TIMTRIG == 3 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T2CC4 +# elif CONFIG_STM32_ADC3_TIMTRIG == 4 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T2TRGO +# else +# error "CONFIG_STM32_ADC3_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM3_ADC3) +# if CONFIG_STM32_ADC3_TIMTRIG == 0 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T3CC1 +# elif CONFIG_STM32_ADC3_TIMTRIG == 1 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T3CC2 +# elif CONFIG_STM32_ADC3_TIMTRIG == 2 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T3CC3 +# elif CONFIG_STM32_ADC3_TIMTRIG == 3 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T3CC4 +# elif CONFIG_STM32_ADC3_TIMTRIG == 4 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T3TRGO +# else +# error "CONFIG_STM32_ADC3_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM4_ADC3) +# if CONFIG_STM32_ADC3_TIMTRIG == 0 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T4CC1 +# elif CONFIG_STM32_ADC3_TIMTRIG == 1 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T4CC2 +# elif CONFIG_STM32_ADC3_TIMTRIG == 2 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T4CC3 +# elif CONFIG_STM32_ADC3_TIMTRIG == 3 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T4CC4 +# elif CONFIG_STM32_ADC3_TIMTRIG == 4 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T4TRGO +# else +# error "CONFIG_STM32_ADC3_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM5_ADC3) +# if CONFIG_STM32_ADC3_TIMTRIG == 0 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T5CC1 +# elif CONFIG_STM32_ADC3_TIMTRIG == 1 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T5CC2 +# elif CONFIG_STM32_ADC3_TIMTRIG == 2 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T5CC3 +# elif CONFIG_STM32_ADC3_TIMTRIG == 3 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T5CC4 +# elif CONFIG_STM32_ADC3_TIMTRIG == 4 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T5TRGO +# else +# error "CONFIG_STM32_ADC3_TIMTRIG is out of range" +# endif +#elif defined(CONFIG_STM32_TIM8_ADC3) +# if CONFIG_STM32_ADC3_TIMTRIG == 0 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T8CC1 +# elif CONFIG_STM32_ADC3_TIMTRIG == 1 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T8CC2 +# elif CONFIG_STM32_ADC3_TIMTRIG == 2 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T8CC3 +# elif CONFIG_STM32_ADC3_TIMTRIG == 3 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T8CC4 +# elif CONFIG_STM32_ADC3_TIMTRIG == 4 +# define ADC3_EXTSEL_VALUE ADC_CR2_EXTSEL_T8TRGO +# else +# error "CONFIG_STM32_ADC3_TIMTRIG is out of range" +# endif +#endif + +/************************************************************************************ + * Public Function Prototypes + ************************************************************************************/ + +#ifndef __ASSEMBLY__ +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" { +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: stm32_adcinitialize + * + * Description: + * Initialize the ADC. + * + * Input Parameters: + * intf - Could be {1,2,3} for ADC1, ADC2, or ADC3 + * chanlist - The list of channels + * nchannels - Number of channels + * + * Returned Value: + * Valid can device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +struct adc_dev_s; +EXTERN struct adc_dev_s *stm32_adcinitialize(int intf, const uint8_t *chanlist, + int nchannels); + +#undef EXTERN +#ifdef __cplusplus +} +#endif +#endif /* __ASSEMBLY__ */ + +#endif /* CONFIG_STM32_ADC || CONFIG_STM32_ADC2 || CONFIG_STM32_ADC3 */ +#endif /* __ARCH_ARM_SRC_STM32_STM32_ADC_H */ + diff --git a/arch/arm/src/efm32/efm32_bitband.c b/arch/arm/src/efm32/efm32_bitband.c new file mode 100644 index 00000000000..5bb1b5ecb46 --- /dev/null +++ b/arch/arm/src/efm32/efm32_bitband.c @@ -0,0 +1,189 @@ +/**************************************************************************** + * arch/arm/src/efm32/efm32_bitband.c + * + * Copyright (C) 2014 Bouteville Pierre-Noel. All rights reserved. + * Authors: Bouteville Pierre-Noel + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "stdint.h" + +#include "efm32_bitband.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if defined(CONFIG_EFM32_BITBAND) + +#ifndef EFM32_BITBAND_PER_BASE +# error "EFM32_BITBAND_PER_BASE not declared bitband may be not supported?" +#endif + +#ifndef EFM32_BITBAND_RAM_BASE +# error "EFM32_BITBAND_RAM_BASE not declared bitband may be not supported?" +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + + +/****************************************************************************** + * Name: bitband_set_peripheral + * Perform bit-band write operation on peripheral memory location. + * + * Description + * Bit-banding provides atomic read-modify-write cycle for single bit + * modification. Please refer to the reference manual for further details + * about bit-banding. + * + * Note + * This function is only atomic on cores which fully support bitbanding. + * + * Parameters + * addr Peripheral address location to modify bit in. + * bit Bit position to modify, 0-31. + * val Value to set bit to, 0 or 1. + * + ******************************************************************************/ +inline void bitband_set_peripheral(uint32_t addr, uint32_t bit, uint32_t val) +{ + uint32_t regval; + regval = EFM32_BITBAND_PER_BASE + ((addr-EFM32_PER_MEM_BASE)*32) + (bit*4); + + *((volatile uint32_t *)regval) = (uint32_t)val; +} + + +/****************************************************************************** + * Name: bitband_get_peripheral + * Perform bit-band operation on peripheral memory location. + * + * Description + * This function reads a single bit from the peripheral bit-band alias region. + * Bit-banding provides atomic read-modify-write cycle for single bit + * modification. Please refer to the reference manual for further details + * about bit-banding. + * + * Note + * This function is only atomic on cores which fully support bitbanding. + * + * Parameters + * addr Peripheral address location to read. + * bit Bit position to modify, 0-31. + * + * Return bit value read, 0 or 1. + * + ******************************************************************************/ +inline uint32_t bitband_get_peripheral(uint32_t addr, uint32_t bit) +{ + uint32_t regval; + regval = EFM32_BITBAND_PER_BASE + ((addr-EFM32_PER_MEM_BASE)*32) + (bit*4); + + return *((volatile uint32_t *)regval); +} + + +/****************************************************************************** + * Name: bitband_set_sram + * Perform bit-band write operation on SRAM memory location. + * + * Description + * Bit-banding provides atomic read-modify-write cycle for single bit + * modification. Please refer to the reference manual for further details + * about bit-banding. + * + * Note + * This function is only atomic on cores which fully support bitbanding. + * + * Parameters + * addr SRAM address location to modify bit in. + * bit Bit position to modify, 0-31. + * val Value to set bit to, 0 or 1. + * + ******************************************************************************/ +inline void bitband_set_sram(uint32_t addr, uint32_t bit, uint32_t val) +{ + uint32_t regval; + regval = EFM32_BITBAND_RAM_BASE + ((addr-EFM32_RAM_MEM_BASE)*32) + (bit*4); + + *((volatile uint32_t *)regval) = (uint32_t)val; +} + + +/****************************************************************************** + * Name: bitband_get_sram + * Perform bit-band operation on SRAM memory location. + * + * Description + * This function reads a single bit from the RAM bit-band alias region. + * Bit-banding provides atomic read-modify-write cycle for single bit + * modification. Please refer to the reference manual for further details + * about bit-banding. + * + * Note + * This function is only atomic on cores which fully support bitbanding. + * + * Parameters + * addr Peripheral address location to read. + * bit Bit position to modify, 0-31. + * + * Return bit value read, 0 or 1. + * + ******************************************************************************/ +inline uint32_t bitband_get_sram(uint32_t addr, uint32_t bit) +{ + uint32_t regval; + regval = EFM32_BITBAND_RAM_BASE + ((addr-EFM32_RAM_MEM_BASE)*32) + (bit*4); + + return *((volatile uint32_t *)regval); +} +#endif + + diff --git a/arch/arm/src/efm32/efm32_bitband.h b/arch/arm/src/efm32/efm32_bitband.h new file mode 100644 index 00000000000..12308a11d7a --- /dev/null +++ b/arch/arm/src/efm32/efm32_bitband.h @@ -0,0 +1,81 @@ +/***************************************************************************** + * arch/arm/src/efm32/efm32_bitband.h + * + * Copyright (C) 2015 Pierre-noel Bouteville . All rights reserved. + * Authors: Pierre-noel Bouteville + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_EFM32_EFM32_BITBAND_H +#define __ARCH_ARM_SRC_EFM32_EFM32_BITBAND_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip/efm32_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ + + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#if defined(CONFIG_EFM32_BITBAND) +inline void bitband_set_peripheral(uint32_t addr, uint32_t bit, uint32_t val); +inline uint32_t bitband_get_peripheral(uint32_t addr, uint32_t bit); +inline void bitband_set_sram(uint32_t addr, uint32_t bit, uint32_t val); +inline uint32_t bitband_get_sram(uint32_t addr, uint32_t bit); + +#else + +# define bitband_set_peripheral(addr,bit,val)\ + modifyreg32(addr,~(1<> bit) & 1) + +# define bitband_set_sram(add,bit,val)\ + modifyreg32(addr,~(1<> bit) & 1) + +#endif + +#endif /* __ARCH_ARM_SRC_EFM32_EFM32_BITBAND_H */ diff --git a/arch/arm/src/efm32/efm32_flash.c b/arch/arm/src/efm32/efm32_flash.c new file mode 100644 index 00000000000..f38105d67e0 --- /dev/null +++ b/arch/arm/src/efm32/efm32_flash.c @@ -0,0 +1,849 @@ +/***************************************************************************** + * arch/arm/src/efm32/efm32_flash.c + * + * Copyright 2014 Silicon Laboratories, Inc. http://www.silabs.com + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software.@n + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software.@n + * 3. This notice may not be removed or altered from any source distribution. + * + * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Laboratories, Inc. + * has no obligation to support this Software. Silicon Laboratories, Inc. is + * providing the Software "AS IS", with no express or implied warranties of any + * kind, including, but not limited to, any implied warranties of + * merchantability or fitness for any particular purpose or warranties against + * infringement of any proprietary rights of a third party. + * + * Silicon Laboratories, Inc. will not be liable for any consequential, + * incidental, or special damages, or any other relief, or for any claim by + * any third party, arising from your use of this Software. + * + * Copyright (C) 2015 Pierre-Noel Bouteville. All rights reserved. + * Author: Pierre-Noel Bouteville + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/* Provides standard flash access functions, to be used by the flash mtd driver. + * The interface is defined in the include/nuttx/progmem.h + * + * Requirements during write/erase operations on FLASH: + * - HSI must be ON. + * - Low Power Modes are not permitted during write/erase + */ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include +#include + + +#include + +#include "up_internal.h" +#include "up_arch.h" + +#include "chip/efm32_msc.h" +#include "chip/efm32_devinfo.h" + +#include "efm32_bitband.h" + +#include "nuttx/progmem.h" + +/* Only for the EFM32 family for now */ + +#if ( defined(CONFIG_ARCH_CHIP_EFM32) && defined(CONFIG_EFM32_FLASHPROG) ) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#ifndef CONFIG_ARCH_RAMFUNCS +# error "Flashing function should executed in ram" +#endif + +#ifndef EFM32_USERDATA_SIZE +# error "EFM32_USERDATA_SIZE should be defined" +#endif + +#ifndef EFM32_USERDATA_BASE +# define "EFM32_USERDATA_BASE should be defined" +#endif + +#ifndef EFM32_USERDATA_NPAGES +# define "EFM32_FLASH_NPAGES should be defined" +#endif + +#ifndef EFM32_USERDATA_PAGESIZE +# define EFM32_USERDATA_PAGESIZE (EFM32_USERDATA_SIZE/EFM32_USERDATA_NPAGES) +#endif + +/* + * brief: + * The timeout used while waiting for the flash to become ready after + * a write. This number indicates the number of iterations to perform before + * issuing a timeout. + * note: + * This timeout is set very large (in the order of 100x longer than + * necessary). This is to avoid any corner cases. + * + */ +#define MSC_PROGRAM_TIMEOUT 10000000ul + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +void efm32_flash_unlock(void) +{ + uint32_t regval; + + /* Unlock the EFM32_MSC */ + + putreg32(MSC_UNLOCK_CODE,EFM32_MSC_LOCK); + + /* Disable writing to the flash */ + + bitband_set_peripheral(EFM32_MSC_WRITECTRL,_MSC_WRITECTRL_WREN_SHIFT,0); + +#if defined( _MSC_TIMEBASE_MASK ) + + regval = getreg32(EFM32_MSC_TIMEBASE); + regval &= ~(_MSC_TIMEBASE_BASE_MASK | _MSC_TIMEBASE_PERIOD_MASK); + + /* Configure EFM32_MSC_TIMEBASE according to selected frequency */ + + if (BOARD_AUXCLK_FREQUENCY > 7000000) + { + uint32_t freq; + uint32_t cycles; + + /* Calculate number of clock cycles for 1us as base period */ + + freq = (BOARD_AUXCLK_FREQUENCY * 11) / 10; + cycles = (freq / 1000000) + 1; + + /* Configure clock cycles for flash timing */ + + regval |= MSC_TIMEBASE_PERIOD_1US; + regval |= (cycles << _MSC_TIMEBASE_BASE_SHIFT); + } + else + { + uint32_t freq; + uint32_t cycles; + + /* Calculate number of clock cycles for 5us as base period */ + + freq = (BOARD_AUXCLK_FREQUENCY * 5 * 11) / 10; + cycles = (freq / 1000000) + 1; + + /* Configure clock cycles for flash timing */ + + regval |= MSC_TIMEBASE_PERIOD_5US; + regval |= (cycles << _MSC_TIMEBASE_BASE_SHIFT); + } + + + putreg32(regval,EFM32_MSC_TIMEBASE); + +#endif +} + + +/******************************************************************************* + * Name: msc_load_verify_address + * Perform address phase of FLASH write cycle. + * Description: + * This function performs the address phase of a Flash write operation by + * writing the given flash address to the ADDRB register and issuing the + * LADDRIM command to load the address. + * note: + * This function MUST be executed from RAM. Failure to execute this portion + * of the code in RAM will result in a hardfault. For IAR, Rowley and + * Codesourcery this will be achieved automatically. For Keil uVision 4 you + * must define a section called "ram_code" and place this manually in your + * project's scatter file. + * param: + * address : Address in flash memory. Must be aligned at a 4 byte boundary. + * return: + * Returns the status of the address load operation, #msc_Return_TypeDef + * OK - Operation completed successfully. + * -EBUSY - Busy timeout. + * -EINVAL - Operation tried to access a non-flash area. + * -EACCES - Operation tried to access a locked area of the flash. + ******************************************************************************* + */ +int __ramfunc__ msc_load_verify_address(uint32_t* address) +{ + uint32_t status; + uint32_t timeout; + + /* Wait for the MSC to become ready. */ + + timeout = MSC_PROGRAM_TIMEOUT; + while ((getreg32(EFM32_MSC_STATUS) & MSC_STATUS_BUSY) && (timeout != 0)) + { + timeout--; + } + + /* Check for timeout */ + + if (timeout == 0) + { + return -EBUSY; + } + + /* Load address */ + + putreg32((uint32_t) (address),EFM32_MSC_ADDRB); + putreg32(MSC_WRITECMD_LADDRIM,EFM32_MSC_WRITECMD); + + status = getreg32(EFM32_MSC_STATUS); + if (status & (MSC_STATUS_INVADDR | MSC_STATUS_LOCKED)) + { + + /* Check for invalid address */ + + if (status & MSC_STATUS_INVADDR) + return -EINVAL; + + /* Check for write protected page */ + + if (status & MSC_STATUS_LOCKED) + return -EACCES; + } + return OK; +} + +/******************************************************************************* + * Name:msc_load_data + * Perform data phase of FLASH write cycle. + * Description: + * This function performs the data phase of a Flash write operation by loading + * the given number of 32-bit words to the WDATA register. + * note: + * This function MUST be executed from RAM. Failure to execute this portion + * of the code in RAM will result in a hardfault. For IAR, Rowley and + * Codesourcery this will be achieved automatically. For Keil uVision 4 you + * must define a section called "ram_code" and place this manually in your + * project's scatter file. + * paramelters: + * data : Pointer to the first data word to load. + * num_words : Number of data words (32-bit) to load. + * return: + * Returns the status of the data load operation, #msc_Return_TypeDef + * OK - Operation completed successfully. + * -ETIMEDOUT - Operation timed out waiting for flash operation + * to complete. + ******************************************************************************/ + +int __ramfunc__ msc_load_write_data(uint32_t* data, uint32_t num_words, + bool write_strategy_safe) +{ + int timeout; + int word_index; + int words_per_data_phase; + int ret = 0; + +#if defined( _MSC_WRITECTRL_LPWRITE_MASK ) && defined( _MSC_WRITECTRL_WDOUBLE_MASK ) + + /* If LPWRITE (Low Power Write) is NOT enabled, set WDOUBLE (Write Double word) */ + + if (!(getreg32(EFM32_MSC_WRITECTRL) & MSC_WRITECTRL_LPWRITE)) + { + + /* If the number of words to be written are odd, we need to align by writing + * a single word first, before setting the WDOUBLE bit. + */ + + if (num_words & 0x1) + { + + /* Wait for the msc to be ready for the next word. */ + + timeout = MSC_PROGRAM_TIMEOUT; + while ((!(getreg32(EFM32_MSC_STATUS) & MSC_STATUS_WDATAREADY)) && \ + (timeout != 0)) + { + timeout--; + } + + /* Check for timeout */ + + if (timeout == 0) + { + return -ETIMEDOUT; + } + + /* Clear double word option, in order to write one single word. */ + + bitband_set_peripheral(EFM32_MSC_WRITECTRL,_MSC_WRITECTRL_WDOUBLE_SHIFT,0); + + /* Write first data word. */ + + putreg32(*data++,EFM32_MSC_WDATA); + putreg32(MSC_WRITECMD_WRITEONCE,EFM32_MSC_WRITECMD); + + /* Wait for the operation to finish. It may be required to change the + * WDOUBLE config after the initial write. It should not be changed + * while BUSY. + */ + + timeout = MSC_PROGRAM_TIMEOUT; + while ((getreg32(EFM32_MSC_STATUS) & MSC_STATUS_BUSY) && (timeout != 0)) + { + timeout--; + } + + /* Check for timeout */ + + if (timeout == 0) + { + return -ETIMEDOUT; + } + + /* Subtract this initial odd word for the write loop below */ + + num_words --; + ret = 0; + } + + /* Now we can set the double word option in order to write two words per + * data phase. + */ + + bitband_set_peripheral(EFM32_MSC_WRITECTRL,_MSC_WRITECTRL_WDOUBLE_SHIFT,1); + words_per_data_phase = 2; + } + else +#endif + { + words_per_data_phase = 1; + } + + /* Write the rest as double word write if wordsPerDataPhase == 2 */ + + if ( num_words > 0 ) + { + + /* Write strategy: msc_write_int_safe */ + + if ( write_strategy_safe ) + { + + /* Requires a system core clock at 1MHz or higher */ + + DEBUGASSERT(BOARD_SYSTEM_FREQUENCY >= 1000000); + + word_index = 0; + while(word_index < num_words) + { + putreg32(*data++,EFM32_MSC_WDATA); + word_index++; + if (words_per_data_phase == 2) + { + while (!(getreg32(EFM32_MSC_STATUS) & MSC_STATUS_WDATAREADY)) + { + } + putreg32(*data++,EFM32_MSC_WDATA); + word_index++; + } + putreg32(MSC_WRITECMD_WRITEONCE,EFM32_MSC_WRITECMD); + + /* Wait for the transaction to finish. */ + + timeout = MSC_PROGRAM_TIMEOUT; + while ((getreg32(EFM32_MSC_STATUS) & MSC_STATUS_BUSY) && \ + (timeout != 0)) + { + timeout--; + } + + /* Check for timeout */ + + if (timeout == 0) + { + ret = -ETIMEDOUT; + break; + } +#if defined( CONFIG_EFM32_EFM32G ) + putreg32(getreg32(EFM32_MSC_ADDRB)+4,EFM32_MSC_ADDRB); + putreg32(MSC_WRITECMD_LADDRIM,EFM32_MSC_WRITECMD); +#endif + } + } + + /* Write strategy: msc_write_fast */ + + else + { +#if defined( CONFIG_EFM32_EFM32G ) + + /* Gecko does not have auto-increment of ADDR. */ + + DEBUGASSERT(0); +#else + + /* Requires a system core clock at 14MHz or higher */ + + DEBUGASSERT(BOARD_SYSTEM_FREQUENCY >= 14000000); + + word_index = 0; + + while(word_index < num_words) + { + + /* Wait for the MSC to be ready for the next word. */ + + while (!(getreg32(EFM32_MSC_STATUS) & MSC_STATUS_WDATAREADY)) + { + uint32_t regval; + + /* If the write to MSC->WDATA below missed the 30us timeout + * and the following MSC_WRITECMD_WRITETRIG command arrived + * while MSC_STATUS_BUSY is 1, then the MSC_WRITECMD_WRITETRIG + * could be ignored by the MSC. In this case, + * MSC_STATUS_WORDTIMEOUT is set to 1 and MSC_STATUS_BUSY is + * 0. A new trigger is therefore needed here to complete write + * of data in MSC->WDATA. If WDATAREADY became high since + * entry into this loop, exit and continue to the next WDATA + * write. + */ + + regval = getreg32(EFM32_MSC_STATUS); + regval &= MSC_STATUS_WORDTIMEOUT; + regval &= MSC_STATUS_BUSY; + regval &= MSC_STATUS_WDATAREADY; + if ( regval == MSC_STATUS_WORDTIMEOUT ) + { + putreg32(MSC_WRITECMD_WRITETRIG,EFM32_MSC_WRITECMD); + } + } + + putreg32(*data,EFM32_MSC_WDATA); + if (( words_per_data_phase == 1) || \ + ((words_per_data_phase == 2) && (word_index & 0x1))) + { + putreg32(MSC_WRITECMD_WRITETRIG,EFM32_MSC_WRITECMD); + } + data++; + word_index++; + } + + /* Wait for the transaction to finish. */ + + timeout = MSC_PROGRAM_TIMEOUT; + while ((getreg32(EFM32_MSC_STATUS) & MSC_STATUS_BUSY) && \ + (timeout != 0)) + { + timeout--; + } + + /* Check for timeout */ + + if (timeout == 0) + { + ret = -ETIMEDOUT; + } +#endif + } + } +#ifdef _MSC_WRITECTRL_WDOUBLE_MASK + + /* Clear double word option, which should not be left on when returning. */ + + bitband_set_peripheral(EFM32_MSC_WRITECTRL,_MSC_WRITECTRL_WDOUBLE_SHIFT,0); + +#endif + + return ret; + +} + + + +void efm32_flash_lock(void) +{ + + /* Disable writing to the flash */ + + bitband_set_peripheral(EFM32_MSC_WRITECTRL,_MSC_WRITECTRL_WREN_SHIFT,0); + + /* Unlock the EFM32_MSC */ + + putreg32(0,EFM32_MSC_LOCK); + +} + +#ifndef EFM32_FLASH_SIZE +#define EFM32_FLASH_SIZE efm32_get_flash_size() +uint32_t efm32_get_flash_size(void) +{ + uint32_t regval; + regval = getreg32(EFM32_DEVINFO_MEMINFO_SIZE); + regval = (regval & _DEVINFO_MEMINFO_SIZE_FLASH_MASK) \ + >> _DEVINFO_MEMINFO_SIZE_FLASH_SHIFT; + + return regval*1024; +} +#endif + +#ifndef EFM32_FLASH_PAGESIZE +#define EFM32_FLASH_PAGESIZE efm32_get_flash_page_size() +uint32_t efm32_get_flash_page_size(void) +{ + uint32_t regval; + regval = getreg32(EFM32_DEVINFO_MEMINFO_PAGE_SIZE); + regval = (regval & _DEVINFO_MEMINFO_FLASH_PAGE_SIZE_MASK) \ + >> _DEVINFO_MEMINFO_FLASH_PAGE_SIZE_SHIFT; + if ( regval == 0xff ) + return 512; + + return 1<<(regval+10); +} +#endif + +#ifndef EFM32_FLASH_NPAGES +#define EFM32_FLASH_NPAGES efm32_get_flash_page_nbr() +uint32_t efm32_get_flash_page_nbr(void) +{ + return (EFM32_FLASH_SIZE/EFM32_FLASH_PAGESIZE); +} +#endif + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +size_t up_progmem_pagesize(size_t page) +{ + if (page < EFM32_FLASH_NPAGES) + return EFM32_FLASH_PAGESIZE; + + page -= EFM32_FLASH_NPAGES; + + if (page < EFM32_USERDATA_NPAGES) + return EFM32_USERDATA_PAGESIZE; + + return 0; +} + +ssize_t up_progmem_getpage(size_t addr) +{ +#if ( EFM32_FLASH_BASE != 0 ) + if ( (addr >= (EFM32_FLASH_BASE) ) && \ + (addr < (EFM32_FLASH_BASE+EFM32_FLASH_SIZE) ) + ) + { + addr -= EFM32_FLASH_BASE; + + return addr / EFM32_FLASH_PAGESIZE; + } +#else + if ( addr < EFM32_FLASH_SIZE ) + { + return addr / EFM32_FLASH_PAGESIZE; + } +#endif + + if ( (addr >= (EFM32_USERDATA_BASE) ) && \ + (addr < (EFM32_USERDATA_BASE+EFM32_USERDATA_SIZE) ) + ) + { + addr -= EFM32_USERDATA_BASE; + + return (addr / EFM32_USERDATA_NPAGES) + EFM32_FLASH_NPAGES; + } + + return -EFAULT; +} + +size_t up_progmem_getaddress(size_t page) +{ + if (page < EFM32_FLASH_NPAGES) + { + return page * EFM32_FLASH_PAGESIZE + EFM32_FLASH_BASE; + } + + page -= EFM32_FLASH_NPAGES; + + if (page < EFM32_USERDATA_NPAGES) + { + return EFM32_USERDATA_BASE + (page * EFM32_USERDATA_NPAGES); + } + + return SIZE_MAX; +} + + +size_t up_progmem_npages(void) +{ + return EFM32_FLASH_NPAGES+EFM32_USERDATA_NPAGES; +} + +bool up_progmem_isuniform(void) +{ + return false; +} + +ssize_t __ramfunc__ up_progmem_erasepage(size_t page) +{ + int ret = 0; + int timeout; + uint32_t regval; + irqstate_t irqs; + + if (page >= (EFM32_FLASH_NPAGES+EFM32_USERDATA_NPAGES)) + { + return -EFAULT; + } + + efm32_flash_unlock(); + + irqs = irqsave(); + + /* enable writing to the flash */ + + bitband_set_peripheral(EFM32_MSC_WRITECTRL,_MSC_WRITECTRL_WREN_SHIFT,1); + + /* Load address */ + + putreg32((uint32_t)up_progmem_getaddress(page),EFM32_MSC_ADDRB); + putreg32(MSC_WRITECMD_LADDRIM,EFM32_MSC_WRITECMD); + + regval = getreg32(EFM32_MSC_STATUS); + + /* Check for invalid address */ + + if (regval & MSC_STATUS_INVADDR) + { + ret = -EINVAL; + } + + /* Check for write protected page */ + + if ( ( ret == 0 ) && (regval & MSC_STATUS_LOCKED) ) + { + ret = -EPERM; + } + + /* Send erase page command */ + + if ( ret == 0 ) + { + putreg32(MSC_WRITECMD_ERASEPAGE,EFM32_MSC_WRITECMD); + + /* Wait for the erase to complete */ + + timeout = MSC_PROGRAM_TIMEOUT; + while ((getreg32(EFM32_MSC_STATUS) & MSC_STATUS_BUSY) && (timeout != 0)) + { + timeout--; + } + + if (timeout == 0) + { + ret = -ETIMEDOUT; + } + } + + /* Disable writing to the MSC */ + + bitband_set_peripheral(EFM32_MSC_WRITECTRL,_MSC_WRITECTRL_WREN_SHIFT,0); + + if ( ret == 0 ) + { + /* Verify */ + + if (up_progmem_ispageerased(page) != 0) + { + ret = -EIO; + } + } + + irqrestore(irqs); + + if ( ret != 0 ) + return ret; + + /* success */ + + return up_progmem_pagesize(page); +} + +ssize_t up_progmem_ispageerased(size_t page) +{ + size_t addr; + size_t count; + size_t bwritten = 0; + + if (page >= (EFM32_FLASH_NPAGES+EFM32_USERDATA_NPAGES)) + { + return -EFAULT; + } + + /* Verify */ + + for (addr = up_progmem_getaddress(page), count = up_progmem_pagesize(page); + count; count--, addr++) + { + if (getreg8(addr) != 0xff) + { + bwritten++; + } + } + + return bwritten; + +} + +ssize_t __ramfunc__ up_progmem_write(size_t addr, const void *buf, size_t size) +{ + int ret = 0; + int word_count; + int num_words; + int page_words; + uint32_t* p_data; + uint32_t* address = (uint32_t*) addr; + uint32_t num_bytes = size; + + /* EFM32 requires word access */ + + if (addr & 3) + { + return -EINVAL; + } + + /* EFM32 requires word access */ + + if (num_bytes & 3) + { + return -EINVAL; + } + + efm32_flash_unlock(); + + /* enable writing to the flash */ + + bitband_set_peripheral(EFM32_MSC_WRITECTRL,_MSC_WRITECTRL_WREN_SHIFT,1); + + /* Convert bytes to words */ + + num_words = num_bytes >> 2; + + /* The following loop splits the data into chunks corresponding to flash pages. + * The address is loaded only once per page, because the hardware automatically + * increments the address internally for each data load inside a page. + */ + + for (word_count = 0, p_data = (uint32_t*) buf; word_count < num_words; ) + { + int page_bytes; + ssize_t page_idx; + irqstate_t irqs; + + /* Compute the number of words to write to the current page. */ + + page_idx = up_progmem_getpage((size_t)address+(word_count<<2)); + if ( page_idx < 0 ) + { + ret = -EINVAL; + break; + } + + page_bytes = up_progmem_pagesize(page_idx); + if ( page_bytes < 0 ) + { + ret = -EINVAL; + break; + } + + page_words = (page_bytes - (((uint32_t) (address + word_count)) & \ + (page_bytes-1))) / sizeof(uint32_t); + + if (page_words > num_words - word_count) + { + page_words = num_words - word_count; + } + + irqs = irqsave(); + + /* First we load address. The address is auto-incremented within a page. + Therefore the address phase is only needed once for each page. */ + + ret = msc_load_verify_address(address + word_count); + + /* Now write the data in the current page. */ + + if (ret == 0) + { + ret = msc_load_write_data( p_data, page_words, true ); + } + + irqrestore(irqs); + + if (ret != 0 ) + { + break; + } + + word_count += page_words; + p_data += page_words; + } + + /* Disable writing to the MSC */ + + bitband_set_peripheral(EFM32_MSC_WRITECTRL,_MSC_WRITECTRL_WREN_SHIFT,0); + +#if (defined(CONFIG_EFM32_EFM32GG) || defined(CONFIG_EFM32_EFM32WG)) && (2==WORDS_PER_DATA_PHASE) + + /* Turn off double word write cycle support. */ + + bitband_set_peripheral(EFM32_MSC_WRITECTRL,_MSC_WRITECTRL_WDOUBLE_SHIFT,0); + +#endif + + if (ret < 0 ) + return ret; + + return word_count; +} + +#endif /* defined(CONFIG_ARCH_CHIP_EFM32) */ + diff --git a/arch/arm/src/efm32/efm32_gpio.c b/arch/arm/src/efm32/efm32_gpio.c index 3f99ff6db9f..6a818c93c92 100644 --- a/arch/arm/src/efm32/efm32_gpio.c +++ b/arch/arm/src/efm32/efm32_gpio.c @@ -171,9 +171,11 @@ static inline void efm32_setdrive(uintptr_t base, uint8_t pin, uint8_t drive) * REVISIT: Is there any sane way to manage this for multiple pins in the port * with different drive values? */ - - putreg32((uint32_t)drive << _GPIO_P_CTRL_DRIVEMODE_SHIFT, - base + EFM32_GPIO_Pn_CTRL_OFFSET); + if (drive != _GPIO_DRIVE_STANDARD) + { + putreg32((uint32_t)drive << _GPIO_P_CTRL_DRIVEMODE_SHIFT, + base + EFM32_GPIO_Pn_CTRL_OFFSET); + } } /************************************************************************************ diff --git a/arch/arm/src/efm32/efm32_gpio.h b/arch/arm/src/efm32/efm32_gpio.h index e51b3033978..ed54bb5148c 100644 --- a/arch/arm/src/efm32/efm32_gpio.h +++ b/arch/arm/src/efm32/efm32_gpio.h @@ -141,15 +141,15 @@ */ #define GPIO_DRIVE_SHIFT (9) /* Bits 9-10: Output drive strength */ -#define GPIO_DRIVE_MASK (3 << GPIO_MODE_SHIFT) +#define GPIO_DRIVE_MASK (3 << GPIO_DRIVE_SHIFT) # define _GPIO_DRIVE_STANDARD (0) /* 6 mA drive current */ # define _GPIO_DRIVE_LOWEST (1) /* 0.5 mA drive current */ # define _GPIO_DRIVE_HIGH (2) /* 20 mA drive current */ # define _GPIO_DRIVE_LOW (3) /* 2 mA drive current */ -# define GPIO_DRIVE_STANDARD (_GPIO_DRIVE_STANDARD << GPIO_MODE_SHIFT) -# define GPIO_DRIVE_LOWEST (_GPIO_DRIVE_LOWEST << GPIO_MODE_SHIFT) -# define GPIO_DRIVE_HIGH (_GPIO_DRIVE_HIGH << GPIO_MODE_SHIFT) -# define GPIO_DRIVE_LOW (_GPIO_DRIVE_LOW << GPIO_MODE_SHIFT) +# define GPIO_DRIVE_STANDARD (_GPIO_DRIVE_STANDARD << GPIO_DRIVE_SHIFT) +# define GPIO_DRIVE_LOWEST (_GPIO_DRIVE_LOWEST << GPIO_DRIVE_SHIFT) +# define GPIO_DRIVE_HIGH (_GPIO_DRIVE_HIGH << GPIO_DRIVE_SHIFT) +# define GPIO_DRIVE_LOW (_GPIO_DRIVE_LOW << GPIO_DRIVE_SHIFT) /* Interrupt Mode (Input only): * @@ -328,6 +328,20 @@ void efm32_gpioirqdisable(int irq); # define efm32_gpioirqdisable(irq) #endif +/************************************************************************************ + * Name: efm32_gpioirqclear + * + * Description: + * clear the interrupt for specified PIO IRQ + * + ************************************************************************************/ + +#ifdef CONFIG_EFM32_GPIO_IRQ +void efm32_gpioirqclear(int irq); +#else +# define efm32_gpioirqclear(irq) +#endif + /************************************************************************************ * Function: efm32_dumpgpio * diff --git a/arch/arm/src/efm32/efm32_gpioirq.c b/arch/arm/src/efm32/efm32_gpioirq.c index 58d42023a78..1043e3dae5d 100644 --- a/arch/arm/src/efm32/efm32_gpioirq.c +++ b/arch/arm/src/efm32/efm32_gpioirq.c @@ -49,6 +49,7 @@ #include "up_arch.h" #include "chip/efm32_gpio.h" #include "efm32_gpio.h" +#include "efm32_bitband.h" #ifdef CONFIG_EFM32_GPIO_IRQ @@ -270,20 +271,24 @@ void efm32_gpioirq(gpio_pinset_t pinset) void efm32_gpioirqenable(int irq) { - irqstate_t flags; - uint32_t regval; - uint32_t bit; if (irq >= EFM32_IRQ_EXTI0 && irq <= EFM32_IRQ_EXTI15) { /* Enable the interrupt associated with the pin */ +#ifndef CONFIG_EFM32_BITBAND + irqstate_t flags; + uint32_t regval; + uint32_t bit; bit = ((uint32_t)1 << (irq - EFM32_IRQ_EXTI0)); flags = irqsave(); regval = getreg32(EFM32_GPIO_IEN); regval |= bit; putreg32(regval, EFM32_GPIO_IEN); irqrestore(flags); +#else + bitband_set_peripheral(EFM32_GPIO_IEN,(irq - EFM32_IRQ_EXTI0),1); +#endif } } @@ -297,20 +302,56 @@ void efm32_gpioirqenable(int irq) void efm32_gpioirqdisable(int irq) { - irqstate_t flags; - uint32_t regval; - uint32_t bit; if (irq >= EFM32_IRQ_EXTI0 && irq <= EFM32_IRQ_EXTI15) { /* Enable the interrupt associated with the pin */ +#ifndef CONFIG_EFM32_BITBAND + irqstate_t flags; + uint32_t regval; + uint32_t bit; bit = ((uint32_t)1 << (irq - EFM32_IRQ_EXTI0)); flags = irqsave(); regval = getreg32(EFM32_GPIO_IEN); regval &= ~bit; putreg32(regval, EFM32_GPIO_IEN); irqrestore(flags); +#else + bitband_set_peripheral(EFM32_GPIO_IEN,(irq - EFM32_IRQ_EXTI0),0); +#endif + } +} + +/************************************************************************************ + * Name: efm32_gpioirqclear + * + * Description: + * Disable the interrupt for specified PIO IRQ + * + ************************************************************************************/ + +void efm32_gpioirqclear(int irq) +{ + + if (irq >= EFM32_IRQ_EXTI0 && irq <= EFM32_IRQ_EXTI15) + { + /* Enable the interrupt associated with the pin */ + +#ifndef CONFIG_EFM32_BITBAND + irqstate_t flags; + uint32_t regval; + uint32_t bit; + + bit = ((uint32_t)1 << (irq - EFM32_IRQ_EXTI0)); + flags = irqsave(); + regval = getreg32(EFM32_GPIO_IFC); + regval |= bit; + putreg32(regval, EFM32_GPIO_IFC); + irqrestore(flags); +#else + bitband_set_peripheral(EFM32_GPIO_IFC,(irq - EFM32_IRQ_EXTI0),1); +#endif } } diff --git a/arch/arm/src/efm32/efm32_i2c.c b/arch/arm/src/efm32/efm32_i2c.c index cdf6b9a8104..7f72dec014a 100644 --- a/arch/arm/src/efm32/efm32_i2c.c +++ b/arch/arm/src/efm32/efm32_i2c.c @@ -212,6 +212,7 @@ struct efm32_trace_s uint32_t i2c_reg_state; /* I2C register I2Cx_STATES */ uint32_t i2c_reg_if; /* I2C register I2Cx_IF */ uint32_t count; /* Interrupt count when status change */ + int dcnt; /* Interrupt count when status change */ uint32_t time; /* First of event or first status */ }; @@ -242,7 +243,7 @@ struct efm32_i2c_priv_s sem_t sem_isr; /* Interrupt wait semaphore */ #endif - volatile uint8_t result; /* result of transfer */ + volatile int8_t result; /* result of transfer */ uint8_t i2c_state; /* i2c state machine */ uint32_t i2c_reg_if; /* Current state of I2Cx_IF register. */ @@ -346,7 +347,9 @@ static int efm32_i2c_transfer(FAR struct i2c_dev_s *dev, FAR struct i2c_msg_s *msgs, int count); #endif /* CONFIG_I2C_TRANSFER */ +#ifdef CONFIG_I2C_TRACE static const char *efm32_i2c_state_str(int i2c_state); +#endif /******************************************************************************* * Private Data @@ -485,6 +488,7 @@ static inline void efm32_i2c_modifyreg(FAR struct efm32_i2c_priv_s *priv, * ******************************************************************************/ +#ifdef CONFIG_I2C_TRACE static const char *efm32_i2c_state_str(int i2c_state) { switch (i2c_state) @@ -515,6 +519,7 @@ static const char *efm32_i2c_state_str(int i2c_state) return "Unknown state!"; } } +#endif /******************************************************************************* * Name: efm32_i2c_sem_wait @@ -779,6 +784,7 @@ static void efm32_i2c_tracenew(FAR struct efm32_i2c_priv_s *priv) if ((trace->count == 0) || (priv->i2c_reg_if != trace->i2c_reg_if) || (priv->i2c_reg_state != trace->i2c_reg_state) || + (priv->dcnt != trace->dcnt) || (priv->i2c_state != trace->i2c_state)) { /* Yes.. Was it the states changed? */ @@ -804,6 +810,7 @@ static void efm32_i2c_tracenew(FAR struct efm32_i2c_priv_s *priv) trace->i2c_reg_state = priv->i2c_reg_state; trace->i2c_reg_if = priv->i2c_reg_if; trace->count = 1; + trace->dcnt = priv->dcnt; trace->time = clock_systimer(); } else @@ -821,15 +828,15 @@ static void efm32_i2c_tracedump(FAR struct efm32_i2c_priv_s *priv) syslog(LOG_DEBUG, "Elapsed time: %d\n", clock_systimer() - priv->start_time); - for (i = 0; i <= priv->tndx; i++) + for (i = 0; i < priv->tndx; i++) { trace = &priv->trace[i]; syslog(LOG_DEBUG, - "%2d. I2Cx_STATE: %08x I2Cx_PENDING: %08x COUNT: %3d " - "STATE: %s(%2d) PARM: %08x TIME: %d\n", - i + 1, trace->i2c_reg_state, trace->i2c_reg_if, trace->count, - efm32_i2c_state_str(trace->i2c_state), trace->i2c_state, - trace->time - priv->start_time); + "%2d. I2Cx_STATE: %08x I2Cx_PENDING: %08x dcnt %3d COUNT: %3d " + "STATE: %s(%2d) TIME: %d\n", + i + 1, trace->i2c_reg_state, trace->i2c_reg_if, trace->dcnt, + trace->count, efm32_i2c_state_str(trace->i2c_state), + trace->i2c_state, trace->time - priv->start_time); } } #endif /* CONFIG_I2C_TRACE */ @@ -1233,19 +1240,22 @@ static int efm32_i2c_isr(struct efm32_i2c_priv_s *priv) efm32_i2c_putreg(priv, EFM32_I2C_CMD_OFFSET, I2C_CMD_STOP); } - else if (priv->dcnt == 1) - { - /* If there is only one byte to receive we need to transmit - * the NACK now, before the stop. - */ - - efm32_i2c_putreg(priv, EFM32_I2C_CMD_OFFSET, I2C_CMD_NACK); - } else { /* Send ACK and wait for next byte */ efm32_i2c_putreg(priv, EFM32_I2C_CMD_OFFSET, I2C_CMD_ACK); + + if (priv->dcnt == 1) + { + /* If there is more than one byte to receive and this is + * the next to last byte we need to transmit the NACK + * now, before receiving the last byte. + */ + + efm32_i2c_putreg(priv,EFM32_I2C_CMD_OFFSET,I2C_CMD_NACK); + } + } } goto done; @@ -1595,23 +1605,47 @@ static int efm32_i2c_process(FAR struct i2c_dev_s *dev, efm32_i2c_putreg(priv, EFM32_I2C_CMD_OFFSET, I2C_CMD_ABORT); } + else + { - /* Check for error status conditions */ + /* Check for error status conditions */ -#if 0 - /* Arbitration Lost (master mode) */ - errval = EAGAIN; - /* Acknowledge Failure */ - errval = ENXIO; - /* Overrun/Underrun */ - errval = EIO; - /* PEC Error in reception */ - errval = EPROTO; - /* Timeout or Tlow Error */ - errval = ETIME; - /* I2C Bus is for some reason busy */ - errval = EBUSY; -#endif + switch(priv->result) + { + + /* Arbitration lost during transfer. */ + + case I2CRESULT_ARBLOST: + errval = EAGAIN; + break; + + /* NACK received during transfer. */ + + case I2CRESULT_NACK: + errval = ENXIO; + break; + + /* SW fault. */ + + case I2CRESULT_SWFAULT: + errval = EIO; + break; + + /* Usage fault. */ + + case I2CRESULT_USAGEFAULT: + errval = EINTR; + break; + + /* Bus error during transfer (misplaced START/STOP). + * I2C Bus is for some reason busy + */ + + case I2CRESULT_BUSERR: + errval = EBUSY; + break; + } + } /* Dump the trace result */ @@ -1738,7 +1772,7 @@ FAR struct i2c_dev_s *up_i2cinitialize(int port) struct efm32_i2c_priv_s *priv = NULL; /* Private data of device with multiple * instances */ struct efm32_i2c_inst_s *inst = NULL; /* Device, single instance */ - int irqs; + irqstate_t irqs; /* Get I2C private structure */ @@ -1801,7 +1835,7 @@ FAR struct i2c_dev_s *up_i2cinitialize(int port) int up_i2cuninitialize(FAR struct i2c_dev_s *dev) { - int irqs; + irqstate_t irqs; ASSERT(dev); diff --git a/arch/arm/src/efm32/efm32_rmu.c b/arch/arm/src/efm32/efm32_rmu.c index 033cc522bde..43d84b1f2a0 100644 --- a/arch/arm/src/efm32/efm32_rmu.c +++ b/arch/arm/src/efm32/efm32_rmu.c @@ -80,13 +80,13 @@ typedef struct static efm32_reset_cause_list_t efm32_reset_cause_list[] = { { - 0x0001, // 0bXXXX XXXX XXXX XXX1 - 0x0001, // 0bXXXX XXXX XXXX XXX1 + 0x0001, //0bXXXX XXXX XXXX XXX1 + 0x0001, //0bXXXX XXXX XXXX XXX1 "A Power-on Reset has been performed. X bits are don't care." }, { - 0x0002, // 0bXXXX XXXX 0XXX XX10 - 0x0003, // 0bXXXX XXXX 1XXX XX11 + 0x0002, //0bXXXX XXXX 0XXX XX10 + 0x0003, //0bXXXX XXXX 1XXX XX11 "A Brown-out has been detected on the unregulated power." }, {